From 79009691c01f2b32b523d91a159aadd0e414f31b Mon Sep 17 00:00:00 2001 From: Aravinda VK Date: Tue, 6 Jan 2015 18:20:45 +0530 Subject: geo-rep: mountbroker user management Non root geo-replication setup is now simplified. This patch provides cli for mountbroker user and options management To set Options, gluster system:: execute mountbroker opt # for example, gluster system:: execute mountbroker opt mountbroker-root /var/mountbroker-root gluster system:: execute mountbroker opt geo-replication-log-group geogroup gluster system:: execute mountbroker opt rpc-auth-allow-insecure on To remove option, gluster system:: execute mountbroker optdel # for example, gluster system:: execute mountbroker optdel geo-replication-log-group To add/edit user, gluster system:: execute mountbroker user # for example gluster system:: execute mountbroker user geoaccount slavevol1,slavevol2 To remove user, gluster system:: execute mountbroker userdel # for example gluster system:: execute mountbroker userdel geoaccount For info, gluster system:: execute mountbroker info gluster system:: execute mountbroker -j info For JSON output add -j after mountbroker, for example, gluster system:: execute mountbroker -j user geoaccount slavevol1,slavevol2 PS: Each peer prints its own JSON output, aggregator required from consumer side BUG: 1136312 Change-Id: Ie52210c0bcc91ac2ffd3ba58988222ffca62b47f Signed-off-by: Aravinda VK Reviewed-on: http://review.gluster.org/9398 Tested-by: Gluster Build System Reviewed-by: darshan n Reviewed-by: Kotresh HR Reviewed-by: Vijay Bellur --- .gitignore | 1 + configure.ac | 10 ++ geo-replication/src/Makefile.am | 3 +- geo-replication/src/peer_mountbroker | 177 ++++++++++++++++++++++++++++++++ geo-replication/src/peer_mountbroker.in | 177 ++++++++++++++++++++++++++++++++ glusterfs.spec.in | 4 + 6 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 geo-replication/src/peer_mountbroker create mode 100644 geo-replication/src/peer_mountbroker.in diff --git a/.gitignore b/.gitignore index 977f99af0fd..acead3677ae 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ geo-replication/src/gsyncd geo-replication/src/gsyncd geo-replication/src/peer_add_secret_pub geo-replication/src/peer_gsec_create +geo-replication/src/peer_mountbroker geo-replication/src/set_geo_rep_pem_keys.sh geo-replication/syncdaemon.egg-info geo-replication/syncdaemon/configinterface.py diff --git a/configure.ac b/configure.ac index fbb6e4a073f..f4de4462aec 100644 --- a/configure.ac +++ b/configure.ac @@ -40,6 +40,7 @@ AC_CONFIG_FILES([Makefile libglusterfs/src/Makefile geo-replication/src/peer_gsec_create geo-replication/src/peer_add_secret_pub + geo-replication/src/peer_mountbroker geo-replication/syncdaemon/configinterface.py glusterfsd/Makefile glusterfsd/src/Makefile @@ -887,6 +888,14 @@ else LOCALSTATEDIR=$(eval echo ${localstatedir}) fi +old_prefix=$prefix +if test "x$prefix" = xNONE; then + prefix=$ac_default_prefix +fi +GLUSTERD_VOLFILE="$(eval echo ${sysconfdir})/glusterfs/glusterd.vol" +prefix=$old_prefix + + case $host_os in linux*) GF_HOST_OS="GF_LINUX_HOST_OS" @@ -1175,6 +1184,7 @@ AM_CONDITIONAL([GF_BSD_HOST_OS], test "${GF_HOST_OS}" = "GF_BSD_HOST_OS") AC_SUBST(GLUSTERD_WORKDIR) AM_CONDITIONAL([GF_INSTALL_GLUSTERD_WORKDIR], test ! -d ${GLUSTERD_WORKDIR} && test -d ${sysconfdir}/glusterd ) +AC_SUBST(GLUSTERD_VOLFILE) dnl pkg-config versioning dnl diff --git a/geo-replication/src/Makefile.am b/geo-replication/src/Makefile.am index 68b18c66e88..512128dfd2d 100644 --- a/geo-replication/src/Makefile.am +++ b/geo-replication/src/Makefile.am @@ -1,6 +1,7 @@ gsyncddir = $(libexecdir)/glusterfs -gsyncd_SCRIPTS = gverify.sh peer_add_secret_pub peer_gsec_create set_geo_rep_pem_keys.sh +gsyncd_SCRIPTS = gverify.sh peer_add_secret_pub peer_gsec_create \ + set_geo_rep_pem_keys.sh peer_mountbroker # peer_gsec_create and peer_add_secret_pub are not added to # EXTRA_DIST as it's derived from a .in file diff --git a/geo-replication/src/peer_mountbroker b/geo-replication/src/peer_mountbroker new file mode 100644 index 00000000000..4d34e7c8e2a --- /dev/null +++ b/geo-replication/src/peer_mountbroker @@ -0,0 +1,177 @@ +#!/usr/bin/env python +import os +from argparse import ArgumentParser, RawDescriptionHelpFormatter +import json +import sys + + +PROG_DESCRIPTION = """ +GlusterFS Mountbroker user management +""" + +args = None + + +def ok(message=""): + if (not args and "-j" in sys.argv) or (args and args.json): + print json.dumps({"ok": True, "message": message}) + else: + if message: + print message + + sys.exit(0) + + +def notok(message=""): + if (not args and "-j" in sys.argv) or (args and args.json): + print json.dumps({"ok": False, "message": message}) + else: + print "error: %s" % message + + # Always return zero due to limitation while executing + # as `gluster system:: execute` + sys.exit(0) + + +class NoStdErrParser(ArgumentParser): + """ + with gluster system:: execute, stderr gives + "Unable to end. Error : Bad file descriptor" error, + so deriving new class, prints error message and + exits with zero. + """ + def error(self, message): + notok(message) + + +class MountbrokerUserMgmt(object): + def __init__(self, volfile): + self.volfile = volfile + self._options = {} + self.commented_lines = [] + self._parse() + + def _parse(self): + with open(self.volfile, "r") as f: + for line in f: + line = line.strip() + if line.startswith("option "): + key, value = line.split(" ")[1:] + self._options[key] = value + if line.startswith("#"): + self.commented_lines.append(line) + + def _get_write_data(self): + op = "volume management\n" + op += " type mgmt/glusterd\n" + for k, v in self._options.iteritems(): + op += " option %s %s\n" % (k, v) + for line in self.commented_lines: + op += " %s\n" % line + op += "end-volume" + return op + + def save(self): + with open(self.volfile + "_tmp", "w") as f: + f.write(self._get_write_data()) + f.flush() + os.fsync(f.fileno()) + os.rename(self.volfile + "_tmp", self.volfile) + + def set_opt(self, key, value): + self._options[key] = value.strip() + + def remove_opt(self, key): + if key in self._options: + del(self._options[key]) + + def add_user(self, user, volumes): + self.set_opt("mountbroker-geo-replication.%s" % user, + ",".join(volumes)) + + def remove_user(self, user): + self.remove_opt("mountbroker-geo-replication.%s" % user) + + def info(self): + data = {"users": []} + + for k, v in self._options.iteritems(): + if k.startswith("mountbroker-geo-replication."): + data["users"].append( + {"name": k.split(".")[-1], "volumes": v.split(",")} + ) + else: + data[k] = v + + return data + + +def format_info(data): + op = "%s %s\n" % ("Option".ljust(50), "Value".ljust(50)) + op += ("-" * 101) + "\n" + for key, value in data.iteritems(): + if key != "users": + op += "%s %s\n" % (key.ljust(50), value) + + op += "\nUsers: %s\n" % ("None" if not data["users"] else "") + for user in data["users"]: + op += "%s: %s\n" % (user["name"], ", ".join(user["volumes"])) + op += "\n\n" + return op + + +def _get_args(): + parser = NoStdErrParser(formatter_class=RawDescriptionHelpFormatter, + description=PROG_DESCRIPTION) + + parser.add_argument('-j', dest="json", help="JSON output", + action="store_true") + subparsers = parser.add_subparsers(title='subcommands', dest='cmd') + parser_useradd = subparsers.add_parser('user') + parser_userdel = subparsers.add_parser('userdel') + subparsers.add_parser('info') + parser_opt = subparsers.add_parser('opt') + parser_optdel = subparsers.add_parser('optdel') + + parser_useradd.add_argument('username', help="Username", type=str) + parser_useradd.add_argument('volumes', type=str, default='', + help="Volumes list. ',' seperated") + + parser_userdel.add_argument('username', help="Username", type=str) + + parser_opt.add_argument('opt_name', help="Name", type=str) + parser_opt.add_argument('opt_value', help="Value", type=str) + + parser_optdel.add_argument('opt_name', help="Name", type=str) + + return parser.parse_args() + + +def main(): + global args + args = _get_args() + + m = MountbrokerUserMgmt("/etc/glusterfs/glusterd.vol") + + if args.cmd == "opt": + m.set_opt(args.opt_name, args.opt_value) + elif args.cmd == "optdel": + m.remove_opt(args.opt_name) + elif args.cmd == "userdel": + m.remove_user(args.username) + elif args.cmd == "user": + volumes = [v.strip() for v in args.volumes.split(",") + if v.strip() != ""] + m.add_user(args.username, volumes) + elif args.cmd == "info": + info = m.info() + if not args.json: + info = format_info(info) + ok(info) + + if args.cmd != "info": + m.save() + ok() + +if __name__ == "__main__": + main() diff --git a/geo-replication/src/peer_mountbroker.in b/geo-replication/src/peer_mountbroker.in new file mode 100644 index 00000000000..4c97c6923c4 --- /dev/null +++ b/geo-replication/src/peer_mountbroker.in @@ -0,0 +1,177 @@ +#!/usr/bin/env python +import os +from argparse import ArgumentParser, RawDescriptionHelpFormatter +import json +import sys + + +PROG_DESCRIPTION = """ +GlusterFS Mountbroker user management +""" + +args = None + + +def ok(message=""): + if (not args and "-j" in sys.argv) or (args and args.json): + print json.dumps({"ok": True, "message": message}) + else: + if message: + print message + + sys.exit(0) + + +def notok(message=""): + if (not args and "-j" in sys.argv) or (args and args.json): + print json.dumps({"ok": False, "message": message}) + else: + print "error: %s" % message + + # Always return zero due to limitation while executing + # as `gluster system:: execute` + sys.exit(0) + + +class NoStdErrParser(ArgumentParser): + """ + with gluster system:: execute, stderr gives + "Unable to end. Error : Bad file descriptor" error, + so deriving new class, prints error message and + exits with zero. + """ + def error(self, message): + notok(message) + + +class MountbrokerUserMgmt(object): + def __init__(self, volfile): + self.volfile = volfile + self._options = {} + self.commented_lines = [] + self._parse() + + def _parse(self): + with open(self.volfile, "r") as f: + for line in f: + line = line.strip() + if line.startswith("option "): + key, value = line.split(" ")[1:] + self._options[key] = value + if line.startswith("#"): + self.commented_lines.append(line) + + def _get_write_data(self): + op = "volume management\n" + op += " type mgmt/glusterd\n" + for k, v in self._options.iteritems(): + op += " option %s %s\n" % (k, v) + for line in self.commented_lines: + op += " %s\n" % line + op += "end-volume" + return op + + def save(self): + with open(self.volfile + "_tmp", "w") as f: + f.write(self._get_write_data()) + f.flush() + os.fsync(f.fileno()) + os.rename(self.volfile + "_tmp", self.volfile) + + def set_opt(self, key, value): + self._options[key] = value.strip() + + def remove_opt(self, key): + if key in self._options: + del(self._options[key]) + + def add_user(self, user, volumes): + self.set_opt("mountbroker-geo-replication.%s" % user, + ",".join(volumes)) + + def remove_user(self, user): + self.remove_opt("mountbroker-geo-replication.%s" % user) + + def info(self): + data = {"users": []} + + for k, v in self._options.iteritems(): + if k.startswith("mountbroker-geo-replication."): + data["users"].append( + {"name": k.split(".")[-1], "volumes": v.split(",")} + ) + else: + data[k] = v + + return data + + +def format_info(data): + op = "%s %s\n" % ("Option".ljust(50), "Value".ljust(50)) + op += ("-" * 101) + "\n" + for key, value in data.iteritems(): + if key != "users": + op += "%s %s\n" % (key.ljust(50), value) + + op += "\nUsers: %s\n" % ("None" if not data["users"] else "") + for user in data["users"]: + op += "%s: %s\n" % (user["name"], ", ".join(user["volumes"])) + op += "\n\n" + return op + + +def _get_args(): + parser = NoStdErrParser(formatter_class=RawDescriptionHelpFormatter, + description=PROG_DESCRIPTION) + + parser.add_argument('-j', dest="json", help="JSON output", + action="store_true") + subparsers = parser.add_subparsers(title='subcommands', dest='cmd') + parser_useradd = subparsers.add_parser('user') + parser_userdel = subparsers.add_parser('userdel') + subparsers.add_parser('info') + parser_opt = subparsers.add_parser('opt') + parser_optdel = subparsers.add_parser('optdel') + + parser_useradd.add_argument('username', help="Username", type=str) + parser_useradd.add_argument('volumes', type=str, default='', + help="Volumes list. ',' seperated") + + parser_userdel.add_argument('username', help="Username", type=str) + + parser_opt.add_argument('opt_name', help="Name", type=str) + parser_opt.add_argument('opt_value', help="Value", type=str) + + parser_optdel.add_argument('opt_name', help="Name", type=str) + + return parser.parse_args() + + +def main(): + global args + args = _get_args() + + m = MountbrokerUserMgmt("@GLUSTERD_VOLFILE@") + + if args.cmd == "opt": + m.set_opt(args.opt_name, args.opt_value) + elif args.cmd == "optdel": + m.remove_opt(args.opt_name) + elif args.cmd == "userdel": + m.remove_user(args.username) + elif args.cmd == "user": + volumes = [v.strip() for v in args.volumes.split(",") + if v.strip() != ""] + m.add_user(args.username, volumes) + elif args.cmd == "info": + info = m.info() + if not args.json: + info = format_info(info) + ok(info) + + if args.cmd != "info": + m.save() + ok() + +if __name__ == "__main__": + main() diff --git a/glusterfs.spec.in b/glusterfs.spec.in index affb753cb67..ef8326f4cbe 100644 --- a/glusterfs.spec.in +++ b/glusterfs.spec.in @@ -943,6 +943,7 @@ fi %{_libexecdir}/glusterfs/set_geo_rep_pem_keys.sh %{_libexecdir}/glusterfs/peer_add_secret_pub %{_libexecdir}/glusterfs/peer_gsec_create +%{_libexecdir}/glusterfs/peer_mountbroker %ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/geo-replication %dir %{_sharedstatedir}/glusterd/hooks %dir %{_sharedstatedir}/glusterd/hooks/1 @@ -1078,6 +1079,9 @@ fi * Fri Jan 16 2015 Niels de Vos - add support for /run/gluster through a tmpfiles.d config file (#1182934) +* Tue Jan 6 2015 Aravinda VK +- Added new libexec script for mountbroker user management (peer_mountbroker) + * Fri Dec 12 2014 Niels de Vos - do not package all /usr/share/glusterfs/* files in regression-tests (#1169005) -- cgit