3604df
From 7fa90a37f083181eac97c646714ca46b6a3fd679 Mon Sep 17 00:00:00 2001
3604df
From: Aravinda VK <avishwan@redhat.com>
3604df
Date: Thu, 17 Nov 2016 15:42:24 +0530
3604df
Subject: [PATCH 217/227] eventsapi: JSON output and different error codes
3604df
3604df
JSON outputs are added to all commands, use `--json` to
3604df
get JSON output.
3604df
3604df
Following error codes are added to differenciate between errors.
3604df
Any other Unknown errors will have return code 1
3604df
3604df
ERROR_SAME_CONFIG             = 2
3604df
ERROR_ALL_NODES_STATUS_NOT_OK = 3
3604df
ERROR_PARTIAL_SUCCESS         = 4
3604df
ERROR_WEBHOOK_ALREADY_EXISTS  = 5
3604df
ERROR_WEBHOOK_NOT_EXISTS      = 6
3604df
ERROR_INVALID_CONFIG          = 7
3604df
ERROR_WEBHOOK_SYNC_FAILED     = 8
3604df
ERROR_CONFIG_SYNC_FAILED      = 9
3604df
3604df
Also hidden `node-` commands in the help message.
3604df
3604df
> Reviewed-on: http://review.gluster.org/15867
3604df
> Smoke: Gluster Build System <jenkins@build.gluster.org>
3604df
> Reviewed-by: Prashanth Pai <ppai@redhat.com>
3604df
> NetBSD-regression: NetBSD Build System <jenkins@build.gluster.org>
3604df
> CentOS-regression: Gluster Build System <jenkins@build.gluster.org>
3604df
3604df
BUG: 1395603
3604df
Change-Id: I962b5435c8a448b4573059da0eae42f3f93cc97e
3604df
Signed-off-by: Aravinda VK <avishwan@redhat.com>
3604df
Reviewed-on: https://code.engineering.redhat.com/gerrit/91988
3604df
Reviewed-by: Atin Mukherjee <amukherj@redhat.com>
3604df
---
3604df
 events/src/eventsapiconf.py.in |  10 ++
3604df
 events/src/peer_eventsapi.py   | 309 ++++++++++++++++++++++++++++++-----------
3604df
 extras/cliutils/__init__.py    |   6 +-
3604df
 extras/cliutils/cliutils.py    |  21 ++-
3604df
 4 files changed, 259 insertions(+), 87 deletions(-)
3604df
3604df
diff --git a/events/src/eventsapiconf.py.in b/events/src/eventsapiconf.py.in
3604df
index ecccd3d..0159241 100644
3604df
--- a/events/src/eventsapiconf.py.in
3604df
+++ b/events/src/eventsapiconf.py.in
3604df
@@ -24,3 +24,13 @@ RESTART_CONFIGS = ["port"]
3604df
 EVENTS_ENABLED = @EVENTS_ENABLED@
3604df
 UUID_FILE = "@GLUSTERD_WORKDIR@/glusterd.info"
3604df
 PID_FILE = "@localstatedir@/run/glustereventsd.pid"
3604df
+
3604df
+# Errors
3604df
+ERROR_SAME_CONFIG = 2
3604df
+ERROR_ALL_NODES_STATUS_NOT_OK = 3
3604df
+ERROR_PARTIAL_SUCCESS = 4
3604df
+ERROR_WEBHOOK_ALREADY_EXISTS = 5
3604df
+ERROR_WEBHOOK_NOT_EXISTS = 6
3604df
+ERROR_INVALID_CONFIG = 7
3604df
+ERROR_WEBHOOK_SYNC_FAILED = 8
3604df
+ERROR_CONFIG_SYNC_FAILED = 9
3604df
diff --git a/events/src/peer_eventsapi.py b/events/src/peer_eventsapi.py
3604df
index 7f80f79..6cba277 100644
3604df
--- a/events/src/peer_eventsapi.py
3604df
+++ b/events/src/peer_eventsapi.py
3604df
@@ -17,13 +17,15 @@ from errno import EEXIST
3604df
 import fcntl
