Project

General

Profile

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

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

    
8
package Stabile::Steamexec;
9

    
10
use Tie::DBI;
11
use URI::Escape;
12
use File::Basename;
13
use File::Copy;
14
use File::Rsync;
15
use Proc::ProcessTable;
16
use Sys::Syslog qw( :DEFAULT setlogsock);
17
use ConfigReader::Simple;
18
use Time::Local;
19
use XML::Simple;
20
use Data::Dumper;
21
use Data::UUID;
22
use lib '/var/www/stabile/cgi';
23
use Stabile;
24

    
25
my $arg1 = shift if $ARGV[0];
26
my $status = shift if $ARGV[0];
27
my $oldstatus = shift if $ARGV[0];
28
my $path1 = shift if $ARGV[0];
29
my $path2 = shift if $ARGV[0];
30
my $cmd1 =  shift if $ARGV[0];
31
my $cmd2 =  shift if $ARGV[0];
32
#my $cmd = join(" ", @ARGV); # The rest
33
my $res;
34
my $dsnap1;
35
my $fstab = "/etc/fstab";
36
my $debugfile = "/var/log/stabile/steamExec.out";
37
my $localpath;
38
my $localpath2;
39
my $localstatus;
40
my $localstatus2;
41
my $newvirtualsize;
42
my $newbackupsize;
43
my $newvirtualsize2;
44
my $newbackupsize2;
45
my $uimsg = '';
46

    
47
my $config = ConfigReader::Simple->new("/etc/stabile/config.cfg",
48
    [qw(RDIFF-BACKUP_ENABLED STORAGE_BACKUPDIR
49
    STORAGE_POOLS_ADDRESS_PATHS STORAGE_POOLS_LOCAL_PATHS
50
    STORAGE_POOLS_NAMES STORAGE_POOLS_DEFAULTS STORAGE_POOLS_RDIFF-BACKUP_ENABLED
51
    RDIFF-BACKUP_USERS DBI_USER DBI_PASSWD ENGINE_LINKED
52
    ENGINE_DATA_NIC)]);
53

    
54
$base = "/var/www/stabile";
55
$base = `cat /etc/stabile/basedir` if -e "/etc/stabile/basedir";
56
chomp $base;
57
my $logfile = "/var/log/stabile/steam.log";
58
my $dbiuser =  $config->get('DBI_USER') || "irigo";
59
my $dbipasswd = $config->get('DBI_PASSWD') || "";
60

    
61
my $backupdir = $config->get('STORAGE_BACKUPDIR') || "/mnt/stabile/backups";
62
my $tenders = $config->get('STORAGE_POOLS_ADDRESS_PATHS');
63
my @tenderlist = split(/,\s*/, $tenders);
64
my $tenderpaths = $config->get('STORAGE_POOLS_LOCAL_PATHS') || "/mnt/stabile/images";
65
my @tenderpathslist = split(/,\s*/, $tenderpaths);
66
my $tendernames = $config->get('STORAGE_POOLS_NAMES') || "Standard storage";
67
my @tendernameslist = split(/,\s*/, $tendernames);
68
my $opentcpports = $config->get('OPEN_TCP_PORTS');
69
my $openudpports = $config->get('OPEN_UDP_PORTS');
70

    
71
$sshcmd = "ssh -l irigo -i /var/www/.ssh/id_rsa_www -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no";
72

    
73
$current_time = time;
74
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($current_time);
75
$year += 1900;
76
$month = substr("0" . ($mon+1), -2);
77
$pretty_time = sprintf "%4d-%02d-%02d@%02d:%02d:%02d",$year,$mon+1,$mday,$hour,$min,$sec;
78

    
79
if (lc($arg1) eq "backupallimages" ) {
80
     debuglog("Backing up all images...");
81
     backupAllImages();
82
} elsif (lc($arg1) eq "backupengine" ) {
83
     if ($config->get('ENGINE_LINKED')) {
84
         debuglog("Backing up engine configuration...");
85
         print `echo users/backupengine | stash`;
86
     } else {
87
         print "Engine not linked. Not backing up configuration.\n";
88
     }
89
} elsif (lc($arg1) eq "billengine" ) {
90
#     debuglog("Updating billing data on origo.io...");
91
     print `echo users/billengine | stash`;
92
} elsif (lc($arg1) eq "updatenetworkbilling" ) {
93
#     debuglog("Updating billing for all networks...");
94
     updateBillingAllNetworks();
95
} elsif (lc($arg1) eq "updateimagebilling" ) {
96
#     debuglog("Updating billing for all images...");
97
     updateBillingAllImages();
98

    
99
} elsif (lc($arg1) eq "updatedownloads" ) {
100
    print `REMOTE_USER=irigo $base/cgi/images.cgi -a gear_updatedownloads`;
101

    
102
} elsif (lc($arg1) eq "releaseolddhcpleases" ) {
103
    releaseOldDhcpLeases();
104
} elsif (lc($arg1) eq "updateimagestatus" ) {
105
    # Incoming params: $status $oldstatus $path1 $path2
106
    # Function params: $localpath, $localstatus, $newvirtualsize, $newbackupsize
107
    updateImageStatus($status, $oldstatus, $path1, $path2);
108
} elsif (lc($arg1) eq "updatebackingfile" ) {
109
    # Incoming params: $status $oldstatus $path1 $path2
110
    # Function params: $localpath, $localstatus, $newvirtualsize, $newbackupsize
111
    updateBackingFile($status);
112
} elsif (lc($arg1) eq "unmountallimages" ) {
113
    debuglog("Unmounting all images...");
114
    print "Unmounting all images - hang on...\n";
115
    unmountAllImages();
116
} elsif (lc($arg1) eq "backupallfuel" ) {
117
    debuglog("Backup up fuel for all users...");
118
    print "Backup up fuel for all users, hang on...\n";
119
    backupAllFuel();
120
} elsif (lc($arg1) eq "post-wake") {
121
    debuglog("Running post-wake routines...");
122
    print `REMOTE_USER=irigo $base/cgi/networks.cgi -a gear_restoreall`;
123
} elsif (lc($arg1) eq "post-boot") {
124
    my $baseip;
125
    if ($opentcpports || $openudpports) {
126
        my $basedom = `awk -F'[/:]' '/https:/{print \$4}' /etc/stabile/baseurl`;
127
        chomp $basedom;
128
        if ($basedom =~ /\d+\.\d+\.\d+\.\d+/) {
129
            $baseip = $basedom;
130
        } else {
131
            $baseip = `dig +short $basedom`;
132
            chomp $baseip;
133
        }
134
        $baseip = '' if ($baseip =~ /^127/);
135
    }
136
    if (($opentcpports || $openudpports) && $baseip) {
137
        print `iptables -D INPUT -p icmp --icmp-type 8 -s 0/0 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT 2>/dev/null`;
138
        print `iptables -I INPUT -p icmp --icmp-type 8 -s 0/0 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT`;
139
        print `iptables -D OUTPUT -p icmp --icmp-type 0 -d 0/0 -m state --state ESTABLISHED,RELATED -j ACCEPT 2>/dev/null`;
140
        print `iptables -I OUTPUT -p icmp --icmp-type 0 -d 0/0 -m state --state ESTABLISHED,RELATED -j ACCEPT`;
141
        print `iptables -D INPUT -m conntrack -j ACCEPT --ctstate RELATED,ESTABLISHED 2>/dev/null`;
142
        print `iptables -I INPUT -m conntrack -j ACCEPT --ctstate RELATED,ESTABLISHED`;
143
        print `iptables -D INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 2>/dev/null`;
144
        print `iptables -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT`;
145
        print `iptables -D INPUT -d $baseip -j DROP 2>/dev/null`;
146
        print `iptables -A INPUT -d $baseip -j DROP`;
147
    }
148
    if ($opentcpports && $baseip) {
149
        print "Allowing incoming TCP traffic to $baseip for ports $opentcpports\n";
150
        my $ports = join(",",split(/, ?/, $opentcpports));
151
        print `iptables -D INPUT -p tcp -m tcp -m multiport --dports $ports -j ACCEPT 2>/dev/null`;
152
        print `iptables -I INPUT -p tcp -m tcp -m multiport --dports $ports -j ACCEPT`;
153
    }
154
    if ($openudpports && $baseip) {
155
        print "Allowing incoming UDP traffic to $baseip for ports $openudpports\n";
156
        my $ports = join(",",split(/, ?/, $openudpports));
157
        print `iptables -D INPUT -p udp -m udp -m multiport --dports $ports -j ACCEPT 2>/dev/null`;
158
        print `iptables -I INPUT -p udp -m udp -m multiport --dports $ports -j ACCEPT`;
159
    }
160
    if (`pgrep pressurecontrol`) {
161
        debuglog("Running post-boot routines...");
162
        print `REMOTE_USER=irigo $base/cgi/networks.cgi -a gear_restoreall`;
163
        print `REMOTE_USER=irigo $base/cgi/servers.cgi -a gear_autostartall`;
164
        print `REMOTE_USER=irigo $base/cgi/images.cgi -a gear_updatedownloads`;
165
    } else {
166
        debuglog("Not running post-boot routines...");
167
    }
168
} elsif (lc($arg1) eq "pre-shutdown") {
169
    debuglog("Running pre-shutdown routines...");
170
    unmountAllImages();
171
    debuglog("Halting nodes...");
172
    `echo "nodes/haltall" | /usr/local/bin/stash`;
173
    #updateBillingAllNetworks();
174
} elsif (lc($arg1) eq "daily") {
175
    debuglog("Running daily maintenance routine...");
176
    backupAllImages();
177
    updateBillingAllNetworks();
178
    unmountAllImages();
179
} elsif (lc($arg1) eq "restoreallnetworks" ) {
180
    debuglog("Restoring all networks...");
181
    print "Restoring all networks - hang on...\n";
182
    print `REMOTE_USER=irigo $base/cgi/networks.cgi -a gear_restoreall`;
183
} elsif (lc($arg1) eq "showautostart" ) {
184
    print "Showing autostart servers - hang on...\n";
185
    print `REMOTE_USER=irigo $base/cgi/servers.cgi -a showautostart`;
186
} elsif (lc($arg1) eq "autostartservers" ) {
187
    debuglog("Autostarting servers...");
188
    print "Autostarting servers - hang on...\n";
189
    print `REMOTE_USER=irigo $base/cgi/servers.cgi -a autostartall`;
190
} elsif (lc($arg1) eq "resetmonitoring" ) {
191
    debuglog("Resetting monitoring...");
192
    print "Resetting monitoring - hang on...\n";
193
    print `REMOTE_USER=irigo $base/cgi/systems.cgi -a resetmonitoring`;
194
} elsif ($arg1 && $status && $path1) {
195
    debuglog("$arg1 : $status : $oldstatus : $path1 : $path2 : $cmd1 : $cmd2");
196
    if ($status eq "snapshotting") {
197
        # $res .= `/bin/echo "$status" > "$path1.meta"`;
198
        my $macip;
199
        $localpath = $path1;
200
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
201
            $macip = $1;
202
            $localpath = $2;
203
            my $esc_localpath = shell_esc_chars($localpath);
204
            $res .= `$sshcmd $macip "sudo /usr/bin/qemu-img snapshot -c snap1 $esc_localpath"`;
205
        } else {
206
            $res .= `/usr/bin/qemu-img snapshot -c snap1 "$path1"`;
207
        }
208
        $dsnap1 = $path2;
209
        $localstatus = $oldstatus;
210
    } elsif ($status eq "unsnapping") {
211
        my $macip;
212
        $localpath = $path1;
213
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
214
            $macip = $1;
215
            $localpath = $2;
216
            my $esc_localpath = shell_esc_chars($localpath);
217
            $res .= `$sshcmd $macip "sudo /usr/bin/qemu-img snapshot -d snap1 $esc_localpath"`;
218
        } else {
219
            $res .= `/usr/bin/qemu-img snapshot -d snap1 "$path1"`;
220
        }
