Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F80597251
hpc_syncusers
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sun, Sep 1, 14:24
Size
8 KB
Mime Type
text/x-perl
Expires
Tue, Sep 3, 14:24 (2 d)
Engine
blob
Format
Raw Data
Handle
20403903
Attached To
rSLURMACCOUNTS slurm-accounts
hpc_syncusers
View Options
#!/usr/bin/perl -w
# hpc_syncusers - synchronise slurm user and account data with group memberships and YAML account definitions.
# - create/delete/modify slurm associations and scratch data
#
# Author : EPFL-SCITAS
# Date : March 2016
use strict;
use FindBin;
use lib "$FindBin::RealBin/../lib/blib";
use HPCUsers;
use Slurm;
use Scratch;
use Cmd;
use ParseAccounts;
use Cwd 'abs_path';
# -----------------------------------------------------------------------------
# Command line options
#
my %opt;
my $account_def_file = "accounts.yaml";
my $cluster = "";
sub print_usage() {
print STDERR << "EOF";
Synchronize SCITAS cluster users with their group membership and account definitions:
- set slurm associations accordingly
- create scratch directory
- read account definitions in file $account_def_file
- log commands in $Log::logfile
By default, does a dry run by only printing the commands required to bring users in sync.
usage: $0 [-f] [-g] [-h]
OPTIONS
-g : go for real by applying the required modifications, and log output.
-f : also free ressources of users having no more access to the cluster, i.e. delete slurm accounts and scratch data
-h : this (help) message
example: $0 -g
EOF
exit;
}
# array_diff(A,B) : return an array with all of the elements in A not being in B
sub array_diff(\@\@)
{
my %a = map{$_ => 1} @{$_[1]};
return grep(!exists($a{$_}), @{$_[0]});
}
# return an array with no dupplicate
sub array_unique(@) {
return keys %{{map {$_ => 1} @_}};
}
sub init()
{
use Getopt::Std;
my $opt_string = 'fhg';
getopts("$opt_string", \%opt) or print_usage();
# pathname of logfile
$cluster = Slurm::get_cluster_name();
my $logdir = "$FindBin::RealBin/../var/";
if ($cluster ne "" && -e "$logdir") {
$Log::logfile = abs_path($logdir . $cluster . "_" . $Log::logfile);
} else {
$Log::logfile = "/var/log/" . $Log::logfile;
}
# pathname of account definition file
my $pathname = abs_path("$FindBin::RealBin/../etc/" . $account_def_file);
if ( defined($pathname) && -e "$pathname" ) {
$account_def_file = $pathname;
} else {
$account_def_file = abs_path("$FindBin::RealBin/" . $account_def_file);
}
print_usage() if $opt{h};
$Cmd::dry_run = 1;
if ($opt{g}) {
$Cmd::dry_run = 0;
}
print_usage() if (scalar @ARGV > 1);
}
sub check_modify_user_associations($)
{
my ($username) = @_;
my @tmp = ();
my @existing_accounts = array_unique(sort(Slurm::user_accounts($username)));
foreach my $groupname (HPCUsers::member_of($username)) {
push(@tmp, ParseAccounts::group_to_slurm_accounts($groupname));
}
my @required_accounts = array_unique(sort(@tmp));
my @to_add = array_diff(@required_accounts, @existing_accounts);
my @to_remove = array_diff(@existing_accounts, @required_accounts);
if (scalar(@to_add) > 0 || scalar(@to_remove) > 0) {
Log::log_message(sprintf("modify user %s (%s)", $username, HPCUsers::user_to_string($username)));
Log::append_msg($Log::log_str) if ! $Cmd::dry_run;
print $Log::log_str, "\n";
# add new associations
foreach my $account (@to_add) {
Log::output_last_cmd_result() if Slurm::create_user_assoc($username,$account);
}
# remove obsolete associations
foreach my $account (@to_remove) {
Log::output_last_cmd_result() if Slurm::delete_user_assoc($username,$account);
}
# reset defaut account if necessary
if (! grep {$_ eq Slurm::default_account($username)} @required_accounts) {
# set default account to first account of @required_accounts
Log::output_last_cmd_result() if Slurm::set_default_account($username,$required_accounts[0]);
}
}
}
# User is new, create the slurm association(s) and private scratch directory
sub new_user($)
{
my ($username) = @_;
Log::log_message(sprintf("new user %s (%s)", $username, HPCUsers::user_to_string($username)));
Log::append_msg($Log::log_str) if !$Cmd::dry_run;
print $Log::log_str, "\n";
$Log::log_str = "";
# get the user accounts from the unix groups the user belongs to
my @groups = HPCUsers::member_of($username);
# print HPCUsers::groups_to_string($username), "\n";
my @accounts = ();
foreach my $groupname (@groups) {
push(@accounts,ParseAccounts::group_to_slurm_accounts($groupname));
}
# print $username, ":", Log::array_to_string(@accounts), "\n";
# Create slurm association(s)
foreach my $accountname (@accounts) {
Log::output_last_cmd_result() if Slurm::create_user_assoc($username,$accountname);
}
# Create scratch dir
Log::output_last_cmd_result() if Scratch::create_user_scratch_dir($username,HPCUsers::gid($username));
}
# User has no more access to SCITAS clusters:
# - delete the user from slurm dbd, along with all of its association(s)
# - delete its private scratch directory
sub free_user($)
{
my ($username) = @_;
return if (($username eq "query" || $username eq "root"));
Log::log_message(sprintf("free user %s (%s)", $username, HPCUsers::user_to_string($username)));
Log::append_msg($Log::log_str) if ! $Cmd::dry_run;
print $Log::log_str, "\n";
# delete slurm data
Log::output_last_cmd_result() if Slurm::delete_user($username);
# delete, or move scratch directory
Log::output_last_cmd_result() if Scratch::delete_user_scratch_dir($username);
}
sub scan_users()
{
foreach my $username (HPCUsers::all_users()) {
next if ($username eq "scitasbuild");
if (! HPCUsers::is_in_hpc_group($username) || ! HPCUsers::is_accredited($username)) {
# if user has no more access to the cluster, we can remove it's account and data
free_user($username) if ($opt{f} && Scratch::user_scratch_dir_exists($username));
} elsif (! Scratch::user_scratch_dir_exists($username)) {
# we assume that's a new user, because his private scratch directory doesn't exist
new_user($username);
} else {
check_modify_user_associations($username);
}
}
}
# Scan and update slurm accounts with definitions found in account YAML file
sub scan_accounts()
{
my $modified;
my $nb_iterations = 0;
rescan:
return if ++$nb_iterations > 25; # we never know, to avoid infinite loop
$modified = 0;
foreach my $account (ParseAccounts::all_accounts_tree()) {
my $parent = ParseAccounts::account_parent($account);
my $share = ParseAccounts::account_share($account);
if (! Slurm::account_exists($account)) {
# print "NOT EXISTS ", $account, " parent=", $parent, " share=", $share, "\n";
Log::output_last_cmd_result() if (Slurm::create_account($account,$parent,$share));
}
# check and set acount parent
if (Slurm::set_account_parent($account,$parent)) {
Log::output_last_cmd_result();
# if slurm account depedence tree is modified, we need to rescan accounts from scratch
goto rescan if ($Cmd::result{status} eq "reinitialized");
}
# check and set fairshare value
Log::output_last_cmd_result() if (Slurm::set_account_share($account,$share));
# check and set maxwall value
if (Slurm::set_account_maxwall($account,ParseAccounts::account_maxwall($account))) {
Log::output_last_cmd_result();
$modified = 1 if $Cmd::result{status} eq "reinitialized";
}
# check and set maxnodes value
if (Slurm::set_account_maxnodes($account,ParseAccounts::account_maxnodes($account))) {
Log::output_last_cmd_result();
$modified = 1 if $Cmd::result{status} eq "reinitialized";
}
# if slurm data has been modified enough, it's safer to rescan all accounts from scratch
goto rescan if ($modified);
}
}
# -----------------------------------------------------------------------------
init();
HPCUsers::init();
# search all groups to be able to associate users to accounts
HPCUsers::search_scoldap_for_groups();
# For debugging
# HPCUsers::print_users();
# Slurm::print_accounts();
# parse YAML account definition file
ParseAccounts::init($cluster,$account_def_file);
#ParseAccounts::print();
# first scan, modify the accounts
scan_accounts();
# then add, delete user associations
scan_users();
Event Timeline
Log In to Comment