13
from __future__ import print_function
16
from errno import EEXIST
18
from errno import EACCES, EAGAIN
24
from prettytable import PrettyTable
26
from gluster.cliutils import (Cmd, node_output_ok, node_output_notok,
27
sync_file_to_peers, GlusterCmdException,
28
output_error, execute_in_peers, runcli,
30
from gfevents.utils import LockedOpen, get_jwt_token, save_https_cert, NamedTempOpen
32
from gfevents.eventsapiconf import (WEBHOOKS_FILE_TO_SYNC,
36
CUSTOM_CONFIG_FILE_TO_SYNC,
44
ERROR_WEBHOOK_NOT_EXISTS,
45
ERROR_CONFIG_SYNC_FAILED,
46
ERROR_WEBHOOK_ALREADY_EXISTS,
47
ERROR_PARTIAL_SUCCESS,
48
ERROR_ALL_NODES_STATUS_NOT_OK,
50
ERROR_WEBHOOK_SYNC_FAILED,
54
def handle_output_error(err, errcode=1, json_output=False):
62
output_error(err, errcode)
65
def file_content_overwrite(fname, data):
66
with open(fname + ".tmp", "w") as f:
67
f.write(json.dumps(data))
69
os.rename(fname + ".tmp", fname)
72
def create_custom_config_file_if_not_exists(args):
74
config_dir = os.path.dirname(CUSTOM_CONFIG_FILE)
77
handle_output_error("Failed to create dir %s: %s" % (config_dir, e),
78
json_output=args.json)
80
if not os.path.exists(CUSTOM_CONFIG_FILE):
81
with NamedTempOpen(CUSTOM_CONFIG_FILE, "w") as f:
82
f.write(json.dumps({}))
85
def create_webhooks_file_if_not_exists(args):
87
webhooks_dir = os.path.dirname(WEBHOOKS_FILE)
90
handle_output_error("Failed to create dir %s: %s" % (webhooks_dir, e),
91
json_output=args.json)
93
if not os.path.exists(WEBHOOKS_FILE):
94
with NamedTempOpen(WEBHOOKS_FILE, "w") as f:
95
f.write(json.dumps({}))
100
if value.lower() in ["enabled", "true", "on", "yes"]:
105
def mkdirp(path, exit_on_err=False, logger=None):
107
Try creating required directory structure
108
ignore EEXIST and raise exception for rest of the errors.
109
Print error in stderr and exit
114
if e.errno != EEXIST or not os.path.isdir(path):
121
with open(PID_FILE, "a+") as f:
122
fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
124
except (IOError, OSError) as e:
125
if e.errno in (EACCES, EAGAIN):
136
with open(PID_FILE) as f:
138
pid = int(f.read().strip())
142
os.kill(pid, signal.SIGUSR2)
147
def rows_to_json(json_out, column_name, rows):
150
num_ok_rows += 1 if row.ok else 0
152
"node": row.hostname,
153
"node_status": "UP" if row.node_up else "DOWN",
154
column_name: "OK" if row.ok else "NOT OK",
160
def rows_to_table(table, rows):
163
num_ok_rows += 1 if row.ok else 0
164
table.add_row([row.hostname,
165
"UP" if row.node_up else "DOWN",
166
"OK" if row.ok else "NOT OK: {0}".format(
171
def sync_to_peers(args):
172
if os.path.exists(WEBHOOKS_FILE):
174
sync_file_to_peers(WEBHOOKS_FILE_TO_SYNC)
175
except GlusterCmdException as e:
177
errmsg = e.args[0][2] if e.args[0][2] else e.args[0][1]
178
handle_output_error("Failed to sync Webhooks file: [Error: {0}]"
179
"{1}".format(e.args[0][0], errmsg),
180
errcode=ERROR_WEBHOOK_SYNC_FAILED,
181
json_output=args.json)
183
if os.path.exists(CUSTOM_CONFIG_FILE):
185
sync_file_to_peers(CUSTOM_CONFIG_FILE_TO_SYNC)
186
except GlusterCmdException as e:
188
errmsg = e.args[0][2] if e.args[0][2] else e.args[0][1]
189
handle_output_error("Failed to sync Config file: [Error: {0}]"
190
"{1}".format(e.args[0][0], errmsg),
191
errcode=ERROR_CONFIG_SYNC_FAILED,
192
json_output=args.json)
194
out = execute_in_peers("node-reload")
196
table = PrettyTable(["NODE", "NODE STATUS", "SYNC STATUS"])
197
table.align["NODE STATUS"] = "r"
198
table.align["SYNC STATUS"] = "r"
202
num_ok_rows = rows_to_json(json_out, "sync_status", out)
204
num_ok_rows = rows_to_table(table, out)
208
ret = ERROR_ALL_NODES_STATUS_NOT_OK
209
elif num_ok_rows != len(out):
210
ret = ERROR_PARTIAL_SUCCESS
224
def node_output_handle(resp):
229
node_output_notok(err)
232
def action_handle(action, json_output=False):
233
out = execute_in_peers("node-" + action)
234
column_name = action.upper()
235
if action == "status":
236
column_name = EVENTSD.upper()
239
table = PrettyTable(["NODE", "NODE STATUS", column_name + " STATUS"])
240
table.align["NODE STATUS"] = "r"
241
table.align[column_name + " STATUS"] = "r"
245
rows_to_json(json_out, column_name.lower() + "_status", out)
247
rows_to_table(table, out)
249
return json_out if json_output else table
252
class NodeReload(Cmd):
256
node_output_handle(reload_service())
263
out = action_handle("reload", args.json)
273
class NodeStatus(Cmd):
277
node_output_ok("UP" if is_active() else "DOWN")
285
if os.path.exists(WEBHOOKS_FILE):
286
webhooks = json.load(open(WEBHOOKS_FILE))
288
json_out = {"webhooks": [], "data": []}
290
json_out["webhooks"] = webhooks.keys()
292
print ("Webhooks: " + ("" if webhooks else "None"))
298
out = action_handle("status", args.json)
300
json_out["data"] = out
309
class WebhookAddCmd(Cmd):
312
def args(self, parser):
313
parser.add_argument("url", help="URL of Webhook")
314
parser.add_argument("--bearer_token", "-t", help="Bearer Token",
316
parser.add_argument("--secret", "-s",
317
help="Secret to add JWT Bearer Token", default="")
320
create_webhooks_file_if_not_exists(args)
322
with LockedOpen(WEBHOOKS_FILE, 'r+'):
323
data = json.load(open(WEBHOOKS_FILE))
324
if data.get(args.url, None) is not None:
325
handle_output_error("Webhook already exists",
326
errcode=ERROR_WEBHOOK_ALREADY_EXISTS,
327
json_output=args.json)
329
data[args.url] = {"token": args.bearer_token,
330
"secret": args.secret}
331
file_content_overwrite(WEBHOOKS_FILE, data)
336
class WebhookModCmd(Cmd):
339
def args(self, parser):
340
parser.add_argument("url", help="URL of Webhook")
341
parser.add_argument("--bearer_token", "-t", help="Bearer Token",
343
parser.add_argument("--secret", "-s",
344
help="Secret to add JWT Bearer Token", default="")
347
create_webhooks_file_if_not_exists(args)
349
with LockedOpen(WEBHOOKS_FILE, 'r+'):
350
data = json.load(open(WEBHOOKS_FILE))
351
if data.get(args.url, None) is None:
352
handle_output_error("Webhook does not exists",
353
errcode=ERROR_WEBHOOK_NOT_EXISTS,
354
json_output=args.json)
356
if isinstance(data[args.url], str):
357
data[args.url]["token"] = data[args.url]
359
if args.bearer_token != "":
360
data[args.url]["token"] = args.bearer_token
362
if args.secret != "":
363
data[args.url]["secret"] = args.secret
365
file_content_overwrite(WEBHOOKS_FILE, data)
370
class WebhookDelCmd(Cmd):
373
def args(self, parser):
374
parser.add_argument("url", help="URL of Webhook")
377
create_webhooks_file_if_not_exists(args)
379
with LockedOpen(WEBHOOKS_FILE, 'r+'):
380
data = json.load(open(WEBHOOKS_FILE))
381
if data.get(args.url, None) is None:
382
handle_output_error("Webhook does not exists",
383
errcode=ERROR_WEBHOOK_NOT_EXISTS,
384
json_output=args.json)
387
file_content_overwrite(WEBHOOKS_FILE, data)
392
class NodeWebhookTestCmd(Cmd):
393
name = "node-webhook-test"
395
def args(self, parser):
396
parser.add_argument("url")
397
parser.add_argument("bearer_token")
398
parser.add_argument("secret")
403
if args.bearer_token != ".":
404
hashval = args.bearer_token
406
if args.secret != ".":
407
hashval = get_jwt_token(args.secret, "TEST", int(time.time()))
410
http_headers["Authorization"] = "Bearer " + hashval
412
urldata = requests.utils.urlparse(args.url)
413
parts = urldata.netloc.split(":")
420
cert_path = os.path.join(CERTS_DIR, args.url.replace("/", "_").strip())
424
resp = requests.post(args.url, headers=http_headers,
428
except requests.exceptions.SSLError as e:
432
if verify == cert_path:
439
save_https_cert(domain, port, cert_path)
446
except Exception as e:
447
node_output_notok("{0}".format(e))
450
if resp.status_code != 200:
451
node_output_notok("{0}".format(resp.status_code))
456
class WebhookTestCmd(Cmd):
457
name = "webhook-test"
459
def args(self, parser):
460
parser.add_argument("url", help="URL of Webhook")
461
parser.add_argument("--bearer_token", "-t", help="Bearer Token")
462
parser.add_argument("--secret", "-s",
463
help="Secret to generate Bearer Token")
467
bearer_token = args.bearer_token
472
if not args.bearer_token:
477
out = execute_in_peers("node-webhook-test", [url, bearer_token,
481
table = PrettyTable(["NODE", "NODE STATUS", "WEBHOOK STATUS"])
482
table.align["NODE STATUS"] = "r"
483
table.align["WEBHOOK STATUS"] = "r"
488
num_ok_rows = rows_to_json(json_out, "webhook_status", out)
490
num_ok_rows = rows_to_table(table, out)
494
ret = ERROR_ALL_NODES_STATUS_NOT_OK
495
elif num_ok_rows != len(out):
496
ret = ERROR_PARTIAL_SUCCESS
509
class ConfigGetCmd(Cmd):
512
def args(self, parser):
513
parser.add_argument("--name", help="Config Name")
516
data = json.load(open(DEFAULT_CONFIG_FILE))
517
if os.path.exists(CUSTOM_CONFIG_FILE):
518
data.update(json.load(open(CUSTOM_CONFIG_FILE)))
520
if args.name is not None and args.name not in CONFIG_KEYS:
521
handle_output_error("Invalid Config item",
522
errcode=ERROR_INVALID_CONFIG,
523
json_output=args.json)
527
if args.name is None:
530
json_out[args.name] = data[args.name]
537
table = PrettyTable(["NAME", "VALUE"])
538
if args.name is None:
539
for k, v in data.items():
540
table.add_row([k, v])
542
table.add_row([args.name, data[args.name]])
547
def read_file_content_json(fname):
549
with open(fname) as f:
551
if content.strip() == "":
554
return json.loads(content)
557
class ConfigSetCmd(Cmd):
560
def args(self, parser):
561
parser.add_argument("name", help="Config Name")
562
parser.add_argument("value", help="Config Value")
565
if args.name not in CONFIG_KEYS:
566
handle_output_error("Invalid Config item",
567
errcode=ERROR_INVALID_CONFIG,
568
json_output=args.json)
570
create_custom_config_file_if_not_exists(args)
572
with LockedOpen(CUSTOM_CONFIG_FILE, 'r+'):
573
data = json.load(open(DEFAULT_CONFIG_FILE))
574
if os.path.exists(CUSTOM_CONFIG_FILE):
575
config_json = read_file_content_json(CUSTOM_CONFIG_FILE)
576
data.update(config_json)
579
if data[args.name] == args.value:
580
handle_output_error("Config value not changed. Same config",
581
errcode=ERROR_SAME_CONFIG,
582
json_output=args.json)
585
new_data = read_file_content_json(CUSTOM_CONFIG_FILE)
588
if args.name in BOOL_CONFIGS:
589
v = boolify(args.value)
591
if args.name in INT_CONFIGS:
594
new_data[args.name] = v
595
file_content_overwrite(CUSTOM_CONFIG_FILE, new_data)
599
if args.name in RESTART_CONFIGS:
603
print ("\nRestart glustereventsd in all nodes")
608
class ConfigResetCmd(Cmd):
609
name = "config-reset"
611
def args(self, parser):
612
parser.add_argument("name", help="Config Name or all")
615
create_custom_config_file_if_not_exists(args)
617
with LockedOpen(CUSTOM_CONFIG_FILE, 'r+'):
620
if os.path.exists(CUSTOM_CONFIG_FILE):
621
data = read_file_content_json(CUSTOM_CONFIG_FILE)
626
(args.name != "all" and data.get(args.name, None) is None):
627
handle_output_error("Config value not reset. Already "
628
"set to default value",
629
errcode=ERROR_SAME_CONFIG,
630
json_output=args.json)
632
if args.name.lower() == "all":
633
for k, v in data.items():
634
changed_keys.append(k)
637
file_content_overwrite(CUSTOM_CONFIG_FILE, {})
639
changed_keys.append(args.name)
641
file_content_overwrite(CUSTOM_CONFIG_FILE, data)
645
for key in changed_keys:
646
if key in RESTART_CONFIGS:
651
print ("\nRestart glustereventsd in all nodes")
663
def common_args(parser):
664
parser.add_argument("--json", help="JSON Output", action="store_true")
667
if __name__ == "__main__":
668
set_common_args_func(common_args)