Project

General

Profile

Download (151 KB) Statistics
| Branch: | Revision:
1 95b003ff Origo
#!/usr/bin/perl
2
3
# All rights reserved and Copyright (c) 2020 Origo Systems ApS.
4
# This file is provided with no warranty, and is subject to the terms and conditions defined in the license file LICENSE.md.
5
# The license file is part of this source code package and its content is also available at:
6
# https://www.origo.io/info/stabiledocs/licensing/stabile-open-source-license
7
8
package Stabile::Systems;
9
10
use Webmin::API;
11
use File::Basename;
12
use lib dirname (__FILE__);
13
use Stabile;
14
use Error qw(:try);
15
use String::Escape qw( unbackslash backslash );
16
use Config::Simple;
17
use Time::Local;
18
use Mon::Client;
19
use File::Glob qw(bsd_glob);
20
use POSIX;
21
use Proc::Daemon;
22
use Data::UUID;
23
use LWP::Simple qw(!head);
24
use MIME::Lite;
25
use RRDTool::OO;
26
use Text::CSV_XS qw( csv );
27
28
my $cfg = new Config::Simple("/etc/stabile/config.cfg");
29
30
my $engineid = $Stabile::config->get('ENGINEID') || "";
31
my $enginename = $Stabile::config->get('ENGINENAME') || "";
32
my $doxmpp = $Stabile::config->get('DO_XMPP') || "";
33
my $disablesnat = $Stabile::config->get('DISABLE_SNAT') || "";
34 2a63870a Christian Orellana
my ($datanic, $extnic) = $main::getNics->();
35 95b003ff Origo
my $extiprangestart = $Stabile::config->get('EXTERNAL_IP_RANGE_START');
36
my $extiprangeend = $Stabile::config->get('EXTERNAL_IP_RANGE_END');
37
38
if (!$Stabile::Servers::q && !$Stabile::Images::q  && !$Stabile::Networks::q && !$Stabile::Users::q && !$Stabile::Nodes::q) { # We are not being called from another script
39
    $q = new CGI;
40
    my %cgiparams = $q->Vars;
41
    %params = %cgiparams if (%cgiparams);
42
} else {
43
    $console = 1;
44
}
45
46
my %ahash; # A hash of accounts and associated privileges current user has access to
47
$uiuuid;
48
$uistatus;
49
$help = 0; # If this is set, functions output help
50
51
our %ahash; # A hash of accounts and associated privileges current user has access to
52
#our %options=();
53
# -a action -h help -u uuid -m match pattern -f full list, i.e. all users
54
# -v verbose, include HTTP headers -s impersonate subaccount -t target [uuid or image]
55
# -g args to gearman task
56
#Getopt::Std::getopts("a:hfu:g:m:vs:t:", \%options);
57
58
try {
59
    Init(); # Perform various initalization tasks
60
    process() if ($package);
61
62
} catch Error with {
63
	my $ex = shift;
64
    print header('text/html', '500 Internal Server Error') unless ($console);
65
	if ($ex->{-text}) {
66
        print "Got error $package: ", $ex->{-text}, " on line ", $ex->{-line}, "\n";
67
	} else {
68
	    print "Status=ERROR\n";
69
	}
70
} finally {
71
};
72
73
1;
74
75
sub getObj {
76
    my %h = %{@_[0]};
77
    $console = 1 if $obj->{"console"};
78
    my $obj;
79
    $action =  $action || $h{'action'};
80
    if ($action =~ /updateaccountinfo|monitors|listuptime|buildsystem|removeusersystems|updateengineinfo|^register$|^packages$/) {
81
        $obj = \%h;
82
        $obj->{domuuid} = $curdomuuid if ($curdomuuid);
83
    } else {
84
        my $uuid =$h{"uuid"} || $curuuid;
85
        $uuid = $curuuid if ($uuid eq 'this');
86
        my $status = $h{"status"};
87
        if ((!$uuid && $uuid ne '0') && (!$status || $status eq 'new')) {
88
            my $ug = new Data::UUID;
89
            $uuid = $ug->create_str();
90
            $status = 'new';
91
        };
92
        return 0 unless ($uuid && length $uuid == 36);
93
94
        $obj = {uuid => $uuid};
95
        my @props = qw(uuid name  user  notes  created  opemail  opfullname  opphone  email  fullname  phone  services
96
            recovery  alertemail  image  networkuuid1  internalip autostart issystem system systemstatus from to
97 d3d1a2d4 Origo
            appid callback installsystem installaccount networkuuids);
98 95b003ff Origo
        if ($register{$uuid}) {
99
            foreach my $prop (@props) {
100
                my $val = $h{$prop} || $register{$uuid}->{$prop};
101
                $obj->{$prop} = $val if ($val);
102
            }
103
        } else {
104
            foreach my $prop (@props) {
105
                my $val = $h{$prop};
106
                $obj->{$prop} = $val if ($val);
107
            }
108
        }
109
    }
110
    return $obj;
111
}
112
113
sub Init {
114
    unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username', CLOBBER=>1}, $Stabile::dbopts)) ) {$posterror = "Unable to access user register"; return;};
115
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {$posterror = "Unable to access domain register"; return;};
116
    unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {$posterror = "Unable to access network register"; return;};
117
    unless ( tie(%register,'Tie::DBI', Hash::Merge::merge({table=>'systems'}, $Stabile::dbopts)) ) {$posterror = "Unable to access system register"; return;};
118
119
    $cursysuuid = $domreg{$curuuid}->{'system'}if ($domreg{$curuuid});
120
    $tktuser = $tktuser || $Stabile::tktuser;
121
    $user = $user || $Stabile::user;
122
123
    *Deletesystem = \&Removesystem;
124
    *Backup = \&systemAction;
125
126
    *do_help = \&action;
127
    *do_tablelist = \&do_list;
128
    *do_arraylist = \&do_list;
129
    *do_flatlist = \&do_list;
130
    *do_monitors = \&privileged_action;
131
    *do_suspend = \&systemAction;
132
    *do_resume = \&systemAction;
133
    *do_shutdown = \&systemAction;
134
    *do_destroy = \&systemAction;
135
    *do_start = \&systemAction;
136
    *do_backup = \&privileged_action;
137
    *do_packages_load = \&privileged_action;
138
    *do_monitors_save = \&privileged_action;
139
    *do_monitors_remove = \&privileged_action;
140
    *do_monitors_enable = \&privileged_action;
141
    *do_monitors_disable = \&privileged_action;
142
    *do_monitors_acknowledge = \&privileged_action;
143
    *do_save = \&privileged_action;
144
    *do_changemonitoremail = \&privileged_action;
145
    *do_buildsystem = \&privileged_action;
146
    *do_removesystem = \&privileged_action;
147
    *do_deletesystem = \&privileged_action;
148
    *do_removeusersystems = \&privileged_action;
149
    *do_updateengineinfo = \&privileged_action;
150
151
    *do_gear_backup = \&do_gear_action;
152
    *do_gear_packages_load = \&do_gear_action;
153
    *do_gear_monitors = \&do_gear_action;
154
    *do_gear_monitors_enable = \&do_gear_action;
155
    *do_gear_monitors_save = \&do_gear_action;
156
    *do_gear_monitors_remove = \&do_gear_action;
157
    *do_gear_monitors_disable = \&do_gear_action;
158
    *do_gear_monitors_acknowledge = \&do_gear_action;
159
    *do_gear_save = \&do_gear_action;
160
    *do_gear_changemonitoremail = \&do_gear_action;
161
    *do_gear_buildsystem = \&do_gear_action;
162
    *do_gear_removesystem = \&do_gear_action;
163
    *do_gear_deletesystem = \&do_gear_action;
164
    *do_gear_removeusersystems = \&do_gear_action;
165
    *do_gear_updateengineinfo = \&do_gear_action;
166
    *Monitors_remove = \&Monitors_save;
167
    *Monitors_enable = \&Monitors_action;
168
    *Monitors_disable = \&Monitors_action;
169
    *Monitors_acknowledge = \&Monitors_action;
170
}
171
172
sub do_uuidlookup {
173
    if ($help) {
174
        return <<END
175
GET:uuid:
176
Simple action for looking up a uuid or part of a uuid and returning the complete uuid.
177
END
178
    }
179
    my $res;
180
    $res .= header('text/plain') unless $console;
181
    my $u = $options{u};
182
    $u = $curuuid unless ($u || $u eq '0');
183
    my $ruuid;
184
    if ($u || $u eq '0') {
185
        my $match;
186
        foreach my $uuid (keys %register) {
187
            if ($uuid =~ /^$u/) {
188
                $ruuid = $uuid if ($register{$uuid}->{'user'} eq $user || index($privileges,"a")!=-1);
189
                $match = 1;
190
                last;
191
            }
192
        }
193
        unless ($match) {
194
            foreach my $uuid (keys %domreg) {
195
                if ($uuid =~ /^$u/) {
196
                    $ruuid = $uuid if ((!$domreg{$uuid}->{'system'} || $domreg{$uuid}->{'system'} eq '--' )&&  ($domreg{$uuid}->{'user'} eq $user || index($privileges,"a")!=-1));
197
                    last;
198
                }
199
            }
200
        }
201
    }
202
    $res .= "$ruuid\n" if ($ruuid);
203
    return $res;
204
}
205
206
sub do_uuidshow {
207
    if ($help) {
208
        return <<END
209
GET:uuid:
210
Simple action for showing a single system.
211
END
212
    }
213
    my $res;
214
    $res .= header('application/json') unless $console;
215
    my $u = $options{u};
216
    $u = $curuuid unless ($u || $u eq '0');
217
    if ($u) {
218
        foreach my $uuid (keys %register) {
219
            if (($register{$uuid}->{'user'} eq $user || $register{$uuid}->{'user'} eq 'common' || index($privileges,"a")!=-1)
220
                && $uuid =~ /^$u/) {
221
                my %hash = %{$register{$uuid}};
222
                delete $hash{'action'};
223
                delete $hash{'nextid'};
224
                my $dump = to_json(\%hash, {pretty=>1});
225
                $dump =~ s/undef/"--"/g;
226
                $res .= $dump;
227
                last;
228
            }
229
        }
230
    }
231
    return $res;
232
}
233
234
sub do_list {
235
    my ($uuid, $action, $obj) = @_;
236
    if ($help) {
237
        return <<END
238 8d7785ff Origo
GET:uuid:
239 95b003ff Origo
List systems current user has access to.
240
END
241
    }
242
    my $sysuuid;
243
    if ($uripath =~ /systems(\.cgi)?\/(\?|)(this)/) {
244
        $sysuuid = $cursysuuid || $curuuid;
245
    } elsif ($uripath =~ /systems(\.cgi)?\/(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})/) {
246
        $sysuuid = $2;
247
    } elsif ($params{'system'}) {
248
        $sysuuid = $obj->{'system'};
249
        $sysuuid = $cursysuuid || $curuuid if ($obj->{system} eq 'this');
250
    }
251
    $postreply = getSystemsListing($action, $uuid);
252
    return $postreply;
