def main(): parser = argparse.ArgumentParser() parser.add_argument('--username', help="User's username", required=True) parser.add_argument('--new-group', help="New group to assign", required=True) parser.add_argument('--old-group', help="Old group", required=True) parser.add_argument('--config-env', help="config environment", dest="config_env", default="production") parser.add_argument('--debug', help="set debug level (0-4)", dest="debug", nargs="?", const=0, type=int) args = parser.parse_args() options = vars(args) config = load_config() config_env = config[args.config_env]["ldap"] _account_home_config = config[args.config_env].get("account_home") _auth_token = config[args.config_env].get("api_auth_token") _host = config[args.config_env].get("host") _port = config[args.config_env].get("port") _https = config[args.config_env].get("https") _protocol = 'https' if _https else 'http' _url = "%s://%s:%s/" % ( _protocol, _host, _port) if _port else "%s://%s/" % (_protocol, _host) _json_headers = { "Accept": "application/json", "Content-Type": "application/json", "Authorization": "Token token=%s" % _auth_token, } # Setup logging setup_logging(debug=args.debug, noop=False) logger.debug4("OPTIONS: %s" % options) logger.debug4("CONFIG: %s" % config_env) _ldap_url = config_env.get("url") _use_tls = config_env.get("tls") _bind_dn = config_env.get("bind_dn", None) _bind_pass = config_env.get("bind_pass", None) group_search_base = "ou=Groups,dc=brazos,dc=tamu,dc=edu" user_search_base = "ou=People,dc=brazos,dc=tamu,dc=edu" new_group_filter = "cn=%s" % args.new_group old_group_filter = "cn=%s" % args.old_group user_filter = "uid=%s" % args.username group_attribs = [ "dn", "cn", "gidNumber", "uniqueMember", "slurmAccountName", ] user_attribs = [ "dn", "uid", "gidNumber", ] scope = "one" local_ldap = LocalLdap(url=_ldap_url[0], use_tls=_use_tls, bind_dn=_bind_dn, bind_pass=_bind_pass, log_level=None) new_group_results = local_ldap.paged_search(base=group_search_base, sfilter=new_group_filter, attrlist=group_attribs, scope=scope) old_group_results = local_ldap.paged_search(base=group_search_base, sfilter=old_group_filter, attrlist=group_attribs, scope=scope) user_results = local_ldap.paged_search(base=user_search_base, sfilter=user_filter, attrlist=user_attribs, scope=scope) logger.debug("LDAP new group: %s", json.dumps(new_group_results)) logger.debug("LDAP old group: %s", json.dumps(old_group_results)) logger.debug("LDAP user: %s", json.dumps(user_results)) if len(new_group_results) != 1 or len(old_group_results) != 1: logger.error("Incorrect number of LDAP group results returned") sys.exit(1) if len(user_results) != 1: logger.error("Incorrect number of LDAP user results returned") sys.exit(1) ldap_group_new = LdapGroup() ldap_group_new.setattrs(data=new_group_results[0], listvals=["uniqueMember"]) ldap_group_old = LdapGroup() ldap_group_old.setattrs(data=old_group_results[0], listvals=["uniqueMember"]) ldap_user = LdapUser() ldap_user.setattrs(data=user_results[0]) # Not all LDAP groups have slurmAccountName attribute if not hasattr(ldap_group_new, 'slurmAccountName'): ldap_group_new.slurmAccountName = ldap_group_new.cn if not hasattr(ldap_group_old, 'slurmAccountName'): ldap_group_old.slurmAccountName = ldap_group_old.cn # Check certain things exist to avoid sending None to LDAP which could delete more than we want _ldap_group_new_valid = True _ldap_group_old_valid = True for a in ['dn', 'gidNumber', 'slurmAccountName', 'uniqueMember', 'cn']: if not hasattr(ldap_group_new, a): _ldap_group_new_valid = False elif getattr(ldap_group_new, a) is None: _ldap_group_new_valid = False if not hasattr(ldap_group_old, a): _ldap_group_old_valid = False elif getattr(ldap_group_old, a) is None: _ldap_group_old_valid = False if not _ldap_group_new_valid: logger.error("LDAP group %s does not have all necessary information", args.new_group) if not _ldap_group_old_valid: logger.error("LDAP group %s does not have all necessary information", args.old_group) if ldap_user.dn is None or ldap_user.uid is None or ldap_user.gidNumber is None: logger.error("LDAP user %s does not have all necessary information", args.username) ## Update account management database get_old_group_params = { "name": args.old_group, } old_group_data = actmgr_api.get_groups(_url, _json_headers, get_old_group_params) old_group = old_group_data[0] logger.debug("Old Group API data: %s", json.dumps(old_group)) get_new_group_params = { "name": args.new_group, } new_group_data = actmgr_api.get_groups(_url, _json_headers, get_new_group_params) new_group = new_group_data[0] logger.debug("New Group API data: %s", json.dumps(new_group)) get_account_params = { "username": args.username, } account_data = actmgr_api.get_accounts(_url, _json_headers, get_account_params) account = account_data[0] logger.debug("Account API data: %s", json.dumps(account)) update_account_data = { "primary_group_id": new_group['id'], } # Handle cases where person is assigned old group in both primary and auxiliary groups group_ids = [] for g in account["groups"]: if g["id"] == old_group["id"]: group_ids.append(new_group["id"]) else: group_ids.append(g["id"]) if group_ids: update_account_data['group_ids'] = group_ids account = actmgr_api.update_account(_url, _json_headers, account['id'], update_account_data) if not account or not account["account"]: logger.error("Failed to update account management data") sys.exit(1) logger.debug("Account updated API data: %s", json.dumps(account)) account = account["account"] ## Update LDAP if ldap_user.gidNumber != ldap_group_new.gidNumber: logger.info("LDAP replace %s gidNumber=%s", ldap_user.dn, ldap_group_new.gidNumber) local_ldap.modify( ldap_user.dn, [(ldap.MOD_REPLACE, 'gidNumber', ldap_group_new.gidNumber)]) else: logger.warn("Skipping LDAP update of user gidNumber - already updated") if ldap_user.dn not in ldap_group_new.uniqueMember: logger.info("LDAP add to %s uniqueMember=%s", ldap_group_new.dn, ldap_user.dn) local_ldap.modify(ldap_group_new.dn, [(ldap.MOD_ADD, "uniqueMember", ldap_user.dn)]) else: logger.warn( "Skipping LDAP update of group add uniqueMember - already updated") if ldap_user.dn in ldap_group_old.uniqueMember: logger.info("LDAP delete from %s uniqueMember=%s", ldap_group_old.dn, ldap_user.dn) local_ldap.modify(ldap_group_old.dn, [(ldap.MOD_DELETE, "uniqueMember", ldap_user.dn)]) else: logger.warn( "Skipping LDAP update of group delete uniqueMember - already updated" ) ## Update SLURM _slurm_account = account["primary_group"]["alias"] _slurm_accounts = [g["alias"] for g in account["groups"] if "alias" in g] if _slurm_account not in _slurm_accounts: _slurm_accounts.append(_slurm_account) if not _slurm_account or not _slurm_accounts: logger.error("SLURM accounts not correctly determined") sys.exit(1) sacctmgr_check_args = [ "--parsable2", "--noheader", "show", "user", "name=%s" % args.username, "account=%s" % _slurm_account, "format=User,DefaultAccount,Account", "WithAssoc", ] logger.debug("Executing: sacctmgr %s", " ".join(sacctmgr_check_args)) try: output = sacctmgr(sacctmgr_check_args) except ErrorReturnCode: logger.error("FAILED to check if SLURM account already exists.") sys.exit(1) expected_output = "%s|%s|%s" % (args.username, _slurm_account, _slurm_account) existing_slurm_accounts = output.split(os.linesep) if expected_output not in existing_slurm_accounts: sacctmgr_delete_args = [ "-i", "delete", "user", "where", "name=%s" % args.username, "account=%s" % ldap_group_old.slurmAccountName ] logger.debug("Executing: sacctmgr %s", " ".join(sacctmgr_delete_args)) try: output = sacctmgr(sacctmgr_delete_args) except ErrorReturnCode: logger.error("FAILED to delete user from SLURM.") sys.exit(1) sacctmgr_create_args = [ "-i", "create", "user", args.username, "account=%s" % ",".join(_slurm_accounts), "defaultaccount=%s" % _slurm_account, ] logger.debug("Executing: sacctmgr %s", " ".join(sacctmgr_create_args)) try: output = sacctmgr(sacctmgr_create_args) except ErrorReturnCode: logger.error("FAILED to retrieve all user names from SLURM.") sys.exit(1) else: logger.warn( "Skipping SLURM account modifications - record already exists") ## Update permissions of $HOME and $SCRATCH home_path = os.path.join(_account_home_config.get("base_dir"), args.username) scratch_path = os.path.join(_account_home_config.get("scratch_base"), args.username) find_home_args = [ home_path, "-group", args.old_group, "-exec", "chgrp", args.new_group, '{}', ';' ] logger.info("Changing group ownership of files under %s", home_path) logger.debug("Executing: find %s", " ".join(find_home_args)) try: find(find_home_args) except ErrorReturnCode, e: logger.error("Failed to fix permissions of %s: %s", home_path, e.stderr) sys.exit(1)
def main(): args = parse_args() config = load_config() config_env = config[args.config_env]["ldap"] # Setup logging setup_logging(debug=args.debug, noop=False) logger.debug4("OPTIONS: %s" % vars(args)) logger.debug4("CONFIG: %s" % config_env) _ldap_url = config_env.get("url") _use_tls = config_env.get("tls") _bind_dn = config_env.get("bind_dn", None) _bind_pass = config_env.get("bind_pass", None) local_ldap = LocalLdap(url=_ldap_url[0], use_tls=_use_tls, bind_dn=_bind_dn, bind_pass=_bind_pass, log_level=None) ldap_users = local_ldap.paged_search(base=search_base, sfilter=search_filter, attrlist=search_return_attribs, scope=search_scope) users_over_quota = [] users_over_ldap_quota = [] users_over_zfs_quota = [] users_ldap_quota_mismatch = [] zfs_set_cmds = [] for user in ldap_users: _user_data = {} _user = LdapUser() _user.setattrs(data=user, listvals=["mail"]) _username = _user.uid _uid = _user.uidNumber _shell = _user.loginShell _quota = _user.quota if hasattr(_user, "mail"): _mail = ",".join(_user.mail) else: _mail = "" if active_only and _shell != "/bin/bash": continue mount, softlimit, hardlimit, softinode, hardinode = re.findall(r"^(.*):([0-9]+),([0-9]+),([0-9]+),([0-9]+)$", _quota)[0] _ldap_quota = int(hardlimit) * 1024 zfs_fs = "tank%s" % mount # Get current ZFS quota userquota_args = ["get", "-H", "-p", "-o", "value", "userquota@%s" % _username, zfs_fs] logger.debug("Executing: zfs %s", " ".join(userquota_args)) userquota_output = zfs(userquota_args) _userquota = userquota_output.strip() if _userquota != "-": current_quota = int(_userquota) else: current_quota = 0 # Get current used space userused_args = ["get", "-H", "-p", "-o", "value", "userused@%s" % _username, zfs_fs] logger.debug("Executing: zfs %s", " ".join(userused_args)) userused_output = zfs(userused_args) _userused = userused_output.strip() if _userused != "-": current_used = int(_userused) else: current_used = 0 _user_data["username"] = _username _user_data["uid"] = _uid _user_data["mail"] = _mail _user_data["zfs_fs"] = zfs_fs _user_data["ldap_quota"] = _ldap_quota _user_data["zfs_quota"] = current_quota _user_data["zfs_used"] = current_used if current_used >= _ldap_quota and current_used >= current_quota: users_over_quota.append(_user_data) elif current_used and current_used >= _ldap_quota: users_over_ldap_quota.append(_user_data) elif current_used and current_used >= current_quota: users_over_zfs_quota.append(_user_data) if _ldap_quota != current_quota: users_ldap_quota_mismatch.append(_user_data) zfs_set_cmd = [ "set", "userquota@%s=%s" % (_username, _ldap_quota), zfs_fs ] zfs_set_cmds.append(zfs_set_cmd) for user in users_over_quota: print_data("WARNING: over quota", user) print "---------" for user in users_over_ldap_quota: print_data("WARNING: over LDAP quota", user) print "---------" for user in users_over_zfs_quota: print_data("WARNING: over ZFS quota", user) print "---------" for user in users_ldap_quota_mismatch: print_data("WARNING: quota does not match LDAP", user) print "---------" for zfs_set_cmd in zfs_set_cmds: logger.debug("Executing: zfs %s", " ".join(zfs_set_cmd)) if args.noop: pass else: try: zfs_set_output = zfs(zfs_set_cmd) except ErrorReturnCode: logger.error("FAILED to execute zfs set: %s", zfs_set_output)
def main(): parser = argparse.ArgumentParser() parser.add_argument('--username', help="User's username", required=True) parser.add_argument('--new-group', help="New group to assign", required=True) parser.add_argument('--old-group', help="Old group", required=True) parser.add_argument('--config-env', help="config environment", dest="config_env", default="production") parser.add_argument('--debug', help="set debug level (0-4)", dest="debug", nargs="?", const=0, type=int) args = parser.parse_args() options = vars(args) config = load_config() config_env = config[args.config_env]["ldap"] _account_home_config = config[args.config_env].get("account_home") _auth_token = config[args.config_env].get("api_auth_token") _host = config[args.config_env].get("host") _port = config[args.config_env].get("port") _https = config[args.config_env].get("https") _protocol = 'https' if _https else 'http' _url = "%s://%s:%s/" % (_protocol, _host, _port) if _port else "%s://%s/" % (_protocol, _host) _json_headers = { "Accept": "application/json", "Content-Type": "application/json", "Authorization": "Token token=%s" % _auth_token, } # Setup logging setup_logging(debug=args.debug, noop=False) logger.debug4("OPTIONS: %s" % options) logger.debug4("CONFIG: %s" % config_env) _ldap_url = config_env.get("url") _use_tls = config_env.get("tls") _bind_dn = config_env.get("bind_dn", None) _bind_pass = config_env.get("bind_pass", None) group_search_base = "ou=Groups,dc=brazos,dc=tamu,dc=edu" user_search_base = "ou=People,dc=brazos,dc=tamu,dc=edu" new_group_filter = "cn=%s" % args.new_group old_group_filter = "cn=%s" % args.old_group user_filter = "uid=%s" % args.username group_attribs = [ "dn", "cn", "gidNumber", "uniqueMember", "slurmAccountName", ] user_attribs = [ "dn", "uid", "gidNumber", ] scope = "one" local_ldap = LocalLdap(url=_ldap_url[0], use_tls=_use_tls, bind_dn=_bind_dn, bind_pass=_bind_pass, log_level=None) new_group_results = local_ldap.paged_search(base=group_search_base, sfilter=new_group_filter, attrlist=group_attribs, scope=scope) old_group_results = local_ldap.paged_search(base=group_search_base, sfilter=old_group_filter, attrlist=group_attribs, scope=scope) user_results = local_ldap.paged_search(base=user_search_base, sfilter=user_filter, attrlist=user_attribs, scope=scope) logger.debug("LDAP new group: %s", json.dumps(new_group_results)) logger.debug("LDAP old group: %s", json.dumps(old_group_results)) logger.debug("LDAP user: %s", json.dumps(user_results)) if len(new_group_results) != 1 or len(old_group_results) != 1: logger.error("Incorrect number of LDAP group results returned") sys.exit(1) if len(user_results) != 1: logger.error("Incorrect number of LDAP user results returned") sys.exit(1) ldap_group_new = LdapGroup() ldap_group_new.setattrs(data=new_group_results[0], listvals=["uniqueMember"]) ldap_group_old = LdapGroup() ldap_group_old.setattrs(data=old_group_results[0], listvals=["uniqueMember"]) ldap_user = LdapUser() ldap_user.setattrs(data=user_results[0]) # Not all LDAP groups have slurmAccountName attribute if not hasattr(ldap_group_new, 'slurmAccountName'): ldap_group_new.slurmAccountName = ldap_group_new.cn if not hasattr(ldap_group_old, 'slurmAccountName'): ldap_group_old.slurmAccountName = ldap_group_old.cn # Check certain things exist to avoid sending None to LDAP which could delete more than we want _ldap_group_new_valid = True _ldap_group_old_valid = True for a in ['dn', 'gidNumber', 'slurmAccountName', 'uniqueMember', 'cn']: if not hasattr(ldap_group_new, a): _ldap_group_new_valid = False elif getattr(ldap_group_new, a) is None: _ldap_group_new_valid = False if not hasattr(ldap_group_old, a): _ldap_group_old_valid = False elif getattr(ldap_group_old, a) is None: _ldap_group_old_valid = False if not _ldap_group_new_valid: logger.error("LDAP group %s does not have all necessary information", args.new_group) if not _ldap_group_old_valid: logger.error("LDAP group %s does not have all necessary information", args.old_group) if ldap_user.dn is None or ldap_user.uid is None or ldap_user.gidNumber is None: logger.error("LDAP user %s does not have all necessary information", args.username) ## Update account management database get_old_group_params = { "name": args.old_group, } old_group_data = actmgr_api.get_groups(_url, _json_headers, get_old_group_params) old_group = old_group_data[0] logger.debug("Old Group API data: %s", json.dumps(old_group)) get_new_group_params = { "name": args.new_group, } new_group_data = actmgr_api.get_groups(_url, _json_headers, get_new_group_params) new_group = new_group_data[0] logger.debug("New Group API data: %s", json.dumps(new_group)) get_account_params = { "username": args.username, } account_data = actmgr_api.get_accounts(_url, _json_headers, get_account_params) account = account_data[0] logger.debug("Account API data: %s", json.dumps(account)) update_account_data = { "primary_group_id": new_group['id'], } # Handle cases where person is assigned old group in both primary and auxiliary groups group_ids = [] for g in account["groups"]: if g["id"] == old_group["id"]: group_ids.append(new_group["id"]) else: group_ids.append(g["id"]) if group_ids: update_account_data['group_ids'] = group_ids account = actmgr_api.update_account(_url, _json_headers, account['id'], update_account_data) if not account or not account["account"]: logger.error("Failed to update account management data") sys.exit(1) logger.debug("Account updated API data: %s", json.dumps(account)) account = account["account"] ## Update LDAP if ldap_user.gidNumber != ldap_group_new.gidNumber: logger.info("LDAP replace %s gidNumber=%s", ldap_user.dn, ldap_group_new.gidNumber) local_ldap.modify(ldap_user.dn, [(ldap.MOD_REPLACE, 'gidNumber', ldap_group_new.gidNumber)]) else: logger.warn("Skipping LDAP update of user gidNumber - already updated") if ldap_user.dn not in ldap_group_new.uniqueMember: logger.info("LDAP add to %s uniqueMember=%s", ldap_group_new.dn, ldap_user.dn) local_ldap.modify(ldap_group_new.dn, [(ldap.MOD_ADD, "uniqueMember", ldap_user.dn)]) else: logger.warn("Skipping LDAP update of group add uniqueMember - already updated") if ldap_user.dn in ldap_group_old.uniqueMember: logger.info("LDAP delete from %s uniqueMember=%s", ldap_group_old.dn, ldap_user.dn) local_ldap.modify(ldap_group_old.dn, [(ldap.MOD_DELETE, "uniqueMember", ldap_user.dn)]) else: logger.warn("Skipping LDAP update of group delete uniqueMember - already updated") ## Update SLURM _slurm_account = account["primary_group"]["alias"] _slurm_accounts = [g["alias"] for g in account["groups"] if "alias" in g] if _slurm_account not in _slurm_accounts: _slurm_accounts.append(_slurm_account) if not _slurm_account or not _slurm_accounts: logger.error("SLURM accounts not correctly determined") sys.exit(1) sacctmgr_check_args = [ "--parsable2", "--noheader", "show", "user", "name=%s" % args.username, "account=%s" % _slurm_account, "format=User,DefaultAccount,Account", "WithAssoc", ] logger.debug("Executing: sacctmgr %s", " ".join(sacctmgr_check_args)) try: output = sacctmgr(sacctmgr_check_args) except ErrorReturnCode: logger.error("FAILED to check if SLURM account already exists.") sys.exit(1) expected_output = "%s|%s|%s" % (args.username, _slurm_account, _slurm_account) existing_slurm_accounts = output.split(os.linesep) if expected_output not in existing_slurm_accounts: sacctmgr_delete_args = ["-i", "delete", "user","where", "name=%s" % args.username, "account=%s" % ldap_group_old.slurmAccountName] logger.debug("Executing: sacctmgr %s", " ".join(sacctmgr_delete_args)) try: output = sacctmgr(sacctmgr_delete_args) except ErrorReturnCode: logger.error("FAILED to delete user from SLURM.") sys.exit(1) sacctmgr_create_args = [ "-i", "create", "user", args.username, "account=%s" % ",".join(_slurm_accounts), "defaultaccount=%s" % _slurm_account, ] logger.debug("Executing: sacctmgr %s", " ".join(sacctmgr_create_args)) try: output = sacctmgr(sacctmgr_create_args) except ErrorReturnCode: logger.error("FAILED to retrieve all user names from SLURM.") sys.exit(1) else: logger.warn("Skipping SLURM account modifications - record already exists") ## Update permissions of $HOME and $SCRATCH home_path = os.path.join(_account_home_config.get("base_dir"), args.username) scratch_path = os.path.join(_account_home_config.get("scratch_base"), args.username) find_home_args = [ home_path, "-group", args.old_group, "-exec", "chgrp", args.new_group, '{}', ';' ] logger.info("Changing group ownership of files under %s", home_path) logger.debug("Executing: find %s", " ".join(find_home_args)) try: find(find_home_args) except ErrorReturnCode, e: logger.error("Failed to fix permissions of %s: %s", home_path, e.stderr) sys.exit(1)