Project

General

Profile

Download (46.6 KB) Statistics
| Branch: | Revision:
1
#!/usr/bin/perl
2

    
3
# All rights reserved and Copyright (c) 2020 Origo Systems ApS.
4
# This file is provided with no warranty, and is subject to the terms and conditions defined in the license file LICENSE.md.
5
# The license file is part of this source code package and its content is also available at:
6
# https://www.origo.io/info/stabiledocs/licensing/stabile-open-source-license
7

    
8
BEGIN {
9
    open STDERR, '>>', '/dev/null' or die "Couldn't redirect STDERR: $!";
10
}
11

    
12
package Stabile::Piston;
13

    
14
use Error qw(:try);
15
use Socket;
16
use Data::UUID;
17
use File::Basename;
18
use Time::Local;
19
use Time::HiRes qw( time );
20
use LWP::Simple;
21
use lib dirname (__FILE__) . "/../cgi";
22
use Stabile;
23

    
24
$q = new CGI;
25
%params = $q->Vars;
26

    
27
my $servername = $ENV{'SERVER_NAME'};
28
$servername = "localhost" unless $servername;
29
my $serverip = scalar(inet_ntoa(inet_aton($servername)));
30

    
31
$backupdir = $Stabile::config->get('STORAGE_BACKUPDIR') || "/mnt/stabile/backups";
32
my $engineid = $Stabile::config->get('ENGINEID') || "";
33
#my $enginelinked = $Stabile::config->get('ENGINE_LINKED') || "";
34
$brutalsleep = $Stabile::config->get('BRUTAL_SLEEP') || "";
35
$amtpasswd = $Stabile::config->get('AMT_PASSWD') || "";
36

    
37
try {
38
	my $logentry = "";
39
	my @keys = keys %params;
40
	my @values = values %params;
41
	while ($#keys >= 0)
42
	{
43
		$key = pop(@keys); $value = pop(@values);
44
		$logentry .= "$key: $value; ";
45
	}
46
	$logentry .= "REMOTE_ADDR: $ENV{'REMOTE_ADDR'}; Time: $current_time";
47
	# $main::syslogit->('--', 'debug', $logentry);
48

    
49
	my $status = $params{'status'};
50
	my ($user, $uitab, $uiuuid, $uistatus, $plogentry) = split(/: /, uri_unescape($params{'logentry'}));
51
	my $uipath;
52

    
53
# We got a request for clearing the local log file
54
	if ($status eq "clearlog") {
55
		unlink $logfile;
56
		print "\nStatus=OK Log cleared\n";
57
		print end_html(), "\n";
58
		return;
59
	}
60

    
61
    my $mac = uri_unescape($params{'mac'});
62
	$mac =~ tr/[A-Z]/[a-z]/;
63
	$mac =~ s/:/-/g;	
64
	unless ($status eq 'permitopen' || $status eq 'listimagemaster' || $mac =~ /^(\S{2}-\S{2}-\S{2}-\S{2}-\S{2}-\S{2})$/) {throw Error::Simple ("Status=Error invalid mac address: $mac for $id $ENV{'REMOTE_ADDR'}")};
65
	my $filename = $1; # $filename now untainted
66
	my $file = "/mnt/stabile/tftp/pxelinux.cfg/01-$filename";
67
	$mac =~ s/-//g;
68

    
69
	my $ipmiip;
70
	$ipmiip = uri_unescape($params{'ipmiip'}) if ($params{'ipmiip'});
71
	
72
	unless (tie %register,'Tie::DBI', {
73
		db=>'mysql:steamregister',
74
		table=>'nodes',
75
		key=>'mac',
76
		autocommit=>0,
77
		CLOBBER=>3,
78
		user=>$dbiuser,
79
		password=>$dbipasswd}) {throw Error::Simple("Status=Error Register could not be accessed")};
80

    
81
	unless (tie %domreg,'Tie::DBI', {
82
		db=>'mysql:steamregister',
83
		table=>'domains',
84
		key=>'uuid',
85
		autocommit=>0,
86
		CLOBBER=>3,
87
		user=>$dbiuser,
88
		password=>$dbipasswd}) {throw Error::Simple("Status=Error Register could not be accessed")};
89

    
90
    unless (tie %imagereg,'Tie::DBI', {
91
        db=>'mysql:steamregister',
92
        table=>'images',
93
        key=>'path',
94
        autocommit=>0,
95
        CLOBBER=>3,
96
        user=>$dbiuser,
97
        password=>$dbipasswd}) {throw Error::Simple("Status=Image register could not be accessed")};
98

    
99
	unless (tie %idreg,'Tie::DBI', {
100
		db=>'mysql:steamregister',
101
		table=>'nodeidentities',
102
		key=>'identity',
103
		autocommit=>0,
104
		CLOBBER=>3,
105
		user=>$dbiuser,
106
		password=>$dbipasswd}) {throw Error::Simple("Status=Error Register could not be accessed")};
107

    
108
	if ($uiuuid) {
109
        if ($uitab eq 'images' && $imagereg{$uiuuid}) { # We got a path
110
            $uipath = $uiuuid;
111
            $uiuuid = $imagereg{$uipath}->{'uuid'};
112
        } else {
113
            $uiuuid =~ tr/[A-Z]/[a-z]/;
114
            $uiuuid =~ s/\%3a//g;
115
        }
116
	} else {
117
        $uiuuid = $mac;
118
	}
119

    
120

    
121
	if ($status eq "joining" && $mac) {
122
		print header(),
123
		     start_html('Updating Stabile node...'),
124
		     h1('Examining piston request...'),
125
		     hr;
126
		# A new node is trying to join
127
		# First find out which kind of nodes are needed
128

    
129
		my $id = $idreg{'default'}->{'hypervisor'};
130
		my $dist = $idreg{'default'}->{'dist'};
131
		my $path = $idreg{'default'}->{'path'};
132
		my $kernel = $idreg{'default'}->{'kernel'};
133
        $kernel = "-$kernel" if ($kernel);
134
#		untie %idreg;
135
		my $bootentry;
136
		
137
		unless ($dist) {$dist = "lucid"};
138

    
139
		unless (open(TEMP2, ">$file")) {throw Error::Simple("Status=Error boot file \"$file\" could not be created")};
140
		if ($dist eq 'lucid') {
141
			$bootentry = <<ENDBOOT;
142
prompt 0
143
default Stabile Node
144
label Stabile Node
145
kernel vmlinuz$kernel
146
ipappend 2
147
append initrd=initrd.img$kernel ro nomodeset root=/dev/nfs nfsroot=$serverip:$path netboot=nfs union=aufs boot=live ip=dhcp identity=$id acpi=force console=ttyS4,115200n81 console=ttyS1,115200n81 console=tty0 ipv6.disable=1 intel_iommu=on
148
ENDBOOT
149

    
150
    		print TEMP2 $bootentry . "\n";
151
	    	close(TEMP2);
152
		} elsif ($dist) {
153
			$bootentry = <<ENDBOOT;
154
prompt 0
155
default Stabile Node
156
label Stabile Node
157
kernel vmlinuz$kernel
158
ipappend 2
159
append initrd=initrd.img$kernel ro nomodeset root=/dev/nfs nfsroot=$serverip:$path netboot=nfs union=aufs boot=casper ip=dhcp identity=$id acpi=force console=ttyS4,115200n81 console=ttyS1,115200n81 console=tty0 ipv6.disable=1 intel_iommu=on disable_mtrr_cleanup
160
ENDBOOT
161

    
162
			print TEMP2 $bootentry . "\n";
163
			close(TEMP2);
164
		} else {throw Error::Simple("Status=Error no default node identity")};
165

    
166
		my $macname = $mac;
167
        $macname = $register{$mac}->{'name'} if ($register{$mac});
168
        $register{$mac} = {
169
            identity=>$id,
170
            timestamp=>$current_time,
171
            ip=>$ENV{'REMOTE_ADDR'},
172
            name=>$macname,
173
            cpucores=>$params{'cpucores'},
174
            cpucount=>$params{'cpucount'},
175
            cpuspeed=>$params{'cpuspeed'},
176
            cpuname=>uri_unescape($params{'cpuname'}),
177
            cpufamily=>$params{'cpufamily'},
178
            cpumodel=>$params{'cpumodel'},
179
            memtotal=>$params{'memtotal'},
180
            memfree=>$params{'memfree'},
181
            stortotal=>$params{'stortotal'},
182
            storfree=>$params{'storfree'},
183
            status=>$status,
184
            ipmiip=>$ipmiip
185
		};
186
		tied(%register)->commit;
187
		print "\nAssimilation=OK $mac\n";
188
		print end_html(), "\n";
189

    
190
# We got a request for updating a user's UI
191
	} elsif ($status eq "updateui") {
192
		print header();
193
		if ($user && $uitab eq "images" && $uiuuid && !($uistatus =~ /backingup/)) {
194
            $imagereg{$uipath}->{'status'} = $uistatus;
195
            tied(%imagereg)->commit();
196
            if ($plogentry =~ /Backed up/) { # An image was backed up from the node
197
                $imagereg{$uipath}->{'btime'} = $current_time;
198
                my $imguser = $imagereg{$uipath}->{'user'};
199
                my($fname, $dirpath, $suffix) = fileparse($uipath, (".vmdk", ".img", ".vhd", ".qcow", ".qcow2", ".vdi", ".iso"));
200
                my $subdir = "";
201
                if ($dirpath =~ /\/$user(\/.+)\//) {
202
                    $subdir = $1;
203
                }
204
                my $backupsize = getBackupSize($subdir, "$fname$suffix", $imguser);
205
                updateImageBilling($user, $uipath, "backed up", $backupsize);
206
            }
207
            if ($plogentry) {
208
				if ($plogentry =~ /Backup aborted/) {
209
					# A backup has been aborted - possibly a node was rebooted - update image status
210
					$Stabile::Images::user = $user;
211
					$Stabile::Images::console = 1;
212
					require "$Stabile::basedir/cgi/images.cgi";
213
					my $res = Stabile::Images::Updateregister($uipath, 'updateregister');
214
					$main::syslogit->($user, 'info', "Updated image status - $user, $uipath, $res");
215
					$uistatus = $res if ($res);
216
				}
217
				my $upd = {user=>$user, uuid=>$uiuuid, status=>$uistatus, message=>$plogentry, type=>'update', tab=>'images'};
218
				$upd->{'backup'} = $uipath if ($plogentry =~ /Backed up/);
219
				$main::updateUI->($upd);
220
                $main::syslogit->($user, 'info', "$plogentry $uiuuid ($uitab, $uistatus)");
221
                $plogentry = "";
222
            }
223
        }
224
# List the master associated with an image if any
225
	} elsif ($status eq "listimagemaster") {
226
		print header('text/xml');
227
		my $path = $params{'image'};
228
		$path = uri_unescape($path);
229
		my $master = $imagereg{$path}->{'master'};
230
		$master = uri_escape($master);
231
        print $master;
232
# We got a request for listing a domains xml description
233
	} elsif ($status eq "listxml") {
234
		print header('text/xml');
235
		my %xmlreg;
236
		unless (tie %xmlreg,'Tie::DBI', {
237
			db=>'mysql:steamregister',
238
			table=>'domainxml',
239
			key=>'uuid',
240
			autocommit=>0,
241
			CLOBBER=>3,
242
			user=>$dbiuser,
243
			password=>$dbipasswd}) {throw Error::Simple("Status=Error Register could not be accessed")};
244

    
245
		my $uuid = $params{'uuid'};
246
		unless ((defined $uuid) && ($uuid =~ /^(\S{8}-\S{4}-\S{4}-\S{4}-\S{12})$/)) {throw Error::Simple ("Status=Error invalid uuid: $uuid")};
247
		my $xml = $xmlreg{$uuid}->{'xml'};
248
		print uri_unescape($xml);
249
		untie %xmlreg;
250

    
251
# Update sshd_config to allow ssh port forwarding to consoles of a users vm's
252
	} elsif ($status eq "permitopen") {
253
		print header;
254
		my $user = $params{'user'};
255
        $user =~ /(.+)/; $user = $1; #untaint
256
		print start_html('Opening ports...');
257
		permitOpen($user);
258
		print end_html();
259

    
260
# A node is updating it's status
261
	} else {
262
		print header(),
263
		     start_html('Updating Stabile node...'),
264
		     h1('Examining piston request...'),
265
		     hr;
266
		# Look for action requests (from users)
267
		$action = $register{$mac}->{'action'};
268

    
269
        # Look for node tasks, only post requests, get requests generally only update this side
270
        if ($ENV{'REQUEST_METHOD'} eq 'POST') {
271
            $tasks = $register{$mac}->{'tasks'};
272
            $register{$mac}->{'tasks'} = '';
273
            tied(%register)->commit;
274
#            chomp $tasks;
275
        }
276

    
277
		$maintenance = $register{$mac}->{'maintenance'};
278
		# If the node is shutting down or joining, don't reboot it
279
		if ($status eq "shutdown" || $status eq "joining") {
280
			$action = "";
281
		}
282
		my $dbstatus = $register{$mac}->{'status'};
283
		my $macname = $register{$mac}->{'name'};
284
		my $nodestatus = $status;
285
        $nodestatus = 'maintenance' if ($status eq 'running' && $maintenance);
286
		if (($dbstatus eq "maintenance" && $status ne "drowsing") || $dbstatus eq "sleeping" || $dbstatus eq "shuttingdown" || !$status || $status eq '--') {
287
            $nodestatus = $dbstatus;
288
		} elsif ( $status eq 'drowsing' && ($dbstatus eq 'running' || $dbstatus eq 'maintenance')) {
289
            if ($brutalsleep && (
290
                    ($register{$mac}->{'amtip'} && $register{$mac}->{'amtip'} ne '--')
291
                || ($register{$mac}->{'ipmiip'} && $register{$mac}->{'ipmiip'} ne '--')
292
                )) {
293
                my $sleepcmd;
294
                $uistatus = "asleep";
295
                print  "\nStatus=SWEETDREAMS";
296
                sleep 2;
297
                if ($register{$mac}->{'amtip'} && $register{$mac}->{'amtip'} ne '--') {
298
                    $sleepcmd = "echo 'y' | AMT_PASSWORD='$amtpasswd' /usr/bin/amttool $register{$mac}->{'amtip'} powerdown";
299
                } else {
300
                    $sleepcmd = "ipmitool -I lanplus -H $register{$mac}->{'ipmiip'} -U ADMIN -P ADMIN power off";
301
                }
302
                my $logmsg = "Node $mac marked for drowse ";
303
                $logmsg .= `$sleepcmd`;
304
                $logmsg =~ s/\n/ /g;
305
                $main::syslogit->('--', "info", $logmsg);
306
            }
307
            $nodestatus = 'asleep';
308
		}
309

    
310
        my %billing;
311

    
312
	# Look for info on whether if this node is waiting to receive vm's and activate the sender
313
        my $receive = uri_unescape($params{'receive'});
314
        if ($receive) {
315
            @uuids = split(/, */,$receive);
316
            foreach my $uuid (@uuids) {
317
                # Sender is the current node/mac running the vm
318
                my $sendmac = $domreg{$uuid}->{'mac'};
319
                my $rip = $register{$mac}->{'ip'};
320
                my $sendtasks = "MOVE $uuid $rip $mac $user\n". $register{$sendmac}->{'tasks'};
321
                chop $sendtasks;
322
                $register{$sendmac}->{'tasks'} .= $sendtasks;
323
            }
324
        }
325
        my $returntasks = uri_unescape($params{'returntasks'});
326
        if ($returntasks && $returntasks ne "--") {
327
            $register{$mac}->{'tasks'} .= $returntasks; # Some tasks have failed, try again
328
        }
329

    
330
        # Don't update anything for node feedbacks from actions
331
        if ($status ne '--'
332
            && $status ne 'asleep'
333
            && $status ne 'awake'
334
            && $status ne 'shutdown'
335
            && $status ne 'reboot'
336
            && $status ne 'unjoin'
337
            && $status ne 'permitopen'
338
            && $status ne 'reload'
339
        ) {
340
    # Update basic parameters
341
            my $memfree = $params{'memfree'} || $register{$mac}->{'memfree'};
342
            my $memtotal = $params{'memtotal'} || $register{$mac}->{'memtotal'};
343
            my $cpuload = $params{'cpuload'} || $register{$mac}->{'cpuload'};
344
            my $cpucount = $params{'cpucount'} || $register{$mac}->{'cpucount'};
345
            my $cpucores = $params{'cpucores'} || $register{$mac}->{'cpucores'};
346
            my $nfsroot = uri_unescape($params{'nfsroot'}) || $register{$mac}->{'nfsroot'};
347
            my $kernel = uri_unescape($params{'kernel'}) || $register{$mac}->{'kernel'};
348
            my $reservedvcpus = 0;
349

    
350
            $register{$mac} = {
351
                timestamp=>$current_time,
352
                identity=>$params{'identity'},
353
                ip=>$ENV{'REMOTE_ADDR'},
354
                status=>$nodestatus,
355
                memfree=>$memfree,
356
                memtotal=>$memtotal,
357
                cpuload=>$cpuload,
358
                cpucount=>$cpucount,
359
                cpucores=>$cpucores,
360
    #            reservedvcpus=>0,
361
                nfsroot=>$nfsroot,
362
                kernel=>$kernel,
363
                action=>""
364
            };
365

    
366
            if ($ipmiip) {
367
                $register{$mac}->{'ipmiip'} = $ipmiip;
368
            }
369
            if ($params{'stortotal'} || $params{'stortotal'} eq "0") {
370
                $register{$mac}->{'stortotal'} = $params{'stortotal'};
371
                $register{$mac}->{'storfree'} = $params{'storfree'};
372
                $register{$mac}->{'stor'} = $params{'stor'};
373
            }
374
            tied(%register)->commit;
375

    
376
    # Look for supplied info on domains running on this node, and locally stored images, and update db
377
            my @keys = keys %params;
378
            my @values = values %params;
379
            my $vmvcpus = 0;
380
            my $vms = 0;
381
            my $vmuuids;
382
            my $vmnames;
383
            my $vmusers;
384
            my %reportedimgs;
385
            my $ug = new Data::UUID;
386
            my %nodedomains;
387
            while ($#keys >= 0)
388
            {
389
                $key = pop(@keys); $value = pop(@values);
390
                if ($key =~ m/dom(\d+)/) {
391
                    my $i = $1;
392
                    my $domstatus = $params{"domstate$i"};
393
                    $domreg{$value}->{'statustime'} = $current_time unless ($domreg{$value}->{'statustime'});
394
                    my $statedelta = $current_time - $domreg{$value}->{'statustime'}; # The number of seconds domain has been in same state
395
                    my $domdisplay = $params{"domdisplay$i"};
396
                    my $domport = $params{"domport$i"};
397
                    my $dbdomstatus = $domreg{$value}->{'status'};
398
                    my $dbdommac = $domreg{$value}->{'mac'};
399
                    my $dommac = $mac;
400
                    my $duser = $domreg{$value}->{'user'};
401
                    $nodedomains{$value} = 1;
402
                    $vms++;
403
                    $vmuuids .= "$value, ";
404
                    $vmnames .= "$domreg{$value}->{'name'}, ";
405
                    $vmusers .= "$domreg{$value}->{'user'}, ";
406
                    # Domain status has changed, evaluate if it warrants a ui update
407
                    if ($dbdomstatus eq 'moving') {
408
    #				    $main::syslogit->($user, 'info', "MOVING: $domstatus/$dommac, $dbdomstatus/$dbdommac");
409
                    }
410
                    if ($dbdomstatus && $domstatus && ($dbdomstatus ne $domstatus)) {
411
                        # Transitional states like shuttingdown are not reported by hypervisor
412
                        # we only update db with permanent states when exiting a transitional hypervisor state or
413
                        # too much time has passed
414
                        if (($dbdomstatus eq "shuttingdown" && $domstatus eq "running" && $statedelta<120)
415
                            || ($dbdomstatus eq "starting" && $domstatus eq "inactive" && $statedelta<30)
416
                            || ($dbdomstatus eq "starting" && $domstatus eq "shutdown" && $statedelta<30)
417
                            || ($dbdomstatus eq "starting" && $domstatus eq "shutoff" && $statedelta<30)
418
                            || ($dbdomstatus eq "suspending" && $domstatus eq "running" && $statedelta<30)
419
                            || ($dbdomstatus eq "resuming" && $domstatus eq "paused" && $statedelta<30)
420
                        # When moving $dbdommac is the originating mac, wait 5 min for moves
421
                            || ($dbdomstatus eq "moving" && $domstatus eq "running" && $dbdommac eq $mac && $statedelta<300)
422
                            || ($dbdomstatus eq "moving" && $domstatus eq "paused" && $dbdommac ne $mac && $statedelta<300)
423
                            || ($dbdomstatus eq "moving" && $domstatus eq "shutoff" && $dbdommac eq $mac && $statedelta<300)
424
                            || ($domstatus eq "nostate")
425
                            || ($dbdomstatus eq "destroying" && $domstatus eq "running" && $statedelta<30)
426
                            || ($dbdomstatus eq "destroying" && $domstatus eq "paused" && $statedelta<30)
427
                            || ($dbdomstatus eq "upgrading" && $statedelta<600)
428
                        ) {
429
                            $domstatus = $dbdomstatus;
430
                            $dommac = $dbdommac;
431
                        } else {
432
                        # We have exited from a transition, update the UI
433
                            $domreg{$value}->{'statustime'} = $current_time;
434
                            $billing{$duser}->{'event'} .= "$domstatus $value\n";
435
                            $main::updateUI->({tab=>"servers", user=>"$duser", uuid=>$value, status=>$domstatus,
436
                                                mac=>$mac, macname=>$macname});
437
                            if ($enginelinked && $engineid) {
438
                                my $sysuuid = $domreg{$value}->{'uuid'};
439
                                my $sysstatus = $domstatus;
440
                                if ($domreg{$value}->{'system'} && $domreg{$value}->{'system'} ne '--') { # This is a system
441
                                    $sysuuid = $domreg{$value}->{'system'};
442
                                    unless (tie %sysreg,'Tie::DBI', {
443
                                        db=>'mysql:steamregister',
444
                                        table=>'systems',
445
                                        key=>'uuid',
446
                                        autocommit=>0,
447
                                        CLOBBER=>3,
448
                                        user=>$dbiuser,
449
                                        password=>$dbipasswd}) {throw Error::Simple("Status=ERROR System register could not be accessed")};
450
                                    # Check if we are dealing with the admin server
451
                                    if ($domreg{$value}->{'image'} ne $sysreg{$sysuuid}->{'image'}) {
452
                                        $sysuuid = '';
453
                                    }
454

    
455
                                    untie %sysreg;
456
                                }
457
                                if ($sysuuid) {
458
                                my $json_text = <<END
459
{"uuid": "$sysuuid" , "status": "$sysstatus"}
460
END
461
;
462
                                    print "\n" . $main::postAsyncToOrigo->($engineid, 'updateapps', "[$json_text]") . "\n";
463
                                }
464
                            }
465
                        }
466
                    }
467

    
468
                    # If a domain is shutoff or state is undetermined, dont't count it in billing
469
                    # if ($domstatus eq "shutoff" || $domstatus eq "inactive" ) {
470
                    if ($domstatus eq "shutoff" || $domstatus eq "inactive" ) {
471
                        $billing{$duser}->{'vcpu'} += 0;
472
                        $billing{$duser}->{'memory'} += 0;
473
                    # All other states count
474
                    } else {
475
                        $billing{$duser}->{'vcpu'} += $domreg{$value}->{'vcpu'};
476
                        $billing{$duser}->{'memory'} += $domreg{$value}->{'memory'};
477
                    }
478
                    # We don't update timestamp for moving domains, so if move fails, eventually they will be marked as inactive
479
                    my $timestamp = $current_time;
480
                    $timestamp = $domreg{$value}->{'timestamp'} if ($domstatus eq "moving");
481
                    $domreg{$value} = {
482
                        status=>$domstatus,
483
                        mac=>$dommac,
484
                        macname=>$register{$dommac}->{'name'},
485
                        macip=>$register{$dommac}->{'ip'},
486
                        maccpucores=>$register{$dommac}->{'cpucores'},
487
                        timestamp=>$timestamp
488
                    };
489
                    $domreg{$value}->{'mac'} = $dommac unless ($domstatus eq 'moving');
490
                    $domreg{$value}->{'display'} = $domdisplay if $domdisplay;
491
                    $domreg{$value}->{'port'} = $domport if $domport;
492
                    if ($params{"domstate$i"} eq 'running') {$vmvcpus += $domreg{$value}->{'vcpu'}};
493
                # If a domain was moved, update permitted ports
494
                    if (($dbdomstatus eq "moving" && $domstatus eq "running" && $dbdommac ne $mac)) {
495
                        $main::syslogit->($duser, 'info', "Moved $domreg{$value}->{'name'} ($value) to $register{$dommac}->{'name'}");
496
                        permitOpen($duser);
497
                    }
498
                # Update status of server's images
499
                    my $image = $domreg{$value}->{'image'};
500
                    my $image2 = $domreg{$value}->{'image2'};
501
                    my $image3 = $domreg{$value}->{'image3'};
502
                    my $image4 = $domreg{$value}->{'image4'};
503
                    my $imgstatus = 'active'; # if server is running, moving, etc.
504
                    if ($domstatus eq 'paused') {
505
                        $imgstatus = 'paused'
506
                    } elsif ($domstatus eq "shutoff" || $domstatus eq "inactive")  {
507
                        $imgstatus = 'used'
508
                    }
509
                    print "$image for $domreg{$value}->{name} not in DB" unless ($imagereg{$image});
510
                    $imagereg{$image}->{'status'} = $imgstatus if ($imagereg{$image} && $imagereg{$image}->{'status'} !~ /backingup/);
511
                    $imagereg{$image2}->{'status'} = $imgstatus if ($image2 && $imagereg{$image2} && $image2 ne '--' && $imagereg{$image2}->{'status'} !~ /backingup/);
512
                    $imagereg{$image3}->{'status'} = $imgstatus if ($image3 && $imagereg{$image3} && $image3 ne '--' && $imagereg{$image3}->{'status'} !~ /backingup/);
513
                    $imagereg{$image4}->{'status'} = $imgstatus if ($image4 && $imagereg{$image4} && $image4 ne '--' && $imagereg{$image4}->{'status'} !~ /backingup/);
514

    
515
                } elsif ($key =~ m/img(\d+)/) {
516
            # The node is reporting about a locally stored image
517
                    my $f = uri_unescape($value);
518
                    my $size = $params{"size$1"};
519
                    my $realsize = $params{"realsize$1"};
520
                    my $virtualsize = $params{"virtualsize$1"};
521
                    my($fname, $dirpath, $suffix) = fileparse($f, (".vmdk", ".img", ".vhd", ".qcow", ".qcow2", ".vdi", ".iso"));
522
                    my $regimg = $imagereg{$f};
523
                    my $uuid = $regimg->{'uuid'};
524

    
525
                    my $storagepool = -1;
526
                    $f =~ m/\/mnt\/stabile\/node\/(.+?)\/.+/; # ungready matching
527
                    my $imguser = $1;
528

    
529
            # Create a new uuid if we are dealing with a new file in the file-system
530
                    if (!$uuid) {
531
                        $uuid = $ug->create_str() unless ($uuid);
532
                        $main::syslogit->($imguser, 'info', "Assigned new uuid $uuid to $f belonging to $imguser");
533
                    }
534

    
535
                    my $mtime = $newmtime || $regimg->{'mtime'};
536
                    my $name = $regimg->{'name'} || $fname;
537

    
538
                    my $subdir = "";
539
                    if ($dirpath =~ /\/$imguser(\/.+)\//) {
540
                        $subdir = $1;
541
                    }
542
                    my $bdu;
543
                    my $backupsize = 0;
544
                    my $imgpath = "$fname$suffix";
545
                    $imgpath = $1 if $cmdpath =~ /(.+)/; # untaint
546
                    $backupsize = getBackupSize($subdir, $imgpath, $imguser);
547
            # If image on node is attached to a domain, reserve vcpus for starting domain on node
548
                    my $imgdom = $regimg->{'domains'};
549
                    if ($imgdom && $domreg{$imgdom}) {
550
                        my $imgvcpus = $domreg{$imgdom}->{'vcpu'};
551
                        my $imgdomstatus = $domreg{$imgdom}->{'status'};
552
                        $reservedvcpus += $imgvcpus if ($imgdomstatus eq 'shutoff' || $imgdomstatus eq 'inactive');
553
                    }
554

    
555
                    $reportedimgs{$f} = 1;
556
                    if (($regimg->{'virtualsize'} == 0 && $virtualsize) || $regimg->{'status'} eq 'moving') {
557
                        $reportedimgs{$f} = 2; # Mark that we should update the UI - this is a recently transferred image
558
                    }
559
                    if ($f && $imguser) {
560
                        my $imgstatus = $regimg->{'status'};
561
                        # This only happens first time after an image has been transferred manually to a node
562
                        if (!$imgstatus || $imgstatus eq '--' || $imgstatus eq 'cloning') {
563
                            $imgstatus = "unused";
564
                            my $imgdomains = $regimg->{'domains'};
565
                            my $imgdomainnames = $regimg->{'domainnames'};
566
                            (tied %domreg)->select_where("user = '$imguser' or user = 'common'") unless ($fulllist);
567
                            foreach my $dom (values %domreg) {
568
                                my $img = $dom->{'image'};
569
                                my $img2 = $dom->{'image2'};
570
                                my $img3 = $dom->{'image3'};
571
                                my $img4 = $dom->{'image4'};
572
                                if ($f eq $img || $f eq $img2 || $f eq $img3 || $f eq $img4) {
573
                                    $imgstatus = "active";
574
                                    my $domstatus = $dom->{'status'};
575
                                    if ($domstatus eq "shutoff" || $domstatus eq "inactive") {$imgstatus = "used";}
576
                                    elsif ($domstatus eq "paused") {$imgstatus = "paused";}
577
                                    $imgdomains = $dom->{'uuid'};
578
                                    $imgdomainnames = $dom->{'name'};
579
                                };
580
                            }
581
                            $imagereg{$f} = {
582
                                user=>$imguser,
583
                                type=>substr($suffix,1),
584
                                size=>$size,
585
                                realsize=>$realsize,
586
                                virtualsize=>$virtualsize,
587
                                backupsize=>$backupsize,
588
                                name=>$name,
589
                                uuid=>$uuid,
590
                                storagepool=>$storagepool,
591
                                mac=>$mac,
592
                                mtime=>$mtime,
593
                                status=>$imgstatus,
594
                                domains=>$imgdomains,
595
                                domainnames=>$imgdomainnames
596
                            }
597
                        } else {
598
                            $imagereg{$f} = {
599
                                user=>$imguser,
600
                                type=>substr($suffix,1),
601
                                size=>$size,
602
                                realsize=>$realsize,
603
                                virtualsize=>$virtualsize,
604
                                backupsize=>$backupsize,
605
                                name=>$name,
606
                                uuid=>$uuid,
607
                                storagepool=>$storagepool,
608
                                mac=>$mac,
609
                                mtime=>$mtime
610
                            }
611
                        }
612
                    }
613

    
614
                }
615
            }
616

    
617
            if ($params{'dominfo'} || $params{'dom1'}) {
618
                $register{$mac}->{'vms'} = $vms;
619
                $register{$mac}->{'vmvcpus'} = $vmvcpus;
620
                $register{$mac}->{'vmuuids'} = substr($vmuuids,0,-2);
621
                $register{$mac}->{'vmnames'} = substr($vmnames,0,-2);
622
                $register{$mac}->{'vmusers'} = substr($vmusers,0,-2);
623
            }
624
            if ($params{'stortotal'}) {
625
                $register{$mac}->{'reservedvcpus'} = $reservedvcpus;
626
            }
627

    
628
    # Clean up image db - remove images that are no longer on the node
629
            if ($params{'stortotal'} || $params{'stortotal'} eq "0") {
630
                my @regkeys = (tied %imagereg)->select_where("mac = '$mac'");
631
                foreach my $k (@regkeys) {
632
                    my $valref = $imagereg{$k};
633
                    if ( ($valref->{'storagepool'} == -1) && ($valref->{'mac'} eq $mac) && ($valref->{'status'} ne "moving") && !($valref->{'status'} =~ /cloning/) ) {
634
                        if ($reportedimgs{$valref->{'path'}} == 1) {
635
                        } elsif ($reportedimgs{$valref->{'path'}} == 2){
636
                            updateImageBilling($valref->{'user'}, $valref->{'path'}, "new image");
637
                        } else {
638
                            $main::updateUI->({tab=>"images", user=>$valref->{'user'}});
639
                            $main::syslogit->($valref->{'user'}, 'info', "Deleting image from db $valref->{'user'} - $reportedimgs{$valref->{'path'}} - $valref->{'path'} - $valref->{'status'} - $valref->{'mac'}");
640
                            delete $imagereg{$valref->{'path'}};
641
                            updateImageBilling($valref->{'user'}, $valref->{'path'}, "no image");
642
                        }
643
                    } elsif ($valref->{'storagepool'} == -1) {
644
                        ;
645
                    }
646
                }
647
            }
648

    
649
    # Clean up domain status, mark domains which are inactive or shuttingdown and not present on this node as shutoff
650
            my @regkeys = (tied %domreg)->select_where("mac = '$mac'");
651
            foreach my $domkey (@regkeys) {
652
                my $domref = $domreg{$domkey};
653
                if ($domref->{'mac'} eq $mac) {
654
                    if ($domref->{'status'} eq 'inactive' ||
655
                        ($domref->{'status'} eq 'shuttingdown' && $params{'memfree'} && !($nodedomains{$domref->{'uuid'}})) # domain has shut down, checking for param 'memfree' to make sure it's not just a status update from node
656
                    ) {
657
                        $domref->{'status'} = 'shutoff';
658
    #                    $main::updateUI->({tab=>"servers", user=>$domref->{'user'}, uuid=>$domref->{'uuid'}, status=>'shutoff',
659
    #                        message=>"shutoff ".$vmuuids."::".$domref->{'uuid'}});
660
                    }
661
                }
662
            }
663

    
664

    
665
    # Update billing
666
            my %billingreg;
667
            $monthtimestamp = timelocal(0,0,0,1,$mon,$year); #$sec,$min,$hour,$mday,$mon,$year
668
            # $monthtimestamp = timelocal(0,0,$hour,$mday,$mon,$year); #$sec,$min,$hour,$mday,$mon,$year
669
            unless (tie %userreg,'Tie::DBI', {
670
                db=>'mysql:steamregister',
671
                table=>'users',
672
                key=>'username',
673
                autocommit=>0,
674
                CLOBBER=>1,
675
                user=>$dbiuser,
676
                password=>$dbipasswd}) {return 0};
677
            my @pusers = keys %userreg;
678
            untie %userreg;
679
            unless (tie %billingreg,'Tie::DBI', {
680
                db=>'mysql:steamregister',
681
                table=>'billing_domains',
682
                key=>'usernodetime',
683
                autocommit=>0,
684
                CLOBBER=>3,
685
                user=>$dbiuser,
686
                password=>$dbipasswd}) {throw Error::Simple("Status=Error Billing register could not be accessed")};
687

    
688
            foreach my $puser (@pusers) {
689
                my $b = $billing{$puser};
690
                my $vcpu = $b->{'vcpu'};
691
                my $memory = $b->{'memory'};
692
                my $startvcpuavg = 0;
693
                my $startmemoryavg = 0;
694
                my $vcpuavg = 0;
695
                my $memoryavg = 0;
696
                my $starttimestamp = $current_time;
697

    
698
            # Are we just starting a new month
699
                if ($current_time - $monthtimestamp < 4*3600) {
700
                    $starttimestamp = $monthtimestamp;
701
                    $vcpuavg = $vcpu;
702
                    $startvcpuavg = $vcpu;
703
                    $memoryavg = $memory;
704
                    $startmemoryavg = $memory;
705
                }
706

    
707
                if ($billingreg{"$puser-$mac-$year-$month"}) {
708
                # Update timestamp and averages
709
                    $startvcpuavg = $billingreg{"$puser-$mac-$year-$month"}->{'startvcpuavg'};
710
                    $startmemoryavg = $billingreg{"$puser-$mac-$year-$month"}->{'startmemoryavg'};
711
                    $starttimestamp = $billingreg{"$puser-$mac-$year-$month"}->{'starttimestamp'};
712
                    $vcpuavg = ($startvcpuavg*($starttimestamp - $monthtimestamp) + $vcpu*($current_time - $starttimestamp)) /
713
                                    ($current_time - $monthtimestamp);
714
                    $memoryavg = ($startmemoryavg*($starttimestamp - $monthtimestamp) + $memory*($current_time - $starttimestamp)) /
715
                                    ($current_time - $monthtimestamp);
716

    
717
                    $billingreg{"$puser-$mac-$year-$month"}->{'vcpuavg'} = $vcpuavg;
718
                    $billingreg{"$puser-$mac-$year-$month"}->{'memoryavg'} = $memoryavg;
719
                    $billingreg{"$puser-$mac-$year-$month"}->{'timestamp'} = $current_time;
720
                }
721

    
722
                # No row found or something happened which justifies writing a new row
723
                if (!$billingreg{"$puser-$mac-$year-$month"}
724
                || ($vcpu != $billingreg{"$puser-$mac-$year-$month"}->{'vcpu'})
725
                || ($memory != $billingreg{"$puser-$mac-$year-$month"}->{'memory'})
726
                ) {
727
                    my $inc = 0;
728
                    if ($billingreg{"$puser-$mac-$year-$month"}) {
729
                        $startvcpuavg = $vcpuavg;
730
                        $startmemoryavg = $memoryavg;
731
                        $starttimestamp = $current_time;
732
                        $inc = $billingreg{"$puser-$mac-$year-$month"}->{'inc'};
733
                    }
734
                    # Write a new row
735
                    $billingreg{"$puser-$mac-$year-$month"} = {
736
                        vcpu=>$vcpu,
737
                        memory=>$memory,
738
                        vcpuavg=>$vcpuavg,
739
                        memoryavg=>$memoryavg,
740
                        startvcpuavg=>$startvcpuavg,
741
                        startmemoryavg=>$startmemoryavg,
742
                        timestamp=>$current_time,
743
                        starttimestamp=>$starttimestamp,
744
                        event=>$b->{'event'},
745
                        inc=>$inc+1,
746
                    };
747
                }
748
            }
749
            untie %billingreg;
750

    
751
            tied(%domreg)->commit;
752

    
753
		}
754
# Check if this node has tasks, and send them to the node them if any
755

    
756
		if ($tasks) {
757
    		my $sendtasks = '';
758
			@tasklist = split(/\n/,$tasks);
759
			$sendtasks .= "\n";
760
			foreach $thetask (@tasklist) {
761
			    my ($task,$user) = split(/ /, $tasks);
762
				if ($task eq 'reboot') {
763
					$sendtasks .= "\nStatus=REBOOT $user\n";
764
				} elsif ($task eq 'shutdown' || $task eq 'halt') {
765
					$sendtasks .= "\nStatus=HALT $user\n";
766
				} elsif ($task eq 'unjoin') {
767
					unlink $file;
768
					$sendtasks .= "\nStatus=UNJOIN $user\n";
769
				} elsif ($task eq 'reload') {
770
					$sendtasks .= "\nStatus=RELOAD $user\n";
771
				} elsif ($task eq 'wipe') {
772
					$sendtasks .= "\nStatus=WIPE $user\n";
773
				} elsif ($task eq 'sleep') {
774
					$sendtasks .= "\nStatus=SLEEP $user\n";
775
				} elsif ($task eq 'wake') {
776
					$sendtasks .= "\nStatus=WAKE $user\n";
777
				} else {
778
				     if ($task) {
779
                        $sendtasks .= "Status=$thetask\n";
780
                    }
781
				};
782
			}
783
            `echo "SENDING TASKS to $mac: $sendtasks" >> /var/log/stabile/steamExec.out` if ($sendtasks);
784
    		print $sendtasks if ($sendtasks);
785
		} else {
786
			print  "\nStatus=OK $mac\n";
787
			my $sleepafter = $idreg{'default'}->{'sleepafter'};
788
			$sleepafter = 60 * $sleepafter;
789
			print "Status=SLEEPAFTER ". $sleepafter . "\n";
790
		}
791
		print end_html(), "\n";
792
	}
793
	untie %register;
794
	untie %domreg;
795
	untie %imagereg;
796
	untie %idreg;
797

    
798
    if ($plogentry && $plogentry ne '' && $uistatus) {
799
        $uistatus = 'maintenance' if ($uistatus eq 'running' && $maintenance);
800
        $main::updateUI->({tab=>$uitab, user=>$user, uuid=>$uiuuid, status=>$uistatus, mac=>$mac, macname=>$macname}) unless ($status eq '--');
801
        $main::syslogit->($user, 'info', "$plogentry $uiuuid ($uitab, $uistatus)");
802
    }
803

    
804
} catch Error with {
805
	my $ex = shift;
806
	print "\n", "$ex->{-text} (line: $ex->{-line})", "\n";
807
} finally {
808
};
809

    
810
sub permitOpen {
811
    my ($user) = @_;
812
    my $permit;
813

    
814
    unless (tie %userreg,'Tie::DBI', {
815
        db=>'mysql:steamregister',
816
        table=>'users',
817
        key=>'username',
818
        autocommit=>0,
819
        CLOBBER=>1,
820
        user=>$dbiuser,
821
        password=>$dbipasswd}) {return 0};
822

    
823
    my $privileges = $userreg{$user}->{'privileges'};
824
    my $allowfrom = $userreg{$user}->{'allowfrom'};
825
    untie %userreg;
826

    
827
    my @allows = split(/,\s*/, $allowfrom);
828

    
829
    if ($privileges && (index($privileges,"r")!=-1 || index($privileges,"d")!=-1)) {
830
        ; # User is disabled or has only readonly access
831
    } elsif ($user) {
832
        my @regkeys = (tied %domreg)->select_where("user = '$user'");
833
        foreach my $k (@regkeys) {
834
            my $val = $domreg{$k};
835
        # Only include VM's belonging to current user
836
            if ($user eq $val->{'user'}) {
837
                # Only include drivers we have heard from in the last 20 secs
838
                #if ($current_time - ($val->{'timestamp'}) < 20) {
839
                    my $targetmac = $val->{'mac'};
840
                    my $targetip = $register{$targetmac}->{'ip'};
841
                    my $targetport = $val->{'port'};
842
                    if ($targetip && $targetport) {$permit .= " $targetip:$targetport";};
843
                #} else {
844
                #};
845
            }
846
        }
847
        $permit = " 192.168.0.254:8000" unless $permit;
848
    #    $main::syslogit->($user, 'info', "Allowed portforwarding for $user: $permit");
849

    
850
        open(TEMP1, "</etc/ssh/sshd_config") || (die "Problem reading sshd_config");
851
        open(TEMP2, ">/etc/ssh/sshd_config.new") || (die "Problem writing sshd_config");
852
        print TEMP2 "# Timestamp: $pretty_time\n";
853
        my $umatch = 0;
854
        my $allowusers;
855
        my $auser = $user;
856
        $auser =~ s/\@/\?/; # sshd_config does not support @'s in AllowUsers usernames
857
        if ($allowfrom) { # Only allow login from certain ip's
858
            $allowusers = "AllowUsers";
859
            foreach my $ip (@allows) {
860
                $ip = "$1*" if ($ip =~ /(\d+\.)0\.0\.0/);
861
                $ip = "$1*" if ($ip =~ /(\d+\.\d+\.)0\.0/);
862
                $ip = "$1*" if ($ip =~ /(\d+\.\d+\.\d+\.)0/);
863
                $allowusers .= " irigo-$auser\@$ip ";
864
            }
865
            $allowusers .= "\n";
866
        } else {
867
            $allowusers = "AllowUsers irigo-$auser\n"; # Allow from anywhere
868
        }
869

    
870
        my $matchuser = "irigo-$auser";
871
        $matchuser =~ tr/\?/./; # question marks don't work in regexp match
872
        while (<TEMP1>) {
873
            my $line = $_;
874

    
875
            if ($user && $line =~ m/Match User $matchuser/) {$umatch = 1;}
876
            elsif ($umatch && $line =~ m/Match User/) {$umatch = 0;}
877

    
878
            if ($line =~ m/AllowUsers irigo\@localhost/) {
879
                print TEMP2 $line;
880
                print TEMP2 "$allowusers";
881
                next;
882
            }
883
            if (!$umatch && !($line =~ /^AllowUsers $matchuser/) && !($line =~ m/^# Timestamp/)) {
884
                print TEMP2 $line;
885
            }
886
        }
887

    
888
        print TEMP2 <<END1;
889
Match User irigo-$user
890
ForceCommand /usr/local/bin/permitOpen $user 1
891
PermitOpen$permit
892
END1
893

    
894
;
895
    #ForceCommand /usr/bin/perl -e '\$|=1;while (1) { print scalar localtime() . "\\n";sleep 30}'
896
        close(TEMP1);
897
        close(TEMP2);
898
        rename("/etc/ssh/sshd_config", "/etc/ssh/sshd_config.old") || print "Status=ERROR Don't have permission to rename sshd_config";
899
        rename("/etc/ssh/sshd_config.new", "/etc/ssh/sshd_config") || print "Status=ERROR Don't have permission to rename sshd_config";
900
        eval {$output = `/etc/init.d/ssh restart`; 1;}  or do {print "Status=ERROR $@";};
901
    }
902
}
903

    
904
sub trim{
905
   my $string = shift;
906
   $string =~ s/^\s+|\s+$//g;
907
   return $string;
908
}
909

    
910
sub updateImageBilling {
911
    my ($user, $bpath, $status, $backupsize) = @_; # Update billing for specific image storage pool with either virtualsize and backupsize
912

    
913
    if ($backupsize) {
914
        $imagereg{$bpath}->{'backupsize'} = $backupsize;
915
    }
916
    return "No user" unless ($user);
917
    my $tenders = $Stabile::config->get('STORAGE_POOLS_ADDRESS_PATHS');
918
    my @tenderlist = split(/,\s*/, $tenders);
919
    my $tenderpaths = $Stabile::config->get('STORAGE_POOLS_LOCAL_PATHS') || "/mnt/stabile/images";
920
    my @tenderpathslist = split(/,\s*/, $tenderpaths);
921
    my $tendernames = $Stabile::config->get('STORAGE_POOLS_NAMES') || "Standard storage";
922
    my @tendernameslist = split(/,\s*/, $tendernames);
923
    my $storagepools = $Stabile::config->get('STORAGE_POOLS_DEFAULTS') || "0";
924
    my $storagepool = 0;
925
    if ($bpath =~ /\/mnt\/stabile\/node\//) {
926
        $storagepool = -1;
927
    } else {
928
        my @spl = split(/,\s*/, $storagepools);
929
        foreach my $p (@spl) {
930
            if ($tenderlist[$p] && $tenderpathslist[$p] && $tendernameslist[$p]) {
931
                my %pool = ("hostpath", $tenderlist[$p],
932
                            "path", $tenderpathslist[$p],
933
                            "name", $tendernameslist[$p],
934
                            "rdiffenabled", $rdiffenabledlist[$p],
935
                            "id", $p);
936
                $spools[$p] = \%pool;
937
                $storagepool = $p if ($bpath =~ /$tenderpathslist[$p]/)
938
            }
939
        }
940
    }
941

    
942
    my %billing;
943

    
944
    my @regkeys = (tied %imagereg)->select_where("user = '$user' AND storagepool = '$storagepool'");
945
    foreach my $k (@regkeys) {
946
        my $valref = $imagereg{$k};
947
        my %val = %{$valref}; # Deference and assign to new array, effectively cloning object
948
        $val{'virtualsize'} += 0;
949
        $val{'realsize'} += 0;
950
        $val{'backupsize'} += 0;
951

    
952
        if ($val{'user'} eq $user && $val{'storagepool'} == $storagepool) {
953
            $billing{$val{'storagepool'}}->{'virtualsize'} += $val{'virtualsize'};
954
            $billing{$val{'storagepool'}}->{'realsize'} += $val{'realsize'};
955
            $billing{$val{'storagepool'}}->{'backupsize'} += $val{'backupsize'};
956
        }
957
    }
958

    
959
    my %billingreg;
960
    my $monthtimestamp = timelocal(0,0,0,1,$mon,$year); #$sec,$min,$hour,$mday,$mon,$year
961

    
962
    unless (tie %billingreg,'Tie::DBI', {
963
        db=>'mysql:steamregister',
964
        table=>'billing_images',
965
        key=>'userstoragepooltime',
966
        autocommit=>0,
967
        CLOBBER=>3,
968
        user=>$dbiuser,
969
        password=>$dbipasswd}) {$main::syslogit->($user, 'info', "Status=Error Billing register could not be accessed")};
970

    
971
    my $b = $billing{$storagepool};
972
    my $virtualsize = $b->{'virtualsize'};
973
    my $realsize = $b->{'realsize'};
974
    my $backupsize = $b->{'backupsize'};
975
    my $startvirtualsizeavg = 0;
976
    my $startrealsizeavg = 0;
977
    my $startbackupsizeavg = 0;
978
    my $starttimestamp = $current_time;
979
    # No row found or something happened which justifies writing a new row
980
    if ($b->{'event'} || !$billingreg{"$user-$storagepool-$year-$month"}
981
    || ($b->{'virtualsize'} != $billingreg{"$user-$storagepool-$year-$month"}->{'virtualsize'})
982
    || ($b->{'realsize'} != $billingreg{"$user-$storagepool-$year-$month"}->{'realsize'})
983
    || ($b->{'backupsize'} != $billingreg{"$user-$storagepool-$year-$month"}->{'backupsize'})
984
    ) {
985
        my $inc = 0;
986
        if ($billingreg{"$user-$storagepool-$year-$month"}) {
987
            $startvirtualsizeavg = $billingreg{"$user-$storagepool-$year-$month"}->{'virtualsizeavg'};
988
            $startrealsizeavg = $billingreg{"$user-$storagepool-$year-$month"}->{'realsizeavg'};
989
            $startbackupsizeavg = $billingreg{"$user-$storagepool-$year-$month"}->{'backupsizeavg'};
990
            $starttimestamp = $billingreg{"$user-$storagepool-$year-$month"}->{'timestamp'};
991
            $inc = $billingreg{"$user-$storagepool-$year-$month"}->{'inc'};
992
        # Copy the old row for archival purposes
993
#            my %bill = %{$billingreg{"$user-$storagepool-$year-$month"}};
994
#            $billingreg{"$user-$storagepool-$year-$month-$current_time"} = \%bill;
995
        }
996
        # Write a new row
997
        $billingreg{"$user-$storagepool-$year-$month"} = {
998
            virtualsize=>$virtualsize+0,
999
            realsize=>$realsize+0,
1000
            backupsize=>$backupsize+0,
1001
            virtualsizeavg=>$startvirtualsizeavg,
1002
            realsizeavg=>$startrealsizeavg,
1003
            backupsizeavg=>$startbackupsizeavg,
1004
            timestamp=>$current_time,
1005
            startvirtualsizeavg=>$startvirtualsizeavg,
1006
            startrealsizeavg=>$startrealsizeavg,
1007
            startbackupsizeavg=>$startbackupsizeavg,
1008
            starttimestamp=>$starttimestamp,
1009
            event=>"$status $bpath",
1010
            inc=>$inc+1,
1011
        };
1012
    } else {
1013
    # Update timestamp and averages
1014
        $startvirtualsizeavg = $billingreg{"$user-$storagepool-$year-$month"}->{'startvirtualsizeavg'};
1015
        $startrealsizeavg = $billingreg{"$user-$storagepool-$year-$month"}->{'startrealsizeavg'};
1016
        $startbackupsizeavg = $billingreg{"$user-$storagepool-$year-$month"}->{'startbackupsizeavg'};
1017
        $starttimestamp = $billingreg{"$user-$storagepool-$year-$month"}->{'starttimestamp'};
1018
        my $virtualsizeavg = ($startvirtualsizeavg*($starttimestamp - $monthtimestamp) + $virtualsize*($current_time - $starttimestamp)) /
1019
                        ($current_time - $monthtimestamp);
1020
        my $realsizeavg = ($startrealsizeavg*($starttimestamp - $monthtimestamp) + $realsize*($current_time - $starttimestamp)) /
1021
                        ($current_time - $monthtimestamp);
1022
        my $backupsizeavg = ($startbackupsizeavg*($starttimestamp - $monthtimestamp) + $backupsize*($current_time - $starttimestamp)) /
1023
                        ($current_time - $monthtimestamp);
1024

    
1025
        $billingreg{"$user-$storagepool-$year-$month"}->{'virtualsizeavg'} = $virtualsizeavg;
1026
        $billingreg{"$user-$storagepool-$year-$month"}->{'realsizeavg'} = $realsizeavg;
1027
        $billingreg{"$user-$storagepool-$year-$month"}->{'backupsizeavg'} = $backupsizeavg;
1028
        $billingreg{"$user-$storagepool-$year-$month"}->{'timestamp'} = $current_time;
1029
    }
1030
    untie %billingreg;
1031
}
(1-1/2)