253
}
254
255
sub Monitors_action {
256
    my ($uuid, $action, $obj) = @_;
257
    if ($help) {
258
        return <<END
259
GET:id:
260
Enable, disable or acknowledge a monitor. Id is of the form serveruuid:service
261
END
262
    }
263
    my $monitor_action = "enable";
264
    $monitor_action = "disable" if ($action eq 'monitors_disable');
265
    $monitor_action = "acknowledge" if ($action eq 'monitors_acknowledge');
266
    my $log_action = uc $monitor_action;
267
    my $group;
268
    my $service;
269
    my $logline;
270
    if ($uuid =~ /(.+):(.+)/) {
271
        $group = $1;
272
        $service = $2;
273
    }
274
    if ($group && $service) {
275
        my $reguser = $domreg{$group}->{'user'};
276
        # Security check
277
        if ($user eq $reguser || index($privileges,"a")!=-1) {
278
            my $oplogfile = "/var/log/stabile/$year-$month:$group:$service";
279
            unless (-e $oplogfile) {
280
                `/usr/bin/touch "$oplogfile"`;
281
                `/bin/chown mon:mon "$oplogfile"`;
282
            }
283
            if ($monitor_action =~ /enable|disable/) {
284
                my $res = `/usr/bin/moncmd $monitor_action service $group $service`;
285
                chomp $res;
286
                $logline = "$current_time, $log_action, , $pretty_time";
287
            } elsif ($monitor_action eq "acknowledge") {
288
                my $ackcomment = $obj->{"ackcomment"};
289
                # my $ackcomment = backslash( $obj->{"ackcomment"} );
290
                #$ackcomment =~ s/ /\\\20/g;
291
                my $monc = new Mon::Client (
292
                    host => "127.0.0.1"
293
                );
294
                $ackcomment = ($ackcomment)?"$user, $ackcomment":$user;
295
                $monc->connect();
296
                $monc->ack($group, $service, $ackcomment);
297
                $monc->disconnect();
298
                $logline = "$current_time, ACKNOWLEDGE, $ackcomment, $pretty_time";
299
                my %emails;
300
                my @emaillist = split(/\n/, `/bin/cat /etc/mon/mon.cf`);
301
                my $emailuuid;
302
                foreach my $eline (@emaillist) {
303
                    my ($a, $b, $c, $d) = split(/ +/, $eline);
304
                    if ($a eq 'watch') {
305
                        if ($b =~ /\S+-\S+-\S+-\S+-\S+/) {$emailuuid = $b;}
306
                        else {$emailuuid = ''};
307
                    }
308
                    $emails{$emailuuid} = $d if ($emailuuid && $b eq 'alert' && $c eq 'stabile.alert');
309
                };
310
                my $email = $emails{$group};
311
                my $servername = $domreg{$group}->{'name'};
312
                my $serveruser = $domreg{$group}->{'user'};
313
                if ($email) {
314
                    my $mailtext = <<EOF;
315
Acknowledged by: $user
316
Server name: $servername
317
Server UUID: $group
318
System UUID: $sysuuid
319
Server user: $serveruser
320
Service: $service
321
EOF
322
                    ;
323
324
                    my $mailhtml = <<END;
325
<!DOCTYPE html
326
    PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
327
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
328
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
329
    <head>
330
        <title>Problems with $servername:$service are being handled</title>
331
        <meta http-equiv="Pragma" content="no-cache" />
332
		<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" />
333
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
334
    </head>
335
    <body class="tundra">
336
        <div>
337
            <div class="well" style="margin:20px;">
338
                <h3 style="color: #2980b9!important; margin-bottom:30px;">Relax, the problems with your service are being handled!</h3>
339
                <div>The problems with the service <strong>$service</strong> on the server <strong>$servername</strong> running on <strong>$enginename</strong> have been acknowledged at $pretty_time and are being handled by <strong>$tktuser ($user)</strong>.</div>
340
                <br>
341
                <div>Thanks,<br>your friendly monitoring daemon</div>
342
            </div>
343
        </div>
344
    </body>
345
</html>
346
END
347
                    ;
348
349
                    my $xmpptext = "ACK: $servername:$service is being handled ($pretty_time)\n";
350
                    $xmpptext .= "Acknowledged by: $tktuser ($user)\n";
351
352
                    my $msg = MIME::Lite->new(
353
                        From     => 'monitoring',
354
                        To       => $email,
355
                        Type     => 'multipart/alternative',
356
                        Subject  => "ACK: $servername:$service is being handled ($pretty_time)",
357
                    );
358
                    $msg->add("sysuuid" => $sysuuid);
359
360
                    my $att_text = MIME::Lite->new(
361
                        Type     => 'text',
362
                        Data     => $mailtext,
363
                        Encoding => 'quoted-printable',
364
                    );
365
                    $att_text->attr('content-type'
366
                        => 'text/plain; charset=UTF-8');
367
                    $msg->attach($att_text);
368
369
                    my $att_html = MIME::Lite->new(
370
                        Type     => 'text',
371
                        Data     => $mailhtml,
372
                        Encoding => 'quoted-printable',
373
                    );
374
                    $att_html->attr('content-type'
375
                        => 'text/html; charset=UTF-8');
376
                    $msg->attach($att_html);
377
378
                    $msg->send;
379
380
                    if ($doxmpp) {
381
                        foreach my $to (split /, */, $email) {
382
                            my $xres = $main::xmppSend->($to, $xmpptext, $engineid, $sysuuid);
383
                        }
384
                        # Send alerts to Origo operators on duty
385
                        my $oponduty = 'operator@sa.origo.io';
386
                        $msg->replace('to', $oponduty);
387
                        $msg->send;
388
                        my $xres = $main::xmppSend->($oponduty, $xmpptext, $engineid, $sysuuid);
389
                    }
390
                }
391
            }
392
            `/bin/echo >> $oplogfile "$logline"`;
393
            $postreply .= "Status=OK OK $monitor_action"." $service service\n";
394
        }
395
    } else {
396
        $postreply = "Status=Error problem $monitor_action monitor $uuid\n";
397
    }
398
    return $postreply;
399
}
400
401
sub do_register {
402
    my ($uuid, $action, $obj) = @_;
403
    if ($help) {
404
        return <<END
405
GET:uuid,format:
406
Print software register for server or system of servers with given uuid. Format is html, csv or json (default).
407
END
408
    }
409
410
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
411
    my @domregvalues = values %domreg;
412
    my %reghash;
413
    foreach my $valref (@domregvalues) {
414
        if ($valref->{'user'} eq $user || $fulllist) {
415
            if (!$uuid || $uuid eq '*' || $uuid eq $valref->{'uuid'} || $uuid eq $valref->{'system'}) {
416
                my $os = $valref->{'os'} || 'unknown';
417
                my $domname = $valref->{'name'};
418
                utf8::decode($domname);
419
                if ($reghash{$os}) {
420
                    $reghash{ $os . '-' . $reghash{$os}->{'oscount'} } = {
421
                        os=>'',
422
                        sortos=>$os."*",
423
                        user=>$valref->{'user'},
424
                        name=>$domname,
425
                        hostname=>$valref->{'hostname'}
426
                    };
427
                    $reghash{$os}->{'oscount'}++;
428
                } else {
429
                    $reghash{$os} = {
430
                        os=>$os,
431
                        sortos=>$os,
432
                        user=>$valref->{'user'},
433
                        name=>$domname,
434
                        hostname=>$valref->{'hostname'},
435
                        oscount=>1
436
                    }
437
                }
438
            }
439
        }
440
441
    }
442
    untie %domreg;
443
    my @sorted_oslist = sort {$a->{'sortos'} cmp $b->{'sortos'}} values %reghash;
444
    if ($obj->{'format'} eq 'html') {
445
        my $res;
446
        $res .= qq[<tr><th>OS</th><th>Name</th><th>Hostname</th><th>Count</th></tr>];
447
        foreach my $valref (@sorted_oslist) {
448
            $res .= qq[<tr><td>$valref->{'os'}</td><td>$valref->{'name'}</td><td>$valref->{'hostname'}</td><td>$valref->{'oscount'}</td></tr>];
449
        }
450
        $postreply = header();
451
        $postreply .= qq[<table cellspacing="0" frame="void" rules="rows" class="systemTables">$res</table>];
452
    } elsif ($obj->{'format'} eq 'csv') {
453
        $postreply = header("text/plain");
454
        csv(in => \@sorted_oslist, out => \my $csvdata);
455
        $postreply .= $csvdata;
456
    } else {
457
        $postreply .= to_json(\@sorted_oslist);
458
    }
459
    return $postreply;
460
461
}
462
463
sub Monitors {
464
    my ($uuid, $action, $obj) = @_;
465
    if ($help) {
466
        return <<END
467
GET:uuid:
468
Handling of monitors
469
END
470
    }
471
# We are dealing with a POST request, i.e. an action on a monitor
472
# or a PUT or DELETE request, i.e. creating/saving/deleting items
473
    if (($ENV{'REQUEST_METHOD'} eq 'DELETE' || $params{"PUTDATA"} || $ENV{'REQUEST_METHOD'} eq 'PUT' || $ENV{'REQUEST_METHOD'} eq 'POST') && !$isreadonly) {
474
        my @json_array;
475
        my %json_hash;
476
        my $delete;
477
        if ($ENV{'REQUEST_METHOD'} eq 'DELETE' && $uripath =~ /action=monitors\/(.+):(.+)/) {
478
            print header('text/json', '204 No Content') unless $console;
479
            %json_hash = ('serveruuid', $1, 'service', $2);
480
            @json_array = (\%json_hash);
481
            $delete = 1;
482
#            print Monitors_save(\%json_hash, $delete);
483
            print Monitors_save($uuid, "monitors_remove", $obj);
484
        } else {
485
            my $json_text = $params{"PUTDATA"} || $params{'keywords'};
486
            $json_text = encode('latin1', decode('utf8', $json_text));
487
            $json_text =~ s/\x/ /g;
488
            @json_array = from_json($json_text);
489
            $json_hash_ref = @json_array[0];
490
#            my $res = Monitors_save($json_hash_ref, $delete);
491
            my $res = Monitors_save($uuid, "monitors_save", $obj);
492
            if ($res =~ /^{/) {
493
                print header('text/json') unless $console;
494
                print $res;
495
            } else {
496
                print header('text/html', '400 Bad Request') unless $console;
497
                print qq|$res|;
498
            }
499
        }
500
501
# We are dealing with a regular GET request, i.e. a listing
502
    } else {
503
        my $selgroup;
504
        my $selservice;
505
        if ($uuid && $uuid ne '*') { # List all monitors for specific server
506
            $selgroup = $uuid;
507
            if ($uuid =~ /(.+):(.+)/){ # List specific monitor for specific server
508
                $selgroup = $1;
509
                $selservice = $2;
510
            }
511
        }
512
        my $usemoncmd = 0;
513
        my %opstatus = getOpstatus($selgroup, $selservice, $usemoncmd);
514
        my @monitors = values(%opstatus);
515
        my @sorted_monitors = sort {$a->{'opstatus'} cmp $b->{'opstatus'}} @monitors;
516
        my $json_text;
517
        if ($obj->{'listaction'} eq 'show' && scalar @monitors == 1) {
518
            $json_text = to_json($sorted_monitors[0], {pretty => 1});
519
        } else {
520
            $json_text = to_json(\@sorted_monitors, {pretty => 1});
521
        }
522
        utf8::decode($json_text);
523
        $postreply = $json_text;
524
        return $postreply;
525
    }
526
527
}
528
529
sub do_remove {
530
    my ($uuid, $action, $obj) = @_;
531
    if ($help) {
532
        return <<END
533
DELETE:uuid:
534
Delete a system from database and make all member servers free agents.
535
END
536
    }
537
    if ($register{$uuid}) {
538
        unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
539
        my @domregvalues = values %domreg;
540
        my @curregvalues;
541
        foreach my $valref (@domregvalues) {
542
            # Only include VM's belonging to current user (or all users if specified and user is admin)
543
            if ($user eq $valref->{'user'} || $fulllist) {
544
                my $system = $valref->{'system'};
545
                if ($system eq $uuid) {
546
                    $valref->{'system'} = '';
547
                    push(@curregvalues, $valref);
548
                }
549
            }
550
        }
551
        delete $register{$uuid};
552
        tied(%domreg)->commit;
553
        tied(%register)->commit;
554
        untie %domreg;
555
        if ($match) {
556
            $postreply = to_json(@curregvalues);
557
        } else {
558
            $postreply = header('text/plain', '204 No Content') unless $console;
559
        }
560
    }
561
    return $postreply;
562
}
563
564
sub Save {
565
    my ($uuid, $action, $obj) = @_;
566
    if ($help) {
567
        return <<END
568 d3d1a2d4 Origo
PUT:uuid, fullname, email, phone, opfullname, opemail, opphone, alertemail, services, recovery, networkuuids:
569
Save properties for a system. If no uuid is provided, a new stack is created.[networkuuids] is a comma-separated list of networks reserved to this stack for use not associated with specific servers.
570
[networkuuids] is a list of UUIDs of linked network connections, i.e. connections reserved for this system to handle
571
572
        Specify '--' to clear a value.
573 95b003ff Origo
END
574
    }
575
    my $name = $obj->{"name"} || $register{$uuid}->{'name'};
576
    my $reguser;
577
    $reguser = $register{$uuid}->{'user'} if ($register{$uuid});
578
    $console = 1 if ($obj->{'console'});
579
    my $issystem = $obj->{'issystem'} || $register{$uuid};
580
    my $notes = $obj->{"notes"};
581
    my $email = $obj->{'email'};
582
    my $fullname = $obj->{'fullname'};
583
    my $phone = $obj->{'phone'};
584
    my $opemail = $obj->{'opemail'};
585
    my $opfullname = $obj->{'opfullname'};
586
    my $opphone = $obj->{'opphone'};
587
    my $alertemail = $obj->{'alertemail'};
588
    my $services = $obj->{'services'};
589
    my $recovery = $obj->{'recovery'};
590 d3d1a2d4 Origo
    my $networkuuids = $obj->{'networkuuids'};
591 c899e439 Origo
    my $autostart = $obj->{'autostart'};
592 95b003ff Origo
593
    if ((!$uuid)) {
594
        my $ug = new Data::UUID;
595
        $uuid = $ug->create_str();
596
        $issystem = 1;
597
    };
598
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Status=Error Unable to access domain register"};
599
    unless ($register{$uuid} || $domreg{$uuid}) {
600
        $obj->{'status'} = 'new';
601
        $issystem = 1;
602
    }
603
    $issystem = 1 if ($register{$uuid});
604
    unless (($uuid && length $uuid == 36)) {
605
        $postreply = "Status=Error Invalid UUID\n";
606
        return $postreply;
607
    }
608
609
    # Sanity checks
610
    if ($name && length $name > 255) {
611
        $postreply .= "Status=Error Bad data: $name " . (length $name) . "\n";
612
        return $postreply;
613
    };
614
615
    if ($issystem) { # We are dealing with a system
616
        # Security check
617
        if (($user eq $reguser || $isadmin) && $register{$uuid}) { # Existing system
618
            my @props = ('name','fullname','email','phone','opfullname','opemail','opphone','alertemail'
619 c899e439 Origo
                ,'notes','services','recovery','autostart');
620 95b003ff Origo
            my %oldvals;
621
            foreach my $prop (@props) {
622
                my $val = $obj->{$prop};
623
                if ($val) {
624
                    $val = '' if ($val eq '--');
625
                    $oldvals{$prop} = $register{$uuid}->{$prop} || $userreg{$user}->{$prop};
626 c899e439 Origo
                    if ($val eq $userreg{$user}->{$prop}) {
627 95b003ff Origo
                        $register{$uuid}->{$prop} = ''; # Same val as parent (user val), reset
628
                    } else {
629
                        $register{$uuid}->{$prop} = $val;
630
                    }
631 c899e439 Origo
                    if ($prop eq 'autostart') {
632
                        $register{$uuid}->{$prop} = ($val)?'1':'';
633
                    }
634 95b003ff Origo
                    if ($prop eq 'name') {
635
                        my $json_text = qq|{"uuid": "$uuid" , "name": "$val"}|;
636
                        $main::postAsyncToOrigo->($engineid, 'updateapps', "[$json_text]");
637
                    }
638
                }
639
            }
640
            my %childrenhash;
641
            my $alertmatch;
642
            foreach my $prop (@props) {
643
                my $val = $obj->{$prop};
644
                if ($val) {
645
                    $val = '' if ($val eq '--');
646
                    # Update children
647
                    foreach my $domvalref (values %domreg) {
648
                        if ($domvalref->{'user'} eq $user && $domvalref->{'system'} eq $uuid) {
649
                            my %domval = %{$domvalref};
650
                            $childrenhash{$domvalref->{'uuid'}} =\%domval unless ($childrenhash{$domvalref->{'uuid'}});
651
                            $childrenhash{$domvalref->{'uuid'}}->{$prop} = $val;
652 c899e439 Origo
                            if ($prop eq 'autostart') {
653
                                $domvalref->{$prop} = ($val)?'1':''; # Always update child servers with autostart prop
654
                            } elsif (!$domvalref->{$prop} || $domvalref->{$prop} eq $oldvals{$prop}) {
655 95b003ff Origo
                                $domvalref->{$prop} = '';
656
                                if ($prop eq 'alertemail') {
657
                                    if (change_monitor_email($domvalref->{'uuid'}, $val, $oldvals{$prop})) {
658
                                        $alertmatch = 1;
659
                                    }
660
                                }
661
                            }
662
                        }
663
                    }
664
                }
665
            }
666
            my @children = values %childrenhash;
667
            $obj->{'children'} = \@children if (@children);
668
            $postreply = getSystemsListing();
669
        } elsif ($obj->{'status'} eq 'new')  { # New system
670
            $register{$uuid} = {
671
                uuid=>$uuid,
672
                name=>$name,
673
                user=>$user,
674
                created=>$current_time
675
            };
676
            my $valref = $register{$uuid};
677
            my %val = %{$valref};
678
            $val{'issystem'} = 1;
679
            $val{'status'} = '--';
680
            $dojson = 1;
681
            $postreply = to_json(\%val, {pretty=>1});
682
        } else {
683
            $postreply .= "Status=Error Not enough privileges: $user\n";
684
        }
685
    } else { # We are dealing with a server
686
        my $valref = $domreg{$uuid};
687
        if (!$valref && $obj->{'uuid'}[0]) {$valref = $domreg{ $obj->{'uuid'}[0] }}; # We are dealing with a newly created server
688
        if ($valref && ($valref->{'user'} eq $user || $isadmin)) {
689
            my $system = $obj->{'system'};
690
            my $servername = $obj->{'name'};
691
            if ($servername && $servername ne $valref->{'name'}) {
692
                $valref->{'name'} = $servername;
693
                # Update status of images
694
                my @imgs = ($domreg{$uuid}->{image}, $domreg{$uuid}->{image2}, $domreg{$uuid}->{image3}, $domreg{$uuid}->{image4});
695
                my @imgkeys = ('image', 'image2', 'image3', 'image4');
696
                unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {return "Status=Error Unable to access image register"};
697
                for (my $i=0; $i<4; $i++) {
698
                    my $img = $imgs[$i];
699
                    my $k = $imgkeys[$i];
700
                    if ($img && $img ne '--') {
701
                        $imagereg{$img}->{'domains'} = $uuid;
702
                        $imagereg{$img}->{'domainnames'} = $servername;
703
                    }
704
                }
705
                untie %imagereg;
706
                my $json_text = qq|{"uuid": "$uuid" , "name": "$servername"}|;
707
                $main::postAsyncToOrigo->($engineid, 'updateapps', "[$json_text]");
708
            }
709
            $valref->{'system'} = ($system eq '--'?'':$system) if ($system);
710
            $valref->{'notes'} = (($notes eq '--')?'':$notes) if ($notes);
711
            $valref->{'email'} = ($email eq '--'?'':$email) if ($email);
712
            $valref->{'fullname'} = ($fullname eq '--'?'':$fullname) if ($fullname);
713
            $valref->{'phone'} = ($phone eq '--'?'':$phone) if ($phone);
714
            $valref->{'opemail'} = ($opemail eq '--'?'':$opemail) if ($opemail);
715
            $valref->{'opfullname'} = ($opfullname eq '--'?'':$opfullname) if ($opfullname);
716
            $valref->{'opphone'} = ($opphone eq '--'?'':$opphone) if ($opphone);
717
            $valref->{'services'} = ($services eq '--'?'':$services) if ($services);
718
            $valref->{'recovery'} = ($recovery eq '--'?'':$recovery) if ($recovery);
719 c899e439 Origo
            $valref->{'autostart'} = ($autostart && $autostart ne '--'?'1':'');
720 95b003ff Origo
            if ($alertemail) {
721
                $alertemail = '' if ($alertemail eq '--');
722
                if ($valref->{'alertemail'} ne $alertemail) {
723
                    # If alert email is changed, update monitor if it is configured with this email
724
                    if (change_monitor_email($valref->{'uuid'}, $alertemail, $valref->{'alertemail'})){
725
                        $alertmatch = 1;
726
                        #`/usr/bin/moncmd reset keepstate`;
727
                    }
728
                    $valref->{'alertemail'} = $alertemail;
729
                }
730
            }
731
732
            tied(%domreg)->commit;
733
            $postreply = getSystemsListing(); # Hard to see what else to do, than to send entire table
734
        }
735
    }
736 c899e439 Origo
    if ($networkuuids && $networkuuids ne '--') { # link networks to this system
737 d3d1a2d4 Origo
        my @networks = split(/, ?/, $networkuuids);
738
        my @newnetworks = ();
739
        my @newnetworknames = ();
740
        unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {return "Unable to access networks register"};
741
        foreach my $networkuuid (@networks) {
742 c899e439 Origo
            next unless ($networkreg{$networkuuid});
743 d3d1a2d4 Origo
            if (
744
                !$networkreg{$networkuuid}->{'domains'} # a network cannot both be linked and in active use
745
                    && (!$networkreg{$networkuuid}->{'systems'} ||  $networkreg{$networkuuid}->{'systems'} eq $uuid) # check if network is already linked to another system
746
            ) {
747
                $networkreg{$networkuuid}->{'systems'} = $uuid;
748
                $networkreg{$networkuuid}->{'systemnames'} = $name;
749
                push @newnetworks, $networkuuid;
750
                push @newnetworknames, $networkreg{$networkuuid}->{'name'};
751
            }
752
        }
753 c899e439 Origo
        if ($issystem && $register{$uuid}) {
754 d3d1a2d4 Origo
            $register{$uuid}->{'networkuuids'} = join(", ", @newnetworks);
755
            $register{$uuid}->{'networknames'} = join(", ", @newnetworknames);
756 c899e439 Origo
        } elsif ($domreg{$uuid}) {
757 d3d1a2d4 Origo
            $domreg{$uuid}->{'networkuuids'} = join(", ", @newnetworks);
758
            $domreg{$uuid}->{'networknames'} = join(", ", @newnetworknames);
759
        }
760
    }
761 95b003ff Origo
    untie %domreg;
762
    return $postreply;
763
}
764
765
sub do_resettoaccountinfo {
766
    my ($uuid, $action, $obj) = @_;
767
    if ($help) {
768
        return <<END
769
GET::
770
Recursively reset contact data for all systems and servers
771
END
772
    }
773
    my @props = ('fullname','email','phone','opfullname','opemail','opphone','alertemail');
774
    my $alertmatch;
775
    foreach my $sysvalref (values %register) {
776
        if ($user eq $sysvalref->{'user'}) {
777
            my $sysuuid = $sysvalref->{'uuid'};
778
            foreach my $prop (@props) {
779
                # Does this system have a value?
780
                if ($sysvalref->{$prop}) {
781
                    $sysvalref->{$prop} = ''; # An empty val refers to parent (user) val
782
                }
783
            }
784
        }
785
    }
786
    # Update domains
787
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {$posterror = "Unable to access domain register"; return;};
788
    foreach my $domvalref (values %domreg) {
789
        if ($domvalref->{'user'} eq $user) {
790
            foreach my $prop (@props) {
791
                if ($domvalref->{$prop}) {
792
                    $domvalref->{$prop} = '';
793
                }
794
                if ($prop eq 'alertemail') {
795
                    if (change_monitor_email($domvalref->{'uuid'}, $userreg{$user}->{$prop})) {
796
                        $alertmatch = 1;
797
                    }
798
                }
799
            }
800
        }
801
    }
802
    tied(%domreg)->commit;
803
    untie %domreg;
804
    #`/usr/bin/moncmd reset keepstate` if ($alertmatch);
805
    $postreply .= "Status=OK OK - reset systems and servers contacts to account values\n";
806
    return $postreply;
807
}
808
809
sub do_start_server {
810
    my ($uuid, $action, $obj) = @_;
811
    if ($help) {
812
        return <<END
813
GET:uuid:
814
Start specific server.
815
END
816
    }
817
    $Stabile::Servers::console = 1;
818
    require "$Stabile::basedir/cgi/servers.cgi";
819
    $postreply .= Stabile::Servers::Start($uuid, 'start', { buildsystem => 0 });
820
}
821
822
sub systemAction {
823
    my ($uuid, $action, $obj) = @_;
824
    if ($help) {
825
        return <<END
826
GET:uuid:
827
Suspend, resume, start, shutdown, destroy og backup individual servers or servers belonging to a system.
828
END
829
    }
830
    my $issystem = $obj->{'issystem'} || $register{$uuid};
831 91a21c75 hq
    my $reguser;
832
    $reguser = $register{$uuid}->{'user'} if ($register{$uuid});
833 95b003ff Origo
834
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
835
    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;};
836
837
    if ($issystem) { # Existing system
838
        if (($user eq $reguser || $isadmin) && $register{$uuid}){ # Existing system
839
            my $domactions;
840
            my $imageactions;
841
842
            foreach my $domvalref (values %domreg) {
843
                if (($domvalref->{'system'} eq $uuid || $domvalref->{'uuid'} eq $uuid)
844
                    && ($domvalref->{'user'} eq $user || $isadmin)) {
845
846
                    my $domaction;
847
                    my $imageaction;
848
                    if ($domvalref->{'status'} eq 'paused' && ($action eq 'start' || $action eq 'resume')) {
849
                        $domaction = 'resume';
850
                    } elsif ($domvalref->{'status'} eq 'running' && $action eq 'suspend') {
851
                        $domaction = $action;
852
                    } elsif ($domvalref->{'status'} eq 'shutoff' && $action eq 'start') {
853
                        $domaction = $action;
854
                    } elsif ($domvalref->{'status'} eq 'inactive' && $action eq 'start') {
855
                        $domaction = $action;
856
                    } elsif ($domvalref->{'status'} eq 'running' && $action eq 'shutdown') {
857
                        $domaction = $action;
858
                    } elsif ($domvalref->{'status'} eq 'running' && $action eq 'destroy') {
859
                        $domaction = $action;
860
                    } elsif ($domvalref->{'status'} eq 'shuttingdown' && $action eq 'destroy') {
861
                        $domaction = $action;
862
                    } elsif ($domvalref->{'status'} eq 'destroying' && $action eq 'destroy') {
863
                        $domaction = $action;
864
                    } elsif ($domvalref->{'status'} eq 'starting' && $action eq 'destroy') {
865
                        $domaction = $action;
866
                    } elsif ($domvalref->{'status'} eq 'inactive' && $action eq 'destroy') {
867
                        $domaction = $action;
868
                    } elsif ($domvalref->{'status'} eq 'paused' && $action eq 'destroy') {
869
                        $domaction = $action;
870
                    } elsif ($action eq 'backup') {
871
                        $imageaction = $action;
872
                    }
873
                    if ($domaction) {
874
                        $domactions .= qq/{"uuid":"$domvalref->{'uuid'}","action":"$domaction"},/;
875
                    }
876
                    if ($imageaction) {
877
                        my $image = $domvalref->{'image'};
878
                        if ($imagereg{$image}->{'status'} =~ /used|active/) {
879
                            $imageactions .= qq/{"uuid":"$imagereg{$image}->{'uuid'}","action":"backup"},/;
880
                        }
881
                        my $image2 = $domvalref->{'image2'};
882
                        if ($image2 && $image2 ne '--' && $imagereg{$image2}->{'status'} =~ /used|active/) {
883
                            $imageactions .= qq/{"uuid":"$imagereg{$image2}->{'uuid'}","action":"backup"},/;
884
                        }
885
                        my $image3 = $domvalref->{'image3'};
886
                        if ($image3 && $image3 ne '--' && $imagereg{$image3}->{'status'} =~ /used|active/) {
887
                            $imageactions .= qq/{"uuid":"$imagereg{$image3}->{'uuid'}","action":"backup"},/;
888
                        }
889
                        my $image4 = $domvalref->{'image4'};
890
                        if ($image4 && $image4 ne '--' && $imagereg{$image4}->{'status'} =~ /used|active/) {
891
                            $imageactions .= qq/{"uuid":"$imagereg{$image4}->{'uuid'}","action":"backup"},/;
892
                        }
893
                    }
894
                }
895
            }
896
897
            if ($domactions) {
898
                $domactions = substr($domactions,0,-1);
899
                my $uri_action = qq/{"items":[$domactions]}/;
900
                $uri_action = URI::Escape::uri_escape($uri_action);
901
                $uri_action =~ /(.+)/; $uri_action = $1; #untaint
902
                $postreply .= `REMOTE_USER=$user $Stabile::basedir/cgi/servers.cgi -k $uri_action`;
903
            }
904
            if ($imageactions) {
905
                $imageactions = substr($imageactions,0,-1);
906
                my $uri_action = qq/{"items":[$imageactions]}/;
907
                $uri_action = URI::Escape::uri_escape($uri_action);
908
                $uri_action =~ /(.+)/; $uri_action = $1; #untaint
909
                $postreply .= `REMOTE_USER=$user $Stabile::basedir/cgi/images.cgi -k $uri_action`;
910
            }
911
            if (!$domactions && !$imageactions) {
912
                $postreply .= "Stream=ERROR $action";
913
            }
914
        }
915
    } else {
916
        if ($action eq 'backup') {
917
            my $image = $domreg{$uuid}->{'image'};
918
            my $imageactions;
919
            if ($imagereg{$image}->{'status'} =~ /used|active/) {
920
                $imageactions .= qq/{"uuid":"$imagereg{$image}->{'uuid'}","action":"gear_backup"},/;
921
            }
922
            my $image2 = $domreg{$uuid}->{'image2'};
923
            if ($image2 && $image2 ne '--' && $imagereg{$image2}->{'status'} =~ /used|active/) {
924
                $imageactions .= qq/{"uuid":"$imagereg{$image2}->{'uuid'}","action":"gear_backup"},/;
925
            }
926
            my $image3 = $domreg{$uuid}->{'image3'};
927
            if ($image3 && $image3 ne '--' && $imagereg{$image3}->{'status'} =~ /used|active/) {
928
                $imageactions .= qq/{"uuid":"$imagereg{$image3}->{'uuid'}","action":"gear_backup"},/;
929
            }
930
            my $image4 = $domreg{$uuid}->{'image4'};
931
            if ($image4 && $image4 ne '--' && $imagereg{$image4}->{'status'} =~ /used|active/) {
932
                $imageactions .= qq/{"uuid":"$imagereg{$image4}->{'uuid'}","action":"gear_backup"},/;
933
            }
934
            if ($imageactions) {
935
                $imageactions = substr($imageactions,0,-1);
936
                my $uri_action = qq/{"items":[$imageactions]}/;
937
                $uri_action = URI::Escape::uri_escape($uri_action);
938
                $uri_action = $1 if $uri_action =~ /(.+)/; #untaint
939
                $postreply .= `REQUEST_METHOD=POST REMOTE_USER=$user $Stabile::basedir/cgi/images.cgi -k "$uri_action"`;
940
            }
941
        } else {
942
            my $cmd = qq|REQUEST_METHOD=GET REMOTE_USER=$user $Stabile::basedir/cgi/servers.cgi -a $action -u $uuid|;
943
            $postreply = `$cmd`;
944
            #$postreply = $cmd;
945
            my $uistatus = $action."ing";
946
            $uistatus = "resuming" if ($action eq 'resume');
947
            $uistatus = "shuttingdown" if ($action eq 'shutdown');
948
            $main::updateUI->({ tab => 'servers',
949
                user                => $user,
950
                uuid                => $uuid,
951
                status              => $uistatus })
952
953
        }
954
    }
955
    untie %domreg;
956
    untie %imagereg;
957
958
    return $postreply;
959
}
960
961
sub Updateengineinfo {
962
    my ($uuid, $action, $obj) = @_;
963
    if ($help) {
964
        return <<END
965 c899e439 Origo
PUT:downloadmasters, externaliprangestart, externaliprangeend, proxyiprangestart, proxyiprangeend, proxygw, vmreadlimit, vmwritelimit, vmiopsreadlimit, vmiopswritelimit:
966 95b003ff Origo
Save engine information.
967
END
968
    }
969
    unless ($isadmin) {
970
        $postreply = "Status=Error Not allowed\n";
971
        return $postreply;
972
    }
973
    my $msg = "Engine updated";
974 2a63870a Christian Orellana
    my $dl = $obj->{'downloadmasters'};
975
    if ($dl eq '--' || $dl eq '0') {
976 95b003ff Origo
        if ($downloadmasters) {
977
            $downloadmasters = '';
978
            `perl -pi -e 's/DOWNLOAD_MASTERS=.*/DOWNLOAD_MASTERS=0/;' /etc/stabile/config.cfg`;
979
        }
980
        $postreply .= "Status=OK Engine updated\n";
981
        my @ps = split("\n",  `pgrep pressurecontrol` ); `kill -HUP $ps[0]`;
982
    }
983 2a63870a Christian Orellana
    elsif ($dl eq '1' || $dl eq '2') {
984
        if (!$downloadmasters || $dl eq '2') { # We use a value of 2 to force check for downloads
985 95b003ff Origo
            $downloadmasters = 1;
986 2a63870a Christian Orellana
            `perl -pi -e 's/DOWNLOAD_MASTERS=.*/DOWNLOAD_MASTERS=$dl/;' /etc/stabile/config.cfg`;
987
        }
988
        if ($dl eq '2') {
989
            $msg = "Checking for new or updated masters...";
990 95b003ff Origo
        }
991
        $postreply .= "Status=OK Engine updated\n";
992 2a63870a Christian Orellana
        my @ps = split("\n",  `pgrep pressurecontrol` );
993
        `kill -HUP $ps[0]`;
994 95b003ff Origo
    }
995
    elsif ($obj->{'disablesnat'} eq '--' || $obj->{'disablesnat'} eq '0') {
996
        if ($disablesnat) {
997
            $disablesnat = '';
998
            `perl -pi -e 's/DISABLE_SNAT=.*/DISABLE_SNAT=0/;' /etc/stabile/config.cfg`;
999
        }
1000
        $postreply .= "Status=OK Engine updated\n";
1001
    }
1002
    elsif ($obj->{'disablesnat'} eq '1') {
1003
        unless ($disablesnat) {
1004
            $disablesnat = 1;
1005
            `perl -pi -e 's/DISABLE_SNAT=.*/DISABLE_SNAT=1/;' /etc/stabile/config.cfg`;
1006
        }
1007
        $postreply .= "Status=OK Engine updated\n";
1008
    }
1009
    elsif ($obj->{'externaliprangestart'}) {
1010
        if ($obj->{'externaliprangestart'} =~ /\d+\.\d+\.\d+\.\d+/) {
1011
            $extiprangestart = $obj->{'externaliprangestart'};
1012
            $msg = "Setting external IP range start to $extiprangestart";
1013
            `perl -pi -e 's/EXTERNAL_IP_RANGE_START=.*/EXTERNAL_IP_RANGE_START=$extiprangestart/;' /etc/stabile/config.cfg`;
1014
            $postreply .= "Status=OK Engine updated\n";
1015
        } else {
1016
            $msg = "Not changing IP range - $obj->{'externaliprangestart'} is not valid";
1017
        }
1018
    }
1019
    elsif ($obj->{'externaliprangeend'}) {
1020
        if ($obj->{'externaliprangeend'} =~ /\d+\.\d+\.\d+\.\d+/) {
1021
            $extiprangeend = $obj->{'externaliprangeend'};
1022
            $msg = "Setting external IP range end to $extiprangeend";
1023
            `perl -pi -e 's/EXTERNAL_IP_RANGE_END=.*/EXTERNAL_IP_RANGE_END=$extiprangeend/;' /etc/stabile/config.cfg`;
1024
            $postreply .= "Status=OK Engine updated\n";
1025
        } else {
1026
            $msg = "Not changing IP range - $obj->{'externaliprangeend'} is not valid";
1027
        }
1028
    }
1029
    elsif ($obj->{'proxyiprangestart'}) {
1030
        if ($obj->{'proxyiprangestart'} =~ /\d+\.\d+\.\d+\.\d+/) {
1031
            $extiprangestart = $obj->{'proxyiprangestart'};
1032
            $msg = "Setting proxy IP range start to $extiprangestart";
1033
            `perl -pi -e 's/PROXY_IP_RANGE_START=.*/PROXY_IP_RANGE_START=$extiprangestart/;' /etc/stabile/config.cfg`;
1034
            $postreply .= "Status=OK Engine updated\n";
1035
        } else {
1036
            $msg = "Not changing IP range - $obj->{'proxyiprangestart'} is not valid";
1037
        }
1038
    }
1039
    elsif ($obj->{'proxyiprangeend'}) {
1040
        if ($obj->{'proxyiprangeend'} =~ /\d+\.\d+\.\d+\.\d+/) {
1041
            $extiprangeend = $obj->{'proxyiprangeend'};
1042
            $msg = "Setting proxy IP range end to $extiprangeend";
1043
            `perl -pi -e 's/PROXY_IP_RANGE_END=.*/PROXY_IP_RANGE_END=$extiprangeend/;' /etc/stabile/config.cfg`;
1044
            $postreply .= "Status=OK Engine updated\n";
1045
        } else {
1046
            $msg = "Not changing IP range - $obj->{'proxyiprangeend'} is not valid";
1047
        }
1048
    }
1049
    elsif ($obj->{'proxygw'}) {
1050
        if ($obj->{'proxygw'} =~ /\d+\.\d+\.\d+\.\d+/) {
1051
            $proxygw = $obj->{'proxygw'};
1052
            $msg = "Setting proxy gw to $proxygw";
1053
            `perl -pi -e 's/PROXY_GW=.*/PROXY_GW=$proxygw/;' /etc/stabile/config.cfg`;
1054
            $postreply .= "Status=OK Engine updated\n";
1055
        } else {
1056
            $msg = "Not changing IP range - $obj->{'proxygw'} is not valid";
1057
        }
1058
    }
1059
    elsif ($obj->{'vmreadlimit'} || $obj->{'vmwritelimit'} || $obj->{'vmiopsreadlimit'} || $obj->{'vmiopswritelimit'}) {
1060
        my $lim = 'vmreadlimit';
1061
        my $uclim = 'VM_READ_LIMIT';
1062
        if ($obj->{'vmwritelimit'}) {
1063
            $lim = 'vmwritelimit';
1064
            $uclim = 'VM_WRITE_LIMIT';
1065
        } elsif ($obj->{'vmiopsreadlimit'}) {
1066
            $lim = 'vmiopsreadlimit';
1067
            $uclim = 'VM_IOPS_READ_LIMIT';
1068
        } elsif ($obj->{'vmiopswritelimit'}) {
1069
            $lim = 'vmiopswritelimit';
1070
            $uclim = 'VM_IOPS_WRITE_LIMIT';
1071
        }
1072
        if ($obj->{$lim} >= 0 &&  $obj->{$lim} < 10000 *1024*1024) { #sanity checks
1073
            unless ( tie(%idreg,'Tie::DBI', Hash::Merge::merge({table=>'nodeidentities',key=>'identity',CLOBBER=>3}, $Stabile::dbopts)) ) {return "Unable to access id register"};
1074
            my @nodeconfigs;
1075
            # Build hash of known node config files
1076
            foreach my $valref (values %idreg) {
1077
                my $nodeconfigfile = $valref->{'path'} . "/casper/filesystem.dir/etc/stabile/nodeconfig.cfg";
1078
                next if ($nodeconfigs{$nodeconfigfile}); # Node identities may share basedir and node config file
1079
                if (-e $nodeconfigfile) {
1080
                    push @nodeconfigs, $nodeconfigfile;
1081
                }
1082
            }
1083
            untie %idreg;
1084
            push @nodeconfigs, "/etc/stabile/nodeconfig.cfg";
1085
            my $limit = int $obj->{$lim};
1086
            $msg = "Setting read limit to $limit";
1087
            foreach my $nodeconfig (@nodeconfigs) {
1088
                my $cfg = new Config::Simple($nodeconfig);
1089
                $cfg->param($uclim, $limit);
1090
                $cfg->save();
1091
            }
1092
            $Stabile::Nodes::console = 1;
1093
            require "$Stabile::basedir/cgi/nodes.cgi";
1094
            $postreply .= Stabile::Nodes::Configurecgroups();
1095
            $postreply .= Stabile::Nodes::do_reloadall('','reloadall', {'nodeaction'=>'CGLOAD'});
1096
            $postreply .= "Status=OK Engine and nodes updated: $lim set to $limit\n";
1097
        } else {
1098
            $msg = "Not changing limit - $obj->{$lim} is not valid";
1099
        }
1100
    }
1101
    if (!$postreply) {
1102
        $msg = "Engine not updated";
1103
        $postreply = "Status=Error Engine not updated\n" ;
1104
    }
1105
    $main::updateUI->({tab=>'home', user=>$user, type=>'update', message=>$msg});
1106
    return $postreply;
1107
}
1108
1109
sub do_updateaccountinfo {
1110
    my ($uuid, $action, $obj) = @_;
1111
    if ($help) {
1112
        return <<END
1113
PUT:fullname, email, phone, opfullname, opemail, opphone, alertemail, allowfrom, allowinternalapi:
1114
Save user information.
1115
END
1116
    }
1117
    my @props = ('fullname','email','phone','opfullname','opemail','opphone','alertemail', 'allowfrom', 'allowinternalapi');
1118
    my %oldvals;
1119
    if ($obj->{'allowfrom'} && $obj->{'allowfrom'} ne '--') {
1120
        my @allows = split(/,\s*/, $obj->{'allowfrom'});
1121
        $obj->{'allowfrom'} = '';
1122
        foreach my $ip (@allows) {
1123
            $obj->{'allowfrom'}  .= "$1$2, " if ($ip =~ /(\d+\.\d+\.\d+\.\d+)(\/\d+)?/);
1124
        }
1125
        $obj->{'allowfrom'} = substr($obj->{'allowfrom'},0,-2);
1126
        unless ($obj->{'allowfrom'}) {
1127
            $postreply .= "Status=Error Account not updated\n";
1128
            return $postreply;
1129
        }
1130
    }
1131
1132
    foreach my $prop (@props) {
1133
        if ($obj->{$prop}) {
1134
            $obj->{$prop} = '' if ($obj->{$prop} eq '--');
1135
            $oldvals{$prop} = $userreg{$user}->{$prop};
1136
            $userreg{$user}->{$prop} = decode('utf8', $obj->{$prop});
1137
        }
1138
    }
1139
1140
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
1141
    unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username', CLOBBER=>1}, $Stabile::dbopts)) ) {return "Unable to access user register"};
1142
    my $alertmatch;
1143
    foreach my $sysvalref (values %register) {
1144
        if ($user eq $sysvalref->{'user'}) {
1145
            my $sysuuid = $sysvalref->{'uuid'};
1146
            foreach my $prop (@props) {
1147
                my $val = $obj->{$prop};
1148
                if ($val) {
1149
                    $val = '' if ($val eq '--');
1150
                    # Does this system have the same value as the old user value or, equivalently, is it empty?
1151
                    if (!$sysvalref->{$prop} || $sysvalref->{$prop} eq $oldvals{$prop}) {
1152
                    #    $postreply .= "Resetting system prop $prop to $val\n";
1153
                        $sysvalref->{$prop} = ''; # An empty val refers to parent (user) val
1154
                    # Update children
1155
                        foreach my $domvalref (values %domreg) {
1156
                            if ($domvalref->{'user'} eq $user && ($domvalref->{'system'} eq $sysuuid || $domvalref->{'system'} eq '--' || !$domvalref->{'system'})) {
1157
                                if (!$domvalref->{$prop} || $domvalref->{$prop} eq $oldvals{$prop}) {
1158
                                    $domvalref->{$prop} = '';
1159
                                    if ($prop eq 'alertemail') {
1160
                                        if (change_monitor_email($domvalref->{'uuid'}, $val, $oldvals{$prop})) {
1161
                                            $alertmatch = 1;
1162
                                        }
1163
                                    }
1164
                                }
1165
                            }
1166
                        }
1167
                    }
1168
                }
1169
            }
1170
        }
1171
    }
1172
    #`/usr/bin/moncmd reset keepstate` if ($alertmatch);
1173
    tied(%domreg)->commit;
1174
    tied(%userreg)->commit;
1175
    untie %domreg;
1176
    untie %userreg;
1177
    $postreply .= "Status=OK Account updated\n";
1178
    # Send changes to origo.io
1179
    $Stabile::Users::console = 1;
1180
    require "$Stabile::basedir/cgi/users.cgi";
1181
    $postreply .= Stabile::Users::sendEngineUser($user) if ($enginelinked);
1182
    $main::updateUI->({tab=>'home', user=>$user, type=>'update', message=>"Account updated"});
1183
    return $postreply;
1184
}
1185
1186
sub do_listuptime {
1187
    my ($uuid, $action, $obj) = @_;
1188
    if ($help) {
1189
        return <<END
1190
GET:yearmonth,uuid,format:
1191
List uptime for defined monitors. If uuid is supplied, only uptime for matching server or servers belonging to matching
1192
system is shown. Format is either html or json.
1193
END
1194
    }
1195
    my $format = $obj->{'format'};
1196
    my $yearmonth = $obj->{'yearmonth'} || "$year-$month";
1197
    my $pathid = $yearmonth . ':';
1198
    my $name;
1199
1200
    my %sysdoms;
1201
    if ($uuid && $register{$uuid}) {
1202
        $name = $register{$uuid}->{'name'};
1203
        foreach my $valref (values %domreg) {
1204
            $sysdoms{$valref->{'uuid'}} = $uuid if ($valref->{system} eq $uuid);
1205
        }
1206
    } else {
1207
        $pathid .= $uuid;
1208
        $name = $domreg{$uuid}->{'name'} if ($domreg{$uuid});
1209
    }
1210
    my %uptimes;
1211
    my $jtext = {};
1212
    my @csvrows;
1213
1214
    unless ($pathid =~ /\// || $pathid =~ /\./) { # Security check
1215
        my $path = "/var/log/stabile/$pathid*"; # trailing / is required. No $pathid lists all files in log dir.
1216
        my $utext = '';
1217
        my %numfiles;
1218
        my %sumupp;
1219
        ## loop through the files contained in the directory
1220
        for my $eachFile (bsd_glob($path.'*')) {
1221
            if (!(-d $eachFile) && $eachFile =~ /\/var\/log\/stabile\/(.+):(.+):(.+)/) {
1222
                my $ymonth = $1;
1223
                my $domuuid = $2;
1224
                my $service = $3;
1225
                next unless ($domreg{$domuuid});
1226
                my $servername = $domreg{$domuuid}->{'name'};
1227
                if ($domreg{$domuuid}->{'user'} eq $user) {
1228
                    next if (%sysdoms && !$sysdoms{$domuuid}); # If we are listing a system, match system uuid
1229
                    open(FILE, $eachFile) or {print("Unable to access $eachFile")};
1230
                    @lines = <FILE>;
1231
                    close(FILE);
1232
                    my $starttime;
1233
                    my $lastup;
1234
                    my $firststamp; # First timestamp of measuring period
1235
                    my $laststamp; # Last timestamp of measuring period
1236
                    my $curstate = 'UNKNOWN';
1237
                    my $dstate = 'UNKNOWN';
1238
                    my ($y, $m) = split('-', $ymonth);
1239
                    my $timespan = 0;
1240
                    my $dtime = 0; # Time disabled
1241
                    my $lastdtime = 0;
1242
                    my $uptime = 0;
1243
                    foreach my $line (@lines) {
1244
                        my ($timestamp, $event, $summary, $ptime) = split(/, */,$line);
1245
                        if (!$starttime) { # First line
1246
                            $starttime = $timestamp;
1247
                            # Find 00:00 of first day of month - http://www.perlmonks.org/?node_id=97120
1248
                            $firststamp = POSIX::mktime(0,0,0,1,$m-1,$year-1900,0,0,-1);
1249
                            # Round to month start if within 15 min
1250
                            $starttime = $firststamp if ($starttime-$firststamp<15*60);
1251
                            $lastup = $starttime if ($event eq 'STARTUP' || $event eq 'UP');
1252
                            $curstate = 'UP'; # Assume up - down alerts are always triggered
1253
                        }
1254
                        if ($event eq 'UP') {
1255
                            if ($curstate eq 'UP') {
1256
                                $uptime += ($timestamp - $lastup) if ($lastup);
1257
                            }
1258
                            $lastup = $timestamp;
1259
                            $curstate = 'UP';
1260
                        } elsif ($event eq 'DOWN') {
1261
                            if ($curstate eq 'UP' && $lastup!=$starttime) { # If down is immediately after startup - dont count uptime
1262
                                $uptime += ($timestamp - $lastup) if ($lastup);
1263
                                $lastup = $timestamp;
1264
                            }
1265
                            $curstate = 'DOWN';
1266
                        } elsif ($event eq 'STARTUP') {
1267
                        } elsif ($event eq 'DISABLE' && $curstate ne 'UNKNOWN') {
1268
                            if ($curstate eq 'UP') {
1269
                                $uptime += ($timestamp - $lastup) if ($lastup);
1270
                                $lastup = $timestamp;
1271
                            }
1272
                            $lastdtime = $timestamp;
1273
                            $dstate = $curstate;
1274
                            $curstate = 'UNKNOWN';
1275
                        } elsif ($event eq 'ENABLE') {
1276
                            if ($dstate eq 'UP' && $curstate eq 'UNKNOWN') {
1277
                                $lastup = $timestamp;
1278
                            }
1279
                            $curstate = 'UP';
1280
                        }
1281
                        # All non-disable events must mean monitor is enabled again
1282
                        if ($event ne 'DISABLE') {
1283
                            if ($lastdtime) {
1284
                                $dtime += ($timestamp - $lastdtime);
1285
                                $lastdtime = 0;
1286
                            }
1287
                        }
1288
1289
                    }
1290
                    if ($ymonth ne "$year-$month") { # If not current month, assume monitoring to end of month
1291
                        # Find 00:00 of first day of next month - http://www.perlmonks.org/?node_id=97120
1292
                        $laststamp = POSIX::mktime(0,0,0,1,$m,$year-1900,0,0,-1);
1293
                    } else {
1294
                        $laststamp = $current_time;
1295
                    }
1296
                    if ($curstate eq 'UP' && !$lastdtime && $lastup) {
1297
                        $uptime += ($laststamp - $lastup);
1298
                    }
1299
                    if ($lastdtime) {
1300
                        $dtime += ($laststamp - $lastdtime);
1301
                    }
1302
                    $timespan = $laststamp - $starttime;
1303
                    $uptimes{"$domuuid:$service"}->{'timespan'} = $timespan;
1304
                    $uptimes{"$domuuid:$service"}->{'uptime'} = $uptime;
1305
                    my $timespanh = int(0.5 + 100*$timespan/3600)/100;
1306
                    my $dtimeh = int(0.5 + 100*$dtime/3600)/100;
1307
                    my $uptimeh = int(0.5 + 100*$uptime/3600)/100;
1308
                    my $upp = int(0.5+ 10000*$uptime/($timespan-$dtime) ) / 100;
1309
                    $sumupp{$service} += $upp;
1310
                    $numfiles{$service} += 1;
1311
1312
                    utf8::decode($servername);
1313
1314
                    $utext .= qq[<div class="uptime_header">$service on $servername:</div>\n];
1315
                    my $color = ($upp<98)?'red':'green';
1316
                    $utext .= qq[<span style="color: $color;">Uptime: $uptimeh hours ($upp%)</span>\n];
1317
                    $utext .= qq{[timespan: $timespanh hours, \n};
1318
                    $utext .= qq{disabled: $dtimeh hours]\n};
1319
1320
                    $jtext->{$domuuid}->{'servername'} = $servername;
1321
                    $jtext->{$domuuid}->{$service}->{'uptime'} = $upp;
1322
                    $jtext->{$domuuid}->{$service}->{'uptimeh'} = $uptimeh;
1323
                    $jtext->{$domuuid}->{$service}->{'color'} = ($upp<98)?'red':'green';
1324
                    $jtext->{$domuuid}->{$service}->{'disabledtimeh'} = $dtimeh;
1325
                    $jtext->{$domuuid}->{$service}->{'timespanh'} = $timespanh;
1326
1327
                    push @csvrows, {serveruuid=>$domuuid, service=>$service, servername=>$servername, uptime=>$upp, uptimeh=>$uptimeh, color=>($upp<98)?'red':'green',disabledtimeh=>$dtimeh, timespanh=>$timespanh, yearmonth=>$yearmonth};
1328
                }
1329
            }
1330
        }
1331
        my @avgtxt;
1332
        my $alertclass = "info";
1333
        my $compcolor;
1334
        $jtext->{'averages'} = {};
1335
        $jtext->{'year-month'} = $yearmonth;
1336
        foreach $svc (keys %sumupp) {
1337
            my $avgupp = int(0.5 + 100*$sumupp{$svc}/$numfiles{$svc})/100;
1338
            my $color = ($avgupp<98)?'red':'green';
1339
            push @avgtxt, qq[<span style="color: $color;" class="uptime_header">$svc: $avgupp%</span>\n];
1340
            $jtext->{'averages'}->{$svc}->{'uptime'} = $avgupp;
1341
            $jtext->{'averages'}->{$svc}->{'color'} = $color;
1342
            $compcolor = ($compcolor)? ( ($compcolor eq $color)? $color : 'info' ) : $color;
1343
        }
1344
        $alertclass = "warning" if ($compcolor eq 'red');
1345
        $alertclass = "success" if ($compcolor eq 'green');
1346
        $postreply = header();
1347
        if ($name) {
1348
            $postreply .= qq[<div class="alert alert-$alertclass uptime_alert"><h4 class="uptime_header">Average uptime for $name:</h4>\n<div style="margin-top:10px;">\n];
1349
        } else {
1350
            $postreply .= qq[<div class="alert alert-$alertclass uptime_alert"><h4 class="uptime_header">Average uptime report</h4>\n<div style="margin-top:10px;">\n];
1351
        }
1352
        $postreply .= join(", ", @avgtxt);
1353
        my $uuidlink = "&uuid=$uuid" if ($uuid);
1354
        $postreply .= qq[</div></div><hr class="uptime_line"><h5 class="uptime_header">Uptime details: (<span><a href="/stabile/systems?action=listuptime&format=csv$uuidlink&yearmonth=$yearmonth" target="blank" title="Download as CSV">csv</a></span>)</h5>\n];
1355
        $postreply .= "<span class=\"uptime_text\">$utext</span>";
1356
    }
1357
    if ($params{'format'} eq 'csv') {
1358
        $postreply = header("text/plain");
1359
        csv(in => \@csvrows, out => \my $csvdata, key => "servername");
1360
        $postreply .= $csvdata;
1361
    } elsif ($format ne 'html') {
1362
        $postreply = to_json($jtext, {pretty=>1});
1363
    }
1364
    return $postreply;
1365
}
1366
1367
sub do_appstore {
1368
    my ($uuid, $action, $obj) = @_;
1369
    if ($help) {
1370
        return <<END
1371
GET:appid,callback:
1372
Look up app info for app with given appid in appstore on origo.io. Data is returned as padded JSON (JSONP).
1373
Optionally provide name of your JSONP callback function, which should parse the returned script data.
1374
END
1375
    }
1376
    my $appid = $params{'appid'};
1377
    my $callback = $params{'callback'};
1378
    if ($appid) {
1379
        $postreply = header("application/javascript");
1380
        $postreply .= $main::postToOrigo->($engineid, 'engineappstore', $appid, 'appid', $callback);
1381
    } else {
1382
        $postreply = qq|Status=Error Please provide appid|;
1383
    }
1384
    return $postreply;
1385
}
1386
1387
sub do_resetmonitoring {
1388
    my ($uuid, $action, $obj) = @_;
1389
    if ($help) {
1390
        return <<END
1391
GET::
1392
Reset mon daemon while keeping states.
1393
END
1394
    }
1395
    saveOpstatus();
1396
    $postreply = "Status=OK " . `/usr/bin/moncmd reset keepstate`;
1397
    return $postreply;
1398
}
1399
1400
sub do_installsystem {
1401
    my ($uuid, $action, $obj) = @_;
1402
    if ($help) {
1403
        return <<END
1404
GET:installsystem,installaccount:
1405
Helper function to initiate the installation of a new stack with system ID [installsystem] to account [installaccount] by redirecting with appropriate cookies set.
1406
END
1407
    }
1408
    my $installsystem = $obj->{'installsystem'};
1409
    my $installaccount = $obj->{'installaccount'};
1410
    my $systemcookie;
1411
    my $ia_cookie;
1412
    my $sa_cookie;
1413
1414
    push(@INC, "$Stabile::basedir/auth");
1415
    require Apache::AuthTkt;# 0.03;
1416
    require AuthTktConfig;
1417
    my $at = Apache::AuthTkt->new(conf => $ENV{MOD_AUTH_TKT_CONF});
1418
    my ($server_name, $server_port) = split /:/, $ENV{HTTP_HOST} if $ENV{HTTP_HOST};
1419
    $server_name ||= $ENV{SERVER_NAME} if $ENV{SERVER_NAME};
1420
    $server_port ||= $ENV{SERVER_PORT} if $ENV{SERVER_PORT};
1421
    my $AUTH_DOMAIN = $at->domain || $server_name;
1422
    my @auth_domain = $AUTH_DOMAIN ? ( -domain => $AUTH_DOMAIN ) : ();
1423
1424
    if ($installsystem) {
1425
        $systemcookie = CGI::Cookie->new(
1426
            -name => 'installsystem',
1427
            -value => "$installsystem",
1428
            -path => '/',
1429
            @auth_domain
1430
        );
1431
    };
1432
    if ($installaccount) {
1433
        $ia_cookie = CGI::Cookie->new(
1434
            -name => 'installaccount',
1435
            -value => "$installaccount",
1436
            -path => '/',
1437
            @auth_domain
1438
        );
1439
        $sa_cookie = CGI::Cookie->new(
1440
            -name => 'steamaccount',
1441
            -value => "$installaccount",
1442
            -path => '/',
1443
            @auth_domain
1444
        );
1445
    };
1446
1447
    $tktcookie = CGI::Cookie->new(
1448
        -name => 'tktuser',
1449
        -value => "$tktuser",
1450
        -path => '/',
1451
        @auth_domain
1452
    );
1453
1454
    $postreply = redirect(
1455
        -uri => '/stabile/mainvalve/',
1456
        -cookie => [$tktcookie, $systemcookie, $ia_cookie, $sa_cookie]
1457
    );
1458
    return $postreply;
1459
}
1460
1461
sub Changemonitoremail {
1462
    my ($uuid, $action, $obj) = @_;
1463
    if ($help) {
1464
        return <<END
1465
GET:uuid,email:
1466
Change the email for all monitors belonging to server with given uuid. May be called with command line switches -u server uuid, -m old email, -k new email.
1467
END
1468
    }
1469
    if ($isreadonly) {
1470
        $postreply = "Status=Error Not permitted\n";
1471
    } else {
1472
        my $serveruuid = $options{u} || $uuid;
1473
        my $email = $options{k} || $obj->{'email'};
1474
        if (change_monitor_email($serveruuid, $email)) {
1475
            $postreply = "Status=OK " . `/usr/bin/moncmd reset keepstate`;
1476
        } else {
1477
            $postreply = "Status=Error There was a problem changing monitor email for $serveruuid\n";
1478
        }
1479
    }
1480
    return $postreply;
1481
}
1482
1483
sub do_getmetrics {
1484
    my ($suuid, $action, $obj) = @_;
1485
    if ($help) {
1486
        return <<END
1487
GET:uuid,metric,from,until,last,format:
1488
Get performance and load metrics in JSON format from Graphite backend. [metric] is one of: cpuload, diskreads, diskwrites, networkactivityrx, networkactivitytx
1489
From and until are Unix timestamps. Alternatively specify "last" number of seconds you want metrics for. Format is "json" (default) or "csv".
1490
END
1491
    }
1492
    my $metric = $params{metric} || "cpuLoad";
1493
    my $now = time();
1494
    my $from = $params{"from"} || ($now-$params{"last"}) || ($now-300);
1495
    my $until = $params{"until"} || $now;
1496
1497
    my @uuids;
1498
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
1499
1500
    if ($domreg{$suuid}) { # We are dealing with a server
1501
        push @uuids, $suuid;
1502
    } else { # We are dealing with a system
1503
        foreach my $valref (values %domreg) {
1504
            my $sysuuid = $valref->{'system'};
1505
            push @uuids, $valref->{'uuid'} if ($sysuuid eq $suuid)
1506
        }
1507
    }
1508
    untie %domreg;
1509
1510
    my @datapoints;
1511
    my @targets;
1512
    my $all;
1513
    my $jobj = [];
1514
    foreach my $uuid (@uuids) {
1515
        next unless (-e "/var/lib/graphite/whisper/domains/$uuid");
1516
        my $url = "https://127.0.0.1/graphite/graphite.wsgi/render?format=json&from=$from&until=$until&target=domains.$uuid.$metric";
1517
        my $jstats = `curl -k "$url"`;
1518
        $jobj = from_json($jstats);
1519
        push @targets, $jobj->[0]->{target};
1520
        if ($jobj->[0]->{target}) {
1521
            if (@datapoints) {
1522
                my $j=0;
1523
                foreach my $p ( @{$jobj->[0]->{datapoints}} ) {
1524
#                    print "adding: ", $datapoints[$j]->[0], " + ", $p->[0];
1525
                    $datapoints[$j]->[0] += $p->[0];
1526
#                    print " = ", $datapoints[$j]->[0], " to ",$datapoints[$j]->[1],  "\n";
1527
                    $j++;
1528
                }
1529
            } else {
1530
                @datapoints = @{$jobj->[0]->{datapoints}};
1531
            }
1532
        }
1533
    }
1534
    pop @datapoints; # We discard the last datapoint because of possible clock drift
1535
    $all = [{targets=>\@targets, datapoints=>\@datapoints, period=>{from=>$from, until=>$until, span=>$until-$from}}];
1536
    if ($params{'format'} eq 'csv') {
1537
        $postreply = header("text/plain");
1538
        csv(in => \@datapoints, out => \my $csvdata);
1539
        $postreply .= $csvdata;
1540
    } else {
1541
        $postreply = to_json($all);
1542
    }
1543
    return $postreply;
1544
}
1545
1546
sub do_metrics {
1547
    my ($suuid, $action, $obj) = @_;
1548
    if ($help) {
1549
        return <<END
1550
GET:uuid,metric,from,to:
1551
Get performance and load metrics in JSON format from RRD backend. [metric] is one of: cpuload, diskreads, diskwrites, networkactivityrx, networkactivitytx
1552
From and to are Unix timestamps.
1553
END
1554
    }
1555
1556
    my $from = $params{"from"};
1557
    my $to = $params{"to"};
1558
    my $dif = $to - $from;
1559
    my $now = time();
1560
1561
    my @items;
1562
    my %cpuLoad = ();
1563
    my %networkActivityRX = ();
1564
    my %networkActivityTX = ();
1565
    my %diskReads = ();
1566
    my %diskWrites = ();
1567
1568
    my $i = 0;
1569
    my @uuids;
1570
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
1571
1572
    if ($domreg{$suuid}) { # We are dealing with a server
1573
        push @uuids, $suuid;
1574
    } else { # We are dealing with a system
1575
        foreach my $valref (values %domreg) {
1576
            my $sysuuid = $valref->{'system'};
1577
            push @uuids, $valref->{'uuid'} if ($sysuuid eq $suuid)
1578
        }
1579
    }
1580
    untie %domreg;
1581
1582
    foreach my $uuid (@uuids) {
1583
        next unless hasRRD($uuid);
1584
        $i++;
1585
        # Fetch data from RRD buckets...
1586
        my $rrd = RRDTool::OO->new(file =>"/var/cache/rrdtool/".$uuid."_highres.rrd");
1587
        my $last = $rrd->last();
1588
        $rrd->fetch_start(start => $now-$dif, end=> $now);
1589
        while(my($timestamp, @value) = $rrd->fetch_next()) {
1590
            last if ($timestamp >= $last && $now-$last<20);
1591
            my $domain_cpuTime = shift(@value);
1592
            my $blk_hda_rdBytes = shift(@value);
1593
            my $blk_hda_wrBytes = shift(@value);
1594
            my $if_vnet0_rxBytes = shift(@value);
1595
            my $if_vnet0_txBytes = shift(@value);
1596
1597
            # domain_cpuTime is avg. nanosecs spent pr. 1s
1598
            # convert to value [0;1]
1599
            $domain_cpuTime = $domain_cpuTime / 10**9 if ($domain_cpuTime);
1600
            $cpuLoad{$timestamp} +=  $domain_cpuTime;
1601
1602
            $blk_hda_rdBytes = $blk_hda_rdBytes if ($blk_hda_rdBytes);
1603
            $diskReads{$timestamp} += $blk_hda_rdBytes;
1604
1605
            $blk_hda_wrBytes = $blk_hda_wrBytes if ($blk_hda_wrBytes);
1606
            $diskWrites{$timestamp} += $blk_hda_wrBytes;
1607
1608
            $networkActivityRX{$timestamp} += $if_vnet0_rxBytes;
1609
            $networkActivityTX{$timestamp} += $if_vnet0_txBytes;
1610
        }
1611
    }
1612
    my @t = ( $now-$dif, $now);
1613
    my @a = (undef, undef);
1614
    $i = $i || 1;
1615
1616
    my $item = ();
1617
    $item->{"uuid"} = $suuid if ($suuid);
1618
    my @tstamps = sort keys %cpuLoad;
1619
    $item->{"timestamps"} = \@tstamps || \@t;
1620
1621
    if ($params{"metric"} eq "cpuload" || $params{'cpuload'}) {
1622
        my @vals;
1623
        my $load = int(100*$cpuLoad{$_})/100;
1624
        $load = $i if  ($cpuLoad{$_} > $i);
1625
        foreach(@tstamps) {push @vals, $load};
1626
        $item->{"cpuload"} = \@vals || \@a;
1627
    }
1628
    elsif ($params{"metric"} eq "diskreads" || $params{'diskReads'}) {
1629
        my @vals;
1630
        foreach(@tstamps) {push @vals, int(100*$diskReads{$_})/100;};
1631
        $item->{"diskReads"} = \@vals || \@a;
1632
      }
1633
    elsif ($params{"metric"} eq "diskwrites" || $params{'diskWrites'}) {
1634
        my @vals;
1635
        foreach(@tstamps) {push @vals, int(100*$diskWrites{$_})/100;};
1636
        $item->{"diskWrites"} = \@vals || \@a;
1637
    }
1638
    elsif ($params{"metric"} eq "networkactivityrx" || $params{'networkactivityrx'}) {
1639
        my @vals;
1640
        foreach(@tstamps) {push @vals, int(100*$networkActivityRX{$_})/100;};
1641
        $item->{"networkactivityrx"} = \@vals || \@a;
1642
    }
1643
    elsif ($params{"metric"} eq "networkactivitytx" || $params{'networkactivitytx'}) {
1644
        my @vals;
1645
        foreach(@tstamps) {push @vals, int(100*$networkActivityTX{$_})/100;};
1646
        $item->{"networkactivitytx"} = \@vals || \@a;
1647
    }
1648
    push @items, $item;
1649
    $postreply .= to_json(\@items, {pretty=>1});
1650
    return $postreply;
1651
}
1652
1653
sub hasRRD {
1654
	my($uuid) = @_;
1655
	my $rrd_file = "/var/cache/rrdtool/".$uuid."_highres.rrd";
1656
1657
	if ((not -e $rrd_file) and ($uuid)) {
1658
		return(0);
1659
	} else {
1660
		return(1);
1661
	}
1662
}
1663
1664
sub do_packages_remove {
1665
    my ($uuid, $action, $obj) = @_;
1666
    if ($help) {
1667
        return <<END
1668
DELETE:uuid:
1669
Remove packages belonging to server or system with given uuid.
1670
END
1671
    }
1672
    my $issystem = $obj->{"issystem"} || $register{$uuid};
1673
    unless ( tie(%packreg,'Tie::DBI', Hash::Merge::merge({table=>'packages', key=>'id'}, $Stabile::dbopts)) ) {return "Unable to access package register"};
1674
    my @domains;
1675
    if ($issystem) {
1676
        foreach my $valref (values %domreg) {
1677
            if (($valref->{'system'} eq $uuid || $uuid eq '*')
1678
                    && ($valref->{'user'} eq $user || $fulllist)) {
1679
                push(@domains, $valref->{'uuid'});
1680
            }
1681
        }
1682
    } else { # Allow if domain no longer exists or belongs to user
1683
        push(@domains, $uuid) if (!$domreg{$uuid} || $domreg{$uuid}->{'user'} eq $user || $fulllist);
1684
    }
1685
    foreach my $domuuid (@domains) {
1686
        foreach my $packref (values %packreg) {
1687
            my $id = $packref->{'id'};
1688
            if (substr($id, 0,36) eq $domuuid || ($uuid eq '*' && $packref->{'user'} eq $user)) {
1689
                delete $packreg{$id};
1690
            }
1691
        }
1692
    }
1693
    tied(%packreg)->commit;# if (%packreg);
1694
    if ($issystem && $register{$uuid}) {
1695
        $postreply = "Status=OK Cleared packages for $register{$uuid}->{'name'}\n";
1696
    } elsif ($domreg{$uuid}) {
1697
        $postreply = "Status=OK Cleared packages for $domreg{$uuid}->{'name'}\n";
1698
    } else {
1699
        $postreply = "Status=OK Cleared packages. System not registered\n";
1700
    }
1701
    return $postreply;
1702
}
1703
1704
sub Packages_load {
1705
    my ($uuid, $action, $obj) = @_;
1706
    if ($help) {
1707
        return <<END
1708
POST:uuid:
1709
Load list of installed software packages that are installed on the image. Image must contain a valid OS.
1710
END
1711
    }
1712
    if (!$isreadonly) {
1713
        unless ( tie(%packreg,'Tie::DBI', Hash::Merge::merge({table=>'packages', key=>'id'}, $Stabile::dbopts)) ) {return "Unable to access package register"};
1714
        unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
1715
        my $curimg;
1716
        my $apps;
1717
        my @domains;
1718
        my $issystem = $obj->{'issystem'};
1719
        if ($issystem) {
1720
            foreach my $valref (values %domreg) {
1721
                if (($valref->{'system'} eq $uuid || $uuid eq '*')
1722
                        && ($valref->{'user'} eq $user || $fulllist)) {
1723
                    push(@domains, $valref->{'uuid'});
1724
                }
1725
            }
1726
        } else {
1727
            push(@domains, $uuid) if ($domreg{$uuid}->{'user'} eq $user || $fulllist);
1728
        }
1729
1730
        foreach my $domuuid (@domains) {
1731
            if ($domreg{$domuuid}) {
1732
                $curimg = $domreg{$domuuid}->{'image'};
1733
                $apps = getPackages($curimg);
1734
                if ($apps) {
1735
                    my @packages;
1736
                    my @packages2;
1737
                    open my $fh, '<', \$apps or die $!;
1738
                    my $distro;
1739
                    my $hostname;
1740
                    my $i;
1741
                    while (<$fh>) {
1742
                        if (!$distro) {
1743
                            $distro = $_;
1744
                            chomp $distro;
1745
                        } elsif (!$hostname) {
1746
                            $hostname = $_;
1747
                            chomp $hostname;
1748
                        } elsif ($_ =~ /\[(\d+)\]/) {
1749
                            push @packages2, $packages[$i];
1750
                            $i = $1;
1751
                        } elsif ($_ =~ /(\S+): (.+)/ && $2) {
1752
                            $packages[$i]->{$1} = $2;
1753
                        }
1754
                    }
1755
                    close $fh or die $!;
1756
                    $domreg{$domuuid}->{'os'} = $distro;
1757
                    $domreg{$domuuid}->{'hostname'} = $hostname;
1758
                    foreach $package (@packages) {
1759
                        my $id = "$domuuid-$package->{'app_name'}";
1760
                        $packreg{$id} = $package;
1761
                        $packreg{$id}->{'app_display_name'} = $packreg{$id}->{'app_name'} unless ($packreg{$id}->{'app_display_name'});
1762
                        $packreg{$id}->{'domuuid'} = $domuuid;
1763
                        $packreg{$id}->{'user'} = $user;
1764
                    }
1765
                    $postreply .= "Status=OK Updated packages for $domreg{$domuuid}->{'name'}\n";
1766
                } else {
1767
                    $domreg{$domuuid}->{'os'} = 'unknown';
1768
                    $domreg{$domuuid}->{'hostname'} = 'unknown';
1769
                    $postreply .= "Status=Error Could not update packages for $domreg{$domuuid}->{'name'}";
1770
                }
1771
            }
1772
        }
1773
        tied(%packreg)->commit;
1774
        tied(%domreg)->commit;
1775
        untie %domreg;
1776
        untie %packreg;
1777
1778
    } else {
1779
        $postreply .= "Status=Error Not allowed\n";
1780
    }
1781
    return $postreply;
1782
}
1783
1784
sub do_packages {
1785
    my ($uuid, $action, $obj) = @_;
1786
    if ($help) {
1787
        return <<END
1788
GET:uuid:
1789
Handling of packages
1790
END
1791
    }
1792
1793
    unless ( tie(%packreg,'Tie::DBI', Hash::Merge::merge({table=>'packages', key=>'id'}, $Stabile::dbopts)) ) {return "Unable to access package register"};
1794
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
1795
1796
    # List packages
1797
    my @packregvalues = values %packreg;
1798
    my @curregvalues;
1799
    my %packhash;
1800
    my %sysdoms; # Build list of members of system
1801
    foreach $sysdom (values %domreg) {
1802
        if ($sysdom->{'system'} eq $curuuid) {
1803
            $sysdoms{$sysdom->{'uuid'}} = $curuuid;
1804
        }
1805
    }
1806
    foreach my $valref (@packregvalues) {
1807
        if ($valref->{'user'} eq $user || $fulllist) {
1808
            if ((!$curuuid || $curuuid eq '*') # List packages from all servers
1809
                || ($domreg{$curuuid} && $curuuid eq $valref->{'domuuid'}) # List packages from a single server
1810
                || ($register{$curuuid} && $sysdoms{ $valref->{'domuuid'} }) # List packages from multiple servers - a system
1811
            ) {
1812
            #    push(@curregvalues, $valref);
1813
                my $packid = "$valref->{'app_display_name'}:$valref->{'app_version'}";
1814
                if ($packhash{$packid}) {
1815
                    ($packhash{$packid}->{'app_count'})++;
1816
                } else {
1817
                    $packhash{$packid} = {
1818
                        app_display_name=>$valref->{'app_display_name'},
1819
                        app_name=>$valref->{'app_name'},
1820
                        app_release=>$valref->{'app_release'},
1821
                    #    app_publisher=>$valref->{'app_publisher'},
1822
                        app_version=>$valref->{'app_version'},
1823
                        app_count=>1
1824
                    }
1825
                }
1826
            }
1827
        }
1828
    }
1829
    my @sorted_packs = sort {$a->{'app_display_name'} cmp $b->{'app_display_name'}} values %packhash;
1830
    if ($obj->{format} eq 'html') {
1831
        my $res;
1832
        $res .= qq[<tr><th>Name</th><th>Version</th><th>Count</th></tr>\n];
1833
        foreach my $valref (@sorted_packs) {
1834
            $res .= qq[<tr><td>$valref->{'app_display_name'}</td><td>$valref->{'app_version'}</td><td>$valref->{'app_count'}</td></tr>\n];
1835
        }
1836
        $postreply .= qq[<table cellspacing="0" frame="void" rules="rows" class="systemTables">\n$res</table>\n];
1837
    } elsif ($obj->{'format'} eq 'csv') {
1838
        $postreply = header("text/plain");
1839
        csv(in => \@sorted_packs, out => \my $csvdata);
1840
        $postreply .= $csvdata;
1841
    } else {
1842
        $postreply .= to_json(\@sorted_packs);
1843
    }
1844
    untie %domreg;
1845
    untie %packreg;
1846
    return $postreply;
1847
}
1848
1849
sub Buildsystem {
1850
    my ($uuid, $action, $obj) = @_;
1851
    if ($help) {
1852
        return <<END
1853 c899e439 Origo
GET:name, master, storagepool, system, instances, networkuuid, bschedule, networktype1, ports, memory, vcpu, diskbus, cdrom, boot, nicmodel1, nicmac1, networkuuid2, nicmac2, storagepool2, monitors, managementlink, start:
1854 95b003ff Origo
Build a complete system from cloned master image.
1855 c899e439 Origo
master is the only required parameter. Set [storagepool2] to -1 if you want data images to be put on node storage.
1856 95b003ff Origo
END
1857
    }
1858
    $curuuid = $uuid unless ($curuuid);
1859
    $postreply = buildSystem(
1860
        $obj->{name},
1861
        $obj->{master},
1862
        $obj->{storagepool},
1863
        $obj->{system},
1864
        $obj->{instances},
1865
        $obj->{networkuuid1},
1866
        $obj->{bschedule},
1867
        $obj->{networktype1},
1868
        $obj->{ports},
1869
        $obj->{memory},
1870
        $obj->{vcpu},
1871
        $obj->{diskbus},
1872
        $obj->{cdrom},
1873
        $obj->{boot},
1874
        $obj->{nicmodel1},
1875
        $obj->{nicmac1},
1876
        $obj->{networkuuid2},
1877
        $obj->{nicmac2},
1878
        $obj->{monitors},
1879
        $obj->{managementlink},
1880
        $obj->{start},
1881 c899e439 Origo
        $obj->{domuuid},
1882
        $obj->{storagepool2}
1883 95b003ff Origo
    );
1884
    
1885
    return $postreply;
1886
}
1887
1888
sub Upgradesystem {
1889
    my ($uuid, $action, $obj) = @_;
1890
    if ($help) {
1891
        return <<END
1892
GET:uuid,internalip:
1893
Upgrades a system
1894
END
1895
    }
1896
    my $internalip = $params{'internalip'};
1897
    $postreply = upgradeSystem($internalip);
1898
    return $postreply;
1899
}
1900
1901
sub Removeusersystems {
1902
    my ($uuid, $action, $obj) = @_;
1903
    if ($help) {
1904
        return <<END
1905
GET::
1906
Removes all systems belonging to a user, i.e. completely deletes all servers, images and networks belonging to an account.
1907
Use with extreme care.
1908
END
1909
    }
1910
    $postreply = removeusersystems($user);
1911
    return $postreply;
1912
}
1913
1914
sub Removesystem {
1915
    my ($uuid, $action, $obj) = @_;
1916
    if ($help) {
1917
        return <<END
1918
GET:uuid:
1919
Removes specified system, i.e. completely deletes all servers, images, networks and backups belonging to a system.
1920
Use with care.
1921
END
1922
    }
1923
    $postreply = remove($uuid, 0, 1);
1924
    return $postreply;
1925
}
1926
1927
1;
1928
1929
# Print list of available actions on objects
1930
sub do_plainhelp {
1931
    my $res;
1932
    $res .= header('text/plain') unless $console;
1933
    $res .= <<END
1934
new [name="name"]
1935
start
1936
suspend
1937
resume
1938
shutdown
1939
destroy
1940
buildsystem [master, storagepool, system (uuid), instances, networkuuid1,bschedule,
1941
networktype1, ports, memory, vcpu, diskbus, cdrom, boot, nicmodel1, nicmac1, networkuuid2,
1942
nicmac2, monitors, start]
1943
removesystem
1944
updateaccountinfo
1945
resettoaccountinfo
1946
1947
END
1948
;
1949
}
1950
1951
# Save current mon status to /etc/stabile/opstatus, in order to preserve state when reloading mon
1952
sub saveOpstatus {
1953
    my $deleteid = shift;
1954
    my %opstatus = getSavedOpstatus();
1955
    my @monarray = split("\n", `/usr/bin/moncmd list opstatus`);
1956
    my $opfile = "/etc/stabile/opstatus";
1957
    open(FILE, ">$opfile") or {throw Error::Simple("Unable to write $opfile")};
1958
    foreach my $line (@monarray) {
1959
        my @pairs = split(/ /,$line);
1960
        my %h;
1961
        my $ALERT;
1962
        foreach my $pair (@pairs) {
1963
            my ($key, $val) = split(/=/,$pair);
1964
            $obj->{$key} = $val;
1965
        }
1966
        my $ops = $opstatus{"$group:$service"};
1967
        my $group = $obj->{'group'};
1968
        my $service = $obj->{'service'};
1969
        my $curstatus = $ops->{'opstatus'};
1970
        my $curack = $ops->{'ack'};
1971
        my $curackcomment = $ops->{'ackcomment'};
1972
        my $curline = $ops->{'line'};
1973
        if ($deleteid && $deleteid eq "$group:$service") {
1974
            ; # Don't write line for service we are deleting
1975
        } elsif (($obj->{'opstatus'} eq '0' || $obj->{'opstatus'} eq '7') && $curack && $curstatus eq '0') {
1976
            # A failure has been acknowledged and service is still down
1977
            print FILE "$curline\n";
1978
            $ALERT = ($obj->{'opstatus'}?'UP':'DOWN');
1979
        } elsif (($obj->{'opstatus'} || $obj->{'opstatus'} eq '0') && $obj->{'opstatus'} ne '7') {
1980
            print FILE "$line\n";
1981
            $ALERT = ($obj->{'opstatus'}?'UP':'DOWN');
1982
        } elsif (($curstatus || $curstatus eq '0') && $curstatus ne '7') {
1983
            print FILE "$curline\n";
1984
            $ALERT = ($obj->{'opstatus'}?'UP':'DOWN');
1985
        } else {
1986
            # Don't write anything if neither is different from 7
1987
        }
1988
    # Create empty log file if it does not exist
1989
        my $oplogfile = "/var/log/stabile/$year-$month:$group:$service";
1990
        unless (-s $oplogfile) {
1991
            if ($group && $service && $ALERT) {
1992
                `/usr/bin/touch "$oplogfile"`;
1993
                `/bin/chown mon:mon "$oplogfile"`;
1994
                my $logline = "$current_time, $ALERT, MARK, $pretty_time";
1995
                `/bin/echo >> $oplogfile "$logline"`;
1996
            }
1997
        }
1998
    }
1999
    close (FILE);
2000
    #if ((!-e $opfile) || ($current_time - (stat($opfile))[9] > 120) ) {
2001
    #    `/usr/bin/moncmd list opstatus > $opfile`;
2002
    #}
2003
}
2004
2005
sub getSavedOpstatus {
2006
    my $dounbackslash = shift;
2007
    my $opfile = "/etc/stabile/opstatus";
2008
    my @oparray;
2009
    my %opstatus;
2010
    # Build hash (%opstatus) with opstatus'es etc. to use for services that are in state unknown because of mon reload
2011
    if (-e $opfile) {
2012
        open(FILE, $opfile) or {throw Error::Simple("Unable to read $opfile")};
2013
        @oparray = <FILE>;
2014
        close(FILE);
2015
        foreach my $line (@oparray) {
2016
            my @pairs = split(/ /,$line);
2017
            my %h;
2018
            foreach my $pair (@pairs) {
2019
                my ($key, $val) = split(/=/,$pair);
2020
                if ($key eq 'last_result' || !$dounbackslash) {
2021
                    $obj->{$key} = $val;
2022
                } else {
2023
                    $val =~ s/\\/\\x/g;
2024
                    $obj->{$key} = unbackslash($val);
2025
                }
2026
            }
2027
            $obj->{'line'} = $line;
2028
            $opstatus{"$obj->{'group'}:$obj->{'service'}"} = \%h;
2029
        }
2030
    }
2031
    return %opstatus;
2032
}
2033
2034
sub getOpstatus {
2035
    my ($selgroup, $selservice, $usemoncmd) = @_;
2036
    my %opcodes = ("", "checking", "0", "down", "1", "ok", "3", "3", "4", "4", "5", "5", "6", "6", "7", "checking", "9", "disabled");
2037
    my %s;
2038
    my %opstatus;
2039
    my %savedopstatus = getSavedOpstatus(1);
2040
    my %sysdoms;
2041
2042
    my %disabled;
2043
    my %desc;
2044
    my @dislist = split(/\n/, `/usr/bin/moncmd list disabled`);
2045
    foreach my $disline (@dislist) {
2046
        my ($a, $b, $c, $d) = split(' ', $disline);
2047
        $disabled{"$b" . ($d?":$d":'')} = 1;
2048
    };
2049
    my %emails;
2050
    my @emaillist = split(/\n/, `/bin/cat /etc/mon/mon.cf`);
2051
    my $emailuuid;
2052
    foreach my $eline (@emaillist) {
2053
        my ($a, $b, $c, $d) = split(/ +/, $eline, 4);
2054
        if ($a eq 'watch') {
2055
            if ($b =~ /\S+-\S+-\S+-\S+-\S+/) {$emailuuid = $b;}
2056
            else {$emailuuid = ''};
2057
        }
2058
        $emails{$emailuuid} = $d if ($emailuuid && $b eq 'alert' && $c eq 'stabile.alert');
2059
    };
2060
2061
    # We are dealing with a system group rather than a domain, build hash of domains in system
2062
    if ($selgroup && !$domreg{$selgroup} && $register{$selgroup}) {
2063
        foreach my $valref (values %domreg) {
2064
            $sysdoms{$valref->{'uuid'}} = $selgroup if ($valref->{system} eq $selgroup);
2065
        }
2066
    }
2067
    if ($usemoncmd) {
2068
        my @oparray = split("\n", `/usr/bin/moncmd list opstatus`);
2069
        foreach my $line (@oparray) {
2070
            my @pairs = split(/ /,$line);
2071
            my %h;
2072
            foreach my $pair (@pairs) {
2073
                my ($key, $val) = split(/=/,$pair);
2074
                if ($key eq 'last_result') {
2075
                    $obj->{$key} = $val;
2076
                } else {
2077
                    $val =~ s/\\/\\x/g;
2078
                    $obj->{$key} = unbackslash($val);
2079
                }
2080
            }
2081
            if (!$selgroup || $sysdoms{$obj->{'group'}}
2082
                (!$selservice && $selgroup eq $obj->{'group'}) ||
2083
                ($selgroup eq $obj->{'group'} && $selservice eq $obj->{'service'})
2084
            )
2085
            {
2086
                #$obj->{'line'} = $line;
2087
                #$opstatus{"$obj->{'group'}:$obj->{'service'}"} = \%h;
2088
                $s{$obj->{'group'}}->{$obj->{'service'}} = \%h if($obj->{'group'});
2089
            }
2090
        }
2091
2092
    } else {
2093
        my $monc;
2094
        $monc = new Mon::Client (
2095
            host => "127.0.0.1"
2096
        );
2097
        $monc->connect();
2098
        %desc = $monc->list_descriptions; # Get descriptions
2099
        #%disabled = $monc->list_disabled;
2100
        $selgroup = '' if (%sysdoms);
2101
        my @selection = [$selgroup, $selservice];
2102
        if ($selgroup && $selservice) {%s = $monc->list_opstatus( @selection );}
2103
        elsif ($selgroup) {%s = $monc->list_opstatus( (@selection) );}# List selection
2104
        else {%s = $monc->list_opstatus;} # List all
2105
        $monc->disconnect();
2106
    }
2107
2108
    foreach my $group (keys %s) {
2109
        if ($domreg{$group} && ($domreg{$group}->{'user'} eq $user || $fulllist)) {
2110
            foreach my $service (values %{$s{$group}}) {
2111
2112
                next if (%sysdoms && !$sysdoms{$group});
2113
                next unless ($service->{'monitor'});
2114
                my $ostatus = $service->{'opstatus'};
2115
                my $id = "$group:$service->{'service'}";
2116
                if (%sysdoms) {
2117
                    $service->{'system'} = $sysdoms{$group};
2118
                }
2119
                if ($ostatus == 7 && $savedopstatus{$id}) { # Get status etc. from %savedopstatus because mon has recently been reloaded
2120
                    $service->{'opstatus'} = $savedopstatus{$id}->{'opstatus'};
2121
                    $service->{'last_success'} = $savedopstatus{$id}->{'last_success'};
2122
                    $service->{'last_check'} = $savedopstatus{$id}->{'last_check'};
2123
                    $service->{'last_detail'} = $savedopstatus{$id}->{'last_detail'};
2124
                    $service->{'checking'} = "1";
2125
                }
2126
#                if (($ostatus == 7 || $ostatus == 0) &&  $savedopstatus{$id}->{'ack'}) { # Get ack because mon has recently been reloaded
2127
                if ($ostatus == 7 &&  $savedopstatus{$id}->{'ack'}) { # Get ack because mon has recently been reloaded
2128
                    $service->{'ack'} = $savedopstatus{$id}->{'ack'};
2129
                    $service->{'ackcomment'} = $savedopstatus{$id}->{'ackcomment'};
2130
                    $service->{'first_failure'} = $savedopstatus{$id}->{'first_failure'};
2131
                }
2132
                $service->{'ackcomment'} = $1 if ($service->{'ackcomment'} =~ /^: *(.*)/);
2133
                my $status = $opcodes{$service->{'opstatus'}};
2134
                if ($disabled{$id} || $disabled{$group}){
2135
                    $status = 'disabled';
2136
                    $service->{'opstatus'} = "9";
2137
                }
2138
                $service->{'status'} = $status;
2139
                $service->{'id'} = $id;
2140
                $service->{'name'} = "$domreg{$group}->{'name'} : $service->{'service'}";
2141
                $service->{'servername'} = $domreg{$group}->{'name'};
2142
                $service->{'serveruuid'} = $domreg{$group}->{'uuid'};
2143
                $service->{'serverstatus'} = $domreg{$group}->{'status'};
2144 6fdc8676 hq
                my $serverip = `cat /etc/mon/mon.cf |sed -n -e 's/^hostgroup $group //p'`;
2145
                chomp $serverip;
2146
                $service->{'serverip'} = $serverip;
2147 95b003ff Origo
2148
                my $desc = $desc{$group}->{$service->{'service'}};
2149
                $desc = '' if ($desc eq '--');
2150
                $service->{'desc'} = $desc;
2151
                $service->{'last_detail'} =~ s/-//g;
2152
                $service->{'last_detail'} =~ s/^\n//;
2153
                $service->{'last_detail'} =~ s/\n+/\n/g;
2154
2155
                my $monitor = $service->{'monitor'};
2156
2157
                $service->{'request'} = $service->{'okstring'} = $service->{'port'} = $service->{'email'} = '';
2158
                #$monitor = URI::Escape::uri_unescape($monitor);
2159
                #if ( $monitor =~ /stabile-diskspace\.monitor\s+(\S+)\s+(\S+)\s+(\S+)/ ) {
2160
                if ( $monitor =~ /stabile-diskspace\.monitor\s+(\S+)\s+(\S+)/ ) {
2161
                    $service->{'request'} = $2 if ( $monitor =~ /stabile-diskspace\.monitor\s+(\S+)\s+(\S+)/ );
2162
                    $service->{'okstring'} = $3 if ( $monitor =~ /stabile-diskspace\.monitor\s+(\S+)\s+(\S+)\s+(\S+)/ );
2163
                }
2164
2165
                $service->{'okstring'} = $1 if ( $monitor =~ /--okstring \"(.*)\"/ );
2166
                $service->{'okstring'} = $1 if ( $monitor =~ /-l \"(.*)\"/ );
2167
#                $service->{'request'} = $2 if ( $monitor =~ /http(s*):\/\/.+\/(.*)/ );
2168
                $service->{'request'} = $2 if ( $monitor =~ /http(s*):\/\/[^\/]+\/(.*)/ );
2169
                $service->{'port'} = $2 if ( $monitor =~ /http(s*):\/\/.+:(\d+)/ );
2170
                $service->{'request'} = $1 if ( $monitor =~ /--from \"(\S*)\"/ );
2171
                $service->{'okstring'} = $1 if ( $monitor =~ /--to \"(\S*)\"/ );
2172
                $service->{'port'} = $1 if ( $monitor =~ /--port (\d+)/ );
2173
2174
                $service->{'email'} = $emails{$group};
2175
2176
                $opstatus{$id} = $service;
2177
                #push @monitors, $service;
2178
            }
2179
        }
2180
    }
2181
    return %opstatus;
2182
}
2183
2184
sub change_monitor_email {
2185
    my $serveruuid = shift;
2186
    my $email = shift;
2187
    my $match;
2188
    if ($email && $serveruuid) {
2189
        unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
2190
        if ($domreg{$serveruuid}->{'user'} eq $user || $isadmin) {
2191
            local($^I, @ARGV) = ('.bak', "/etc/mon/mon.cf"); # $^I is the in-place edit switch
2192
            # undef $/; # This makes <> read in the entire file in one go
2193
            my $uuidmatch;
2194
            while (<>) {
2195
                if (/^watch (\S+)/) {
2196
                    if ($1 eq $serveruuid) {$uuidmatch = $serveruuid}
2197
                    else {$uuidmatch = ''};
2198
                };
2199
                if ($uuidmatch) {
2200
                    $match = 1 if (s/(stabile\.alert) (.*)/$1 $email/);
2201
                }
2202
                print;
2203
                close ARGV if eof;
2204
        #       $match = 1 if (s/(watch $serveruuid\n.+\n.+\n.+\n.+\n.+)$oldemail(\n.+)$oldemail(\n.+)$oldemail/$1$email$2$email$3$email/g);
2205
            }
2206
        #    $/ = "\n";
2207
        }
2208
    }
2209
    return $match;
2210
}
2211
2212
# Delete all monitors belonging to a server
2213
sub deleteMonitors {
2214
    my ($serveruuid) = @_;
2215
    my $match;
2216
    if ($serveruuid) {
2217
        if ($domreg{$serveruuid}->{'user'} eq $user || $isadmin) {
2218
            local($^I, @ARGV) = ('.bak', "/etc/mon/mon.cf");
2219
            # undef $/; # This makes <> read in the entire file in one go
2220
            my $uuidmatch;
2221
            while (<>) {
2222
                if (/^watch (\S+)/) {
2223
                    if ($1 eq $serveruuid) {$uuidmatch = $serveruuid}
2224
                    else {$uuidmatch = ''};
2225
                };
2226
                if ($uuidmatch) {
2227
                    $match = 1;
2228
                } else {
2229
                    #chomp;
2230
                    print unless (/^hostgroup $serveruuid/);
2231
                }
2232
                close ARGV if eof;
2233
            }
2234
            #$/ = "\n";
2235
        }
2236
        unlink glob "/var/log/stabile/*:$serveruuid:*";
2237
    }
2238
    `/usr/bin/moncmd reset keepstate` if ($match);
2239
    return $match;
2240
}
2241
2242
# Add a monitors to a server when building system
2243
sub addSimpleMonitors {
2244
    my ($serveruuid, $email, $monitors_ref) = @_;
2245
    my @mons = @{$monitors_ref};
2246
2247
    my $match;
2248
    my $hmatch1;
2249
    my $hmatch2;
2250
    my $hmatch3;
2251 3657de20 Origo
    if ($serveruuid && $domreg{$serveruuid}) {
2252 95b003ff Origo
        if ($domreg{$serveruuid}->{'user'} eq $user || $isadmin) {
2253
            my $monitors = {
2254
                ping=>"fping.monitor",
2255
                diskspace=>"stabile-diskspace.monitor $serveruuid",
2256
                http=>"http_tppnp.monitor",
2257
                https=>"http_tppnp.monitor",
2258
                smtp=>"smtp3.monitor",
2259
                smtps=>"smtp3.monitor",
2260
                imap=>"imap.monitor",
2261
                imaps=>"imap-ssl.monitor",
2262
                ldap=>"ldap.monitor",
2263
                telnet=>"telnet.monitor"
2264
            };
2265
2266
            if (!$email) {$email = $domreg{$serveruuid}->{'alertemail'}};
2267
            if (!$email && $register{$domreg{$serveruuid}->{'system'}}) {$email = $register{$domreg{$serveruuid}->{'system'}}->{'alertemail'}};
2268
            if (!$email) {$email = $userreg{$user}->{'alertemail'}};
2269
2270
            unless (tie %networkreg,'Tie::DBI', {
2271
                db=>'mysql:steamregister',
2272
                table=>'networks',
2273
                key=>'uuid',
2274
                autocommit=>0,
2275
                CLOBBER=>3,
2276
                user=>$dbiuser,
2277
                password=>$dbipasswd}) {throw Error::Simple("Stroke=Error Register could not be accessed")};
2278
2279
            my $networkuuid1 = $domreg{$serveruuid}->{'networkuuid1'};
2280
            my $networktype = $networkreg{$networkuuid1}->{'type'};
2281
            my $ip = $networkreg{$networkuuid1}->{'internalip'};
2282
            $ip = $networkreg{$networkuuid1}->{'externalip'} if ($networktype eq 'externalip');
2283
            $ip = '127.0.0.1' if ($networktype eq 'gateway'); #Dummy IP - we only support diskspace checks
2284
            untie %networkreg;
2285
2286
            local($^I, @ARGV) = ('.bak', "/etc/mon/mon.cf");
2287
            my $uuidmatch;
2288
            while (<>) {
2289
                $hmatch1=1 if (/^hostgroup/);
2290
                $hmatch2=1 if ($hmatch1 && !/^hostgroup/);
2291
                if ($hmatch1 && $hmatch2 && !$hmatch3) {
2292
                    print "hostgroup $serveruuid $ip\n";
2293
                    $hmatch3 = 1;
2294
                }
2295
                print;
2296
                if (eof) {
2297
                    print "watch $serveruuid\n";
2298
                    foreach $service (@mons) {
2299
                        print <<END;
2300
    service $service
2301
        interval 1m
2302
        monitor $monitors->{$service}
2303
        description --
2304
        period
2305
            alert stabile.alert $email
2306
            upalert stabile.alert $email
2307
            startupalert stabile.alert $email
2308
            numalerts 2
2309
            no_comp_alerts
2310
END
2311
;
2312
                        my $oplogfile = "/var/log/stabile/$year-$month:$serveruuid:$service";
2313
                        unless (-e $oplogfile) {
2314
                            `/usr/bin/touch "$oplogfile"`;
2315
                            `/bin/chown mon:mon "$oplogfile"`;
2316
                            my $logline = "$current_time, UP, STARTUP, $pretty_time";
2317
                            `/bin/echo >> $oplogfile "$logline"`;
2318
                        }
2319
                    }
2320
                    close ARGV;
2321
                }
2322
            }
2323
        } else {
2324 3657de20 Origo
            return "Server $serveruuid not available";
2325 95b003ff Origo
        }
2326
    } else {
2327 3657de20 Origo
        return "Invalid uuid $serveruuid";
2328 95b003ff Origo
    }
2329
    return "OK";
2330
}
2331
2332
sub Monitors_save {
2333
    my ($id, $action, $obj) = @_;
2334
    if ($help) {
2335
        return <<END
2336
PUT:id:
2337
Enable, disable or acknowledge a monitor. Id is of the form serveruuid:service
2338
END
2339
    }
2340
2341
    my $delete = ($action eq 'monitors_remove'); # Delete an existing monitor
2342
    $id = $obj->{'id'} || $id; # ID in params supersedes id in path
2343
    my $update; # Update an existing monitor?
2344
    my $postmsg;
2345
2346
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
2347
    unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {return "Unable to access networks register"};
2348
    foreign_require("mon", "mon-lib.pl");
2349
    $conf = mon::get_mon_config();
2350
#    my @ogroups = mon::find("hostgroup", $conf);
2351
#    my @owatches = mon::find("watch", $conf);
2352
    my $doreset;
2353
    my $email;
2354
2355
    my $serveruuid;
2356
    my $servicename;
2357
    if ($id =~ /(.+):(.+)/){ # List specific monitor for specific server
2358
        $serveruuid = $1;
2359
        $servicename = $2;
2360
    }
2361
    $serveruuid = $serveruuid || $obj->{'serveruuid'};
2362
    my $desc = $obj->{'desc'};
2363
    my $okstring = $obj->{'okstring'};
2364
    my $request = $obj->{'request'};
2365
    my $port = $obj->{'port'};
2366
    $servicename = $servicename || $obj->{'service'};
2367
    my $interval = '1'; # Number of minutes between checks
2368
    $interval = '20' if ($servicename eq 'diskspace');
2369
    $email = $obj->{'alertemail'};
2370
    my $serv = $domreg{$serveruuid};
2371
    if (!$email) {$email = $serv->{'alertemail'}};
2372
    if (!$email && $serv->{'system'}) {$email = $register{$serv->{'system'}}->{'alertemail'}};
2373
    if (!$email) {$email = $userreg{$user}->{'alertemail'}};
2374
    my $networkuuid1 = $serv->{'networkuuid1'};
2375
    my $networktype = $networkreg{$networkuuid1}->{'type'};
2376
    my $deleteid;
2377
    
2378
    if (!$serveruuid || !$servicename) {
2379
        $postmsg = qq|No monitor specified|;
2380
        $postreply = "Status=Error $postmsg\n";
2381
        return $postreply;
2382
    }
2383
2384
    if (!$delete && $networktype eq 'gateway' && $servicename ne 'diskspace'
2385
            && (!$obj->{'serverip'} || !($obj->{'serverip'} =~ /^\d+\.\d+\.\d+\.\d+$/) )) {
2386
        $postmsg = qq|Invalid IP address|;
2387
    } elsif (!$domreg{$serveruuid}) {
2388
        $postmsg = qq|Unknown server $serveruuid|;
2389
# Security check
2390
    } elsif ($domreg{$serveruuid}->{'user'} ne $user) {
2391
        $postmsg = qq|Bad server|;
2392
    } else {
2393
        my $monitors = {
2394
            ping=>"fping.monitor",
2395
            diskspace=>"stabile-diskspace.monitor",
2396
            http=>"http_tppnp.monitor",
2397
            https=>"http_tppnp.monitor",
2398
            smtp=>"smtp3.monitor",
2399
            smtps=>"smtp3.monitor",
2400
            imap=>"imap.monitor",
2401
            imaps=>"imap-ssl.monitor",
2402
            ldap=>"ldap.monitor",
2403
            telnet=>"telnet.monitor"
2404
        };
2405
        my $args = '';
2406
        my $ip = $networkreg{$networkuuid1}->{'internalip'};
2407
        $ip = $networkreg{$networkuuid1}->{'externalip'} if ($networktype eq 'externalip');
2408
        $ip = '127.0.0.1' if ($networktype eq 'gateway' && $servicename eq 'diskspace'); #Dummy IP - we only support diskspace checks
2409
        if ($networktype eq 'gateway' && $servicename eq 'ping') {
2410
            $ip = $obj->{'serverip'};
2411
        # We can only check 10.x.x.x addresses on vlan because of routing
2412
            if ($ip =~ /^10\./) {
2413
                $monitors->{'ping'} = "stabile-arping.monitor";
2414
                my $id = $networkreg{$networkuuid1}->{'id'};
2415
                if ($id > 1) {
2416
                    my $if = $datanic . "." . $id;
2417
                    $args = " $if";
2418
                } else {
2419
                    $args = " $extnic";
2420
                }
2421
                $args .= " $ip";
2422
            }
2423
        }
2424
2425
        if ($servicename eq 'ping') {
2426
            ;
2427
        } elsif ($servicename eq 'diskspace'){
2428
            #my $macip = $domreg{$serveruuid}->{'macip'};
2429
            #my $image = URI::Escape::uri_escape($domreg{$serveruuid}->{'image'});
2430
            #$args .= " $macip $image $serveruuid";
2431
            $args .= " $serveruuid";
2432
            $args .= ($request)?" $request":" 10"; #min free %
2433
            $args .= " $okstring" if ($okstring); #Comma-separated partion list, e.g. 0,1
2434
        } elsif ($servicename eq 'http'){
2435
            $args .= " --okcodes \"200,403\" --debuglog -";
2436
            $args .= " --okstring \"$okstring\"" if ($okstring);
2437
            $args .= " http://$ip";
2438
            $args .= ":$port" if ($port && $port>10 && $port<65535);
2439
            $request = substr($request,1) if ($request =~ /^\//);
2440
            $args .= "/$request" if ($request);
2441
        } elsif ($servicename eq 'https'){
2442
            $args .= " --okcodes \"200,403\" --debuglog -";
2443
            $args .= " --okstring \"$okstring\"" if ($okstring);
2444
            $args .= " https://$ip";
2445
            $args .= ":$port" if ($port && $port>10 && $port<65535);
2446
            $request = substr($request,1) if ($request =~ /^\//);
2447
            $args .= "/$request" if ($request);
2448
        } elsif ($servicename eq 'smtp'){
2449
            $args .= " --from \"$request\"" if ($request);
2450
            $args .= " --to \"$okstring\"" if ($okstring);
2451
            $args .= " --port $port" if ($port && $port>10 && $port<65535);
2452
        } elsif ($servicename eq 'smtps'){
2453
            $args .= " --requiretls";
2454
            $args .= " --from \"$request\"" if ($request);
2455
            $args .= " --to \"$okstring\"" if ($okstring);
2456
            $args .= " --port $port" if ($port && $port>10 && $port<65535);
2457
        } elsif ($servicename eq 'imap'){
2458
            $args .= " -p $port" if ($port && $port>10 && $port<65535);
2459
        } elsif ($servicename eq 'imaps'){
2460
            $args .= " -p $port" if ($port && $port>10 && $port<65535);
2461
        } elsif ($servicename eq 'ldap'){
2462
            $args .= " --port $port" if ($port && $port>10 && $port<65535);
2463 d24d9a01 hq
            $args .= " --basedn \"$request\"" if ($request);
2464
            $args .= " --attribute \"$okstring\"" if ($okstring);
2465 95b003ff Origo
        } elsif ($servicename eq 'telnet'){
2466
            $args .= " -l \"$okstring\"" if ($okstring);
2467
            $args .= " -p $port" if ($port && $port>10 && $port<65535);
2468
        }
2469
2470
        my @ogroups = mon::find("hostgroup", $conf);
2471
        my @owatches = mon::find("watch", $conf);
2472
2473
        $group = { 'name' => 'hostgroup', 'values' => [ $serveruuid, $ip ] };
2474
        my $ogroup = undef;
2475
        my $i;
2476
        for($i=0; $i<scalar @ogroups; $i++) {
2477
            if ($ogroups[$i]->{'values'}[0] eq  $serveruuid) {
2478
                $ogroup = $ogroups[$i];
2479
                last;
2480
            }
2481
        }
2482
        mon::save_directive($conf, $ogroup, $group); #Update host hostgroup
2483
2484
        $watch = { 'name' => 'watch','values' => [ $serveruuid ], 'members' => [ ] };
2485
        my $owatch = undef;
2486
        my $oservice = undef;
2487
        my $widx = undef;
2488
        for($i=0; $i<scalar @owatches; $i++) { # Run through all watches and locate match
2489
            if ($owatches[$i]->{'values'}[0] eq  $serveruuid) {
2490
                $owatch = $watch = $owatches[$i];
2491
                $widx = $owatch->{'index'};
2492
                my @oservices = mon::find("service", $watch->{'members'});
2493
                for($j=0; $j<@oservices; $j++) { # Run through all services for watch and locate match
2494
                    if ($oservices[$j]->{'values'}[0] eq $servicename) {
2495
                        $oservice = $oservices[$j];
2496
                        my $newmonargs = "$monitors->{$servicename}$args";
2497
                        $newmonargs =~ s/\s+$//; # Remove trailing spaces
2498
                        my $oldmonargs = "$oservices[$j]->{'members'}[2]->{'values'}[0] $oservices[$j]->{'members'}[2]->{'values'}[1]";
2499
                        $oldmonargs =~ s/\s+$//; # Remove trailing spaces
2500
                        if ($newmonargs ne $oldmonargs) {
2501
                            $update = 1; #We are changing an existing service definition
2502
                        };
2503
                        last;
2504
                    }
2505
                }
2506
                last;
2507
            }
2508
        }
2509
        my $in = {
2510
            args=>undef,
2511
            desc=>"$desc",
2512
            idx=>$widx,
2513
            interval=>$interval,
2514
            interval_u=>'m',
2515
            monitor=>$monitors->{$servicename} . $args,
2516
            monitor_def=>1,
2517
            name=>$servicename,
2518
            other=>undef,
2519
            sidx=>undef,
2520
            delete=>$delete,
2521
            email=>$email
2522
        };
2523
2524
        if ($update || $delete) {
2525
            unlink glob "/var/log/stabile/*:$serveruuid:$servicename";
2526
        } else {
2527
            my $oplogfile = "/var/log/stabile/$year-$month:$serveruuid:$servicename";
2528
            unless (-e $oplogfile) {
2529
                `/usr/bin/touch "$oplogfile"`;
2530
                `/bin/chown mon:mon "$oplogfile"`;
2531
                my $logline = "$current_time, UP, STARTUP, $pretty_time";
2532
                `/bin/echo >> $oplogfile "$logline"`;
2533
            }
2534
        }
2535
        $deleteid = (($delete || $update)?"$serveruuid:$servicename":'');
2536
        save_service($in, $owatch, $oservice);
2537
        $doreset = 1;
2538
        $obj->{'last_check'} = '--';
2539
        $obj->{'opstatus'} = '7';
2540
        $obj->{'status'} = 'checking';
2541
        $obj->{'alertemail'} = $email;
2542
        mon::flush_file_lines();
2543
        $main::syslogit->($user, 'info', "updating monitor $serveruuid:$servicename" .  (($delete)?" delete":""));
2544
        saveOpstatus($deleteid);
2545
        `/usr/bin/moncmd reset keepstate`;
2546
    }
2547
2548
    untie %networkreg;
2549
    untie %domreg;
2550
2551
    $postreply = to_json(\%h, {pretty => 1});
2552
    $postmsg = "OK" unless ($postmsg);
2553
    return $postreply;
2554
}
2555
2556
## Copied from save_service.cgi (from webmin) and slightly modified - well heavily perhaps
2557
2558
sub save_service {
2559
    my $sin = shift;
2560
    my $owatch = shift;
2561
    my $oservice = shift;
2562
    my %in = %{$sin};
2563
    my $oldservice = undef;
2564
    my $service;
2565
    if ($oservice) {
2566
        # $oldservice = $service = $watch->{'members'}->[$in{'sidx'}];
2567
        $oldservice = $service = $oservice;
2568
    } else {
2569
        $service = { 'name' => 'service',
2570
                 'indent' => '    ',
2571
                 'members' => [ ] };
2572
    }
2573
2574
    if ($in{'delete'}) {
2575
        # Delete this service from the watch
2576
        mon::save_directive($watch->{'members'}, $service, undef) if ($oservice);
2577
        my @rservices = mon::find("service", $watch->{'members'});
2578
        # Delete watch and hostgroup if no services left
2579
        if (@rservices==0) {
2580
            mon::save_directive($conf, $watch, undef);
2581
            mon::save_directive($conf, $group, undef);
2582
        }
2583
    } else {
2584
        # Validate and store service inputs
2585
        $in{'name'} =~ /^\S+$/ || {$in{'name'} = 'ping'};
2586
        $service->{'values'} = [ $in{'name'} ];
2587
        $in{'interval'} =~ /^\d+$/ || {$in{'interval'} = 1};
2588
2589
        &set_directive($service->{'members'}, "interval", $in{'interval'}.$in{'interval_u'});
2590
2591
        if ($in{'monitor_def'}) {
2592
            &set_directive($service->{'members'}, "monitor", $in{'monitor'}.' '.$in{'args'});
2593
        }
2594
        else {
2595
            $in{'other'} =~ /^\S+$/ || return "No other monitor specified";
2596
            &set_directive($service->{'members'}, "monitor", $in{'other'}.' '.$in{'args'});
2597
        }
2598
2599
        # Save the description
2600
        if ($in{'desc'}) {
2601
            my $desc = $in{'desc'};
2602
            $desc =~ tr/\n/ /;
2603
            &set_directive($service->{'members'}, "description", $in{'desc'});
2604
        }
2605
        else {
2606
            &set_directive($service->{'members'}, "description", '--');
2607
        }
2608
2609
        my $period = { 'name' => 'period', 'members' => [ ] };
2610
        my @alert;
2611
        my @v = ( "stabile.alert", $in{'email'} );
2612
        my @num = (2); # The number of alerts to send
2613
        push(@alert, { 'name' => 'alert', 'values' => \@v });
2614
		&set_directive($period->{'members'}, "alert", @alert);
2615
        my @upalert;
2616
        push(@upalert, { 'name' => 'upalert', 'values' => \@v });
2617
		&set_directive($period->{'members'}, "upalert", @upalert);
2618
        my @startupalert;
2619
        push(@startupalert, { 'name' => 'startupalert', 'values' => \@v });
2620
		&set_directive($period->{'members'}, "startupalert", @startupalert);
2621
        my @numalerts;
2622
        push(@numalerts, { 'name' => 'numalerts', 'values' => \@num });
2623
		&set_directive($period->{'members'}, "numalerts", @numalerts);
2624
        my @no_comp_alerts;
2625
        push(@no_comp_alerts, { 'name' => 'no_comp_alerts', 'values' => 0 });
2626
		&set_directive($period->{'members'}, "no_comp_alerts", @no_comp_alerts);
2627
2628
        push(@period, $period);
2629
2630
    	&set_directive($service->{'members'}, "period", @period);
2631
2632
        if ($owatch) {
2633
            # Store the service in existing watch in the config file
2634
            mon::save_directive($watch->{'members'}, $oldservice, $service);
2635
        } else {
2636
            # Create new watch
2637
            push(@service, $service);
2638
            &set_directive($watch->{'members'}, "service", @service);
2639
            mon::save_directive($conf, undef, $watch);
2640
        }
2641
    }
2642
}
2643
2644
# set_directive(&config, name, value, value, ..)
2645
sub set_directive
2646
{
2647
local @o = mon::find($_[1], $_[0]);
2648
local @n = @_[2 .. @_-1];
2649
local $i;
2650
for($i=0; $i<@o || $i<@n; $i++) {
2651
	local $idx = &indexof($o[$i], @{$_[0]}) if ($o[$i]);
2652
	local $nv = ref($n[$i]) ? $n[$i] : { 'name' => $_[1],
2653
					     'values' => [ $n[$i] ] }
2654
						if (defined($n[$i]));
2655
	if ($o[$i] && defined($n[$i])) {
2656
		$_[0]->[$idx] = $nv;
2657
		}
2658
	elsif ($o[$i]) {
2659
		splice(@{$_[0]}, $idx, 1);
2660
		}
2661
	else {
2662
		push(@{$_[0]}, $nv);
2663
		}
2664
	}
2665
}
2666
2667
sub getSystemsListing {
2668
    my ($action, $curuuid, $username) = @_;
2669
    $username = $user unless ($username);
2670
    my @domregvalues = values %domreg;
2671
    my @curregvalues;
2672
    my %curreg;
2673
2674
    $userfullname = $userreg{$username}->{'fullname'};
2675
    $useremail = $userreg{$username}->{'email'};
2676
    $userphone = $userreg{$username}->{'phone'};
2677
    $useropfullname = $userreg{$username}->{'opfullname'};
2678
    $useropemail = $userreg{$username}->{'opemail'};
2679
    $useropphone = $userreg{$username}->{'opphone'};
2680
    $useralertemail = $userreg{$username}->{'alertemail'};
2681
2682
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$postreply = "Unable to access image register"; return;};
2683 d24d9a01 hq
    unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {return "Unable to access networks register"};
2684 95b003ff Origo
2685
    # Collect systems from domains and include domains as children
2686
    if ($action ne 'flatlist') { # Dont include children in select
2687
        my @imagenames = qw(image image2 image3 image4);
2688
        foreach my $valref (@domregvalues) {
2689
        # Only include VM's belonging to current user (or all users if specified and user is admin)
2690
            if ($username eq $valref->{'user'} || $fulllist) {
2691
                next unless (!$curuuid || ($valref->{'uuid'} eq $curuuid || $valref->{'system'} eq $curuuid));
2692
2693
                my %val = %{$valref}; # Deference and assign to new ass array, effectively cloning object
2694
                my $sysuuid = $val{'system'};
2695
                my $dbobj = $register{$sysuuid};
2696
                $val{'memory'} += 0;
2697
                $val{'vcpu'} += 0;
2698
                $val{'nodetype'} = 'child';
2699
                $val{'fullname'} = $val{'fullname'} || $dbobj->{'fullname'} || $userfullname;
2700
                $val{'email'} = $val{'email'} || $dbobj->{'email'} || $useremail;
2701
                $val{'phone'} = $val{'phone'} || $dbobj->{'phone'} || $userphone;
2702
                $val{'opfullname'} = $val{'opfullname'} || $dbobj->{'opfullname'} || $useropfullname;
2703
                $val{'opemail'} = $val{'opemail'} || $dbobj->{'opemail'} || $useropemail;
2704
                $val{'opphone'} = $val{'opphone'} || $dbobj->{'opphone'} || $useropphone;
2705
                $val{'alertemail'} = $val{'alertemail'} || $dbobj->{'alertemail'} || $useralertemail;
2706 c899e439 Origo
                $val{'autostart'} = ($val{'autostart'})?'1':'';
2707 95b003ff Origo
2708
                foreach my $img (@imagenames) {
2709
                    if ($imagereg{$val{$img}} && $imagereg{$val{$img}}->{'storagepool'} == -1) {
2710
                        $val{'nodestorage'} += $imagereg{$val{$img}}->{'virtualsize'};
2711
                    } else {
2712
                        $val{'storage'} += $imagereg{$val{$img}}->{'virtualsize'} if ($imagereg{$val{$img}});
2713
                    }
2714
                }
2715
                $val{'externalips'} += 1 if ($networkreg{$val{'networkuuid1'}} && $networkreg{$val{'networkuuid1'}}->{'type'} =~ /externalip|ipmapping/);
2716
                $val{'externalips'} += 1 if ($networkreg{$val{'networkuuid2'}} && $networkreg{$val{'networkuuid2'}}->{'type'} =~ /externalip|ipmapping/);
2717
                $val{'externalips'} += 1 if ($networkreg{$val{'networkuuid3'}} && $networkreg{$val{'networkuuid3'}}->{'type'} =~ /externalip|ipmapping/);
2718
                $val{'networktype1'} = $networkreg{$val{'networkuuid1'}}->{'type'} if ($networkreg{$val{'networkuuid1'}});
2719
                $val{'imageuuid'} = $imagereg{$val{'image'}}->{'uuid'} if ($imagereg{$val{'image'}});
2720
                $val{'imageuuid2'} = $imagereg{$val{'image2'}}->{'uuid'} if ($imagereg{$val{'image2'}} && $val{'image2'} && $val{'image2'} ne '--');
2721 afc024ef hq
                $val{'internalip'} = $networkreg{$val{'networkuuid1'}}->{'internalip'} if ($networkreg{$val{'networkuuid1'}});
2722
                $val{'externalip'} = $networkreg{$val{'networkuuid1'}}->{'externalip'} if ($networkreg{$val{'networkuuid1'}});
2723 95b003ff Origo
2724
                my $networkuuid1; # needed for generating management url
2725 c899e439 Origo
                if ($sysuuid && $sysuuid ne '--') { # We are dealing with a server that's part of a system
2726 95b003ff Origo
                    if (!$register{$sysuuid}) { #System does not exist - create it
2727
                        $sysname = $val{'name'};
2728
                        $sysname = $1 if ($sysname =~ /(.+)\..*/);
2729
                        $sysname =~ s/server/System/i;
2730
                        $register{$sysuuid} = {
2731
                            uuid => $sysuuid,
2732
                            name => $sysname,
2733
                            user => $username,
2734
                            created => $current_time
2735
                        };
2736
                    }
2737
2738
                    my %pval = %{$register{$sysuuid}};
2739
                    $pval{'status'} = '--';
2740
                    $pval{'issystem'} = 1;
2741
                    $pval{'fullname'} = $pval{'fullname'} || $userfullname;
2742
                    $pval{'email'} = $pval{'email'} || $useremail;
2743
                    $pval{'phone'} = $pval{'phone'} || $userphone;
2744
                    $pval{'opfullname'} = $pval{'opfullname'} || $useropfullname;
2745
                    $pval{'opemail'} = $pval{'opemail'} || $useropemail;
2746
                    $pval{'opphone'} = $pval{'opphone'} || $useropphone;
2747
                    $pval{'alertemail'} = $pval{'alertemail'} || $useralertemail;
2748 c899e439 Origo
                    $pval{'autostart'} = ($pval{'autostart'})?'1':'';
2749 95b003ff Origo
2750
                    my @children;
2751
                    if ($curreg{$sysuuid}->{'children'}) {
2752
                        @children = @{$curreg{$sysuuid}->{'children'}};
2753
                    }
2754
                    # If system has an admin image, update networkuuid1 with the image's server's info
2755
                    if ($pval{'image'} && $pval{'image'} ne '--') {
2756
                        my $dbimg = $imagereg{$pval{'image'}};
2757
                        $networkuuid1 = $domreg{$dbimg->{'domains'}}->{'networkuuid1'} if ($domreg{$dbimg->{'domains'}});
2758
                        my $externalip = $networkreg{$networkuuid1}->{'externalip'} if ($networkreg{$networkuuid1});
2759
                        $register{$sysuuid}->{'networkuuid1'} = $networkuuid1;
2760
                        $register{$sysuuid}->{'internalip'} = $networkreg{$networkuuid1}->{'internalip'} if ($networkreg{$networkuuid1});
2761
                        $pval{'master'} = $dbimg->{'master'};
2762
                        $pval{'appid'} = $dbimg->{'appid'};
2763
                        $pval{'version'} = $dbimg->{'version'};
2764
                        my $managementurl;
2765
                        $managementurl = $dbimg->{'managementlink'};
2766
                        $managementurl =~ s/\{uuid\}/$networkuuid1/;
2767
                        $managementurl =~ s/\{externalip\}/$externalip/;
2768
                        $pval{'managementurl'} = $managementurl;
2769
                        my $upgradeurl;
2770
                        $upgradeurl = $dbimg->{'upgradelink'};
2771
                        $upgradeurl =~ s/\{uuid\}/$networkuuid1/;
2772
                        $pval{'upgradeurl'} = $upgradeurl;
2773
                        my $terminalurl;
2774
                        $terminalurl = $dbimg->{'terminallink'};
2775
                        $terminalurl =~ s/\{uuid\}/$networkuuid1/;
2776
                        $pval{'terminalurl'} = $terminalurl;
2777
                        $pval{'externalip'} = $externalip;
2778
                        $pval{'imageuuid'} = $dbimg->{'uuid'};
2779
                        $pval{'imageuuid2'} = $imagereg{$pval{'image2'}}->{'uuid'} if ($pval{'image2'} && $pval{'image2'} ne '--');
2780
                    }
2781
                    push @children,\%val;
2782
                    $pval{'children'} = \@children;
2783
                    $curreg{$sysuuid} = \%pval;
2784
                } else { # This server is not part of a system
2785
                    $sysuuid = $val{'uuid'};
2786
                    my $dbimg = $imagereg{$val{'image'}};
2787
                    $networkuuid1 = $domreg{$dbimg->{'domains'}}->{'networkuuid1'} if ($domreg{$dbimg->{'domains'}});
2788
                    my $externalip;
2789
                    $externalip = $networkreg{$networkuuid1}->{'externalip'} if ($networkreg{$networkuuid1});
2790
                    $val{'networkuuid1'} = $networkuuid1;
2791
                    $val{'internalip'} = $networkreg{$networkuuid1}->{'internalip'} if ($networkreg{$networkuuid1});
2792
                    $val{'master'} = $dbimg->{'master'};
2793
                    $val{'appid'} = $dbimg->{'appid'};
2794
                    $val{'version'} = $dbimg->{'version'};
2795
                    $val{'imageuuid'} = $dbimg->{'uuid'};
2796
                    $val{'imageuuid2'} = $imagereg{$val{'image2'}}->{'uuid'} if ($val{'image2'} && $val{'image2'} ne '--' && $imagereg{$val{'image2'}});
2797
2798
                    my $managementurl = $dbimg->{'managementlink'};
2799
                    $managementurl =~ s/\{uuid\}/$networkuuid1/;
2800
                    $managementurl =~ s/\{externalip\}/$externalip/;
2801
                    $val{'managementurl'} = $managementurl;
2802
                    my $upgradeurl;
2803
                    $upgradeurl = $dbimg->{'upgradelink'};
2804
                    $upgradeurl =~ s/\{uuid\}/$networkuuid1/;
2805
                    $val{'upgradeurl'} = $upgradeurl;
2806
                    my $terminalurl;
2807
                    $terminalurl = $dbimg->{'terminallink'};
2808
                    $terminalurl =~ s/\{uuid\}/$networkuuid1/;
2809
                    $val{'terminalurl'} = $terminalurl;
2810
                    $val{'externalip'} = $externalip;
2811
                    $val{'system'} = '--';
2812
2813
                    $curreg{$sysuuid} = \%val;
2814
                }
2815
            }
2816
        }
2817
        tied(%register)->commit;
2818
    }
2819
    untie %imagereg;
2820
2821
    my @regvalues = values %register;
2822
    # Go through systems register, add empty systems and update statuses
2823
    foreach my $valref (@regvalues) {
2824
    # Only include items belonging to current user (or all users if specified and user is admin)
2825
        if ($username eq $valref->{'user'} || $fulllist) {
2826
            next unless (!$curuuid || $valref->{'uuid'} eq $curuuid);
2827
2828
            my %val = %{$valref};
2829
            # add empty system (must be empty since not included from going through servers
2830
            if (!($curreg{$val{'uuid'}})) {
2831
                $val{'issystem'} = 1;
2832
                $val{'status'} = 'inactive';
2833
                $curreg{$val{'uuid'}} = \%val;
2834
            } else {
2835
            # Update status
2836
                my $status = 'running';
2837 d24d9a01 hq
                my $externalips = 0;
2838 95b003ff Origo
                foreach my $child (@{$curreg{$val{'uuid'}}-> {'children'}}) {
2839
                    $status = $child->{'status'} unless ($child->{'status'} eq $status);
2840 d24d9a01 hq
                    $externalips += $child->{'externalips'} unless ($child->{'externalips'} eq '');
2841 95b003ff Origo
                }
2842
                $status = 'degraded' unless ($status eq 'running' || $status eq 'shutoff');
2843
                $curreg{$val{'uuid'}}->{'status'} = $status;
2844 d24d9a01 hq
                $curreg{$val{'uuid'}}->{'externalips'} = $externalips;
2845 322b9953 hq
                # $networkreg{$domreg{$curdomuuid}->{'networkuuid1'}}->{'internalip'};
2846
                if ($curuuid && !$curreg{$val{'uuid'}}->{'internalip'}) { # Add calling server's own internalip if it's part of an ad-hoc assembled system
2847
                    $curreg{$val{'uuid'}}->{'internalip'} = $networkreg{$domreg{$curdomuuid}->{'networkuuid1'}}->{'internalip'};
2848
                }
2849 95b003ff Origo
            }
2850
        }
2851
    }
2852 322b9953 hq
    untie %networkreg;
2853 95b003ff Origo
2854
    @curregvalues = values %curreg;
2855 2a63870a Christian Orellana
    my @sorted_systems = sort {$a->{'name'} cmp $b->{'name'}} @curregvalues;
2856
    @sorted_systems = sort {$a->{'status'} cmp $b->{'status'}} @sorted_systems;
2857 95b003ff Origo
2858
    if ($action eq 'tablelist') {
2859
        my $t2 = Text::SimpleTable->new(40,24,14);
2860
2861
        $t2->row('uuid', 'name', 'user');
2862
        $t2->hr;
2863
        my $pattern = $options{m};
2864
        foreach $rowref (@sorted_systems){
2865
            if ($pattern) {
2866
                my $rowtext = $rowref->{'uuid'} . " " . $rowref->{'name'} . " " . $rowref->{'user'};
2867
                next unless ($rowtext =~ /$pattern/i);
2868
            }
2869
            $t2->row($rowref->{'uuid'}, $rowref->{'name'}||'--', $rowref->{'user'}||'--');
2870
        }
2871
        return $t2->draw;
2872
    } elsif ($action eq 'removeusersystems') {
2873
        return @sorted_systems;
2874
    } elsif ($action eq 'arraylist') {
2875
        return @sorted_systems;
2876
    } elsif ($console) {
2877
        return Dumper(\@sorted_systems);
2878
    } else {
2879
        my %it = ('uuid','--','name','--', 'issystem', 1);
2880
        push(@sorted_systems, \%it) if ($action eq 'flatlist');
2881
        my $json_text = to_json(\@sorted_systems, {pretty => 1});
2882
        $json_text =~ s/"false"/false/g;
2883
        $json_text =~ s/"true"/true/g;
2884
#        $json_text =~ s/""/"--"/g;
2885
        $json_text =~ s/null/"--"/g;
2886
        $json_text =~ s/\x/ /g;
2887
        if ($action eq 'flatlist') {
2888
            return qq|{"identifier": "uuid", "label": "name", "items": $json_text}|;
2889
        } else {
2890
            return $json_text;
2891
        }
2892
    }
2893
}
2894
2895
# Build a complete system around cloned image
2896
sub buildSystem {
2897
    my ($name, $hmaster, $hstoragepool, $hsystem, $hinstances,
2898
        $hnetworkuuid1, $hbschedule, $hnetworktype1, $hports, $hmemory, $hvcpu, $hdiskbus,
2899
        $hcdrom, $hboot, $hnicmodel1, $hnicmac1, $hnetworkuuid2, $hnicmac2, $hmonitors,
2900 c899e439 Origo
        $hmanagementlink, $hstart, $duuid, $hstoragepool2 ) = @_;
2901 95b003ff Origo
2902
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {$postreply = "Unable to access domain register"; return $postreply;};
2903
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$postreply = "Unable to access image register"; return $postreply;};
2904
2905
    my $master = $hmaster;
2906
2907
    if ($curuuid && !$domreg{$curuuid} && $duuid) { # curuuid is a system uuid
2908
        $curuuid = $duuid;
2909
    }
2910
2911
    if (!$master && $curuuid && $domreg{$curuuid} && $imagereg{$domreg{$curuuid}->{image}}) {
2912
        $master = $imagereg{$domreg{$curuuid}->{image}}->{master};
2913
    }
2914
    my $cdrom = $hcdrom;
2915
    my $storagepool = $hstoragepool;
2916 c899e439 Origo
    my $storagepool2 = $hstoragepool2 || '0';
2917 95b003ff Origo
    my $image2;
2918
    $hinstances = 1 unless ($hinstances);
2919
    my $ioffset = 0;
2920
    if (!$name && $curuuid) {
2921
        $ioffset = 1; # Looks like we are called from an existing server - bump
2922
        $name = $domreg{$curuuid}->{'name'};
2923
        $name = $1 if ($name =~ /(.+)\.\d+$/);
2924
        foreach my $dom (values %domreg) { # Sequential naming of related systems
2925
            if ($dom->{'user'} eq $user && $dom->{'name'} =~ /$name\.(\d+)$/) {
2926
                $ioffset = $1+1 if ($1 >= $ioffset);
2927
            }
2928
        }
2929
    }
2930
    if ($master && !$imagereg{"$master"}) {
2931
    # Try to look up master based on file name
2932
        my @spoolpaths = $cfg->param('STORAGE_POOLS_LOCAL_PATHS');
2933
        my @users = ('common', $user);
2934
        foreach my $u (@accounts) {push @users,$u;};
2935
        # Include my sponsors master images
2936
        my $billto = $userreg{$user}->{'billto'};
2937
        push @users, $billto if ($billto);
2938
        # Also include my subusers' master images
2939
        my @userregkeys = (tied %userreg)->select_where("billto = '$user'");
2940
        push @users, @userregkeys if (@userregkeys);
2941
2942
        my $match;
2943
        foreach my $u (@users) {
2944
            foreach $sp (@spoolpaths) {
2945
                if ($imagereg{"$sp/$u/$master"}) {
2946
                    $master = "$sp/$u/$master";
2947
                    $match = 1;
2948
                    last;
2949
                }
2950
            }
2951
            last if ($match),
2952
        }
2953
    }
2954
2955
    if (!$imagereg{$master} && length $master == 36) {
2956
    # Try to look up master by uuid
2957
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {$postreply = "Unable to access image register"; return $postreply;};
2958
        $master = $imagereg2{$master}->{'path'} if ($imagereg2{$master});
2959
        untie %imagereg2;
2960
    }
2961
2962
    if (!$master && $curuuid) {
2963
        $master = $imagereg{$domreg{$curuuid}->{'image'}}->{'master'};
2964
    }
2965
2966
    unless ($imagereg{$master}) {$postreply = "Status=Error Invalid master $master"; return $postreply;};
2967
    my $masterimage2 = $imagereg{"$master"}->{'image2'};
2968
    my $sysuuid = $hsystem;
2969
2970
    if ($cdrom && $cdrom ne '--' && !$imagereg{"$cdrom"}) {
2971
    # Try to look up cdrom based on file name
2972
        my @spoolpaths = $cfg->param('STORAGE_POOLS_LOCAL_PATHS');
2973
        my @users = ('common', $user);
2974
        foreach my $u (@accounts) {push @users,$u;};
2975
        my $match;
2976
        foreach my $u (@users) {
2977
            foreach $sp (@spoolpaths) {
2978
                if ($imagereg{"$sp/$u/$cdrom"}) {
2979
                    $cdrom = "$sp/$u/$cdrom";
2980
                    $match = 1;
2981
                    last;
2982
                }
2983
            }
2984
            last if ($match),
2985
        }
2986
    }
2987
2988
    #open OUTPUT, '>', "/dev/null"; select OUTPUT;
2989
    $Stabile::Images::console = 1;
2990
    require "$Stabile::basedir/cgi/images.cgi";
2991
    $Stabile::Networks::console = 1;
2992
    require "$Stabile::basedir/cgi/networks.cgi";
2993
    $Stabile::Servers::console = 1;
2994
    require "$Stabile::basedir/cgi/servers.cgi";
2995
2996
    #close(OUTPUT); select STDOUT;
2997
    # reset stdout to be the default file handle
2998
    my $oipath; # This var stores admin servers image, if only one server initially
2999
    if ($sysuuid eq 'new') {
3000
        $sysuuid = '';
3001
    } elsif ($sysuuid eq 'auto' || (!$sysuuid && $curuuid)) { # $curuuid means request is coming from a running vm
3002
        my $domuuid = $curuuid || Stabile::Networks::ip2domain( $ENV{'REMOTE_ADDR'} );
3003
        if ($domuuid && $domreg{$domuuid}) {
3004
            if ($domreg{$domuuid}->{'system'}) {
3005
                $sysuuid = $domreg{$domuuid}->{'system'};
3006
            } else {
3007
                my $ug = new Data::UUID;
3008
                $sysuuid = $ug->create_str();
3009
                #$sysuuid = $domuuid; # Make sysuuid same as primary domains uuid
3010
                $domreg{$domuuid}->{'system'} = $sysuuid;
3011
                $oipath = $domreg{$domuuid}->{'image'};
3012
            }
3013
        } else {
3014
            $sysuuid = '';
3015
        }
3016
    }
3017
3018
    # Check if images should be moved to node storage
3019
    if ($storagepool eq "-1") {
3020
        if (index($privileges, 'n')==-1 && !$isadmin) {
3021
            $storagepool = '';
3022
        } else {
3023
            $storagepool = -1;
3024
            # %nodereg is needed in order to increment reservedvcpus for nodes
3025
            unless ( tie(%nodereg,'Tie::DBI', Hash::Merge::merge({table=>'nodes', key=>'mac'}, $Stabile::dbopts)) ) {$postreply = "Unable to access node register"; return $postreply;};
3026
        }
3027
    }
3028
3029
    my @domains;
3030
    my $systemuuid;
3031
    for (my $i=$ioffset; $i<$hinstances+$ioffset; $i++) {
3032
        my $ipath;
3033
        my $mac;
3034
        my $res;
3035
        my $istr = ".$i";
3036
        $istr = '' if ($hinstances==1 && $i==0);
3037
3038
    # Clone image
3039
        my $imagename = $name;
3040
        $imagename =~ s/system/Image/i;
3041 c899e439 Origo
        $res = Stabile::Images::Clone($master, 'clone', '', $storagepool, '', "$imagename$istr", $hbschedule, 1, $hmanagementlink, $appid, 1, $hvcpu, $hmemory);
3042 95b003ff Origo
        $postreply .= $res;
3043
        if ($res =~ /path: (.+)/) {
3044
            $ipath = $1;
3045
        } else {
3046
            next;
3047
        }
3048
        $mac = $1 if ($res =~ /mac: (.+)/);
3049
        Stabile::Images::updateBilling();
3050
3051
        # Secondary image - clone it
3052
        if ($masterimage2 && $masterimage2 ne '--' && $masterimage2 =~ /\.master\.qcow2$/) {
3053 c899e439 Origo
            $res = Stabile::Images::Clone($masterimage2, 'clone', '', $storagepool2, $mac, "$imagename$istr-data", $hbschedule, 1, '', '', 1);
3054 95b003ff Origo
            $postreply .= $res;
3055
            $image2 = $1 if ($res =~ /path: (.+)/);
3056
        }
3057
3058
    # Create network
3059
        my $networkuuid1;
3060
        if ($hnetworkuuid1) { # An existing network was specified
3061
            $networkuuid1 = $hnetworkuuid1;
3062
        } else { # Create new network
3063
            my $networkname = $name;
3064
            $networkname =~ s/system/Connection/i;
3065 d3d1a2d4 Origo
            my $type = ($i==0)?$hnetworktype1 : '';
3066 95b003ff Origo
            if (!$type) {
3067 d3d1a2d4 Origo
                if ($curuuid && $i==0) { # This should never be true, leaving for now...
3068 95b003ff Origo
                    unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {$postreply = "Unable to access networks register"; return $postreply;};
3069
                    $type = $networkreg{$domreg{$curuuid}->{'networkuuid1'}}->{'type'};
3070
                    untie %networkreg;
3071
                } else {
3072
                    $type = 'internalip';
3073
                }
3074
            }
3075
            $main::syslogit->($user, 'info', "saving network $networkname$istr");
3076
            $res = Stabile::Networks::save('', '', "$networkname$istr", 'new', $type, '','',$hports,1,$user);
3077
            $postreply .= $res;
3078
            if ($res =~ /uuid: (.+)/) {
3079
                $networkuuid1 = $1;
3080
            } else {
3081
                next;
3082
            }
3083
        }
3084
3085
    # Create server
3086
        my $servername = $name;
3087
        $servername =~ s/system/Server/i;
3088
        if ($curuuid) {
3089
            $hmemory = $hmemory || $domreg{$curuuid}->{'memory'};
3090
            $hvcpu = $hvcpu || $domreg{$curuuid}->{'vcpu'};
3091
            $hdiskbus = $hdiskbus || $domreg{$curuuid}->{'diskbus'};
3092
            $cdrom = $cdrom || $domreg{$curuuid}->{'cdrom'};
3093
            $hboot = $hboot || $domreg{$curuuid}->{'boot'};
3094
            $hnicmodel1 = $hnicmodel1 || $domreg{$curuuid}->{'nicmodel1'};
3095
        }
3096
3097
        $main::syslogit->($user, 'info', "saving server $servername$istr");
3098
        $res =  Stabile::Servers::Save('', '', {
3099
                 uuid => '',
3100
                 name => "$servername$istr",
3101
                 memory => $hmemory,
3102
                 vcpu => $hvcpu,
3103
                 image => $ipath,
3104
                 imagename => '',
3105
                 image2 => $image2,
3106
                 image2name => '',
3107
                 diskbus => $hdiskbus,
3108
                 cdrom => $cdrom,
3109
                 boot => $hboot,
3110
                 networkuuid1 => $networkuuid1,
3111
                 networkid1 => '',
3112
                 networkname1 => '',
3113
                 nicmodel1 => $hnicmodel1,
3114
                 nicmac1 => $hnicmac1,
3115
                 nicmac2 => $hnicmac2,
3116
                 status => 'new',
3117
                 notes => $notes,
3118
                 system => $sysuuid,
3119
                 newsystem => ($hinstances>1 && !$sysuuid),
3120
                 buildsystem => 1,
3121
                 console => 1
3122
             });
3123
3124 48fcda6b Origo
        $postreply .= "$res\n";
3125 3657de20 Origo
        $sysuuid = $1 if ($res =~ /sysuuid: (\S+)/);
3126 95b003ff Origo
        my $serveruuid;
3127 3657de20 Origo
        $serveruuid = $1 if ($res =~ /uuid: (\S+)/);
3128 95b003ff Origo
        my $sys = $register{$sysuuid};
3129
        if ($sysuuid && $i==$ioffset) {
3130
            $register{$sysuuid} = {
3131
                uuid => $sysuuid,
3132
                name => $sys->{'name'} || $servername, #Don't rename existing system
3133
                user => $user,
3134
                image => $sys->{'image'} || $oipath || $ipath, #Don't update admin image for existing system
3135
                created => $current_time
3136
            };
3137
        }
3138
3139
    # Create monitors
3140
        my @monitors = split(",", $hmonitors);
3141
        if (@monitors) {
3142
            $res = addSimpleMonitors($serveruuid, $alertemail, \@monitors);
3143
            if ( $res eq 'OK' ) {
3144
                `/usr/bin/moncmd reset keepstate &`;
3145
                $postreply .= "Status=OK Saved monitors @monitors\n";
3146
            } else {
3147
                $postreply .= "Status=OK Not saving monitors: $res\n";
3148
            }
3149
3150
        }
3151
3152
        if ($serveruuid) {
3153
            unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {$postreply = "Unable to access networks register"; return $postreply;};
3154
            $networkreg{$networkuuid1}->{'domains'} = $serveruuid;
3155
            tied(%networkreg)->commit;
3156
            untie %networkreg;
3157
3158
            push @domains, $serveruuid;
3159
            $imagereg{$ipath}->{'domains'} = $serveruuid;
3160
            $imagereg{$ipath}->{'domainnames'} = "$servername$istr";
3161
            if ($storagepool == -1) {
3162
                # my $mac = $imagereg{$ipath}->{'mac'};
3163
                # Increment reserved vcpus in order for location of target node to spread out
3164
                $postreply .= "Status=OK Cloned image to node $mac: $nodereg{$mac}->{'reservedvcpus'}";
3165
                $nodereg{$mac}->{'reservedvcpus'} += $hvcpu;
3166
                $postreply .= ":$nodereg{$mac}->{'reservedvcpus'}\n";
3167
                tied(%nodereg)->commit;
3168
                if (!$hstart) { # If we are not starting servers, wake up node anyway to perform clone operation
3169
                    if ($nodereg{$mac}->{'status'} eq 'asleep') {
3170
                        require "$Stabile::basedir/cgi/nodes.cgi";
3171
                        $Stabile::Nodes::console = 1;
3172
                        Stabile::Nodes::wake($mac);
3173
                    }
3174
                }
3175
            }
3176
        }
3177
        $systemuuid = (($sysuuid)? $sysuuid : $serveruuid) unless ($systemuuid);
3178
    }
3179
    if ($storagepool == -1) {
3180
        untie %nodereg;
3181
    }
3182
3183
    $postreply .= "Status=OK sysuuid: $systemuuid\n" if ($systemuuid);
3184
    if ($hstart) {
3185
        foreach my $serveruuid (@domains) {
3186
            $postreply .= Stabile::Servers::Start($serveruuid, 'start',{buildsystem=>1});
3187
        }
3188
    } else {
3189
        $main::updateUI->({tab=>'servers', user=>$user, uuid=>$serveruuid, status=>'shutoff'});
3190
    }
3191
    untie %imagereg;
3192
    #if (@domains) {
3193
    #    return to_json(\@domains, {pretty=>1});
3194
    #} else {
3195
        return $postreply;
3196
    #}
3197
}
3198
3199
sub upgradeSystem {
3200
    my $internalip = shift;
3201
3202
    unless (tie %imagereg,'Tie::DBI', { # Needed for ValidateItem
3203
        db=>'mysql:steamregister',
3204
        table=>'images',
3205
        key=>'path',
3206
        autocommit=>0,
3207
        CLOBBER=>3,
3208
        user=>$dbiuser,
3209
        password=>$dbipasswd}) {throw Error::Simple("Stroke=ERROR Image register could not be accessed")};
3210
3211
    my $appid;
3212
    my $appversion;
3213
    my $appname;
3214
    my $master;
3215
    my $progress;
3216
    my $currentversion;
3217
3218
# Locate the system we should upgrade
3219
    if ($internalip) {
3220
        foreach my $network (values %networkreg) {
3221
            if ($internalip =~ /^10\.\d+\.\d+\.\d+/
3222
                && $network->{'internalip'} eq $internalip
3223
                && $network->{'user'} eq $user
3224
            ) {
3225
                $curuuid = $domreg{$network->{'domains'}}->{'uuid'};
3226
                $cursysuuid = $domreg{$curuuid}->{'system'};
3227
                $master = $imagereg{$domreg{$curuuid}->{'image'}}->{'master'};
3228
                $appid = $imagereg{$master}->{'appid'};
3229
                $appversion = $imagereg{$master}->{'version'};
3230
                $appname = $imagereg{$master}->{'name'};
3231
                last;
3232
            }
3233
        }
3234
    }
3235
# Locate the newest version of master image
3236
    my $currentmaster;
3237
    foreach my $imgref (values %imagereg) {
3238
        if ($imgref->{'path'} =~ /\.master\.qcow2$/
3239
            && $imgref->{'path'} !~ /-data\.master\.qcow2$/
3240
            && $imgref->{'appid'} eq $appid
3241
        ) {
3242
            if ($imgref->{'version'} > $currentversion) {
3243
                $currentmaster = $imgref;
3244
                $currentversion = $imgref->{'version'};
3245
            }
3246
        }
3247
    }
3248
# Build list of system members
3249
    my @doms;
3250
    if ($cursysuuid && $register{$cursysuuid}) {
3251
        $register{$cursysuuid}->{'status'} = 'upgrading';
3252
        foreach my $domref (values %domreg) {
3253
            push( @doms, $domref ) if ($domref->{'system'} eq $cursysuuid && $domref->{'user'} eq $user);
3254
        }
3255
    } else {
3256
        push( @doms, $domreg{$curuuid} ) if ($domreg{$curuuid}->{'user'} eq $user);
3257
    }
3258
    $membs = int @doms;
3259
3260
    my $problem = 0;
3261
    foreach my $dom (@doms) {
3262
        if ($dom->{'status'} ne 'running') {
3263
            $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user,
3264
            status=>qq|Server $dom->{name} is not running. All member servers must be running when upgrading an app.|});
3265
            $problem = 1;
3266
            last;
3267
        }
3268
    }
3269
# First dump each servers data to nfs
3270
    unless ($problem) {
3271
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>"Already newest version, reinstalling version $currentversion!", title=>'Reinstalling, hold on...'});
3272
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>'Beginning data dump!'});
3273
3274
        my $browser = LWP::UserAgent->new;
3275
        $browser->agent('movepiston/1.0b');
3276
        $browser->protocols_allowed( [ 'http','https'] );
3277
3278
        foreach my $dom (@doms) {
3279
            my $upgradelink = $imagereg{$dom->{'image'}}->{'upgradelink'};
3280
            if ($upgradelink) {
3281
                my $res;
3282
                my $networkuuid1 = $dom->{'networkuuid1'};
3283
                my $ip = $networkreg{$networkuuid1}->{'internalip'};
3284
                $upgradelink = "http://internalip$upgradelink" unless ($upgradelink =~ s/\{internalip\}/$ip/);
3285
                $domreg{$dom->{'uuid'}}->{'status'} = 'upgrading';
3286
                $main::updateUI->({tab=>'servers', user=>$user, uuid=>$dom->{'uuid'}, status=>'upgrading'});
3287
                my $content = $browser->get($upgradelink)->content();
3288
                if ($content =~ /^\{/) { # Looks like json
3289
                    $jres = from_json($content);
3290
                    $res = $jres->{'message'};
3291
                    unless (lc $jres->{'status'} eq 'ok') {
3292
                        $problem = 2;
3293
                    }
3294
                } else { # no json returned, assume things went hayward
3295
                    $res = $content;
3296
                    $res =~ s/</&lt;/g;
3297
                    $res =~ s/>/&gt;/g;
3298
                    $problem = "Data dump failed ($upgradelink)";
3299
                }
3300
                $res =~ s/\n/ /;
3301
                $progress += 10;
3302
                $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>"$ip: $res", progress=>$progress});
3303
            }
3304
        }
3305
    }
3306
    tied(%domreg)->commit;
3307
3308
# Shut down all servers
3309
    unless ($problem) {
3310
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>'Beginning shutdown of servers!'});
3311
        require "$Stabile::basedir/cgi/servers.cgi";
3312
        $Stabile::Servers::console = 1;
3313
        foreach my $dom (@doms) {
3314
            $progress += 10;
3315
            my $networkuuid1 = $dom->{'networkuuid1'};
3316
            my $ip = $networkreg{$networkuuid1}->{'internalip'};
3317
            $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>"$ip: Shutting down...", progress=>$progress});
3318
            if ($dom->{'status'} eq 'shutoff' || $dom->{'status'} eq 'inactive') {
3319
                next;
3320
            } else {
3321
                my $res = Stabile::Servers::destroyUserServers($user, 1, $dom->{'uuid'});
3322
                if ($dom->{'status'} ne 'shutoff' && $dom->{'status'} ne 'inactive') {
3323
                    $problem = "ERROR $res"; # We could not shut down a server, fail...
3324
                    last;
3325
                }
3326
            }
3327
        }
3328
    }
3329
# Then replace each image with new version
3330
    unless ($problem) {
3331
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>'Attaching new images!'});
3332
        require "$Stabile::basedir/cgi/images.cgi";