221
        $dsnap1 = "--";
222
        $localstatus = $oldstatus;
223
    } elsif ($status eq "reverting") {
224
        my $macip;
225
        $localpath = $path1;
226
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
227
            $macip = $1;
228
            $localpath = $2;
229
            my $esc_localpath = shell_esc_chars($localpath);
230
            $res .= `$sshcmd $macip "sudo /usr/bin/qemu-img snapshot -a snap1 $esc_localpath"`;
231
        } else {
232
            $res .= `/usr/bin/qemu-img snapshot -a snap1 "$path1"`;
233
        }
234
        $localstatus = $oldstatus;
235
    } elsif ($status eq "injecting") {
236
        $localpath = $path1;
237
        $localpath2 = $path2;
238
        my $esc_localpath = shell_esc_chars($localpath);
239

    
240
        # Find out if we are dealing with a Windows image
241
        my $xml = `bash -c '/usr/bin/virt-inspector -a "$esc_localpath"'`;
242
        #my $xml = `bash -c '/usr/bin/virt-inspector -a "$esc_localpath"' 2>&1`;
243
        # $res .= $xml . "\n";
244
        my $xmlref;
245
        my $osname;
246
        $xmlref = XMLin($xml) if ($xml =~ /^<\?xml/);
247
        $osname = $xmlref->{operatingsystem}->{name} if ($xmlref);
248
        if ($xmlref && $osname eq 'windows') {
249
            $res .= "We are dealing with a Windows image - trying to fix registry\n";
250
            my $upath = $esc_localpath;
251
            # We need write privileges
252
            $res .= `chmod 666 "$upath"`;
253
            # First try to merge storage registry keys into Windows registry. If not a windows vm it simply fails.
254
            $res .= `bash -c 'cat /usr/share/stabile/mergeide.reg | /usr/bin/virt-win-reg --merge "$upath"' 2>&1`;
255
            # Then try to merge the critical device keys. This has been removed in win8 and 2012, so will simply fail for these.
256
            $res .= `bash -c 'cat /usr/share/stabile/mergeide-CDDB.reg | /usr/bin/virt-win-reg --merge "$upath"' 2>&1`;
257
            if ($res) { debuglog($res); $res = ''; }
258

    
259
            # Try to copy viostor.sys into image
260
            my @winpaths = (
261
                '/Windows/System32/drivers',
262
                '/WINDOWS/system32/drivers/viostor.sys',
263
                '/WINDOWS/System32/drivers/viostor.sys',
264
                '/WINNT/system32/drivers/viostor.sys'
265
            );
266
            foreach my $winpath (@winpaths) {
267
                $res .= "Trying $winpath\n";
268
                my $lscmd = qq|bash -c 'virt-ls -a "$upath" "$winpath"'|;
269
                debuglog("$res"); $res = '';
270
                my $drivers = `$lscmd`;
271
                if ($drivers =~ /viostor/i) {
272
                    $res .= "OK: viostor already installed in $winpath in $upath\n";
273
                    debuglog($res); $res = '';
274
                    syslogit('info', "viostor already installed in $winpath in $upath");
275
                    last;
276
                } elsif ($drivers) {
277
                    my $cmd = qq|bash -c 'guestfish -i -a "$upath" upload /usr/share/stabile/VIOSTOR.SYS $winpath/viostor.sys' 2>&1|;
278
                    my $error = `$cmd`;
279
                    if ($error) {
280
                        $res .= "Error injecting virtio drivers into $upath: $error\n";
281
                        debuglog($res); $res = '';
282
                        syslogit('info', "Error injecting virtio drivers into $upath: $error");
283
                    } else {
284
                        $res .= "Injected virtio drivers into $upath";
285
                        debuglog($res); $res = '';
286
                        syslogit('info', "Injected virtio drivers into $upath");
287
                    }
288
                    last;
289
                } else {
290
                    $res .= "No drivers found in $winpath\n";
291
                    debuglog($res); $res = '';
292
                }
293
            }
294

    
295
        } else {
296
            $res .= "No Windows OS found ($osname) in image, not injecting drivers: $esc_localpath\n";
297
            syslogit('info', "No Windows OS found ($osname) in image, not injecting drivers: $esc_localpath");
298
        }
299
        $localstatus = $oldstatus;
300
    } elsif ($status eq "converting") {
301
        $localpath = $path1;
302
        $localpath2 = $path2;
303
        my $esc_localpath2 = shell_esc_chars($localpath2);
304
        my $upath = $esc_localpath2;
305

    
306
        my $macip;
307
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
308
            $macip = $1;
309
            $localpath = $2;
310
            my $esc_localpath = shell_esc_chars($localpath);
311
            my $esc_localpath2 = shell_esc_chars($localpath2);
312
            $res .= `$sshcmd $macip "/usr/bin/qemu-img convert $esc_localpath -O qcow2 $esc_localpath2"`;
313
        } else {
314
            my $mpath;
315
            if (substr($path1,-5) eq '.vmdk' && ( -s (substr($path1,0,-5) . "-flat.vmdk") ) ) {
316
                my $pathname = substr($path1,0,-5);
317
                $mpath = "\"$pathname-flat.vmdk\" ";
318
            # } elsif (substr($path1,-5) eq '.vmdk' && ( -s (substr($path1,0,-5) . "-s001.vmdk")) ) {
319
            #     my $pathname = substr($path1,0,-5);
320
            #     my $i = 1;
321
            #     my $num = '001';
322
            #     while (-e "$pathname-s$num.vmdk") {
323
            #         $mpath .= "\"$pathname-s$num.vmdk\" ";
324
            #         $i++;
325
            #     	$num = substr("00$i", -3);
326
            #     }
327
            } else {
328
                $mpath = "\"$path1\"";
329
            }
330

    
331
            $res .= qq|Firing /usr/bin/qemu-img convert $mpath -O qcow2 "$path2"\n|;
332
            $res .= `/usr/bin/qemu-img convert $mpath -O qcow2 "$path2"`;
333

    
334
            # Setting real UID: $< to UID: $>
335
            # $< = $>;
336

    
337
            # Find out if we are dealing with a Windows image
338
            my $xml = `bash -c '/usr/bin/virt-inspector -a "$esc_localpath2"'`;
339
            # my $xml = `bash -c '/usr/bin/virt-inspector -a "$esc_localpath2"' 2>&1`;
340
            # $res .= $xml . "\n";
341
            my $xmlref;
342
            my $osname;
343
            $xmlref = XMLin($xml) if ($xml =~ /^<\?xml/);
344
            $osname = $xmlref->{operatingsystem}->{name} if ($xmlref);
345
            if ($xmlref && $osname eq 'windows') {
346
                $res .= "We are converting a Windows image - trying to fix registry\n";
347
                # We need write privileges
348
                $res .= `chmod 666 "$upath"`;
349
                # First try to merge storage registry keys into Windows registry. If not a windows vm it simply fails.
350
                $res .= `bash -c 'cat /usr/share/stabile/mergeide.reg | /usr/bin/virt-win-reg --merge "$upath"' 2>&1`;
351
                # Then try to merge the critical device keys. This has been removed in win8 and 2012, so will simply fail for these.
352
                $res .= `bash -c 'cat /usr/share/stabile/mergeide-CDDB.reg | /usr/bin/virt-win-reg --merge "$upath"' 2>&1`;
353
                if ($res) { debuglog($res); $res = ''; }
354

    
355
                # Try to copy viostor.sys into image
356
                my @winpaths = (
357
                    '/Windows/System32/drivers/viostor.sys',
358
                    '/WINDOWS/system32/drivers/viostor.sys',
359
                    '/WINDOWS/System32/drivers/viostor.sys',
360
                    '/WINNT/system32/drivers/viostor.sys'
361
                );
362
                foreach my $winpath (@winpaths) {
363
                    $res .= "Trying $winpath\n";
364
                    my $lscmd = qq|bash -c 'virt-ls -a "$upath" "$winpath"'|;
365
                    debuglog("$res"); $res = '';
366
                    my $drivers = `$lscmd`;
367
                    if ($drivers =~ /viostor/i) {
368
                        $res .= "OK: viostor already installed in $winpath in $upath\n";
369
                        debuglog($res); $res = '';
370
                        syslogit('info', "viostor already installed in $winpath in $upath");
371
                        last;
372
                    } elsif ($drivers) {
373
                        my $cmd = qq|bash -c 'guestfish -i -a "$upath" upload /usr/share/stabile/VIOSTOR.SYS $winpath/viostor.sys' 2>&1|;
374
                        my $error = `$cmd`;
375
                        if ($error) {
376
                            $res .= "Error injecting virtio drivers into $upath: $error\n";
377
                            debuglog($res); $res = '';
378
                            syslogit('info', "Error injecting virtio drivers into $upath: $error");
379
                        } else {
380
                            $res .= "Injected virtio drivers into $upath";
381
                            debuglog($res); $res = '';
382
                            syslogit('info', "Injected virtio drivers into $upath");
383
                        }
384
                        last;
385
                    } else {
386
                        $res .= "No drivers found in $winpath\n";
387
                        debuglog($res); $res = '';
388
                    }
389
                }
390

    
391
            } else {
392
                $res .= "No Windows OS found ($osname) in converted image, not injecting drivers: $esc_localpath2\n";
393
                syslogit('info', "No Windows OS found ($osname) in image, not injecting drivers: $esc_localpath2");
394
            }
395

    
396
            if (-e "$path2") {
397
                syslogit('info', "Converted image to: $path2");
398
            } else {
399
                syslogit('info', "Unable to convert image to: $path2");
400
            }
401
        }
