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::
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
    $postreply = removeusersystems($user);
1971
    return $postreply;
1972
}
1973

    
1974
sub Removesystem {
1975
    my ($uuid, $action, $obj) = @_;
1976
    if ($help) {
1977
        return <<END
1978
GET:uuid:
1979
Removes specified system, i.e. completely deletes all servers, images, networks and backups belonging to a system.
1980
Use with care.
1981
END
1982
    }
1983
    my $duuid = $obj->{uuid} || $uuid;
1984
    $postreply = remove($duuid, 0, 1);
1985
    return $postreply;
1986
}
1987

    
1988
1;
1989

    
1990
# Print list of available actions on objects
1991
sub do_plainhelp {
1992
    my $res;
1993
    $res .= header('text/plain') unless $console;
1994
    $res .= <<END
1995
new [name="name"]
1996
start
1997
suspend
1998
resume
1999
shutdown
2000
destroy
2001
buildsystem [master, storagepool, system (uuid), instances, networkuuid1,bschedule,
2002
networktype1, ports, memory, vcpu, diskbus, cdrom, boot, nicmodel1, nicmac1, networkuuid2,
2003
nicmac2, monitors, start]
2004
removesystem
2005
updateaccountinfo
2006
resettoaccountinfo
2007

    
2008
END
2009
;
2010
}
2011

    
2012
# Save current mon status to /etc/stabile/opstatus, in order to preserve state when reloading mon
2013
sub saveOpstatus {
2014
    my $deleteid = shift;
2015
    my %opstatus = getSavedOpstatus();
2016
    my @monarray = split("\n", `/usr/bin/moncmd list opstatus`);
2017
    my $opfile = "/etc/stabile/opstatus";
2018
    open(FILE, ">$opfile") or {throw Error::Simple("Unable to write $opfile")};
2019
    foreach my $line (@monarray) {
2020
        my @pairs = split(/ /,$line);
2021
        my %h;
2022
        my $ALERT;
2023
        foreach my $pair (@pairs) {
2024
            my ($key, $val) = split(/=/,$pair);
2025
            $obj->{$key} = $val;
2026
        }
2027
        my $ops = $opstatus{"$group:$service"};
2028
        my $group = $obj->{'group'};
2029
        my $service = $obj->{'service'};
2030
        my $curstatus = $ops->{'opstatus'};
2031
        my $curack = $ops->{'ack'};
2032
        my $curackcomment = $ops->{'ackcomment'};
2033
        my $curline = $ops->{'line'};
2034
        if ($deleteid && $deleteid eq "$group:$service") {
2035
            ; # Don't write line for service we are deleting
2036
        } elsif (($obj->{'opstatus'} eq '0' || $obj->{'opstatus'} eq '7') && $curack && $curstatus eq '0') {
2037
            # A failure has been acknowledged and service is still down
2038
            print FILE "$curline\n";
2039
            $ALERT = ($obj->{'opstatus'}?'UP':'DOWN');
2040
        } elsif (($obj->{'opstatus'} || $obj->{'opstatus'} eq '0') && $obj->{'opstatus'} ne '7') {
2041
            print FILE "$line\n";
2042
            $ALERT = ($obj->{'opstatus'}?'UP':'DOWN');
2043
        } elsif (($curstatus || $curstatus eq '0') && $curstatus ne '7') {
2044
            print FILE "$curline\n";
2045
            $ALERT = ($obj->{'opstatus'}?'UP':'DOWN');
2046
        } else {
2047
            # Don't write anything if neither is different from 7
2048
        }
2049
    # Create empty log file if it does not exist
2050
        my $oplogfile = "/var/log/stabile/$year-$month:$group:$service";
2051
        unless (-s $oplogfile) {
2052
            if ($group && $service && $ALERT) {
2053
                `/usr/bin/touch "$oplogfile"`;
2054
                `/bin/chown mon:mon "$oplogfile"`;
2055
                my $logline = "$current_time, $ALERT, MARK, $pretty_time";
2056
                `/bin/echo >> $oplogfile "$logline"`;
2057
            }
2058
        }
2059
    }
2060
    close (FILE);
2061
    #if ((!-e $opfile) || ($current_time - (stat($opfile))[9] > 120) ) {
2062
    #    `/usr/bin/moncmd list opstatus > $opfile`;
2063
    #}
2064
}
2065

    
2066
sub getSavedOpstatus {
2067
    my $dounbackslash = shift;
2068
    my $opfile = "/etc/stabile/opstatus";
2069
    my @oparray;
2070
    my %opstatus;
2071
    # Build hash (%opstatus) with opstatus'es etc. to use for services that are in state unknown because of mon reload
2072
    if (-e $opfile) {
2073
        open(FILE, $opfile) or {throw Error::Simple("Unable to read $opfile")};
2074
        @oparray = <FILE>;
2075
        close(FILE);
2076
        foreach my $line (@oparray) {
2077
            my @pairs = split(/ /,$line);
2078
            my %h;
2079
            foreach my $pair (@pairs) {
2080
                my ($key, $val) = split(/=/,$pair);
2081
                if ($key eq 'last_result' || !$dounbackslash) {
2082
                    $obj->{$key} = $val;
2083
                } else {
2084
                    $val =~ s/\\/\\x/g;
2085
                    $obj->{$key} = unbackslash($val);
2086
                }
2087
            }
2088
            $obj->{'line'} = $line;
2089
            $opstatus{"$obj->{'group'}:$obj->{'service'}"} = \%h;
2090
        }
2091
    }
2092
    return %opstatus;
2093
}
2094

    
2095
sub getOpstatus {
2096
    my ($selgroup, $selservice, $usemoncmd) = @_;
2097
    my %opcodes = ("", "checking", "0", "down", "1", "ok", "3", "3", "4", "4", "5", "5", "6", "6", "7", "checking", "9", "disabled");
2098
    my %s;
2099
    my %opstatus;
2100
    my %savedopstatus = getSavedOpstatus(1);
2101
    my %sysdoms;
2102

    
2103
    my %disabled;
2104
    my %desc;
2105
    my @dislist = split(/\n/, `/usr/bin/moncmd list disabled`);
2106
    foreach my $disline (@dislist) {
2107
        my ($a, $b, $c, $d) = split(' ', $disline);
2108
        $disabled{"$b" . ($d?":$d":'')} = 1;
2109
    };
2110
    my %emails;
2111
    my @emaillist = split(/\n/, `/bin/cat /etc/mon/mon.cf`);
2112
    my $emailuuid;
2113
    foreach my $eline (@emaillist) {
2114
        my ($a, $b, $c, $d) = split(/ +/, $eline, 4);
2115
        if ($a eq 'watch') {
2116
            if ($b =~ /\S+-\S+-\S+-\S+-\S+/) {$emailuuid = $b;}
2117
            else {$emailuuid = ''};
2118
        }
2119
        $emails{$emailuuid} = $d if ($emailuuid && $b eq 'alert' && $c eq 'stabile.alert');
2120
    };
2121

    
2122
    # We are dealing with a system group rather than a domain, build hash of domains in system
2123
    if ($selgroup && !$domreg{$selgroup} && $register{$selgroup}) {
2124
        foreach my $valref (values %domreg) {
2125
            $sysdoms{$valref->{'uuid'}} = $selgroup if ($valref->{system} eq $selgroup);
2126
        }
2127
    }
2128
    if ($usemoncmd) {
2129
        my @oparray = split("\n", `/usr/bin/moncmd list opstatus`);
2130
        foreach my $line (@oparray) {
2131
            my @pairs = split(/ /,$line);
2132
            my %h;
2133
            foreach my $pair (@pairs) {
2134
                my ($key, $val) = split(/=/,$pair);
2135
                if ($key eq 'last_result') {
2136
                    $obj->{$key} = $val;
2137
                } else {
2138
                    $val =~ s/\\/\\x/g;
2139
                    $obj->{$key} = unbackslash($val);
2140
                }
2141
            }
2142
            if (!$selgroup || $sysdoms{$obj->{'group'}}
2143
                (!$selservice && $selgroup eq $obj->{'group'}) ||
2144
                ($selgroup eq $obj->{'group'} && $selservice eq $obj->{'service'})
2145
            )
2146
            {
2147
                #$obj->{'line'} = $line;
2148
                #$opstatus{"$obj->{'group'}:$obj->{'service'}"} = \%h;
2149
                $s{$obj->{'group'}}->{$obj->{'service'}} = \%h if($obj->{'group'});
2150
            }
2151
        }
2152

    
2153
    } else {
2154
        my $monc;
2155
        $monc = new Mon::Client (
2156
            host => "127.0.0.1"
2157
        );
2158
        $monc->connect();
2159
        %desc = $monc->list_descriptions; # Get descriptions
2160
        #%disabled = $monc->list_disabled;
2161
        $selgroup = '' if (%sysdoms);
2162
        my @selection = [$selgroup, $selservice];
2163
        if ($selgroup && $selservice) {%s = $monc->list_opstatus( @selection );}
2164
        elsif ($selgroup) {%s = $monc->list_opstatus( (@selection) );}# List selection
2165
        else {%s = $monc->list_opstatus;} # List all
2166
        $monc->disconnect();
2167
    }
2168

    
2169
    foreach my $group (keys %s) {
2170
        if ($domreg{$group} && ($domreg{$group}->{'user'} eq $user || $fulllist)) {
2171
            foreach my $service (values %{$s{$group}}) {
2172

    
2173
                next if (%sysdoms && !$sysdoms{$group});
2174
                next unless ($service->{'monitor'});
2175
                my $ostatus = $service->{'opstatus'};
2176
                my $id = "$group:$service->{'service'}";
2177
                if (%sysdoms) {
2178
                    $service->{'system'} = $sysdoms{$group};
2179
                }
2180
                if ($ostatus == 7 && $savedopstatus{$id}) { # Get status etc. from %savedopstatus because mon has recently been reloaded
2181
                    $service->{'opstatus'} = $savedopstatus{$id}->{'opstatus'};
2182
                    $service->{'last_success'} = $savedopstatus{$id}->{'last_success'};
2183
                    $service->{'last_check'} = $savedopstatus{$id}->{'last_check'};
2184
                    $service->{'last_detail'} = $savedopstatus{$id}->{'last_detail'};
2185
                    $service->{'checking'} = "1";
2186
                }
2187
#                if (($ostatus == 7 || $ostatus == 0) &&  $savedopstatus{$id}->{'ack'}) { # Get ack because mon has recently been reloaded
2188
                if ($ostatus == 7 &&  $savedopstatus{$id}->{'ack'}) { # Get ack because mon has recently been reloaded
2189
                    $service->{'ack'} = $savedopstatus{$id}->{'ack'};
2190
                    $service->{'ackcomment'} = $savedopstatus{$id}->{'ackcomment'};
2191
                    $service->{'first_failure'} = $savedopstatus{$id}->{'first_failure'};
2192
                }
2193
                $service->{'ackcomment'} = $1 if ($service->{'ackcomment'} =~ /^: *(.*)/);
2194
                my $status = $opcodes{$service->{'opstatus'}};
2195
                if ($disabled{$id} || $disabled{$group}){
2196
                    $status = 'disabled';
2197
                    $service->{'opstatus'} = "9";
2198
                }
2199
                $service->{'status'} = $status;
2200
                $service->{'id'} = $id;
2201
                $service->{'name'} = "$domreg{$group}->{'name'} : $service->{'service'}";
2202
                $service->{'servername'} = $domreg{$group}->{'name'};
2203
                $service->{'serveruuid'} = $domreg{$group}->{'uuid'};
2204
                $service->{'serverstatus'} = $domreg{$group}->{'status'};
2205
                my $serverip = `cat /etc/mon/mon.cf |sed -n -e 's/^hostgroup $group //p'`;
2206
                chomp $serverip;
2207
                $service->{'serverip'} = $serverip;
2208

    
2209
                my $desc = $desc{$group}->{$service->{'service'}};
2210
                $desc = '' if ($desc eq '--');
2211
                $service->{'desc'} = $desc;
2212
                $service->{'last_detail'} =~ s/-//g;
2213
                $service->{'last_detail'} =~ s/^\n//;
2214
                $service->{'last_detail'} =~ s/\n+/\n/g;
2215

    
2216
                my $monitor = $service->{'monitor'};
2217

    
2218
                $service->{'request'} = $service->{'okstring'} = $service->{'port'} = $service->{'email'} = '';
2219
                #$monitor = URI::Escape::uri_unescape($monitor);
2220
                #if ( $monitor =~ /stabile-diskspace\.monitor\s+(\S+)\s+(\S+)\s+(\S+)/ ) {
2221
                if ( $monitor =~ /stabile-diskspace\.monitor\s+(\S+)\s+(\S+)/ ) {
2222
                    $service->{'request'} = $2 if ( $monitor =~ /stabile-diskspace\.monitor\s+(\S+)\s+(\S+)/ );
2223
                    $service->{'okstring'} = $3 if ( $monitor =~ /stabile-diskspace\.monitor\s+(\S+)\s+(\S+)\s+(\S+)/ );
2224
                }
2225

    
2226
                $service->{'okstring'} = $1 if ( $monitor =~ /--okstring \"(.*)\"/ );
2227
                $service->{'okstring'} = $1 if ( $monitor =~ /-l \"(.*)\"/ );
2228
#                $service->{'request'} = $2 if ( $monitor =~ /http(s*):\/\/.+\/(.*)/ );
2229
                $service->{'request'} = $2 if ( $monitor =~ /http(s*):\/\/[^\/]+\/(.*)/ );
2230
                $service->{'port'} = $2 if ( $monitor =~ /http(s*):\/\/.+:(\d+)/ );
2231
                $service->{'request'} = $1 if ( $monitor =~ /--from \"(\S*)\"/ );
2232
                $service->{'okstring'} = $1 if ( $monitor =~ /--to \"(\S*)\"/ );
2233
                $service->{'port'} = $1 if ( $monitor =~ /--port (\d+)/ );
2234

    
2235
                $service->{'email'} = $emails{$group};
2236

    
2237
                $opstatus{$id} = $service;
2238
                #push @monitors, $service;
2239
            }
2240
        }
2241
    }
2242
    return %opstatus;
2243
}
2244

    
2245
sub change_monitor_email {
2246
    my $serveruuid = shift;
2247
    my $email = shift;
2248
    my $match;
2249
    if ($email && $serveruuid) {
2250
        unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
2251
        if ($domreg{$serveruuid}->{'user'} eq $user || $isadmin) {
2252
            local($^I, @ARGV) = ('.bak', "/etc/mon/mon.cf"); # $^I is the in-place edit switch
2253
            # undef $/; # This makes <> read in the entire file in one go
2254
            my $uuidmatch;
2255
            while (<>) {
2256
                if (/^watch (\S+)/) {
2257
                    if ($1 eq $serveruuid) {$uuidmatch = $serveruuid}
2258
                    else {$uuidmatch = ''};
2259
                };
2260
                if ($uuidmatch) {
2261
                    $match = 1 if (s/(stabile\.alert) (.*)/$1 $email/);
2262
                }
2263
                print;
2264
                close ARGV if eof;
2265
        #       $match = 1 if (s/(watch $serveruuid\n.+\n.+\n.+\n.+\n.+)$oldemail(\n.+)$oldemail(\n.+)$oldemail/$1$email$2$email$3$email/g);
2266
            }
2267
        #    $/ = "\n";
2268
        }
2269
    }
2270
    return $match;
2271
}
2272

    
2273
# Delete all monitors belonging to a server
2274
sub deleteMonitors {
2275
    my ($serveruuid) = @_;
2276
    my $match;
2277
    if ($serveruuid) {
2278
        if ($domreg{$serveruuid}->{'user'} eq $user || $isadmin) {
2279
            local($^I, @ARGV) = ('.bak', "/etc/mon/mon.cf");
2280
            # undef $/; # This makes <> read in the entire file in one go
2281
            my $uuidmatch;
2282
            while (<>) {
2283
                if (/^watch (\S+)/) {
2284
                    if ($1 eq $serveruuid) {$uuidmatch = $serveruuid}
2285
                    else {$uuidmatch = ''};
2286
                };
2287
                if ($uuidmatch) {
2288
                    $match = 1;
2289
                } else {
2290
                    #chomp;
2291
                    print unless (/^hostgroup $serveruuid/);
2292
                }
2293
                close ARGV if eof;
2294
            }
2295
            #$/ = "\n";
2296
        }
2297
        unlink glob "/var/log/stabile/*:$serveruuid:*";
2298
    }
2299
    `/usr/bin/moncmd reset keepstate` if ($match);
2300
    return $match;
2301
}
2302

    
2303
# Add a monitors to a server when building system
2304
sub addSimpleMonitors {
2305
    my ($serveruuid, $email, $monitors_ref) = @_;
2306
    my @mons = @{$monitors_ref};
2307

    
2308
    my $match;
2309
    my $hmatch1;
2310
    my $hmatch2;
2311
    my $hmatch3;
2312
    if ($serveruuid && $domreg{$serveruuid}) {
2313
        if ($domreg{$serveruuid}->{'user'} eq $user || $isadmin) {
2314
            my $monitors = {
2315
                ping=>"fping.monitor",
2316
                diskspace=>"stabile-diskspace.monitor $serveruuid",
2317
                http=>"http_tppnp.monitor",
2318
                https=>"http_tppnp.monitor",
2319
                smtp=>"smtp3.monitor",
2320
                smtps=>"smtp3.monitor",
2321
                imap=>"imap.monitor",
2322
                imaps=>"imap-ssl.monitor",
2323
                ldap=>"ldap.monitor",
2324
                telnet=>"telnet.monitor"
2325
            };
2326

    
2327
            if (!$email) {$email = $domreg{$serveruuid}->{'alertemail'}};
2328
            if (!$email && $register{$domreg{$serveruuid}->{'system'}}) {$email = $register{$domreg{$serveruuid}->{'system'}}->{'alertemail'}};
2329
            if (!$email) {$email = $userreg{$user}->{'alertemail'}};
2330

    
2331
            unless (tie %networkreg,'Tie::DBI', {
2332
                db=>'mysql:steamregister',
2333
                table=>'networks',
2334
                key=>'uuid',
2335
                autocommit=>0,
2336
                CLOBBER=>3,
2337
                user=>$dbiuser,
2338
                password=>$dbipasswd}) {throw Error::Simple("Stroke=Error Register could not be accessed")};
2339

    
2340
            my $networkuuid1 = $domreg{$serveruuid}->{'networkuuid1'};
2341
            my $networktype = $networkreg{$networkuuid1}->{'type'};
2342
            my $ip = $networkreg{$networkuuid1}->{'internalip'};
2343
            $ip = $networkreg{$networkuuid1}->{'externalip'} if ($networktype eq 'externalip');
2344
            $ip = '127.0.0.1' if ($networktype eq 'gateway'); #Dummy IP - we only support diskspace checks
2345
            untie %networkreg;
2346

    
2347
            local($^I, @ARGV) = ('.bak', "/etc/mon/mon.cf");
2348
            my $uuidmatch;
2349
            while (<>) {
2350
                $hmatch1=1 if (/^hostgroup/);
2351
                $hmatch2=1 if ($hmatch1 && !/^hostgroup/);
2352
                if ($hmatch1 && $hmatch2 && !$hmatch3) {
2353
                    print "hostgroup $serveruuid $ip\n";
2354
                    $hmatch3 = 1;
2355
                }
2356
                print;
2357
                if (eof) {
2358
                    print "watch $serveruuid\n";
2359
                    foreach $service (@mons) {
2360
                        print <<END;
2361
    service $service
2362
        interval 1m
2363
        monitor $monitors->{$service}
2364
        description --
2365
        period
2366
            alert stabile.alert $email
2367
            upalert stabile.alert $email
2368
            startupalert stabile.alert $email
2369
            numalerts 2
2370
            no_comp_alerts
2371
END
2372
;
2373
                        my $oplogfile = "/var/log/stabile/$year-$month:$serveruuid:$service";
2374
                        unless (-e $oplogfile) {
2375
                            `/usr/bin/touch "$oplogfile"`;
2376
                            `/bin/chown mon:mon "$oplogfile"`;
2377
                            my $logline = "$current_time, UP, STARTUP, $pretty_time";
2378
                            `/bin/echo >> $oplogfile "$logline"`;
2379
                        }
2380
                    }
2381
                    close ARGV;
2382
                }
2383
            }
2384
        } else {
2385
            return "Server $serveruuid not available";
2386
        }
2387
    } else {
2388
        return "Invalid uuid $serveruuid";
2389
    }
2390
    return "OK";
2391
}
2392

    
2393
sub Monitors_save {
2394
    my ($id, $action, $obj) = @_;
2395
    if ($help) {
2396
        return <<END
2397
PUT:id:
2398
Enable, disable or acknowledge a monitor. Id is of the form serveruuid:service
2399
END
2400
    }
2401

    
2402
    my $delete = ($action eq 'monitors_remove'); # Delete an existing monitor
2403
    $id = $obj->{'id'} || $id; # ID in params supersedes id in path
2404
    my $update; # Update an existing monitor?
2405
    my $postmsg;
2406

    
2407
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domain register"};
2408
    unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {return "Unable to access networks register"};
2409
    foreign_require("mon", "mon-lib.pl");
2410
    $conf = mon::get_mon_config();
2411
#    my @ogroups = mon::find("hostgroup", $conf);
2412
#    my @owatches = mon::find("watch", $conf);
2413
    my $doreset;
2414
    my $email;
2415

    
2416
    my $serveruuid;
2417
    my $servicename;
2418
    if ($id =~ /(.+):(.+)/){ # List specific monitor for specific server
2419
        $serveruuid = $1;
2420
        $servicename = $2;
2421
    }
2422
    $serveruuid = $serveruuid || $obj->{'serveruuid'};
2423
    my $desc = $obj->{'desc'};
2424
    my $okstring = $obj->{'okstring'};
2425
    my $request = $obj->{'request'};
2426
    my $port = $obj->{'port'};
2427
    $servicename = $servicename || $obj->{'service'};
2428
    my $interval = '1'; # Number of minutes between checks
2429
    $interval = '20' if ($servicename eq 'diskspace');
2430
    $email = $obj->{'alertemail'};
2431
    my $serv = $domreg{$serveruuid};
2432
    if (!$email) {$email = $serv->{'alertemail'}};
2433
    if (!$email && $serv->{'system'}) {$email = $register{$serv->{'system'}}->{'alertemail'}};
2434
    if (!$email) {$email = $userreg{$user}->{'alertemail'}};
2435
    my $networkuuid1 = $serv->{'networkuuid1'};
2436
    my $networktype = $networkreg{$networkuuid1}->{'type'};
2437
    my $deleteid;
2438
    
2439
    if (!$serveruuid || !$servicename) {
2440
        $postmsg = qq|No monitor specified|;
2441
        $postreply = "Status=Error $postmsg\n";
2442
        return $postreply;
2443
    }
2444

    
2445
    if (!$delete && $networktype eq 'gateway' && $servicename ne 'diskspace'
2446
            && (!$obj->{'serverip'} || !($obj->{'serverip'} =~ /^\d+\.\d+\.\d+\.\d+$/) )) {
2447
        $postmsg = qq|Invalid IP address|;
2448
    } elsif (!$domreg{$serveruuid}) {
2449
        $postmsg = qq|Unknown server $serveruuid|;
2450
# Security check
2451
    } elsif ($domreg{$serveruuid}->{'user'} ne $user) {
2452
        $postmsg = qq|Bad server|;
2453
    } else {
2454
        my $monitors = {
2455
            ping=>"fping.monitor",
2456
            diskspace=>"stabile-diskspace.monitor",
2457
            http=>"http_tppnp.monitor",
2458
            https=>"http_tppnp.monitor",
2459
            smtp=>"smtp3.monitor",
2460
            smtps=>"smtp3.monitor",
2461
            imap=>"imap.monitor",
2462
            imaps=>"imap-ssl.monitor",
2463
            ldap=>"ldap.monitor",
2464
            telnet=>"telnet.monitor"
2465
        };
2466
        my $args = '';
2467
        my $ip = $networkreg{$networkuuid1}->{'internalip'};
2468
        $ip = $networkreg{$networkuuid1}->{'externalip'} if ($networktype eq 'externalip');
2469
        $ip = '127.0.0.1' if ($networktype eq 'gateway' && $servicename eq 'diskspace'); #Dummy IP - we only support diskspace checks
2470
        if ($networktype eq 'gateway' && $servicename eq 'ping') {
2471
            $ip = $obj->{'serverip'};
2472
        # We can only check 10.x.x.x addresses on vlan because of routing
2473
            if ($ip =~ /^10\./) {
2474
                $monitors->{'ping'} = "stabile-arping.monitor";
2475
                my $id = $networkreg{$networkuuid1}->{'id'};
2476
                if ($id > 1) {
2477
                    my $if = $datanic . "." . $id;
2478
                    $args = " $if";
2479
                } else {
2480
                    $args = " $extnic";
2481
                }
2482
                $args .= " $ip";
2483
            }
2484
        }
2485

    
2486
        if ($servicename eq 'ping') {
2487
            ;
2488
        } elsif ($servicename eq 'diskspace'){
2489
            #my $macip = $domreg{$serveruuid}->{'macip'};
2490
            #my $image = URI::Escape::uri_escape($domreg{$serveruuid}->{'image'});
2491
            #$args .= " $macip $image $serveruuid";
2492
            $args .= " $serveruuid";
2493
            $args .= ($request)?" $request":" 10"; #min free %
2494
            $args .= " $okstring" if ($okstring); #Comma-separated partion list, e.g. 0,1
2495
        } elsif ($servicename eq 'http'){
2496
            $args .= " --okcodes \"200,403\" --debuglog -";
2497
            $args .= " --okstring \"$okstring\"" if ($okstring);
2498
            $args .= " http://$ip";
2499
            $args .= ":$port" if ($port && $port>10 && $port<65535);
2500
            $request = substr($request,1) if ($request =~ /^\//);
2501
            $args .= "/$request" if ($request);
2502
        } elsif ($servicename eq 'https'){
2503
            $args .= " --okcodes \"200,403\" --debuglog -";
2504
            $args .= " --okstring \"$okstring\"" if ($okstring);
2505
            $args .= " https://$ip";
2506
            $args .= ":$port" if ($port && $port>10 && $port<65535);
2507
            $request = substr($request,1) if ($request =~ /^\//);
2508
            $args .= "/$request" if ($request);
2509
        } elsif ($servicename eq 'smtp'){
2510
            $args .= " --from \"$request\"" if ($request);
2511
            $args .= " --to \"$okstring\"" if ($okstring);
2512
            $args .= " --port $port" if ($port && $port>10 && $port<65535);
2513
        } elsif ($servicename eq 'smtps'){
2514
            $args .= " --requiretls";
2515
            $args .= " --from \"$request\"" if ($request);
2516
            $args .= " --to \"$okstring\"" if ($okstring);
2517
            $args .= " --port $port" if ($port && $port>10 && $port<65535);
2518
        } elsif ($servicename eq 'imap'){
2519
            $args .= " -p $port" if ($port && $port>10 && $port<65535);
2520
        } elsif ($servicename eq 'imaps'){
2521
            $args .= " -p $port" if ($port && $port>10 && $port<65535);
2522
        } elsif ($servicename eq 'ldap'){
2523
            $args .= " --port $port" if ($port && $port>10 && $port<65535);
2524
            $args .= " --basedn \"$request\"" if ($request);
2525
            $args .= " --attribute \"$okstring\"" if ($okstring);
2526
        } elsif ($servicename eq 'telnet'){
2527
            $args .= " -l \"$okstring\"" if ($okstring);
2528
            $args .= " -p $port" if ($port && $port>10 && $port<65535);
2529
        }
2530

    
2531
        my @ogroups = mon::find("hostgroup", $conf);
2532
        my @owatches = mon::find("watch", $conf);
2533

    
2534
        $group = { 'name' => 'hostgroup', 'values' => [ $serveruuid, $ip ] };
2535
        my $ogroup = undef;
2536
        my $i;
2537
        for($i=0; $i<scalar @ogroups; $i++) {
2538
            if ($ogroups[$i]->{'values'}[0] eq  $serveruuid) {
2539
                $ogroup = $ogroups[$i];
2540
                last;
2541
            }
2542
        }
2543
        mon::save_directive($conf, $ogroup, $group); #Update host hostgroup
2544

    
2545
        $watch = { 'name' => 'watch','values' => [ $serveruuid ], 'members' => [ ] };
2546
        my $owatch = undef;
2547
        my $oservice = undef;
2548
        my $widx = undef;
2549
        for($i=0; $i<scalar @owatches; $i++) { # Run through all watches and locate match
2550
            if ($owatches[$i]->{'values'}[0] eq  $serveruuid) {
2551
                $owatch = $watch = $owatches[$i];
2552
                $widx = $owatch->{'index'};
2553
                my @oservices = mon::find("service", $watch->{'members'});
2554
                for($j=0; $j<@oservices; $j++) { # Run through all services for watch and locate match
2555
                    if ($oservices[$j]->{'values'}[0] eq $servicename) {
2556
                        $oservice = $oservices[$j];
2557
                        my $newmonargs = "$monitors->{$servicename}$args";
2558
                        $newmonargs =~ s/\s+$//; # Remove trailing spaces
2559
                        my $oldmonargs = "$oservices[$j]->{'members'}[2]->{'values'}[0] $oservices[$j]->{'members'}[2]->{'values'}[1]";
2560
                        $oldmonargs =~ s/\s+$//; # Remove trailing spaces
2561
                        if ($newmonargs ne $oldmonargs) {
2562
                            $update = 1; #We are changing an existing service definition
2563
                        };
2564
                        last;
2565
                    }
2566
                }
2567
                last;
2568
            }
2569
        }
2570
        my $in = {
2571
            args=>undef,
2572
            desc=>"$desc",
2573
            idx=>$widx,
2574
            interval=>$interval,
2575
            interval_u=>'m',
2576
            monitor=>$monitors->{$servicename} . $args,
2577
            monitor_def=>1,
2578
            name=>$servicename,
2579
            other=>undef,
2580
            sidx=>undef,
2581
            delete=>$delete,
2582
            email=>$email
2583
        };
2584

    
2585
        if ($update || $delete) {
2586
            unlink glob "/var/log/stabile/*:$serveruuid:$servicename";
2587
        } else {
2588
            my $oplogfile = "/var/log/stabile/$year-$month:$serveruuid:$servicename";
2589
            unless (-e $oplogfile) {
2590
                `/usr/bin/touch "$oplogfile"`;
2591
                `/bin/chown mon:mon "$oplogfile"`;
2592
                my $logline = "$current_time, UP, STARTUP, $pretty_time";
2593
                `/bin/echo >> $oplogfile "$logline"`;
2594
            }
2595
        }
2596
        $deleteid = (($delete || $update)?"$serveruuid:$servicename":'');
2597
        save_service($in, $owatch, $oservice);
2598
        $doreset = 1;
2599
        $obj->{'last_check'} = '--';
2600
        $obj->{'opstatus'} = '7';
2601
        $obj->{'status'} = 'checking';
2602
        $obj->{'alertemail'} = $email;
2603
        mon::flush_file_lines();
2604
        $main::syslogit->($user, 'info', "updating monitor $serveruuid:$servicename" .  (($delete)?" delete":""));
2605
        saveOpstatus($deleteid);
2606
        `/usr/bin/moncmd reset keepstate`;
2607
    }
2608

    
2609
    untie %networkreg;
2610
    untie %domreg;
2611

    
2612
    $postreply = to_json(\%h, {pretty => 1});
2613
    $postmsg = "OK" unless ($postmsg);
2614
    return $postreply;
2615
}
2616

    
2617
## Copied from save_service.cgi (from webmin) and slightly modified - well heavily perhaps
2618

    
2619
sub save_service {
2620
    my $sin = shift;
2621
    my $owatch = shift;
2622
    my $oservice = shift;
2623
    my %in = %{$sin};
2624
    my $oldservice = undef;
2625
    my $service;
2626
    if ($oservice) {
2627
        # $oldservice = $service = $watch->{'members'}->[$in{'sidx'}];
2628
        $oldservice = $service = $oservice;
2629
    } else {
2630
        $service = { 'name' => 'service',
2631
                 'indent' => '    ',
2632
                 'members' => [ ] };
2633
    }
2634

    
2635
    if ($in{'delete'}) {
2636
        # Delete this service from the watch
2637
        mon::save_directive($watch->{'members'}, $service, undef) if ($oservice);
2638
        my @rservices = mon::find("service", $watch->{'members'});
2639
        # Delete watch and hostgroup if no services left
2640
        if (@rservices==0) {
2641
            mon::save_directive($conf, $watch, undef);
2642
            mon::save_directive($conf, $group, undef);
2643
        }
2644
    } else {
2645
        # Validate and store service inputs
2646
        $in{'name'} =~ /^\S+$/ || {$in{'name'} = 'ping'};
2647
        $service->{'values'} = [ $in{'name'} ];
2648
        $in{'interval'} =~ /^\d+$/ || {$in{'interval'} = 1};
2649

    
2650
        &set_directive($service->{'members'}, "interval", $in{'interval'}.$in{'interval_u'});
2651

    
2652
        if ($in{'monitor_def'}) {
2653
            &set_directive($service->{'members'}, "monitor", $in{'monitor'}.' '.$in{'args'});
2654
        }
2655
        else {
2656
            $in{'other'} =~ /^\S+$/ || return "No other monitor specified";
2657
            &set_directive($service->{'members'}, "monitor", $in{'other'}.' '.$in{'args'});
2658
        }
2659

    
2660
        # Save the description
2661
        if ($in{'desc'}) {
2662
            my $desc = $in{'desc'};
2663
            $desc =~ tr/\n/ /;
2664
            &set_directive($service->{'members'}, "description", $in{'desc'});
2665
        }
2666
        else {
2667
            &set_directive($service->{'members'}, "description", '--');
2668
        }
2669

    
2670
        my $period = { 'name' => 'period', 'members' => [ ] };
2671
        my @alert;
2672
        my @v = ( "stabile.alert", $in{'email'} );
2673
        my @num = (2); # The number of alerts to send
2674
        push(@alert, { 'name' => 'alert', 'values' => \@v });
2675
		&set_directive($period->{'members'}, "alert", @alert);
2676
        my @upalert;
2677
        push(@upalert, { 'name' => 'upalert', 'values' => \@v });
2678
		&set_directive($period->{'members'}, "upalert", @upalert);
2679
        my @startupalert;
2680
        push(@startupalert, { 'name' => 'startupalert', 'values' => \@v });
2681
		&set_directive($period->{'members'}, "startupalert", @startupalert);
2682
        my @numalerts;
2683
        push(@numalerts, { 'name' => 'numalerts', 'values' => \@num });
2684
		&set_directive($period->{'members'}, "numalerts", @numalerts);
2685
        my @no_comp_alerts;
2686
        push(@no_comp_alerts, { 'name' => 'no_comp_alerts', 'values' => 0 });
2687
		&set_directive($period->{'members'}, "no_comp_alerts", @no_comp_alerts);
2688

    
2689
        push(@period, $period);
2690

    
2691
    	&set_directive($service->{'members'}, "period", @period);
2692

    
2693
        if ($owatch) {
2694
            # Store the service in existing watch in the config file
2695
            mon::save_directive($watch->{'members'}, $oldservice, $service);
2696
        } else {
2697
            # Create new watch
2698
            push(@service, $service);
2699
            &set_directive($watch->{'members'}, "service", @service);
2700
            mon::save_directive($conf, undef, $watch);
2701
        }
2702
    }
2703
}
2704

    
2705
# set_directive(&config, name, value, value, ..)
2706
sub set_directive
2707
{
2708
local @o = mon::find($_[1], $_[0]);
2709
local @n = @_[2 .. @_-1];
2710
local $i;
2711
for($i=0; $i<@o || $i<@n; $i++) {
2712
	local $idx = &indexof($o[$i], @{$_[0]}) if ($o[$i]);
2713
	local $nv = ref($n[$i]) ? $n[$i] : { 'name' => $_[1],
2714
					     'values' => [ $n[$i] ] }
2715
						if (defined($n[$i]));
2716
	if ($o[$i] && defined($n[$i])) {
2717
		$_[0]->[$idx] = $nv;
2718
		}
2719
	elsif ($o[$i]) {
2720
		splice(@{$_[0]}, $idx, 1);
2721
		}
2722
	else {
2723
		push(@{$_[0]}, $nv);
2724
		}
2725
	}
2726
}
2727

    
2728
sub getSystemsListing {
2729
    my ($action, $curuuid, $username) = @_;
2730
    $username = $user unless ($username);
2731
    my @domregvalues = values %domreg;
2732
    my @curregvalues;
2733
    my %curreg;
2734

    
2735
    $userfullname = $userreg{$username}->{'fullname'};
2736
    $useremail = $userreg{$username}->{'email'};
2737
    $userphone = $userreg{$username}->{'phone'};
2738
    $useropfullname = $userreg{$username}->{'opfullname'};
2739
    $useropemail = $userreg{$username}->{'opemail'};
2740
    $useropphone = $userreg{$username}->{'opphone'};
2741
    $useralertemail = $userreg{$username}->{'alertemail'};
2742

    
2743
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$postreply = "Unable to access image register"; return;};
2744
    unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {return "Unable to access networks register"};
2745

    
2746
    # Collect systems from domains and include domains as children
2747
    if ($action ne 'flatlist') { # Dont include children in select
2748
        my @imagenames = qw(image image2 image3 image4);
2749
        foreach my $valref (@domregvalues) {
2750
        # Only include VM's belonging to current user (or all users if specified and user is admin)
2751
            if ($username eq $valref->{'user'} || $fulllist) {
2752
                next unless (!$curuuid || ($valref->{'uuid'} eq $curuuid || $valref->{'system'} eq $curuuid));
2753

    
2754
                my %val = %{$valref}; # Deference and assign to new ass array, effectively cloning object
2755
                my $sysuuid = $val{'system'};
2756
                my $dbobj = $register{$sysuuid};
2757
                $val{'memory'} += 0;
2758
                $val{'vcpu'} += 0;
2759
                $val{'nodetype'} = 'child';
2760
                $val{'fullname'} = $val{'fullname'} || $dbobj->{'fullname'} || $userfullname;
2761
                $val{'email'} = $val{'email'} || $dbobj->{'email'} || $useremail;
2762
                $val{'phone'} = $val{'phone'} || $dbobj->{'phone'} || $userphone;
2763
                $val{'opfullname'} = $val{'opfullname'} || $dbobj->{'opfullname'} || $useropfullname;
2764
                $val{'opemail'} = $val{'opemail'} || $dbobj->{'opemail'} || $useropemail;
2765
                $val{'opphone'} = $val{'opphone'} || $dbobj->{'opphone'} || $useropphone;
2766
                $val{'alertemail'} = $val{'alertemail'} || $dbobj->{'alertemail'} || $useralertemail;
2767
                $val{'autostart'} = ($val{'autostart'})?'1':'';
2768

    
2769
                foreach my $img (@imagenames) {
2770
                    if ($imagereg{$val{$img}} && $imagereg{$val{$img}}->{'storagepool'} == -1) {
2771
                        $val{'nodestorage'} += $imagereg{$val{$img}}->{'virtualsize'};
2772
                    } else {
2773
                        $val{'storage'} += $imagereg{$val{$img}}->{'virtualsize'} if ($imagereg{$val{$img}});
2774
                    }
2775
                }
2776
                $val{'externalips'} += 1 if ($networkreg{$val{'networkuuid1'}} && $networkreg{$val{'networkuuid1'}}->{'type'} =~ /externalip|ipmapping/);
2777
                $val{'externalips'} += 1 if ($networkreg{$val{'networkuuid2'}} && $networkreg{$val{'networkuuid2'}}->{'type'} =~ /externalip|ipmapping/);
2778
                $val{'externalips'} += 1 if ($networkreg{$val{'networkuuid3'}} && $networkreg{$val{'networkuuid3'}}->{'type'} =~ /externalip|ipmapping/);
2779
                $val{'networktype1'} = $networkreg{$val{'networkuuid1'}}->{'type'} if ($networkreg{$val{'networkuuid1'}});
2780
                $val{'imageuuid'} = $imagereg{$val{'image'}}->{'uuid'} if ($imagereg{$val{'image'}});
2781
                $val{'imageuuid2'} = $imagereg{$val{'image2'}}->{'uuid'} if ($imagereg{$val{'image2'}} && $val{'image2'} && $val{'image2'} ne '--');
2782
                $val{'internalip'} = $networkreg{$val{'networkuuid1'}}->{'internalip'} if ($networkreg{$val{'networkuuid1'}});
2783
                $val{'externalip'} = $networkreg{$val{'networkuuid1'}}->{'externalip'} if ($networkreg{$val{'networkuuid1'}});
2784

    
2785
                my $networkuuid1; # needed for generating management url
2786
                if ($sysuuid && $sysuuid ne '--') { # We are dealing with a server that's part of a system
2787
                    if (!$register{$sysuuid}) { #System does not exist - create it
2788
                        $sysname = $val{'name'};
2789
                        $sysname = $1 if ($sysname =~ /(.+)\..*/);
2790
                        $sysname =~ s/server/System/i;
2791
                        $register{$sysuuid} = {
2792
                            uuid => $sysuuid,
2793
                            name => $sysname,
2794
                            user => $username,
2795
                            created => $current_time
2796
                        };
2797
                    }
2798

    
2799
                    my %pval = %{$register{$sysuuid}};
2800
                    $pval{'status'} = '--';
2801
                    $pval{'issystem'} = 1;
2802
                    $pval{'fullname'} = $pval{'fullname'} || $userfullname;
2803
                    $pval{'email'} = $pval{'email'} || $useremail;
2804
                    $pval{'phone'} = $pval{'phone'} || $userphone;
2805
                    $pval{'opfullname'} = $pval{'opfullname'} || $useropfullname;
2806
                    $pval{'opemail'} = $pval{'opemail'} || $useropemail;
2807
                    $pval{'opphone'} = $pval{'opphone'} || $useropphone;
2808
                    $pval{'alertemail'} = $pval{'alertemail'} || $useralertemail;
2809
                    $pval{'autostart'} = ($pval{'autostart'})?'1':'';
2810

    
2811
                    my @children;
2812
                    if ($curreg{$sysuuid}->{'children'}) {
2813
                        @children = @{$curreg{$sysuuid}->{'children'}};
2814
                    }
2815
                    # If system has an admin image, update networkuuid1 with the image's server's info
2816
                    if ($pval{'image'} && $pval{'image'} ne '--') {
2817
                        my $dbimg = $imagereg{$pval{'image'}};
2818
                        $networkuuid1 = $domreg{$dbimg->{'domains'}}->{'networkuuid1'} if ($domreg{$dbimg->{'domains'}});
2819
                        my $externalip = '';
2820
                        my $ports = '';
2821
                        if ($networkreg{$networkuuid1}) {
2822
                            $externalip = $networkreg{$networkuuid1}->{'externalip'};
2823
                            $ports = $networkreg{$networkuuid1}->{'ports'}
2824
                        }
2825
                        $register{$sysuuid}->{'networkuuid1'} = $networkuuid1;
2826
                        $register{$sysuuid}->{'internalip'} = $networkreg{$networkuuid1}->{'internalip'} if ($networkreg{$networkuuid1});
2827
                        $pval{'master'} = $dbimg->{'master'};
2828
                        $pval{'appid'} = $dbimg->{'appid'};
2829
                        $pval{'version'} = $dbimg->{'version'};
2830
                        my $managementurl;
2831
                        $managementurl = $dbimg->{'managementlink'};
2832
                        $managementurl =~ s/\{uuid\}/$networkuuid1/;
2833
                        $managementurl =~ s/\{externalip\}/$externalip/;
2834
                        $pval{'managementurl'} = $managementurl;
2835
                        my $upgradeurl;
2836
                        $upgradeurl = $dbimg->{'upgradelink'};
2837
                        $upgradeurl =~ s/\{uuid\}/$networkuuid1/;
2838
                        $pval{'upgradeurl'} = $upgradeurl;
2839
                        my $terminalurl;
2840
                        $terminalurl = $dbimg->{'terminallink'};
2841
                        $terminalurl =~ s/\{uuid\}/$networkuuid1/;
2842
                        $pval{'terminalurl'} = $terminalurl;
2843
                        $pval{'externalip'} = $externalip;
2844
                        $pval{'ports'} = $ports;
2845
                        $pval{'imageuuid'} = $dbimg->{'uuid'};
2846
                        $pval{'imageuuid2'} = $imagereg{$pval{'image2'}}->{'uuid'} if ($pval{'image2'} && $pval{'image2'} ne '--');
2847
                    }
2848
                    push @children,\%val;
2849
                    $pval{'children'} = \@children;
2850
                    $curreg{$sysuuid} = \%pval;
2851
                } else { # This server is not part of a system
2852
                    $sysuuid = $val{'uuid'};
2853
                    my $dbimg = $imagereg{$val{'image'}};
2854
                    $networkuuid1 = $domreg{$dbimg->{'domains'}}->{'networkuuid1'} if ($domreg{$dbimg->{'domains'}});
2855
                    my $externalip;
2856
                    if ($networkreg{$networkuuid1}) {
2857
                        $externalip = $networkreg{$networkuuid1}->{'externalip'};
2858
                        $val{'internalip'} = $networkreg{$networkuuid1}->{'internalip'};
2859
                        $val{'ports'} = $networkreg{$networkuuid1}->{'ports'};
2860
                    }
2861
                    $val{'networkuuid1'} = $networkuuid1;
2862
                    $val{'master'} = $dbimg->{'master'};
2863
                    $val{'appid'} = $dbimg->{'appid'};
2864
                    $val{'version'} = $dbimg->{'version'};
2865
                    $val{'imageuuid'} = $dbimg->{'uuid'};
2866
                    $val{'imageuuid2'} = $imagereg{$val{'image2'}}->{'uuid'} if ($val{'image2'} && $val{'image2'} ne '--' && $imagereg{$val{'image2'}});
2867

    
2868
                    my $managementurl = $dbimg->{'managementlink'};
2869
                    $managementurl =~ s/\{uuid\}/$networkuuid1/;
2870
                    $managementurl =~ s/\{externalip\}/$externalip/;
2871
                    $val{'managementurl'} = $managementurl;
2872
                    my $upgradeurl;
2873
                    $upgradeurl = $dbimg->{'upgradelink'};
2874
                    $upgradeurl =~ s/\{uuid\}/$networkuuid1/;
2875
                    $val{'upgradeurl'} = $upgradeurl;
2876
                    my $terminalurl;
2877
                    $terminalurl = $dbimg->{'terminallink'};
2878
                    $terminalurl =~ s/\{uuid\}/$networkuuid1/;
2879
                    $val{'terminalurl'} = $terminalurl;
2880
                    $val{'externalip'} = $externalip;
2881
                    $val{'system'} = '--';
2882

    
2883
                    $curreg{$sysuuid} = \%val;
2884
                }
2885
            }
2886
        }
2887
        tied(%register)->commit;
2888
    }
2889
    untie %imagereg;
2890

    
2891
    my @regvalues = values %register;
2892
    # Go through systems register, add empty systems and update statuses
2893
    foreach my $valref (@regvalues) {
2894
    # Only include items belonging to current user (or all users if specified and user is admin)
2895
        if ($username eq $valref->{'user'} || $fulllist) {
2896
            next unless (!$curuuid || $valref->{'uuid'} eq $curuuid);
2897

    
2898
            my %val = %{$valref};
2899
            # add empty system (must be empty since not included from going through servers
2900
            if (!($curreg{$val{'uuid'}})) {
2901
                $val{'issystem'} = 1;
2902
                $val{'status'} = 'inactive';
2903
                $curreg{$val{'uuid'}} = \%val;
2904
            } else {
2905
            # Update status
2906
                my $status = 'running';
2907
                my $externalips = 0;
2908
                foreach my $child (@{$curreg{$val{'uuid'}}-> {'children'}}) {
2909
                    $status = $child->{'status'} unless ($child->{'status'} eq $status);
2910
                    $externalips += $child->{'externalips'} unless ($child->{'externalips'} eq '');
2911
                }
2912
                $status = 'degraded' unless ($status eq 'running' || $status eq 'shutoff');
2913
                $curreg{$val{'uuid'}}->{'status'} = $status;
2914
                $curreg{$val{'uuid'}}->{'externalips'} = $externalips;
2915
                # $networkreg{$domreg{$curdomuuid}->{'networkuuid1'}}->{'internalip'};
2916
                if ($curuuid && !$curreg{$val{'uuid'}}->{'internalip'}) { # Add calling server's own internalip if it's part of an ad-hoc assembled system
2917
                    $curreg{$val{'uuid'}}->{'internalip'} = $networkreg{$domreg{$curdomuuid}->{'networkuuid1'}}->{'internalip'};
2918
                }
2919
            }
2920
        }
2921
    }
2922
    untie %networkreg;
2923

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

    
2928
    if ($action eq 'tablelist') {
2929
        my $t2 = Text::SimpleTable->new(40,24,14);
2930

    
2931
        $t2->row('uuid', 'name', 'user');
2932
        $t2->hr;
2933
        my $pattern = $options{m};
2934
        foreach $rowref (@sorted_systems){
2935
            if ($pattern) {
2936
                my $rowtext = $rowref->{'uuid'} . " " . $rowref->{'name'} . " " . $rowref->{'user'};
2937
                next unless ($rowtext =~ /$pattern/i);
2938
            }
2939
            $t2->row($rowref->{'uuid'}, $rowref->{'name'}||'--', $rowref->{'user'}||'--');
2940
        }
2941
        return $t2->draw;
2942
    } elsif ($action eq 'removeusersystems') {
2943
        return @sorted_systems;
2944
    } elsif ($action eq 'arraylist') {
2945
        return @sorted_systems;
2946
    } elsif ($console) {
2947
        return Dumper(\@sorted_systems);
2948
    } else {
2949
        my %it = ('uuid','--','name','--', 'issystem', 1);
2950
        push(@sorted_systems, \%it) if ($action eq 'flatlist');
2951
        my $json_text = to_json(\@sorted_systems, {pretty => 1});
2952
        $json_text =~ s/"false"/false/g;
2953
        $json_text =~ s/"true"/true/g;
2954
#        $json_text =~ s/""/"--"/g;
2955
        $json_text =~ s/null/"--"/g;
2956
        $json_text =~ s/\x/ /g;
2957
        if ($action eq 'flatlist') {
2958
            return qq|{"identifier": "uuid", "label": "name", "items": $json_text}|;
2959
        } else {
2960
            return $json_text;
2961
        }
2962
    }
2963
}
2964

    
2965
# Build a complete system around cloned image
2966
sub buildSystem {
2967
    my ($name, $hmaster, $hstoragepool, $hsystem, $hinstances,
2968
        $hnetworkuuid1, $hbschedule, $hnetworktype1, $hports, $hmemory, $hvcpu, $hdiskbus,
2969
        $hcdrom, $hboot, $hnicmodel1, $hnicmac1, $hnetworkuuid2, $hnicmac2, $hmonitors,
2970
        $hmanagementlink, $hstart, $duuid, $hstoragepool2, $hloader ) = @_;
2971

    
2972
    unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {$postreply = "Unable to access domain register"; return $postreply;};
2973
    unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', key=>'path'}, $Stabile::dbopts)) ) {$postreply = "Unable to access image register"; return $postreply;};
2974

    
2975
    my $master = $hmaster;
2976

    
2977
    if ($curuuid && !$domreg{$curuuid} && $duuid) { # curuuid is a system uuid
2978
        $curuuid = $duuid;
2979
    }
2980

    
2981
    if (!$master && $curuuid && $domreg{$curuuid} && $imagereg{$domreg{$curuuid}->{image}}) {
2982
        $master = $imagereg{$domreg{$curuuid}->{image}}->{master};
2983
    }
2984
    my $cdrom = $hcdrom;
2985
    my $storagepool = $hstoragepool;
2986
    my $storagepool2 = $hstoragepool2 || '0';
2987
    my $loader = $hloader || 'bios';
2988
    my $image2;
2989
    $hinstances = 1 unless ($hinstances);
2990
    my $ioffset = 0;
2991
    if (!$name && $curuuid) {
2992
        $ioffset = 1; # Looks like we are called from an existing server - bump
2993
        $name = $domreg{$curuuid}->{'name'};
2994
        $name = $1 if ($name =~ /(.+)\.\d+$/);
2995
        foreach my $dom (values %domreg) { # Sequential naming of related systems
2996
            if ($dom->{'user'} eq $user && $dom->{'name'} =~ /$name\.(\d+)$/) {
2997
                $ioffset = $1+1 if ($1 >= $ioffset);
2998
            }
2999
        }
3000
    }
3001
    if ($master && !$imagereg{"$master"}) {
3002
    # Try to look up master based on file name
3003
        my @spoolpaths = $cfg->param('STORAGE_POOLS_LOCAL_PATHS');
3004
        my @users = ('common', $user);
3005
        foreach my $u (@accounts) {push @users,$u;};
3006
        # Include my sponsors master images
3007
        my $billto = $userreg{$user}->{'billto'};
3008
        push @users, $billto if ($billto);
3009
        # Also include my subusers' master images
3010
        my @userregkeys = (tied %userreg)->select_where("billto = '$user'");
3011
        push @users, @userregkeys if (@userregkeys);
3012

    
3013
        my $match;
3014
        foreach my $u (@users) {
3015
            foreach $sp (@spoolpaths) {
3016
                if ($imagereg{"$sp/$u/$master"}) {
3017
                    $master = "$sp/$u/$master";
3018
                    $match = 1;
3019
                    last;
3020
                }
3021
            }
3022
            last if ($match),
3023
        }
3024
    }
3025

    
3026
    if (!$imagereg{$master} && length $master == 36) {
3027
    # Try to look up master by uuid
3028
        unless ( tie(%imagereg2,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {$postreply = "Unable to access image register"; return $postreply;};
3029
        $master = $imagereg2{$master}->{'path'} if ($imagereg2{$master});
3030
        untie %imagereg2;
3031
    }
3032

    
3033
    if (!$master && $curuuid) {
3034
        $master = $imagereg{$domreg{$curuuid}->{'image'}}->{'master'};
3035
    }
3036

    
3037
    unless ($imagereg{$master}) {$postreply = "Status=Error Invalid master $master"; return $postreply;};
3038
    my $masterimage2 = $imagereg{$master}->{'image2'};
3039
    my $sysuuid = $hsystem;
3040

    
3041
    if ($cdrom && $cdrom ne '--' && !$imagereg{"$cdrom"}) {
3042
    # Try to look up cdrom based on file name
3043
        my @spoolpaths = $cfg->param('STORAGE_POOLS_LOCAL_PATHS');
3044
        my @users = ('common', $user);
3045
        foreach my $u (@accounts) {push @users,$u;};
3046
        my $match;
3047
        foreach my $u (@users) {
3048
            foreach $sp (@spoolpaths) {
3049
                if ($imagereg{"$sp/$u/$cdrom"}) {
3050
                    $cdrom = "$sp/$u/$cdrom";
3051
                    $match = 1;
3052
                    last;
3053
                }
3054
            }
3055
            last if ($match),
3056
        }
3057
    }
3058

    
3059
    #open OUTPUT, '>', "/dev/null"; select OUTPUT;
3060
    $Stabile::Images::console = 1;
3061
    require "$Stabile::basedir/cgi/images.cgi";
3062
    $Stabile::Networks::console = 1;
3063
    require "$Stabile::basedir/cgi/networks.cgi";
3064
    $Stabile::Servers::console = 1;
3065
    require "$Stabile::basedir/cgi/servers.cgi";
3066

    
3067
    #close(OUTPUT); select STDOUT;
3068
    # reset stdout to be the default file handle
3069
    my $oipath; # This var stores admin servers image, if only one server initially
3070
    if ($sysuuid eq 'new') {
3071
        $sysuuid = '';
3072
    } elsif ($sysuuid eq 'auto' || (!$sysuuid && $curuuid)) { # $curuuid means request is coming from a running vm
3073
        my $domuuid = $curuuid || Stabile::Networks::ip2domain( $ENV{'REMOTE_ADDR'} );
3074
        if ($domuuid && $domreg{$domuuid}) {
3075
            if ($domreg{$domuuid}->{'system'}) {
3076
                $sysuuid = $domreg{$domuuid}->{'system'};
3077
            } else {
3078
                my $ug = new Data::UUID;
3079
                $sysuuid = $ug->create_str();
3080
                #$sysuuid = $domuuid; # Make sysuuid same as primary domains uuid
3081
                $domreg{$domuuid}->{'system'} = $sysuuid;
3082
                $oipath = $domreg{$domuuid}->{'image'};
3083
            }
3084
        } else {
3085
            $sysuuid = '';
3086
        }
3087
    }
3088

    
3089
    # Check if images should be moved to node storage
3090
    if ($storagepool eq "-1") {
3091
        if (index($privileges, 'n')==-1 && !$isadmin) {
3092
            $storagepool = '';
3093
        } else {
3094
            $storagepool = -1;
3095
            # %nodereg is needed in order to increment reservedvcpus for nodes
3096
            unless ( tie(%nodereg,'Tie::DBI', Hash::Merge::merge({table=>'nodes', key=>'mac'}, $Stabile::dbopts)) ) {$postreply = "Unable to access node register"; return $postreply;};
3097
        }
3098
    }
3099

    
3100
    my @domains;
3101
    my $systemuuid;
3102
    for (my $i=$ioffset; $i<$hinstances+$ioffset; $i++) {
3103
        my $ipath;
3104
        my $mac;
3105
        my $res;
3106
        my $istr = ".$i";
3107
        $istr = '' if ($hinstances==1 && $i==0);
3108

    
3109
    # Clone image
3110
        my $imagename = $name;
3111
        $imagename =~ s/system/Image/i;
3112
        $res = Stabile::Images::Clone($master, 'clone', '', $storagepool, '', "$imagename$istr", $hbschedule, 1, $hmanagementlink, $appid, 1, $hvcpu, $hmemory);
3113
        $postreply .= $res;
3114
        if ($res =~ /path: (.+)/) {
3115
            $ipath = $1;
3116
        } else {
3117
            next;
3118
        }
3119
        $mac = $1 if ($res =~ /mac: (.+)/);
3120
        Stabile::Images::updateBilling();
3121

    
3122
        # Secondary image - clone it
3123
        if ($masterimage2 && $masterimage2 ne '--' && $masterimage2 =~ /\.master\.qcow2$/) {
3124
            $res = Stabile::Images::Clone($masterimage2, 'clone', '', $storagepool2, $mac, "$imagename$istr-data", $hbschedule, 1, '', '', 1);
3125
            $postreply .= $res;
3126
            $image2 = $1 if ($res =~ /path: (.+)/);
3127
        }
3128

    
3129
    # Create network
3130
        my $networkuuid1;
3131
        if ($hnetworkuuid1) { # An existing network was specified
3132
            $networkuuid1 = $hnetworkuuid1;
3133
        } else { # Create new network
3134
            my $networkname = $name;
3135
            $networkname =~ s/system/Connection/i;
3136
            my $type = ($i==0)?$hnetworktype1 : '';
3137
            if (!$type) {
3138
                if ($curuuid && $i==0) { # This should never be true, leaving for now...
3139
                    unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {$postreply = "Unable to access networks register"; return $postreply;};
3140
                    $type = $networkreg{$domreg{$curuuid}->{'networkuuid1'}}->{'type'};
3141
                    untie %networkreg;
3142
                } else {
3143
                    $type = 'internalip';
3144
                }
3145
            }
3146
            $main::syslogit->($user, 'info', "saving network $networkname$istr");
3147
            $res = Stabile::Networks::save('', '', "$networkname$istr", 'new', $type, '','',$hports,1,$user);
3148
            $postreply .= $res;
3149
            if ($res =~ /uuid: (.+)/) {
3150
                $networkuuid1 = $1;
3151
            } else {
3152
                next;
3153
            }
3154
        }
3155

    
3156
    # Create server
3157
        my $servername = $name;
3158
        $servername =~ s/system/Server/i;
3159
        if ($curuuid) {
3160
            $hmemory = $hmemory || $domreg{$curuuid}->{'memory'};
3161
            $hvcpu = $hvcpu || $domreg{$curuuid}->{'vcpu'};
3162
            $hdiskbus = $hdiskbus || $domreg{$curuuid}->{'diskbus'};
3163
            $cdrom = $cdrom || $domreg{$curuuid}->{'cdrom'};
3164
            $hboot = $hboot || $domreg{$curuuid}->{'boot'};
3165
            $hnicmodel1 = $hnicmodel1 || $domreg{$curuuid}->{'nicmodel1'};
3166
        }
3167

    
3168
        $main::syslogit->($user, 'info', "saving server $servername$istr");
3169
        $res =  Stabile::Servers::Save('', '', {
3170
                 uuid => '',
3171
                 name => "$servername$istr",
3172
                 memory => $hmemory,
3173
                 vcpu => $hvcpu,
3174
                 image => $ipath,
3175
                 imagename => '',
3176
                 image2 => $image2,
3177
                 image2name => '',
3178
                 diskbus => $hdiskbus,
3179
                 cdrom => $cdrom,
3180
                 boot => $hboot,
3181
                 loader => $loader,
3182
                 networkuuid1 => $networkuuid1,
3183
                 networkid1 => '',
3184
                 networkname1 => '',
3185
                 nicmodel1 => $hnicmodel1,
3186
                 nicmac1 => $hnicmac1,
3187
                 nicmac2 => $hnicmac2,
3188
                 status => 'new',
3189
                 notes => $notes,
3190
                 system => $sysuuid,
3191
                 newsystem => ($hinstances>1 && !$sysuuid),
3192
                 buildsystem => 1,
3193
                 console => 1
3194
             });
3195

    
3196
        $postreply .= "$res\n";
3197
        $sysuuid = $1 if ($res =~ /sysuuid: (\S+)/);
3198
        my $serveruuid;
3199
        $serveruuid = $1 if ($res =~ /uuid: (\S+)/);
3200
        my $sys = $register{$sysuuid};
3201
        if ($sysuuid && $i==$ioffset) {
3202
            $register{$sysuuid} = {
3203
                uuid => $sysuuid,
3204
                name => $sys->{'name'} || $servername, #Don't rename existing system
3205
                user => $user,
3206
                image => $sys->{'image'} || $oipath || $ipath, #Don't update admin image for existing system
3207
                created => $current_time
3208
            };
3209
        }
3210

    
3211
    # Create monitors
3212
        my @monitors = split(",", $hmonitors);
3213
        if (@monitors) {
3214
            $res = addSimpleMonitors($serveruuid, $alertemail, \@monitors);
3215
            if ( $res eq 'OK' ) {
3216
                `/usr/bin/moncmd reset keepstate &`;
3217
                $postreply .= "Status=OK Saved monitors @monitors\n";
3218
            } else {
3219
                $postreply .= "Status=OK Not saving monitors: $res\n";
3220
            }
3221

    
3222
        }
3223

    
3224
        if ($serveruuid) {
3225
            unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {$postreply = "Unable to access networks register"; return $postreply;};
3226
            $networkreg{$networkuuid1}->{'domains'} = $serveruuid;
3227
            tied(%networkreg)->commit;
3228
            untie %networkreg;
3229

    
3230
            push @domains, $serveruuid;
3231
            $imagereg{$ipath}->{'domains'} = $serveruuid;
3232
            $imagereg{$ipath}->{'domainnames'} = "$servername$istr";
3233
            if ($storagepool == -1) {
3234
                # my $mac = $imagereg{$ipath}->{'mac'};
3235
                # Increment reserved vcpus in order for location of target node to spread out
3236
                $postreply .= "Status=OK Cloned image to node $mac: $nodereg{$mac}->{'reservedvcpus'}";
3237
                $nodereg{$mac}->{'reservedvcpus'} += $hvcpu;
3238
                $postreply .= ":$nodereg{$mac}->{'reservedvcpus'}\n";
3239
                tied(%nodereg)->commit;
3240
                if (!$hstart) { # If we are not starting servers, wake up node anyway to perform clone operation
3241
                    if ($nodereg{$mac}->{'status'} eq 'asleep') {
3242
                        require "$Stabile::basedir/cgi/nodes.cgi";
3243
                        $Stabile::Nodes::console = 1;
3244
                        Stabile::Nodes::wake($mac);
3245
                    }
3246
                }
3247
            }
3248
        }
3249
        $systemuuid = (($sysuuid)? $sysuuid : $serveruuid) unless ($systemuuid);
3250
    }
3251
    if ($storagepool == -1) {
3252
        untie %nodereg;
3253
    }
3254

    
3255
    $postreply .= "Status=OK sysuuid: $systemuuid\n" if ($systemuuid);
3256
    if ($hstart) {
3257
        foreach my $serveruuid (@domains) {
3258
            $postreply .= Stabile::Servers::Start($serveruuid, 'start',{buildsystem=>1});
3259
        }
3260
    } else {
3261
        $main::updateUI->({tab=>'servers', user=>$user, uuid=>$serveruuid, status=>'shutoff'});
3262
    }
3263
    untie %imagereg;
3264
    #if (@domains) {
3265
    #    return to_json(\@domains, {pretty=>1});
3266
    #} else {
3267
        return $postreply;
3268
    #}
3269
}
3270

    
3271
sub upgradeSystem {
3272
    my $internalip = shift;
3273

    
3274
    unless (tie %imagereg,'Tie::DBI', { # Needed for ValidateItem
3275
        db=>'mysql:steamregister',
3276
        table=>'images',
3277
        key=>'path',
3278
        autocommit=>0,
3279
        CLOBBER=>3,
3280
        user=>$dbiuser,
3281
        password=>$dbipasswd}) {throw Error::Simple("Stroke=ERROR Image register could not be accessed")};
3282

    
3283
    my $appid;
3284
    my $appversion;
3285
    my $appname;
3286
    my $master;
3287
    my $progress;
3288
    my $currentversion;
3289

    
3290
# Locate the system we should upgrade
3291
    if ($internalip) {
3292
        foreach my $network (values %networkreg) {
3293
            if ($internalip =~ /^10\.\d+\.\d+\.\d+/
3294
                && $network->{'internalip'} eq $internalip
3295
                && $network->{'user'} eq $user
3296
            ) {
3297
                $curuuid = $domreg{$network->{'domains'}}->{'uuid'};
3298
                $cursysuuid = $domreg{$curuuid}->{'system'};
3299
                $master = $imagereg{$domreg{$curuuid}->{'image'}}->{'master'};
3300
                $appid = $imagereg{$master}->{'appid'};
3301
                $appversion = $imagereg{$master}->{'version'};
3302
                $appname = $imagereg{$master}->{'name'};
3303
                last;
3304
            }
3305
        }
3306
    }
3307
# Locate the newest version of master image
3308
    my $currentmaster;
3309
    foreach my $imgref (values %imagereg) {
3310
        if ($imgref->{'path'} =~ /\.master\.qcow2$/
3311
            && $imgref->{'path'} !~ /-data\.master\.qcow2$/
3312
            && $imgref->{'appid'} eq $appid
3313
        ) {
3314
            if ($imgref->{'version'} > $currentversion) {
3315
                $currentmaster = $imgref;
3316
                $currentversion = $imgref->{'version'};
3317
            }
3318
        }
3319
    }
3320
# Build list of system members
3321
    my @doms;
3322
    if ($cursysuuid && $register{$cursysuuid}) {
3323
        $register{$cursysuuid}->{'status'} = 'upgrading';
3324
        foreach my $domref (values %domreg) {
3325
            push( @doms, $domref ) if ($domref->{'system'} eq $cursysuuid && $domref->{'user'} eq $user);
3326
        }
3327
    } else {
3328
        push( @doms, $domreg{$curuuid} ) if ($domreg{$curuuid}->{'user'} eq $user);
3329
    }
3330
    $membs = int @doms;
3331

    
3332
    my $problem = 0;
3333
    foreach my $dom (@doms) {
3334
        if ($dom->{'status'} ne 'running') {
3335
            $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user,
3336
            status=>qq|Server $dom->{name} is not running. All member servers must be running when upgrading an app.|});
3337
            $problem = 1;
3338
            last;
3339
        }
3340
    }
3341
# First dump each servers data to nfs
3342
    unless ($problem) {
3343
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>"Already newest version, reinstalling version $currentversion!", title=>'Reinstalling, hold on...'});
3344
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>'Beginning data dump!'});
3345

    
3346
        my $browser = LWP::UserAgent->new;
3347
        $browser->agent('movepiston/1.0b');
3348
        $browser->protocols_allowed( [ 'http','https'] );
3349

    
3350
        foreach my $dom (@doms) {
3351
            my $upgradelink = $imagereg{$dom->{'image'}}->{'upgradelink'};
3352
            if ($upgradelink) {
3353
                my $res;
3354
                my $networkuuid1 = $dom->{'networkuuid1'};
3355
                my $ip = $networkreg{$networkuuid1}->{'internalip'};
3356
                $upgradelink = "http://internalip$upgradelink" unless ($upgradelink =~ s/\{internalip\}/$ip/);
3357
                $domreg{$dom->{'uuid'}}->{'status'} = 'upgrading';
3358
                $main::updateUI->({tab=>'servers', user=>$user, uuid=>$dom->{'uuid'}, status=>'upgrading'});
3359
                my $content = $browser->get($upgradelink)->content();
3360
                if ($content =~ /^\{/) { # Looks like json
3361
                    $jres = from_json($content);
3362
                    $res = $jres->{'message'};
3363
                    unless (lc $jres->{'status'} eq 'ok') {
3364
                        $problem = 2;
3365
                    }
3366
                } else { # no json returned, assume things went hayward
3367
                    $res = $content;
3368
                    $res =~ s/</&lt;/g;
3369
                    $res =~ s/>/&gt;/g;
3370
                    $problem = "Data dump failed ($upgradelink)";
3371
                }
3372
                $res =~ s/\n/ /;
3373
                $progress += 10;
3374
                $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>"$ip: $res", progress=>$progress});
3375
            }
3376
        }
3377
    }
3378
    tied(%domreg)->commit;
3379

    
3380
# Shut down all servers
3381
    unless ($problem) {
3382
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>'Beginning shutdown of servers!'});
3383
        require "$Stabile::basedir/cgi/servers.cgi";
