1 #!@PYTHON@ -tt
2
3 import sys
4 import stat
5 import re
6 import os
7 import time
8 import logging
9 import atexit
10 import ctypes
11 sys.path.append("@FENCEAGENTSLIBDIR@")
12 from fencing import fail_usage, run_command, atexit_handler, check_input, process_input, show_docs
13 from fencing import fence_action, all_opt, run_delay
14
15 def get_status(conn, options):
16 del conn
17 status = "off"
18 for dev in options["devices"]:
19 is_block_device(dev)
20 if options["--plug"] in get_registration_keys(options, dev):
21 status = "on"
22 else:
23 logging.debug("No registration for key "\
24 + options["--plug"] + " on device " + dev + "\n")
25
26 if options["--action"] == "monitor":
27 dev_read(options)
28
29 return status
30
31
32 def set_status(conn, options):
33 del conn
34 count = 0
35 if options["--action"] == "on":
36 for dev in options["devices"]:
37 is_block_device(dev)
38
39 register_dev(options, dev)
40 if options["--plug"] not in get_registration_keys(options, dev):
41 count += 1
42 logging.debug("Failed to register key "\
43 + options["--plug"] + " on device " + dev + "\n")
44 continue
45 dev_write(options, dev)
46
47 if get_reservation_key(options, dev) is None \
48 and not reserve_dev(options, dev) \
49 and get_reservation_key(options, dev) is None:
50 count += 1
51 logging.debug("Failed to create reservation (key="\
52 + options["--plug"] + ", device=" + dev + ")\n")
53
54 else:
55 dev_keys = dev_read(options)
56
57 for dev in options["devices"]:
58 is_block_device(dev)
59
60 if options["--plug"] in get_registration_keys(options, dev):
61 preempt_abort(options, dev_keys[dev], dev)
62
63 for dev in options["devices"]:
64 if options["--plug"] in get_registration_keys(options, dev):
65 count += 1
66 logging.debug("Failed to remove key "\
67 + options["--plug"] + " on device " + dev + "\n")
68 continue
69
70 if not get_reservation_key(options, dev):
71 count += 1
72 logging.debug("No reservation exists on device " + dev + "\n")
73 if count:
74 logging.error("Failed to verify " + str(count) + " device(s)")
75 sys.exit(1)
76
77
78 # run command, returns dict, ret["rc"] = exit code; ret["out"] = output;
79 # ret["err"] = error
80 def run_cmd(options, cmd):
81 ret = {}
82
83 if "--use-sudo" in options:
84 prefix = options["--sudo-path"] + " "
85 else:
86 prefix = ""
87
88 (ret["rc"], ret["out"], ret["err"]) = run_command(options,
89 prefix + cmd)
90 ret["out"] = "".join([i for i in ret["out"] if i is not None])
91 ret["err"] = "".join([i for i in ret["err"] if i is not None])
92 return ret
93
94
95 # check if device exist and is block device
96 def is_block_device(dev):
97 if not os.path.exists(dev):
98 fail_usage("Failed: device \"" + dev + "\" does not exist")
99 if not stat.S_ISBLK(os.stat(dev).st_mode):
100 fail_usage("Failed: device \"" + dev + "\" is not a block device")
101
102 # cancel registration
103 def preempt_abort(options, host, dev):
104 cmd = options["--mpathpersist-path"] + " -o --preempt-abort --prout-type=5 --param-rk=" + host +" --param-sark=" + options["--plug"] +" -d " + dev
105 return not bool(run_cmd(options, cmd)["rc"])
106
107 def register_dev(options, dev):
108 cmd = options["--mpathpersist-path"] + " -o --register --param-sark=" + options["--plug"] + " -d " + dev
109 #cmd return code != 0 but registration can be successful
110 return not bool(run_cmd(options, cmd)["rc"])
111
112 def reserve_dev(options, dev):
113 cmd = options["--mpathpersist-path"] + " -o --reserve --prout-type=5 --param-rk=" + options["--plug"] + " -d " + dev
114 return not bool(run_cmd(options, cmd)["rc"])
115
116 def get_reservation_key(options, dev):
117 cmd = options["--mpathpersist-path"] + " -i -r -d " + dev
118 out = run_cmd(options, cmd)
119 if out["rc"]:
120 fail_usage('Cannot get reservation key on device "' + dev
121 + '": ' + out["err"])
122 match = re.search(r"\s+key\s*=\s*0x(\S+)\s+", out["out"], re.IGNORECASE)
123 return match.group(1) if match else None
124
125 def get_registration_keys(options, dev, fail=True):
126 keys = []
127 cmd = options["--mpathpersist-path"] + " -i -k -d " + dev
128 out = run_cmd(options, cmd)
129 if out["rc"]:
130 fail_usage('Cannot get registration keys on device "' + dev
131 + '": ' + out["err"], fail)
132 if not fail:
133 return []
134 for line in out["out"].split("\n"):
135 match = re.search(r"\s+0x(\S+)\s*", line)
136 if match:
137 keys.append(match.group(1))
138 return keys
139
140 def dev_write(options, dev):
141 file_path = options["--store-path"] + "/mpath.devices"
142
143 if not os.path.isdir(options["--store-path"]):
144 os.makedirs(options["--store-path"])
145
146 try:
147 store_fh = open(file_path, "a+")
148 except IOError:
149 fail_usage("Failed: Cannot open file \""+ file_path + "\"")
150 store_fh.seek(0)
151 out = store_fh.read()
152 if not re.search(r"^{}\s+{}$".format(dev, options["--plug"]), out, flags=re.MULTILINE):
153 store_fh.write(dev + "\t" + options["--plug"] + "\n")
154 store_fh.close()
155
156 def dev_read(options, fail=True):
157 dev_key = {}
158 file_path = options["--store-path"] + "/mpath.devices"
159 try:
160 store_fh = open(file_path, "r")
161 except IOError:
162 if fail:
163 fail_usage("Failed: Cannot open file \"" + file_path + "\"")
164 else:
165 return None
166 # get not empty lines from file
167 for (device, key) in [line.strip().split() for line in store_fh if line.strip()]:
168 dev_key[device] = key
169 store_fh.close()
170 return dev_key
171
172 def mpath_check_get_options(options):
173 try:
174 f = open("/etc/sysconfig/stonith", "r")
175 except IOError:
176 return options
177
178 match = re.findall(r"^\s*(\S*)\s*=\s*(\S*)\s*", "".join(f.readlines()), re.MULTILINE)
179
180 for m in match:
181 options[m[0].lower()] = m[1].lower()
182
183 f.close()
184
185 return options
186
187 def mpath_check(hardreboot=False):
188 if len(sys.argv) >= 3 and sys.argv[1] == "repair":
189 return int(sys.argv[2])
190 options = {}
191 options["--mpathpersist-path"] = "/usr/sbin/mpathpersist"
192 options["--store-path"] = "@STORE_PATH@"
193 options["--power-timeout"] = "5"
194 options["retry"] = "0"
195 options["retry-sleep"] = "1"
196 options = mpath_check_get_options(options)
197 if "verbose" in options and options["verbose"] == "yes":
|
CID (unavailable; MK=77f356d8468259291d89e68a472ea539) (#1 of 1): Excessive log level (SIGMA.debug_logging_enabled): |
|
(1) Event Sigma main event: |
The Python application has been configured to create excessive logs using a `DEBUG` log level. Excessive logging can expose sensitive information in log files. |
|
(2) Event remediation: |
The log level of a production Python application should be set to `ERROR`, `WARN`, or `INFO`, instead of `DEBUG`. |
198 logging.getLogger().setLevel(logging.DEBUG)
199 devs = dev_read(options, fail=False)
200 if not devs:
201 if "--suppress-errors" not in options:
202 logging.error("No devices found")
203 return 0
204 for dev, key in list(devs.items()):
205 for n in range(int(options["retry"]) + 1):
206 if n > 0:
207 logging.debug("retry: " + str(n) + " of " + options["retry"])
208 if key in get_registration_keys(options, dev, fail=False):
209 logging.debug("key " + key + " registered with device " + dev)
210 return 0
211 else:
212 logging.debug("key " + key + " not registered with device " + dev)
213
214 if n < int(options["retry"]):
215 time.sleep(float(options["retry-sleep"]))
216 logging.debug("key " + key + " registered with any devices")
217
218 if hardreboot == True:
219 libc = ctypes.cdll['libc.so.6']
220 libc.reboot(0x1234567)
221 return 2
222
223 def define_new_opts():
224 all_opt["devices"] = {
225 "getopt" : "d:",
226 "longopt" : "devices",
227 "help" : "-d, --devices=[devices] List of devices to use for current operation",
228 "required" : "0",
229 "shortdesc" : "List of devices to use for current operation. Devices can \
230 be comma or space separated list of device-mapper multipath devices (eg. /dev/mapper/3600508b400105df70000e00000ac0000 or /dev/mapper/mpath1). \
231 Each device must support SCSI-3 persistent reservations.",
232 "order": 1
233 }
234 all_opt["key"] = {
235 "getopt" : "k:",
236 "longopt" : "key",
237 "help" : "-k, --key=[key] Replaced by -n, --plug",
238 "required" : "0",
239 "shortdesc" : "Replaced by port/-n/--plug",
240 "order": 1
241 }
242 all_opt["suppress-errors"] = {
243 "getopt" : "",
244 "longopt" : "suppress-errors",
245 "help" : "--suppress-errors Suppress error log. Suppresses error logging when run from the watchdog service before pacemaker starts.",
246 "required" : "0",
247 "shortdesc" : "Error log suppression.",
248 "order": 4
249 }
250 all_opt["mpathpersist_path"] = {
251 "getopt" : ":",
252 "longopt" : "mpathpersist-path",
253 "help" : "--mpathpersist-path=[path] Path to mpathpersist binary",
254 "required" : "0",
255 "shortdesc" : "Path to mpathpersist binary",
256 "default" : "@MPATH_PATH@",
257 "order": 200
258 }
259 all_opt["store_path"] = {
260 "getopt" : ":",
261 "longopt" : "store-path",
262 "help" : "--store-path=[path] Path to directory containing cached keys",
263 "required" : "0",
264 "shortdesc" : "Path to directory where fence agent can store information",
265 "default" : "@STORE_PATH@",
266 "order": 200
267 }
268
269 def main():
270 atexit.register(atexit_handler)
271
272 device_opt = ["no_login", "no_password", "devices", "key", "sudo", \
273 "fabric_fencing", "on_target", "store_path", \
274 "suppress-errors", "mpathpersist_path", "force_on", "port", "no_port"]
275
276 define_new_opts()
277
278 all_opt["port"]["required"] = "0"
279 all_opt["port"]["help"] = "-n, --plug=[key] Key to use for the current operation"
280 all_opt["port"]["shortdesc"] = "Key to use for the current operation. \
281 This key should be unique to a node and have to be written in \
282 /etc/multipath.conf. For the \"on\" action, the key specifies the key use to \
283 register the local node. For the \"off\" action, this key specifies the key to \
284 be removed from the device(s)."
285
286 # fence_mpath_check
287 if os.path.basename(sys.argv[0]) == "fence_mpath_check":
288 sys.exit(mpath_check())
289 elif os.path.basename(sys.argv[0]) == "fence_mpath_check_hardreboot":
290 sys.exit(mpath_check(hardreboot=True))
291
292 options = check_input(device_opt, process_input(device_opt), other_conditions=True)
293
294 # hack to remove list/list-status actions which are not supported
295 options["device_opt"] = [ o for o in options["device_opt"] if o != "separator" ]
296
297 # workaround to avoid regressions
298 if "--key" in options:
299 options["--plug"] = options["--key"]
300 del options["--key"]
301 elif "--help" not in options and options["--action"] in ["off", "on", \
302 "reboot", "status", "validate-all"] and "--plug" not in options:
303 stop_after_error = False if options["--action"] == "validate-all" else True
304 fail_usage("Failed: You have to enter plug number or machine identification", stop_after_error)
305
306 docs = {}
307 docs["shortdesc"] = "Fence agent for multipath persistent reservation"
308 docs["longdesc"] = "fence_mpath is an I/O Fencing agent that uses SCSI-3 \
309 persistent reservations to control access multipath devices. Underlying \
310 devices must support SCSI-3 persistent reservations (SPC-3 or greater) as \
311 well as the \"preempt-and-abort\" subcommand.\nThe fence_mpath agent works by \
312 having a unique key for each node that has to be set in /etc/multipath.conf. \
313 Once registered, a single node will become the reservation holder \
314 by creating a \"write exclusive, registrants only\" reservation on the \
315 device(s). The result is that only registered nodes may write to the \
316 device(s). When a node failure occurs, the fence_mpath agent will remove the \
317 key belonging to the failed node from the device(s). The failed node will no \
318 longer be able to write to the device(s). A manual reboot is required.\
319 \n.P\n\
320 When used as a watchdog device you can define e.g. retry=1, retry-sleep=2 and \
321 verbose=yes parameters in /etc/sysconfig/stonith if you have issues with it \
322 failing."
323 docs["vendorurl"] = "https://www.sourceware.org/dm/"
324 show_docs(options, docs)
325
326 run_delay(options)
327
328 # Input control BEGIN
329 if options["--action"] == "validate-all":
330 sys.exit(0)
331
332 if not ("--devices" in options and options["--devices"]):
333 fail_usage("Failed: No devices found")
334
335 options["devices"] = [d for d in re.split(r"\s*,\s*|\s+", options["--devices"].strip()) if d]
336 options["--plug"] = re.sub(r"^0x0*|^0+", "", options.get("--plug", ""))
337 # Input control END
338
339 result = fence_action(None, options, set_status, get_status)
340 sys.exit(result)
341
342 if __name__ == "__main__":
343 main()
344