From c07255959a4d3cf90f2e4ddcde71f0bfa140a6bf Mon Sep 17 00:00:00 2001 From: Frederick Dilger Date: Tue, 9 Jul 2024 16:01:36 -0600 Subject: [PATCH] LU-17702 utils: 'lfs quota' MOUNT_POINT optional Specifying the MOUNT_POINT is now optional for 'lfs quota'. Additionally multiple mount points can now be specified to print quota information for each one. If the mount point is not specified, quota information for ALL mounted filesystems will be printed instead. Changes were made to fix the quota formatting as some columns were misaligned. Filesystem is no longer placed on newline if the name exceeds 16 characters, this should make for better argument parsing. Signed-off-by: Frederick Dilger Change-Id: I3985855c5ce67be0fb8107c046bd54b802e1f6f1 Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/55683 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Andreas Dilger Reviewed-by: Hongchao Zhang Reviewed-by: Oleg Drokin --- lustre/doc/lfs-quota.1 | 46 ++++-- lustre/doc/lfs-setquota.1 | 8 +- lustre/tests/sanity-quota.sh | 149 ++++++++++++++--- lustre/utils/lfs.c | 379 +++++++++++++++++++++++-------------------- 4 files changed, 361 insertions(+), 221 deletions(-) diff --git a/lustre/doc/lfs-quota.1 b/lustre/doc/lfs-quota.1 index e08a56f..8f349f1 100644 --- a/lustre/doc/lfs-quota.1 +++ b/lustre/doc/lfs-quota.1 @@ -2,28 +2,37 @@ .SH NAME lfs-quota \- display quota limits and status for users, groups, or projects. .SH SYNOPSIS -.BR "lfs quota " [ -hqv "] [" --ost " \fIobd_uuid\fR|" --mdt " \fIMDT_IDX\fR] - [\fB-u \fIUSER\fR|\fB-g <\fIGROUP\fR>|\fB-p \fIPROJID\fR] -[\fB--pool\fR \fIPOOL_NAME\fR] \fIFILESYSTEM +.BR "lfs quota " [ -q "] [" -v "] [" -h "] [" -o +.IR OBD_UUID | \fB-i +.IR MDT_IDX | \fB-I +.IR OST_IDX "] [" \fB-u +.IR USER | \fB-g +.IR GROUP | \fB-p +.IR PROJID "] [" \fB--pool +.IR OST_POOL_NAME "] [" MOUNT_POINT " ...]" .br -.BR "lfs quota " [ -hq "] {" -U | -G | -P } " " \fIFILESYSTEM +.BR "lfs quota " [ -hq "] {" -U | -G | -P } +.RI [ MOUNT_POINT " ...]" .br .br -.BR "lfs quota " -t " {" -u | -g | -p "} " \fIFILESYSTEM +.BR "lfs quota " -t " {" -u | -g | -p } +.RI [ MOUNT_POINT " ...]" .br .br -.B lfs quota -a \fR<\fB-u\fR|\fB-g\fR|\fB-p\fR> <\fIfilesystem\fR> +.BR "lfs quota " -a " {" -u | -g | -p } +.RI [ MOUNT_POINT " ...]" .br .TP .SH DESCRIPTION .PP .TP -.B lfs quota \fIFILESYSTEM -Display disk usage and limits for individual users, groups, and projects. +.BR "lfs quota " [ \fIMOUNT_POINT " ...]" +Display disk usage and limits for individual users, groups, and projects for +each MOUNT_POINT. An asterisk is displayed when the quota is exceeded. By default the statistics for the entire filesystem are displayed but individual MDTs and OSTs can be specified with the @@ -33,6 +42,7 @@ or options. A user, group, or project ID can be specified. If user, group, and project are omitted, quotas for the current uid/gid/projid are shown. +If no MOUNT_POINT is specified, quotas for all Lustre mountpoints will be shown. .TP .BR -h | --human-readable This will change the formatting of @@ -63,8 +73,14 @@ Display quota information for project \fIPROJID\fR. Display only the line containing the data. The line saying what the data is, and the column headers will not be printed. .TP -.BR -u | --user " {" \fIUSER | \fIUID } -Display quota information for user name \fIUSER\fR or numeric \fIUID\fR. +.BR -u | --user " {" \fIUSER \fR| \fIUID \fR} +Display quota information for user name +.I USER +or numeric +.IR UID . +Can be used without specifying the mount point to get quota information +from all filesystems for the specified +.IR USER | UID . .TP .BR -v | --verbose Display per-MDT and per-OST statistics in addition @@ -73,7 +89,7 @@ the quota is exceeded only for that specific target. The user is over the quota only if an asterisk is near the whole filesystem usage. Inactive target will also be printed but marked as "inact". .TP -.BR "lfs quota " { -U | -G | -P "} " FILESYSTEM +.BR "lfs quota " { -U | -G | -P "} " MOUNT_POINT Display default quota values for users, groups, or projects. This command requires super user permissions. .TP @@ -86,11 +102,15 @@ Display default quota limits for project \fIPROJID\fR. .BR -U | --default-usr " {" \fIUSER | UID } Display default quota limits for username \fIUSER\fR or numeric \fIUID\fR. .TP -.BR "lfs quota -a" " {" -u | -g | -p "} " \fIFILESYSTEM +.BR "lfs quota -a" " {" -u | -g | -p "} " \fIMOUNT_POINT .TP Display all quota setting for all users, groups, or projects. .TP -.BR "lfs quota -t" | --times " {" -u | -g | -p "} [" --pool " \fIPOOL_NAME\fR] " \fIFILESYSTEM +.B "lfs quota -a -u" +.TP +Display all quota settings for all users across all mounted filesystems. +.TP +.BR "lfs quota -t" | --times " {" -u | -g | -p "} [" --pool " \fIPOOL_NAME\fR] " \fIMOUNT_POINT Display grace times for users, groups, or projects. Time values use the "XXwXXdXXhXXmXXs" format, which specifies weeks, days, hours, minutes, seconds. diff --git a/lustre/doc/lfs-setquota.1 b/lustre/doc/lfs-setquota.1 index 92b4f6d..0b0cf6c 100644 --- a/lustre/doc/lfs-setquota.1 +++ b/lustre/doc/lfs-setquota.1 @@ -7,20 +7,20 @@ lfs-setquota \- set quota limits or grace time for users, groups or projects. [\fB-b|\fB--block-softlimit\fR \fIBLOCK_SOFTLIMIT\fR[\fBKMGTPE\fR]] [\fB-B|\fB--block-hardlimit\fR \fIBLOCK_HARDLIMIT\fR[\fBKMGTPE\fR]] [\fB-i|\fB--inode-softlimit\fR \fIINODE_SOFTLIMIT\fR[\fBKMGTPE\fR]] - [\fB-I|\fB--inode-hardlimit\fR \fIINODE_HARDLIMIT\fR[\fBKMGTPE\fR]] \fIFILESYSTEM + [\fB-I|\fB--inode-hardlimit\fR \fIINODE_HARDLIMIT\fR[\fBKMGTPE\fR]] \fIMOUNT_POINT .TP .BR "lfs setquota " { -u | --user | -g | --group | -p | --projid "} " \fIID - {\fB--default\fR|\fB-D\fR|\fB--delete\fR} \fIFILESYSTEM + {\fB--default\fR|\fB-D\fR|\fB--delete\fR} \fIMOUNT_POINT .TP .BR "lfs setquota " { -t | --times "} {" -h | -u | -g | -p "} [" "--pool " \fIPOOLNAME ] [\fB-b\fR|\fB--block-grace\fR \fIBLOCK_GRACE_TIME\fR] - [\fB-i\fR|\fB--inode-grace\fR \fIINODE_GRACE_TIME\fR] \fIFILESYSTEM + [\fB-i\fR|\fB--inode-grace\fR \fIINODE_GRACE_TIME\fR] \fIMOUNT_POINT .TP .BR "lfs setquota " { -U | --default-usr | -G | --default-grp | -P | --default-prj } [\fB-b\fR|\fB--block-softlimit\fR \fIBLOCK_SOFTLIMIT\fR[\fBKMGTPE\fR]] [\fB-B\fR|\fB--block-hardlimit\fR \fIBLOCK_HARDLIMIT\fR[\fBKMGTPE\fR]] [\fB-i\fR|\fB--inode-softlimit\fR \fIINODE_SOFTLIMIT\fR[\fBKMGTPE\fR]] - [\fB-I\fR|\fB--inode-hardlimit\fR \fIINODE_HARDLIMIT\fR[\fBKMGTPE\fR]] \fIFILESYSTEM + [\fB-I\fR|\fB--inode-hardlimit\fR \fIINODE_HARDLIMIT\fR[\fBKMGTPE\fR]] \fIMOUNT_POINT .TP .BR "lfs setquota " { -u | --user | -g | --group | -p | --projid "} " \fIUID\fR|\fIGID\fR|\fIPROJID\fR [\fB--delete\fR] <\fIfilesystem\fR> diff --git a/lustre/tests/sanity-quota.sh b/lustre/tests/sanity-quota.sh index 654d633..be86060 100755 --- a/lustre/tests/sanity-quota.sh +++ b/lustre/tests/sanity-quota.sh @@ -201,8 +201,9 @@ getquota() { [ ! -z "$5" ] && pool_arg="--pool $5 " [ "$uuid" = "global" ] && uuid=$DIR + $LFS quota -v "$1" "$2" $pool_arg $DIR 1>&2 $LFS quota -v "$1" "$2" $pool_arg $DIR | - awk 'BEGIN { num='$spec' } { if ($1 == "'$uuid'") \ + awk 'BEGIN { num='$spec' } { if ($1 ~ "'$uuid'") \ { if (NF == 1) { getline } else { num++ } ; print $num;} }' \ | tr -d "*" } @@ -564,7 +565,7 @@ wait_quota_synced() { check_system_is_clean() { local used - lfs quota -uv $TSTUSR $MOUNT + lfs quota -v -u $TSTUSR $MOUNT for cur in "curspace" "curinodes"; do used=$(getquota -u $TSTUSR global $cur) @@ -1260,7 +1261,7 @@ test_1i() { $LFS setquota -u $TSTUSR -B 0 --pool $qpool1 $DIR || error "set user quota failed" - $LFS quota -uv $TSTUSR --pool $qpool1 $DIR + $LFS quota -v -u $TSTUSR --pool $qpool1 $DIR $RUNAS $DD of=$testfile1 count=$((limit1/2)) || quota_error u $TSTUSR "write failure, but expect success" @@ -2146,7 +2147,7 @@ test_7a() { resetquota -u $TSTUSR set_ost_qtype "none" || error "disable ost quota failed" - local OSTUUID=$(ostuuid_from_index 0) + local OSTUUID=$(ostname_from_index 0) USED=$(getquota -u $TSTUSR $OSTUUID bhardlimit) [ $USED -ne 0 ] && error "limit($USED) on $OSTUUID for user $TSTUSR isn't 0" @@ -2213,7 +2214,7 @@ test_7b() { resetquota -u $TSTUSR set_ost_qtype "none" || error "disable ost quota failed" - local OSTUUID=$(ostuuid_from_index 0) + local OSTUUID=$(ostname_from_index 0) USED=$(getquota -u $TSTUSR $OSTUUID bhardlimit) [ $USED -ne 0 ] && error "limit($USED) on $OSTUUID for user $TSTUSR isn't 0" @@ -2685,7 +2686,7 @@ test_13(){ [ $nlock -eq $init_nlock ] || error "per-ID lock isn't cleared" # spare quota should be released - local OSTUUID=$(ostuuid_from_index 0) + local OSTUUID=$(ostname_from_index 0) local limit=$(getquota -u $TSTUSR $OSTUUID bhardlimit) local space=$(getquota -u $TSTUSR $OSTUUID curspace) [ $limit -le $space ] || @@ -3229,7 +3230,7 @@ test_23_sub() { cleanup_quota_test - local OST0_UUID=$(ostuuid_from_index 0) + local OST0_UUID=$(ostname_from_index 0) local OST0_QUOTA_USED=$(getquota -u $TSTUSR $OST0_UUID curspace) [ $OST0_QUOTA_USED -ne 0 ] && ($SHOW_QUOTA_USER; \ @@ -4127,11 +4128,11 @@ test_get_allquota() { eval $($LFS quota -a -s $start_qid -e $end_qid -u $MOUNT | awk 'NR > 2 {printf("u_blimits[%d]=%d;u_ilimits[%d]=%d; \ u_busage[%d]=%d;u_iusage[%d]=%d;", \ - NR, $4, NR, $8, NR, $2, NR, $6)}') + NR, $5, NR, $9, NR, $3, NR, $7)}') eval $($LFS quota -a -s $start_qid -e $end_qid -g $MOUNT | awk 'NR > 2 {printf("g_blimits[%d]=%d;g_ilimits[%d]=%d; \ g_busage[%d]=%d;g_iusage[%d]=%d;", \ - NR, $4, NR, $8, NR, $2, NR, $6)}') + NR, $5, NR, $9, NR, $3, NR, $7)}') for i in $(seq $qid_cnt); do [ $i -le 2 ] && continue @@ -4176,11 +4177,11 @@ test_get_allquota() { eval $($LFS quota -a -s $start_qid -e $end_qid -u $MOUNT | awk 'NR > 2 {printf("u_blimits[%d]=%d;u_ilimits[%d]=%d; \ u_busage2[%d]=%d;u_iusage2[%d]=%d;", \ - NR, $4, NR, $8, NR, $2, NR, $6)}') + NR, $5, NR, $9, NR, $3, NR, $7)}') eval $($LFS quota -a -s $start_qid -e $end_qid -g $MOUNT | awk 'NR > 2 {printf("g_blimits[%d]=%d;g_ilimits[%d]=%d; \ g_busage2[%d]=%d;g_iusage2[%d]=%d;", \ - NR, $4, NR, $8, NR, $2, NR, $6)}') + NR, $5, NR, $9, NR, $3, NR, $7)}') sz=$((sz / 1024)) for i in $(seq $qid_cnt); do @@ -4240,7 +4241,7 @@ test_49() do_facet mds1 $LCTL set_param fail_loc=0 start=$SECONDS - $LFS quota -a -u $MOUNT | tail -n 100 + $LFS quota -a -u $MOUNT | head -n 100 echo "get all usr quota: $total_file_cnt / $((SECONDS - start)) seconds" start=$SECONDS @@ -5609,7 +5610,7 @@ test_72() used=$(getquota -u $TSTUSR global bhardlimit $qpool) echo "used $used" [ $used -ge $limit ] || error "used($used) is less than limit($limit)" - # check that lfs quota -uv --pool prints only OST that + # check that lfs quota -v -u --pool prints only OST that # was added in a pool lfs quota -v -u $TSTUSR --pool $qpool $DIR | grep -v "OST0001" | grep "OST\|MDT" && error "$qpool consists wrong targets" @@ -5719,12 +5720,13 @@ test_74() [ $tmp -eq $((limit2 * 1024)) ] || error "wrong limit $tmp for $qpool2" # check limits in pools + $LFS quota -u $TSTUSR --pool $DIR tmp=$($LFS quota -u $TSTUSR --pool $DIR | \ - grep -A4 $qpool | awk 'NR == 4{print $4}') + grep -A4 $qpool | awk 'NR == 2{print $4}') echo "pool limit for $qpool $tmp" - [ $tmp -eq $((limit * 1024)) ] || error "wrong limit:tmp for $qpool" + [ $tmp -eq $((limit * 1024)) ] || error "wrong limit:$tmp for $qpool" tmp=$($LFS quota -u $TSTUSR --pool $DIR | \ - grep -A4 $qpool2 | awk 'NR == 4{print $4}') + grep -A4 $qpool2 | awk 'NR == 2{print $4}') echo "pool limit for $qpool2 $tmp" [ $tmp -eq $((limit2 * 1024)) ] || error "wrong limit:$tmp for $qpool2" } @@ -6098,7 +6100,7 @@ test_80() lfs getstripe $dir2 sleep 3 - $LFS quota -uv $TSTUSR $DIR + $LFS quota -v -u $TSTUSR $DIR #define OBD_FAIL_QUOTA_PREACQ 0xA06 do_facet mds1 $LCTL set_param fail_loc=0xa06 $RUNAS $DD of=$TESTFILE3 count=3 || @@ -6109,16 +6111,16 @@ test_80() quota_error u $TSTUSR "write failed" sync sleep 3 - $LFS quota -uv --pool $qpool $TSTUSR $DIR + $LFS quota -v -u --pool $qpool $TSTUSR $DIR rm -f $TESTFILE2 stop ost2 do_facet mds1 $LCTL set_param fail_loc=0 start ost2 $(ostdevname 2) $OST_MOUNT_OPTS || error "start ost2 failed" - $LFS quota -uv $TSTUSR --pool $qpool $DIR + $LFS quota -v -u $TSTUSR --pool $qpool $DIR # OST0 needs some time to update quota usage after removing TESTFILE2 sleep 4 - $LFS quota -uv $TSTUSR --pool $qpool $DIR + $LFS quota -v -u $TSTUSR --pool $qpool $DIR $RUNAS $DD of=$TESTFILE0 count=2 oflag=direct || quota_error u $TSTUSR "write failure, but expect success" } @@ -6283,8 +6285,8 @@ test_84() # the grant quota should be larger than 0 waited=0 while (( $waited < 60 )); do - grant=$(getquota -g $TSTUSR lustre-OST0000_UUID bhardlimit $qp) - grant2=$(getquota -g $TSTUSR lustre-OST0000_UUID bhardlimit) + grant=$(getquota -g $TSTUSR lustre-OST0000 bhardlimit $qp) + grant2=$(getquota -g $TSTUSR lustre-OST0000 bhardlimit) (( ${grant} > 0 && ${grant2} > 0 )) && break do_facet ost1 $LCTL set_param \ @@ -6315,8 +6317,8 @@ test_84() # the grant quota should be set as insane value waited=0 while (( $waited < 60 )); do - grant=$(getquota -g $TSTUSR lustre-OST0000_UUID bhardlimit $qp) - grant2=$(getquota -g $TSTUSR lustre-OST0000_UUID bhardlimit) + grant=$(getquota -g $TSTUSR lustre-OST0000 bhardlimit $qp) + grant2=$(getquota -g $TSTUSR lustre-OST0000 bhardlimit) (( ${#grant} == 20 && ${#grant2} == 20 )) && break sleep 1 @@ -6343,9 +6345,9 @@ test_84() $LFS quota -gv --pool $qp $TSTUSR $DIR # the grant quota should be reset - grant=$(getquota -g $TSTUSR lustre-OST0000_UUID bhardlimit) + grant=$(getquota -g $TSTUSR lustre-OST0000 bhardlimit) (( ${#grant} == 20 )) && error "grant is not cleared" - grant=$(getquota -g $TSTUSR lustre-OST0000_UUID bhardlimit $qp) + grant=$(getquota -g $TSTUSR lustre-OST0000 bhardlimit $qp) (( ${#grant} == 20 )) && error "pool grant is not cleared" $LFS quota -gv $TSTUSR --pool $qp $DIR @@ -6452,7 +6454,7 @@ test_86() local test_dir="$DIR/$tdir/test_dir" - setup_quota_test || error "setup quota failed with %?" + setup_quota_test || error "setup quota failed with $?" set_mdt_qtype $QTYPE || error "enable mdt quota failed" $LFS setdirstripe -c 1 -i 0 $test_dir || error "setdirstripe failed" @@ -6466,6 +6468,99 @@ test_86() } run_test 86 "Pre-acquired quota should be released if quota is over limit" +check_quota_no_mount() +{ + local opts="$1" + local id="$2" + + echo "cmd: $LFS quota $opts $id" + local expected=$($LFS quota $opts $id $MOUNT) + local actual=$($LFS quota $opts $id) + + [[ "$actual" == "$expected" ]] || + error "quota info not $expected, found: $actual" +} + +check_quota_two_mounts() +{ + local opts="$1" + local id="$2" + + local cmd="$LFS quota -q $opts $id $MOUNT $MOUNT2" + echo "cmd: $cmd" +# remove the header for comparison + local actual + local full=$($cmd) + local head=$($LFS quota -q $opts $id $MOUNT) + local tail=$($LFS quota -q $opts $id $MOUNT2) + + actual=$(echo "$full" | head -n$(echo "$head" | wc -l)) + [[ "$actual" == "$head" ]] || + error "quota info from $MOUNT not '$head', found '$actual'" + + actual=$(echo "$full" | tail -n$(echo "$tail" | wc -l)) + [[ "$actual" == "$tail" ]] || + error "quota info from $MOUNT2 not '$tail', found '$actual'" +} + +test_90a() +{ + (( MDS1_VERSION >= $(version_code 2.15.60) )) || + skip "Need MDS version at least 2.15.60" + + setup_quota_test || error "setup quota failed with $?" + + stack_trap cleanup_quota_test + + check_quota_no_mount + check_quota_no_mount -u $TSTUSR + check_quota_no_mount "-a -u" + check_quota_no_mount "-t -u" + check_quota_no_mount -U + check_quota_no_mount -g $TSTUSR + check_quota_no_mount "-a -g" + check_quota_no_mount "-t -g" + check_quota_no_mount -G + + ! is_project_quota_supported && + skip "Project quota is not supported" + check_quota_no_mount -p 100 + check_quota_no_mount "-a -p" + check_quota_no_mount "-t -p" + check_quota_no_mount -P +} +run_test 90a "lfs quota should work without mount point" + +test_90b() +{ + (( MDS1_VERSION >= $(version_code 2.15.60) )) || + skip "Need MDS version at least 2.15.60" + + setup_quota_test || error "setup quota failed with $?" + mount_client $MOUNT2 + + stack_trap "umount $MOUNT2" + stack_trap cleanup_quota_test + + check_quota_two_mounts -u $TSTUSR + check_quota_two_mounts "-a -u" + check_quota_two_mounts "-t -u" + check_quota_two_mounts -U + check_quota_two_mounts -g $TSTUSR + check_quota_two_mounts "-a -g" + check_quota_two_mounts "-t -g" + check_quota_two_mounts -G + + ! is_project_quota_supported && + skip "Project quota is not supported" + check_quota_two_mounts -p 1000 + check_quota_two_mounts "-a -p" + check_quota_two_mounts "-t -p" + check_quota_two_mounts -P +} +run_test 90b "lfs quota should work with multiple mount points" + + quota_fini() { do_nodes $(comma_list $(nodes_list)) \ diff --git a/lustre/utils/lfs.c b/lustre/utils/lfs.c index a172a94..2d6191c 100755 --- a/lustre/utils/lfs.c +++ b/lustre/utils/lfs.c @@ -98,6 +98,16 @@ static int lfs_df(int argc, char **argv); static int lfs_getname(int argc, char **argv); static int lfs_check(int argc, char **argv); #ifdef HAVE_SYS_QUOTA_H +struct quota_param { + __u32 qp_valid; + unsigned int qp_verbose:1; + unsigned int qp_quiet:1; + unsigned int qp_human_readable:1; + unsigned int qp_show_default:1; + unsigned int qp_show_pools:1; + unsigned int qp_show_qid:1; +}; + static int lfs_setquota(int argc, char **argv); static int lfs_quota(int argc, char **argv); static int lfs_project(int argc, char **argv); @@ -446,15 +456,16 @@ command_t cmdlist[] = { "Usage: getname [--help|-h] [--instance|-i] [--fsname|-n] [path ...]"}, #ifdef HAVE_SYS_QUOTA_H {"setquota", lfs_setquota, 0, "Set filesystem quotas.\n" - "usage: setquota [-t] {-u|-U|-g|-G|-p|-P ID} {-b|-B|-i|-I LIMIT} [--pool POOL] FILESYSTEM\n" - " setquota {-u|-g|-p ID} {--default|--delete} FILESYSTEM\n"}, + "usage: setquota [-t] {-u|-U|-g|-G|-p|-P ID} {-b|-B|-i|-I LIMIT}\n" + " [--pool POOL] MOUNT_POINT\n" + " setquota {-u|-g|-p ID} {--default|--delete} MOUNT_POINT\n"}, {"quota", lfs_quota, 0, "Display disk usage and limits.\n" "usage: quota [-q] [-v] [-h] [-o OBD_UUID|-i MDT_IDX|-I OST_IDX]\n" - " [{-u|-g|-p} UNAME|UID|GNAME|GID|PROJID]\n" - " [--pool OST_POOL_NAME] FILESYSTEM\n" - " quota -t {-u|-g|-p} [--pool OST_POOL_NAME] FILESYSTEM\n" - " quota [-hqv] {-U|-G|-P} [--pool OST_POOL_NAME] FILESYSTEM\n" - " quota -a {-u|-g|-p} [-s START_QID] [-e END_QID] FILESYSTEM"}, + " [{-u|-g|-p} UNAME|UID|GNAME|GID|PROJID]\n" + " [--pool OST_POOL_NAME] [MOUNT_POINT ...]\n" + " quota -t {-u|-g|-p} [--pool OST_POOL_NAME] [MOUNT_POINT ...]\n" + " quota [-hqv] {-U|-G|-P} [--pool OST_POOL_NAME] [MOUNT_POINT ...]\n" + " quota -a {-u|-g|-p} [-s START_QID] [-e END_QID] [MOUNT_POINT ...]\n"}, {"project", lfs_project, 0, "Change or list project attribute for specified file or directory.\n" "usage: project [-d|-r] \n" @@ -8908,20 +8919,34 @@ static void diff2str(time_t seconds, char *buf, time_t now) } static void print_quota_title(char *name, struct if_quotactl *qctl, - bool human_readable, bool show_default) + struct quota_param *param) { - if (show_default) { + if (param->qp_quiet || + qctl->qc_cmd == LUSTRE_Q_GETINFO || + qctl->qc_cmd == LUSTRE_Q_GETINFOPOOL || + qctl->qc_cmd == Q_GETOINFO) + return; + + if (param->qp_show_qid) { + printf("Disk %s quotas\n", qtype_name(qctl->qc_type)); + printf("%16s %9s %7s %7s %7s %7s %7s %7s %7s %7s\n", + "Filesystem", "quota_id", + param->qp_human_readable ? "used" : "kbytes", + "quota", "limit", "grace", + "files", "quota", "limit", "grace"); + } else if (param->qp_show_default) { printf("Disk default %s quota:\n", qtype_name(qctl->qc_type)); - printf("%15s %8s%8s%8s %8s%8s%8s\n", + printf("%16s %7s %7s %7s %7s %7s %7s\n", "Filesystem", "bquota", "blimit", "bgrace", "iquota", "ilimit", "igrace"); } else { printf("Disk quotas for %s %s (%cid %u):\n", qtype_name(qctl->qc_type), name, *qtype_name(qctl->qc_type), qctl->qc_id); - printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n", - "Filesystem", human_readable ? "used" : "kbytes", - "quota", "limit", "grace", + printf("%16s %7s %7s %7s %7s %7s %7s %7s %7s\n", + "Filesystem", + param->qp_human_readable ? "used" : "kbytes", + "quota", "limit", "grace", "files", "quota", "limit", "grace"); } } @@ -8965,7 +8990,7 @@ static void kbytes2str(__u64 num, char *buf, int buflen, bool h) static void print_quota(char *mnt, struct if_quotactl *qctl, int type, int rc, bool h, bool show_default, bool show_qid) { - char *name; + char *name, *tmp; time_t now; time(&now); @@ -9003,25 +9028,25 @@ static void print_quota(char *mnt, struct if_quotactl *qctl, int type, iover = 3; } + tmp = strstr(mnt, "_UUID"); + if (tmp) + *tmp = '\0'; + + printf("%16s", mnt); /* Filesystem */ if (show_qid) { if (qctl->qc_type == USRQUOTA) { if (uid2name(&name, qctl->qc_id)) - printf("%10u", qctl->qc_id); + printf(" %9u", qctl->qc_id); else - printf("%10s", name); + printf(" %9s", name); } else if (qctl->qc_type == GRPQUOTA) { if (gid2name(&name, qctl->qc_id)) - printf("%10u", qctl->qc_id); + printf(" %9u", qctl->qc_id); else - printf("%10s", name); + printf(" %9s", name); } else { - printf("%10u", qctl->qc_id); + printf(" %9u", qctl->qc_id); } - } else { - if (strlen(mnt) > 15) - printf("%s\n%15s", mnt, ""); - else - printf("%15s", mnt); } if (show_default) @@ -9050,7 +9075,7 @@ static void print_quota(char *mnt, struct if_quotactl *qctl, int type, "%s" : "[%s]", strbuf); if (show_default) - printf(" %6s %7s %7s", numbuf[1], numbuf[2], timebuf); + printf(" %7s %7s %7s", numbuf[1], numbuf[2], timebuf); else printf(" %7s%c %6s %7s %7s", numbuf[0], bover ? '*' : ' ', numbuf[1], @@ -9077,7 +9102,7 @@ static void print_quota(char *mnt, struct if_quotactl *qctl, int type, "%ju" : "[%ju]", (uintmax_t)dqb->dqb_ihardlimit); if (show_default) - printf(" %6s %7s %7s", numbuf[1], numbuf[2], timebuf); + printf(" %7s %7s %7s", numbuf[1], numbuf[2], timebuf); else if (type != QC_OSTIDX) printf(" %7s%c %6s %7s %7s", numbuf[0], iover ? '*' : ' ', numbuf[1], @@ -9229,8 +9254,7 @@ out: } static int print_one_quota(char *mnt, char *name, struct if_quotactl *qctl, - int verbose, int quiet, bool human_readable, - bool show_default, bool show_qid, int rc) + struct quota_param *param, int rc) { int rc1 = 0, rc2 = 0; char *obd_type = (char *)qctl->obd_type; @@ -9238,7 +9262,7 @@ static int print_one_quota(char *mnt, char *name, struct if_quotactl *qctl, __u64 total_ialloc = 0, total_balloc = 0; int inacc; - if (!show_default && qctl->qc_id == 0) { + if (!param->qp_show_default && qctl->qc_id == 0) { qctl->qc_dqblk.dqb_bhardlimit = 0; qctl->qc_dqblk.dqb_bsoftlimit = 0; qctl->qc_dqblk.dqb_ihardlimit = 0; @@ -9256,12 +9280,6 @@ static int print_one_quota(char *mnt, char *name, struct if_quotactl *qctl, LQUOTA_FLAG(qctl->qc_dqblk.dqb_itime) & LQUOTA_FLAG_DEFAULT) qctl->qc_dqblk.dqb_itime &= LQUOTA_GRACE_MASK; - if (!show_qid && (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || - qctl->qc_cmd == LUSTRE_Q_GETQUOTAPOOL || - qctl->qc_cmd == LUSTRE_Q_GETDEFAULT_POOL || - qctl->qc_cmd == LUSTRE_Q_GETDEFAULT) && !quiet) - print_quota_title(name, qctl, human_readable, show_default); - if (rc && *obd_type) fprintf(stderr, "%s %s ", obd_type, obd_uuid); @@ -9273,25 +9291,26 @@ static int print_one_quota(char *mnt, char *name, struct if_quotactl *qctl, ((qctl->qc_dqblk.dqb_valid & (QIF_LIMITS|QIF_USAGE)) != (QIF_LIMITS|QIF_USAGE)); - print_quota(mnt, qctl, QC_GENERAL, rc, human_readable, show_default, - show_qid); + print_quota(mnt, qctl, QC_GENERAL, rc, param->qp_human_readable, + param->qp_show_default, param->qp_show_qid); - if (!show_qid && !show_default && verbose && - qctl->qc_valid == QC_GENERAL && qctl->qc_cmd != LUSTRE_Q_GETINFO && + if (!param->qp_show_qid && !param->qp_show_default && + param->qp_verbose && qctl->qc_valid == QC_GENERAL && + qctl->qc_cmd != LUSTRE_Q_GETINFO && qctl->qc_cmd != LUSTRE_Q_GETINFOPOOL) { char strbuf[STRBUF_LEN]; - rc1 = print_obd_quota(mnt, qctl, 1, human_readable, + rc1 = print_obd_quota(mnt, qctl, 1, param->qp_human_readable, &total_ialloc); - rc2 = print_obd_quota(mnt, qctl, 0, human_readable, + rc2 = print_obd_quota(mnt, qctl, 0, param->qp_human_readable, &total_balloc); kbytes2str(total_balloc, strbuf, sizeof(strbuf), - human_readable); + param->qp_human_readable); printf("Total allocated inode limit: %ju, total allocated block limit: %s\n", (uintmax_t)total_ialloc, strbuf); } - if (!show_qid && (rc || rc1 || rc2 || inacc)) + if (!param->qp_show_qid && (rc || rc1 || rc2 || inacc)) printf("%d Some errors happened when getting quota info. Some devices may be not working or deactivated. The data in \"[]\" is inaccurate.\n", inacc); if (rc) @@ -9306,8 +9325,8 @@ static int print_one_quota(char *mnt, char *name, struct if_quotactl *qctl, return 0; } -static int iter_all_quota(char *mnt, struct if_quotactl *qctl, int quiet, - bool human_readable) +static int iter_all_quota(char *mnt, struct if_quotactl *qctl, + struct quota_param *param) { struct if_quotactl qctl_tmp, *qctl_iter; void *buffer = NULL; @@ -9338,12 +9357,6 @@ static int iter_all_quota(char *mnt, struct if_quotactl *qctl, int quiet, if (rc) goto out; - printf("Filesystem %s, Disk %s quotas\n", mnt, - qtype_name(qctl->qc_type)); - printf("%10s%8s %7s%8s%8s%8s %7s%8s%8s\n", "quota_id", - human_readable ? "used" : "kbytes", "quota", "limit", "grace", - "files", "quota", "limit", "grace"); - cur = 0; while (cur < buflen) { if ((buflen - cur) < sizeof(struct if_quotactl)) { @@ -9359,8 +9372,7 @@ static int iter_all_quota(char *mnt, struct if_quotactl *qctl, int quiet, if ((qctl_iter->qc_dqblk.dqb_valid & QIF_USAGE) != QIF_USAGE) qctl_iter->qc_dqblk.dqb_valid |= QIF_USAGE; - print_one_quota(mnt, NULL, qctl_iter, 0, quiet, - human_readable, false, true, 0); + print_one_quota(mnt, NULL, qctl_iter, param, 0); } out: @@ -9373,10 +9385,10 @@ out: return rc; } -static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl, - int verbose, int quiet, bool human_readable, - bool show_default) +static int get_print_quota(char *mnt, struct if_quotactl *qctl, + struct quota_param *param) { + char *name = NULL; int rc; rc = llapi_quotactl(mnt, qctl); @@ -9400,8 +9412,7 @@ static int get_print_quota(char *mnt, char *name, struct if_quotactl *qctl, return rc; } - return print_one_quota(mnt, name, qctl, verbose, quiet, human_readable, - show_default, false, rc); + return print_one_quota(mnt, name, qctl, param, rc); } static int lfs_project(int argc, char **argv) @@ -9564,20 +9575,65 @@ static int lfs_project(int argc, char **argv) return ret; } +static int do_quota_op(char *mnt, struct if_quotactl *qctl, + struct quota_param *param) +{ + struct if_quotactl *qctl_tmp; + char **poollist = NULL; + char *buf = NULL; + int poolcount, i, rc = 0; + + /* avoid modifying the original qctl */ + qctl_tmp = malloc(sizeof(*qctl_tmp) + LOV_MAXPOOLNAME + 1); + memcpy(qctl_tmp, qctl, sizeof(*qctl_tmp) + LOV_MAXPOOLNAME + 1); + + if (qctl_tmp->qc_cmd == LUSTRE_Q_ITERQUOTA) { + rc = iter_all_quota(mnt, qctl_tmp, param); + goto out; + } + + if (param->qp_show_pools) { + char *p; + + rc = llapi_get_poolbuf(mnt, &buf, &poollist, &poolcount); + if (rc) + goto out; + + for (i = 0; i < poolcount; i++) { + p = memchr(poollist[i], '.', MAXNAMLEN); + if (!p) { + fprintf(stderr, "bad string format %.*s\n", + MAXNAMLEN, poollist[i]); + rc = -EINVAL; + goto out; + } + p++; + printf("Quotas for pool: %s\n", p); + snprintf(qctl_tmp->qc_poolname, LOV_MAXPOOLNAME + 1, + "%s", p); + rc = get_print_quota(mnt, qctl_tmp, param); + if (rc) + goto out; + } + } + rc = get_print_quota(mnt, qctl_tmp, param); + goto out; +out: + free(qctl_tmp); + free(buf); + return rc; +} + static int lfs_quota(int argc, char **argv) { - int c; - char *mnt, *name = NULL; + struct quota_param param = { .qp_valid = QC_GENERAL }; struct if_quotactl *qctl; - char *obd_uuid, *endp; - int rc = 0, rc1 = 0, verbose = 0, quiet = 0; - __u32 valid = QC_GENERAL; - long idx = 0; + char *obd_uuid, *endp, *name = NULL; __u32 start_qid = 0, end_qid = 0; - bool human_readable = false; - bool show_default = false; - int qtype; - bool show_pools = false; + int c, qtype, rc = 0; + long idx = 0; + bool all = false; + struct option long_opts[] = { { .val = 'a', .name = "all", .has_arg = required_argument }, { .val = 'e', .name = "end-qid", .has_arg = required_argument }, @@ -9603,9 +9659,6 @@ static int lfs_quota(int argc, char **argv) { .val = 'U', .name = "default-usr", .has_arg = required_argument }, { .val = 'v', .name = "verbose", .has_arg = no_argument }, { .name = NULL } }; - char **poollist = NULL; - char *buf = NULL; - int poolcount, i; qctl = calloc(1, sizeof(*qctl) + LOV_MAXPOOLNAME + 1); if (!qctl) @@ -9619,12 +9672,13 @@ static int lfs_quota(int argc, char **argv) long_opts, NULL)) != -1) { switch (c) { case 'a': + param.qp_show_qid = 1; qctl->qc_cmd = LUSTRE_Q_ITERQUOTA; break; case 'e': if (optarg == NULL || *optarg == '\0') { fprintf(stderr, - "%s quota: invalid start quota ID\n", + "%s quota: invalid end quota ID\n", progname); rc = CMD_HELP; goto out; @@ -9632,13 +9686,13 @@ static int lfs_quota(int argc, char **argv) end_qid = strtoul(optarg, NULL, 0); break; case 'G': - show_default = true; + param.qp_show_default = 1; /* fallthrough */ case 'g': qtype = GRPQUOTA; goto quota_type; case 'h': - human_readable = true; + param.qp_human_readable = 1; break; #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 22, 53, 0) case 'i': @@ -9657,7 +9711,7 @@ static int lfs_quota(int argc, char **argv) rc = CMD_HELP; goto out; } - valid = qctl->qc_valid = QC_MDTIDX; + param.qp_valid = qctl->qc_valid = QC_MDTIDX; qctl->qc_idx = idx; break; #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 22, 53, 0) @@ -9678,17 +9732,17 @@ static int lfs_quota(int argc, char **argv) rc = CMD_HELP; goto out; } - valid = qctl->qc_valid = QC_OSTIDX; + param.qp_valid = qctl->qc_valid = QC_OSTIDX; qctl->qc_idx = idx; break; } /* need to also handle a UUID for compatibility */ - valid = qctl->qc_valid = QC_UUID; + param.qp_valid = qctl->qc_valid = QC_UUID; snprintf(obd_uuid, sizeof(*obd_uuid), "%s", optarg); break; case 'P': - show_default = true; + param.qp_show_default = 1; /* fallthrough */ case 'p': qtype = PRJQUOTA; @@ -9712,11 +9766,11 @@ static int lfs_quota(int argc, char **argv) } /* optarg is NULL */ - show_pools = true; + param.qp_show_pools = 1; qctl->qc_cmd = LUSTRE_Q_GETQUOTAPOOL; break; case 'q': - quiet = 1; + param.qp_quiet = 1; break; case 's': if (optarg == NULL || *optarg == '\0') { @@ -9732,11 +9786,15 @@ static int lfs_quota(int argc, char **argv) qctl->qc_cmd = LUSTRE_Q_GETINFO; break; case 'U': - show_default = true; + param.qp_show_default = 1; /* fallthrough */ case 'u': qtype = USRQUOTA; quota_type: + /* + * since ID is not required for when -a or -t is used + * it is only set after all options have been processed + */ if (qctl->qc_type != ALLQUOTA) { fprintf(stderr, "%s quota: only one of -u, -g, or -p may be specified\n", @@ -9747,7 +9805,7 @@ quota_type: qctl->qc_type = qtype; break; case 'v': - verbose = 1; + param.qp_verbose = 1; break; default: fprintf(stderr, "%s quota: unrecognized option '%s'\n", @@ -9757,49 +9815,36 @@ quota_type: } } - /* current uid/gid info for "lfs quota /path/to/lustre/mount" */ - if ((qctl->qc_cmd == LUSTRE_Q_GETQUOTA || - qctl->qc_cmd == LUSTRE_Q_GETQUOTAPOOL) && - qctl->qc_type == ALLQUOTA && - optind == argc - 1 && !show_default) { - qctl->qc_idx = idx; - - for (qtype = USRQUOTA; qtype <= GRPQUOTA; qtype++) { - qctl->qc_type = qtype; - qctl->qc_valid = valid; - if (qtype == USRQUOTA) { - qctl->qc_id = geteuid(); - rc = uid2name(&name, qctl->qc_id); - } else { - qctl->qc_id = getegid(); - rc = gid2name(&name, qctl->qc_id); - memset(&qctl->qc_dqblk, 0, - sizeof(qctl->qc_dqblk)); - } - if (rc) - name = ""; - mnt = argv[optind]; - rc1 = get_print_quota(mnt, name, qctl, verbose, quiet, - human_readable, show_default); - if (rc1 && !rc) - rc = rc1; + if (qctl->qc_cmd == LUSTRE_Q_ITERQUOTA) { + if (qctl->qc_type == ALLQUOTA) { + fprintf(stderr, "%s quota: no quota type to iterate\n", + progname); + rc = CMD_HELP; + return rc; } - goto out; - /* lfs quota -u username /path/to/lustre/mount */ - } else if (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || - qctl->qc_cmd == LUSTRE_Q_GETQUOTAPOOL) { - /* options should be followed by u/g-name and mntpoint */ - if ((!show_default && optind + 2 != argc) || - (show_default && optind + 1 != argc) || - qctl->qc_type == ALLQUOTA) { + + if (end_qid != 0 && start_qid > end_qid) { fprintf(stderr, - "%s quota: name and mount point must be specified\n", + "%s quota: end qid is smaller than start qid\n", progname); rc = CMD_HELP; - goto out; + return rc; } - if (!show_default) { + qctl->qc_allquota_qid_start = start_qid; + qctl->qc_allquota_qid_end = end_qid; + } else if (qctl->qc_type != ALLQUOTA && + (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || + qctl->qc_cmd == LUSTRE_Q_GETQUOTAPOOL)) { + if (!param.qp_show_default) { + if (optind + 1 > argc) { + fprintf(stderr, + "%s quota: u/g-name is required\n", + progname); + rc = CMD_HELP; + return rc; + } + name = argv[optind++]; switch (qctl->qc_type) { case USRQUOTA: @@ -9831,74 +9876,54 @@ quota_type: goto out; } } - } else if (qctl->qc_cmd == LUSTRE_Q_ITERQUOTA) { - if (optind + 1 != argc) { - fprintf(stderr, - "%s quota: mount point must be specified\n", - progname); - rc = CMD_HELP; - goto out; - } + } else if (qctl->qc_type == ALLQUOTA) { + all = true; + qctl->qc_type = USRQUOTA; + } - if (qctl->qc_type == ALLQUOTA) { - fprintf(stderr, "%s quota: no quota type to iterate\n", - progname); - rc = CMD_HELP; - goto out; + do { + if (all) { + qctl->qc_valid = param.qp_valid; + if (qctl->qc_type == USRQUOTA) { + qctl->qc_id = geteuid(); + rc = uid2name(&name, qctl->qc_id); + } else { + qctl->qc_id = getegid(); + rc = gid2name(&name, qctl->qc_id); + memset(&qctl->qc_dqblk, 0, + sizeof(qctl->qc_dqblk)); + } + if (rc) + name = ""; } - if (end_qid != 0 && start_qid > end_qid) { - fprintf(stderr, - "%s quota: end qid is smaller than start qid\n", - progname); - rc = CMD_HELP; - goto out; - } + print_quota_title(name, qctl, ¶m); - qctl->qc_allquota_qid_start = start_qid; - qctl->qc_allquota_qid_end = end_qid; - rc = iter_all_quota(argv[optind], qctl, quiet, human_readable); - goto out; - } else if (optind + 1 != argc || qctl->qc_type == ALLQUOTA) { - fprintf(stderr, "%s quota: missing quota info argument(s)\n", - progname); - rc = CMD_HELP; - goto out; - } + if (optind == argc) { + char mnt[PATH_MAX]; + int i = 0; - mnt = argv[optind]; - if (show_pools) { - char *p; + while (!llapi_search_mounts(NULL, i++, mnt, NULL)) { + if (mnt[0] == '\0') + continue; - i = 0; - rc = llapi_get_poolbuf(mnt, &buf, &poollist, &poolcount); - if (rc) - goto out; + rc = do_quota_op(mnt, qctl, ¶m); + if (rc) + break; - for (i = 0; i < poolcount; i++) { - p = memchr(poollist[i], '.', MAXNAMLEN); - if (!p) { - fprintf(stderr, "bad string format %.*s\n", - MAXNAMLEN, poollist[i]); - rc = -EINVAL; - goto out; + mnt[0] = '\0'; /* avoid matching in next loop */ } - p++; - printf("Quotas for pool: %s\n", p); - snprintf(qctl->qc_poolname, LOV_MAXPOOLNAME + 1, "%s", - p); - rc = get_print_quota(mnt, name, qctl, verbose, quiet, - human_readable, show_default); - if (rc) - break; - } - goto out; - } + } else { + int i = optind; - rc = get_print_quota(mnt, name, qctl, verbose, quiet, - human_readable, show_default); + while (i < argc) { + rc = do_quota_op(argv[i++], qctl, ¶m); + if (rc) + break; + } + } + } while (all && ++qctl->qc_type <= GRPQUOTA); out: - free(buf); free(qctl); return rc; } -- 1.8.3.1