3333
        $Stabile::Images::console = 1;
3334
        foreach my $dom (@doms) {
3335
            $progress += 10;
3336
            my $networkuuid1 = $dom->{'networkuuid1'};
3337
            my $ip = $networkreg{$networkuuid1}->{'internalip'};
3338
            $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>"$ip: Attaching image...", progress=>$progress});
3339
            my $image = $imagereg{$dom->{'image'}};
3340
            my $ipath;
3341
            # Clone image
3342
            my $imagename = $image->{'name'};
3343
            my $res = Stabile::Images::Clone($currentmaster->{'path'}, 'clone', '', $image->{'storagepool'}, '', $imagename, $image->{'bschedule'}, 1, $currentmaster->{'managementlink'}, $appid, 1);
3344
            $postreply .= $res;
3345
            if ($res =~ /path: (.+)/) {
3346
                $ipath = $1;
3347
            } else {
3348
                $problem = 5;
3349
            }
3350
3351
            if ($ipath =~ /\.qcow2$/) {
3352
                Stabile::Images::updateBilling();
3353
                # Attach new image to server
3354
                $main::syslogit->($user, 'info', "attaching new image to server $dom->{'name'} ($dom->{'uuid'})");
3355
                $res =  Stabile::Servers::Save({
3356
                         uuid => $dom->{'uuid'},
3357
                         image => $ipath,
3358
                         imagename => $imagename,
3359
                     });
3360
                # Update systems admin image
3361
                $register{$cursysuuid}->{'image'} = $ipath if ($register{$cursysuuid} && $dom->{'uuid'} eq $curuuid);
3362
                # Update image properties
3363
                $imagereg{$ipath}->{'domains'} = $dom->{'uuid'};
3364
                $imagereg{$ipath}->{'domainnames'} = $dom->{'name'};
3365
            } else {
3366
                $problem = 6;
3367
            }
