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