3384
        $Stabile::Servers::console = 1;
3385
        foreach my $dom (@doms) {
3386
            $progress += 10;
3387
            my $networkuuid1 = $dom->{'networkuuid1'};
3388
            my $ip = $networkreg{$networkuuid1}->{'internalip'};
3389
            $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>"$ip: Shutting down...", progress=>$progress});
3390
            if ($dom->{'status'} eq 'shutoff' || $dom->{'status'} eq 'inactive') {
3391
                next;
3392
            } else {
3393
                my $res = Stabile::Servers::destroyUserServers($user, 1, $dom->{'uuid'});
3394
                if ($dom->{'status'} ne 'shutoff' && $dom->{'status'} ne 'inactive') {
3395
                    $problem = "ERROR $res"; # We could not shut down a server, fail...
3396
                    last;
3397
                }
3398
            }
3399
        }
3400
    }
3401
# Then replace each image with new version
3402
    unless ($problem) {
3403
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>'Attaching new images!'});
3404
        require "$Stabile::basedir/cgi/images.cgi";
3405
        $Stabile::Images::console = 1;
3406
        foreach my $dom (@doms) {
3407
            $progress += 10;
3408
            my $networkuuid1 = $dom->{'networkuuid1'};
3409
            my $ip = $networkreg{$networkuuid1}->{'internalip'};
3410
            $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>"$ip: Attaching image...", progress=>$progress});
3411
            my $image = $imagereg{$dom->{'image'}};
3412
            my $ipath;
3413
            # Clone image
3414
            my $imagename = $image->{'name'};
3415
            my $res = Stabile::Images::Clone($currentmaster->{'path'}, 'clone', '', $image->{'storagepool'}, '', $imagename, $image->{'bschedule'}, 1, $currentmaster->{'managementlink'}, $appid, 1);
3416
            $postreply .= $res;
3417
            if ($res =~ /path: (.+)/) {
3418
                $ipath = $1;
3419
            } else {
3420
                $problem = 5;
3421
            }
3422

    
3423
            if ($ipath =~ /\.qcow2$/) {
3424
                Stabile::Images::updateBilling();
3425
                # Attach new image to server
3426
                $main::syslogit->($user, 'info', "attaching new image to server $dom->{'name'} ($dom->{'uuid'})");
3427
                $res =  Stabile::Servers::Save({
3428
                         uuid => $dom->{'uuid'},
3429
                         image => $ipath,
3430
                         imagename => $imagename,
3431
                     });
3432
                # Update systems admin image
3433
                $register{$cursysuuid}->{'image'} = $ipath if ($register{$cursysuuid} && $dom->{'uuid'} eq $curuuid);
3434
                # Update image properties
3435
                $imagereg{$ipath}->{'domains'} = $dom->{'uuid'};
3436
                $imagereg{$ipath}->{'domainnames'} = $dom->{'name'};
3437
            } else {
3438
                $problem = 6;
3439
            }
