Project

General

Profile

Download (155 KB) Statistics
| Branch: | Revision:
1
#!/usr/bin/perl
2

    
3
# All rights reserved and Copyright (c) 2020 Origo Systems ApS.
4
# This file is provided with no warranty, and is subject to the terms and conditions defined in the license file LICENSE.md.
5
# The license file is part of this source code package and its content is also available at:
6
# https://www.origo.io/info/stabiledocs/licensing/stabile-open-source-license
7

    
8
package Stabile::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
use Geo::IP;
28

    
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
my ($datanic, $extnic) = $main::getNics->();
36
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
        my @props = qw(uuid name memory vcpu  user  notes  created  opemail  opfullname  opphone  email  fullname  phone  services
97
            recovery  alertemail  image  networkuuid1  internalip autostart issystem system systemstatus from to
98
            appid callback installsystem installaccount networkuuids ports);
99
        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
GET:uuid:
240
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
PUT:uuid, name, servers, memory, vcpu, fullname, email, phone, opfullname, opemail, opphone, alertemail, services, recovery, notes, networkuuids:
570
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
END
575
    }
576

    
577
    my $name = $obj->{"name"};
578
    my $memory = $obj->{"memory"};
579
    my $vcpu = $obj->{"vcpu"};
580
    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
    my $networkuuids = $obj->{'networkuuids'};
595
    my $ports = $obj->{'ports'};
596
    my $autostart = $obj->{'autostart'};
597
    if (!$name) {
598
        if ($issystem) {
599
            $name = $register{$uuid}->{'name'};
600
        } else {
601
            $name = $domreg{$uuid}->{'name'};
602
        }
603
    }
604
    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
            my @props = ('name', 'fullname','email','phone','opfullname','opemail','opphone','alertemail'
630
                ,'notes','services','recovery','autostart');
631
            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
                    if ($val eq $userreg{$user}->{$prop}) {
638
                        $register{$uuid}->{$prop} = ''; # Same val as parent (user val), reset
639
                    } else {
640
                        if ($prop eq 'name' && $obj->{ports}) {
641
                            next; # TODO: ugly hack because we dont know why UTF8 is not handled correctly
642
                        }
643
                        $register{$uuid}->{$prop} = $val;
644
                    }
645
                    if ($prop eq 'autostart') {
646
                        $register{$uuid}->{$prop} = ($val)?'1':'';
647
                    }
648
                    if ($prop eq 'name') {
649
                        my $json_text = qq|{"uuid": "$uuid" , "name": "$name"}|;
650
                        $main::postAsyncToOrigo->($engineid, 'updateapps', "[$json_text]");
651
                    }
652
                }
653
            }
654
            my %childrenhash;
655
            my $alertmatch;
656
            push @props, ('vcpu', 'memory', 'ports');
657
            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
                            my $serveruuid = $domvalref->{'uuid'};
666
                            $childrenhash{$serveruuid} =\%domval unless ($childrenhash{$serveruuid});
667
                            $childrenhash{$serveruuid}->{$prop} = $val;
668
                            if ($prop eq 'autostart') {
669
                                $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
                                $domvalref->{$prop} = '';
689
                                if ($prop eq 'alertemail') {
690
                                    if (change_monitor_email($serveruuid, $val, $oldvals{$prop})) {
691
                                        $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
            $valref->{'autostart'} = ($autostart && $autostart ne '--'?'1':'');
753
            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
            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

    
783
            }
784
            tied(%domreg)->commit;
785
            $postreply = getSystemsListing(); # Hard to see what else to do, than to send entire table
786
        }
787
    }
788
    if ($networkuuids && $networkuuids ne '--') { # link networks to this system
789
        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
            next unless ($networkreg{$networkuuid});
795
            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
        if ($issystem && $register{$uuid}) {
806
            $register{$uuid}->{'networkuuids'} = join(", ", @newnetworks);
807
            $register{$uuid}->{'networknames'} = join(", ", @newnetworknames);
808
        } elsif ($domreg{$uuid}) {
809
            $domreg{$uuid}->{'networkuuids'} = join(", ", @newnetworks);
810
            $domreg{$uuid}->{'networknames'} = join(", ", @newnetworknames);
811
        }
812
    }
813
    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
    my $reguser;
884
    $reguser = $register{$uuid}->{'user'} if ($register{$uuid});
885

    
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
PUT:downloadmasters, externaliprangestart, externaliprangeend, proxyiprangestart, proxyiprangeend, proxygw, vmreadlimit, vmwritelimit, vmiopsreadlimit, vmiopswritelimit:
1018
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
    my $dl = $obj->{'downloadmasters'};
