Project

General

Profile

Download (46.7 KB) Statistics
| Branch: | Revision:
1 95b003ff Origo
#!/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 8d7785ff Origo
$backupdir = $Stabile::config->get('STORAGE_BACKUPDIR') || "/mnt/stabile/backups";
32 95b003ff Origo
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 e9af6c24 Origo
		     start_html('Updating Stabile node...'),
124 95b003ff Origo
		     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 e9af6c24 Origo
        $kernel = "-$kernel" if ($kernel);
134 95b003ff Origo
#		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 e9af6c24 Origo
default Stabile Node
144
label Stabile Node
145
kernel vmlinuz$kernel
146 95b003ff Origo
ipappend 2
147 e9af6c24 Origo
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 95b003ff Origo
ENDBOOT
149
150
    		print TEMP2 $bootentry . "\n";
151
	    	close(TEMP2);
152
		} elsif ($dist) {
153
			$bootentry = <<ENDBOOT;
154
prompt 0
155 e9af6c24 Origo
default Stabile Node
156
label Stabile Node
157
kernel vmlinuz$kernel
158 95b003ff Origo
ipappend 2
159 e9af6c24 Origo
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 95b003ff Origo
ENDBOOT
161
162
			print TEMP2 $bootentry . "\n";
163
			close(TEMP2);
164
		} else {throw Error::Simple("Status=Error no default node identity")};
165
166 d24d9a01 hq
		my $macname = $mac;
167
        $macname = $register{$mac}->{'name'} if ($register{$mac});
168
        $register{$mac} = {
169 95b003ff Origo
            identity=>$id,
170
            timestamp=>$current_time,
171
            ip=>$ENV{'REMOTE_ADDR'},
172 d24d9a01 hq
            name=>$macname,
173 95b003ff Origo
            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 8d7785ff Origo
                my $imguser = $imagereg{$uipath}->{'user'};
199 95b003ff Origo
                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 8d7785ff Origo
                my $backupsize = getBackupSize($subdir, "$fname$suffix", $imguser);
205 95b003ff Origo
                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 e9af6c24 Origo
		     start_html('Updating Stabile node...'),
264 95b003ff Origo
		     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
        }
275
276
		$maintenance = $register{$mac}->{'maintenance'};
277
		# If the node is shutting down or joining, don't reboot it
278
		if ($status eq "shutdown" || $status eq "joining") {
279
			$action = "";
280
		}
281
		my $dbstatus = $register{$mac}->{'status'};
282
		my $macname = $register{$mac}->{'name'};
283
		my $nodestatus = $status;
284
        $nodestatus = 'maintenance' if ($status eq 'running' && $maintenance);
285
		if (($dbstatus eq "maintenance" && $status ne "drowsing") || $dbstatus eq "sleeping" || $dbstatus eq "shuttingdown" || !$status || $status eq '--') {
286
            $nodestatus = $dbstatus;
287
		} elsif ( $status eq 'drowsing' && ($dbstatus eq 'running' || $dbstatus eq 'maintenance')) {
288
            if ($brutalsleep && (
289
                    ($register{$mac}->{'amtip'} && $register{$mac}->{'amtip'} ne '--')
290
                || ($register{$mac}->{'ipmiip'} && $register{$mac}->{'ipmiip'} ne '--')
291
                )) {
292
                my $sleepcmd;
293
                $uistatus = "asleep";
294
                print  "\nStatus=SWEETDREAMS";
295
                sleep 2;
296
                if ($register{$mac}->{'amtip'} && $register{$mac}->{'amtip'} ne '--') {
297
                    $sleepcmd = "echo 'y' | AMT_PASSWORD='$amtpasswd' /usr/bin/amttool $register{$mac}->{'amtip'} powerdown";
298
                } else {
299
                    $sleepcmd = "ipmitool -I lanplus -H $register{$mac}->{'ipmiip'} -U ADMIN -P ADMIN power off";
300
                }
301
                my $logmsg = "Node $mac marked for drowse ";
302
                $logmsg .= `$sleepcmd`;
303
                $logmsg =~ s/\n/ /g;
304
                $main::syslogit->('--', "info", $logmsg);
305
            }
306
            $nodestatus = 'asleep';
307
		}
308
309
        my %billing;
310
311
	# Look for info on whether if this node is waiting to receive vm's and activate the sender
312
        my $receive = uri_unescape($params{'receive'});
313
        if ($receive) {
314
            @uuids = split(/, */,$receive);
315
            foreach my $uuid (@uuids) {
316
                # Sender is the current node/mac running the vm
317
                my $sendmac = $domreg{$uuid}->{'mac'};
318
                my $rip = $register{$mac}->{'ip'};
319
                my $sendtasks = "MOVE $uuid $rip $mac $user\n". $register{$sendmac}->{'tasks'};
320
                chop $sendtasks;
321
                $register{$sendmac}->{'tasks'} .= $sendtasks;
322
            }
323
        }
324
        my $returntasks = uri_unescape($params{'returntasks'});
325
        if ($returntasks && $returntasks ne "--") {
326
            $register{$mac}->{'tasks'} .= $returntasks; # Some tasks have failed, try again
327
        }
328
329
        # Don't update anything for node feedbacks from actions
