Project

General

Profile

Download (120 KB) Statistics
| Branch: | Revision:
1 95b003ff Origo
#!/usr/bin/perl
2
3
# All rights reserved and Copyright (c) 2020 Origo Systems ApS.
4
# This file is provided with no warranty, and is subject to the terms and conditions defined in the license file LICENSE.md.
5
# The license file is part of this source code package and its content is also available at:
6
# https://www.origo.io/info/stabiledocs/licensing/stabile-open-source-license
7
8
package Stabile::Servers;
9
10
use Error qw(:try);
11
use Data::UUID;
12
use Proc::Daemon;
13
use File::Basename;
14
use lib dirname (__FILE__);
15
use File::Basename;
16
use lib dirname (__FILE__);
17
use Stabile;
18
#use Encode::Escape;
19
20
$\ = ''; # Some of the above seems to set this to \n, resulting in every print appending a line feed
21
22
$cpuovercommision = $Stabile::config->get('CPU_OVERCOMMISION') || 1;
23
$dpolicy = $Stabile::config->get('DISTRIBUTION_POLICY') || 'disperse'; #"disperse" or "pack"
24
$amtpasswd = $Stabile::config->get('AMT_PASSWD') || "";
25
$brutalsleep = $Stabile::config->get('BRUTAL_SLEEP') || "";
26
$sshcmd = $sshcmd || $Stabile::sshcmd;
27
28
my %ahash; # A hash of accounts and associated privileges current user has access to
29
30
#my %options=();
31
#Getopt::Std::getopts("a:hfu:m:k:", \%options); # -a action -h help -f full-list (all users) -u uuid -m match pattern -k keywords
32
33
try {
34
    Init(); # Perform various initalization tasks
35
    process() if ($package);
36
37
    if ($action || %params) {
38
    	untie %register;
39
    	untie %networkreg;
40
        untie %nodereg;
41
        untie %xmlreg;
42
    }
43
44
} catch Error with {
45
	my $ex = shift;
46
    print $Stabile::q->header('text/html', '500 Internal Server Error') unless ($console);
47
	if ($ex->{-text}) {
48
        print "Got error: ", $ex->{-text}, " on line ", $ex->{-line}, "\n";
49
	} else {
50
	    print "Status=ERROR\n";
51
	}
52
} finally {
53
};
54
55
1;
56
57
sub getObj {
58
    my %h = %{@_[0]};
59
    $console = 1 if $h{"console"};
60
    $api = 1 if $h{"api"};
61
    my $uuid = $h{"uuid"};
62
    $uuid = $curuuid if ($uuid eq 'this');
63
    my $obj;
64 c899e439 Origo
    $action = $action || $h{'action'};
65
66
    if ($h{'action'} eq 'destroy' || $action eq 'destroy' || $action eq 'attach' || $action eq 'detach' || $action =~ /changepassword|sshaccess/) {
67 95b003ff Origo
        $obj = \%h;
68
        return $obj;
69
    }
70
71
    # Allow specifying nicmac1 instead of uuid if known
72
    if (!$uuid) {
73
        $uuid = nicmac1ToUuid($h{"nicmac1"});
74
    }
75
    my $status = 'new';
76
    $status = $register{$uuid}->{'status'} if ($register{$uuid});
77
78
    my $objaction = lc $h{"action"};
79
    $objaction = "" if ($status eq "new");
80
81
    if ((!$uuid) && $status eq 'new') {
82
        my $ug = new Data::UUID;
83
        $uuid = $ug->create_str();
84
        if ($uripath =~ /servers(\.cgi)?\/(.+)/) {
85
            my $huuid = $2;
86
            if ($ug->to_string($ug->from_string($huuid)) eq $huuid) { # Check for valid uuid
87
                $uuid = $huuid;
88
            }
89
        }
90
    };
91
    unless ($uuid && length $uuid == 36) {
92
        $posterror .= "Status=Error Invalid uuid.\n";
93
        return;
94
    }
95
96
    my $dbobj = $register{$uuid} || {};
97
98
    my $name = $h{"name"} || $dbobj->{'name'};
99
    utf8::decode($name);
100
    my $memory = $h{"memory"} || $dbobj->{'memory'};
101
    my $vcpu = $h{"vcpu"} || $dbobj->{'vcpu'};
102
    my $boot = $h{"boot"} || $dbobj->{'boot'};
103 04c16f26 hq
    my $loader = $h{"loader"} || $dbobj->{'loader'};
104 95b003ff Origo
    my $image = $h{"image"} || $dbobj->{'image'};
105
    my $imagename = $h{"imagename"} || $dbobj->{'imagename'};
106
    if ($image && $image ne '--' && !($image =~ /^\//)) { # Image is registered by uuid - we find the path
107
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {$posterror = "Unable to access image uuid register"; return;};
108
        $image = $imagereg2{$image}->{'path'};
109
        $imagename = $imagereg2{$image}->{'name'};
110
        untie %imagereg2;
111
        return unless ($image);
112
    }
113
    my $image2 = $h{"image2"} || $dbobj->{'image2'};
114
    my $image3 = $h{"image3"} || $dbobj->{'image3'};
115
    my $image4 = $h{"image4"} || $dbobj->{'image4'};
116
    my $image2name = $h{"image2name"} || $dbobj->{'image2name'};
117
    my $image3name = $h{"image3name"} || $dbobj->{'image3name'};
118
    my $image4name = $h{"image4name"} || $dbobj->{'image4name'};
119
    if ($image2 && $image2 ne '--' && !($image2 =~ /^\//)) { # Image2 is registered by uuid - we find the path
120
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {$postreply = "Unable to access image uuid register"; return $postreply;};
121
        $image2 = $imagereg2{$image2}->{'path'};
122
        $image2name = $imagereg2{$image2}->{'name'};
123
        untie %imagereg2;
124
    }
125
    my $diskbus = $h{"diskbus"} || $dbobj->{'diskbus'};
126
    my $diskdev = "vda";
127
    my $diskdev2 = "vdb";
128
    my $diskdev3 = "vdc";
129
    my $diskdev4 = "vdd";
130
    if ($diskbus eq "ide") {$diskdev = "hda"; $diskdev2 = "hdb"; $diskdev3 = "hdc"; $diskdev4 = "hdd"};
131
    my $cdrom = $h{"cdrom"} || $dbobj->{'cdrom'};
132 04c16f26 hq
    if ($cdrom && $cdrom ne '--' && !($cdrom =~ /^\//) && $cdrom ne 'virtio') {
133 95b003ff Origo
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {$postreply = "Unable to access image uuid register"; return $postreply;};
134
        $cdrom = $imagereg2{$cdrom}->{'path'};
135
        untie %imagereg2;
136
    }
137
138
    my $networkuuid1 = $h{"networkuuid1"} || $dbobj->{'networkuuid1'};
139
    if ($h{"networkuuid1"} eq "0") {$networkuuid1 = "0"}; #Stupid perl... :-)
140
    my $networkid1 = $h{"networkid1"} || $dbobj->{'networkid1'};
141
    my $networkname1 = $h{"networkname1"} || $dbobj->{'networkname1'};
142
    my $nicmodel1 = $h{"nicmodel1"} || $dbobj->{'nicmodel1'};
143
    my $nicmac1 = $h{"nicmac1"} || $dbobj->{'nicmac1'};
144
    if (!$nicmac1 || $nicmac1 eq "--") {$nicmac1 = randomMac();}
145
146
    my $networkuuid2 = $h{"networkuuid2"} || $dbobj->{'networkuuid2'};
147
    if ($h{"networkuuid2"} eq "0") {$networkuuid2 = "0"};
148
    my $networkid2 = $h{"networkid2"} || $dbobj->{'networkid2'};
149
    my $networkname2 = $h{"networkname2"} || $dbobj->{'networkname2'};
150
    my $nicmac2 = $h{"nicmac2"} || $dbobj->{'nicmac2'};
151
    if (!$nicmac2 || $nicmac2 eq "--") {$nicmac2 = randomMac();}
152
153
    my $networkuuid3 = $h{"networkuuid3"} || $dbobj->{'networkuuid3'};
154
    if ($h{"networkuuid3"} eq "0") {$networkuuid3 = "0"};
155
    my $networkid3 = $h{"networkid3"} || $dbobj->{'networkid3'};
156
    my $networkname3 = $h{"networkname3"} || $dbobj->{'networkname3'};
157
    my $nicmac3 = $h{"nicmac3"} || $dbobj->{'nicmac3'};
158
    if (!$nicmac3 || $nicmac3 eq "--") {$nicmac3 = randomMac();}
159
160
    my $action = $h{"action"};
161
    my $notes = $h{"notes"};
162
    $notes = $dbobj->{'notes'} if (!$notes || $notes eq '--');
163
    my $reguser = $dbobj->{'user'};
164
    my $autostart = ($h{"autostart"} ."") || $dbobj->{'autostart'};
165
    if ($autostart && $autostart ne "false") {$autostart = "true";}
166
    my $locktonode = ($h{"locktonode"} ."") || $dbobj->{'locktonode'};
167
    if ($locktonode && $locktonode ne "false") {$locktonode = "true";}
168
    my $mac;
169
    $mac = $dbobj->{'mac'} unless ($objaction eq 'start');
170
#    $mac = $h{"mac"} if ($isadmin && $locktonode eq 'true' && $h{"mac"});
171
    $mac = $h{"mac"} if ($isadmin && $h{"mac"});
172
    my $domuser = $h{"user"} || $user; # Set if user is trying to move server to another account
173
174
    # Sanity checks
175
    if (
176
        ($name && length $name > 255)
177
            || ($networkuuid1<0)
178
            || ($networkuuid2<0)
179
            || ($networkuuid3<0)
180
            || ($networkuuid1>1 && length $networkuuid1 != 36)
181
            || ($networkuuid2>1 && length $networkuuid2 != 36)
182
            || ($networkuuid3>1 && length $networkuuid3 != 36)
183
            || ($image && length $image > 255)
184
            || ($imagename && length $imagename > 255)
185
            || ($image2 && length $image2 > 255)
186
            || ($image3 && length $image3 > 255)
187
            || ($image4 && length $image4 > 255)
188
            || ($image2name && length $image2name > 255)
189
            || ($image3name && length $image3name > 255)
190
            || ($image4name && length $image4name > 255)
191
            || ($cdrom && length $cdrom > 255)
192 a439a9c4 hq
            || ($memory && ($memory<64 || $memory >1024*64))
193 95b003ff Origo
    ) {
194 a439a9c4 hq
        $postreply .= "Status=ERROR Invalid server data: $name\n";
195 95b003ff Origo
        return 0;
196
    }
197
198
    # Security check
199 2a63870a Christian Orellana
    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 95b003ff Origo
        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 91a21c75 hq
        if ( ($reguser && ($user ne $reguser) && $action ) || ($reguser && $status eq "new"))
219 95b003ff Origo
        {
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 04c16f26 hq
    $cdrom = '--' if ($cdrom eq 'virtio' && $action ne 'mountcd');
236 95b003ff Origo
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 04c16f26 hq
        loader=> $loader,
255 95b003ff Origo
        networkuuid1 => $networkuuid1,
256
        networkid1 => $networkid1,
257
        networkname1 => $networkname1,
258
        nicmodel1 => $nicmodel1,
259
        nicmac1 => $nicmac1,
260
        networkuuid2 => $networkuuid2,
261
        networkid2 => $networkid2,
262
        networkname2 => $networkname2,
263
        nicmac2 => $nicmac2,
264
        networkuuid3 => $networkuuid3,
265
        networkid3 => $networkid3,
266
        networkname3 => $networkname3,
267
        nicmac3 => $nicmac3,
268
        notes => $notes,
269
        autostart => $autostart,
270
        locktonode => $locktonode,
271
        mac => $mac,
272
        user => $domuser
273
    };
274
    return $obj;
275
}
276
277
sub Init {
278
    # Tie database tables to hashes
279
    unless ( tie(%register,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access image register"};
280
    unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {return "Unable to access network register"};
281
    unless ( tie(%nodereg,'Tie::DBI', Hash::Merge::merge({table=>'nodes', key=>'mac'}, $Stabile::dbopts)) ) {return "Unable to access nodes register"};
282
    unless ( tie(%xmlreg,'Tie::DBI', Hash::Merge::merge({table=>'domainxml'}, $Stabile::dbopts)) ) {return "Unable to access domainxml register"};
283
284
    # simplify globals initialized in Stabile.pm
285
    $tktuser = $tktuser || $Stabile::tktuser;
286
    $user = $user || $Stabile::user;
287
    $isadmin = $isadmin || $Stabile::isadmin;
288
    $privileges = $privileges || $Stabile::privileges;
289
290
    # Create aliases of functions
291
    *header = \&CGI::header;
292
    *to_json = \&JSON::to_json;
293
294
    *Showautostart = \&Autostartall;
295
296
    *do_save = \&Save;
297
    *do_tablelist = \&do_list;
298
    *do_jsonlist = \&do_list;
299
    *do_showautostart = \&action;
300
    *do_autostartall = \&privileged_action;
301
    *do_help = \&action;
302
303
    *do_start = \&privileged_action;
304
    *do_destroy = \&action;
305
    *do_shutdown = \&action;
306
    *do_suspend = \&action;
307
    *do_resume = \&action;
308
    *do_remove = \&privileged_action;
309
    *do_move = \&action;
310
    *do_mountcd = \&action;
311 c899e439 Origo
    *do_changepassword = \&privileged_action;
312
    *do_sshaccess = \&privileged_action;
313 95b003ff Origo
314
    *do_gear_start = \&do_gear_action;
315
    *do_gear_autostart = \&do_gear_action;
316
    *do_gear_showautostart = \&do_gear_action;
317
    *do_gear_autostartall = \&do_gear_action;
318
    *do_gear_remove = \&do_gear_action;
319 c899e439 Origo
    *do_gear_changepassword = \&do_gear_action;
320
    *do_gear_sshaccess = \&do_gear_action;
321 95b003ff Origo
322
}
323
324
sub do_list {
325
    my ($uuid, $action) = @_;
326
    if ($help) {
327
        return <<END
328
GET:uuid:
329
List servers current user has access to.
330
END
331
    }
332
333
    my $res;
334
    my $filter;
335
    my $statusfilter;
336
    my $uuidfilter;
337
    my $curserv = $register{$curuuid};
338
    if ($curuuid && ($isadmin || $curserv->{'user'} eq $user) && $uripath =~ /servers(\.cgi)?\/(\?|)(this)/) {
339
        $uuidfilter = $curuuid;
340
    } elsif ($uripath =~ /servers(\.cgi)?\/(\?|)(name|status)/) {
341
        $filter = $3 if ($uripath =~ /servers(\.cgi)?\/\??name(:|=)(.+)/);
342
        $filter = $1 if ($filter =~ /(.*)\*$/);
343
        $statusfilter = $4 if ($uripath =~ /servers(\.cgi)?\/\??(.+ AND )?status(:|=)(\w+)/);
344
    } elsif ($uripath =~ /servers(\.cgi)?\/(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})/) {
345
        $uuidfilter = $2;
346
    }
347
    $filter = $1 if ($filter =~ /(.*)\*/);
348
349
    my $sysuuid;
350
    if ($params{'system'}) {
351
        $sysuuid = $params{'system'};
352
        $sysuuid = $cursysuuid || $curuuid if ($params{'system'} eq 'this');
353
    }
354
    my @curregvalues;
355
    my @regkeys;
356
    if ($fulllist && $isadmin) {
357
        @regkeys = keys %register;
358
    } elsif ($uuidfilter && $isadmin) {
359
        @regkeys = (tied %register)->select_where("uuid = '$uuidfilter'");
360
    } elsif ($sysuuid) {
361
        @regkeys = (tied %register)->select_where("system = '$sysuuid' OR uuid = '$sysuuid'");
362
    } else {
363
        @regkeys = (tied %register)->select_where("user = '$user'");
364
    }
365
366
    unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
367
    unless (tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access images register"}|; return $res;};
368
369
    foreach my $k (@regkeys) {
370
        $valref = $register{$k};
371
        # Only include VM's belonging to current user (or all users if specified and user is admin)
372
        if ($user eq $valref->{'user'} || $fulllist || ($uuidfilter && $isadmin)) {
373
            next unless (!$sysuuid || $valref->{'system'} eq $sysuuid || $valref->{'uuid'} eq $sysuuid);
374
375
            my $validatedref = validateItem($valref);
376
            my %val = %{$validatedref}; # Deference and assign to new ass array, effectively cloning object
377
            $val{'memory'} += 0;
378
            $val{'vcpu'} += 0;
379
            $val{'nodetype'} = 'parent';
380
            $val{'internalip'} = $networkreg{$val{'networkuuid1'}}->{'internalip'};
381
            $val{'self'} = 1 if ($curuuid && $curuuid eq $val{'uuid'});
382
            if ($action eq 'treelist') {
383
                if ($val{'system'} && $val{'system'} ne '') {
384
                    my $sysuuid = $val{'system'};
385
                    my $sysname = $sysreg{$sysuuid}->{'name'};
386
                    if (!$sysname) {
387
                        $sysname = $1 if ($sysname =~ /(.+)\..*/);
388
                        $sysname = $val{'name'};
389
                        $sysname =~ s/server/System/i;
390
                    }
391
                    $sysreg{$sysuuid} = {
392
                        uuid => $sysuuid,
393
                        name => $sysname,
394
                        user => 'irigo'
395
                    };
396
397
                    my %pval = %{$sysreg{$sysuuid}};
398
                    $pval{'nodetype'} = 'parent';
399
                    $pval{'status'} = '--';
400
                    $val{'nodetype'} = 'child';
401
402
                    my @children;
403
                    push @children,\%val;
404
                    $pval{'children'} = \@children;
405
                    push @curregvalues,\%pval;
406
                } else {
407
                    push @curregvalues,\%val;
408
                }
409
            } elsif ($filter || $statusfilter || $uuidfilter) { # List filtered servers
410
                my $fmatch;
411
                my $smatch;
412
                my $umatch;
413
                $fmatch = 1 if (!$filter || $val{'name'}=~/$filter/i);
414
                $smatch = 1 if (!$statusfilter || $statusfilter eq 'all'
415
                    || $statusfilter eq $val{'status'}
416
                );
417
                $umatch = 1 if ($val{'uuid'} eq $uuidfilter);
418
                if ($fmatch && $smatch && !$uuidfilter) {
419
                    push @curregvalues,\%val;
420
                } elsif ($umatch) {
421
                    push @curregvalues,\%val;
422
                    last;
423
                }
424
            } else {
425
                push @curregvalues,\%val;
426
            }
427
        }
428
    }
429
    tied(%sysreg)->commit;
430
    untie(%sysreg);
431
    untie %imagereg;
432
    @curregvalues = (sort {$a->{'status'} cmp $b->{'status'}} @curregvalues); # Sort by status
433
434
    # Sort @curregvalues
435 2a63870a Christian Orellana
    @curregvalues = (sort {$b->{'name'} <=> $a->{'name'}} @curregvalues); # Always sort by name first
436 95b003ff Origo
    my $sort = 'status';
437
    $sort = $2 if ($uripath =~ /sort\((\+|\-)(\S+)\)/);
438
    my $reverse;
439
    $reverse = 1 if ($1 eq '-');
440
    if ($reverse) { # sort reverse
441
        if ($sort =~ /memory|vcpu/) {
442
            @curregvalues = (sort {$b->{$sort} <=> $a->{$sort}} @curregvalues); # Sort as number
443
        } else {
444
            @curregvalues = (sort {$b->{$sort} cmp $a->{$sort}} @curregvalues); # Sort as string
445
        }
446
    } else {
447
        if ($sort =~ /memory|vcpu/) {
448
            @curregvalues = (sort {$a->{$sort} <=> $b->{$sort}} @curregvalues); # Sort as number
449
        } else {
450
            @curregvalues = (sort {$a->{$sort} cmp $b->{$sort}} @curregvalues); # Sort as string
451
        }
452
    }
453
454
    if ($action eq 'tablelist') {
455
        my $t2;
456
457
        if ($isadmin) {
458
            $t2 = Text::SimpleTable->new(36,20,20,10,10,12,7);
459
            $t2->row('uuid', 'name', 'imagename', 'memory', 'user', 'mac', 'status');
460
        } else {
461
            $t2 = Text::SimpleTable->new(36,20,20,10,10,7);
462
            $t2->row('uuid', 'name', 'imagename', 'memory', 'user', 'status');
463
        }
464
        $t2->hr;
465
        my $pattern = $options{m};
466
        foreach $rowref (@curregvalues){
467
            if ($pattern) {
468
                my $rowtext = $rowref->{'uuid'} . " " . $rowref->{'name'} . " " . $rowref->{'imagename'} . " " . $rowref->{'memory'}
469
                    . " " .  $rowref->{'user'} . " " . $rowref->{'status'};
470
                $rowtext .= " " . $rowref->{'mac'} if ($isadmin);
471
                next unless ($rowtext =~ /$pattern/i);
472
            }
473
            if ($isadmin) {
474
                $t2->row($rowref->{'uuid'}, $rowref->{'name'}, $rowref->{'imagename'}, $rowref->{'memory'},
475
                    $rowref->{'user'}, $rowref->{'mac'}, $rowref->{'status'});
476
            } else {
477
                $t2->row($rowref->{'uuid'}, $rowref->{'name'}, $rowref->{'imagename'}, $rowref->{'memory'},
478
                    $rowref->{'user'}, $rowref->{'status'});
479
            }
480
        }
481
        $res .= $t2->draw;
482
    } elsif ($console) {
483
        $res .= Dumper(\@curregvalues);
484
    } else {
485
        my $json_text;
486
        if ($uuidfilter && @curregvalues) {
487
            $json_text = to_json($curregvalues[0], {pretty => 1});
488
        } else {
489
            $json_text = to_json(\@curregvalues, {pretty => 1});
490
        }
491
492
        $json_text =~ s/\x/ /g;
493
        $json_text =~ s/\"\"/"--"/g;
494 c899e439 Origo
        $json_text =~ s/null/"--"/g;
495 04c16f26 hq
        $json_text =~ s/"autostart"\s?:\s?"true"/"autostart": true/g;
496
        $json_text =~ s/"autostart"\s?:\s?"--"/"autostart": false/g;
497
        $json_text =~ s/"locktonode"\s?:\s?"true"/"locktonode": true/g;
498
        $json_text =~ s/"locktonode"\s?:\s?"--"/"locktonode": false/g;
499
        $json_text =~ s/"loader"\s?:\s?"--"/"loader": "bios"/g;
500 95b003ff Origo
        if ($action eq 'jsonlist' || $action eq 'list' || !$action) {
501
            $res .= $json_text;
502
        } else {
503
            $res .= qq|{"action": "$action", "identifier": "uuid", "label": "uuid", "items" : $json_text}|;
504
        }
505
    }
506
    return $res;
507
}
508
509
sub do_uuidshow {
510
    my ($uuid, $action) = @_;
511
    if ($help) {
512
        return <<END
513
GET:uuid:
514
Simple action for showing a single server.
515
END
516
    }
517
    my $res;
518
    $res .= $Stabile::q->header('text/plain') unless $console;
519
    my $u = $uuid || $options{u};
520
    if ($u || $u eq '0') {
521
        foreach my $uuid (keys %register) {
522
            if (($register{$uuid}->{'user'} eq $user || $register{$uuid}->{'user'} eq 'common' || $isadmin)
523
                && $uuid =~ /^$u/) {
524
                my %hash = %{$register{$uuid}};
525
                delete $hash{'action'};
526
                my $dump = Dumper(\%hash);
527
                $dump =~ s/undef/"--"/g;
528
                $res .= $dump;
529
                last;
530
            }
531
        }
532
    }
533
    return $res;
534
}
535
536
sub do_uuidlookup {
537
    if ($help) {
538
        return <<END
539
GET:uuid:
540
Simple action for looking up a uuid or part of a uuid and returning the complete uuid.
541
END
542
    }
543
    my $res;
544
    $res .= header('text/plain') unless $console;
545
    my $u = $options{u};
546
    $u = $curuuid unless ($u || $u eq '0');
547
    my $ruuid;
548
    if ($u || $u eq '0') {
549
        my $match;
550
        foreach my $uuid (keys %register) {
551
            if ($uuid =~ /^$u/) {
552
                $ruuid = $uuid if ($register{$uuid}->{'user'} eq $user || index($privileges,"a")!=-1);
553
                $match = 1;
554
                last;
555
            }
556
        }
557
        if (!$match && $isadmin) { # If no match and user is admin, do comprehensive lookup
558
            foreach my $uuid (keys %register) {
559
                if ($uuid =~ /^$u/ || $register{$uuid}->{'name'} =~ /^$u/) {
560
                    $ruuid = $uuid;
561
                    last;
562
                }
563
            }
564
        }
565
    }
566
    $res .= "$ruuid\n" if ($ruuid);
567
    return $res;
568
}
569
570
sub do_destroyuserservers {
571
    if ($help) {
572
        return <<END
573
GET::
574
Simple action for destroying all servers belonging to a user
575
END
576
    }
577
    my $res;
578
    $res .= $Stabile::q->header('text/plain') unless $console;
579
    destroyUserServers($user);
580
    $res .= $postreply;
581
    return $res;
582
}
583
584
sub do_removeuserservers {
585
    if ($help) {
586
        return <<END
587
GET::
588
Simple action for removing all servers belonging to a user
589
END
590
    }
591
    my $res;
592
    $res .= $Stabile::q->header('text/plain') unless $console;
593
    removeUserServers($user);
594
    $res .= $postreply;
595
    return $res;
596
}
597
598
sub do_getappid {
599
    my ($uuid, $action) = @_;
600
    if ($help) {
601
        return <<END
602
GET:uuid:
603
Simple action for getting the app id
604
END
605
    }
606
    my $res;
607
    $res .= $Stabile::q->header('text/plain') unless $console;
608
    $uuid = $uuid || $options{u};
609
    $uuid = $curuuid unless ($uuid);
610
    if ($uuid && $register{$uuid}) {
611
        unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access image register"};
612
        $res .= "appid: ". $imagereg{$register{$uuid}->{image}}->{appid}, "\n";
613
        untie %imagereg;
614
    }
615
    return $res;
616
}
617
618
sub do_setrunning {
619
    my ($uuid, $action) = @_;
620
    if ($help) {
621
        return <<END
622
GET:uuid:
623
Simple action for setting status back to running after e.g. an upgrade
624
END
625
    }
626
    my $res;
627
    $res .= $Stabile::q->header('text/plain') unless $console;
628
    $uuid = $uuid || $options{u};
629
    $uuid = $curuuid unless ($uuid);
630
    if ($uuid && $register{$uuid}) {
631
        $register{$uuid}->{'status'} = 'running';
632
        $main::updateUI->({ tab => 'servers',
633
            user                => $user,
634
            uuid                => $uuid,
635
            status              => 'running' })
636
637
    };
638
    $res .= "Status=OK Set status of $register{$uuid}->{'name'} to running\n";
639
    return $res;
640
}
641
642
sub do_getappinfo {
643
    my ($uuid, $action) = @_;
644
    if ($help) {
645
        return <<END
646
GET:uuid:
647
Simple action for getting the apps basic info
648
END
649
    }
650
    my $res;
651
    $res .= $Stabile::q->header('application/json') unless $console;
652
    $uuid = $uuid || $options{u};
653
    $uuid = $curuuid unless ($uuid);
654
    my %appinfo;
655
    if ($uuid && $register{$uuid}) {
656
        unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access image register"};
657
        $appinfo{'appid'} = $imagereg{$register{$uuid}->{image}}->{appid} || '';
658
        $appinfo{'managementlink'} = $imagereg{$register{$uuid}->{image}}->{managementlink} || '';
659
        $appinfo{'managementlink'} =~ s/{uuid}/$register{$uuid}->{networkuuid1}/;
660
661
        my $termlink = $imagereg{$register{$uuid}->{image}}->{terminallink} || '';
662
        $termlink =~ s/{uuid}/$register{$uuid}->{networkuuid1}/;
663
        my $burl = $baseurl;
664
        $burl = $1 if ($termlink =~ /\/stabile/ && $baseurl =~ /(.+)\/stabile/); # Unpretty, but works for now
665 6fdc8676 hq
        # $termlink = $1 if ($termlink =~ /\/(.+)/);
666
        # $termlink = "$burl/$termlink" unless ($termlink =~ /^http/ || !$termlink); # || $termlink =~ /^\//
667 95b003ff Origo
        $appinfo{'terminallink'} = $termlink;
668
669
        $appinfo{'upgradelink'} = $imagereg{$register{$uuid}->{image}}->{upgradelink} || '';
670
        $appinfo{'upgradelink'} =~ s/{uuid}/$register{$uuid}->{networkuuid1}/;
671
        $appinfo{'version'} = $imagereg{$register{$uuid}->{image}}->{version} || '';
672
        $appinfo{'status'} = $register{$uuid}->{status} || '';
673
        $appinfo{'name'} = $register{$uuid}->{name} || '';
674 d3d1a2d4 Origo
        $appinfo{'system'} = $register{$uuid}->{system} || '';
675
676
        if ($appinfo{'system'}) {
677
            unless (tie(%sysreg,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access systems register"}|; return $res;};
678
            $appinfo{'systemname'} = $sysreg{$appinfo{'system'}}->{name} || '';
679
            untie(%sysreg);
680
        } else {
681
            $appinfo{'systemname'} = $appinfo{'name'};
682
        }
683
684 95b003ff Origo
685
        if ($appinfo{'appid'}) {
686
            my @regkeys = (tied %imagereg)->select_where("appid = '$appinfo{appid}'");
687
            foreach my $k (@regkeys) {
688
                my $imgref = $imagereg{$k};
689
                if ($imgref->{'path'} =~ /\.master\.qcow2$/ && $imgref->{'appid'} eq $appinfo{'appid'}
690
                     && $imgref->{'installable'} && $imgref->{'installable'} ne 'false'
691
                ) {
692
                    if ($imgref->{'version'} > $appinfo{'currentversion'}) {
693
                        $appinfo{'currentversion'} = $imgref->{'version'};
694
                        $appinfo{'appname'} = $imgref->{'name'};
695
                    }
696
                }
697
            }
698
        }
699
700
        untie %imagereg;
701
    }
702
    $appinfo{'appstoreurl'} = $appstoreurl;
703
    $appinfo{'dnsdomain'} = ($enginelinked)?$dnsdomain:'';
704 6fdc8676 hq
    $appinfo{'dnssubdomain'} = ($enginelinked)?substr($engineid, 0, 8):'';
705 95b003ff Origo
    $appinfo{'uuid'} = $uuid;
706
    $appinfo{'user'} = $user;
707
    $appinfo{'remoteip'} = $remoteip;
708
    $res .= to_json(\%appinfo, { pretty => 1 });
709
    return $res;
710
}
711
712
sub do_removeserver {
713
    if ($help) {
714
        return <<END
715
GET:uuid:
716
Simple action for destroying and removing a single server
717
END
718
    }
719
    my $res;
720
    $res .= $Stabile::q->header('text/plain') unless $console;
721
    if ($curuuid) {
722
        removeUserServers($user, $curuuid, 1);
723
    }
724
    else {
725
        $postreply .= "Status=Error Unable to uninstall\n";
726
    }
727
    $res .= $postreply;
728
    return $res;
729
}
730
731
sub do_updateregister {
732
    if ($help) {
733
        return <<END
734
GET::
735
Update server register
736
END
737
    }
738
    my $res;
739
    $res .= $Stabile::q->header('text/plain') unless $console;
740
    return unless $isadmin;
741
    updateRegister();
742
    $res .= "Status=OK Updated server registry for all users\n";
743
    return $res;
744
}
745
746
sub Autostartall {
747
    my ($uuid, $action) = @_;
748
    if ($help) {
749
        return <<END
750
GET::
751
Start all servers marked for autostart. When called as showautostart only shows which would be started.
752
END
753
    }
754
    my $res;
755
    $res .= $Stabile::q->header('text/plain') unless $console;
756
    my $mes;
757
    return $res if ($isreadonly);
758
759
    # Wait for all pistons to be online
760
    my $nodedown;
761
    my $nodecount;
762
    for (my $i = 0; $i < 10; $i++) {
763
        $nodedown = 0;
764
        foreach my $node (values %nodereg) {
765
            if ($node->{'status'} ne 'running' && $node->{'status'} ne 'maintenance') {
766
                $nodedown = 1;
767
            }
768
            else {
769
                $nodecount++ unless ($node->{'status'} eq 'maintenance');
770
            }
771
        }
772
        if ($nodedown) {
773
            # Wait and see if nodes come online
774
            $mes = "Waiting for nodes...(" . (10 - $i) . ")\n";
775
            print $mes if ($console);
776
            $res .= $mes;
777
            sleep 5;
778
        }
779
        else {
780
            last;
781
        }
782
    }
783
784
    if (!%nodereg || $nodedown || !$nodecount) {
785
        $mes = "Not autostarting servers - not all nodes ready!\n";
786
        print $mes if ($console);
787
        $res .= $mes;
788
    }
789
    else {
790
        $mes = "$nodecount nodes ready - autostarting servers...\n";
791
        print $mes if ($console);
792
        $res .= $mes;
793
        if ($action eq "showautostart") {
794
            $mes = "Only showing which servers would be starting!\n";
795
            print $mes if ($console);
796
            $res .= $mes;
797
        }
798
799
        $Stabile::Networks::user = $user;
800
        require "$Stabile::basedir/cgi/networks.cgi";
801
        $Stabile::Networks::console = 1;
802
803
        foreach my $dom (values %register) {
804 2a63870a Christian Orellana
            if ($dom->{'autostart'} eq '1' || $dom->{'autostart'} eq 'true') {
805
                $res .= "Checking if $dom->{'name'} ($dom->{'user'}, $dom->{'uuid'}) should be started\n";
806 95b003ff Origo
                my $networkstatus1 = $networkreg{$dom->{'networkuuid1'}}->{status};
807
                my $networkstatus2 = ($networkreg{$dom->{'networkuuid2'}})?$networkreg{$dom->{'networkuuid2'}}->{status}:'';
808
                my $networkstatus3 = ($networkreg{$dom->{'networkuuid3'}})?$networkreg{$dom->{'networkuuid3'}}->{status}:'';
809
                my @dnets;
810
                push @dnets, $dom->{'networkuuid1'} if ($dom->{'networkuuid1'} && $dom->{'networkuuid1'} ne '--' && $networkstatus1 ne 'up');
811
                push @dnets, $dom->{'networkuuid2'} if ($dom->{'networkuuid2'} && $dom->{'networkuuid2'} ne '--' && $networkstatus2 ne 'up');
812
                push @dnets, $dom->{'networkuuid3'} if ($dom->{'networkuuid3'} && $dom->{'networkuuid3'} ne '--' && $networkstatus3 ne 'up');
813
                my $i;
814
                for ($i=0; $i<5; $i++) { # wait for status newer than 10 secs
815
                    validateItem($dom);
816
                    last if (time() - $dom->{timestamp} < 10);
817 2a63870a Christian Orellana
                    $mes = "Waiting for newer timestamp, current is " . (time() - $dom->{timestamp}) . " old\n";
818 95b003ff Origo
                    print $mes if ($console);
819
                    $res .= $mes;
820
                    sleep 2;
821
                }
822
                if (
823
                    $dom->{'status'} eq 'shutoff' || $dom->{'status'} eq 'inactive'
824
                ) {
825
                    if ($action eq "showautostart") { # Dry run
826
                        $mes = "Starting $dom->{'name'} ($dom->{'user'}, $dom->{'uuid'})\n";
827
                        print $mes if ($console);
828
                        $res .= $mes;
829
                    }
830
                    else {
831
                        $mes = "Starting $dom->{'name'} ($dom->{'user'}, $dom->{'uuid'})\n";
832
                        print $mes if ($console);
833
                        $res .= $mes;
834
                        $postreply = Start($dom->{'uuid'});
835
                        print $postreply if ($console);
836
                        $res .= $postreply;
837 48fcda6b Origo
#                        $mes = `REMOTE_USER=$dom->{'user'} $base/cgi/servers.cgi -a start -u $dom->{'uuid'}`;
838
                        print $mes if ($console);
839
                        $res .= $mes;
840 95b003ff Origo
                        sleep 1;
841
                    }
842
                }
843
                elsif (@dnets) {
844
                    if ($action eq "showautostart") { # Dry run
845
                        foreach my $networkuuid (@dnets) {
846
                            $mes = "Would bring network $networkreg{$networkuuid}->{name} up for $dom->{'name'} ($dom->{'user'}, $dom->{'uuid'})\n";
847
                            print $mes if ($console);
848
                            $res .= $mes;
849
                        }
850
                    }
851
                    else {
852
                        foreach my $networkuuid (@dnets) {
853
                            $mes = "Bringing network $networkreg{$networkuuid}->{name} up for $dom->{'name'} ($dom->{'user'}, $dom->{'uuid'})\n";
854
                            print $mes if ($console);
855
                            $res .= $mes;
856
                            $mes = Stabile::Networks::Activate($networkuuid, 'activate');
857
                            print $mes if ($console);
858
                            $res .= $mes;
859
                            sleep 1;
860
                        }
861
                    }
862
                }
863 2a63870a Christian Orellana
            } else {
864
                $res .= "Not marked for autostart $dom->{'name'} ($dom->{'user'}, $dom->{'uuid'})\n";
865 95b003ff Origo
            }
866
        }
867
    }
868
    return $res;
869
}
870
871
sub do_listnodeavailability {
872
    if ($help) {
873
        return <<END
874
GET::
875
Utility call - only informational. Shows availability of nodes for starting servers.
876
END
877
    }
878
    my $res;
879
    $res .= $Stabile::q->header('application/json') unless ($console);
880
    my ($temp1, $temp2, $temp3, $temp4, $ahashref) = locateTargetNode();
881
    my @avalues = values %$ahashref;
882
    my @sorted_values = (sort {$b->{'index'} <=> $a->{'index'}} @avalues);
883
    $res .= to_json(\@sorted_values, { pretty => 1 });
884
    return $res;
885
}
886
887
sub do_listbillingdata {
888
    if ($help) {
889
        return <<END
890
GET::
891
List current billing data.
892
END
893
    }
894
    my $res;
895
    $res .= $Stabile::q->header('application/json') unless ($console);
896
    my $buser = URI::Escape::uri_unescape($params{'user'}) || $user;
897
    my %b;
898
    my @bmonths;
899
    if ($isadmin || $buser eq $user) {
900
        my $bmonth = URI::Escape::uri_unescape($params{'month'}) || $month;
901
        my $byear = URI::Escape::uri_unescape($params{'year'}) || $year;
902
        if ($bmonth eq "all") {
903
            @bmonths = ("01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12");
904
        }
905
        else {
906
            @bmonths = ($bmonth);
907
        }
908
909
        unless ( tie(%billingreg,'Tie::DBI', Hash::Merge::merge({table=>'billing_domains', key=>'usernodetime'}, $Stabile::dbopts)) ) {return "Unable to access billing register"};
910
911
        my @nkeys = keys %nodereg;
912
        foreach my $bm (@bmonths) {
913
            my $vcpuavg = 0;
914
            my $memoryavg = 0;
915
            foreach my $nmac (@nkeys) {
916
                $vcpuavg += $billingreg{"$buser-$nmac-$byear-$bm"}->{'vcpuavg'};
917
                $memoryavg += $billingreg{"$buser-$nmac-$byear-$bm"}->{'memoryavg'};
918
            }
919
            $b{"$buser-$byear-$bm"} = {
920
                id        => "$buser-$byear-$bm",
921
                vcpuavg   => $vcpuavg,
922
                memoryavg => $memoryavg,
923
                month     => $bm + 0,
924
                year      => $byear + 0
925
            }
926
        }
927
        untie %billingreg;
928
    }
929
    my @bvalues = values %b;
930
    $res .= "{\"identifier\": \"id\", \"label\": \"id\", \"items\":" . to_json(\@bvalues) . "}";
931
    return $res;
932
}
933
934
# Print list of available actions on objects
935
sub do_plainhelp {
936
    my $res;
937
    $res .= $Stabile::q->header('text/plain') unless $console;
938
    $res .= <<END
939
new [name="name"]
940
* start: Starts a server
941
* destroy: Destroys a server, i.e. terminates the VM, equivalent of turning the power off a physical computer
942
* shutdown: Asks the operating system of a server to shut down via ACPI
943
* suspend: Suspends the VM, effectively putting the server to sleep
944
* resume: Resumes a suspended VM, effectively waking the server from sleep
945
* move [mac="mac"]: Moves a server to specified node. If no node is specified, moves to other node with highest availability
946
index
947
* delete: Deletes a server. Image and network are not deleted, only information about the server. Server cannot be
948
runing
949
* mountcd [cdrom="path"]: Mounts a cd rom
950
END
951
    ;
952
    return $res;
953
}
954
955
# Helper function
956
sub recurse($) {
957
	my($path) = @_;
958
	my @files;
959
	## append a trailing / if it's not there
960
	$path .= '/' if($path !~ /\/$/);
961
	## loop through the files contained in the directory
962
	for my $eachFile (glob($path.'*')) {
963
		## if the file is a directory
964
		if( -d $eachFile) {
965
			## pass the directory to the routine ( recursion )
966
			push(@files,recurse($eachFile));
967
		} else {
968
			push(@files,$eachFile);
969
		}
970
	}
971
	return @files;
972
}
973
974
sub Start {
975
    my ($uuid, $action, $obj) = @_;
976
    $dmac = $obj->{mac};
977
    $buildsystem = $obj->{buildsystem};
978
    $uistatus = $obj->{uistatus};
979
    if ($help) {
980
        return <<END
981
GET:uuid,mac:
982
Start a server. Supply mac for starting on specific node.
983
END
984
    }
985
    $dmac = $dmac || $params{'mac'};
986
    return "Status=ERROR No uuid\n" unless ($register{$uuid});
987
    my $serv = $register{$uuid};
988
    $postreply = '' if ($buildsystem);
989
990
    my $name = $serv->{'name'};
991
    utf8::decode($name);
992
    my $image = $serv->{'image'};
993
    my $image2 = $serv->{'image2'};
994
    my $image3 = $serv->{'image3'};
995
    my $image4 = $serv->{'image4'};
996
    my $memory = $serv->{'memory'};
997
    my $vcpu = $serv->{'vcpu'};
998
    my $vgpu = $serv->{'vgpu'};
999
    my $dbstatus = $serv->{'status'};
1000
    my $mac = $serv->{'mac'};
1001
    my $macname = $serv->{'macname'};
1002
    my $networkuuid1 = $serv->{'networkuuid1'};
1003
    my $networkuuid2 = $serv->{'networkuuid2'};
1004
    my $networkuuid3 = $serv->{'networkuuid3'};
1005
    my $nicmodel1 = $serv->{'nicmodel1'};
1006
    my $nicmac1 = $serv->{'nicmac1'};
1007
    my $nicmac2 = $serv->{'nicmac2'};
1008
    my $nicmac3 = $serv->{'nicmac3'};
1009
    my $boot = $serv->{'boot'};
1010 04c16f26 hq
    my $loader = $serv->{'loader'};
1011 95b003ff Origo
    my $diskbus = $serv->{'diskbus'};
1012
    my $cdrom = $serv->{'cdrom'};
1013
    my $diskdev = "vda";
1014
    my $diskdev2 = "vdb";
1015
    my $diskdev3 = "vdc";
1016
    my $diskdev4 = "vdd";
1017
    if ($diskbus eq "ide") {$diskdev = "hda"; $diskdev2 = "hdb"; $diskdev3 = "hdc"; $diskdev4 = "hdd"};
1018
1019
    my $mem = $memory * 1024;
1020
1021
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access image register"};
1022
1023
    my $img = $imagereg{$image};
1024
    my $imagename = $img->{'name'};
1025
    my $imagestatus = $img->{'status'};
1026
    my $img2 = $imagereg{$image2};
1027
    my $image2status = $img2->{'status'};
1028
    my $img3 = $imagereg{$image3};
1029
    my $image3status = $img3->{'status'};
1030
    my $img4 = $imagereg{$image4};
1031
    my $image4status = $img4->{'status'};
1032
1033
    if (!$imagereg{$image}) {
1034
        $postreply .= "Status=Error Image $image not found - please select a new image for your server, not starting $name\n";
1035
        untie %imagereg;
1036
        return $postreply;
1037
    }
1038
    untie %imagereg;
1039
1040
    if ($imagestatus ne "used" && $imagestatus ne "cloning") {
1041
        $postreply .= "Status=ERROR Image $imagename $image is $imagestatus, not starting $name\n";
1042
    } elsif ($image2 && $image2 ne '--' && $image2status ne "used" && $image2status ne "cloning") {
1043
        $postreply .= "Status=ERROR Image2 is $image2status, not starting $name\n";
1044
    } elsif ($image3 && $image3 ne '--' && $image3status ne "used" && $image3status ne "cloning") {
1045
        $postreply .= "Status=ERROR Image3 is $image3status, not starting $name\n";
1046
    } elsif ($image4 && $image4 ne '--' && $image4status ne "used" && $image4status ne "cloning") {
1047
        $postreply .= "Status=ERROR Image4 is $image4status, not starting $name\n";
1048
    } elsif (overQuotas($memory,$vcpu)) {
1049
        $main::syslogit->($user, "info", "Over quota starting a $dbstatus domain: $uuid");
1050
        $postreply .= "Status=ERROR Over quota - not starting $name\n";
1051
    # Status inactive is typically caused by a movepiston having problems. We should not start inactive servers since
1052
    # they could possibly be running even if movepiston is down. Movepiston on the node should be brought up to update
1053
    # the status, or the node should be removed from the stabile.
1054
    # We now allow to force start of inactive server when dmac is specified
1055
    } elsif ((!$dmac || $dmac eq $mac) && $dbstatus eq 'inactive' && $nodereg{$mac} && ($nodereg{$mac}->{'status'} eq 'inactive' || $nodereg{$mac}->{'status'} eq 'shutdown')) {
1056
        $main::syslogit->($user, "info", "Not starting inactive domain: $uuid (last seen on $mac)");
1057
        $postreply .= "Status=ERROR Not starting $name - Please bring up node $macname\n";
1058
    } elsif ($dbstatus eq 'inactive' || $dbstatus eq 'shutdown' || $dbstatus eq 'shutoff' || $dbstatus eq 'new') {
1059
        unless ($dmac && $isadmin) {
1060
            $dmac = $mac if ($dbstatus eq 'inactive'); # If movepiston crashed while shutting down, allow server to start on same node
1061
        }
1062
        $uistatus = "starting" unless ($uistatus);
1063
        my $hypervisor = getHypervisor($image);
1064
        my ($targetmac, $targetname, $targetip, $port) = locateTargetNode($uuid, $dmac, $mem, $vcpu, $image, $image2 ,$image3, $image4, $hypervisor);
1065
1066
        # Build XML for starting domain
1067
        my $graphics = "vnc";
1068
        $graphics = "rdp" if ($hypervisor eq "vbox");
1069
        my $net1 = $networkreg{$networkuuid1};
1070
        my $networkid1 = $net1->{'id'}; # Get the current vlan id of the network
1071
        my $net2 = $networkreg{$networkuuid2};
1072
        my $networkid2 = $net2->{'id'}; # Get the current vlan id of the network
1073
        my $net3 = $networkreg{$networkuuid2};
1074
        my $networkid3 = $net3->{'id'}; # Get the current vlan id of the network
1075
        my $networkid1ip = $net1->{'internalip'};
1076
        $networkid1ip = $net1->{'externalip'} if ($net1->{'type'} eq 'externalip');
1077
1078
        my $uname = $name . substr($uuid,0,8); # We don't enforce unique names, so we make them
1079
        $uname =~ s/[^[:ascii:]]/_/g; # Get rid of funny chars - they mess up Guacamole
1080
        $uname =~ s/\W/_/g;
1081
1082
        my $driver1;
1083
        my $driver2;
1084
        if ($hypervisor eq 'kvm') {
1085
            my $fmt1 = ($image =~ /\.qcow2$/)?'qcow2':'raw';
1086
            my $fmt2 = ($image2 =~ /\.qcow2$/)?'qcow2':'raw';
1087
            my $fmt3 = ($image3 =~ /\.qcow2$/)?'qcow2':'raw';
1088
            my $fmt4 = ($image4 =~ /\.qcow2$/)?'qcow2':'raw';
1089 2a63870a Christian Orellana
            my $cache1 = ($image =~ /\/node\//)?'default':'writeback';
1090
            my $cache2 = ($image2 =~ /\/node\//)?'default':'writeback';
1091
            my $cache3 = ($image3 =~ /\/node\//)?'default':'writeback';
1092
            my $cache4 = ($image4 =~ /\/node\//)?'default':'writeback';
1093
            $driver1 = "\n      <driver name='qemu' type='$fmt1' cache='$cache1'/>";
1094
            $driver2 = "\n      <driver name='qemu' type='$fmt2' cache='$cache2'/>";
1095
            $driver3 = "\n      <driver name='qemu' type='$fmt3' cache='$cache3'/>";
1096
            $driver4 = "\n      <driver name='qemu' type='$fmt4' cache='$cache4'/>";
1097 95b003ff Origo
        }
1098
1099
        my $networktype1 = "user";
1100
        my $networksource1 = "default";
1101
        my $networkforward1 = "bridge";
1102
        my $networkisolated1 = "no";
1103
        $networksource1 = "vboxnet0" if ($hypervisor eq "vbox");
1104
        if ($networkid1 eq '0') {
1105
            $networktype1 = "user";
1106
            $networkforward1 = "nat";
1107
            $networkisolated1 = "yes"
1108
        } elsif ($networkid1 == 1) {
1109
            $networktype1 = "network" ;
1110
            $networkforward1 = "nat";
1111
            $networkisolated1 = "yes"
1112
        } elsif ($networkid1 > 1) {
1113
            $networktype1 = "bridge";
1114
            $networksource1 = "br$networkid1";
1115
        }
1116
        my $networktype2 = "user";
1117
        my $networksource2 = "default";
1118
        my $networkforward2 = "bridge";
1119
        my $networkisolated2 = "no";
1120
        $networksource2 = "vboxnet0" if ($hypervisor eq "vbox");
1121
        if ($networkid2 eq '0') {
1122
            $networktype2 = "user";
1123
            $networkforward2 = "nat";
1124
            $networkisolated2 = "yes"
1125
        } elsif ($networkid2 == 1) {
1126
            $networktype2 = "network" ;
1127
            $networkforward2 = "nat";
1128
            $networkisolated2 = "yes"
1129
        } elsif ($networkid2 > 1) {
1130
            $networktype2 = "bridge";
1131
            $networksource2 = "br$networkid2";
1132
        }
1133
        my $networktype3 = "user";
1134
        my $networksource3 = "default";
1135
        my $networkforward3 = "bridge";
1136
        my $networkisolated3 = "no";
1137
        $networksource3 = "vboxnet0" if ($hypervisor eq "vbox");
1138
        if ($networkid3 eq '0') {
1139
            $networktype3 = "user";
1140
            $networkforward3 = "nat";
1141
            $networkisolated3 = "yes"
1142
        } elsif ($networkid3 == 1) {
1143
            $networktype3 = "network" ;
1144
            $networkforward3 = "nat";
1145
            $networkisolated3 = "yes"
1146
        } elsif ($networkid3 > 1) {
1147
            $networktype3 = "bridge";
1148
            $networksource3 = "br$networkid3";
1149
        }
1150
1151
        my $xml = "<domain type='$hypervisor' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>\n";
1152
#        if ($vgpu && $vgpu ne "--") {
1153
#            $xml .= <<ENDXML2
1154
#  <qemu:commandline>
1155
#    <qemu:arg value='-device'/>
1156
#    <qemu:arg value='vfio-pci,host=01:00.0,x-vga=on'/>
1157
#    <qemu:arg value='-device'/>
1158
#    <qemu:arg value='vfio-pci,host=02:00.0,x-vga=on'/>
1159
#  </qemu:commandline>
1160
#ENDXML2
1161
#            ;
1162
#        }
1163
1164
#    <qemu:arg value='-set'/>
1165
#    <qemu:arg value='device.hostdev1.x-vga=on'/>
1166
#    <qemu:arg value='-cpu'/>
1167
#	<qemu:arg value='host,kvm=off'/>
1168
#    <qemu:arg value='-device'/>
1169
#	<qemu:arg value='pci-assign,host=01:00.0,id=hostdev0,configfd=20,bus=pci.0,addr=0x6,x-pci-vendor-id=0x10DE,x-pci-device-id=0x11BA,x-pci-sub-vendor-id=0x10DE,x-pci-sub-device-id=0x0965'/>
1170
1171
#  <cpu mode='host-model'>
1172
#    <vendor>Intel</vendor>
1173
#    <model>core2duo</model>
1174
#  </cpu>
1175
1176
#    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
1177
#    <nvram template='/usr/share/OVMF/OVMF_VARS.fd'/>
1178 04c16f26 hq
        my $loader_xml = <<ENDXML
1179
    <bootmenu enable='yes' timeout='200'/>
1180
    <smbios mode='sysinfo'/>
1181
ENDXML
1182
        ;
1183
if ($loader eq 'uefi') {
1184
    $loader_xml = <<ENDXML
1185
  <loader readonly='yes' secure='no' type='pflash'>/usr/share/ovmf/OVMF.fd</loader>
1186
  <nvram template='/usr/share/OVMF/OVMF_VARS.fd'>/tmp/guest_VARS.fd</nvram>
1187
ENDXML
1188
    ;
1189
}
1190 95b003ff Origo
1191 705b5366 hq
        if ($vgpu && $vgpu ne "--") {
1192
            $xml .= <<ENDXML
1193 95b003ff Origo
  <cpu mode='host-passthrough'>
1194
    <feature policy='disable' name='hypervisor'/>
1195
  </cpu>
1196
ENDXML
1197
;
1198 705b5366 hq
        } else {
1199
            $xml .= <<ENDXML
1200
  <cpu mode='host-model'>
1201
  </cpu>
1202
ENDXML
1203
            ;
1204 95b003ff Origo
        }
1205
        $xml .=  <<ENDXML
1206
  <name>$uname</name>
1207
  <uuid>$uuid</uuid>
1208
  <memory>$mem</memory>
1209
  <vcpu>$vcpu</vcpu>
1210
  <os>
1211
    <type arch='x86_64' machine='pc'>hvm</type>
1212
    <boot dev='$boot'/>
1213 04c16f26 hq
$loader_xml
1214 95b003ff Origo
  </os>
1215
  <sysinfo type='smbios'>
1216
    <bios>
1217
      <entry name='vendor'>Origo</entry>
1218
    </bios>
1219
    <system>
1220
      <entry name='manufacturer'>Origo</entry>
1221
      <entry name='sku'>$networkid1ip</entry>
1222
    </system>
1223
  </sysinfo>
1224
  <features>
1225
ENDXML
1226
;
1227
        if ($vgpu && $vgpu ne "--") { $xml .= <<ENDXML
1228
    <kvm>
1229
      <hidden state='on'/>
1230
    </kvm>
1231
ENDXML
1232
;
1233
        }
1234
        $xml .= <<ENDXML
1235
    <pae/>
1236
    <acpi/>
1237
    <apic/>
1238
  </features>
1239
  <clock offset='localtime'>
1240
    <timer name='rtc' tickpolicy='catchup' track='guest'/>
1241
    <timer name='pit' tickpolicy='delay'/>
1242
    <timer name='hpet' present='no'/>
1243
  </clock>
1244
  <on_poweroff>destroy</on_poweroff>
1245 04c16f26 hq
  <on_reboot>restart</on_reboot>½
1246 95b003ff Origo
  <on_crash>restart</on_crash>
1247
  <devices>
1248 e837d785 hq
  <sound model='ich6'/>
1249 95b003ff Origo
ENDXML
1250
;
1251
#        if ($vgpu && $vgpu ne "--") {
1252
#            $xml .= <<ENDXML2
1253
#  <hostdev mode='subsystem' type='pci' managed='yes'>
1254
#    <source>
1255
#      <address domain='0x0000' bus='0x01' slot='0x00' function='0x0' multifunction='on'/>
1256
#    </source>
1257
#  </hostdev>
1258
#  <hostdev mode='subsystem' type='pci' managed='yes'>
1259
#    <source>
1260
#      <address domain='0x0000' bus='0x02' slot='0x00' function='0x0' multifunction='on'/>
1261
#    </source>
1262
#  </hostdev>
1263
#ENDXML2
1264
#;
1265
#        }
1266
        if ($image && $image ne "" && $image ne "--") {
1267
						$xml .= <<ENDXML2
1268
    <disk type='file' device='disk'>
1269
      <source file='$image'/>$driver1
1270
      <target dev='$diskdev' bus='$diskbus'/>
1271
    </disk>
1272
ENDXML2
1273
;
1274
        };
1275
1276
        if ($image2 && $image2 ne "" && $image2 ne "--") {
1277
						$xml .= <<ENDXML2
1278
    <disk type='file' device='disk'>$driver2
1279
      <source file='$image2'/>
1280
      <target dev='$diskdev2' bus='$diskbus'/>
1281
    </disk>
1282
ENDXML2
1283
;
1284
        };
1285
        if ($image3 && $image3 ne "" && $image3 ne "--") {
1286
						$xml .= <<ENDXML2
1287
    <disk type='file' device='disk'>$driver3
1288
      <source file='$image3'/>
1289
      <target dev='$diskdev3' bus='$diskbus'/>
1290
    </disk>
1291
ENDXML2
1292
;
1293
        };
1294
        if ($image4 && $image4 ne "" && $image4 ne "--") {
1295
						$xml .= <<ENDXML2
1296
    <disk type='file' device='disk'>$driver4
1297
      <source file='$image4'/>
1298
      <target dev='$diskdev4' bus='$diskbus'/>
1299
    </disk>
1300
ENDXML2
1301
;
1302
        };
1303
1304
        unless ($image4 && $image4 ne '--' && $diskbus eq 'ide') {
1305
            if ($cdrom && $cdrom ne "" && $cdrom ne "--") {
1306
						$xml .= <<ENDXML3
1307
    <disk type='file' device='cdrom'>
1308
      <source file='$cdrom'/>
1309
      <target dev='hdd' bus='ide'/>
1310
      <readonly/>
1311
    </disk>
1312
ENDXML3
1313
;
1314
            } elsif ($hypervisor ne "vbox") {
1315
						$xml .= <<ENDXML3
1316
    <disk type='file' device='cdrom'>
1317
      <target dev='hdd' bus='ide'/>
1318
      <readonly/>
1319
    </disk>
1320
ENDXML3
1321
;
1322
            }
1323
        }
1324
1325
        $xml .= <<ENDXML4
1326
    <interface type='$networktype1'>
1327
      <source $networktype1='$networksource1'/>
1328
      <forward mode='$networkforward1'/>
1329
      <port isolated='$networkisolated1'/>
1330
      <model type='$nicmodel1'/>
1331
      <mac address='$nicmac1'/>
1332
    </interface>
1333
ENDXML4
1334
;
1335
1336
        if (($networkuuid2 && $networkuuid2 ne '--') || $networkuuid2 eq '0') {
1337
            $xml .= <<ENDXML5
1338
    <interface type='$networktype2'>
1339
      <source $networktype2='$networksource2'/>
1340
      <forward mode='$networkforward2'/>
1341
      <port isolated='$networkisolated2'/>
1342
      <model type='$nicmodel1'/>
1343
      <mac address='$nicmac2'/>
1344
    </interface>
1345
ENDXML5
1346
;
1347
        }
1348
        if (($networkuuid3 && $networkuuid3 ne '--') || $networkuuid3 eq '0') {
1349
            $xml .= <<ENDXML5
1350
    <interface type='$networktype3'>
1351
      <source $networktype3='$networksource3'/>
1352
      <forward mode='$networkforward3'/>
1353
      <port isolated='$networkisolated3'/>
1354
      <model type='$nicmodel1'/>
1355
      <mac address='$nicmac3'/>
1356
    </interface>
1357
ENDXML5
1358
;
1359
        }
1360
        $xml .= <<ENDXML6
1361
     <serial type='pty'>
1362
       <source path='/dev/pts/0'/>
1363
       <target port='0'/>
1364
     </serial>
1365
    <input type='tablet' bus='usb'/>
1366
    <graphics type='$graphics' port='$port'/>
1367
  </devices>
1368
</domain>
1369
ENDXML6
1370
;
1371
1372
1373
#    <graphics type='$graphics' port='$port' keymap='en-us'/>
1374
#     <console type='pty' tty='/dev/pts/0'>
1375
#       <source path='/dev/pts/0'/>
1376
#       <target port='0'/>
1377
#     </console>
1378
#     <graphics type='$graphics' port='-1' autoport='yes'/>
1379
1380
        $xmlreg{$uuid} = {
1381
            xml=>URI::Escape::uri_escape($xml)
1382
        };
1383
1384
        # Actually ask node to start domain
1385
        if ($targetmac) {
1386
            $register{$uuid}->{'mac'} = $targetmac;
1387
            $register{$uuid}->{'macname'} = $targetname;
1388
            $register{$uuid}->{'macip'} = $targetip;
1389
1390
            my $tasks = $nodereg{$targetmac}->{'tasks'};
1391
            $tasks .= "START $uuid $user\n";
1392
    # Also update allowed port forwards - obsolete
1393
    #        $tasks .= "PERMITOPEN $user\n";
1394
            $nodereg{$targetmac}->{'tasks'} = $tasks;
1395
            tied(%nodereg)->commit;
1396
            $uiuuid = $uuid;
1397
            $uidisplayip = $targetip;
1398
            $uidisplayport = $port;
1399
            $register{$uuid}->{'status'} = $uistatus;
1400
            $register{$uuid}->{'statustime'} = $current_time;
1401
            tied(%register)->commit;
1402
1403
            # Activate networks
1404
            require "$Stabile::basedir/cgi/networks.cgi";
1405
            Stabile::Networks::Activate($networkuuid1, 'activate');
1406
            Stabile::Networks::Activate($networkuuid2, 'activate') if ($networkuuid2 && $networkuuid2 ne '--');
1407
            Stabile::Networks::Activate($networkuuid3, 'activate') if ($networkuuid3 && $networkuuid3 ne '--');
1408
1409
            $main::syslogit->($user, "info", "Marked $name ($uuid) for ". $serv->{'status'} . " on $targetname ($targetmac)");
1410
            $postreply .= "Status=starting OK $uistatus ". $serv->{'name'} . "\n";
1411
        } else {
1412
            $main::syslogit->($user, "info", "Could not find $hypervisor target for creating $uuid ($image)");
1413
            $postreply .= "Status=ERROR problem $uistatus ". $serv->{'name'} . " (unable to locate target node)\n";
1414
        };
1415
    } else {
1416
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
1417
        $postreply .= "Status=ERROR problem $uistatus ". $serv->{'name'} . "\n";
1418
    }
1419
    #return ($uiuuid, $uidisplayip, $uidisplayport, $postreply, $targetmac);
1420
    return $postreply;
1421
}
1422
1423
sub do_attach {
1424
    my ($uuid, $action, $obj) = @_;
1425
    if ($help) {
1426
        return <<END
1427
GET:uuid,image:
1428
Attaches an image to a server as a disk device. Image must not be in use.
1429
END
1430
    }
1431
    my $dev = '';
1432
    my $imagenum = 0;
1433
    my $serv = $register{$uuid};
1434
1435
    if (!$serv->{'uuid'} || ($serv->{'status'} ne 'running' && $serv->{'status'} ne 'paused')) {
1436
        return "Status=Error Server must exist and be running\n";
1437
    }
1438
    my $macip = $serv->{macip};
1439
    my $image = $obj->{image} || $obj->{path};
1440
    if ($image && !($image =~ /^\//)) { # We have a uuid
1441
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Status=Error Unable to access images register\n"};
1442
        $image = $imagereg2{$image}->{'path'} if ($imagereg2{$image});
1443
        untie %imagereg2;
1444
    }
1445
    unless (tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$postreply .= "Status=Error Unable to access images register\n"; return $postreply;};
1446
    unless ($macip && $imagereg{$image} && $imagereg{$image}->{'user'} eq $user && $serv->{'user'} eq $user)  {$postreply .= "Status=Error Invalid image or server\n"; return $postreply;};
1447
    if ($imagereg{$image}->{'status'} ne 'unused') {return "Status=Error Image $image is already in use ($imagereg{$image}->{'status'})\n"};
1448
1449
    my $cmd = qq|$sshcmd $macip "LIBVIRT_DEFAULT_URI=qemu:///system virsh domblklist $uuid"|;
1450
    my $res = `$cmd`;
1451
    unless ($res =~ /vdb\s+.+/) {$dev = 'vdb'; $imagenum = 2};
1452
    unless ($dev || $res =~ /vdc\s+.+/)  {$dev = 'vdc'; $imagenum = 3};
1453
    unless ($dev || $res =~ /vdd\s+.+/)  {$dev = 'vdd'; $imagenum = 4};
1454
    if (!$dev) {
1455
        $postreply = "Status=Error No more images can be attached\n";
1456
    } else {
1457
        my $xml = <<END
1458
<disk type='file' device='disk'>
1459
  <driver type='qcow2' name='qemu' cache='default'/>
1460
  <source file='$image'/>
1461
  <target dev='$dev' bus='virtio'/>
1462
</disk>
1463
END
1464
;
1465
        $cmd = qq|$sshcmd $macip "echo \\"$xml\\" > /tmp/attach-device-$uuid.xml"|;
1466
        $res = `$cmd`;
1467
        $res .= `$sshcmd $macip LIBVIRT_DEFAULT_URI=qemu:///system virsh attach-device $uuid /tmp/attach-device-$uuid.xml`;
1468
        chomp $res;
1469
        if ($res =~ /successfully/) {
1470
            $postreply .= "Status=OK Attaching $image to $dev\n";
1471
            $imagereg{$image}->{'status'} = 'active';
1472
            $imagereg{$image}->{'domains'} = $uuid;
1473
            $imagereg{$image}->{'domainnames'} = $serv->{'name'};
1474
            $serv->{"image$imagenum"} = $image;
1475
            $serv->{"image$imagenum"."name"} = $imagereg{$image}->{'name'};
1476
            $serv->{"image$imagenum"."type"} = 'qcow2';
1477
        } else {
1478
            $postreply .= "Status=Error Unable to attach image $image to $dev ($res)\n";
1479
        }
1480
    }
1481
    untie %imagereg;
1482
    return $postreply;
1483
}
1484
1485
sub do_detach {
1486
    my ($uuid, $action, $obj) = @_;
1487
    if ($help) {
1488
        return <<END
1489
GET:uuid,image:
1490
Detaches a disk device and the associated image from a running server. All associated file-systems within the server should be unmounted before detaching, otherwise data loss i very probable. Use with care.
1491
END
1492
    }
1493
    my $dev = '';
1494
    my $serv = $register{$uuid};
1495
1496
    if (!$serv->{'uuid'} || ($serv->{'status'} ne 'running' && $serv->{'status'} ne 'paused')) {
1497
        return "Status=Error Server must exist and be running\n";
1498
    }
1499
    my $macip = $serv->{macip};
1500
1501
    my $image = $obj->{image} || $obj->{path} || $serv->{'image2'};
1502
    if ($image && !($image =~ /^\//)) { # We have a uuid
1503
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1504
        $image = $imagereg2{$image}->{'path'} if ($imagereg2{$image});
1505
        untie %imagereg2;
1506
    }
1507
    unless (tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$postreply .= "Status=Error Unable to access images register\n"; return $postreply;};
1508
    unless ($macip && $imagereg{$image} && $imagereg{$image}->{'user'} eq $user && $serv->{'user'} eq $user)  {$postreply .= "Status=Error Invalid image or server. Server must have a secondary image attached.\n"; return $postreply;};
1509
1510
    my $cmd = qq|$sshcmd $macip "LIBVIRT_DEFAULT_URI=qemu:///system virsh domblklist $uuid"|;
1511
    my $res = `$cmd`;
1512
    $dev = $1 if ($res =~ /(vd.)\s+.+$image/);
1513
    if (!$dev) {
1514
        $postreply =  qq|Status=Error Image $image, $cmd, is not currently attached\n|;
1515
    } elsif ($dev eq 'vda') {
1516
        $postreply = "Status=Error You cannot detach the primary image\n";
1517
    } else {
1518
        $res = `$sshcmd $macip LIBVIRT_DEFAULT_URI=qemu:///system virsh detach-disk $uuid $dev`;
1519
        chomp $res;
1520
        if ($res =~ /successfully/) {
1521
            $postreply .= "Status=OK Detaching image $image, $imagereg{$image}->{'uuid'} from $dev\n";
1522
            my $imagenum;
1523
            $imagenum = 2 if ($serv->{'image2'} eq $image);
1524
            $imagenum = 3 if ($serv->{'image3'} eq $image);
1525
            $imagenum = 4 if ($serv->{'image4'} eq $image);
1526
            $imagereg{$image}->{'status'} = 'unused';
1527
            $imagereg{$image}->{'domains'} = '';
1528
            $imagereg{$image}->{'domainnames'} = '';
1529
            if ($imagenum) {
1530
                $serv->{"image$imagenum"} = '';
1531
                $serv->{"image$imagenum"."name"} = '';
1532
                $serv->{"image$imagenum"."type"} = '';
1533
            }
1534
        } else {
1535
            $postreply .= "Status=Error Unable to attach image $image to $dev ($res)\n";
1536
        }
1537
    }
1538
    untie %imagereg;
1539
    return $postreply;
1540
}
1541
1542
sub Destroy {
1543
    my ($uuid, $action, $obj) = @_;
1544
    if ($help) {
1545
        return <<END
1546
GET:uuid,wait:
1547
Marks a server for halt, i.e. pull the plug if regular shutdown does not work or is not desired. Server and storage is preserved.
1548
END
1549
    }
1550
    my $uistatus = 'destroying';
1551
    my $name = $register{$uuid}->{'name'};
1552
    my $mac = $register{$uuid}->{'mac'};
1553
    my $macname = $register{$uuid}->{'macname'};
1554
    my $dbstatus = $register{$uuid}->{'status'};
1555
    my $wait = $obj->{'wait'};
1556
    if ($dbstatus eq 'running' or $dbstatus eq 'paused'
1557
        or $dbstatus eq 'shuttingdown' or $dbstatus eq 'starting'
1558
        or $dbstatus eq 'destroying' or $dbstatus eq 'upgrading'
1559
        or $dbstatus eq 'suspending' or $dbstatus eq 'resuming') {
1560
        if ($wait) {
1561
            $postreply = destroyUserServers($user, 1, $uuid);
1562
        } else {
1563
            my $tasks = $nodereg{$mac}->{'tasks'};
1564
            $nodereg{$mac}->{'tasks'} = $tasks . "DESTROY $uuid $user\n";
1565
            tied(%nodereg)->commit;
1566
            $register{$uuid}->{'status'} = $uistatus;
1567
            $register{$uuid}->{'statustime'} = $current_time;
1568
            $uiuuid = $uuid;
1569
            $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus on $macname ($mac)");
1570
            $postreply .= "Status=destroying $uistatus ". $register{$uuid}->{'name'} . "\n";
1571
        }
1572
    } else {
1573
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $name ($uuid)");
1574
        $postreply .= "Status=ERROR problem $uistatus $name\n";
1575
    }
1576
    return $postreply;
1577
}
1578
1579
sub getHypervisor {
1580
	my $image = shift;
1581
	# Produce a mapping of image file suffixes to hypervisors
1582
	my %idreg;
1583
    unless ( tie(%idreg,'Tie::DBI', Hash::Merge::merge({table=>'nodeidentities', key=>'identity'}, $Stabile::dbopts)) ) {return "Unable to access nodeidentities register"};
1584
    my @idvalues = values %idreg;
1585
	my %formats;
1586
	foreach my $val (@idvalues) {
1587
		my %h = %$val;
1588
		foreach (split(/,/,$h{'formats'})) {
1589
			$formats{lc $_} = $h{'hypervisor'}
1590
		}
1591
	}
1592
	untie %idreg;
1593
1594
	# and then determine the hypervisor in question
1595
	my $hypervisor = "vbox";
1596
	my ($pathname, $path, $suffix) = fileparse($image, '\.[^\.]*');
1597
	$suffix = substr $suffix, 1;
1598
	my $hypervisor = $formats{lc $suffix};
1599
	return $hypervisor;
1600
}
1601
1602
sub nicmac1ToUuid {
1603
    my $nicmac1 = shift;
1604
    my $uuid;
1605
    return $uuid unless $nicmac1;
1606
    my @regkeys = (tied %register)->select_where("user = '$user' AND nicmac1 = '$nicmac1");
1607
	foreach my $k (@regkeys) {
1608
	    my $val = $register{$k};
1609
		my %h = %$val;
1610
		if (lc $h{'nicmac1'} eq lc $nicmac1 && $user eq $h{'user'}) {
1611
    		$uuid =  $h{'uuid'};
1612
    		last;
1613
		}
1614
	}
1615
	return $uuid;
1616
}
1617
1618
sub randomMac {
1619
	my ( %vendor, $lladdr, $i );
1620
#	$lladdr = '00';
1621
	$lladdr = '52:54:00';# KVM vendor string
1622
	while ( ++$i )
1623
#	{ last if $i > 10;
1624
	{ last if $i > 6;
1625
		$lladdr .= ':' if $i % 2;
1626
		$lladdr .= sprintf "%" . ( qw (X x) [int ( rand ( 2 ) ) ] ), int ( rand ( 16 ) );
1627
	}
1628
	return $lladdr;
1629
}
1630
1631
sub overQuotas {
1632
    my $meminc = shift;
1633
    my $vcpuinc = shift;
1634
	my $usedmemory = 0;
1635
	my $usedvcpus = 0;
1636
	my $overquota = 0;
1637
    return $overquota if ($isadmin || $Stabile::userprivileges =~ /a/); # Don't enforce quotas for admins
1638
1639
	my $memoryquota = $usermemoryquota;
1640
	my $vcpuquota = $uservcpuquota;
1641
1642
	if (!$memoryquota || !$vcpuquota) { # 0 or empty quota means use defaults
1643
        $memoryquota = $memoryquota || $Stabile::config->get('MEMORY_QUOTA');
1644
        $vcpuquota = $vcpuquota || $Stabile::config->get('VCPU_QUOTA');
1645
    }
1646
1647
    my @regkeys = (tied %register)->select_where("user = '$user'");
1648
	foreach my $k (@regkeys) {
1649
	    my $val = $register{$k};
1650
		if ($val->{'user'} eq $user && $val->{'status'} ne "shutoff" &&
1651
		    $val->{'status'} ne "inactive" && $val->{'status'} ne "shutdown" ) {
1652
1653
		    $usedmemory += $val->{'memory'};
1654
		    $usedvcpus += $val->{'vcpu'};
1655
		}
1656
	}
1657
	$overquota = $usedmemory+$meminc if ($memoryquota!=-1 && $usedmemory+$meminc > $memoryquota); # -1 means no quota
1658
	$overquota = $usedvcpus+$vcpuinc if ($vcpuquota!=-1 && $usedvcpus+$vcpuinc > $vcpuquota);
1659
	return $overquota;
1660
}
1661
1662
sub validateItem {
1663
    my $valref = shift;
1664
    my $img = $imagereg{$valref->{'image'}};
1665
    my $imagename = $img->{'name'};
1666
    $valref->{'imagename'} = $imagename if ($imagename);
1667
    my $imagetype = $img->{'type'};
1668
    $valref->{'imagetype'} = $imagetype if ($imagetype);
1669
1670
    # imagex may be registered by uuid instead of path - find the path
1671
    # We now support up to 4 images
1672
    for (my $i=2; $i<=4; $i++) {
1673
        if ($valref->{"image$i"} && $valref->{"image$i"} ne '--' && !($valref->{"image$i"} =~ /^\//)) {
1674
            unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1675
            $valref->{"image$i"} = $imagereg2{$valref->{"image$i"}}->{'path'};
1676
            untie %imagereg2;
1677
        }
1678
1679
        my $imgi = $imagereg{$valref->{"image$i"}};
1680
        $valref->{"image$i" . 'name'} = $imgi->{'name'} || $valref->{"image$i" . 'name'};
1681
        $valref->{"image$i" . 'type'} = $imgi->{'type'} || $valref->{"image$i" . 'type'};
1682
    }
1683
1684
    my $net1 = $networkreg{$valref->{'networkuuid1'}};
1685
    my $networkname1 = $net1->{'name'};
1686
    $valref->{'networkname1'} = $networkname1 if ($networkname1);
1687
    my $net2 = $networkreg{$valref->{'networkuuid2'}};
1688
    my $networkname2 = $net2->{'name'};
1689
    $valref->{'networkname2'} = $networkname2 if ($networkname2);
1690
    my $name = $valref->{'name'};
1691
    $valref->{'name'} = $imagename unless $name;
1692
1693
    if ($valref->{'status'} eq "shutoff" || $valref->{'status'} eq "inactive") {
1694
        my $node = $nodereg{$valref->{'mac'}};
1695
        if ($valref->{'image'} =~ /\/mnt\/stabile\/node\//) {
1696
            $valref->{'mac'} = $img->{'mac'};
1697
            $valref->{'macname'} = $node->{'name'};
1698
            $valref->{'macip'} = $node->{'ip'};
1699
        } elsif ($valref->{'image2'} =~ /\/mnt\/stabile\/node\//) {
1700
            $valref->{'mac'} = $imagereg{$valref->{'image2'}}->{'mac'};
1701
            $valref->{'macname'} = $node->{'name'};
1702
            $valref->{'macip'} = $node->{'ip'};
1703
        } elsif ($valref->{'image3'} =~ /\/mnt\/stabile\/node\//) {
1704
            $valref->{'mac'} = $imagereg{$valref->{'image3'}}->{'mac'};
1705
            $valref->{'macname'} = $node->{'name'};
1706
            $valref->{'macip'} = $node->{'ip'};
1707
        } elsif ($valref->{'image4'} =~ /\/mnt\/stabile\/node\//) {
1708
            $valref->{'mac'} = $imagereg{$valref->{'image4'}}->{'mac'};
1709
            $valref->{'macname'} = $node->{'name'};
1710
            $valref->{'macip'} = $node->{'ip'};
1711
        }
1712
    }
1713
# Mark domains we have heard from in the last 20 secs as inactive
1714
    my $dbtimestamp = 0;
1715
    $dbtimestamp = $register{$valref->{'uuid'}}->{'timestamp'} if ($register{$valref->{'uuid'}});
1716
    my $timediff = $current_time - $dbtimestamp;
1717
    if ($timediff >= 20) {
1718
        if  (! ($valref->{'status'} eq "shutoff"
1719
                || $valref->{'status'} eq "starting"
1720
            #    || $valref->{'status'} eq "shuttingdown"
1721
            #    || $valref->{'status'} eq "destroying"
1722
                || ($valref->{'status'} eq "moving" && $timediff<40)
1723
            )) { # Move has probably failed
1724
            $valref->{'status'} = "inactive";
1725
            $imagereg{$valref->{'image'}}->{'status'} = "used" if ($valref->{'image'} && $imagereg{$valref->{'image'}});
1726
            $imagereg{$valref->{'image2'}}->{'status'} = "used" if ($valref->{'image2'} && $imagereg{$valref->{'imag2'}});
1727
            $imagereg{$valref->{'image3'}}->{'status'} = "used" if ($valref->{'image3'} && $imagereg{$valref->{'image3'}});
1728
            $imagereg{$valref->{'image4'}}->{'status'} = "used" if ($valref->{'image4'} && $imagereg{$valref->{'image4'}});
1729
        }
1730
    };
1731
    return $valref;
1732
}
1733
1734
# Run through all domains and mark domains we have heard from in the last 20 secs as inactive
1735
sub updateRegister {
1736
    unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Unable to access user register"};
1737
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1738
1739
    my @regkeys = (tied %register)->select_where("user = '$user'");
1740
1741
    foreach my $k (@regkeys) {
1742
        my $valref = $register{$k};
1743
        next unless ($userreg{$valref->{'user'}});
1744
        my $dbtimestamp = $valref->{'timestamp'};
1745
        my $dbstatus = $valref->{'status'};
1746
        my $timediff = $current_time - $dbtimestamp;
1747
        my $imgstatus;
1748
        my $domstatus;
1749
        if ($timediff >= 20) {
1750
            if  ( $valref->{'status'} eq "shutoff" ) {
1751
                $imgstatus = 'used';
1752
            } elsif ((  $valref->{'status'} eq "starting"
1753
                            || $valref->{'status'} eq "shuttingdown"
1754
                        ) && $timediff>50) {
1755
                $imgstatus = 'used';
1756
                $domstatus = 'inactive';
1757
            } elsif ($valref->{'status'} eq "destroying" || $valref->{'status'} eq "moving") {
1758
                ;
1759
            } else {
1760
                $domstatus = 'inactive';
1761
                $imgstatus = 'used';
1762
            }
1763
            $valref->{'status'} = $domstatus if ($domstatus);
1764
            my $image = $valref->{'image'};
1765
            my $image2 = $valref->{'image2'};
1766
            my $image3 = $valref->{'image3'};
1767
            my $image4 = $valref->{'image4'};
1768
            $imagereg{$image}->{'status'} = $imgstatus if ($imgstatus);
1769
            $imagereg{$image2}->{'status'} = $imgstatus if ($image2 && $imgstatus);
1770
            $imagereg{$image3}->{'status'} = $imgstatus if ($image3 && $imgstatus);
1771
            $imagereg{$image4}->{'status'} = $imgstatus if ($image4 && $imgstatus);
1772
            if ($domstatus eq 'inactive ' && $dbstatus ne 'inactive') {
1773
                $main::updateUI->({ tab=>'servers',
1774
                                    user=>$valref->{'user'},
1775
                                    uuid=>$valref->{'uuid'},
1776
                                    sender=>'updateRegister',
1777
                                    status=>'inactive'})
1778
            }
1779
        };
1780
1781
    }
1782
    untie %userreg;
1783
    untie %imagereg;
1784
}
1785
1786
1787
sub locateTargetNode {
1788
    my ($uuid, $dmac, $mem, $vcpu, $image, $image2, $image3, $image4, $hypervisor, $smac)= @_;
1789
    my $targetname;
1790
    my $targetip;
1791
    my $port;
1792
    my $targetnode;
1793
    my $targetindex; # Availability index of located target node
1794
    my %avhash;
1795
1796
    my $mnode = $register{$uuid};
1797
    $dmac = $mnode->{'mac'}
1798
        if (!$dmac
1799
            && $mnode->{'locktonode'} eq 'true'
1800
            && $mnode->{'mac'}
1801
            && $mnode->{'mac'} ne '--'
1802
            );
1803
1804
    $dmac = '' unless ($isadmin); # Only allow admins to select specific node
1805
    if ($dmac && !$nodereg{$dmac}) {
1806
        $main::syslogit->($user, "info", "The target node $dmac no longer exists, starting $uuid on another node if possible");
1807
        $dmac = '';
1808
    }
1809
1810
    my $imageonnode = ($image =~ /\/mnt\/stabile\/node\//
1811
                                          || $image2 =~ /\/mnt\/stabile\/node\//
1812
                                          || $image3 =~ /\/mnt\/stabile\/node\//
1813
                                          || $image4 =~ /\/mnt\/stabile\/node\//
1814
                                          );
1815
1816
    foreach $node (values %nodereg) {
1817
        my $nstatus = $node->{'status'};
1818
        my $maintenance = $node->{'maintenance'};
1819
        my $nmac = $node->{'mac'};
1820
1821
        if (($nstatus eq 'running' || $nstatus eq 'asleep' || $nstatus eq 'maintenance' || $nstatus eq 'waking')
1822
         && $smac ne $nmac
1823
         && (( ($node->{'memfree'} > $mem+512*1024)
1824
         && (($node->{'vmvcpus'} + $vcpu) <= ($cpuovercommision * $node->{'cpucores'} * $node->{'cpucount'})) ) || $action eq 'listnodeavailability')
1825
        ) {
1826
        # Determine how available this node is
1827
        # Available memory
1828
            my $memweight = 0.2; # memory weighing factor
1829
            my $memindex = $avhash{$nmac}->{'memindex'} = int(100* $memweight* $node->{'memfree'} / (1024*1024) )/100;
1830
        # Free cores
1831
            my $cpuindex = $avhash{$nmac}->{'cpuindex'} = int(100*($cpuovercommision * $node->{'cpucores'} * $node->{'cpucount'} - $node->{'vmvcpus'} - $node->{'reservedvcpus'}))/100;
1832
        # Asleep - not asleep gives a +3
1833
            my $sleepindex = $avhash{$nmac}->{'sleepindex'} = ($node->{'status'} eq 'asleep' || $node->{'status'} eq 'waking')?'0':'3';
1834
            $avhash{$nmac}->{'vmvcpus'} = $node->{'vmvcpus'};
1835
#            $avhash{$nmac}->{'cpucommision'} = $cpuovercommision * $node->{'cpucores'} * $node->{'cpucount'};
1836
#            $avhash{$nmac}->{'cpureservation'} = $node->{'vmvcpus'} + $node->{'reservedvcpus'};
1837
1838
            $avhash{$nmac}->{'name'} = $node->{'name'};
1839
            $avhash{$nmac}->{'mac'} = $node->{'mac'};
1840
1841
            my $aindex = $memindex + $cpuindex + $sleepindex;
1842
        # Don't use nodes that are out of memory of cores
1843
            $aindex = 0 if ($memindex <= 0 || $cpuindex <= 0);
1844
            $avhash{$nmac}->{'index'} = $aindex;
1845
1846
            $avhash{$nmac}->{'storfree'} = $node->{'storfree'};
1847 c899e439 Origo
            $avhash{$nmac}->{'memfree'} = $node->{'memfree'};
1848 95b003ff Origo
            $avhash{$nmac}->{'ip'} = $node->{'ip'};
1849
            $avhash{$nmac}->{'identity'} = $node->{'identity'};
1850
            $avhash{$nmac}->{'status'} = $node->{'status'};
1851
            $avhash{$nmac}->{'maintenance'} = $maintenance;
1852
            $avhash{$nmac}->{'reservedvcpus'} = $node->{'reservedvcpus'};
1853
            my $nodeidentity = $node->{'identity'};
1854
            $nodeidentity = 'kvm' if ($nodeidentity eq 'local_kvm');
1855
1856
            if ($hypervisor eq $nodeidentity) {
1857
                # If image is on node, we must start on same node - registered when moving image
1858
                if ($imageonnode) {
1859
                    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
1860
                    $dmac = $imagereg{$image}->{'mac'};
1861
                    $dmac = $imagereg{$image2}->{'mac'} unless ($dmac);
1862
                    $dmac = $imagereg{$image3}->{'mac'} unless ($dmac);
1863
                    $dmac = $imagereg{$image4}->{'mac'} unless ($dmac);
1864
1865
                    untie %imagereg;
1866
                    if (!$dmac) {
1867
                        $postreply .= "Status=ERROR Image node not found\n";
1868
                        last;
1869
                    }
1870
                }
1871
                $dmac = "" if ($dmac eq "--");
1872 a439a9c4 hq
            # If a specific node is asked for, match mac addresses
1873 95b003ff Origo
                if ($dmac eq $nmac) {
1874
                    $targetnode = $node;
1875
                    last;
1876
                } elsif (!$dmac && $nstatus ne "maintenance" && !$maintenance) {
1877
            # pack or disperse
1878
                    if (!$targetindex) {
1879
                        $targetindex = $aindex;
1880
                        $targetnode = $node;
1881
                    } elsif ($dpolicy eq 'pack') {
1882
                        if ($aindex < $targetindex) {
1883
                            $targetnode = $node;
1884
                            $targetindex = $aindex;
1885
                        }
1886
                    } else {
1887
                        if ($aindex > $targetindex) {
1888
                            $targetnode = $node;
1889
                            $targetindex = $aindex;
1890
                        }
1891
                    }
1892
                }
1893
            }
1894
        }
1895
    }
1896
1897
    if ($targetnode && $uuid) {
1898
        if ($targetnode->{'status'} eq 'asleep') {
1899
            my $nmac = $targetnode->{'mac'};
1900
            my $realmac = substr($nmac,0,2).":".substr($nmac,2,2).":".substr($nmac,4,2).":".substr($nmac,6,2).":".substr($nmac,8,2).":".substr($nmac,10,2);
1901
            my $nlogmsg = "Node $nmac marked for wake ";
1902
            if ($brutalsleep && (
1903
                    ($targetnode->{'amtip'} && $targetnode->{'amtip'} ne '--')
1904
                || ($targetnode->{'ipmiip'} && $targetnode->{'ipmiip'} ne '--')
1905
                )) {
1906
                my $wakecmd;
1907
                if ($targetnode->{'amtip'} && $targetnode->{'amtip'} ne '--') {
1908
                    $wakecmd = "echo 'y' | AMT_PASSWORD='$amtpasswd' /usr/bin/amttool $targetnode->{'amtip'} powerup pxe";
1909
                } else {
1910
                    $wakecmd = "ipmitool -I lanplus -H $targetnode->{'ipmiip'} -U ADMIN -P ADMIN power on";
1911
                }
1912
                $nlogmsg .= `$wakecmd`;
1913
            } else {
1914
                my $broadcastip = $targetnode->{'ip'};
1915
                $broadcastip =~ s/\.\d{1,3}$/.255/;
1916
                $nlogmsg .= 'on lan ' . `/usr/bin/wakeonlan -i $broadcastip $realmac`;
1917
            }
1918
            $targetnode->{'status'} = "waking";
1919
            $nlogmsg =~ s/\n/ /g;
1920
            $main::syslogit->($user, "info", $nlogmsg);
1921
            $postreply .= "Status=OK waking $targetnode->{'name'}\n";
1922
        }
1923
        $targetname = $targetnode->{'name'};
1924
        $targetmac = $targetnode->{'mac'};
1925
        $targetip = $targetnode->{'ip'};
1926
        $targetip = $targetnode->{'ip'};
1927
        my $porttaken = 1;
1928
        while ($porttaken) {
1929
            $porttaken = 0;
1930
            $port = $targetnode->{'vms'} + (($hypervisor eq "vbox")?3389:5900);
1931
            $port += int(rand(200));
1932
            my @regkeys = (tied %register)->select_where("port = '$port' AND macip = '$targetip'");
1933
            foreach my $k (@regkeys) {
1934
                $r = $register{$k};
1935
                if ($r->{'port'} eq $port && $r->{'macip'} eq $targetip) {
1936
                    $porttaken = 1;
1937
                }
1938
            }
1939
        }
1940
        $targetnode->{'vms'}++;
1941
        $targetnode->{'vmvcpus'} += $vcpu;
1942
        $register{$uuid}->{'port'} = $port;
1943
#        $register{$uuid}->{'mac'} = $targetmac;
1944
#        $register{$uuid}->{'macname'} = $targetname;
1945
#        $register{$uuid}->{'macip'} = $targetip;
1946
        $register{$uuid}->{'display'} = (($hypervisor eq "vbox")?'rdp':'vnc');
1947
    } else {
1948
        my $macstatus;
1949
        $macstatus = $nodereg{$dmac}->{status} if ($nodereg{$dmac});
1950
        $main::syslogit->($user, "info", "Could not find target for $uuid, $dmac, $mem, $vcpu, $image, $image2,$image3,$image4, $hypervisor, $smac, dmac-status: $macstatus") if ($uuid);
1951
    }
1952
    return ($targetmac, $targetname, $targetip, $port, \%avhash);
1953
}
1954
1955
sub destroyUserServers {
1956
    my $username = shift;
1957
    my $wait = shift; # Should we wait for servers do die
1958
    my $duuid = shift;
1959
    return unless ($isadmin || $user eq $username);
1960
    my @updateList;
1961
1962
    my @regkeys = (tied %register)->select_where("user = '$username'");
1963
    foreach my $uuid (@regkeys) {
1964
        if ($register{$uuid}->{'user'} eq $username
1965
            && $register{$uuid}->{'status'} ne 'shutoff'
1966
            && (!$duuid || $duuid eq $uuid)
1967
        ) {
1968
            $postreply .= "Destroying $username server $register{$uuid}->{'name'}, $uuid\n";
1969
            Destroy($uuid);
1970
            push (@updateList,{ tab=>'servers',
1971
                                user=>$user,
1972
                                uuid=>$duuid,
1973
                                status=>'destroying'});
1974
        }
1975
    }
1976
    $main::updateUI->(@updateList) if (@updateList);
1977
    if ($wait) {
1978
        my @regkeys = (tied %register)->select_where("user = '$username'");
1979
        my $activeservers = 1;
1980
        my $i = 0;
1981
        while ($activeservers && $i<10) {
1982
            $activeservers = 0;
1983
            foreach my $k (@regkeys) {
1984
                my $valref = $register{$k};
1985
                if ($username eq $valref->{'user'}
1986
                    && ($valref->{'status'} ne 'shutoff'
1987
                    && $valref->{'status'} ne 'inactive')
1988
                    && (!$duuid || $duuid eq $valref->{'uuid'})
1989
                ) {
1990
                    $activeservers = $valref->{'uuid'};
1991
                }
1992
            }
1993
            $i++;
1994
            if ($activeservers) {
1995
                my $res .= "Status=OK Waiting $i for server $register{$activeservers}->{'name'}, $register{$activeservers}->{'status'} to die...\n";
1996 9de5a3f1 hq
            #    print $res if ($console);
1997 95b003ff Origo
                $postreply .= $res;
1998
                sleep 2;
1999
            }
2000
        }
2001
        $postreply .= "Status=OK Servers halted for $username\n" unless ($activeservers);
2002
    }
2003
    return $postreply;
2004
}
2005
2006
sub removeUserServers {
2007
    my $username = shift;
2008
    my $uuid = shift;
2009
    my $destroy = shift; # Should running servers be destroyed before removing
2010
    return unless (($isadmin || $user eq $username) && !$isreadonly);
2011
    $user = $username;
2012
    my @regkeys = (tied %register)->select_where("user = '$username'");
2013
    foreach my $ruuid (@regkeys) {
2014
        next if ($uuid && $ruuid ne $uuid);
2015
        if ($destroy && $register{$ruuid}->{'user'} eq $username && ($register{$ruuid}->{'status'} ne 'shutoff' && $register{$ruuid}->{'status'} ne 'inactive')) {
2016
            destroyUserServers($username, 1, $ruuid);
2017
        }
2018
2019
        if ($register{$ruuid}->{'user'} eq $username && ($register{$ruuid}->{'status'} eq 'shutoff' || $register{$ruuid}->{'status'} eq 'inactive')) {
2020
            $postreply .= "Removing $username server $register{$ruuid}->{'name'}, $ruuid" . ($console?'':'<br>') . "\n";
2021
            Remove($ruuid);
2022
        }
2023
    }
2024
}
2025
2026
sub Remove {
2027
    my ($uuid, $action) = @_;
2028
    if ($help) {
2029
        return <<END
2030
DELETE:uuid:
2031
Removes a server. Server must be shutoff. Does not remove associated images or networks.
2032
END
2033
    }
2034
    my $reguser = $register{$uuid}->{'user'};
2035
    my $dbstatus = $register{$uuid}->{'status'};
2036
    my $image = $register{$uuid}->{'image'};
2037
    my $image2 = $register{$uuid}->{'image2'};
2038
    my $image3 = $register{$uuid}->{'image3'};
2039
    my $image4 = $register{$uuid}->{'image4'};
2040
    my $name = $register{$uuid}->{'name'};
2041
    $image2 = '' if ($image2 eq '--');
2042
    $image3 = '' if ($image3 eq '--');
2043
    $image4 = '' if ($image4 eq '--');
2044
2045
    if ($reguser ne $user) {
2046
        $postreply .= "Status=ERROR You cannot delete a vm you don't own\n";
2047
    } elsif ($dbstatus eq 'inactive' || $dbstatus eq 'shutdown' || $dbstatus eq 'shutoff') {
2048
2049
        # Delete software packages and monitors from register
2050
        $postmsg .= deletePackages($uuid);
2051
        my $sname = $register{$uuid}->{'name'};
2052
        utf8::decode($sname);
2053 48fcda6b Origo
        $postmsg .= deleteMonitors($uuid)?" deleted monitors for $sname ":'';
2054 95b003ff Origo
2055
        delete $register{$uuid};
2056
        delete $xmlreg{$uuid};
2057
2058
        unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
2059
        $imagereg{$image}->{'status'} = "unused" if ($imagereg{$image});
2060
        $imagereg{$image2}->{'status'} = "unused" if ($image2 && $imagereg{$image2});
2061
        $imagereg{$image3}->{'status'} = "unused" if ($image3 && $imagereg{$image3});
2062
        $imagereg{$image4}->{'status'} = "unused" if ($image4 && $imagereg{$image4});
2063
        untie %imagereg;
2064
2065
        # Delete metrics
2066
        my $metricsdir = "/var/lib/graphite/whisper/domains/$uuid";
2067
        `rm -r $metricsdir` if (-e $metricsdir);
2068
        my $rrdfile = "/var/cache/rrdtool/".$uuid."_highres.rrd";
2069
        `rm $rrdfile` if (-e $rrdfile);
2070
2071
        $main::syslogit->($user, "info", "Deleted domain $uuid from db");
2072
        utf8::decode($name);
2073 48fcda6b Origo
        $postmsg .= " deleted server $name";
2074 95b003ff Origo
        $postreply = "[]";
2075
        sleep 1;
2076
    } else {
2077
        $postreply .= "Status=ERROR Cannot delete a $dbstatus server\n";
2078
    }
2079
    return $postreply;
2080
}
2081
2082
# Delete all monitors belonging to a server
2083
sub deleteMonitors {
2084
    my ($serveruuid) = @_;
2085
    my $match;
2086
    if ($serveruuid) {
2087
        if ($register{$serveruuid}->{'user'} eq $user || $isadmin) {
2088
            local($^I, @ARGV) = ('.bak', "/etc/mon/mon.cf");
2089
            # undef $/; # This makes <> read in the entire file in one go
2090
            my $uuidmatch;
2091
            while (<>) {
2092
                if (/^watch (\S+)/) {
2093
                    if ($1 eq $serveruuid) {$uuidmatch = $serveruuid}
2094
                    else {$uuidmatch = ''};
2095
                };
2096
                if ($uuidmatch) {
2097
                    $match = 1;
2098
                } else {
2099
                    #chomp;
2100
                    print unless (/^hostgroup $serveruuid/);
2101
                }
2102
                close ARGV if eof;
2103
            }
2104
            #$/ = "\n";
2105
        }
2106
        unlink glob "/var/log/stabile/*:$serveruuid:*";
2107
    }
2108
    `/usr/bin/moncmd reset keepstate` if ($match);
2109
    return $match;
2110
}
2111
2112
sub deletePackages {
2113
    my ($uuid, $issystem, %packreg) = @_;
2114
    unless ( tie(%packreg,'Tie::DBI', Hash::Merge::merge({table=>'packages', key=>'id'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
2115
2116
    my @domains;
2117
    if ($issystem) {
2118
        foreach my $valref (values %register) {
2119
            if (($valref->{'system'} eq $uuid || $uuid eq '*')
2120
                    && ($valref->{'user'} eq $user || $fulllist)) {
2121
                push(@domains, $valref->{'uuid'});
2122
            }
2123
        }
2124
    } else { # Allow if domain no longer exists or belongs to user
2125
        push(@domains, $uuid) if (!$register{$uuid} || $register{$uuid}->{'user'} eq $user || $fulllist);
2126
    }
2127
2128
    foreach my $domuuid (@domains) {
2129
        foreach my $packref (values %packreg) {
2130
            my $id = $packref->{'id'};
2131
            if (substr($id, 0,36) eq $domuuid || ($uuid eq '*' && $packref->{'user'} eq $user)) {
2132
                delete $packreg{$id};
2133
            }
2134
        }
2135
    }
2136
    tied(%packreg)->commit;# if (%packreg);
2137
    if ($issystem) {
2138
        my $sname = $register{$uuid}->{'name'};
2139
        utf8::decode($sname);
2140
        return "Status=OK Cleared packages for $sname\n";
2141
    } elsif ($register{$uuid}) {
2142
        my $sname = $register{$uuid}->{'name'};
2143
        utf8::decode($sname);
2144
        return "Status=OK Cleared packages for $sname\n";
2145
    } else {
2146
        return "Status=OK Cleared packages. System not registered\n";
2147
    }
2148
}
2149
2150
sub Save {
2151
    my ($uuid, $action, $obj) = @_;
2152
    if ($help) {
2153
        return <<END
2154 04c16f26 hq
POST:uuid, name, user, system, autostart, locktonode, mac, memory, vcpu, boot, loader, diskbus, nicmodel1, vgpu, cdrom, image, image2, image3, image4, networkuuid2, networkuuid3, networkuuid1, nicmac1, nicmac2, nicmac3:
2155 95b003ff Origo
To save a servers of networks you either PUT or POST a JSON array to the main endpoint with objects representing the servers with the changes you want.
2156
Depending on your privileges not all changes are permitted. If you save without specifying a uuid, a new server is created.
2157
If you pass [user] parameter it is assumed you want to move server to this user's account.
2158
Supported parameters:
2159
2160
uuid: UUID
2161
name: string
2162
user: string
2163 48fcda6b Origo
system: UUID of stack this server belongs to
2164 95b003ff Origo
autostart: true|false
2165
locktonode: true|false
2166
mac: MAC address of target node
2167
2168
memory: int bytes
2169
vcpu: int
2170
boot: hd|cdrom|network
2171 04c16f26 hq
loader: bios|uefi
2172 95b003ff Origo
diskbus: virtio|ide|scsi
2173
nicmodel1: virtio|rtl8139|ne2k_pci|e1000|i82551|i82557b|i82559er|pcnet
2174
vgpu: int
2175
2176
cdrom: string path
2177
image: string path
2178
image2: string path
2179
image3: string path
2180
image4: string path
2181
2182
networkuuid1: UUID of network connection
2183
networkuuid2: UUID of network connection
2184
networkuuid3: UUID of network connection
2185
2186
END
2187
    }
2188
2189
# notes, opemail, opfullname, opphone, email, fullname, phone, services, recovery, alertemail
2190
# notes: string
2191
# opemail: string
2192
# opfullname: string
2193
# opphone: string
2194
# email: string
2195
# fullname: string
2196
# phone: string
2197
# services: string
2198
# recovery: string
2199
# alertemail: string
2200
2201
    my $system = $obj->{system};
2202
    my $newsystem = $obj->{newsystem};
2203
    my $buildsystem = $obj->{buildsystem};
2204
    my $nicmac1 = $obj->{nicmac1};
2205
    $console = $console || $obj->{console};
2206
2207
    $postmsg = '' if ($buildsystem);
2208
    if (!$uuid && $nicmac1) {
2209
        $uuid = nicmac1ToUuid($nicmac1); # If no uuid try to locate based on mac
2210
    }
2211
    if (!$uuid && $uripath =~ /servers(\.cgi)?\/(.+)/) { # Try to parse uuid out of URI
2212
        my $huuid = $2;
2213
        if ($ug->to_string($ug->from_string($huuid)) eq $huuid) { # Check for valid uuid
2214
            $uuid = $huuid;
2215
        }
2216
    }
2217
    my $regserv = $register{$uuid};
2218
    my $status = $regserv->{'status'} || 'new';
2219
    if ((!$uuid) && $status eq 'new') {
2220
        my $ug = new Data::UUID;
2221
        $uuid = $ug->create_str();
2222
    };
2223
    unless ($uuid && length $uuid == 36){
2224 48fcda6b Origo
        $postmsg = "Status=Error No valid uuid ($uuid), $obj->{image}";
2225 95b003ff Origo
        return $postmsg;
2226
    }
2227
    $nicmac1 = $nicmac1 || $regserv->{'nicmac1'};
2228
    my $name = $obj->{name} || $regserv->{'name'};
2229
    my $memory = $obj->{memory} || $regserv->{'memory'};
2230
    my $vcpu = $obj->{vcpu} || $regserv->{'vcpu'};
2231
    my $image = $obj->{image} || $regserv->{'image'};
2232
    my $imagename = $obj->{imagename} || $regserv->{'imagename'};
2233
    my $image2 = $obj->{image2} || $regserv->{'image2'};
2234
    my $image2name = $obj->{image2name} || $regserv->{'image2name'};
2235
    my $image3 = $obj->{image3} || $regserv->{'image3'};
2236
    my $image3name = $obj->{image3name} || $regserv->{'image3name'};
2237
    my $image4 = $obj->{image4} || $regserv->{'image4'};
2238
    my $image4name = $obj->{image4name} || $regserv->{'image4name'};
2239
    my $diskbus = $obj->{diskbus} || $regserv->{'diskbus'};
2240
    my $cdrom = $obj->{cdrom} || $regserv->{'cdrom'};
2241
    my $boot = $obj->{boot} || $regserv->{'boot'};
2242 04c16f26 hq
    my $loader = $obj->{loader} || $regserv->{'loader'};
2243 95b003ff Origo
    my $networkuuid1 = ($obj->{networkuuid1} || $obj->{networkuuid1} eq '0')?$obj->{networkuuid1}:$regserv->{'networkuuid1'};
2244
    my $networkid1 = $obj->{networkid1} || $regserv->{'networkid1'};
2245
    my $networkname1 = $obj->{networkname1} || $regserv->{'networkname1'};
2246
    my $nicmodel1 = $obj->{nicmodel1} || $regserv->{'nicmodel1'};
2247
    my $networkuuid2 = ($obj->{networkuuid2} || $obj->{networkuuid2} eq '0')?$obj->{networkuuid2}:$regserv->{'networkuuid2'};
2248
    my $networkid2 = $obj->{networkid2} || $regserv->{'networkid2'};
2249
    my $networkname2 = $obj->{networkname2} || $regserv->{'networkname2'};
2250
    my $nicmac2 = $obj->{nicmac2} || $regserv->{'nicmac2'};
2251
    my $networkuuid3 = ($obj->{networkuuid3} || $obj->{networkuuid3} eq '0')?$obj->{networkuuid3}:$regserv->{'networkuuid3'};
2252
    my $networkid3 = $obj->{networkid3} || $regserv->{'networkid3'};
2253
    my $networkname3 = $obj->{networkname3} || $regserv->{'networkname3'};
2254
    my $nicmac3 = $obj->{nicmac3} || $regserv->{'nicmac3'};
2255
    my $notes = $obj->{notes} || $regserv->{'notes'};
2256
    my $autostart = $obj->{autostart} || $regserv->{'autostart'};
2257
    my $locktonode = $obj->{locktonode} || $regserv->{'locktonode'};
2258
    my $mac = $obj->{mac} || $regserv->{'mac'};
2259
    my $created = $regserv->{'created'} || time;
2260
    # Sanity checks
2261
    my $tenderpaths = $Stabile::config->get('STORAGE_POOLS_LOCAL_PATHS') || "/mnt/stabile/images";
2262
    my @tenderpathslist = split(/,\s*/, $tenderpaths);
2263
2264
    $networkid1 = $networkreg{$networkuuid1}->{'id'};
2265
    my $networktype1 = $networkreg{$networkuuid1}->{'type'};
2266
    my $networktype2;
2267
    if (!$nicmac1 || $nicmac1 eq "--") {$nicmac1 = randomMac();}
2268
    if ($networkuuid2 && $networkuuid2 ne "--") {
2269
        $networkid2 = $networkreg{$networkuuid2}->{'id'};
2270
        $nicmac2 = randomMac() if (!$nicmac2 || $nicmac2 eq "--");
2271
        $networktype2 = $networkreg{$networkuuid2}->{'type'};
2272
    }
2273
    if ($networkuuid3 && $networkuuid3 ne "--") {
2274
        $networkid3 = $networkreg{$networkuuid3}->{'id'};
2275
        $networkname3 = $networkreg{$networkuuid3}->{'name'};
2276
        $nicmac3 = randomMac() if (!$nicmac3 || $nicmac3 eq "--");
2277
        $networktype3 = $networkreg{$networkuuid3}->{'type'};
2278
    }
2279
2280
    my $imgdup;
2281
    my $netdup;
2282
    my $json_text; # returned if all goes well
2283
2284
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
2285
2286
    if ($networkid1 > 1 && $networkid2 > 1 && $networktype1 ne 'gateway' && $networktype2 ne 'gateway'
2287
        && $networkuuid1 eq $networkuuid2) {
2288
        $netdup = 1;
2289
    }
2290
    if ($networkid1 > 1 && $networkid3 > 1 && $networktype1 ne 'gateway' && $networktype3 ne 'gateway'
2291
        && $networkuuid1 eq $networkuuid3) {
2292
        $netdup = 11;
2293
    }
2294
2295
    if ($image eq $image2
2296
        || $image eq $image3
2297
        || $image eq $image4
2298
        || $image2 && $image2 ne '--' && $image2 eq $image3
2299
        || $image2 && $image2 ne '--' && $image2 eq $image4
2300
        || $image3 && $image3 ne '--' && $image3 eq $image4
2301
    ) {
2302
        $imgdup = 1;
2303
    } elsif ($image =~ m/\.master\.qcow2/
2304
        || $image2 =~ m/\.master\.qcow2/
2305
        || $image3 =~ m/\.master\.qcow2/
2306
        || $image4 =~ m/\.master\.qcow2/
2307
    ) {
2308
        $imgdup = 2;
2309
    } else {
2310
        # Check if another server is using image
2311
        my @regkeys = (tied %register)->select_where("user = '$user' OR user = 'common'");
2312
        foreach my $k (@regkeys) {
2313
            my $val = $register{$k};
2314
            my %h = %$val;
2315
            if ($h{'uuid'} ne $uuid) {
2316
                if (
2317
                    $image eq $h{'image'} || $image eq $h{'image2'}|| $image eq $h{'image3'}|| $image eq $h{'image4'}
2318
                ) {
2319
                    $imgdup = 51;
2320
                } elsif ($image2 && $image2 ne "--" &&
2321
                    ($image2 eq $h{'image'} || $image2 eq $h{'image2'} || $image2 eq $h{'image3'} || $image2 eq $h{'image4'})
2322
                ) {
2323
                    $imgdup = 52;
2324
                } elsif ($image3 && $image3 ne "--" &&
2325
                    ($image3 eq $h{'image'} || $image3 eq $h{'image2'} || $image3 eq $h{'image3'} || $image3 eq $h{'image4'})
2326
                ) {
2327
                    $imgdup = 53;
2328
                } elsif ($image4 && $image4 ne "--" &&
2329
                    ($image4 eq $h{'image'} || $image4 eq $h{'image2'} || $image4 eq $h{'image3'} || $image4 eq $h{'image4'})
2330
                ) {
2331
                    $imgdup = 54;
2332
                }
2333
2334
                if ($networkid1>1) {
2335
                    if ($networktype1 ne 'gateway' &&
2336
                        ($networkuuid1 eq $h{'networkuuid1'} || $networkuuid1 eq $h{'networkuuid2'})
2337
                    ) {
2338
                        $netdup = 51;
2339
                    }
2340
                }
2341
                if ($networkid2>1) {
2342
                    if ($networktype2 ne 'gateway' && $networkuuid2 && $networkuuid2 ne "--" &&
2343
                        ($networkuuid2 eq $h{'networkuuid1'} || $networkuuid2 eq $h{'networkuuid2'})
2344
                    ) {
2345
                        $netdup = 52;
2346
                    }
2347
                }
2348
            }
2349
        }
2350
        my $legalpath;
2351
        if ($image =~ m/\/mnt\/stabile\/node\/$user/) {
2352
            $legalpath = 1;
2353
        } else {
2354
            foreach my $path (@tenderpathslist) {
2355
                if ($image =~ m/$path\/$user/) {
2356
                    $legalpath = 1;
2357
                    last;
2358
                }
2359
            }
2360
        }
2361
        $imgdup = 6 unless $legalpath;
2362
        if ($image2 && $image2 ne "--") { # TODO: We should probably check for conflicting nodes for image3 and image 4 too
2363
            if ($image2 =~ m/\/mnt\/stabile\/node\/$user/) {
2364
                if ($image =~ m/\/mnt\/stabile\/node\/$user/) {
2365
                    if ($imagereg{$image}->{'mac'} eq $imagereg{$image2}->{'mac'}) {
2366
                        $legalpath = 1;
2367
                    } else {
2368
                        $legalpath = 0; # Images are on two different nodes
2369
                    }
2370
                } else {
2371
                    $legalpath = 1;
2372
                }
2373
            } else {
2374
                $legalpath = 0;
2375
                foreach my $path (@tenderpathslist) {
2376
                    if ($image2 =~ m/$path\/$user/) {
2377
                        $legalpath = 1;
2378
                        last;
2379
                    }
2380
                }
2381
            }
2382
            $imgdup = 7 unless $legalpath;
2383
        }
2384
    }
2385
2386
    if (!$imgdup && !$netdup) {
2387
        if ($status eq "new") {
2388
            $status = "shutoff";
2389
            $name = $name || 'New Server';
2390
            $memory = $memory || 1024;
2391
            $vcpu = $vcpu || 1;
2392
            $imagename = $imagename || '--';
2393
            $image2 = $image2 || '--';
2394
            $image2name = $image2name || '--';
2395
            $image3 = $image3 || '--';
2396
            $image3name = $image3name || '--';
2397
            $image4 = $image4 || '--';
2398
            $image4name = $image4name || '--';
2399
            $diskbus = $diskbus || 'ide';
2400
            $cdrom = $cdrom || '--';
2401
            $boot = $boot || 'hd';
2402 04c16f26 hq
            $loader = $loader || 'bios';
2403 95b003ff Origo
            $networkuuid1 = $networkuuid1 || 1;
2404
            $networkid1 = $networkid1 || 1;
2405
            $networkname1 = $networkname1 || '--';
2406
            $nicmodel1 = $nicmodel1 || 'rtl8139';
2407
            $nicmac1 = $nicmac1 || randomMac();
2408
            $networkuuid2 = $networkuuid2 || '--';
2409
            $networkid2 = $networkid2 || '--';
2410
            $networkname2 = $networkname2 || '--';
2411
            $nicmac2 = $nicmac2 || randomMac();
2412
            $networkuuid3 = $networkuuid3 || '--';
2413
            $networkid3 = $networkid3 || '--';
2414
            $networkname3 = $networkname3 || '--';
2415
            $nicmac3 = $nicmac3 || randomMac();
2416
            #    $uiuuid = $uuid; # No need to update ui for new server with jsonreststore
2417 8d7785ff Origo
            $postmsg .= "OK Created new server: $name";
2418 3657de20 Origo
            $postmsg .= ", uuid: $uuid " if ($console);
2419 95b003ff Origo
        }
2420
        # Update status of images
2421
        my @imgs = ($image, $image2, $image3, $image4);
2422
        my @imgkeys = ('image', 'image2', 'image3', 'image4');
2423
        for (my $i=0; $i<4; $i++) {
2424
            my $img = $imgs[$i];
2425
            my $k = $imgkeys[$i];
2426
            my $regimg = $imagereg{$img};
2427
            # if ($img && $img ne '--' && ($status eq 'new' || $img ne $regserv->{$k})) { # Servers image changed - update image status
2428
            if ($img && $img ne '--') { # Always update image status
2429
                $regimg->{'status'} = 'used' if (
2430
                    $regimg->{'status'} eq 'unused'
2431
                        # Image cannot be active if server is shutoff
2432
                        || ($regimg->{'status'} eq 'active' && $status eq 'shutoff')
2433
                );
2434
                $regimg->{'domains'} = $uuid;
2435
                $regimg->{'domainnames'} = $name;
2436
            }
2437
            # If image has changed, release the old image
2438
            if ($status ne 'new' && $img ne $regserv->{$k} && $imagereg{$regserv->{$k}}) {
2439
                $imagereg{$regserv->{$k}}->{'status'} = 'unused';
2440
                delete $imagereg{$regserv->{$k}}->{'domains'};
2441
                delete $imagereg{$regserv->{$k}}->{'domainnames'};
2442
            }
2443
        }
2444
2445
        my $valref = {
2446
            uuid=>$uuid,
2447
            user=>$user,
2448
            name=>$name,
2449
            memory=>$memory,
2450
            vcpu=>$vcpu,
2451
            image=>$image,
2452
            imagename=>$imagename,
2453
            image2=>$image2,
2454
            image2name=>$image2name,
2455
            image3=>$image3,
2456
            image3name=>$image3name,
2457
            image4=>$image4,
2458
            image4name=>$image4name,
2459
            diskbus=>$diskbus,
2460
            cdrom=>$cdrom,
2461
            boot=>$boot,
2462 04c16f26 hq
            loader=>$loader,
2463 95b003ff Origo
            networkuuid1=>$networkuuid1,
2464
            networkid1=>$networkid1,
2465
            networkname1=>$networkname1,
2466
            nicmodel1=>$nicmodel1,
2467
            nicmac1=>$nicmac1,
2468
            networkuuid2=>$networkuuid2,
2469
            networkid2=>$networkid2,
2470
            networkname2=>$networkname2,
2471
            nicmac2=>$nicmac2,
2472
            networkuuid3=>$networkuuid3,
2473
            networkid3=>$networkid3,
2474
            networkname3=>$networkname3,
2475
            nicmac3=>$nicmac3,
2476
            status=>$status,
2477
            notes=>$notes,
2478
            autostart=>$autostart,
2479
            locktonode=>$locktonode,
2480
            action=>"",
2481
            created=>$created
2482
        };
2483
        $valref->{'system'} = $system if ($system);
2484
        if ($mac && $locktonode eq 'true') {
2485
            $valref->{'mac'} = $mac;
2486
            $valref->{'macip'} = $nodereg{$mac}->{'ip'};
2487
            $valref->{'macname'} = $nodereg{$mac}->{'name'};
2488
        }
2489
        if ($newsystem) {
2490
            my $ug = new Data::UUID;
2491
            $sysuuid = $ug->create_str();
2492
            $valref->{'system'} = $sysuuid;
2493 3657de20 Origo
            $postmsg .= "OK sysuuid: $sysuuid " if ($console);
2494 95b003ff Origo
        }
2495
2496
        # Remove domain uuid from old networks. Leave gateways alone - they get updated on next listing
2497
        my $oldnetworkuuid1 = $regserv->{'networkuuid1'};
2498
        if ($oldnetworkuuid1 ne $networkuuid1 && $networkreg{$oldnetworkuuid1}) {
2499
            $networkreg{$oldnetworkuuid1}->{'domains'} =~ s/($uuid)(,?)( ?)//;
2500
        }
2501
2502
        $register{$uuid} = validateItem($valref);
2503
2504
        if ($networkreg{$networkuuid1}->{'type'} eq 'gateway') {
2505 04c16f26 hq
            # We now remove before adding to support API calls that dont necessarily list afterwards
2506
            $networkreg{$networkuuid1}->{'domains'} =~ s/($uuid)(,?)( ?)//;
2507 95b003ff Origo
            my $domains = $networkreg{$networkuuid1}->{'domains'};
2508
            $networkreg{$networkuuid1}->{'domains'} = ($domains?"$domains, ":"") . $uuid;
2509 04c16f26 hq
2510
            $networkreg{$networkuuid1}->{'domainnames'} =~ s/($name)(,?)( ?)//;
2511 95b003ff Origo
            my $domainnames = $networkreg{$networkuuid1}->{'domainnames'};
2512
            $networkreg{$networkuuid1}->{'domainnames'} = ($domainnames?"$domainnames, ":"") . $name;
2513
        } else {
2514
            $networkreg{$networkuuid1}->{'domains'}  = $uuid;
2515
            $networkreg{$networkuuid1}->{'domainnames'}  = $name;
2516
        }
2517
2518
        if ($networkuuid2 && $networkuuid2 ne '--') {
2519
            if ($networkreg{$networkuuid2}->{'type'} eq 'gateway') {
2520 04c16f26 hq
                $networkreg{$networkuuid2}->{'domains'} =~ s/($uuid)(,?)( ?)//;
2521 95b003ff Origo
                my $domains = $networkreg{$networkuuid2}->{'domains'};
2522
                $networkreg{$networkuuid2}->{'domains'} = ($domains?"$domains, ":"") . $uuid;
2523 04c16f26 hq
2524
                $networkreg{$networkuuid2}->{'domainnames'} =~ s/($name)(,?)( ?)//;
2525 95b003ff Origo
                my $domainnames = $networkreg{$networkuuid2}->{'domainnames'};
2526
                $networkreg{$networkuuid2}->{'domainnames'} = ($domainnames?"$domainnames, ":"") . $name;
2527
            } else {
2528
                $networkreg{$networkuuid2}->{'domains'}  = $uuid;
2529
                $networkreg{$networkuuid2}->{'domainnames'}  = $name;
2530
            }
2531
        }
2532
2533
        if ($networkuuid3 && $networkuuid3 ne '--') {
2534
            if ($networkreg{$networkuuid3}->{'type'} eq 'gateway') {
2535
                my $domains = $networkreg{$networkuuid3}->{'domains'};
2536
                $networkreg{$networkuuid3}->{'domains'} = ($domains?"$domains, ":"") . $uuid;
2537
                my $domainnames = $networkreg{$networkuuid3}->{'domainnames'};
2538
                $networkreg{$networkuuid3}->{'domainnames'} = ($domainnames?"$domainnames, ":"") . $name;
2539
            } else {
2540
                $networkreg{$networkuuid3}->{'domains'}  = $uuid;
2541
                $networkreg{$networkuuid3}->{'domainnames'}  = $name;
2542
            }
2543
        }
2544
        my %jitem = %{$register{$uuid}};
2545
        $json_text = to_json(\%jitem, {pretty=>1});
2546
        $json_text =~ s/null/"--"/g;
2547
        $uiuuid = $uuid;
2548
        $uiname = $name;
2549
2550
        tied(%register)->commit;
2551
        tied(%imagereg)->commit;
2552
        tied(%networkreg)->commit;
2553
2554
    } else {
2555 48fcda6b Origo
        $postmsg .= "ERROR This image ($image) cannot be used ($imgdup) " if ($imgdup);
2556
        $postmsg .= "ERROR This network ($networkname1) cannot be used ($netdup)" if ($netdup);
2557 95b003ff Origo
    }
2558
2559
    my $domuser = $obj->{'user'};
2560
    # We were asked to move server to another account
2561
    if ($domuser && $domuser ne '--' && $domuser ne $user) {
2562
        unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username', CLOBBER=>0}, $Stabile::dbopts)) ) {throw Error::Simple("Stroke=Error User register could not be  accessed")};
2563
        if ($status eq 'shutoff' || $status eq 'inactive') {
2564
            unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username', CLOBBER=>1}, $Stabile::dbopts)) ) {$posterror =  "Unable to access user register"; return 0;};
2565
            my @accounts = split(/,\s*/, $userreg{$tktuser}->{'accounts'});
2566
            my @accountsprivs = split(/,\s*/, $userreg{$tktuser}->{'accountsprivileges'});
2567
            %ahash = ($tktuser, $userreg{$tktuser}->{'privileges'}); # Include tktuser in accounts hash
2568
            for my $i (0 .. scalar @accounts)
2569
            {
2570
                next unless $accounts[$i];
2571
                $ahash{$accounts[$i]} = $accountsprivs[$i] || 'r';
2572
            }
2573
            untie %userreg;
2574
2575
            if (!$isreadonly && $ahash{$domuser} && !($ahash{$domuser} =~ /r/)) { # Check if user is allow to access account
2576
                my $imgdone;
2577
                my $netdone;
2578
                # First move main image
2579
                $Stabile::Images::user = $user;
2580
                require "$Stabile::basedir/cgi/images.cgi";
2581
                $Stabile::Images::console = 1;
2582
                $main::updateUI->({tab=>"servers", user=>$user, message=>"Moving image $imagename to account: $domuser"});
2583
                my $nimage = Stabile::Images::Move($image, $domuser);
2584 48fcda6b Origo
                chomp $nimage;
2585 95b003ff Origo
                if ($nimage) {
2586
                    $main::syslogit->($user, "info", "Moving $nimage to account: $domuser");
2587
                    $register{$uuid}->{'image'} = $nimage;
2588
                    $imgdone = 1;
2589
                } else {
2590
                    $main::syslogit->($user, "info", "Unable to move image $imagename to account: $domuser");
2591
                }
2592
                # Move other attached images
2593
                my @images = ($image2, $image3, $image4);
2594
                my @imagenames = ($image2name, $image3name, $image4name);
2595
                my @imagekeys = ('image2', 'image3', 'image4');
2596
                for (my $i=0; $i<3; $i++) {
2597
                    my $img = $images[$i];
2598
                    my $imgname = $imagenames[$i];
2599
                    my $imgkey = $imagekeys[$i];
2600
                    if ($img && $img ne '--') {
2601
                        $main::updateUI->({tab=>"servers", user=>$user, message=>"Moving $imgkey $imgname to account: $domuser"});
2602
                        $nimage = Stabile::Images::Move($img, $domuser);
2603 48fcda6b Origo
                        chomp $nimage;
2604 95b003ff Origo
                        if ($nimage) {
2605
                            $main::syslogit->($user, "info", "Moving $nimage to account: $domuser");
2606
                            $register{$uuid}->{$imgkey} = $nimage;
2607
                        } else {
2608
                            $main::syslogit->($user, "info", "Unable to move $imagekeys[$i] $img to account: $domuser");
2609
                        }
2610
                    }
2611
                }
2612 6fdc8676 hq
                # Then move network(s)
2613 95b003ff Origo
                if ($imgdone) {
2614
                    $Stabile::Networks::user = $user;
2615
                    require "$Stabile::basedir/cgi/networks.cgi";
2616
                    $Stabile::Networks::console = 1;
2617
                    my @networks = ($networkuuid1, $networkuuid2, $networkuuid3);
2618
                    my @netkeys = ('networkuuid1', 'networkuuid2', 'networkuuid3');
2619
                    my @netnamekeys = ('networkname1', 'networkname2', 'networkname3');
2620
                    for (my $i=0; $i<scalar @networks; $i++) {
2621
                        my $net = $networks[$i];
2622
                        my $netkey = $netkeys[$i];
2623
                        my $netnamekey = $netnamekeys[$i];
2624 48fcda6b Origo
                        my $regnet = $networkreg{$net};
2625
                        my $oldid = $regnet->{'id'};
2626 95b003ff Origo
                        next if ($net eq '' || $net eq '--');
2627 48fcda6b Origo
                        if ($regnet->{'type'} eq 'gateway') {
2628 95b003ff Origo
                            if ($oldid > 1) { # Private gateway
2629
                                foreach my $networkvalref (values %networkreg) { # use gateway with same id if it exists
2630
                                    if ($networkvalref->{'user'} eq $domuser
2631
                                        && $networkvalref->{'type'} eq 'gateway'
2632
                                        && $networkvalref->{'id'} == $oldid) {
2633
                                        # We found an existing gateway with same id - use it
2634
                                        $register{$uuid}->{$netkey} = $networkvalref->{'uuid'};
2635
                                        $register{$uuid}->{$netnamekey} = $networkvalref->{'name'};
2636
                                        $netdone = 1;
2637
                                        $main::updateUI->({tab=>"networks", user=>$user, message=>"Using network $networkvalref->{'name'} from account: $domuser"});
2638
                                        last;
2639
                                    }
2640
                                }
2641
                                if (!($netdone)) {
2642
                                    # Make a new gateway
2643
                                    my $ug = new Data::UUID;
2644
                                    my $newuuid = $ug->create_str();
2645 48fcda6b Origo
                                    Stabile::Networks::save($oldid, $newuuid, $regnet->{'name'}, 'new', 'gateway', '', '', $regnet->{'ports'}, 0, $domuser);
2646 95b003ff Origo
                                    $register{$uuid}->{$netkey} = $newuuid;
2647 48fcda6b Origo
                                    $register{$uuid}->{$netnamekey} = $regnet->{'name'};
2648 95b003ff Origo
                                    $netdone = 1;
2649 48fcda6b Origo
                                    $main::updateUI->({tab=>"networks", user=>$user, message=>"Created gateway $regnet->{'name'} for account: $domuser"});
2650
                                    $main::syslogit->($user, "info", "Created gateway $regnet->{'name'} for account: $domuser");
2651 95b003ff Origo
                                }
2652
                            } elsif ($oldid==0 || $oldid==1) {
2653
                                $netdone = 1; # Use common gateway
2654 48fcda6b Origo
                                $main::updateUI->({tab=>"networks", user=>$user, message=>"Reused network $regnet->{'name'} for account: $domuser"});
2655 95b003ff Origo
                            }
2656
                        } else {
2657
                            my $newid = Stabile::Networks::getNextId('', $domuser);
2658
                            $networkreg{$net}->{'id'} = $newid;
2659
                            $networkreg{$net}->{'user'} = $domuser;
2660 6fdc8676 hq
                        #    if ($regnet->{'type'} eq 'internalip' || $regnet->{'type'} eq 'ipmapping') {
2661 95b003ff Origo
                                # Deactivate network and assign new internal ip
2662 48fcda6b Origo
                                Stabile::Networks::Deactivate($regnet->{'uuid'});
2663 95b003ff Origo
                                $networkreg{$net}->{'internalip'} =
2664 48fcda6b Origo
                                    Stabile::Networks::getNextInternalIP('',$regnet->{'uuid'}, $newid, $domuser);
2665 6fdc8676 hq
                        #    }
2666 95b003ff Origo
                            $netdone = 1;
2667 48fcda6b Origo
                            $main::updateUI->({tab=>"networks", user=>$user, message=>"Moved network $regnet->{'name'} to account: $domuser"});
2668
                            $main::syslogit->($user, "info", "Moved network $regnet->{'name'} to account: $domuser");
2669 95b003ff Origo
                        }
2670
                    }
2671
                    if ($netdone) {
2672
                        # Finally move the server
2673
                        $register{$uuid}->{'user'} = $domuser;
2674 48fcda6b Origo
                        $postmsg .= "OK Moved server $name to account: $domuser";
2675 95b003ff Origo
                        $main::syslogit->($user, "info", "Moved server $name ($uuid) to account: $domuser");
2676 48fcda6b Origo
                        $main::updateUI->({tab=>"servers", user=>$user, type=>"update"});
2677 95b003ff Origo
                    } else {
2678 48fcda6b Origo
                        $postmsg .= "ERROR Unable to move network to account: $domuser";
2679 95b003ff Origo
                        $main::updateUI->({tab=>"image", user=>$user, message=>"Unable to move network to account: $domuser"});
2680
                    }
2681
                } else {
2682
                    $main::updateUI->({tab=>"image", user=>$user, message=>"Could not move image to account: $domuser"});
2683
                }
2684
            } else {
2685 48fcda6b Origo
                $postmsg .= "ERROR No access to move server";
2686 95b003ff Origo
            }
2687
        } else {
2688 48fcda6b Origo
            $postmsg .= "Error Unable to move $status server";
2689 95b003ff Origo
            $main::updateUI->({tab=>"servers", user=>$user, message=>"Please shut down before moving server"});
2690
        }
2691
        untie %userreg;
2692
    }
2693
2694
    if ($console) {
2695
        $postreply = $postmsg;
2696
    } else {
2697
        $postreply = $json_text || $postmsg;
2698
    }
2699
    return $postreply;
2700
    untie %imagereg;
2701
}
2702
2703
2704
sub Shutdown {
2705
    my ($uuid, $action, $obj) = @_;
2706
    if ($help) {
2707
        return <<END
2708
GET:uuid:
2709
Marks a server for shutdown, i.e. send and ACPI shutdown event to the server. If OS supports ACPI, it begins a shutdown.
2710
END
2711
    }
2712
    $uistatus = "shuttingdown";
2713
    my $dbstatus = $obj->{status};
2714
    my $mac = $obj->{mac};
2715
    my $macname = $obj->{macname};
2716
    my $name = $obj->{name};
2717
    if ($dbstatus eq 'running') {
2718
        my $tasks;
2719
        $tasks = $nodereg{$mac}->{'tasks'} if ($nodereg{$mac});
2720
        $nodereg{$mac}->{'tasks'} = $tasks . "SHUTDOWN $uuid $user\n";
2721
        tied(%nodereg)->commit;
2722
        $register{$uuid}->{'status'} = $uistatus;
2723
        $register{$uuid}->{'statustime'} = $current_time;
2724
        $uiuuid = $uuid;
2725
        $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus by $macname ($mac)");
2726
        $postreply .= "Status=$uistatus OK $uistatus $name\n";
2727
    } else {
2728
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
2729
        $postreply .= "Status=ERROR problem $uistatus $name...\n";
2730
    }
2731
    return $postreply;
2732
}
2733
2734
sub Suspend {
2735
    my ($uuid, $action, $obj) = @_;
2736
    if ($help) {
2737
        return <<END
2738
GET:uuid:
2739
Marks a server for suspend, i.e. pauses the server. Server must be running
2740
END
2741
    }
2742
#    my $obj = getObj(('uuid', $uuid));
2743
    $uistatus = "suspending";
2744
    my $dbstatus = $obj->{status};
2745
    my $mac = $obj->{mac};
2746
    my $macname = $obj->{macname};
2747
    my $name = $obj->{name};
2748
    if ($dbstatus eq 'running') {
2749
        my $tasks = $nodereg{$mac}->{'tasks'};
2750
        $nodereg{$mac}->{'tasks'} = $tasks . "SUSPEND $uuid $user\n";
2751
        tied(%nodereg)->commit;
2752
        $register{$uuid}->{'status'} = $uistatus;
2753
        $register{$uuid}->{'statustime'} = $current_time;
2754
        $uiuuid = $uuid;
2755
        $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus by $macname ($mac)");
2756
        $postreply .= "Status=$uistatus OK $uistatus $name.\n";
2757
    } else {
2758
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
2759
        $postreply .= "Status=ERROR problem $uistatus $name.\n";
2760
    }
2761
    return $postreply;
2762
}
2763
2764
sub Resume {
2765
    my ($uuid, $action, $obj) = @_;
2766
    if ($help) {
2767
        return <<END
2768
GET:uuid:
2769
Marks a server for resume running. Server must be paused.
2770
END
2771
    }
2772
    my $dbstatus = $obj->{status};
2773
    my $mac = $obj->{mac};
2774
    my $macname = $obj->{macname};
2775
    my $name = $obj->{name};
2776
    my $image = $obj->{image};
2777
    my $image2 = $obj->{image2};
2778
    my $image3 = $obj->{image3};
2779
    my $image4 = $obj->{image4};
2780
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$posterror = "Unable to access image register"; return;};
2781
    if ($imagereg{$image}->{'status'} ne "paused"
2782
        || ($image2 && $image2 ne '--' && $imagereg{$image}->{'status'} ne "paused")
2783
        || ($image3 && $image3 ne '--' && $imagereg{$image3}->{'status'} ne "paused")
2784
        || ($image4 && $image4 ne '--' && $imagereg{$image4}->{'status'} ne "paused")
2785
    ) {
2786
        $postreply .= "Status=ERROR Image $uuid busy ($imagereg{$image}->{'status'}), please wait 30 sec.\n";
2787
        untie %imagereg;
2788
        return $postreply   ;
2789
    } else {
2790
        untie %imagereg;
2791
    }
2792
    $uistatus = "resuming";
2793
    if ($dbstatus eq 'paused') {
2794
        my $tasks = $nodereg{$mac}->{'tasks'};
2795
        $nodereg{$mac}->{'tasks'} = $tasks . "RESUME $uuid $user\n";
2796
        tied(%nodereg)->commit;
2797
        $register{$uuid}->{'status'} = $uistatus;
2798
        $register{$uuid}->{'statustime'} = $current_time;
2799
        $uiuuid = $uuid;
2800
        $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus by $macname ($mac)");
2801
        $postreply .= "Status=$uistatus OK $uistatus ". $register{$uuid}->{'name'} . "\n";
2802
    } else {
2803
        $main::syslogit->($user, "info", "Problem $uistatus a $dbstatus domain: $uuid");
2804
        $postreply .= "Status=ERROR problem $uistatus ". $register{$uuid}->{'name'} . "\n";
2805
    }
2806
    return $postreply;
2807
}
2808
2809
sub Move {
2810
    my ($uuid, $action, $obj) = @_;
2811
    if ($help) {
2812
        return <<END
2813
GET:uuid,mac:
2814 48fcda6b Origo
Moves a server to a different node (Qemu live migration). Server must be running
2815 95b003ff Origo
END
2816
    }
2817
    my $dbstatus = $obj->{status};
2818
    my $dmac = $obj->{mac};
2819
    my $name = $obj->{name};
2820
    my $mem = $obj->{memory};
2821
    my $vcpu = $obj->{vcpu};
2822
    my $image = $obj->{image};
2823
    my $image2 = $obj->{image2};
2824
    my $image3 = $obj->{image3};
2825
    my $image4 = $obj->{image4};
2826
    $uistatus = "moving";
2827
    if ($dbstatus eq 'running' && $isadmin) {
2828
        my $hypervisor = getHypervisor($image);
2829
        my $mac = $register{$uuid}->{'mac'};
2830
        $dmac = "" if ($dmac eq "--");
2831
        $mac = "" if ($mac eq "--");
2832
2833
        if ($image =~ /\/mnt\/stabile\/node\//
2834
            || $image2 =~ /\/mnt\/stabile\/node\//
2835
            || $image3 =~ /\/mnt\/stabile\/node\//
2836
            || $image4 =~ /\/mnt\/stabile\/node\//
2837
        ) {
2838
            # We do not support moving locally stored VM's yet...
2839
            $postreply = qq|{"error": 1, "message": "Moving servers with local storage not supported"}|;
2840
        } else {
2841
            my ($targetmac, $targetname, $targetip, $port) =
2842
                locateTargetNode($uuid, $dmac, $mem, $vcpu, $image, $image2, $image3, $image4, $hypervisor, $mac);
2843
            if ($targetmac) {
2844
                my $tasks = $nodereg{$targetmac}->{'tasks'};
2845
                $tasks = $tasks . "RECEIVE $uuid $user\n";
2846
                # Also update allowed port forwards
2847
                $nodereg{$targetmac}->{'tasks'} = $tasks . "PERMITOPEN $user\n";
2848
                $register{$uuid}->{'status'} = "moving";
2849
                $register{$uuid}->{'statustime'} = $current_time;
2850
                $uiuuid = $uuid;
2851
                $uidisplayip = $targetip;
2852
                $uidisplayport = $port;
2853
                $main::syslogit->($user, "info", "Marked $name ($uuid) for $uistatus to $targetname ($targetmac)");
2854
                $postreply .= "Status=OK $uistatus ". $register{$uuid}->{'name'} . "\n";
2855
2856
                if ($params{'PUTDATA'}) {
2857
                    my %jitem = %{$register{$uuid}};
2858
                    my $json_text = to_json(\%jitem);
2859
                    $json_text =~ s/null/"--"/g;
2860
                    $postreply = $json_text;
2861
                }
2862
                $main::updateUI->({tab=>"servers", user=>$user, status=>'moving', uuid=>$uuid, type=>'update', message=>"Moving $register{$uuid}->{name} to $targetmac"});
2863
            } else {
2864
                $main::syslogit->($user, "info", "Could not find $hypervisor target for $uistatus $uuid ($image)");
2865
                $postreply = qq|{"error": 1, "message": "Could not find target for $uistatus $register{$uuid}->{'name'}"}|;
2866
            }
2867
        }
2868
    } else {
2869
        $main::syslogit->($user, "info", "Problem moving a $dbstatus domain: $uuid");
2870
        $postreply .= qq|{"error": 1, "message": "ERROR problem moving $register{$uuid}->{'name'} ($dbstatus)"}|;
2871
    }
2872
    return $postreply;
2873
}
2874
2875 c899e439 Origo
sub Changepassword {
2876
    my ($uuid, $action, $obj) = @_;
2877
    if ($help) {
2878
        return <<END
2879
POST:uuid,username,password:
2880
Attempts to set password for [username] to [password] using guestfish. If no username is specified, user 'stabile' is assumed.
2881
END
2882
    }
2883
    my $img = $register{$uuid}->{'image'};
2884
    my $username = $obj->{'username'} || 'stabile';
2885
    my $password = $obj->{'password'};
2886
    return "Status=Error Please supply a password\n" unless ($password);
2887
    return "Status=Error Please shut down the server before changing password\n" unless ($register{$uuid} && $register{$uuid}->{'status'} eq 'shutoff');
2888
    return "Status=Error Not allowed\n" unless ($isadmin || $register{$uuid}->{'user'} eq $user);
2889
2890
    unless (tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access images register"}|; return $res;};
2891
    my $cmd = qq/guestfish --rw -a $img -i command "bash -c 'echo $username:$password | chpasswd'" 2>\&1/;
2892
    if ($imagereg{$img} && $imagereg{$img}->{'mac'}) {
2893
        my $mac = $imagereg{$img}->{'mac'};
2894
        my $macip = $nodereg{$mac}->{'ip'};
2895
        $cmd = "$sshcmd $macip $cmd";
2896
    }
2897
    my $res = `$cmd`;
2898
    $res = $1 if ($res =~ /guestfish: (.*)/);
2899
    chomp $res;
2900
    return "Status=OK Ran chpasswd for user $username in server $register{$uuid}->{'name'}: $res\n";
2901
}
2902
2903
sub Sshaccess {
2904
    my ($uuid, $action, $obj) = @_;
2905
    if ($help) {
2906
        return <<END
2907
POST:uuid,address:
2908
Attempts to change the ip addresses you can access the server over SSH (port 22) from, by adding [address] to /etc/hosts.allow.
2909
[address] should either be an IP address or a range in CIDR notation. Please note that no validation of [address] is performed.
2910
END
2911
    }
2912
    my $img = $register{$uuid}->{'image'};
2913
    my $address = $obj->{'address'};
2914
    return "Status=Error Please supply an aaddress\n" unless ($address);
2915
    return "Status=Error Please shut down the server before changing SSH access\n" unless ($register{$uuid} && $register{$uuid}->{'status'} eq 'shutoff');
2916
    return "Status=Error Not allowed\n" unless ($isadmin || $register{$uuid}->{'user'} eq $user);
2917
2918
    unless (tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$res .= qq|{"status": "Error": "message": "Unable to access images register"}|; return $res;};
2919
2920
    my $isshcmd = '';
2921
    my $cmd = qq[guestfish --rw -a $img -i command "sed -i -re 's|(sshd: .*)#stabile|\\1 $address #stabile|' /etc/hosts.allow"];
2922
#    my $cmd = qq[guestfish --rw -a $img -i command "bash -c 'echo sshd: $address >> /etc/hosts.allow'"];
2923
    if ($imagereg{$img} && $imagereg{$img}->{'mac'}) {
2924
        my $mac = $imagereg{$img}->{'mac'};
2925
        my $macip = $nodereg{$mac}->{'ip'};
2926
        $isshcmd = "$sshcmd $macip ";
2927
    }
2928
    my $res = `$isshcmd$cmd`;
2929
    chomp $res;
2930
    #$cmd = qq[guestfish --rw -a $img -i command "bash -c 'cat /etc/hosts.allow'"];
2931
    #$res .= `$isshcmd$cmd`;
2932
    #chomp $res;
2933
    return "Status=OK Tried to add sshd: $address to /etc/hosts.allow in server $register{$uuid}->{'name'}\n";
2934
}
2935
2936 95b003ff Origo
sub Mountcd {
2937
    my ($uuid, $action, $obj) = @_;
2938
    if ($help) {
2939
        return <<END
2940
GET:uuid,cdrom:
2941
Mounts a cdrom on a server. Server must be running. Mounting the special cdrom named '--' unomunts any currently mounted cdrom.
2942
END
2943
    }
2944
    my $dbstatus = $obj->{status};
2945
    my $mac = $obj->{mac};
2946
    my $cdrom = $obj->{cdrom};
2947
    unless ($cdrom && $dbstatus eq 'running') {
2948
        $main::updateUI->({tab=>"servers", user=>$user, uuid=>$uuid, type=>'update', message=>"Unable to mount cdrom"});
2949
        $postreply = qq|{"Error": 1, "message": "Problem mounting cdrom on $obj->{name}"}|;
2950
        return;
2951
    }
2952
    my $tasks = $nodereg{$mac}->{'tasks'};
2953
    # $user is in the middle here, because $cdrom may contain spaces...
2954
    $nodereg{$mac}->{'tasks'} = $tasks . "MOUNT $uuid $user \"$cdrom\"\n";
2955
    tied(%nodereg)->commit;
2956
    if ($cdrom eq "--") {
2957
        $postreply = qq|{"OK": 1, "message": "OK unmounting cdrom from $obj->{name}"}|;
2958
    } else {
2959
        $postreply = qq|{"OK": 1, "message": "OK mounting cdrom $cdrom on $obj->{name}"}|;
2960
    }
2961
    $register{$uuid}->{'cdrom'} = $cdrom unless ($cdrom eq 'virtio');
2962
    return $postreply;
2963
}