1027
    if ($dl eq '--' || $dl eq '0') {
1028
        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
    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
            $downloadmasters = 1;
1038
            `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
        }
1043
        $postreply .= "Status=OK Engine updated\n";
1044
        my @ps = split("\n",  `pgrep pressurecontrol` );
1045
        `kill -HUP $ps[0]`;
1046
    }
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
        my %allowshash;
1175
        foreach my $ip (@allows) {
1176
            $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
        }
1184
        $obj->{'allowfrom'} = join(", ", sort(keys %allowshash));
1185
        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
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
Build a complete system from cloned master image.
1914
master is the only required parameter. Set [storagepool2] to -1 if you want data images to be put on node storage.
1915
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
        $obj->{domuuid},
1941
        $obj->{storagepool2},
1942
        $obj->{loader}
1943
    );
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
GET:username:
1966
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
    my $username = $obj->{username};
1971
    $username = $username || $user;
1972
    $postreply = removeusersystems($username); # method performs security check
1973
    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
    my $duuid = $obj->{uuid} || $uuid;
1986
    $postreply = remove($duuid, 0, 1);
1987
    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
                my $serverip = `cat /etc/mon/mon.cf |sed -n -e 's/^hostgroup $group //p'`;
2208
                chomp $serverip;
2209
                $service->{'serverip'} = $serverip;
2210

    
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
    if ($serveruuid && $domreg{$serveruuid}) {
2315
        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
            return "Server $serveruuid not available";
2388
        }
2389
    } else {
2390
        return "Invalid uuid $serveruuid";
2391
    }
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
            $args .= " --basedn \"$request\"" if ($request);
2527
            $args .= " --attribute \"$okstring\"" if ($okstring);
2528
        } 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
    unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {return "Unable to access networks register"};
2747

    
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
                $val{'autostart'} = ($val{'autostart'})?'1':'';
2770

    
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
                $val{'internalip'} = $networkreg{$val{'networkuuid1'}}->{'internalip'} if ($networkreg{$val{'networkuuid1'}});
2785
                $val{'externalip'} = $networkreg{$val{'networkuuid1'}}->{'externalip'} if ($networkreg{$val{'networkuuid1'}});
2786

    
2787
                my $networkuuid1; # needed for generating management url
2788
                if ($sysuuid && $sysuuid ne '--') { # We are dealing with a server that's part of a system
2789
                    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
                    $pval{'autostart'} = ($pval{'autostart'})?'1':'';
2812

    
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
                        my $externalip = '';
2822
                        my $ports = '';
2823
                        if ($networkreg{$networkuuid1}) {
2824
                            $externalip = $networkreg{$networkuuid1}->{'externalip'};
2825
                            $ports = $networkreg{$networkuuid1}->{'ports'}
2826
                        }
2827
                        $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
                        $pval{'ports'} = $ports;
2847
                        $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
                    if ($networkreg{$networkuuid1}) {
2859
                        $externalip = $networkreg{$networkuuid1}->{'externalip'};
2860
                        $val{'internalip'} = $networkreg{$networkuuid1}->{'internalip'};
2861
                        $val{'ports'} = $networkreg{$networkuuid1}->{'ports'};
2862
                    }
2863
                    $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
                my $externalips = 0;
2910
                foreach my $child (@{$curreg{$val{'uuid'}}-> {'children'}}) {
2911
                    $status = $child->{'status'} unless ($child->{'status'} eq $status);
2912
                    $externalips += $child->{'externalips'} unless ($child->{'externalips'} eq '');
2913
                }
2914
                $status = 'degraded' unless ($status eq 'running' || $status eq 'shutoff');
2915
                $curreg{$val{'uuid'}}->{'status'} = $status;
2916
                $curreg{$val{'uuid'}}->{'externalips'} = $externalips;
2917
                # $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
            }
2922
        }
2923
    }
2924
    untie %networkreg;
2925

    
2926
    @curregvalues = values %curreg;
2927
    my @sorted_systems = sort {$a->{'name'} cmp $b->{'name'}} @curregvalues;
2928
    @sorted_systems = sort {$a->{'status'} cmp $b->{'status'}} @sorted_systems;
2929

    
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
        $hmanagementlink, $hstart, $duuid, $hstoragepool2, $hloader ) = @_;
2973

    
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
    my $storagepool2 = $hstoragepool2 || '0';
2989
    my $loader = $hloader || 'bios';
2990
    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
    my $masterimage2 = $imagereg{$master}->{'image2'};
3041
    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
        $res = Stabile::Images::Clone($master, 'clone', '', $storagepool, '', "$imagename$istr", $hbschedule, 1, $hmanagementlink, $appid, 1, $hvcpu, $hmemory);
3115
        $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
            $res = Stabile::Images::Clone($masterimage2, 'clone', '', $storagepool2, $mac, "$imagename$istr-data", $hbschedule, 1, '', '', 1);
3127
            $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
            my $type = ($i==0)?$hnetworktype1 : '';
3139
            if (!$type) {
3140
                if ($curuuid && $i==0) { # This should never be true, leaving for now...
3141
                    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
                 loader => $loader,
3184
                 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
        $postreply .= "$res\n";
3199
        $sysuuid = $1 if ($res =~ /sysuuid: (\S+)/);
3200
        my $serveruuid;
3201
        $serveruuid = $1 if ($res =~ /uuid: (\S+)/);
3202
        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
    return $postreply unless (($isadmin || $user eq $username) && !$isreadonly);
3490
    $user = $username;
3491
    my @allsystems = getSystemsListing('removeusersystems');
3492
    foreach my $sys (@allsystems) {
3493
        next unless $sys->{'uuid'};
3494
#        $postreply .= "Status=OK Removing $username system $sys->{'name'} ($sys->{'uuid'})\n";
3495
        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
        # 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
            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
            Stabile::Servers::destroyUserServers($reguser, 1, $uuid);
3557
            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
    if ($register{$uuid}) {
3599
        delete $register{$uuid};
3600
        tied(%register)->commit;
3601
    }
3602
    if (@domains) {
3603
        $main::updateUI->(
3604
                        {tab=>'servers',
3605
                        user=>$user,
3606
                        type=>'update',
3607
                        message=>((scalar @domains==1)?"Server has been removed":"Stack has been removed!")
3608
                        },
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

    
3632
    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
    return $postreply || qq|Content-type: application/json\n\n|;
3638
}
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
}
(7-7/9)