3440
        }
3441
    }
3442

    
3443
# Finally start all servers with new image
3444
    unless ($problem) {
3445
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>'Starting servers!'});
3446
        require "$Stabile::basedir/cgi/servers.cgi";
3447
        $Stabile::Servers::console = 1;
3448
        foreach my $dom (@doms) {
3449
            $progress += 10;
3450
            my $networkuuid1 = $dom->{'networkuuid1'};
3451
            my $ip = $networkreg{$networkuuid1}->{'internalip'};
3452
            $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, status=>"$ip: Starting...", progress=>$progress});
3453
            if ($dom->{'status'} eq 'shutoff' || $dom->{'status'} eq 'inactive') {
3454
                Stabile::Servers::Start($dom->{'uuid'}, 'start', {uistatus=>'upgrading'});
3455
                $main::updateUI->({ tab=>'servers',
3456
                                    user=>$user,
3457
                                    uuid=>$dom->{'uuid'},
3458
                                    status=>'upgrading'})
3459
            }
3460
        }
3461
    } else {
3462
        foreach my $dom (@doms) {
3463
            $dom->{'status'} = 'inactive'; # Prevent servers from being stuck in upgrading status
3464
        }
3465
    }
3466

    
3467
    my $nlink = $imagereg{$doms[0]->{'image'}}->{'managementlink'}; # There might be a new managementlink for image
