Skip to content

Commit 7b1d692

Browse files
authored
Merge pull request redhat-performance#402 from grafuls/development
feat+test: nic attribute modification
2 parents 2263524 + 6f1df6c commit 7b1d692

File tree

6 files changed

+2026
-147
lines changed

6 files changed

+2026
-147
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,26 @@ badfish -H mgmt-your-server.example.com -u root -p yourpass --disable-sriov
524524
NOTE:
525525
* This is only supported on DELL devices.
526526

527+
### Get FQDDs for all nics
528+
To get a list of all FQDDs for all NICs on the server you can run badfish with ```--get-nic-fqdds```.
529+
```bash
530+
badfish -H mgmt-your-server.example.com -u root -p yourpass --get-nic-fqdds
531+
```
532+
533+
### Get NIC attributes
534+
To get a list of all NIC attributes we can potentially modify (some might be set as read-only), you can run badfish with ```--get-nic-attribute``` passing the desired FQDD and this will return a list off all NIC attributes with their current value set.
535+
```bash
536+
badfish -H mgmt-your-server.example.com -u root -p yourpass --get-nic-attribute NIC.Integrated.1-1-1
537+
```
538+
539+
### Set NIC attribute
540+
To change the value of a NIC attribute you can use ```--set-nic-attribute``` with the desired FQDD, passing both ```--attribute``` and desired ```--value```.
541+
```bash
542+
badfish -H mgmt-your-server.example.com -u root -p yourpass --set-nic-attribute NIC.Integrated.1-1-1 --attribute LegacyBootProto --value PXE
543+
```
544+
NOTE:
545+
* This action will trigger a reboot of the server to apply the changes. Changes will be reflected after the reboot is completed.
546+
527547
### Get BIOS attributes
528548
To get a list of all BIOS attributes we can potentially modify (some might be set as read-only), you can run badfish with ```--get-bios-attribute``` alone and this will return a list off all BIOS attributes with their current value set.
529549
```bash

src/badfish/main.py

Lines changed: 238 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1014,7 +1014,7 @@ async def reset_idrac(self):
10141014
_response = await self.post_request(_url, _payload, _headers)
10151015

10161016
status_code = _response.status
1017-
if status_code == 204:
1017+
if status_code in [200, 204]:
10181018
self.logger.info("Status code %s returned for POST command to reset iDRAC." % status_code)
10191019
else:
10201020
data = await _response.text("utf-8", "ignore")
@@ -2193,6 +2193,216 @@ async def import_scp(self, file_path, targets="ALL"):
21932193
continue
21942194
return True
21952195

2196+
async def get_nic_fqdds(self):
2197+
uri = "%s%s/NetworkAdapters" % (self.host_uri, self.system_resource)
2198+
resp = await self.get_request(uri)
2199+
if resp.status == 404 or self.vendor == "Supermicro":
2200+
self.logger.error("Operation not supported by vendor.")
2201+
return False
2202+
2203+
try:
2204+
raw = await resp.text("utf-8", "ignore")
2205+
data = json.loads(raw.strip())
2206+
nic_list = [[nic[1].split("/")[-1] for nic in member.items()][0] for member in data.get("Members")]
2207+
self.logger.debug("Detected NIC FQDDs for existing network adapters.")
2208+
for nic in nic_list:
2209+
uri = "%s%s/NetworkAdapters/%s/NetworkDeviceFunctions" % (self.host_uri, self.system_resource, nic)
2210+
resp = await self.get_request(uri)
2211+
raw = await resp.text("utf-8", "ignore")
2212+
data = json.loads(raw.strip())
2213+
nic_fqqds = [[fqdd[1].split("/")[-1] for fqdd in member.items()][0] for member in data.get("Members")]
2214+
self.logger.info(f"{nic}:")
2215+
for i, fqdd in enumerate(nic_fqqds, start=1):
2216+
self.logger.info(f" {i}: {fqdd}")
2217+
except (AttributeError, IndexError, KeyError, TypeError, ValueError):
2218+
self.logger.error("Was unable to get NIC FQDDs, invalid server response.")
2219+
return False
2220+
return True
2221+
2222+
async def get_nic_attribute(self, fqdd, log=True):
2223+
uri = "%s/Chassis/%s/NetworkAdapters/%s/NetworkDeviceFunctions/%s/Oem/Dell/DellNetworkAttributes/%s" % (
2224+
self.root_uri,
2225+
self.system_resource.split("/")[-1],
2226+
fqdd.split("-")[0],
2227+
fqdd,
2228+
fqdd,
2229+
)
2230+
resp = await self.get_request(uri)
2231+
if resp.status == 404 or self.vendor == "Supermicro":
2232+
self.logger.error("Operation not supported by vendor.")
2233+
return False
2234+
2235+
try:
2236+
raw = await resp.text("utf-8", "ignore")
2237+
data = json.loads(raw.strip())
2238+
attributes_list = [(key, value) for key, value in data.get("Attributes").items()]
2239+
if not log:
2240+
return attributes_list
2241+
self.logger.debug(f"All NIC attributes of {fqdd}.")
2242+
self.logger.info(f"{fqdd}")
2243+
for key, value in attributes_list:
2244+
self.logger.info(f" {key}: {value}")
2245+
except (AttributeError, KeyError, TypeError, ValueError):
2246+
self.logger.error("Was unable to get NIC attribute(s) info, invalid server response.")
2247+
return False
2248+
return True
2249+
2250+
async def get_idrac_fw_version(self):
2251+
idrac_fw_version = 0
2252+
try:
2253+
uri = "%s%s/" % (self.host_uri, self.manager_resource)
2254+
resp = await self.get_request(uri)
2255+
if resp.status == 404 or self.vendor == "Supermicro":
2256+
self.logger.error("Operation not supported by vendor.")
2257+
return 0
2258+
raw = await resp.text("utf-8", "ignore")
2259+
data = json.loads(raw.strip())
2260+
idrac_fw_version = int(data["FirmwareVersion"].replace(".", ""))
2261+
except (AttributeError, ValueError, StopIteration):
2262+
self.logger.error("Was unable to get iDRAC Firmware Version.")
2263+
return 0
2264+
return idrac_fw_version
2265+
2266+
async def get_nic_attribute_registry(self, fqdd=None):
2267+
registry = []
2268+
idrac_fw_version = await self.get_idrac_fw_version()
2269+
if not idrac_fw_version or idrac_fw_version < 5100000:
2270+
self.logger.error("Unsupported iDRAC version.")
2271+
return []
2272+
try:
2273+
uri = "%s/Registries/NetworkAttributesRegistry_%s/NetworkAttributesRegistry_%s.json" % (
2274+
self.root_uri,
2275+
fqdd,
2276+
fqdd,
2277+
)
2278+
resp = await self.get_request(uri)
2279+
if resp.status == 404:
2280+
self.logger.error("Was unable to get network attribute registry.")
2281+
return []
2282+
raw = await resp.text("utf-8", "ignore")
2283+
data = json.loads(raw.strip())
2284+
registry = [attr for attr in data.get("RegistryEntries").get("Attributes")]
2285+
except (AttributeError, KeyError, TypeError, ValueError):
2286+
self.logger.error("Was unable to get network attribute registry.")
2287+
return []
2288+
return registry
2289+
2290+
async def get_nic_attribute_info(self, fqdd, attribute, log=True):
2291+
if self.vendor == "Supermicro":
2292+
self.logger.error("Operation not supported by vendor.")
2293+
return False
2294+
registry = await self.get_nic_attribute_registry(fqdd)
2295+
if not registry:
2296+
self.logger.error("Was unable to get network attribute info.")
2297+
return False
2298+
try:
2299+
registry = [attr_dict for attr_dict in registry if attr_dict.get("AttributeName") == attribute][0]
2300+
current_value = await self.get_nic_attribute(fqdd, False)
2301+
current_value = [tup[1] for tup in current_value if tup[0] == attribute][0]
2302+
registry.update({"CurrentValue": current_value})
2303+
if not log:
2304+
return registry
2305+
for key, value in registry.items():
2306+
self.logger.info(f"{key}: {value}")
2307+
except (AttributeError, IndexError, KeyError, TypeError):
2308+
self.logger.error("Was unable to get network attribute info.")
2309+
return False
2310+
return True
2311+
2312+
async def set_nic_attribute(self, fqdd, attribute, value):
2313+
if self.vendor == "Supermicro":
2314+
self.logger.error("Operation not supported by vendor.")
2315+
return False
2316+
2317+
attr_info = await self.get_nic_attribute_info(fqdd, attribute, False)
2318+
if not attr_info:
2319+
self.logger.error("Was unable to set a network attribute. Attribute most likely doesn't exist.")
2320+
return False
2321+
2322+
try:
2323+
type = attr_info.get("Type")
2324+
current_value = attr_info.get("CurrentValue")
2325+
if value == current_value:
2326+
self.logger.warning("This attribute already is set to this value. Skipping.")
2327+
return True
2328+
if type == "Enumeration":
2329+
allowed_values = [value_spec.get("ValueName") for value_spec in attr_info.get("Value")]
2330+
if value not in allowed_values:
2331+
self.logger.error("Value not allowed for this attribute.")
2332+
self.logger.error("Was unable to set a network attribute.")
2333+
return False
2334+
if type == "String":
2335+
max, min = int(attr_info.get("MaxLength")), int(attr_info.get("MinLength"))
2336+
if len(value) > max or len(value) < min:
2337+
self.logger.error("Value not allowed for this attribute. (Incorrect string length)")
2338+
self.logger.error("Was unable to set a network attribute.")
2339+
return False
2340+
if type == "Integer":
2341+
max, min = int(attr_info.get("UpperBound")), int(attr_info.get("LowerBound"))
2342+
value = int(value)
2343+
if value > max or value < min:
2344+
self.logger.error("Value not allowed for this attribute. (Incorrect number bounds)")
2345+
self.logger.error("Was unable to set a network attribute.")
2346+
return False
2347+
except (AttributeError, IndexError, KeyError, TypeError):
2348+
self.logger.error("Was unable to set a network attribute.")
2349+
return False
2350+
2351+
try:
2352+
uri = (
2353+
"%s/Chassis/System.Embedded.1/NetworkAdapters/%s/NetworkDeviceFunctions/%s/Oem/Dell/DellNetworkAttributes/%s/Settings"
2354+
% (
2355+
self.root_uri,
2356+
fqdd.split("-")[0],
2357+
fqdd,
2358+
fqdd,
2359+
)
2360+
)
2361+
self.logger.debug(uri)
2362+
except (IndexError, ValueError):
2363+
self.logger.error("Invalid FQDD suplied.")
2364+
return False
2365+
2366+
headers = {"content-type": "application/json"}
2367+
payload = {
2368+
"@Redfish.SettingsApplyTime": {"ApplyTime": "OnReset"},
2369+
"Attributes": {attribute: value},
2370+
}
2371+
first_reset = False
2372+
try:
2373+
for i in range(self.retries):
2374+
response = await self.patch_request(uri, payload, headers)
2375+
status_code = response.status
2376+
if status_code in [200, 202]:
2377+
self.logger.info("Patch command to set network attribute values and create next reboot job PASSED.")
2378+
break
2379+
else:
2380+
self.logger.error(
2381+
"Patch command to set network attribute values and create next reboot job FAILED, error code is: %s."
2382+
% status_code
2383+
)
2384+
if status_code == 503 and i - 1 != self.retries:
2385+
self.logger.info("Retrying to send the patch command.")
2386+
continue
2387+
elif status_code == 400:
2388+
self.logger.info("Retrying to send the patch command.")
2389+
await self.clear_job_queue()
2390+
if not first_reset:
2391+
await self.reset_idrac()
2392+
await asyncio.sleep(10)
2393+
first_reset = True
2394+
continue
2395+
self.logger.error(
2396+
"Patch command to set network attribute values and create next reboot job FAILED, error code is: %s."
2397+
% status_code
2398+
)
2399+
self.logger.error("Was unable to set a network attribute.")
2400+
return False
2401+
except (AttributeError, ValueError):
2402+
self.logger.error("Was unable to set a network attribute.")
2403+
2404+
await self.reboot_server()
2405+
21962406

21972407
async def execute_badfish(_host, _args, logger, format_handler=None):
21982408
_username = _args["u"]
@@ -2249,6 +2459,9 @@ async def execute_badfish(_host, _args, logger, format_handler=None):
22492459
scp_include_read_only = _args["scp_include_read_only"]
22502460
export_scp = _args["export_scp"]
22512461
import_scp = _args["import_scp"]
2462+
get_nic_fqdds = _args["get_nic_fqdds"]
2463+
get_nic_attribute = _args["get_nic_attribute"]
2464+
set_nic_attribute = _args["set_nic_attribute"]
22522465
result = True
22532466

22542467
try:
@@ -2355,6 +2568,15 @@ async def execute_badfish(_host, _args, logger, format_handler=None):
23552568
await badfish.export_scp(export_scp, scp_targets, scp_include_read_only)
23562569
elif import_scp:
23572570
await badfish.import_scp(import_scp, scp_targets)
2571+
elif get_nic_fqdds:
2572+
await badfish.get_nic_fqdds()
2573+
elif get_nic_attribute:
2574+
if attribute:
2575+
await badfish.get_nic_attribute_info(get_nic_attribute, attribute)
2576+
else:
2577+
await badfish.get_nic_attribute(get_nic_attribute)
2578+
elif set_nic_attribute:
2579+
await badfish.set_nic_attribute(set_nic_attribute, attribute, value)
23582580

23592581
if pxe and not host_type:
23602582
await badfish.set_next_boot_pxe()
@@ -2632,6 +2854,21 @@ def main(argv=None):
26322854
"imported.",
26332855
default="",
26342856
)
2857+
parser.add_argument(
2858+
"--get-nic-fqdds",
2859+
help="List FQDDs for all NICs.",
2860+
action="store_true",
2861+
)
2862+
parser.add_argument(
2863+
"--get-nic-attribute",
2864+
help="Get a NIC attribute values, specify a NIC FQDD.",
2865+
default="",
2866+
)
2867+
parser.add_argument(
2868+
"--set-nic-attribute",
2869+
help="Set a NIC attribute value",
2870+
default="",
2871+
)
26352872

26362873
_args = vars(parser.parse_args(argv))
26372874

0 commit comments

Comments
 (0)