Add GetHealthReport commands (#57494)

* add GetHealthReport commands

* add NetworkInterfaces and SimpleStorage

* convert report uri property keys from plural to singular

* one more plural to sungular uri conversion

* change module name to redfish_info in EXAMPLES docstring
This commit is contained in:
Bill Dodd 2019-11-05 05:46:58 -06:00 committed by John R Barker
parent 8f853fef11
commit 48e132b9c3
2 changed files with 198 additions and 39 deletions

View file

@ -269,7 +269,6 @@ class RedfishUtils(object):
return {'ret': True}
def _find_chassis_resource(self):
chassis_service = []
response = self.get_request(self.root_uri + self.service_root)
if response['ret'] is False:
return response
@ -280,21 +279,21 @@ class RedfishUtils(object):
response = self.get_request(self.root_uri + chassis)
if response['ret'] is False:
return response
self.chassis_uri_list = [
self.chassis_uris = [
i['@odata.id'] for i in response['data'].get('Members', [])]
if not self.chassis_uri_list:
if not self.chassis_uris:
return {'ret': False,
'msg': "Chassis Members array is either empty or missing"}
self.chassis_uri = self.chassis_uri_list[0]
self.chassis_uri = self.chassis_uris[0]
if self.data_modification:
if self.resource_id:
self.chassis_uri = self._get_resource_uri_by_id(self.chassis_uri_list,
self.chassis_uri = self._get_resource_uri_by_id(self.chassis_uris,
self.resource_id)
if not self.chassis_uri:
return {
'ret': False,
'msg': "Chassis resource %s not found" % self.resource_id}
elif len(self.chassis_uri_list) > 1:
elif len(self.chassis_uris) > 1:
self.module.deprecate(DEPRECATE_MSG % {'resource': 'Chassis'},
version='2.13')
return {'ret': True}
@ -310,21 +309,21 @@ class RedfishUtils(object):
response = self.get_request(self.root_uri + manager)
if response['ret'] is False:
return response
self.manager_uri_list = [
self.manager_uris = [
i['@odata.id'] for i in response['data'].get('Members', [])]
if not self.manager_uri_list:
if not self.manager_uris:
return {'ret': False,
'msg': "Managers Members array is either empty or missing"}
self.manager_uri = self.manager_uri_list[0]
self.manager_uri = self.manager_uris[0]
if self.data_modification:
if self.resource_id:
self.manager_uri = self._get_resource_uri_by_id(self.manager_uri_list,
self.manager_uri = self._get_resource_uri_by_id(self.manager_uris,
self.resource_id)
if not self.manager_uri:
return {
'ret': False,
'msg': "Manager resource %s not found" % self.resource_id}
elif len(self.manager_uri_list) > 1:
elif len(self.manager_uris) > 1:
self.module.deprecate(DEPRECATE_MSG % {'resource': 'Manager'},
version='2.13')
return {'ret': True}
@ -411,17 +410,26 @@ class RedfishUtils(object):
return response
return {'ret': True}
def aggregate(self, func):
def aggregate(self, func, uri_list, uri_name):
ret = True
entries = []
for systems_uri in self.systems_uris:
inventory = func(systems_uri)
for uri in uri_list:
inventory = func(uri)
ret = inventory.pop('ret') and ret
if 'entries' in inventory:
entries.append(({'systems_uri': systems_uri},
entries.append(({uri_name: uri},
inventory['entries']))
return dict(ret=ret, entries=entries)
def aggregate_chassis(self, func):
return self.aggregate(func, self.chassis_uris, 'chassis_uri')
def aggregate_managers(self, func):
return self.aggregate(func, self.manager_uris, 'manager_uri')
def aggregate_systems(self, func):
return self.aggregate(func, self.systems_uris, 'system_uri')
def get_storage_controller_inventory(self, systems_uri):
result = {}
controller_list = []
@ -471,7 +479,7 @@ class RedfishUtils(object):
return {'ret': False, 'msg': "Storage resource not found"}
def get_multi_storage_controller_inventory(self):
return self.aggregate(self.get_storage_controller_inventory)
return self.aggregate_systems(self.get_storage_controller_inventory)
def get_disk_inventory(self, systems_uri):
result = {'entries': []}
@ -575,7 +583,7 @@ class RedfishUtils(object):
return result
def get_multi_disk_inventory(self):
return self.aggregate(self.get_disk_inventory)
return self.aggregate_systems(self.get_disk_inventory)
def get_volume_inventory(self, systems_uri):
result = {'entries': []}
@ -667,7 +675,7 @@ class RedfishUtils(object):
return result
def get_multi_volume_inventory(self):
return self.aggregate(self.get_volume_inventory)
return self.aggregate_systems(self.get_volume_inventory)
def restart_manager_gracefully(self):
result = {}
@ -694,7 +702,7 @@ class RedfishUtils(object):
payloads = {'IndicatorLedOn': 'Lit', 'IndicatorLedOff': 'Off', "IndicatorLedBlink": 'Blinking'}
result = {}
for chassis_uri in self.chassis_uri_list:
for chassis_uri in self.chassis_uris:
response = self.get_request(self.root_uri + chassis_uri)
if response['ret'] is False:
return response
@ -1258,7 +1266,7 @@ class RedfishUtils(object):
return result
def get_multi_bios_attributes(self):
return self.aggregate(self.get_bios_attributes)
return self.aggregate_systems(self.get_bios_attributes)
def _get_boot_options_dict(self, boot):
# Get these entries from BootOption, if present
@ -1332,7 +1340,7 @@ class RedfishUtils(object):
return result
def get_multi_boot_order(self):
return self.aggregate(self.get_boot_order)
return self.aggregate_systems(self.get_boot_order)
def get_boot_override(self, systems_uri):
result = {}
@ -1365,7 +1373,7 @@ class RedfishUtils(object):
return result
def get_multi_boot_override(self):
return self.aggregate(self.get_boot_override)
return self.aggregate_systems(self.get_boot_override)
def set_bios_default_settings(self):
result = {}
@ -1598,7 +1606,7 @@ class RedfishUtils(object):
'Manufacturer', 'IndicatorLED', 'SerialNumber', 'Model']
# Go through list
for chassis_uri in self.chassis_uri_list:
for chassis_uri in self.chassis_uris:
response = self.get_request(self.root_uri + chassis_uri)
if response['ret'] is False:
return response
@ -1621,7 +1629,7 @@ class RedfishUtils(object):
properties = ['FanName', 'Reading', 'ReadingUnits', 'Status']
# Go through list
for chassis_uri in self.chassis_uri_list:
for chassis_uri in self.chassis_uris:
response = self.get_request(self.root_uri + chassis_uri)
if response['ret'] is False:
return response
@ -1657,7 +1665,7 @@ class RedfishUtils(object):
chassis_power_results = []
# Go through list
for chassis_uri in self.chassis_uri_list:
for chassis_uri in self.chassis_uris:
chassis_power_result = {}
response = self.get_request(self.root_uri + chassis_uri)
if response['ret'] is False:
@ -1696,7 +1704,7 @@ class RedfishUtils(object):
'SensorNumber']
# Go through list
for chassis_uri in self.chassis_uri_list:
for chassis_uri in self.chassis_uris:
response = self.get_request(self.root_uri + chassis_uri)
if response['ret'] is False:
return response
@ -1772,7 +1780,7 @@ class RedfishUtils(object):
return result
def get_multi_cpu_inventory(self):
return self.aggregate(self.get_cpu_inventory)
return self.aggregate_systems(self.get_cpu_inventory)
def get_memory_inventory(self, systems_uri):
result = {}
@ -1829,7 +1837,7 @@ class RedfishUtils(object):
return result
def get_multi_memory_inventory(self):
return self.aggregate(self.get_memory_inventory)
return self.aggregate_systems(self.get_memory_inventory)
def get_nic_inventory(self, resource_uri):
result = {}
@ -1886,8 +1894,7 @@ class RedfishUtils(object):
if resource_type == 'Systems':
resource_uris = self.systems_uris
elif resource_type == 'Manager':
# put in a list to match what we're doing with systems_uris
resource_uris = [self.manager_uri]
resource_uris = self.manager_uris
for resource_uri in resource_uris:
inventory = self.get_nic_inventory(resource_uri)
@ -1948,9 +1955,7 @@ class RedfishUtils(object):
ret = True
entries = []
# Because _find_managers_resource() only find last Manager uri in self.manager_uri, not one list. This should be 1 issue.
# I have to put manager_uri into list to reduce future changes when the issue is fixed.
resource_uris = [self.manager_uri]
resource_uris = self.manager_uris
for resource_uri in resource_uris:
virtualmedia = self.get_virtualmedia(resource_uri)
@ -1972,7 +1977,7 @@ class RedfishUtils(object):
# Get a list of all Chassis and build URIs, then get all PowerSupplies
# from each Power entry in the Chassis
chassis_uri_list = self.chassis_uri_list
chassis_uri_list = self.chassis_uris
for chassis_uri in chassis_uri_list:
response = self.get_request(self.root_uri + chassis_uri)
if response['ret'] is False:
@ -2014,7 +2019,7 @@ class RedfishUtils(object):
return result
def get_multi_psu_inventory(self):
return self.aggregate(self.get_psu_inventory)
return self.aggregate_systems(self.get_psu_inventory)
def get_system_inventory(self, systems_uri):
result = {}
@ -2039,7 +2044,7 @@ class RedfishUtils(object):
return result
def get_multi_system_inventory(self):
return self.aggregate(self.get_system_inventory)
return self.aggregate_systems(self.get_system_inventory)
def get_network_protocols(self):
result = {}
@ -2136,3 +2141,125 @@ class RedfishUtils(object):
if response['ret'] is False:
return response
return {'ret': True, 'changed': True, 'msg': "Modified Manager NetworkProtocol services"}
@staticmethod
def to_singular(resource_name):
if resource_name.endswith('ies'):
resource_name = resource_name[:-3] + 'y'
elif resource_name.endswith('s'):
resource_name = resource_name[:-1]
return resource_name
def get_health_resource(self, subsystem, uri, health, expanded):
status = 'Status'
if expanded:
d = expanded
else:
r = self.get_request(self.root_uri + uri)
if r.get('ret'):
d = r.get('data')
else:
return
if 'Members' in d: # collections case
for m in d.get('Members'):
u = m.get('@odata.id')
r = self.get_request(self.root_uri + u)
if r.get('ret'):
p = r.get('data')
if p:
e = {self.to_singular(subsystem.lower()) + '_uri': u,
status: p.get(status,
"Status not available")}
health[subsystem].append(e)
else: # non-collections case
e = {self.to_singular(subsystem.lower()) + '_uri': uri,
status: d.get(status,
"Status not available")}
health[subsystem].append(e)
def get_health_subsystem(self, subsystem, data, health):
if subsystem in data:
sub = data.get(subsystem)
if isinstance(sub, list):
for r in sub:
if '@odata.id' in r:
uri = r.get('@odata.id')
expanded = None
if '#' in uri and len(r) > 1:
expanded = r
self.get_health_resource(subsystem, uri, health, expanded)
elif isinstance(sub, dict):
if '@odata.id' in sub:
uri = sub.get('@odata.id')
self.get_health_resource(subsystem, uri, health, None)
elif 'Members' in data:
for m in data.get('Members'):
u = m.get('@odata.id')
r = self.get_request(self.root_uri + u)
if r.get('ret'):
d = r.get('data')
self.get_health_subsystem(subsystem, d, health)
def get_health_report(self, category, uri, subsystems):
result = {}
health = {}
status = 'Status'
# Get health status of top level resource
response = self.get_request(self.root_uri + uri)
if response['ret'] is False:
return response
result['ret'] = True
data = response['data']
health[category] = {status: data.get(status, "Status not available")}
# Get health status of subsystems
for sub in subsystems:
d = None
if sub.startswith('Links.'): # ex: Links.PCIeDevices
sub = sub[len('Links.'):]
d = data.get('Links', {})
elif '.' in sub: # ex: Thermal.Fans
p, sub = sub.split('.')
u = data.get(p, {}).get('@odata.id')
if u:
r = self.get_request(self.root_uri + u)
if r['ret']:
d = r['data']
if not d:
continue
else: # ex: Memory
d = data
health[sub] = []
self.get_health_subsystem(sub, d, health)
if not health[sub]:
del health[sub]
result["entries"] = health
return result
def get_system_health_report(self, systems_uri):
subsystems = ['Processors', 'Memory', 'SimpleStorage', 'Storage',
'EthernetInterfaces', 'NetworkInterfaces.NetworkPorts',
'NetworkInterfaces.NetworkDeviceFunctions']
return self.get_health_report('System', systems_uri, subsystems)
def get_multi_system_health_report(self):
return self.aggregate_systems(self.get_system_health_report)
def get_chassis_health_report(self, chassis_uri):
subsystems = ['Power.PowerSupplies', 'Thermal.Fans',
'Links.PCIeDevices']
return self.get_health_report('Chassis', chassis_uri, subsystems)
def get_multi_chassis_health_report(self):
return self.aggregate_chassis(self.get_chassis_health_report)
def get_manager_health_report(self, manager_uri):
subsystems = []
return self.get_health_report('Manager', manager_uri, subsystems)
def get_multi_manager_health_report(self):
return self.aggregate_managers(self.get_manager_health_report)

View file

@ -237,6 +237,30 @@ EXAMPLES = '''
baseuri: "{{ baseuri }}"
username: "{{ username }}"
password: "{{ password }}"
- name: Get system health report
redfish_info:
category: Systems
command: GetHealthReport
baseuri: "{{ baseuri }}"
username: "{{ username }}"
password: "{{ password }}"
- name: Get chassis health report
redfish_info:
category: Chassis
command: GetHealthReport
baseuri: "{{ baseuri }}"
username: "{{ username }}"
password: "{{ password }}"
- name: Get manager health report
redfish_info:
category: Manager
command: GetHealthReport
baseuri: "{{ baseuri }}"
username: "{{ username }}"
password: "{{ password }}"
'''
RETURN = '''
@ -252,14 +276,16 @@ from ansible.module_utils.redfish_utils import RedfishUtils
CATEGORY_COMMANDS_ALL = {
"Systems": ["GetSystemInventory", "GetPsuInventory", "GetCpuInventory",
"GetMemoryInventory", "GetNicInventory",
"GetMemoryInventory", "GetNicInventory", "GetHealthReport",
"GetStorageControllerInventory", "GetDiskInventory", "GetVolumeInventory",
"GetBiosAttributes", "GetBootOrder", "GetBootOverride"],
"Chassis": ["GetFanInventory", "GetPsuInventory", "GetChassisPower", "GetChassisThermals", "GetChassisInventory"],
"Chassis": ["GetFanInventory", "GetPsuInventory", "GetChassisPower",
"GetChassisThermals", "GetChassisInventory", "GetHealthReport"],
"Accounts": ["ListUsers"],
"Sessions": ["GetSessions"],
"Update": ["GetFirmwareInventory", "GetFirmwareUpdateCapabilities", "GetSoftwareInventory"],
"Manager": ["GetManagerNicInventory", "GetVirtualMedia", "GetLogs", "GetNetworkProtocols"],
"Manager": ["GetManagerNicInventory", "GetVirtualMedia", "GetLogs", "GetNetworkProtocols",
"GetHealthReport"],
}
CATEGORY_COMMANDS_DEFAULT = {
@ -360,6 +386,8 @@ def main():
result["boot_order"] = rf_utils.get_multi_boot_order()
elif command == "GetBootOverride":
result["boot_override"] = rf_utils.get_multi_boot_override()
elif command == "GetHealthReport":
result["health_report"] = rf_utils.get_multi_system_health_report()
elif category == "Chassis":
# execute only if we find Chassis resource
@ -378,6 +406,8 @@ def main():
result["chassis_power"] = rf_utils.get_chassis_power()
elif command == "GetChassisInventory":
result["chassis"] = rf_utils.get_chassis_inventory()
elif command == "GetHealthReport":
result["health_report"] = rf_utils.get_multi_chassis_health_report()
elif category == "Accounts":
# execute only if we find an Account service resource
@ -428,6 +458,8 @@ def main():
result["log"] = rf_utils.get_logs()
elif command == "GetNetworkProtocols":
result["network_protocols"] = rf_utils.get_network_protocols()
elif command == "GetHealthReport":
result["health_report"] = rf_utils.get_multi_manager_health_report()
# Return data back
if is_old_facts: