Project

General

Profile

Download (120 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 lib dirname (__FILE__);
17
use Stabile;
18
#use Encode::Escape;
19

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

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

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

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

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

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

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

    
55
1;
56

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

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

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

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

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

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

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

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

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

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

    
160
    my $action = $h{"action"};
161
    my $notes = $h{"notes"};
162
    $notes = $dbobj->{'notes'} if (!$notes || $notes eq '--');
163
    my $reguser = $dbobj->{'user'};
164
    my $autostart = ($h{"autostart"} ."") || $dbobj->{'autostart'};
165
    if ($autostart && $autostart ne "false") {$autostart = "true";}
166
    my $locktonode = ($h{"locktonode"} ."") || $dbobj->{'locktonode'};
167
    if ($locktonode && $locktonode ne "false") {$locktonode = "true";}
168
    my $mac;
169
    $mac = $dbobj->{'mac'} unless ($objaction eq 'start');
170
#    $mac = $h{"mac"} if ($isadmin && $locktonode eq 'true' && $h{"mac"});
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

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

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

    
314
    *do_gear_start = \&do_gear_action;
315
    *do_gear_autostart = \&do_gear_action;
316
    *do_gear_showautostart = \&do_gear_action;
317
    *do_gear_autostartall = \&do_gear_action;
318
    *do_gear_remove = \&do_gear_action;
319
    *do_gear_changepassword = \&do_gear_action;
320
    *do_gear_sshaccess = \&do_gear_action;
321

    
322
}
323

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

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

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

    
366
    unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
367
    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;};
368

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

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

    
397
                    my %pval = %{$sysreg{$sysuuid}};
398
                    $pval{'nodetype'} = 'parent';
399
                    $pval{'status'} = '--';
400
                    $val{'nodetype'} = 'child';
401

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

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

    
454
    if ($action eq 'tablelist') {
455
        my $t2;
456

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

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

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

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

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

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

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

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

    
637
    };
638
    $res .= "Status=OK Set status of $register{$uuid}->{'name'} to running\n";
639
    return $res;
640
}
641

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

    
661
        my $termlink = $imagereg{$register{$uuid}->{image}}->{terminallink} || '';
662
        $termlink =~ s/{uuid}/$register{$uuid}->{networkuuid1}/;
663
        my $burl = $baseurl;
664
        $burl = $1 if ($termlink =~ /\/stabile/ && $baseurl =~ /(.+)\/stabile/); # Unpretty, but works for now
665
        # $termlink = $1 if ($termlink =~ /\/(.+)/);
666
        # $termlink = "$burl/$termlink" unless ($termlink =~ /^http/ || !$termlink); # || $termlink =~ /^\//
667
        $appinfo{'terminallink'} = $termlink;
668

    
669
        $appinfo{'upgradelink'} = $imagereg{$register{$uuid}->{image}}->{upgradelink} || '';
670
        $appinfo{'upgradelink'} =~ s/{uuid}/$register{$uuid}->{networkuuid1}/;
671
        $appinfo{'version'} = $imagereg{$register{$uuid}->{image}}->{version} || '';
672
        $appinfo{'status'} = $register{$uuid}->{status} || '';
673
        $appinfo{'name'} = $register{$uuid}->{name} || '';
674
        $appinfo{'system'} = $register{$uuid}->{system} || '';
675

    
676
        if ($appinfo{'system'}) {
677
            unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
678
            $appinfo{'systemname'} = $sysreg{$appinfo{'system'}}->{name} || '';
679
            untie(%sysreg);
680
        } else {
681
            $appinfo{'systemname'} = $appinfo{'name'};
682
        }
683

    
684

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

    
700
        untie %imagereg;
701
    }
702
    $appinfo{'appstoreurl'} = $appstoreurl;
703
    $appinfo{'dnsdomain'} = ($enginelinked)?$dnsdomain:'';
704
    $appinfo{'dnssubdomain'} = ($enginelinked)?substr($engineid, 0, 8):'';
705
    $appinfo{'uuid'} = $uuid;
706
    $appinfo{'user'} = $user;
707
    $appinfo{'remoteip'} = $remoteip;
708
    $res .= to_json(\%appinfo, { pretty => 1 });
709
    return $res;
710
}
711

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

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

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

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

    
784
    if (!%nodereg || $nodedown || !$nodecount) {
785
        $mes = "Not autostarting servers - not all nodes ready!\n";
786
        print $mes if ($console);
787
        $res .= $mes;
788
    }
789
    else {
790
        $mes = "$nodecount nodes ready - autostarting servers...\n";
791
        print $mes if ($console);
792
        $res .= $mes;
793
        if ($action eq "showautostart") {
794
            $mes = "Only showing which servers would be starting!\n";
795
            print $mes if ($console);
796
            $res .= $mes;
797
        }
798

    
799
        $Stabile::Networks::user = $user;
800
        require "$Stabile::basedir/cgi/networks.cgi";
801
        $Stabile::Networks::console = 1;
802

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

    
871
sub do_listnodeavailability {
872
    if ($help) {
873
        return <<END
874
GET::
875
Utility call - only informational. Shows availability of nodes for starting servers.
876
END
877
    }
878
    my $res;
879
    $res .= $Stabile::q->header('application/json') unless ($console);
880
    my ($temp1, $temp2, $temp3, $temp4, $ahashref) = locateTargetNode();
881
    my @avalues = values %$ahashref;
882
    my @sorted_values = (sort {$b->{'index'} <=> $a->{'index'}} @avalues);
883
    $res .= to_json(\@sorted_values, { pretty => 1 });
884
    return $res;
885
}
886

    
887
sub do_listbillingdata {
888
    if ($help) {
889
        return <<END
890
GET::
891
List current billing data.
892
END
893
    }
894
    my $res;
895
    $res .= $Stabile::q->header('application/json') unless ($console);
896
    my $buser = URI::Escape::uri_unescape($params{'user'}) || $user;
897
    my %b;
898
    my @bmonths;
899
    if ($isadmin || $buser eq $user) {
900
        my $bmonth = URI::Escape::uri_unescape($params{'month'}) || $month;
901
        my $byear = URI::Escape::uri_unescape($params{'year'}) || $year;
902
        if ($bmonth eq "all") {
903
            @bmonths = ("01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12");
904
        }
905
        else {
906
            @bmonths = ($bmonth);
907
        }
908

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

    
911
        my @nkeys = keys %nodereg;
912
        foreach my $bm (@bmonths) {
913
            my $vcpuavg = 0;
914
            my $memoryavg = 0;
915
            foreach my $nmac (@nkeys) {
916
                $vcpuavg += $billingreg{"$buser-$nmac-$byear-$bm"}->{'vcpuavg'};
917
                $memoryavg += $billingreg{"$buser-$nmac-$byear-$bm"}->{'memoryavg'};
918
            }
919
            $b{"$buser-$byear-$bm"} = {
920
                id        => "$buser-$byear-$bm",
921
                vcpuavg   => $vcpuavg,
922
                memoryavg => $memoryavg,
923
                month     => $bm + 0,
924
                year      => $byear + 0
925
            }
926
        }
927
        untie %billingreg;
928
    }
929
    my @bvalues = values %b;
930
    $res .= "{\"identifier\": \"id\", \"label\": \"id\", \"items\":" . to_json(\@bvalues) . "}";
931
    return $res;
932
}
933

    
934
# Print list of available actions on objects
935
sub do_plainhelp {
936
    my $res;
937
    $res .= $Stabile::q->header('text/plain') unless $console;
938
    $res .= <<END
939
new [name="name"]
940
* start: Starts a server
941
* destroy: Destroys a server, i.e. terminates the VM, equivalent of turning the power off a physical computer
942
* shutdown: Asks the operating system of a server to shut down via ACPI
943
* suspend: Suspends the VM, effectively putting the server to sleep
944
* resume: Resumes a suspended VM, effectively waking the server from sleep
945
* move [mac="mac"]: Moves a server to specified node. If no node is specified, moves to other node with highest availability
946
index
947
* delete: Deletes a server. Image and network are not deleted, only information about the server. Server cannot be
948
runing
949
* mountcd [cdrom="path"]: Mounts a cd rom
950
END
951
    ;
952
    return $res;
953
}
954

    
955
# Helper function
956
sub recurse($) {
957
	my($path) = @_;
958
	my @files;
959
	## append a trailing / if it's not there
960
	$path .= '/' if($path !~ /\/$/);
961
	## loop through the files contained in the directory
962
	for my $eachFile (glob($path.'*')) {
963
		## if the file is a directory
964
		if( -d $eachFile) {
965
			## pass the directory to the routine ( recursion )
966
			push(@files,recurse($eachFile));
967
		} else {
968
			push(@files,$eachFile);
969
		}
970
	}
971
	return @files;
972
}
973

    
974
sub Start {
975
    my ($uuid, $action, $obj) = @_;
976
    $dmac = $obj->{mac};
977
    $buildsystem = $obj->{buildsystem};
978
    $uistatus = $obj->{uistatus};
979
    if ($help) {
980
        return <<END
981
GET:uuid,mac:
982
Start a server. Supply mac for starting on specific node.
983
END
984
    }
985
    $dmac = $dmac || $params{'mac'};
986
    return "Status=ERROR No uuid\n" unless ($register{$uuid});
987
    my $serv = $register{$uuid};
988
    $postreply = '' if ($buildsystem);
989

    
990
    my $name = $serv->{'name'};
991
    utf8::decode($name);
992
    my $image = $serv->{'image'};
993
    my $image2 = $serv->{'image2'};
994
    my $image3 = $serv->{'image3'};
995
    my $image4 = $serv->{'image4'};
996
    my $memory = $serv->{'memory'};
997
    my $vcpu = $serv->{'vcpu'};
998
    my $vgpu = $serv->{'vgpu'};
999
    my $dbstatus = $serv->{'status'};
1000
    my $mac = $serv->{'mac'};
1001
    my $macname = $serv->{'macname'};
1002
    my $networkuuid1 = $serv->{'networkuuid1'};
1003
    my $networkuuid2 = $serv->{'networkuuid2'};
1004
    my $networkuuid3 = $serv->{'networkuuid3'};
1005
    my $nicmodel1 = $serv->{'nicmodel1'};
1006
    my $nicmac1 = $serv->{'nicmac1'};
1007
    my $nicmac2 = $serv->{'nicmac2'};
1008
    my $nicmac3 = $serv->{'nicmac3'};
1009
    my $boot = $serv->{'boot'};
1010
    my $loader = $serv->{'loader'};
1011
    my $diskbus = $serv->{'diskbus'};
1012
    my $cdrom = $serv->{'cdrom'};
1013
    my $diskdev = "vda";
1014
    my $diskdev2 = "vdb";
1015
    my $diskdev3 = "vdc";
1016
    my $diskdev4 = "vdd";
1017
    if ($diskbus eq "ide") {$diskdev = "hda"; $diskdev2 = "hdb"; $diskdev3 = "hdc"; $diskdev4 = "hdd"};
1018

    
1019
    my $mem = $memory * 1024;
1020

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

    
1023
    my $img = $imagereg{$image};
1024
    my $imagename = $img->{'name'};
1025
    my $imagestatus = $img->{'status'};
1026
    my $img2 = $imagereg{$image2};
1027
    my $image2status = $img2->{'status'};
1028
    my $img3 = $imagereg{$image3};
1029
    my $image3status = $img3->{'status'};
1030
    my $img4 = $imagereg{$image4};
1031
    my $image4status = $img4->{'status'};
1032

    
1033
    if (!$imagereg{$image}) {
1034
        $postreply .= "Status=Error Image $image not found - please select a new image for your server, not starting $name\n";
1035
        untie %imagereg;
1036
        return $postreply;
1037
    }
1038
    untie %imagereg;
1039

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

    
1066
        # Build XML for starting domain
1067
        my $graphics = "vnc";
1068
        $graphics = "rdp" if ($hypervisor eq "vbox");
1069
        my $net1 = $networkreg{$networkuuid1};
1070
        my $networkid1 = $net1->{'id'}; # Get the current vlan id of the network
1071
        my $net2 = $networkreg{$networkuuid2};
1072
        my $networkid2 = $net2->{'id'}; # Get the current vlan id of the network
1073
        my $net3 = $networkreg{$networkuuid2};
1074
        my $networkid3 = $net3->{'id'}; # Get the current vlan id of the network
1075
        my $networkid1ip = $net1->{'internalip'};
1076
        $networkid1ip = $net1->{'externalip'} if ($net1->{'type'} eq 'externalip');
1077

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

    
1082
        my $driver1;
1083
        my $driver2;
1084
        if ($hypervisor eq 'kvm') {
1085
            my $fmt1 = ($image =~ /\.qcow2$/)?'qcow2':'raw';
1086
            my $fmt2 = ($image2 =~ /\.qcow2$/)?'qcow2':'raw';
1087
            my $fmt3 = ($image3 =~ /\.qcow2$/)?'qcow2':'raw';
1088
            my $fmt4 = ($image4 =~ /\.qcow2$/)?'qcow2':'raw';
1089
            my $cache1 = ($image =~ /\/node\//)?'default':'writeback';
1090
            my $cache2 = ($image2 =~ /\/node\//)?'default':'writeback';
1091
            my $cache3 = ($image3 =~ /\/node\//)?'default':'writeback';
1092
            my $cache4 = ($image4 =~ /\/node\//)?'default':'writeback';
1093
            $driver1 = "\n      <driver name='qemu' type='$fmt1' cache='$cache1'/>";
1094
            $driver2 = "\n      <driver name='qemu' type='$fmt2' cache='$cache2'/>";
1095
            $driver3 = "\n      <driver name='qemu' type='$fmt3' cache='$cache3'/>";
1096
            $driver4 = "\n      <driver name='qemu' type='$fmt4' cache='$cache4'/>";
1097
        }
1098

    
1099
        my $networktype1 = "user";
1100
        my $networksource1 = "default";
1101
        my $networkforward1 = "bridge";
1102
        my $networkisolated1 = "no";
1103
        $networksource1 = "vboxnet0" if ($hypervisor eq "vbox");
1104
        if ($networkid1 eq '0') {
1105
            $networktype1 = "user";
1106
            $networkforward1 = "nat";
1107
            $networkisolated1 = "yes"
1108
        } elsif ($networkid1 == 1) {
1109
            $networktype1 = "network" ;
1110
            $networkforward1 = "nat";
1111
            $networkisolated1 = "yes"
1112
        } elsif ($networkid1 > 1) {
1113
            $networktype1 = "bridge";
1114
            $networksource1 = "br$networkid1";
1115
        }
1116
        my $networktype2 = "user";
1117
        my $networksource2 = "default";
1118
        my $networkforward2 = "bridge";
1119
        my $networkisolated2 = "no";
1120
        $networksource2 = "vboxnet0" if ($hypervisor eq "vbox");
1121
        if ($networkid2 eq '0') {
1122
            $networktype2 = "user";
1123
            $networkforward2 = "nat";
1124
            $networkisolated2 = "yes"
1125
        } elsif ($networkid2 == 1) {
1126
            $networktype2 = "network" ;
1127
            $networkforward2 = "nat";
1128
            $networkisolated2 = "yes"
1129
        } elsif ($networkid2 > 1) {
1130
            $networktype2 = "bridge";
1131
            $networksource2 = "br$networkid2";
1132
        }
1133
        my $networktype3 = "user";
1134
        my $networksource3 = "default";
1135
        my $networkforward3 = "bridge";
1136
        my $networkisolated3 = "no";
1137
        $networksource3 = "vboxnet0" if ($hypervisor eq "vbox");
1138
        if ($networkid3 eq '0') {
1139
            $networktype3 = "user";
1140
            $networkforward3 = "nat";
1141
            $networkisolated3 = "yes"
1142
        } elsif ($networkid3 == 1) {
1143
            $networktype3 = "network" ;
1144
            $networkforward3 = "nat";
1145
            $networkisolated3 = "yes"
1146
        } elsif ($networkid3 > 1) {
1147
            $networktype3 = "bridge";
1148
            $networksource3 = "br$networkid3";
1149
        }
1150

    
1151
        my $xml = "<domain type='$hypervisor' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>\n";
1152
#        if ($vgpu && $vgpu ne "--") {
1153
#            $xml .= <<ENDXML2
1154
#  <qemu:commandline>
1155
#    <qemu:arg value='-device'/>
1156
#    <qemu:arg value='vfio-pci,host=01:00.0,x-vga=on'/>
1157
#    <qemu:arg value='-device'/>
1158
#    <qemu:arg value='vfio-pci,host=02:00.0,x-vga=on'/>
1159
#  </qemu:commandline>
1160
#ENDXML2
1161
#            ;
1162
#        }
1163

    
1164
#    <qemu:arg value='-set'/>
1165
#    <qemu:arg value='device.hostdev1.x-vga=on'/>
1166
#    <qemu:arg value='-cpu'/>
1167
#	<qemu:arg value='host,kvm=off'/>
1168
#    <qemu:arg value='-device'/>
1169
#	<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'/>
1170

    
1171
#  <cpu mode='host-model'>
1172
#    <vendor>Intel</vendor>
1173
#    <model>core2duo</model>
1174
#  </cpu>
1175

    
1176
#    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
1177
#    <nvram template='/usr/share/OVMF/OVMF_VARS.fd'/>
1178
        my $loader_xml = <<ENDXML
1179
    <bootmenu enable='yes' timeout='200'/>
1180
    <smbios mode='sysinfo'/>
1181
ENDXML
1182
        ;
1183
if ($loader eq 'uefi') {
1184
    $loader_xml = <<ENDXML
1185
  <loader readonly='yes' secure='no' type='pflash'>/usr/share/ovmf/OVMF.fd</loader>
1186
  <nvram template='/usr/share/OVMF/OVMF_VARS.fd'>/tmp/guest_VARS.fd</nvram>
1187
ENDXML
1188
    ;
1189
}
1190

    
1191
        if ($vgpu && $vgpu ne "--") {
1192
            $xml .= <<ENDXML
1193
  <cpu mode='host-passthrough'>
1194
    <feature policy='disable' name='hypervisor'/>
1195
  </cpu>
1196
ENDXML
1197
;
1198
        } else {
1199
            $xml .= <<ENDXML
1200
  <cpu mode='host-model'>
1201
  </cpu>
1202
ENDXML
1203
            ;
1204
        }
1205
        $xml .=  <<ENDXML
1206
  <name>$uname</name>
1207
  <uuid>$uuid</uuid>
1208
  <memory>$mem</memory>
1209
  <vcpu>$vcpu</vcpu>
1210
  <os>
1211
    <type arch='x86_64' machine='pc'>hvm</type>
1212
    <boot dev='$boot'/>
1213
$loader_xml
1214
  </os>
1215
  <sysinfo type='smbios'>
1216
    <bios>
1217
      <entry name='vendor'>Origo</entry>
1218
    </bios>
1219
    <system>
1220
      <entry name='manufacturer'>Origo</entry>
1221
      <entry name='sku'>$networkid1ip</entry>
1222
    </system>
1223
  </sysinfo>
1224
  <features>
1225
ENDXML
1226
;
1227
        if ($vgpu && $vgpu ne "--") { $xml .= <<ENDXML
1228
    <kvm>
1229
      <hidden state='on'/>
1230
    </kvm>
1231
ENDXML
1232
;
1233
        }
1234
        $xml .= <<ENDXML
1235
    <pae/>
1236
    <acpi/>
1237
    <apic/>
1238
  </features>
1239
  <clock offset='localtime'>
1240
    <timer name='rtc' tickpolicy='catchup' track='guest'/>
1241
    <timer name='pit' tickpolicy='delay'/>
1242
    <timer name='hpet' present='no'/>
1243
  </clock>
1244
  <on_poweroff>destroy</on_poweroff>
1245
  <on_reboot>restart</on_reboot>½
1246
  <on_crash>restart</on_crash>
1247
  <devices>
1248
  <sound model='ich6'/>
1249
ENDXML
1250
;
1251
#        if ($vgpu && $vgpu ne "--") {
1252
#            $xml .= <<ENDXML2
1253
#  <hostdev mode='subsystem' type='pci' managed='yes'>
1254
#    <source>
1255
#      <address domain='0x0000' bus='0x01' slot='0x00' function='0x0' multifunction='on'/>
1256
#    </source>
1257
#  </hostdev>
1258
#  <hostdev mode='subsystem' type='pci' managed='yes'>
1259
#    <source>
1260
#      <address domain='0x0000' bus='0x02' slot='0x00' function='0x0' multifunction='on'/>
1261
#    </source>
1262
#  </hostdev>
1263
#ENDXML2
1264
#;
1265
#        }
1266
        if ($image && $image ne "" && $image ne "--") {
1267
						$xml .= <<ENDXML2
1268
    <disk type='file' device='disk'>
1269
      <source file='$image'/>$driver1
1270
      <target dev='$diskdev' bus='$diskbus'/>
1271
    </disk>
1272
ENDXML2
1273
;
1274
        };
1275

    
1276
        if ($image2 && $image2 ne "" && $image2 ne "--") {
1277
						$xml .= <<ENDXML2
1278
    <disk type='file' device='disk'>$driver2
1279
      <source file='$image2'/>
1280
      <target dev='$diskdev2' bus='$diskbus'/>
1281
    </disk>
1282
ENDXML2
1283
;
1284
        };
1285
        if ($image3 && $image3 ne "" && $image3 ne "--") {
1286
						$xml .= <<ENDXML2
1287
    <disk type='file' device='disk'>$driver3
1288
      <source file='$image3'/>
1289
      <target dev='$diskdev3' bus='$diskbus'/>
1290
    </disk>
1291
ENDXML2
1292
;
1293
        };
1294
        if ($image4 && $image4 ne "" && $image4 ne "--") {
1295
						$xml .= <<ENDXML2
1296
    <disk type='file' device='disk'>$driver4
1297
      <source file='$image4'/>
1298
      <target dev='$diskdev4' bus='$diskbus'/>
1299
    </disk>
1300
ENDXML2
1301
;
1302
        };
1303

    
1304
        unless ($image4 && $image4 ne '--' && $diskbus eq 'ide') {
1305
            if ($cdrom && $cdrom ne "" && $cdrom ne "--") {
1306
						$xml .= <<ENDXML3
1307
    <disk type='file' device='cdrom'>
1308
      <source file='$cdrom'/>
1309
      <target dev='hdd' bus='ide'/>
1310
      <readonly/>
1311
    </disk>
1312
ENDXML3
1313
;
1314
            } elsif ($hypervisor ne "vbox") {
1315
						$xml .= <<ENDXML3
1316
    <disk type='file' device='cdrom'>
1317
      <target dev='hdd' bus='ide'/>
1318
      <readonly/>
1319
    </disk>
1320
ENDXML3
1321
;
1322
            }
1323
        }
1324

    
1325
        $xml .= <<ENDXML4
1326
    <interface type='$networktype1'>
1327
      <source $networktype1='$networksource1'/>
1328
      <forward mode='$networkforward1'/>
1329
      <port isolated='$networkisolated1'/>
1330
      <model type='$nicmodel1'/>
1331
      <mac address='$nicmac1'/>
1332
    </interface>
1333
ENDXML4
1334
;
1335

    
1336
        if (($networkuuid2 && $networkuuid2 ne '--') || $networkuuid2 eq '0') {
1337
            $xml .= <<ENDXML5
1338
    <interface type='$networktype2'>
1339
      <source $networktype2='$networksource2'/>
1340
      <forward mode='$networkforward2'/>
1341
      <port isolated='$networkisolated2'/>
1342
      <model type='$nicmodel1'/>
1343
      <mac address='$nicmac2'/>
1344
    </interface>
1345
ENDXML5
1346
;
1347
        }
1348
        if (($networkuuid3 && $networkuuid3 ne '--') || $networkuuid3 eq '0') {
1349
            $xml .= <<ENDXML5
1350
    <interface type='$networktype3'>
1351
      <source $networktype3='$networksource3'/>
1352
      <forward mode='$networkforward3'/>
1353
      <port isolated='$networkisolated3'/>
1354
      <model type='$nicmodel1'/>
1355
      <mac address='$nicmac3'/>
1356
    </interface>
1357
ENDXML5
1358
;
1359
        }
1360
        $xml .= <<ENDXML6
1361
     <serial type='pty'>
1362
       <source path='/dev/pts/0'/>
1363
       <target port='0'/>
1364
     </serial>
1365
    <input type='tablet' bus='usb'/>
1366
    <graphics type='$graphics' port='$port'/>
1367
  </devices>
1368
</domain>
1369
ENDXML6
1370
;
1371

    
1372

    
1373
#    <graphics type='$graphics' port='$port' keymap='en-us'/>
1374
#     <console type='pty' tty='/dev/pts/0'>
1375
#       <source path='/dev/pts/0'/>
1376
#       <target port='0'/>
1377
#     </console>
1378
#     <graphics type='$graphics' port='-1' autoport='yes'/>
1379

    
1380
        $xmlreg{$uuid} = {
1381
            xml=>URI::Escape::uri_escape($xml)
1382
        };
1383

    
1384
        # Actually ask node to start domain
1385
        if ($targetmac) {
1386
            $register{$uuid}->{'mac'} = $targetmac;
1387
            $register{$uuid}->{'macname'} = $targetname;
1388
            $register{$uuid}->{'macip'} = $targetip;
1389

    
1390
            my $tasks = $nodereg{$targetmac}->{'tasks'};
1391
            $tasks .= "START $uuid $user\n";
1392
    # Also update allowed port forwards - obsolete
1393
    #        $tasks .= "PERMITOPEN $user\n";
1394
            $nodereg{$targetmac}->{'tasks'} = $tasks;
1395
            tied(%nodereg)->commit;
1396
            $uiuuid = $uuid;
1397
            $uidisplayip = $targetip;
1398
            $uidisplayport = $port;
1399
            $register{$uuid}->{'status'} = $uistatus;
1400
            $register{$uuid}->{'statustime'} = $current_time;
1401
            tied(%register)->commit;
1402

    
1403
            # Activate networks
1404
            require "$Stabile::basedir/cgi/networks.cgi";
1405
            Stabile::Networks::Activate($networkuuid1, 'activate');
1406
            Stabile::Networks::Activate($networkuuid2, 'activate') if ($networkuuid2 && $networkuuid2 ne '--');
1407
            Stabile::Networks::Activate($networkuuid3, 'activate') if ($networkuuid3 && $networkuuid3 ne '--');
1408

    
1409
            $main::syslogit->($user, "info", "Marked $name ($uuid) for ". $serv->{'status'} . " on $targetname ($targetmac)");
1410
            $postreply .= "Status=starting OK $uistatus ". $serv->{'name'} . "\n";
1411
        } else {
1412
            $main::syslogit->($user, "info", "Could not find $hypervisor target for creating $uuid ($image)");
1413
            $postreply .= "Status=ERROR problem $uistatus ". $serv->{'name'} . " (unable to locate target node)\n";
1414
        };
1415
    } else {
1416
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
1417
        $postreply .= "Status=ERROR problem $uistatus ". $serv->{'name'} . "\n";
1418
    }
1419
    #return ($uiuuid, $uidisplayip, $uidisplayport, $postreply, $targetmac);
1420
    return $postreply;
1421
}
1422

    
1423
sub do_attach {
1424
    my ($uuid, $action, $obj) = @_;
1425
    if ($help) {
1426
        return <<END
1427
GET:uuid,image:
1428
Attaches an image to a server as a disk device. Image must not be in use.
1429
END
1430
    }
1431
    my $dev = '';
1432
    my $imagenum = 0;
1433
    my $serv = $register{$uuid};
1434

    
1435
    if (!$serv->{'uuid'} || ($serv->{'status'} ne 'running' && $serv->{'status'} ne 'paused')) {
1436
        return "Status=Error Server must exist and be running\n";
1437
    }
1438
    my $macip = $serv->{macip};
1439
    my $image = $obj->{image} || $obj->{path};
1440
    if ($image && !($image =~ /^\//)) { # We have a uuid
1441
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Status=Error Unable to access images register\n"};
1442
        $image = $imagereg2{$image}->{'path'} if ($imagereg2{$image});
1443
        untie %imagereg2;
1444
    }
1445
    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;};
1446
    unless ($macip && $imagereg{$image} && $imagereg{$image}->{'user'} eq $user && $serv->{'user'} eq $user)  {$postreply .= "Status=Error Invalid image or server\n"; return $postreply;};
1447
    if ($imagereg{$image}->{'status'} ne 'unused') {return "Status=Error Image $image is already in use ($imagereg{$image}->{'status'})\n"};
1448

    
1449
    my $cmd = qq|$sshcmd $macip "LIBVIRT_DEFAULT_URI=qemu:///system virsh domblklist $uuid"|;
1450
    my $res = `$cmd`;
1451
    unless ($res =~ /vdb\s+.+/) {$dev = 'vdb'; $imagenum = 2};
1452
    unless ($dev || $res =~ /vdc\s+.+/)  {$dev = 'vdc'; $imagenum = 3};
1453
    unless ($dev || $res =~ /vdd\s+.+/)  {$dev = 'vdd'; $imagenum = 4};
1454
    if (!$dev) {
1455
        $postreply = "Status=Error No more images can be attached\n";
1456
    } else {
1457
        my $xml = <<END
1458
<disk type='file' device='disk'>
1459
  <driver type='qcow2' name='qemu' cache='default'/>
1460
  <source file='$image'/>
1461
  <target dev='$dev' bus='virtio'/>
1462
</disk>
1463
END
1464
;
1465
        $cmd = qq|$sshcmd $macip "echo \\"$xml\\" > /tmp/attach-device-$uuid.xml"|;
1466
        $res = `$cmd`;
1467
        $res .= `$sshcmd $macip LIBVIRT_DEFAULT_URI=qemu:///system virsh attach-device $uuid /tmp/attach-device-$uuid.xml`;
1468
        chomp $res;
1469
        if ($res =~ /successfully/) {
1470
            $postreply .= "Status=OK Attaching $image to $dev\n";
1471
            $imagereg{$image}->{'status'} = 'active';
1472
            $imagereg{$image}->{'domains'} = $uuid;
1473
            $imagereg{$image}->{'domainnames'} = $serv->{'name'};
1474
            $serv->{"image$imagenum"} = $image;
1475
            $serv->{"image$imagenum"."name"} = $imagereg{$image}->{'name'};
1476
            $serv->{"image$imagenum"."type"} = 'qcow2';
1477
        } else {
1478
            $postreply .= "Status=Error Unable to attach image $image to $dev ($res)\n";
1479
        }
1480
    }
1481
    untie %imagereg;
1482
    return $postreply;
1483
}
1484

    
1485
sub do_detach {
1486
    my ($uuid, $action, $obj) = @_;
1487
    if ($help) {
1488
        return <<END
1489
GET:uuid,image:
1490
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.
1491
END
1492
    }
1493
    my $dev = '';
1494
    my $serv = $register{$uuid};
1495

    
1496
    if (!$serv->{'uuid'} || ($serv->{'status'} ne 'running' && $serv->{'status'} ne 'paused')) {
1497
        return "Status=Error Server must exist and be running\n";
1498
    }
1499
    my $macip = $serv->{macip};
1500

    
1501
    my $image = $obj->{image} || $obj->{path} || $serv->{'image2'};
1502
    if ($image && !($image =~ /^\//)) { # We have a uuid
1503
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1504
        $image = $imagereg2{$image}->{'path'} if ($imagereg2{$image});
1505
        untie %imagereg2;
1506
    }
1507
    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;};
1508
    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;};
1509

    
1510
    my $cmd = qq|$sshcmd $macip "LIBVIRT_DEFAULT_URI=qemu:///system virsh domblklist $uuid"|;
1511
    my $res = `$cmd`;
1512
    $dev = $1 if ($res =~ /(vd.)\s+.+$image/);
1513
    if (!$dev) {
1514
        $postreply =  qq|Status=Error Image $image, $cmd, is not currently attached\n|;
1515
    } elsif ($dev eq 'vda') {
1516
        $postreply = "Status=Error You cannot detach the primary image\n";
1517
    } else {
1518
        $res = `$sshcmd $macip LIBVIRT_DEFAULT_URI=qemu:///system virsh detach-disk $uuid $dev`;
1519
        chomp $res;
1520
        if ($res =~ /successfully/) {
1521
            $postreply .= "Status=OK Detaching image $image, $imagereg{$image}->{'uuid'} from $dev\n";
1522
            my $imagenum;
1523
            $imagenum = 2 if ($serv->{'image2'} eq $image);
1524
            $imagenum = 3 if ($serv->{'image3'} eq $image);
1525
            $imagenum = 4 if ($serv->{'image4'} eq $image);
1526
            $imagereg{$image}->{'status'} = 'unused';
1527
            $imagereg{$image}->{'domains'} = '';
1528
            $imagereg{$image}->{'domainnames'} = '';
1529
            if ($imagenum) {
1530
                $serv->{"image$imagenum"} = '';
1531
                $serv->{"image$imagenum"."name"} = '';
1532
                $serv->{"image$imagenum"."type"} = '';
1533
            }
1534
        } else {
1535
            $postreply .= "Status=Error Unable to attach image $image to $dev ($res)\n";
1536
        }
1537
    }
1538
    untie %imagereg;
1539
    return $postreply;
1540
}
1541

    
1542
sub Destroy {
1543
    my ($uuid, $action, $obj) = @_;
1544
    if ($help) {
1545
        return <<END
1546
GET:uuid,wait:
1547
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.
1548
END
1549
    }
1550
    my $uistatus = 'destroying';
1551
    my $name = $register{$uuid}->{'name'};
1552
    my $mac = $register{$uuid}->{'mac'};
1553
    my $macname = $register{$uuid}->{'macname'};
1554
    my $dbstatus = $register{$uuid}->{'status'};
1555
    my $wait = $obj->{'wait'};
1556
    if ($dbstatus eq 'running' or $dbstatus eq 'paused'
1557
        or $dbstatus eq 'shuttingdown' or $dbstatus eq 'starting'
1558
        or $dbstatus eq 'destroying' or $dbstatus eq 'upgrading'
1559
        or $dbstatus eq 'suspending' or $dbstatus eq 'resuming') {
1560
        if ($wait) {
1561
            $postreply = destroyUserServers($user, 1, $uuid);
1562
        } else {
1563
            my $tasks = $nodereg{$mac}->{'tasks'};
1564
            $nodereg{$mac}->{'tasks'} = $tasks . "DESTROY $uuid $user\n";
1565
            tied(%nodereg)->commit;
1566
            $register{$uuid}->{'status'} = $uistatus;
1567
            $register{$uuid}->{'statustime'} = $current_time;
1568
            $uiuuid = $uuid;
1569
            $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus on $macname ($mac)");
1570
            $postreply .= "Status=destroying $uistatus ". $register{$uuid}->{'name'} . "\n";
1571
        }
1572
    } else {
1573
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $name ($uuid)");
1574
        $postreply .= "Status=ERROR problem $uistatus $name\n";
1575
    }
1576
    return $postreply;
1577
}
1578

    
1579
sub getHypervisor {
1580
	my $image = shift;
1581
	# Produce a mapping of image file suffixes to hypervisors
1582
	my %idreg;
1583
    unless ( tie(%idreg,'Tie::DBI', Hash::Merge::merge({table=>'nodeidentities', key=>'identity'}, $Stabile::dbopts)) ) {return "Unable to access nodeidentities register"};
1584
    my @idvalues = values %idreg;
1585
	my %formats;
1586
	foreach my $val (@idvalues) {
1587
		my %h = %$val;
1588
		foreach (split(/,/,$h{'formats'})) {
1589
			$formats{lc $_} = $h{'hypervisor'}
1590
		}
1591
	}
1592
	untie %idreg;
1593

    
1594
	# and then determine the hypervisor in question
1595
	my $hypervisor = "vbox";
1596
	my ($pathname, $path, $suffix) = fileparse($image, '\.[^\.]*');
1597
	$suffix = substr $suffix, 1;
1598
	my $hypervisor = $formats{lc $suffix};
1599
	return $hypervisor;
1600
}
1601

    
1602
sub nicmac1ToUuid {
1603
    my $nicmac1 = shift;
1604
    my $uuid;
1605
    return $uuid unless $nicmac1;
1606
    my @regkeys = (tied %register)->select_where("user = '$user' AND nicmac1 = '$nicmac1");
1607
	foreach my $k (@regkeys) {
1608
	    my $val = $register{$k};
1609
		my %h = %$val;
1610
		if (lc $h{'nicmac1'} eq lc $nicmac1 && $user eq $h{'user'}) {
1611
    		$uuid =  $h{'uuid'};
1612
    		last;
1613
		}
1614
	}
1615
	return $uuid;
1616
}
1617

    
1618
sub randomMac {
1619
	my ( %vendor, $lladdr, $i );
1620
#	$lladdr = '00';
1621
	$lladdr = '52:54:00';# KVM vendor string
1622
	while ( ++$i )
1623
#	{ last if $i > 10;
1624
	{ last if $i > 6;
1625
		$lladdr .= ':' if $i % 2;
1626
		$lladdr .= sprintf "%" . ( qw (X x) [int ( rand ( 2 ) ) ] ), int ( rand ( 16 ) );
1627
	}
1628
	return $lladdr;
1629
}
1630

    
1631
sub overQuotas {
1632
    my $meminc = shift;
1633
    my $vcpuinc = shift;
1634
	my $usedmemory = 0;
1635
	my $usedvcpus = 0;
1636
	my $overquota = 0;
1637
    return $overquota if ($isadmin || $Stabile::userprivileges =~ /a/); # Don't enforce quotas for admins
1638

    
1639
	my $memoryquota = $usermemoryquota;
1640
	my $vcpuquota = $uservcpuquota;
1641

    
1642
	if (!$memoryquota || !$vcpuquota) { # 0 or empty quota means use defaults
1643
        $memoryquota = $memoryquota || $Stabile::config->get('MEMORY_QUOTA');
1644
        $vcpuquota = $vcpuquota || $Stabile::config->get('VCPU_QUOTA');
1645
    }
1646

    
1647
    my @regkeys = (tied %register)->select_where("user = '$user'");
1648
	foreach my $k (@regkeys) {
1649
	    my $val = $register{$k};
1650
		if ($val->{'user'} eq $user && $val->{'status'} ne "shutoff" &&
1651
		    $val->{'status'} ne "inactive" && $val->{'status'} ne "shutdown" ) {
1652

    
1653
		    $usedmemory += $val->{'memory'};
1654
		    $usedvcpus += $val->{'vcpu'};
1655
		}
1656
	}
1657
	$overquota = $usedmemory+$meminc if ($memoryquota!=-1 && $usedmemory+$meminc > $memoryquota); # -1 means no quota
1658
	$overquota = $usedvcpus+$vcpuinc if ($vcpuquota!=-1 && $usedvcpus+$vcpuinc > $vcpuquota);
1659
	return $overquota;
1660
}
1661

    
1662
sub validateItem {
1663
    my $valref = shift;
1664
    my $img = $imagereg{$valref->{'image'}};
1665
    my $imagename = $img->{'name'};
1666
    $valref->{'imagename'} = $imagename if ($imagename);
1667
    my $imagetype = $img->{'type'};
1668
    $valref->{'imagetype'} = $imagetype if ($imagetype);
1669

    
1670
    # imagex may be registered by uuid instead of path - find the path
1671
    # We now support up to 4 images
1672
    for (my $i=2; $i<=4; $i++) {
1673
        if ($valref->{"image$i"} && $valref->{"image$i"} ne '--' && !($valref->{"image$i"} =~ /^\//)) {
1674
            unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1675
            $valref->{"image$i"} = $imagereg2{$valref->{"image$i"}}->{'path'};
1676
            untie %imagereg2;
1677
        }
1678

    
1679
        my $imgi = $imagereg{$valref->{"image$i"}};
1680
        $valref->{"image$i" . 'name'} = $imgi->{'name'} || $valref->{"image$i" . 'name'};
1681
        $valref->{"image$i" . 'type'} = $imgi->{'type'} || $valref->{"image$i" . 'type'};
1682
    }
1683

    
1684
    my $net1 = $networkreg{$valref->{'networkuuid1'}};
1685
    my $networkname1 = $net1->{'name'};
1686
    $valref->{'networkname1'} = $networkname1 if ($networkname1);
1687
    my $net2 = $networkreg{$valref->{'networkuuid2'}};
1688
    my $networkname2 = $net2->{'name'};
1689
    $valref->{'networkname2'} = $networkname2 if ($networkname2);
1690
    my $name = $valref->{'name'};
1691
    $valref->{'name'} = $imagename unless $name;
1692

    
1693
    if ($valref->{'status'} eq "shutoff" || $valref->{'status'} eq "inactive") {
1694
        my $node = $nodereg{$valref->{'mac'}};
1695
        if ($valref->{'image'} =~ /\/mnt\/stabile\/node\//) {
1696
            $valref->{'mac'} = $img->{'mac'};
1697
            $valref->{'macname'} = $node->{'name'};
1698
            $valref->{'macip'} = $node->{'ip'};
1699
        } elsif ($valref->{'image2'} =~ /\/mnt\/stabile\/node\//) {
1700
            $valref->{'mac'} = $imagereg{$valref->{'image2'}}->{'mac'};
1701
            $valref->{'macname'} = $node->{'name'};
1702
            $valref->{'macip'} = $node->{'ip'};
1703
        } elsif ($valref->{'image3'} =~ /\/mnt\/stabile\/node\//) {
1704
            $valref->{'mac'} = $imagereg{$valref->{'image3'}}->{'mac'};
1705
            $valref->{'macname'} = $node->{'name'};
1706
            $valref->{'macip'} = $node->{'ip'};
1707
        } elsif ($valref->{'image4'} =~ /\/mnt\/stabile\/node\//) {
1708
            $valref->{'mac'} = $imagereg{$valref->{'image4'}}->{'mac'};
1709
            $valref->{'macname'} = $node->{'name'};
1710
            $valref->{'macip'} = $node->{'ip'};
1711
        }
1712
    }
1713
# Mark domains we have heard from in the last 20 secs as inactive
1714
    my $dbtimestamp = 0;
1715
    $dbtimestamp = $register{$valref->{'uuid'}}->{'timestamp'} if ($register{$valref->{'uuid'}});
1716
    my $timediff = $current_time - $dbtimestamp;
1717
    if ($timediff >= 20) {
1718
        if  (! ($valref->{'status'} eq "shutoff"
1719
                || $valref->{'status'} eq "starting"
1720
            #    || $valref->{'status'} eq "shuttingdown"
1721
            #    || $valref->{'status'} eq "destroying"
1722
                || ($valref->{'status'} eq "moving" && $timediff<40)
1723
            )) { # Move has probably failed
1724
            $valref->{'status'} = "inactive";
1725
            $imagereg{$valref->{'image'}}->{'status'} = "used" if ($valref->{'image'} && $imagereg{$valref->{'image'}});
1726
            $imagereg{$valref->{'image2'}}->{'status'} = "used" if ($valref->{'image2'} && $imagereg{$valref->{'imag2'}});
1727
            $imagereg{$valref->{'image3'}}->{'status'} = "used" if ($valref->{'image3'} && $imagereg{$valref->{'image3'}});
1728
            $imagereg{$valref->{'image4'}}->{'status'} = "used" if ($valref->{'image4'} && $imagereg{$valref->{'image4'}});
1729
        }
1730
    };
1731
    return $valref;
1732
}
1733

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

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

    
1741
    foreach my $k (@regkeys) {
1742
        my $valref = $register{$k};
1743
        next unless ($userreg{$valref->{'user'}});
1744
        my $dbtimestamp = $valref->{'timestamp'};
1745
        my $dbstatus = $valref->{'status'};
1746
        my $timediff = $current_time - $dbtimestamp;
1747
        my $imgstatus;
1748
        my $domstatus;
1749
        if ($timediff >= 20) {
1750
            if  ( $valref->{'status'} eq "shutoff" ) {
1751
                $imgstatus = 'used';
1752
            } elsif ((  $valref->{'status'} eq "starting"
1753
                            || $valref->{'status'} eq "shuttingdown"
1754
                        ) && $timediff>50) {
1755
                $imgstatus = 'used';
1756
                $domstatus = 'inactive';
1757
            } elsif ($valref->{'status'} eq "destroying" || $valref->{'status'} eq "moving") {
1758
                ;
1759
            } else {
1760
                $domstatus = 'inactive';
1761
                $imgstatus = 'used';
1762
            }
1763
            $valref->{'status'} = $domstatus if ($domstatus);
1764
            my $image = $valref->{'image'};
1765
            my $image2 = $valref->{'image2'};
1766
            my $image3 = $valref->{'image3'};
1767
            my $image4 = $valref->{'image4'};
1768
            $imagereg{$image}->{'status'} = $imgstatus if ($imgstatus);
1769
            $imagereg{$image2}->{'status'} = $imgstatus if ($image2 && $imgstatus);
1770
            $imagereg{$image3}->{'status'} = $imgstatus if ($image3 && $imgstatus);
1771
            $imagereg{$image4}->{'status'} = $imgstatus if ($image4 && $imgstatus);
1772
            if ($domstatus eq 'inactive ' && $dbstatus ne 'inactive') {
1773
                $main::updateUI->({ tab=>'servers',
1774
                                    user=>$valref->{'user'},
1775
                                    uuid=>$valref->{'uuid'},
1776
                                    sender=>'updateRegister',
1777
                                    status=>'inactive'})
1778
            }
1779
        };
1780

    
1781
    }
1782
    untie %userreg;
1783
    untie %imagereg;
1784
}
1785

    
1786

    
1787
sub locateTargetNode {
1788
    my ($uuid, $dmac, $mem, $vcpu, $image, $image2, $image3, $image4, $hypervisor, $smac)= @_;
1789
    my $targetname;
1790
    my $targetip;
1791
    my $port;
1792
    my $targetnode;
1793
    my $targetindex; # Availability index of located target node
1794
    my %avhash;
1795

    
1796
    my $mnode = $register{$uuid};
1797
    $dmac = $mnode->{'mac'}
1798
        if (!$dmac
1799
            && $mnode->{'locktonode'} eq 'true'
1800
            && $mnode->{'mac'}
1801
            && $mnode->{'mac'} ne '--'
1802
            );
1803

    
1804
    $dmac = '' unless ($isadmin); # Only allow admins to select specific node
1805
    if ($dmac && !$nodereg{$dmac}) {
1806
        $main::syslogit->($user, "info", "The target node $dmac no longer exists, starting $uuid on another node if possible");
1807
        $dmac = '';
1808
    }
1809

    
1810
    my $imageonnode = ($image =~ /\/mnt\/stabile\/node\//
1811
                                          || $image2 =~ /\/mnt\/stabile\/node\//
1812
                                          || $image3 =~ /\/mnt\/stabile\/node\//
1813
                                          || $image4 =~ /\/mnt\/stabile\/node\//
1814
                                          );
1815

    
1816
    foreach $node (values %nodereg) {
1817
        my $nstatus = $node->{'status'};
1818
        my $maintenance = $node->{'maintenance'};
1819
        my $nmac = $node->{'mac'};
1820

    
1821
        if (($nstatus eq 'running' || $nstatus eq 'asleep' || $nstatus eq 'maintenance' || $nstatus eq 'waking')
1822
         && $smac ne $nmac
1823
         && (( ($node->{'memfree'} > $mem+512*1024)
1824
         && (($node->{'vmvcpus'} + $vcpu) <= ($cpuovercommision * $node->{'cpucores'} * $node->{'cpucount'})) ) || $action eq 'listnodeavailability')
1825
        ) {
1826
        # Determine how available this node is
1827
        # Available memory
1828
            my $memweight = 0.2; # memory weighing factor
1829
            my $memindex = $avhash{$nmac}->{'memindex'} = int(100* $memweight* $node->{'memfree'} / (1024*1024) )/100;
1830
        # Free cores
1831
            my $cpuindex = $avhash{$nmac}->{'cpuindex'} = int(100*($cpuovercommision * $node->{'cpucores'} * $node->{'cpucount'} - $node->{'vmvcpus'} - $node->{'reservedvcpus'}))/100;
1832
        # Asleep - not asleep gives a +3
1833
            my $sleepindex = $avhash{$nmac}->{'sleepindex'} = ($node->{'status'} eq 'asleep' || $node->{'status'} eq 'waking')?'0':'3';
1834
            $avhash{$nmac}->{'vmvcpus'} = $node->{'vmvcpus'};
1835
#            $avhash{$nmac}->{'cpucommision'} = $cpuovercommision * $node->{'cpucores'} * $node->{'cpucount'};
1836
#            $avhash{$nmac}->{'cpureservation'} = $node->{'vmvcpus'} + $node->{'reservedvcpus'};
1837

    
1838
            $avhash{$nmac}->{'name'} = $node->{'name'};
1839
            $avhash{$nmac}->{'mac'} = $node->{'mac'};
1840

    
1841
            my $aindex = $memindex + $cpuindex + $sleepindex;
1842
        # Don't use nodes that are out of memory of cores
1843
            $aindex = 0 if ($memindex <= 0 || $cpuindex <= 0);
1844
            $avhash{$nmac}->{'index'} = $aindex;
1845

    
1846
            $avhash{$nmac}->{'storfree'} = $node->{'storfree'};
1847
            $avhash{$nmac}->{'memfree'} = $node->{'memfree'};
1848
            $avhash{$nmac}->{'ip'} = $node->{'ip'};
1849
            $avhash{$nmac}->{'identity'} = $node->{'identity'};
1850
            $avhash{$nmac}->{'status'} = $node->{'status'};
1851
            $avhash{$nmac}->{'maintenance'} = $maintenance;
1852
            $avhash{$nmac}->{'reservedvcpus'} = $node->{'reservedvcpus'};
1853
            my $nodeidentity = $node->{'identity'};
1854
            $nodeidentity = 'kvm' if ($nodeidentity eq 'local_kvm');
1855

    
1856
            if ($hypervisor eq $nodeidentity) {
1857
                # If image is on node, we must start on same node - registered when moving image
1858
                if ($imageonnode) {
1859
                    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1860
                    $dmac = $imagereg{$image}->{'mac'};
1861
                    $dmac = $imagereg{$image2}->{'mac'} unless ($dmac);
1862
                    $dmac = $imagereg{$image3}->{'mac'} unless ($dmac);
1863
                    $dmac = $imagereg{$image4}->{'mac'} unless ($dmac);
1864

    
1865
                    untie %imagereg;
1866
                    if (!$dmac) {
1867
                        $postreply .= "Status=ERROR Image node not found\n";
1868
                        last;
1869
                    }
1870
                }
1871
                $dmac = "" if ($dmac eq "--");
1872
            # If a specific node is asked for, match mac addresses
1873
                if ($dmac eq $nmac) {
1874
                    $targetnode = $node;
1875
                    last;
1876
                } elsif (!$dmac && $nstatus ne "maintenance" && !$maintenance) {
1877
            # pack or disperse
1878
                    if (!$targetindex) {
1879
                        $targetindex = $aindex;
1880
                        $targetnode = $node;
1881
                    } elsif ($dpolicy eq 'pack') {
1882
                        if ($aindex < $targetindex) {
1883
                            $targetnode = $node;
1884
                            $targetindex = $aindex;
1885
                        }
1886
                    } else {
1887
                        if ($aindex > $targetindex) {
1888
                            $targetnode = $node;
1889
                            $targetindex = $aindex;
1890
                        }
1891
                    }
1892
                }
1893
            }
1894
        }
1895
    }
1896

    
1897
    if ($targetnode && $uuid) {
1898
        if ($targetnode->{'status'} eq 'asleep') {
1899
            my $nmac = $targetnode->{'mac'};
1900
            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);
1901
            my $nlogmsg = "Node $nmac marked for wake ";
1902
            if ($brutalsleep && (
1903
                    ($targetnode->{'amtip'} && $targetnode->{'amtip'} ne '--')
1904
                || ($targetnode->{'ipmiip'} && $targetnode->{'ipmiip'} ne '--')
1905
                )) {
1906
                my $wakecmd;
1907
                if ($targetnode->{'amtip'} && $targetnode->{'amtip'} ne '--') {
1908
                    $wakecmd = "echo 'y' | AMT_PASSWORD='$amtpasswd' /usr/bin/amttool $targetnode->{'amtip'} powerup pxe";
1909
                } else {
1910
                    $wakecmd = "ipmitool -I lanplus -H $targetnode->{'ipmiip'} -U ADMIN -P ADMIN power on";
1911
                }
1912
                $nlogmsg .= `$wakecmd`;
1913
            } else {
1914
                my $broadcastip = $targetnode->{'ip'};
1915
                $broadcastip =~ s/\.\d{1,3}$/.255/;
1916
                $nlogmsg .= 'on lan ' . `/usr/bin/wakeonlan -i $broadcastip $realmac`;
1917
            }
1918
            $targetnode->{'status'} = "waking";
1919
            $nlogmsg =~ s/\n/ /g;
1920
            $main::syslogit->($user, "info", $nlogmsg);
1921
            $postreply .= "Status=OK waking $targetnode->{'name'}\n";
1922
        }
1923
        $targetname = $targetnode->{'name'};
1924
        $targetmac = $targetnode->{'mac'};
1925
        $targetip = $targetnode->{'ip'};
1926
        $targetip = $targetnode->{'ip'};
1927
        my $porttaken = 1;
1928
        while ($porttaken) {
1929
            $porttaken = 0;
1930
            $port = $targetnode->{'vms'} + (($hypervisor eq "vbox")?3389:5900);
1931
            $port += int(rand(200));
1932
            my @regkeys = (tied %register)->select_where("port = '$port' AND macip = '$targetip'");
1933
            foreach my $k (@regkeys) {
1934
                $r = $register{$k};
1935
                if ($r->{'port'} eq $port && $r->{'macip'} eq $targetip) {
1936
                    $porttaken = 1;
1937
                }
1938
            }
1939
        }
1940
        $targetnode->{'vms'}++;
1941
        $targetnode->{'vmvcpus'} += $vcpu;
1942
        $register{$uuid}->{'port'} = $port;
1943
#        $register{$uuid}->{'mac'} = $targetmac;
1944
#        $register{$uuid}->{'macname'} = $targetname;
1945
#        $register{$uuid}->{'macip'} = $targetip;
1946
        $register{$uuid}->{'display'} = (($hypervisor eq "vbox")?'rdp':'vnc');
1947
    } else {
1948
        my $macstatus;
1949
        $macstatus = $nodereg{$dmac}->{status} if ($nodereg{$dmac});
1950
        $main::syslogit->($user, "info", "Could not find target for $uuid, $dmac, $mem, $vcpu, $image, $image2,$image3,$image4, $hypervisor, $smac, dmac-status: $macstatus") if ($uuid);
1951
    }
1952
    return ($targetmac, $targetname, $targetip, $port, \%avhash);
1953
}
1954

    
1955
sub destroyUserServers {
1956
    my $username = shift;
1957
    my $wait = shift; # Should we wait for servers do die
1958
    my $duuid = shift;
1959
    return unless ($isadmin || $user eq $username);
1960
    my @updateList;
1961

    
1962
    my @regkeys = (tied %register)->select_where("user = '$username'");
1963
    foreach my $uuid (@regkeys) {
1964
        if ($register{$uuid}->{'user'} eq $username
1965
            && $register{$uuid}->{'status'} ne 'shutoff'
1966
            && (!$duuid || $duuid eq $uuid)
1967
        ) {
1968
            $postreply .= "Destroying $username server $register{$uuid}->{'name'}, $uuid\n";
1969
            Destroy($uuid);
1970
            push (@updateList,{ tab=>'servers',
1971
                                user=>$user,
1972
                                uuid=>$duuid,
1973
                                status=>'destroying'});
1974
        }
1975
    }
1976
    $main::updateUI->(@updateList) if (@updateList);
1977
    if ($wait) {
1978
        my @regkeys = (tied %register)->select_where("user = '$username'");
1979
        my $activeservers = 1;
1980
        my $i = 0;
1981
        while ($activeservers && $i<10) {
1982
            $activeservers = 0;
1983
            foreach my $k (@regkeys) {
1984
                my $valref = $register{$k};
1985
                if ($username eq $valref->{'user'}
1986
                    && ($valref->{'status'} ne 'shutoff'
1987
                    && $valref->{'status'} ne 'inactive')
1988
                    && (!$duuid || $duuid eq $valref->{'uuid'})
1989
                ) {
1990
                    $activeservers = $valref->{'uuid'};
1991
                }
1992
            }
1993
            $i++;
1994
            if ($activeservers) {
1995
                my $res .= "Status=OK Waiting $i for server $register{$activeservers}->{'name'}, $register{$activeservers}->{'status'} to die...\n";
1996
            #    print $res if ($console);
1997
                $postreply .= $res;
1998
                sleep 2;
1999
            }
2000
        }
2001
        $postreply .= "Status=OK Servers halted for $username\n" unless ($activeservers);
2002
    }
2003
    return $postreply;
2004
}
2005

    
2006
sub removeUserServers {
2007
    my $username = shift;
2008
    my $uuid = shift;
2009
    my $destroy = shift; # Should running servers be destroyed before removing
2010
    return unless (($isadmin || $user eq $username) && !$isreadonly);
2011
    $user = $username;
2012
    my @regkeys = (tied %register)->select_where("user = '$username'");
2013
    foreach my $ruuid (@regkeys) {
2014
        next if ($uuid && $ruuid ne $uuid);
2015
        if ($destroy && $register{$ruuid}->{'user'} eq $username && ($register{$ruuid}->{'status'} ne 'shutoff' && $register{$ruuid}->{'status'} ne 'inactive')) {
2016
            destroyUserServers($username, 1, $ruuid);
2017
        }
2018

    
2019
        if ($register{$ruuid}->{'user'} eq $username && ($register{$ruuid}->{'status'} eq 'shutoff' || $register{$ruuid}->{'status'} eq 'inactive')) {
2020
            $postreply .= "Removing $username server $register{$ruuid}->{'name'}, $ruuid" . ($console?'':'<br>') . "\n";
2021
            Remove($ruuid);
2022
        }
2023
    }
2024
}
2025

    
2026
sub Remove {
2027
    my ($uuid, $action) = @_;
2028
    if ($help) {
2029
        return <<END
2030
DELETE:uuid:
2031
Removes a server. Server must be shutoff. Does not remove associated images or networks.
2032
END
2033
    }
2034
    my $reguser = $register{$uuid}->{'user'};
2035
    my $dbstatus = $register{$uuid}->{'status'};
2036
    my $image = $register{$uuid}->{'image'};
2037
    my $image2 = $register{$uuid}->{'image2'};
2038
    my $image3 = $register{$uuid}->{'image3'};
2039
    my $image4 = $register{$uuid}->{'image4'};
2040
    my $name = $register{$uuid}->{'name'};
2041
    $image2 = '' if ($image2 eq '--');
2042
    $image3 = '' if ($image3 eq '--');
2043
    $image4 = '' if ($image4 eq '--');
2044

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

    
2049
        # Delete software packages and monitors from register
2050
        $postmsg .= deletePackages($uuid);
2051
        my $sname = $register{$uuid}->{'name'};
2052
        utf8::decode($sname);
2053
        $postmsg .= deleteMonitors($uuid)?" deleted monitors for $sname ":'';
2054

    
2055
        delete $register{$uuid};
2056
        delete $xmlreg{$uuid};
2057

    
2058
        unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
2059
        $imagereg{$image}->{'status'} = "unused" if ($imagereg{$image});
2060
        $imagereg{$image2}->{'status'} = "unused" if ($image2 && $imagereg{$image2});
2061
        $imagereg{$image3}->{'status'} = "unused" if ($image3 && $imagereg{$image3});
2062
        $imagereg{$image4}->{'status'} = "unused" if ($image4 && $imagereg{$image4});
2063
        untie %imagereg;
2064

    
2065
        # Delete metrics
2066
        my $metricsdir = "/var/lib/graphite/whisper/domains/$uuid";
2067
        `rm -r $metricsdir` if (-e $metricsdir);
2068
        my $rrdfile = "/var/cache/rrdtool/".$uuid."_highres.rrd";
2069
        `rm $rrdfile` if (-e $rrdfile);
2070

    
2071
        $main::syslogit->($user, "info", "Deleted domain $uuid from db");
2072
        utf8::decode($name);
2073
        $postmsg .= " deleted server $name";
2074
        $postreply = "[]";
2075
        sleep 1;
2076
    } else {
2077
        $postreply .= "Status=ERROR Cannot delete a $dbstatus server\n";
2078
    }
2079
    return $postreply;
2080
}
2081

    
2082
# Delete all monitors belonging to a server
2083
sub deleteMonitors {
2084
    my ($serveruuid) = @_;
2085
    my $match;
2086
    if ($serveruuid) {
2087
        if ($register{$serveruuid}->{'user'} eq $user || $isadmin) {
2088
            local($^I, @ARGV) = ('.bak', "/etc/mon/mon.cf");
2089
            # undef $/; # This makes <> read in the entire file in one go
2090
            my $uuidmatch;
2091
            while (<>) {
2092
                if (/^watch (\S+)/) {
2093
                    if ($1 eq $serveruuid) {$uuidmatch = $serveruuid}
2094
                    else {$uuidmatch = ''};
2095
                };
2096
                if ($uuidmatch) {
2097
                    $match = 1;
2098
                } else {
2099
                    #chomp;
2100
                    print unless (/^hostgroup $serveruuid/);
2101
                }
2102
                close ARGV if eof;
2103
            }
2104
            #$/ = "\n";
2105
        }
2106
        unlink glob "/var/log/stabile/*:$serveruuid:*";
2107
    }
2108
    `/usr/bin/moncmd reset keepstate` if ($match);
2109
    return $match;
2110
}
2111

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

    
2116
    my @domains;
2117
    if ($issystem) {
2118
        foreach my $valref (values %register) {
2119
            if (($valref->{'system'} eq $uuid || $uuid eq '*')
2120
                    && ($valref->{'user'} eq $user || $fulllist)) {
2121
                push(@domains, $valref->{'uuid'});
2122
            }
2123
        }
2124
    } else { # Allow if domain no longer exists or belongs to user
2125
        push(@domains, $uuid) if (!$register{$uuid} || $register{$uuid}->{'user'} eq $user || $fulllist);
2126
    }
2127

    
2128
    foreach my $domuuid (@domains) {
2129
        foreach my $packref (values %packreg) {
2130
            my $id = $packref->{'id'};
2131
            if (substr($id, 0,36) eq $domuuid || ($uuid eq '*' && $packref->{'user'} eq $user)) {
2132
                delete $packreg{$id};
2133
            }
2134
        }
2135
    }
2136
    tied(%packreg)->commit;# if (%packreg);
2137
    if ($issystem) {
2138
        my $sname = $register{$uuid}->{'name'};
2139
        utf8::decode($sname);
2140
        return "Status=OK Cleared packages for $sname\n";
2141
    } elsif ($register{$uuid}) {
2142
        my $sname = $register{$uuid}->{'name'};
2143
        utf8::decode($sname);
2144
        return "Status=OK Cleared packages for $sname\n";
2145
    } else {
2146
        return "Status=OK Cleared packages. System not registered\n";
2147
    }
2148
}
2149

    
2150
sub Save {
2151
    my ($uuid, $action, $obj) = @_;
2152
    if ($help) {
2153
        return <<END
2154
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:
2155
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.
2156
Depending on your privileges not all changes are permitted. If you save without specifying a uuid, a new server is created.
2157
If you pass [user] parameter it is assumed you want to move server to this user's account.
2158
Supported parameters:
2159

    
2160
uuid: UUID
2161
name: string
2162
user: string
2163
system: UUID of stack this server belongs to
2164
autostart: true|false
2165
locktonode: true|false
2166
mac: MAC address of target node
2167

    
2168
memory: int bytes
2169
vcpu: int
2170
boot: hd|cdrom|network
2171
loader: bios|uefi
2172
diskbus: virtio|ide|scsi
2173
nicmodel1: virtio|rtl8139|ne2k_pci|e1000|i82551|i82557b|i82559er|pcnet
2174
vgpu: int
2175

    
2176
cdrom: string path
2177
image: string path
2178
image2: string path
2179
image3: string path
2180
image4: string path
2181

    
2182
networkuuid1: UUID of network connection
2183
networkuuid2: UUID of network connection
2184
networkuuid3: UUID of network connection
2185

    
2186
END
2187
    }
2188

    
2189
# notes, opemail, opfullname, opphone, email, fullname, phone, services, recovery, alertemail
2190
# notes: string
2191
# opemail: string
2192
# opfullname: string
2193
# opphone: string
2194
# email: string
2195
# fullname: string
2196
# phone: string
2197
# services: string
2198
# recovery: string
2199
# alertemail: string
2200

    
2201
    my $system = $obj->{system};
2202
    my $newsystem = $obj->{newsystem};
2203
    my $buildsystem = $obj->{buildsystem};
2204
    my $nicmac1 = $obj->{nicmac1};
2205
    $console = $console || $obj->{console};
2206

    
2207
    $postmsg = '' if ($buildsystem);
2208
    if (!$uuid && $nicmac1) {
2209
        $uuid = nicmac1ToUuid($nicmac1); # If no uuid try to locate based on mac
2210
    }
2211
    if (!$uuid && $uripath =~ /servers(\.cgi)?\/(.+)/) { # Try to parse uuid out of URI
2212
        my $huuid = $2;
2213
        if ($ug->to_string($ug->from_string($huuid)) eq $huuid) { # Check for valid uuid
2214
            $uuid = $huuid;
2215
        }
2216
    }
2217
    my $regserv = $register{$uuid};
2218
    my $status = $regserv->{'status'} || 'new';
2219
    if ((!$uuid) && $status eq 'new') {
2220
        my $ug = new Data::UUID;
2221
        $uuid = $ug->create_str();
2222
    };
2223
    unless ($uuid && length $uuid == 36){
2224
        $postmsg = "Status=Error No valid uuid ($uuid), $obj->{image}";
2225
        return $postmsg;
2226
    }
2227
    $nicmac1 = $nicmac1 || $regserv->{'nicmac1'};
2228
    my $name = $obj->{name} || $regserv->{'name'};
2229
    my $memory = $obj->{memory} || $regserv->{'memory'};
2230
    my $vcpu = $obj->{vcpu} || $regserv->{'vcpu'};
2231
    my $image = $obj->{image} || $regserv->{'image'};
2232
    my $imagename = $obj->{imagename} || $regserv->{'imagename'};
2233
    my $image2 = $obj->{image2} || $regserv->{'image2'};
2234
    my $image2name = $obj->{image2name} || $regserv->{'image2name'};
2235
    my $image3 = $obj->{image3} || $regserv->{'image3'};
2236
    my $image3name = $obj->{image3name} || $regserv->{'image3name'};
2237
    my $image4 = $obj->{image4} || $regserv->{'image4'};
2238
    my $image4name = $obj->{image4name} || $regserv->{'image4name'};
2239
    my $diskbus = $obj->{diskbus} || $regserv->{'diskbus'};
2240
    my $cdrom = $obj->{cdrom} || $regserv->{'cdrom'};
2241
    my $boot = $obj->{boot} || $regserv->{'boot'};
2242
    my $loader = $obj->{loader} || $regserv->{'loader'};
2243
    my $networkuuid1 = ($obj->{networkuuid1} || $obj->{networkuuid1} eq '0')?$obj->{networkuuid1}:$regserv->{'networkuuid1'};
2244
    my $networkid1 = $obj->{networkid1} || $regserv->{'networkid1'};
2245
    my $networkname1 = $obj->{networkname1} || $regserv->{'networkname1'};
2246
    my $nicmodel1 = $obj->{nicmodel1} || $regserv->{'nicmodel1'};
2247
    my $networkuuid2 = ($obj->{networkuuid2} || $obj->{networkuuid2} eq '0')?$obj->{networkuuid2}:$regserv->{'networkuuid2'};
2248
    my $networkid2 = $obj->{networkid2} || $regserv->{'networkid2'};
2249
    my $networkname2 = $obj->{networkname2} || $regserv->{'networkname2'};
2250
    my $nicmac2 = $obj->{nicmac2} || $regserv->{'nicmac2'};
2251
    my $networkuuid3 = ($obj->{networkuuid3} || $obj->{networkuuid3} eq '0')?$obj->{networkuuid3}:$regserv->{'networkuuid3'};
2252
    my $networkid3 = $obj->{networkid3} || $regserv->{'networkid3'};
2253
    my $networkname3 = $obj->{networkname3} || $regserv->{'networkname3'};
2254
    my $nicmac3 = $obj->{nicmac3} || $regserv->{'nicmac3'};
2255
    my $notes = $obj->{notes} || $regserv->{'notes'};
2256
    my $autostart = $obj->{autostart} || $regserv->{'autostart'};
2257
    my $locktonode = $obj->{locktonode} || $regserv->{'locktonode'};
2258
    my $mac = $obj->{mac} || $regserv->{'mac'};
2259
    my $created = $regserv->{'created'} || time;
2260
    # Sanity checks
2261
    my $tenderpaths = $Stabile::config->get('STORAGE_POOLS_LOCAL_PATHS') || "/mnt/stabile/images";
2262
    my @tenderpathslist = split(/,\s*/, $tenderpaths);
2263

    
2264
    $networkid1 = $networkreg{$networkuuid1}->{'id'};
2265
    my $networktype1 = $networkreg{$networkuuid1}->{'type'};
2266
    my $networktype2;
2267
    if (!$nicmac1 || $nicmac1 eq "--") {$nicmac1 = randomMac();}
2268
    if ($networkuuid2 && $networkuuid2 ne "--") {
2269
        $networkid2 = $networkreg{$networkuuid2}->{'id'};
2270
        $nicmac2 = randomMac() if (!$nicmac2 || $nicmac2 eq "--");
2271
        $networktype2 = $networkreg{$networkuuid2}->{'type'};
2272
    }
2273
    if ($networkuuid3 && $networkuuid3 ne "--") {
2274
        $networkid3 = $networkreg{$networkuuid3}->{'id'};
2275
        $networkname3 = $networkreg{$networkuuid3}->{'name'};
2276
        $nicmac3 = randomMac() if (!$nicmac3 || $nicmac3 eq "--");
2277
        $networktype3 = $networkreg{$networkuuid3}->{'type'};
2278
    }
2279

    
2280
    my $imgdup;
2281
    my $netdup;
2282
    my $json_text; # returned if all goes well
2283

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

    
2286
    if ($networkid1 > 1 && $networkid2 > 1 && $networktype1 ne 'gateway' && $networktype2 ne 'gateway'
2287
        && $networkuuid1 eq $networkuuid2) {
2288
        $netdup = 1;
2289
    }
2290
    if ($networkid1 > 1 && $networkid3 > 1 && $networktype1 ne 'gateway' && $networktype3 ne 'gateway'
2291
        && $networkuuid1 eq $networkuuid3) {
2292
        $netdup = 11;
2293
    }
2294

    
2295
    if ($image eq $image2
2296
        || $image eq $image3
2297
        || $image eq $image4
2298
        || $image2 && $image2 ne '--' && $image2 eq $image3
2299
        || $image2 && $image2 ne '--' && $image2 eq $image4
2300
        || $image3 && $image3 ne '--' && $image3 eq $image4
2301
    ) {
2302
        $imgdup = 1;
2303
    } elsif ($image =~ m/\.master\.qcow2/
2304
        || $image2 =~ m/\.master\.qcow2/
2305
        || $image3 =~ m/\.master\.qcow2/
2306
        || $image4 =~ m/\.master\.qcow2/
2307
    ) {
2308
        $imgdup = 2;
2309
    } else {
2310
        # Check if another server is using image
2311
        my @regkeys = (tied %register)->select_where("user = '$user' OR user = 'common'");
2312
        foreach my $k (@regkeys) {
2313
            my $val = $register{$k};
2314
            my %h = %$val;
2315
            if ($h{'uuid'} ne $uuid) {
2316
                if (
2317
                    $image eq $h{'image'} || $image eq $h{'image2'}|| $image eq $h{'image3'}|| $image eq $h{'image4'}
2318
                ) {
2319
                    $imgdup = 51;
2320
                } elsif ($image2 && $image2 ne "--" &&
2321
                    ($image2 eq $h{'image'} || $image2 eq $h{'image2'} || $image2 eq $h{'image3'} || $image2 eq $h{'image4'})
2322
                ) {
2323
                    $imgdup = 52;
2324
                } elsif ($image3 && $image3 ne "--" &&
2325
                    ($image3 eq $h{'image'} || $image3 eq $h{'image2'} || $image3 eq $h{'image3'} || $image3 eq $h{'image4'})
2326
                ) {
2327
                    $imgdup = 53;
2328
                } elsif ($image4 && $image4 ne "--" &&
2329
                    ($image4 eq $h{'image'} || $image4 eq $h{'image2'} || $image4 eq $h{'image3'} || $image4 eq $h{'image4'})
2330
                ) {
2331
                    $imgdup = 54;
2332
                }
2333

    
2334
                if ($networkid1>1) {
2335
                    if ($networktype1 ne 'gateway' &&
2336
                        ($networkuuid1 eq $h{'networkuuid1'} || $networkuuid1 eq $h{'networkuuid2'})
2337
                    ) {
2338
                        $netdup = 51;
2339
                    }
2340
                }
2341
                if ($networkid2>1) {
2342
                    if ($networktype2 ne 'gateway' && $networkuuid2 && $networkuuid2 ne "--" &&
2343
                        ($networkuuid2 eq $h{'networkuuid1'} || $networkuuid2 eq $h{'networkuuid2'})
2344
                    ) {
2345
                        $netdup = 52;
2346
                    }
2347
                }
2348
            }
2349
        }
2350
        my $legalpath;
2351
        if ($image =~ m/\/mnt\/stabile\/node\/$user/) {
2352
            $legalpath = 1;
2353
        } else {
2354
            foreach my $path (@tenderpathslist) {
2355
                if ($image =~ m/$path\/$user/) {
2356
                    $legalpath = 1;
2357
                    last;
2358
                }
2359
            }
2360
        }
2361
        $imgdup = 6 unless $legalpath;
2362
        if ($image2 && $image2 ne "--") { # TODO: We should probably check for conflicting nodes for image3 and image 4 too
2363
            if ($image2 =~ m/\/mnt\/stabile\/node\/$user/) {
2364
                if ($image =~ m/\/mnt\/stabile\/node\/$user/) {
2365
                    if ($imagereg{$image}->{'mac'} eq $imagereg{$image2}->{'mac'}) {
2366
                        $legalpath = 1;
2367
                    } else {
2368
                        $legalpath = 0; # Images are on two different nodes
2369
                    }
2370
                } else {
2371
                    $legalpath = 1;
2372
                }
2373
            } else {
2374
                $legalpath = 0;
2375
                foreach my $path (@tenderpathslist) {
2376
                    if ($image2 =~ m/$path\/$user/) {
2377
                        $legalpath = 1;
2378
                        last;
2379
                    }
2380
                }
2381
            }
2382
            $imgdup = 7 unless $legalpath;
2383
        }
2384
    }
2385

    
2386
    if (!$imgdup && !$netdup) {
2387
        if ($status eq "new") {
2388
            $status = "shutoff";
2389
            $name = $name || 'New Server';
2390
            $memory = $memory || 1024;
2391
            $vcpu = $vcpu || 1;
2392
            $imagename = $imagename || '--';
2393
            $image2 = $image2 || '--';
2394
            $image2name = $image2name || '--';
2395
            $image3 = $image3 || '--';
2396
            $image3name = $image3name || '--';
2397
            $image4 = $image4 || '--';
2398
            $image4name = $image4name || '--';
2399
            $diskbus = $diskbus || 'ide';
2400
            $cdrom = $cdrom || '--';
2401
            $boot = $boot || 'hd';
2402
            $loader = $loader || 'bios';
2403
            $networkuuid1 = $networkuuid1 || 1;
2404
            $networkid1 = $networkid1 || 1;
2405
            $networkname1 = $networkname1 || '--';
2406
            $nicmodel1 = $nicmodel1 || 'rtl8139';
2407
            $nicmac1 = $nicmac1 || randomMac();
2408
            $networkuuid2 = $networkuuid2 || '--';
2409
            $networkid2 = $networkid2 || '--';
2410
            $networkname2 = $networkname2 || '--';
2411
            $nicmac2 = $nicmac2 || randomMac();
2412
            $networkuuid3 = $networkuuid3 || '--';
2413
            $networkid3 = $networkid3 || '--';
2414
            $networkname3 = $networkname3 || '--';
2415
            $nicmac3 = $nicmac3 || randomMac();
2416
            #    $uiuuid = $uuid; # No need to update ui for new server with jsonreststore
2417
            $postmsg .= "OK Created new server: $name";
2418
            $postmsg .= ", uuid: $uuid " if ($console);
2419
        }
2420
        # Update status of images
2421
        my @imgs = ($image, $image2, $image3, $image4);
2422
        my @imgkeys = ('image', 'image2', 'image3', 'image4');
2423
        for (my $i=0; $i<4; $i++) {
2424
            my $img = $imgs[$i];
2425
            my $k = $imgkeys[$i];
2426
            my $regimg = $imagereg{$img};
2427
            # if ($img && $img ne '--' && ($status eq 'new' || $img ne $regserv->{$k})) { # Servers image changed - update image status
2428
            if ($img && $img ne '--') { # Always update image status
2429
                $regimg->{'status'} = 'used' if (
2430
                    $regimg->{'status'} eq 'unused'
2431
                        # Image cannot be active if server is shutoff
2432
                        || ($regimg->{'status'} eq 'active' && $status eq 'shutoff')
2433
                );
2434
                $regimg->{'domains'} = $uuid;
2435
                $regimg->{'domainnames'} = $name;
2436
            }
2437
            # If image has changed, release the old image
2438
            if ($status ne 'new' && $img ne $regserv->{$k} && $imagereg{$regserv->{$k}}) {
2439
                $imagereg{$regserv->{$k}}->{'status'} = 'unused';
2440
                delete $imagereg{$regserv->{$k}}->{'domains'};
2441
                delete $imagereg{$regserv->{$k}}->{'domainnames'};
2442
            }
2443
        }
2444

    
2445
        my $valref = {
2446
            uuid=>$uuid,
2447
            user=>$user,
2448
            name=>$name,
2449
            memory=>$memory,
2450
            vcpu=>$vcpu,
2451
            image=>$image,
2452
            imagename=>$imagename,
2453
            image2=>$image2,
2454
            image2name=>$image2name,
2455
            image3=>$image3,
2456
            image3name=>$image3name,
2457
            image4=>$image4,
2458
            image4name=>$image4name,
2459
            diskbus=>$diskbus,
2460
            cdrom=>$cdrom,
2461
            boot=>$boot,
2462
            loader=>$loader,
2463
            networkuuid1=>$networkuuid1,
2464
            networkid1=>$networkid1,
2465
            networkname1=>$networkname1,
2466
            nicmodel1=>$nicmodel1,
2467
            nicmac1=>$nicmac1,
2468
            networkuuid2=>$networkuuid2,
2469
            networkid2=>$networkid2,
2470
            networkname2=>$networkname2,
2471
            nicmac2=>$nicmac2,
2472
            networkuuid3=>$networkuuid3,
2473
            networkid3=>$networkid3,
2474
            networkname3=>$networkname3,
2475
            nicmac3=>$nicmac3,
2476
            status=>$status,
2477
            notes=>$notes,
2478
            autostart=>$autostart,
2479
            locktonode=>$locktonode,
2480
            action=>"",
2481
            created=>$created
2482
        };
2483
        $valref->{'system'} = $system if ($system);
2484
        if ($mac && $locktonode eq 'true') {
2485
            $valref->{'mac'} = $mac;
2486
            $valref->{'macip'} = $nodereg{$mac}->{'ip'};
2487
            $valref->{'macname'} = $nodereg{$mac}->{'name'};
2488
        }
2489
        if ($newsystem) {
2490
            my $ug = new Data::UUID;
2491
            $sysuuid = $ug->create_str();
2492
            $valref->{'system'} = $sysuuid;
2493
            $postmsg .= "OK sysuuid: $sysuuid " if ($console);
2494
        }
2495

    
2496
        # Remove domain uuid from old networks. Leave gateways alone - they get updated on next listing
2497
        my $oldnetworkuuid1 = $regserv->{'networkuuid1'};
2498
        if ($oldnetworkuuid1 ne $networkuuid1 && $networkreg{$oldnetworkuuid1}) {
2499
            $networkreg{$oldnetworkuuid1}->{'domains'} =~ s/($uuid)(,?)( ?)//;
2500
        }
2501

    
2502
        $register{$uuid} = validateItem($valref);
2503

    
2504
        if ($networkreg{$networkuuid1}->{'type'} eq 'gateway') {
2505
            # We now remove before adding to support API calls that dont necessarily list afterwards
2506
            $networkreg{$networkuuid1}->{'domains'} =~ s/($uuid)(,?)( ?)//;
2507
            my $domains = $networkreg{$networkuuid1}->{'domains'};
2508
            $networkreg{$networkuuid1}->{'domains'} = ($domains?"$domains, ":"") . $uuid;
2509

    
2510
            $networkreg{$networkuuid1}->{'domainnames'} =~ s/($name)(,?)( ?)//;
2511
            my $domainnames = $networkreg{$networkuuid1}->{'domainnames'};
2512
            $networkreg{$networkuuid1}->{'domainnames'} = ($domainnames?"$domainnames, ":"") . $name;
2513
        } else {
2514
            $networkreg{$networkuuid1}->{'domains'}  = $uuid;
2515
            $networkreg{$networkuuid1}->{'domainnames'}  = $name;
2516
        }
2517

    
2518
        if ($networkuuid2 && $networkuuid2 ne '--') {
2519
            if ($networkreg{$networkuuid2}->{'type'} eq 'gateway') {
2520
                $networkreg{$networkuuid2}->{'domains'} =~ s/($uuid)(,?)( ?)//;
2521
                my $domains = $networkreg{$networkuuid2}->{'domains'};
2522
                $networkreg{$networkuuid2}->{'domains'} = ($domains?"$domains, ":"") . $uuid;
2523

    
2524
                $networkreg{$networkuuid2}->{'domainnames'} =~ s/($name)(,?)( ?)//;
2525
                my $domainnames = $networkreg{$networkuuid2}->{'domainnames'};
2526
                $networkreg{$networkuuid2}->{'domainnames'} = ($domainnames?"$domainnames, ":"") . $name;
2527
            } else {
2528
                $networkreg{$networkuuid2}->{'domains'}  = $uuid;
2529
                $networkreg{$networkuuid2}->{'domainnames'}  = $name;
2530
            }
2531
        }
2532

    
2533
        if ($networkuuid3 && $networkuuid3 ne '--') {
2534
            if ($networkreg{$networkuuid3}->{'type'} eq 'gateway') {
2535
                my $domains = $networkreg{$networkuuid3}->{'domains'};
2536
                $networkreg{$networkuuid3}->{'domains'} = ($domains?"$domains, ":"") . $uuid;
2537
                my $domainnames = $networkreg{$networkuuid3}->{'domainnames'};
2538
                $networkreg{$networkuuid3}->{'domainnames'} = ($domainnames?"$domainnames, ":"") . $name;
2539
            } else {
2540
                $networkreg{$networkuuid3}->{'domains'}  = $uuid;
2541
                $networkreg{$networkuuid3}->{'domainnames'}  = $name;
2542
            }
2543
        }
2544
        my %jitem = %{$register{$uuid}};
2545
        $json_text = to_json(\%jitem, {pretty=>1});
2546
        $json_text =~ s/null/"--"/g;
2547
        $uiuuid = $uuid;
2548
        $uiname = $name;
2549

    
2550
        tied(%register)->commit;
2551
        tied(%imagereg)->commit;
2552
        tied(%networkreg)->commit;
2553

    
2554
    } else {
2555
        $postmsg .= "ERROR This image ($image) cannot be used ($imgdup) " if ($imgdup);
2556
        $postmsg .= "ERROR This network ($networkname1) cannot be used ($netdup)" if ($netdup);
2557
    }
2558

    
2559
    my $domuser = $obj->{'user'};
2560
    # We were asked to move server to another account
2561
    if ($domuser && $domuser ne '--' && $domuser ne $user) {
2562
        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")};
2563
        if ($status eq 'shutoff' || $status eq 'inactive') {
2564
            unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username', CLOBBER=>1}, $Stabile::dbopts)) ) {$posterror =  "Unable to access user register"; return 0;};
2565
            my @accounts = split(/,\s*/, $userreg{$tktuser}->{'accounts'});
2566
            my @accountsprivs = split(/,\s*/, $userreg{$tktuser}->{'accountsprivileges'});
2567
            %ahash = ($tktuser, $userreg{$tktuser}->{'privileges'}); # Include tktuser in accounts hash
2568
            for my $i (0 .. scalar @accounts)
2569
            {
2570
                next unless $accounts[$i];
2571
                $ahash{$accounts[$i]} = $accountsprivs[$i] || 'r';
2572
            }
2573
            untie %userreg;
2574

    
2575
            if (!$isreadonly && $ahash{$domuser} && !($ahash{$domuser} =~ /r/)) { # Check if user is allow to access account
2576
                my $imgdone;
2577
                my $netdone;
2578
                # First move main image
2579
                $Stabile::Images::user = $user;
2580
                require "$Stabile::basedir/cgi/images.cgi";
2581
                $Stabile::Images::console = 1;
2582
                $main::updateUI->({tab=>"servers", user=>$user, message=>"Moving image $imagename to account: $domuser"});
2583
                my $nimage = Stabile::Images::Move($image, $domuser);
2584
                chomp $nimage;
2585
                if ($nimage) {
2586
                    $main::syslogit->($user, "info", "Moving $nimage to account: $domuser");
2587
                    $register{$uuid}->{'image'} = $nimage;
2588
                    $imgdone = 1;
2589
                } else {
2590
                    $main::syslogit->($user, "info", "Unable to move image $imagename to account: $domuser");
2591
                }
2592
                # Move other attached images
2593
                my @images = ($image2, $image3, $image4);
2594
                my @imagenames = ($image2name, $image3name, $image4name);
2595
                my @imagekeys = ('image2', 'image3', 'image4');
2596
                for (my $i=0; $i<3; $i++) {
2597
                    my $img = $images[$i];
2598
                    my $imgname = $imagenames[$i];
2599
                    my $imgkey = $imagekeys[$i];
2600
                    if ($img && $img ne '--') {
2601
                        $main::updateUI->({tab=>"servers", user=>$user, message=>"Moving $imgkey $imgname to account: $domuser"});
2602
                        $nimage = Stabile::Images::Move($img, $domuser);
2603
                        chomp $nimage;
2604
                        if ($nimage) {
2605
                            $main::syslogit->($user, "info", "Moving $nimage to account: $domuser");
2606
                            $register{$uuid}->{$imgkey} = $nimage;
2607
                        } else {
2608
                            $main::syslogit->($user, "info", "Unable to move $imagekeys[$i] $img to account: $domuser");
2609
                        }
2610
                    }
2611
                }
2612
                # Then move network(s)
2613
                if ($imgdone) {
2614
                    $Stabile::Networks::user = $user;
2615
                    require "$Stabile::basedir/cgi/networks.cgi";
2616
                    $Stabile::Networks::console = 1;
2617
                    my @networks = ($networkuuid1, $networkuuid2, $networkuuid3);
2618
                    my @netkeys = ('networkuuid1', 'networkuuid2', 'networkuuid3');
2619
                    my @netnamekeys = ('networkname1', 'networkname2', 'networkname3');
2620
                    for (my $i=0; $i<scalar @networks; $i++) {
2621
                        my $net = $networks[$i];
2622
                        my $netkey = $netkeys[$i];
2623
                        my $netnamekey = $netnamekeys[$i];
2624
                        my $regnet = $networkreg{$net};
2625
                        my $oldid = $regnet->{'id'};
2626
                        next if ($net eq '' || $net eq '--');
2627
                        if ($regnet->{'type'} eq 'gateway') {
2628
                            if ($oldid > 1) { # Private gateway
2629
                                foreach my $networkvalref (values %networkreg) { # use gateway with same id if it exists
2630
                                    if ($networkvalref->{'user'} eq $domuser
2631
                                        && $networkvalref->{'type'} eq 'gateway'
2632
                                        && $networkvalref->{'id'} == $oldid) {
2633
                                        # We found an existing gateway with same id - use it
2634
                                        $register{$uuid}->{$netkey} = $networkvalref->{'uuid'};
2635
                                        $register{$uuid}->{$netnamekey} = $networkvalref->{'name'};
2636
                                        $netdone = 1;
2637
                                        $main::updateUI->({tab=>"networks", user=>$user, message=>"Using network $networkvalref->{'name'} from account: $domuser"});
2638
                                        last;
2639
                                    }
2640
                                }
2641
                                if (!($netdone)) {
2642
                                    # Make a new gateway
2643
                                    my $ug = new Data::UUID;
2644
                                    my $newuuid = $ug->create_str();
2645
                                    Stabile::Networks::save($oldid, $newuuid, $regnet->{'name'}, 'new', 'gateway', '', '', $regnet->{'ports'}, 0, $domuser);
2646
                                    $register{$uuid}->{$netkey} = $newuuid;
2647
                                    $register{$uuid}->{$netnamekey} = $regnet->{'name'};
2648
                                    $netdone = 1;
2649
                                    $main::updateUI->({tab=>"networks", user=>$user, message=>"Created gateway $regnet->{'name'} for account: $domuser"});
2650
                                    $main::syslogit->($user, "info", "Created gateway $regnet->{'name'} for account: $domuser");
2651
                                }
2652
                            } elsif ($oldid==0 || $oldid==1) {
2653
                                $netdone = 1; # Use common gateway
2654
                                $main::updateUI->({tab=>"networks", user=>$user, message=>"Reused network $regnet->{'name'} for account: $domuser"});
2655
                            }
2656
                        } else {
2657
                            my $newid = Stabile::Networks::getNextId('', $domuser);
2658
                            $networkreg{$net}->{'id'} = $newid;
2659
                            $networkreg{$net}->{'user'} = $domuser;
2660
                        #    if ($regnet->{'type'} eq 'internalip' || $regnet->{'type'} eq 'ipmapping') {
2661
                                # Deactivate network and assign new internal ip
2662
                                Stabile::Networks::Deactivate($regnet->{'uuid'});
2663
                                $networkreg{$net}->{'internalip'} =
2664
                                    Stabile::Networks::getNextInternalIP('',$regnet->{'uuid'}, $newid, $domuser);
2665
                        #    }
2666
                            $netdone = 1;
2667
                            $main::updateUI->({tab=>"networks", user=>$user, message=>"Moved network $regnet->{'name'} to account: $domuser"});
2668
                            $main::syslogit->($user, "info", "Moved network $regnet->{'name'} to account: $domuser");
2669
                        }
2670
                    }
2671
                    if ($netdone) {
2672
                        # Finally move the server
2673
                        $register{$uuid}->{'user'} = $domuser;
2674
                        $postmsg .= "OK Moved server $name to account: $domuser";
2675
                        $main::syslogit->($user, "info", "Moved server $name ($uuid) to account: $domuser");
2676
                        $main::updateUI->({tab=>"servers", user=>$user, type=>"update"});
2677
                    } else {
2678
                        $postmsg .= "ERROR Unable to move network to account: $domuser";
2679
                        $main::updateUI->({tab=>"image", user=>$user, message=>"Unable to move network to account: $domuser"});
2680
                    }
2681
                } else {
2682
                    $main::updateUI->({tab=>"image", user=>$user, message=>"Could not move image to account: $domuser"});
2683
                }
2684
            } else {
2685
                $postmsg .= "ERROR No access to move server";
2686
            }
2687
        } else {
2688
            $postmsg .= "Error Unable to move $status server";
2689
            $main::updateUI->({tab=>"servers", user=>$user, message=>"Please shut down before moving server"});
2690
        }
2691
        untie %userreg;
2692
    }
2693

    
2694
    if ($console) {
2695
        $postreply = $postmsg;
2696
    } else {
2697
        $postreply = $json_text || $postmsg;
2698
    }
2699
    return $postreply;
2700
    untie %imagereg;
2701
}
2702

    
2703

    
2704
sub Shutdown {
2705
    my ($uuid, $action, $obj) = @_;
2706
    if ($help) {
2707
        return <<END
2708
GET:uuid:
2709
Marks a server for shutdown, i.e. send and ACPI shutdown event to the server. If OS supports ACPI, it begins a shutdown.
2710
END
2711
    }
2712
    $uistatus = "shuttingdown";
2713
    my $dbstatus = $obj->{status};
2714
    my $mac = $obj->{mac};
2715
    my $macname = $obj->{macname};
2716
    my $name = $obj->{name};
2717
    if ($dbstatus eq 'running') {
2718
        my $tasks;
2719
        $tasks = $nodereg{$mac}->{'tasks'} if ($nodereg{$mac});
2720
        $nodereg{$mac}->{'tasks'} = $tasks . "SHUTDOWN $uuid $user\n";
2721
        tied(%nodereg)->commit;
2722
        $register{$uuid}->{'status'} = $uistatus;
2723
        $register{$uuid}->{'statustime'} = $current_time;
2724
        $uiuuid = $uuid;
2725
        $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus by $macname ($mac)");
2726
        $postreply .= "Status=$uistatus OK $uistatus $name\n";
2727
    } else {
2728
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
2729
        $postreply .= "Status=ERROR problem $uistatus $name...\n";
2730
    }
2731
    return $postreply;
2732
}
2733

    
2734
sub Suspend {
2735
    my ($uuid, $action, $obj) = @_;
2736
    if ($help) {
2737
        return <<END
2738
GET:uuid:
2739
Marks a server for suspend, i.e. pauses the server. Server must be running
2740
END
2741
    }
2742
#    my $obj = getObj(('uuid', $uuid));
2743
    $uistatus = "suspending";
2744
    my $dbstatus = $obj->{status};
2745
    my $mac = $obj->{mac};
2746
    my $macname = $obj->{macname};
2747
    my $name = $obj->{name};
2748
    if ($dbstatus eq 'running') {
2749
        my $tasks = $nodereg{$mac}->{'tasks'};
2750
        $nodereg{$mac}->{'tasks'} = $tasks . "SUSPEND $uuid $user\n";
2751
        tied(%nodereg)->commit;
2752
        $register{$uuid}->{'status'} = $uistatus;
2753
        $register{$uuid}->{'statustime'} = $current_time;
2754
        $uiuuid = $uuid;
2755
        $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus by $macname ($mac)");
2756
        $postreply .= "Status=$uistatus OK $uistatus $name.\n";
2757
    } else {
2758
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
2759
        $postreply .= "Status=ERROR problem $uistatus $name.\n";
2760
    }
2761
    return $postreply;
2762
}
2763

    
2764
sub Resume {
2765
    my ($uuid, $action, $obj) = @_;
2766
    if ($help) {
2767
        return <<END
2768
GET:uuid:
2769
Marks a server for resume running. Server must be paused.
2770
END
2771
    }
2772
    my $dbstatus = $obj->{status};
2773
    my $mac = $obj->{mac};
2774
    my $macname = $obj->{macname};
2775
    my $name = $obj->{name};
2776
    my $image = $obj->{image};
2777
    my $image2 = $obj->{image2};
2778
    my $image3 = $obj->{image3};
2779
    my $image4 = $obj->{image4};
2780
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$posterror = "Unable to access image register"; return;};
2781
    if ($imagereg{$image}->{'status'} ne "paused"
2782
        || ($image2 && $image2 ne '--' && $imagereg{$image}->{'status'} ne "paused")
2783
        || ($image3 && $image3 ne '--' && $imagereg{$image3}->{'status'} ne "paused")
2784
        || ($image4 && $image4 ne '--' && $imagereg{$image4}->{'status'} ne "paused")
2785
    ) {
2786
        $postreply .= "Status=ERROR Image $uuid busy ($imagereg{$image}->{'status'}), please wait 30 sec.\n";
2787
        untie %imagereg;
2788
        return $postreply   ;
2789
    } else {
2790
        untie %imagereg;
2791
    }