3604df
 from errno import EACCES, EAGAIN
3604df
 import signal
3604df
+import sys
3604df
 
3604df
 import requests
3604df
 from prettytable import PrettyTable
3604df
 
3604df
-from gluster.cliutils import (Cmd, execute, node_output_ok, node_output_notok,
3604df
+from gluster.cliutils import (Cmd, node_output_ok, node_output_notok,
3604df
                               sync_file_to_peers, GlusterCmdException,
3604df
-                              output_error, execute_in_peers, runcli)
3604df
+                              output_error, execute_in_peers, runcli,
3604df
+                              set_common_args_func)
3604df
 from events.utils import LockedOpen
3604df
 
3604df
 from events.eventsapiconf import (WEBHOOKS_FILE_TO_SYNC,
3604df
@@ -36,7 +38,26 @@ from events.eventsapiconf import (WEBHOOKS_FILE_TO_SYNC,
3604df
                                   BOOL_CONFIGS,
3604df
                                   INT_CONFIGS,
3604df
                                   PID_FILE,
3604df
-                                  RESTART_CONFIGS)
3604df
+                                  RESTART_CONFIGS,
3604df
+                                  ERROR_INVALID_CONFIG,
3604df
+                                  ERROR_WEBHOOK_NOT_EXISTS,
3604df
+                                  ERROR_CONFIG_SYNC_FAILED,
3604df
+                                  ERROR_WEBHOOK_ALREADY_EXISTS,
3604df
+                                  ERROR_PARTIAL_SUCCESS,
3604df
+                                  ERROR_ALL_NODES_STATUS_NOT_OK,
3604df
+                                  ERROR_SAME_CONFIG,
3604df
+                                  ERROR_WEBHOOK_SYNC_FAILED)
3604df
+
3604df
+
3604df
+def handle_output_error(err, errcode=1, json_output=False):
3604df
+    if json_output:
3604df
+        print (json.dumps({
3604df
+            "output": "",
3604df
+            "error": err
3604df
+            }))
3604df
+        sys.exit(errcode)
3604df
+    else:
3604df
+        output_error(err, errcode)
3604df
 
3604df
 
3604df
 def file_content_overwrite(fname, data):
3604df
@@ -46,15 +67,27 @@ def file_content_overwrite(fname, data):
3604df
     os.rename(fname + ".tmp", fname)
3604df
 
3604df
 
3604df
-def create_custom_config_file_if_not_exists():
3604df
-    mkdirp(os.path.dirname(CUSTOM_CONFIG_FILE))
3604df
+def create_custom_config_file_if_not_exists(args):
3604df
+    try:
3604df
+        config_dir = os.path.dirname(CUSTOM_CONFIG_FILE)
3604df
+        mkdirp(config_dir)
3604df
+    except OSError as e:
3604df
+        handle_output_error("Failed to create dir %s: %s" % (config_dir, e),
3604df
+                            json_output=args.json)
3604df
+
3604df
     if not os.path.exists(CUSTOM_CONFIG_FILE):
3604df
         with open(CUSTOM_CONFIG_FILE, "w") as f:
3604df
             f.write("{}")
3604df
 
3604df
 
3604df
-def create_webhooks_file_if_not_exists():
3604df
-    mkdirp(os.path.dirname(WEBHOOKS_FILE))
3604df
+def create_webhooks_file_if_not_exists(args):
3604df
+    try:
3604df
+        webhooks_dir = os.path.dirname(WEBHOOKS_FILE)
3604df
+        mkdirp(webhooks_dir)
3604df
+    except OSError as e:
3604df
+        handle_output_error("Failed to create dir %s: %s" % (webhooks_dir, e),
3604df
+                            json_output=args.json)
3604df
+
3604df
     if not os.path.exists(WEBHOOKS_FILE):
