Last active
September 24, 2018 08:28
-
-
Save grafuls/0e9f7c516c68f7cf950abb5a13cc934a to your computer and use it in GitHub Desktop.
Client tool for changing boot order and setting next boot to PXE via Redfish API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #! /usr/bin/env python | |
| import argparse | |
| import json | |
| import re | |
| import requests | |
| import sys | |
| import time | |
| import warnings | |
| import yaml | |
| warnings.filterwarnings("ignore") | |
| def get_bios_boot_mode(_host, _username, _password): | |
| _response = requests.get( | |
| "https://%s/redfish/v1/Systems/System.Embedded.1/Bios" % _host, verify=False, auth=(_username, _password) | |
| ) | |
| data = _response.json() | |
| return data[u"Attributes"]["BootMode"] | |
| def get_boot_seq(_bios_boot_mode): | |
| if _bios_boot_mode == "Uefi": | |
| return "UefiBootSeq" | |
| else: | |
| return "BootSeq" | |
| def get_boot_devices(_host, _username, _password, _boot_seq): | |
| _response = requests.get( | |
| "https://%s/redfish/v1/Systems/System.Embedded.1/BootSources" % _host, | |
| verify=False, | |
| auth=(_username, _password), | |
| ) | |
| data = _response.json() | |
| return data[u"Attributes"][_boot_seq] | |
| def change_boot_order(_host, _username, _password): | |
| with open(interfaces_path, "r") as f: | |
| try: | |
| definitions = yaml.safe_load(f) | |
| except yaml.YAMLError as ex: | |
| print(ex) | |
| sys.exit(1) | |
| host_model = host.split(".")[0].split("-")[-1] | |
| interfaces = definitions["%s_%s_interfaces" % (host_type, host_model)].split(",") | |
| bios_boot_mode = get_bios_boot_mode(_host, _username, _password) | |
| boot_seq = get_boot_seq(bios_boot_mode) | |
| boot_devices = get_boot_devices(_host, _username, _password, boot_seq) | |
| change = False | |
| for i in range(len(interfaces)): | |
| for device in boot_devices: | |
| if interfaces[i] == device[u"Name"]: | |
| if device[u"Index"] != i: | |
| device[u"Index"] = i | |
| change = True | |
| break | |
| if change: | |
| url = "https://%s/redfish/v1/Systems/System.Embedded.1/BootSources/Settings" % host | |
| payload = {"Attributes": {boot_seq: boot_devices}} | |
| headers = {"content-type": "application/json"} | |
| response = requests.patch( | |
| url, data=json.dumps(payload), headers=headers, verify=False, auth=(username, password) | |
| ) | |
| if response.status_code == 200: | |
| print("\n- PASS, PATCH command passed to update boot order") | |
| else: | |
| print("FAIL: There was something wrong with your request") | |
| else: | |
| print("WARN: No changes were made since the boot order already matches the requested.") | |
| sys.exit() | |
| def set_next_boot_pxe(_host, _username, _password): | |
| _url = "https://%s/redfish/v1/Systems/System.Embedded.1" % _host | |
| _payload = {"Boot": {"BootSourceOverrideTarget": "Pxe"}} | |
| _headers = {"content-type": "application/json"} | |
| _response = requests.patch( | |
| _url, data=json.dumps(_payload), headers=_headers, verify=False, auth=(_username, _password) | |
| ) | |
| status_code = _response.status_code | |
| time.sleep(5) | |
| if status_code == 200: | |
| print('\n- PASS, PATCH command passed to set next boot onetime boot device to: "%s"' % "Pxe") | |
| else: | |
| print("\n- FAIL, Command failed, error code is %s" % status_code) | |
| detail_message = str(_response.__dict__) | |
| print(detail_message) | |
| sys.exit() | |
| def create_bios_config_job(_host, _username, _password): | |
| _url = "https://%s/redfish/v1/Managers/iDRAC.Embedded.1/Jobs" % _host | |
| _payload = {"TargetSettingsURI": "/redfish/v1/Systems/System.Embedded.1/Bios/Settings"} | |
| _headers = {"content-type": "application/json"} | |
| _response = requests.post( | |
| _url, data=json.dumps(_payload), headers=_headers, verify=False, auth=(_username, _password) | |
| ) | |
| statusCode = _response.status_code | |
| if statusCode == 200: | |
| print("- PASS: POST command passed to create target config job, status code 200 returned.") | |
| else: | |
| print("- FAIL, POST command failed to create BIOS config job, status code is %s\n" % statusCode) | |
| detail_message = str(_response.__dict__) | |
| print(detail_message) | |
| sys.exit() | |
| convert_to_string = str(_response.__dict__) | |
| job_id_search = re.search("JID_.+?,", convert_to_string).group() | |
| _job_id = re.sub("[,']", "", job_id_search) | |
| print("- WARNING: %s job ID successfully created\n" % _job_id) | |
| return _job_id | |
| def get_job_status(_host, _username, _password, _job_id): | |
| while True: | |
| req = requests.get( | |
| "https://%s/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/%s" % (_host, _job_id), | |
| verify=False, | |
| auth=(_username, _password), | |
| ) | |
| statusCode = req.status_code | |
| if statusCode == 200: | |
| print("- PASS, Command passed to check job status, code 200 returned") | |
| time.sleep(20) | |
| else: | |
| print("\n- FAIL, Command failed to check job status, return code is %s" % statusCode) | |
| print("Extended Info Message: {0}".format(req.json())) | |
| sys.exit() | |
| data = req.json() | |
| if data[u"Message"] == "Task successfully scheduled.": | |
| print("- PASS, job id %s successfully scheduled" % _job_id) | |
| break | |
| else: | |
| print("- WARNING: JobStatus not scheduled, current status is: %s" % data[u"Message"]) | |
| def reboot_server(_host, _username, _password): | |
| _response = requests.get( | |
| "https://%s/redfish/v1/Systems/System.Embedded.1/" % _host, verify=False, auth=(_username, _password) | |
| ) | |
| data = _response.json() | |
| print("\n- WARNING, Current server power state is: %s" % data[u"PowerState"]) | |
| if data[u"PowerState"] == "On": | |
| _url = "https://%s/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" % _host | |
| _payload = {"ResetType": "GracefulShutdown"} | |
| _headers = {"content-type": "application/json"} | |
| _response = requests.post( | |
| _url, data=json.dumps(_payload), headers=_headers, verify=False, auth=(_username, _password) | |
| ) | |
| statusCode = _response.status_code | |
| if statusCode == 204: | |
| print("- PASS, Command passed to gracefully power OFF server, code return is %s" % statusCode) | |
| time.sleep(10) | |
| else: | |
| print("\n- FAIL, Command failed to gracefully power OFF server, status code is: %s\n" % statusCode) | |
| print("Extended Info Message: {0}".format(_response.json())) | |
| sys.exit() | |
| count = 0 | |
| while True: | |
| _response = requests.get( | |
| "https://%s/redfish/v1/Systems/System.Embedded.1/" % _host, verify=False, auth=(_username, _password) | |
| ) | |
| data = _response.json() | |
| if data[u"PowerState"] == "Off": | |
| print("- PASS, GET command passed to verify server is in OFF state") | |
| break | |
| elif count == 20: | |
| print("- WARNING, unable to graceful shutdown the server, will perform forced shutdown now") | |
| _url = "https://%s/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" % _host | |
| _payload = {"ResetType": "ForceOff"} | |
| _headers = {"content-type": "application/json"} | |
| _response = requests.post( | |
| _url, data=json.dumps(_payload), headers=_headers, verify=False, auth=(_username, _password) | |
| ) | |
| statusCode = _response.status_code | |
| if statusCode == 204: | |
| print("- PASS, Command passed to forcefully power OFF server, code return is %s" % statusCode) | |
| time.sleep(15) | |
| else: | |
| print("\n- FAIL, Command failed to gracefully power OFF server, status code is: %s\n" % statusCode) | |
| print("Extended Info Message: {0}".format(_response.json())) | |
| sys.exit() | |
| else: | |
| time.sleep(2) | |
| count += 1 | |
| continue | |
| _payload = {"ResetType": "On"} | |
| _headers = {"content-type": "application/json"} | |
| _response = requests.post( | |
| _url, data=json.dumps(_payload), headers=_headers, verify=False, auth=(_username, _password) | |
| ) | |
| statusCode = _response.status_code | |
| if statusCode == 204: | |
| print("- PASS, Command passed to power ON server, code return is %s" % statusCode) | |
| else: | |
| print("\n- FAIL, Command failed to power ON server, status code is: %s\n" % statusCode) | |
| print("Extended Info Message: {0}".format(_response.json())) | |
| sys.exit() | |
| elif data[u"PowerState"] == "Off": | |
| _url = "https://%s/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" % _host | |
| _payload = {"ResetType": "On"} | |
| _headers = {"content-type": "application/json"} | |
| _response = requests.post( | |
| _url, data=json.dumps(_payload), headers=_headers, verify=False, auth=(_username, _password) | |
| ) | |
| statusCode = _response.status_code | |
| if statusCode == 204: | |
| print("- PASS, Command passed to power ON server, code return is %s" % statusCode) | |
| else: | |
| print("\n- FAIL, Command failed to power ON server, status code is: %s\n" % statusCode) | |
| print("Extended Info Message: {0}".format(_response.json())) | |
| sys.exit() | |
| else: | |
| print("- FAIL, unable to get current server power state to perform either reboot or power on") | |
| sys.exit() | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser(description="Client tool for changing boot order via Redfish API.") | |
| parser.add_argument("-H", help="iDRAC host address", required=True) | |
| parser.add_argument("-u", help="iDRAC username", required=True) | |
| parser.add_argument("-p", help="iDRAC password", required=True) | |
| parser.add_argument("-i", help="Path to iDRAC interfaces yaml", required=True) | |
| parser.add_argument("-t", help="Type of host. Accepts: foreman, director", required=True) | |
| args = vars(parser.parse_args()) | |
| host = args["H"] | |
| username = args["u"] | |
| password = args["p"] | |
| host_type = args["t"] | |
| interfaces_path = args["i"] | |
| if host_type.lower() not in ("foreman", "director"): | |
| raise argparse.ArgumentTypeError('Expected values for -t argument are "foreman" or "director"') | |
| change_boot_order(host, username, password) | |
| set_next_boot_pxe(host, username, password) | |
| job_id = create_bios_config_job(host, username, password) | |
| get_job_status(host, username, password, job_id) | |
| reboot_server(host, username, password) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment