Project

General

Profile

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

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

    
8
package Stabile::Servers;
9

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

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

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

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

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

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

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

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

    
56
1;
57

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
325
}
326

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
690

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1034
    my $mem = $memory * 1024;
1035

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

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

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

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

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

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

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

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

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

    
1179
        my $xml = "<domain type='$hypervisor' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>\n";
1180
        my $vgpuxml = '';
1181
        if ($vgpu && $vgpu ne "--") {
1182
            $Stabile::Nodes::user = $user;
1183
            require "$Stabile::basedir/cgi/networks.cgi";
1184
            $Stabile::Nodes::console = 1;
1185
            my @gpus = Stabile::Nodes::getNextGpus($vgpu);
1186
            if (@gpus) {
1187
                foreach my $gpu (@gpus) {
1188
                    $vgpuxml .= <<ENDXML2
1189
 <hostdev mode='subsystem' type='pci' managed='yes'>
1190
   <source>
1191
     <address domain='0x0000' bus='0x$gpu->{bus}' slot='0x$gpu->{devide}' function='0x$gpu->{function}' multifunction='on'/>
1192
   </source>
1193
 </hostdev>
1194
ENDXML2
1195
                    ;
1196
                }
1197
            }
1198
        }
1199

    
1200
        #        if ($vgpu && $vgpu ne "--") {
1201
#            $xml .= <<ENDXML2
1202
#  <qemu:commandline>
1203
#    <qemu:arg value='-device'/>
1204
#    <qemu:arg value='vfio-pci,host=01:00.0,x-vga=on'/>
1205
#    <qemu:arg value='-device'/>
1206
#    <qemu:arg value='vfio-pci,host=02:00.0,x-vga=on'/>
1207
#  </qemu:commandline>
1208
#ENDXML2
1209
#            ;
1210
#        }
1211

    
1212
#    <qemu:arg value='-set'/>
1213
#    <qemu:arg value='device.hostdev1.x-vga=on'/>
1214
#    <qemu:arg value='-cpu'/>
1215
#	<qemu:arg value='host,kvm=off'/>
1216
#    <qemu:arg value='-device'/>
1217
#	<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'/>
1218

    
1219
#  <cpu mode='host-model'>
1220
#    <vendor>Intel</vendor>
1221
#    <model>core2duo</model>
1222
#  </cpu>
1223

    
1224
#    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
1225
#    <nvram template='/usr/share/OVMF/OVMF_VARS.fd'/>
1226
        my $loader_xml = <<ENDXML
1227
    <bootmenu enable='yes' timeout='200'/>
1228
    <smbios mode='sysinfo'/>
1229
ENDXML
1230
        ;
1231
        if ($loader eq 'uefi') {
1232
            $loader_xml = <<ENDXML
1233
  <loader readonly='yes' secure='no' type='pflash'>/usr/share/ovmf/OVMF.fd</loader>
1234
  <nvram template='/usr/share/OVMF/OVMF_VARS.fd'>/tmp/guest_VARS.fd</nvram>
1235
ENDXML
1236
    ;
1237
        }
1238
        my $iotune_xml = <<ENDXML
1239
      <iotune>
1240
        <read_bytes_sec>$vm_readlimit</read_bytes_sec>
1241
        <write_bytes_sec>$vm_writelimit</write_bytes_sec>
1242
        <read_iops_sec>$vm_iopsreadlimit</read_iops_sec>
1243
        <write_iops_sec>$vm_iopswritelimit</write_iops_sec>
1244
      </iotune>
1245
ENDXML
1246
;
1247
        $iotune_xml = '' unless ($enforceiolimits);
1248

    
1249
        if ($vgpu && $vgpu ne "--") {
1250
            $xml .= <<ENDXML
1251
  <cpu mode='host-passthrough'>
1252
    <feature policy='disable' name='hypervisor'/>
1253
  </cpu>
1254
ENDXML
1255
;
1256
        } else {
1257
            $xml .= <<ENDXML
1258
  <cpu mode='host-model'>
1259
  </cpu>
1260
ENDXML
1261
            ;
1262
        }
1263
        $xml .=  <<ENDXML
1264
  <name>$uname</name>
1265
  <uuid>$uuid</uuid>
1266
  <memory>$mem</memory>
1267
  <vcpu>$vcpu</vcpu>
1268
  <os>
1269
    <type arch='x86_64' machine='pc'>hvm</type>
1270
    <boot dev='$boot'/>
1271
$loader_xml
1272
  </os>
1273
  <sysinfo type='smbios'>
1274
    <bios>
1275
      <entry name='vendor'>Origo</entry>
1276
    </bios>
1277
    <system>
1278
      <entry name='manufacturer'>Origo</entry>
1279
      <entry name='sku'>$networkid1ip</entry>
1280
    </system>
1281
  </sysinfo>
1282
  <features>
1283
ENDXML
1284
;
1285
        if ($vgpu && $vgpu ne "--") { $xml .= <<ENDXML
1286
    <kvm>
1287
      <hidden state='on'/>
1288
    </kvm>
1289
ENDXML
1290
;
1291
        }
1292
        $xml .= <<ENDXML
1293
    <pae/>
1294
    <acpi/>
1295
    <apic/>
1296
  </features>
1297
  <clock offset='localtime'>
1298
    <timer name='rtc' tickpolicy='catchup' track='guest'/>
1299
    <timer name='pit' tickpolicy='delay'/>
1300
    <timer name='hpet' present='no'/>
1301
  </clock>
1302
  <on_poweroff>destroy</on_poweroff>
1303
  <on_reboot>restart</on_reboot>½
1304
  <on_crash>restart</on_crash>
1305
  <devices>
1306
  <sound model='ich6'/>
1307
ENDXML
1308
;
1309
#        if ($vgpu && $vgpu ne "--") {
1310
#            $xml .= <<ENDXML2
1311
#  <hostdev mode='subsystem' type='pci' managed='yes'>
1312
#    <source>
1313
#      <address domain='0x0000' bus='0x01' slot='0x00' function='0x0' multifunction='on'/>
1314
#    </source>
1315
#  </hostdev>
1316
#  <hostdev mode='subsystem' type='pci' managed='yes'>
1317
#    <source>
1318
#      <address domain='0x0000' bus='0x02' slot='0x00' function='0x0' multifunction='on'/>
1319
#    </source>
1320
#  </hostdev>
1321
#ENDXML2
1322
#;
1323
#        }
1324
       if ($vgpu && $vgpu ne "--") {
1325
           $xml .= <<ENDXML2
1326
 <hostdev mode='subsystem' type='pci' managed='yes'>
1327
   <source>
1328
     <address domain='0x0000' bus='0x04' slot='0x00' function='0x0' multifunction='on'/>
1329
   </source>
1330
 </hostdev>
1331
ENDXML2
1332
;
1333
       }
1334
        if ($image && $image ne "" && $image ne "--") {
1335
						$xml .= <<ENDXML2
1336
    <disk type='file' device='disk'>
1337
      <source file='$image'/>$driver1
1338
      <target dev='$diskdev' bus='$diskbus'/>
1339
$iotune_xml
1340
    </disk>
1341
ENDXML2
1342
;
1343
        };
1344

    
1345
        if ($image2 && $image2 ne "" && $image2 ne "--") {
1346
						$xml .= <<ENDXML2
1347
    <disk type='file' device='disk'>$driver2
1348
      <source file='$image2'/>
1349
      <target dev='$diskdev2' bus='$diskbus'/>
1350
$iotune_xml
1351
    </disk>
1352
ENDXML2
1353
;
1354
        };
1355
        if ($image3 && $image3 ne "" && $image3 ne "--") {
1356
						$xml .= <<ENDXML2
1357
    <disk type='file' device='disk'>$driver3
1358
      <source file='$image3'/>
1359
      <target dev='$diskdev3' bus='$diskbus'/>
1360
$iotune_xml
1361
    </disk>
1362
ENDXML2
1363
;
1364
        };
1365
        if ($image4 && $image4 ne "" && $image4 ne "--") {
1366
						$xml .= <<ENDXML2
1367
    <disk type='file' device='disk'>$driver4
1368
      <source file='$image4'/>
1369
      <target dev='$diskdev4' bus='$diskbus'/>
1370
$iotune_xml
1371
    </disk>
1372
ENDXML2
1373
;
1374
        };
1375

    
1376
        unless ($image4 && $image4 ne '--' && $diskbus eq 'ide') {
1377
            if ($cdrom && $cdrom ne "" && $cdrom ne "--") {
1378
						$xml .= <<ENDXML3
1379
    <disk type='file' device='cdrom'>
1380
      <source file='$cdrom'/>
1381
      <target dev='hdd' bus='ide'/>
1382
      <readonly/>
1383
    </disk>
1384
ENDXML3
1385
;
1386
            } elsif ($hypervisor ne "vbox") {
1387
						$xml .= <<ENDXML3
1388
    <disk type='file' device='cdrom'>
1389
      <target dev='hdd' bus='ide'/>
1390
      <readonly/>
1391
    </disk>
1392
ENDXML3
1393
;
1394
            }
1395
        }
1396

    
1397
        $xml .= <<ENDXML4
1398
    <interface type='$networktype1'>
1399
      <source $networktype1='$networksource1'/>
1400
      <forward mode='$networkforward1'/>
1401
      <port isolated='$networkisolated1'/>
1402
      <model type='$nicmodel1'/>
1403
      <mac address='$nicmac1'/>
1404
    </interface>
1405
ENDXML4
1406
;
1407

    
1408
        if (($networkuuid2 && $networkuuid2 ne '--') || $networkuuid2 eq '0') {
1409
            $xml .= <<ENDXML5
1410
    <interface type='$networktype2'>
1411
      <source $networktype2='$networksource2'/>
1412
      <forward mode='$networkforward2'/>
1413
      <port isolated='$networkisolated2'/>
1414
      <model type='$nicmodel1'/>
1415
      <mac address='$nicmac2'/>
1416
    </interface>
1417
ENDXML5
1418
;
1419
        }
1420
        if (($networkuuid3 && $networkuuid3 ne '--') || $networkuuid3 eq '0') {
1421
            $xml .= <<ENDXML5
1422
    <interface type='$networktype3'>
1423
      <source $networktype3='$networksource3'/>
1424
      <forward mode='$networkforward3'/>
1425
      <port isolated='$networkisolated3'/>
1426
      <model type='$nicmodel1'/>
1427
      <mac address='$nicmac3'/>
1428
    </interface>
1429
ENDXML5
1430
;
1431
        }
1432
        $xml .= <<ENDXML6
1433
     <serial type='pty'>
1434
       <source path='/dev/pts/0'/>
1435
       <target port='0'/>
1436
     </serial>
1437
    <input type='tablet' bus='usb'/>
1438
    <graphics type='$graphics' port='$port'/>
1439
  </devices>
1440
</domain>
1441
ENDXML6
1442
;
1443

    
1444

    
1445
#    <graphics type='$graphics' port='$port' keymap='en-us'/>
1446
#     <console type='pty' tty='/dev/pts/0'>
1447
#       <source path='/dev/pts/0'/>
1448
#       <target port='0'/>
1449
#     </console>
1450
#     <graphics type='$graphics' port='-1' autoport='yes'/>
1451

    
1452
        $xmlreg{$uuid} = {
1453
            xml=>URI::Escape::uri_escape($xml)
1454
        };
1455

    
1456
        # Actually ask node to start domain
1457
        if ($targetmac) {
1458
            $register{$uuid}->{'mac'} = $targetmac;
1459
            $register{$uuid}->{'macname'} = $targetname;
1460
            $register{$uuid}->{'macip'} = $targetip;
1461

    
1462
            my $tasks = $nodereg{$targetmac}->{'tasks'};
1463
            $tasks .= "START $uuid $user\n";
1464
            $nodereg{$targetmac}->{'tasks'} = $tasks;
1465
            tied(%nodereg)->commit;
1466
            $uiuuid = $uuid;
1467
            $uidisplayip = $targetip;
1468
            $uidisplayport = $port;
1469
            $register{$uuid}->{'status'} = $uistatus;
1470
            $register{$uuid}->{'statustime'} = $current_time;
1471
            tied(%register)->commit;
1472

    
1473
            # Activate networks
1474
            require "$Stabile::basedir/cgi/networks.cgi";
1475
            Stabile::Networks::Activate($networkuuid1, 'activate');
1476
            Stabile::Networks::Activate($networkuuid2, 'activate') if ($networkuuid2 && $networkuuid2 ne '--');
1477
            Stabile::Networks::Activate($networkuuid3, 'activate') if ($networkuuid3 && $networkuuid3 ne '--');
1478

    
1479
            $main::syslogit->($user, "info", "Marked $name ($uuid) for ". $serv->{'status'} . " on $targetname ($targetmac)");
1480
            $postreply .= "Status=starting OK $uistatus ". $serv->{'name'} . "\n";
1481
        } else {
1482
            $main::syslogit->($user, "info", "Could not find $hypervisor target for creating $uuid ($image)");
1483
            $postreply .= "Status=ERROR problem $uistatus ". $serv->{'name'} . " (unable to locate target node)\n";
1484
        };
1485
    } else {
1486
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
1487
        $postreply .= "Status=ERROR problem $uistatus ". $serv->{'name'} . "\n";
1488
    }
1489
    #return ($uiuuid, $uidisplayip, $uidisplayport, $postreply, $targetmac);
1490
    return $postreply;
1491
}
1492

    
1493
sub do_attach {
1494
    my ($uuid, $action, $obj) = @_;
1495
    if ($help) {
1496
        return <<END
1497
GET:uuid,image:
1498
Attaches an image to a server as a disk device. Image must not be in use.
1499
END
1500
    }
1501
    my $dev = '';
1502
    my $imagenum = 0;
1503
    my $serv = $register{$uuid};
1504

    
1505
    if (!$serv->{'uuid'} || ($serv->{'status'} ne 'running' && $serv->{'status'} ne 'paused')) {
1506
        return "Status=Error Server must exist and be running\n";
1507
    }
1508
    my $macip = $serv->{macip};
1509
    my $image = $obj->{image} || $obj->{path};
1510
    if ($image && !($image =~ /^\//)) { # We have a uuid
1511
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Status=Error Unable to access images register\n"};
1512
        $image = $imagereg2{$image}->{'path'} if ($imagereg2{$image});
1513
        untie %imagereg2;
1514
    }
1515
    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;};
1516
    unless ($macip && $imagereg{$image} && $imagereg{$image}->{'user'} eq $user && $serv->{'user'} eq $user)  {$postreply .= "Status=Error Invalid image or server\n"; return $postreply;};
1517
    if ($imagereg{$image}->{'status'} ne 'unused') {return "Status=Error Image $image is already in use ($imagereg{$image}->{'status'})\n"};
1518

    
1519
    my $cmd = qq|$sshcmd $macip "LIBVIRT_DEFAULT_URI=qemu:///system virsh domblklist $uuid"|;
1520
    my $res = `$cmd`;
1521
    unless ($res =~ /vdb\s+.+/) {$dev = 'vdb'; $imagenum = 2};
1522
    unless ($dev || $res =~ /vdc\s+.+/)  {$dev = 'vdc'; $imagenum = 3};
1523
    unless ($dev || $res =~ /vdd\s+.+/)  {$dev = 'vdd'; $imagenum = 4};
1524
    if (!$dev) {
1525
        $postreply = "Status=Error No more images can be attached\n";
1526
    } else {
1527
        my $xml = <<END
1528
<disk type='file' device='disk'>
1529
  <driver type='qcow2' name='qemu' cache='default'/>
1530
  <source file='$image'/>
1531
  <target dev='$dev' bus='virtio'/>
1532
</disk>
1533
END
1534
;
1535
        $cmd = qq|$sshcmd $macip "echo \\"$xml\\" > /tmp/attach-device-$uuid.xml"|;
1536
        $res = `$cmd`;
1537
        $res .= `$sshcmd $macip LIBVIRT_DEFAULT_URI=qemu:///system virsh attach-device $uuid /tmp/attach-device-$uuid.xml`;
1538
        chomp $res;
1539
        if ($res =~ /successfully/) {
1540
            $postreply .= "Status=OK Attaching $image to $dev\n";
1541
            $imagereg{$image}->{'status'} = 'active';
1542
            $imagereg{$image}->{'domains'} = $uuid;
1543
            $imagereg{$image}->{'domainnames'} = $serv->{'name'};
1544
            $serv->{"image$imagenum"} = $image;
1545
            $serv->{"image$imagenum"."name"} = $imagereg{$image}->{'name'};
1546
            $serv->{"image$imagenum"."type"} = 'qcow2';
1547
        } else {
1548
            $postreply .= "Status=Error Unable to attach image $image to $dev ($res)\n";
1549
        }
1550
    }
1551
    untie %imagereg;
1552
    return $postreply;
1553
}
1554

    
1555
sub do_detach {
1556
    my ($uuid, $action, $obj) = @_;
1557
    if ($help) {
1558
        return <<END
1559
GET:uuid,image:
1560
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.
1561
END
1562
    }
1563
    my $dev = '';
1564
    my $serv = $register{$uuid};
1565

    
1566
    if (!$serv->{'uuid'} || ($serv->{'status'} ne 'running' && $serv->{'status'} ne 'paused')) {
1567
        return "Status=Error Server must exist and be running\n";
1568
    }
1569
    my $macip = $serv->{macip};
1570

    
1571
    my $image = $obj->{image} || $obj->{path} || $serv->{'image2'};
1572
    if ($image && !($image =~ /^\//)) { # We have a uuid
1573
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1574
        $image = $imagereg2{$image}->{'path'} if ($imagereg2{$image});
1575
        untie %imagereg2;
1576
    }
1577
    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;};
1578
    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;};
1579

    
1580
    my $cmd = qq|$sshcmd $macip "LIBVIRT_DEFAULT_URI=qemu:///system virsh domblklist $uuid"|;
1581
    my $res = `$cmd`;
1582
    $dev = $1 if ($res =~ /(vd.)\s+.+$image/);
1583
    if (!$dev) {
1584
        $postreply =  qq|Status=Error Image $image, $cmd, is not currently attached\n|;
1585
    } elsif ($dev eq 'vda') {
1586
        $postreply = "Status=Error You cannot detach the primary image\n";
1587
    } else {
1588
        $res = `$sshcmd $macip LIBVIRT_DEFAULT_URI=qemu:///system virsh detach-disk $uuid $dev`;
1589
        chomp $res;
1590
        if ($res =~ /successfully/) {
1591
            $postreply .= "Status=OK Detaching image $image, $imagereg{$image}->{'uuid'} from $dev\n";
1592
            my $imagenum;
1593
            $imagenum = 2 if ($serv->{'image2'} eq $image);
1594
            $imagenum = 3 if ($serv->{'image3'} eq $image);
1595
            $imagenum = 4 if ($serv->{'image4'} eq $image);
1596
            $imagereg{$image}->{'status'} = 'unused';
1597
            $imagereg{$image}->{'domains'} = '';
1598
            $imagereg{$image}->{'domainnames'} = '';
1599
            if ($imagenum) {
1600
                $serv->{"image$imagenum"} = '';
1601
                $serv->{"image$imagenum"."name"} = '';
1602
                $serv->{"image$imagenum"."type"} = '';
1603
            }
1604
        } else {
1605
            $postreply .= "Status=Error Unable to attach image $image to $dev ($res)\n";
1606
        }
1607
    }
1608
    untie %imagereg;
1609
    return $postreply;
1610
}
1611

    
1612
sub Destroy {
1613
    my ($uuid, $action, $obj) = @_;
1614
    if ($help) {
1615
        return <<END
1616
GET:uuid,wait:
1617
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.
1618
END
1619
    }
1620
    my $uistatus = 'destroying';
1621
    my $name = $register{$uuid}->{'name'};
1622
    my $mac = $register{$uuid}->{'mac'};
1623
    my $macname = $register{$uuid}->{'macname'};
1624
    my $dbstatus = $register{$uuid}->{'status'};
1625
    my $wait = $obj->{'wait'};
1626
    if ($dbstatus eq 'running' or $dbstatus eq 'paused'
1627
        or $dbstatus eq 'shuttingdown' or $dbstatus eq 'starting'
1628
        or $dbstatus eq 'destroying' or $dbstatus eq 'upgrading'
1629
        or $dbstatus eq 'suspending' or $dbstatus eq 'resuming') {
1630
        if ($wait) {
1631
            my $username = $register{$uuid}->{'user'} || $user;
1632
            $username = $user unless ($isadmin);
1633
            $postreply = destroyUserServers($username, 1, $uuid);
1634
        } else {
1635
            my $node = $nodereg{$mac};
1636
            my $tasks = $node->{'tasks'};
1637
            $node->{'tasks'} = $tasks . "DESTROY $uuid $user\n";
1638
            tied(%nodereg)->commit;
1639
            $register{$uuid}->{'status'} = $uistatus;
1640
            $register{$uuid}->{'statustime'} = $current_time;
1641
            $uiuuid = $uuid;
1642
            $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus on $macname ($mac)");
1643
            $postreply .= "Status=destroying $uistatus ". $register{$uuid}->{'name'} . "\n";
1644
        }
1645
    } else {
1646
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $name ($uuid)");
1647
        $postreply .= "Status=ERROR problem $uistatus $name\n";
1648
    }
1649
    return $postreply;
1650
}
1651

    
1652
sub getHypervisor {
1653
	my $image = shift;
1654
	# Produce a mapping of image file suffixes to hypervisors
1655
	my %idreg;
1656
    unless ( tie(%idreg,'Tie::DBI', Hash::Merge::merge({table=>'nodeidentities', key=>'identity'}, $Stabile::dbopts)) ) {return "Unable to access nodeidentities register"};
1657
    my @idvalues = values %idreg;
1658
	my %formats;
1659
	foreach my $val (@idvalues) {
1660
		my %h = %$val;
1661
		foreach (split(/,/,$h{'formats'})) {
1662
			$formats{lc $_} = $h{'hypervisor'}
1663
		}
1664
	}
1665
	untie %idreg;
1666

    
1667
	# and then determine the hypervisor in question
1668
	my $hypervisor = "vbox";
1669
	my ($pathname, $path, $suffix) = fileparse($image, '\.[^\.]*');
1670
	$suffix = substr $suffix, 1;
1671
	my $hypervisor = $formats{lc $suffix};
1672
	return $hypervisor;
1673
}
1674

    
1675
sub nicmac1ToUuid {
1676
    my $nicmac1 = shift;
1677
    my $uuid;
1678
    return $uuid unless $nicmac1;
1679
    my @regkeys = (tied %register)->select_where("user = '$user' AND nicmac1 = '$nicmac1");
1680
	foreach my $k (@regkeys) {
1681
	    my $val = $register{$k};
1682
		my %h = %$val;
1683
		if (lc $h{'nicmac1'} eq lc $nicmac1 && $user eq $h{'user'}) {
1684
    		$uuid =  $h{'uuid'};
1685
    		last;
1686
		}
1687
	}
1688
	return $uuid;
1689
}
1690

    
1691
sub randomMac {
1692
	my ( %vendor, $lladdr, $i );
1693
#	$lladdr = '00';
1694
	$lladdr = '52:54:00';# KVM vendor string
1695
	while ( ++$i )
1696
#	{ last if $i > 10;
1697
	{ last if $i > 6;
1698
		$lladdr .= ':' if $i % 2;
1699
		$lladdr .= sprintf "%" . ( qw (X x) [int ( rand ( 2 ) ) ] ), int ( rand ( 16 ) );
1700
	}
1701
	return $lladdr;
1702
}
1703

    
1704
sub overQuotas {
1705
    my $meminc = shift;
1706
    my $vcpuinc = shift;
1707
	my $usedmemory = 0;
1708
	my $usedvcpus = 0;
1709
	my $overquota = 0;
1710
    return $overquota if ($isadmin || $Stabile::userprivileges =~ /a/); # Don't enforce quotas for admins
1711

    
1712
	my $memoryquota = $Stabile::usermemoryquota;
1713
	my $vcpuquota = $Stabile::uservcpuquota;
1714

    
1715
	if (!$memoryquota || !$vcpuquota) { # 0 or empty quota means use defaults
1716
        $memoryquota = $memoryquota || $Stabile::config->get('MEMORY_QUOTA');
1717
        $vcpuquota = $vcpuquota || $Stabile::config->get('VCPU_QUOTA');
1718
    }
1719

    
1720
    my @regkeys = (tied %register)->select_where("user = '$user'");
1721
	foreach my $k (@regkeys) {
1722
	    my $val = $register{$k};
1723
		if ($val->{'user'} eq $user && $val->{'status'} ne "shutoff" &&
1724
		    $val->{'status'} ne "inactive" && $val->{'status'} ne "shutdown" ) {
1725

    
1726
		    $usedmemory += $val->{'memory'};
1727
		    $usedvcpus += $val->{'vcpu'};
1728
		}
1729
	}
1730
	$overquota = $usedmemory+$meminc if ($memoryquota!=-1 && $usedmemory+$meminc > $memoryquota); # -1 means no quota
1731
	$overquota = $usedvcpus+$vcpuinc if ($vcpuquota!=-1 && $usedvcpus+$vcpuinc > $vcpuquota);
1732
	return $overquota;
1733
}
1734

    
1735
sub validateItem {
1736
    unless (%imagereg) {
1737
        unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1738
    }
1739
    my $valref = shift;
1740
    my $img = $imagereg{$valref->{'image'}};
1741
    my $imagename = $img->{'name'};
1742
    $valref->{'imagename'} = $imagename if ($imagename);
1743
    my $imagetype = $img->{'type'};
1744
    $valref->{'imagetype'} = $imagetype if ($imagetype);
1745

    
1746
    # imagex may be registered by uuid instead of path - find the path
1747
    # We now support up to 4 images
1748
    for (my $i=2; $i<=4; $i++) {
1749
        if ($valref->{"image$i"} && $valref->{"image$i"} ne '--' && !($valref->{"image$i"} =~ /^\//)) {
1750
            unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1751
            $valref->{"image$i"} = $imagereg2{$valref->{"image$i"}}->{'path'};
1752
            untie %imagereg2;
1753
        }
1754

    
1755
        my $imgi = $imagereg{$valref->{"image$i"}};
1756
        $valref->{"image$i" . 'name'} = $imgi->{'name'} || $valref->{"image$i" . 'name'};
1757
        $valref->{"image$i" . 'type'} = $imgi->{'type'} || $valref->{"image$i" . 'type'};
1758
    }
1759

    
1760
    my $net1 = $networkreg{$valref->{'networkuuid1'}};
1761
    my $networkname1 = $net1->{'name'};
1762
    $valref->{'networkname1'} = $networkname1 if ($networkname1);
1763
    my $net2 = $networkreg{$valref->{'networkuuid2'}};
1764
    my $networkname2 = $net2->{'name'};
1765
    $valref->{'networkname2'} = $networkname2 if ($networkname2);
1766
    my $name = $valref->{'name'};
1767
    $valref->{'name'} = $imagename unless $name;
1768

    
1769
    # Make sure we start shutoff servers on the node their image is on
1770
    if ($valref->{'status'} eq "shutoff" || $valref->{'status'} eq "inactive") {
1771
        my $node = $nodereg{$valref->{'mac'}};
1772
        if ($valref->{'image'} =~ /\/mnt\/stabile\/node\//) {
1773
            $valref->{'mac'} = $img->{'mac'};
1774
            $valref->{'macname'} = $node->{'name'};
1775
            $valref->{'macip'} = $node->{'ip'};
1776
        } elsif ($valref->{'image2'} =~ /\/mnt\/stabile\/node\//) {
1777
            $valref->{'mac'} = $imagereg{$valref->{'image2'}}->{'mac'};
1778
            $valref->{'macname'} = $node->{'name'};
1779
            $valref->{'macip'} = $node->{'ip'};
1780
        } elsif ($valref->{'image3'} =~ /\/mnt\/stabile\/node\//) {
1781
            $valref->{'mac'} = $imagereg{$valref->{'image3'}}->{'mac'};
1782
            $valref->{'macname'} = $node->{'name'};
1783
            $valref->{'macip'} = $node->{'ip'};
1784
        } elsif ($valref->{'image4'} =~ /\/mnt\/stabile\/node\//) {
1785
            $valref->{'mac'} = $imagereg{$valref->{'image4'}}->{'mac'};
1786
            $valref->{'macname'} = $node->{'name'};
1787
            $valref->{'macip'} = $node->{'ip'};
1788
        }
1789
    }
1790
# Mark domains we have heard from in the last 20 secs as inactive
1791
    my $dbtimestamp = 0;
1792
    $dbtimestamp = $register{$valref->{'uuid'}}->{'timestamp'} if ($register{$valref->{'uuid'}});
1793
    my $timediff = $current_time - $dbtimestamp;
1794
    if ($timediff >= 20) {
1795
        if  (! ($valref->{'status'} eq "shutoff"
1796
                || $valref->{'status'} eq "starting"
1797
            #    || $valref->{'status'} eq "shuttingdown"
1798
            #    || $valref->{'status'} eq "destroying"
1799
                || ($valref->{'status'} =~ /moving/ && $timediff<40)
1800
            )) { # Move has probably failed
1801
            $valref->{'status'} = "inactive";
1802
            $imagereg{$valref->{'image'}}->{'status'} = "used" if ($valref->{'image'} && $imagereg{$valref->{'image'}});
1803
            $imagereg{$valref->{'image2'}}->{'status'} = "used" if ($valref->{'image2'} && $imagereg{$valref->{'image2'}});
1804
            $imagereg{$valref->{'image3'}}->{'status'} = "used" if ($valref->{'image3'} && $imagereg{$valref->{'image3'}});
1805
            $imagereg{$valref->{'image4'}}->{'status'} = "used" if ($valref->{'image4'} && $imagereg{$valref->{'image4'}});
1806
        }
1807
    };
1808
#    untie %imagereg;
1809
    return $valref;
1810
}
1811

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

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

    
1819
    foreach my $k (@regkeys) {
1820
        my $valref = $register{$k};
1821
        next unless ($userreg{$valref->{'user'}});
1822
        my $dbtimestamp = $valref->{'timestamp'};
1823
        my $dbstatus = $valref->{'status'};
1824
        my $timediff = $current_time - $dbtimestamp;
1825
        my $imgstatus;
1826
        my $domstatus;
1827
        if ($timediff >= 20) {
1828
            if  ( $valref->{'status'} eq "shutoff" ) {
1829
                $imgstatus = 'used';
1830
            } elsif ((  $valref->{'status'} eq "starting"
1831
                            || $valref->{'status'} eq "shuttingdown"
1832
                        ) && $timediff>50) {
1833
                $imgstatus = 'used';
1834
                $domstatus = 'inactive';
1835
            } elsif ($valref->{'status'} eq "destroying" || $valref->{'status'} eq "moving") {
1836
                ;
1837
            } else {
1838
                $domstatus = 'inactive';
1839
                $imgstatus = 'used';
1840
            }
1841
            $valref->{'status'} = $domstatus if ($domstatus);
1842
            my $image = $valref->{'image'};
1843
            my $image2 = $valref->{'image2'};
1844
            my $image3 = $valref->{'image3'};
1845
            my $image4 = $valref->{'image4'};
1846
            $imagereg{$image}->{'status'} = $imgstatus if ($imgstatus);
1847
            $imagereg{$image2}->{'status'} = $imgstatus if ($image2 && $imgstatus);
1848
            $imagereg{$image3}->{'status'} = $imgstatus if ($image3 && $imgstatus);
1849
            $imagereg{$image4}->{'status'} = $imgstatus if ($image4 && $imgstatus);
1850
            if ($domstatus eq 'inactive ' && $dbstatus ne 'inactive') {
1851
                $main::updateUI->({ tab=>'servers',
1852
                                    user=>$valref->{'user'},
1853
                                    uuid=>$valref->{'uuid'},
1854
                                    sender=>'updateRegister',
1855
                                    status=>'inactive'})
1856
            }
1857
        };
1858

    
1859
    }
1860
    untie %userreg;
1861
    untie %imagereg;
1862
}
1863

    
1864

    
1865
sub locateTargetNode {
1866
    my ($uuid, $dmac, $mem, $vcpu, $image, $image2, $image3, $image4, $hypervisor, $smac, $stormove)= @_;
1867
    my $targetname;
1868
    my $targetip;
1869
    my $port;
1870
    my $targetnode;
1871
    my $targetindex; # Availability index of located target node
1872
    my %avhash;
1873

    
1874
    $dmac = '' unless ($isadmin); # Only allow admins to select specific node
1875
    my $mnode = $register{$uuid};
1876
    if (!$dmac
1877
            && $mnode->{'locktonode'} eq 'true'
1878
            && $mnode->{'mac'}
1879
            && $mnode->{'mac'} ne '--'
1880
            ) {
1881
        $dmac = $mnode->{'mac'}; # Server is locked to specific node
1882
    }
1883
    if ($dmac && !$nodereg{$dmac}) {
1884
        $main::syslogit->($user, "info", "The target node $dmac no longer exists, starting $uuid on another node if possible");
1885
        $dmac = '';
1886
    }
1887
    my $imageonnode = ((!$stormove) && ($image =~ /\/mnt\/stabile\/node\//
1888
                                          || $image2 =~ /\/mnt\/stabile\/node\//
1889
                                          || $image3 =~ /\/mnt\/stabile\/node\//
1890
                                          || $image4 =~ /\/mnt\/stabile\/node\//
1891
                                          ));
1892

    
1893
    foreach $node (values %nodereg) {
1894
        my $nstatus = $node->{'status'};
1895
        my $maintenance = $node->{'maintenance'};
1896
        my $nmac = $node->{'mac'};
1897

    
1898
        if (($nstatus eq 'running' || $nstatus eq 'asleep' || $nstatus eq 'maintenance' || $nstatus eq 'waking')
1899
         && $smac ne $nmac
1900
         && (( ($node->{'memfree'} > $mem+512*1024)
1901
         && (($node->{'vmvcpus'} + $vcpu) <= ($cpuovercommision * $node->{'cpucores'} * $node->{'cpucount'})) ) || $action eq 'listnodeavailability')
1902
        ) {
1903
        # Determine how available this node is
1904
        # Available memory
1905
            my $memweight = 0.2; # memory weighing factor
1906
            my $memindex = $avhash{$nmac}->{'memindex'} = int(100* $memweight* $node->{'memfree'} / (1024*1024) )/100;
1907
        # Free cores
1908
            my $cpuindex = $avhash{$nmac}->{'cpuindex'} = int(100*($cpuovercommision * $node->{'cpucores'} * $node->{'cpucount'} - $node->{'vmvcpus'} - $node->{'reservedvcpus'}))/100;
1909
        # Asleep - not asleep gives a +3
1910
            my $sleepindex = $avhash{$nmac}->{'sleepindex'} = ($node->{'status'} eq 'asleep' || $node->{'status'} eq 'waking')?'0':'3';
1911
            $avhash{$nmac}->{'vmvcpus'} = $node->{'vmvcpus'};
1912
#            $avhash{$nmac}->{'cpucommision'} = $cpuovercommision * $node->{'cpucores'} * $node->{'cpucount'};
1913
#            $avhash{$nmac}->{'cpureservation'} = $node->{'vmvcpus'} + $node->{'reservedvcpus'};
1914
            $avhash{$nmac}->{'name'} = $node->{'name'};
1915
            $avhash{$nmac}->{'mac'} = $node->{'mac'};
1916

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

    
2027
sub destroyUserServers {
2028
    my $username = shift;
2029
    my $wait = shift; # Should we wait for servers do die
2030
    my $duuid = shift;
2031
    return unless ($username && ($isadmin || $user eq $username));
2032
    my @updateList;
2033

    
2034
    my @regkeys = (tied %register)->select_where("user = '$username'");
2035
    foreach my $uuid (@regkeys) {
2036
        if ($register{$uuid}->{'user'} eq $username
2037
            && $register{$uuid}->{'status'} ne 'shutoff'
2038
            && (!$duuid || $duuid eq $uuid)
2039
        ) {
2040
            $postreply .= "Destroying $username server $register{$uuid}->{'name'}, $uuid\n";
2041
            Destroy($uuid);
2042
            push (@updateList,{ tab=>'servers',
2043
                                user=>$user,
2044
                                uuid=>$duuid,
2045
                                status=>'destroying'});
2046
        }
2047
    }
2048
    $main::updateUI->(@updateList) if (@updateList);
2049
    if ($wait) {
2050
        my @regkeys = (tied %register)->select_where("user = '$username'");
2051
        my $activeservers = 1;
2052
        my $i = 0;
2053
        while ($activeservers && $i<30) {
2054
            $activeservers = 0;
2055
            foreach my $k (@regkeys) {
2056
                my $valref = $register{$k};
2057
                if ($username eq $valref->{'user'}
2058
                    && ($valref->{'status'} ne 'shutoff'
2059
                    && $valref->{'status'} ne 'inactive')
2060
                    && (!$duuid || $duuid eq $valref->{'uuid'})
2061
                ) {
2062
                    $activeservers = $valref->{'uuid'};
2063
                }
2064
            }
2065
            $i++;
2066
            if ($activeservers) {
2067
                my $res .= "Status=OK Waiting $i for server $register{$activeservers}->{'name'}, $register{$activeservers}->{'status'} to die...\n";
2068
            #    print $res if ($console);
2069
                $postreply .= $res;
2070
                sleep 2;
2071
            }
2072
        }
2073
        $postreply .= "Status=OK Servers halted for $username\n" unless ($activeservers);
2074
    }
2075
    return $postreply;
2076
}
2077

    
2078
sub removeUserServers {
2079
    my $username = shift;
2080
    my $uuid = shift;
2081
    my $destroy = shift; # Should running servers be destroyed before removing
2082
    return unless (($isadmin || $user eq $username) && !$isreadonly);
2083
    $user = $username;
2084
    my @regkeys = (tied %register)->select_where("user = '$username'");
2085
    foreach my $ruuid (@regkeys) {
2086
        next if ($uuid && $ruuid ne $uuid);
2087
        if ($destroy && $register{$ruuid}->{'user'} eq $username && ($register{$ruuid}->{'status'} ne 'shutoff' && $register{$ruuid}->{'status'} ne 'inactive')) {
2088
            destroyUserServers($username, 1, $ruuid);
2089
        }
2090

    
2091
        if ($register{$ruuid}->{'user'} eq $username && ($register{$ruuid}->{'status'} eq 'shutoff' || $register{$ruuid}->{'status'} eq 'inactive')) {
2092
            $postreply .= "Removing $username server $register{$ruuid}->{'name'}, $ruuid" . ($console?'':'<br>') . "\n";
2093
            Remove($ruuid);
2094
        }
2095
    }
2096
}
2097

    
2098
sub Remove {
2099
    my ($uuid, $action) = @_;
2100
    if ($help) {
2101
        return <<END
2102
DELETE:uuid:
2103
Removes a server. Server must be shutoff. Does not remove associated images or networks.
2104
END
2105
    }
2106
    my $reguser = $register{$uuid}->{'user'};
2107
    my $dbstatus = $register{$uuid}->{'status'};
2108
    my $image = $register{$uuid}->{'image'};
2109
    my $image2 = $register{$uuid}->{'image2'};
2110
    my $image3 = $register{$uuid}->{'image3'};
2111
    my $image4 = $register{$uuid}->{'image4'};
2112
    my $name = $register{$uuid}->{'name'};
2113
    $image2 = '' if ($image2 eq '--');
2114
    $image3 = '' if ($image3 eq '--');
2115
    $image4 = '' if ($image4 eq '--');
2116

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

    
2121
        # Delete software packages and monitors from register
2122
        $postmsg .= deletePackages($uuid);
2123
        my $sname = $register{$uuid}->{'name'};
2124
        utf8::decode($sname);
2125
        $postmsg .= deleteMonitors($uuid)?" deleted monitors for $sname ":'';
2126

    
2127
        delete $register{$uuid};
2128
        delete $xmlreg{$uuid};
2129

    
2130
        unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
2131
        $imagereg{$image}->{'status'} = "unused" if ($imagereg{$image});
2132
        $imagereg{$image2}->{'status'} = "unused" if ($image2 && $imagereg{$image2});
2133
        $imagereg{$image3}->{'status'} = "unused" if ($image3 && $imagereg{$image3});
2134
        $imagereg{$image4}->{'status'} = "unused" if ($image4 && $imagereg{$image4});
2135
        untie %imagereg;
2136

    
2137
        # Delete metrics
2138
        my $metricsdir = "/var/lib/graphite/whisper/domains/$uuid";
2139
        `rm -r $metricsdir` if (-e $metricsdir);
2140
        my $rrdfile = "/var/cache/rrdtool/".$uuid."_highres.rrd";
2141
        `rm $rrdfile` if (-e $rrdfile);
2142

    
2143
        $main::syslogit->($user, "info", "Deleted domain $uuid from db");
2144
        utf8::decode($name);
2145
        $postmsg .= " deleted server $name";
2146
        $postreply = "[]";
2147
        sleep 1;
2148
    } else {
2149
        $postreply .= "Status=ERROR Cannot delete a $dbstatus server\n";
2150
    }
2151
    return $postreply;
2152
}
2153

    
2154
# Delete all monitors belonging to a server
2155
sub deleteMonitors {
2156
    my ($serveruuid) = @_;
2157
    my $match;
2158
    if ($serveruuid) {
2159
        if ($register{$serveruuid}->{'user'} eq $user || $isadmin) {
2160
            local($^I, @ARGV) = ('.bak', "/etc/mon/mon.cf");
2161
            # undef $/; # This makes <> read in the entire file in one go
2162
            my $uuidmatch;
2163
            while (<>) {
2164
                if (/^watch (\S+)/) {
2165
                    if ($1 eq $serveruuid) {$uuidmatch = $serveruuid}
2166
                    else {$uuidmatch = ''};
2167
                };
2168
                if ($uuidmatch) {
2169
                    $match = 1;
2170
                } else {
2171
                    #chomp;
2172
                    print unless (/^hostgroup $serveruuid/);
2173
                }
2174
                close ARGV if eof;
2175
            }
2176
            #$/ = "\n";
2177
        }
2178
        unlink glob "/var/log/stabile/*:$serveruuid:*";
2179
    }
2180
    `/usr/bin/moncmd reset keepstate` if ($match);
2181
    return $match;
2182
}
2183

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

    
2188
    my @domains;
2189
    if ($issystem) {
2190
        foreach my $valref (values %register) {
2191
            if (($valref->{'system'} eq $uuid || $uuid eq '*')
2192
                    && ($valref->{'user'} eq $user || $fulllist)) {
2193
                push(@domains, $valref->{'uuid'});
2194
            }
2195
        }
2196
    } else { # Allow if domain no longer exists or belongs to user
2197
        push(@domains, $uuid) if (!$register{$uuid} || $register{$uuid}->{'user'} eq $user || $fulllist);
2198
    }
2199

    
2200
    foreach my $domuuid (@domains) {
2201
        foreach my $packref (values %packreg) {
2202
            my $id = $packref->{'id'};
2203
            if (substr($id, 0,36) eq $domuuid || ($uuid eq '*' && $packref->{'user'} eq $user)) {
2204
                delete $packreg{$id};
2205
            }
2206
        }
2207
    }
2208
    tied(%packreg)->commit;# if (%packreg);
2209
    if ($issystem) {
2210
        my $sname = $register{$uuid}->{'name'};
2211
        utf8::decode($sname);
2212
        return "Status=OK Cleared packages for $sname\n";
2213
    } elsif ($register{$uuid}) {
2214
        my $sname = $register{$uuid}->{'name'};
2215
        utf8::decode($sname);
2216
        return "Status=OK Cleared packages for $sname\n";
2217
    } else {
2218
        return "Status=OK Cleared packages. System not registered\n";
2219
    }
2220
}
2221

    
2222
sub Save {
2223
    my ($uuid, $action, $obj) = @_;
2224
    if ($help) {
2225
        return <<END
2226
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:
2227
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.
2228
Depending on your privileges not all changes are permitted. If you save without specifying a uuid, a new server is created.
2229
If you pass [user] parameter it is assumed you want to move server to this user's account.
2230
Supported parameters:
2231

    
2232
uuid: UUID
2233
name: string
2234
user: string
2235
system: UUID of stack this server belongs to
2236
autostart: true|false
2237
locktonode: true|false
2238
mac: MAC address of target node
2239

    
2240
memory: int bytes
2241
vcpu: int
2242
boot: hd|cdrom|network
2243
loader: bios|uefi
2244
diskbus: virtio|ide|scsi
2245
nicmodel1: virtio|rtl8139|ne2k_pci|e1000|i82551|i82557b|i82559er|pcnet
2246
vgpu: int
2247

    
2248
cdrom: string path
2249
image: string path
2250
image2: string path
2251
image3: string path
2252
image4: string path
2253

    
2254
networkuuid1: UUID of network connection
2255
networkuuid2: UUID of network connection
2256
networkuuid3: UUID of network connection
2257

    
2258
END
2259
    }
2260

    
2261
# notes, opemail, opfullname, opphone, email, fullname, phone, services, recovery, alertemail
2262
# notes: string
2263
# opemail: string
2264
# opfullname: string
2265
# opphone: string
2266
# email: string
2267
# fullname: string
2268
# phone: string
2269
# services: string
2270
# recovery: string
2271
# alertemail: string
2272

    
2273
    my $system = $obj->{system};
2274
    my $newsystem = $obj->{newsystem};
2275
    my $buildsystem = $obj->{buildsystem};
2276
    my $nicmac1 = $obj->{nicmac1};
2277
    $console = $console || $obj->{console};
2278

    
2279
    $postmsg = '' if ($buildsystem);
2280
    if (!$uuid && $nicmac1) {
2281
        $uuid = nicmac1ToUuid($nicmac1); # If no uuid try to locate based on mac
2282
    }
2283
    if (!$uuid && $uripath =~ /servers(\.cgi)?\/(.+)/) { # Try to parse uuid out of URI
2284
        my $huuid = $2;
2285
        if ($ug->to_string($ug->from_string($huuid)) eq $huuid) { # Check for valid uuid
2286
            $uuid = $huuid;
2287
        }
2288
    }
2289
    my $regserv = $register{$uuid};
2290
    my $status = $regserv->{'status'} || 'new';
2291
    if ((!$uuid) && $status eq 'new') {
2292
        my $ug = new Data::UUID;
2293
        $uuid = $ug->create_str();
2294
    };
2295
    unless ($uuid && length $uuid == 36){
2296
        $postmsg = "Status=Error No valid uuid ($uuid), $obj->{image}";
2297
        return $postmsg;
2298
    }
2299
    $nicmac1 = $nicmac1 || $regserv->{'nicmac1'};
2300
    my $name = $obj->{name} || $regserv->{'name'};
2301
    my $memory = $obj->{memory} || $regserv->{'memory'};
2302
    my $vcpu = $obj->{vcpu} || $regserv->{'vcpu'};
2303
    my $image = $obj->{image} || $regserv->{'image'};
2304
    my $imagename = $obj->{imagename} || $regserv->{'imagename'};
2305
    my $image2 = $obj->{image2} || $regserv->{'image2'};
2306
    my $image2name = $obj->{image2name} || $regserv->{'image2name'};
2307
    my $image3 = $obj->{image3} || $regserv->{'image3'};
2308
    my $image3name = $obj->{image3name} || $regserv->{'image3name'};
2309
    my $image4 = $obj->{image4} || $regserv->{'image4'};
2310
    my $image4name = $obj->{image4name} || $regserv->{'image4name'};
2311
    my $diskbus = $obj->{diskbus} || $regserv->{'diskbus'};
2312
    my $cdrom = $obj->{cdrom} || $regserv->{'cdrom'};
2313
    my $boot = $obj->{boot} || $regserv->{'boot'};
2314
    my $loader = $obj->{loader} || $regserv->{'loader'};
2315
    my $networkuuid1 = ($obj->{networkuuid1} || $obj->{networkuuid1} eq '0')?$obj->{networkuuid1}:$regserv->{'networkuuid1'};
2316
    my $networkid1 = $obj->{networkid1} || $regserv->{'networkid1'};
2317
    my $networkname1 = $obj->{networkname1} || $regserv->{'networkname1'};
2318
    my $nicmodel1 = $obj->{nicmodel1} || $regserv->{'nicmodel1'};
2319
    my $networkuuid2 = ($obj->{networkuuid2} || $obj->{networkuuid2} eq '0')?$obj->{networkuuid2}:$regserv->{'networkuuid2'};
2320
    my $networkid2 = $obj->{networkid2} || $regserv->{'networkid2'};
2321
    my $networkname2 = $obj->{networkname2} || $regserv->{'networkname2'};
2322
    my $nicmac2 = $obj->{nicmac2} || $regserv->{'nicmac2'};
2323
    my $networkuuid3 = ($obj->{networkuuid3} || $obj->{networkuuid3} eq '0')?$obj->{networkuuid3}:$regserv->{'networkuuid3'};
2324
    my $networkid3 = $obj->{networkid3} || $regserv->{'networkid3'};
2325
    my $networkname3 = $obj->{networkname3} || $regserv->{'networkname3'};
2326
    my $nicmac3 = $obj->{nicmac3} || $regserv->{'nicmac3'};
2327
    my $notes = $obj->{notes} || $regserv->{'notes'};
2328
    my $autostart = $obj->{autostart} || $regserv->{'autostart'};
2329
    my $locktonode = $obj->{locktonode} || $regserv->{'locktonode'};
2330
    my $mac = $obj->{mac} || $regserv->{'mac'};
2331
    my $created = $regserv->{'created'} || time;
2332
    # Sanity checks
2333
    my $tenderpaths = $Stabile::config->get('STORAGE_POOLS_LOCAL_PATHS') || "/mnt/stabile/images";
2334
    my @tenderpathslist = split(/,\s*/, $tenderpaths);
2335

    
2336
    $networkid1 = $networkreg{$networkuuid1}->{'id'};
2337
    my $networktype1 = $networkreg{$networkuuid1}->{'type'};
2338
    my $networktype2;
2339
    if (!$nicmac1 || $nicmac1 eq "--") {$nicmac1 = randomMac();}
2340
    if ($networkuuid2 && $networkuuid2 ne "--") {
2341
        $networkid2 = $networkreg{$networkuuid2}->{'id'};
2342
        $nicmac2 = randomMac() if (!$nicmac2 || $nicmac2 eq "--");
2343
        $networktype2 = $networkreg{$networkuuid2}->{'type'};
2344
    }
2345
    if ($networkuuid3 && $networkuuid3 ne "--") {
2346
        $networkid3 = $networkreg{$networkuuid3}->{'id'};
2347
        $networkname3 = $networkreg{$networkuuid3}->{'name'};
2348
        $nicmac3 = randomMac() if (!$nicmac3 || $nicmac3 eq "--");
2349
        $networktype3 = $networkreg{$networkuuid3}->{'type'};
2350
    }
2351

    
2352
    my $imgdup;
2353
    my $netdup;
2354
    my $json_text; # returned if all goes well
2355

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

    
2358
    if ($networkid1 > 1 && $networkid2 > 1 && $networktype1 ne 'gateway' && $networktype2 ne 'gateway'
2359
        && $networkuuid1 eq $networkuuid2) {
2360
        $netdup = 1;
2361
    }
2362
    if ($networkid1 > 1 && $networkid3 > 1 && $networktype1 ne 'gateway' && $networktype3 ne 'gateway'
2363
        && $networkuuid1 eq $networkuuid3) {
2364
        $netdup = 11;
2365
    }
2366
    if ($image eq $image2
2367
        || $image eq $image3
2368
        || $image eq $image4
2369
        || $image2 && $image2 ne '--' && $image2 eq $image3
2370
        || $image2 && $image2 ne '--' && $image2 eq $image4
2371
        || $image3 && $image3 ne '--' && $image3 eq $image4
2372
    ) {
2373
        $imgdup = 1;
2374
    } elsif ($image =~ m/\.master\.qcow2/
2375
        || $image2 =~ m/\.master\.qcow2/
2376
        || $image3 =~ m/\.master\.qcow2/
2377
        || $image4 =~ m/\.master\.qcow2/
2378
    ) {
2379
        $imgdup = 2;
2380
    } else {
2381
        # Check if another server is using image
2382
        my @regkeys = (tied %register)->select_where("user = '$user' OR user = 'common'");
2383
        foreach my $k (@regkeys) {
2384
            my $val = $register{$k};
2385
            if ($val->{'uuid'} ne $uuid) {
2386
                if (
2387
                    $image eq $val->{'image'} || $image eq $val->{'image2'}|| $image eq $val->{'image3'}|| $image eq $val->{'image4'}
2388
                ) {
2389
                    $imgdup = 51;
2390
                } elsif ($image2 && $image2 ne "--" &&
2391
                    ($image2 eq $val->{'image'} || $image2 eq $val->{'image2'} || $image2 eq $val->{'image3'} || $image2 eq $val->{'image4'})
2392
                ) {
2393
                    $imgdup = 52;
2394
                } elsif ($image3 && $image3 ne "--" &&
2395
                    ($image3 eq $val->{'image'} || $image3 eq $val->{'image2'} || $image3 eq $val->{'image3'} || $image3 eq $val->{'image4'})
2396
                ) {
2397
                    $imgdup = 53;
2398
                } elsif ($image4 && $image4 ne "--" &&
2399
                    ($image4 eq $val->{'image'} || $image4 eq $val->{'image2'} || $image4 eq $val->{'image3'} || $image4 eq $val->{'image4'})
2400
                ) {
2401
                    $imgdup = 54;
2402
                }
2403

    
2404
                if ($networkid1>1) {
2405
                    if ($networktype1 ne 'gateway' &&
2406
                        ($networkuuid1 eq $val->{'networkuuid1'} || $networkuuid1 eq $val->{'networkuuid2'})
2407
                    ) {
2408
                        $netdup = 51;
2409
                    }
2410
                }
2411
                if ($networkid2>1) {
2412
                    if ($networktype2 ne 'gateway' && $networkuuid2 && $networkuuid2 ne "--" &&
2413
                        ($networkuuid2 eq $val->{'networkuuid1'} || $networkuuid2 eq $val->{'networkuuid2'})
2414
                    ) {
2415
                        $netdup = 52;
2416
                    }
2417
                }
2418
            }
2419
        }
2420
        my $legalpath;
2421
        if ($image =~ m/\/mnt\/stabile\/node\/$user/) {
2422
            $legalpath = 1;
2423
        } else {
2424
            foreach my $path (@tenderpathslist) {
2425
                if ($image =~ m/$path\/$user/) {
2426
                    $legalpath = 1;
2427
                    last;
2428
                }
2429
            }
2430
        }
2431
        $imgdup = 6 unless $legalpath;
2432
        if ($image2 && $image2 ne "--") { # TODO: We should probably check for conflicting nodes for image3 and image 4 too
2433
            if ($image2 =~ m/\/mnt\/stabile\/node\/$user/) {
2434
                if ($image =~ m/\/mnt\/stabile\/node\/$user/) {
2435
                    if ($imagereg{$image}->{'mac'} eq $imagereg{$image2}->{'mac'}) {
2436
                        $legalpath = 1;
2437
                    } else {
2438
                        $legalpath = 0; # Images are on two different nodes
2439
                    }
2440
                } else {
2441
                    $legalpath = 1;
2442
                }
2443
            } else {
2444
                $legalpath = 0;
2445
                foreach my $path (@tenderpathslist) {
2446
                    if ($image2 =~ m/$path\/$user/) {
2447
                        $legalpath = 1;
2448
                        last;
2449
                    }
2450
                }
2451
            }
2452
            $imgdup = 7 unless $legalpath;
2453
        }
2454
    }
2455

    
2456
    if (!$imgdup && !$netdup) {
2457
        if ($status eq "new") {
2458
            $status = "shutoff";
2459
            $name = $name || 'New Server';
2460
            $memory = $memory || 1024;
2461
            $vcpu = $vcpu || 1;
2462
            $imagename = $imagename || '--';
2463
            $image2 = $image2 || '--';
2464
            $image2name = $image2name || '--';
2465
            $image3 = $image3 || '--';
2466
            $image3name = $image3name || '--';
2467
            $image4 = $image4 || '--';
2468
            $image4name = $image4name || '--';
2469
            $diskbus = $diskbus || 'ide';
2470
            $cdrom = $cdrom || '--';
2471
            $boot = $boot || 'hd';
2472
            $loader = $loader || 'bios';
2473
            $networkuuid1 = $networkuuid1 || 1;
2474
            $networkid1 = $networkid1 || 1;
2475
            $networkname1 = $networkname1 || '--';
2476
            $nicmodel1 = $nicmodel1 || 'rtl8139';
2477
            $nicmac1 = $nicmac1 || randomMac();
2478
            $networkuuid2 = $networkuuid2 || '--';
2479
            $networkid2 = $networkid2 || '--';
2480
            $networkname2 = $networkname2 || '--';
2481
            $nicmac2 = $nicmac2 || randomMac();
2482
            $networkuuid3 = $networkuuid3 || '--';
2483
            $networkid3 = $networkid3 || '--';
2484
            $networkname3 = $networkname3 || '--';
2485
            $nicmac3 = $nicmac3 || randomMac();
2486
            #    $uiuuid = $uuid; # No need to update ui for new server with jsonreststore
2487
            $postmsg .= "OK Created new server: $name";
2488
            $postmsg .= ", uuid: $uuid " if ($console);
2489
        }
2490
        # Update status of images
2491
        my @imgs = ($image, $image2, $image3, $image4);
2492
        my @imgkeys = ('image', 'image2', 'image3', 'image4');
2493
        for (my $i=0; $i<4; $i++) {
2494
            my $img = $imgs[$i];
2495
            my $k = $imgkeys[$i];
2496
            my $regimg = $imagereg{$img};
2497
            # if ($img && $img ne '--' && ($status eq 'new' || $img ne $regserv->{$k})) { # Servers image changed - update image status
2498
            if ($img && $img ne '--') { # Always update image status
2499
                $regimg->{'status'} = 'used' if (
2500
                    $regimg->{'status'} eq 'unused'
2501
                        # Image cannot be active if server is shutoff
2502
                        || ($regimg->{'status'} eq 'active' && $status eq 'shutoff')
2503
                );
2504
                $regimg->{'domains'} = $uuid;
2505
                $regimg->{'domainnames'} = $name;
2506
            }
2507
            # If image has changed, release the old image
2508
            if ($status ne 'new' && $img ne $regserv->{$k} && $imagereg{$regserv->{$k}}) {
2509
                $imagereg{$regserv->{$k}}->{'status'} = 'unused';
2510
                delete $imagereg{$regserv->{$k}}->{'domains'};
2511
                delete $imagereg{$regserv->{$k}}->{'domainnames'};
2512
            }
2513
        }
2514

    
2515
        my $valref = {
2516
            uuid=>$uuid,
2517
            user=>$user,
2518
            name=>$name,
2519
            memory=>$memory,
2520
            vcpu=>$vcpu,
2521
            image=>$image,
2522
            imagename=>$imagename,
2523
            image2=>$image2,
2524
            image2name=>$image2name,
2525
            image3=>$image3,
2526
            image3name=>$image3name,
2527
            image4=>$image4,
2528
            image4name=>$image4name,
2529
            diskbus=>$diskbus,
2530
            cdrom=>$cdrom,
2531
            boot=>$boot,
2532
            loader=>$loader,
2533
            networkuuid1=>$networkuuid1,
2534
            networkid1=>$networkid1,
2535
            networkname1=>$networkname1,
2536
            nicmodel1=>$nicmodel1,
2537
            nicmac1=>$nicmac1,
2538
            networkuuid2=>$networkuuid2,
2539
            networkid2=>$networkid2,
2540
            networkname2=>$networkname2,
2541
            nicmac2=>$nicmac2,
2542
            networkuuid3=>$networkuuid3,
2543
            networkid3=>$networkid3,
2544
            networkname3=>$networkname3,
2545
            nicmac3=>$nicmac3,
2546
            status=>$status,
2547
            notes=>$notes,
2548
            autostart=>$autostart,
2549
            locktonode=>$locktonode,
2550
            action=>"",
2551
            created=>$created
2552
        };
2553
        $valref->{'system'} = $system if ($system);
2554
        if ($mac && $locktonode eq 'true') {
2555
            $valref->{'mac'} = $mac;
2556
            $valref->{'macip'} = $nodereg{$mac}->{'ip'};
2557
            $valref->{'macname'} = $nodereg{$mac}->{'name'};
2558
        }
2559
        if ($newsystem) {
2560
            my $ug = new Data::UUID;
2561
            $sysuuid = $ug->create_str();
2562
            $valref->{'system'} = $sysuuid;
2563
            $postmsg .= "OK sysuuid: $sysuuid " if ($console);
2564
        }
2565

    
2566
        # Remove domain uuid from old networks. Leave gateways alone - they get updated on next listing
2567
        my $oldnetworkuuid1 = $regserv->{'networkuuid1'};
2568
        if ($oldnetworkuuid1 ne $networkuuid1 && $networkreg{$oldnetworkuuid1}) {
2569
            $networkreg{$oldnetworkuuid1}->{'domains'} =~ s/($uuid)(,?)( ?)//;
2570
        }
2571
        $register{$uuid} = validateItem($valref);
2572

    
2573
        if ($networkreg{$networkuuid1}->{'type'} eq 'gateway') {
2574
            # We now remove before adding to support API calls that dont necessarily list afterwards
2575
            $networkreg{$networkuuid1}->{'domains'} =~ s/($uuid)(,?)( ?)//;
2576
            my $domains = $networkreg{$networkuuid1}->{'domains'};
2577
            $networkreg{$networkuuid1}->{'domains'} = ($domains?"$domains, ":"") . $uuid;
2578

    
2579
            $networkreg{$networkuuid1}->{'domainnames'} =~ s/($name)(,?)( ?)//;
2580
            my $domainnames = $networkreg{$networkuuid1}->{'domainnames'};
2581
            $networkreg{$networkuuid1}->{'domainnames'} = ($domainnames?"$domainnames, ":"") . $name;
2582
        } else {
2583
            $networkreg{$networkuuid1}->{'domains'}  = $uuid;
2584
            $networkreg{$networkuuid1}->{'domainnames'}  = $name;
2585
        }
2586

    
2587
        if ($networkuuid2 && $networkuuid2 ne '--') {
2588
            if ($networkreg{$networkuuid2}->{'type'} eq 'gateway') {
2589
                $networkreg{$networkuuid2}->{'domains'} =~ s/($uuid)(,?)( ?)//;
2590
                my $domains = $networkreg{$networkuuid2}->{'domains'};
2591
                $networkreg{$networkuuid2}->{'domains'} = ($domains?"$domains, ":"") . $uuid;
2592

    
2593
                $networkreg{$networkuuid2}->{'domainnames'} =~ s/($name)(,?)( ?)//;
2594
                my $domainnames = $networkreg{$networkuuid2}->{'domainnames'};
2595
                $networkreg{$networkuuid2}->{'domainnames'} = ($domainnames?"$domainnames, ":"") . $name;
2596
            } else {
2597
                $networkreg{$networkuuid2}->{'domains'}  = $uuid;
2598
                $networkreg{$networkuuid2}->{'domainnames'}  = $name;
2599
            }
2600
        }
2601

    
2602
        if ($networkuuid3 && $networkuuid3 ne '--') {
2603
            if ($networkreg{$networkuuid3}->{'type'} eq 'gateway') {
2604
                my $domains = $networkreg{$networkuuid3}->{'domains'};
2605
                $networkreg{$networkuuid3}->{'domains'} = ($domains?"$domains, ":"") . $uuid;
2606
                my $domainnames = $networkreg{$networkuuid3}->{'domainnames'};
2607
                $networkreg{$networkuuid3}->{'domainnames'} = ($domainnames?"$domainnames, ":"") . $name;
2608
            } else {
2609
                $networkreg{$networkuuid3}->{'domains'}  = $uuid;
2610
                $networkreg{$networkuuid3}->{'domainnames'}  = $name;
2611
            }
2612
        }
2613
        my %jitem = %{$register{$uuid}};
2614
        $json_text = to_json(\%jitem, {pretty=>1});
2615
        $json_text =~ s/null/"--"/g;
2616
        $uiuuid = $uuid;
2617
        $uiname = $name;
2618

    
2619
        tied(%register)->commit;
2620
        tied(%networkreg)->commit;
2621
        tied(%imagereg)->commit;
2622

    
2623
    } else {
2624
        $postmsg .= "ERROR This image ($image) cannot be used ($imgdup) " if ($imgdup);
2625
        $postmsg .= "ERROR This network ($networkname1) cannot be used ($netdup)" if ($netdup);
2626
    }
2627

    
2628
    my $domuser = $obj->{'user'};
2629
    # We were asked to move server to another account
2630
    if ($domuser && $domuser ne '--' && $domuser ne $user) {
2631
        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")};
2632
        if ($status eq 'shutoff' || $status eq 'inactive') {
2633
            unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username', CLOBBER=>1}, $Stabile::dbopts)) ) {$posterror =  "Unable to access user register"; return 0;};
2634
            my @accounts = split(/,\s*/, $userreg{$tktuser}->{'accounts'});
2635
            my @accountsprivs = split(/,\s*/, $userreg{$tktuser}->{'accountsprivileges'});
2636
            %ahash = ($tktuser, $userreg{$tktuser}->{'privileges'}); # Include tktuser in accounts hash
2637
            for my $i (0 .. scalar @accounts)
2638
            {
2639
                next unless $accounts[$i];
2640
                $ahash{$accounts[$i]} = $accountsprivs[$i] || 'r';
2641
            }
2642
            untie %userreg;
2643

    
2644
            if (!$isreadonly && $ahash{$domuser} && !($ahash{$domuser} =~ /r/)) { # Check if user is allow to access account
2645
                my $imgdone;
2646
                my $netdone;
2647
                # First move main image
2648
                $Stabile::Images::user = $user;
2649
                require "$Stabile::basedir/cgi/images.cgi";
2650
                $Stabile::Images::console = 1;
2651
                $main::updateUI->({tab=>"servers", user=>$user, message=>"Moving image $imagename to account: $domuser"});
2652
                my $nimage = Stabile::Images::Move($image, $domuser);
2653
                chomp $nimage;
2654
                if ($nimage) {
2655
                    $main::syslogit->($user, "info", "Moving $nimage to account: $domuser");
2656
                    $register{$uuid}->{'image'} = $nimage;
2657
                    $imgdone = 1;
2658
                } else {
2659
                    $main::syslogit->($user, "info", "Unable to move image $imagename to account: $domuser");
2660
                }
2661
                # Move other attached images
2662
                my @images = ($image2, $image3, $image4);
2663
                my @imagenames = ($image2name, $image3name, $image4name);
2664
                my @imagekeys = ('image2', 'image3', 'image4');
2665
                for (my $i=0; $i<3; $i++) {
2666
                    my $img = $images[$i];
2667
                    my $imgname = $imagenames[$i];
2668
                    my $imgkey = $imagekeys[$i];
2669
                    if ($img && $img ne '--') {
2670
                        $main::updateUI->({tab=>"servers", user=>$user, message=>"Moving $imgkey $imgname to account: $domuser"});
2671
                        $nimage = Stabile::Images::Move($img, $domuser);
2672
                        chomp $nimage;
2673
                        if ($nimage) {
2674
                            $main::syslogit->($user, "info", "Moving $nimage to account: $domuser");
2675
                            $register{$uuid}->{$imgkey} = $nimage;
2676
                        } else {
2677
                            $main::syslogit->($user, "info", "Unable to move $imagekeys[$i] $img to account: $domuser");
2678
                        }
2679
                    }
2680
                }
2681
                # Then move network(s)
2682
                if ($imgdone) {
2683
                    $Stabile::Networks::user = $user;
2684
                    require "$Stabile::basedir/cgi/networks.cgi";
2685
                    $Stabile::Networks::console = 1;
2686
                    my @networks = ($networkuuid1, $networkuuid2, $networkuuid3);
2687
                    my @netkeys = ('networkuuid1', 'networkuuid2', 'networkuuid3');
2688
                    my @netnamekeys = ('networkname1', 'networkname2', 'networkname3');
2689
                    for (my $i=0; $i<scalar @networks; $i++) {
2690
                        my $net = $networks[$i];
2691
                        my $netkey = $netkeys[$i];
2692
                        my $netnamekey = $netnamekeys[$i];
2693
                        my $regnet = $networkreg{$net};
2694
                        my $oldid = $regnet->{'id'};
2695
                        next if ($net eq '' || $net eq '--');
2696
                        if ($regnet->{'type'} eq 'gateway') {
2697
                            if ($oldid > 1) { # Private gateway
2698
                                foreach my $networkvalref (values %networkreg) { # use gateway with same id if it exists
2699
                                    if ($networkvalref->{'user'} eq $domuser
2700
                                        && $networkvalref->{'type'} eq 'gateway'
2701
                                        && $networkvalref->{'id'} == $oldid) {
2702
                                        # We found an existing gateway with same id - use it
2703
                                        $register{$uuid}->{$netkey} = $networkvalref->{'uuid'};
2704
                                        $register{$uuid}->{$netnamekey} = $networkvalref->{'name'};
2705
                                        $netdone = 1;
2706
                                        $main::updateUI->({tab=>"networks", user=>$user, message=>"Using network $networkvalref->{'name'} from account: $domuser"});
2707
                                        last;
2708
                                    }
2709
                                }
2710
                                if (!($netdone)) {
2711
                                    # Make a new gateway
2712
                                    my $ug = new Data::UUID;
2713
                                    my $newuuid = $ug->create_str();
2714
                                    Stabile::Networks::save($oldid, $newuuid, $regnet->{'name'}, 'new', 'gateway', '', '', $regnet->{'ports'}, 0, $domuser);
2715
                                    $register{$uuid}->{$netkey} = $newuuid;
2716
                                    $register{$uuid}->{$netnamekey} = $regnet->{'name'};
2717
                                    $netdone = 1;
2718
                                    $main::updateUI->({tab=>"networks", user=>$user, message=>"Created gateway $regnet->{'name'} for account: $domuser"});
2719
                                    $main::syslogit->($user, "info", "Created gateway $regnet->{'name'} for account: $domuser");
2720
                                }
2721
                            } elsif ($oldid==0 || $oldid==1) {
2722
                                $netdone = 1; # Use common gateway
2723
                                $main::updateUI->({tab=>"networks", user=>$user, message=>"Reused network $regnet->{'name'} for account: $domuser"});
2724
                            }
2725
                        } else {
2726
                            my $newid = Stabile::Networks::getNextId('', $domuser);
2727
                            $networkreg{$net}->{'id'} = $newid;
2728
                            $networkreg{$net}->{'user'} = $domuser;
2729
                        #    if ($regnet->{'type'} eq 'internalip' || $regnet->{'type'} eq 'ipmapping') {
2730
                                # Deactivate network and assign new internal ip
2731
                                Stabile::Networks::Deactivate($regnet->{'uuid'});
2732
                                $networkreg{$net}->{'internalip'} =
2733
                                    Stabile::Networks::getNextInternalIP('',$regnet->{'uuid'}, $newid, $domuser);
2734
                        #    }
2735
                            $netdone = 1;
2736
                            $main::updateUI->({tab=>"networks", user=>$user, message=>"Moved network $regnet->{'name'} to account: $domuser"});
2737
                            $main::syslogit->($user, "info", "Moved network $regnet->{'name'} to account: $domuser");
2738
                        }
2739
                    }
2740
                    if ($netdone) {
2741
                        # Finally move the server
2742
                        $register{$uuid}->{'user'} = $domuser;
2743
                        $postmsg .= "OK Moved server $name to account: $domuser";
2744
                        $main::syslogit->($user, "info", "Moved server $name ($uuid) to account: $domuser");
2745
                        $main::updateUI->({tab=>"servers", user=>$user, type=>"update"});
2746
                        # Remove the server's IP from pressurecontrol's cache
2747
                        # Repeat 8 times because pressurecontrol runs 8 http daemons
2748
                        for (my $i = 0; $i < 8; $i++) {
2749
                            my $nuuid = $register{$uuid}->{networkuuid1};
2750
                            `curl "http://localhost:8082//http://$nuuid/networkreload"`;
2751
                        }
2752
                    } else {
2753
                        $postmsg .= "ERROR Unable to move network to account: $domuser";
2754
                        $main::updateUI->({tab=>"image", user=>$user, message=>"Unable to move network to account: $domuser"});
2755
                    }
2756
                } else {
2757
                    $main::updateUI->({tab=>"image", user=>$user, message=>"Could not move image to account: $domuser"});
2758
                }
2759
            } else {
2760
                $postmsg .= "ERROR No access to move server";
2761
            }
2762
        } else {
2763
            $postmsg .= "Error Unable to move $status server";
2764
            $main::updateUI->({tab=>"servers", user=>$user, message=>"Please shut down before moving server"});
2765
        }
2766
        untie %userreg;
2767
    }
2768

    
2769
    if ($console) {
2770
        $postreply = $postmsg;
2771
    } else {
2772
        $postreply = $json_text || $postmsg;
2773
    }
2774
    return $postreply;
2775
    untie %imagereg;
2776
}
2777

    
2778

    
2779
sub Shutdown {
2780
    my ($uuid, $action, $obj) = @_;
2781
    if ($help) {
2782
        return <<END
2783
GET:uuid:
2784
Marks a server for shutdown, i.e. send and ACPI shutdown event to the server. If OS supports ACPI, it begins a shutdown.
2785
END
2786
    }
2787
    $uistatus = "shuttingdown";
2788
    my $dbstatus = $obj->{status};
2789
    my $mac = $obj->{mac};
2790
    my $macname = $obj->{macname};
2791
    my $name = $obj->{name};
2792
    if ($dbstatus eq 'running') {
2793
        my $tasks;
2794
        $tasks = $nodereg{$mac}->{'tasks'} if ($nodereg{$mac});
2795
        $nodereg{$mac}->{'tasks'} = $tasks . "SHUTDOWN $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 $name\n";
2802
    } else {
2803
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
2804
        $postreply .= "Status=ERROR problem $uistatus $name...\n";
2805
    }
2806
    return $postreply;
2807
}
2808

    
2809
sub Suspend {
2810
    my ($uuid, $action, $obj) = @_;
2811
    if ($help) {
2812
        return <<END
2813
GET:uuid:
2814
Marks a server for suspend, i.e. pauses the server. Server must be running
2815
END
2816
    }
2817
    $uistatus = "suspending";
2818
    my $dbstatus = $obj->{status};
2819
    my $mac = $obj->{mac};
2820
    my $macname = $obj->{macname};
2821
    my $name = $obj->{name};
2822
    my $areply = '';
2823
    if ($dbstatus eq 'running') {
2824
        my $tasks = $nodereg{$mac}->{'tasks'};
2825
        $nodereg{$mac}->{'tasks'} = $tasks . "SUSPEND $uuid $user\n";
2826
        tied(%nodereg)->commit;
2827
        $register{$uuid}->{'status'} = $uistatus;
2828
        $register{$uuid}->{'statustime'} = $current_time;
2829
        $uiuuid = $uuid;
2830
        $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus by $macname ($mac)");
2831
        $areply .= "Status=$uistatus OK $uistatus $name.\n";
2832
    } else {
2833
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
2834
        $areply .= "Status=ERROR problem $uistatus $name.\n";
2835
    }
2836
    return $areply;
2837
}
2838

    
2839
sub Resume {
2840
    my ($uuid, $action, $obj) = @_;
2841
    if ($help) {
2842
        return <<END
2843
GET:uuid:
2844
Marks a server for resume running. Server must be paused.
2845
END
2846
    }
2847
    my $dbstatus = $obj->{status};
2848
    my $mac = $obj->{mac};
2849
    my $macname = $obj->{macname};
2850
    my $name = $obj->{name};
2851
    my $image = $obj->{image};
2852
    my $image2 = $obj->{image2};
2853
    my $image3 = $obj->{image3};
2854
    my $image4 = $obj->{image4};
2855
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$posterror = "Unable to access image register"; return;};
2856
    if ($imagereg{$image}->{'status'} ne "paused"
2857
        || ($image2 && $image2 ne '--' && $imagereg{$image}->{'status'} ne "paused")
2858
        || ($image3 && $image3 ne '--' && $imagereg{$image3}->{'status'} ne "paused")
2859
        || ($image4 && $image4 ne '--' && $imagereg{$image4}->{'status'} ne "paused")
2860
    ) {
2861
        $postreply .= "Status=ERROR Image $uuid busy ($imagereg{$image}->{'status'}), please wait 30 sec.\n";
2862
        untie %imagereg;
2863
        return $postreply   ;
2864
    } else {
2865
        untie %imagereg;
2866
    }
2867
    $uistatus = "resuming";
2868
    if ($dbstatus eq 'paused') {
2869
        my $tasks = $nodereg{$mac}->{'tasks'};
2870
        $nodereg{$mac}->{'tasks'} = $tasks . "RESUME $uuid $user\n";
2871
        tied(%nodereg)->commit;
2872
        $register{$uuid}->{'status'} = $uistatus;
2873
        $register{$uuid}->{'statustime'} = $current_time;
2874
        $uiuuid = $uuid;
2875
        $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus by $macname ($mac)");
2876
        $postreply .= "Status=$uistatus OK $uistatus ". $register{$uuid}->{'name'} . "\n";
2877
    } else {
2878
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
2879
        $postreply .= "Status=ERROR problem $uistatus ". $register{$uuid}->{'name'} . "\n";
2880
    }
2881
    return $postreply;
2882
}
2883

    
2884
sub Abort {
2885
    my ($uuid, $action, $obj) = @_;
2886
    if ($help) {
2887
        return <<END
2888
GET:uuid,mac:
2889
Aborts an ongoing server move between nodes initiated with move or stormove.
2890
END
2891
    }
2892
    my $dbstatus = $obj->{status};
2893
    my $dmac = $obj->{mac};
2894
    my $name = $obj->{name};
2895
    if ($isadmin || $register{$uuid}->{user} eq $user) {
2896
        my $tasks = $nodereg{$dmac}->{'tasks'};
2897
        $tasks .= "ABORT $uuid $user\n";
2898
        $nodereg{$dmac}->{'tasks'} = $tasks;
2899
        tied(%nodereg)->commit;
2900
        $postreply = "Status=aborting Aborting move of server $name ($dbstatus) on node $dmac\n";
2901
    } else {
2902
        $postreply = "Status=OK Insufficient privileges\n";
2903
    }
2904
}
2905

    
2906
sub Move {
2907
    my ($uuid, $action, $obj) = @_;
2908
    if ($help) {
2909
        return <<END
2910
GET:uuid,mac:
2911
Moves a server to a different node (Qemu live migration). Server must be running. When called as stormove, non-shared disks are migrated. This may of course take a lot of time, dependeing on the size of the backing images involved.
2912
END
2913
    }
2914
    my $dbstatus = $obj->{status};
2915
    my $dmac = $obj->{mac};
2916
    my $name = $obj->{name};
2917
    my $mem = $obj->{memory};
2918
    my $vcpu = $obj->{vcpu};
2919
    my $image = $obj->{image};
2920
    my $image2 = $obj->{image2};
2921
    my $image3 = $obj->{image3};
2922
    my $image4 = $obj->{image4};
2923

    
2924
    $uistatus = "moving";
2925
    if ($dbstatus eq 'running' && $isadmin) {
2926
        my $hypervisor = getHypervisor($image);
2927
        my $mac = $register{$uuid}->{'mac'};
2928
        $dmac = "" if ($dmac eq "--");
2929
        $mac = "" if ($mac eq "--");
2930

    
2931
        if (( $image =~ /\/mnt\/stabile\/node\//
2932
            || $image2 =~ /\/mnt\/stabile\/node\//
2933
            || $image3 =~ /\/mnt\/stabile\/node\//
2934
            || $image4 =~ /\/mnt\/stabile\/node\// ) && $action ne 'stormove'
2935
        ) {
2936
            $postreply = qq|{"error": 1, "message": "Servers with local storage must be moved with stormove"}|;
2937
            $main::updateUI->({tab=>"servers", user=>$user, message=>"Servers with local storage must be moved with stormove"});
2938
        } else {
2939
            my ($targetmac, $targetname, $targetip, $port) =
2940
                locateTargetNode($uuid, $dmac, $mem, $vcpu, $image, $image2, $image3, $image4, $hypervisor, $mac, 1);
2941
            if ($targetmac) {
2942
                my $tasks = $nodereg{$targetmac}->{'tasks'};
2943
                if ($action eq 'stormove') {
2944
                    $tasks = $tasks . "RECEIVESTOR $uuid $user\n";
2945
                } else {
2946
                    $tasks = $tasks . "RECEIVE $uuid $user\n";
2947
                }
2948
                # Also update allowed port forwards
2949
                $nodereg{$targetmac}->{'tasks'} = $tasks . "PERMITOPEN $user\n";
2950
                $register{$uuid}->{'status'} = "moving";
2951
                $register{$uuid}->{'statustime'} = $current_time;
2952
                $uiuuid = $uuid;
2953
                $uidisplayip = $targetip;
2954
                $uidisplayport = $port;
2955
                $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus to $targetname ($targetmac)");
2956
                $postreply .= "Status=OK $uistatus ". $register{$uuid}->{'name'} . "\n";
2957

    
2958
                # Precreate images on destination node
2959
                if ($action eq 'stormove') {
2960
                    my $preimages = '';
2961
                    $Stabile::Images::user = $user;
2962
                    require "$Stabile::basedir/cgi/images.cgi";
2963
                    $Stabile::Images::console = 1;
2964
                    if ($targetip eq '10.0.0.1') { # Moving from node
2965
                        if ($image =~ /\/mnt\/stabile\/node\//) { # Only move to shared storage if not already on shared storage
2966
                            my $res = Stabile::Images::Move($image, $user, '0', '', 0, 1);
2967
                            $preimages .= " $register{$uuid}->{imagename}";
2968
                        }
2969
                        if ($image2 =~ /\/mnt\/stabile\/node\//) { # Only move to shared storage if not already on shared storage
2970
                            my $res = Stabile::Images::Move($image2, $user, '0', '', 0, 1);
2971
                            $preimages .= " $register{$uuid}->{image2name}";
2972
                        }
2973
                        if ($image3 =~ /\/mnt\/stabile\/node\//) { # Only move to shared storage if not already on shared storage
2974
                            my $res = Stabile::Images::Move($image3, $user, '0', '', 0, 1);
2975
                            $preimages .= " $register{$uuid}->{image3name}";
2976
                        }
2977
                        if ($image4 =~ /\/mnt\/stabile\/node\//) { # Only move to shared storage if not already on shared storage
2978
                            my $res = Stabile::Images::Move($image4, $user, '0', '', 0, 1);
2979
                            $preimages .= " $register{$uuid}->{image4name}";
2980
                        }
2981
                    } else { # Moving to node or between nodes - always move primary image, also if on shared storage
2982
                        my $res = Stabile::Images::Move($image, $user, '-1', $targetmac, 0, 1);
2983
                        $preimages .= " $register{$uuid}->{imagename}";
2984
                        if ($image2 && $image2 ne '--') {
2985
                            # We don't migrate data disks away from shared storage
2986
                            unless ($image2 =~ /\/stabile-images\/images\/.*-data\..*\.qcow2/) {
2987
                                my $res = Stabile::Images::Move($image2, $user, '-1', $targetmac, 0, 1);
2988
                                $preimages .= " $register{$uuid}->{image2name}";
2989
                            }
2990
                        }
2991
                        if ($image3 && $image3 ne '--') {
2992
                            unless ($image3 =~ /\/stabile-images\/images\/.*-data\..*\.qcow2/) {
2993
                                my $res = Stabile::Images::Move($image3, $user, '-1', $targetmac, 0, 1);
2994
                                $preimages .= " $register{$uuid}->{image3name}";
2995
                            }
2996
                        }
2997
                        if ($image4 && $image4 ne '--') {
2998
                            unless ($image4 =~ /\/stabile-images\/images\/.*-data\..*\.qcow2/) {
2999
                                my $res = Stabile::Images::Move($image4, $user, '-1', $targetmac, 0, 1);
3000
                                $preimages .= " $register{$uuid}->{image4name}";
3001
                            }
3002
                        }
3003
                    }
3004
                    if ($preimages) {
3005
                        $main::syslogit->($user, "info", "Precreating images $preimages on node $targetmac");
3006
                        $main::updateUI->({tab=>"servers", user=>$user, message=>"Precreating images $preimages on node $targetmac"});
3007
                    }
3008
                }
3009
                if ($params{'PUTDATA'}) {
3010
                    my %jitem = %{$register{$uuid}};
3011
                    my $json_text = to_json(\%jitem);
3012
                    $json_text =~ s/null/"--"/g;
3013
                    $postreply = $json_text;
3014
                }
3015
#                $main::updateUI->({tab=>"servers", user=>$user, status=>'moving', uuid=>$uuid, type=>'update', message=>"Moving $register{$uuid}->{name} to $targetmac"});
3016
            } else {
3017
                $main::syslogit->($user, "info", "Could not find $hypervisor target for $uistatus $uuid ($image)");
3018
                $main::updateUI->({tab=>"servers", user=>$user, message=>"Could not find target for $uistatus $register{$uuid}->{'name'}"});
3019
                $postreply = qq|{"error": 1, "message": "Could not find target for $uistatus $register{$uuid}->{'name'}"}|;
3020
            }
3021
        }
3022
    } else {
3023
        $main::syslogit->($user, "info", "Problem moving a $dbstatus domain: $uuid");
3024
        my $serv = $register{$uuid};
3025
        $postreply .= qq|{"error": 1, "message": "ERROR problem moving $serv->{'name'} ($dbstatus)"}|;
3026
    }
3027
    return $postreply;
3028
}
3029

    
3030
sub Changepassword {
3031
    my ($uuid, $action, $obj) = @_;
3032
    if ($help) {
3033
        return <<END
3034
POST:uuid,username,password:
3035
Attempts to set password for [username] to [password] using guestfish. If no username is specified, user 'stabile' is assumed.
3036
END
3037
    }
3038
    my $img = $register{$uuid}->{'image'};
3039
    my $username = $obj->{'username'} || 'stabile';
3040
    my $password = $obj->{'password'};
3041
    return "Status=Error Please supply a password\n" unless ($password);
3042
    return "Status=Error Please shut down the server before changing password\n" unless ($register{$uuid} && $register{$uuid}->{'status'} eq 'shutoff');
3043
    return "Status=Error Not allowed\n" unless ($isadmin || $register{$uuid}->{'user'} eq $user);
3044

    
3045
    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;};
3046
    my $cmd = qq/guestfish --rw -a $img -i command "bash -c 'echo $username:$password | chpasswd'" 2>\&1/;
3047
    if ($imagereg{$img} && $imagereg{$img}->{'mac'}) {
3048
        my $mac = $imagereg{$img}->{'mac'};
3049
        my $macip = $nodereg{$mac}->{'ip'};
3050
        $cmd = "$sshcmd $macip $cmd";
3051
    }
3052
    my $res = `$cmd`;
3053
    $res = $1 if ($res =~ /guestfish: (.*)/);
3054
    chomp $res;
3055
    return "Status=OK Ran chpasswd for user $username in server $register{$uuid}->{'name'}: $res\n";
3056
}
3057

    
3058
sub Sshaccess {
3059
    my ($uuid, $action, $obj) = @_;
3060
    if ($help) {
3061
        return <<END
3062
POST:uuid,address:
3063
Attempts to change the ip addresses you can access the server over SSH (port 22) from, by adding [address] to /etc/hosts.allow.
3064
[address] should either be an IP address or a range in CIDR notation. Please note that no validation of [address] is performed.
3065
END
3066
    }
3067
    my $img = $register{$uuid}->{'image'};
3068
    my $address = $obj->{'address'};
3069
    return "Status=Error Please supply an aaddress\n" unless ($address);
3070
    return "Status=Error Please shut down the server before changing SSH access\n" unless ($register{$uuid} && $register{$uuid}->{'status'} eq 'shutoff');
3071
    return "Status=Error Not allowed\n" unless ($isadmin || $register{$uuid}->{'user'} eq $user);
3072

    
3073
    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;};
3074

    
3075
    my $isshcmd = '';
3076
    my $cmd = qq[guestfish --rw -a $img -i command "sed -i -re 's|(sshd: .*)#stabile|\\1 $address #stabile|' /etc/hosts.allow"];
3077
#    my $cmd = qq[guestfish --rw -a $img -i command "bash -c 'echo sshd: $address >> /etc/hosts.allow'"];
3078
    if ($imagereg{$img} && $imagereg{$img}->{'mac'}) {
3079
        my $mac = $imagereg{$img}->{'mac'};
3080
        my $macip = $nodereg{$mac}->{'ip'};
3081
        $isshcmd = "$sshcmd $macip ";
3082
    }
3083
    my $res = `$isshcmd$cmd`;
3084
    chomp $res;
3085
    #$cmd = qq[guestfish --rw -a $img -i command "bash -c 'cat /etc/hosts.allow'"];
3086
    #$res .= `$isshcmd$cmd`;
3087
    #chomp $res;
3088
    return "Status=OK Tried to add sshd: $address to /etc/hosts.allow in server $register{$uuid}->{'name'}\n";
3089
}
3090

    
3091
sub Mountcd {
3092
    my ($uuid, $action, $obj) = @_;
3093
    if ($help) {
3094
        return <<END
3095
GET:uuid,cdrom:
3096
Mounts a cdrom on a server. Server must be running. Mounting the special cdrom named '--' unomunts any currently mounted cdrom.
3097
END
3098
    }
3099
    my $dbstatus = $obj->{status};
3100
    my $mac = $obj->{mac};
3101
    my $cdrom = $obj->{cdrom};
3102
    unless ($cdrom && $dbstatus eq 'running') {
3103
        $main::updateUI->({tab=>"servers", user=>$user, uuid=>$uuid, type=>'update', message=>"Unable to mount cdrom"});
3104
        $postreply = qq|{"Error": 1, "message": "Problem mounting cdrom on $obj->{name}"}|;
3105
        return;
3106
    }
3107
    my $tasks = $nodereg{$mac}->{'tasks'};
3108
    # $user is in the middle here, because $cdrom may contain spaces...
3109
    $nodereg{$mac}->{'tasks'} = $tasks . "MOUNT $uuid $user \"$cdrom\"\n";
3110
    tied(%nodereg)->commit;
3111
    if ($cdrom eq "--") {
3112
        $postreply = qq|{"OK": 1, "message": "OK unmounting cdrom from $obj->{name}"}|;
3113
    } else {
3114
        $postreply = qq|{"OK": 1, "message": "OK mounting cdrom $cdrom on $obj->{name}"}|;
3115
    }
3116
    $register{$uuid}->{'cdrom'} = $cdrom unless ($cdrom eq 'virtio');
3117
    return $postreply;
3118
}
(5-5/9)