diff options
author | Matt Bostock <matt@mattbostock.com> | 2016-10-04 09:38:00 +0100 |
---|---|---|
committer | Matt Bostock <matt@mattbostock.com> | 2016-12-22 22:55:58 +0000 |
commit | 2c025710400b7d15b2b5dd4d7845423ec0caf823 (patch) | |
tree | c3cea797b4ac12016ecc435892a4dfc87b2c39ab /text_collector_examples | |
parent | 296d7fdd2e3bc48af436e733f029eaece8947d9a (diff) | |
download | prometheus_node_collector-2c025710400b7d15b2b5dd4d7845423ec0caf823.tar.bz2 prometheus_node_collector-2c025710400b7d15b2b5dd4d7845423ec0caf823.tar.xz prometheus_node_collector-2c025710400b7d15b2b5dd4d7845423ec0caf823.zip |
Add StorCli text collector example script
Collect metrics from the StorCLI utility on the health of MegaRAID
hardware RAID controllers and write them to stdout so that they can be
used by the textfile collector.
We parse the JSON output that StorCLI provides.
Script must be run as root or with appropriate capabilities for storcli
to access the RAID card.
Designed to run under Python 2.7, using the system Python provided with
many Linux distributions.
The metrics look like this:
mbostock@host:~$ sudo ./storcli.py
megaraid_status_code 0
megaraid_controllers_count 1
megaraid_emergency_hot_spare{controller="0"} 1
megaraid_scheduled_patrol_read{controller="0"} 1
megaraid_virtual_drives{controller="0"} 1
megaraid_drive_groups{controller="0"} 1
megaraid_virtual_drives_optimal{controller="0"} 1
megaraid_degraded{controller="0"} 0
megaraid_battery_backup_healthy{controller="0"} 1
megaraid_ports{controller="0"} 8
megaraid_failed{controller="0"} 0
megaraid_drive_groups_optimal{controller="0"} 1
megaraid_healthy{controller="0"} 1
megaraid_physical_drives{controller="0"} 24
megaraid_controller_info{controller="0", model="AVAGOMegaRAIDSASPCIExpressROMB"} 1
mbostock@host:~$
Diffstat (limited to 'text_collector_examples')
-rwxr-xr-x | text_collector_examples/storcli.py | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/text_collector_examples/storcli.py b/text_collector_examples/storcli.py new file mode 100755 index 0000000..f1a8a60 --- /dev/null +++ b/text_collector_examples/storcli.py | |||
@@ -0,0 +1,101 @@ | |||
1 | #!/usr/bin/env python | ||
2 | |||
3 | # Script to parse StorCLI's JSON output and expose | ||
4 | # MegaRAID health as Prometheus metrics. | ||
5 | # | ||
6 | # Tested against StorCLI 'Ver 1.14.12 Nov 25, 2014'. | ||
7 | # | ||
8 | # StorCLI reference manual: | ||
9 | # http://docs.avagotech.com/docs/12352476 | ||
10 | # | ||
11 | # Advanced Software Options (ASO) not exposed as metrics currently. | ||
12 | # | ||
13 | # JSON key abbreviations used by StorCLI are documented in the standard command | ||
14 | # output, i.e. when you omit the trailing 'J' from the command. | ||
15 | |||
16 | import argparse | ||
17 | import json | ||
18 | import subprocess | ||
19 | |||
20 | DESCRIPTION = """Parses StorCLI's JSON output and exposes MegaRAID health as | ||
21 | Prometheus metrics.""" | ||
22 | VERSION = '0.0.1' | ||
23 | |||
24 | METRIC_PREFIX = 'megaraid_' | ||
25 | METRIC_CONTROLLER_LABELS = '{{controller="{}", model="{}"}}' | ||
26 | |||
27 | |||
28 | def main(args): | ||
29 | data = json.loads(get_storcli_json(args.storcli_path)) | ||
30 | |||
31 | # It appears that the data we need will always be present in the first | ||
32 | # item in the Controllers array | ||
33 | status = data['Controllers'][0] | ||
34 | |||
35 | metrics = { | ||
36 | 'status_code': status['Command Status']['Status Code'], | ||
37 | 'controllers': status['Response Data']['Number of Controllers'], | ||
38 | } | ||
39 | |||
40 | for name, value in metrics.iteritems(): | ||
41 | print("{}{} {}".format(METRIC_PREFIX, name, value)) | ||
42 | |||
43 | controller_info = [] | ||
44 | controller_metrics = {} | ||
45 | overview = [] | ||
46 | |||
47 | try: | ||
48 | overview = status['Response Data']['System Overview'] | ||
49 | except KeyError: | ||
50 | pass | ||
51 | |||
52 | for controller in overview: | ||
53 | controller_index = controller['Ctl'] | ||
54 | model = controller['Model'] | ||
55 | controller_info.append(METRIC_CONTROLLER_LABELS.format(controller_index, model)) | ||
56 | |||
57 | controller_metrics = { | ||
58 | # FIXME: Parse dimmer switch options | ||
59 | # 'dimmer_switch': controller['DS'], | ||
60 | |||
61 | 'battery_backup_healthy': int(controller['BBU'] == 'Opt'), | ||
62 | 'degraded': int(controller['Hlth'] == 'Dgd'), | ||
63 | 'drive_groups': controller['DGs'], | ||
64 | 'emergency_hot_spare': int(controller['EHS'] == 'Y'), | ||
65 | 'failed': int(controller['Hlth'] == 'Fld'), | ||
66 | 'healthy': int(controller['Hlth'] == 'Opt'), | ||
67 | 'physical_drives': controller['PDs'], | ||
68 | 'ports': controller['Ports'], | ||
69 | 'scheduled_patrol_read': int(controller['sPR'] == 'On'), | ||
70 | 'virtual_drives': controller['VDs'], | ||
71 | |||
72 | # Reverse StorCLI's logic to make metrics consistent | ||
73 | 'drive_groups_optimal': int(controller['DNOpt'] == 0), | ||
74 | 'virtual_drives_optimal': int(controller['VNOpt'] == 0), | ||
75 | } | ||
76 | |||
77 | for name, value in controller_metrics.iteritems(): | ||
78 | print('{}{}{{controller="{}"}} {}'.format(METRIC_PREFIX, name, controller_index, value)) | ||
79 | |||
80 | for labels in controller_info: | ||
81 | print('{}{}{} {}'.format(METRIC_PREFIX, 'controller_info', labels, 1)) | ||
82 | |||
83 | |||
84 | def get_storcli_json(storcli_path): | ||
85 | storcli_cmd = [storcli_path, 'show', 'all', 'J'] | ||
86 | proc = subprocess.Popen(storcli_cmd, stdout=subprocess.PIPE, | ||
87 | stderr=subprocess.PIPE) | ||
88 | return proc.communicate()[0] | ||
89 | |||
90 | if __name__ == "__main__": | ||
91 | parser = argparse.ArgumentParser(description=DESCRIPTION, | ||
92 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) | ||
93 | parser.add_argument('--storcli_path', | ||
94 | default='/opt/MegaRAID/storcli/storcli64', | ||
95 | help='path to StorCLi binary') | ||
96 | parser.add_argument('--version', | ||
97 | action='version', | ||
98 | version='%(prog)s {}'.format(VERSION)) | ||
99 | args = parser.parse_args() | ||
100 | |||
101 | main(args) | ||