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.stabile.io/info/stabiledocs/licensing/stabile-open-source-license
|
7
|
|
8
|
package Stabile::Users;
|
9
|
|
10
|
use Error qw(:try);
|
11
|
use Time::Local;
|
12
|
# use Time::HiRes qw( time );
|
13
|
use Config::Simple;
|
14
|
use Text::CSV_XS qw( csv );
|
15
|
use Proc::Daemon;
|
16
|
use MIME::Lite;
|
17
|
use File::Basename;
|
18
|
use Data::Password qw(:all);
|
19
|
use Geo::IP;
|
20
|
use lib dirname (__FILE__);
|
21
|
use Stabile;
|
22
|
|
23
|
$engineid = $Stabile::config->get('ENGINEID') || "";
|
24
|
$enginename = $Stabile::config->get('ENGINENAME') || "";
|
25
|
#$enginelinked = $Stabile::config->get('ENGINE_LINKED') || "";
|
26
|
$showcost = $Stabile::config->get('SHOW_COST') || "";
|
27
|
$cur = $Stabile::config->get('CURRENCY') || "USD";
|
28
|
$engineuser = $Stabile::config->get('ENGINEUSER') || "";
|
29
|
$externaliprangestart = $Stabile::config->get('EXTERNAL_IP_RANGE_START') || "";
|
30
|
$externaliprangeend = $Stabile::config->get('EXTERNAL_IP_RANGE_END') || "";
|
31
|
$proxyiprangestart = $Stabile::config->get('PROXY_IP_RANGE_START') || "";
|
32
|
$proxyiprangeend = $Stabile::config->get('PROXY_IP_RANGE_END') || "";
|
33
|
$proxygw = $Stabile::config->get('PROXY_GW') || "";
|
34
|
|
35
|
$uiuuid;
|
36
|
$uistatus;
|
37
|
$help = 0; # If this is set, functions output help
|
38
|
|
39
|
#our %options=();
|
40
|
# -a action -h help -u uuid -m match pattern -f full list, i.e. all users
|
41
|
# -v verbose, include HTTP headers -s impersonate subaccount -t target [uuid or image]
|
42
|
# -g args to gearman task
|
43
|
#Getopt::Std::getopts("a:hfu:g:m:vs:t:", \%options);
|
44
|
|
45
|
try {
|
46
|
Init(); # Perform various initalization tasks
|
47
|
process() if ($package);
|
48
|
|
49
|
} catch Error with {
|
50
|
my $ex = shift;
|
51
|
print header('text/html', '500 Internal Server Error') unless ($console);
|
52
|
if ($ex->{-text}) {
|
53
|
print "Got error: ", $ex->{-text}, " on line ", $ex->{-line}, "\n";
|
54
|
} else {
|
55
|
print "Status=ERROR\n";
|
56
|
}
|
57
|
} finally {
|
58
|
};
|
59
|
|
60
|
1;
|
61
|
|
62
|
sub getObj {
|
63
|
my %h = %{@_[0]};
|
64
|
$console = 1 if $h{"console"};
|
65
|
$api = 1 if $h{"api"};
|
66
|
my $username = $h{"username"} || $h{"uuid"};
|
67
|
my $obj;
|
68
|
$action = $action || $h{'action'};
|
69
|
if ($action=~ /engine$|updateclientui$|updateui$/) {
|
70
|
$obj = \%h;
|
71
|
$obj->{pwd} = $obj->{password} if ($obj->{password});
|
72
|
} else {
|
73
|
$obj = $register{$username};
|
74
|
my %hobj = %{$register{$username}};
|
75
|
$obj = \%hobj; # We do this to get around a weird problem with freeze...
|
76
|
my @props = qw ( restorefile engineid enginename engineurl username user password pwd fullname email
|
77
|
opemail alertemail phone opphone opfullname allowfrom allowinternalapi privileges accounts accountsprivileges
|
78
|
storagepools memoryquota storagequota nodestoragequota vcpuquota externalipquota rxquota txquota billto dnsdomains appstoreurl totpsecret );
|
79
|
foreach my $prop (@props) {
|
80
|
if (defined $h{$prop}) {
|
81
|
$obj->{$prop} = $h{$prop};
|
82
|
}
|
83
|
}
|
84
|
}
|
85
|
return $obj;
|
86
|
}
|
87
|
|
88
|
sub Init {
|
89
|
# Tie database tables to hashes
|
90
|
unless ( tie(%register,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username'}, $Stabile::dbopts)) ) {return "Unable to access users register"};
|
91
|
|
92
|
# simplify globals initialized in Stabile.pm
|
93
|
$tktuser = $tktuser || $Stabile::tktuser;
|
94
|
$user = $user || $Stabile::user;
|
95
|
|
96
|
$fullname = $register{$user}->{'fullname'};
|
97
|
$email = $register{$user}->{'email'};
|
98
|
$opemail = $register{$user}->{'opemail'};
|
99
|
$alertemail = $register{$user}->{'alertemail'};
|
100
|
$phone = $register{$user}->{'phone'};
|
101
|
$opphone = $register{$user}->{'opphone'};
|
102
|
$opfullname = $register{$user}->{'opfullname'};
|
103
|
$allowfrom = $register{$user}->{'allowfrom'};
|
104
|
$allowinternalapi = $register{$user}->{'allowinternalapi'};
|
105
|
$lastlogin = $register{$user}->{'lastlogin'};
|
106
|
$lastloginfrom = $register{$user}->{'lastloginfrom'};
|
107
|
|
108
|
# if ($register{$user}->{'lastlogin'} ne $tkt) {
|
109
|
# $register{$user}->{'lastlogin'} = time;
|
110
|
# $register{$user}->{'lastloginfrom'} = $ENV{'REMOTE_ADDR'};
|
111
|
# $register{$user}->{'lasttkt'} = $tkt;
|
112
|
# }
|
113
|
|
114
|
$Stabile::userstoragequota = 0+ $register{$user}->{'storagequota'};
|
115
|
$Stabile::usernodestoragequota = 0+ $register{$user}->{'nodestoragequota'};
|
116
|
$usermemoryquota = 0+ $register{$user}->{'memoryquota'};
|
117
|
$uservcpuquota = 0+ $register{$user}->{'vcpuquota'};
|
118
|
$userexternalipquota = 0+ $register{$user}->{'externalipquota'};
|
119
|
$userrxquota = 0+ $register{$user}->{'rxquota'};
|
120
|
$usertxquota = 0+ $register{$user}->{'txquota'};
|
121
|
|
122
|
$storagequota = $Stabile::userstoragequota || $defaultstoragequota;
|
123
|
$nodestoragequota = $Stabile::usernodestoragequota || $defaultnodestoragequota;
|
124
|
$memoryquota = $usermemoryquota || $defaultmemoryquota;
|
125
|
$vcpuquota = $uservcpuquota || $defaultvcpuquota;
|
126
|
$externalipquota = $userexternalipquota || $defaultexternalipquota;
|
127
|
$rxquota = $userrxquota || $defaultrxquota;
|
128
|
$txquota = $usertxquota || $defaulttxquota;
|
129
|
|
130
|
# Create aliases of functions
|
131
|
*header = \&CGI::header;
|
132
|
|
133
|
*Unlinkengine = \&Linkengine;
|
134
|
*Updateengine = \&Linkengine;
|
135
|
*Saveengine = \&Linkengine;
|
136
|
*Syncusers = \&Linkengine;
|
137
|
|
138
|
*do_help = \&action;
|
139
|
*do_show = \&do_uuidshow;
|
140
|
*do_delete = \&do_remove;
|
141
|
*do_tablelist = \&do_list;
|
142
|
*do_billingstatus = \&do_billing;
|
143
|
*do_usage = \&do_billing;
|
144
|
*do_usagestatus = \&do_billing;
|
145
|
*do_billingavgstatus = \&do_billing;
|
146
|
*do_usageavgstatus = \&do_billing;
|
147
|
*do_upgradeengine = \&privileged_action;
|
148
|
*do_gear_upgradeengine = \&do_gear_action;
|
149
|
*do_backupengine = \&privileged_action;
|
150
|
*do_gear_backupengine = \&do_gear_action;
|
151
|
*do_restoreengine = \&privileged_action;
|
152
|
*do_gear_restoreengine = \&do_gear_action;
|
153
|
*do_releasepressure = \&privileged_action_async;
|
154
|
*do_gear_releasepressure = \&do_gear_action;
|
155
|
|
156
|
*do_linkengine = \&privileged_action;
|
157
|
*do_gear_linkengine = \&do_gear_action;
|
158
|
*do_saveengine = \&privileged_action_async;
|
159
|
*do_gear_saveengine = \&do_gear_action;
|
160
|
*do_unlinkengine = \&privileged_action;
|
161
|
*do_gear_unlinkengine = \&do_gear_action;
|
162
|
*do_updateengine = \&privileged_action;
|
163
|
*do_syncusers = \&privileged_action;
|
164
|
*do_gear_updateengine = \&do_gear_action;
|
165
|
*do_gear_syncusers = \&do_gear_action;
|
166
|
*do_deleteentirely = \&privileged_action;
|
167
|
*do_gear_deleteentirely = \&do_gear_action;
|
168
|
*do_vent = \&privileged_action;
|
169
|
*do_gear_vent = \&do_gear_action;
|
170
|
*do_updateui = \&privileged_action;
|
171
|
*do_gear_updateui = \&do_gear_action;
|
172
|
}
|
173
|
|
174
|
sub do_listaccounts {
|
175
|
my ($uuid, $action, $obj) = @_;
|
176
|
if ($help) {
|
177
|
return <<END
|
178
|
GET:common:
|
179
|
List other user accounts current user has access to use and switch to. This is an internal method which includes html
|
180
|
specifically for use with Dojo.
|
181
|
END
|
182
|
}
|
183
|
my $common = $params{'common'};
|
184
|
my %bhash;
|
185
|
my @accounts = split(/,\s*/, $register{$tktuser}->{'accounts'});
|
186
|
my @accountsprivs = split(/,\s*/, $register{$tktuser}->{'accountsprivileges'});
|
187
|
for my $i (0 .. $#accounts) {
|
188
|
$bhash{$accounts[$i]} = {
|
189
|
id=>$accounts[$i],
|
190
|
privileges=>$accountsprivs[$i] || 'r'
|
191
|
} if ($register{$accounts[$i]}); # Only include accounts that exist on this engine
|
192
|
};
|
193
|
$bhash{$tktuser} = {id=>$tktuser, privileges=>$privileges};
|
194
|
delete $bhash{$user};
|
195
|
$bhash{'common'} = {id=>'common', privileges=>'--'} if ($common);
|
196
|
my @bvalues = values %bhash;
|
197
|
unshift(@bvalues, {id=>$user, privileges=>$privileges});
|
198
|
my $logout = {privileges=>'', id=>'<span class="glyphicon glyphicon-log-out" aria-hidden="true" style="font-size:15px;color:#3c3c3c; vertical-align:top; margin-top:8px;"></span> Log out '};
|
199
|
push(@bvalues, $logout) unless ($common);
|
200
|
$postreply = "{\"identifier\": \"id\",\"label\": \"id\", \"items\":" . JSON::to_json(\@bvalues, {pretty=>1}) . "}";
|
201
|
return $postreply;
|
202
|
}
|
203
|
|
204
|
sub do_listids {
|
205
|
my ($uuid, $action, $obj) = @_;
|
206
|
if ($help) {
|
207
|
return <<END
|
208
|
GET::
|
209
|
List other user accounts current user has read access to. Call with flat=1 if you want a flat array.
|
210
|
END
|
211
|
}
|
212
|
require "$Stabile::basedir/cgi/images.cgi";
|
213
|
my $backupdevice = Stabile::Images::Getbackupdevice('', 'getbackupdevice');
|
214
|
my $imagesdevice = Stabile::Images::Getimagesdevice('', 'getimagesdevice');
|
215
|
my $mounts = `cat /proc/mounts | grep zfs`;
|
216
|
my %engine_h;
|
217
|
my $zbackupavailable = ( (($mounts =~ /$backupdevice\/backup (\S+) zfs/) && ($mounts =~ /$imagesdevice\/images (\S+) zfs/) )?1:'');
|
218
|
my $jsontext = qq|{"identifier": "id","label": "id", "items":[| .
|
219
|
qq|{"id": "$user", "privileges": "$privileges", "userprivileges": "$dbprivileges", "tktuser": "$tktuser", |.
|
220
|
qq|"storagequota": $storagequota, "nodestoragequota": $nodestoragequota, "memoryquota": $memoryquota, "vcpuquota": $vcpuquota, |.
|
221
|
qq|"fullname": "$fullname", "email": "$email", "opemail": "$opemail", "alertemail": "$alertemail", |.
|
222
|
qq|"phone": "$phone", "opphone": "$opphone", "opfullname": "$opfullname", "appstoreurl": "$appstoreurl", |.
|
223
|
qq|"allowfrom": "$allowfrom", "lastlogin": "$lastlogin", "lastloginfrom": "$lastloginfrom", "allowinternalapi": "$allowinternalapi", "billto": "$billto", |.
|
224
|
qq|"dnsdomain": "$dnsdomain", "appstoreurl": "$appstoreurl", |;
|
225
|
|
226
|
if ($isadmin && $engineid) {
|
227
|
$engine_h{"engineid"} = $engineid;
|
228
|
$engine_h{"engineuser"} = $engineuser;
|
229
|
$engine_h{"externaliprangestart"} = $externaliprangestart;
|
230
|
$engine_h{"externaliprangeend"} = $externaliprangeend;
|
231
|
$engine_h{"proxyiprangestart"} = $proxyiprangestart;
|
232
|
$engine_h{"proxyiprangeend"} = $proxyiprangeend;
|
233
|
$engine_h{"proxygw"} = $proxygw;
|
234
|
|
235
|
$engine_h{"disablesnat"} = $disablesnat;
|
236
|
$engine_h{"imagesdevice"} = $imagesdevice;
|
237
|
$engine_h{"backupdevice"} = $backupdevice;
|
238
|
|
239
|
my $nodecfg = new Config::Simple("/etc/stabile/nodeconfig.cfg");
|
240
|
my $readlimit = $nodecfg->param('VM_READ_LIMIT'); # e.g. 125829120 = 120 * 1024 * 1024 = 120 MB / s
|
241
|
my $writelimit = $nodecfg->param('VM_WRITE_LIMIT');
|
242
|
my $iopsreadlimit = $nodecfg->param('VM_IOPS_READ_LIMIT'); # e.g. 1000 IOPS
|
243
|
my $iopswritelimit = $nodecfg->param('VM_IOPS_WRITE_LIMIT');
|
244
|
$engine_h{"vmreadlimit"} = $readlimit;
|
245
|
$engine_h{"vmwritelimit"} = $writelimit;
|
246
|
$engine_h{"vmiopsreadlimit"} = $iopsreadlimit;
|
247
|
$engine_h{"vmiopswritelimit"} = $iopswritelimit;
|
248
|
|
249
|
$engine_h{"zfsavailable"} = $zbackupavailable;
|
250
|
$engine_h{"downloadmasters"} = $downloadmasters;
|
251
|
}
|
252
|
if (-e "/var/www/stabile/static/img/logo-icon-" . $ENV{HTTP_HOST} . ".png") {
|
253
|
$jsontext .= qq|"favicon": "/stabile/static/img/logo-icon-$ENV{HTTP_HOST}.png", |;
|
254
|
}
|
255
|
$engine_h{"enginename"} = $enginename;
|
256
|
$engine_h{"enginelinked"} = $enginelinked;
|
257
|
$jsontext .= "\"showcost\": \"$showcost\", ";
|
258
|
$jsontext .= "\"externalipquota\": $externalipquota, \"rxquota\": $rxquota, \"txquota\": $txquota, ";
|
259
|
$jsontext .= qq|"defaultstoragequota": $defaultstoragequota, "defaultnodestoragequota": $defaultnodestoragequota, "defaultmemoryquota": $defaultmemoryquota, "defaultvcpuquota": $defaultvcpuquota, |;
|
260
|
$jsontext .= "\"defaultexternalipquota\": $defaultexternalipquota, \"defaultrxquota\": $defaultrxquota, \"defaulttxquota\": $defaulttxquota, ";
|
261
|
$jsontext .= qq|"engine": | . to_json(\%engine_h);
|
262
|
$jsontext .= "}, ";
|
263
|
|
264
|
$jsontext .= "{\"id\": \"common\", \"privileges\": \"--\"," .
|
265
|
"\"fullname\": \"--\", \"email\": \"--\"," .
|
266
|
"\"storagequota\": 0, \"memoryquota\": 0, \"vcpuquota\": 0, \"externalipquota\": 0," .
|
267
|
"\"rxquota\": 0, \"txquota\": 0}";
|
268
|
|
269
|
$jsontext .= ", {\"id\": \"$billto\"}" if ($billto && $billto ne '--');
|
270
|
|
271
|
foreach my $aid (keys %ahash) {
|
272
|
my $privs = $ahash{$aid};
|
273
|
$jsontext .= qq|, {"id": "$aid", "privileges": "$privs"}| unless ($aid eq $user || $aid eq $billto);
|
274
|
}
|
275
|
|
276
|
$jsontext .= "]}";
|
277
|
# Create ui_update link in case we are logging in with a remotely generated ticket, i.e. not passing through login.cgi
|
278
|
`/bin/ln -s ../ui_update.cgi ../cgi/ui_update/$user~ui_update.cgi` unless (-e "../cgi/ui_update/$user~ui_update.cgi");
|
279
|
$postreply = to_json(from_json($jsontext), {pretty=>1});
|
280
|
return $postreply;
|
281
|
}
|
282
|
|
283
|
|
284
|
sub do_listengines{
|
285
|
my ($uuid, $action, $obj) = @_;
|
286
|
if ($help) {
|
287
|
return <<END
|
288
|
GET::
|
289
|
List other engines user has access to
|
290
|
END
|
291
|
}
|
292
|
if ($enginelinked) {
|
293
|
require LWP::Simple;
|
294
|
my $browser = LWP::UserAgent->new;
|
295
|
$browser->agent('stabile/1.0b');
|
296
|
$browser->protocols_allowed( [ 'http','https'] );
|
297
|
|
298
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
299
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
300
|
|
301
|
$postreq->{'engineid'} = $engineid;
|
302
|
# $postreq->{'user'} = $tktuser;
|
303
|
$postreq->{'user'} = $user;
|
304
|
$postreq->{'enginetkthash'} = Digest::SHA::sha512_hex($tktkey);
|
305
|
|
306
|
my $content = $browser->post("https://www.stabile.io/irigo/engine.cgi?action=listengines", $postreq)->content();
|
307
|
if ($content =~ /ERROR:(.+)"/) {
|
308
|
$postreply = qq|{"identifier": "url", "label": "name", "items": [{"url": "# $1", "name": "$enginename"}]}|;
|
309
|
} else {
|
310
|
$postreply = qq|{"identifier": "url", "label": "name", "items": $content}|;
|
311
|
}
|
312
|
} else {
|
313
|
$postreply = qq|{"identifier": "url", "label": "name", "items": [{"url": "#", "name": "$enginename"}]}|;
|
314
|
}
|
315
|
return $postreply;
|
316
|
}
|
317
|
|
318
|
sub do_billing {
|
319
|
my ($uuid, $action, $obj) = @_;
|
320
|
if ($help) {
|
321
|
return <<END
|
322
|
GET:uuid,username,month,startmonth,endmonth,format:
|
323
|
List usage data, optionally for specific server/system [uuid] or user [username]. May be called as usage, usagestatus or usageavgstatus.
|
324
|
When called as "usage", format may be csv, in which case startmonth and endmonth may be specified.
|
325
|
END
|
326
|
}
|
327
|
my $buser = $params{'user'} || $params{'username'} || $user;
|
328
|
my $bmonth = $params{'month'} || $month;
|
329
|
$bmonth = substr("0$bmonth", -2);
|
330
|
my $byear = $params{'year'} || $year;
|
331
|
my $vcpu=0, $memory=0, $virtualsize=0, $nodevirtualsize=0, $backupsize=0, $externalip=0;
|
332
|
my $rx = 0;
|
333
|
my $tx = 0;
|
334
|
my $vcpuavg = 0;
|
335
|
my $externalipavg = 0;
|
336
|
$uuid = '' if ($register{$uuid}); # check if $uuid was set to $user because no actual uuid passed
|
337
|
|
338
|
if ($user eq $buser || index($privileges,"a")!=-1) {
|
339
|
my %stats = collectBillingData( $uuid, $buser, $bmonth, $byear, $showcost );
|
340
|
my $memoryquotagb = int(0.5 + 100*$memoryquota/1024)/100;
|
341
|
my $storagequotagb = int(0.5 + 100*$storagequota/1024)/100;
|
342
|
my $nodestoragequotagb = int(0.5 + 100*$nodestoragequota/1024)/100;
|
343
|
my $irigo_cost = ($showcost?"showcost":"hidecost");
|
344
|
|
345
|
if ($action eq "billing" || $action eq "usage") {
|
346
|
if ($params{'format'} eq 'csv') {
|
347
|
$postreply = header("text/plain");
|
348
|
my $startmonth = $params{'startmonth'} || 1;
|
349
|
my $endmonth = $params{'endmonth'} || $bmonth;
|
350
|
my @vals;
|
351
|
for (my $i=$startmonth; $i<=$endmonth; $i++) {
|
352
|
my $m = substr("0$i", -2);
|
353
|
my %mstats = collectBillingData( $uuid, $buser, $m, $byear, $showcost );
|
354
|
push @vals, \%mstats;
|
355
|
}
|
356
|
csv(in => \@vals, out => \my $csvdata);
|
357
|
$postreply .= $csvdata;
|
358
|
} else {
|
359
|
my $json_text = JSON::to_json(\%stats, {pretty => 1});
|
360
|
$postreply = "$json_text";
|
361
|
}
|
362
|
|
363
|
} elsif ($action eq "billingstatus" || $action eq "usagestatus") {
|
364
|
my $virtualsizegb = $stats{'virtualsize'};
|
365
|
my $backupsizegb = $stats{'backupsize'};
|
366
|
my $externalip = $stats{'externalip'};
|
367
|
my $memorygb = $stats{'memory'};
|
368
|
my $nodevirtualsizegb = $stats{'nodevirtualsize'};
|
369
|
$rx = $stats{'rx'};
|
370
|
$tx = $stats{'tx'};
|
371
|
$vcpu = $stats{'vcpu'};
|
372
|
|
373
|
my $res;
|
374
|
if ($params{'format'} eq 'html') {
|
375
|
$postreply .= header("text/html");
|
376
|
$res .= qq[<tr><th>Ressource</th><th>Quantity</th><th class="$irigo_cost">Cost/month</th><th>Quota</th></tr>];
|
377
|
$res .= qq[<tr><td>vCPU's:</td><td align="right">$vcpu</td><td align="right" class="$irigo_cost">$cur ] . int(0.5+$vcpu*$vcpuprice) . qq[</td><td align="right">$vcpuquota</td></tr>];
|
378
|
$res .= qq[<tr><td>Memory:</td><td align="right">$memorygb GB</td><td align="right" class="$irigo_cost">$cur ] . int(0.5+$memorygb*$memoryprice) . qq[</td><td align="right">$memoryquotagb GB</td></tr>];
|
379
|
$res .= qq[<tr><td>Shared storage:</td><td align="right">$virtualsizegb GB</td><td align="right" class="$irigo_cost">$cur ] . int(0.5+$virtualsizegb*$storageprice) . qq[</td><td align="right">$storagequotagb GB</td></tr>];
|
380
|
$res .= qq[<tr><td>Node storage:</td><td align="right">$nodevirtualsizegb GB</td><td align="right" class="$irigo_cost">$cur ] . int(0.5+$nodevirtualsizegb*$nodestorageprice) . qq[</td><td align="right">$nodestoragequotagb GB</td></tr>];
|
381
|
$res .= qq[<tr><td>Backup storage (est.):</td><td align="right">$backupsizegb GB</td><td align="right" class="$irigo_cost">$cur ] . int(0.5+$backupsizegb*$storageprice) . qq[</td><td align="right">∞</td></tr>];
|
382
|
$res .= qq[<tr><td>External IP addresses:</td><td align="right">$externalip</td><td align="right" class="$irigo_cost">$cur ] . int(0.5+$externalip*$externalipprice) . qq[</td><td align="right">$externalipquota</td></tr>];
|
383
|
if (!$uuid) {
|
384
|
$res .= qq[<tr><td>Network traffic out:</td><td align="right">] . $rx . qq[ GB</td><td align="right" class="$irigo_cost">$cur 0</td><td align="right">] . int(0.5 + $rxquota/1024/1024) . qq[ GB</td></tr>];
|
385
|
$res .= qq[<tr><td>Network traffic in:</td><td align="right">] . $tx . qq[ GB</td><td align="right" class="$irigo_cost">$cur 0</td><td align="right">] . int(0.5 + $txquota/1024/1024) . qq[ GB</td></tr>];
|
386
|
}
|
387
|
|
388
|
$res =~ s/-1/∞/g;
|
389
|
$res =~ s/>0 .B<\/td><\/tr>/>∞<\/td><\/tr>/g;
|
390
|
$postreply .= qq[<table cellspacing="0" noframe="void" norules="rows" class="systemTables">$res</table>];
|
391
|
} else {
|
392
|
my $bill = {
|
393
|
vcpus => {quantity => $vcpu, quota => $vcpuquota},
|
394
|
memory => {quantity => $memorygb, unit => 'GB', quota => $memoryquotagb},
|
395
|
shared_storage => {quantity => $virtualsizegb, unit => 'GB', quota => $storagequotagb},
|
396
|
node_storage => {quantity => $nodevirtualsizegb, unit => 'GB', quota => $nodestoragequotagb},
|
397
|
backup_storage => {quantity => $backupsizegb, unit => 'GB'},
|
398
|
external_ips => {quantity => $externalip, quota => $externalipquota},
|
399
|
network_traffic_out => {quantity => $rx, unit => 'GB', quota => int(0.5 + $rxquota/1024/1024)},
|
400
|
network_traffic_in => {quantity => $tx, unit => 'GB', quota => int(0.5 + $txquota/1024/1024)}
|
401
|
};
|
402
|
if ($showcost) {
|
403
|
$bill->{vcpus}->{cost} = int(0.5+$vcpu*$vcpuprice);
|
404
|
$bill->{memory}->{cost} = int(0.5+$memorygb*$memoryprice);
|
405
|
$bill->{shared_storage}->{cost} = int(0.5+$virtualsizegb*$storageprice);
|
406
|
$bill->{node_storage}->{cost} = int(0.5+$nodevirtualsizegb*$nodestorageprice);
|
407
|
$bill->{backup_storage}->{cost} = int(0.5+$backupsizegb*$storageprice);
|
408
|
$bill->{external_ips}->{cost} = int(0.5+$externalip*$externalipprice);
|
409
|
$bill->{currency} = $cur;
|
410
|
$bill->{username} = $buser;
|
411
|
}
|
412
|
$postreply .= to_json($bill, {pretty=>1});
|
413
|
}
|
414
|
} elsif ($action eq "billingavgstatus" || $action eq "usageavgstatus") {
|
415
|
my $virtualsizeavggb = $stats{'virtualsizeavg'};
|
416
|
my $backupsizeavggb = $stats{'backupsizeavg'};
|
417
|
my $memoryavggb = $stats{'memoryavg'};
|
418
|
my $nodevirtualsizeavggb = $stats{'nodevirtualsizeavg'};
|
419
|
$vcpuavg = $stats{'vcpuavg'};
|
420
|
$externalipavg = $stats{'externalipavg'};
|
421
|
$rx = $stats{'rx'};
|
422
|
$tx = $stats{'tx'};
|
423
|
if ($params{'format'} eq 'html') {
|
424
|
$postreply .= header("text/html");
|
425
|
my $res;
|
426
|
$res .= qq[<tr><th>Ressource</th><th>Quantity</th><th class="$irigo_cost">Cost/month</th><th>Quota</th></tr>];
|
427
|
$res .= qq[<tr><td>vCPU's:</td><td align="right">] . int(0.5+100*$vcpuavg)/100 . qq[</td><td align="right" class="$irigo_cost">$cur ] . int(0.5+$vcpuavg*$vcpuprice) . qq[</td><td align="right">$vcpuquota</td></tr>];
|
428
|
$res .= qq[<tr><td>Memory:</td><td align="right">$memoryavggb GB</td><td align="right" class="$irigo_cost">$cur ] . int(0.5+$memoryavggb*$memoryprice) . qq[</td><td align="right">$memoryquotagb GB</td></tr>];
|
429
|
$res .= qq[<tr><td>Shared storage:</td><td align="right">$virtualsizeavggb GB</td><td align="right" class="$irigo_cost">$cur ] . int(0.5+$virtualsizeavggb*$storageprice) . qq[</td><td align="right">$storagequotagb GB</td></tr>];
|
430
|
$res .= qq[<tr><td>Node storage:</td><td align="right">$nodevirtualsizeavggb GB</td><td align="right" class="$irigo_cost">$cur ] . int(0.5+$nodevirtualsizeavggb*$nodestorageprice) . qq[</td><td align="right">$nodestoragequotagb GB</td></tr>];
|
431
|
$res .= qq[<tr><td>Backup storage (est.):</td><td align="right">$backupsizeavggb GB</td><td align="right" class="$irigo_cost">$cur ] . int(0.5+$backupsizeavggb*$storageprice) . qq[</td><td align="right">∞</td></tr>];
|
432
|
$res .= qq[<tr><td>External IP addresses:</td><td align="right">] . int(0.5+100*$externalipavg)/100 . qq[</td><td align="right" class="$irigo_cost">$cur ] . int(0.5+$externalipavg*$externalipprice) . qq[</td><td align="right">$externalipquota</td></tr>];
|
433
|
$res .= qq[<tr><td>Network traffic in:</td><td align="right">] . int(0.5 + $rx) . qq[ GB</td><td align="right" class="$irigo_cost">$cur 0</td><td align="right">] . int(0.5 + $rxquota/1024/1024) . qq[ GB</td></tr>];
|
434
|
$res .= qq[<tr><td>Network traffic out:</td><td align="right">] . int(0.5 + $tx) . qq[ GB</td><td align="right" class="$irigo_cost">$cur 0</td><td align="right">] . int(0.5 + $txquota/1024/1024) . qq[ GB</td></tr>];
|
435
|
|
436
|
$res =~ s/-1/∞/g;
|
437
|
$res =~ s/>0 .B<\/td><\/tr>/>∞<\/td><\/tr>/g;
|
438
|
$postreply .= qq[<table cellspacing="0" noframe="void" norules="rows" class="systemTables">$res</table>];
|
439
|
} else {
|
440
|
my $bill = {
|
441
|
vcpus => {quantity => $vcpuavg, quota => $vcpuquota},
|
442
|
memory => {quantity => $memoryavggb, unit => 'GB', quota => $memoryquotagb},
|
443
|
shared_storage => {quantity => $virtualsizeavggb, unit => 'GB', quota => $storagequotagb},
|
444
|
node_storage => {quantity => $nodevirtualsizeavggb, unit => 'GB', quota => $nodestoragequotagb},
|
445
|
backup_storage => {quantity => $backupsizeavggb, unit => 'GB'},
|
446
|
external_ips => {quantity => $externalipavg, quota => $externalipquota},
|
447
|
network_traffic_out => {quantity => int(0.5 + $rx), unit => 'GB', quota => int(0.5 + $rxquota/1024/1024)},
|
448
|
network_traffic_in => {quantity => int(0.5 + $tx), unit => 'GB', quota => int(0.5 + $txquota/1024/1024)}
|
449
|
};
|
450
|
if ($showcost) {
|
451
|
$bill->{vcpus}->{cost} = int(0.5+$vcpuavg*$vcpuprice);
|
452
|
$bill->{memory}->{cost} = int(0.5+$memoryavggb*$memoryprice);
|
453
|
$bill->{shared_storage}->{cost} = int(0.5+$virtualsizeavggb*$storageprice);
|
454
|
$bill->{node_storage}->{cost} = int(0.5+$nodevirtualsizeavggb*$nodestorageprice);
|
455
|
$bill->{backup_storage}->{cost} = int(0.5+$backupsizeavggb*$storageprice);
|
456
|
$bill->{external_ips}->{cost} = int(0.5+$externalipavg*$externalipprice);
|
457
|
$bill->{currency} = $cur;
|
458
|
$bill->{username} = $buser;
|
459
|
}
|
460
|
$postreply .= to_json($bill, {pretty=>1});
|
461
|
}
|
462
|
}
|
463
|
} else {
|
464
|
$postreply .= "Status=ERROR no privileges!!\n";
|
465
|
}
|
466
|
return $postreply;
|
467
|
}
|
468
|
|
469
|
sub do_listenginebackups {
|
470
|
my ($uuid, $action, $obj) = @_;
|
471
|
if ($help) {
|
472
|
return <<END
|
473
|
GET::
|
474
|
List the backups of this engine's configuration in the registry.
|
475
|
END
|
476
|
}
|
477
|
if ($enginelinked) {
|
478
|
require LWP::Simple;
|
479
|
my $browser = LWP::UserAgent->new;
|
480
|
$browser->agent('stabile/1.0b');
|
481
|
$browser->protocols_allowed( [ 'http','https'] );
|
482
|
|
483
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
484
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
485
|
|
486
|
$postreq->{'engineid'} = $engineid;
|
487
|
$postreq->{'enginetkthash'} = Digest::SHA::sha512_hex($tktkey);
|
488
|
|
489
|
my $content = $browser->post("https://www.stabile.io/irigo/engine.cgi?action=listbackups", $postreq)->content();
|
490
|
if ($content =~ /\[\]/) {
|
491
|
$postreply = qq|{"identifier": "path", "label": "name", "items": [{"path": "#", "name": "No backups"}]}|;
|
492
|
} else {
|
493
|
$postreply = qq|{"identifier": "path", "label": "name", "items": $content}|;
|
494
|
}
|
495
|
} else {
|
496
|
$postreply = qq|{"identifier": "path", "label": "name", "items": [{"path": "#", "name": "Engine not linked"}]}|;
|
497
|
}
|
498
|
return $postreply;
|
499
|
}
|
500
|
|
501
|
sub Backupengine {
|
502
|
my ($uuid, $action, $obj) = @_;
|
503
|
if ($help) {
|
504
|
return <<END
|
505
|
GET::
|
506
|
Backup this engine's configuration to the registry.
|
507
|
END
|
508
|
}
|
509
|
my $backupname = "$enginename.$engineid.$pretty_time";
|
510
|
$backupname =~ tr/:/-/; # tar has a problem with colons in filenames
|
511
|
if (-e "/tmp/$backupname.tgz") {
|
512
|
$postreply .= "Status=ERROR Engine is already being backed up";
|
513
|
} else {
|
514
|
$res .= `mysqldump --ignore-table=steamregister.nodeidentities steamregister > /etc/stabile/steamregister.sql`;
|
515
|
$res .= `cp /etc/apache2/conf-available/auth_tkt_cgi.conf /etc/stabile`;
|
516
|
$res .= `cp /etc/apache2/ssl/*.crt /etc/stabile`;
|
517
|
$res .= `cp /etc/apache2/ssl/*.pem /etc/stabile`;
|
518
|
$res .= `cp /etc/apache2/ssl/*.key /etc/stabile`;
|
519
|
$res .= `cp /etc/hosts.allow /etc/stabile`;
|
520
|
$res .= `cp /etc/mon/mon.cf /etc/stabile`;
|
521
|
|
522
|
# copy default node configuration to /etc/stabile
|
523
|
unless ( tie(%register,'Tie::DBI', Hash::Merge::merge({table=>'nodeidentities', key=>'identity'}, $Stabile::dbopts)) ) {return "Unable to access identity register"};
|
524
|
|
525
|
my $defaultpath = $idreg{'default'}->{'path'} . "/casper/filesystem.dir/etc/stabile/nodeconfig.cfg";
|
526
|
$res .= `cp $defaultpath /etc/stabile`;
|
527
|
|
528
|
# Make tarball
|
529
|
my $cmd = qq[(cd /etc/stabile; /bin/tar -czf "/tmp/$backupname.tgz" * 2>/dev/null)];
|
530
|
$res .= `$cmd`;
|
531
|
|
532
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
533
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
534
|
my $enginetkthash = Digest::SHA::sha512_hex($tktkey);
|
535
|
|
536
|
my $res = `/usr/bin/curl -k -F engineid=$engineid -F enginetkthash=$enginetkthash -F filedata=@"/tmp/$backupname.tgz" https://www.stabile.io/irigo/engine.cgi?action=backup`;
|
537
|
if ($res =~ /OK: $backupname.tgz received/) {
|
538
|
$postreply .= "Status=OK Engine configuration saved to the registry";
|
539
|
$main::syslogit->($user, "info", "Engine configuration saved to the registry");
|
540
|
unlink("/tmp/$backupname.tgz");
|
541
|
} else {
|
542
|
$postreply .= "Status=ERROR Problem backing configuration up to the registry\n$res\n";
|
543
|
}
|
544
|
}
|
545
|
return $postreply;
|
546
|
}
|
547
|
|
548
|
sub Upgradeengine {
|
549
|
my ($uuid, $action, $obj) = @_;
|
550
|
if ($help) {
|
551
|
return <<END
|
552
|
GET::
|
553
|
Try to upgrade this engine to latest release from the registry
|
554
|
END
|
555
|
}
|
556
|
$postreply = "Status=OK Requesting upgrade of Stabile\n";
|
557
|
print header("text/plain"), $postreply;
|
558
|
`echo "UPGRADE=1" >> /etc/stabile/config.cfg` unless ( `grep ^UPGRADE=1 /etc/stabile/config.cfg`);
|
559
|
my $cmd = "echo 'sleep 5 ; /usr/bin/pkill pressurecontrol' | at now";
|
560
|
system($cmd);
|
561
|
exit 0;
|
562
|
}
|
563
|
|
564
|
sub do_billengine {
|
565
|
my ($uuid, $action, $obj) = @_;
|
566
|
if ($help) {
|
567
|
return <<END
|
568
|
GET::
|
569
|
Submit billing data for this engine to the registry.
|
570
|
END
|
571
|
}
|
572
|
require LWP::Simple;
|
573
|
my $browser = LWP::UserAgent->new;
|
574
|
$browser->agent('stabile/1.0b');
|
575
|
$browser->protocols_allowed( [ 'http','https'] );
|
576
|
|
577
|
my $bmonth = $params{'month'} || $month;
|
578
|
$bmonth = substr("0$bmonth", -2);
|
579
|
my $byear = $params{'year'} || $year;
|
580
|
$showcost = 1;
|
581
|
|
582
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
583
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
584
|
my $tkthash = Digest::SHA::sha512_hex($tktkey);
|
585
|
|
586
|
my $postreq = ();
|
587
|
my %bill;
|
588
|
my @regvalues = values %register; # Sort by id
|
589
|
foreach my $valref (@regvalues) {
|
590
|
my $cuser = $valref->{'username'};
|
591
|
my %stats = collectBillingData( '', $cuser, $bmonth, $byear, $showcost );
|
592
|
$bill{"$cuser-$byear-$bmonth"} = \%stats;
|
593
|
}
|
594
|
$postreq->{'engineid'} = $engineid;
|
595
|
$postreq->{'enginetkthash'} = $tkthash;
|
596
|
$postreq->{'keywords'} = JSON::to_json(\%bill, {pretty=>1});
|
597
|
my $url = "https://www.stabile.io/irigo/engine.cgi";
|
598
|
$content = $browser->post($url, $postreq)->content();
|
599
|
$postreply = "Status=OK Billed this engine ($engineid)\n";
|
600
|
$postreply .= "$postreq->{'keywords'}\n$content";
|
601
|
return $postreply;
|
602
|
}
|
603
|
|
604
|
sub Linkengine {
|
605
|
my ($uuid, $action, $obj) = @_;
|
606
|
if ($help) {
|
607
|
return <<END
|
608
|
PUT:username,password,engineid,enginename,engineurl:
|
609
|
Links engine to the registry
|
610
|
END
|
611
|
}
|
612
|
return "Status=Error Not allowed\n" unless ($isadmin || ($user eq $engineuser));
|
613
|
my $linkaction = 'update';
|
614
|
$linkaction = 'link' if ($action eq 'linkengine');
|
615
|
$linkaction = 'unlink' if ($action eq 'unlinkengine');
|
616
|
$linkaction = 'update' if ($action eq 'updateengine');
|
617
|
$linkaction = 'update' if ($action eq 'syncusers');
|
618
|
|
619
|
require LWP::Simple;
|
620
|
my $browser = LWP::UserAgent->new;
|
621
|
$browser->agent('stabile/1.0b');
|
622
|
$browser->protocols_allowed( [ 'http','https'] );
|
623
|
|
624
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
625
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
626
|
|
627
|
my $postreq = ();
|
628
|
$postreq->{'user'} = $user || $obj->{'username'};
|
629
|
$postreq->{'engineid'} = $obj->{'engineid'} || $engineid;
|
630
|
$postreq->{'pwd'} = $obj->{'pwd'} if ($obj->{'pwd'});
|
631
|
$postreq->{'enginename'} = $obj->{'enginename'} if ($obj->{'enginename'});
|
632
|
$postreq->{'engineurl'} = $obj->{'engineurl'} if ($obj->{'engineurl'});
|
633
|
if ($tktkey) {
|
634
|
if ($action eq 'linkengine') {
|
635
|
$main::syslogit->($user, "info", "Linking engine with the registry");
|
636
|
$postreq->{'enginetktkey'} = $tktkey;
|
637
|
} else {
|
638
|
$postreq->{'enginetkthash'} = Digest::SHA::sha512_hex($tktkey);
|
639
|
}
|
640
|
}
|
641
|
if ($action eq "saveengine") { # Save request from the registry - don't post back
|
642
|
# Pressurecontrol reads new configuration data from the registry, simply reload it
|
643
|
my $pressureon = !(`systemctl is-active pressurecontrol` =~ /inactive/);
|
644
|
$postreply = ($pressureon)? "Status=OK Engine updating...\n":"Status=OK Engine not updating because pressurecontrol not active\n";
|
645
|
$postreply .= `systemctl restart pressurecontrol` if ($pressureon);
|
646
|
} else {
|
647
|
my $res;
|
648
|
my $cfg = new Config::Simple("/etc/stabile/config.cfg");
|
649
|
if ($action eq 'linkengine' || $action eq 'syncusers') {
|
650
|
# Send engine users to the registry
|
651
|
my @vals = values %register;
|
652
|
my $json = JSON::to_json(\@vals);
|
653
|
$json =~ s/null/""/g;
|
654
|
$json = URI::Escape::uri_escape($json);
|
655
|
$postreq->{'POSTDATA'} = $json;
|
656
|
}
|
657
|
if ($action eq 'linkengine' || $action eq 'updateengine') {
|
658
|
# Update name in config file
|
659
|
if ($postreq->{'enginename'} && $cfg->param("ENGINENAME") ne $postreq->{'enginename'}) {
|
660
|
$cfg->param("ENGINENAME", $postreq->{'enginename'});
|
661
|
$cfg->save();
|
662
|
}
|
663
|
# Send entire engine config file to the registry
|
664
|
my %cfghash = $cfg->vars();
|
665
|
foreach my $param (keys %cfghash) {
|
666
|
$param =~ /default\.(.+)/; # Get rid of default. prefix
|
667
|
if ($1) {
|
668
|
my $k = $1;
|
669
|
my @cvals = $cfg->param($param);
|
670
|
my $cval = join(", ", @cvals);
|
671
|
$postreq->{$k} = URI::Escape::uri_escape($cval);
|
672
|
}
|
673
|
}
|
674
|
# Send entire engine piston config file to the registry
|
675
|
my $nodeconfigfile = "/mnt/stabile/tftp/bionic/casper/filesystem.dir/etc/stabile/nodeconfig.cfg";
|
676
|
if (-e $nodeconfigfile) {
|
677
|
my $pistoncfg = new Config::Simple($nodeconfigfile);
|
678
|
%cfghash = $pistoncfg->vars();
|
679
|
foreach my $param (keys %cfghash) {
|
680
|
$param =~ /default\.(.+)/; # Get rid of default. prefix
|
681
|
if ($1) {
|
682
|
my $k = $1;
|
683
|
my @cvals = $pistoncfg->param($param);
|
684
|
my $cval = join(", ", @cvals);
|
685
|
$postreq->{$k} = URI::Escape::uri_escape($cval);
|
686
|
}
|
687
|
}
|
688
|
}
|
689
|
}
|
690
|
if ($linkaction eq 'link' || $enginelinked) {
|
691
|
my $content = $browser->post("https://www.stabile.io/irigo/engine.cgi?action=$linkaction", $postreq)->content();
|
692
|
if ($content =~ /(Engine linked|Engine not linked|Engine unlinked|Engine updated|Unknown engine|Invalid credentials .+\.)/i) {
|
693
|
$res = "Status=OK $1";
|
694
|
my $linked = 1;
|
695
|
$linked = 0 unless ($content =~ /Engine linked/i || $content =~ /Engine updated/i);
|
696
|
$cfg->param("ENGINE_LINKED", $linked);
|
697
|
$cfg->save();
|
698
|
} elsif ($action eq 'syncusers' || $action eq 'linkengine') { # If we send user list to the registry we get merged list back
|
699
|
if ($content =~ /^\[/) { # Sanity check to see if we got json back
|
700
|
$res .= "Status=OK Engine linked\n" if ($action eq 'linkengine');
|
701
|
# Update engine users with users from the registry
|
702
|
$res .= updateEngineUsers($content);
|
703
|
$res .= "Status=OK Users synced with registry\n";
|
704
|
$main::updateUI->({ tab => 'users', type=>'update', user=>$user});
|
705
|
}
|
706
|
$res .= "$content" unless ($res =~ /Status=OK/); # Only add if there are problems
|
707
|
}
|
708
|
$postreply = $res;
|
709
|
$content =~ s/\n/ - /;
|
710
|
$res =~ s/\n/ - /;
|
711
|
# $main::syslogit->($user, "info", "$content");
|
712
|
$main::syslogit->($user, "info", "Synced users");
|
713
|
} else {
|
714
|
$postreply .= "Status=OK Engine not linked, saving name\n";
|
715
|
}
|
716
|
}
|
717
|
return $postreply;
|
718
|
}
|
719
|
|
720
|
sub Releasepressure {
|
721
|
my ($uuid, $action, $obj) = @_;
|
722
|
if ($help) {
|
723
|
return <<END
|
724
|
GET::
|
725
|
Restarts pressurecontrol.
|
726
|
END
|
727
|
}
|
728
|
my $res;
|
729
|
unless (`systemctl is-active pressurecontrol` =~ /inactive/) {
|
730
|
my $daemon = Proc::Daemon->new(
|
731
|
work_dir => '/usr/local/bin',
|
732
|
exec_command => "systemctl restart pressurecontrol"
|
733
|
) or do {$postreply .= "Status=ERROR $@\n";};
|
734
|
my $pid = $daemon->Init();
|
735
|
# $res = `systemctl restart pressurecontrol`;
|
736
|
return "Status=OK Venting...\n";
|
737
|
} else {
|
738
|
return "Status=OK Not venting\n";
|
739
|
}
|
740
|
}
|
741
|
|
742
|
sub do_enable {
|
743
|
my ($uuid, $action, $obj) = @_;
|
744
|
if ($help) {
|
745
|
return <<END
|
746
|
GET:username:
|
747
|
Enable a user.
|
748
|
END
|
749
|
}
|
750
|
my $username = $obj->{'username'};
|
751
|
return unless ($username);
|
752
|
if ($isadmin || ($user eq $engineuser)) {
|
753
|
# Create user on this engine if not yet created
|
754
|
do_save($username, 'save', $obj);
|
755
|
my $uprivileges = $register{$username}->{'privileges'};
|
756
|
$uprivileges =~ s/d//;
|
757
|
$uprivileges .= 'n' unless ($uprivileges =~ /n/);# These are constant sources of problems - enable by default when enabling users to alleviate situation
|
758
|
$register{$username}->{'privileges'} = $uprivileges;
|
759
|
$register{$username}->{'allowinternalapi'} = 1;
|
760
|
$postreply .= "Status=OK User $username enabled\n";
|
761
|
} else {
|
762
|
$postreply .= "Status=ERROR Not allowed\n";
|
763
|
}
|
764
|
$uiuuid = $username;
|
765
|
return $postreply;
|
766
|
}
|
767
|
|
768
|
sub do_disable {
|
769
|
my ($uuid, $action, $obj) = @_;
|
770
|
if ($help) {
|
771
|
return <<END
|
772
|
GET:username:
|
773
|
Disable a user.
|
774
|
END
|
775
|
}
|
776
|
my $username = $obj->{'username'};
|
777
|
if ($isadmin || ($user eq $engineuser)) {
|
778
|
my $uprivileges = $register{$username}->{'privileges'};
|
779
|
$uprivileges .= 'd' unless ($uprivileges =~ /d/);
|
780
|
$register{$username}->{'privileges'} = $uprivileges;
|
781
|
$postreply .= "Stream=OK User $username disabled, halting servers...\n";
|
782
|
require "$Stabile::basedir/cgi/servers.cgi";
|
783
|
$Stabile::Servers::console = 1;
|
784
|
$postreply .= Stabile::Servers::destroyUserServers($username,1);
|
785
|
`/bin/rm /tmp/$username~*.tasks`;
|
786
|
} else {
|
787
|
$postreply .= "Status=ERROR Not allowed\n";
|
788
|
}
|
789
|
$uiuuid = $username;
|
790
|
return $postreply;
|
791
|
}
|
792
|
|
793
|
sub Updateui {
|
794
|
my ($uuid, $action, $obj) = @_;
|
795
|
if ($help) {
|
796
|
return <<END
|
797
|
GET:username,message,tab:
|
798
|
Update the UI for given user if logged into UI.
|
799
|
END
|
800
|
}
|
801
|
my $username = $obj->{'username'} || $user;
|
802
|
my $message = $obj->{'message'};
|
803
|
my $tab = $obj->{'tab'} || 'home';
|
804
|
if ($isadmin || ($username eq $user) || ($user eq $engineuser)) {
|
805
|
$postreply = $main::updateUI->({ tab => $tab, user => $username, message =>$message, type=>'update'});
|
806
|
} else {
|
807
|
$postreply = "Status=ERROR Not allowed\n";
|
808
|
}
|
809
|
}
|
810
|
|
811
|
sub do_updateclientui {
|
812
|
my ($uuid, $action, $obj) = @_;
|
813
|
if ($help) {
|
814
|
return <<END
|
815
|
GET:username,message,tab,type:
|
816
|
Update the UI for given user if logged into UI.
|
817
|
END
|
818
|
}
|
819
|
my $username = $obj->{'username'} || $user;
|
820
|
my $message = $obj->{'message'};
|
821
|
my $tab = $obj->{'tab'} || 'home';
|
822
|
my $type= $obj->{'type'} || 'update';
|
823
|
if ($isadmin || ($username eq $user) || ($user eq $engineuser)) {
|
824
|
$postreply = $main::updateUI->({ tab => $tab, user => $username, message =>$message, type=>$type});
|
825
|
} else {
|
826
|
$postreply = "Status=ERROR Not allowed\n";
|
827
|
}
|
828
|
}
|
829
|
|
830
|
sub Vent {
|
831
|
my ($uuid, $action, $obj) = @_;
|
832
|
if ($help) {
|
833
|
return <<END
|
834
|
GET::
|
835
|
Restart pressurecontrol.
|
836
|
END
|
837
|
}
|
838
|
`systemctl restart pressurecontrol`;
|
839
|
$postreply = "Status=OK Restarting pressurecontrol\n";
|
840
|
return $postreply;
|
841
|
}
|
842
|
|
843
|
sub Deleteentirely {
|
844
|
my ($uuid, $action, $obj) = @_;
|
845
|
if ($help) {
|
846
|
return <<END
|
847
|
GET:username:
|
848
|
Deletes a user and all the user's servers, images, networks etc. Warning: This destroys data
|
849
|
END
|
850
|
}
|
851
|
my $username = $obj->{'username'};
|
852
|
my $reply = "Status=OK Removed $username\n";
|
853
|
if (($isadmin || ($user eq $engineuser)) && $register{$username} && !($register{$username}->{'privileges'} =~ /a/) && !($username eq $engineuser)) {
|
854
|
#Never delete admins
|
855
|
my @dusers = ($username);
|
856
|
# Add list of subusers - does not look like a good idea
|
857
|
# foreach my $u (values %register) {
|
858
|
# push @dusers, $u->{'username'} if ($u->{'billto'} && $u->{'billto'} eq $username);
|
859
|
# };
|
860
|
|
861
|
foreach my $uname (@dusers) {
|
862
|
next if ($register{$uname}->{privileges} =~ /a/); #Never delete admins
|
863
|
$main::updateUI->({ tab => 'users', type=>'update', user=>$user, username=>$username, status=>'deleting'});
|
864
|
|
865
|
$postreply .= "Stream=OK Deleting user $uname and all associated data!!!\n";
|
866
|
|
867
|
require "$Stabile::basedir/cgi/servers.cgi";
|
868
|
$Stabile::Servers::console = 1;
|
869
|
|
870
|
require "$Stabile::basedir/cgi/systems.cgi";
|
871
|
$Stabile::Systems::console = 1;
|
872
|
Stabile::Systems::removeusersystems($uname);
|
873
|
Stabile::Servers::removeUserServers($uname);
|
874
|
|
875
|
require "$Stabile::basedir/cgi/images.cgi";
|
876
|
$Stabile::Images::console = 1;
|
877
|
$postreply .= Stabile::Images::removeUserImages($uname);
|
878
|
|
879
|
require "$Stabile::basedir/cgi/networks.cgi";
|
880
|
$Stabile::Networks::console = 1;
|
881
|
Stabile::Networks::Removeusernetworks($uname);
|
882
|
|
883
|
remove($uname);
|
884
|
$reply = "$reply\n$postreply";
|
885
|
|
886
|
# Also remove billing data, so next user with same username does not get old billing data
|
887
|
`echo "delete from billing_domains where usernodetime like '$uname-%';" | mysql steamregister`;
|
888
|
`echo "delete from billing_images where userstoragepooltime like '$uname-%';" | mysql steamregister`;
|
889
|
`echo "delete from billing_networks where useridtime like '$uname-%';" | mysql steamregister`;
|
890
|
}
|
891
|
$main::updateUI->({tab => 'users', type=>'update', user=>$user});
|
892
|
|
893
|
} else {
|
894
|
$postreply .= "Stream=ERROR Cannot delete user $username - you cannot delete administrators!\n";
|
895
|
$reply = $postreply;
|
896
|
}
|
897
|
return $reply;
|
898
|
}
|
899
|
|
900
|
sub do_save {
|
901
|
my ($username, $action, $obj) = @_;
|
902
|
if ($help) {
|
903
|
return <<END
|
904
|
POST:username, password, privileges, fullname, email, opemail, alertemail, phone, opphone, opfullname, allowfrom, allowinternalapi, accounts, accountsprivileges, storagepools, memoryquota, storagequota, nodestoragequota, vcpuquota, externalipquota, rxquota, txquota:
|
905
|
Saves a user. If [username] does not exist, it is created if privileges allow this. [password] can be plaintext or a SHA256 hash.
|
906
|
END
|
907
|
}
|
908
|
$username = $username || $obj->{"username"};
|
909
|
unless ($username && (($user eq $username) || $isadmin || ($user eq $engineuser))) {
|
910
|
$postreply = "Status=ERROR Please provide a valid username\n";
|
911
|
return $postreply;
|
912
|
}
|
913
|
my $password = '';
|
914
|
my $reguser = $register{$username};
|
915
|
if ($obj->{"password"} && $obj->{"password"} ne '--') {
|
916
|
if (length $obj->{'password'} == 86) {
|
917
|
$password = $obj->{"password"}; # This is already encoded
|
918
|
} else {
|
919
|
$password = $obj->{"password"};
|
920
|
$MAXLEN = 20;
|
921
|
my $msg = IsBadPassword($password);
|
922
|
if ($msg) {
|
923
|
$postreply = "Status=Error $msg - please choose a stronger password\n";
|
924
|
$postmsg = "$msg - please choose a stronger password";
|
925
|
return $postreply;
|
926
|
} else {
|
927
|
$password = Digest::SHA::sha512_base64($password);
|
928
|
}
|
929
|
}
|
930
|
} else {
|
931
|
$password = $reguser->{'password'};
|
932
|
}
|
933
|
my $fullname = $obj->{"fullname"} || $reguser->{'fullname'};
|
934
|
my $email = $obj->{"email"} || $reguser->{'email'};
|
935
|
my $opemail = $obj->{"opemail"} || $reguser->{'opemail'};
|
936
|
my $alertemail = $obj->{"alertemail"} || $reguser->{'alertemail'};
|
937
|
my $phone = $obj->{"phone"} || $reguser->{'phone'};
|
938
|
my $opphone = $obj->{"opphone"} || $reguser->{'opphone'};
|
939
|
my $opfullname = $obj->{"opfullname"} || $reguser->{'opfullname'};
|
940
|
my $allowfrom = $obj->{"allowfrom"};
|
941
|
my $totpsecret = $reguser->{'totpsecret'};
|
942
|
$totpsecret = $obj->{"totpsecret"} if (defined $obj->{"totpsecret"});
|
943
|
my $allowinternalapi = $obj->{"allowinternalapi"} || $reguser->{'allowinternalapi'};
|
944
|
|
945
|
if (defined $obj->{"allowfrom"}) {
|
946
|
my @allows = split(/(,\s*|\s+)/, $allowfrom);
|
947
|
$allowfrom = '';
|
948
|
my %allowshash;
|
949
|
foreach my $ip (@allows) {
|
950
|
$allowshash{"$1$2"} = 1 if ($ip =~ /(\d+\.\d+\.\d+\.\d+)(\/\d+)?/);
|
951
|
if ($ip =~ /\w\w/) { # Check if we are dealing with a country code
|
952
|
$ip = uc $ip;
|
953
|
my $geoip = Geo::IP->new(GEOIP_MEMORY_CACHE);
|
954
|
my $tz = $geoip->time_zone($ip, '');
|
955
|
$allowshash{$ip} = 1 if ($tz); # We have a valid country code
|
956
|
}
|
957
|
}
|
958
|
$allowfrom = join(", ", sort(keys %allowshash));
|
959
|
}
|
960
|
|
961
|
my $uprivileges = $reguser->{'privileges'};
|
962
|
my $uaccounts = $reguser->{'accounts'};
|
963
|
my $uaccountsprivileges = $reguser->{'accountsprivileges'};
|
964
|
my $storagepools = $reguser->{'storagepools'};
|
965
|
my $memoryquota = $reguser->{'memoryquota'};
|
966
|
my $storagequota = $reguser->{'storagequota'};
|
967
|
my $nodestoragequota = $reguser->{'nodestoragequota'};
|
968
|
my $vcpuquota = $reguser->{'vcpuquota'};
|
969
|
my $externalipquota = $reguser->{'externalipquota'};
|
970
|
my $rxquota = $reguser->{'rxquota'};
|
971
|
my $txquota = $reguser->{'txquota'};
|
972
|
my $tasks = $reguser->{'tasks'};
|
973
|
my $ubillto = $reguser->{'billto'};
|
974
|
my $udnsdomains = $reguser->{'dnsdomains'};
|
975
|
my $uappstoreurl = $reguser->{'appstoreurl'}; $uappstoreurl = '' if ($uappstoreurl eq '--');
|
976
|
my $created = $reguser->{'created'} || $current_time; # set created timestamp for new users
|
977
|
|
978
|
# Only allow admins to change user privileges and quotas
|
979
|
if ($isadmin || $user eq $engineuser) {
|
980
|
$uprivileges = $obj->{"privileges"} || $reguser->{'privileges'};
|
981
|
$uprivileges = '' if ($uprivileges eq '--');
|
982
|
$uprivileges = 'n' if (!$reguser->{'username'} && !$uprivileges); # Allow new users to use node storage unless explicitly disallowed
|
983
|
$uprivileges =~ tr/adnrpu//cd; # filter out non-valid privileges
|
984
|
$uprivileges =~ s/(.)(?=.*?\1)//g; # filter out duplicates using positive lookahead
|
985
|
$storagepools = ($obj->{"storagepools"} || $obj->{"storagepools"} eq '0')?$obj->{"storagepools"} : $reguser->{'storagepools'};
|
986
|
$memoryquota = (defined $obj->{"memoryquota"}) ? $obj->{"memoryquota"} : $reguser->{'memoryquota'};
|
987
|
$storagequota = (defined $obj->{"storagequota"}) ? $obj->{"storagequota"} : $reguser->{'storagequota'};
|
988
|
$nodestoragequota = (defined $obj->{"nodestoragequota"}) ? $obj->{"nodestoragequota"} : $reguser->{'nodestoragequota'};
|
989
|
$vcpuquota = (defined $obj->{"vcpuquota"}) ? $obj->{"vcpuquota"} : $reguser->{'vcpuquota'};
|
990
|
$externalipquota = (defined $obj->{"externalipquota"}) ? $obj->{"externalipquota"} : $reguser->{'externalipquota'};
|
991
|
$rxquota = (defined $obj->{"rxquota"}) ? $obj->{"rxquota"} : $reguser->{'rxquota'};
|
992
|
$txquota = (defined $obj->{"txquota"}) ? $obj->{"txquota"} : $reguser->{'txquota'};
|
993
|
$tasks = $obj->{"tasks"} || $reguser->{'tasks'};
|
994
|
$ubillto = $obj->{"billto"} || $reguser->{'billto'};
|
995
|
$udnsdomains = $obj->{"dnsdomains"} || $udnsdomains; $udnsdomains = '' if ($udnsdomains eq '--');
|
996
|
$uappstoreurl = $obj->{"appstoreurl"} || $uappstoreurl;
|
997
|
$uaccounts = $obj->{"accounts"} || $reguser->{'accounts'};
|
998
|
$uaccountsprivileges = $obj->{"accountsprivileges"} || $reguser->{'accountsprivileges'};
|
999
|
my @ua = split(/, ?/, $uaccounts);
|
1000
|
my @up = split(/, ?/, $uaccountsprivileges);
|
1001
|
my @ua2 = ();
|
1002
|
my @up2 = ();
|
1003
|
my $i = 0;
|
1004
|
foreach my $u (@ua) {
|
1005
|
if ($register{$u} && ($u ne $username)) {
|
1006
|
push @ua2, $u;
|
1007
|
my $uprivs = $up[$i] || 'u';
|
1008
|
$uprivs =~ tr/adnrpu//cd; # filter out non-valid privileges
|
1009
|
$uprivs =~ s/(.)(?=.*?\1)//g; # filter out duplicates using positive lookahead
|
1010
|
push @up2, $uprivs;
|
1011
|
}
|
1012
|
$i++;
|
1013
|
}
|
1014
|
$uaccounts = join(", ", @ua2);
|
1015
|
$uaccountsprivileges = join(", ", @up2);
|
1016
|
}
|
1017
|
|
1018
|
# Sanity checks
|
1019
|
if (
|
1020
|
($fullname && length $fullname > 255)
|
1021
|
|| ($password && length $password > 255)
|
1022
|
) {
|
1023
|
$postreply .= "Status=ERROR Bad data: $username\n";
|
1024
|
return $postreply;
|
1025
|
}
|
1026
|
# Only allow new users to be created by admins, i.e. no auto-registration
|
1027
|
if ($reguser->{'username'} || $isadmin) {
|
1028
|
$register{$username} = {
|
1029
|
password => $password,
|
1030
|
fullname => $fullname,
|
1031
|
email => $email,
|
1032
|
opemail => $opemail,
|
1033
|
alertemail => $alertemail,
|
1034
|
phone => $phone,
|
1035
|
opphone => $opphone,
|
1036
|
opfullname => $opfullname,
|
1037
|
allowfrom => $allowfrom,
|
1038
|
totpsecret => $totpsecret,
|
1039
|
privileges => $uprivileges,
|
1040
|
accounts => $uaccounts,
|
1041
|
accountsprivileges => $uaccountsprivileges,
|
1042
|
storagepools => $storagepools,
|
1043
|
memoryquota => $memoryquota+0,
|
1044
|
storagequota => $storagequota+0,
|
1045
|
nodestoragequota => $nodestoragequota+0,
|
1046
|
vcpuquota => $vcpuquota+0,
|
1047
|
externalipquota => $externalipquota+0,
|
1048
|
rxquota => $rxquota+0,
|
1049
|
txquota => $txquota+0,
|
1050
|
tasks => $tasks,
|
1051
|
allowinternalapi => $allowinternalapi || 1, # specify '--' to explicitly disallow
|
1052
|
billto => $ubillto,
|
1053
|
dnsdomains => $udnsdomains,
|
1054
|
appstoreurl => $uappstoreurl,
|
1055
|
created => $created,
|
1056
|
modified => $current_time,
|
1057
|
action => ""
|
1058
|
};
|
1059
|
my %uref = %{$register{$username}};
|
1060
|
$uref{result} = "OK";
|
1061
|
$uref{password} = "";
|
1062
|
$uref{status} = ($uprivileges =~ /d/)?'disabled':'enabled';
|
1063
|
$postreply = JSON::to_json(\%uref, { pretty => 1 });
|
1064
|
# $postreply =~ s/""/"--"/g;
|
1065
|
$postreply =~ s/null/""/g;
|
1066
|
# $postreply =~ s/\x/ /g;
|
1067
|
}
|
1068
|
return $postreply;
|
1069
|
}
|
1070
|
|
1071
|
sub do_list {
|
1072
|
my ($uuid, $action, $obj) = @_;
|
1073
|
if ($help) {
|
1074
|
return <<END
|
1075
|
GET::
|
1076
|
List users registered on this engine.
|
1077
|
END
|
1078
|
}
|
1079
|
my $userfilter;
|
1080
|
my $usermatch;
|
1081
|
my $propmatch;
|
1082
|
if ($uripath =~ /users(\.cgi)?\/(\?|)(me|this)/) {
|
1083
|
$usermatch = $user;
|
1084
|
$propmatch = $4 if ($uripath =~ /users(\.cgi)?\/(\?|)(me|this)\/(.+)/);
|
1085
|
} elsif ($uripath =~ /users(\.cgi)?\/(\?|)(username)/) {
|
1086
|
$userfilter = $3 if ($uripath =~ /users(\.cgi)?\/\??username(:|=)(.+)/);
|
1087
|
$userfilter = $1 if ($userfilter =~ /(.*)\*/);
|
1088
|
} elsif ($uripath =~ /users(\.cgi)?\/(\S+)/) {
|
1089
|
$usermatch = $2;
|
1090
|
$propmatch = $4 if ($uripath =~ /users(\.cgi)?\/(\S+)\/(.+)/);
|
1091
|
}
|
1092
|
|
1093
|
my @regvalues = (sort {$a->{'id'} <=> $b->{'id'}} values %register); # Sort by id
|
1094
|
my @curregvalues;
|
1095
|
|
1096
|
foreach my $valref (@regvalues) {
|
1097
|
my $reguser = $valref->{'username'};
|
1098
|
if ($user eq $reguser || $isadmin) {
|
1099
|
next if ($reguser eq 'irigo' || $reguser eq 'guest');
|
1100
|
my %val = %{$valref}; # Deference and assign to new ass array, effectively cloning object
|
1101
|
$val{'password'} = '';
|
1102
|
$val{'status'} = ($val{'privileges'} =~ /d/)?'disabled':'enabled';
|
1103
|
if ((!$userfilter && !$usermatch) || ($userfilter && $reguser =~ /$userfilter/) || $reguser eq $usermatch) {
|
1104
|
push @curregvalues,\%val;
|
1105
|
}
|
1106
|
}
|
1107
|
}
|
1108
|
if ($action eq 'tablelist') {
|
1109
|
my $t2 = Text::SimpleTable->new(14,32,24,10);
|
1110
|
|
1111
|
$t2->row('username', 'fullname', 'lastlogin', 'privileges');
|
1112
|
$t2->hr;
|
1113
|
my $pattern = $options{m};
|
1114
|
foreach $rowref (@curregvalues){
|
1115
|
if ($pattern) {
|
1116
|
my $rowtext = $rowref->{'username'} . " " . $rowref->{'fullname'} . " " . $rowref->{'lastlogin'}
|
1117
|
. " " . $rowref->{'privileges'};
|
1118
|
$rowtext .= " " . $rowref->{'mac'} if ($isadmin);
|
1119
|
next unless ($rowtext =~ /$pattern/i);
|
1120
|
}
|
1121
|
$t2->row($rowref->{'username'}, $rowref->{'fullname'}||'--', localtime($rowref->{'lastlogin'})||'--',
|
1122
|
$rowref->{'privileges'}||'--');
|
1123
|
}
|
1124
|
#$t2->row('common', '--', '--', '--');
|
1125
|
#$t2->row('all', '--', '--', '--') if (index($privileges,"a")!=-1);
|
1126
|
$postreply .= $t2->draw;
|
1127
|
} elsif ($console) {
|
1128
|
$postreply = Dumper(\@curregvalues);
|
1129
|
} else {
|
1130
|
my $json_text;
|
1131
|
if ($propmatch) {
|
1132
|
$json_text = JSON::to_json($curregvalues[0]->{$propmatch}, {allow_nonref=>1});
|
1133
|
} else {
|
1134
|
$json_text = JSON::to_json(\@curregvalues, {pretty=>1});
|
1135
|
}
|
1136
|
$json_text =~ s/"--"/""/g;
|
1137
|
$json_text =~ s/null/""/g;
|
1138
|
# $json_text =~ s/\x/ /g;
|
1139
|
$postreply = qq|{"identifier": "username", "label": "username", "items": | unless ($usermatch || $action ne 'listusers');
|
1140
|
$postreply .= $json_text;
|
1141
|
$postreply .= "}\n" unless ($usermatch || $action ne 'listusers');
|
1142
|
}
|
1143
|
return $postreply;
|
1144
|
}
|
1145
|
|
1146
|
sub do_uuidlookup {
|
1147
|
if ($help) {
|
1148
|
return <<END
|
1149
|
GET:uuid:
|
1150
|
Simple action for looking up a username (uuid) or part of a username and returning the complete username.
|
1151
|
END
|
1152
|
}
|
1153
|
my $u = $options{u};
|
1154
|
$u = $params{'uuid'} unless ($u || $u eq '0');
|
1155
|
if ($u || $u eq '0') {
|
1156
|
foreach my $uuid (keys %register) {
|
1157
|
if ($uuid =~ /^$u/) {
|
1158
|
return "$uuid\n" if ($uuid eq $user || index($privileges,"a")!=-1);
|
1159
|
}
|
1160
|
}
|
1161
|
}
|
1162
|
}
|
1163
|
|
1164
|
sub do_uuidshow {
|
1165
|
if ($help) {
|
1166
|
return <<END
|
1167
|
GET:uuid:
|
1168
|
Simple action for showing a single user. Pass username as uuid.
|
1169
|
END
|
1170
|
}
|
1171
|
my $u = $options{u};
|
1172
|
$u = $params{'uuid'} unless ($u || $u eq '0');
|
1173
|
if ($u eq $user || index($privileges,"a")!=-1) {
|
1174
|
foreach my $uuid (keys %register) {
|
1175
|
if ($uuid =~ /^$u/) {
|
1176
|
my %hash = %{$register{$uuid}};
|
1177
|
delete $hash{'action'};
|
1178
|
my $dump = to_json(\%hash, {pretty=>1});
|
1179
|
$dump =~ s/undef/"--"/g;
|
1180
|
return $dump;
|
1181
|
}
|
1182
|
}
|
1183
|
}
|
1184
|
}
|
1185
|
|
1186
|
sub Restoreengine {
|
1187
|
my ($uuid, $action, $obj) = @_;
|
1188
|
if ($help) {
|
1189
|
return <<END
|
1190
|
GET:restorefile:
|
1191
|
Restores this engine's configuration from "restorefile", which must be one of the paths listed in listenginebackups
|
1192
|
END
|
1193
|
}
|
1194
|
if (!$isadmin) {
|
1195
|
$postreply = "Status=ERROR You must be an administrator in order to restore this engine";
|
1196
|
} else {
|
1197
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
1198
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
1199
|
my $enginetkthash = Digest::SHA::sha512_hex($tktkey);
|
1200
|
|
1201
|
my $restoredir = "/etc";
|
1202
|
my $dbname = "steamregister";
|
1203
|
my $restorefile = $obj->{'restorefile'};
|
1204
|
|
1205
|
if ($restorefile && !($restorefile =~ /\//)) {
|
1206
|
my $urifile = URI::Escape::uri_escape($restorefile);
|
1207
|
my $uri = "https://www.stabile.io/irigo/engine.cgi";
|
1208
|
my $cmd = qq|/usr/bin/curl -f --cookie -O -L -F action=getbackup -F restorefile=$urifile -F engineid=$engineid -F enginetkthash=$enginetkthash "$uri" > "/tmp/$restorefile"|;
|
1209
|
my $res = `$cmd`;
|
1210
|
if (-s "/tmp/$restorefile") {
|
1211
|
$res .= `(mkdir $restoredir/stabile; cd $restoredir/stabile; /bin/tar -zxf "/tmp/$restorefile")`;
|
1212
|
$res .= `/usr/bin/mysql -e "create database $dbname;"`;
|
1213
|
$res .= `/usr/bin/mysql $dbname < $restoredir/stabile/steamregister.sql`;
|
1214
|
$res .= `cp -b $restoredir/stabile/hosts.allow /etc/hosts.allow`;
|
1215
|
$res .= `cp -b $restoredir/stabile/auth_tkt_cgi.conf /etc/apache2/conf.d/`;
|
1216
|
$res .= `cp -b $restoredir/stabile/*.crt /etc/apache2/ssl/`;
|
1217
|
$res .= `cp -b $restoredir/stabile/*.key /etc/apache2/ssl/`;
|
1218
|
$res .= `cp -b $restoredir/stabile/mon.cf /etc/mon/`;
|
1219
|
$res .= `service apache2 reload`;
|
1220
|
|
1221
|
# Restore default node configuration
|
1222
|
unless ( tie(%idreg,'Tie::DBI', Hash::Merge::merge({table=>'nodeidentities', key=>'identity'}, $Stabile::dbopts)) ) {return "Unable to access identity register"};
|
1223
|
my $defaultpath = $idreg{'default'}->{'path'} . "/casper/filesystem.dir/etc/stabile/nodeconfig.cfg";
|
1224
|
untie %idreg;
|
1225
|
$res .= `cp $restoredir/stabile/nodeconfig.cfg $defaultpath`;
|
1226
|
$main::syslogit->($user, "info", "Engine configuration $restorefile restored from the registry");
|
1227
|
$postreply .= "Status=OK Engine configuration $restorefile restored from the registry - reloading UI\n";
|
1228
|
} else {
|
1229
|
$postreply .= "Status=ERROR Restore failed, $restorefile not found...\n";
|
1230
|
}
|
1231
|
} else {
|
1232
|
$postreply .= "Status=ERROR You must select a restore file\n";
|
1233
|
}
|
1234
|
}
|
1235
|
return $postreply;
|
1236
|
}
|
1237
|
|
1238
|
# Print list of available actions on objects
|
1239
|
sub do_plainhelp {
|
1240
|
my $res;
|
1241
|
$res .= header('text/plain') unless $console;
|
1242
|
$res .= <<END
|
1243
|
new [username="name", password="password"]
|
1244
|
* enable: Enables a disabled user
|
1245
|
* disable: Disables a user, disallowing login
|
1246
|
* remove: Deletes a user, leaving servers, images, networks etc. untouched
|
1247
|
* deleteentirely: Deletes a user and all the user's servers, images, networks etc. Warning: This destroys data
|
1248
|
|
1249
|
END
|
1250
|
;
|
1251
|
}
|
1252
|
|
1253
|
sub do_cleanbillingdata {
|
1254
|
my ($uuid, $action, $obj) = @_;
|
1255
|
if ($help) {
|
1256
|
return <<END
|
1257
|
GET:year,dryrun,cleanup:
|
1258
|
Deletes billing from [year]. Default is current year-2. Set dryrun to do a test run. Set cleanup to remove invalid entries.
|
1259
|
END
|
1260
|
}
|
1261
|
return "Status=Error Not allowed\n" unless ($isadmin);
|
1262
|
|
1263
|
my $y = $params{'year'} || ($year-2);
|
1264
|
my $dryrun = $params{'dryrun'};
|
1265
|
my $cleanup = $params{'cleanup'};
|
1266
|
my $pattern = qq|like '%-$y-__'|;
|
1267
|
if ($cleanup) {
|
1268
|
$pattern = qq|not like '%-____-__'|;
|
1269
|
$y = '';
|
1270
|
}
|
1271
|
|
1272
|
unless ( tie(%bnetworksreg,'Tie::DBI', Hash::Merge::merge({table=>'billing_networks', key=>'useridtime'}, $Stabile::dbopts)) ) {return "Status=Error Unable to access billing register"};
|
1273
|
my @bkeys = (tied %bnetworksreg)->select_where("useridtime $pattern");
|
1274
|
$postreply .= "Status=OK -- this is only a test run ---\n" if ($dryrun);
|
1275
|
$postreply .= "Status=OK Cleaning " . scalar @bkeys . " $y network rows\n";
|
1276
|
foreach my $bkey (@bkeys) {
|
1277
|
$postreply .= "Status=OK removing $bnetworksreg{$bkey}->{useridtime}\n";
|
1278
|
delete($bnetworksreg{$bkey}) unless ($dryrun);
|
1279
|
}
|
1280
|
untie(%bnetworksreg);
|
1281
|
|
1282
|
unless ( tie(%bimagesreg,'Tie::DBI', Hash::Merge::merge({table=>'billing_images', key=>'userstoragepooltime'}, $Stabile::dbopts)) ) {return "Status=Error Unable to access billing register"};
|
1283
|
my @bkeys = (tied %bimagesreg)->select_where("userstoragepooltime $pattern");
|
1284
|
$postreply .= "Status=OK Cleaning " . scalar @bkeys . " $y image rows\n";
|
1285
|
foreach my $bkey (@bkeys) {
|
1286
|
$postreply .= "Status=OK removing $bimagesreg{$bkey}->{userstoragepooltime}\n";
|
1287
|
delete($bimagesreg{$bkey}) unless ($dryrun);
|
1288
|
}
|
1289
|
untie(%bimagesreg);
|
1290
|
|
1291
|
unless ( tie(%bserversreg,'Tie::DBI', Hash::Merge::merge({table=>'billing_domains', key=>'usernodetime'}, $Stabile::dbopts)) ) {return "Status=Error Unable to access billing register"};
|
1292
|
my @bkeys = (tied %bserversreg)->select_where("usernodetime $pattern");
|
1293
|
$postreply .= "Status=OK Cleaning " . scalar @bkeys . " $y server rows\n";
|
1294
|
foreach my $bkey (@bkeys) {
|
1295
|
$postreply .= "Status=OK removing $bserversreg{$bkey}->{usernodetime}\n";
|
1296
|
delete($bserversreg{$bkey}) unless ($dryrun);
|
1297
|
}
|
1298
|
untie(%bserversreg);
|
1299
|
|
1300
|
return $postreply;
|
1301
|
|
1302
|
}
|
1303
|
|
1304
|
sub collectBillingData {
|
1305
|
my ( $curuuid, $buser, $bmonth, $byear, $showcost ) = @_;
|
1306
|
|
1307
|
my $vcpu=0;
|
1308
|
my $rx = 0;
|
1309
|
my $tx = 0;
|
1310
|
my $vcpuavg = 0;
|
1311
|
my $memory = 0;
|
1312
|
my $memoryavg = 0;
|
1313
|
my $backupsize = 0;
|
1314
|
my $backupsizeavg = 0;
|
1315
|
my $nodevirtualsize = 0;
|
1316
|
my $nodevirtualsizeavg = 0;
|
1317
|
my $virtualsize = 0;
|
1318
|
my $virtualsizeavg = 0;
|
1319
|
my $externalip = 0;
|
1320
|
my $externalipavg = 0;
|
1321
|
|
1322
|
my $prevmonth = $bmonth-1;
|
1323
|
my $prevyear = $byear;
|
1324
|
if ($prevmonth == 0) {$prevmonth=12; $prevyear--;};
|
1325
|
$prevmonth = substr("0" . $prevmonth, -2);
|
1326
|
my $prev_rx = 0;
|
1327
|
my $prev_tx = 0;
|
1328
|
# List pricing for a single system/server
|
1329
|
if ($curuuid) {
|
1330
|
unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains'}, $Stabile::dbopts)) ) {return "Unable to access domains register"};
|
1331
|
unless ( tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images',key=>'path'}, $Stabile::dbopts)) ) {return "Unable to access images register"};
|
1332
|
unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {return "Unable to access networks register"};
|
1333
|
|
1334
|
my @domains;
|
1335
|
my $isserver = 1 if ($domreg{$curuuid});
|
1336
|
if ($isserver) {
|
1337
|
@domains = $domreg{$curuuid};
|
1338
|
} else {
|
1339
|
@domains = values %domreg;
|
1340
|
}
|
1341
|
foreach my $valref (@domains) {
|
1342
|
if ($valref->{'system'} eq $curuuid || $isserver) {
|
1343
|
$memory += $valref->{'memory'};
|
1344
|
$vcpu += $valref->{'vcpu'};
|
1345
|
my $image = $valref->{'image'};
|
1346
|
my $storagepool;
|
1347
|
if ($imagereg{$image}) {
|
1348
|
$storagepool = $imagereg{$image}->{'storagepool'};
|
1349
|
if ($storagepool == -1) {
|
1350
|
$nodevirtualsize += $imagereg{$image}->{'virtualsize'};
|
1351
|
} else {
|
1352
|
$virtualsize += $imagereg{$image}->{'virtualsize'};
|
1353
|
}
|
1354
|
$backupsize += $imagereg{$image}->{'backupsize'};
|
1355
|
}
|
1356
|
$image = $valref->{'image2'};
|
1357
|
if ($imagereg{$image}) {
|
1358
|
$storagepool = $imagereg{$image}->{'storagepool'};
|
1359
|
if ($storagepool == -1) {
|
1360
|
$nodevirtualsize += $imagereg{$image}->{'virtualsize'};
|
1361
|
} else {
|
1362
|
$virtualsize += $imagereg{$image}->{'virtualsize'};
|
1363
|
}
|
1364
|
$backupsize += $imagereg{$image}->{'backupsize'};
|
1365
|
}
|
1366
|
my $networkuuid = $valref->{'networkuuid1'};
|
1367
|
my $networktype = $networkreg{$networkuuid}->{'type'};
|
1368
|
$externalip++ if ($networktype eq 'externalip'|| $networktype eq 'ipmapping');
|
1369
|
$networkuuid = $valref->{'networkuuid2'};
|
1370
|
if ($networkreg{$networkuuid}) {
|
1371
|
$networktype = $networkreg{$networkuuid}->{'type'};
|
1372
|
$externalip++ if ($networktype eq 'externalip'|| $networktype eq 'ipmapping');
|
1373
|
}
|
1374
|
}
|
1375
|
}
|
1376
|
untie %domreg;
|
1377
|
untie %imagereg;
|
1378
|
untie %networkreg;
|
1379
|
|
1380
|
# List pricing for all servers
|
1381
|
} else {
|
1382
|
# Network billing
|
1383
|
unless ( tie(%bnetworksreg,'Tie::DBI', Hash::Merge::merge({table=>'billing_networks', key=>'useridtime'}, $Stabile::dbopts)) ) {return "Unable to access billing register"};
|
1384
|
unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks'}, $Stabile::dbopts)) ) {return "Unable to access networks register"};
|
1385
|
|
1386
|
# Build list of the user's network id's
|
1387
|
my %usernetworks;
|
1388
|
my @nkeys = (tied %networkreg)->select_where("user = '$buser'");
|
1389
|
foreach $network (@nkeys) {
|
1390
|
my $id = $networkreg{$network}->{'id'};
|
1391
|
$usernetworks{$id} = $id unless ($usernetworks{$id} || $id==0 || $id==1);
|
1392
|
}
|
1393
|
untie %networkreg;
|
1394
|
|
1395
|
foreach $id (keys %usernetworks) {
|
1396
|
my $networkobj = $bnetworksreg{"$buser-$id-$byear-$bmonth"};
|
1397
|
my $prevnetworkobj = $bnetworksreg{"$buser-$id-$prevyear-$prevmonth"};
|
1398
|
$externalip += $networkobj->{'externalip'};
|
1399
|
$externalipavg += $networkobj->{'externalipavg'};
|
1400
|
$rx += $networkobj->{'rx'};
|
1401
|
$tx += $networkobj->{'tx'};
|
1402
|
$prev_rx += $prevnetworkobj->{'rx'};
|
1403
|
$prev_tx += $prevnetworkobj->{'tx'};
|
1404
|
}
|
1405
|
untie %bnetworksreg;
|
1406
|
|
1407
|
# Image billing
|
1408
|
|
1409
|
unless ( tie(%bimagesreg,'Tie::DBI', Hash::Merge::merge({table=>'billing_images', key=>'userstoragepooltime'}, $Stabile::dbopts)) ) {return "Unable to access billing register"};
|
1410
|
|
1411
|
# Build list of the users storage pools
|
1412
|
my $storagepools = $Stabile::config->get('STORAGE_POOLS_DEFAULTS') || "0";
|
1413
|
my $upools = $register{$buser}->{'storagepools'}; # Prioritized list of users storage pools as numbers, e.g. "0,2,1"
|
1414
|
$storagepools = $upools if ($upools && $upools ne '--');
|
1415
|
my @spl = split(/,\s*/, $storagepools);
|
1416
|
my $bimageobj = $bimagesreg{"$buser--1-$byear-$bmonth"};
|
1417
|
$backupsize = $bimageobj->{'backupsize'}+0;
|
1418
|
$nodevirtualsize = $bimageobj->{'virtualsize'}+0;
|
1419
|
$backupsizeavg = $bimageobj->{'backupsizeavg'}+0;
|
1420
|
$nodevirtualsizeavg = $bimageobj->{'virtualsizeavg'}+0;
|
1421
|
foreach $pool (@spl) {
|
1422
|
$bimageobj = $bimagesreg{"$buser-$pool-$byear-$bmonth"};
|
1423
|
$virtualsize += $bimageobj->{'virtualsize'};
|
1424
|
$backupsize += $bimageobj->{'backupsize'};
|
1425
|
$virtualsizeavg += $bimageobj->{'virtualsizeavg'};
|
1426
|
$backupsizeavg += $bimageobj->{'backupsizeavg'};
|
1427
|
}
|
1428
|
untie %bimagesreg;
|
1429
|
|
1430
|
# Server billing
|
1431
|
|
1432
|
unless ( tie(%bserversreg,'Tie::DBI', Hash::Merge::merge({table=>'billing_domains', key=>'usernodetime'}, $Stabile::dbopts)) ) {return "Unable to access billing register"};
|
1433
|
unless ( tie(%nodereg,'Tie::DBI', Hash::Merge::merge({table=>'nodes', key=>'mac'}, $Stabile::dbopts)) ) {return "Unable to access billing register"};
|
1434
|
|
1435
|
my @usernodes = keys %nodereg;
|
1436
|
untie %nodereg;
|
1437
|
|
1438
|
my @nodebills;
|
1439
|
foreach $mac (@usernodes) {
|
1440
|
my $bserverobj = $bserversreg{"$buser-$mac-$byear-$bmonth"};
|
1441
|
$vcpu += $bserverobj->{'vcpu'};
|
1442
|
$memory += $bserverobj->{'memory'};
|
1443
|
$vcpuavg += $bserverobj->{'vcpuavg'};
|
1444
|
$memoryavg += $bserverobj->{'memoryavg'};
|
1445
|
}
|
1446
|
untie %bserversreg;
|
1447
|
}
|
1448
|
|
1449
|
my $uservcpuprice = 0+ $register{$user}->{'vcpuprice'};
|
1450
|
my $usermemoryprice = 0+ $register{$user}->{'memoryprice'};
|
1451
|
my $userstorageprice = 0+ $register{$user}->{'storageprice'};
|
1452
|
my $usernodestorageprice = 0+ $register{$user}->{'nodestorageprice'};
|
1453
|
my $userexternalipprice = 0+ $register{$user}->{'externalipprice'};
|
1454
|
|
1455
|
$vcpuprice = $uservcpuprice || $Stabile::config->get('VCPU_PRICE') + 0;
|
1456
|
$memoryprice = $usermemoryprice || $Stabile::config->get('MEMORY_PRICE') + 0;
|
1457
|
$storageprice = $userstorageprice || $Stabile::config->get('STORAGE_PRICE') + 0;
|
1458
|
$nodestorageprice = $usernodestorageprice || $Stabile::config->get('NODESTORAGE_PRICE') + 0;
|
1459
|
$externalipprice = $userexternalipprice || $Stabile::config->get('EXTERNALIP_PRICE') + 0;
|
1460
|
|
1461
|
my $memorygb = int(0.5 + 100*$memory/1024)/100;
|
1462
|
my $virtualsizegb = int(0.5 + 100*$virtualsize/1024/1024/1024)/100;
|
1463
|
my $nodevirtualsizegb = int(0.5 + 100*$nodevirtualsize/1024/1024/1024)/100;
|
1464
|
my $backupsizegb = int(0.5 + 100*$backupsize/1024/1024/1024)/100;
|
1465
|
|
1466
|
my $totalprice = int(0.5 + 100*($vcpu*$vcpuprice + $memorygb*$memoryprice + $virtualsizegb*$storageprice
|
1467
|
+ $nodevirtualsizegb*$nodestorageprice + $backupsizegb*$storageprice + $externalip*$externalipprice)) /100;
|
1468
|
|
1469
|
my $memoryavggb = int(0.5 + 100*$memoryavg/1024)/100;
|
1470
|
my $virtualsizeavggb = int(0.5 + 100*$virtualsizeavg/1024/1024/1024)/100;
|
1471
|
my $nodevirtualsizeavggb = int(0.5 + 100*$nodevirtualsizeavg/1024/1024/1024)/100;
|
1472
|
my $backupsizeavggb = int(0.5 + 100*$backupsizeavg/1024/1024/1024)/100;
|
1473
|
|
1474
|
my $monfac = 1;
|
1475
|
if ($bmonth == $month) {
|
1476
|
# Find 00:00 of first day of month - http://www.perlmonks.org/?node_id=97120
|
1477
|
my $fstamp = POSIX::mktime(0,0,0,1,$mon,$year-1900,0,0,-1);
|
1478
|
my $lstamp = POSIX::mktime(0,0,0,1,$mon+1,$year-1900,0,0,-1);
|
1479
|
$monfac = ($current_time-$fstamp)/($lstamp-$fstamp);
|
1480
|
}
|
1481
|
|
1482
|
my $totalpriceavg = int(0.5 + 100*$monfac * ($vcpuavg*$vcpuprice + $memoryavggb*$memoryprice + $virtualsizeavggb*$storageprice
|
1483
|
+ $nodevirtualsizeavggb*$nodestorageprice + $backupsizeavggb*$storageprice + $externalipavg*$externalipprice)) /100;
|
1484
|
|
1485
|
$prev_rx = 0 if ($prev_rx>$rx); # Something is fishy
|
1486
|
$prev_tx = 0 if ($prev_tx>$tx);
|
1487
|
my $rxgb = int(0.5 + 100*($rx-$prev_rx)/1024**3)/100;
|
1488
|
my $txgb = int(0.5 + 100*($tx-$prev_tx)/1024**3)/100;
|
1489
|
|
1490
|
my %stats;
|
1491
|
$stats{'virtualsize'} = $virtualsizegb;
|
1492
|
$stats{'backupsize'} = $backupsizegb;
|
1493
|
$stats{'externalip'} = $externalip;
|
1494
|
$stats{'memory'} = $memorygb;
|
1495
|
$stats{'month'} = $bmonth;
|
1496
|
$stats{'nodevirtualsize'} = $nodevirtualsizegb;
|
1497
|
$stats{'rx'} = $rxgb;
|
1498
|
$stats{'tx'} = $txgb;
|
1499
|
$stats{'username'} = $buser;
|
1500
|
$stats{'vcpu'} = $vcpu;
|
1501
|
$stats{'year'} = $byear;
|
1502
|
$stats{'totalcost'} = "$cur $totalprice" if ($showcost);
|
1503
|
$stats{'curtotal'} = $totalprice if ($showcost);
|
1504
|
|
1505
|
if (!$curuuid) {
|
1506
|
$stats{'virtualsizeavg'} = $virtualsizeavggb;
|
1507
|
$stats{'backupsizeavg'} = $backupsizeavggb;
|
1508
|
$stats{'memoryavg'} = $memoryavggb;
|
1509
|
$stats{'nodevirtualsizeavg'} = $nodevirtualsizeavggb;
|
1510
|
$stats{'vcpuavg'} = int(0.5 + 100*$vcpuavg)/100;
|
1511
|
$stats{'externalipavg'} = int(0.5 + 100*$externalipavg)/100;
|
1512
|
$stats{'totalcostavg'} = "$cur $totalpriceavg" if ($showcost);
|
1513
|
}
|
1514
|
return %stats;
|
1515
|
}
|
1516
|
|
1517
|
sub do_resetpassword {
|
1518
|
my ($uuid, $action, $obj) = @_;
|
1519
|
if ($help) {
|
1520
|
return <<END
|
1521
|
GET:username:
|
1522
|
Sends an email to a user with a link to reset his password. The user must have a valid email address.
|
1523
|
END
|
1524
|
}
|
1525
|
my $username = $obj->{'username'} || $user;
|
1526
|
if ($register{$username} && ($username eq $user || $isadmin)) {
|
1527
|
my $mailaddrs = $register{$username}->{'email'};
|
1528
|
$mailaddrs = $username if (!$mailaddrs && $username =~ /\@/);
|
1529
|
if ($mailaddrs) {
|
1530
|
require (dirname(__FILE__)) . "/../auth/Apache/AuthTkt.pm";
|
1531
|
my $tktname = 'auth_' . substr($engineid, 0, 8);
|
1532
|
my $at = Apache::AuthTkt->new(conf => $ENV{MOD_AUTH_TKT_CONF});
|
1533
|
my $tkt = $at->ticket(uid => $username, digest_type => 'SHA512', tokens => '', debug => 0);
|
1534
|
# my $valid = $at->valid_ticket($tkt);
|
1535
|
|
1536
|
my $mailhtml = <<END;
|
1537
|
<!DOCTYPE html
|
1538
|
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
1539
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
1540
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
|
1541
|
<head>
|
1542
|
<title>Password reset</title>
|
1543
|
<meta http-equiv="Pragma" content="no-cache" />
|
1544
|
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" />
|
1545
|
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
1546
|
</head>
|
1547
|
<body class="tundra">
|
1548
|
<div>
|
1549
|
<div class="well" style="margin:20px;">
|
1550
|
<h3 style="color: #e74c3c!important; margin-bottom:30px;">You requested a password reset at $enginename</h3>
|
1551
|
To log in and set a new password, please click <a href="$baseurl/auth/autologin?$tktname=$tkt\&back=#chpwd">here</a>.<br>
|
1552
|
<div>Thanks,<br>your friendly infrastructure services</div>
|
1553
|
</div>
|
1554
|
</div>
|
1555
|
</div>
|
1556
|
</body>
|
1557
|
</html>
|
1558
|
END
|
1559
|
;
|
1560
|
my $msg = MIME::Lite->new(
|
1561
|
From => "$enginename",
|
1562
|
To => $mailaddrs,
|
1563
|
Type => 'multipart/alternative',
|
1564
|
Subject => "Password reset on $enginename",
|
1565
|
);
|
1566
|
# my $att_text = MIME::Lite->new(
|
1567
|
# Type => 'text',
|
1568
|
# Data => $mailtext,
|
1569
|
# Encoding => 'quoted-printable',
|
1570
|
# );
|
1571
|
# $att_text->attr('content-type' => 'text/plain; charset=UTF-8');
|
1572
|
# $msg->attach($att_text);
|
1573
|
my $att_html = MIME::Lite->new(
|
1574
|
Type => 'text',
|
1575
|
Data => $mailhtml,
|
1576
|
Encoding => 'quoted-printable',
|
1577
|
);
|
1578
|
$att_html->attr('content-type' => 'text/html; charset=UTF-8');
|
1579
|
$msg->attach($att_html);
|
1580
|
my $res = $msg->send;
|
1581
|
$postreply = "Status=OK Password reset email sent to $mailaddrs\n";
|
1582
|
} else {
|
1583
|
$postreply = "Status=Error user does not have a registered email address\n";
|
1584
|
}
|
1585
|
} else {
|
1586
|
$postreply = "Status=Error invalid data submitted\n";
|
1587
|
}
|
1588
|
return $postreply;
|
1589
|
}
|
1590
|
|
1591
|
sub do_changepassword {
|
1592
|
my ($uuid, $action, $obj) = @_;
|
1593
|
if ($help) {
|
1594
|
return <<END
|
1595
|
GET:username,password:
|
1596
|
Changes the password for a user.
|
1597
|
END
|
1598
|
}
|
1599
|
my $username = $obj->{'username'} || $user;
|
1600
|
my $password = $obj->{'password'};
|
1601
|
if ($password && $register{$username} && ($username eq $user || $isadmin)) {
|
1602
|
$MAXLEN = 20;
|
1603
|
var $msg = IsBadPassword($password);
|
1604
|
if ($msg) {
|
1605
|
$postreply = "Status=Error $msg - please choose a stronger password\n";
|
1606
|
} else {
|
1607
|
$password = Digest::SHA::sha512_base64($password);
|
1608
|
$register{$username}->{'password'} = $password;
|
1609
|
$postreply = "Status=OK Password changed for $username\n";
|
1610
|
}
|
1611
|
} else {
|
1612
|
$postreply = "Status=Error invalid data submitted\n";
|
1613
|
}
|
1614
|
return $postreply;
|
1615
|
}
|
1616
|
|
1617
|
sub do_remove {
|
1618
|
my ($uuid, $action, $obj) = @_;
|
1619
|
if ($help) {
|
1620
|
return <<END
|
1621
|
GET:username:
|
1622
|
Removes a user.
|
1623
|
END
|
1624
|
}
|
1625
|
my $username = $obj->{'username'};
|
1626
|
$postreply = remove($username);
|
1627
|
return $postreply;
|
1628
|
}
|
1629
|
|
1630
|
sub remove {
|
1631
|
my $username = shift;
|
1632
|
if (!$isadmin && ($user ne $engineuser)) {
|
1633
|
$postreply .= "Status=ERROR You are not allowed to remove user $username\n";
|
1634
|
} elsif ($register{$username}) {
|
1635
|
delete $register{$username};
|
1636
|
tied(%register)->commit;
|
1637
|
`/bin/rm /tmp/$username~*.tasks`;
|
1638
|
unlink "../cgi/ui_update/$username~ui_update.cgi" if (-e "../cgi/ui_update/$username~ui_update.cgi");
|
1639
|
$main::syslogit->($user, "info", "Deleted user $username from db");
|
1640
|
if ($console) {
|
1641
|
$postreply .= "Status=OK Deleted user $username\n";
|
1642
|
} else {
|
1643
|
# $main::updateUI->({ tab => 'users', type=>'update', user=>$user});
|
1644
|
return "{}";
|
1645
|
}
|
1646
|
return $postreply;
|
1647
|
} else {
|
1648
|
$postreply .= "Status=ERROR No such user: $username\n";
|
1649
|
}
|
1650
|
}
|
1651
|
|
1652
|
# Update engine users with users received from the registry
|
1653
|
sub updateEngineUsers {
|
1654
|
my ($json_text) = @_;
|
1655
|
return unless ($isadmin || ($user eq $engineuser));
|
1656
|
my $res;
|
1657
|
my $json = JSON->new;
|
1658
|
$json->utf8([1]);
|
1659
|
my $json_obj = $json->decode($json_text);
|
1660
|
my @ulist = @$json_obj;
|
1661
|
my @efields = qw(password
|
1662
|
address city company country email fullname phone
|
1663
|
state zip alertemail opemail opfullname opphone billto
|
1664
|
memoryquota storagequota vcpuquota externalipquota rxquota txquota nodestoragequota
|
1665
|
accounts accountsprivileges privileges modified dnsdomains appstoreurl totpsecret
|
1666
|
);
|
1667
|
my $ures;
|
1668
|
my $ucount = 0;
|
1669
|
foreach my $u (@ulist) {
|
1670
|
my $username = $u->{'username'};
|
1671
|
if (!$register{$username} && $u->{'password'}) {
|
1672
|
$register{$username} = {
|
1673
|
username => $username,
|
1674
|
password => $u->{'password'},
|
1675
|
allowinternalapi => 1
|
1676
|
};
|
1677
|
$ures .= " *";
|
1678
|
}
|
1679
|
next unless ($register{$username});
|
1680
|
next if ($register{$username}->{'modified'} && $register{$username}->{'modified'} > $u->{'modified'});
|
1681
|
foreach my $efield (@efields) {
|
1682
|
if ($efield eq 'privileges') {
|
1683
|
$u->{$efield} =~ tr/adnrpu//cd; # filter out non-valid privileges
|
1684
|
}
|
1685
|
if (defined $u->{$efield}) {
|
1686
|
$u->{$efield} += 0 if ($efield =~ /(quota|price)$/);
|
1687
|
$register{$username}->{$efield} = $u->{$efield};
|
1688
|
}
|
1689
|
delete $u->{$efield} if (defined $u->{$efield} && $u->{$efield} eq '' && $efield ne 'password')
|
1690
|
}
|
1691
|
$ures .= "$username ($u->{'fullname'}), ";
|
1692
|
$ucount++;
|
1693
|
my $uid = `id -u irigo-$username`; chomp $uid;
|
1694
|
if (!$uid) { # Check user has system account for disk quotas
|
1695
|
$main::syslogit->($user, "info", "Adding system user $username");
|
1696
|
`/usr/sbin/useradd -m "irigo-$username"`;
|
1697
|
`echo "[User]\nSystemAccount=true" > /var/lib/AccountsService/users/irigo-$username`; # Don't show in login screen
|
1698
|
}
|
1699
|
|
1700
|
}
|
1701
|
$ures = substr($res, 0, -2) . "\n";
|
1702
|
$res .= "Status=OK Received $ucount updates on " .(scalar(@ulist)). " registry users\n";
|
1703
|
return $res;
|
1704
|
}
|
1705
|
|
1706
|
sub sendEngineUser {
|
1707
|
my ($username) = @_;
|
1708
|
if ($enginelinked) {
|
1709
|
# Send engine user to the registry
|
1710
|
require LWP::Simple;
|
1711
|
my $browser = LWP::UserAgent->new;
|
1712
|
$browser->agent('stabile/1.0b');
|
1713
|
$browser->protocols_allowed( [ 'http','https'] );
|
1714
|
|
1715
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
1716
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
1717
|
my $tkthash = Digest::SHA::sha512_hex($tktkey);
|
1718
|
my $json = '[' . JSON::to_json(\%{$register{$username}}) . ']';
|
1719
|
$json =~ s/null/""/g;
|
1720
|
# $json = uri_escape_utf8($json);
|
1721
|
$json = URI::Escape::uri_escape($json);
|
1722
|
my $posturl = "https://www.stabile.io/irigo/engine.cgi?action=update";
|
1723
|
my $postreq = ();
|
1724
|
$postreq->{'POSTDATA'} = $json;
|
1725
|
$postreq->{'engineid'} = $engineid;
|
1726
|
$postreq->{'enginetkthash'} = $tkthash;
|
1727
|
|
1728
|
# my $req = HTTP::Request->new(POST => $posturl);
|
1729
|
# $req->content_type("application/json; charset='utf8'");
|
1730
|
# $req->content($postreq);
|
1731
|
|
1732
|
$content = $browser->post($posturl, $postreq)->content();
|
1733
|
# $content = $browser->post($posturl, 'Content-type' => 'text/plain;charset=utf-8', Content => $postreq)->content();
|
1734
|
# $content = $browser->request($req)->content();
|
1735
|
my $fullname = $register{$username}->{'fullname'};
|
1736
|
$fullname = Encode::decode('utf8', $fullname);
|
1737
|
return "Updated $fullname in registry\n";
|
1738
|
}
|
1739
|
}
|