Project

General

Profile

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