402
        $localstatus = $oldstatus;
403
        $localstatus2 = "unused";
404
    } elsif ($status eq "rebasing") {
405
        $localpath = $path1;
406
        $localpath2 = $path2;
407
        my $macip;
408
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
409
            $macip = $1;
410
            $localpath = $2;
411
            my $esc_localpath = shell_esc_chars($localpath);
412
            my $esc_localpath2 = shell_esc_chars($localpath2);
413
            $res .= `$sshcmd $macip "/usr/bin/qemu-img convert $esc_localpath -O qcow2 $esc_localpath2"`;
414
            $res .= `$sshcmd $macip "if [ -f $esc_localpath2 ]; then /bin/mv -v $esc_localpath2 $esc_localpath; fi"`;
415
        } else {
416
            $res .= `/usr/bin/qemu-img convert -O qcow2 "$path1" "$path2"`;
417
            $res .= `if [ -f "$path2" ]; then /bin/mv -v "$path2" "$path1"; fi`;
418
        }
419
        $localstatus = $oldstatus;
420

    
421
        # Release master image if not used by other images
422
        unless (tie %imagereg,'Tie::DBI', {
423
            db=>'mysql:steamregister',
424
            table=>'images',
425
            key=>'path',
426
            autocommit=>0,
427
            CLOBBER=>3,
428
            user=>$dbiuser,
429
            password=>$dbipasswd}) {syslogit('info', "Image register could not be accessed")};
430

    
431
        my $master = ($imagereg{$path1}->{'master'} && $imagereg{$path1}->{'master'} ne '--')?$imagereg{$path1}->{'master'}:'';
432
        my $usedmaster = '';
433
        my @regvalues = values %imagereg;
434
        if ($master) {
435
            foreach my $valref (@regvalues) {
436
                $usedmaster = 1 if ($valref->{'master'} eq $master && $valref->{'path'} ne $path1); # Check if another image is also using this master
437
            }
438
        }
439
        if ($master && !$usedmaster) {
440
            $imagereg{$master}->{'status'} = 'unused';
441
        }
442
        $imagereg{$path1}->{'master'} = '';
443
        syslogit->('info', "Freeing master $master");
444
        untie %imagereg;
445

    
446
    } elsif ($status eq "cloning" || $status eq "copying" || $status eq "vcloning" || $status eq "bcloning") {
447
        $localpath = $path1;
448
        $localpath2 = $path2;
449
        my $macip;
450
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) { # Target is on a node
451
            $macip = $1;
452
            $localpath = $2;
453
            my $esc_localpath = shell_esc_chars($localpath);
454
            my $esc_localpath2 = shell_esc_chars($localpath2);
455

    
456
            $esc_localpath =~ /(.+)\/.*/;
457
            my $sdir = $1;
458
            $esc_localpath2 =~ /(.+)\/.*/;
459
            my $dir = $1;
460

    
461
        # Creating target directory in case it doesn't exist
462
            `$sshcmd $macip /bin/mkdir -p "$dir"`;
463

    
464
        # Mounting remote share on node in case it isn't
465
            for (my $i=0; $i<=$#tenderpathslist; $i++
466
                )
467
            {
468
                my $path = $tenderpathslist[$i];
469
                my $host = $tenderlist[$i];
470
                $host = "10.0.0.1:$path" if ($host eq 'local');
471
                if ($sdir =~ /$path\//) {
472
                    my $cmd = qq/$sshcmd $macip "mountpoint -q $path || sudo mount -o intr,noatime,nfsvers=3 $host $path"/;
473
                    $res .= `$cmd`;
474
                    last;
475
                }
476
            }
477

    
478
            if ($status eq "cloning" || $status eq "bcloning") {
479
                my $cmd = qq|$sshcmd $macip "/usr/bin/qemu-img create -f qcow2 -b $esc_localpath $esc_localpath2 2>\&1"|;
480
                $res .= "$cmd\n";
481
                $res .= `$cmd`;
482
            } elsif ($status eq "copying") {
483
#                $res .= `ssh -l irigo -i /var/www/.ssh/id_rsa_www $macip "/usr/bin/rsync -uv --inplace $esc_localpath $esc_localpath2"`;
484
                $res .= `$sshcmd $macip "/bin/cp -vn $esc_localpath $esc_localpath2"`;
485
            } elsif ($status eq "vcloning") {
486
                $res .= `$sshcmd $macip "/usr/bin/VBoxManage clonehd $esc_localpath $esc_localpath2"`;
487
                $res .= `$sshcmd $macip "/bin/chmod 666 $esc_localpath2"`;
488
            }
489
        } else {
490
            $path2 =~ /(.+)\/.*/;
491
            my $dir = $1;
492
            `/bin/mkdir "$dir"` unless -e $dir;
493
            if ($status eq "cloning" || $status eq "bcloning") {
494
                $res .= `/usr/bin/qemu-img create -f qcow2 -b "$path1" "$path2" 2>\&1`;
495
                debuglog($res) if ($res);
496
            } elsif ($status eq "copying") {
497
#                $res .= `/usr/bin/ionice -c3 /usr/bin/rsync -uv --inplace "$path1" "$path2"`;
498
                $res .= `/usr/bin/ionice -c3 /bin/cp -vn "$path1" "$path2"`;
499
            } elsif ($status eq "vcloning") {
500
                $res .= `/usr/bin/ionice -c3 /usr/bin/VBoxManage clonehd "$path1" "$path2"`;
501
                $res .= `/bin/chmod 666 "$path2"`;
502
            }
503
        }
