Project

General

Profile

Download (126 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::Servers;
9

    
10
use Error qw(:try);
11
use Data::UUID;
12
use Proc::Daemon;
13
use File::Basename;
14
use lib dirname (__FILE__);
15
use File::Basename;
16
use Config::Simple;
17
use lib dirname (__FILE__);
18
use Stabile;
19
#use Encode::Escape;
20

    
21
$\ = ''; # Some of the above seems to set this to \n, resulting in every print appending a line feed
22

    
23
$cpuovercommision = $Stabile::config->get('CPU_OVERCOMMISION') || 1;
24
$dpolicy = $Stabile::config->get('DISTRIBUTION_POLICY') || 'disperse'; #"disperse" or "pack"
25
$amtpasswd = $Stabile::config->get('AMT_PASSWD') || "";
26
$brutalsleep = $Stabile::config->get('BRUTAL_SLEEP') || "";
27
$sshcmd = $sshcmd || $Stabile::sshcmd;
28

    
29
my %ahash; # A hash of accounts and associated privileges current user has access to
30

    
31
#my %options=();
32
#Getopt::Std::getopts("a:hfu:m:k:", \%options); # -a action -h help -f full-list (all users) -u uuid -m match pattern -k keywords
33

    
34
try {
35
    Init(); # Perform various initalization tasks
36
    process() if ($package);
37

    
38
    if ($action || %params) {
39
    	untie %register;
40
    	untie %networkreg;
41
        untie %nodereg;
42
        untie %xmlreg;
43
    }
44

    
45
} catch Error with {
46
	my $ex = shift;
47
    print $Stabile::q->header('text/html', '500 Internal Server Error') unless ($console);
48
	if ($ex->{-text}) {
49
        print "Got error: ", $ex->{-text}, " on line ", $ex->{-line}, "\n";
50
	} else {
51
	    print "Status=ERROR\n";
52
	}
53
} finally {
54
};
55

    
56
1;
57

    
58
sub getObj {
59
    my %h = %{@_[0]};
60
    $console = 1 if $h{"console"};
61
    $api = 1 if $h{"api"};
62
    my $uuid = $h{"uuid"};
63
    $uuid = $curuuid if ($uuid eq 'this');
64
    my $obj;
65
    $action = $action || $h{'action'};
66

    
67
    if ($h{'action'} eq 'destroy' || $action eq 'destroy' || $action eq 'destroyuserservers' || $action eq 'attach' || $action eq 'detach' || $action =~ /changepassword|sshaccess/) {
68
        $obj = \%h;
69
        return $obj;
70
    }
71

    
72
    # Allow specifying nicmac1 instead of uuid if known
73
    if (!$uuid) {
74
        $uuid = nicmac1ToUuid($h{"nicmac1"});
75
    }
76
    my $status = 'new';
77
    $status = $register{$uuid}->{'status'} if ($register{$uuid});
78

    
79
    my $objaction = lc $h{"action"};
80
    $objaction = "" if ($status eq "new");
81

    
82
    if ((!$uuid) && $status eq 'new') {
83
        my $ug = new Data::UUID;
84
        $uuid = $ug->create_str();
85
        if ($uripath =~ /servers(\.cgi)?\/(.+)/) {
86
            my $huuid = $2;
87
            if ($ug->to_string($ug->from_string($huuid)) eq $huuid) { # Check for valid uuid
88
                $uuid = $huuid;
89
            }
90
        }
91
    };
92
    unless ($uuid && length $uuid == 36) {
93
        $posterror .= "Status=Error Invalid uuid.\n";
94
        return;
95
    }
96

    
97
    my $dbobj = $register{$uuid} || {};
98

    
99
    my $name = $h{"name"} || $dbobj->{'name'};
100
    utf8::decode($name);
101
    my $memory = $h{"memory"} || $dbobj->{'memory'};
102
    my $vcpu = $h{"vcpu"} || $dbobj->{'vcpu'};
103
    my $boot = $h{"boot"} || $dbobj->{'boot'};
104
    my $loader = $h{"loader"} || $dbobj->{'loader'};
105
    my $image = $h{"image"} || $dbobj->{'image'};
106
    my $imagename = $h{"imagename"} || $dbobj->{'imagename'};
107
    if ($image && $image ne '--' && !($image =~ /^\//)) { # Image is registered by uuid - we find the path
108
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {$posterror = "Unable to access image uuid register"; return;};
109
        $image = $imagereg2{$image}->{'path'};
110
        $imagename = $imagereg2{$image}->{'name'};
111
        untie %imagereg2;
112
        return unless ($image);
113
    }
114
    my $image2 = $h{"image2"} || $dbobj->{'image2'};
115
    my $image3 = $h{"image3"} || $dbobj->{'image3'};
116
    my $image4 = $h{"image4"} || $dbobj->{'image4'};
117
    my $image2name = $h{"image2name"} || $dbobj->{'image2name'};
118
    my $image3name = $h{"image3name"} || $dbobj->{'image3name'};
119
    my $image4name = $h{"image4name"} || $dbobj->{'image4name'};
120
    if ($image2 && $image2 ne '--' && !($image2 =~ /^\//)) { # Image2 is registered by uuid - we find the path
121
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {$postreply = "Unable to access image uuid register"; return $postreply;};
122
        $image2 = $imagereg2{$image2}->{'path'};
123
        $image2name = $imagereg2{$image2}->{'name'};
124
        untie %imagereg2;
125
    }
126
    my $diskbus = $h{"diskbus"} || $dbobj->{'diskbus'};
127
    my $diskdev = "vda";
128
    my $diskdev2 = "vdb";
129
    my $diskdev3 = "vdc";
130
    my $diskdev4 = "vdd";
131
    if ($diskbus eq "ide") {$diskdev = "hda"; $diskdev2 = "hdb"; $diskdev3 = "hdc"; $diskdev4 = "hdd"};
132
    my $cdrom = $h{"cdrom"} || $dbobj->{'cdrom'};
133
    if ($cdrom && $cdrom ne '--' && !($cdrom =~ /^\//) && $cdrom ne 'virtio') {
134
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {$postreply = "Unable to access image uuid register"; return $postreply;};
135
        $cdrom = $imagereg2{$cdrom}->{'path'};
136
        untie %imagereg2;
137
    }
138

    
139
    my $networkuuid1 = $h{"networkuuid1"} || $dbobj->{'networkuuid1'};
140
    if ($h{"networkuuid1"} eq "0") {$networkuuid1 = "0"}; #Stupid perl... :-)
141
    my $networkid1 = $h{"networkid1"} || $dbobj->{'networkid1'};
142
    my $networkname1 = $h{"networkname1"} || $dbobj->{'networkname1'};
143
    my $nicmodel1 = $h{"nicmodel1"} || $dbobj->{'nicmodel1'};
144
    my $nicmac1 = $h{"nicmac1"} || $dbobj->{'nicmac1'};
145
    if (!$nicmac1 || $nicmac1 eq "--") {$nicmac1 = randomMac();}
146

    
147
    my $networkuuid2 = $h{"networkuuid2"} || $dbobj->{'networkuuid2'};
148
    if ($h{"networkuuid2"} eq "0") {$networkuuid2 = "0"};
149
    my $networkid2 = $h{"networkid2"} || $dbobj->{'networkid2'};
150
    my $networkname2 = $h{"networkname2"} || $dbobj->{'networkname2'};
151
    my $nicmac2 = $h{"nicmac2"} || $dbobj->{'nicmac2'};
152
    if (!$nicmac2 || $nicmac2 eq "--") {$nicmac2 = randomMac();}
153

    
154
    my $networkuuid3 = $h{"networkuuid3"} || $dbobj->{'networkuuid3'};
155
    if ($h{"networkuuid3"} eq "0") {$networkuuid3 = "0"};
156
    my $networkid3 = $h{"networkid3"} || $dbobj->{'networkid3'};
157
    my $networkname3 = $h{"networkname3"} || $dbobj->{'networkname3'};
158
    my $nicmac3 = $h{"nicmac3"} || $dbobj->{'nicmac3'};
159
    if (!$nicmac3 || $nicmac3 eq "--") {$nicmac3 = randomMac();}
160

    
161
    my $action = $h{"action"};
162
    my $notes = $h{"notes"};
163
    $notes = $dbobj->{'notes'} if (!$notes || $notes eq '--');
164
    my $reguser = $dbobj->{'user'};
165
    my $autostart = ($h{"autostart"} ."") || $dbobj->{'autostart'};
166
    if ($autostart && $autostart ne "false") {$autostart = "true";}
167
    my $locktonode = ($h{"locktonode"} ."") || $dbobj->{'locktonode'};
168
    if ($locktonode && $locktonode ne "false") {$locktonode = "true";}
169
    my $mac;
170
    $mac = $dbobj->{'mac'} unless ($objaction eq 'start' || $objaction eq 'move' || $objaction eq 'stormove');
171
    $mac = $h{"mac"} if ($isadmin && $h{"mac"});
172
    my $domuser = $h{"user"} || $user; # Set if user is trying to move server to another account
173

    
174
    # Sanity checks
175
    if (
176
        ($name && length $name > 255)
177
            || ($networkuuid1<0)
178
            || ($networkuuid2<0)
179
            || ($networkuuid3<0)
180
            || ($networkuuid1>1 && length $networkuuid1 != 36)
181
            || ($networkuuid2>1 && length $networkuuid2 != 36)
182
            || ($networkuuid3>1 && length $networkuuid3 != 36)
183
            || ($image && length $image > 255)
184
            || ($imagename && length $imagename > 255)
185
            || ($image2 && length $image2 > 255)
186
            || ($image3 && length $image3 > 255)
187
            || ($image4 && length $image4 > 255)
188
            || ($image2name && length $image2name > 255)
189
            || ($image3name && length $image3name > 255)
190
            || ($image4name && length $image4name > 255)
191
            || ($cdrom && length $cdrom > 255)
192
            || ($memory && ($memory<64 || $memory >1024*64))
193
    ) {
194
        $postreply .= "Status=ERROR Invalid server data: $name\n";
195
        return 0;
196
    }
197

    
198
    # Security check
199
    if ($status eq 'new' && (($action && $action ne '--' && $action ne 'save') || !$image || $image eq '--')) {
200
        $postreply .= "Status=ERROR Bad server data: $name\n";
201
        $postmsg = "Bad server data";
202
        return 0;
203
    }
204
    if (!$reguser && $status ne 'new'
205
        && !($name && $memory && $vcpu && $boot && $image && $diskbus && $networkuuid1 && $nicmodel1)) {
206
        $posterror .= "Status=ERROR Insufficient data: $name\n";
207
        return 0;
208
    }
209
    if (!$isadmin) {
210
        if (($networkuuid1>1 && $networkreg{$networkuuid1}->{'user'} ne $user)
211
            || ($networkuuid2>1 && $networkreg{$networkuuid2}->{'user'} ne $user)
212
            || ($networkuuid3>1 && $networkreg{$networkuuid3}->{'user'} ne $user)
213
        )
214
        {
215
            $postreply .= "Status=ERROR No privileges: $networkname1 $networkname2\n";
216
            return 0;
217
        }
218
        if ( ($reguser && ($user ne $reguser) && $action ) || ($reguser && $status eq "new"))
219
        {
220
            $postreply .= "Status=ERROR No privileges: $name\n";
221
            return 0;
222
        }
223
        if (!($image =~ /\/$user\//)
224
            || ($image2 && $image2 ne "--" && !($image2 =~ /\/$user\//))
225
            || ($image3 && $image3 ne "--" && !($image3 =~ /\/$user\//))
226
            || ($image4 && $image4 ne "--" && !($image4 =~ /\/$user\//))
227
        )
228
        {
229
            $postreply .= "Status=ERROR No image privileges: $name\n";
230
            return 0;
231
        }
232
    }
233

    
234
    # No action - regular save of domain properties
235
    $cdrom = '--' if ($cdrom eq 'virtio' && $action ne 'mountcd');
236

    
237
    $obj = {
238
        uuid => $uuid,
239
        status => $status,
240
        name => $name,
241
        memory => $memory,
242
        vcpu => $vcpu,
243
        image => $image,
244
        imagename => $imagename,
245
        image2 => $image2,
246
        image2name => $image2name,
247
        image3 => $image3,
248
        image3name => $image3name,
249
        image4 => $image4,
250
        image4name => $image4name,
251
        diskbus => $diskbus,
252
        cdrom => $cdrom,
253
        boot => $boot,
254
        loader=> $loader,
255
        networkuuid1 => $networkuuid1,
256
        networkid1 => $networkid1,
257
        networkname1 => $networkname1,
258
        nicmodel1 => $nicmodel1,
259
        nicmac1 => $nicmac1,
260
        networkuuid2 => $networkuuid2,
261
        networkid2 => $networkid2,
262
        networkname2 => $networkname2,
263
        nicmac2 => $nicmac2,
264
        networkuuid3 => $networkuuid3,
265
        networkid3 => $networkid3,
266
        networkname3 => $networkname3,
267
        nicmac3 => $nicmac3,
268
        notes => $notes,
269
        autostart => $autostart,
270
        locktonode => $locktonode,
271
        mac => $mac,
272
        user => $domuser
273
    };
274
    return $obj;
275
}
276

    
277
sub Init {
278
    # Tie database tables to hashes
279
    unless ( tie(%register,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access image register"};
280
    unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {return "Unable to access network register"};
281
    unless ( tie(%nodereg,'Tie::DBI', Hash::Merge::merge({table=>'nodes', key=>'mac'}, $Stabile::dbopts)) ) {return "Unable to access nodes register"};
282
    unless ( tie(%xmlreg,'Tie::DBI', Hash::Merge::merge({table=>'domainxml'}, $Stabile::dbopts)) ) {return "Unable to access domainxml register"};
283

    
284
    # simplify globals initialized in Stabile.pm
285
    $tktuser = $tktuser || $Stabile::tktuser;
286
    $user = $user || $Stabile::user;
287
    $isadmin = $isadmin || $Stabile::isadmin;
288
    $privileges = $privileges || $Stabile::privileges;
289

    
290
    # Create aliases of functions
291
    *header = \&CGI::header;
292
    *to_json = \&JSON::to_json;
293

    
294
    *Showautostart = \&Autostartall;
295
    *Stormove = \&Move;
296

    
297
    *do_save = \&Save;
298
    *do_tablelist = \&do_list;
299
    *do_jsonlist = \&do_list;
300
    *do_showautostart = \&action;
301
    *do_autostartall = \&privileged_action;
302
    *do_help = \&action;
303

    
304
    *do_start = \&privileged_action;
305
    *do_destroy = \&action;
306
    *do_shutdown = \&action;
307
    *do_suspend = \&action;
308
    *do_resume = \&action;
309
    *do_remove = \&privileged_action;
310
    *do_move = \&action;
311
    *do_abort = \&action;
312
    *do_stormove = \&action;
313
    *do_mountcd = \&action;
314
    *do_changepassword = \&privileged_action;
315
    *do_sshaccess = \&privileged_action;
316

    
317
    *do_gear_start = \&do_gear_action;
318
    *do_gear_autostart = \&do_gear_action;
319
    *do_gear_showautostart = \&do_gear_action;
320
    *do_gear_autostartall = \&do_gear_action;
321
    *do_gear_remove = \&do_gear_action;
322
    *do_gear_changepassword = \&do_gear_action;
323
    *do_gear_sshaccess = \&do_gear_action;
324

    
325
}
326

    
327
sub do_list {
328
    my ($uuid, $action) = @_;
329
    if ($help) {
330
        return <<END
331
GET:uuid:
332
List servers current user has access to.
333
END
334
    }
335

    
336
    my $res;
337
    my $filter;
338
    my $statusfilter;
339
    my $uuidfilter;
340
    my $curserv = $register{$curuuid};
341
    if ($curuuid && ($isadmin || $curserv->{'user'} eq $user) && $uripath =~ /servers(\.cgi)?\/(\?|)(this)/) {
342
        $uuidfilter = $curuuid;
343
    } elsif ($uripath =~ /servers(\.cgi)?\/(\?|)(name|status)/) {
344
        $filter = $3 if ($uripath =~ /servers(\.cgi)?\/\??name(:|=)(.+)/);
345
        $filter = $1 if ($filter =~ /(.*)\*$/);
346
        $statusfilter = $4 if ($uripath =~ /servers(\.cgi)?\/\??(.+ AND )?status(:|=)(\w+)/);
347
    } elsif ($uripath =~ /servers(\.cgi)?\/(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})/) {
348
        $uuidfilter = $2;
349
    }
350
    $filter = $1 if ($filter =~ /(.*)\*/);
351

    
352
    my $sysuuid;
353
    if ($params{'system'}) {
354
        $sysuuid = $params{'system'};
355
        $sysuuid = $cursysuuid || $curuuid if ($params{'system'} eq 'this');
356
    }
357
    my @curregvalues;
358
    my @regkeys;
359
    if ($fulllist && $isadmin) {
360
        @regkeys = keys %register;
361
    } elsif ($uuidfilter && $isadmin) {
362
        @regkeys = (tied %register)->select_where("uuid = '$uuidfilter'");
363
    } elsif ($sysuuid) {
364
        @regkeys = (tied %register)->select_where("system = '$sysuuid' OR uuid = '$sysuuid'");
365
    } else {
366
        @regkeys = (tied %register)->select_where("user = '$user'");
367
    }
368

    
369
    unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
370
    unless (tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access images register"}|; return $res;};
371

    
372
    foreach my $k (@regkeys) {
373
        $valref = $register{$k};
374
        # Only include VM's belonging to current user (or all users if specified and user is admin)
375
        if ($user eq $valref->{'user'} || $fulllist || ($uuidfilter && $isadmin)) {
376
            next unless (!$sysuuid || $valref->{'system'} eq $sysuuid || $valref->{'uuid'} eq $sysuuid);
377

    
378
            my $validatedref = validateItem($valref);
379
            my %val = %{$validatedref}; # Deference and assign to new ass array, effectively cloning object
380
            $val{'memory'} += 0;
381
            $val{'vcpu'} += 0;
382
            $val{'nodetype'} = 'parent';
383
            $val{'internalip'} = $networkreg{$val{'networkuuid1'}}->{'internalip'};
384
            $val{'self'} = 1 if ($curuuid && $curuuid eq $val{'uuid'});
385
            if ($action eq 'treelist') {
386
                if ($val{'system'} && $val{'system'} ne '') {
387
                    my $sysuuid = $val{'system'};
388
                    my $sysname = $sysreg{$sysuuid}->{'name'};
389
                    if (!$sysname) {
390
                        $sysname = $1 if ($sysname =~ /(.+)\..*/);
391
                        $sysname = $val{'name'};
392
                        $sysname =~ s/server/System/i;
393
                    }
394
                    $sysreg{$sysuuid} = {
395
                        uuid => $sysuuid,
396
                        name => $sysname,
397
                        user => 'irigo'
398
                    };
399

    
400
                    my %pval = %{$sysreg{$sysuuid}};
401
                    $pval{'nodetype'} = 'parent';
402
                    $pval{'status'} = '--';
403
                    $val{'nodetype'} = 'child';
404

    
405
                    my @children;
406
                    push @children,\%val;
407
                    $pval{'children'} = \@children;
408
                    push @curregvalues,\%pval;
409
                } else {
410
                    push @curregvalues,\%val;
411
                }
412
            } elsif ($filter || $statusfilter || $uuidfilter) { # List filtered servers
413
                my $fmatch;
414
                my $smatch;
415
                my $umatch;
416
                $fmatch = 1 if (!$filter || $val{'name'}=~/$filter/i);
417
                $smatch = 1 if (!$statusfilter || $statusfilter eq 'all'
418
                    || $statusfilter eq $val{'status'}
419
                );
420
                $umatch = 1 if ($val{'uuid'} eq $uuidfilter);
421
                if ($fmatch && $smatch && !$uuidfilter) {
422
                    push @curregvalues,\%val;
423
                } elsif ($umatch) {
424
                    push @curregvalues,\%val;
425
                    last;
426
                }
427
            } else {
428
                push @curregvalues,\%val;
429
            }
430
        }
431
    }
432
    tied(%sysreg)->commit;
433
    untie(%sysreg);
434
    untie %imagereg;
435
    @curregvalues = (sort {$a->{'status'} cmp $b->{'status'}} @curregvalues); # Sort by status
436

    
437
    # Sort @curregvalues
438
    @curregvalues = (sort {$b->{'name'} <=> $a->{'name'}} @curregvalues); # Always sort by name first
439
    my $sort = 'status';
440
    $sort = $2 if ($uripath =~ /sort\((\+|\-)(\S+)\)/);
441
    my $reverse;
442
    $reverse = 1 if ($1 eq '-');
443
    if ($reverse) { # sort reverse
444
        if ($sort =~ /memory|vcpu/) {
445
            @curregvalues = (sort {$b->{$sort} <=> $a->{$sort}} @curregvalues); # Sort as number
446
        } else {
447
            @curregvalues = (sort {$b->{$sort} cmp $a->{$sort}} @curregvalues); # Sort as string
448
        }
449
    } else {
450
        if ($sort =~ /memory|vcpu/) {
451
            @curregvalues = (sort {$a->{$sort} <=> $b->{$sort}} @curregvalues); # Sort as number
452
        } else {
453
            @curregvalues = (sort {$a->{$sort} cmp $b->{$sort}} @curregvalues); # Sort as string
454
        }
455
    }
456

    
457
    if ($action eq 'tablelist') {
458
        my $t2;
459

    
460
        if ($isadmin) {
461
            $t2 = Text::SimpleTable->new(36,20,20,10,10,12,7);
462
            $t2->row('uuid', 'name', 'imagename', 'memory', 'user', 'mac', 'status');
463
        } else {
464
            $t2 = Text::SimpleTable->new(36,20,20,10,10,7);
465
            $t2->row('uuid', 'name', 'imagename', 'memory', 'user', 'status');
466
        }
467
        $t2->hr;
468
        my $pattern = $options{m};
469
        foreach $rowref (@curregvalues){
470
            if ($pattern) {
471
                my $rowtext = $rowref->{'uuid'} . " " . $rowref->{'name'} . " " . $rowref->{'imagename'} . " " . $rowref->{'memory'}
472
                    . " " .  $rowref->{'user'} . " " . $rowref->{'status'};
473
                $rowtext .= " " . $rowref->{'mac'} if ($isadmin);
474
                next unless ($rowtext =~ /$pattern/i);
475
            }
476
            if ($isadmin) {
477
                $t2->row($rowref->{'uuid'}, $rowref->{'name'}, $rowref->{'imagename'}, $rowref->{'memory'},
478
                    $rowref->{'user'}, $rowref->{'mac'}, $rowref->{'status'});
479
            } else {
480
                $t2->row($rowref->{'uuid'}, $rowref->{'name'}, $rowref->{'imagename'}, $rowref->{'memory'},
481
                    $rowref->{'user'}, $rowref->{'status'});
482
            }
483
        }
484
        $res .= $t2->draw;
485
    } elsif ($console) {
486
        $res .= Dumper(\@curregvalues);
487
    } else {
488
        my $json_text;
489
        if ($uuidfilter && @curregvalues) {
490
            $json_text = to_json($curregvalues[0], {pretty => 1});
491
        } else {
492
            $json_text = to_json(\@curregvalues, {pretty => 1});
493
        }
494

    
495
        $json_text =~ s/\x/ /g;
496
        $json_text =~ s/\"\"/"--"/g;
497
        $json_text =~ s/null/"--"/g;
498
        $json_text =~ s/"autostart"\s?:\s?"true"/"autostart": true/g;
499
        $json_text =~ s/"autostart"\s?:\s?"--"/"autostart": false/g;
500
        $json_text =~ s/"locktonode"\s?:\s?"true"/"locktonode": true/g;
501
        $json_text =~ s/"locktonode"\s?:\s?"--"/"locktonode": false/g;
502
        $json_text =~ s/"loader"\s?:\s?"--"/"loader": "bios"/g;
503
        if ($action eq 'jsonlist' || $action eq 'list' || !$action) {
504
            $res .= $json_text;
505
        } else {
506
            $res .= qq|{"action": "$action", "identifier": "uuid", "label": "uuid", "items" : $json_text}|;
507
        }
508
    }
509
    return $res;
510
}
511

    
512
sub do_uuidshow {
513
    my ($uuid, $action) = @_;
514
    if ($help) {
515
        return <<END
516
GET:uuid:
517
Simple action for showing a single server.
518
END
519
    }
520
    my $res;
521
    $res .= $Stabile::q->header('text/plain') unless $console;
522
    my $u = $uuid || $options{u};
523
    if ($u || $u eq '0') {
524
        foreach my $uuid (keys %register) {
525
            if (($register{$uuid}->{'user'} eq $user || $register{$uuid}->{'user'} eq 'common' || $isadmin)
526
                && $uuid =~ /^$u/) {
527
                my %hash = %{$register{$uuid}};
528
                delete $hash{'action'};
529
                my $dump = Dumper(\%hash);
530
                $dump =~ s/undef/"--"/g;
531
                $res .= $dump;
532
                last;
533
            }
534
        }
535
    }
536
    return $res;
537
}
538

    
539
sub do_uuidlookup {
540
    if ($help) {
541
        return <<END
542
GET:uuid:
543
Simple action for looking up a uuid or part of a uuid and returning the complete uuid.
544
END
545
    }
546
    my $res;
547
    $res .= header('text/plain') unless $console;
548
    my $u = $options{u};
549
    $u = $curuuid unless ($u || $u eq '0');
550
    my $ruuid;
551
    if ($u || $u eq '0') {
552
        my $match;
553
        foreach my $uuid (keys %register) {
554
            if ($uuid =~ /^$u/) {
555
                $ruuid = $uuid if ($register{$uuid}->{'user'} eq $user || index($privileges,"a")!=-1);
556
                $match = 1;
557
                last;
558
            }
559
        }
560
        if (!$match && $isadmin) { # If no match and user is admin, do comprehensive lookup
561
            foreach my $uuid (keys %register) {
562
                if ($uuid =~ /^$u/ || $register{$uuid}->{'name'} =~ /^$u/) {
563
                    $ruuid = $uuid;
564
                    last;
565
                }
566
            }
567
        }
568
    }
569
    $res .= "$ruuid\n" if ($ruuid);
570
    return $res;
571
}
572

    
573
sub do_destroyuserservers {
574
    my ($uuid, $action, $obj) = @_;
575
    if ($help) {
576
        return <<END
577
GET:username:
578
Simple action for destroying all servers belonging to a user
579
END
580
    }
581
    $username = $obj->{username};
582
    my $res;
583
    $res .= $Stabile::q->header('text/plain') unless $console;
584

    
585
    destroyUserServers($username);
586
    $res .= $postreply;
587
    return $res;
588
}
589

    
590
sub do_removeuserservers {
591
    if ($help) {
592
        return <<END
593
GET::
594
Simple action for removing all servers belonging to a user
595
END
596
    }
597
    my $res;
598
    $res .= $Stabile::q->header('text/plain') unless $console;
599
    removeUserServers($user);
600
    $res .= $postreply;
601
    return $res;
602
}
603

    
604
sub do_getappid {
605
    my ($uuid, $action) = @_;
606
    if ($help) {
607
        return <<END
608
GET:uuid:
609
Simple action for getting the app id
610
END
611
    }
612
    my $res;
613
    $res .= $Stabile::q->header('text/plain') unless $console;
614
    $uuid = $uuid || $options{u};
615
    $uuid = $curuuid unless ($uuid);
616
    if ($uuid && $register{$uuid}) {
617
        unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access image register"};
618
        $res .= "appid: ". $imagereg{$register{$uuid}->{image}}->{appid}, "\n";
619
        untie %imagereg;
620
    }
621
    return $res;
622
}
623

    
624
sub do_setrunning {
625
    my ($uuid, $action) = @_;
626
    if ($help) {
627
        return <<END
628
GET:uuid:
629
Simple action for setting status back to running after e.g. an upgrade
630
END
631
    }
632
    my $res;
633
    $res .= $Stabile::q->header('text/plain') unless $console;
634
    $uuid = $uuid || $options{u};
635
    $uuid = $curuuid unless ($uuid);
636
    if ($uuid && $register{$uuid}) {
637
        $register{$uuid}->{'status'} = 'running';
638
        $main::updateUI->({ tab => 'servers',
639
            user                => $user,
640
            uuid                => $uuid,
641
            status              => 'running' })
642

    
643
    };
644
    $res .= "Status=OK Set status of $register{$uuid}->{'name'} to running\n";
645
    return $res;
646
}
647

    
648
sub do_getappinfo {
649
    my ($uuid, $action) = @_;
650
    if ($help) {
651
        return <<END
652
GET:uuid:
653
Simple action for getting the apps basic info
654
END
655
    }
656
    my $res;
657
    $res .= $Stabile::q->header('application/json') unless $console;
658
    $uuid = $uuid || $options{u};
659
    $uuid = $curuuid unless ($uuid);
660
    my %appinfo;
661
    if ($uuid && $register{$uuid}) {
662
        unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access image register"};
663
        $appinfo{'appid'} = $imagereg{$register{$uuid}->{image}}->{appid} || '';
664
        $appinfo{'managementlink'} = $imagereg{$register{$uuid}->{image}}->{managementlink} || '';
665
        $appinfo{'managementlink'} =~ s/{uuid}/$register{$uuid}->{networkuuid1}/;
666

    
667
        my $termlink = $imagereg{$register{$uuid}->{image}}->{terminallink} || '';
668
        $termlink =~ s/{uuid}/$register{$uuid}->{networkuuid1}/;
669
        my $burl = $baseurl;
670
        $burl = $1 if ($termlink =~ /\/stabile/ && $baseurl =~ /(.+)\/stabile/); # Unpretty, but works for now
671
        # $termlink = $1 if ($termlink =~ /\/(.+)/);
672
        # $termlink = "$burl/$termlink" unless ($termlink =~ /^http/ || !$termlink); # || $termlink =~ /^\//
673
        $appinfo{'terminallink'} = $termlink;
674

    
675
        $appinfo{'upgradelink'} = $imagereg{$register{$uuid}->{image}}->{upgradelink} || '';
676
        $appinfo{'upgradelink'} =~ s/{uuid}/$register{$uuid}->{networkuuid1}/;
677
        $appinfo{'version'} = $imagereg{$register{$uuid}->{image}}->{version} || '';
678
        $appinfo{'status'} = $register{$uuid}->{status} || '';
679
        $appinfo{'name'} = $register{$uuid}->{name} || '';
680
        $appinfo{'system'} = $register{$uuid}->{system} || '';
681

    
682
        if ($appinfo{'system'}) {
683
            unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
684
            $appinfo{'systemname'} = $sysreg{$appinfo{'system'}}->{name} || '';
685
            untie(%sysreg);
686
        } else {
687
            $appinfo{'systemname'} = $appinfo{'name'};
688
        }
689

    
690

    
691
        if ($appinfo{'appid'}) {
692
            my @regkeys = (tied %imagereg)->select_where("appid = '$appinfo{appid}'");
693
            foreach my $k (@regkeys) {
694
                my $imgref = $imagereg{$k};
695
                if ($imgref->{'path'} =~ /\.master\.qcow2$/ && $imgref->{'appid'} eq $appinfo{'appid'}
696
                     && $imgref->{'installable'} && $imgref->{'installable'} ne 'false'
697
                ) {
698
                    if ($imgref->{'version'} > $appinfo{'currentversion'}) {
699
                        $appinfo{'currentversion'} = $imgref->{'version'};
700
                        $appinfo{'appname'} = $imgref->{'name'};
701
                    }
702
                }
703
            }
704
        }
705

    
706
        untie %imagereg;
707
    }
708
    $appinfo{'appstoreurl'} = $appstoreurl;
709
    $appinfo{'dnsdomain'} = ($enginelinked)?$dnsdomain:'';
710
    $appinfo{'dnssubdomain'} = ($enginelinked)?substr($engineid, 0, 8):'';
711
    $appinfo{'uuid'} = $uuid;
712
    $appinfo{'user'} = $user;
713
    $appinfo{'remoteip'} = $remoteip;
714
    $res .= to_json(\%appinfo, { pretty => 1 });
715
    return $res;
716
}
717

    
718
sub do_removeserver {
719
    if ($help) {
720
        return <<END
721
GET:uuid:
722
Simple action for destroying and removing a single server
723
END
724
    }
725
    my $res;
726
    $res .= $Stabile::q->header('text/plain') unless $console;
727
    if ($curuuid) {
728
        removeUserServers($user, $curuuid, 1);
729
    }
730
    else {
731
        $postreply .= "Status=Error Unable to uninstall\n";
732
    }
733
    $res .= $postreply;
734
    return $res;
735
}
736

    
737
sub do_updateregister {
738
    if ($help) {
739
        return <<END
740
GET::
741
Update server register
742
END
743
    }
744
    my $res;
745
    $res .= $Stabile::q->header('text/plain') unless $console;
746
    return unless $isadmin;
747
    updateRegister();
748
    $res .= "Status=OK Updated server registry for all users\n";
749
    return $res;
750
}
751

    
752
sub Autostartall {
753
    my ($uuid, $action) = @_;
754
    if ($help) {
755
        return <<END
756
GET::
757
Start all servers marked for autostart. When called as showautostart only shows which would be started.
758
END
759
    }
760
    my $res;
761
    $res .= $Stabile::q->header('text/plain') unless $console;
762
    my $mes;
763
    return $res if ($isreadonly);
764

    
765
    # Wait for all pistons to be online
766
    my $nodedown;
767
    my $nodecount;
768
    for (my $i = 0; $i < 20; $i++) {
769
        $nodedown = 0;
770
        foreach my $node (values %nodereg) {
771
            if ($node->{'status'} ne 'running' && $node->{'status'} ne 'maintenance') {
772
                $nodedown = 1;
773
            }
774
            else {
775
                $nodecount++ unless ($node->{'status'} eq 'maintenance');
776
            }
777
        }
778
        if ($nodedown) {
779
            # Wait and see if nodes come online
780
            $mes = "Waiting for nodes...(" . (10 - $i) . ")\n";
781
            print $mes if ($console);
782
            $res .= $mes;
783
            sleep 10;
784
        }
785
        else {
786
            last;
787
        }
788
    }
789

    
790
    $mes = "$nodecount nodes ready - autostarting servers...\n";
791
    $main::syslogit->("irigo", "info", "$nodecount nodes ready - autostarting servers...");
792

    
793
    print $mes if ($console);
794
    $res .= $mes;
795
    if (!%nodereg || $nodedown || !$nodecount) {
796
        $mes = "Only autostarting servers on local node - not all nodes ready!\n";
797
        print $mes if ($console);
798
        $res .= $mes;
799
    }
800
    if ($action eq "showautostart") {
801
        $mes = "Only showing which servers would be starting!\n";
802
        print $mes if ($console);
803
        $res .= $mes;
804
    }
805

    
806
    $Stabile::Networks::user = $user;
807
    require "$Stabile::basedir/cgi/networks.cgi";
808
    $Stabile::Networks::console = 1;
809

    
810
    foreach my $dom (values %register) {
811
        if ($nodedown) { # Only start local servers
812
            unless ($dom->{mac} && $nodereg{$dom->{mac}}->{identity} eq 'local_kvm') {
813
                $mes = "Skipping non-local domain $dom->{name}, $dom->{status}\n";
814
                print $mes if ($console);
815
                $res .= $mes;
816
                next;
817
            }
818
        }
819
        if ($dom->{'autostart'} eq '1' || $dom->{'autostart'} eq 'true') {
820
            $res .= "Checking if $dom->{'name'} ($dom->{'user'}, $dom->{'uuid'}) should be started\n";
821
            my $networkstatus1 = $networkreg{$dom->{'networkuuid1'}}->{status};
822
            my $networkstatus2 = ($networkreg{$dom->{'networkuuid2'}})?$networkreg{$dom->{'networkuuid2'}}->{status}:'';
823
            my $networkstatus3 = ($networkreg{$dom->{'networkuuid3'}})?$networkreg{$dom->{'networkuuid3'}}->{status}:'';
824
            my @dnets;
825
            push @dnets, $dom->{'networkuuid1'} if ($dom->{'networkuuid1'} && $dom->{'networkuuid1'} ne '--' && $networkstatus1 ne 'up');
826
            push @dnets, $dom->{'networkuuid2'} if ($dom->{'networkuuid2'} && $dom->{'networkuuid2'} ne '--' && $networkstatus2 ne 'up');
827
            push @dnets, $dom->{'networkuuid3'} if ($dom->{'networkuuid3'} && $dom->{'networkuuid3'} ne '--' && $networkstatus3 ne 'up');
828
            my $i;
829
            for ($i=0; $i<5; $i++) { # wait for status newer than 10 secs
830
                validateItem($dom);
831
                last if (time() - $dom->{timestamp} < 10);
832
                $mes = "Waiting for newer timestamp, current is " . (time() - $dom->{timestamp}) . " old\n";
833
                print $mes if ($console);
834
                $res .= $mes;
835
                sleep 2;
836
            }
837
            if (
838
                $dom->{'status'} eq 'shutoff' || $dom->{'status'} eq 'inactive'
839
            ) {
840
                if ($action eq "showautostart") { # Dry run
841
                    $mes = "Starting $dom->{'name'} ($dom->{'user'}, $dom->{'uuid'})\n";
842
                    print $mes if ($console);
843
                    $res .= $mes;
844
                }
845
                else {
846
                    $mes = "Starting $dom->{'name'} ($dom->{'user'}, $dom->{'uuid'})\n";
847
                    print $mes if ($console);
848
                    $res .= $mes;
849
                    $postreply = Start($dom->{'uuid'});
850
                    print $postreply if ($console);
851
                    $res .= $postreply;
852
#                        $mes = `REMOTE_USER=$dom->{'user'} $base/cgi/servers.cgi -a start -u $dom->{'uuid'}`;
853
                    print $mes if ($console);
854
                    $res .= $mes;
855
                    sleep 1;
856
                }
857
            }
858
            elsif (@dnets) {
859
                if ($action eq "showautostart") { # Dry run
860
                    foreach my $networkuuid (@dnets) {
861
                        $mes = "Would bring network $networkreg{$networkuuid}->{name} up for $dom->{'name'} ($dom->{'user'}, $dom->{'uuid'})\n";
862
                        print $mes if ($console);
863
                        $res .= $mes;
864
                    }
865
                }
866
                else {
867
                    foreach my $networkuuid (@dnets) {
868
                        $mes = "Bringing network $networkreg{$networkuuid}->{name} up for $dom->{'name'} ($dom->{'user'}, $dom->{'uuid'})\n";
869
                        print $mes if ($console);
870
                        $res .= $mes;
871
                        $mes = Stabile::Networks::Activate($networkuuid, 'activate');
872
                        print $mes if ($console);
873
                        $res .= $mes;
874
                        sleep 1;
875
                    }
876
                }
877
            }
878
        } else {
879
            $res .= "Not marked for autostart ($dom->{'autostart'}): $dom->{'name'} ($dom->{'user'}, $dom->{'uuid'})\n";
880
            validateItem($dom);
881
        }
882
    }
883
    return $res;
884
}
885

    
886
sub do_listnodeavailability {
887
    if ($help) {
888
        return <<END
889
GET::
890
Utility call - only informational. Shows availability of nodes for starting servers.
891
END
892
    }
893
    my $res;
894
    $res .= $Stabile::q->header('application/json') unless ($console);
895
    my ($temp1, $temp2, $temp3, $temp4, $ahashref) = locateTargetNode();
896
    my @avalues = values %$ahashref;
897
    my @sorted_values = (sort {$b->{'index'} <=> $a->{'index'}} @avalues);
898
    $res .= to_json(\@sorted_values, { pretty => 1 });
899
    return $res;
900
}
901

    
902
sub do_listbillingdata {
903
    if ($help) {
904
        return <<END
905
GET::
906
List current billing data.
907
END
908
    }
909
    my $res;
910
    $res .= $Stabile::q->header('application/json') unless ($console);
911
    my $buser = URI::Escape::uri_unescape($params{'user'}) || $user;
912
    my %b;
913
    my @bmonths;
914
    if ($isadmin || $buser eq $user) {
915
        my $bmonth = URI::Escape::uri_unescape($params{'month'}) || $month;
916
        my $byear = URI::Escape::uri_unescape($params{'year'}) || $year;
917
        if ($bmonth eq "all") {
918
            @bmonths = ("01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12");
919
        }
920
        else {
921
            @bmonths = ($bmonth);
922
        }
923

    
924
        unless ( tie(%billingreg,'Tie::DBI', Hash::Merge::merge({table=>'billing_domains', key=>'usernodetime'}, $Stabile::dbopts)) ) {return "Unable to access billing register"};
925

    
926
        my @nkeys = keys %nodereg;
927
        foreach my $bm (@bmonths) {
928
            my $vcpuavg = 0;
929
            my $memoryavg = 0;
930
            foreach my $nmac (@nkeys) {
931
                $vcpuavg += $billingreg{"$buser-$nmac-$byear-$bm"}->{'vcpuavg'};
932
                $memoryavg += $billingreg{"$buser-$nmac-$byear-$bm"}->{'memoryavg'};
933
            }
934
            $b{"$buser-$byear-$bm"} = {
935
                id        => "$buser-$byear-$bm",
936
                vcpuavg   => $vcpuavg,
937
                memoryavg => $memoryavg,
938
                month     => $bm + 0,
939
                year      => $byear + 0
940
            }
941
        }
942
        untie %billingreg;
943
    }
944
    my @bvalues = values %b;
945
    $res .= "{\"identifier\": \"id\", \"label\": \"id\", \"items\":" . to_json(\@bvalues) . "}";
946
    return $res;
947
}
948

    
949
# Print list of available actions on objects
950
sub do_plainhelp {
951
    my $res;
952
    $res .= $Stabile::q->header('text/plain') unless $console;
953
    $res .= <<END
954
new [name="name"]
955
* start: Starts a server
956
* destroy: Destroys a server, i.e. terminates the VM, equivalent of turning the power off a physical computer
957
* shutdown: Asks the operating system of a server to shut down via ACPI
958
* suspend: Suspends the VM, effectively putting the server to sleep
959
* resume: Resumes a suspended VM, effectively waking the server from sleep
960
* move [mac="mac"]: Moves a server to specified node. If no node is specified, moves to other node with highest availability
961
index
962
* delete: Deletes a server. Image and network are not deleted, only information about the server. Server cannot be
963
runing
964
* mountcd [cdrom="path"]: Mounts a cd rom
965
END
966
    ;
967
    return $res;
968
}
969

    
970
# Helper function
971
sub recurse($) {
972
	my($path) = @_;
973
	my @files;
974
	## append a trailing / if it's not there
975
	$path .= '/' if($path !~ /\/$/);
976
	## loop through the files contained in the directory
977
	for my $eachFile (glob($path.'*')) {
978
		## if the file is a directory
979
		if( -d $eachFile) {
980
			## pass the directory to the routine ( recursion )
981
			push(@files,recurse($eachFile));
982
		} else {
983
			push(@files,$eachFile);
984
		}
985
	}
986
	return @files;
987
}
988

    
989
sub Start {
990
    my ($uuid, $action, $obj) = @_;
991
    $dmac = $obj->{mac};
992
    $buildsystem = $obj->{buildsystem};
993
    $uistatus = $obj->{uistatus};
994
    if ($help) {
995
        return <<END
996
GET:uuid,mac:
997
Start a server. Supply mac for starting on specific node.
998
END
999
    }
1000
    $dmac = $dmac || $params{'mac'};
1001
    return "Status=ERROR No uuid\n" unless ($register{$uuid});
1002
    my $serv = $register{$uuid};
1003
    $postreply = '' if ($buildsystem);
1004

    
1005
    my $name = $serv->{'name'};
1006
    utf8::decode($name);
1007
    my $image = $serv->{'image'};
1008
    my $image2 = $serv->{'image2'};
1009
    my $image3 = $serv->{'image3'};
1010
    my $image4 = $serv->{'image4'};
1011
    my $memory = $serv->{'memory'};
1012
    my $vcpu = $serv->{'vcpu'};
1013
    my $vgpu = $serv->{'vgpu'};
1014
    my $dbstatus = $serv->{'status'};
1015
    my $mac = $serv->{'mac'};
1016
    my $macname = $serv->{'macname'};
1017
    my $networkuuid1 = $serv->{'networkuuid1'};
1018
    my $networkuuid2 = $serv->{'networkuuid2'};
1019
    my $networkuuid3 = $serv->{'networkuuid3'};
1020
    my $nicmodel1 = $serv->{'nicmodel1'};
1021
    my $nicmac1 = $serv->{'nicmac1'};
1022
    my $nicmac2 = $serv->{'nicmac2'};
1023
    my $nicmac3 = $serv->{'nicmac3'};
1024
    my $boot = $serv->{'boot'};
1025
    my $loader = $serv->{'loader'};
1026
    my $diskbus = $serv->{'diskbus'};
1027
    my $cdrom = $serv->{'cdrom'};
1028
    my $diskdev = "vda";
1029
    my $diskdev2 = "vdb";
1030
    my $diskdev3 = "vdc";
1031
    my $diskdev4 = "vdd";
1032
    if ($diskbus eq "ide") {$diskdev = "hda"; $diskdev2 = "hdb"; $diskdev3 = "hdc"; $diskdev4 = "hdd"};
1033

    
1034
    my $mem = $memory * 1024;
1035

    
1036
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access image register"};
1037

    
1038
    my $img = $imagereg{$image};
1039
    my $imagename = $img->{'name'};
1040
    my $imagestatus = $img->{'status'};
1041
    my $img2 = $imagereg{$image2};
1042
    my $image2status = $img2->{'status'};
1043
    my $img3 = $imagereg{$image3};
1044
    my $image3status = $img3->{'status'};
1045
    my $img4 = $imagereg{$image4};
1046
    my $image4status = $img4->{'status'};
1047

    
1048
    if (!$imagereg{$image}) {
1049
        $postreply .= "Status=Error Image $image not found - please select a new image for your server, not starting $name\n";
1050
        untie %imagereg;
1051
        return $postreply;
1052
    }
1053
    untie %imagereg;
1054

    
1055
    if ($imagestatus ne "used" && $imagestatus ne "cloning") {
1056
        $postreply .= "Status=ERROR Image $imagename $image is $imagestatus, not starting $name\n";
1057
    } elsif ($image2 && $image2 ne '--' && $image2status ne "used" && $image2status ne "cloning") {
1058
        $postreply .= "Status=ERROR Image2 is $image2status, not starting $name\n";
1059
    } elsif ($image3 && $image3 ne '--' && $image3status ne "used" && $image3status ne "cloning") {
1060
        $postreply .= "Status=ERROR Image3 is $image3status, not starting $name\n";
1061
    } elsif ($image4 && $image4 ne '--' && $image4status ne "used" && $image4status ne "cloning") {
1062
        $postreply .= "Status=ERROR Image4 is $image4status, not starting $name\n";
1063
    } elsif (Stabile::Servers::overQuotas($memory,$vcpu)) {
1064
        $main::syslogit->($user, "info", "Over quota ($memory, $vcpu, " . Stabile::Servers::overQuotas($memory,$vcpu) .  ") starting a $dbstatus domain: $uuid");
1065
        $postreply .= "Status=ERROR Over quota - not starting $name\n";
1066
    # Status inactive is typically caused by a movepiston having problems. We should not start inactive servers since
1067
    # they could possibly be running even if movepiston is down. Movepiston on the node should be brought up to update
1068
    # the status, or the node should be removed from the stabile.
1069
    # We now allow to force start of inactive server when dmac is specified
1070
    } elsif ((!$dmac || $dmac eq $mac) && $dbstatus eq 'inactive' && $nodereg{$mac} && ($nodereg{$mac}->{'status'} eq 'inactive' || $nodereg{$mac}->{'status'} eq 'shutdown')) {
1071
        $main::syslogit->($user, "info", "Not starting inactive domain: $uuid (last seen on $mac)");
1072
        $postreply .= "Status=ERROR Not starting $name - Please bring up node $macname\n";
1073
    } elsif ($dbstatus eq 'inactive' || $dbstatus eq 'shutdown' || $dbstatus eq 'shutoff' || $dbstatus eq 'new') {
1074
        unless ($dmac && $isadmin) {
1075
            $dmac = $mac if ($dbstatus eq 'inactive'); # If movepiston crashed while shutting down, allow server to start on same node
1076
        }
1077
        $uistatus = "starting" unless ($uistatus);
1078
        my $hypervisor = getHypervisor($image);
1079
        my ($targetmac, $targetname, $targetip, $port) = locateTargetNode($uuid, $dmac, $mem, $vcpu, $image, $image2 ,$image3, $image4, $hypervisor);
1080

    
1081
        # Read limits from nodeconfig
1082
        my $vm_readlimit = '';
1083
        my $vm_writelimit = '';
1084
        my $vm_iopsreadlimit = ''; # e.g. 1000 IOPS
1085
        my $vm_iopswritelimit = '';
1086
        if  (-e "/etc/stabile/nodeconfig.cfg") {
1087
            my $nodecfg = new Config::Simple("/etc/stabile/nodeconfig.cfg");
1088
            $vm_readlimit = $nodecfg->param('VM_READ_LIMIT'); # e.g. 125829120 = 120 * 1024 * 1024 = 120 MB / s
1089
            $vm_writelimit = $nodecfg->param('VM_WRITE_LIMIT');
1090
            $vm_iopsreadlimit = $nodecfg->param('VM_IOPS_READ_LIMIT'); # e.g. 1000 IOPS
1091
            $vm_iopswritelimit = $nodecfg->param('VM_IOPS_WRITE_LIMIT');
1092
        }
1093

    
1094
        # Build XML for starting domain
1095
        my $graphics = "vnc";
1096
        $graphics = "rdp" if ($hypervisor eq "vbox");
1097
        my $net1 = $networkreg{$networkuuid1};
1098
        my $networkid1 = $net1->{'id'}; # Get the current vlan id of the network
1099
        my $net2 = $networkreg{$networkuuid2};
1100
        my $networkid2 = $net2->{'id'}; # Get the current vlan id of the network
1101
        my $net3 = $networkreg{$networkuuid2};
1102
        my $networkid3 = $net3->{'id'}; # Get the current vlan id of the network
1103
        my $networkid1ip = $net1->{'internalip'};
1104
        $networkid1ip = $net1->{'externalip'} if ($net1->{'type'} eq 'externalip');
1105

    
1106
        my $uname = $name . substr($uuid,0,8); # We don't enforce unique names, so we make them
1107
        $uname =~ s/[^[:ascii:]]/_/g; # Get rid of funny chars - they mess up Guacamole
1108
        $uname =~ s/\W/_/g;
1109

    
1110
        my $driver1;
1111
        my $driver2;
1112
        if ($hypervisor eq 'kvm') {
1113
            my $fmt1 = ($image =~ /\.qcow2$/)?'qcow2':'raw';
1114
            my $fmt2 = ($image2 =~ /\.qcow2$/)?'qcow2':'raw';
1115
            my $fmt3 = ($image3 =~ /\.qcow2$/)?'qcow2':'raw';
1116
            my $fmt4 = ($image4 =~ /\.qcow2$/)?'qcow2':'raw';
1117
            my $cache1 = ($image =~ /\/node\//)?'default':'writeback';
1118
            my $cache2 = ($image2 =~ /\/node\//)?'default':'writeback';
1119
            my $cache3 = ($image3 =~ /\/node\//)?'default':'writeback';
1120
            my $cache4 = ($image4 =~ /\/node\//)?'default':'writeback';
1121
            $driver1 = "\n      <driver name='qemu' type='$fmt1' cache='$cache1'/>";
1122
            $driver2 = "\n      <driver name='qemu' type='$fmt2' cache='$cache2'/>";
1123
            $driver3 = "\n      <driver name='qemu' type='$fmt3' cache='$cache3'/>";
1124
            $driver4 = "\n      <driver name='qemu' type='$fmt4' cache='$cache4'/>";
1125
        }
1126

    
1127
        my $networktype1 = "user";
1128
        my $networksource1 = "default";
1129
        my $networkforward1 = "bridge";
1130
        my $networkisolated1 = "no";
1131
        $networksource1 = "vboxnet0" if ($hypervisor eq "vbox");
1132
        if ($networkid1 eq '0') {
1133
            $networktype1 = "user";
1134
            $networkforward1 = "nat";
1135
            $networkisolated1 = "no"
1136
        } elsif ($networkid1 == 1) {
1137
            $networktype1 = "network" ;
1138
            $networkforward1 = "nat";
1139
            $networkisolated1 = "yes"
1140
        } elsif ($networkid1 > 1) {
1141
            $networktype1 = "bridge";
1142
            $networksource1 = "br$networkid1";
1143
        }
1144
        my $networktype2 = "user";
1145
        my $networksource2 = "default";
1146
        my $networkforward2 = "bridge";
1147
        my $networkisolated2 = "no";
1148
        $networksource2 = "vboxnet0" if ($hypervisor eq "vbox");
1149
        if ($networkid2 eq '0') {
1150
            $networktype2 = "user";
1151
            $networkforward2 = "nat";
1152
            $networkisolated2 = "yes"
1153
        } elsif ($networkid2 == 1) {
1154
            $networktype2 = "network" ;
1155
            $networkforward2 = "nat";
1156
            $networkisolated2 = "yes"
1157
        } elsif ($networkid2 > 1) {
1158
            $networktype2 = "bridge";
1159
            $networksource2 = "br$networkid2";
1160
        }
1161
        my $networktype3 = "user";
1162
        my $networksource3 = "default";
1163
        my $networkforward3 = "bridge";
1164
        my $networkisolated3 = "no";
1165
        $networksource3 = "vboxnet0" if ($hypervisor eq "vbox");
1166
        if ($networkid3 eq '0') {
1167
            $networktype3 = "user";
1168
            $networkforward3 = "nat";
1169
            $networkisolated3 = "yes"
1170
        } elsif ($networkid3 == 1) {
1171
            $networktype3 = "network" ;
1172
            $networkforward3 = "nat";
1173
            $networkisolated3 = "yes"
1174
        } elsif ($networkid3 > 1) {
1175
            $networktype3 = "bridge";
1176
            $networksource3 = "br$networkid3";
1177
        }
1178

    
1179
        my $xml = "<domain type='$hypervisor' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>\n";
1180
#        if ($vgpu && $vgpu ne "--") {
1181
#            $xml .= <<ENDXML2
1182
#  <qemu:commandline>
1183
#    <qemu:arg value='-device'/>
1184
#    <qemu:arg value='vfio-pci,host=01:00.0,x-vga=on'/>
1185
#    <qemu:arg value='-device'/>
1186
#    <qemu:arg value='vfio-pci,host=02:00.0,x-vga=on'/>
1187
#  </qemu:commandline>
1188
#ENDXML2
1189
#            ;
1190
#        }
1191

    
1192
#    <qemu:arg value='-set'/>
1193
#    <qemu:arg value='device.hostdev1.x-vga=on'/>
1194
#    <qemu:arg value='-cpu'/>
1195
#	<qemu:arg value='host,kvm=off'/>
1196
#    <qemu:arg value='-device'/>
1197
#	<qemu:arg value='pci-assign,host=01:00.0,id=hostdev0,configfd=20,bus=pci.0,addr=0x6,x-pci-vendor-id=0x10DE,x-pci-device-id=0x11BA,x-pci-sub-vendor-id=0x10DE,x-pci-sub-device-id=0x0965'/>
1198

    
1199
#  <cpu mode='host-model'>
1200
#    <vendor>Intel</vendor>
1201
#    <model>core2duo</model>
1202
#  </cpu>
1203

    
1204
#    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
1205
#    <nvram template='/usr/share/OVMF/OVMF_VARS.fd'/>
1206
        my $loader_xml = <<ENDXML
1207
    <bootmenu enable='yes' timeout='200'/>
1208
    <smbios mode='sysinfo'/>
1209
ENDXML
1210
        ;
1211
        if ($loader eq 'uefi') {
1212
            $loader_xml = <<ENDXML
1213
  <loader readonly='yes' secure='no' type='pflash'>/usr/share/ovmf/OVMF.fd</loader>
1214
  <nvram template='/usr/share/OVMF/OVMF_VARS.fd'>/tmp/guest_VARS.fd</nvram>
1215
ENDXML
1216
    ;
1217
        }
1218
        my $iotune_xml = <<ENDXML
1219
      <iotune>
1220
        <read_bytes_sec>$vm_readlimit</read_bytes_sec>
1221
        <write_bytes_sec>$vm_writelimit</write_bytes_sec>
1222
        <read_iops_sec>$vm_iopsreadlimit</read_iops_sec>
1223
        <write_iops_sec>$vm_iopswritelimit</write_iops_sec>
1224
      </iotune>
1225
ENDXML
1226
;
1227
        $iotune_xml = '' unless ($enforceiolimits);
1228

    
1229
        if ($vgpu && $vgpu ne "--") {
1230
            $xml .= <<ENDXML
1231
  <cpu mode='host-passthrough'>
1232
    <feature policy='disable' name='hypervisor'/>
1233
  </cpu>
1234
ENDXML
1235
;
1236
        } else {
1237
            $xml .= <<ENDXML
1238
  <cpu mode='host-model'>
1239
  </cpu>
1240
ENDXML
1241
            ;
1242
        }
1243
        $xml .=  <<ENDXML
1244
  <name>$uname</name>
1245
  <uuid>$uuid</uuid>
1246
  <memory>$mem</memory>
1247
  <vcpu>$vcpu</vcpu>
1248
  <os>
1249
    <type arch='x86_64' machine='pc'>hvm</type>
1250
    <boot dev='$boot'/>
1251
$loader_xml
1252
  </os>
1253
  <sysinfo type='smbios'>
1254
    <bios>
1255
      <entry name='vendor'>Origo</entry>
1256
    </bios>
1257
    <system>
1258
      <entry name='manufacturer'>Origo</entry>
1259
      <entry name='sku'>$networkid1ip</entry>
1260
    </system>
1261
  </sysinfo>
1262
  <features>
1263
ENDXML
1264
;
1265
        if ($vgpu && $vgpu ne "--") { $xml .= <<ENDXML
1266
    <kvm>
1267
      <hidden state='on'/>
1268
    </kvm>
1269
ENDXML
1270
;
1271
        }
1272
        $xml .= <<ENDXML
1273
    <pae/>
1274
    <acpi/>
1275
    <apic/>
1276
  </features>
1277
  <clock offset='localtime'>
1278
    <timer name='rtc' tickpolicy='catchup' track='guest'/>
1279
    <timer name='pit' tickpolicy='delay'/>
1280
    <timer name='hpet' present='no'/>
1281
  </clock>
1282
  <on_poweroff>destroy</on_poweroff>
1283
  <on_reboot>restart</on_reboot>½
1284
  <on_crash>restart</on_crash>
1285
  <devices>
1286
  <sound model='ich6'/>
1287
ENDXML
1288
;
1289
#        if ($vgpu && $vgpu ne "--") {
1290
#            $xml .= <<ENDXML2
1291
#  <hostdev mode='subsystem' type='pci' managed='yes'>
1292
#    <source>
1293
#      <address domain='0x0000' bus='0x01' slot='0x00' function='0x0' multifunction='on'/>
1294
#    </source>
1295
#  </hostdev>
1296
#  <hostdev mode='subsystem' type='pci' managed='yes'>
1297
#    <source>
1298
#      <address domain='0x0000' bus='0x02' slot='0x00' function='0x0' multifunction='on'/>
1299
#    </source>
1300
#  </hostdev>
1301
#ENDXML2
1302
#;
1303
#        }
1304
        if ($image && $image ne "" && $image ne "--") {
1305
						$xml .= <<ENDXML2
1306
    <disk type='file' device='disk'>
1307
      <source file='$image'/>$driver1
1308
      <target dev='$diskdev' bus='$diskbus'/>
1309
$iotune_xml
1310
    </disk>
1311
ENDXML2
1312
;
1313
        };
1314

    
1315
        if ($image2 && $image2 ne "" && $image2 ne "--") {
1316
						$xml .= <<ENDXML2
1317
    <disk type='file' device='disk'>$driver2
1318
      <source file='$image2'/>
1319
      <target dev='$diskdev2' bus='$diskbus'/>
1320
$iotune_xml
1321
    </disk>
1322
ENDXML2
1323
;
1324
        };
1325
        if ($image3 && $image3 ne "" && $image3 ne "--") {
1326
						$xml .= <<ENDXML2
1327
    <disk type='file' device='disk'>$driver3
1328
      <source file='$image3'/>
1329
      <target dev='$diskdev3' bus='$diskbus'/>
1330
$iotune_xml
1331
    </disk>
1332
ENDXML2
1333
;
1334
        };
1335
        if ($image4 && $image4 ne "" && $image4 ne "--") {
1336
						$xml .= <<ENDXML2
1337
    <disk type='file' device='disk'>$driver4
1338
      <source file='$image4'/>
1339
      <target dev='$diskdev4' bus='$diskbus'/>
1340
$iotune_xml
1341
    </disk>
1342
ENDXML2
1343
;
1344
        };
1345

    
1346
        unless ($image4 && $image4 ne '--' && $diskbus eq 'ide') {
1347
            if ($cdrom && $cdrom ne "" && $cdrom ne "--") {
1348
						$xml .= <<ENDXML3
1349
    <disk type='file' device='cdrom'>
1350
      <source file='$cdrom'/>
1351
      <target dev='hdd' bus='ide'/>
1352
      <readonly/>
1353
    </disk>
1354
ENDXML3
1355
;
1356
            } elsif ($hypervisor ne "vbox") {
1357
						$xml .= <<ENDXML3
1358
    <disk type='file' device='cdrom'>
1359
      <target dev='hdd' bus='ide'/>
1360
      <readonly/>
1361
    </disk>
1362
ENDXML3
1363
;
1364
            }
1365
        }
1366

    
1367
        $xml .= <<ENDXML4
1368
    <interface type='$networktype1'>
1369
      <source $networktype1='$networksource1'/>
1370
      <forward mode='$networkforward1'/>
1371
      <port isolated='$networkisolated1'/>
1372
      <model type='$nicmodel1'/>
1373
      <mac address='$nicmac1'/>
1374
    </interface>
1375
ENDXML4
1376
;
1377

    
1378
        if (($networkuuid2 && $networkuuid2 ne '--') || $networkuuid2 eq '0') {
1379
            $xml .= <<ENDXML5
1380
    <interface type='$networktype2'>
1381
      <source $networktype2='$networksource2'/>
1382
      <forward mode='$networkforward2'/>
1383
      <port isolated='$networkisolated2'/>
1384
      <model type='$nicmodel1'/>
1385
      <mac address='$nicmac2'/>
1386
    </interface>
1387
ENDXML5
1388
;
1389
        }
1390
        if (($networkuuid3 && $networkuuid3 ne '--') || $networkuuid3 eq '0') {
1391
            $xml .= <<ENDXML5
1392
    <interface type='$networktype3'>
1393
      <source $networktype3='$networksource3'/>
1394
      <forward mode='$networkforward3'/>
1395
      <port isolated='$networkisolated3'/>
1396
      <model type='$nicmodel1'/>
1397
      <mac address='$nicmac3'/>
1398
    </interface>
1399
ENDXML5
1400
;
1401
        }
1402
        $xml .= <<ENDXML6
1403
     <serial type='pty'>
1404
       <source path='/dev/pts/0'/>
1405
       <target port='0'/>
1406
     </serial>
1407
    <input type='tablet' bus='usb'/>
1408
    <graphics type='$graphics' port='$port'/>
1409
  </devices>
1410
</domain>
1411
ENDXML6
1412
;
1413

    
1414

    
1415
#    <graphics type='$graphics' port='$port' keymap='en-us'/>
1416
#     <console type='pty' tty='/dev/pts/0'>
1417
#       <source path='/dev/pts/0'/>
1418
#       <target port='0'/>
1419
#     </console>
1420
#     <graphics type='$graphics' port='-1' autoport='yes'/>
1421

    
1422
        $xmlreg{$uuid} = {
1423
            xml=>URI::Escape::uri_escape($xml)
1424
        };
1425

    
1426
        # Actually ask node to start domain
1427
        if ($targetmac) {
1428
            $register{$uuid}->{'mac'} = $targetmac;
1429
            $register{$uuid}->{'macname'} = $targetname;
1430
            $register{$uuid}->{'macip'} = $targetip;
1431

    
1432
            my $tasks = $nodereg{$targetmac}->{'tasks'};
1433
            $tasks .= "START $uuid $user\n";
1434
            $nodereg{$targetmac}->{'tasks'} = $tasks;
1435
            tied(%nodereg)->commit;
1436
            $uiuuid = $uuid;
1437
            $uidisplayip = $targetip;
1438
            $uidisplayport = $port;
1439
            $register{$uuid}->{'status'} = $uistatus;
1440
            $register{$uuid}->{'statustime'} = $current_time;
1441
            tied(%register)->commit;
1442

    
1443
            # Activate networks
1444
            require "$Stabile::basedir/cgi/networks.cgi";
1445
            Stabile::Networks::Activate($networkuuid1, 'activate');
1446
            Stabile::Networks::Activate($networkuuid2, 'activate') if ($networkuuid2 && $networkuuid2 ne '--');
1447
            Stabile::Networks::Activate($networkuuid3, 'activate') if ($networkuuid3 && $networkuuid3 ne '--');
1448

    
1449
            $main::syslogit->($user, "info", "Marked $name ($uuid) for ". $serv->{'status'} . " on $targetname ($targetmac)");
1450
            $postreply .= "Status=starting OK $uistatus ". $serv->{'name'} . "\n";
1451
        } else {
1452
            $main::syslogit->($user, "info", "Could not find $hypervisor target for creating $uuid ($image)");
1453
            $postreply .= "Status=ERROR problem $uistatus ". $serv->{'name'} . " (unable to locate target node)\n";
1454
        };
1455
    } else {
1456
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
1457
        $postreply .= "Status=ERROR problem $uistatus ". $serv->{'name'} . "\n";
1458
    }
1459
    #return ($uiuuid, $uidisplayip, $uidisplayport, $postreply, $targetmac);
1460
    return $postreply;
1461
}
1462

    
1463
sub do_attach {
1464
    my ($uuid, $action, $obj) = @_;
1465
    if ($help) {
1466
        return <<END
1467
GET:uuid,image:
1468
Attaches an image to a server as a disk device. Image must not be in use.
1469
END
1470
    }
1471
    my $dev = '';
1472
    my $imagenum = 0;
1473
    my $serv = $register{$uuid};
1474

    
1475
    if (!$serv->{'uuid'} || ($serv->{'status'} ne 'running' && $serv->{'status'} ne 'paused')) {
1476
        return "Status=Error Server must exist and be running\n";
1477
    }
1478
    my $macip = $serv->{macip};
1479
    my $image = $obj->{image} || $obj->{path};
1480
    if ($image && !($image =~ /^\//)) { # We have a uuid
1481
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Status=Error Unable to access images register\n"};
1482
        $image = $imagereg2{$image}->{'path'} if ($imagereg2{$image});
1483
        untie %imagereg2;
1484
    }
1485
    unless (tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$postreply .= "Status=Error Unable to access images register\n"; return $postreply;};
1486
    unless ($macip && $imagereg{$image} && $imagereg{$image}->{'user'} eq $user && $serv->{'user'} eq $user)  {$postreply .= "Status=Error Invalid image or server\n"; return $postreply;};
1487
    if ($imagereg{$image}->{'status'} ne 'unused') {return "Status=Error Image $image is already in use ($imagereg{$image}->{'status'})\n"};
1488

    
1489
    my $cmd = qq|$sshcmd $macip "LIBVIRT_DEFAULT_URI=qemu:///system virsh domblklist $uuid"|;
1490
    my $res = `$cmd`;
1491
    unless ($res =~ /vdb\s+.+/) {$dev = 'vdb'; $imagenum = 2};
1492
    unless ($dev || $res =~ /vdc\s+.+/)  {$dev = 'vdc'; $imagenum = 3};
1493
    unless ($dev || $res =~ /vdd\s+.+/)  {$dev = 'vdd'; $imagenum = 4};
1494
    if (!$dev) {
1495
        $postreply = "Status=Error No more images can be attached\n";
1496
    } else {
1497
        my $xml = <<END
1498
<disk type='file' device='disk'>
1499
  <driver type='qcow2' name='qemu' cache='default'/>
1500
  <source file='$image'/>
1501
  <target dev='$dev' bus='virtio'/>
1502
</disk>
1503
END
1504
;
1505
        $cmd = qq|$sshcmd $macip "echo \\"$xml\\" > /tmp/attach-device-$uuid.xml"|;
1506
        $res = `$cmd`;
1507
        $res .= `$sshcmd $macip LIBVIRT_DEFAULT_URI=qemu:///system virsh attach-device $uuid /tmp/attach-device-$uuid.xml`;
1508
        chomp $res;
1509
        if ($res =~ /successfully/) {
1510
            $postreply .= "Status=OK Attaching $image to $dev\n";
1511
            $imagereg{$image}->{'status'} = 'active';
1512
            $imagereg{$image}->{'domains'} = $uuid;
1513
            $imagereg{$image}->{'domainnames'} = $serv->{'name'};
1514
            $serv->{"image$imagenum"} = $image;
1515
            $serv->{"image$imagenum"."name"} = $imagereg{$image}->{'name'};
1516
            $serv->{"image$imagenum"."type"} = 'qcow2';
1517
        } else {
1518
            $postreply .= "Status=Error Unable to attach image $image to $dev ($res)\n";
1519
        }
1520
    }
1521
    untie %imagereg;
1522
    return $postreply;
1523
}
1524

    
1525
sub do_detach {
1526
    my ($uuid, $action, $obj) = @_;
1527
    if ($help) {
1528
        return <<END
1529
GET:uuid,image:
1530
Detaches a disk device and the associated image from a running server. All associated file-systems within the server should be unmounted before detaching, otherwise data loss i very probable. Use with care.
1531
END
1532
    }
1533
    my $dev = '';
1534
    my $serv = $register{$uuid};
1535

    
1536
    if (!$serv->{'uuid'} || ($serv->{'status'} ne 'running' && $serv->{'status'} ne 'paused')) {
1537
        return "Status=Error Server must exist and be running\n";
1538
    }
1539
    my $macip = $serv->{macip};
1540

    
1541
    my $image = $obj->{image} || $obj->{path} || $serv->{'image2'};
1542
    if ($image && !($image =~ /^\//)) { # We have a uuid
1543
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1544
        $image = $imagereg2{$image}->{'path'} if ($imagereg2{$image});
1545
        untie %imagereg2;
1546
    }
1547
    unless (tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$postreply .= "Status=Error Unable to access images register\n"; return $postreply;};
1548
    unless ($macip && $imagereg{$image} && $imagereg{$image}->{'user'} eq $user && $serv->{'user'} eq $user)  {$postreply .= "Status=Error Invalid image or server. Server must have a secondary image attached.\n"; return $postreply;};
1549

    
1550
    my $cmd = qq|$sshcmd $macip "LIBVIRT_DEFAULT_URI=qemu:///system virsh domblklist $uuid"|;
1551
    my $res = `$cmd`;
1552
    $dev = $1 if ($res =~ /(vd.)\s+.+$image/);
1553
    if (!$dev) {
1554
        $postreply =  qq|Status=Error Image $image, $cmd, is not currently attached\n|;
1555
    } elsif ($dev eq 'vda') {
1556
        $postreply = "Status=Error You cannot detach the primary image\n";
1557
    } else {
1558
        $res = `$sshcmd $macip LIBVIRT_DEFAULT_URI=qemu:///system virsh detach-disk $uuid $dev`;
1559
        chomp $res;
1560
        if ($res =~ /successfully/) {
1561
            $postreply .= "Status=OK Detaching image $image, $imagereg{$image}->{'uuid'} from $dev\n";
1562
            my $imagenum;
1563
            $imagenum = 2 if ($serv->{'image2'} eq $image);
1564
            $imagenum = 3 if ($serv->{'image3'} eq $image);
1565
            $imagenum = 4 if ($serv->{'image4'} eq $image);
1566
            $imagereg{$image}->{'status'} = 'unused';
1567
            $imagereg{$image}->{'domains'} = '';
1568
            $imagereg{$image}->{'domainnames'} = '';
1569
            if ($imagenum) {
1570
                $serv->{"image$imagenum"} = '';
1571
                $serv->{"image$imagenum"."name"} = '';
1572
                $serv->{"image$imagenum"."type"} = '';
1573
            }
1574
        } else {
1575
            $postreply .= "Status=Error Unable to attach image $image to $dev ($res)\n";
1576
        }
1577
    }
1578
    untie %imagereg;
1579
    return $postreply;
1580
}
1581

    
1582
sub Destroy {
1583
    my ($uuid, $action, $obj) = @_;
1584
    if ($help) {
1585
        return <<END
1586
GET:uuid,wait:
1587
Marks a server for halt, i.e. pull the plug if regular shutdown does not work or is not desired. Server and storage is preserved.
1588
END
1589
    }
1590
    my $uistatus = 'destroying';
1591
    my $name = $register{$uuid}->{'name'};
1592
    my $mac = $register{$uuid}->{'mac'};
1593
    my $macname = $register{$uuid}->{'macname'};
1594
    my $dbstatus = $register{$uuid}->{'status'};
1595
    my $wait = $obj->{'wait'};
1596
    if ($dbstatus eq 'running' or $dbstatus eq 'paused'
1597
        or $dbstatus eq 'shuttingdown' or $dbstatus eq 'starting'
1598
        or $dbstatus eq 'destroying' or $dbstatus eq 'upgrading'
1599
        or $dbstatus eq 'suspending' or $dbstatus eq 'resuming') {
1600
        if ($wait) {
1601
            my $username = $register{$uuid}->{'user'} || $user;
1602
            $username = $user unless ($isadmin);
1603
            $postreply = destroyUserServers($username, 1, $uuid);
1604
        } else {
1605
            my $node = $nodereg{$mac};
1606
            my $tasks = $node->{'tasks'};
1607
            $node->{'tasks'} = $tasks . "DESTROY $uuid $user\n";
1608
            tied(%nodereg)->commit;
1609
            $register{$uuid}->{'status'} = $uistatus;
1610
            $register{$uuid}->{'statustime'} = $current_time;
1611
            $uiuuid = $uuid;
1612
            $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus on $macname ($mac)");
1613
            $postreply .= "Status=destroying $uistatus ". $register{$uuid}->{'name'} . "\n";
1614
        }
1615
    } else {
1616
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $name ($uuid)");
1617
        $postreply .= "Status=ERROR problem $uistatus $name\n";
1618
    }
1619
    return $postreply;
1620
}
1621

    
1622
sub getHypervisor {
1623
	my $image = shift;
1624
	# Produce a mapping of image file suffixes to hypervisors
1625
	my %idreg;
1626
    unless ( tie(%idreg,'Tie::DBI', Hash::Merge::merge({table=>'nodeidentities', key=>'identity'}, $Stabile::dbopts)) ) {return "Unable to access nodeidentities register"};
1627
    my @idvalues = values %idreg;
1628
	my %formats;
1629
	foreach my $val (@idvalues) {
1630
		my %h = %$val;
1631
		foreach (split(/,/,$h{'formats'})) {
1632
			$formats{lc $_} = $h{'hypervisor'}
1633
		}
1634
	}
1635
	untie %idreg;
1636

    
1637
	# and then determine the hypervisor in question
1638
	my $hypervisor = "vbox";
1639
	my ($pathname, $path, $suffix) = fileparse($image, '\.[^\.]*');
1640
	$suffix = substr $suffix, 1;
1641
	my $hypervisor = $formats{lc $suffix};
1642
	return $hypervisor;
1643
}
1644

    
1645
sub nicmac1ToUuid {
1646
    my $nicmac1 = shift;
1647
    my $uuid;
1648
    return $uuid unless $nicmac1;
1649
    my @regkeys = (tied %register)->select_where("user = '$user' AND nicmac1 = '$nicmac1");
1650
	foreach my $k (@regkeys) {
1651
	    my $val = $register{$k};
1652
		my %h = %$val;
1653
		if (lc $h{'nicmac1'} eq lc $nicmac1 && $user eq $h{'user'}) {
1654
    		$uuid =  $h{'uuid'};
1655
    		last;
1656
		}
1657
	}
1658
	return $uuid;
1659
}
1660

    
1661
sub randomMac {
1662
	my ( %vendor, $lladdr, $i );
1663
#	$lladdr = '00';
1664
	$lladdr = '52:54:00';# KVM vendor string
1665
	while ( ++$i )
1666
#	{ last if $i > 10;
1667
	{ last if $i > 6;
1668
		$lladdr .= ':' if $i % 2;
1669
		$lladdr .= sprintf "%" . ( qw (X x) [int ( rand ( 2 ) ) ] ), int ( rand ( 16 ) );
1670
	}
1671
	return $lladdr;
1672
}
1673

    
1674
sub overQuotas {
1675
    my $meminc = shift;
1676
    my $vcpuinc = shift;
1677
	my $usedmemory = 0;
1678
	my $usedvcpus = 0;
1679
	my $overquota = 0;
1680
    return $overquota if ($isadmin || $Stabile::userprivileges =~ /a/); # Don't enforce quotas for admins
1681

    
1682
	my $memoryquota = $Stabile::usermemoryquota;
1683
	my $vcpuquota = $Stabile::uservcpuquota;
1684

    
1685
	if (!$memoryquota || !$vcpuquota) { # 0 or empty quota means use defaults
1686
        $memoryquota = $memoryquota || $Stabile::config->get('MEMORY_QUOTA');
1687
        $vcpuquota = $vcpuquota || $Stabile::config->get('VCPU_QUOTA');
1688
    }
1689

    
1690
    my @regkeys = (tied %register)->select_where("user = '$user'");
1691
	foreach my $k (@regkeys) {
1692
	    my $val = $register{$k};
1693
		if ($val->{'user'} eq $user && $val->{'status'} ne "shutoff" &&
1694
		    $val->{'status'} ne "inactive" && $val->{'status'} ne "shutdown" ) {
1695

    
1696
		    $usedmemory += $val->{'memory'};
1697
		    $usedvcpus += $val->{'vcpu'};
1698
		}
1699
	}
1700
	$overquota = $usedmemory+$meminc if ($memoryquota!=-1 && $usedmemory+$meminc > $memoryquota); # -1 means no quota
1701
	$overquota = $usedvcpus+$vcpuinc if ($vcpuquota!=-1 && $usedvcpus+$vcpuinc > $vcpuquota);
1702
	return $overquota;
1703
}
1704

    
1705
sub validateItem {
1706
    unless (%imagereg) {
1707
        unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1708
    }
1709
    my $valref = shift;
1710
    my $img = $imagereg{$valref->{'image'}};
1711
    my $imagename = $img->{'name'};
1712
    $valref->{'imagename'} = $imagename if ($imagename);
1713
    my $imagetype = $img->{'type'};
1714
    $valref->{'imagetype'} = $imagetype if ($imagetype);
1715

    
1716
    # imagex may be registered by uuid instead of path - find the path
1717
    # We now support up to 4 images
1718
    for (my $i=2; $i<=4; $i++) {
1719
        if ($valref->{"image$i"} && $valref->{"image$i"} ne '--' && !($valref->{"image$i"} =~ /^\//)) {
1720
            unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1721
            $valref->{"image$i"} = $imagereg2{$valref->{"image$i"}}->{'path'};
1722
            untie %imagereg2;
1723
        }
1724

    
1725
        my $imgi = $imagereg{$valref->{"image$i"}};
1726
        $valref->{"image$i" . 'name'} = $imgi->{'name'} || $valref->{"image$i" . 'name'};
1727
        $valref->{"image$i" . 'type'} = $imgi->{'type'} || $valref->{"image$i" . 'type'};
1728
    }
1729

    
1730
    my $net1 = $networkreg{$valref->{'networkuuid1'}};
1731
    my $networkname1 = $net1->{'name'};
1732
    $valref->{'networkname1'} = $networkname1 if ($networkname1);
1733
    my $net2 = $networkreg{$valref->{'networkuuid2'}};
1734
    my $networkname2 = $net2->{'name'};
1735
    $valref->{'networkname2'} = $networkname2 if ($networkname2);
1736
    my $name = $valref->{'name'};
1737
    $valref->{'name'} = $imagename unless $name;
1738

    
1739
    # Make sure we start shutoff servers on the node their image is on
1740
    if ($valref->{'status'} eq "shutoff" || $valref->{'status'} eq "inactive") {
1741
        my $node = $nodereg{$valref->{'mac'}};
1742
        if ($valref->{'image'} =~ /\/mnt\/stabile\/node\//) {
1743
            $valref->{'mac'} = $img->{'mac'};
1744
            $valref->{'macname'} = $node->{'name'};
1745
            $valref->{'macip'} = $node->{'ip'};
1746
        } elsif ($valref->{'image2'} =~ /\/mnt\/stabile\/node\//) {
1747
            $valref->{'mac'} = $imagereg{$valref->{'image2'}}->{'mac'};
1748
            $valref->{'macname'} = $node->{'name'};
1749
            $valref->{'macip'} = $node->{'ip'};
1750
        } elsif ($valref->{'image3'} =~ /\/mnt\/stabile\/node\//) {
1751
            $valref->{'mac'} = $imagereg{$valref->{'image3'}}->{'mac'};
1752
            $valref->{'macname'} = $node->{'name'};
1753
            $valref->{'macip'} = $node->{'ip'};
1754
        } elsif ($valref->{'image4'} =~ /\/mnt\/stabile\/node\//) {
1755
            $valref->{'mac'} = $imagereg{$valref->{'image4'}}->{'mac'};
1756
            $valref->{'macname'} = $node->{'name'};
1757
            $valref->{'macip'} = $node->{'ip'};
1758
        }
1759
    }
1760
# Mark domains we have heard from in the last 20 secs as inactive
1761
    my $dbtimestamp = 0;
1762
    $dbtimestamp = $register{$valref->{'uuid'}}->{'timestamp'} if ($register{$valref->{'uuid'}});
1763
    my $timediff = $current_time - $dbtimestamp;
1764
    if ($timediff >= 20) {
1765
        if  (! ($valref->{'status'} eq "shutoff"
1766
                || $valref->{'status'} eq "starting"
1767
            #    || $valref->{'status'} eq "shuttingdown"
1768
            #    || $valref->{'status'} eq "destroying"
1769
                || ($valref->{'status'} =~ /moving/ && $timediff<40)
1770
            )) { # Move has probably failed
1771
            $valref->{'status'} = "inactive";
1772
            $imagereg{$valref->{'image'}}->{'status'} = "used" if ($valref->{'image'} && $imagereg{$valref->{'image'}});
1773
            $imagereg{$valref->{'image2'}}->{'status'} = "used" if ($valref->{'image2'} && $imagereg{$valref->{'image2'}});
1774
            $imagereg{$valref->{'image3'}}->{'status'} = "used" if ($valref->{'image3'} && $imagereg{$valref->{'image3'}});
1775
            $imagereg{$valref->{'image4'}}->{'status'} = "used" if ($valref->{'image4'} && $imagereg{$valref->{'image4'}});
1776
        }
1777
    };
1778
#    untie %imagereg;
1779
    return $valref;
1780
}
1781

    
1782
# Run through all domains and mark domains we have heard from in the last 20 secs as inactive
1783
sub updateRegister {
1784
    unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Unable to access user register"};
1785
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1786

    
1787
    my @regkeys = (tied %register)->select_where("user = '$user'");
1788

    
1789
    foreach my $k (@regkeys) {
1790
        my $valref = $register{$k};
1791
        next unless ($userreg{$valref->{'user'}});
1792
        my $dbtimestamp = $valref->{'timestamp'};
1793
        my $dbstatus = $valref->{'status'};
1794
        my $timediff = $current_time - $dbtimestamp;
1795
        my $imgstatus;
1796
        my $domstatus;
1797
        if ($timediff >= 20) {
1798
            if  ( $valref->{'status'} eq "shutoff" ) {
1799
                $imgstatus = 'used';
1800
            } elsif ((  $valref->{'status'} eq "starting"
1801
                            || $valref->{'status'} eq "shuttingdown"
1802
                        ) && $timediff>50) {
1803
                $imgstatus = 'used';
1804
                $domstatus = 'inactive';
1805
            } elsif ($valref->{'status'} eq "destroying" || $valref->{'status'} eq "moving") {
1806
                ;
1807
            } else {
1808
                $domstatus = 'inactive';
1809
                $imgstatus = 'used';
1810
            }
1811
            $valref->{'status'} = $domstatus if ($domstatus);
1812
            my $image = $valref->{'image'};
1813
            my $image2 = $valref->{'image2'};
1814
            my $image3 = $valref->{'image3'};
1815
            my $image4 = $valref->{'image4'};
1816
            $imagereg{$image}->{'status'} = $imgstatus if ($imgstatus);
1817
            $imagereg{$image2}->{'status'} = $imgstatus if ($image2 && $imgstatus);
1818
            $imagereg{$image3}->{'status'} = $imgstatus if ($image3 && $imgstatus);
1819
            $imagereg{$image4}->{'status'} = $imgstatus if ($image4 && $imgstatus);
1820
            if ($domstatus eq 'inactive ' && $dbstatus ne 'inactive') {
1821
                $main::updateUI->({ tab=>'servers',
1822
                                    user=>$valref->{'user'},
1823
                                    uuid=>$valref->{'uuid'},
1824
                                    sender=>'updateRegister',
1825
                                    status=>'inactive'})
1826
            }
1827
        };
1828

    
1829
    }
1830
    untie %userreg;
1831
    untie %imagereg;
1832
}
1833

    
1834

    
1835
sub locateTargetNode {
1836
    my ($uuid, $dmac, $mem, $vcpu, $image, $image2, $image3, $image4, $hypervisor, $smac, $stormove)= @_;
1837
    my $targetname;
1838
    my $targetip;
1839
    my $port;
1840
    my $targetnode;
1841
    my $targetindex; # Availability index of located target node
1842
    my %avhash;
1843

    
1844
    $dmac = '' unless ($isadmin); # Only allow admins to select specific node
1845
    my $mnode = $register{$uuid};
1846
    if (!$dmac
1847
            && $mnode->{'locktonode'} eq 'true'
1848
            && $mnode->{'mac'}
1849
            && $mnode->{'mac'} ne '--'
1850
            ) {
1851
        $dmac = $mnode->{'mac'}; # Server is locked to specific node
1852
    }
1853
    if ($dmac && !$nodereg{$dmac}) {
1854
        $main::syslogit->($user, "info", "The target node $dmac no longer exists, starting $uuid on another node if possible");
1855
        $dmac = '';
1856
    }
1857
    my $imageonnode = ((!$stormove) && ($image =~ /\/mnt\/stabile\/node\//
1858
                                          || $image2 =~ /\/mnt\/stabile\/node\//
1859
                                          || $image3 =~ /\/mnt\/stabile\/node\//
1860
                                          || $image4 =~ /\/mnt\/stabile\/node\//
1861
                                          ));
1862

    
1863
    foreach $node (values %nodereg) {
1864
        my $nstatus = $node->{'status'};
1865
        my $maintenance = $node->{'maintenance'};
1866
        my $nmac = $node->{'mac'};
1867

    
1868
        if (($nstatus eq 'running' || $nstatus eq 'asleep' || $nstatus eq 'maintenance' || $nstatus eq 'waking')
1869
         && $smac ne $nmac
1870
         && (( ($node->{'memfree'} > $mem+512*1024)
1871
         && (($node->{'vmvcpus'} + $vcpu) <= ($cpuovercommision * $node->{'cpucores'} * $node->{'cpucount'})) ) || $action eq 'listnodeavailability')
1872
        ) {
1873
        # Determine how available this node is
1874
        # Available memory
1875
            my $memweight = 0.2; # memory weighing factor
1876
            my $memindex = $avhash{$nmac}->{'memindex'} = int(100* $memweight* $node->{'memfree'} / (1024*1024) )/100;
1877
        # Free cores
1878
            my $cpuindex = $avhash{$nmac}->{'cpuindex'} = int(100*($cpuovercommision * $node->{'cpucores'} * $node->{'cpucount'} - $node->{'vmvcpus'} - $node->{'reservedvcpus'}))/100;
1879
        # Asleep - not asleep gives a +3
1880
            my $sleepindex = $avhash{$nmac}->{'sleepindex'} = ($node->{'status'} eq 'asleep' || $node->{'status'} eq 'waking')?'0':'3';
1881
            $avhash{$nmac}->{'vmvcpus'} = $node->{'vmvcpus'};
1882
#            $avhash{$nmac}->{'cpucommision'} = $cpuovercommision * $node->{'cpucores'} * $node->{'cpucount'};
1883
#            $avhash{$nmac}->{'cpureservation'} = $node->{'vmvcpus'} + $node->{'reservedvcpus'};
1884
            $avhash{$nmac}->{'name'} = $node->{'name'};
1885
            $avhash{$nmac}->{'mac'} = $node->{'mac'};
1886

    
1887
            my $aindex = $memindex + $cpuindex + $sleepindex;
1888
        # Don't use nodes that are out of memory of cores
1889
            $aindex = 0 if ($memindex <= 0 || $cpuindex <= 0);
1890
            $avhash{$nmac}->{'index'} = $aindex;
1891
            $avhash{$nmac}->{'storfree'} = $node->{'storfree'};
1892
            $avhash{$nmac}->{'memfree'} = $node->{'memfree'};
1893
            $avhash{$nmac}->{'ip'} = $node->{'ip'};
1894
            $avhash{$nmac}->{'identity'} = $node->{'identity'};
1895
            $avhash{$nmac}->{'status'} = $node->{'status'};
1896
            $avhash{$nmac}->{'maintenance'} = $maintenance;
1897
            $avhash{$nmac}->{'reservedvcpus'} = $node->{'reservedvcpus'};
1898
            my $nodeidentity = $node->{'identity'};
1899
            $nodeidentity = 'kvm' if ($nodeidentity eq 'local_kvm');
1900
            if ($hypervisor eq $nodeidentity) {
1901
                # If image is on node, we must start on same node - registered when moving image
1902
                if ($imageonnode) {
1903
                    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1904
                    $dmac = $imagereg{$image}->{'mac'};
1905
                    $dmac = $imagereg{$image2}->{'mac'} unless ($dmac);
1906
                    $dmac = $imagereg{$image3}->{'mac'} unless ($dmac);
1907
                    $dmac = $imagereg{$image4}->{'mac'} unless ($dmac);
1908
                    untie %imagereg;
1909
                    if (!$dmac) {
1910
                        $postreply .= "Status=ERROR Image node not found\n";
1911
                        last;
1912
                    }
1913
                }
1914
                $dmac = "" if ($dmac eq "--");
1915
            # If a specific node is asked for, match mac addresses
1916
                if ($dmac eq $nmac) {
1917
                    $targetnode = $node;
1918
                    last;
1919
                } elsif (!$dmac && $nstatus ne "maintenance" && !$maintenance) {
1920
            # pack or disperse
1921
                    if (!$targetindex) {
1922
                        $targetindex = $aindex;
1923
                        $targetnode = $node;
1924
                    } elsif ($dpolicy eq 'pack') {
1925
                        if ($aindex < $targetindex) {
1926
                            $targetnode = $node;
1927
                            $targetindex = $aindex;
1928
                        }
1929
                    } else {
1930
                        if ($aindex > $targetindex) {
1931
                            $targetnode = $node;
1932
                            $targetindex = $aindex;
1933
                        }
1934
                    }
1935
                }
1936
            }
1937
        }
1938
    }
1939
    if ($targetnode && $uuid) {
1940
        if ($targetnode->{'status'} eq 'asleep') {
1941
            my $nmac = $targetnode->{'mac'};
1942
            my $realmac = substr($nmac,0,2).":".substr($nmac,2,2).":".substr($nmac,4,2).":".substr($nmac,6,2).":".substr($nmac,8,2).":".substr($nmac,10,2);
1943
            my $nlogmsg = "Node $nmac marked for wake ";
1944
            if ($brutalsleep && (
1945
                    ($targetnode->{'amtip'} && $targetnode->{'amtip'} ne '--')
1946
                || ($targetnode->{'ipmiip'} && $targetnode->{'ipmiip'} ne '--')
1947
                )) {
1948
                my $wakecmd;
1949
                if ($targetnode->{'amtip'} && $targetnode->{'amtip'} ne '--') {
1950
                    $wakecmd = "echo 'y' | AMT_PASSWORD='$amtpasswd' /usr/bin/amttool $targetnode->{'amtip'} powerup pxe";
1951
                } else {
1952
                    $wakecmd = "ipmitool -I lanplus -H $targetnode->{'ipmiip'} -U ADMIN -P ADMIN power on";
1953
                }
1954
                $nlogmsg .= `$wakecmd`;
1955
            } else {
1956
                my $broadcastip = $targetnode->{'ip'};
1957
                $broadcastip =~ s/\.\d{1,3}$/.255/;
1958
                $nlogmsg .= 'on lan ' . `/usr/bin/wakeonlan -i $broadcastip $realmac`;
1959
            }
1960
            $targetnode->{'status'} = "waking";
1961
            $nlogmsg =~ s/\n/ /g;
1962
            $main::syslogit->($user, "info", $nlogmsg);
1963
            $postreply .= "Status=OK waking $targetnode->{'name'}\n";
1964
        }
1965
        $targetname = $targetnode->{'name'};
1966
        $targetmac = $targetnode->{'mac'};
1967
        $targetip = $targetnode->{'ip'};
1968
        $targetip = $targetnode->{'ip'};
1969
        my $porttaken = 1;
1970
        while ($porttaken) {
1971
            $porttaken = 0;
1972
            $port = $targetnode->{'vms'} + (($hypervisor eq "vbox")?3389:5900);
1973
            $port += int(rand(200));
1974
            my @regkeys = (tied %register)->select_where("port = '$port' AND macip = '$targetip'");
1975
            foreach my $k (@regkeys) {
1976
                $r = $register{$k};
1977
                if ($r->{'port'} eq $port && $r->{'macip'} eq $targetip) {
1978
                    $porttaken = 1;
1979
                }
1980
            }
1981
        }
1982
        $targetnode->{'vms'}++;
1983
        $targetnode->{'vmvcpus'} += $vcpu;
1984
        $register{$uuid}->{'port'} = $port;
1985
#        $register{$uuid}->{'mac'} = $targetmac;
1986
#        $register{$uuid}->{'macname'} = $targetname;
1987
#        $register{$uuid}->{'macip'} = $targetip;
1988
        $register{$uuid}->{'display'} = (($hypervisor eq "vbox")?'rdp':'vnc');
1989
    } else {
1990
        my $macstatus;
1991
        $macstatus = $nodereg{$dmac}->{status} if ($nodereg{$dmac});
1992
        $main::syslogit->($user, "info", "Could not find target for $uuid, $dmac, $imageonnode, $mem, $vcpu, $image, $image2,$image3,$image4, $hypervisor, $smac, dmac-status: $macstatus") if ($uuid);
1993
    }
1994
    return ($targetmac, $targetname, $targetip, $port, \%avhash);
1995
}
1996

    
1997
sub destroyUserServers {
1998
    my $username = shift;
1999
    my $wait = shift; # Should we wait for servers do die
2000
    my $duuid = shift;
2001
    return unless ($username && ($isadmin || $user eq $username));
2002
    my @updateList;
2003

    
2004
    my @regkeys = (tied %register)->select_where("user = '$username'");
2005
    foreach my $uuid (@regkeys) {
2006
        if ($register{$uuid}->{'user'} eq $username
2007
            && $register{$uuid}->{'status'} ne 'shutoff'
2008
            && (!$duuid || $duuid eq $uuid)
2009
        ) {
2010
            $postreply .= "Destroying $username server $register{$uuid}->{'name'}, $uuid\n";
2011
            Destroy($uuid);
2012
            push (@updateList,{ tab=>'servers',
2013
                                user=>$user,
2014
                                uuid=>$duuid,
2015
                                status=>'destroying'});
2016
        }
2017
    }
2018
    $main::updateUI->(@updateList) if (@updateList);
2019
    if ($wait) {
2020
        my @regkeys = (tied %register)->select_where("user = '$username'");
2021
        my $activeservers = 1;
2022
        my $i = 0;
2023
        while ($activeservers && $i<30) {
2024
            $activeservers = 0;
2025
            foreach my $k (@regkeys) {
2026
                my $valref = $register{$k};
2027
                if ($username eq $valref->{'user'}
2028
                    && ($valref->{'status'} ne 'shutoff'
2029
                    && $valref->{'status'} ne 'inactive')
2030
                    && (!$duuid || $duuid eq $valref->{'uuid'})
2031
                ) {
2032
                    $activeservers = $valref->{'uuid'};
2033
                }
2034
            }
2035
            $i++;
2036
            if ($activeservers) {
2037
                my $res .= "Status=OK Waiting $i for server $register{$activeservers}->{'name'}, $register{$activeservers}->{'status'} to die...\n";
2038
            #    print $res if ($console);
2039
                $postreply .= $res;
2040
                sleep 2;
2041
            }
2042
        }
2043
        $postreply .= "Status=OK Servers halted for $username\n" unless ($activeservers);
2044
    }
2045
    return $postreply;
2046
}
2047

    
2048
sub removeUserServers {
2049
    my $username = shift;
2050
    my $uuid = shift;
2051
    my $destroy = shift; # Should running servers be destroyed before removing
2052
    return unless (($isadmin || $user eq $username) && !$isreadonly);
2053
    $user = $username;
2054
    my @regkeys = (tied %register)->select_where("user = '$username'");
2055
    foreach my $ruuid (@regkeys) {
2056
        next if ($uuid && $ruuid ne $uuid);
2057
        if ($destroy && $register{$ruuid}->{'user'} eq $username && ($register{$ruuid}->{'status'} ne 'shutoff' && $register{$ruuid}->{'status'} ne 'inactive')) {
2058
            destroyUserServers($username, 1, $ruuid);
2059
        }
2060

    
2061
        if ($register{$ruuid}->{'user'} eq $username && ($register{$ruuid}->{'status'} eq 'shutoff' || $register{$ruuid}->{'status'} eq 'inactive')) {
2062
            $postreply .= "Removing $username server $register{$ruuid}->{'name'}, $ruuid" . ($console?'':'<br>') . "\n";
2063
            Remove($ruuid);
2064
        }
2065
    }
2066
}
2067

    
2068
sub Remove {
2069
    my ($uuid, $action) = @_;
2070
    if ($help) {
2071
        return <<END
2072
DELETE:uuid:
2073
Removes a server. Server must be shutoff. Does not remove associated images or networks.
2074
END
2075
    }
2076
    my $reguser = $register{$uuid}->{'user'};
2077
    my $dbstatus = $register{$uuid}->{'status'};
2078
    my $image = $register{$uuid}->{'image'};
2079
    my $image2 = $register{$uuid}->{'image2'};
2080
    my $image3 = $register{$uuid}->{'image3'};
2081
    my $image4 = $register{$uuid}->{'image4'};
2082
    my $name = $register{$uuid}->{'name'};
2083
    $image2 = '' if ($image2 eq '--');
2084
    $image3 = '' if ($image3 eq '--');
2085
    $image4 = '' if ($image4 eq '--');
2086

    
2087
    if ($reguser ne $user) {
2088
        $postreply .= "Status=ERROR You cannot delete a vm you don't own\n";
2089
    } elsif ($dbstatus eq 'inactive' || $dbstatus eq 'shutdown' || $dbstatus eq 'shutoff') {
2090

    
2091
        # Delete software packages and monitors from register
2092
        $postmsg .= deletePackages($uuid);
2093
        my $sname = $register{$uuid}->{'name'};
2094
        utf8::decode($sname);
2095
        $postmsg .= deleteMonitors($uuid)?" deleted monitors for $sname ":'';
2096

    
2097
        delete $register{$uuid};
2098
        delete $xmlreg{$uuid};
2099

    
2100
        unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
2101
        $imagereg{$image}->{'status'} = "unused" if ($imagereg{$image});
2102
        $imagereg{$image2}->{'status'} = "unused" if ($image2 && $imagereg{$image2});
2103
        $imagereg{$image3}->{'status'} = "unused" if ($image3 && $imagereg{$image3});
2104
        $imagereg{$image4}->{'status'} = "unused" if ($image4 && $imagereg{$image4});
2105
        untie %imagereg;
2106

    
2107
        # Delete metrics
2108
        my $metricsdir = "/var/lib/graphite/whisper/domains/$uuid";
2109
        `rm -r $metricsdir` if (-e $metricsdir);
2110
        my $rrdfile = "/var/cache/rrdtool/".$uuid."_highres.rrd";
2111
        `rm $rrdfile` if (-e $rrdfile);
2112

    
2113
        $main::syslogit->($user, "info", "Deleted domain $uuid from db");
2114
        utf8::decode($name);
2115
        $postmsg .= " deleted server $name";
2116
        $postreply = "[]";
2117
        sleep 1;
2118
    } else {
2119
        $postreply .= "Status=ERROR Cannot delete a $dbstatus server\n";
2120
    }
2121
    return $postreply;
2122
}
2123

    
2124
# Delete all monitors belonging to a server
2125
sub deleteMonitors {
2126
    my ($serveruuid) = @_;
2127
    my $match;
2128
    if ($serveruuid) {
2129
        if ($register{$serveruuid}->{'user'} eq $user || $isadmin) {
2130
            local($^I, @ARGV) = ('.bak', "/etc/mon/mon.cf");
2131
            # undef $/; # This makes <> read in the entire file in one go
2132
            my $uuidmatch;
2133
            while (<>) {
2134
                if (/^watch (\S+)/) {
2135
                    if ($1 eq $serveruuid) {$uuidmatch = $serveruuid}
2136
                    else {$uuidmatch = ''};
2137
                };
2138
                if ($uuidmatch) {
2139
                    $match = 1;
2140
                } else {
2141
                    #chomp;
2142
                    print unless (/^hostgroup $serveruuid/);
2143
                }
2144
                close ARGV if eof;
2145
            }
2146
            #$/ = "\n";
2147
        }
2148
        unlink glob "/var/log/stabile/*:$serveruuid:*";
2149
    }
2150
    `/usr/bin/moncmd reset keepstate` if ($match);
2151
    return $match;
2152
}
2153

    
2154
sub deletePackages {
2155
    my ($uuid, $issystem, %packreg) = @_;
2156
    unless ( tie(%packreg,'Tie::DBI', Hash::Merge::merge({table=>'packages', key=>'id'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
2157

    
2158
    my @domains;
2159
    if ($issystem) {
2160
        foreach my $valref (values %register) {
2161
            if (($valref->{'system'} eq $uuid || $uuid eq '*')
2162
                    && ($valref->{'user'} eq $user || $fulllist)) {
2163
                push(@domains, $valref->{'uuid'});
2164
            }
2165
        }
2166
    } else { # Allow if domain no longer exists or belongs to user
2167
        push(@domains, $uuid) if (!$register{$uuid} || $register{$uuid}->{'user'} eq $user || $fulllist);
2168
    }
2169

    
2170
    foreach my $domuuid (@domains) {
2171
        foreach my $packref (values %packreg) {
2172
            my $id = $packref->{'id'};
2173
            if (substr($id, 0,36) eq $domuuid || ($uuid eq '*' && $packref->{'user'} eq $user)) {
2174
                delete $packreg{$id};
2175
            }
2176
        }
2177
    }
2178
    tied(%packreg)->commit;# if (%packreg);
2179
    if ($issystem) {
2180
        my $sname = $register{$uuid}->{'name'};
2181
        utf8::decode($sname);
2182
        return "Status=OK Cleared packages for $sname\n";
2183
    } elsif ($register{$uuid}) {
2184
        my $sname = $register{$uuid}->{'name'};
2185
        utf8::decode($sname);
2186
        return "Status=OK Cleared packages for $sname\n";
2187
    } else {
2188
        return "Status=OK Cleared packages. System not registered\n";
2189
    }
2190
}
2191

    
2192
sub Save {
2193
    my ($uuid, $action, $obj) = @_;
2194
    if ($help) {
2195
        return <<END
2196
POST:uuid, name, user, system, autostart, locktonode, mac, memory, vcpu, boot, loader, diskbus, nicmodel1, vgpu, cdrom, image, image2, image3, image4, networkuuid2, networkuuid3, networkuuid1, nicmac1, nicmac2, nicmac3:
2197
To save a servers of networks you either PUT or POST a JSON array to the main endpoint with objects representing the servers with the changes you want.
2198
Depending on your privileges not all changes are permitted. If you save without specifying a uuid, a new server is created.
2199
If you pass [user] parameter it is assumed you want to move server to this user's account.
2200
Supported parameters:
2201

    
2202
uuid: UUID
2203
name: string
2204
user: string
2205
system: UUID of stack this server belongs to
2206
autostart: true|false
2207
locktonode: true|false
2208
mac: MAC address of target node
2209

    
2210
memory: int bytes
2211
vcpu: int
2212
boot: hd|cdrom|network
2213
loader: bios|uefi
2214
diskbus: virtio|ide|scsi
2215
nicmodel1: virtio|rtl8139|ne2k_pci|e1000|i82551|i82557b|i82559er|pcnet
2216
vgpu: int
2217

    
2218
cdrom: string path
2219
image: string path
2220
image2: string path
2221
image3: string path
2222
image4: string path
2223

    
2224
networkuuid1: UUID of network connection
2225
networkuuid2: UUID of network connection
2226
networkuuid3: UUID of network connection
2227

    
2228
END
2229
    }
2230

    
2231
# notes, opemail, opfullname, opphone, email, fullname, phone, services, recovery, alertemail
2232
# notes: string
2233
# opemail: string
2234
# opfullname: string
2235
# opphone: string
2236
# email: string
2237
# fullname: string
2238
# phone: string
2239
# services: string
2240
# recovery: string
2241
# alertemail: string
2242

    
2243
    my $system = $obj->{system};
2244
    my $newsystem = $obj->{newsystem};
2245
    my $buildsystem = $obj->{buildsystem};
2246
    my $nicmac1 = $obj->{nicmac1};
2247
    $console = $console || $obj->{console};
2248

    
2249
    $postmsg = '' if ($buildsystem);
2250
    if (!$uuid && $nicmac1) {
2251
        $uuid = nicmac1ToUuid($nicmac1); # If no uuid try to locate based on mac
2252
    }
2253
    if (!$uuid && $uripath =~ /servers(\.cgi)?\/(.+)/) { # Try to parse uuid out of URI
2254
        my $huuid = $2;
2255
        if ($ug->to_string($ug->from_string($huuid)) eq $huuid) { # Check for valid uuid
2256
            $uuid = $huuid;
2257
        }
2258
    }
2259
    my $regserv = $register{$uuid};
2260
    my $status = $regserv->{'status'} || 'new';
2261
    if ((!$uuid) && $status eq 'new') {
2262
        my $ug = new Data::UUID;
2263
        $uuid = $ug->create_str();
2264
    };
2265
    unless ($uuid && length $uuid == 36){
2266
        $postmsg = "Status=Error No valid uuid ($uuid), $obj->{image}";
2267
        return $postmsg;
2268
    }
2269
    $nicmac1 = $nicmac1 || $regserv->{'nicmac1'};
2270
    my $name = $obj->{name} || $regserv->{'name'};
2271
    my $memory = $obj->{memory} || $regserv->{'memory'};
2272
    my $vcpu = $obj->{vcpu} || $regserv->{'vcpu'};
2273
    my $image = $obj->{image} || $regserv->{'image'};
2274
    my $imagename = $obj->{imagename} || $regserv->{'imagename'};
2275
    my $image2 = $obj->{image2} || $regserv->{'image2'};
2276
    my $image2name = $obj->{image2name} || $regserv->{'image2name'};
2277
    my $image3 = $obj->{image3} || $regserv->{'image3'};
2278
    my $image3name = $obj->{image3name} || $regserv->{'image3name'};
2279
    my $image4 = $obj->{image4} || $regserv->{'image4'};
2280
    my $image4name = $obj->{image4name} || $regserv->{'image4name'};
2281
    my $diskbus = $obj->{diskbus} || $regserv->{'diskbus'};
2282
    my $cdrom = $obj->{cdrom} || $regserv->{'cdrom'};
2283
    my $boot = $obj->{boot} || $regserv->{'boot'};
2284
    my $loader = $obj->{loader} || $regserv->{'loader'};
2285
    my $networkuuid1 = ($obj->{networkuuid1} || $obj->{networkuuid1} eq '0')?$obj->{networkuuid1}:$regserv->{'networkuuid1'};
2286
    my $networkid1 = $obj->{networkid1} || $regserv->{'networkid1'};
2287
    my $networkname1 = $obj->{networkname1} || $regserv->{'networkname1'};
2288
    my $nicmodel1 = $obj->{nicmodel1} || $regserv->{'nicmodel1'};
2289
    my $networkuuid2 = ($obj->{networkuuid2} || $obj->{networkuuid2} eq '0')?$obj->{networkuuid2}:$regserv->{'networkuuid2'};
2290
    my $networkid2 = $obj->{networkid2} || $regserv->{'networkid2'};
2291
    my $networkname2 = $obj->{networkname2} || $regserv->{'networkname2'};
2292
    my $nicmac2 = $obj->{nicmac2} || $regserv->{'nicmac2'};
2293
    my $networkuuid3 = ($obj->{networkuuid3} || $obj->{networkuuid3} eq '0')?$obj->{networkuuid3}:$regserv->{'networkuuid3'};
2294
    my $networkid3 = $obj->{networkid3} || $regserv->{'networkid3'};
2295
    my $networkname3 = $obj->{networkname3} || $regserv->{'networkname3'};
2296
    my $nicmac3 = $obj->{nicmac3} || $regserv->{'nicmac3'};
2297
    my $notes = $obj->{notes} || $regserv->{'notes'};
2298
    my $autostart = $obj->{autostart} || $regserv->{'autostart'};
2299
    my $locktonode = $obj->{locktonode} || $regserv->{'locktonode'};
2300
    my $mac = $obj->{mac} || $regserv->{'mac'};
2301
    my $created = $regserv->{'created'} || time;
2302
    # Sanity checks
2303
    my $tenderpaths = $Stabile::config->get('STORAGE_POOLS_LOCAL_PATHS') || "/mnt/stabile/images";
2304
    my @tenderpathslist = split(/,\s*/, $tenderpaths);
2305

    
2306
    $networkid1 = $networkreg{$networkuuid1}->{'id'};
2307
    my $networktype1 = $networkreg{$networkuuid1}->{'type'};
2308
    my $networktype2;
2309
    if (!$nicmac1 || $nicmac1 eq "--") {$nicmac1 = randomMac();}
2310
    if ($networkuuid2 && $networkuuid2 ne "--") {
2311
        $networkid2 = $networkreg{$networkuuid2}->{'id'};
2312
        $nicmac2 = randomMac() if (!$nicmac2 || $nicmac2 eq "--");
2313
        $networktype2 = $networkreg{$networkuuid2}->{'type'};
2314
    }
2315
    if ($networkuuid3 && $networkuuid3 ne "--") {
2316
        $networkid3 = $networkreg{$networkuuid3}->{'id'};
2317
        $networkname3 = $networkreg{$networkuuid3}->{'name'};
2318
        $nicmac3 = randomMac() if (!$nicmac3 || $nicmac3 eq "--");
2319
        $networktype3 = $networkreg{$networkuuid3}->{'type'};
2320
    }
2321

    
2322
    my $imgdup;
2323
    my $netdup;
2324
    my $json_text; # returned if all goes well
2325

    
2326
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
2327

    
2328
    if ($networkid1 > 1 && $networkid2 > 1 && $networktype1 ne 'gateway' && $networktype2 ne 'gateway'
2329
        && $networkuuid1 eq $networkuuid2) {
2330
        $netdup = 1;
2331
    }
2332
    if ($networkid1 > 1 && $networkid3 > 1 && $networktype1 ne 'gateway' && $networktype3 ne 'gateway'
2333
        && $networkuuid1 eq $networkuuid3) {
2334
        $netdup = 11;
2335
    }
2336
    if ($image eq $image2
2337
        || $image eq $image3
2338
        || $image eq $image4
2339
        || $image2 && $image2 ne '--' && $image2 eq $image3
2340
        || $image2 && $image2 ne '--' && $image2 eq $image4
2341
        || $image3 && $image3 ne '--' && $image3 eq $image4
2342
    ) {
2343
        $imgdup = 1;
2344
    } elsif ($image =~ m/\.master\.qcow2/
2345
        || $image2 =~ m/\.master\.qcow2/
2346
        || $image3 =~ m/\.master\.qcow2/
2347
        || $image4 =~ m/\.master\.qcow2/
2348
    ) {
2349
        $imgdup = 2;
2350
    } else {
2351
        # Check if another server is using image
2352
        my @regkeys = (tied %register)->select_where("user = '$user' OR user = 'common'");
2353
        foreach my $k (@regkeys) {
2354
            my $val = $register{$k};
2355
            if ($val->{'uuid'} ne $uuid) {
2356
                if (
2357
                    $image eq $val->{'image'} || $image eq $val->{'image2'}|| $image eq $val->{'image3'}|| $image eq $val->{'image4'}
2358
                ) {
2359
                    $imgdup = 51;
2360
                } elsif ($image2 && $image2 ne "--" &&
2361
                    ($image2 eq $val->{'image'} || $image2 eq $val->{'image2'} || $image2 eq $val->{'image3'} || $image2 eq $val->{'image4'})
2362
                ) {
2363
                    $imgdup = 52;
2364
                } elsif ($image3 && $image3 ne "--" &&
2365
                    ($image3 eq $val->{'image'} || $image3 eq $val->{'image2'} || $image3 eq $val->{'image3'} || $image3 eq $val->{'image4'})
2366
                ) {
2367
                    $imgdup = 53;
2368
                } elsif ($image4 && $image4 ne "--" &&
2369
                    ($image4 eq $val->{'image'} || $image4 eq $val->{'image2'} || $image4 eq $val->{'image3'} || $image4 eq $val->{'image4'})
2370
                ) {
2371
                    $imgdup = 54;
2372
                }
2373

    
2374
                if ($networkid1>1) {
2375
                    if ($networktype1 ne 'gateway' &&
2376
                        ($networkuuid1 eq $val->{'networkuuid1'} || $networkuuid1 eq $val->{'networkuuid2'})
2377
                    ) {
2378
                        $netdup = 51;
2379
                    }
2380
                }
2381
                if ($networkid2>1) {
2382
                    if ($networktype2 ne 'gateway' && $networkuuid2 && $networkuuid2 ne "--" &&
2383
                        ($networkuuid2 eq $val->{'networkuuid1'} || $networkuuid2 eq $val->{'networkuuid2'})
2384
                    ) {
2385
                        $netdup = 52;
2386
                    }
2387
                }
2388
            }
2389
        }
2390
        my $legalpath;
2391
        if ($image =~ m/\/mnt\/stabile\/node\/$user/) {
2392
            $legalpath = 1;
2393
        } else {
2394
            foreach my $path (@tenderpathslist) {
2395
                if ($image =~ m/$path\/$user/) {
2396
                    $legalpath = 1;
2397
                    last;
2398
                }
2399
            }
2400
        }
2401
        $imgdup = 6 unless $legalpath;
2402
        if ($image2 && $image2 ne "--") { # TODO: We should probably check for conflicting nodes for image3 and image 4 too
2403
            if ($image2 =~ m/\/mnt\/stabile\/node\/$user/) {
2404
                if ($image =~ m/\/mnt\/stabile\/node\/$user/) {
2405
                    if ($imagereg{$image}->{'mac'} eq $imagereg{$image2}->{'mac'}) {
2406
                        $legalpath = 1;
2407
                    } else {
2408
                        $legalpath = 0; # Images are on two different nodes
2409
                    }
2410
                } else {
2411
                    $legalpath = 1;
2412
                }
2413
            } else {
2414
                $legalpath = 0;
2415
                foreach my $path (@tenderpathslist) {
2416
                    if ($image2 =~ m/$path\/$user/) {
2417
                        $legalpath = 1;
2418
                        last;
2419
                    }
2420
                }
2421
            }
2422
            $imgdup = 7 unless $legalpath;
2423
        }
2424
    }
2425

    
2426
    if (!$imgdup && !$netdup) {
2427
        if ($status eq "new") {
2428
            $status = "shutoff";
2429
            $name = $name || 'New Server';
2430
            $memory = $memory || 1024;
2431
            $vcpu = $vcpu || 1;
2432
            $imagename = $imagename || '--';
2433
            $image2 = $image2 || '--';
2434
            $image2name = $image2name || '--';
2435
            $image3 = $image3 || '--';
2436
            $image3name = $image3name || '--';
2437
            $image4 = $image4 || '--';
2438
            $image4name = $image4name || '--';
2439
            $diskbus = $diskbus || 'ide';
2440
            $cdrom = $cdrom || '--';
2441
            $boot = $boot || 'hd';
2442
            $loader = $loader || 'bios';
2443
            $networkuuid1 = $networkuuid1 || 1;
2444
            $networkid1 = $networkid1 || 1;
2445
            $networkname1 = $networkname1 || '--';
2446
            $nicmodel1 = $nicmodel1 || 'rtl8139';
2447
            $nicmac1 = $nicmac1 || randomMac();
2448
            $networkuuid2 = $networkuuid2 || '--';
2449
            $networkid2 = $networkid2 || '--';
2450
            $networkname2 = $networkname2 || '--';
2451
            $nicmac2 = $nicmac2 || randomMac();
2452
            $networkuuid3 = $networkuuid3 || '--';
2453
            $networkid3 = $networkid3 || '--';
2454
            $networkname3 = $networkname3 || '--';
2455
            $nicmac3 = $nicmac3 || randomMac();
2456
            #    $uiuuid = $uuid; # No need to update ui for new server with jsonreststore
2457
            $postmsg .= "OK Created new server: $name";
2458
            $postmsg .= ", uuid: $uuid " if ($console);
2459
        }
2460
        # Update status of images
2461
        my @imgs = ($image, $image2, $image3, $image4);
2462
        my @imgkeys = ('image', 'image2', 'image3', 'image4');
2463
        for (my $i=0; $i<4; $i++) {
2464
            my $img = $imgs[$i];
2465
            my $k = $imgkeys[$i];
2466
            my $regimg = $imagereg{$img};
2467
            # if ($img && $img ne '--' && ($status eq 'new' || $img ne $regserv->{$k})) { # Servers image changed - update image status
2468
            if ($img && $img ne '--') { # Always update image status
2469
                $regimg->{'status'} = 'used' if (
2470
                    $regimg->{'status'} eq 'unused'
2471
                        # Image cannot be active if server is shutoff
2472
                        || ($regimg->{'status'} eq 'active' && $status eq 'shutoff')
2473
                );
2474
                $regimg->{'domains'} = $uuid;
2475
                $regimg->{'domainnames'} = $name;
2476
            }
2477
            # If image has changed, release the old image
2478
            if ($status ne 'new' && $img ne $regserv->{$k} && $imagereg{$regserv->{$k}}) {
2479
                $imagereg{$regserv->{$k}}->{'status'} = 'unused';
2480
                delete $imagereg{$regserv->{$k}}->{'domains'};
2481
                delete $imagereg{$regserv->{$k}}->{'domainnames'};
2482
            }
2483
        }
2484

    
2485
        my $valref = {
2486
            uuid=>$uuid,
2487
            user=>$user,
2488
            name=>$name,
2489
            memory=>$memory,
2490
            vcpu=>$vcpu,
2491
            image=>$image,
2492
            imagename=>$imagename,
2493
            image2=>$image2,
2494
            image2name=>$image2name,
2495
            image3=>$image3,
2496
            image3name=>$image3name,
2497
            image4=>$image4,
2498
            image4name=>$image4name,
2499
            diskbus=>$diskbus,
2500
            cdrom=>$cdrom,
2501
            boot=>$boot,
2502
            loader=>$loader,
2503
            networkuuid1=>$networkuuid1,
2504
            networkid1=>$networkid1,
2505
            networkname1=>$networkname1,
2506
            nicmodel1=>$nicmodel1,
2507
            nicmac1=>$nicmac1,
2508
            networkuuid2=>$networkuuid2,
2509
            networkid2=>$networkid2,
2510
            networkname2=>$networkname2,
2511
            nicmac2=>$nicmac2,
2512
            networkuuid3=>$networkuuid3,
2513
            networkid3=>$networkid3,
2514
            networkname3=>$networkname3,
2515
            nicmac3=>$nicmac3,
2516
            status=>$status,
2517
            notes=>$notes,
2518
            autostart=>$autostart,
2519
            locktonode=>$locktonode,
2520
            action=>"",
2521
            created=>$created
2522
        };
2523
        $valref->{'system'} = $system if ($system);
2524
        if ($mac && $locktonode eq 'true') {
2525
            $valref->{'mac'} = $mac;
2526
            $valref->{'macip'} = $nodereg{$mac}->{'ip'};
2527
            $valref->{'macname'} = $nodereg{$mac}->{'name'};
2528
        }
2529
        if ($newsystem) {
2530
            my $ug = new Data::UUID;
2531
            $sysuuid = $ug->create_str();
2532
            $valref->{'system'} = $sysuuid;
2533
            $postmsg .= "OK sysuuid: $sysuuid " if ($console);
2534
        }
2535

    
2536
        # Remove domain uuid from old networks. Leave gateways alone - they get updated on next listing
2537
        my $oldnetworkuuid1 = $regserv->{'networkuuid1'};
2538
        if ($oldnetworkuuid1 ne $networkuuid1 && $networkreg{$oldnetworkuuid1}) {
2539
            $networkreg{$oldnetworkuuid1}->{'domains'} =~ s/($uuid)(,?)( ?)//;
2540
        }
2541
        $register{$uuid} = validateItem($valref);
2542

    
2543
        if ($networkreg{$networkuuid1}->{'type'} eq 'gateway') {
2544
            # We now remove before adding to support API calls that dont necessarily list afterwards
2545
            $networkreg{$networkuuid1}->{'domains'} =~ s/($uuid)(,?)( ?)//;
2546
            my $domains = $networkreg{$networkuuid1}->{'domains'};
2547
            $networkreg{$networkuuid1}->{'domains'} = ($domains?"$domains, ":"") . $uuid;
2548

    
2549
            $networkreg{$networkuuid1}->{'domainnames'} =~ s/($name)(,?)( ?)//;
2550
            my $domainnames = $networkreg{$networkuuid1}->{'domainnames'};
2551
            $networkreg{$networkuuid1}->{'domainnames'} = ($domainnames?"$domainnames, ":"") . $name;
2552
        } else {
2553
            $networkreg{$networkuuid1}->{'domains'}  = $uuid;
2554
            $networkreg{$networkuuid1}->{'domainnames'}  = $name;
2555
        }
2556

    
2557
        if ($networkuuid2 && $networkuuid2 ne '--') {
2558
            if ($networkreg{$networkuuid2}->{'type'} eq 'gateway') {
2559
                $networkreg{$networkuuid2}->{'domains'} =~ s/($uuid)(,?)( ?)//;
2560
                my $domains = $networkreg{$networkuuid2}->{'domains'};
2561
                $networkreg{$networkuuid2}->{'domains'} = ($domains?"$domains, ":"") . $uuid;
2562

    
2563
                $networkreg{$networkuuid2}->{'domainnames'} =~ s/($name)(,?)( ?)//;
2564
                my $domainnames = $networkreg{$networkuuid2}->{'domainnames'};
2565
                $networkreg{$networkuuid2}->{'domainnames'} = ($domainnames?"$domainnames, ":"") . $name;
2566
            } else {
2567
                $networkreg{$networkuuid2}->{'domains'}  = $uuid;
2568
                $networkreg{$networkuuid2}->{'domainnames'}  = $name;
2569
            }
2570
        }
2571

    
2572
        if ($networkuuid3 && $networkuuid3 ne '--') {
2573
            if ($networkreg{$networkuuid3}->{'type'} eq 'gateway') {
2574
                my $domains = $networkreg{$networkuuid3}->{'domains'};
2575
                $networkreg{$networkuuid3}->{'domains'} = ($domains?"$domains, ":"") . $uuid;
2576
                my $domainnames = $networkreg{$networkuuid3}->{'domainnames'};
2577
                $networkreg{$networkuuid3}->{'domainnames'} = ($domainnames?"$domainnames, ":"") . $name;
2578
            } else {
2579
                $networkreg{$networkuuid3}->{'domains'}  = $uuid;
2580
                $networkreg{$networkuuid3}->{'domainnames'}  = $name;
2581
            }
2582
        }
2583
        my %jitem = %{$register{$uuid}};
2584
        $json_text = to_json(\%jitem, {pretty=>1});
2585
        $json_text =~ s/null/"--"/g;
2586
        $uiuuid = $uuid;
2587
        $uiname = $name;
2588

    
2589
        tied(%register)->commit;
2590
        tied(%networkreg)->commit;
2591
        tied(%imagereg)->commit;
2592

    
2593
    } else {
2594
        $postmsg .= "ERROR This image ($image) cannot be used ($imgdup) " if ($imgdup);
2595
        $postmsg .= "ERROR This network ($networkname1) cannot be used ($netdup)" if ($netdup);
2596
    }
2597

    
2598
    my $domuser = $obj->{'user'};
2599
    # We were asked to move server to another account
2600
    if ($domuser && $domuser ne '--' && $domuser ne $user) {
2601
        unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username', CLOBBER=>0}, $Stabile::dbopts)) ) {throw Error::Simple("Stroke=Error User register could not be  accessed")};
2602
        if ($status eq 'shutoff' || $status eq 'inactive') {
2603
            unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username', CLOBBER=>1}, $Stabile::dbopts)) ) {$posterror =  "Unable to access user register"; return 0;};
2604
            my @accounts = split(/,\s*/, $userreg{$tktuser}->{'accounts'});
2605
            my @accountsprivs = split(/,\s*/, $userreg{$tktuser}->{'accountsprivileges'});
2606
            %ahash = ($tktuser, $userreg{$tktuser}->{'privileges'}); # Include tktuser in accounts hash
2607
            for my $i (0 .. scalar @accounts)
2608
            {
2609
                next unless $accounts[$i];
2610
                $ahash{$accounts[$i]} = $accountsprivs[$i] || 'r';
2611
            }
2612
            untie %userreg;
2613

    
2614
            if (!$isreadonly && $ahash{$domuser} && !($ahash{$domuser} =~ /r/)) { # Check if user is allow to access account
2615
                my $imgdone;
2616
                my $netdone;
2617
                # First move main image
2618
                $Stabile::Images::user = $user;
2619
                require "$Stabile::basedir/cgi/images.cgi";
2620
                $Stabile::Images::console = 1;
2621
                $main::updateUI->({tab=>"servers", user=>$user, message=>"Moving image $imagename to account: $domuser"});
2622
                my $nimage = Stabile::Images::Move($image, $domuser);
2623
                chomp $nimage;
2624
                if ($nimage) {
2625
                    $main::syslogit->($user, "info", "Moving $nimage to account: $domuser");
2626
                    $register{$uuid}->{'image'} = $nimage;
2627
                    $imgdone = 1;
2628
                } else {
2629
                    $main::syslogit->($user, "info", "Unable to move image $imagename to account: $domuser");
2630
                }
2631
                # Move other attached images
2632
                my @images = ($image2, $image3, $image4);
2633
                my @imagenames = ($image2name, $image3name, $image4name);
2634
                my @imagekeys = ('image2', 'image3', 'image4');
2635
                for (my $i=0; $i<3; $i++) {
2636
                    my $img = $images[$i];
2637
                    my $imgname = $imagenames[$i];
2638
                    my $imgkey = $imagekeys[$i];
2639
                    if ($img && $img ne '--') {
2640
                        $main::updateUI->({tab=>"servers", user=>$user, message=>"Moving $imgkey $imgname to account: $domuser"});
2641
                        $nimage = Stabile::Images::Move($img, $domuser);
2642
                        chomp $nimage;
2643
                        if ($nimage) {
2644
                            $main::syslogit->($user, "info", "Moving $nimage to account: $domuser");
2645
                            $register{$uuid}->{$imgkey} = $nimage;
2646
                        } else {
2647
                            $main::syslogit->($user, "info", "Unable to move $imagekeys[$i] $img to account: $domuser");
2648
                        }
2649
                    }
2650
                }
2651
                # Then move network(s)
2652
                if ($imgdone) {
2653
                    $Stabile::Networks::user = $user;
2654
                    require "$Stabile::basedir/cgi/networks.cgi";
2655
                    $Stabile::Networks::console = 1;
2656
                    my @networks = ($networkuuid1, $networkuuid2, $networkuuid3);
2657
                    my @netkeys = ('networkuuid1', 'networkuuid2', 'networkuuid3');
2658
                    my @netnamekeys = ('networkname1', 'networkname2', 'networkname3');
2659
                    for (my $i=0; $i<scalar @networks; $i++) {
2660
                        my $net = $networks[$i];
2661
                        my $netkey = $netkeys[$i];
2662
                        my $netnamekey = $netnamekeys[$i];
2663
                        my $regnet = $networkreg{$net};
2664
                        my $oldid = $regnet->{'id'};
2665
                        next if ($net eq '' || $net eq '--');
2666
                        if ($regnet->{'type'} eq 'gateway') {
2667
                            if ($oldid > 1) { # Private gateway
2668
                                foreach my $networkvalref (values %networkreg) { # use gateway with same id if it exists
2669
                                    if ($networkvalref->{'user'} eq $domuser
2670
                                        && $networkvalref->{'type'} eq 'gateway'
2671
                                        && $networkvalref->{'id'} == $oldid) {
2672
                                        # We found an existing gateway with same id - use it
2673
                                        $register{$uuid}->{$netkey} = $networkvalref->{'uuid'};
2674
                                        $register{$uuid}->{$netnamekey} = $networkvalref->{'name'};
2675
                                        $netdone = 1;
2676
                                        $main::updateUI->({tab=>"networks", user=>$user, message=>"Using network $networkvalref->{'name'} from account: $domuser"});
2677
                                        last;
2678
                                    }
2679
                                }
2680
                                if (!($netdone)) {
2681
                                    # Make a new gateway
2682
                                    my $ug = new Data::UUID;
2683
                                    my $newuuid = $ug->create_str();
2684
                                    Stabile::Networks::save($oldid, $newuuid, $regnet->{'name'}, 'new', 'gateway', '', '', $regnet->{'ports'}, 0, $domuser);
2685
                                    $register{$uuid}->{$netkey} = $newuuid;
2686
                                    $register{$uuid}->{$netnamekey} = $regnet->{'name'};
2687
                                    $netdone = 1;
2688
                                    $main::updateUI->({tab=>"networks", user=>$user, message=>"Created gateway $regnet->{'name'} for account: $domuser"});
2689
                                    $main::syslogit->($user, "info", "Created gateway $regnet->{'name'} for account: $domuser");
2690
                                }
2691
                            } elsif ($oldid==0 || $oldid==1) {
2692
                                $netdone = 1; # Use common gateway
2693
                                $main::updateUI->({tab=>"networks", user=>$user, message=>"Reused network $regnet->{'name'} for account: $domuser"});
2694
                            }
2695
                        } else {
2696
                            my $newid = Stabile::Networks::getNextId('', $domuser);
2697
                            $networkreg{$net}->{'id'} = $newid;
2698
                            $networkreg{$net}->{'user'} = $domuser;
2699
                        #    if ($regnet->{'type'} eq 'internalip' || $regnet->{'type'} eq 'ipmapping') {
2700
                                # Deactivate network and assign new internal ip
2701
                                Stabile::Networks::Deactivate($regnet->{'uuid'});
2702
                                $networkreg{$net}->{'internalip'} =
2703
                                    Stabile::Networks::getNextInternalIP('',$regnet->{'uuid'}, $newid, $domuser);
2704
                        #    }
2705
                            $netdone = 1;
2706
                            $main::updateUI->({tab=>"networks", user=>$user, message=>"Moved network $regnet->{'name'} to account: $domuser"});
2707
                            $main::syslogit->($user, "info", "Moved network $regnet->{'name'} to account: $domuser");
2708
                        }
2709
                    }
2710
                    if ($netdone) {
2711
                        # Finally move the server
2712
                        $register{$uuid}->{'user'} = $domuser;
2713
                        $postmsg .= "OK Moved server $name to account: $domuser";
2714
                        $main::syslogit->($user, "info", "Moved server $name ($uuid) to account: $domuser");
2715
                        $main::updateUI->({tab=>"servers", user=>$user, type=>"update"});
2716
                    } else {
2717
                        $postmsg .= "ERROR Unable to move network to account: $domuser";
2718
                        $main::updateUI->({tab=>"image", user=>$user, message=>"Unable to move network to account: $domuser"});
2719
                    }
2720
                } else {
2721
                    $main::updateUI->({tab=>"image", user=>$user, message=>"Could not move image to account: $domuser"});
2722
                }
2723
            } else {
2724
                $postmsg .= "ERROR No access to move server";
2725
            }
2726
        } else {
2727
            $postmsg .= "Error Unable to move $status server";
2728
            $main::updateUI->({tab=>"servers", user=>$user, message=>"Please shut down before moving server"});
2729
        }
2730
        untie %userreg;
2731
    }
2732

    
2733
    if ($console) {
2734
        $postreply = $postmsg;
2735
    } else {
2736
        $postreply = $json_text || $postmsg;
2737
    }
2738
    return $postreply;
2739
    untie %imagereg;
2740
}
2741

    
2742

    
2743
sub Shutdown {
2744
    my ($uuid, $action, $obj) = @_;
2745
    if ($help) {
2746
        return <<END
2747
GET:uuid:
2748
Marks a server for shutdown, i.e. send and ACPI shutdown event to the server. If OS supports ACPI, it begins a shutdown.
2749
END
2750
    }
2751
    $uistatus = "shuttingdown";
2752
    my $dbstatus = $obj->{status};
2753
    my $mac = $obj->{mac};
2754
    my $macname = $obj->{macname};
2755
    my $name = $obj->{name};
2756
    if ($dbstatus eq 'running') {
2757
        my $tasks;
2758
        $tasks = $nodereg{$mac}->{'tasks'} if ($nodereg{$mac});
2759
        $nodereg{$mac}->{'tasks'} = $tasks . "SHUTDOWN $uuid $user\n";
2760
        tied(%nodereg)->commit;
2761
        $register{$uuid}->{'status'} = $uistatus;
2762
        $register{$uuid}->{'statustime'} = $current_time;
2763
        $uiuuid = $uuid;
2764
        $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus by $macname ($mac)");
2765
        $postreply .= "Status=$uistatus OK $uistatus $name\n";
2766
    } else {
2767
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
2768
        $postreply .= "Status=ERROR problem $uistatus $name...\n";
2769
    }
2770
    return $postreply;
2771
}
2772

    
2773
sub Suspend {
2774
    my ($uuid, $action, $obj) = @_;
2775
    if ($help) {
2776
        return <<END
2777
GET:uuid:
2778
Marks a server for suspend, i.e. pauses the server. Server must be running
2779
END
2780
    }
2781
    $uistatus = "suspending";
2782
    my $dbstatus = $obj->{status};
2783
    my $mac = $obj->{mac};
2784
    my $macname = $obj->{macname};
2785
    my $name = $obj->{name};
2786
    my $areply = '';
2787
    if ($dbstatus eq 'running') {
2788
        my $tasks = $nodereg{$mac}->{'tasks'};
2789
        $nodereg{$mac}->{'tasks'} = $tasks . "SUSPEND $uuid $user\n";
2790
        tied(%nodereg)->commit;
2791
        $register{$uuid}->{'status'} = $uistatus;
2792
        $register{$uuid}->{'statustime'} = $current_time;
2793
        $uiuuid = $uuid;
2794
        $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus by $macname ($mac)");
2795
        $areply .= "Status=$uistatus OK $uistatus $name.\n";
2796
    } else {
2797
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
2798
        $areply .= "Status=ERROR problem $uistatus $name.\n";
2799
    }
2800
    return $areply;
2801
}
2802

    
2803
sub Resume {
2804
    my ($uuid, $action, $obj) = @_;
2805
    if ($help) {
2806
        return <<END
2807
GET:uuid:
2808
Marks a server for resume running. Server must be paused.
2809
END
2810
    }
2811
    my $dbstatus = $obj->{status};
2812
    my $mac = $obj->{mac};
2813
    my $macname = $obj->{macname};
2814
    my $name = $obj->{name};
2815
    my $image = $obj->{image};
2816
    my $image2 = $obj->{image2};
2817
    my $image3 = $obj->{image3};
2818
    my $image4 = $obj->{image4};
2819
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$posterror = "Unable to access image register"; return;};
2820
    if ($imagereg{$image}->{'status'} ne "paused"
2821
        || ($image2 && $image2 ne '--' && $imagereg{$image}->{'status'} ne "paused")
2822
        || ($image3 && $image3 ne '--' && $imagereg{$image3}->{'status'} ne "paused")
2823
        || ($image4 && $image4 ne '--' && $imagereg{$image4}->{'status'} ne "paused")
2824
    ) {
2825
        $postreply .= "Status=ERROR Image $uuid busy ($imagereg{$image}->{'status'}), please wait 30 sec.\n";
2826
        untie %imagereg;
2827
        return $postreply   ;
2828
    } else {
2829
        untie %imagereg;
2830
    }
2831
    $uistatus = "resuming";
2832
    if ($dbstatus eq 'paused') {
2833
        my $tasks = $nodereg{$mac}->{'tasks'};
2834
        $nodereg{$mac}->{'tasks'} = $tasks . "RESUME $uuid $user\n";
2835
        tied(%nodereg)->commit;
2836
        $register{$uuid}->{'status'} = $uistatus;
2837
        $register{$uuid}->{'statustime'} = $current_time;
2838
        $uiuuid = $uuid;
2839
        $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus by $macname ($mac)");
2840
        $postreply .= "Status=$uistatus OK $uistatus ". $register{$uuid}->{'name'} . "\n";
2841
    } else {
2842
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
2843
        $postreply .= "Status=ERROR problem $uistatus ". $register{$uuid}->{'name'} . "\n";
2844
    }
2845
    return $postreply;
2846
}
2847

    
2848
sub Abort {
2849
    my ($uuid, $action, $obj) = @_;
2850
    if ($help) {
2851
        return <<END
2852
GET:uuid,mac:
2853
Aborts an ongoing server move between nodes initiated with move or stormove.
2854
END
2855
    }
2856
    my $dbstatus = $obj->{status};
2857
    my $dmac = $obj->{mac};
2858
    my $name = $obj->{name};
2859
    if ($isadmin || $register{$uuid}->{user} eq $user) {
2860
        my $tasks = $nodereg{$dmac}->{'tasks'};
2861
        $tasks .= "ABORT $uuid $user\n";
2862
        $nodereg{$dmac}->{'tasks'} = $tasks;
2863
        tied(%nodereg)->commit;
2864
        $postreply = "Status=aborting Aborting move of server $name ($dbstatus) on node $dmac\n";
2865
    } else {
2866
        $postreply = "Status=OK Insufficient privileges\n";
2867
    }
2868
}
2869

    
2870
sub Move {
2871
    my ($uuid, $action, $obj) = @_;
2872
    if ($help) {
2873
        return <<END
2874
GET:uuid,mac:
2875
Moves a server to a different node (Qemu live migration). Server must be running. When called as stormove, non-shared disks are migrated. This may of course take a lot of time, dependeing on the size of the backing images involved.
2876
END
2877
    }
2878
    my $dbstatus = $obj->{status};
2879
    my $dmac = $obj->{mac};
2880
    my $name = $obj->{name};
2881
    my $mem = $obj->{memory};
2882
    my $vcpu = $obj->{vcpu};
2883
    my $image = $obj->{image};
2884
    my $image2 = $obj->{image2};
2885
    my $image3 = $obj->{image3};
2886
    my $image4 = $obj->{image4};
2887

    
2888
    $uistatus = "moving";
2889
    if ($dbstatus eq 'running' && $isadmin) {
2890
        my $hypervisor = getHypervisor($image);
2891
        my $mac = $register{$uuid}->{'mac'};
2892
        $dmac = "" if ($dmac eq "--");
2893
        $mac = "" if ($mac eq "--");
2894

    
2895
        if (( $image =~ /\/mnt\/stabile\/node\//
2896
            || $image2 =~ /\/mnt\/stabile\/node\//
2897
            || $image3 =~ /\/mnt\/stabile\/node\//
2898
            || $image4 =~ /\/mnt\/stabile\/node\// ) && $action ne 'stormove'
2899
        ) {
2900
            $postreply = qq|{"error": 1, "message": "Servers with local storage must be moved with stormove"}|;
2901
            $main::updateUI->({tab=>"servers", user=>$user, message=>"Servers with local storage must be moved with stormove"});
2902
        } else {
2903
            my ($targetmac, $targetname, $targetip, $port) =
2904
                locateTargetNode($uuid, $dmac, $mem, $vcpu, $image, $image2, $image3, $image4, $hypervisor, $mac, 1);
2905
            if ($targetmac) {
2906
                my $tasks = $nodereg{$targetmac}->{'tasks'};
2907
                if ($action eq 'stormove') {
2908
                    $tasks = $tasks . "RECEIVESTOR $uuid $user\n";
2909
                } else {
2910
                    $tasks = $tasks . "RECEIVE $uuid $user\n";
2911
                }
2912
                # Also update allowed port forwards
2913
                $nodereg{$targetmac}->{'tasks'} = $tasks . "PERMITOPEN $user\n";
2914
                $register{$uuid}->{'status'} = "moving";
2915
                $register{$uuid}->{'statustime'} = $current_time;
2916
                $uiuuid = $uuid;
2917
                $uidisplayip = $targetip;
2918
                $uidisplayport = $port;
2919
                $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus to $targetname ($targetmac)");
2920
                $postreply .= "Status=OK $uistatus ". $register{$uuid}->{'name'} . "\n";
2921

    
2922
                # Precreate images on destination node
2923
                if ($action eq 'stormove') {
2924
                    my $preimages = '';
2925
                    $Stabile::Images::user = $user;
2926
                    require "$Stabile::basedir/cgi/images.cgi";
2927
                    $Stabile::Images::console = 1;
2928
                    if ($targetip eq '10.0.0.1') { # Moving from node
2929
                        if ($image =~ /\/mnt\/stabile\/node\//) { # Only move to shared storage if not already on shared storage
2930
                            my $res = Stabile::Images::Move($image, $user, '0', '', 0, 1);
2931
                            $preimages .= " $register{$uuid}->{imagename}";
2932
                        }
2933
                        if ($image2 =~ /\/mnt\/stabile\/node\//) { # Only move to shared storage if not already on shared storage
2934
                            my $res = Stabile::Images::Move($image2, $user, '0', '', 0, 1);
2935
                            $preimages .= " $register{$uuid}->{image2name}";
2936
                        }
2937
                        if ($image3 =~ /\/mnt\/stabile\/node\//) { # Only move to shared storage if not already on shared storage
2938
                            my $res = Stabile::Images::Move($image3, $user, '0', '', 0, 1);
2939
                            $preimages .= " $register{$uuid}->{image3name}";
2940
                        }
2941
                        if ($image4 =~ /\/mnt\/stabile\/node\//) { # Only move to shared storage if not already on shared storage
2942
                            my $res = Stabile::Images::Move($image4, $user, '0', '', 0, 1);
2943
                            $preimages .= " $register{$uuid}->{image4name}";
2944
                        }
2945
                    } else { # Moving to node or between nodes - always move primary image, also if on shared storage
2946
                        my $res = Stabile::Images::Move($image, $user, '-1', $targetmac, 0, 1);
2947
                        $preimages .= " $register{$uuid}->{imagename}";
2948
                        if ($image2 && $image2 ne '--') {
2949
                            # We don't migrate data disks away from shared storage
2950
                            unless ($image2 =~ /\/stabile-images\/images\/.*-data\..*\.qcow2/) {
2951
                                my $res = Stabile::Images::Move($image2, $user, '-1', $targetmac, 0, 1);
2952
                                $preimages .= " $register{$uuid}->{image2name}";
2953
                            }
2954
                        }
2955
                        if ($image3 && $image3 ne '--') {
2956
                            unless ($image3 =~ /\/stabile-images\/images\/.*-data\..*\.qcow2/) {
2957
                                my $res = Stabile::Images::Move($image3, $user, '-1', $targetmac, 0, 1);
2958
                                $preimages .= " $register{$uuid}->{image3name}";
2959
                            }
2960
                        }
2961
                        if ($image4 && $image4 ne '--') {
2962
                            unless ($image4 =~ /\/stabile-images\/images\/.*-data\..*\.qcow2/) {
2963
                                my $res = Stabile::Images::Move($image4, $user, '-1', $targetmac, 0, 1);
2964
                                $preimages .= " $register{$uuid}->{image4name}";
2965
                            }
2966
                        }
2967
                    }
2968
                    if ($preimages) {
2969
                        $main::syslogit->($user, "info", "Precreating images $preimages on node $targetmac");
2970
                        $main::updateUI->({tab=>"servers", user=>$user, message=>"Precreating images $preimages on node $targetmac"});
2971
                    }
2972
                }
2973
                if ($params{'PUTDATA'}) {
2974
                    my %jitem = %{$register{$uuid}};
2975
                    my $json_text = to_json(\%jitem);
2976
                    $json_text =~ s/null/"--"/g;
2977
                    $postreply = $json_text;
2978
                }
2979
#                $main::updateUI->({tab=>"servers", user=>$user, status=>'moving', uuid=>$uuid, type=>'update', message=>"Moving $register{$uuid}->{name} to $targetmac"});
2980
            } else {
2981
                $main::syslogit->($user, "info", "Could not find $hypervisor target for $uistatus $uuid ($image)");
2982
                $main::updateUI->({tab=>"servers", user=>$user, message=>"Could not find target for $uistatus $register{$uuid}->{'name'}"});
2983
                $postreply = qq|{"error": 1, "message": "Could not find target for $uistatus $register{$uuid}->{'name'}"}|;
2984
            }
2985
        }
2986
    } else {
2987
        $main::syslogit->($user, "info", "Problem moving a $dbstatus domain: $uuid");
2988
        my $serv = $register{$uuid};
2989
        $postreply .= qq|{"error": 1, "message": "ERROR problem moving $serv->{'name'} ($dbstatus)"}|;
2990
    }
2991
    return $postreply;
2992
}
2993

    
2994
sub Changepassword {
2995
    my ($uuid, $action, $obj) = @_;
2996
    if ($help) {
2997
        return <<END
2998
POST:uuid,username,password:
2999
Attempts to set password for [username] to [password] using guestfish. If no username is specified, user 'stabile' is assumed.
3000
END
3001
    }
3002
    my $img = $register{$uuid}->{'image'};
3003
    my $username = $obj->{'username'} || 'stabile';
3004
    my $password = $obj->{'password'};
3005
    return "Status=Error Please supply a password\n" unless ($password);
3006
    return "Status=Error Please shut down the server before changing password\n" unless ($register{$uuid} && $register{$uuid}->{'status'} eq 'shutoff');
3007
    return "Status=Error Not allowed\n" unless ($isadmin || $register{$uuid}->{'user'} eq $user);
3008

    
3009
    unless (tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access images register"}|; return $res;};
3010
    my $cmd = qq/guestfish --rw -a $img -i command "bash -c 'echo $username:$password | chpasswd'" 2>\&1/;
3011
    if ($imagereg{$img} && $imagereg{$img}->{'mac'}) {
3012
        my $mac = $imagereg{$img}->{'mac'};
3013
        my $macip = $nodereg{$mac}->{'ip'};
3014
        $cmd = "$sshcmd $macip $cmd";
3015
    }
3016
    my $res = `$cmd`;
3017
    $res = $1 if ($res =~ /guestfish: (.*)/);
3018
    chomp $res;
3019
    return "Status=OK Ran chpasswd for user $username in server $register{$uuid}->{'name'}: $res\n";
3020
}
3021

    
3022
sub Sshaccess {
3023
    my ($uuid, $action, $obj) = @_;
3024
    if ($help) {
3025
        return <<END
3026
POST:uuid,address:
3027
Attempts to change the ip addresses you can access the server over SSH (port 22) from, by adding [address] to /etc/hosts.allow.
3028
[address] should either be an IP address or a range in CIDR notation. Please note that no validation of [address] is performed.
3029
END
3030
    }
3031
    my $img = $register{$uuid}->{'image'};
3032
    my $address = $obj->{'address'};
3033
    return "Status=Error Please supply an aaddress\n" unless ($address);
3034
    return "Status=Error Please shut down the server before changing SSH access\n" unless ($register{$uuid} && $register{$uuid}->{'status'} eq 'shutoff');
3035
    return "Status=Error Not allowed\n" unless ($isadmin || $register{$uuid}->{'user'} eq $user);
3036

    
3037
    unless (tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access images register"}|; return $res;};
3038

    
3039
    my $isshcmd = '';
3040
    my $cmd = qq[guestfish --rw -a $img -i command "sed -i -re 's|(sshd: .*)#stabile|\\1 $address #stabile|' /etc/hosts.allow"];
3041
#    my $cmd = qq[guestfish --rw -a $img -i command "bash -c 'echo sshd: $address >> /etc/hosts.allow'"];
3042
    if ($imagereg{$img} && $imagereg{$img}->{'mac'}) {
3043
        my $mac = $imagereg{$img}->{'mac'};
3044
        my $macip = $nodereg{$mac}->{'ip'};
3045
        $isshcmd = "$sshcmd $macip ";
3046
    }
3047
    my $res = `$isshcmd$cmd`;
3048
    chomp $res;
3049
    #$cmd = qq[guestfish --rw -a $img -i command "bash -c 'cat /etc/hosts.allow'"];
3050
    #$res .= `$isshcmd$cmd`;
3051
    #chomp $res;
3052
    return "Status=OK Tried to add sshd: $address to /etc/hosts.allow in server $register{$uuid}->{'name'}\n";
3053
}
3054

    
3055
sub Mountcd {
3056
    my ($uuid, $action, $obj) = @_;
3057
    if ($help) {
3058
        return <<END
3059
GET:uuid,cdrom:
3060
Mounts a cdrom on a server. Server must be running. Mounting the special cdrom named '--' unomunts any currently mounted cdrom.
3061
END
3062
    }
3063
    my $dbstatus = $obj->{status};
3064
    my $mac = $obj->{mac};
3065
    my $cdrom = $obj->{cdrom};
3066
    unless ($cdrom && $dbstatus eq 'running') {
3067
        $main::updateUI->({tab=>"servers", user=>$user, uuid=>$uuid, type=>'update', message=>"Unable to mount cdrom"});
3068
        $postreply = qq|{"Error": 1, "message": "Problem mounting cdrom on $obj->{name}"}|;
3069
        return;
3070
    }
3071
    my $tasks = $nodereg{$mac}->{'tasks'};
3072
    # $user is in the middle here, because $cdrom may contain spaces...
3073
    $nodereg{$mac}->{'tasks'} = $tasks . "MOUNT $uuid $user \"$cdrom\"\n";
3074
    tied(%nodereg)->commit;
3075
    if ($cdrom eq "--") {
3076
        $postreply = qq|{"OK": 1, "message": "OK unmounting cdrom from $obj->{name}"}|;
3077
    } else {
3078
        $postreply = qq|{"OK": 1, "message": "OK mounting cdrom $cdrom on $obj->{name}"}|;
3079
    }
3080
    $register{$uuid}->{'cdrom'} = $cdrom unless ($cdrom eq 'virtio');
3081
    return $postreply;
3082
}
(5-5/9)