3468
    my $nuuid = $doms[0]->{'networkuuid1'};
3469
    $nlink =~ s/\{uuid\}/$nuuid/;
3470

    
3471
    unless ($problem) {
3472
# All servers successfully upgraded
3473
        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.|;
3474
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, progress=>100, status=>$status, managementlink=>$nlink, message=>"All done!"});
3475
    } else {
3476
        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.|;
3477
        $main::updateUI->({tab=>'upgrade', type=>'update', user=>$user, progress=>100, status=>$status, managementlink=>$nlink, message=>"Something went wrong :("});
3478
    }
3479
    untie %imagereg;
3480

    
3481
    my $reply = qq|{"message": "Upgrading $domreg{$curuuid}->{name} with $membs members"}|;
3482
    return "$reply\n";
3483
}
3484

    
3485
sub removeusersystems {
3486
    my $username = shift;
3487
    return unless (($isadmin || $user eq $username) && !$isreadonly);
3488
    $user = $username;
3489
    my @allsystems = getSystemsListing('removeusersystems');
3490
    foreach my $sys (@allsystems) {
3491
        next unless $sys->{'uuid'};
3492
        remove($sys->{'uuid'}, $sys->{'issystem'}, 1);
3493
        #$postreply .= "Status=OK Removing system $sys->{'name'} ($sys->{'uuid'})\n";
3494
    }
3495
    return $postreply || "[]";
3496
}
3497

    
3498

    
3499
# Remove every trace of a system including servers, images, etc.
3500
sub remove {
3501
    my ($uuid, $issystem, $destroy) = @_;
3502
    my $sysuuid = $uuid;
3503
    my $reguser = $register{$uuid}->{'user'} if ($register{$uuid});
3504
    $reguser = $domreg{$uuid}->{'user'} if (!$reguser && $domreg{$uuid});
3505

    
3506
    $Stabile::Images::user = $user;
3507
    require "$Stabile::basedir/cgi/images.cgi";
3508
    $Stabile::Images::console = 1;
3509

    
3510
    $Stabile::Networks::user = $user;
3511
    require "$Stabile::basedir/cgi/networks.cgi";
3512
    $Stabile::Networks::console = 1;
3513

    
3514
    $Stabile::Servers::user = $user;
3515
    require "$Stabile::basedir/cgi/servers.cgi";
3516
    $Stabile::Servers::console = 1;
3517

    
3518
    $issystem = 1 if ($register{$uuid});
3519
    my @domains;
3520
    my $res;
3521

    
3522
    if ($issystem) {
3523
    # Delete child servers
3524
        if (($user eq $reguser || $isadmin) && $register{$uuid}){ # Existing system
3525
        # First delete any linked networks
3526
            if ($register{$uuid}->{'networkuuids'} && $register{$uuid}->{'networkuuids'} ne '--') {
3527
                my @lnetworks = split /, ?/, $register{$uuid}->{'networkuuids'};
3528
                foreach my $networkuuid (@lnetworks) {
3529
                    if ($networkuuid) {
3530
                        Stabile::Networks::Deactivate($networkuuid);
3531
                        $res .= Stabile::Networks::Remove($networkuuid, 'remove', {force=>1});
3532
                    }
3533
                }
3534
            }
3535
            foreach my $domvalref (values %domreg) {
3536
                if ($domvalref->{'system'} eq $uuid && ($domvalref->{'user'} eq $user || $isadmin)) {
3537
                    if ($domvalref->{'status'} eq 'shutoff' || $domvalref->{'status'} eq 'inactive') {
3538
                        push @domains, $domvalref->{'uuid'};
3539
                    } elsif ($destroy) {
3540
                        Stabile::Servers::destroyUserServers($reguser, 1, $domvalref->{'uuid'});
3541
                        push @domains, $domvalref->{'uuid'} if ($domvalref->{'status'} eq 'shutoff' || $domvalref->{'status'} eq 'inactive');
3542
                    }
3543
                }
3544
            }
3545
        }
3546
        $postreply .= "Status=removing OK Removing system $register{$uuid}->{'name'} ($uuid)\n";
3547
        delete $register{$uuid};
3548
        tied(%register)->commit;
3549
    } elsif ($domreg{$uuid} && $domreg{$uuid}->{uuid}) {
3550
    # Delete single server
3551
        if ($domreg{$uuid}->{'status'} eq 'shutoff' || $domreg{$uuid}->{'status'} eq 'inactive') {
3552
            push @domains, $uuid;
3553
        } elsif ($destroy) {
3554
            Stabile::Servers::destroyUserServers($reguser, 1, $uuid);
3555
            push @domains, $uuid if ($domreg{$uuid}->{'status'} eq 'shutoff' || $domreg{$uuid}->{'status'} eq 'inactive');
3556
        }
3557
     #   $postreply .= "Status=OK Removing server $domreg{$uuid}->{'name'} ($uuid)\n";
3558
    } else {
3559
        $postreply .= "Status=Error System $uuid not found\n";
3560
        return $postreply;
3561
    }
3562
    my $duuid;
3563
    foreach my $domuuid (@domains) {
3564
        if ($domreg{$domuuid}->{'status'} ne 'shutoff' && $domreg{$domuuid}->{'status'} ne 'inactive' ) {
3565
            $postreply .= "Status=ERROR Cannot delete server (active)\n";
3566
        } else {
3567
            my $imagepath = $domreg{$domuuid}->{'image'};
3568
            my $image2path = $domreg{$domuuid}->{'image2'};
3569
            my $networkuuid1 = $domreg{$domuuid}->{'networkuuid1'};
3570
            my $networkuuid2 = $domreg{$domuuid}->{'networkuuid2'};
3571

    
3572
            # Delete packages from software register
3573
        #    $postreply .= deletePackages($domuuid);
3574
            # Delete monitors
3575
        #    $postreply .= deleteMonitors($domuuid)?"Stream=OK Deleted monitors for $domreg{$domuuid}->{'name'}\n":"Stream=OK No monitors to delete for $domreg{$domuuid}->{'name'}\n";
3576
            # Delete server
3577
            $res .= Stabile::Servers::Remove($domuuid);
3578

    
3579
            # Delete images
3580
            $res .= Stabile::Images::Remove($imagepath);
3581
            if ($image2path && $image2path ne '--') {
3582
                $res .= Stabile::Images::Remove($image2path);
3583
            }
3584
            # Delete networks
3585
            if ($networkuuid1 && $networkuuid1 ne '--' && $networkuuid1 ne '0' && $networkuuid1 ne '1') {
3586
                Stabile::Networks::Deactivate($networkuuid1);
3587
                $res .= Stabile::Networks::Remove($networkuuid1);
3588
            }
3589
            if ($networkuuid2 && $networkuuid2 ne '--' && $networkuuid2 ne '0' && $networkuuid2 ne '1') {
3590
                Stabile::Networks::Deactivate($networkuuid2);
3591
                $res .= Stabile::Networks::Remove($networkuuid2);
3592
            }
3593
        }
3594
        $duuid = $domuuid;
3595
    }
3596
    if ($register{$uuid}) {
3597
        delete $register{$uuid};
3598
        tied(%register)->commit;
3599
    }
3600
    if (@domains) {
3601
        $main::updateUI->(
3602
                        {tab=>'servers',
3603
                        user=>$user,
3604
                        type=>'update',
3605
                        message=>((scalar @domains==1)?"Server has been removed":"Stack has been removed!")
3606
                        },
3607
                        {tab=>'images',
3608
                        user=>$user
3609
                        },
3610
                        {tab=>'networks',
3611
                        user=>$user
3612
                        },
3613
                        {tab=>'home',
3614
                        user=>$user,
3615
                        type=>'removal',
3616
                        uuid=>$uuid,
3617
                        domuuid=>$duuid
3618
                        }
3619
                    );
3620
    } else {
3621
        $main::updateUI->(
3622
                        {tab=>'servers',
3623
                        user=>$user,
3624
                        type=>'update',
3625
                        message=>"Nothing to remove!"
3626
                        }
3627
                    );
3628
    }
3629

    
3630
    if ($engineid && $enginelinked) {
3631
        # Remove domain from origo.io
3632
        my $json_text = qq|{"uuid": "$sysuuid" , "status": "delete"}|;
3633
        $main::postAsyncToOrigo->($engineid, 'updateapps', "[$json_text]");
3634
    }
3635
    return $postreply || qq|Content-type: application/json\n\n|;
3636
}
3637

    
3638
sub getPackages {
3639
    my $curimg = shift;
3640

    
3641
    unless (tie %imagereg,'Tie::DBI', { # Needed for ValidateItem
3642
        db=>'mysql:steamregister',
3643
        table=>'images',
3644
        key=>'path',
3645
        autocommit=>0,
3646
        CLOBBER=>0,
3647
        user=>$dbiuser,
3648
        password=>$dbipasswd}) {throw Error::Simple("Stroke=ERROR Image register could not be accessed")};
3649

    
3650
    my $mac = $imagereg{$curimg}->{'mac'};
3651
    untie %imagereg;
3652

    
3653
    my $macip;
3654
    if ($mac && $mac ne '--') {
3655
        unless (tie %nodereg,'Tie::DBI', {
3656
            db=>'mysql:steamregister',
3657
            table=>'nodes',
3658
            key=>'mac',
3659
            autocommit=>0,
3660
            CLOBBER=>1,
3661
            user=>$dbiuser,
3662
            password=>$dbipasswd}) {return 0};
3663
        $macip = $nodereg{$mac}->{'ip'};
3664
        untie %nodereg;
3665
    }
3666
    $curimg =~ /(.+)/; $curimg = $1;
3667
    my $sshcmd;
3668
    if ($macip && $macip ne '--') {
3669
        $sshcmd = "/usr/bin/ssh -q -l irigo -i /var/www/.ssh/id_rsa_www -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $macip";
3670
    }
3671
    my $apps;
3672

    
3673
    if ($sshcmd) {
3674
        my $cmd = qq[eval \$(/usr/bin/guestfish --ro -a "$curimg" --i --listen); ]; # sets $GUESTFISH_PID shell var
3675
        $cmd .= qq[root="\$(/usr/bin/guestfish --remote inspect-get-roots)"; ];
3676
        $cmd .= qq[guestfish --remote inspect-get-product-name "\$root"; ];
3677
        $cmd .= qq[guestfish --remote inspect-get-hostname "\$root"; ];
3678
        $cmd .= qq[guestfish --remote inspect-list-applications "\$root"; ];
3679
        $cmd .= qq[guestfish --remote exit];
3680
        $cmd = "$sshcmd '$cmd'";
3681
        $apps = `$cmd`;
3682
    } else {
3683
        my $cmd;
3684
#        my $pid = open my $cmdpipe, "-|",qq[/usr/bin/guestfish --ro -a "$curimg" --i --listen];
3685
            $cmd .= qq[eval \$(/usr/bin/guestfish --ro -a "$curimg" --i --listen); ];
3686
        # Start listening guestfish
3687
        my $daemon = Proc::Daemon->new(
3688
                work_dir => '/usr/local/bin',
3689
                setuid => 'www-data',
3690
                exec_command => $cmd
3691
            ) or do {$posterror .= "Stream=ERROR $@\n";};
3692
        my $pid = $daemon->Init();
3693
        while ($daemon->Status($pid)) {
3694
            sleep 1;
3695
        }
3696
        # Find pid of the listening guestfish
3697
        my $pid2;
3698
        my $t = new Proc::ProcessTable;
3699
        foreach $p ( @{$t->table} ){
3700
            my $pcmd = $p->cmndline;
3701
            if ($pcmd =~ /guestfish.+$curimg/) {
3702
                $pid2 = $p->pid;
3703
                last;
3704
            }
3705
        }
3706
        my $cmd2;
3707
        if ($pid2) {
3708
            $cmd2 .= qq[root="\$(/usr/bin/guestfish --remote=$pid2 inspect-get-roots)"; ];
3709
            $cmd2 .= qq[guestfish --remote=$pid2 inspect-get-product-name "\$root"; ];
3710
            $cmd2 .= qq[guestfish --remote=$pid2 inspect-get-hostname "\$root"; ];
3711
            $cmd2 .= qq[guestfish --remote=$pid2 inspect-list-applications "\$root"; ];
3712
            $cmd2 .= qq[guestfish --remote=$pid2 exit];
3713
        }
3714
        $apps = `$cmd2`;
3715
        $apps .= $cmd2;
3716
    }
3717
    return $apps;
3718
}
(7-7/9)