mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
firmware update
This commit is contained in:
parent
549a73bd78
commit
0b631e789c
6 changed files with 1010 additions and 0 deletions
6
.github/BOTMETA.yml
vendored
6
.github/BOTMETA.yml
vendored
|
@ -610,6 +610,12 @@ files:
|
|||
maintainers: jameslivulpi
|
||||
$modules/honeybadger_deployment.py:
|
||||
maintainers: stympy
|
||||
$modules/hpc_get_power_state.py:
|
||||
maintainers: srujana
|
||||
$modules/hpc_get_system_fw_inv.py:
|
||||
maintainers: srujana
|
||||
$modules/hpc_update_system_firmware.py:
|
||||
maintainers: srujana
|
||||
$modules/hpilo_:
|
||||
ignore: dagwieers
|
||||
maintainers: haad
|
||||
|
|
444
plugins/module_utils/hpc_system_firmware_utils.py
Normal file
444
plugins/module_utils/hpc_system_firmware_utils.py
Normal file
|
@ -0,0 +1,444 @@
|
|||
# !/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2021-2022 Hewlett Packard Enterprise, Inc. All rights reserved.
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
import json
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.redfish_utils import RedfishUtils
|
||||
from ansible.module_utils.urls import open_url
|
||||
try:
|
||||
from requests_toolbelt import MultipartEncoder
|
||||
HAS_REQUESTS_TOOLBELT = True
|
||||
except ImportError:
|
||||
HAS_REQUESTS_TOOLBELT = False
|
||||
|
||||
|
||||
REQUESTS_TOOLBELT_REQUIRED = "Requests_toolbelt is required for this module."
|
||||
|
||||
|
||||
def has_requests_toolbelt(module):
|
||||
"""
|
||||
Check Request_toolbelt is installed
|
||||
:param module:
|
||||
"""
|
||||
if not HAS_REQUESTS_TOOLBELT:
|
||||
module.fail_json(msg=REQUESTS_TOOLBELT_REQUIRED)
|
||||
|
||||
|
||||
supported_models = ["HPE CRAY XD220V", "HPE CRAY XD225V", "HPE CRAY XD295V", "HPE CRAY XD665", "HPE CRAY XD670"]
|
||||
|
||||
# To get inventory, update
|
||||
supported_targets = {
|
||||
"HPE CRAY XD220V": ["BMC", "BIOS", "MainCPLD", "HDDBPPIC", "PDBPIC"],
|
||||
"HPE CRAY XD225V": ["BMC", "BIOS", "MainCPLD", "HDDBPPIC", "PDBPIC"],
|
||||
"HPE CRAY XD295V": ["BMC", "BIOS", "MainCPLD", "HDDBPPIC", "PDBPIC"],
|
||||
"HPE CRAY XD665": ["BMC", "BIOS", "RT_NVME", "RT_OTHER", "RT_SA", "PDB", "MainCPLD", "UBM6"],
|
||||
"HPE CRAY XD670": ["BMCImage1", "BMCImage2", "BIOS", "BIOS2", "BPB_CPLD1", "BPB_CPLD2", "MB_CPLD1", "SCM_CPLD1"],
|
||||
}
|
||||
|
||||
unsupported_targets = ["BMCImage1", "BPB_CPLD1", "BPB_CPLD2", "MB_CPLD1", "SCM_CPLD1"] # Only of Jakku
|
||||
# BMCImage1 equivalent to BMC
|
||||
# BPB_CPLD1 and BPB_CPLD2 together equivalent to BPB_CPLD
|
||||
# MB_CPLD1 and SCM_CPLD1 together equivalent to MB_CPLD1_SCM_CPLD1
|
||||
all_targets = ['BMC', 'BMCImage1', 'BMCImage2', 'BIOS', 'BIOS2', 'MainCPLD',
|
||||
'MB_CPLD1', 'BPB_CPLD1', 'BPB_CPLD2', 'SCM_CPLD1', 'PDB', 'PDBPIC', 'HDDBPPIC', 'RT_NVME', 'RT_OTHER', 'RT_SA', 'UBM6']
|
||||
|
||||
reboot = {
|
||||
"BIOS": ["AC_PC_redfish"],
|
||||
"BIOS2": ["AC_PC_redfish"],
|
||||
"MainCPLD": ["AC_PC_ipmi"],
|
||||
"PDB": ["AC_PC_ipmi"],
|
||||
"RT_NVME": ["AC_PC_redfish", "AC_PC_ipmi", "AC_PC_redfish"],
|
||||
"RT_SA": ["AC_PC_redfish", "AC_PC_ipmi", "AC_PC_redfish"],
|
||||
"RT_OTHER": ["AC_PC_redfish", "AC_PC_ipmi", "AC_PC_redfish"],
|
||||
"HDDBPPIC": ["AC_PC_redfish"],
|
||||
"PDBPIC": ["AC_PC_redfish"]
|
||||
}
|
||||
|
||||
routing = {
|
||||
"HPE CRAY XD220V": "0x34 0xa2 0x00 0x19 0xA9",
|
||||
"HPE CRAY XD225V": "0x34 0xa2 0x00 0x19 0xA9",
|
||||
"HPE CRAY XD295V": "0x34 0xa2 0x00 0x19 0xA9",
|
||||
"HPE CRAY XD665": "0x34 0xA2 0x00 0x19 0xa9 0x00"
|
||||
}
|
||||
|
||||
|
||||
class CrayRedfishUtils(RedfishUtils):
|
||||
def post_multi_request(self, uri, headers, payload):
|
||||
username, password, basic_auth = self._auth_params(headers)
|
||||
try:
|
||||
resp = open_url(uri, data=payload, headers=headers, method="POST",
|
||||
url_username=username, url_password=password,
|
||||
force_basic_auth=basic_auth, validate_certs=False,
|
||||
follow_redirects='all',
|
||||
use_proxy=True, timeout=self.timeout)
|
||||
resp_headers = dict((k.lower(), v) for (k, v) in resp.info().items())
|
||||
return True
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def get_model(self):
|
||||
response = self.get_request(self.root_uri + "/redfish/v1/Systems/Self")
|
||||
if response['ret'] is False:
|
||||
return "NA"
|
||||
try:
|
||||
if 'Model' in response['data']:
|
||||
model = response['data'][u'Model']
|
||||
if model is not None:
|
||||
model = model[:15]
|
||||
else:
|
||||
model = 'None'
|
||||
else:
|
||||
model = 'None'
|
||||
return model
|
||||
except Exception:
|
||||
if 'Model' in response:
|
||||
model = response[u'Model']
|
||||
if model is not None:
|
||||
model = model[:15]
|
||||
else:
|
||||
model = 'None'
|
||||
else:
|
||||
model = 'None'
|
||||
return model
|
||||
|
||||
def power_state(self):
|
||||
response = self.get_request(self.root_uri + "/redfish/v1/Systems/Self")
|
||||
if response['ret'] is False:
|
||||
return "NA"
|
||||
try:
|
||||
if 'PowerState' in response['data']:
|
||||
state = response['data'][u'PowerState']
|
||||
if state is None:
|
||||
state = 'None'
|
||||
else:
|
||||
state = 'None'
|
||||
return state
|
||||
except Exception:
|
||||
if 'PowerState' in response:
|
||||
state = response[u'PowerState']
|
||||
if state is None:
|
||||
state = 'None'
|
||||
else:
|
||||
state = 'None'
|
||||
return state
|
||||
|
||||
def power_on(self):
|
||||
payload = {"ResetType": "On"}
|
||||
target_uri = "/redfish/v1/Systems/Self/Actions/ComputerSystem.Reset"
|
||||
response1 = self.post_request(self.root_uri + target_uri, payload)
|
||||
time.sleep(120)
|
||||
|
||||
def power_off(self):
|
||||
payload = {"ResetType": "ForceOff"}
|
||||
target_uri = "/redfish/v1/Systems/Self/Actions/ComputerSystem.Reset"
|
||||
response1 = self.post_request(self.root_uri + target_uri, payload)
|
||||
time.sleep(120)
|
||||
|
||||
def get_PS_CrayXD670(self, attr):
|
||||
IP = attr.get('baseuri')
|
||||
option = attr.get('power_state')
|
||||
csv_file_name = attr.get('output_file_name')
|
||||
if not os.path.exists(csv_file_name):
|
||||
f = open(csv_file_name, "w")
|
||||
to_write = "IP_Address, Model, Power_State\n"
|
||||
f.write(to_write)
|
||||
f.close()
|
||||
model = self.get_model()
|
||||
if model.upper() == "HPE CRAY XD670":
|
||||
power_state = self.power_state()
|
||||
if option.upper() == "NA":
|
||||
lis = [IP, model, power_state]
|
||||
elif option.upper() == "ON":
|
||||
if power_state.upper() == "OFF":
|
||||
self.power_on()
|
||||
power_state = self.power_state()
|
||||
lis = [IP, model, power_state]
|
||||
elif option.upper() == "OFF":
|
||||
if power_state.upper() == "ON":
|
||||
self.power_off()
|
||||
power_state = self.power_state()
|
||||
lis = [IP, model, power_state]
|
||||
else:
|
||||
return {'ret': False, 'changed': True, 'msg': 'Must specify the correct required option for power_state'}
|
||||
|
||||
else:
|
||||
lis = [IP, model, "unsupported_model"]
|
||||
new_data = ", ".join(lis)
|
||||
return {'ret': True, 'changed': True, 'msg': str(new_data)}
|
||||
|
||||
def target_supported(self, model, target):
|
||||
if target in supported_targets[model.upper()]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_fw_version(self, target):
|
||||
try:
|
||||
response = self.get_request(self.root_uri + "/redfish/v1/UpdateService/FirmwareInventory" + "/" + target)
|
||||
try:
|
||||
version = response['data']['Version']
|
||||
return version
|
||||
except Exception:
|
||||
version = response['Version']
|
||||
return version
|
||||
except Exception:
|
||||
return "failed_FI_GET_call/no_version_field"
|
||||
|
||||
def AC_PC_redfish(self):
|
||||
payload = {"ResetType": "ForceRestart"}
|
||||
target_uri = "/redfish/v1/Systems/Self/Actions/ComputerSystem.Reset"
|
||||
response1 = self.post_request(self.root_uri + target_uri, payload)
|
||||
time.sleep(180)
|
||||
target_uri = "/redfish/v1/Chassis/Self/Actions/Chassis.Reset"
|
||||
response2 = self.post_request(self.root_uri + target_uri, payload)
|
||||
time.sleep(180)
|
||||
return response1 or response2
|
||||
|
||||
def AC_PC_ipmi(self, IP, username, password, routing_value):
|
||||
try:
|
||||
command = 'ipmitool -I lanplus -H ' + IP + ' -U ' + username + ' -P ' + password + ' raw ' + routing_value
|
||||
subprocess.run(command, shell=True, check=True, timeout=15, capture_output=True)
|
||||
time.sleep(300)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def get_sys_fw_inventory(self, attr):
|
||||
IP = attr.get('baseuri')
|
||||
csv_file_name = attr.get('output_file_name')
|
||||
if not os.path.exists(csv_file_name):
|
||||
f = open(csv_file_name, "w")
|
||||
to_write = """IP_Address, Model, BMC, BMCImage1, BMCImage2, BIOS, BIOS2, MainCPLD, MB_CPLD1,
|
||||
BPB_CPLD1, BPB_CPLD2, SCM_CPLD1, PDB, PDBPIC, HDDBPPIC, RT_NVME, RT_OTHER, RT_SA, UBM6\n"""
|
||||
f.write(to_write)
|
||||
f.close()
|
||||
model = self.get_model()
|
||||
entry = []
|
||||
entry.append(IP)
|
||||
if model.upper() not in supported_models:
|
||||
entry.append("unsupported_model")
|
||||
for target in all_targets:
|
||||
entry.append("NA")
|
||||
else:
|
||||
entry.append(model)
|
||||
for target in all_targets:
|
||||
if target in supported_targets[model.upper()]:
|
||||
version = self.get_fw_version(target)
|
||||
if version.startswith("failed"):
|
||||
version = "NA" # "no_comp/no_version"
|
||||
else:
|
||||
version = "NA"
|
||||
entry.append(version)
|
||||
new_data = ", ".join(entry)
|
||||
return {'ret': True, 'changed': True, 'msg': str(new_data)}
|
||||
|
||||
def helper_update(self, update_status, target, image_path, image_type, IP, username, password, model):
|
||||
before_version = "failed"
|
||||
if target != "BPB_CPLD" and target != "SCM_CPLD1" and target != "MB_CPLD1":
|
||||
before_version = self.get_fw_version(target)
|
||||
after_version = "NA"
|
||||
else:
|
||||
before_version = "NA"
|
||||
if not before_version.startswith("failed"):
|
||||
# proceed for update
|
||||
response = self.get_request(self.root_uri + "/redfish/v1/UpdateService")
|
||||
if response['ret'] is False:
|
||||
update_status = "UpdateService api not found"
|
||||
else:
|
||||
data = response['data']
|
||||
if 'MultipartHttpPushUri' in data:
|
||||
headers = {'Expect': 'Continue', 'Content-Type': 'multipart/form-data'}
|
||||
body = {}
|
||||
if target != "BPB_CPLD":
|
||||
targets_uri = '/redfish/v1/UpdateService/FirmwareInventory/' + target + '/'
|
||||
body['UpdateParameters'] = (None, json.dumps({"Targets": [targets_uri]}), 'application/json')
|
||||
else:
|
||||
body['UpdateParameters'] = (None, json.dumps({"Targets":
|
||||
['/redfish/v1/UpdateService/FirmwareInventory/BPB_CPLD1/',
|
||||
'/redfish/v1/UpdateService/FirmwareInventory/BPB_CPLD2/']}),
|
||||
'application/json')
|
||||
body['OemParameters'] = (None, json.dumps({"ImageType": image_type}), 'application/json')
|
||||
with open(image_path, 'rb') as image_path_rb:
|
||||
body['UpdateFile'] = (image_path, image_path_rb, 'application/octet-stream')
|
||||
encoder = MultipartEncoder(body)
|
||||
body = encoder.to_string()
|
||||
headers['Content-Type'] = encoder.content_type
|
||||
|
||||
response = self.post_multi_request(self.root_uri + data['MultipartHttpPushUri'],
|
||||
headers=headers, payload=body)
|
||||
if response is False:
|
||||
update_status = "failed_POST"
|
||||
else:
|
||||
# Add time.sleep (for system to comeback after flashing)
|
||||
time.sleep(300)
|
||||
# Call reboot logic based on target
|
||||
if target in reboot:
|
||||
what_reboots = reboot[target]
|
||||
for reb in what_reboots:
|
||||
if reb == "AC_PC_redfish":
|
||||
result = self.AC_PC_redfish()
|
||||
if not result:
|
||||
update_status = "reboot_failed"
|
||||
break
|
||||
time.sleep(300)
|
||||
elif reb == "AC_PC_ipmi":
|
||||
# based on the model end routing code changes
|
||||
result = self.AC_PC_ipmi(IP, username, password, routing[model.upper()])
|
||||
if not result:
|
||||
update_status = "reboot_failed"
|
||||
break
|
||||
|
||||
# if target=="MB_CPLD1" or "BPB" in target:
|
||||
# turn node back to on -- call power_on_node function
|
||||
# self.power_on()
|
||||
# not required to power on the node as it useful only after physical POWER CYCLE and we can't keep the track of the
|
||||
# physical power cycle so skipping it
|
||||
if update_status.lower() == "success":
|
||||
# call version of respective target and store versions after update
|
||||
time.sleep(180) # extra time requiring as of now for systems under test
|
||||
if target != "BPB_CPLD" and target != "SCM_CPLD1" and target != "MB_CPLD1":
|
||||
after_version = self.get_fw_version(target)
|
||||
else:
|
||||
if target != "BPB_CPLD" and target != "SCM_CPLD1" and target != "MB_CPLD1":
|
||||
after_version = "NA"
|
||||
update_status = "failed"
|
||||
|
||||
if target != "BPB_CPLD" and target != "SCM_CPLD1" and target != "MB_CPLD1":
|
||||
return before_version, after_version, update_status
|
||||
else:
|
||||
return update_status
|
||||
|
||||
def system_fw_update(self, attr):
|
||||
IP = attr.get('baseuri')
|
||||
username = attr.get('username')
|
||||
password = attr.get('password')
|
||||
image_type = attr.get('update_image_type')
|
||||
update_status = "Success"
|
||||
is_target_supported = False
|
||||
image_path = "NA"
|
||||
target = attr.get('update_target')
|
||||
image_path_inputs = {
|
||||
"HPE CRAY XD220V": attr.get('update_image_path_xd220V'),
|
||||
"HPE CRAY XD225V": attr.get('update_image_path_xd225V'),
|
||||
"HPE CRAY XD295V": attr.get('update_image_path_xd295V'),
|
||||
"HPE CRAY XD665": attr.get('update_image_path_xd665'),
|
||||
"HPE CRAY XD670": attr.get('update_image_path_xd670')}
|
||||
csv_file_name = attr.get('output_file_name')
|
||||
|
||||
# Have a check that atleast one image path set based out of the above new logic
|
||||
if not any(image_path_inputs.values()):
|
||||
return {'ret': False, 'changed': True, 'msg': 'Must specify atleast one update_image_path'}
|
||||
|
||||
if target == "" or target.upper() in unsupported_targets:
|
||||
return {'ret': False, 'changed': True, 'msg': 'Must specify the correct target for firmware update'}
|
||||
|
||||
model = self.get_model()
|
||||
|
||||
if not os.path.exists(csv_file_name):
|
||||
f = open(csv_file_name, "w")
|
||||
if target == "BPB_CPLD" or target == "SCM_CPLD1_MB_CPLD1":
|
||||
to_write = "IP_Address, Model, Update_Status, Remarks\n"
|
||||
else:
|
||||
to_write = "IP_Address, Model, " + target + '_Pre_Ver, ' + target + '_Post_Ver, ' + "Update_Status\n"
|
||||
f.write(to_write)
|
||||
f.close()
|
||||
|
||||
# check if model is Cray XD670 and target is BMC assign default value of BMC as BMCImage1
|
||||
if model.upper() == "HPE CRAY XD670" and target == "BMC":
|
||||
target = "BMCImage1"
|
||||
|
||||
if model.upper() not in supported_models:
|
||||
update_status = "unsupported_model"
|
||||
if target == "SCM_CPLD1_MB_CPLD1" or target == "BPB_CPLD":
|
||||
lis = [IP, model, update_status, "NA"]
|
||||
else:
|
||||
lis = [IP, model, "NA", "NA", update_status]
|
||||
new_data = ", ".join(lis)
|
||||
return {'ret': True, 'changed': True, 'msg': str(new_data)}
|
||||
else:
|
||||
image_path = image_path_inputs[model.upper()]
|
||||
if model.upper() == "HPE CRAY XD670" and "CPLD" in target.upper():
|
||||
power_state = self.power_state()
|
||||
if power_state.lower() != "on":
|
||||
update_status = "NA"
|
||||
lis = [IP, model, update_status, "node is not ON, please power on the node"]
|
||||
new_data = ", ".join(lis)
|
||||
return {'ret': True, 'changed': True, 'msg': str(new_data)}
|
||||
elif target == 'SCM_CPLD1_MB_CPLD1':
|
||||
is_target_supported = True
|
||||
image_paths = image_path_inputs["HPE CRAY XD670"].split()
|
||||
if len(image_paths) != 2:
|
||||
return {'ret': False, 'changed': True, 'msg': '''Must specify exactly 2 image_paths,
|
||||
first for SCM_CPLD1 of Cray XD670 and second for MB_CPLD1 of Cray XD670'''}
|
||||
for img_path in image_paths:
|
||||
if not os.path.isfile(img_path):
|
||||
return {'ret': False, 'changed': True,
|
||||
'msg': '''Must specify correct image_paths for SCM_CPLD1_MB_CPLD1, first for SCM_CPLD1
|
||||
of Cray XD670 and second for MB_CPLD1 of Cray XD670'''}
|
||||
|
||||
if target != "SCM_CPLD1_MB_CPLD1" and not os.path.isfile(image_path):
|
||||
update_status = "NA_fw_file_absent"
|
||||
if target == "BPB_CPLD":
|
||||
lis = [IP, model, update_status, "NA"]
|
||||
else:
|
||||
lis = [IP, model, "NA", "NA", update_status]
|
||||
new_data = ", ".join(lis)
|
||||
return {'ret': True, 'changed': True, 'msg': str(new_data)}
|
||||
else:
|
||||
if target != "SCM_CPLD1_MB_CPLD1" and target != "BPB_CPLD":
|
||||
is_target_supported = self.target_supported(model, target)
|
||||
if model.upper() == "HPE CRAY XD670" and (target == "BMC" or target == "BPB_CPLD"):
|
||||
is_target_supported = True
|
||||
|
||||
if not is_target_supported:
|
||||
update_status = "target_not_supported"
|
||||
if target == "SCM_CPLD1_MB_CPLD1" or target == "BPB_CPLD":
|
||||
lis = [IP, model, update_status, "NA"]
|
||||
else:
|
||||
lis = [IP, model, "NA", "NA", update_status]
|
||||
new_data = ", ".join(lis)
|
||||
return {'ret': True, 'changed': True, 'msg': str(new_data)}
|
||||
else:
|
||||
# check if model is Cray XD670 and target is BMC assign default value of BMC as BMCImage1
|
||||
if model.upper() == "HPE CRAY XD670" and target == "BMC":
|
||||
target = "BMCImage1"
|
||||
|
||||
# call version of respective target and store versions before update
|
||||
if target == "SCM_CPLD1_MB_CPLD1":
|
||||
update_status = self.helper_update(update_status, "SCM_CPLD1", image_paths[0], image_type, IP, username, password, "HPE Cray XD670")
|
||||
if update_status.lower() == "success":
|
||||
# SCM has updates successfully, proceed for MB_CPLD1 update
|
||||
# check node to be off -- call power_off_node function
|
||||
power_state = self.power_state()
|
||||
if power_state.lower() == "on":
|
||||
self.power_off()
|
||||
power_state = self.power_state()
|
||||
if power_state.lower() == "on":
|
||||
lis = [IP, model, "NA", "MB_CPLD1 requires node off, tried powering off the node, but failed to power off"]
|
||||
update_status = self.helper_update(update_status, "MB_CPLD1",
|
||||
image_paths[1], image_type, IP, username, password, "HPE Cray XD670")
|
||||
if update_status.lower() == "success":
|
||||
remarks = "Please plug out and plug in power cables physically"
|
||||
else:
|
||||
remarks = "Please reflash the firmware and DO NOT DO physical power cycle"
|
||||
lis = [IP, model, update_status, remarks]
|
||||
elif target == "BPB_CPLD":
|
||||
update_status = self.helper_update(update_status, target, image_path, image_type, IP, username, password, model)
|
||||
if update_status.lower() == "success":
|
||||
remarks = "Please plug out and plug in power cables physically"
|
||||
else:
|
||||
remarks = "Please reflash the firmware and DO NOT DO physical power cycle"
|
||||
lis = [IP, model, update_status, remarks]
|
||||
else:
|
||||
bef_ver, aft_ver, update_status = self.helper_update(update_status, target, image_path, image_type, IP, username, password, model)
|
||||
lis = [IP, model, bef_ver, aft_ver, update_status]
|
||||
new_data = ", ".join(lis)
|
||||
return {'ret': True, 'changed': True, 'msg': str(new_data)}
|
170
plugins/modules/hpc_get_power_state.py
Normal file
170
plugins/modules/hpc_get_power_state.py
Normal file
|
@ -0,0 +1,170 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2021-2022 Hewlett Packard Enterprise, Inc. All rights reserved.
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: hpc_get_power_state
|
||||
short_description: Inventory Information of CrayXD components using Redfish APIs
|
||||
version_added: 1.1.0
|
||||
description:
|
||||
- using Redfish URI's Fetch the CrayXD components Inventory Information
|
||||
attributes:
|
||||
check_mode:
|
||||
support: none
|
||||
diff_mode:
|
||||
support: none
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
options:
|
||||
category:
|
||||
required: true
|
||||
description:
|
||||
- Category to Get Inventory of the CrayXD components.
|
||||
type: str
|
||||
command:
|
||||
required: true
|
||||
description:
|
||||
- List of commands to execute on the CrayXD.
|
||||
type: list
|
||||
elements: str
|
||||
baseuri:
|
||||
required: true
|
||||
description:
|
||||
- Base URI of OOB controller.
|
||||
type: str
|
||||
username:
|
||||
required: true
|
||||
description:
|
||||
- Username for authenticating to CrayXD.
|
||||
type: str
|
||||
password:
|
||||
required: true
|
||||
description:
|
||||
- Password for authenticating to CrayXD.
|
||||
type: str
|
||||
auth_token:
|
||||
required: false
|
||||
description:
|
||||
- Security token for authenticating to CrayXD.
|
||||
type: str
|
||||
timeout:
|
||||
required: false
|
||||
description:
|
||||
- Timeout in seconds for HTTP requests to CrayXD.
|
||||
default: 300
|
||||
type: int
|
||||
power_state:
|
||||
required: true
|
||||
description:
|
||||
- To get or modify the power state of CrayXD670
|
||||
choices: ['ON', 'OFF', 'NA']
|
||||
type: str
|
||||
output_file_name:
|
||||
required: false
|
||||
description:
|
||||
- To save the output of the Inventory, mention the output file name in csv.
|
||||
default: Power_output.csv
|
||||
type: str
|
||||
|
||||
|
||||
author:
|
||||
- Srujana Yasa (@srujana)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Getting Power State of Cray XD670 Server nodes
|
||||
get_power_state:
|
||||
category: Get_Power_State
|
||||
command: Get_PS
|
||||
baseuri: "baseuri"
|
||||
username: "bmc_username"
|
||||
password: "bmc_password"
|
||||
power_state: "off"
|
||||
output_file_name: "output_file"
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
csv:
|
||||
description: Output of this Task is saved to a csv file.
|
||||
returned: Returned an output file containing the details of update
|
||||
type: str
|
||||
sample: Output_file.csv
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.hpc_system_firmware_utils import CrayRedfishUtils
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
|
||||
category_commands = {
|
||||
"Get_Power_State": ["Get_PS"],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
result = {}
|
||||
return_values = {}
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
category=dict(required=True),
|
||||
command=dict(required=True, type='list', elements='str'),
|
||||
baseuri=dict(required=True),
|
||||
username=dict(required=True),
|
||||
password=dict(required=True, no_log=True),
|
||||
auth_token=dict(no_log=True),
|
||||
timeout=dict(type='int', default=300),
|
||||
power_state=dict(required=True, choices=['ON', 'OFF', 'NA']),
|
||||
output_file_name=dict(type='str', default='Power_output.csv'),
|
||||
),
|
||||
supports_check_mode=False
|
||||
)
|
||||
|
||||
category = module.params['category']
|
||||
command_list = module.params['command']
|
||||
|
||||
# admin credentials used for authentication
|
||||
creds = {'user': module.params['username'],
|
||||
'pswd': module.params['password'],
|
||||
'token': module.params['auth_token']}
|
||||
|
||||
timeout = module.params['timeout']
|
||||
# Build root URI
|
||||
root_uri = "https://" + module.params['baseuri']
|
||||
# update_uri = "/redfish/v1/UpdateService"
|
||||
rf_utils = CrayRedfishUtils(creds, root_uri, timeout, module, data_modification=True)
|
||||
|
||||
# Check that Category is valid
|
||||
if category not in category_commands:
|
||||
module.fail_json(msg=to_native("Invalid Category '%s'. Valid Categories = %s" % (category, list(category_commands.keys()))))
|
||||
|
||||
# Check that all commands are valid
|
||||
for cmd in command_list:
|
||||
# Fail if even one command given is invalid
|
||||
if cmd not in category_commands[category]:
|
||||
module.fail_json(msg=to_native("Invalid Command '%s'. Valid Commands = %s" % (cmd, category_commands[category])))
|
||||
|
||||
if category == "Get_Power_State":
|
||||
for command in command_list:
|
||||
if command == "Get_PS":
|
||||
result = rf_utils.get_PS_CrayXD670({'baseuri': module.params['baseuri'],
|
||||
'username': module.params['username'],
|
||||
'password': module.params['password'],
|
||||
'power_state': module.params['power_state'],
|
||||
'output_file_name': module.params['output_file_name']})
|
||||
if result['ret']:
|
||||
msg = result.get('msg', False)
|
||||
module.exit_json(msg=msg)
|
||||
else:
|
||||
module.fail_json(msg=to_native(result))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
161
plugins/modules/hpc_get_system_fw_inv.py
Normal file
161
plugins/modules/hpc_get_system_fw_inv.py
Normal file
|
@ -0,0 +1,161 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2021-2022 Hewlett Packard Enterprise, Inc. All rights reserved.
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: hpc_get_system_fw_inv
|
||||
short_description: Inventory Information of CrayXD components using Redfish APIs
|
||||
version_added: 1.1.0
|
||||
description:
|
||||
- using Redfish URI's Fetch the CrayXD components Inventory Information
|
||||
attributes:
|
||||
check_mode:
|
||||
support: none
|
||||
diff_mode:
|
||||
support: none
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
options:
|
||||
category:
|
||||
required: true
|
||||
description:
|
||||
- Category to Get Inventory of the CrayXD components.
|
||||
type: str
|
||||
command:
|
||||
required: true
|
||||
description:
|
||||
- List of commands to execute on the CrayXD.
|
||||
type: list
|
||||
elements: str
|
||||
baseuri:
|
||||
required: true
|
||||
description:
|
||||
- Base URI of OOB controller.
|
||||
type: str
|
||||
username:
|
||||
required: true
|
||||
description:
|
||||
- Username for authenticating to CrayXD.
|
||||
type: str
|
||||
password:
|
||||
required: true
|
||||
description:
|
||||
- Password for authenticating to CrayXD.
|
||||
type: str
|
||||
auth_token:
|
||||
required: false
|
||||
description:
|
||||
- Security token for authenticating to CrayXD.
|
||||
type: str
|
||||
timeout:
|
||||
required: false
|
||||
description:
|
||||
- Timeout in seconds for HTTP requests to CrayXD.
|
||||
default: 300
|
||||
type: int
|
||||
output_file_name:
|
||||
required: false
|
||||
description:
|
||||
- To save the output of the Inventory, mention the output file name in csv.
|
||||
default: get_output.csv
|
||||
type: str
|
||||
|
||||
|
||||
author:
|
||||
- Srujana Yasa (@srujana)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Fetching System Firmware Inventory Details
|
||||
hpc_get_system_fw_inv:
|
||||
category: GetInventory
|
||||
command: GetSystemFWInventory
|
||||
baseuri: "baseuri"
|
||||
username: "bmc_username"
|
||||
password: "bmc_password"
|
||||
output_file_name: "output_file.csv"
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
csv:
|
||||
description: Output of this Task is saved to a csv file.
|
||||
returned: Returned an output file containing the details of update.
|
||||
type: str
|
||||
sample: Output_file.csv
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.hpc_system_firmware_utils import CrayRedfishUtils
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
|
||||
category_commands = {
|
||||
"GetInventory": ["GetSystemFWInventory"],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
result = {}
|
||||
return_values = {}
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
category=dict(required=True),
|
||||
command=dict(required=True, type='list', elements='str'),
|
||||
baseuri=dict(required=True),
|
||||
username=dict(required=True),
|
||||
password=dict(no_log=True, required=True),
|
||||
auth_token=dict(no_log=True),
|
||||
timeout=dict(type='int', default=300),
|
||||
output_file_name=dict(type='str', default='get_output.csv'),
|
||||
),
|
||||
supports_check_mode=False
|
||||
)
|
||||
|
||||
category = module.params['category']
|
||||
command_list = module.params['command']
|
||||
|
||||
# admin credentials used for authentication
|
||||
creds = {'user': module.params['username'],
|
||||
'pswd': module.params['password'],
|
||||
'token': module.params['auth_token']}
|
||||
|
||||
timeout = module.params['timeout']
|
||||
# Build root URI
|
||||
root_uri = "https://" + module.params['baseuri']
|
||||
# update_uri = "/redfish/v1/UpdateService"
|
||||
rf_utils = CrayRedfishUtils(creds, root_uri, timeout, module, data_modification=True)
|
||||
|
||||
# Check that Category is valid
|
||||
if category not in category_commands:
|
||||
module.fail_json(msg=to_native("Invalid Category '%s'. Valid Categories = %s" % (category, list(category_commands.keys()))))
|
||||
|
||||
# Check that all commands are valid
|
||||
for cmd in command_list:
|
||||
# Fail if even one command given is invalid
|
||||
if cmd not in category_commands[category]:
|
||||
module.fail_json(msg=to_native("Invalid Command '%s'. Valid Commands = %s" % (cmd, category_commands[category])))
|
||||
|
||||
if category == "GetInventory":
|
||||
for command in command_list:
|
||||
if command == "GetSystemFWInventory":
|
||||
result = rf_utils.get_sys_fw_inventory({'baseuri': module.params['baseuri'],
|
||||
'username': module.params['username'],
|
||||
'password': module.params['password'],
|
||||
'output_file_name': module.params['output_file_name']})
|
||||
if result['ret']:
|
||||
msg = result.get('msg', False)
|
||||
module.exit_json(msg=msg)
|
||||
else:
|
||||
module.fail_json(msg=to_native(result))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
226
plugins/modules/hpc_update_system_firmware.py
Normal file
226
plugins/modules/hpc_update_system_firmware.py
Normal file
|
@ -0,0 +1,226 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2021-2022 Hewlett Packard Enterprise, Inc. All rights reserved.
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: hpc_update_system_firmware
|
||||
short_description: Updates CrayXD components using Redfish APIs
|
||||
version_added: 1.1.0
|
||||
description:
|
||||
- using Redfish URI's updates the CrayXD components from the local HPM file
|
||||
attributes:
|
||||
check_mode:
|
||||
support: none
|
||||
diff_mode:
|
||||
support: none
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
options:
|
||||
category:
|
||||
required: true
|
||||
description:
|
||||
- Category to Update the components of CrayXD.
|
||||
type: str
|
||||
command:
|
||||
required: true
|
||||
description:
|
||||
- List of commands to execute on the CrayXD.
|
||||
type: list
|
||||
elements: str
|
||||
baseuri:
|
||||
required: true
|
||||
description:
|
||||
- Base URI of OOB controller.
|
||||
type: str
|
||||
username:
|
||||
required: true
|
||||
description:
|
||||
- Username for authenticating to CrayXD.
|
||||
type: str
|
||||
password:
|
||||
required: true
|
||||
description:
|
||||
- Password for authenticating to CrayXD.
|
||||
type: str
|
||||
auth_token:
|
||||
required: false
|
||||
description:
|
||||
- Security token for authenticating to CrayXD.
|
||||
type: str
|
||||
timeout:
|
||||
required: false
|
||||
description:
|
||||
- Timeout in seconds for HTTP requests to CrayXD.
|
||||
default: 300
|
||||
type: int
|
||||
output_file_name:
|
||||
required: false
|
||||
description:
|
||||
- To save the output of the update mention the output file name in csv.
|
||||
type: str
|
||||
default: update_output.csv
|
||||
update_target:
|
||||
required: true
|
||||
description:
|
||||
- To build the Redfish URI and to update that component it is required.
|
||||
type: str
|
||||
choices: [BMC , BIOS , BIOS2 , MainCPLD , HDDBPPIC , PDBPIC , RT_NVME , RT_OTHER , RT_SA , PDB , UBM6 , SCM_CPLD1_MB_CPLD1 , BPB_CPLD]
|
||||
update_image_path_xd295V:
|
||||
required: false
|
||||
description:
|
||||
- To get the path of local HPM file the Image path needs to be mentioned
|
||||
type: str
|
||||
default: NA
|
||||
update_image_path_xd225V:
|
||||
required: false
|
||||
description:
|
||||
- To get the path of local HPM file the Image path needs to be mentioned
|
||||
type: str
|
||||
default: NA
|
||||
update_image_path_xd220V:
|
||||
required: false
|
||||
description:
|
||||
- To get the path of local HPM file the Image path needs to be mentioned
|
||||
type: str
|
||||
default: NA
|
||||
update_image_path_xd665:
|
||||
required: false
|
||||
description:
|
||||
- To get the path of local HPM file the Image path needs to be mentioned
|
||||
type: str
|
||||
default: NA
|
||||
update_image_path_xd670:
|
||||
required: false
|
||||
description:
|
||||
- To get the path of local HPM file the Image path needs to be mentioned
|
||||
type: str
|
||||
default: NA
|
||||
update_image_type:
|
||||
required: false
|
||||
description:
|
||||
- Type of the file that is being uploaded for the update
|
||||
default: HPM
|
||||
type: str
|
||||
|
||||
author:
|
||||
- Srujana Yasa (@srujana)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Running Firmware Update for Cray XD Servers
|
||||
hpc_update_system_firmware:
|
||||
category: Update
|
||||
command: SystemFirmwareUpdate
|
||||
baseuri: "baseuri"
|
||||
username: "bmc_username"
|
||||
password: "bmc_password"
|
||||
output_file_name: "output_file_name"
|
||||
update_target: "target"
|
||||
update_image_path_xd295V: "path"
|
||||
update_image_path_xd225V: "path"
|
||||
update_image_path_xd220V: "path"
|
||||
update_image_path_xd665: "path"
|
||||
update_image_path_xd670: "path"
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
csv:
|
||||
description: Output of this Task is saved to a csv file.
|
||||
returned: Returned an output file containing the details of update.
|
||||
type: str
|
||||
sample: Output_file.csv
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.hpc_system_firmware_utils import CrayRedfishUtils
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
|
||||
category_commands = {
|
||||
"Update": ["SystemFirmwareUpdate"],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
result = {}
|
||||
return_values = {}
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
category=dict(required=True),
|
||||
command=dict(required=True, type='list', elements='str'),
|
||||
baseuri=dict(required=True),
|
||||
username=dict(required=True),
|
||||
password=dict(no_log=True, required=True),
|
||||
auth_token=dict(no_log=True),
|
||||
timeout=dict(type='int', default=300),
|
||||
update_image_type=dict(type='str', default='HPM'),
|
||||
update_target=dict(required=True, type='str', choices=['BMC', 'BIOS', 'BIOS2', 'MainCPLD',
|
||||
'HDDBPPIC', 'PDBPIC', 'RT_NVME',
|
||||
'RT_OTHER', 'RT_SA', 'PDB', 'UBM6',
|
||||
'SCM_CPLD1_MB_CPLD1', 'BPB_CPLD']),
|
||||
update_image_path_xd220V=dict(type='str', default='NA'),
|
||||
update_image_path_xd225V=dict(type='str', default='NA'),
|
||||
update_image_path_xd295V=dict(type='str', default='NA'),
|
||||
update_image_path_xd665=dict(type='str', default='NA'),
|
||||
update_image_path_xd670=dict(type='str', default='NA'),
|
||||
output_file_name=dict(type='str', default='update_output.csv')
|
||||
),
|
||||
supports_check_mode=False
|
||||
)
|
||||
|
||||
category = module.params['category']
|
||||
command_list = module.params['command']
|
||||
|
||||
# admin credentials used for authentication
|
||||
creds = {'user': module.params['username'],
|
||||
'pswd': module.params['password'],
|
||||
'token': module.params['auth_token']}
|
||||
|
||||
timeout = module.params['timeout']
|
||||
# Build root URI
|
||||
root_uri = "https://" + module.params['baseuri']
|
||||
update_uri = "/redfish/v1/UpdateService"
|
||||
rf_utils = CrayRedfishUtils(creds, root_uri, timeout, module, data_modification=True)
|
||||
|
||||
# Check that Category is valid
|
||||
if category not in category_commands:
|
||||
module.fail_json(msg=to_native("Invalid Category '%s'. Valid Categories = %s" % (category, list(category_commands.keys()))))
|
||||
|
||||
# Check that all commands are valid
|
||||
for cmd in command_list:
|
||||
# Fail if even one command given is invalid
|
||||
if cmd not in category_commands[category]:
|
||||
module.fail_json(msg=to_native("Invalid Command '%s'. Valid Commands = %s" % (cmd, category_commands[category])))
|
||||
|
||||
if category == "Update":
|
||||
for command in command_list:
|
||||
if command == "SystemFirmwareUpdate":
|
||||
result = rf_utils.system_fw_update({'baseuri': module.params['baseuri'],
|
||||
'username': module.params['username'],
|
||||
'password': module.params['password'],
|
||||
'update_image_type': module.params['update_image_type'],
|
||||
'update_target': module.params['update_target'],
|
||||
'power_state': module.params['power_state'],
|
||||
'update_image_path_xd220V': module.params['update_image_path_xd220V'],
|
||||
'update_image_path_xd225V': module.params['update_image_path_xd225V'],
|
||||
'update_image_path_xd295V': module.params['update_image_path_xd295V'],
|
||||
'update_image_path_xd665': module.params['update_image_path_xd665'],
|
||||
'update_image_path_xd670': module.params['update_image_path_xd670'],
|
||||
'output_file_name': module.params['output_file_name']})
|
||||
if result['ret']:
|
||||
msg = result.get('msg', False)
|
||||
module.exit_json(msg=msg)
|
||||
else:
|
||||
module.fail_json(msg=to_native(result))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -54,3 +54,6 @@ proxmoxer ; python_version > '3.6'
|
|||
#requirements for nomad_token modules
|
||||
python-nomad < 2.0.0 ; python_version <= '3.6'
|
||||
python-nomad >= 2.0.0 ; python_version >= '3.7'
|
||||
|
||||
#requirements for hpc modules
|
||||
requests-toolbelt
|
||||
|
|
Loading…
Reference in a new issue