|
1 | 1 | #!/usr/bin/python |
2 | 2 |
|
| 3 | +import json |
3 | 4 | import os |
4 | 5 | import re |
5 | 6 | import sys |
6 | 7 |
|
7 | | -binarypath = "/usr/sbin/tw-cli" |
8 | | - |
9 | | -if len(sys.argv) > 2: |
10 | | - print 'Usage: 3ware-status [--nagios]' |
11 | | - sys.exit(1) |
12 | | - |
13 | | -nagiosmode=False |
14 | | -nagiosoutput='' |
15 | | -nagiosgoodarray=0 |
16 | | -nagiosbadarray=0 |
17 | | -nagiosgooddisk=0 |
18 | | -nagiosbaddisk=0 |
| 8 | +from subprocess import check_output |
19 | 9 |
|
20 | | -if len(sys.argv) > 1: |
21 | | - if sys.argv[1] == '--nagios': |
22 | | - nagiosmode=True |
| 10 | +binarypath = "/usr/sbin/tw-cli" |
| 11 | +normalmode = False |
| 12 | +nagiosmode = False |
| 13 | +zabbixmode = False |
| 14 | +argv = list() |
| 15 | + |
| 16 | +if len(sys.argv) > 1 and sys.argv[1][:2] == '--': |
| 17 | + if len(sys.argv) == 2 and sys.argv[1] == '--nagios': |
| 18 | + nagiosmode = True |
| 19 | + elif len(sys.argv) > 2 and sys.argv[1] == '--zabbix': |
| 20 | + zabbixmode = True |
| 21 | + argv = sys.argv[2:] |
23 | 22 | else: |
24 | | - print 'Usage: 3ware-status [--nagios]' |
| 23 | + print 'Usage: 3ware-status [--nagios|[[--zabbix] <prop> [<key>]]]' |
25 | 24 | sys.exit(1) |
26 | | - |
27 | | -# Check binary exists (and +x), if not print an error message |
28 | | -# or return UNKNOWN nagios error code |
29 | | -if os.path.exists(binarypath) and os.access(binarypath, os.X_OK): |
30 | | - pass |
31 | 25 | else: |
32 | | - if nagiosmode: |
33 | | - print 'UNKNOWN - Cannot find '+binarypath |
34 | | - else: |
35 | | - print 'Cannot find '+binarypath+'. Please install it.' |
36 | | - sys.exit(3) |
| 26 | + normalmode = True |
| 27 | + argv = sys.argv[1:] |
37 | 28 |
|
| 29 | +# Check binary exists (and +x), if not print an error message |
| 30 | +if not (os.path.exists(binarypath) and os.access(binarypath, os.X_OK)): |
| 31 | + if nagiosmode: |
| 32 | + print 'UNKNOWN - Cannot find %s' % binarypath |
| 33 | + else: |
| 34 | + print 'Cannot find %s. Please install it.' % binarypath |
| 35 | + sys.exit(3) |
38 | 36 |
|
39 | 37 | # Get command output |
40 | | -def getOutput(cmd): |
41 | | - output = os.popen(cmd) |
42 | | - lines = [] |
43 | | - for line in output: |
44 | | - if not re.match(r'^$',line.strip()): |
45 | | - lines.append(line.strip()) |
46 | | - return lines |
| 38 | +def getOutput(args = ''): |
| 39 | + output = check_output([binarypath] + args.split()) |
| 40 | + return [line for line in output.splitlines() if not re.match(r'^$', line.strip())] |
| 41 | + |
| 42 | +def getProperty(args): |
| 43 | + try: |
| 44 | + return getOutput('info %s' % args)[0].split(' = ')[1].strip() |
| 45 | + except IndexError: |
| 46 | + return 'N/A' |
47 | 47 |
|
48 | | -def returnControllerList(output): |
49 | | - lines = [] |
50 | | - for line in output: |
51 | | - if re.match(r'^c[0-9]+\s.*$',line.strip()): |
52 | | - lines.append(line.split()[0]) |
53 | | - return lines |
54 | | - |
55 | | -def returnDiskList(output): |
56 | | - lines = [] |
57 | | - for line in output: |
58 | | - if re.match(r'^[p][0-9]+\s.*$',line.strip()): |
59 | | - # Shoudl contain something like 'u0' |
60 | | - # '-' means the drive doesn't belong to any array |
61 | | - # If is NOT PRESENT too, it just means this is an empty port |
62 | | - if not line.split()[2].strip() == '-' and not line.split()[1].strip() == 'NOT-PRESENT': |
63 | | - lines.append(line.split()) |
64 | | - if fake_failure: |
65 | | - lines[0][1] = 'NOT PRESENT' |
66 | | - return lines |
67 | | - |
68 | | -def returnArrayList(output): |
69 | | - lines = [] |
70 | | - for line in output: |
71 | | - if re.match(r'^[u][0-9]+\s.*$',line.strip()): |
72 | | - lines.append(line.split()) |
73 | | - if fake_failure: |
74 | | - lines[0][2] = 'DEGRADED' |
75 | | - return lines |
76 | | - |
77 | | -# A way to force a fake failure |
78 | | -fake_failure = False |
79 | | -if os.path.exists('/root/fake_3ware_failure'): |
80 | | - fake_failure = True |
81 | | - |
82 | | -cmd = binarypath+' info' |
83 | | -output = getOutput(cmd) |
84 | | -controllerlist = returnControllerList(output) |
85 | | - |
86 | | -bad = False |
87 | | - |
88 | | - |
89 | | -# List available controller |
90 | | -if not nagiosmode: |
91 | | - print '-- Controller informations --' |
92 | | - print '-- ID | Model' |
93 | | - for controller in controllerlist: |
94 | | - cmd = binarypath+' info '+controller+' model' |
95 | | - # https://github.com/eLvErDe/hwraid/issues/69 |
96 | | - try: |
97 | | - model = getOutput(cmd)[0].split(' = ')[1].strip() |
98 | | - except IndexError: |
99 | | - model = 'N/A' |
100 | | - print controller+' | '+model |
101 | | - print '' |
102 | | - |
103 | | -# List arrays |
104 | | -if not nagiosmode: |
105 | | - print '-- Arrays informations --' |
106 | | - print '-- ID\tType\tSize\tStatus' |
107 | | -for controller in controllerlist: |
108 | | - cmd = binarypath+' info '+controller |
109 | | - output = getOutput(cmd) |
110 | | - arraylist = returnArrayList(output) |
111 | | - for array in arraylist: |
112 | | - type = array[1].replace('-','') |
113 | | - id = controller+array[0] |
114 | | - size = array[6].split('.')[0]+'G' |
115 | | - status = array[2] |
116 | | - if not status in ['OK','VERIFYING']: |
117 | | - bad = True |
118 | | - nagiosbadarray=nagiosbadarray+1 |
119 | | - else: |
120 | | - nagiosgoodarray=nagiosgoodarray+1 |
121 | | - if not nagiosmode: |
122 | | - print id+'\t'+type+'\t'+size+'\t'+status |
123 | | -if not nagiosmode: |
124 | | - print '' |
125 | | - |
126 | | -# List disks |
127 | | -if not nagiosmode: |
128 | | - print '-- Disks informations' |
129 | | - print '-- ID\tModel\t\t\tStatus' |
130 | | -for controller in controllerlist: |
131 | | - cmd = binarypath+' info '+controller |
132 | | - output = getOutput(cmd) |
133 | | - disklist = returnDiskList(output) |
134 | | - for disk in disklist: |
135 | | - id = controller+disk[2]+disk[0] |
136 | | - cmd = binarypath+' info '+controller+' '+disk[0]+' model' |
137 | | - model = getOutput(cmd)[0].split(' = ')[1].strip() |
138 | | - cmd = binarypath+' info '+controller+' '+disk[0]+' status' |
139 | | - status = getOutput(cmd)[0].split(' = ')[1].strip() |
140 | | - if not status == 'OK': |
141 | | - bad = True |
142 | | - nagiosbaddisk=nagiosbaddisk+1 |
143 | | - else: |
144 | | - nagiosgooddisk=nagiosgooddisk+1 |
145 | | - if not nagiosmode: |
146 | | - print id+'\t'+model+'\t'+status |
147 | | - |
148 | | -if nagiosmode: |
149 | | - if bad: |
150 | | - print 'RAID ERROR - Arrays: OK:'+str(nagiosgoodarray)+' Bad:'+str(nagiosbadarray)+' - Disks: OK:'+str(nagiosgooddisk)+' Bad:'+str(nagiosbaddisk) |
151 | | - sys.exit(2) |
| 48 | +def parseControllers(): |
| 49 | + controllers = dict() |
| 50 | + for line in getOutput('info'): |
| 51 | + if re.match(r'^c[0-9]+\s.*$', line.strip()): |
| 52 | + parts = line.split() |
| 53 | + |
| 54 | + id = parts[0] |
| 55 | + model = getProperty('%s model' % id) |
| 56 | + |
| 57 | + controllers[id] = { 'model': model } |
| 58 | + return controllers |
| 59 | + |
| 60 | +def parseInfo(controller): |
| 61 | + arrays = dict() |
| 62 | + disks = dict() |
| 63 | + for line in getOutput('info %s' % controller): |
| 64 | + if re.match(r'^u[0-9]+\s.*$', line.strip()): |
| 65 | + parts = line.split() |
| 66 | + |
| 67 | + id = controller + parts[0] |
| 68 | + type = parts[1].replace('-', '') |
| 69 | + status = parts[2] |
| 70 | + size = '%sG' % parts[6].split('.')[0] |
| 71 | + |
| 72 | + arrays[id] = { 'type': type, 'status': status, 'size': size } |
| 73 | + |
| 74 | + if re.match(r'^p[0-9]+\s.*$', line.strip()): |
| 75 | + parts = line.split() |
| 76 | + |
| 77 | + if not parts[2].strip() == '-' and not parts[1].strip() == 'NOT-PRESENT': |
| 78 | + array = controller + parts[2] |
| 79 | + id = array + parts[0] |
| 80 | + model = getProperty('%s %s model' % (controller, parts[0])) |
| 81 | + status = getProperty('%s %s status' % (controller, parts[0])) |
| 82 | + |
| 83 | + disks[id] = { 'model': model, 'status': status } |
| 84 | + |
| 85 | + return (arrays, disks) |
| 86 | + |
| 87 | + |
| 88 | +controllers = dict() |
| 89 | +arrays = dict() |
| 90 | +disks = dict() |
| 91 | + |
| 92 | +def init(): |
| 93 | + controllers.update(parseControllers()) |
| 94 | + for controller in controllers.keys(): |
| 95 | + a, d = parseInfo(controller) |
| 96 | + arrays.update(a) |
| 97 | + disks.update(d) |
| 98 | + |
| 99 | +def info(args): |
| 100 | + str = '-- Controller informations --\n' |
| 101 | + str += '-- ID | Model\n' |
| 102 | + for c, v in controllers.items(): |
| 103 | + str += '%s | %s\n' % (c, v['model']) |
| 104 | + str += '\n' |
| 105 | + |
| 106 | + str += '-- Arrays informations --\n' |
| 107 | + str += '-- ID\tType\tSize\tStatus\n' |
| 108 | + for a, v in arrays.items(): |
| 109 | + str += '%s\t%s\t%s\t%s\n' % (a, v['type'], v['size'], v['status']) |
| 110 | + str += '\n' |
| 111 | + |
| 112 | + str += '-- Disks informations\n' |
| 113 | + str += '-- ID\tModel\t\t\tStatus\n' |
| 114 | + for d, v in disks.items(): |
| 115 | + str += '%s\t%s\t%s\n' % (d, v['model'], v['status']) |
| 116 | + return str[:-1] |
| 117 | + |
| 118 | +def discover(objs, label): |
| 119 | + return lambda args: json.dumps({ 'data': [{label: o} for o in objs.keys()] }) |
| 120 | + |
| 121 | +def property(objs, prop): |
| 122 | + return lambda args: objs[args[0]][prop] |
| 123 | + |
| 124 | +def execute(command, args = []): |
| 125 | + try: |
| 126 | + init() |
| 127 | + print commands[command](args) |
| 128 | + except (KeyError, IndexError): |
| 129 | + print 'Usage: 3ware-status [--zabbix][<prop> [<key>]]' |
| 130 | + print '' |
| 131 | + print 'Where prop is one of:' |
| 132 | + print '\n'.join([' %s' % command for command in commands]) |
| 133 | + print '' |
| 134 | + print 'And key is one of the items output by <prop.discover>' |
| 135 | + |
| 136 | +if normalmode or zabbixmode: |
| 137 | + commands = dict() |
| 138 | + if normalmode: |
| 139 | + commands['default'] = info |
| 140 | + commands['controller.discover'] = discover(controllers, '{#3WCONT}') |
| 141 | + commands['controller.model'] = property(controllers, 'model') |
| 142 | + commands['array.discover'] = discover(arrays, '{#3WARRAY}') |
| 143 | + commands['array.size'] = property(arrays, 'size') |
| 144 | + commands['array.status'] = property(arrays, 'status') |
| 145 | + commands['array.type'] = property(arrays, 'type') |
| 146 | + commands['disk.discover'] = discover(disks, '{#3WDISK}') |
| 147 | + commands['disk.model'] = property(disks, 'model') |
| 148 | + commands['disk.status'] = property(disks, 'status') |
| 149 | + |
| 150 | + if len(argv) > 0: |
| 151 | + command = argv[0] |
| 152 | + args = argv[1:] |
| 153 | + execute(command, args) |
152 | 154 | else: |
153 | | - print 'RAID OK - Arrays: OK:'+str(nagiosgoodarray)+' Bad:'+str(nagiosbadarray)+' - Disks: OK:'+str(nagiosgooddisk)+' Bad:'+str(nagiosbaddisk) |
154 | | -else: |
155 | | - if bad: |
156 | | - print '\nThere is at least one disk/array in a NOT OPTIMAL state.' |
157 | | - sys.exit(1) |
| 155 | + execute('default') |
| 156 | +elif nagiosmode: |
| 157 | + init() |
| 158 | + badarray = len([a for a, v in arrays.items() if v['status'] not in ['OK', 'VERIFYING']]) |
| 159 | + goodarray = len([a for a, v in arrays.items() if v['status'] in ['OK', 'VERIFYING']]) |
| 160 | + baddisk = len([d for d, v in disks.items() if v['status'] not in ['OK', 'VERIFYING']]) |
| 161 | + gooddisk = len([d for d, v in disks.items() if v['status'] in ['OK', 'VERIFYING']]) |
| 162 | + bad = badarray or baddisk |
| 163 | + |
| 164 | + print 'RAID %s - Arrays: OK:%s Bad:%s - Disks: OK:%s Bad:%s' % ('ERROR' if bad else 'OK', goodarray, badarray, gooddisk, baddisk) |
| 165 | + sys.exit(bad) |
0 commit comments