#!/usr/libexec/platform-python
from __future__ import print_function
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.items():
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):
vols = set()
for k, v in self._options.items():
if k.startswith("mountbroker-geo-replication.") \
and user == k.split(".")[-1]:
vols.update(v.split(","))
vols.update(volumes)
self.set_opt("mountbroker-geo-replication.%s" % user,
",".join(vols))
def remove_volume(self, user, volumes):
vols = set()
for k, v in self._options.items():
if k.startswith("mountbroker-geo-replication.") \
and user == k.split(".")[-1]:
vols.update(v.split(","))
for v1 in volumes:
vols.discard(v1)
if vols:
self.set_opt("mountbroker-geo-replication.%s" % user,
",".join(vols))
else:
self.remove_opt("mountbroker-geo-replication.%s" % user)
def remove_user(self, user):
self.remove_opt("mountbroker-geo-replication.%s" % user)
def info(self):
data = {"users": []}
for k, v in self._options.items():
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.items():
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')
parser_volumedel = subparsers.add_parser('volumedel')
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. ',' separated")
parser_volumedel.add_argument('username', help="Username", type=str)
parser_volumedel.add_argument('volumes', type=str, default='',
help="Volumes list. ',' separated")
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 == "volumedel":
volumes = [v.strip() for v in args.volumes.split(",")
if v.strip() != ""]
m.remove_volume(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()