504
        $newvirtualsize2 = getVirtualSize($path2, $macip); # report size of new image for billing purposes
505
        $localstatus = ($status eq "bcloning" || $status eq "cloning")?"used": $oldstatus;
506
        $localstatus2 = ($status eq "bcloning")?"unused":"unused"; # bcloning = building a system
507

    
508
    } elsif ($status eq "urluploading") {
509
        $localpath = $path1;
510
        $imageurl = $path2;
511
#        $res .= `/bin/dd if=/dev/zero of="$localpath"  bs=1M  count=2; /bin/rm -f "$localpath.meta"`;
512
        $res .= `/usr/bin/wget --no-check-certificate -O "$localpath" "$imageurl"; /bin/rm -f "$localpath.meta"`;
513

    
514
        my $qinfo = `/usr/bin/qemu-img info --force-share "$localpath"`;
515
        $qinfo =~ /virtual size:.*\((.+) bytes\)/g;
516
        $newvirtualsize = int($1);
517
        unless ($newvirtualsize) {
518
            my @stat = stat($localpath);
519
            $newvirtualsize = $stat[7];
520
        }
521
        $localstatus = $oldstatus;
522
    } elsif ($status eq "qcreating") {
523
        $status = "creating";
524
        $localpath = $path1;
525
        # $path2 contains the size in k
526
        my $size = ($path2 / 1024)."M";
527
        my $format = "qcow2";
528
        $format = "vmdk" if ($path1 =~ /\.vmdk$/);
529
        $res .= `/usr/bin/qemu-img create -f $format "$path1" "$size"`;
530
		if(($? >> 8) != 0) { # Report if there was an error creating the image.
531
			syslogit('err', "An error was reported creating the qcow2/raw image.");
532
		}
533
		$res .= `/bin/chmod 666 "$path1"`;
534
		$res .= "$size";
535
		$newvirtualsize = $path2 * 1024;
536
		$localstatus = "unused";
537
    } elsif ($status eq "icreating") {
538
        $status = "creating";
539
        $localpath = $path1;
540
        # $path2 contains the size in k
541
        my $size = ($path2 / 1024)."M";
542
        $res .= `/usr/bin/qemu-img create -f raw "$path1" "$size"`;
543
		$res .= `/bin/chmod 666 "$path1"`;
544
		$newvirtualsize = $path2 * 1024;
545
		$localstatus = "unused";
546
    } elsif ($status eq "vcreating") {
547
        $status = "creating";
548
        $localpath = $path1;
549
        # $path2 contains the size in k
550
        $res .= `/usr/bin/VBoxManage createhd --filename "$path1" --size "$path2" --format VDI`;
551
		$res .= `/bin/chmod 666 "$path1"`;
552
		$newvirtualsize = $path2 * 1024;
553
		$localstatus = "unused";
554
    } elsif ($status eq "resizing") {
555
        $localpath = $path1;
556
        # $path2 contains the size in k
557
        my $size = int($path2 / 1024)."M";
558
        my $macip;
559
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) { # We are dealing with an image on a node
560
            $macip = $1;
561
            $localpath = $2;
562
            my $esc_localpath = shell_esc_chars($localpath);
563
            my $cmd = qq|$sshcmd $macip "sudo /usr/bin/qemu-img resize $esc_localpath $size" 2>&1|;
564
            $res .= "COMMAND: $cmd\n";
565
            $res .= `$cmd`;
566
        } else {
567
            $res .= `/usr/bin/qemu-img resize "$path1" "$size"`;
568
        }
569
        $newvirtualsize = $path2 * 1024;
570
        $localstatus = $oldstatus;