3368
        }
3369
    }
3370
3371
# Finally start all servers with new image
3372
    unless ($problem) {
3373
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>'Starting servers!'});
3374
        require "$Stabile::basedir/cgi/servers.cgi";
3375
        $Stabile::Servers::console = 1;
3376
        foreach my $dom (@doms) {
3377
            $progress += 10;
3378
            my $networkuuid1 = $dom->{'networkuuid1'};
3379
            my $ip = $networkreg{$networkuuid1}->{'internalip'};
3380
            $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>"$ip: Starting...", progress=>$progress});
3381
            if ($dom->{'status'} eq 'shutoff' || $dom->{'status'} eq 'inactive') {
3382
                Stabile::Servers::Start($dom->{'uuid'}, 'start', {uistatus=>'upgrading'});
3383
                $main::updateUI->({ tab=>'servers',
3384
                                    user=>$user,
3385
                                    uuid=>$dom->{'uuid'},
3386
                                    status=>'upgrading'})
3387
            }
3388
        }
3389
    } else {
3390
        foreach my $dom (@doms) {
3391
            $dom->{'status'} = 'inactive'; # Prevent servers from being stuck in upgrading status
3392
        }
3393
    }
3394
3395
    my $nlink = $imagereg{$doms[0]->{'image'}}->{'managementlink'}; # There might be a new managementlink for image
