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
|
use Getopt::Std;
|
9
|
use URI::Escape;
|
10
|
use Error qw(:try);
|
11
|
use Data::Dumper;
|
12
|
use Text::SimpleTable;
|
13
|
use sigtrap 'handler' => \&cleanup, 'QUIT', 'INT', 'TERM', 'KILL', 'STOP';
|
14
|
use Term::ReadLine;
|
15
|
|
16
|
my %options=();
|
17
|
getopts("a:hi:du:", \%options); # -a action -h help -i image -d debug
|
18
|
|
19
|
my $action = $options{a};
|
20
|
my $cmduser = $options{u};
|
21
|
my $debug = $options{d};
|
22
|
|
23
|
my $uuser = scalar getpwuid $<;
|
24
|
my $isadmin;
|
25
|
|
26
|
my $user;
|
27
|
if ($uuser eq "root" || $uuser eq "irigo" || $uuser eq "mon") {
|
28
|
$isadmin = 1;
|
29
|
if ($cmduser) {$user = $cmduser;}
|
30
|
else {$user = 'irigo';};
|
31
|
} elsif ($uuser =~ /irigo-(.+)/) {
|
32
|
$user = $1;
|
33
|
} else {
|
34
|
print "You don't have privileges to access this Stabile installation\n";
|
35
|
exit 0;
|
36
|
}
|
37
|
inithelp();
|
38
|
|
39
|
$ENV{PATH} = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin';
|
40
|
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
|
41
|
|
42
|
$ENV{'REMOTE_USER'} = $user;
|
43
|
|
44
|
my $cmdpath = '/';
|
45
|
my $cmdobj = '/';
|
46
|
my $oldpath;
|
47
|
my $oldobj;
|
48
|
my $basedir = "/var/www/stabile";
|
49
|
$basedir = `cat /etc/stabile/basedir` if -e "/etc/stabile/basedir";
|
50
|
chomp $basedir;
|
51
|
my $cgi = "$basedir/cgi";
|
52
|
|
53
|
my $term;
|
54
|
if (-t STDIN) { # We are talking to a tty
|
55
|
print "Welcome to stash!". ($debug?" (debug)":'') ."\nType \"help\" to list your commands.\n";
|
56
|
$term = new Term::ReadLine 'stash';
|
57
|
}
|
58
|
|
59
|
try {
|
60
|
while (!$halt) {
|
61
|
my $cmdaction;
|
62
|
if ($oldpath) {
|
63
|
$cmdpath = $oldpath;
|
64
|
$oldpath = '';
|
65
|
}
|
66
|
if ($oldobj) {
|
67
|
$cmdobj = $oldobj;
|
68
|
$oldobj = '';
|
69
|
}
|
70
|
|
71
|
chomp $cmdobj;
|
72
|
my $prompt = "$user\@stash:" . "$cmdpath" . (($cmdobj ne '/')?"$cmdobj":"") . "\$";
|
73
|
if ($term) {
|
74
|
# Interactive session
|
75
|
$input = $term->readline($prompt);
|
76
|
} else {
|
77
|
$input = <STDIN>;
|
78
|
}
|
79
|
unless (defined $input) {
|
80
|
exit;
|
81
|
}
|
82
|
chomp ($input);
|
83
|
next unless ($input || $input eq '0');
|
84
|
next if ($input eq '.');
|
85
|
|
86
|
# Handle composite commands
|
87
|
my @pathelements;
|
88
|
my @params;
|
89
|
my $paramstxt;
|
90
|
if ($input =~ /^(\w+=.+)/) {
|
91
|
$paramstxt = $input;
|
92
|
} elsif ($input =~ /(.+)\/(\w+=.+)/) {
|
93
|
@pathelements = split('/', $1);
|
94
|
$paramstxt = $2;
|
95
|
} elsif ($input =~ /(\S+)\s?(.*)/) {
|
96
|
@pathelements = split('/', $1);
|
97
|
$paramstxt = $2;
|
98
|
} else {
|
99
|
"stash does not understand: $input\n";
|
100
|
next;
|
101
|
}
|
102
|
@params = split(/,\s?/, $paramstxt);
|
103
|
if (!$pathelements[0] && $pathelements[1]) { # $input starts with "/"
|
104
|
$oldpath = $cmdpath;
|
105
|
if ($pathelements[1] eq '..'){next;}
|
106
|
elsif ('servers' =~ /^$pathelements[1]/) {$cmdpath = '/servers'}
|
107
|
elsif ('images' =~ /^$pathelements[1]/) {$cmdpath = '/images'}
|
108
|
elsif ('networks' =~ /^$pathelements[1]/) {$cmdpath = '/networks'}
|
109
|
elsif ('nodes' =~ /^$pathelements[1]/ && $isadmin) {$cmdpath = '/nodes'}
|
110
|
elsif ('users' =~ /^$pathelements[1]/ && $isadmin) {$cmdpath = '/users'}
|
111
|
elsif ('systems' =~ /^$pathelements[1]/ && $isadmin) {$cmdpath = '/systems'}
|
112
|
else {next;};
|
113
|
|
114
|
if ($pathelements[2] =~ /^\w{8}-/ || ($cmdpath eq '/users' && $pathelements[2])) {
|
115
|
$oldobj = $cmdobj;
|
116
|
$cmdobj = "/$pathelements[2]";
|
117
|
$cmdaction = $pathelements[3];
|
118
|
} else {
|
119
|
$cmdaction = $pathelements[2];
|
120
|
}
|
121
|
if (!$cmdaction && !@params) {
|
122
|
$oldpath = '';
|
123
|
}
|
124
|
|
125
|
} elsif ($cmdpath ne '/') { # && $user) { # A path has been selected
|
126
|
if ($cmdobj ne '/') { # An object has previously been selected
|
127
|
$cmdaction = $pathelements[0];
|
128
|
} elsif ($pathelements[0] =~ /^\w{8}-/ && $pathelements[1]) {
|
129
|
$oldobj = $cmdobj;
|
130
|
$cmdobj = "/$pathelements[0]";
|
131
|
$cmdaction = $pathelements[1];
|
132
|
} else {
|
133
|
$cmdaction = $pathelements[0];
|
134
|
}
|
135
|
} else { # No path has been selected
|
136
|
if ($pathelements[0] eq '..'){;}
|
137
|
elsif ('servers' =~ /^$pathelements[0]/) {$cmdpath = '/servers';}
|
138
|
elsif ('images' =~ /^$pathelements[0]/) {$cmdpath = '/images';}
|
139
|
elsif ('networks' =~ /^$pathelements[0]/) {$cmdpath = '/networks';}
|
140
|
elsif ('nodes' =~ /^$pathelements[0]/ && $isadmin) {$cmdpath = '/nodes';}
|
141
|
elsif ('users' =~ /^$pathelements[0]/ && $isadmin) {$cmdpath = '/users';}
|
142
|
elsif ('systems' =~ /^$pathelements[0]/ && $isadmin) {$cmdpath = '/systems';}
|
143
|
else {$cmdpath = '/'; $cmdobj = '/';};
|
144
|
|
145
|
if ($cmdpath ne '/') {
|
146
|
if ($pathelements[1] =~ /^\w{8}-/ && $pathelements[2]) {
|
147
|
$oldpath = '/';
|
148
|
$oldobj = $cmdobj;
|
149
|
$cmdobj = "/$pathelements[1]";
|
150
|
$cmdaction = $pathelements[2];
|
151
|
} else {
|
152
|
$cmdaction = $pathelements[1];
|
153
|
}
|
154
|
} else {
|
155
|
$cmdaction = $pathelements[0];
|
156
|
}
|
157
|
}
|
158
|
|
159
|
# my @commands = split(' ', $cmdaction);
|
160
|
# print "Got command $cmdaction, with path $cmdpath and object $cmdobj ($oldpath and $oldobj)\n";
|
161
|
|
162
|
## General commands
|
163
|
if ($cmdaction eq '..') {
|
164
|
if ($cmdpath eq '/' && $cmdobj eq '/') {;}
|
165
|
if ($cmdobj eq '/') {$cmdpath = '/'}
|
166
|
$cmdobj = '/';
|
167
|
} elsif ($cmdaction eq 'quit') {
|
168
|
$halt = 1;
|
169
|
cleanup();
|
170
|
last;
|
171
|
} elsif ($cmdaction eq 'listlog' || $cmdaction eq 'log') {
|
172
|
my $p = '/nodes';
|
173
|
print "Latest log entries:", `$cgi$p.cgi -a listlog`, "\n";
|
174
|
} elsif ($cmdaction eq 'clearlog') {
|
175
|
my $p = '/nodes';
|
176
|
print `$cgi$p.cgi -a clearlog`, "\n";
|
177
|
} elsif ($cmdpath eq '/' && $isadmin && $cmdaction eq 'setuser' && $params[0]) {
|
178
|
$user = $params[0];
|
179
|
$ENV{'REMOTE_USER'} = $user;
|
180
|
print "User changed to: $user\n";
|
181
|
} elsif ($cmdaction eq 'debug') {
|
182
|
$debug = ($params[0] eq 'on' || !$params[0])? 1:0;
|
183
|
print "Debug is " . ($debug?'on':'off') . "\n";
|
184
|
} elsif ($cmdpath eq '/') {
|
185
|
if ($cmdaction eq 'list' || $cmdaction eq 'ls') {
|
186
|
print $objhelp{$cmdpath};
|
187
|
} elsif ($cmdaction eq 'help') {
|
188
|
print $cmdhelp{$cmdpath};
|
189
|
} elsif ($cmdaction eq 'showuser') {
|
190
|
my $suser;
|
191
|
$suser = $params[0]if ($isadmin);
|
192
|
$suser = $user unless ($suser);
|
193
|
print `$cgi/users.cgi -a uuidshow -u $suser`, "\n";
|
194
|
} elsif ($cmdaction eq 'listusers') {
|
195
|
print `$cgi/users.cgi -a tablelist`, "\n" if ($isadmin);
|
196
|
} else {
|
197
|
print "Unknown command\n";
|
198
|
}
|
199
|
} else {
|
200
|
|
201
|
## Servers, images, networks, nodes
|
202
|
if ($cmdpath eq '/servers' || $cmdpath eq '/images' || $cmdpath eq '/networks' || $cmdpath eq '/nodes'
|
203
|
|| $cmdpath eq '/users' || $cmdpath eq '/systems') {
|
204
|
|
205
|
my $path = $cmdpath;
|
206
|
|
207
|
my $parms = '';
|
208
|
if ($params[0] eq '-f' || $params[1] eq '-f') {$parms = '-f';};
|
209
|
if ($params[0] && !($params[0] =~ /^-\w/)) {$parms .= " -m $params[0]";};
|
210
|
if ($params[1] && !($params[1] =~ /^-\w/)) {$parms .= " -m $params[1]";};
|
211
|
if (($cmdaction eq 'list' || $cmdaction eq 'ls') && $cmdobj eq '/') {
|
212
|
print "Firing: $cgi$path.cgi -a tablelist $parms\n" if ($debug);
|
213
|
print `$cgi$path.cgi -a tablelist $parms`, "\n";
|
214
|
} elsif (($cmdaction eq 'listall' && $cmdpath eq '/images') && $cmdobj eq '/') {
|
215
|
print "Firing: $cgi$path.cgi -a tablelistall $parms\n" if ($debug);
|
216
|
print `$cgi$path.cgi -a tablelistall $parms`, "\n";
|
217
|
} elsif ($cmdaction eq 'longlist' || $cmdaction eq 'll') {
|
218
|
print `$cgi$path.cgi -a list $parms`, "\n" unless ($cmdobj ne '/');
|
219
|
} elsif ($cmdaction eq 'help') {
|
220
|
if ($cmdobj eq '/') {
|
221
|
my $res = `$cgi$path.cgi -a help`;
|
222
|
chomp $res;
|
223
|
print "\n$res\n\n";
|
224
|
} else {
|
225
|
if ($path eq '/') {
|
226
|
print $objhelp{$path};
|
227
|
} else {
|
228
|
my $res = `$cgi$path.cgi -a plainhelp`;
|
229
|
chomp $res;
|
230
|
print "\n$res\n\n";
|
231
|
}
|
232
|
};
|
233
|
} elsif ($cmdaction eq 'listbackups' && $path eq '/images' && $cmdobj ne '/') {
|
234
|
my $uuid = substr($cmdobj,1);
|
235
|
my $res = `$cgi$path.cgi -a listbackups -u $uuid`;
|
236
|
chomp $res;
|
237
|
print "\n$res\n\n";
|
238
|
} elsif ($cmdaction =~ /updateallbtimes|removeuserimages/ && $path eq '/images' && $cmdobj eq '/') {
|
239
|
my $res = `$cgi$path.cgi -a $cmdaction`;
|
240
|
chomp $res;
|
241
|
print "\n$res\n\n";
|
242
|
# } elsif ($cmdaction =~ /deactivate|deleteentirely/ && $path eq '/users' && $cmdobj eq '/') {
|
243
|
# my $res = `$cgi$path.cgi -a $cmdaction`;
|
244
|
# chomp $res;
|
245
|
# print "\n$res\n\n";
|
246
|
} elsif ($cmdaction =~ /removeuserservers|destroyuserservers/ && $path eq '/servers' && $cmdobj eq '/') {
|
247
|
my $res = `$cgi$path.cgi -a $cmdaction`;
|
248
|
chomp $res;
|
249
|
print "\n$res\n\n";
|
250
|
} elsif ($cmdaction =~ /removeusersystems/ && $path eq '/systems' && $cmdobj eq '/') {
|
251
|
my $res = `$cgi$path.cgi -a $cmdaction`;
|
252
|
chomp $res;
|
253
|
print "\n$res\n\n";
|
254
|
} elsif ($cmdaction =~ /removeusernetworks/ && $path eq '/networks' && $cmdobj eq '/') {
|
255
|
my $res = `$cgi$path.cgi -a $cmdaction`;
|
256
|
chomp $res;
|
257
|
print "\n$res\n\n";
|
258
|
} elsif ($cmdaction =~ /^monitors/ && $path eq '/systems') {
|
259
|
my $uuid = substr($cmdobj,1);
|
260
|
my $cgicmd = "$cgi$path.cgi -a monitors" . ($uuid?" -u $uuid":"") . " $parms";
|
261
|
my $res = `$cgicmd`;
|
262
|
print "Firing: $cgicmd\n" if ($debug);
|
263
|
chomp $res;
|
264
|
print "\n$res\n\n";
|
265
|
} elsif ($cmdaction =~ /^billengine/ && $path eq '/users') {
|
266
|
my $cgicmd = "$cgi$path.cgi -a billengine $parms";
|
267
|
my $res = `$cgicmd`;
|
268
|
print "Firing: $cgicmd\n" if ($debug);
|
269
|
chomp $res;
|
270
|
print "\n$res\n\n";
|
271
|
} elsif (($cmdaction eq 'listfiles' || $cmdaction eq 'lf') && $path eq '/images' && $cmdobj ne '/') {
|
272
|
my $uuid = substr($cmdobj,1);
|
273
|
my $esc_paramstxt = uri_escape($paramstxt);
|
274
|
print "Firing: $cgi$path.cgi -a listfiles -u $uuid path=$esc_paramstxt" if ($debug);
|
275
|
my $res = `$cgi$path.cgi -a listfiles -u $uuid path=$esc_paramstxt`;
|
276
|
chomp $res;
|
277
|
print "\n$res\n\n";
|
278
|
} elsif ($cmdaction eq 'listpackages' && $path eq '/images' && $cmdobj ne '/') {
|
279
|
my $uuid = substr($cmdobj,1);
|
280
|
print "Firing: $cgi$path.cgi -a listpackages -u $uuid" if ($debug);
|
281
|
my $res = `$cgi$path.cgi -a listpackages -u $uuid`;
|
282
|
chomp $res;
|
283
|
print "\n$res\n\n";
|
284
|
} elsif (($cmdaction eq 'restorefiles' || $cmdaction eq 'rf') && $path eq '/images' && $cmdobj ne '/') {
|
285
|
my $uuid = substr($cmdobj,1);
|
286
|
my $esc_paramstxt = uri_escape($paramstxt);
|
287
|
print "Firing: $cgi$path.cgi -a restorefiles -u $uuid files=$esc_paramstxt" if ($debug);
|
288
|
my $res = `$cgi$path.cgi -a restorefiles -u $uuid files=$esc_paramstxt`;
|
289
|
chomp $res;
|
290
|
print "\n$res\n\n";
|
291
|
} elsif ($cmdaction eq 'show' || $cmdaction eq 'list' || $cmdaction eq 'ls') {
|
292
|
if ($cmdobj ne '/') {
|
293
|
my $uuid = substr($cmdobj,1);
|
294
|
print "Firing: $cgi$path.cgi -a uuidshow -u $uuid\n" if ($debug);
|
295
|
my $fcmd = qq[$cgi$path.cgi -a uuidshow -u $uuid];
|
296
|
$fcmd = $1 if ($fcmd =~ /(.*)/);
|
297
|
print `$fcmd`, "\n";
|
298
|
}
|
299
|
} elsif ($cmdaction || @params) {
|
300
|
if ($cmdobj ne '/' || (($cmdaction =~ /^new (.+)/ || $cmdaction eq 'new') && $cmdobj eq '/')) {
|
301
|
#Object operation
|
302
|
my $postdata;
|
303
|
my $uuid = substr($cmdobj,1);
|
304
|
my $actionstr;
|
305
|
if ($cmdaction eq 'new') { # New object
|
306
|
$actionstr .= qq{"action": "save", };
|
307
|
foreach my $action (@params) {
|
308
|
if ($action =~ /^(\w+)=(.+)/) {
|
309
|
$actionstr .= qq{"$1": "$2", };
|
310
|
}
|
311
|
}
|
312
|
$actionstr .= qq{"status": "new"};
|
313
|
} else { # Perform action, possibly on object
|
314
|
my $caction = $cmdaction || 'save';
|
315
|
$actionstr .= qq{"action": "$caction", };
|
316
|
foreach my $action (@params) {
|
317
|
if ($action =~ /^(\w+)=(.+)/) {
|
318
|
$actionstr .= qq{"$1": "$2", };
|
319
|
}
|
320
|
}
|
321
|
my $identifier = 'uuid';
|
322
|
if ($path eq '/nodes') {$identifier = 'mac';}
|
323
|
elsif ($path eq '/users') {$identifier = 'username';};
|
324
|
$actionstr .= qq{"$identifier": "$uuid"};
|
325
|
}
|
326
|
$postdata = uri_escape(
|
327
|
# qq/{"identifier": "uuid", "label": "uuid", "items":[{$actionstr, "console": 1}]}/
|
328
|
qq/[{$actionstr, "console": 1}]/
|
329
|
) unless $postdata;
|
330
|
print "Firing: REQUEST_METHOD=post $cgi$path.cgi -k ". uri_unescape($postdata) . "\n" if ($debug);
|
331
|
print "$postdata\n" if ($debug);
|
332
|
my $res = `REQUEST_METHOD=post $cgi$path.cgi -k $postdata`;
|
333
|
my $outres;
|
334
|
chomp $res;
|
335
|
for (split /^/, $res) {
|
336
|
if ($_ =~ /\w+=OK (.+)/i || $_ =~ /\w+=ERROR (.+)/i) {$outres .= "$1\n"};
|
337
|
}
|
338
|
print "$outres\n";
|
339
|
} else {
|
340
|
#Object selection
|
341
|
print "Firing: $cgi$path.cgi -a uuidlookup -u $cmdaction\n" if ($debug);
|
342
|
my $res = `$cgi$path.cgi -a uuidlookup -u $cmdaction`;
|
343
|
chomp $res;
|
344
|
if ($res || $res eq '0') {
|
345
|
if ($user) {
|
346
|
$cmdobj = "/$res";
|
347
|
} else {
|
348
|
if (isadmin || $uuser eq "irigo-$res") {
|
349
|
$user = "$res";
|
350
|
$ENV{'REMOTE_USER'} = $user;
|
351
|
}
|
352
|
}
|
353
|
} else { # No object found - treat $cmdaction as action (instead of uuid)
|
354
|
print "Firing: $cgi$path.cgi -a $cmdaction\n" if ($debug);
|
355
|
my $res = `$cgi$path.cgi -a $cmdaction`;
|
356
|
my $outres;
|
357
|
chomp $res;
|
358
|
#print "Got: $res\n" if ($debug);
|
359
|
for (split /^/, $res) {
|
360
|
if ($_ =~ /\w+=OK (.+)/i || $_ =~ /\w+=ERROR (.+)/i) {$outres .= "$1\n"}
|
361
|
else {$outres .= $_;};
|
362
|
}
|
363
|
print "$outres\n";
|
364
|
}
|
365
|
}
|
366
|
}
|
367
|
} else {
|
368
|
print "Unknown command!\n";
|
369
|
}
|
370
|
}
|
371
|
}
|
372
|
print "\n";
|
373
|
|
374
|
} catch Error with {
|
375
|
my $ex = shift;
|
376
|
if ($ex->{-text}) {
|
377
|
print $ex->{-text}, "\n";
|
378
|
} else {
|
379
|
print "Stream=ERROR\n";
|
380
|
}
|
381
|
} finally {
|
382
|
};
|
383
|
|
384
|
sub cleanup {
|
385
|
print "Unmounting all images\n";
|
386
|
my $res = `$cgi/images.cgi -a unmountall`;
|
387
|
print $res if ($debug);
|
388
|
print "Thanks for now\n";
|
389
|
exit(1);
|
390
|
}
|
391
|
|
392
|
sub inithelp {
|
393
|
|
394
|
$cmdhelp{'/'} = <<END
|
395
|
Available commands:
|
396
|
list
|
397
|
"path"
|
398
|
setuser "user"
|
399
|
END
|
400
|
;
|
401
|
|
402
|
$cmdhelp{'/servers'} = <<END
|
403
|
Available commands:
|
404
|
..
|
405
|
list
|
406
|
"object uuid"
|
407
|
END
|
408
|
;
|
409
|
|
410
|
$cmdhelp{'/images'} = <<END
|
411
|
Available commands:
|
412
|
..
|
413
|
list
|
414
|
"object uuid"
|
415
|
END
|
416
|
;
|
417
|
|
418
|
$cmdhelp{'/networks'} = <<END
|
419
|
Available commands:
|
420
|
..
|
421
|
list
|
422
|
"object uuid"
|
423
|
END
|
424
|
;
|
425
|
|
426
|
$cmdhelp{'/users'} = <<END
|
427
|
Available commands:
|
428
|
..
|
429
|
list
|
430
|
billing
|
431
|
"object uuid"
|
432
|
END
|
433
|
;
|
434
|
|
435
|
|
436
|
my $t2 = Text::SimpleTable->new(36);
|
437
|
$t2->row('Path');
|
438
|
$t2->hr;
|
439
|
$t2->row('systems');
|
440
|
$t2->row('servers');
|
441
|
$t2->row('images');
|
442
|
$t2->row('networks');
|
443
|
$t2->row('nodes') if ($isadmin);
|
444
|
$t2->row('users') if ($isadmin);
|
445
|
$objhelp{'/'} = $t2->draw;
|
446
|
|
447
|
|
448
|
#$objhelp{'/'} = <<END
|
449
|
#servers
|
450
|
#images
|
451
|
#networks
|
452
|
#END
|
453
|
#;
|
454
|
|
455
|
#$objhelp{'/'} .= "nodes\n" if ($isadmin);
|
456
|
|
457
|
$objhelp{'/networks'} = <<END
|
458
|
Available commands:
|
459
|
..
|
460
|
show
|
461
|
END
|
462
|
;
|
463
|
|
464
|
|
465
|
}
|