3604df
         with open(WEBHOOKS_FILE, "w") as f:
3604df
             f.write("{}")
3604df
@@ -75,11 +108,9 @@ def mkdirp(path, exit_on_err=False, logger=None):
3604df
     """
3604df
     try:
3604df
         os.makedirs(path)
3604df
-    except (OSError, IOError) as e:
3604df
-        if e.errno == EEXIST and os.path.isdir(path):
3604df
-            pass
3604df
-        else:
3604df
-            output_error("Fail to create dir %s: %s" % (path, e))
3604df
+    except OSError as e:
3604df
+        if e.errno != EEXIST or not os.path.isdir(path):
3604df
+            raise
3604df
 
3604df
 
3604df
 def is_active():
3604df
@@ -111,33 +142,77 @@ def reload_service():
3604df
     return (0, "", "")
3604df
 
3604df
 
3604df
-def sync_to_peers():
3604df
+def rows_to_json(json_out, column_name, rows):
3604df
+    num_ok_rows = 0
3604df
+    for row in rows:
3604df
+        num_ok_rows += 1 if row.ok else 0
3604df
+        json_out.append({
3604df
+            "node": row.hostname,
3604df
+            "node_status": "UP" if row.node_up else "DOWN",
3604df
+            column_name: "OK" if row.ok else "NOT OK",
3604df
+            "error": row.error
3604df
+        })
3604df
+    return num_ok_rows
3604df
+
3604df
+
3604df
+def rows_to_table(table, rows):
3604df
+    num_ok_rows = 0
3604df
+    for row in rows:
3604df
+        num_ok_rows += 1 if row.ok else 0
3604df
+        table.add_row([row.hostname,
3604df
+                       "UP" if row.node_up else "DOWN",
3604df
+                       "OK" if row.ok else "NOT OK: {1}".format(
3604df
+                           row.error)])
3604df
+    return num_ok_rows
3604df
+
3604df
+
3604df
+def sync_to_peers(args):
3604df
     if os.path.exists(WEBHOOKS_FILE):
3604df
         try:
3604df
             sync_file_to_peers(WEBHOOKS_FILE_TO_SYNC)
3604df
         except GlusterCmdException as e:
3604df
-            output_error("Failed to sync Webhooks file: [Error: {0}]"
3604df
-                         "{1}".format(e[0], e[2]))
3604df
+            handle_output_error("Failed to sync Webhooks file: [Error: {0}]"
3604df
+                                "{1}".format(e[0], e[2]),
3604df
+                                errcode=ERROR_WEBHOOK_SYNC_FAILED,
3604df
+                                json_output=args.json)
3604df
 
3604df
     if os.path.exists(CUSTOM_CONFIG_FILE):
3604df
         try:
3604df
             sync_file_to_peers(CUSTOM_CONFIG_FILE_TO_SYNC)
3604df
         except GlusterCmdException as e:
3604df
-            output_error("Failed to sync Config file: [Error: {0}]"
3604df
-                         "{1}".format(e[0], e[2]))
3604df
+            handle_output_error("Failed to sync Config file: [Error: {0}]"
3604df
+                                "{1}".format(e[0], e[2]),
3604df
+                                errcode=ERROR_CONFIG_SYNC_FAILED,
3604df
+                                json_output=args.json)
3604df
 
3604df
     out = execute_in_peers("node-reload")
3604df
-    table = PrettyTable(["NODE", "NODE STATUS", "SYNC STATUS"])
3604df
-    table.align["NODE STATUS"] = "r"
3604df
-    table.align["SYNC STATUS"] = "r"
3604df
+    if not args.json:
3604df
+        table = PrettyTable(["NODE", "NODE STATUS", "SYNC STATUS"])
3604df
+        table.align["NODE STATUS"] = "r"
3604df
+        table.align["SYNC STATUS"] = "r"
3604df
 
3604df
-    for p in out:
3604df
-        table.add_row([p.hostname,
3604df
-                       "UP" if p.node_up else "DOWN",
3604df
-                       "OK" if p.ok else "NOT OK: {0}".format(
3604df
-                           p.error)])
3604df
+    json_out = []
3604df
+    if args.json:
3604df
+        num_ok_rows = rows_to_json(json_out, "sync_status", out)
3604df
+    else:
3604df
+        num_ok_rows = rows_to_table(table, out)
3604df
+
3604df
+    ret = 0
3604df
+    if num_ok_rows == 0:
3604df
+        ret = ERROR_ALL_NODES_STATUS_NOT_OK
3604df
+    elif num_ok_rows != len(out):
3604df
+        ret = ERROR_PARTIAL_SUCCESS
3604df
+
3604df
+    if args.json:
3604df
+        print (json.dumps({
3604df
+            "output": json_out,
3604df
+            "error": ""
3604df
+        }))
3604df
+    else:
3604df
+        print (table)
3604df
 
3604df
-    print (table)
3604df
+    # If sync status is not ok for any node set error code as partial success
3604df
+    sys.exit(ret)
3604df
 
3604df
 
3604df
 def node_output_handle(resp):
3604df
@@ -148,29 +223,24 @@ def node_output_handle(resp):
3604df
         node_output_notok(err)
3604df
 
3604df
 
3604df
-def action_handle(action):
3604df
+def action_handle(action, json_output=False):
3604df
     out = execute_in_peers("node-" + action)
3604df
     column_name = action.upper()
3604df
     if action == "status":
3604df
         column_name = EVENTSD.upper()
3604df
 
3604df
-    table = PrettyTable(["NODE", "NODE STATUS", column_name + " STATUS"])
3604df
-    table.align["NODE STATUS"] = "r"
3604df
-    table.align[column_name + " STATUS"] = "r"
3604df
-
3604df
-    for p in out:
3604df
-        status_col_val = "OK" if p.ok else "NOT OK: {0}".format(
3604df
-            p.error)
3604df
-        if action == "status":
3604df
-            status_col_val = "DOWN"
3604df
-            if p.ok:
3604df
-                status_col_val = p.output
3604df
+    if not json_output:
3604df
+        table = PrettyTable(["NODE", "NODE STATUS", column_name + " STATUS"])
3604df
+        table.align["NODE STATUS"] = "r"
3604df
+        table.align[column_name + " STATUS"] = "r"
3604df
 
3604df
-        table.add_row([p.hostname,
3604df
-                       "UP" if p.node_up else "DOWN",
3604df
-                       status_col_val])
3604df
+    json_out = []
3604df
+    if json_output:
3604df
+        rows_to_json(json_out, column_name.lower() + "_status", out)
3604df
+    else:
3604df
+        rows_to_table(table, out)
3604df
 
3604df
-    print (table)
3604df
+    return json_out if json_output else table
3604df
 
3604df
 
3604df
 class NodeReload(Cmd):
3604df
@@ -184,7 +254,14 @@ class ReloadCmd(Cmd):
3604df
     name = "reload"
3604df
 
3604df
     def run(self, args):
3604df
-        action_handle("reload")
3604df
+        out = action_handle("reload", args.json)
3604df
+        if args.json:
3604df
+            print (json.dumps({
3604df
+                "output": out,
3604df
+                "error": ""
3604df
+            }))
3604df
+        else:
3604df
+            print (out)
3604df
 
3604df
 
3604df
 class NodeStatus(Cmd):
3604df
@@ -202,12 +279,25 @@ class StatusCmd(Cmd):
3604df
         if os.path.exists(WEBHOOKS_FILE):
3604df
             webhooks = json.load(open(WEBHOOKS_FILE))
3604df
 
3604df
-        print ("Webhooks: " + ("" if webhooks else "None"))
3604df
-        for w in webhooks:
3604df
-            print (w)
3604df
-
3604df
-        print ()
3604df
-        action_handle("status")
3604df
+        json_out = {"webhooks": [], "data": []}
3604df
+        if args.json:
3604df
+            json_out["webhooks"] = webhooks.keys()
3604df
+        else:
3604df
+            print ("Webhooks: " + ("" if webhooks else "None"))
3604df
+            for w in webhooks:
3604df
+                print (w)
3604df
+
3604df
+            print ()
3604df
+
3604df
+        out = action_handle("status", args.json)
3604df
+        if args.json:
3604df
+            json_out["data"] = out
3604df
+            print (json.dumps({
3604df
+                "output": json_out,
3604df
+                "error": ""
3604df
+            }))
3604df
+        else:
3604df
+            print (out)
3604df
 
3604df
 
3604df
 class WebhookAddCmd(Cmd):
3604df
@@ -219,17 +309,19 @@ class WebhookAddCmd(Cmd):
3604df
                             default="")
3604df
 
3604df
     def run(self, args):
3604df
-        create_webhooks_file_if_not_exists()
3604df
+        create_webhooks_file_if_not_exists(args)
3604df
 
3604df
         with LockedOpen(WEBHOOKS_FILE, 'r+'):
3604df
             data = json.load(open(WEBHOOKS_FILE))
3604df
             if data.get(args.url, None) is not None:
3604df
-                output_error("Webhook already exists")
3604df
+                handle_output_error("Webhook already exists",
3604df
+                                    errcode=ERROR_WEBHOOK_ALREADY_EXISTS,
3604df
+                                    json_output=args.json)
3604df
 
3604df
             data[args.url] = args.bearer_token
3604df
             file_content_overwrite(WEBHOOKS_FILE, data)
3604df
 
3604df
-        sync_to_peers()
3604df
+        sync_to_peers(args)
3604df
 
3604df
 
3604df
 class WebhookModCmd(Cmd):
3604df
@@ -241,17 +333,19 @@ class WebhookModCmd(Cmd):
3604df
                             default="")
3604df
 
3604df
     def run(self, args):
3604df
-        create_webhooks_file_if_not_exists()
3604df
+        create_webhooks_file_if_not_exists(args)
3604df
 
3604df
         with LockedOpen(WEBHOOKS_FILE, 'r+'):
3604df
             data = json.load(open(WEBHOOKS_FILE))
3604df
             if data.get(args.url, None) is None:
3604df
-                output_error("Webhook does not exists")
3604df
+                handle_output_error("Webhook does not exists",
3604df
+                                    errcode=ERROR_WEBHOOK_NOT_EXISTS,
3604df
+                                    json_output=args.json)
3604df
 
3604df
             data[args.url] = args.bearer_token
3604df
             file_content_overwrite(WEBHOOKS_FILE, data)
3604df
 
3604df
-        sync_to_peers()
3604df
+        sync_to_peers(args)
3604df
 
3604df
 
3604df
 class WebhookDelCmd(Cmd):
3604df
@@ -261,17 +355,19 @@ class WebhookDelCmd(Cmd):
3604df
         parser.add_argument("url", help="URL of Webhook")
3604df
 
3604df
     def run(self, args):
3604df
-        create_webhooks_file_if_not_exists()
3604df
+        create_webhooks_file_if_not_exists(args)
3604df
 
3604df
         with LockedOpen(WEBHOOKS_FILE, 'r+'):
3604df
             data = json.load(open(WEBHOOKS_FILE))
3604df
             if data.get(args.url, None) is None:
3604df
-                output_error("Webhook does not exists")
3604df
+                handle_output_error("Webhook does not exists",
3604df
+                                    errcode=ERROR_WEBHOOK_NOT_EXISTS,
3604df
+                                    json_output=args.json)
3604df
 
3604df
             del data[args.url]
3604df
             file_content_overwrite(WEBHOOKS_FILE, data)
3604df
 
3604df
-        sync_to_peers()
3604df
+        sync_to_peers(args)
3604df
 
3604df
 
3604df
 class NodeWebhookTestCmd(Cmd):
3604df
@@ -314,17 +410,33 @@ class WebhookTestCmd(Cmd):
3604df
 
3604df
         out = execute_in_peers("node-webhook-test", [url, bearer_token])
3604df
 
3604df
-        table = PrettyTable(["NODE", "NODE STATUS", "WEBHOOK STATUS"])
3604df
-        table.align["NODE STATUS"] = "r"
3604df
-        table.align["WEBHOOK STATUS"] = "r"
3604df
+        if not args.json:
3604df
+            table = PrettyTable(["NODE", "NODE STATUS", "WEBHOOK STATUS"])
3604df
+            table.align["NODE STATUS"] = "r"
3604df
+            table.align["WEBHOOK STATUS"] = "r"
3604df
 
3604df
-        for p in out:
3604df
-            table.add_row([p.hostname,
3604df
-                           "UP" if p.node_up else "DOWN",
3604df
-                           "OK" if p.ok else "NOT OK: {0}".format(
3604df
-                               p.error)])
3604df
+        num_ok_rows = 0
3604df
+        json_out = []
3604df
+        if args.json:
3604df
+            num_ok_rows = rows_to_json(json_out, "webhook_status", out)
3604df
+        else:
3604df
+            num_ok_rows = rows_to_table(table, out)
3604df
+
3604df
+        ret = 0
3604df
+        if num_ok_rows == 0:
3604df
+            ret = ERROR_ALL_NODES_STATUS_NOT_OK
3604df
+        elif num_ok_rows != len(out):
3604df
+            ret = ERROR_PARTIAL_SUCCESS
3604df
+
3604df
+        if args.json:
3604df
+            print (json.dumps({
3604df
+                "output": json_out,
3604df
+                "error": ""
3604df
+            }))
3604df
+        else:
3604df
+            print (table)
3604df
 
3604df
-        print (table)
3604df
+        sys.exit(ret)
3604df
 
3604df
 
3604df
 class ConfigGetCmd(Cmd):
3604df
@@ -339,16 +451,30 @@ class ConfigGetCmd(Cmd):
3604df
             data.update(json.load(open(CUSTOM_CONFIG_FILE)))
3604df
 
3604df
         if args.name is not None and args.name not in CONFIG_KEYS:
3604df
-            output_error("Invalid Config item")
3604df
+            handle_output_error("Invalid Config item",
3604df
+                                errcode=ERROR_INVALID_CONFIG,
3604df
+                                json_output=args.json)
3604df
+
3604df
+        if args.json:
3604df
+            json_out = {}
3604df
+            if args.name is None:
3604df
+                json_out = data
3604df
+            else:
3604df
+                json_out[args.name] = data[args.name]
3604df
 
3604df
-        table = PrettyTable(["NAME", "VALUE"])
3604df
-        if args.name is None:
3604df
-            for k, v in data.items():
3604df
-                table.add_row([k, v])
3604df
+            print (json.dumps({
3604df
+                "output": json_out,
3604df
+                "error": ""
3604df
+            }))
3604df
         else:
3604df
-            table.add_row([args.name, data[args.name]])
3604df
+            table = PrettyTable(["NAME", "VALUE"])
3604df
+            if args.name is None:
3604df
+                for k, v in data.items():
3604df
+                    table.add_row([k, v])
3604df
+            else:
3604df
+                table.add_row([args.name, data[args.name]])
3604df
 
3604df
-        print (table)
3604df
+            print (table)
3604df
 
3604df
 
3604df
 def read_file_content_json(fname):
3604df
@@ -370,9 +496,11 @@ class ConfigSetCmd(Cmd):
3604df
 
3604df
     def run(self, args):
3604df
         if args.name not in CONFIG_KEYS:
3604df
-            output_error("Invalid Config item")
3604df
+            handle_output_error("Invalid Config item",
3604df
+                                errcode=ERROR_INVALID_CONFIG,
3604df
+                                json_output=args.json)
3604df
 
3604df
-        create_custom_config_file_if_not_exists()
3604df
+        create_custom_config_file_if_not_exists(args)
3604df
 
3604df
         with LockedOpen(CUSTOM_CONFIG_FILE, 'r+'):
3604df
             data = json.load(open(DEFAULT_CONFIG_FILE))
3604df
@@ -382,7 +510,9 @@ class ConfigSetCmd(Cmd):
3604df
 
3604df
             # Do Nothing if same as previous value
3604df
             if data[args.name] == args.value:
3604df
-                return
3604df
+                handle_output_error("Config value not changed. Same config",
3604df
+                                    errcode=ERROR_SAME_CONFIG,
3604df
+                                    json_output=args.json)
3604df
 
3604df
             # TODO: Validate Value
3604df
             new_data = read_file_content_json(CUSTOM_CONFIG_FILE)
3604df
@@ -402,10 +532,11 @@ class ConfigSetCmd(Cmd):
3604df
             if args.name in RESTART_CONFIGS:
3604df
                 restart = True
3604df
 
3604df
-            sync_to_peers()
3604df
             if restart:
3604df
                 print ("\nRestart glustereventsd in all nodes")
3604df
 
3604df
+            sync_to_peers(args)
3604df
+
3604df
 
3604df
 class ConfigResetCmd(Cmd):
3604df
     name = "config-reset"
3604df
@@ -414,7 +545,7 @@ class ConfigResetCmd(Cmd):
3604df
         parser.add_argument("name", help="Config Name or all")
3604df
 
3604df
     def run(self, args):
3604df
-        create_custom_config_file_if_not_exists()
3604df
+        create_custom_config_file_if_not_exists(args)
3604df
 
3604df
         with LockedOpen(CUSTOM_CONFIG_FILE, 'r+'):
3604df
             changed_keys = []
3604df
@@ -422,8 +553,14 @@ class ConfigResetCmd(Cmd):
3604df
             if os.path.exists(CUSTOM_CONFIG_FILE):
3604df
                 data = read_file_content_json(CUSTOM_CONFIG_FILE)
3604df
 
3604df
-            if not data:
3604df
-                return
3604df
+            # If No data available in custom config or, the specific config
3604df
+            # item is not available in custom config
3604df
+            if not data or \
3604df
+               (args.name != "all" and data.get(args.name, None) is None):
3604df
+                handle_output_error("Config value not reset. Already "
3604df
+                                    "set to default value",
3604df
+                                    errcode=ERROR_SAME_CONFIG,
3604df
+                                    json_output=args.json)
3604df
 
3604df
             if args.name.lower() == "all":
3604df
                 for k, v in data.items():
3604df
@@ -443,17 +580,23 @@ class ConfigResetCmd(Cmd):
3604df
                     restart = True
3604df
                     break
3604df
 
3604df
-            sync_to_peers()
3604df
             if restart:
3604df
                 print ("\nRestart glustereventsd in all nodes")
3604df
 
3604df
+            sync_to_peers(args)
3604df
+
3604df
 
3604df
 class SyncCmd(Cmd):
3604df
     name = "sync"
3604df
 
3604df
     def run(self, args):
3604df
-        sync_to_peers()
3604df
+        sync_to_peers(args)
3604df
+
3604df
+
3604df
+def common_args(parser):
3604df
+    parser.add_argument("--json", help="JSON Output", action="store_true")
3604df
 
3604df
 
3604df
 if __name__ == "__main__":
3604df
+    set_common_args_func(common_args)
3604df
     runcli()
3604df
diff --git a/extras/cliutils/__init__.py b/extras/cliutils/__init__.py
3604df
index 4bb8395..9c93098 100644
3604df
--- a/extras/cliutils/__init__.py
3604df
+++ b/extras/cliutils/__init__.py
3604df
@@ -11,7 +11,8 @@ from cliutils import (runcli,
3604df
                       yesno,
3604df
                       get_node_uuid,
3604df
                       Cmd,
3604df
-                      GlusterCmdException)
3604df
+                      GlusterCmdException,
3604df
+                      set_common_args_func)
3604df
 
3604df
 
3604df
 # This will be useful when `from cliutils import *`
3604df
@@ -26,4 +27,5 @@ __all__ = ["runcli",
3604df
            "yesno",
3604df
            "get_node_uuid",
3604df
            "Cmd",
3604df
-           "GlusterCmdException"]
3604df
+           "GlusterCmdException",
3604df
+           "set_common_args_func"]
3604df
diff --git a/extras/cliutils/cliutils.py b/extras/cliutils/cliutils.py
3604df
index 4e035d7..d805ac6 100644
3604df
--- a/extras/cliutils/cliutils.py
3604df
+++ b/extras/cliutils/cliutils.py
3604df
@@ -16,6 +16,7 @@ subparsers = parser.add_subparsers(dest="mode")
3604df
 subcommands = {}
3604df
 cache_data = {}
3604df
 ParseError = etree.ParseError if hasattr(etree, 'ParseError') else SyntaxError
3604df
+_common_args_func = lambda p: True
3604df
 
3604df
 
3604df
 class GlusterCmdException(Exception):
3604df
@@ -50,9 +51,9 @@ def oknotok(flag):
3604df
     return "OK" if flag else "NOT OK"
3604df
 
3604df
 
3604df
-def output_error(message):
3604df
+def output_error(message, errcode=1):
3604df
     print (message, file=sys.stderr)
3604df
-    sys.exit(1)
3604df
+    sys.exit(errcode)
3604df
 
3604df
 
3604df
 def node_output_ok(message=""):
3604df
@@ -186,6 +187,7 @@ def runcli():
3604df
     # a subcommand as specified in the Class name. Call the args
3604df
     # method by passing subcommand parser, Derived class can add
3604df
     # arguments to the subcommand parser.
3604df
+    metavar_data = []
3604df
     for c in Cmd.__subclasses__():
3604df
         cls = c()
3604df
         if getattr(cls, "name", "") == "":
3604df
@@ -193,14 +195,24 @@ def runcli():
3604df
                                       "to \"{0}\"".format(
3604df
                                           cls.__class__.__name__))
3604df
 
3604df
+        # Do not show in help message if subcommand starts with node-
3604df
+        if not cls.name.startswith("node-"):
3604df
+            metavar_data.append(cls.name)
3604df
+
3604df
         p = subparsers.add_parser(cls.name)
3604df
         args_func = getattr(cls, "args", None)
3604df
         if args_func is not None:
3604df
             args_func(p)
3604df
 
3604df
+        # Apply common args if any
3604df
+        _common_args_func(p)
3604df
+
3604df
         # A dict to save subcommands, key is name of the subcommand
3604df
         subcommands[cls.name] = cls
3604df
 
3604df
+    # Hide node commands in Help message
3604df
+    subparsers.metavar = "{" + ",".join(metavar_data) + "}"
3604df
+
3604df
     # Get all parsed arguments
3604df
     args = parser.parse_args()
3604df
 
3604df
@@ -210,3 +222,8 @@ def runcli():
3604df
     # Run
3604df
     if cls is not None:
3604df
         cls.run(args)
3604df
+
3604df
+
3604df
+def set_common_args_func(func):
3604df
+    global _common_args_func
3604df
+    _common_args_func = func
3604df
-- 
3604df
2.9.3
3604df