330
        if ($status ne '--'
331
            && $status ne 'asleep'
332
            && $status ne 'awake'
333
            && $status ne 'shutdown'
334
            && $status ne 'reboot'
335
            && $status ne 'unjoin'
336
            && $status ne 'permitopen'
337
            && $status ne 'reload'
338
        ) {
339
    # Update basic parameters
340
            my $memfree = $params{'memfree'} || $register{$mac}->{'memfree'};
341
            my $memtotal = $params{'memtotal'} || $register{$mac}->{'memtotal'};
342
            my $cpuload = $params{'cpuload'} || $register{$mac}->{'cpuload'};
343
            my $cpucount = $params{'cpucount'} || $register{$mac}->{'cpucount'};
344
            my $cpucores = $params{'cpucores'} || $register{$mac}->{'cpucores'};
345
            my $nfsroot = uri_unescape($params{'nfsroot'}) || $register{$mac}->{'nfsroot'};
346
            my $kernel = uri_unescape($params{'kernel'}) || $register{$mac}->{'kernel'};
347
            my $reservedvcpus = 0;
348
349
            $register{$mac} = {
350
                timestamp=>$current_time,
351
                identity=>$params{'identity'},
352
                ip=>$ENV{'REMOTE_ADDR'},
353
                status=>$nodestatus,
354
                memfree=>$memfree,
355
                memtotal=>$memtotal,
356
                cpuload=>$cpuload,
357
                cpucount=>$cpucount,
358
                cpucores=>$cpucores,
359
    #            reservedvcpus=>0,
360
                nfsroot=>$nfsroot,
361
                kernel=>$kernel,
362
                action=>""
363
            };
364
365
            if ($ipmiip) {
366
                $register{$mac}->{'ipmiip'} = $ipmiip;
367
            }
368
            if ($params{'stortotal'} || $params{'stortotal'} eq "0") {
369
                $register{$mac}->{'stortotal'} = $params{'stortotal'};
370
                $register{$mac}->{'storfree'} = $params{'storfree'};
371
                $register{$mac}->{'stor'} = $params{'stor'};
372
            }
373
            tied(%register)->commit;
374
375
    # Look for supplied info on domains running on this node, and locally stored images, and update db
376
            my @keys = keys %params;
377
            my @values = values %params;
378
            my $vmvcpus = 0;
379
            my $vms = 0;
380
            my $vmuuids;
381
            my $vmnames;
382
            my $vmusers;
383
            my %reportedimgs;
384
            my $ug = new Data::UUID;
385
            my %nodedomains;
386
            while ($#keys >= 0)
387
            {
388
                $key = pop(@keys); $value = pop(@values);
389
                if ($key =~ m/dom(\d+)/) {
390
                    my $i = $1;
391
                    my $domstatus = $params{"domstate$i"};
392
                    $domreg{$value}->{'statustime'} = $current_time unless ($domreg{$value}->{'statustime'});
393
                    my $statedelta = $current_time - $domreg{$value}->{'statustime'}; # The number of seconds domain has been in same state
394
                    my $domdisplay = $params{"domdisplay$i"};
395
                    my $domport = $params{"domport$i"};
396
                    my $dbdomstatus = $domreg{$value}->{'status'};
397
                    my $dbdommac = $domreg{$value}->{'mac'};
398
                    my $dommac = $mac;
399
                    my $duser = $domreg{$value}->{'user'};
400
                    $nodedomains{$value} = 1;
401
                    $vms++;
402
                    $vmuuids .= "$value, ";
403
                    $vmnames .= "$domreg{$value}->{'name'}, ";
404
                    $vmusers .= "$domreg{$value}->{'user'}, ";
405
                    # Domain status has changed, evaluate if it warrants a ui update
406
                    if ($dbdomstatus eq 'moving') {
407
    #				    $main::syslogit->($user, 'info', "MOVING: $domstatus/$dommac, $dbdomstatus/$dbdommac");
408
                    }
409
                    if ($dbdomstatus && $domstatus && ($dbdomstatus ne $domstatus)) {
410
                        # Transitional states like shuttingdown are not reported by hypervisor
411
                        # we only update db with permanent states when exiting a transitional hypervisor state or
412
                        # too much time has passed
413
                        if (($dbdomstatus eq "shuttingdown" && $domstatus eq "running" && $statedelta<120)
414
                            || ($dbdomstatus eq "starting" && $domstatus eq "inactive" && $statedelta<30)
415
                            || ($dbdomstatus eq "starting" && $domstatus eq "shutdown" && $statedelta<30)
416
                            || ($dbdomstatus eq "starting" && $domstatus eq "shutoff" && $statedelta<30)
417
                            || ($dbdomstatus eq "suspending" && $domstatus eq "running" && $statedelta<30)
418
                            || ($dbdomstatus eq "resuming" && $domstatus eq "paused" && $statedelta<30)
419
                        # When moving $dbdommac is the originating mac, wait 5 min for moves
420
                            || ($dbdomstatus eq "moving" && $domstatus eq "running" && $dbdommac eq $mac && $statedelta<300)
421
                            || ($dbdomstatus eq "moving" && $domstatus eq "paused" && $dbdommac ne $mac && $statedelta<300)
422
                            || ($dbdomstatus eq "moving" && $domstatus eq "shutoff" && $dbdommac eq $mac && $statedelta<300)
423
                            || ($domstatus eq "nostate")
424
                            || ($dbdomstatus eq "destroying" && $domstatus eq "running" && $statedelta<30)
425
                            || ($dbdomstatus eq "destroying" && $domstatus eq "paused" && $statedelta<30)
426
                            || ($dbdomstatus eq "upgrading" && $statedelta<600)
427
                        ) {
428
                            $domstatus = $dbdomstatus;
429
                            $dommac = $dbdommac;
430
                        } else {
431
                        # We have exited from a transition, update the UI
432
                            $domreg{$value}->{'statustime'} = $current_time;
433
                            $billing{$duser}->{'event'} .= "$domstatus $value\n";
434
                            $main::updateUI->({tab=>"servers", user=>"$duser", uuid=>$value, status=>$domstatus,
435
                                                mac=>$mac, macname=>$macname});
436
                            if ($enginelinked && $engineid) {
437
                                my $sysuuid = $domreg{$value}->{'uuid'};
438
                                my $sysstatus = $domstatus;
439
                                if ($domreg{$value}->{'system'} && $domreg{$value}->{'system'} ne '--') { # This is a system
440
                                    $sysuuid = $domreg{$value}->{'system'};
441
                                    unless (tie %sysreg,'Tie::DBI', {
442
                                        db=>'mysql:steamregister',
443
                                        table=>'systems',
444
                                        key=>'uuid',
445
                                        autocommit=>0,
446
                                        CLOBBER=>3,
447
                                        user=>$dbiuser,
448
                                        password=>$dbipasswd}) {throw Error::Simple("Status=ERROR System register could not be accessed")};
449
                                    # Check if we are dealing with the admin server
450
                                    if ($domreg{$value}->{'image'} ne $sysreg{$sysuuid}->{'image'}) {
451
                                        $sysuuid = '';
452
                                    }
453
454
                                    untie %sysreg;
455
                                }
456
                                if ($sysuuid) {
457
                                my $json_text = <<END
458
{"uuid": "$sysuuid" , "status": "$sysstatus"}
459
END
460
;
461
                                    print "\n" . $main::postAsyncToOrigo->($engineid, 'updateapps', "[$json_text]") . "\n";
462
                                }
463
                            }
464
                        }
465
                    }
466
467
                    # If a domain is shutoff or state is undetermined, dont't count it in billing
468
                    # if ($domstatus eq "shutoff" || $domstatus eq "inactive" ) {
469
                    if ($domstatus eq "shutoff" || $domstatus eq "inactive" ) {
470
                        $billing{$duser}->{'vcpu'} += 0;
471
                        $billing{$duser}->{'memory'} += 0;
472
                    # All other states count
473
                    } else {
474
                        $billing{$duser}->{'vcpu'} += $domreg{$value}->{'vcpu'};
475
                        $billing{$duser}->{'memory'} += $domreg{$value}->{'memory'};
476
                    }
477
                    # We don't update timestamp for moving domains, so if move fails, eventually they will be marked as inactive
478
                    my $timestamp = $current_time;
479
                    $timestamp = $domreg{$value}->{'timestamp'} if ($domstatus eq "moving");
480
                    $domreg{$value} = {
481
                        status=>$domstatus,
482
                        mac=>$dommac,
483
                        macname=>$register{$dommac}->{'name'},
484
                        macip=>$register{$dommac}->{'ip'},
485
                        maccpucores=>$register{$dommac}->{'cpucores'},
486
                        timestamp=>$timestamp
487
                    };
488
                    $domreg{$value}->{'mac'} = $dommac unless ($domstatus eq 'moving');
489
                    $domreg{$value}->{'display'} = $domdisplay if $domdisplay;
490
                    $domreg{$value}->{'port'} = $domport if $domport;
491
                    if ($params{"domstate$i"} eq 'running') {$vmvcpus += $domreg{$value}->{'vcpu'}};
492
                # If a domain was moved, update permitted ports
493
                    if (($dbdomstatus eq "moving" && $domstatus eq "running" && $dbdommac ne $mac)) {
494
                        $main::syslogit->($duser, 'info', "Moved $domreg{$value}->{'name'} ($value) to $register{$dommac}->{'name'}");
495
                        permitOpen($duser);
496
                    }
497
                # Update status of server's images
498
                    my $image = $domreg{$value}->{'image'};
499
                    my $image2 = $domreg{$value}->{'image2'};
500
                    my $image3 = $domreg{$value}->{'image3'};
501
                    my $image4 = $domreg{$value}->{'image4'};
502
                    my $imgstatus = 'active'; # if server is running, moving, etc.
503
                    if ($domstatus eq 'paused') {
504
                        $imgstatus = 'paused'
505
                    } elsif ($domstatus eq "shutoff" || $domstatus eq "inactive")  {
506
                        $imgstatus = 'used'
507
                    }
508 64c667ea hq
                    print "$image for $domreg{$value}->{name} not in DB" unless ($imagereg{$image});
509
                    $imagereg{$image}->{'status'} = $imgstatus if ($imagereg{$image} && $imagereg{$image}->{'status'} !~ /backingup/);
510
                    $imagereg{$image2}->{'status'} = $imgstatus if ($image2 && $imagereg{$image2} && $image2 ne '--' && $imagereg{$image2}->{'status'} !~ /backingup/);
511
                    $imagereg{$image3}->{'status'} = $imgstatus if ($image3 && $imagereg{$image3} && $image3 ne '--' && $imagereg{$image3}->{'status'} !~ /backingup/);
512
                    $imagereg{$image4}->{'status'} = $imgstatus if ($image4 && $imagereg{$image4} && $image4 ne '--' && $imagereg{$image4}->{'status'} !~ /backingup/);
513 95b003ff Origo
514
                } elsif ($key =~ m/img(\d+)/) {
515
            # The node is reporting about a locally stored image
516
                    my $f = uri_unescape($value);
517
                    my $size = $params{"size$1"};
518
                    my $realsize = $params{"realsize$1"};
519
                    my $virtualsize = $params{"virtualsize$1"};
520
                    my($fname, $dirpath, $suffix) = fileparse($f, (".vmdk", ".img", ".vhd", ".qcow", ".qcow2", ".vdi", ".iso"));
521
                    my $regimg = $imagereg{$f};
522
                    my $uuid = $regimg->{'uuid'};
523
524
                    my $storagepool = -1;
525
                    $f =~ m/\/mnt\/stabile\/node\/(.+?)\/.+/; # ungready matching
526
                    my $imguser = $1;
527
528
            # Create a new uuid if we are dealing with a new file in the file-system
529
                    if (!$uuid) {
530
                        $uuid = $ug->create_str() unless ($uuid);
531
                        $main::syslogit->($imguser, 'info', "Assigned new uuid $uuid to $f belonging to $imguser");
532
                    }
533
534
                    my $mtime = $newmtime || $regimg->{'mtime'};
535
                    my $name = $regimg->{'name'} || $fname;
536
537
                    my $subdir = "";
538
                    if ($dirpath =~ /\/$imguser(\/.+)\//) {
539
                        $subdir = $1;
540
                    }
541
                    my $bdu;
542
                    my $backupsize = 0;
543 8d7785ff Origo
                    my $imgpath = "$fname$suffix";
544
                    $imgpath = $1 if $cmdpath =~ /(.+)/; # untaint
545
                    $backupsize = getBackupSize($subdir, $imgpath, $imguser);
546 95b003ff Origo
            # If image on node is attached to a domain, reserve vcpus for starting domain on node
547
                    my $imgdom = $regimg->{'domains'};
548
                    if ($imgdom && $domreg{$imgdom}) {
549
                        my $imgvcpus = $domreg{$imgdom}->{'vcpu'};
550
                        my $imgdomstatus = $domreg{$imgdom}->{'status'};
551
                        $reservedvcpus += $imgvcpus if ($imgdomstatus eq 'shutoff' || $imgdomstatus eq 'inactive');
552
                    }
553
554
                    $reportedimgs{$f} = 1;
555
                    if (($regimg->{'virtualsize'} == 0 && $virtualsize) || $regimg->{'status'} eq 'moving') {
556
                        $reportedimgs{$f} = 2; # Mark that we should update the UI - this is a recently transferred image
557
                    }
558
                    if ($f && $imguser) {
559
                        my $imgstatus = $regimg->{'status'};
560
                        # This only happens first time after an image has been transferred manually to a node
561
                        if (!$imgstatus || $imgstatus eq '--' || $imgstatus eq 'cloning') {
562
                            $imgstatus = "unused";
563
                            my $imgdomains = $regimg->{'domains'};
564
                            my $imgdomainnames = $regimg->{'domainnames'};
565
                            (tied %domreg)->select_where("user = '$imguser' or user = 'common'") unless ($fulllist);
566 8d7785ff Origo
                            foreach my $dom (values %domreg) {
567 95b003ff Origo
                                my $img = $dom->{'image'};
568
                                my $img2 = $dom->{'image2'};
569
                                my $img3 = $dom->{'image3'};
570
                                my $img4 = $dom->{'image4'};
571
                                if ($f eq $img || $f eq $img2 || $f eq $img3 || $f eq $img4) {
572
                                    $imgstatus = "active";
573
                                    my $domstatus = $dom->{'status'};
574
                                    if ($domstatus eq "shutoff" || $domstatus eq "inactive") {$imgstatus = "used";}
575
                                    elsif ($domstatus eq "paused") {$imgstatus = "paused";}
576
                                    $imgdomains = $dom->{'uuid'};
577
                                    $imgdomainnames = $dom->{'name'};
578
                                };
579
                            }
580
                            $imagereg{$f} = {
581
                                user=>$imguser,
582
                                type=>substr($suffix,1),
583
                                size=>$size,
584
                                realsize=>$realsize,
585
                                virtualsize=>$virtualsize,
586
                                backupsize=>$backupsize,
587
                                name=>$name,
588
                                uuid=>$uuid,
589
                                storagepool=>$storagepool,
590
                                mac=>$mac,
591
                                mtime=>$mtime,
592
                                status=>$imgstatus,
593
                                domains=>$imgdomains,
594
                                domainnames=>$imgdomainnames
595
                            }
596
                        } else {
597
                            $imagereg{$f} = {
598
                                user=>$imguser,
599
                                type=>substr($suffix,1),
600
                                size=>$size,
601
                                realsize=>$realsize,
602
                                virtualsize=>$virtualsize,
603
                                backupsize=>$backupsize,
604
                                name=>$name,
605
                                uuid=>$uuid,
606
                                storagepool=>$storagepool,
607
                                mac=>$mac,
608
                                mtime=>$mtime
609
                            }
610
                        }
611
                    }
612
613
                }
614
            }
615
616
            if ($params{'dominfo'} || $params{'dom1'}) {
617
                $register{$mac}->{'vms'} = $vms;
618
                $register{$mac}->{'vmvcpus'} = $vmvcpus;
619
                $register{$mac}->{'vmuuids'} = substr($vmuuids,0,-2);
620
                $register{$mac}->{'vmnames'} = substr($vmnames,0,-2);
621
                $register{$mac}->{'vmusers'} = substr($vmusers,0,-2);
622
            }
623
            if ($params{'stortotal'}) {
624
                $register{$mac}->{'reservedvcpus'} = $reservedvcpus;
625
            }
626
627
    # Clean up image db - remove images that are no longer on the node
628
            if ($params{'stortotal'} || $params{'stortotal'} eq "0") {
629
                my @regkeys = (tied %imagereg)->select_where("mac = '$mac'");
630
                foreach my $k (@regkeys) {
631
                    my $valref = $imagereg{$k};
632
                    if ( ($valref->{'storagepool'} == -1) && ($valref->{'mac'} eq $mac) && ($valref->{'status'} ne "moving") && !($valref->{'status'} =~ /cloning/) ) {
633
                        if ($reportedimgs{$valref->{'path'}} == 1) {
634
                        } elsif ($reportedimgs{$valref->{'path'}} == 2){
635
                            updateImageBilling($valref->{'user'}, $valref->{'path'}, "new image");
636
                        } else {
637
                            $main::updateUI->({tab=>"images", user=>$valref->{'user'}});
638
                            $main::syslogit->($valref->{'user'}, 'info', "Deleting image from db $valref->{'user'} - $reportedimgs{$valref->{'path'}} - $valref->{'path'} - $valref->{'status'} - $valref->{'mac'}");
639
                            delete $imagereg{$valref->{'path'}};
640
                            updateImageBilling($valref->{'user'}, $valref->{'path'}, "no image");
641
                        }
642
                    } elsif ($valref->{'storagepool'} == -1) {
643
                        ;
644
                    }
645
                }
646
            }
647
648
    # Clean up domain status, mark domains which are inactive or shuttingdown and not present on this node as shutoff
649
            my @regkeys = (tied %domreg)->select_where("mac = '$mac'");
650
            foreach my $domkey (@regkeys) {
651
                my $domref = $domreg{$domkey};
652
                if ($domref->{'mac'} eq $mac) {
653
                    if ($domref->{'status'} eq 'inactive' ||
654
                        ($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
655
                    ) {
656
                        $domref->{'status'} = 'shutoff';
657
    #                    $main::updateUI->({tab=>"servers", user=>$domref->{'user'}, uuid=>$domref->{'uuid'}, status=>'shutoff',
658
    #                        message=>"shutoff ".$vmuuids."::".$domref->{'uuid'}});
659
                    }
660
                }
661
            }
662
663
664
    # Update billing
665
            my %billingreg;
666
            $monthtimestamp = timelocal(0,0,0,1,$mon,$year); #$sec,$min,$hour,$mday,$mon,$year
667
            # $monthtimestamp = timelocal(0,0,$hour,$mday,$mon,$year); #$sec,$min,$hour,$mday,$mon,$year
668
            unless (tie %userreg,'Tie::DBI', {
669
                db=>'mysql:steamregister',
670
                table=>'users',
671
                key=>'username',
672
                autocommit=>0,
673
                CLOBBER=>1,
674
                user=>$dbiuser,
675
                password=>$dbipasswd}) {return 0};
676
            my @pusers = keys %userreg;
677
            untie %userreg;
678
            unless (tie %billingreg,'Tie::DBI', {
679
                db=>'mysql:steamregister',
680
                table=>'billing_domains',
681
                key=>'usernodetime',
682
                autocommit=>0,
683
                CLOBBER=>3,
684
                user=>$dbiuser,
685
                password=>$dbipasswd}) {throw Error::Simple("Status=Error Billing register could not be accessed")};
686
687
            foreach my $puser (@pusers) {
688
                my $b = $billing{$puser};
689
                my $vcpu = $b->{'vcpu'};
690
                my $memory = $b->{'memory'};
691
                my $startvcpuavg = 0;
692
                my $startmemoryavg = 0;
693
                my $vcpuavg = 0;
694
                my $memoryavg = 0;
695
                my $starttimestamp = $current_time;
696
697
            # Are we just starting a new month
698
                if ($current_time - $monthtimestamp < 4*3600) {
699
                    $starttimestamp = $monthtimestamp;
700
                    $vcpuavg = $vcpu;
701
                    $startvcpuavg = $vcpu;
702
                    $memoryavg = $memory;
703
                    $startmemoryavg = $memory;
704
                }
705
706
                if ($billingreg{"$puser-$mac-$year-$month"}) {
707
                # Update timestamp and averages
708
                    $startvcpuavg = $billingreg{"$puser-$mac-$year-$month"}->{'startvcpuavg'};
709
                    $startmemoryavg = $billingreg{"$puser-$mac-$year-$month"}->{'startmemoryavg'};
710
                    $starttimestamp = $billingreg{"$puser-$mac-$year-$month"}->{'starttimestamp'};
711
                    $vcpuavg = ($startvcpuavg*($starttimestamp - $monthtimestamp) + $vcpu*($current_time - $starttimestamp)) /
712
                                    ($current_time - $monthtimestamp);
713
                    $memoryavg = ($startmemoryavg*($starttimestamp - $monthtimestamp) + $memory*($current_time - $starttimestamp)) /
714
                                    ($current_time - $monthtimestamp);
715
716
                    $billingreg{"$puser-$mac-$year-$month"}->{'vcpuavg'} = $vcpuavg;
717
                    $billingreg{"$puser-$mac-$year-$month"}->{'memoryavg'} = $memoryavg;
718
                    $billingreg{"$puser-$mac-$year-$month"}->{'timestamp'} = $current_time;
719
                }
720
721
                # No row found or something happened which justifies writing a new row
722
                if (!$billingreg{"$puser-$mac-$year-$month"}
723
                || ($vcpu != $billingreg{"$puser-$mac-$year-$month"}->{'vcpu'})
724
                || ($memory != $billingreg{"$puser-$mac-$year-$month"}->{'memory'})
725
                ) {
726
                    my $inc = 0;
727
                    if ($billingreg{"$puser-$mac-$year-$month"}) {
728
                        $startvcpuavg = $vcpuavg;
729
                        $startmemoryavg = $memoryavg;
730
                        $starttimestamp = $current_time;
731
                        $inc = $billingreg{"$puser-$mac-$year-$month"}->{'inc'};
732
                    }
733
                    # Write a new row
734
                    $billingreg{"$puser-$mac-$year-$month"} = {
735
                        vcpu=>$vcpu,
736
                        memory=>$memory,
737
                        vcpuavg=>$vcpuavg,
738
                        memoryavg=>$memoryavg,
739
                        startvcpuavg=>$startvcpuavg,
740
                        startmemoryavg=>$startmemoryavg,
741
                        timestamp=>$current_time,
742
                        starttimestamp=>$starttimestamp,
743
                        event=>$b->{'event'},
744
                        inc=>$inc+1,
745
                    };
746
                }
747
            }
748
            untie %billingreg;
749
750
            tied(%domreg)->commit;
751
752
		}
753
# Check if this node has tasks, and send them to the node them if any
754
755
		if ($tasks) {
756
    		my $sendtasks = '';
757
			@tasklist = split(/\n/,$tasks);
758
			$sendtasks .= "\n";
759
			foreach $thetask (@tasklist) {
760
			    my ($task,$user) = split(/ /, $tasks);
761
				if ($task eq 'reboot') {
762
					$sendtasks .= "\nStatus=REBOOT $user\n";
763
				} elsif ($task eq 'shutdown' || $task eq 'halt') {
764
					$sendtasks .= "\nStatus=HALT $user\n";
765
				} elsif ($task eq 'unjoin') {
766
					unlink $file;
767
					$sendtasks .= "\nStatus=UNJOIN $user\n";
768
				} elsif ($task eq 'reload') {
769
					$sendtasks .= "\nStatus=RELOAD $user\n";
770
				} elsif ($task eq 'wipe') {
771
					$sendtasks .= "\nStatus=WIPE $user\n";
772
				} elsif ($task eq 'sleep') {
773
					$sendtasks .= "\nStatus=SLEEP $user\n";
774
				} elsif ($task eq 'wake') {
775
					$sendtasks .= "\nStatus=WAKE $user\n";
776
				} else {
777
				     if ($task) {
778
                        $sendtasks .= "Status=$thetask\n";
779
                    }
780
				};
781
			}
782 04c16f26 hq
            if ($sendtasks) {
783
                print  "\nStatus=OK $mac";
784
                print "$sendtasks";
785
                `echo "SENDING TASKS to $mac: $sendtasks" >> /var/log/stabile/steamExec.out`;
786
            }
787 95b003ff Origo
		} else {
788
			print  "\nStatus=OK $mac\n";
789
			my $sleepafter = $idreg{'default'}->{'sleepafter'};
790
			$sleepafter = 60 * $sleepafter;
791
			print "Status=SLEEPAFTER ". $sleepafter . "\n";
792
		}
793
		print end_html(), "\n";
794
	}
795
	untie %register;
796
	untie %domreg;
797
	untie %imagereg;
798
	untie %idreg;
799
800
    if ($plogentry && $plogentry ne '' && $uistatus) {
801
        $uistatus = 'maintenance' if ($uistatus eq 'running' && $maintenance);
802
        $main::updateUI->({tab=>$uitab, user=>$user, uuid=>$uiuuid, status=>$uistatus, mac=>$mac, macname=>$macname}) unless ($status eq '--');
803
        $main::syslogit->($user, 'info', "$plogentry $uiuuid ($uitab, $uistatus)");
804
    }
805
806
} catch Error with {
807
	my $ex = shift;
808
	print "\n", "$ex->{-text} (line: $ex->{-line})", "\n";
809
} finally {
810
};
811
812
sub permitOpen {
813
    my ($user) = @_;
814
    my $permit;
815
816
    unless (tie %userreg,'Tie::DBI', {
817
        db=>'mysql:steamregister',
818
        table=>'users',
819
        key=>'username',
820
        autocommit=>0,
821
        CLOBBER=>1,
822
        user=>$dbiuser,
823
        password=>$dbipasswd}) {return 0};
824
825
    my $privileges = $userreg{$user}->{'privileges'};
826
    my $allowfrom = $userreg{$user}->{'allowfrom'};
827
    untie %userreg;
828
829
    my @allows = split(/,\s*/, $allowfrom);
830
831
    if ($privileges && (index($privileges,"r")!=-1 || index($privileges,"d")!=-1)) {
832
        ; # User is disabled or has only readonly access
833
    } elsif ($user) {
834
        my @regkeys = (tied %domreg)->select_where("user = '$user'");
835
        foreach my $k (@regkeys) {
836
            my $val = $domreg{$k};
837
        # Only include VM's belonging to current user
838
            if ($user eq $val->{'user'}) {
839
                # Only include drivers we have heard from in the last 20 secs
840
                #if ($current_time - ($val->{'timestamp'}) < 20) {
841
                    my $targetmac = $val->{'mac'};
842
                    my $targetip = $register{$targetmac}->{'ip'};
843
                    my $targetport = $val->{'port'};
844
                    if ($targetip && $targetport) {$permit .= " $targetip:$targetport";};
845
                #} else {
846
                #};
847
            }
848
        }
849
        $permit = " 192.168.0.254:8000" unless $permit;
850
    #    $main::syslogit->($user, 'info', "Allowed portforwarding for $user: $permit");
851
852
        open(TEMP1, "</etc/ssh/sshd_config") || (die "Problem reading sshd_config");
853
        open(TEMP2, ">/etc/ssh/sshd_config.new") || (die "Problem writing sshd_config");
854
        print TEMP2 "# Timestamp: $pretty_time\n";
855
        my $umatch = 0;
856
        my $allowusers;
857
        my $auser = $user;
858
        $auser =~ s/\@/\?/; # sshd_config does not support @'s in AllowUsers usernames
859
        if ($allowfrom) { # Only allow login from certain ip's
860
            $allowusers = "AllowUsers";
861
            foreach my $ip (@allows) {
862
                $ip = "$1*" if ($ip =~ /(\d+\.)0\.0\.0/);
863
                $ip = "$1*" if ($ip =~ /(\d+\.\d+\.)0\.0/);
864
                $ip = "$1*" if ($ip =~ /(\d+\.\d+\.\d+\.)0/);
865
                $allowusers .= " irigo-$auser\@$ip ";
866
            }
867
            $allowusers .= "\n";
868
        } else {
869
            $allowusers = "AllowUsers irigo-$auser\n"; # Allow from anywhere
870
        }
871
872
        my $matchuser = "irigo-$auser";
873
        $matchuser =~ tr/\?/./; # question marks don't work in regexp match
874
        while (<TEMP1>) {
875
            my $line = $_;
876
877
            if ($user && $line =~ m/Match User $matchuser/) {$umatch = 1;}
878
            elsif ($umatch && $line =~ m/Match User/) {$umatch = 0;}
879
880
            if ($line =~ m/AllowUsers irigo\@localhost/) {
881
                print TEMP2 $line;
882
                print TEMP2 "$allowusers";
883
                next;
884
            }
885
            if (!$umatch && !($line =~ /^AllowUsers $matchuser/) && !($line =~ m/^# Timestamp/)) {
886
                print TEMP2 $line;
887
            }
888
        }
889
890
        print TEMP2 <<END1;
891
Match User irigo-$user
892
ForceCommand /usr/local/bin/permitOpen $user 1
893
PermitOpen$permit
894
END1
895
896
;
897
    #ForceCommand /usr/bin/perl -e '\$|=1;while (1) { print scalar localtime() . "\\n";sleep 30}'
898
        close(TEMP1);
899
        close(TEMP2);
900
        rename("/etc/ssh/sshd_config", "/etc/ssh/sshd_config.old") || print "Status=ERROR Don't have permission to rename sshd_config";
901
        rename("/etc/ssh/sshd_config.new", "/etc/ssh/sshd_config") || print "Status=ERROR Don't have permission to rename sshd_config";
902
        eval {$output = `/etc/init.d/ssh restart`; 1;}  or do {print "Status=ERROR $@";};
903
    }
904
}
905
906
sub trim{
907
   my $string = shift;
908
   $string =~ s/^\s+|\s+$//g;
909
   return $string;
910
}
911
912
sub updateImageBilling {
913
    my ($user, $bpath, $status, $backupsize) = @_; # Update billing for specific image storage pool with either virtualsize and backupsize
914
915
    if ($backupsize) {
916
        $imagereg{$bpath}->{'backupsize'} = $backupsize;
917
    }
918 8d7785ff Origo
    return "No user" unless ($user);
919 95b003ff Origo
    my $tenders = $Stabile::config->get('STORAGE_POOLS_ADDRESS_PATHS');
920
    my @tenderlist = split(/,\s*/, $tenders);
921
    my $tenderpaths = $Stabile::config->get('STORAGE_POOLS_LOCAL_PATHS') || "/mnt/stabile/images";
922
    my @tenderpathslist = split(/,\s*/, $tenderpaths);
923
    my $tendernames = $Stabile::config->get('STORAGE_POOLS_NAMES') || "Standard storage";
924
    my @tendernameslist = split(/,\s*/, $tendernames);
925
    my $storagepools = $Stabile::config->get('STORAGE_POOLS_DEFAULTS') || "0";
926
    my $storagepool = 0;
927
    if ($bpath =~ /\/mnt\/stabile\/node\//) {
928
        $storagepool = -1;
929
    } else {
930
        my @spl = split(/,\s*/, $storagepools);
931
        foreach my $p (@spl) {
932
            if ($tenderlist[$p] && $tenderpathslist[$p] && $tendernameslist[$p]) {
933
                my %pool = ("hostpath", $tenderlist[$p],
934
                            "path", $tenderpathslist[$p],
935
                            "name", $tendernameslist[$p],
936
                            "rdiffenabled", $rdiffenabledlist[$p],
937
                            "id", $p);
938
                $spools[$p] = \%pool;
939
                $storagepool = $p if ($bpath =~ /$tenderpathslist[$p]/)
940
            }
941
        }
942
    }
943
944
    my %billing;
945
946
    my @regkeys = (tied %imagereg)->select_where("user = '$user' AND storagepool = '$storagepool'");
947
    foreach my $k (@regkeys) {
948
        my $valref = $imagereg{$k};
949
        my %val = %{$valref}; # Deference and assign to new array, effectively cloning object
950
        $val{'virtualsize'} += 0;
951
        $val{'realsize'} += 0;
952
        $val{'backupsize'} += 0;
953
954
        if ($val{'user'} eq $user && $val{'storagepool'} == $storagepool) {
955
            $billing{$val{'storagepool'}}->{'virtualsize'} += $val{'virtualsize'};
956
            $billing{$val{'storagepool'}}->{'realsize'} += $val{'realsize'};
957
            $billing{$val{'storagepool'}}->{'backupsize'} += $val{'backupsize'};
958
        }
959
    }
960
961
    my %billingreg;
962
    my $monthtimestamp = timelocal(0,0,0,1,$mon,$year); #$sec,$min,$hour,$mday,$mon,$year
963
964
    unless (tie %billingreg,'Tie::DBI', {
965
        db=>'mysql:steamregister',
966
        table=>'billing_images',
967
        key=>'userstoragepooltime',
968
        autocommit=>0,
969
        CLOBBER=>3,
970
        user=>$dbiuser,
971
        password=>$dbipasswd}) {$main::syslogit->($user, 'info', "Status=Error Billing register could not be accessed")};
972
973
    my $b = $billing{$storagepool};
974
    my $virtualsize = $b->{'virtualsize'};
975
    my $realsize = $b->{'realsize'};
976
    my $backupsize = $b->{'backupsize'};
977
    my $startvirtualsizeavg = 0;
978
    my $startrealsizeavg = 0;
979
    my $startbackupsizeavg = 0;
980
    my $starttimestamp = $current_time;
981
    # No row found or something happened which justifies writing a new row
982
    if ($b->{'event'} || !$billingreg{"$user-$storagepool-$year-$month"}
983
    || ($b->{'virtualsize'} != $billingreg{"$user-$storagepool-$year-$month"}->{'virtualsize'})
984
    || ($b->{'realsize'} != $billingreg{"$user-$storagepool-$year-$month"}->{'realsize'})
985
    || ($b->{'backupsize'} != $billingreg{"$user-$storagepool-$year-$month"}->{'backupsize'})
986
    ) {
987
        my $inc = 0;
988
        if ($billingreg{"$user-$storagepool-$year-$month"}) {
989
            $startvirtualsizeavg = $billingreg{"$user-$storagepool-$year-$month"}->{'virtualsizeavg'};
990
            $startrealsizeavg = $billingreg{"$user-$storagepool-$year-$month"}->{'realsizeavg'};
991
            $startbackupsizeavg = $billingreg{"$user-$storagepool-$year-$month"}->{'backupsizeavg'};
992
            $starttimestamp = $billingreg{"$user-$storagepool-$year-$month"}->{'timestamp'};
993
            $inc = $billingreg{"$user-$storagepool-$year-$month"}->{'inc'};
994
        # Copy the old row for archival purposes
995
#            my %bill = %{$billingreg{"$user-$storagepool-$year-$month"}};
996
#            $billingreg{"$user-$storagepool-$year-$month-$current_time"} = \%bill;
997
        }
998
        # Write a new row
999
        $billingreg{"$user-$storagepool-$year-$month"} = {
1000
            virtualsize=>$virtualsize+0,
1001
            realsize=>$realsize+0,
1002
            backupsize=>$backupsize+0,
1003
            virtualsizeavg=>$startvirtualsizeavg,
1004
            realsizeavg=>$startrealsizeavg,
1005
            backupsizeavg=>$startbackupsizeavg,
1006
            timestamp=>$current_time,
1007
            startvirtualsizeavg=>$startvirtualsizeavg,
1008
            startrealsizeavg=>$startrealsizeavg,
1009
            startbackupsizeavg=>$startbackupsizeavg,
1010
            starttimestamp=>$starttimestamp,
1011
            event=>"$status $bpath",
1012
            inc=>$inc+1,
1013
        };
1014
    } else {
1015
    # Update timestamp and averages
1016
        $startvirtualsizeavg = $billingreg{"$user-$storagepool-$year-$month"}->{'startvirtualsizeavg'};
1017
        $startrealsizeavg = $billingreg{"$user-$storagepool-$year-$month"}->{'startrealsizeavg'};
1018
        $startbackupsizeavg = $billingreg{"$user-$storagepool-$year-$month"}->{'startbackupsizeavg'};
1019
        $starttimestamp = $billingreg{"$user-$storagepool-$year-$month"}->{'starttimestamp'};
1020
        my $virtualsizeavg = ($startvirtualsizeavg*($starttimestamp - $monthtimestamp) + $virtualsize*($current_time - $starttimestamp)) /
1021
                        ($current_time - $monthtimestamp);
1022
        my $realsizeavg = ($startrealsizeavg*($starttimestamp - $monthtimestamp) + $realsize*($current_time - $starttimestamp)) /
1023
                        ($current_time - $monthtimestamp);
1024
        my $backupsizeavg = ($startbackupsizeavg*($starttimestamp - $monthtimestamp) + $backupsize*($current_time - $starttimestamp)) /
1025
                        ($current_time - $monthtimestamp);
1026
1027
        $billingreg{"$user-$storagepool-$year-$month"}->{'virtualsizeavg'} = $virtualsizeavg;
1028
        $billingreg{"$user-$storagepool-$year-$month"}->{'realsizeavg'} = $realsizeavg;
1029
        $billingreg{"$user-$storagepool-$year-$month"}->{'backupsizeavg'} = $backupsizeavg;
1030
        $billingreg{"$user-$storagepool-$year-$month"}->{'timestamp'} = $current_time;
1031
    }
1032
    untie %billingreg;
1033
}