#!/usr/bin/env python # Script to parse StorCLI's JSON output and expose # MegaRAID health as Prometheus metrics. # # Tested against StorCLI 'Ver 1.14.12 Nov 25, 2014'. # # StorCLI reference manual: # http://docs.avagotech.com/docs/12352476 # # Advanced Software Options (ASO) not exposed as metrics currently. # # JSON key abbreviations used by StorCLI are documented in the standard command # output, i.e. when you omit the trailing 'J' from the command. import argparse import json import subprocess DESCRIPTION = """Parses StorCLI's JSON output and exposes MegaRAID health as Prometheus metrics.""" VERSION = '0.0.1' METRIC_PREFIX = 'megaraid_' METRIC_CONTROLLER_LABELS = '{{controller="{}", model="{}"}}' def main(args): data = json.loads(get_storcli_json(args.storcli_path)) # It appears that the data we need will always be present in the first # item in the Controllers array status = data['Controllers'][0] metrics = { 'status_code': status['Command Status']['Status Code'], 'controllers': status['Response Data']['Number of Controllers'], } for name, value in metrics.iteritems(): print("{}{} {}".format(METRIC_PREFIX, name, value)) controller_info = [] controller_metrics = {} overview = [] try: overview = status['Response Data']['System Overview'] except KeyError: pass for controller in overview: controller_index = controller['Ctl'] model = controller['Model'] controller_info.append(METRIC_CONTROLLER_LABELS.format(controller_index, model)) controller_metrics = { # FIXME: Parse dimmer switch options # 'dimmer_switch': controller['DS'], 'battery_backup_healthy': int(controller['BBU'] == 'Opt'), 'degraded': int(controller['Hlth'] == 'Dgd'), 'drive_groups': controller['DGs'], 'emergency_hot_spare': int(controller['EHS'] == 'Y'), 'failed': int(controller['Hlth'] == 'Fld'), 'healthy': int(controller['Hlth'] == 'Opt'), 'physical_drives': controller['PDs'], 'ports': controller['Ports'], 'scheduled_patrol_read': int(controller['sPR'] == 'On'), 'virtual_drives': controller['VDs'], # Reverse StorCLI's logic to make metrics consistent 'drive_groups_optimal': int(controller['DNOpt'] == 0), 'virtual_drives_optimal': int(controller['VNOpt'] == 0), } for name, value in controller_metrics.iteritems(): print('{}{}{{controller="{}"}} {}'.format(METRIC_PREFIX, name, controller_index, value)) for labels in controller_info: print('{}{}{} {}'.format(METRIC_PREFIX, 'controller_info', labels, 1)) def get_storcli_json(storcli_path): storcli_cmd = [storcli_path, 'show', 'all', 'J'] proc = subprocess.Popen(storcli_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return proc.communicate()[0] if __name__ == "__main__": parser = argparse.ArgumentParser(description=DESCRIPTION, formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('--storcli_path', default='/opt/MegaRAID/storcli/storcli64', help='path to StorCLi binary') parser.add_argument('--version', action='version', version='%(prog)s {}'.format(VERSION)) args = parser.parse_args() main(args)