2792
    $uistatus = "resuming";
2793
    if ($dbstatus eq 'paused') {
2794
        my $tasks = $nodereg{$mac}->{'tasks'};
2795
        $nodereg{$mac}->{'tasks'} = $tasks . "RESUME $uuid $user\n";
2796
        tied(%nodereg)->commit;
2797
        $register{$uuid}->{'status'} = $uistatus;
2798
        $register{$uuid}->{'statustime'} = $current_time;
2799
        $uiuuid = $uuid;
2800
        $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus by $macname ($mac)");
2801
        $postreply .= "Status=$uistatus OK $uistatus ". $register{$uuid}->{'name'} . "\n";
2802
    } else {
2803
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
2804
        $postreply .= "Status=ERROR problem $uistatus ". $register{$uuid}->{'name'} . "\n";
2805
    }
2806
    return $postreply;
2807
}
2808

    
2809
sub Move {
2810
    my ($uuid, $action, $obj) = @_;
2811
    if ($help) {
2812
        return <<END
2813
GET:uuid,mac:
2814
Moves a server to a different node (Qemu live migration). Server must be running
2815
END
2816
    }
2817
    my $dbstatus = $obj->{status};
2818
    my $dmac = $obj->{mac};
2819
    my $name = $obj->{name};
2820
    my $mem = $obj->{memory};
2821
    my $vcpu = $obj->{vcpu};
2822
    my $image = $obj->{image};
2823
    my $image2 = $obj->{image2};
2824
    my $image3 = $obj->{image3};
2825
    my $image4 = $obj->{image4};
2826
    $uistatus = "moving";
2827
    if ($dbstatus eq 'running' && $isadmin) {
2828
        my $hypervisor = getHypervisor($image);
2829
        my $mac = $register{$uuid}->{'mac'};
2830
        $dmac = "" if ($dmac eq "--");
2831
        $mac = "" if ($mac eq "--");
2832

    
2833
        if ($image =~ /\/mnt\/stabile\/node\//
2834
            || $image2 =~ /\/mnt\/stabile\/node\//
2835
            || $image3 =~ /\/mnt\/stabile\/node\//
2836
            || $image4 =~ /\/mnt\/stabile\/node\//
2837
        ) {
2838
            # We do not support moving locally stored VM's yet...
2839
            $postreply = qq|{"error": 1, "message": "Moving servers with local storage not supported"}|;
2840
        } else {
2841
            my ($targetmac, $targetname, $targetip, $port) =
2842
                locateTargetNode($uuid, $dmac, $mem, $vcpu, $image, $image2, $image3, $image4, $hypervisor, $mac);
2843
            if ($targetmac) {
2844
                my $tasks = $nodereg{$targetmac}->{'tasks'};
2845
                $tasks = $tasks . "RECEIVE $uuid $user\n";
2846
                # Also update allowed port forwards
2847
                $nodereg{$targetmac}->{'tasks'} = $tasks . "PERMITOPEN $user\n";
2848
                $register{$uuid}->{'status'} = "moving";
2849
                $register{$uuid}->{'statustime'} = $current_time;
2850
                $uiuuid = $uuid;
2851
                $uidisplayip = $targetip;
2852
                $uidisplayport = $port;
2853
                $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus to $targetname ($targetmac)");
2854
                $postreply .= "Status=OK $uistatus ". $register{$uuid}->{'name'} . "\n";
2855

    
2856
                if ($params{'PUTDATA'}) {
2857
                    my %jitem = %{$register{$uuid}};
2858
                    my $json_text = to_json(\%jitem);
2859
                    $json_text =~ s/null/"--"/g;
2860
                    $postreply = $json_text;
2861
                }
2862
                $main::updateUI->({tab=>"servers", user=>$user, status=>'moving', uuid=>$uuid, type=>'update', message=>"Moving $register{$uuid}->{name} to $targetmac"});
2863
            } else {
2864
                $main::syslogit->($user, "info", "Could not find $hypervisor target for $uistatus $uuid ($image)");
2865
                $postreply = qq|{"error": 1, "message": "Could not find target for $uistatus $register{$uuid}->{'name'}"}|;
2866
            }
2867
        }
2868
    } else {
2869
        $main::syslogit->($user, "info", "Problem moving a $dbstatus domain: $uuid");
2870
        $postreply .= qq|{"error": 1, "message": "ERROR problem moving $register{$uuid}->{'name'} ($dbstatus)"}|;
2871
    }
2872
    return $postreply;
2873
}
2874

    
2875
sub Changepassword {
2876
    my ($uuid, $action, $obj) = @_;
2877
    if ($help) {
2878
        return <<END
2879
POST:uuid,username,password:
2880
Attempts to set password for [username] to [password] using guestfish. If no username is specified, user 'stabile' is assumed.
2881
END
2882
    }
2883
    my $img = $register{$uuid}->{'image'};
2884
    my $username = $obj->{'username'} || 'stabile';
2885
    my $password = $obj->{'password'};
2886
    return "Status=Error Please supply a password\n" unless ($password);
2887
    return "Status=Error Please shut down the server before changing password\n" unless ($register{$uuid} && $register{$uuid}->{'status'} eq 'shutoff');
2888
    return "Status=Error Not allowed\n" unless ($isadmin || $register{$uuid}->{'user'} eq $user);
2889

    
2890
    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;};