571
    } elsif ($status eq "moving") {
572
    # On node
573
        if ($path2 =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/ && $path1 =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
574
            my $macip = $1;
575
            $localpath = $2;
576
            $path2 =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/;
577
            $localpath2 = $2;
578

    
579
            my $esc_path1 = shell_esc_chars($path1);
580
            my $esc_path2 = shell_esc_chars($path2);
581
            my $esc_localpath = shell_esc_chars($localpath);
582
            my $esc_localpath2 = shell_esc_chars($localpath2);
583

    
584
            $newvirtualsize = getVirtualSize($esc_localpath, $macip);
585
            $newvirtualsize2 = 0;
586

    
587
            $localpath2 =~ /(.+)\/.+/;
588
            my $localdir2 = $1;
589
            $res .= qq[Moving: $sshcmd $macip /bin/mv -n "$localpath" "$localpath2"\n];
590
            $res .= `$sshcmd $macip /bin/mv -n "$localpath" "$localpath2"`;
591

    
592
            $localstatus2 = $oldstatus;
593
            $localstatus = "";
594

    
595
    # To node
596
        } elsif ($path2 =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
597
        # Update billing
598
            $newvirtualsize = getVirtualSize($path1);
599
            $newvirtualsize2 = 0;
600
            my $macip = $1;
601
            $localpath = $2;
602
            $localpath2 = $path1;
603
        #    my $md5sum;
604
        #    my $md5sum2;
605

    
606
            my $esc_path1 = shell_esc_chars($path1);
607
            my $esc_localpath = shell_esc_chars($localpath);
608
            my $esc_path2 = "$macip:$esc_localpath";
609

    
610
            $res .= qq[Moving: /usr/bin/rsync -vW --sparse -e "$sshcmd" $esc_path1 "$esc_path2"\n];
611
        #    $md5sum = (split(" ", `/usr/bin/md5sum "$path1"`))[0];
612
        #    $res .= "MD5: $md5sum\n";
613
            my $mvres = system(qq|/usr/bin/rsync -vW --sparse -e "$sshcmd" $esc_path1 "$esc_path2"|);
614
            my $chres .= `$sshcmd $macip "/bin/chmod 666 $esc_localpath"`;
615
        #    $md5sum2 = (split(" ", `$sshcmd $macip "/usr/bin/md5sum -b $esc_localpath"`))[0];
616
        #    $res .= "MD5: $md5sum2\n";
617
        #    if ($md5sum && ($md5sum eq $md5sum2)) {
618
            unless ($mvres || $chres) { # Sanity check
619
                $localstatus = $oldstatus;
620
                $localstatus2 = "unused";
621
                unlink $path1;
622
                $localstatus2 = "removed"; # The image being left behind is removed from db after moving to new location
623
            }
624
    # From node
625
        } elsif ($path1 =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
626
            my $macip = $1;
627
            $localpath2 = $2;
628
            $localpath = $path2;
629
        #    my $md5sum;
630
        #    my $md5sum2;
631
            my $esc_localpath = shell_esc_chars($localpath);
632
            my $esc_localpath2 = shell_esc_chars($localpath2);
633
            my $esc_path1 = "$macip:$esc_localpath2";
634
            my $esc_path2 = shell_esc_chars($path2);
635
        #    $md5sum = (split(" ", `$sshcmd $macip "/usr/bin/md5sum -b $esc_localpath2"`))[0];
636
        #    $res .= "MD5: $md5sum\n";
637
            my $mvres = system(qq|/usr/bin/rsync -vW --sparse -e "$sshcmd" "$esc_path1" $esc_path2|);
638
        #    $md5sum2 = (split(" ", `/usr/bin/md5sum "$path2"`))[0];
639
        #    $res .= "MD5: $md5sum2\n";
640
        #    if ($md5sum && ($md5sum eq $md5sum2)) {
641
            unless ($mvres) { # Sanity check
642
                $localstatus = $oldstatus;
643
                $localstatus2 = "unused";
644
                `$sshcmd $macip "/usr/bin/unlink $esc_localpath2"`;
645
                $localstatus2 = "removed"; # The image being left behind is removed from db after moving to new location
646
            }
647
        # Update billing
648
            $newvirtualsize = getVirtualSize($path2);
649
            $newvirtualsize2 = 0;
650
        } else {
651
            $localpath = $path2;
652
            $localpath2 = $path1;
653
            $res .= `/bin/mv -v "$path1" "$path2"`;
654
            $res .= `/bin/mv -v "$path1.meta" "$path2.meta"` if (-e "$path1.meta");
655
            $localstatus2 = "removed";
656
            $localstatus = $oldstatus;
657
        # Update billing
658
            $newvirtualsize = getVirtualSize($path2);
659
            $newvirtualsize2 = 0;
660
        }
661
    } elsif ($status eq "backingup" || $status eq "lbackingup") {
662
        $user = $arg1;
663
        # $path1 is the image to back up (including potential subdir), $path2 the source dir (storage pool) and $cmd1 the target dir (general backup dir)
664
        $localpath = "$path2/$user/$path1";
665
        mkdir "$cmd1/$user" unless -d "$cmd1/$user"; # Create the target dirs which will contain the backup
666
        my $pool = $path2;
667
        my $image = $path1;
668
        my $subdir; # 1 level of subdirs supported
669
        if ($path1 =~ /(.+)\/(.+)/) {
670
            $subdir = $1;
671
            $image = $2;
672
        }
673
        if ($subdir) {
674
            mkdir "$cmd1/$user/$subdir" unless -d "$cmd1/$user/$subdir";
675
            mkdir "$cmd1/$user/$subdir/$image" unless -d "$cmd1/$user/$subdir/$image";
676
        } else {
677
            mkdir "$cmd1/$user/$image" unless -d "$cmd1/$user/$image";
678
        }
679

    
680
        if (-d "/mnt/$user-$image") {
681
            $res .= "Image is already being backed up";
682
        } else {
683
            my $snapname;
684
            my $snappath;
685
            my $snapsrcdir;
686
            my $lvolgroup;
687
            if ($status eq "lbackingup") { # Do a local lvm snapshot before backing up
688
                `/sbin/modprobe dm-snapshot`; # Make sure we can make lvm snapshots
689
                $snapname = "$user-$image";
690
                $snapname =~ tr/ /-/; #No spaces allowed in snapshot names...
691
                $snapname =~ tr/@/_/; #No funny chars allowed in snapshot names...
692
                $snappath = "/mnt/$snapname"; # The path to mount our snapshot on
693
                mkdir $snappath;
694

    
695
                my $q = `/bin/cat /proc/mounts | grep "$pool"`; # Find the lvm volume mounted on /mnt/images
696
                ($q =~ m/\/dev\/mapper\/(\S+)-(\S+) $pool.+/g)[-1]; # Select last match
697
                $lvolgroup = $1;
698
                my $lvol = $2;
699

    
700
                $res .= `/sbin/lvcreate -L1024M -s -n $snapname /dev/$lvolgroup/$lvol`; # Take a snapshot
701

    
702
                debuglog("$oldstatus $pool /dev/$lvolgroup/$lvol $snapname \"$snappath\"");
703

    
704
                $res .= changeFstab($snapname, $pool); # Change fstab to allow mount
705
                $res .= `/bin/mount "$snappath" 2>&1`; # Mount the snapshot
706
                $res .= `ls -l $snappath/$user` . "\n";
707
                $snapsrcdir = "$snappath/$user"; # Change source dir to our new snapshot
708
            } else {
709
                $snapsrcdir = "$pool/$user";
710
            }
711

    
712
            # Do the backup
713
            $res .= `/usr/bin/rdiff-backup --print-statistics --include "$snapsrcdir/$path1" --exclude '**' "$snapsrcdir" "$cmd1/$user/$path1"`;
714
            $res .= `/usr/bin/rdiff-backup --print-statistics --force --remove-older-than $cmd2  "$cmd1/$user/$path1"` if ($cmd2);
715
            `/bin/chown -R irigo:irigo "$cmd1/$user/$path1"`;
716
            # Clean up
717
            if ($status eq "lbackingup") {
718
                $res .= `/bin/umount "$snappath"`;
719
                $res .= changeFstab($snapname, $pool, 1);
720
                $res .= `/bin/rm -r "$snappath"` unless (-d "$snappath/$user"); # Sanity check - don't delete mount dir if snapshot still mounted
721
                debuglog("removing logical volume /dev/$lvolgroup/$snapname");
722
                $res .= `/sbin/lvremove -f /dev/$lvolgroup/$snapname`;
723
            }
724

    
725
           if (-d "$cmd1/$user/$path1") { # Report new backup size for billing
726
               if ($subdir) {
727
                   $newbackupsize = getBackupSize("/$subdir", $image, $user);
728
               } else {
729
                   $newbackupsize = getBackupSize('', $image, $user);
730
               }
731
           }
732
        # Update btime
733
            my $buser = $user;
734
            $buser = 'irigo' if ($user eq 'common');
735
            my $bcmd = "REMOTE_USER=$buser $base/cgi/images.cgi -a updatebtime -i " . uri_escape($localpath);
736
            my $scmd = "/usr/bin/$sshcmd 127.0.0.1";
737
            $res .= "$scmd $bcmd\n";
738
            $res .= `$scmd $bcmd`;
739
            $res .= "Updating btime: for $localpath\n\n";
740

    
741
            my $bmes;
742
            if ($status eq "backingup" || $status eq "lbackingup") {
743
                $bmes;
744
                if ($res =~ /TotalDestinationSizeChange (\d)(.+\))/) {
745
                    if ($1 eq "0") {
746
                        $bmes = "No changes to back up";
747
                    } else {
748
                        $bmes = "Backed up $1$2";
749
                        #    $bmes .= " in $1$2" if ($res =~ /ElapsedTime (\d)(.+\))/);
750
                    }
751
                } elsif ($res =~ /(Image is already being backed up)/) {
752
                    $bmes = $1;
753
                } else {
754
                    my $hres = $res;
755
                    $hres =~ s/\n/<br>/g;
756
                    $hres =~ s/\"/\\"/g;
757
                    $bmes = "Backup failed: $hres";
758
                }
759
                $uimsg = $bmes;
760
            }
761

    
762
        }
763
        $localstatus = $oldstatus;
