3 # docelic, Sat Dec 22 18:35:37 EST 2007
5 # Script warns people when they're approaching quota. The purpose
6 # is not to 'behave' users but to give them a notice they should
7 # fill quota increase request. (We want to prevent people from
8 # ever actually running into quota limits, especially for Mail
9 # and Database volumes. We use quotas to keep the shop clean, not
10 # to impose restrictions).
12 # Script retrieves volume info from the afs server, from there it
13 # reads mail., user. and db. volumes into %vol, then sees which
14 # volumes have > $threshold usage, relative to volume quota.
16 # For volumes above threshold, it extracts the owner name from
17 # volume name (like, mail.USERNAME) and saves info to %warn.
19 # vos listvol is parsed into structure like this:
21 # $vol{user.docelic}{diskused} = 36718 ( All fields from the
22 # output of vos listvol are loaded into the hash. list of
23 # fields is at the bottom of this script )
25 # people who are over quota are in %warn:
27 # $warn{docelic}{user} = 0.9
28 # $warn{docelic}{mail} = 0.74
30 # "Offenders" are saved to $state{$user} = [ run_number, exp_inc ]
31 # and removed when they resolve their quota situation. For this
32 # to work (and emails to get sent), pass --auto (most certain when
35 # run_number is the run (tick) when they were first spotted to
36 # be over safe value, and exp_inc is the interval at which they
37 # need to be reminded. (We use 1, 2, 4, 8, 16...). Each 'tick'
38 # and number usually refers to 1 day (if ran from cron daily)
40 # In the current implimentation, folks will be reminded on
41 # 1st, 3rd, 7th, 15th and 31st day after their quota overrun.
42 # Then they will be given 2 months of no messages, and then
43 # after 3 months all together, the "warning cycle" will start again.
45 # - If user is over 0.96 on mail or db volume, we will add 2% to his
46 # quota one-time to alleviate immediate problem.
50 use Storable qw
/nstore retrieve/;
51 use Fatal qw
/open write read close/;
52 use Data
::Dumper qw
/Dumper/;
55 our %threshold = ( user
=> 0.8, mail
=> 0.6, db
=> 0.4 );
56 # AFS servers with volumes
57 our @servers = qw
/fritz/;
58 # Which volumes we want?
59 # (Your volumes need to be in format (TYPE).USERNAME, otherwise
60 # you will have to modify below where we take .USERNAME to mean
61 # person's USERNAME). Changing this will also trigger changes
62 # in template email (see DATA below).
63 our $volume_pattern = '(mail|user)\.[a-zA-Z0-9]+'; # implicit ^...$
65 our $statefile = '/var/cache/hcoop/quotacheck';
66 # Hash structure containing load of $statefile
68 # Update cache & send out emails (--auto), or just run terminal report
69 our $auto = grep {/^--auto$/} @ARGV;
70 # Dump statefile in readable format to terminal and exit (--dump)
71 our $dump = grep {/^--dump$/} @ARGV;
73 our $nomail = grep {/^--nomail$/} @ARGV;
74 # E-mail template that is evaled before sending
75 our @template = <DATA
>;
80 # If statefile is there, eventually load it
81 if ( -e
$statefile ) {
82 if ( $auto or $dump) { %state = %{ retrieve
($statefile) } }
86 if ( $dump ) { print Dumper \
%state; exit 0 }
94 # Get volume lists from all servers
96 for (@servers) { push @list, `vos listvol $_ -format` }
99 my $current; # name of volume being parsed
100 my %vol; # will contain hash result of parsing
101 my %warn; # list of people and data for > $threshold folks
102 my %nowarn; # same data as in %warn, but for people NOT over threshold
105 # Copy vol from @list (vos listvol output) into %vol hash.
106 # We read in all values; all values from vol listvol -format
107 # output for a volume are read in. Such as
108 # $vol{user.docelic}{type} = 'RW'
111 my ($parm, $value) = split(/\s+/, $_, 2);
112 defined $parm and defined $value or next;
114 if ( $parm eq 'name' ) {
115 if ( $value =~ /^$volume_pattern$/ ) {
121 $vol{$current}{$parm} = $value
126 # Copy > $threshold people to %warn hash like this:
127 # $warn{docelic}{mail} = 0.9
129 while (my($k,$v) = each %vol) {
130 next unless $$v{maxquota
};
131 my ($type,$user) = split(/\./, $k);
133 my $perc = sprintf('%.3f', $$v{diskused
} / $$v{maxquota
});
135 if ( $perc > $threshold{$type} ) {
136 $warn{$user}{$type} = $perc;
139 # A bit of duplication with %warn, but wth..
140 $nowarn{$user}{$type} = $perc;
144 # Reset counter for people who solved their quota thing somehow
146 my @tmp = keys %state;
147 for my $person ( @tmp ) {
148 next if $person eq '_tick';
149 unless ( $warn{$person} ) {
150 print "User $person resolved quota problem\n";
151 delete $state{$person}
156 # Go over %warn, print info to terminal, update %state
157 # when needed to create new entry for new quota detections.
159 while (my($k,$v) = each %warn) {
162 while (my($k2,$v2) = each %$v) {
163 push @line, "$k2=$v2";
167 # Register person in %state
168 if ( ! $state{$k} ) {
169 $state{$k} = [ $state{_tick
}, 2 ];
176 # See who's entitled to receiving an email at this point;
177 # that is, go over %state, send emails to people who need
178 # to get it in this turn, and exponentially prolong the time
179 # till next message from us.
180 # (They receive message on 1st, 3rd, 7th, 15th, 31st...)
182 while (my($user,$state) = each %state ) {
183 next if $user eq '_tick';
184 #print " $$state[0] + $$state[1] == $state{_tick} + 2\n";
186 if ( $$state[0] + $$state[1] == $state{_tick
} + 2 ) {
188 if ($auto and !$nomail) {
189 print "Notifying $user, next notice in $$state[1] days.\n";
195 # Following code would re-start the notifications we
196 # send to people after 120 days of not clearing
197 # their issue, but let's keep it disabled for
198 # now.. They get a notice on 127th day, and then
199 # on 255th etc., we don't reset the sequence.
201 #$$state[1] >= 120 and do {
202 # $$state[0] = $state{_tick};
211 # Already done above, with also printing a nice
213 #for ( keys %warn ) {
216 #for ( keys %nowarn) {
220 nstore \
%state, $statefile;
225 ##############################################
234 $_ =~ s/\$vol{(\w+)}{(\w+)}/$vol{"$1.$user"}{$2}/mge;
235 $_ =~ s/\$threshold{(\w+)}/$threshold{$1}/mge;
236 $_ =~ s/\$warn{(\w+)}/$nowarn{$user}{$1}/mge;
237 $_ =~ s/\$CRIT{(\w+)}/$warn{$user}{$1} ? " (APPROACHING QUOTA)" : ""/mge;
239 #print join('', @msg);
240 open(MAIL
, "| mail -s 'Approaching quota limit at HCoop' -a 'From: admins\@hcoop.net' -b 'admins\@hcoop.net' '$user\@hcoop.net'");
241 { no warnings
; print main
::MAIL
join('', @msg); }
249 # serv 69.90.123.67 deleuze.hcoop.net
259 # creationDate 1195841197 Fri Nov 23 13:06:37 2007
260 # accessDate 0 Wed Dec 31 19:00:00 1969
261 # updateDate 1198364408 Sat Dec 22 18:00:08 2007
262 # backupDate 1198322931 Sat Dec 22 06:28:51 2007
263 # copyDate 1195843557 Fri Nov 23 13:45:57 2007
267 # minquota 0 (Optional)
270 # weekUse 172438 (Optional)
271 # spare2 0 (Optional)
272 # spare3 0 (Optional)
278 Some of your data volumes at HCoop are approaching quota
281 We
use quotas to
"keep the shop clean", but we regularly
282 grant quota increase requests to all members who submit them
.
284 We wouldn
't want you hit the quota limit, hence the early
285 notice. This is especially true for the mail and database
286 volumes where the data can accumulate "by itself", and where
287 reaching quota limits may be particularly inconvenient.
288 If you plan to store more data, it might be a good
289 time to submit the quota change request through our portal[1].
291 Here are your volume statistics:
294 size=$vol{user}{diskused} KB
295 quota=$vol{user}{maxquota} KB
296 threshold=$threshold{user}
297 use=$warn{user} $CRIT{user}
300 size=$vol{mail}{diskused} KB
301 quota=$vol{mail}{maxquota} KB
302 threshold=$threshold{mail}
303 use=$warn{mail} $CRIT{mail}
305 Please submit your disk quota increase request in a timely
308 [1] https://members.hcoop.net/portal/quota
311 These automatic notices will be sent to you at exponential
312 intervals; that means in 2 days, 4 days, 8 days, 16 days and
313 so on. The method ensures that, if you retain status-quo
314 (don't submit a quota increase
and don
't reduce your data size),
315 you get to receive the reminders less and less often.
316 When and if the usage percentage of all your volumes drops below
317 the corresponding quota thresholds, the notifications will stop