2891
    my $cmd = qq/guestfish --rw -a $img -i command "bash -c 'echo $username:$password | chpasswd'" 2>\&1/;
2892
    if ($imagereg{$img} && $imagereg{$img}->{'mac'}) {
2893
        my $mac = $imagereg{$img}->{'mac'};
2894
        my $macip = $nodereg{$mac}->{'ip'};
2895
        $cmd = "$sshcmd $macip $cmd";
2896
    }
2897
    my $res = `$cmd`;
2898
    $res = $1 if ($res =~ /guestfish: (.*)/);
2899
    chomp $res;
2900
    return "Status=OK Ran chpasswd for user $username in server $register{$uuid}->{'name'}: $res\n";
2901
}
2902

    
2903
sub Sshaccess {
2904
    my ($uuid, $action, $obj) = @_;
2905
    if ($help) {
2906
        return <<END
2907
POST:uuid,address:
2908
Attempts to change the ip addresses you can access the server over SSH (port 22) from, by adding [address] to /etc/hosts.allow.
2909
[address] should either be an IP address or a range in CIDR notation. Please note that no validation of [address] is performed.
2910
END
2911
    }
2912
    my $img = $register{$uuid}->{'image'};
2913
    my $address = $obj->{'address'};
2914
    return "Status=Error Please supply an aaddress\n" unless ($address);
2915
    return "Status=Error Please shut down the server before changing SSH access\n" unless ($register{$uuid} && $register{$uuid}->{'status'} eq 'shutoff');
2916
    return "Status=Error Not allowed\n" unless ($isadmin || $register{$uuid}->{'user'} eq $user);
2917

    
2918
    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;};
