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