3396
    my $nuuid = $doms[0]->{'networkuuid1'};
3397
    $nlink =~ s/\{uuid\}/$nuuid/;
3398
3399
    unless ($problem) {
3400
# All servers successfully upgraded
3401
        my $status = qq|Your $appname app has exported its data and new images have been attached to your servers. Now, your app will start again and import your data.|;
3402
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, progress=>100, status=>$status, managementlink=>$nlink, message=>"All done!"});
3403
    } else {
3404
        my $status = qq|Problem: $problem encountered. Your $appname could not be upgraded to the version $appversion. You can try again, or contact the app developer if this fails.|;
3405
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, progress=>100, status=>$status, managementlink=>$nlink, message=>"Something went wrong :("});
3406
    }
3407
    untie %imagereg;
3408
3409
    my $reply = qq|{"message": "Upgrading $domreg{$curuuid}->{name} with $membs members"}|;
3410
    return "$reply\n";
3411
}
3412
3413
sub removeusersystems {
3414
    my $username = shift;
3415
    return unless (($isadmin || $user eq $username) && !$isreadonly);
3416
    $user = $username;
3417
    my @allsystems = getSystemsListing('removeusersystems');
3418
    foreach my $sys (@allsystems) {
3419
        next unless $sys->{'uuid'};
3420
        remove($sys->{'uuid'}, $sys->{'issystem'}, 1);
3421
        #$postreply .= "Status=OK Removing system $sys->{'name'} ($sys->{'uuid'})\n";
3422
    }
3423
    return $postreply || "[]";
3424
}
3425
3426
3427
# Remove every trace of a system including servers, images, etc.
3428
sub remove {
3429
    my ($uuid, $issystem, $destroy) = @_;
3430
    my $sysuuid = $uuid;
3431
    my $reguser = $register{$uuid}->{'user'} if ($register{$uuid});
3432
    $reguser = $domreg{$uuid}->{'user'} if (!$reguser && $domreg{$uuid});
3433
3434
    $Stabile::Images::user = $user;
3435
    require "$Stabile::basedir/cgi/images.cgi";
3436
    $Stabile::Images::console = 1;
3437
3438
    $Stabile::Networks::user = $user;
3439
    require "$Stabile::basedir/cgi/networks.cgi";
3440
    $Stabile::Networks::console = 1;
3441
3442
    $Stabile::Servers::user = $user;
3443
    require "$Stabile::basedir/cgi/servers.cgi";
3444
    $Stabile::Servers::console = 1;
3445
3446
    $issystem = 1 if ($register{$uuid});
3447
    my @domains;
3448
    my $res;
3449
3450
    if ($issystem) {
3451
    # Delete child servers
3452
        if (($user eq $reguser || $isadmin) && $register{$uuid}){ # Existing system
3453 d3d1a2d4 Origo
        # First delete any linked networks
3454
            if ($register{$uuid}->{'networkuuids'} && $register{$uuid}->{'networkuuids'} ne '--') {
3455
                my @lnetworks = split /, ?/, $register{$uuid}->{'networkuuids'};
3456
                foreach my $networkuuid (@lnetworks) {
3457
                    if ($networkuuid) {
3458
                        Stabile::Networks::Deactivate($networkuuid);
3459
                        $res .= Stabile::Networks::Remove($networkuuid, 'remove', {force=>1});
3460
                    }
3461
                }
3462
            }
3463 95b003ff Origo
            foreach my $domvalref (values %domreg) {
3464
                if ($domvalref->{'system'} eq $uuid && ($domvalref->{'user'} eq $user || $isadmin)) {
3465
                    if ($domvalref->{'status'} eq 'shutoff' || $domvalref->{'status'} eq 'inactive') {
3466
                        push @domains, $domvalref->{'uuid'};
3467
                    } elsif ($destroy) {
3468
                        Stabile::Servers::destroyUserServers($reguser, 1, $domvalref->{'uuid'});
3469
                        push @domains, $domvalref->{'uuid'} if ($domvalref->{'status'} eq 'shutoff' || $domvalref->{'status'} eq 'inactive');
3470
                    }
3471
                }
3472
            }
3473
        }
3474
        $postreply .= "Status=removing OK Removing system $register{$uuid}->{'name'} ($uuid)\n";
3475
        delete $register{$uuid};
3476
        tied(%register)->commit;
3477
    } elsif ($domreg{$uuid} && $domreg{$uuid}->{uuid}) {
3478
    # Delete single server
3479
        if ($domreg{$uuid}->{'status'} eq 'shutoff' || $domreg{$uuid}->{'status'} eq 'inactive') {
3480
            push @domains, $uuid;
3481
        } elsif ($destroy) {
3482 54401133 hq
            Stabile::Servers::destroyUserServers($reguser, 1, $uuid);
3483 95b003ff Origo
            push @domains, $uuid if ($domreg{$uuid}->{'status'} eq 'shutoff' || $domreg{$uuid}->{'status'} eq 'inactive');
3484
        }
3485
     #   $postreply .= "Status=OK Removing server $domreg{$uuid}->{'name'} ($uuid)\n";
3486
    } else {
3487
        $postreply .= "Status=Error System $uuid not found\n";
3488
        return $postreply;
3489
    }
3490
    my $duuid;
3491
    foreach my $domuuid (@domains) {
3492
        if ($domreg{$domuuid}->{'status'} ne 'shutoff' && $domreg{$domuuid}->{'status'} ne 'inactive' ) {
3493
            $postreply .= "Status=ERROR Cannot delete server (active)\n";
3494
        } else {
3495
            my $imagepath = $domreg{$domuuid}->{'image'};
3496
            my $image2path = $domreg{$domuuid}->{'image2'};
3497
            my $networkuuid1 = $domreg{$domuuid}->{'networkuuid1'};
3498
            my $networkuuid2 = $domreg{$domuuid}->{'networkuuid2'};
3499
3500
            # Delete packages from software register
3501
        #    $postreply .= deletePackages($domuuid);
3502
            # Delete monitors
3503
        #    $postreply .= deleteMonitors($domuuid)?"Stream=OK Deleted monitors for $domreg{$domuuid}->{'name'}\n":"Stream=OK No monitors to delete for $domreg{$domuuid}->{'name'}\n";
3504
            # Delete server
3505
            $res .= Stabile::Servers::Remove($domuuid);
3506
3507
            # Delete images
3508
            $res .= Stabile::Images::Remove($imagepath);
3509
            if ($image2path && $image2path ne '--') {
3510
                $res .= Stabile::Images::Remove($image2path);
3511
            }
3512
            # Delete networks
3513
            if ($networkuuid1 && $networkuuid1 ne '--' && $networkuuid1 ne '0' && $networkuuid1 ne '1') {
3514
                Stabile::Networks::Deactivate($networkuuid1);
3515
                $res .= Stabile::Networks::Remove($networkuuid1);
3516
            }
3517
            if ($networkuuid2 && $networkuuid2 ne '--' && $networkuuid2 ne '0' && $networkuuid2 ne '1') {
3518
                Stabile::Networks::Deactivate($networkuuid2);
3519
                $res .= Stabile::Networks::Remove($networkuuid2);
3520
            }
3521
        }
3522
        $duuid = $domuuid;
3523
    }
3524 6fdc8676 hq
    if ($register{$uuid}) {
3525
        delete $register{$uuid};
3526
        tied(%register)->commit;
3527
    }
3528 95b003ff Origo
    if (@domains) {
3529
        $main::updateUI->(
3530
                        {tab=>'servers',
3531
                        user=>$user,
3532
                        type=>'update',
3533 2a63870a Christian Orellana
                        message=>((scalar @domains==1)?"Server has been removed":"Stack has been removed!")
3534 95b003ff Origo
                        },
3535
                        {tab=>'images',
3536
                        user=>$user
3537
                        },
3538
                        {tab=>'networks',
3539
                        user=>$user
3540
                        },
3541
                        {tab=>'home',
3542
                        user=>$user,
3543
                        type=>'removal',
3544
                        uuid=>$uuid,
3545
                        domuuid=>$duuid
3546
                        }
3547
                    );
3548
    } else {
3549
        $main::updateUI->(
3550
                        {tab=>'servers',
3551
                        user=>$user,
3552
                        type=>'update',
3553
                        message=>"Nothing to remove!"
3554
                        }
3555
                    );
3556
    }
3557 6fdc8676 hq
3558 95b003ff Origo
    if ($engineid && $enginelinked) {
3559
        # Remove domain from origo.io
3560
        my $json_text = qq|{"uuid": "$sysuuid" , "status": "delete"}|;
3561
        $main::postAsyncToOrigo->($engineid, 'updateapps', "[$json_text]");
3562
    }
3563 6fdc8676 hq
    return $postreply || qq|Content-type: application/json\n\n|;
3564 95b003ff Origo
}
3565
3566
sub getPackages {
3567
    my $curimg = shift;
3568
3569
    unless (tie %imagereg,'Tie::DBI', { # Needed for ValidateItem
3570
        db=>'mysql:steamregister',
3571
        table=>'images',
3572
        key=>'path',
3573
        autocommit=>0,
3574
        CLOBBER=>0,
3575
        user=>$dbiuser,
3576
        password=>$dbipasswd}) {throw Error::Simple("Stroke=ERROR Image register could not be accessed")};
3577
3578
    my $mac = $imagereg{$curimg}->{'mac'};
3579
    untie %imagereg;
3580
3581
    my $macip;
3582
    if ($mac && $mac ne '--') {
3583
        unless (tie %nodereg,'Tie::DBI', {
3584
            db=>'mysql:steamregister',
3585
            table=>'nodes',
3586
            key=>'mac',
3587
            autocommit=>0,
3588
            CLOBBER=>1,
3589
            user=>$dbiuser,
3590
            password=>$dbipasswd}) {return 0};
3591
        $macip = $nodereg{$mac}->{'ip'};
3592
        untie %nodereg;
3593
    }
3594
    $curimg =~ /(.+)/; $curimg = $1;
3595
    my $sshcmd;
3596
    if ($macip && $macip ne '--') {
3597
        $sshcmd = "/usr/bin/ssh -q -l irigo -i /var/www/.ssh/id_rsa_www -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $macip";
3598
    }
3599
    my $apps;
3600
3601
    if ($sshcmd) {
3602
        my $cmd = qq[eval \$(/usr/bin/guestfish --ro -a "$curimg" --i --listen); ]; # sets $GUESTFISH_PID shell var
3603
        $cmd .= qq[root="\$(/usr/bin/guestfish --remote inspect-get-roots)"; ];
3604
        $cmd .= qq[guestfish --remote inspect-get-product-name "\$root"; ];
3605
        $cmd .= qq[guestfish --remote inspect-get-hostname "\$root"; ];
3606
        $cmd .= qq[guestfish --remote inspect-list-applications "\$root"; ];
3607
        $cmd .= qq[guestfish --remote exit];
3608
        $cmd = "$sshcmd '$cmd'";
3609
        $apps = `$cmd`;
3610
    } else {
3611
        my $cmd;
3612
#        my $pid = open my $cmdpipe, "-|",qq[/usr/bin/guestfish --ro -a "$curimg" --i --listen];
3613
            $cmd .= qq[eval \$(/usr/bin/guestfish --ro -a "$curimg" --i --listen); ];
3614
        # Start listening guestfish
3615
        my $daemon = Proc::Daemon->new(
3616
                work_dir => '/usr/local/bin',
3617
                setuid => 'www-data',
3618
                exec_command => $cmd
3619
            ) or do {$posterror .= "Stream=ERROR $@\n";};
3620
        my $pid = $daemon->Init();
3621
        while ($daemon->Status($pid)) {
3622
            sleep 1;
3623
        }
3624
        # Find pid of the listening guestfish
3625
        my $pid2;
3626
        my $t = new Proc::ProcessTable;
3627
        foreach $p ( @{$t->table} ){
3628
            my $pcmd = $p->cmndline;
3629
            if ($pcmd =~ /guestfish.+$curimg/) {
3630
                $pid2 = $p->pid;
3631
                last;
3632
            }
3633
        }
3634
        my $cmd2;
3635
        if ($pid2) {
3636
            $cmd2 .= qq[root="\$(/usr/bin/guestfish --remote=$pid2 inspect-get-roots)"; ];
3637
            $cmd2 .= qq[guestfish --remote=$pid2 inspect-get-product-name "\$root"; ];
3638
            $cmd2 .= qq[guestfish --remote=$pid2 inspect-get-hostname "\$root"; ];
3639
            $cmd2 .= qq[guestfish --remote=$pid2 inspect-list-applications "\$root"; ];
3640
            $cmd2 .= qq[guestfish --remote=$pid2 exit];
3641
        }
3642
        $apps = `$cmd2`;
3643
        $apps .= $cmd2;
3644
    }
3645
    return $apps;
3646
}