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
|
}
|