2919

    
2920
    my $isshcmd = '';
2921
    my $cmd = qq[guestfish --rw -a $img -i command "sed -i -re 's|(sshd: .*)#stabile|\\1 $address #stabile|' /etc/hosts.allow"];
2922
#    my $cmd = qq[guestfish --rw -a $img -i command "bash -c 'echo sshd: $address >> /etc/hosts.allow'"];
2923
    if ($imagereg{$img} && $imagereg{$img}->{'mac'}) {
2924
        my $mac = $imagereg{$img}->{'mac'};
2925
        my $macip = $nodereg{$mac}->{'ip'};
2926
        $isshcmd = "$sshcmd $macip ";
2927
    }
2928
    my $res = `$isshcmd$cmd`;
2929
    chomp $res;
2930
    #$cmd = qq[guestfish --rw -a $img -i command "bash -c 'cat /etc/hosts.allow'"];
2931
    #$res .= `$isshcmd$cmd`;
2932
    #chomp $res;
2933
    return "Status=OK Tried to add sshd: $address to /etc/hosts.allow in server $register{$uuid}->{'name'}\n";
2934
}
2935

    
2936
sub Mountcd {
2937
    my ($uuid, $action, $obj) = @_;
2938
    if ($help) {
2939
        return <<END
2940
GET:uuid,cdrom:
2941
Mounts a cdrom on a server. Server must be running. Mounting the special cdrom named '--' unomunts any currently mounted cdrom.
2942
END
2943
    }
2944
    my $dbstatus = $obj->{status};
2945
    my $mac = $obj->{mac};
2946
    my $cdrom = $obj->{cdrom};
2947
    unless ($cdrom && $dbstatus eq 'running') {
2948
        $main::updateUI->({tab=>"servers", user=>$user, uuid=>$uuid, type=>'update', message=>"Unable to mount cdrom"});
2949
        $postreply = qq|{"Error": 1, "message": "Problem mounting cdrom on $obj->{name}"}|;
2950
        return;
2951
    }
2952
    my $tasks = $nodereg{$mac}->{'tasks'};
2953
    # $user is in the middle here, because $cdrom may contain spaces...
2954
    $nodereg{$mac}->{'tasks'} = $tasks . "MOUNT $uuid $user \"$cdrom\"\n";
2955
    tied(%nodereg)->commit;
2956
    if ($cdrom eq "--") {
2957
        $postreply = qq|{"OK": 1, "message": "OK unmounting cdrom from $obj->{name}"}|;
2958
    } else {
2959
        $postreply = qq|{"OK": 1, "message": "OK mounting cdrom $cdrom on $obj->{name}"}|;
2960
    }
2961
    $register{$uuid}->{'cdrom'} = $cdrom unless ($cdrom eq 'virtio');
2962
    return $postreply;
2963
}
(5-5/9)