764

    
765
    } elsif ($status eq "frestoring") {
766
        $user = $arg1;
767
        my($bname, $dirpath, $suffix) = fileparse($path1, (".vmdk", ".img", ".vhd", ".qcow", ".qcow2", ".vdi", ".iso"));
768
        my $mountpath = "$dirpath.$bname$suffix";
769
        my $mounts = `/bin/cat /proc/mounts`;
770
        my $mmounts = `/bin/df`;
771
        my $mounted = ($mounts =~ /$mountpath/ && $mmounts =~ /$mountpath/);
772
        my $restorepath = "$dirpath$bname.iso";
773
        if (-e $restorepath) {
774
            my $i = 1;
775
            while (-e "$dirpath$bname.$i.iso") {$i++;}
776
            $restorepath = "$dirpath$bname.$i.iso";
777
        }
778
        if ($mounted) {
779
            $res .= "Restoring files to: /tmp/restore/$user/$bname$suffix -> $restorepath\n";
780
            $res .= `/bin/echo $status > "$restorepath.meta"`;
781

    
782
            `/bin/mkdir -p "/tmp/restore/$user/$bname$suffix"` unless (-e "/tmp/restore/$user/$bname$suffix");
783
            my @files = split(/:/, uri_unescape($path2));
784
            foreach $f (@files) {
785
                if (-e "$mountpath$f" && chdir($mountpath)) {
786
                    $f = substr($f,1) if ($f =~ /^\//);
787
                    eval {`/usr/bin/rsync -aR --sparse "$f" /tmp/restore/$user/$bname$suffix`; 1;}
788
                        or do {$e=1; $res .= "ERROR Problem restoring files $@\n";};
789
                } else {
790
                    $res .= "ERROR $f not found in $mountpath\n";
791
                }
792
            }
793
            if (chdir "/tmp/restore/$user/$bname$suffix") {
794
                eval {$res .= `/usr/bin/genisoimage -o "$restorepath" -iso-level 4 .`; 1;}
795
                    or do {$e=1; $res .= "Stream=ERROR Problem restoring files $@\n";};
796
                $res .= `/bin/rm -rf /tmp/restore/$user/$bname$suffix`;
797
                $res .= "OK Restored files from /tmp/restore/$user/$bname$suffix to $restorepath\n";
798
            } else {
799
                $res .= "ERROR Unable to chdir to /tmp/restore/$user/$bname$suffix\n";
800
            }
801

    
802
            #$res .= `/bin/fusermount -u "$mountpath"`;
803
            #$res .= `/bin/umount "$mountpath"`;
804
            #my $bcmd = "REMOTE_USER=$user $base/cgi/images.cgi " . $postdata;
805
            #$res .= "Unmounting $path1 " . `$bcmd` . "\n";
806

    
807
            $localpath = $path1;
808
            $localstatus = $oldstatus;
809
            $localpath2 = $restorepath;
810
            $localstatus2 = "unused";
811
        # Update billing
812
            $newvirtualsize = getVirtualSize($restorepath);
813
            unlink "$restorepath.meta";
814

    
815
            my $postdata = "-a unmount -i " . uri_escape($path1);
816
            $res .= "Unmounted $path1 " . `REMOTE_USER=$user $base/cgi/images.cgi $postdata`;
817
    #        `/usr/bin/$sshcmd 127.0.0.1 REMOTE_USER=$user $base/cgi/images.cgi $postdata`;
818
        } else {
819
            $res .= "ERROR You must mount image on $mountpath before restoring\n";
820
        }
821

    
822
    } elsif ($status eq "restoring") {
823
        $user = $arg1;
824
        # $path1 is the image path, $path2 backup dir, $cmd1 the increment, $cmd2 is the restore path
825
        my $subdir; # 1 level of subdirs supported
826
        if ($path1 =~ /\/$user\/(.+\/)/) {
827
            $subdir = $1;
828
        }
829
        my($bname, $dirpath, $suffix) = fileparse($path1, (".vmdk", ".img", ".vhd", ".qcow", ".qcow2", ".vdi", ".iso"));
830
        my $incfile;
831
        my $restorepath = $cmd2;
832

    
833
        if ($cmd1 eq "mirror") {
834
            my $mir = `/bin/ls "$path2/$user/$subdir$bname$suffix/rdiff-backup-data" | grep current_mirror`;
835
            if ($mir =~ /current_mirror\.(\S+)\.data/) {
836
                $incfile = "$path2/$user/$subdir$bname$suffix/$bname$suffix";
837
            }
838
        } else {
839
            if ($cmd1 =~ /^SNAPSHOT-/) { # Z-restore
840
                $incfile = "$path2/$user/$subdir$bname$suffix";
841
            } else { # Not a Z-restore
842
                $incfile = "$path2/$user/$subdir$bname$suffix/rdiff-backup-data/increments/$bname$suffix.$cmd1.diff.gz";
843
            }
844
        }
845

    
846
        $res .= `/bin/echo $status > "$restorepath.meta"`;
847

    
848
        $res .= "Restoring: $incfile -> $restorepath ";
849
        if ($cmd1 eq "mirror" || $cmd1 =~ /^SNAPSHOT-/) {
850
            $res .= `/usr/bin/ionice -c3 /bin/cp -vn "$incfile" "$restorepath"`;
851
        } else {
852
            $res .= `/usr/bin/rdiff-backup "$incfile" "$restorepath"`;
853
        }
854

    
855
        $localpath = $path1;
856
        $localstatus = $oldstatus;
857
        $localpath2 = $restorepath;
858
        $localstatus2 = 'unused';
859
        $res .= `/bin/rm -f "$restorepath.meta"`;
860
    # Update billing
861
        $newvirtualsize = getVirtualSize($restorepath);
862
        unlink "$restorepath.meta";
863
    } else {
864
        debuglog("$arg1 : $status : $oldstatus : $path1 : $path2 : $cmd1 : $cmd2 : $res");
865
        print "No action $status\n";
866
    }
867

    
868
    sleep 1;
869
    if ($localpath) {
870
        debuglog("done: $arg1 : $localstatus : $localpath");
871
        updateImageStatus($localpath, $localstatus, $newvirtualsize, $newbackupsize);
872
    }
873
    if ($localpath2) {
874
        debuglog("done2: $arg1 : $localstatus2 : $localpath2");
875
        updateImageStatus($localpath2, $localstatus2, $newvirtualsize2, $newbackupsize2);
876
    }
877

    
878
#    updateClientUI($arg1, $localpath, $localstatus, $dsnap1);
879
    my $updateobj = {user=>$arg1, tab=>"images", path=>$localpath, status=>$localstatus, snap1=>$dsnap1, type=>"update"};
880
    $updateobj->{'message'} = $uimsg if ($uimsg);
881
    my $uires = $main::updateUI->($updateobj);
882
#    debuglog("Updating UI with user=>$arg1 path=>$localpath, status=>$localstatus, $uires");
883

    
884
    if ($localpath2 && ($status =~ /cloning|copying|converting|moving|restoring/)) {
885
#        updateClientUI($arg1, $localpath2, $localstatus2);
886
        $uires = $main::updateUI->({user=>$arg1, tab=>"images", path=>$localpath2, status=>$localstatus, type=>"update"});
887
#        debuglog("Updating UI with user=>$arg1 path=>$localpath2, status=>$localstatus2, $uires");
888
    }
889

    
890
    print $res if ($res);
891
    debuglog($res) if ($res);
892
} else {
893
    print "No valid input...\n";
894
}
895

    
896
exit 0;
897

    
898
sub updateClientUI {
899
    return; # obsolete
900
    my ($username, $dpath, $dstatus, $snap1) = @_;
901
    if ($username) {
902

    
903
        unless (tie %imagereg,'Tie::DBI', {
904
            db=>'mysql:steamregister',
905
            table=>'images',
906
            key=>'path',
907
            autocommit=>0,
908
            CLOBBER=>3,
909
            user=>$dbiuser,
910
            password=>$dbipasswd}) {syslogit('info', "Image register could not be accessed")};
911

    
912
        my $duuid;
913
        my $dname;
914
        if ($dpath && $imagereg{$dpath}) {
915
            $duuid = $imagereg{$dpath}->{'uuid'} ;
916
            $dname = $imagereg{$dpath}->{'name'};
917
        }
918
        untie %imagereg;
919
        #$duuid = $dpath unless ($duuid);
920
        #$duuid = 'none' unless ($duuid);
921

    
922
        my $bmes;
923
        if ($status eq "backingup" || $status eq "lbackingup") {
924
            $bmes;
925
            if ($res =~ /TotalDestinationSizeChange (\d)(.+\))/) {
926
                if ($1 eq "0") {
927
                    $bmes = "No changes to back up ($dname)";
928
                } else {
929
                    $bmes = "Backed up $1$2";
930
                #    $bmes .= " in $1$2" if ($res =~ /ElapsedTime (\d)(.+\))/);
931
                }
932
            } elsif ($res =~ /(Image is already being backed up)/) {
933
                $bmes = $1;
934
            } else {
935
                my $hres = $res;
936
                $hres =~ s/\n/<br>/g;
937
                $hres =~ s/\"/\\"/g;
938
                $bmess = "Backup failed: $hres";
939
            }
940
        }
941
        syslogit('info', "$bmes: $path1") if ($bmes);
942
        my $newtasks = "{\"type\":\"update\",\"tab\":\"images\",\"timestamp\":$current_time" .
943
        ($duuid?",\"uuid\":\"$duuid\"":"") .
944
        ($dstatus?",\"status\":\"$dstatus\"":"") .
945
        ($snap1?",\"snap1\":\"$snap1\"":"") .
946
        ($status eq "backingup"?",\"backup\":\"$path1\"":"") .
947
        ($bmes?",\"message\":\"$bmes\", \"backup\":\"$path1\"":"") .
948
        ",\"sender\":\"steamExec\"" .
949
        "}, ";
950

    
951
        opendir my($dh), '/tmp' or die "Couldn't open '/tmp': $!";
952
        my @files;
953
        if ($username eq 'common') {
954
            # write tasks to all admin user's session task pipes
955
            @files = grep { /.*~A-.*\.tasks$/ } readdir $dh;
956
        } else {
957
            # write tasks to all the user's session task pipes
958
            @files = grep { /^$username~.*\.tasks$/ } readdir $dh;
959
        }
960
        closedir $dh;
961
        my @pfiles;
962
        foreach my $f (@files) {
963
            push @pfiles, "/tmp/$f" if (`pgrep -f "$f"`); # Only include pipes with active listeners
964
        };
965
        my $tasksfiles = join(' ', @pfiles);
966
        $tasksfiles = $1 if ($tasksfiles =~ /(.+)/); #untaint
967
        # Write to users named pipes if user is logged in
968
        if ($tasksfiles) {
969
            $res = `/bin/echo \'$newtasks\' | /usr/bin/tee  $tasksfiles \&`;
970
        }
971
    }
