Project

General

Profile

Download (43.5 KB) Statistics
| Branch: | Revision:
1
#!/usr/bin/perl -U
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
package Stabile::Pressurecontrol;
9

    
10
use Config::Simple;
11
use ConfigReader::Simple; # Needed for parsing Apache config
12
use Data::UUID;
13
use LWP::Simple;
14
use Digest::SHA qw(sha512_base64 sha512_hex);
15
use Data::Dumper;
16
use JSON;
17
use Tie::DBI;
18
use Proc::Daemon;
19
use Proc::ProcessTable;
20
use HTTP::Daemon;
21
use URI::Escape qw(uri_escape uri_unescape);
22
use Error qw(:try);
23
# use sigtrap 'handler' => \&TERMINATE, 'QUIT', 'INT', 'TERM', 'KILL', 'STOP';
24
use sigtrap 'handler' => \&HUP, 'HUP';
25

    
26
my $running = 1;
27
my $naptime = 90; # seconds
28
$user = 'irigo';
29

    
30
my $argv = shift if $ARGV[0];
31
my $debug = 1 if ($argv eq 'debug');
32
my $cfg = new Config::Simple("/etc/stabile/config.cfg");
33
my $nodecfg = new Config::Simple("/etc/stabile/nodeconfig.cfg");
34

    
35
my $uuid = $cfg->param('ENGINEID') || '';
36
my $dbiuser =  $cfg->param('DBI_USER') || "irigo";
37
my $dbipasswd = $cfg->param('DBI_PASSWD') || "";
38
my $engineuser = $cfg->param('ENGINEUSER') || "";
39
my @spoolpaths = $cfg->param('STORAGE_POOLS_LOCAL_PATHS');
40
my $downloadmasters = $cfg->param('DOWNLOAD_MASTERS');
41

    
42
my $valve_readlimit = $cfg->param('VALVE_READ_LIMIT'); # e.g. 125829120 = 120 * 1024 * 1024 = 120 MB / s
43
my $valve_writelimit = $cfg->param('VALVE_WRITE_LIMIT');
44
my $valve_iopsreadlimit = $cfg->param('VALVE_IOPS_READ_LIMIT'); # e.g. 1000 IOPS
45
my $valve_iopswritelimit = $cfg->param('VALVE_IOPS_WRITE_LIMIT');
46

    
47
my $vm_readlimit = $nodecfg->param('VM_READ_LIMIT'); # e.g. 125829120 = 120 * 1024 * 1024 = 120 MB / s
48
my $vm_writelimit = $nodecfg->param('VM_WRITE_LIMIT');
49
my $vm_iopsreadlimit = $nodecfg->param('VM_IOPS_READ_LIMIT'); # e.g. 1000 IOPS
50
my $vm_iopswritelimit = $nodecfg->param('VM_IOPS_WRITE_LIMIT');
51
my $identity = $nodecfg->param('IDENTITY');
52

    
53
my $stabile_upgrade = $cfg->param('UPGRADE');
54
my $datanic = $cfg->param('ENGINE_DATA_NIC');
55

    
56
my $basedir = "/var/www/stabile";
57
$basedir = `cat /etc/stabile/basedir` if -e "/etc/stabile/basedir";
58
chomp $basedir;
59
my $baseurl;
60
if (-e "/etc/stabile/baseurl") {
61
    $baseurl = `cat /etc/stabile/baseurl`;
62
    chomp $baseurl;
63
} else {
64
    my $hostname = `hostname`; chomp $hostname;
65
    chomp $hostname;
66
    $baseurl = "https://$hostname/stabile";
67
}
68

    
69
unless (checkDB()) {
70
    die "Unable to connect to db...\n";
71
}
72

    
73
require "$basedir/cgi/Stabile.pm";
74

    
75
if ($stabile_upgrade) {
76
    print "Steamgine upgrade requested. Hang on...\n";
77
    $cfg->delete('UPGRADE');
78
    $cfg->save();
79
    `apt-get update`;
80
    if (`hostname` =~ /orellana/) {# Don't upgrade devel server
81
        print "Not upgrading development source server...";
82
        `echo "Not upgrading development source server..." >> /tmp/stabile.upgrade.log`
83
    } else {
84
        `echo "upgrading" > /tmp/stabile.upgrading`; # This file makes sure postinst does not kill pressurecontrol
85
        `echo "Steamgine upgrade requested. Hang on..." >> /tmp/stabile.upgrade.log`;
86
        print `DEBCONF_DEBUG=developer apt-get -q -y --force-yes --no-install-recommends install stabile 2>&1 | tee -a /tmp/stabile.upgrade.log`;
87
        unlink '/tmp/stabile.upgrading';
88
        exit;
89
    }
90
}
91

    
92
# Disallow access to portmapper on port 111 to disallow browsing shares using showmount -e 10.0.0.1
93
# Static nfs ports are configured in stabile-postinst
94
print `/sbin/iptables -D INPUT -i lo -p udp --dport 111 -j ACCEPT 2>/dev/null`;
95
print `/sbin/iptables -A INPUT -i lo -p udp --dport 111 -j ACCEPT`;
96
print `/sbin/iptables -D INPUT -p udp --dport 111 -j DROP 2>/dev/null`;
97
print `/sbin/iptables -A INPUT -p udp --dport 111 -j DROP`;
98
print `/sbin/iptables -D INPUT -i lo -p tcp --dport 111 -j ACCEPT 2>/dev/null`;
99
print `/sbin/iptables -A INPUT -i lo -p tcp --dport 111 -j ACCEPT`;
100
print `/sbin/iptables -D INPUT -p tcp --dport 111 -j DROP 2>/dev/null`;
101
print `/sbin/iptables -A INPUT -p tcp --dport 111 -j DROP`;
102
# Make webmin inaccessible from 10.0.0.0
103
print `/sbin/iptables -D INPUT -p udp --destination-port 10000 -s 10.0.0.0/255.0.0.0 -j DROP 2>/dev/null`;
104
print `/sbin/iptables -A INPUT -p udp --destination-port 10000 -s 10.0.0.0/255.0.0.0 -j DROP`;
105
print `/sbin/iptables -D INPUT -p tcp --destination-port 10000 -s 10.0.0.0/255.0.0.0 -j DROP 2>/dev/null`;
106
print `/sbin/iptables -A INPUT -p tcp --destination-port 10000 -s 10.0.0.0/255.0.0.0 -j DROP`;
107
# Make netserver (netperf) inaccessible outside of 10.0.0.0
108
print `/sbin/iptables -D INPUT -p udp --destination-port 12865 ! -s 10.0.0.0/255.0.0.0 -j DROP 2>/dev/null`;
109
print `/sbin/iptables -A INPUT -p udp --destination-port 12865 ! -s 10.0.0.0/255.0.0.0 -j DROP`;
110
print `/sbin/iptables -D INPUT -p tcp --destination-port 12865 ! -s 10.0.0.0/255.0.0.0 -j DROP 2>/dev/null`;
111
print `/sbin/iptables -A INPUT -p tcp --destination-port 12865 ! -s 10.0.0.0/255.0.0.0 -j DROP`;
112
# Make ugly exception for virtual instances running in Stabile
113
if (-e "/tmp/internalip") {
114
    my $internalip = `cat /tmp/internalip`; chomp $internalip;
115
    print `/sbin/iptables -D INPUT -p tcp --destination-port 10000 -s $internalip/24 -j ACCEPT 2>/dev/null`;
116
    print `/sbin/iptables -I INPUT -p tcp --destination-port 10000 -s $internalip/24 -j ACCEPT`;
117
}
118
# Don't route packets to pistons
119
print `/sbin/iptables -D FORWARD -i $datanic+ -o $datanic+ -d 10.0.0.0/24 -j DROP`;
120
print `/sbin/iptables -A FORWARD -i $datanic+ -o $datanic+ -d 10.0.0.0/24 -j DROP`;
121
# Masquerade
122
my $masq = `/sbin/iptables -L -n -t nat`;
123
unless ($masq =~ "MASQUERADE.+all.+--.+0\.0\.0\.0/0") {
124
    `/sbin/iptables --table nat --append POSTROUTING --out-interface $datanic -s 10.0.0.0/8 -j MASQUERADE`;
125
}
126
# Make sure webmin conf is readable
127
`chmod 644 /etc/webmin/miniserv.conf`;
128
# Make sure log is readable and writable
129
`touch /var/log/stabile/steam.log`;
130
`chown www-data:www-data /var/log/stabile/steam.log`;
131
`chmod 664 /var/log/stabile/steam.log`;
132
# Make sure ui_update can remove files from /tmp
133
`chmod -t /tmp`;
134
# Copy customized guacamole index file
135
if (-e "/usr/share/stabile/guacamole-index.html" && !(`grep stabile /var/lib/tomcat8/webapps/guacamole/index.html`)) {
136
    `cp /usr/share/stabile/guacamole-index.html /var/lib/tomcat8/webapps/guacamole/index.html`;
137
    `systemctl restart tomcat8`;
138
}
139
# Handle strange error on node when reading sudoers file
140
#system("perl -pi -e 's/(Defaults.*)/Defaults:irigo !requiretty/' /mnt/stabile/tftp/bionic/casper/filesystem.dir/etc/sudoers.d/stabile");
141

    
142
print "Mounting storage pools\n";
143
mountPaths();
144

    
145
print "Checking quotas\n";
146
foreach my $sp (@spoolpaths) {
147
    `quotacheck -c "$sp"` if (-e "$sp");
148
}
149

    
150
if (-d "/sys/fs/cgroup") {
151
    print "Setting cgroups limits\n";
152
    my @files = ("/etc/stabile/cgconfig.conf");
153
    push @files, "/etc/cgconfig.conf" if (-s "/etc/cgconfig.conf");
154
    foreach my $file (@files) {
155
        open (FILE, "< $file") || die "problem opening $file\n";
156
        my @lines = <FILE>;
157
        close FILE;
158
        chomp @lines;
159
        my $group;
160
        my @newlines;
161
        for my $line (@lines) {
162
            $group = $1 if ($line =~ /^group (\S+) \{/ );
163
            if ($group eq 'stabile') {
164
                $line =~ s/(blkio.throttle.read_bps_device = "\d+:\d+).*/$1 $valve_readlimit";/;
165
                $line =~ s/(blkio.throttle.write_bps_device = "\d+:\d+).*/$1 $valve_writelimit";/;
166
                $line =~ s/(blkio.throttle.read_iops_device = "\d+:\d+).*/$1 $valve_iopsreadlimit";/;
167
                $line =~ s/(blkio.throttle.write_iops_device = "\d+:\d+).*/$1 $valve_iopswritelimit";/;
168
            } elsif ($group eq 'stabilevm') {
169
                $line =~ s/(blkio.throttle.read_bps_device = "\d+:\d+).*/$1 $vm_readlimit";/;
170
                $line =~ s/(blkio.throttle.write_bps_device = "\d+:\d+).*/$1 $vm_writelimit";/;
171
                $line =~ s/(blkio.throttle.read_iops_device = "\d+:\d+).*/$1 $vm_iopsreadlimit";/;
172
                $line =~ s/(blkio.throttle.write_iops_device = "\d+:\d+).*/$1 $vm_iopswritelimit";/;
173
            }
174
            push @newlines, $line;
175
        }
176
        open (FILE, "> $file") || die "problem opening $file\n";
177
        print FILE join("\n", @newlines);
178
        close (FILE);
179
    }
180
    #    `cgconfigparser -l /etc/stabile/cgconfig.conf -l /etc/cgconfig.conf`;
181
    my $cmd = "cgconfigparser " . join(" ", map { '-l ' . $_ } @files);
182
    `$cmd`;
183
} else {
184
    print "cgroups are not enabled!\n";
185
}
186

    
187
if ($uuid && !ref $uuid) {
188
    ;
189
} else {
190
    my $ug = new Data::UUID;
191
    $uuid = $ug->create_str();
192
    $cfg->param('ENGINEID', $uuid);
193
    $cfg->save();
194
    $main::syslogit->('pressurecontrol', 'info', "Generated new UUID: $uuid");
195
}
196

    
197
if ($uuid) {
198
    $main::syslogit->('pressurecontrol', 'info', "Starting pressurecontrol on engine: $uuid");
199
} else {
200
    $main::syslogit->('pressurecontrol', 'info', "Unable to get engine id...");
201
    die "Unable to get engine id...\n";
202
}
203

    
204
my $tktcfg_file = "/etc/apache2/conf-available/auth_tkt_cgi.conf";
205
my $tktcfg = ConfigReader::Simple->new($tktcfg_file, [qw(TKTAuthSecret)]);
206
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
207

    
208
unless ($tktkey) {
209
	$main::syslogit->('pressurecontrol', 'info', "Unable to get engine tktkey...");
210
	die "Unable to get engine tktkey...\n";
211
}
212

    
213
my $browser = LWP::UserAgent->new;
214
$browser->timeout(15);
215
$browser->agent('pressurecontrol/1.0b');
216
$browser->protocols_allowed( [ 'http','https'] );
217

    
218
my %postreq;
219
$postreq->{'engineid'} = $uuid;
220
$postreq->{'enginetkthash'} = sha512_hex($tktkey);
221

    
222
my $linked = $cfg->param('ENGINE_LINKED');
223
my $pullconfigs = $cfg->param('PULL_CONFIGS');
224
print "Engine is marked as linked\n" if ($linked);
225
my $content;
226
if ($linked) {
227
    print "Checking linking with Stabile Registry\n";
228
    $content = $browser->post("https://www.origo.io/irigo/engine.cgi?action=lookup", $postreq)->content();
229
    $linked = ($content =~ /(.*is linked.*)/i);
230
} else {
231
    print "This engine is not marked as linked to Stabile Registry\n";
232
}
233

    
234
if ($content =~ /(.*linked.*)/i) {
235
    print $1,"\n";
236
} else {
237
    print "Linking could not be verified - check your configuration and/or your Internet connection\n";
238
    print $content;
239
}
240
$main::syslogit->('pressurecontrol', 'info', "This engine is " . ($linked?'':'not') . " linked to Stabile Registry.");
241

    
242
# If linked, get config parameters from origo.io
243
$cfg->param('ENGINE_LINKED', 0+$linked);
244
if ($linked) {
245
    my %cfgkeys = filterCfgParams();
246
    my %enginekeys = %{$cfgkeys{'engine'}};
247
    my %pistonkeys = %{$cfgkeys{'piston'}};
248

    
249
    unless (tie %idreg,'Tie::DBI', {
250
        db=>'mysql:steamregister',
251
        table=>'nodeidentities',
252
        key=>'identity',
253
        autocommit=>0,
254
        CLOBBER=>3,
255
        user=>$dbiuser,
256
        password=>$dbipasswd}) {throw Error::Simple("Register could not be accessed")};
257

    
258
    my %nodeconfigs;
259
    # Build hash of known node config files
260
    foreach my $valref (values %idreg) {
261
        my $nodeconfigfile = $valref->{'path'} . "/casper/filesystem.dir/etc/stabile/nodeconfig.cfg";
262
        next if ($nodeconfigs{$nodeconfigfile}); # Node identities may share basedir and node config file
263
        $nodeconfigfile = $valref->{'path'} . "/live/filesystem.dir/etc/stabile/nodeconfig.cfg" unless (-e $nodeconfigfile); # support old naming
264
        if (-e $nodeconfigfile) {
265
            my $nodecfg = new Config::Simple($nodeconfigfile);
266
            $nodeconfigs{$nodeconfigfile} = $nodecfg;
267
        }
268
    }
269
    my $defaultpath = $idreg{'default'}->{'path'} . "/casper/filesystem.dir/etc/stabile/nodeconfig.cfg";
270
    untie %idreg;
271
    my @nodecfgkeys = keys %nodeconfigs;
272

    
273
    foreach my $line (split /\n/, $content) { # This is where we get ENGINENAME and ENGINEOWNER and write them to config
274
        next if ($line=~ /(.*is linked.*)/i || !$line);
275
        if ($line =~ /(\S+): ?(.*)/) {
276
            my $k = $1;
277
            my $v = $2; # Value received from origo.io
278
            if ($enginekeys{$k}) {
279
                my @vals = $cfg->param($k); my $val = join(", ", @vals);
280
                if ($val ne $v) {
281
                    if (($pullconfigs || $k eq 'ENGINEUSER') && $k ne 'ENGINEID' && $k ne 'ENGINE_LINKED') {
282
                        print "Received modified config: $line\n";
283
                        $cfg->param($k, $v);
284
                    }
285
                } else {
286
                    #print "Unmodified config line received: $line, $val[0]\n";
287
                }
288
            } elsif ($pistonkeys{$k}) {
289
                foreach my $cfgkey (@nodecfgkeys) {
290
#                    my @vals = $nodecfg->param($k); my $val = join(", ", @vals);
291
                    my @vals = $nodeconfigs{$cfgkey}->param($k); my $val = join(", ", @vals);
292
                    if ($val ne $v) {
293
                    # Different node types may require different interface specifications
294
                    # maintained manually
295
                        if (($k eq 'ADMIN_NIC' || $k eq 'DATA_NIC') && $cfgkey ne $defaultpath) {
296
                            print "Not changing NIC on non-default nodeidentity $cfgkey: $line\n";
297
                        } elsif ($pullconfigs) {
298
                            print "Received modified node config ($cfgkey): $line\n";
299
                            $nodeconfigs{$cfgkey}->param($k, $v);
300
                            if ($k =~ /READ_LIMIT|WRITE_LIMIT/) {
301
                                print `echo /nodes/reloadall | stash`;
302
                            }
303
                        }
304
                    }
305
                }
306
            }
307
        }
308
    }
309
    foreach my $nodecfg (values %nodeconfigs) {$nodecfg->save() if ($pullconfigs)};
310
} else {
311
	$postreq = {};
312
}
313
$cfg->save();
314

    
315
if ($downloadmasters) {
316
    downloadMasters();
317
} else {
318
    print "Not downloading masters from origo.io, because DOWNLOAD_MASTERS not set in config.\n"
319
}
320

    
321
print "Starting pressurecontrol on engine: $uuid\n";
322

    
323
my $pid = fork();
324

    
325
if ($pid) {
326
    my $pid2 = fork();
327
    if ($pid2) {
328
        my $amtdone;
329
        $amtdone = 1 if ($debug); # Don't wait for this when debugging
330
        if ($uuid) {
331
            my $tktname = substr($uuid, 0, 8);
332
            my $cookiebase;
333
            $cookiebase = `cat /etc/stabile/cookiebase` if -e "/etc/stabile/cookiebase";
334
            chomp $cookiebase;
335

    
336
            my $apachecfg = '/etc/apache2/sites-available/stabile-ssl';
337
            $apachecfg = '/etc/apache2/conf.d/stabile-ssl.conf' if -e ('/etc/apache2/conf.d/stabile-ssl.conf');
338
            $apachecfg = '/etc/apache2/conf-available/stabile-ssl.conf' if -e ('/etc/apache2/conf-available/stabile-ssl.conf');
339
            $apachecfg = '/etc/apache2/sites-available/stabile-ssl.conf' if -e ('/etc/apache2/sites-available/stabile-ssl.conf');
340
            system("perl -pi -e 's/(.*TKTAuthCookieName.*\\n)//' $apachecfg");
341
            system("perl -pi -e 's/(.*TKTAuthDomain.*\\n)//' $apachecfg");
342
            if ($cookiebase) {
343
                system("perl -pi -e 's/(.*TKTAuthLoginURL.*)/\$1\\n        TKTAuthCookieName auth_$tktname\\n        TKTAuthDomain $cookiebase/' $apachecfg");
344
            } else {
345
                system("perl -pi -e 's/(.*TKTAuthLoginURL.*)/\$1\\n        TKTAuthCookieName auth_$tktname/' $apachecfg");
346
            }
347

    
348
            system("perl -pi -e 's/(.*TKTAuthCookieName.*\\n)//' $tktcfg_file");
349
            system("perl -pi -e 's/(^<Directory \\\/var\\\/www\\\/.*auth>.*)/\$1\\n  TKTAuthCookieName auth_$tktname/' $tktcfg_file");
350

    
351
        }
352
        print `systemctl restart apache2`;
353
        my $reportbackup = 0;
354
        my $command = 'fullstatsb';
355
        # Main loop
356
        while ($running) {
357
            $cfg = new Config::Simple("/etc/stabile/config.cfg");
358
            my $dl = $cfg->param('DOWNLOAD_MASTERS');
359
            # Monitor changes in downloadmasters - only download if changed - periodic check is done below
360
            if ((!$downloadmasters && $dl) || $dl==2) { # 2 is force one-shot download
361
                print "Now downloading master images...\n";
362
                my $mastersavailable = downloadMasters();
363
                if ($dl==2) {
364
                    $cfg->param('DOWNLOAD_MASTERS',1 );
365
                    $cfg->save();
366
                    $main::updateUI->({tab=>"nodes", user=>'irigo', type=>"message", message=>"No new or updated masters available"}) unless ($mastersavailable);
367
                }
368
            } else {
369
                print "Not downloading master images right now...\n";
370
            }
371
            $downloadmasters = $dl;
372

    
373
            # Remove dangling up_update processes and pipes. New pipes get created by ui_update cgi's that are active
374
            print "Cleaning up ui_update tasks and named pipes\n";
375
            print `/bin/rm /tmp/*.tasks 2>/dev/null; /usr/bin/pkill -f "/bin/cat < /tmp/.*tasks"`;
376
            `/usr/bin/pkill -f "/usr/bin/tee /tmp"`; # Kill hanging ui updates
377

    
378
#            my $statsjson = `echo /nodes/$command | stash 2>>/dev/null`;
379
            my $statsjson = `REMOTE_USER=irigo $basedir/cgi/nodes.cgi -c -a $command`;
380

    
381
            if ($linked) {
382
                print "Reporting status to Stabile Registry ($command)\n";
383
                $postreq->{'POSTDATA'} = $statsjson;
384
                #            print $statsjson;
385
                my $res = $browser->post("https://www.origo.io/irigo/engine.cgi?action=status", $postreq)->content();
386
                my $ok = ($res =~ /OK: (.*)/i);
387
                print $res;
388
            }
389

    
390
            $reportbackup++;
391
            if ($reportbackup > 100) {
392
                $command = 'fullstatsb';
393
                downloadMasters();
394
                $reportbackup = 0;
395
            } else {
396
                $command = 'fullstats';
397
            }
398

    
399
            print &pretty_time.": ".`REMOTE_USER=irigo $basedir/cgi/nodes.cgi -a updateregister -f`;
400
            print &pretty_time.": ".`REMOTE_USER=irigo $basedir/cgi/servers.cgi -a updateregister -f`;
401
            print &pretty_time.": ".`REMOTE_USER=irigo $basedir/cgi/images.cgi -a updateregister -f`;
402
            unless ($amtdone) {
403
                print "Updating AMT info\n";
404
                print &pretty_time.": ".`REMOTE_USER=irigo $basedir/cgi/nodes.cgi -a updateamtinfo`;
405
                $amtdone = 1;
406
            }
407
            print &pretty_time.": ".`/usr/local/bin/steamExec releaseolddhcpleases`;
408

    
409
            sleep $naptime;
410
        }
411
    } else {
412
        print "Registering Gearman worker\n";
413
        use Storable qw(freeze thaw);
414
        use Gearman::Worker;
415
        use List::Util qw( sum );
416
        my $worker = Gearman::Worker->new;
417
        $worker->job_servers('127.0.0.1:4730');
418
        $worker->register_function(steamexec =>
419
            sub {
420
                my %args = %{ thaw($_[0]->arg) };
421
                my $tktuser = $args{tktuser};
422
                my $user = $args{user};
423
                my $target = $args{target};
424
                my $package = $args{package};
425
                my $uargs;
426
                my $res = "No result";
427
                if ($args{args}) {
428
                    if (ref $args{args}) { # not a string
429
                        $uargs = uri_escape( JSON->new->allow_nonref->encode ( $args{args} ));
430
                    } else { # assume a string
431
                        $uargs = uri_escape($args{args});
432
                    }
433
                }
434
                if ($args{action}) {
435
                    if ($package =~ /systems|servers|images|networks|nodes|users/) {
436
                        my $action = 'gear_' . $args{action};
437
                        my $cmd = qq|REMOTE_USER=$tktuser $basedir/cgi/$package.cgi -v -s $user -a $action|;
438
                        $cmd .= qq| -t "$target"| if ($target);
439
                        $cmd .= qq| -g "$uargs"| if ($uargs);
440
                        print "Executing $cmd\n";
441
                        $res = `$cmd`;
442
                        print "-> $res\n" if ($debug);
443
                    } else {
444
                        print "Not executing $args{action}, $package\n";
445
                    }
446
                }
447
                $res;
448
            });
449
        $worker->register_function(restart_apache =>
450
            sub {
451
                `pkill -HUP apache2`;
452
            });
453
        $worker->register_function(sum => sub { sum @{ thaw($_[0]->arg) } });
454
        $worker->work while ($running);
455
    }
456
} else {
457
    my $d = HTTP::Daemon->new(
458
        LocalAddr => '127.0.0.1',  # remove this to listen from other machines
459
                       # (i.e. open-relay... be careful of spammers!)
460
        LocalPort => 8082,
461
        ReuseAddr => 1
462
    ) || die "Local port not available";
463
    print "Starting managementlink proxy on:", $d->url, "\n";
464

    
465
    # Avoid leaving zombies
466
    $SIG{CHLD} = 'IGNORE';
467
    # Avoid dying from browser cancel
468
    $SIG{PIPE} = 'IGNORE';
469

    
470
    my %networks;
471
    print "Configuring networking and loading network tables\n";
472
    eval {`/bin/echo 1 > /proc/sys/net/ipv4/ip_forward`; 1;} or do {print "Unable to enable nat'ing: $@\n";};
473

    
474
    $content = `REMOTE_USER=irigo $basedir/cgi/networks.cgi -a jsonlist -f`;
475
    my $nets = from_json($content);
476
    foreach my $net (@$nets) {
477
        $networks{$net->{uuid}} = $net;
478
    };
479
    fork(); fork(); fork(); # 2^3 = 8 processes
480

    
481
    my $dbrowser = LWP::UserAgent->new;
482
    $dbrowser->timeout(60);
483
    $dbrowser->agent('pressurecontrol/1.0b');
484
    $dbrowser->protocols_allowed( [ 'http','https'] );
485

    
486
    while (my $c = $d->accept) {
487
        while (my $request = $c->get_request) {
488
            my $uri = $request->uri->as_string;
489
            my $host = $c->sockhost;
490
            my $response;
491
            if ($uri =~ /^\/\/(https?:\/\/?)?(\S{36})(:\d+)?(.*)/) {
492
                my $prot = $1||'http://';
493
                my $networkuuid = $2;
494
                my $uriport = $3;
495
                my $uripath = $4;
496
                $prot = "$prot/" unless ($prot =~ /\/\//);
497
                my $geturi = $prot . "$networkuuid$uriport$uripath";
498
                $host = $2 if ($uripath =~ /(\?|\&)host=(.+)\&/ || $uripath =~ /(\?|\&)host=(.+)/);
499
                my $user = $request->header('STEAM_USER');
500
                my $networkuser = $networks{$networkuuid}->{'user'};
501
                if (!$networkuser) { # If not found, we look up ip etc. in the DB
502
                    print "Loading $networkuuid ($uri, $user) from db\n";
503
                    $content = `REMOTE_USER=$user $basedir/cgi/networks.cgi -a list -u $networkuuid`;
504
                    print "Got: $content\n";
505
                    my $net = from_json($content);
506
                    if (ref $net eq 'HASH') {
507
                        $intip = $net->{'internalip'};
508
                        $extip = $net->{'externalip'};
509
                        $ip = $intip;
510
                        $ip = $extip if (!$ip || $ip eq '--');
511
                        $networkuser = $net->{'user'};
512

    
513
                        $networks{$networkuuid}->{'internalip'} = $intip;
514
                        $networks{$networkuuid}->{'externalip'} = $extip;
515
                        $networks{$networkuuid}->{'user'} = $networkuser;
516
                    }
517
                }
518
                $user = validateUser($user, $networkuser); # Set $user to $networkuser if validated
519
                if ($user) {
520
                    my $baseuri = "$baseurl/pipe/$networkuuid$uriport/";
521
                    my $steamhost = $request->header('STEAM_HOST');
522
                    my $steamdom = $1 if ($steamhost =~ /\w+\.(\w+\.\w+)/);
523
                    my $steamdom2 = "uncloud.co";
524
                    $baseuri = "https://$steamhost/stabile/pipe/$networkuuid$uriport/" if ($steamhost);
525
                    my $extip = $networks{$networkuuid}->{'externalip'};
526
                    my $intip = $networks{$networkuuid}->{'internalip'};
527
                    my $ip = $intip;
528
                    $ip = $extip if (!$ip || $ip eq '--');
529
                    if ($ip && $networkuser && $networkuser eq $user) {
530
                        $geturi =~ s/$networkuuid/$ip/;
531
                        #$geturi .= '/' unless ($geturi =~ /\/$/);
532
                        print "Getting $networkuuid: $geturi ($uri)\n" if ($debug);
533
                        $request->uri($geturi);
534
                        $request->remove_header( 'Referer' );
535
                        $request->header('Host' => $host) if ($host);
536
                        if ($uri =~ /:4200\/\?$/) { # shellinaboxd
537
                            $dbrowser->timeout(120);
538
                            $response = $dbrowser->simple_request( $request );
539
                            $dbrowser->timeout(60);
540
                        } else {
541
                            $response = $dbrowser->simple_request( $request );
542
                        }
543

    
544
                    # Do a lot of proxy text translations in order to fix things up a bit in the browser
545
                        #my $link = $response->header('Link');
546
                        #$link =~ s/<\/(\w)/<$baseuri$1/gi;
547
                        #$response->header('Link', $link);
548

    
549
                        my $loc = $response->header('Location');
550
                        if ($loc && $loc =~ /^https?:\/\/.+\/(.+)\//) {
551
                            $response->header('Location', "$baseuri/$1/");
552
                        }
553
                        ${$response->content_ref} =~ s/\{internalip\}/$ip/gi;
554
                        ${$response->content_ref} =~ s/\{externalip\}/$extip/gi;
555
                        # \w is included to avoid matching url's starting with //, which should be left alone
556
                        # ${$response->content_ref} =~ s/(href|src|action|value)=('|")\/(\w)/$1=$2$baseuri$3/gi;
557
                        # This is purely to fixup javascript in Wordpress install.php
558
                    #    ${$response->content_ref} =~ s/\\\/home/../;
559
                        # This is to fix css links in Wordpress install
560
                        ${$response->content_ref} =~ s/'\/home\/wp-includes/'$baseuri\/home\/wp-includes/g;
561
                        ${$response->content_ref} =~ s/'\/home\/wp-admin/'$baseuri\/home\/wp-admin/g;
562
                        # This is purely to fixup image path in Thirdlane PBX UI
563
#                        ${$response->content_ref} =~ s/\.\.\/\.\.\/\.\.\//..\/..\//;
564
                        
565
                        ${$response->content_ref} =~ s/(load\()('|")\//$1$2$baseuri/gi;
566
                        $response->push_header( 'X-Via' => "1.1 $baseuri" );
567
                        $response->push_header( 'X-BaseUri' => "1.1 $baseuri" );
568
                        $response->remove_header( 'Content-Security-Policy');
569
                        $response->push_header( 'Content-Security-Policy' => "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ajax.googleapis.com https://code.jquery.com https://ajax.microsoft.com https://cdn.jsdelivr.net https://*.$steamdom https://*.$steamdom2; style-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com https://ajax.googleapis.com https://code.jquery.com https://*.$steamdom https://*.$steamdom2" );
570
                        $c->send_response( $response );
571
                        print "Got: $geturi\n" if ($debug);
572
                    } else {
573
                        my $msg = "Not found: $networkuuid, $ip, $networkuser, " . $user;
574
                    #    $c->send_status_line('200', $msg);
575
                        $c->send_status_line('404', $msg);
576
                        $c->send_header('X-Via', "1.1 $geturi");
577
                        print "$msg\n";
578
                        #last;
579
#                        $c->send_error(404);
580
                    }
581
                } else {
582
                    $c->send_error(RC_FORBIDDEN);
583
                }
584
            }    else {
585
                my $msg = "Forbidden - not allowing: $uri";
586
                $c->send_status_line('404', $msg); #403
587
                $c->send_header('X-Via', "1.1 $uri");
588
                #$c->send_error(RC_FORBIDDEN, $msg);
589
                #$main::syslogit->($stackuser, 'info', $msg);
590
                print "$msg\n";
591
                #last;
592
            }
593
        }
594
        $c->close;
595
        undef($c);
596
    }
597
}
598

    
599
print "Done\n";
600
exit;
601

    
602
sub lookupProcess {
603
    my $process = shift;
604
    my $match;
605
    my $t = new Proc::ProcessTable;
606
    foreach $p ( @{$t->table} ){
607
        my $pcmd = $p->cmndline;
608
        if ($pcmd =~ /$process/) {
609
            $match = $p->pid;
610
            last;
611
        }
612
    }
613
    return $match;
614
}
615

    
616
sub validateUser {
617
    my ($vuser, $account) = @_;
618

    
619
    unless (tie %userreg,'Tie::DBI', {
620
        db=>'mysql:steamregister',
621
        table=>'users',
622
        key=>'username',
623
        autocommit=>0,
624
        CLOBBER=>1,
625
        user=>$dbiuser,
626
        password=>$dbipasswd}) {return};
627

    
628
    my $dbuser = $userreg{$vuser}->{'username'};
629
    unless ($dbuser) {untie %userreg; $vuser = '';};
630

    
631
    $privileges = $userreg{$vuser}->{'privileges'};
632
    if (index($privileges,"d")!=-1) {$vuser = ''}; # disabled user
633

    
634
    if ($account && $vuser && $account ne $vuser) {
635
        my %ahash;
636
        my @accounts = split(/,\s*/, $userreg{$vuser}->{'accounts'});
637
        my @accountsprivs = split(/,\s*/, $userreg{$vuser}->{'accountsprivileges'});
638
        for my $i (0 .. $#accounts)
639
            { $ahash{$accounts[$i]} = $accountsprivs[$i] || 'r'; }
640

    
641
        if ($ahash{$account}) {
642
            my $privileges = $ahash{$account};
643
            if ($userreg{$account}->{'username'} && index($privileges,"d")==-1){
644
                $vuser = $account;
645
            } else {
646
                $vuser = '';
647
            }
648
        } else {
649
            $vuser = '';
650
        }
651
    }
652

    
653
    untie %userreg;
654
    return $vuser;
655
}
656

    
657
sub checkDB {
658
	my $dbOK = 0;
659
	eval {
660
		if (tie %userreg,'Tie::DBI', {
661
		    db=>'mysql:steamregister',
662
		    table=>'users',
663
		    key=>'username',
664
		    autocommit=>0,
665
		    CLOBBER=>1,
666
		    user=>$dbiuser,
667
		    password=>$dbipasswd}) {
668
				untie %userreg;	
669
                print "Database looks OK\n";
670
			    $dbOK = 1;
671
		}
672
	};
673
	unless ($dbOK) {
674
        eval {
675
            print "Creating initial DB...\n";
676
            `/var/lib/dpkg/info/stabile.postinst createdb`;
677
            if (tie %userreg, 'Tie::DBI', {
678
                    db         => 'mysql:steamregister',
679
                    table      => 'users',
680
                    key        => 'username',
681
                    autocommit => 0,
682
                    CLOBBER    => 1,
683
                    user       => $dbiuser,
684
                    password   => $dbipasswd }) {
685
                untie %userreg;
686
                $dbOK = 1;
687
                `systemctl restart tomcat8`; # This looks like a first run, restart tomcat to load our auth
688
            }
689
        };
690
    }
691
	return $dbOK;
692
}
693

    
694
sub pretty_time {
695
    my $current_time = time;
696
    ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($current_time);
697
#    my $year += 1900;
698
    my $month = substr("0" . ($mon+1), -2);
699
    my $pretty_time = sprintf "%4d-%02d-%02d@%02d:%02d:%02d",$year+1900,$mon+1,$mday,$hour,$min,$sec;
700
    return $pretty_time;
701
}
702

    
703
# Pick out config file parameters from general hash
704
sub filterCfgParams {
705
    my $paramsref = shift;
706
    my %params = %{$paramsref};
707

    
708
    my @engineparams = qw(
709
    EXTERNAL_IP_RANGE_START
710
    EXTERNAL_IP_RANGE_END
711
    VLAN_RANGE_START
712
    VLAN_RANGE_END
713
    EXTERNAL_SUBNET_SIZE
714
    PROXY_IP
715
    PROXY_SUBNET_SIZE
716
    PROXY_GW
717
    PROXY_NIC
718
    PROXY_IP_RANGE_START
719
    PROXY_IP_RANGE_END
720
    RDIFF-BACKUP_ENABLED
721
    RDIFF-BACKUP_USERS
722
    ENGINE_DATA_NIC
723
    EXTERNAL_NIC
724
    EXTERNAL_IP_QUOTA
725
    MEMORY_QUOTA
726
    VCPU_QUOTA
727
    STORAGE_QUOTA
728
    RX_QUOTA
729
    TX_QUOTA
730
    STORAGE_POOLS_RDIFF-BACKUP_ENABLED
731
    STORAGE_POOLS_ADDRESS_PATHS
732
    STORAGE_POOLS_LOCAL_PATHS
733
    STORAGE_POOLS_NAMES
734
    STORAGE_POOLS_MOUNTABLE
735
    STORAGE_POOLS_DEFAULTS
736
    STORAGE_BACKUPDIR
737
    DBI_USER
738
    DBI_PASSWD
739
    CPU_OVERCOMMISION
740
    DO_DNS
741
    DO_XMPP
742
    SHOW_COST
743
    ENGINEID
744
    ENGINENAME
745
    ENGINEUSER
746
    DOWNLOAD_MASTERS
747
    NODE_STORAGE_OVERCOMMISSION
748
    NODESTORAGE_QUOTA
749
    CURRENCY
750
    EXTERNALIP_PRICE
751
    NODESTORAGE_PRICE
752
    STORAGE_PRICE
753
    MEMORY_PRICE
754
    VCPU_PRICE
755
    VALVE_READ_LIMIT
756
    VALVE_WRITE_LIMIT
757
    VALVE_IOPS_READ_LIMIT
758
    VALVE_IOPS_WRITE_LIMIT
759
    );
760
    my @pistonparams = qw(
761
    ADMIN_SERVER_ADDRESS
762
    STORAGE_SERVERS_ADDRESS_PATHS
763
    STORAGE_SERVERS_LOCAL_PATHS
764
    ADMIN_NIC
765
    DATA_NIC
766
    INITIALIZE_LOCAL_DISK
767
    VM_READ_LIMIT
768
    VM_WRITE_LIMIT
769
    VM_IOPS_READ_LIMIT
770
    VM_IOPS_WRITE_LIMIT
771
    PISTON_READ_LIMIT
772
    PISTON_WRITE_LIMIT
773
    PISTON_IOPS_READ_LIMIT
774
    PISTON_IOPS_WRITE_LIMIT
775
    );
776

    
777
    my %cfghash;
778
    if ($paramsref) {
779
        foreach my $param (@engineparams) {
780
            $cfghash{$param} = $params{$param} if (defined $params{$param});
781
        }
782
        foreach my $param (@pistonparams) {
783
            $cfghash{$param} = $params{$param} if (defined $params{$param});
784
        }
785
    } else {
786
        my %enginehash; @enginehash{@engineparams} = (1) x @engineparams;
787
        my %pistonhash; @pistonhash{@pistonparams} = (1) x @pistonparams;
788

    
789
        $cfghash{engine} = \%enginehash;
790
        $cfghash{piston} = \%pistonhash;
791
    }
792
    return %cfghash;
793
}
794

    
795
sub mountPaths {
796
    my @tenderlist = $cfg->param('STORAGE_POOLS_ADDRESS_PATHS');
797
    my @tenderpathslist = $cfg->param('STORAGE_POOLS_LOCAL_PATHS');
798

    
799
    my $mounts = `cat /proc/mounts`;
800
    for (my $i=0; $i<= scalar @tenderpathslist; $i++) {
801
        my $path = $tenderpathslist[$i];
802
        my $host = $tenderlist[$i];
803
        next unless ($path && $host);
804
        # Directory / mount point must exist
805
        if (!(-d $path)) {
806
            print "Creating storage pool $path ($host)\n";
807
            mkdir "$path" or {print ("Error $path could not be created\n")};
808
        } else {
809
            print "Storage pool exists: $path ($host)\n";
810
        };
811
        unless ($host eq 'local' || $mounts =~ m/$path/i) {
812
            $main::syslogit->('pressurecontrol', 'info', "Mounting $path from $host");
813
            eval {print `mount -o intr,noatime,nfsvers=3 $host $path`; 1;} or do {print $@;};
814
        }
815
    }
816
    # Allow user irigo to receive snapshots from nodes
817
    print `zfs allow irigo create,mount,snapshot,receive,mountpoint,compression,sharenfs,userprop stabile-backups` if (`zfslist` =~ /stabile-backups/);
818
}
819

    
820
sub downloadMasters {
821
    print "Looking for available masters at Stabile Registry...\n";
822
    # Reload config in case storage setup changed
823
    $cfg = new Config::Simple("/etc/stabile/config.cfg");
824
    @spoolpaths = $cfg->param('STORAGE_POOLS_LOCAL_PATHS');
825
    my @dlmasters;
826

    
827
    unless (tie %imagereg,'Tie::DBI', { # Needed for ValidateItem
828
        db=>'mysql:steamregister',
829
        table=>'images',
830
        key=>'path',
831
        autocommit=>0,
832
        CLOBBER=>3,
833
        user=>$dbiuser,
834
        password=>$dbipasswd}) {print "Image register could not be accessed\n"; exit 0;};
835

    
836
    $content = $browser->post("https://www.origo.io/irigo/engine.cgi?action=liststackmasters", $postreq)->content();
837
    my $stacks = {};
838
    if ($content =~ /^{/) {
839
        $stacks = from_json($content);
840
        print Dumper($stacks) if ($debug);
841
    }
842
    my @cmds;
843
    foreach my $stackuser (keys %$stacks) {
844
        my $ustacks = $stacks->{$stackuser};
845
        my @downloadalerts;
846
        foreach my $stack (@$ustacks) {
847
            my $fname = $stack->{'filename'};
848
            my $name = $stack->{'name'};
849
            my $managementlink = $stack->{'managementlink'};
850
            my $upgradelink = $stack->{'upgradelink'};
851
            my $terminallink = $stack->{'terminallink'};
852
            my $fname2 = $stack->{'image2'};
853
            my $name2 = $stack->{'name2'};
854
            my $version = $stack->{'version'};
855
            my $current = $stack->{'current'};
856
            my $appid = $stack->{'appid'};
857
            my $suser = $stack->{'user'};
858
            my $fsize = $stack->{'size'};
859
            my $vsize = $stack->{'virtualsize'};
860
            my $fsize2 = $stack->{'size2'};
861
            my $frealsize = $stack->{'realsize'};
862
            my $frealsize2 = $stack->{'realsize2'};
863
            my $imgurl = "$stack->{'url'}?auth_tkt=$stack->{'tkt'}";
864
            my $imgurl2;
865
            my $match;
866
            my $match2;
867
            my $lsize;
868
            my $lrealsize;
869
            my $lsize2;
870
            my $lrealsize2;
871
            my $f;
872
            my $f2;
873
            foreach my $sp (@spoolpaths) {
874
                $f = "$sp/$stackuser/$fname";
875
                if (-e $f) {
876
                    my @stat = stat($f);
877
                    $lsize = $stat[7];
878
                    $lrealsize = $stat[12] * 512;
879
                    $match = 1;
880
                    last;
881
                }
882
            }
883
            my $msg;
884
            my $cmd;
885
            my $img = $imagereg{$f};
886
            if ($match && $imagereg{$f} && ($imagereg{$f}->{'version'} ge $version || $lsize == $fsize) ) {
887
                $msg = "Image $fname ($version) found in $f [$fsize bytes]. Updating: $name, $suser. ";
888
                $imagereg{$f}->{'status'} = 'unused' if ($imagereg{$f}->{'status'} eq 'downloading');
889
                $imagereg{$f}->{'version'} = $version unless ($imagereg{$f}->{'version'} eq $version);
890
            } else {
891
                if (!$current) {
892
                    $msg = "Image $fname is not latest version, not downloading. ";
893
                } elsif (lookupProcess($fname)) { # Check if image is already being downloaded
894
                    $msg = "Image $fname is already being downloaded. ";
895
                } elsif (-s $f) { # Check if image exists
896
                    $msg = "Image $fname already exists but with a different size than in registry - please remove or correct. ";
897
                } else {
898
                    $f = "$spoolpaths[0]/$stackuser/$fname";
899
                    print `echo "status=downloading" > "$f.meta"`;
900
                    $msg = "Image $fname not found. Downloading $fsize bytes to $f ($stack->{url})... ";
901
                    my $msize = int($fsize /1024/1024);
902
                    my $uimsg = qq|Now downloading $name ($msize) MB)|;
903
                    push @downloadalerts, {tab=>"nodes", user=>'irigo', type=>"message", message=>$uimsg, download=>$f, name=>$name, size=>$frealsize};
904
                    push @dlmasters, $name;
905
                    #                    $main::updateUI->({tab=>"nodes", user=>'irigo', type=>"message", message=>$uimsg}); # notify user
906
                    $cmd = qq|wget --no-check-certificate --no-verbose --tries=4 -a /mnt/stabile/images/stacks-xfer.log -O "$f" $imgurl|;
907
                    $cmd .= qq|; rm "$f.meta"; steamExec updateimagestatus "$f" installable $vsize; steamExec updatebackingfile "$f"|;
908
                }
909
            }
910

    
911
            if ($imagereg{$f}) {
912
                $imagereg{$f}->{'name'} = $name if ($name);
913
                $imagereg{$f}->{'managementlink'} = $managementlink;
914
                $imagereg{$f}->{'upgradelink'} = $upgradelink;
915
                $imagereg{$f}->{'terminallink'} = $terminallink;
916
                $imagereg{$f}->{'version'} = $version;
917
                $imagereg{$f}->{'appid'} = $appid;
918
            } else {
919
                if ($name && $current) {
920
                    my $ug = new Data::UUID;
921
                    my $newuuid = $ug->create_str();
922
                    $imagereg{$f} = {
923
                        uuid=>$newuuid,
924
                        user=>$stackuser,
925
                        name=>$name,
926
                        type=>'qcow2',
927
                        managementlink=>$managementlink,
928
                        upgradelink=>$upgradelink,
929
                        terminallink=>$terminallink,
930
                        version=>$version,
931
                        virtualsize=>$virtualsize,
932
                        appid=>$appid
933
                    }
934
                }
935
            }
936
            if ($imagereg{$f} && $f =~ /\.master\.qcow2$/) {
937
                $imagereg{$f}->{'installable'} = 'true';
938
                $imagereg{$f}->{'type'} = 'qcow2';
939
                # Check for old versions and remove installable flag in order to not confuse matters
940
                if ($appid) {
941
                    foreach my $imgref (values %imagereg) {
942
                        if (
943
                            $imgref->{'path'} =~ /\.master\.qcow2$/
944
                            && $imgref->{'appid'} eq $appid
945
                            && $imgref->{'path'} ne $f
946
                            && $imgref->{'version'} lt $img->{'version'}
947
                        ) {
948
                            my $imgversion = $imgref->{'version'} || '1.0';
949
                            print "Removing installable flag from old version: $imgref->{path} ($imgref->{'version'} < $img->{'version'})\n";
950
                            $imgref->{'installable'} = 'false';
951
                            $imgref->{'name'} .= " ($imgversion)" unless ($imgref->{'name'} =~ /$imgversion/ );
952
                        }
953
                    }
954
                }
955
            }
956

    
957
            my $cmd2;
958
            if ($fname2) { # This app also has a data image
959
                foreach $sp (@spoolpaths) {
960
                    $f2 = "$sp/$stackuser/$fname2";
961
                    if (-e $f2) {
962
                        my @stat = stat($f2);
963
                        $lsize2 = $stat[7];
964
                        $lrealsize2 = $stat[12] * 512;
965
                        $match2 = 1;
966
                        last;
967
                    }
968
                }
969
                $imgurl2 = "$stack->{'url2'}?auth_tkt=$stack->{'tkt'}";
970

    
971
                # if ($match2 && $lsize2 >= $fsize2) {
972
                if ($match2 && $imagereg{$f2} && ($imagereg{$f2}->{'version'} ge $version || $lsize2 == $fsize2) ) {
973
                    $msg .= "Image2 $fname2 found in $f2 [$fsize2 bytes]. Updating: $name2 ";
974
                    $imagereg{$f2}->{'status'} = 'unused' if ($imagereg{$f2}->{'status'} eq 'downloading');
975
                } else {
976
                    if (!lookupProcess($fname2)) { # Check if image is already being downloaded
977
                        $f2 = "$spoolpaths[0]/$stackuser/$fname2";
978
                        print `echo "status=downloading" > "$f2.meta"`;
979
                        $msg .= "Image2 $fname2 not found. Downloading $fsize2 bytes to $f2... ";
980
                        my $msize = int($fsize2 /1024/1024);
981
                        my $uimsg = qq|Now downloading $name2 ($msize) MB)|;
982
                        push @downloadalerts, {tab=>"nodes", user=>'irigo', type=>"message", message=>$uimsg, download=>$f2, name=>$name2, size=>$frealsize2};
983
                        push @dlmasters, $name;
984
                        $cmd2 = qq|wget --no-check-certificate --no-verbose --tries=4 -a /mnt/stabile/images/stacks-xfer.log -O "$f2" $imgurl2|;
985
                        $cmd2 .= qq|; rm "$f2.meta"; steamExec updateimagestatus "$f2" downloaded $fsize2; steamExec updatebackingfile "$f2"|;
986
                    } else {
987
                        $msg .= "Image $fname is already being downloaded. ";
988
                    }
989
                }
990

    
991
                if ($imagereg{$f2}) {
992
                    $imagereg{$f2}->{'name'} = $name2 if ($name2);
993
                    $imagereg{$f2}->{'version'} = $version;
994
                    $imagereg{$f2}->{'appid'} = $appid;
995
                } else {
996
                    if ($name2) {
997
                        my $ug = new Data::UUID;
998
                        my $newuuid = $ug->create_str();
999
                        $imagereg{$f2} = {
1000
                            uuid=>$newuuid,
1001
                            user=>$stackuser,
1002
                            name=>$name2,
1003
                            version=>$version,
1004
                            type=>'qcow2',
1005
                            appid=>$appid
1006
                        }
1007
                    }
1008
                }
1009
            }
1010
            # Update data image information
1011
            $imagereg{$f}->{'image2'} = $f2 if ($imagereg{$f});
1012
            tied(%imagereg)->commit;
1013
            push @cmds, $cmd if ($cmd);
1014
            push @cmds, $cmd2 if ($cmd2);
1015
            print "$msg\n" if ($msg);
1016
            $main::updateUI->(@downloadalerts) if (@downloadalerts); # notify user
1017
            $main::syslogit->($stackuser, 'info', $msg) if ($msg && ($cmd || $cmd2));
1018
        }
1019
    }
1020
    untie %imagereg;
1021
    undef %imagereg;
1022
    foreach my $cmd (@cmds) {
1023
        #print "$cmd\n";
1024
        #system("sh $cmd &");
1025
        my $daemon = Proc::Daemon->new(
1026
                work_dir => '/usr/local/bin',
1027
                exec_command => $cmd
1028
            ) or do {$msg = "Error downloading $fname $@";};
1029
        my $pid = $daemon->Init() or do {$msg = "Error downloading $fname $@";};
1030
    }
1031
    return @dlmasters;
1032
}
1033

    
1034
sub TERMINATE {
1035
    print "Terminating\n" if ($running);
1036
    $running = 0;
1037
}
1038

    
1039
sub HUP {
1040
}
(15-15/27)