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 deleuze, from there it
13 # reads mail., user. and db. volumes into %vol, then
14 # sees which volumes have > $threshold usage, relative to volume
17 # For volumes above threshold, it extracts the owner name from
18 # volume name (like, mail.USERNAME) and saves info to %warn.
20 # vos listvol is parsed into structure like this:
22 # $vol{user.docelic}{diskused} = 36718 ( All fields from the
23 # output of vos listvol are loaded into the hash. list of
24 # fields is at the bottom of this script )
26 # people who are over quota are in %warn:
28 # $warn{docelic}{user} = 0.9
29 # $warn{docelic}{mail} = 0.74
31 # "Offenders" are saved to $state{$user} = [ run_number, exp_inc ]
32 # and removed when they resolve their quota situation. For this
33 # to work (and emails to get sent), pass --auto (most certain when
36 # run_number is the run (tick) when they were first spotted to
37 # be over safe value, and exp_inc is the interval at which they
38 # need to be reminded. (We use 1, 2, 4, 8, 16...). Each 'tick'
39 # and number usually refers to 1 day (if ran from cron daily)
41 # In the current implimentation, folks will be reminded on
42 # 1st, 3rd, 7th, 15th and 31st day after their quota overrun.
43 # Then they will be given 2 months of no messages, and then
44 # after 3 months all together, the "warning cycle" will start again.
46 # - If user is over 0.96 on mail or db volume, we will add 2% to his
47 # quota one-time to alleviate immediate problem.
51 use Storable qw
/nstore retrieve/;
52 use Fatal qw
/open write read close/;
53 use Data
::Dumper qw
/Dumper/;
56 our %threshold = ( user
=> 0.8, mail
=> 0.6, db
=> 0.4 );
57 # AFS servers with volumes
58 our @servers = qw
/deleuze/;
59 # Which volumes we want?
60 # (Your volumes need to be in format (TYPE).USERNAME, otherwise
61 # you will have to modify below where we take .USERNAME to mean
62 # person's USERNAME). Changing this will also trigger changes
63 # in template email (see DATA below).
64 our $volume_pattern = '(mail|user|db)\.[a-zA-Z0-9]+'; # implicit ^...$
66 our $statefile = '/var/cache/hcoop/quotacheck';
67 # Hash structure containing load of $statefile
69 # Update cache & send out emails (--auto), or just run terminal report
70 our $auto = grep {/^--auto$/} @ARGV;
71 # Dump statefile in readable format to terminal and exit (--dump)
72 our $dump = grep {/^--dump$/} @ARGV;
74 our $nomail = grep {/^--nomail$/} @ARGV;
75 # E-mail template that is evaled before sending
76 our @template = <DATA
>;
81 # If statefile is there, eventually load it
82 if ( -e
$statefile ) {
83 if ( $auto or $dump) { %state = %{ retrieve
($statefile) } }
87 if ( $dump ) { print Dumper \
%state; exit 0 }
95 # Get volume lists from all servers
97 for (@servers) { push @list, `vos listvol $_ -format` }
100 my $current; # name of volume being parsed
101 my %vol; # will contain hash result of parsing
102 my %warn; # list of people and data for > $threshold folks
103 my %nowarn; # same data as in %warn, but for people NOT over threshold
106 # Copy vol from @list (vos listvol output) into %vol hash.
107 # We read in all values; all values from vol listvol -format
108 # output for a volume are read in. Such as
109 # $vol{user.docelic}{type} = 'RW'
112 my ($parm, $value) = split(/\s+/, $_, 2);
113 defined $parm and defined $value or next;
115 if ( $parm eq 'name' ) {
116 if ( $value =~ /^$volume_pattern$/ ) {
122 $vol{$current}{$parm} = $value
127 # Copy > $threshold people to %warn hash like this:
128 # $warn{docelic}{mail} = 0.9
130 while (my($k,$v) = each %vol) {
131 next unless $$v{maxquota
};
132 my ($type,$user) = split(/\./, $k);
134 my $perc = sprintf('%.3f', $$v{diskused
} / $$v{maxquota
});
136 if ( $perc > $threshold{$type} ) {
137 $warn{$user}{$type} = $perc;
140 # A bit of duplication with %warn, but wth..
141 $nowarn{$user}{$type} = $perc;
145 # Reset counter for people who solved their quota thing somehow
147 my @tmp = keys %state;
148 for my $person ( @tmp ) {
149 next if $person eq '_tick';
150 unless ( $warn{$person} ) {
151 print "User $person resolved quota problem\n";
152 delete $state{$person}
157 # Go over %warn, print info to terminal, update %state
158 # when needed to create new entry for new quota detections.
160 while (my($k,$v) = each %warn) {
163 while (my($k2,$v2) = each %$v) {
164 push @line, "$k2=$v2";
168 # Register person in %state
169 if ( ! $state{$k} ) {
170 $state{$k} = [ $state{_tick
}, 2 ];
177 # See who's entitled to receiving an email at this point;
178 # that is, go over %state, send emails to people who need
179 # to get it in this turn, and exponentially prolong the time
180 # till next message from us.
181 # (They receive message on 1st, 3rd, 7th, 15th, 31st...)
183 while (my($user,$state) = each %state ) {
184 next if $user eq '_tick';
185 #print " $$state[0] + $$state[1] == $state{_tick} + 2\n";
187 if ( $$state[0] + $$state[1] == $state{_tick
} + 2 ) {
189 if ($auto and !$nomail) {
190 print "Notifying $user, next notice in $$state[1] days.\n";
196 # Following code would re-start the notifications we
197 # send to people after 120 days of not clearing
198 # their issue, but let's keep it disabled for
199 # now.. They get a notice on 127th day, and then
200 # on 255th etc., we don't reset the sequence.
202 #$$state[1] >= 120 and do {
203 # $$state[0] = $state{_tick};
212 # Already done above, with also printing a nice
214 #for ( keys %warn ) {
217 #for ( keys %nowarn) {
221 nstore \
%state, $statefile;
226 ##############################################
235 $_ =~ s/\$vol{(\w+)}{(\w+)}/$vol{"$1.$user"}{$2}/mge;
236 $_ =~ s/\$threshold{(\w+)}/$threshold{$1}/mge;
237 $_ =~ s/\$warn{(\w+)}/$nowarn{$user}{$1}/mge;
238 $_ =~ s/\$CRIT{(\w+)}/$warn{$user}{$1} ? " (APPROACHING QUOTA)" : ""/mge;
240 #print join('', @msg);
241 open(MAIL
, "| mail -s 'Approaching quota limit at HCoop' -a 'From: admins\@hcoop.net' -b 'docelic' '$user\@hcoop.net'");
242 { no warnings
; print main
::MAIL
join('', @msg); }
250 # serv 69.90.123.67 deleuze.hcoop.net
260 # creationDate 1195841197 Fri Nov 23 13:06:37 2007
261 # accessDate 0 Wed Dec 31 19:00:00 1969
262 # updateDate 1198364408 Sat Dec 22 18:00:08 2007
263 # backupDate 1198322931 Sat Dec 22 06:28:51 2007
264 # copyDate 1195843557 Fri Nov 23 13:45:57 2007
268 # minquota 0 (Optional)
271 # weekUse 172438 (Optional)
272 # spare2 0 (Optional)
273 # spare3 0 (Optional)
279 Some of your data volumes at HCoop are approaching quota
282 We
use quotas to
"keep the shop clean", but we regularly
283 grant quota increase requests to all members who submit them
.
285 We wouldn
't want you hit the quota limit, hence the early
286 notice. This is especially true for the mail and database
287 volumes where the data can accumulate "by itself", and where
288 reaching quota limits may be particularly inconvenient.
289 If you plan to store more data, it might be a good
290 time to submit the quota change request through our portal[1].
292 Here are your volume statistics:
295 size=$vol{user}{diskused} KB
296 quota=$vol{user}{maxquota} KB
297 threshold=$threshold{user}
298 use=$warn{user} $CRIT{user}
301 size=$vol{mail}{diskused} KB
302 quota=$vol{mail}{maxquota} KB
303 threshold=$threshold{mail}
304 use=$warn{mail} $CRIT{mail}
307 size=$vol{db}{diskused} KB
308 quota=$vol{db}{maxquota} KB
309 threshold=$threshold{db}
310 use=$warn{db} $CRIT{db}
312 Please submit your disk quota increase request in a timely
315 [1] https://members.hcoop.net/portal/quota
318 These automatic notices will be sent to you at exponential
319 intervals; that means in 2 days, 4 days, 8 days, 16 days and
320 so on. The method ensures that, if you retain status-quo
321 (don't submit a quota increase
and don
't reduce your data size),
322 you get to receive the reminders less and less often.
323 When and if the usage percentage of all your volumes drops below
324 the corresponding quota thresholds, the notifications will stop