Project

General

Profile

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

    
132
print "Mounting storage pools\n";
133
mountPaths();
134

    
135
print "Checking quotas\n";
136
foreach my $sp (@spoolpaths) {
137
    `quotacheck -c "$sp"` if (-e "$sp");
138
}
139

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

    
177
if ($uuid && !ref $uuid) {
178
    ;
179
} else {
180
    my $ug = new Data::UUID;
181
    $uuid = $ug->create_str();
182
    $cfg->param('ENGINEID', $uuid);
183
    $cfg->save();
184
    $main::syslogit->('pressurecontrol', 'info', "Generated new UUID: $uuid");
185
}
186

    
187
if ($uuid) {
188
    $main::syslogit->('pressurecontrol', 'info', "Starting pressurecontrol on engine: $uuid");
189
} else {
190
    $main::syslogit->('pressurecontrol', 'info', "Unable to get engine id...");
191
    die "Unable to get engine id...\n";
192
}
193

    
194
my $tktcfg_file = "/etc/apache2/conf-available/auth_tkt_cgi.conf";
195
my $tktcfg = ConfigReader::Simple->new($tktcfg_file, [qw(TKTAuthSecret)]);
196
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
197

    
198
unless ($tktkey) {
199
	$main::syslogit->('pressurecontrol', 'info', "Unable to get engine tktkey...");
200
	die "Unable to get engine tktkey...\n";
201
}
202

    
203
my $browser = LWP::UserAgent->new;
204
$browser->timeout(15);
205
$browser->agent('pressurecontrol/1.0b');
206
$browser->protocols_allowed( [ 'http','https'] );
207

    
208
my %postreq;
209
$postreq->{'engineid'} = $uuid;
210
$postreq->{'enginetkthash'} = sha512_hex($tktkey);
211

    
212
my $linked = $cfg->param('ENGINE_LINKED');
213
my $pullconfigs = $cfg->param('PULL_CONFIGS');
214
print "Engine is marked as linked\n" if ($linked);
215
my $content;
216
if ($linked) {
217
    print "Checking linking with Stabile Registry\n";
218
    $content = $browser->post("https://www.origo.io/irigo/engine.cgi?action=lookup", $postreq)->content();
219
    $linked = ($content =~ /(.*is linked.*)/i);
220
} else {
221
    print "This engine is not marked as linked to Stabile Registry\n";
222
}
223

    
224
if ($content =~ /(.*linked.*)/i) {
225
    print $1,"\n";
226
} else {
227
    print "Linking could not be verified - check your configuration and/or your Internet connection\n";
228
    print $content;
229
}
230
$main::syslogit->('pressurecontrol', 'info', "This engine is " . ($linked?'':'not') . " linked to Stabile Registry.");
231

    
232
# If linked, get config parameters from origo.io
233
$cfg->param('ENGINE_LINKED', 0+$linked);
234
if ($linked) {
235
    my %cfgkeys = filterCfgParams();
236
    my %enginekeys = %{$cfgkeys{'engine'}};
237
    my %pistonkeys = %{$cfgkeys{'piston'}};
238

    
239
    unless (tie %idreg,'Tie::DBI', {
240
        db=>'mysql:steamregister',
241
        table=>'nodeidentities',
242
        key=>'identity',
243
        autocommit=>0,
244
        CLOBBER=>3,
245
        user=>$dbiuser,
246
        password=>$dbipasswd}) {throw Error::Simple("Register could not be accessed")};
247

    
248
    my %nodeconfigs;
249
    # Build hash of known node config files
250
    foreach my $valref (values %idreg) {
251
        my $nodeconfigfile = $valref->{'path'} . "/casper/filesystem.dir/etc/stabile/nodeconfig.cfg";
252
        next if ($nodeconfigs{$nodeconfigfile}); # Node identities may share basedir and node config file
253
        $nodeconfigfile = $valref->{'path'} . "/live/filesystem.dir/etc/stabile/nodeconfig.cfg" unless (-e $nodeconfigfile); # support old naming
254
        if (-e $nodeconfigfile) {
255
            my $nodecfg = new Config::Simple($nodeconfigfile);
256
            $nodeconfigs{$nodeconfigfile} = $nodecfg;
257
        }
258
    }
259
    my $defaultpath = $idreg{'default'}->{'path'} . "/casper/filesystem.dir/etc/stabile/nodeconfig.cfg";
260
    untie %idreg;
261
    my @nodecfgkeys = keys %nodeconfigs;
262

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

    
305
if ($downloadmasters) {
306
    downloadMasters();
307
} else {
308
    print "Not downloading masters from origo.io, because DOWNLOAD_MASTERS not set in config.\n"
309
}
310

    
311
print "Starting pressurecontrol on engine: $uuid\n";
312

    
313
my $pid = fork();
314

    
315
if ($pid) {
316
    my $pid2 = fork();
317
    if ($pid2) {
318
        my $amtdone;
319
        $amtdone = 1 if ($debug); # Don't wait for this when debugging
320
        if ($uuid) {
321
            my $tktname = substr($uuid, 0, 8);
322
            my $cookiebase;
323
            $cookiebase = `cat /etc/stabile/cookiebase` if -e "/etc/stabile/cookiebase";
324
            chomp $cookiebase;
325

    
326
            my $apachecfg = '/etc/apache2/sites-available/stabile-ssl';
327
            $apachecfg = '/etc/apache2/conf.d/stabile-ssl.conf' if -e ('/etc/apache2/conf.d/stabile-ssl.conf');
328
            $apachecfg = '/etc/apache2/conf-available/stabile-ssl.conf' if -e ('/etc/apache2/conf-available/stabile-ssl.conf');
329
            $apachecfg = '/etc/apache2/sites-available/stabile-ssl.conf' if -e ('/etc/apache2/sites-available/stabile-ssl.conf');
330
            system("perl -pi -e 's/(.*TKTAuthCookieName.*\\n)//' $apachecfg");
331
            system("perl -pi -e 's/(.*TKTAuthDomain.*\\n)//' $apachecfg");
332
            if ($cookiebase) {
333
                system("perl -pi -e 's/(.*TKTAuthLoginURL.*)/\$1\\n        TKTAuthCookieName auth_$tktname\\n        TKTAuthDomain $cookiebase/' $apachecfg");
334
            } else {
335
                system("perl -pi -e 's/(.*TKTAuthLoginURL.*)/\$1\\n        TKTAuthCookieName auth_$tktname/' $apachecfg");
336
            }
337

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

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

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

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

    
371
            if ($linked) {
372
                print "Reporting status to Stabile Registry ($command)\n";
373
                $postreq->{'POSTDATA'} = $statsjson;
374
                #            print $statsjson;
375
                my $res = $browser->post("https://www.origo.io/irigo/engine.cgi?action=status", $postreq)->content();
376
                my $ok = ($res =~ /OK: (.*)/i);
377
                print $res;
378
            }
379

    
380
            $reportbackup++;
381
            if ($reportbackup > 100) {
382
                $command = 'fullstatsb';
383
                downloadMasters();
384
                $reportbackup = 0;
385
            } else {
386
                $command = 'fullstats';
387
            }
388

    
389
            print &pretty_time.": ".`REMOTE_USER=irigo $basedir/cgi/nodes.cgi -a updateregister -f`;
390
            print &pretty_time.": ".`REMOTE_USER=irigo $basedir/cgi/servers.cgi -a updateregister -f`;
391
            print &pretty_time.": ".`REMOTE_USER=irigo $basedir/cgi/images.cgi -a updateregister -f`;
392
            unless ($amtdone) {
393
                print "Updating AMT info\n";
394
                print &pretty_time.": ".`REMOTE_USER=irigo $basedir/cgi/nodes.cgi -a updateamtinfo`;
395
                $amtdone = 1;
396
            }
397
            print &pretty_time.": ".`/usr/local/bin/steamExec releaseolddhcpleases`;
398

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

    
455
    # Avoid leaving zombies
456
    $SIG{CHLD} = 'IGNORE';
457
    # Avoid dying from browser cancel
458
    $SIG{PIPE} = 'IGNORE';
459

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

    
464
    $content = `REMOTE_USER=irigo $basedir/cgi/networks.cgi -a jsonlist -f`;
465
    my $nets = from_json($content);
466
    foreach my $net (@$nets) {
467
        $networks{$net->{uuid}} = $net;
468
    };
469
    fork(); fork(); fork(); # 2^3 = 8 processes
470

    
471
    my $dbrowser = LWP::UserAgent->new;
472
    $dbrowser->timeout(60);
473
    $dbrowser->agent('pressurecontrol/1.0b');
474
    $dbrowser->protocols_allowed( [ 'http','https'] );
475

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

    
503
                        $networks{$networkuuid}->{'internalip'} = $intip;
504
                        $networks{$networkuuid}->{'externalip'} = $extip;
505
                        $networks{$networkuuid}->{'user'} = $networkuser;
506
                    }
507
                }
508
                $user = validateUser($user, $networkuser); # Set $user to $networkuser if validated
509
                if ($user) {
510
                    my $baseuri = "$baseurl/pipe/$networkuuid$uriport/";
511
                    my $steamhost = $request->header('STEAM_HOST');
512
                    $baseuri = "https://$steamhost/stabile/pipe/$networkuuid$uriport/" if ($steamhost);
513
                    my $extip = $networks{$networkuuid}->{'externalip'};
514
                    my $intip = $networks{$networkuuid}->{'internalip'};
515
                    my $ip = $intip;
516
                    $ip = $extip if (!$ip || $ip eq '--');
517
                    if ($ip && $networkuser && $networkuser eq $user) {
518
                        $geturi =~ s/$networkuuid/$ip/;
519
                        #$geturi .= '/' unless ($geturi =~ /\/$/);
520
                        print "Getting $networkuuid: $geturi ($uri)\n" if ($debug);
521
                        $request->uri($geturi);
522
                        $request->remove_header( 'Referer' );
523
                        $request->header('Host' => $host) if ($host);
524
                        if ($uri =~ /:4200\/\?$/) { # shellinaboxd
525
                            $dbrowser->timeout(120);
526
                            $response = $dbrowser->simple_request( $request );
527
                            $dbrowser->timeout(60);
528
                        } else {
529
                            $response = $dbrowser->simple_request( $request );
530
                        }
531

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

    
537
                        my $loc = $response->header('Location');
538
                        if ($loc && $loc =~ /^https?:\/\/.+\/(.+)\//) {
539
                            $response->header('Location', "$baseuri/$1/");
540
                        }
541
                        ${$response->content_ref} =~ s/\{internalip\}/$ip/gi;
542
                        ${$response->content_ref} =~ s/\{externalip\}/$extip/gi;
543
                        # \w is included to avoid matching url's starting with //, which should be left alone
544
                        # ${$response->content_ref} =~ s/(href|src|action|value)=('|")\/(\w)/$1=$2$baseuri$3/gi;
545
                        # This is purely to fixup javascript in Wordpress install.php
546
                        ${$response->content_ref} =~ s/\\\/home/../;
547
                        # This is to fix css links in Wordpress install
548
                        ${$response->content_ref} =~ s/'\/home\/wp-includes/'$baseuri\/home\/wp-includes/g;
549
                        ${$response->content_ref} =~ s/'\/home\/wp-admin/'$baseuri\/home\/wp-admin/g;
550
                        # This is purely to fixup image path in Thirdlane PBX UI
551
#                        ${$response->content_ref} =~ s/\.\.\/\.\.\/\.\.\//..\/..\//;
552

    
553

    
554
                        ${$response->content_ref} =~ s/(load\()('|")\//$1$2$baseuri/gi;
555
                        $response->push_header( 'X-Via' => "1.1 $baseuri" );
556
                        $response->push_header( 'X-BaseUri' => "1.1 $baseuri" );
557
                        $response->remove_header( 'Content-Security-Policy');
558
                        $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; style-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com https://ajax.googleapis.com https://code.jquery.com" );
559
                        $c->send_response( $response );
560
                        print "Got: $geturi\n" if ($debug);
561
                    } else {
562
                        my $msg = "Not found: $networkuuid, $ip, $networkuser, " . $user;
563
                    #    $c->send_status_line('200', $msg);
564
                        $c->send_status_line('404', $msg);
565
                        $c->send_header('X-Via', "1.1 $geturi");
566
                        print "$msg\n";
567
                        #last;
568
#                        $c->send_error(404);
569
                    }
570
                } else {
571
                    $c->send_error(RC_FORBIDDEN);
572
                }
573
            }    else {
574
                my $msg = "Forbidden - not allowing: $uri";
575
                $c->send_status_line('404', $msg); #403
576
                $c->send_header('X-Via', "1.1 $uri");
577
                #$c->send_error(RC_FORBIDDEN, $msg);
578
                #$main::syslogit->($stackuser, 'info', $msg);
579
                print "$msg\n";
580
                #last;
581
            }
582
        }
583
        $c->close;
584
        undef($c);
585
    }
586
}
587

    
588
print "Done\n";
589
exit;
590

    
591
sub lookupProcess {
592
    my $process = shift;
593
    my $match;
594
    my $t = new Proc::ProcessTable;
595
    foreach $p ( @{$t->table} ){
596
        my $pcmd = $p->cmndline;
597
        if ($pcmd =~ /$process/) {
598
            $match = $p->pid;
599
            last;
600
        }
601
    }
602
    return $match;
603
}
604

    
605
sub validateUser {
606
    my ($vuser, $account) = @_;
607

    
608
    unless (tie %userreg,'Tie::DBI', {
609
        db=>'mysql:steamregister',
610
        table=>'users',
611
        key=>'username',
612
        autocommit=>0,
613
        CLOBBER=>1,
614
        user=>$dbiuser,
615
        password=>$dbipasswd}) {return};
616

    
617
    my $dbuser = $userreg{$vuser}->{'username'};
618
    unless ($dbuser) {untie %userreg; $vuser = '';};
619

    
620
    $privileges = $userreg{$vuser}->{'privileges'};
621
    if (index($privileges,"d")!=-1) {$vuser = ''}; # disabled user
622

    
623
    if ($account && $vuser && $account ne $vuser) {
624
        my %ahash;
625
        my @accounts = split(/,\s*/, $userreg{$vuser}->{'accounts'});
626
        my @accountsprivs = split(/,\s*/, $userreg{$vuser}->{'accountsprivileges'});
627
        for my $i (0 .. $#accounts)
628
            { $ahash{$accounts[$i]} = $accountsprivs[$i] || 'r'; }
629

    
630
        if ($ahash{$account}) {
631
            my $privileges = $ahash{$account};
632
            if ($userreg{$account}->{'username'} && index($privileges,"d")==-1){
633
                $vuser = $account;
634
            } else {
635
                $vuser = '';
636
            }
637
        } else {
638
            $vuser = '';
639
        }
640
    }
641

    
642
    untie %userreg;
643
    return $vuser;
644
}
645

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

    
683
sub pretty_time {
684
    my $current_time = time;
685
    ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($current_time);
686
#    my $year += 1900;
687
    my $month = substr("0" . ($mon+1), -2);
688
    my $pretty_time = sprintf "%4d-%02d-%02d@%02d:%02d:%02d",$year+1900,$mon+1,$mday,$hour,$min,$sec;
689
    return $pretty_time;
690
}
691

    
692
# Pick out config file parameters from general hash
693
sub filterCfgParams {
694
    my $paramsref = shift;
695
    my %params = %{$paramsref};
696

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

    
766
    my %cfghash;
767
    if ($paramsref) {
768
        foreach my $param (@engineparams) {
769
            $cfghash{$param} = $params{$param} if (defined $params{$param});
770
        }
771
        foreach my $param (@pistonparams) {
772
            $cfghash{$param} = $params{$param} if (defined $params{$param});
773
        }
774
    } else {
775
        my %enginehash; @enginehash{@engineparams} = (1) x @engineparams;
776
        my %pistonhash; @pistonhash{@pistonparams} = (1) x @pistonparams;
777

    
778
        $cfghash{engine} = \%enginehash;
779
        $cfghash{piston} = \%pistonhash;
780
    }
781
    return %cfghash;
782
}
783

    
784
sub mountPaths {
785
    my @tenderlist = $cfg->param('STORAGE_POOLS_ADDRESS_PATHS');
786
    my @tenderpathslist = $cfg->param('STORAGE_POOLS_LOCAL_PATHS');
787

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

    
809
sub downloadMasters {
810
    print "Looking for available masters at Stabile Registry...\n";
811
    # Reload config in case storage setup changed
812
    $cfg = new Config::Simple("/etc/stabile/config.cfg");
813
    @spoolpaths = $cfg->param('STORAGE_POOLS_LOCAL_PATHS');
814
    my @dlmasters;
815

    
816
    unless (tie %imagereg,'Tie::DBI', { # Needed for ValidateItem
817
        db=>'mysql:steamregister',
818
        table=>'images',
819
        key=>'path',
820
        autocommit=>0,
821
        CLOBBER=>3,
822
        user=>$dbiuser,
823
        password=>$dbipasswd}) {print "Image register could not be accessed\n"; exit 0;};
824

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

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

    
946
            my $cmd2;
947
            if ($fname2) { # This app also has a data image
948
                foreach $sp (@spoolpaths) {
949
                    $f2 = "$sp/$stackuser/$fname2";
950
                    if (-e $f2) {
951
                        my @stat = stat($f2);
952
                        $lsize2 = $stat[7];
953
                        $lrealsize2 = $stat[12] * 512;
954
                        $match2 = 1;
955
                        last;
956
                    }
957
                }
958
                $imgurl2 = "$stack->{'url2'}?auth_tkt=$stack->{'tkt'}";
959

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

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

    
1023
sub TERMINATE {
1024
    print "Terminating\n" if ($running);
1025
    $running = 0;
1026
}
1027

    
1028
sub HUP {
1029
}
(15-15/27)