972
}
973

    
974
sub changeFstab {
975
	my $image = $_[0];
976
	my $pool = $_[1];
977
	my $remove = $_[2];
978
	return 0 unless ($image);
979
	return 0 unless (index($image, " ")==-1);
980
	copy($fstab, "$fstab.steam.bak") or return 0;
981

    
982
	my $q = `/bin/cat /proc/mounts | grep "$pool"`; # Find the lvm volume mounted on /mnt/images
983
    # $q =~ /\/dev\/mapper\/(\S+)-(\S+) $pool.+/;
984
    ($q =~ m/\/dev\/mapper\/(\S+)-(\S+) $pool.+/g)[-1]; # Select last match
985
    my $lvolgroup = $1;
986
    my $lvol = $2;
987

    
988
	my $newfile = "";
989
	my $match;
990
	open (FILE, $fstab);
991
	while (<FILE>) {
992
		chomp;
993
		my $line = $_;
994
		if ($line =~ /^\/dev\/$lvolgroup\/$image/) {
995
			$newfile .= "$line\n" unless ($remove);
996
			$match = 1;
997
		} else {
998
			$newfile .= "$line\n";
999
		}
1000
	}
1001
    $newfile .= "/dev/$lvolgroup/$image /mnt/$image ext4 users,ro 0 0\n" unless ($remove || $match);
1002
	close (FILE);
1003
	open( FILE2, ">$fstab" );
1004
	#open( FILE2, ">/tmp/fstab.new" );
1005
	print FILE2 $newfile;
1006
	close(FILE2);
1007
	#copy("/tmp/fstab.new", $fstab) or return 0;
1008
	return "$fstab updated with /dev/$lvolgroup/$image /mnt/$image $match : $remove\n";
1009
}
1010

    
1011
sub updateBackingFile {
1012
    my $imagepath = $_[0];
1013
    unless (-e $imagepath) {
1014
        print "Not updating backing file for image (not found) $imagepath\n";
1015
        return;
1016
    }
1017
    $imagepath = uri_escape($imagepath);
1018
    my $bcmd = qq|REMOTE_USER=irigo $base/cgi/images.cgi -a updatebackingfile -i "$imagepath"|;
1019
    print `$bcmd`;
1020
}
1021

    
1022
sub updateImageStatus {
1023
	my $imagepath = $_[0];
1024
	my $imagestatus = $_[1];
1025
	my $newvsize = $_[2]; # Size of image has changed
1026
	my $newbsize = $_[3]; # Size of image's backup has changed
1027
	return unless ($imagepath);
1028
    # if (-e $imagepath) {
1029
    #     print "Updating image $imagepath, $imagestatus, $newvsize, $newbsize\n";
1030
    # } else {
1031
    #     print "Not updating image (not found) $imagepath, $imagestatus, $newvsize, $newbsize\n";
1032
    #     return;
1033
    # }
1034

    
1035
    unless (tie %imagereg,'Tie::DBI', {
1036
        db=>'mysql:steamregister',
1037
        table=>'images',
1038
        key=>'path',
1039
        autocommit=>0,
1040
        CLOBBER=>3,
1041
        user=>$dbiuser,
1042
        password=>$dbipasswd}) {syslogit('info', "Image register could not be accessed")};
1043

    
1044
    my $imageuser = $arg1;
1045
    my $imagename;
1046
    if (($imagestatus eq 'uploading' || $imagestatus eq 'downloading') && !$imagereg{$imagepath}) {
1047
        # An image is being uploaded
1048
        my $ug = new Data::UUID;
1049
        my $newuuid = $ug->create_str();
1050
        $imagename = $imagepath;
1051
        if ($imagename =~ /.+\/(.+)\/(.+)\.(.+)/) {
1052
            $imageuser = $1;
1053
            $imagename = $2;
1054
            my $imagetype = $3 || 'qcow2';
1055
            $imagereg{$imagepath} = {
1056
                uuid => $newuuid,
1057
                path => $imagepath,
1058
                name => $imagename,
1059
                user => $imageuser,
1060
                type => $imagetype,
1061
                virtualsize => $newvsize,
1062
                size => $newvsize,
1063
                status => 'uploading'
1064
            };
1065
            $arg1 = $imageuser;
1066
        }
1067
    } elsif ($imagestatus ne 'removed') {
1068
        if ($imagestatus eq 'installable') {
1069
            $imagestatus = 'unused';
1070
            $imagename = $imagereg{$imagepath}->{'name'} if ($imagereg{$imagepath});
1071
            $imagereg{$imagepath}->{'installable'} = 'true';
1072
            $main::updateUI->(
1073
                {user=>"irigo", tab=>"images", path=>"$imagepath", status=>"$imagestatus", message=>"Image $imagename downloaded and ready to install", type=>"update"}
1074
            );
1075
            $arg1 = 'irigo';
1076
            syslogit('info', "Downloaded $imagepath from registry");
1077
        } elsif ($imagestatus eq 'downloaded') {
1078
            $imagestatus = 'unused';
1079
            $imagename = $imagereg{$imagepath}->{'name'} if ($imagereg{$imagepath});
1080
            $main::updateUI->(
1081
                {user=>"irigo", tab=>"images", path=>"$imagepath", status=>"$imagestatus", message=>"Image $imagename downloaded", type=>"update"}
1082
            );
1083
            $arg1 = 'irigo';
1084
            syslogit('info', "Downloaded $imagepath from registry");
1085
        }
1086
        $imagereg{$imagepath}->{'status'} = $imagestatus;
1087
        $imagereg{$imagepath}->{'status'} = $imagestatus;
1088
        $imagereg{$imagepath}->{'virtualsize'} = $newvsize if ($newvsize);
1089
    } else {
1090
        syslogit('info', "Deleting $imagepath from DB ($imagestatus)");
1091
        delete $imagereg{$imagepath};
1092
    }
1093
    tied(%imagereg)->commit;
1094
    untie %imagereg;
1095
    if ((defined $newvsize) || (defined $newbsize)) {
1096
        updateBilling($arg1) if ($arg1 !~ /\//);
1097
    }
1098
}
1099

    
1100
sub updateBillingAllNetworks {
1101
    my $t = new Proc::ProcessTable;
1102
    my $i = 0;
1103
    foreach $p ( @{$t->table} ){
1104
        my $pcmd = $p->cmndline;
1105
        if ($pcmd =~ /(.*steamExec updatenetworkbilling)/) { # || $pcmd =~ /(.*apache2 -k start)/) {
1106
            $i++;
1107
        }
1108
    }
1109
    if ($i>1) {
1110
        print "Already updating network billing! ($i)\n";
1111
        return;
1112
    }
1113
    print "Updating network billing for all users...\n";
1114

    
1115
    unless (tie %userreg,'Tie::DBI', {
1116
        db=>'mysql:steamregister',
1117
        table=>'users',
1118
        key=>'username',
1119
        autocommit=>0,
1120
        CLOBBER=>1,
1121
        user=>$dbiuser,
1122
        password=>$dbipasswd}) {return 0};
1123

    
1124
    my @regvalues = values %userreg;
1125
    foreach my $valref (@regvalues) {
1126
        my $buser = $valref->{'username'};
1127
        print "Updating billing for $buser\n";
1128
        my $bcmd = "REMOTE_USER=$buser $base/cgi/networks.cgi -a updatebilling";
1129
    # Actually do the update
1130
        print `$bcmd`;
1131

    
1132
    }
1133
    untie %userreg;
1134
}
1135

    
1136
sub updateBillingAllImages {
1137
    my $t = new Proc::ProcessTable;
1138
    my $i = 0;
1139
    foreach $p ( @{$t->table} ){
1140
        my $pcmd = $p->cmndline;
1141
        if ($pcmd =~ /(.*steamExec updateimagebilling)/) { # || $pcmd =~ /(.*apache2 -k start)/) {
1142
            $i++;
1143
        }
1144
    }
1145
    if ($i>1) {
1146
        print "Already updating image billing! ($i)\n";
1147
        return;
1148
    }
1149
    print "Updating image billing for all users...\n";
1150

    
1151
    unless (tie %userreg,'Tie::DBI', {
1152
        db=>'mysql:steamregister',
1153
        table=>'users',
1154
        key=>'username',
1155
        autocommit=>0,
1156
        CLOBBER=>1,
1157
        user=>$dbiuser,
1158
        password=>$dbipasswd}) {return 0};
1159

    
1160
    my @regvalues = values %userreg;
1161
    foreach my $valref (@regvalues) {
1162
        my $buser = $valref->{'username'};
1163
        print "Updating billing for $buser\n";
1164
        my $bcmd = "REMOTE_USER=$buser $base/cgi/images.cgi -a updatebilling";
1165
    # Actually do the update
1166
        print `$bcmd`;
1167

    
1168
    }
1169
    untie %userreg;
1170
}
1171

    
1172
sub markMonitoredServers {
1173
    print "Marking monitored servers in /var/log/stabile...\n";
1174

    
1175
}
1176

    
1177
sub releaseOldDhcpLeases {
1178
    print "Releasing DHCP leases logged as old leases in syslog...\n";
1179
    my $datanic = $config->get('ENGINE_DATA_NIC');
1180
	unless (tie %networkreg,'Tie::DBI', {
1181
		db=>'mysql:steamregister',
1182
		table=>'networks',
1183
		key=>'uuid',
1184
		autocommit=>0,
1185
		CLOBBER=>3,
1186
		user=>$dbiuser,
1187
		password=>$dbipasswd}) {throw Error::Simple("Stroke=Error Register could not be accessed")};
1188
    my %leases;
1189
    $cmd = qq[tail -n 500 /var/log/syslog | grep -oP " not using configured address .+ because it is leased to .+\$" | sort |  uniq];
1190
    my @loglines = split("\n", `$cmd`);
1191
    foreach my $line (@loglines) {
1192
        $line =~ /not using configured address (.+) because it is leased to (\S+)/;
1193
        my $relip = $1;
1194
        my $relmac = $2;
1195
        $leases{$relip} = {
1196
            ip=>$relip,
1197
            mac=>$relmac
1198
        };
1199
        my $relid;
1200
        if ($relip =~ /10\.(\d+)\.(\d+)\.\d+/) {
1201
            $relid = (0 + "$1$2");
1202
        } else {
1203
            foreach my $net (values %networkreg) {
1204
                if ($net->{'externalip'} eq $relip) {
1205
                    $relid = $net->{'id'};
1206
                    last
1207
                }
1208
            }
1209
        }
1210
        print "Releasing $datanic.$relid $relip $relmac\n";
1211
        print `/usr/bin/dhcp_release $datanic.$relid $relip $relmac`;
1212
        print `perl -i -ne 'print if !/$relip/' /var/lib/misc/dnsmasq.leases`;
1213
        print `pkill -HUP -f interface=$datanic.$relid`;
1214
    }
1215
    untie %networkreg;
1216
}
1217

    
1218
sub backupAllFuel {
1219
    unless (tie %userreg,'Tie::DBI', {
1220
        db=>'mysql:steamregister',
1221
        table=>'users',
1222
        key=>'username',
1223
        autocommit=>0,
1224
        CLOBBER=>1,
1225
        user=>$dbiuser,
1226
        password=>$dbipasswd}) {return 0};
1227

    
1228
    my @regvalues = values %userreg;
1229
    foreach my $valref (@regvalues) {
1230
        my $buser = $valref->{'username'};
1231
        my $privileges = $valref->{'privileges'};
1232
        unless (index($privileges,"d")!=-1) {
1233
            my $bcmd = "REMOTE_USER=$buser $base/cgi/images.cgi -a backupfuel";
1234
            print `$bcmd`;
1235
        }
1236
    }
1237
    untie %userreg;
1238
}
1239

    
1240

    
1241
sub unmountAllImages {
1242
    unless (tie %userreg,'Tie::DBI', {
1243
        db=>'mysql:steamregister',
1244
        table=>'users',
1245
        key=>'username',
1246
        autocommit=>0,
1247
        CLOBBER=>1,
1248
        user=>$dbiuser,
1249
        password=>$dbipasswd}) {return 0};
1250

    
1251
    my @regvalues = values %userreg;
1252
    foreach my $valref (@regvalues) {
1253
        my $buser = $valref->{'username'};
1254
        my $privileges = $valref->{'privileges'};
1255
        my $bcmd = "REMOTE_USER=$buser $base/cgi/images.cgi -a unmountall" unless (index($privileges,"d")!=-1);
1256
    # Actually do the update
1257
        print `$bcmd`;
1258
    }
1259
    untie %userreg;
1260
}
1261

    
1262
sub backupAllImages {
1263
    my $t = new Proc::ProcessTable;
1264
    my $i = 0;
1265
    foreach $p ( @{$t->table} ){
1266
        my $pcmd = $p->cmndline;
1267
        if ($pcmd =~ /(.*steamExec backupallimages)/) { # || $pcmd =~ /(.*apache2 -k start)/) {
1268
            $i++;
1269
        }
1270
    }
1271
    if ($i>1) {
1272
        print "Already backing up Stabile! ($i)\n";
1273
        return;
1274
    }
1275
    print "Backing up all images...\n";
1276

    
1277
    unless (tie %imagereg,'Tie::DBI', {
1278
        db=>'mysql:steamregister',
1279
        table=>'images',
1280
        key=>'path',
1281
        autocommit=>0,
1282
        CLOBBER=>3,
1283
        user=>$dbiuser,
1284
        password=>$dbipasswd}) {syslogit('info', "Image register could not be accessed")};
1285

    
1286
    my @regvalues = values %imagereg;
1287
    foreach my $valref (@regvalues) {
1288
        my $uuid = $valref->{'uuid'};
1289
        my $buser = $valref->{'user'};
1290
        my $bschedule = $valref->{'bschedule'};
1291
        if ($bschedule =~ /daily/) {
1292
            my $bcmd = qq|REMOTE_USER=$buser $base/cgi/images.cgi -a backup -u $uuid -g '{"skipzfs":1}'|;
1293
            print "Backing up ($bschedule): $valref->{'name'} ($uuid)\n";
1294
        # Actually do the backup
1295
            print `$bcmd`;
1296
        # If more than 4 jobs running, wait for some to finish
1297
            while (runningBackups()>4) {
1298
                print ".";
1299
                sleep 30;
1300
            }
1301
        } else {
1302
            print "Not backing up (" . ($bschedule?bschedule:'no schedule') . "): $valref->{'name'} ($uuid)\n";
1303
        # Update btime - this is done by zbackup below
1304
        #    my $bcmd = "REMOTE_USER=$buser $base/cgi/images.cgi -a updatebtime -u $uuid";
1305
        #    print `$bcmd`;
1306
        }
1307
    }
1308
    untie %imagereg;
1309
    # Finally back up all ZFS volumes
1310
    my $bcmd = "REMOTE_USER=irigo $base/cgi/images.cgi -a zbackup";
1311
    print `$bcmd`;
1312

    
1313
    sub runningBackups {
1314
        my $j = 0;
1315
        foreach my $valref (@regvalues) {
1316
            $j++ if ($valref->{'status'} =~ /backingup/);
1317
        }
1318
        return $j;
1319
    }
1320
}
1321

    
1322
sub syslogit {
1323
	my ($priority, $msg) = @_;
1324

    
1325
    my $current_time = time;
1326
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($current_time);
1327
    $year += 1900;
1328
    my $month = substr("0" . ($mon+1), -2);
1329
    my $pretty_time = sprintf "%4d-%02d-%02d@%02d:%02d:%02d",$year,$mon+1,$mday,$hour,$min,$sec;
1330

    
1331
	if ($msg && $msg ne '') {
1332
		unless (open(TEMP3, ">>$logfile")) {print "SError log file \"$logfile\" could not be written\n";}
1333
		print TEMP3 $pretty_time, " : $arg1 : $msg\n";
1334
		close(TEMP3);
1335
	}
1336
	return 0 unless ($priority =~ /err|debug/);
1337
	setlogsock('unix');
1338
	# $programname is assumed to be a global.  Also log the PID
1339
	# and to CONSole if there's a problem.  Use facility 'user'.
1340
	openlog($programname, 'pid,cons', 'user');
1341
	syslog($priority, $msg);
1342
	closelog();
1343
	return 1;
1344
}
1345

    
1346
sub getVirtualSize {
1347
    my $vpath = shift;
1348
    my $macip = shift;
1349
    my $qinfo;
1350
    my($bname, $dirpath, $suffix) = fileparse($vpath, (".vmdk", ".img", ".vhd", ".qcow", ".qcow2", ".vdi", ".iso"));
1351
    if ($suffix eq ".qcow2") {
1352
        if ($macip) {
1353
            $qinfo = `$sshcmd $macip /usr/bin/qemu-img info --force-share "$vpath"`;
1354
        } else {
1355
            $qinfo = `/usr/bin/qemu-img info --force-share "$vpath"`;
1356
        }
1357
        $qinfo =~ /virtual size:.*\((.+) bytes\)/g;
1358
        return(int($1)); # report size of new image for billing purposes
1359
    } elsif ($status eq ".vdi") {
1360
        if ($macip) {
1361
            $qinfo = `$sshcmd $macip /usr/bin/VBoxManage showhdinfo "$vpath"`;
1362
        } else {
1363
            $qinfo = `/usr/bin/VBoxManage showhdinfo "$vpath"`;
1364
        }
1365
        $qinfo =~ /Logical size:\s*(\d+) MBytes/g;
1366
        return(int($1) * 1024 * 1024); # report size of new image for billing purposes
1367
    } else {
1368
        if ($macip) {
1369
            return `$sshcmd $macip perl -e 'my @stat=stat("$vpath"); print $stat[7];'`;
1370
        } else {
1371
            my @stat = stat($vpath);
1372
            return($stat[7]); # report size of new image for billing purposes
1373
        }
1374
    }
1375
}
1376

    
1377
sub updateBilling {
1378
    my $imageuser = shift;
1379
    if ($imageuser && $imageuser ne 'common') {
1380
        my $bcmd = "/usr/bin/$sshcmd 127.0.0.1 REMOTE_USER=$imageuser $base/cgi/images.cgi -a updatebilling";
1381
        my $ures = `$bcmd`;
1382
        debuglog("updating billing for $imageuser: $ures");
1383
    }
1384
}
1385

    
1386
sub debuglog {
1387
    my $msg = shift;
1388
    chomp $msg;
1389

    
1390
    open(my $fd, ">>", $debugfile);
1391
    print $fd "[$pretty_time] $msg\n";
1392
    close($fd);
1393
}
1394

    
1395
sub shell_esc_chars {
1396
	my $str = shift;
1397
	$str =~ s/([;<>\*\|`&\$!#\(\)\[\]\{\}:'" ])/\\$1/g;
1398
	return $str;
1399
}
(27-27/27)