| 1 |
95b003ff
|
Origo
|
#!/usr/bin/perl
|
| 2 |
|
|
|
| 3 |
|
|
# All rights reserved and Copyright (c) 2020 Origo Systems ApS.
|
| 4 |
|
|
# This file is provided with no warranty, and is subject to the terms and conditions defined in the license file LICENSE.md.
|
| 5 |
|
|
# The license file is part of this source code package and its content is also available at:
|
| 6 |
|
|
# https://www.origo.io/info/stabiledocs/licensing/stabile-open-source-license
|
| 7 |
|
|
|
| 8 |
|
|
# Clear up tainted environment
|
| 9 |
|
|
$ENV{PATH} = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin';
|
| 10 |
|
|
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
|
| 11 |
|
|
|
| 12 |
|
|
#use warnings FATAL => 'all';
|
| 13 |
|
|
use CGI::Carp qw(fatalsToBrowser);
|
| 14 |
|
|
use CGI qw(:standard);
|
| 15 |
|
|
use Getopt::Std;
|
| 16 |
|
|
use JSON;
|
| 17 |
|
|
use URI::Escape qw(uri_escape uri_unescape);
|
| 18 |
|
|
use Tie::DBI;
|
| 19 |
|
|
use Data::Dumper;
|
| 20 |
|
|
use Encode;
|
| 21 |
|
|
use Text::SimpleTable;
|
| 22 |
|
|
use ConfigReader::Simple;
|
| 23 |
|
|
use Sys::Syslog qw( :DEFAULT setlogsock);
|
| 24 |
|
|
use Digest::SHA qw(sha512_base64 sha512_hex);
|
| 25 |
|
|
use utf8;
|
| 26 |
|
|
use Hash::Merge qw( merge );
|
| 27 |
|
|
use Storable qw(freeze thaw);
|
| 28 |
|
|
use Gearman::Client;
|
| 29 |
|
|
use Proc::ProcessTable;
|
| 30 |
|
|
use HTTP::Async;
|
| 31 |
|
|
use HTTP::Request::Common;
|
| 32 |
|
|
use LWP::Simple qw(!head);
|
| 33 |
|
|
use Error::Simple;
|
| 34 |
|
|
|
| 35 |
|
|
our %options=();
|
| 36 |
|
|
# -a action -h help -f full list -p full update -u uuid -i image -m match pattern -k keywords -g args to gearman task
|
| 37 |
|
|
# -v verbose, include HTTP headers -s impersonate subaccount -t target [uuid or image] -c force console
|
| 38 |
|
|
Getopt::Std::getopts("a:hfpu:i:g:m:k:vs:t:c", \%options);
|
| 39 |
|
|
|
| 40 |
|
|
$Stabile::config = ConfigReader::Simple->new("/etc/stabile/config.cfg",
|
| 41 |
|
|
[qw(
|
| 42 |
|
|
AMT_PASSWD
|
| 43 |
|
|
DBI_PASSWD
|
| 44 |
|
|
DBI_USER
|
| 45 |
|
|
DO_DNS
|
| 46 |
|
|
DNS_DOMAIN
|
| 47 |
|
|
DO_XMPP
|
| 48 |
|
|
ENGINEID
|
| 49 |
|
|
ENGINENAME
|
| 50 |
|
|
ENGINE_DATA_NIC
|
| 51 |
|
|
ENGINE_LINKED
|
| 52 |
|
|
EXTERNAL_IP_RANGE_START
|
| 53 |
|
|
EXTERNAL_IP_RANGE_END
|
| 54 |
|
|
EXTERNAL_IP_QUOTA
|
| 55 |
|
|
EXTERNAL_NIC
|
| 56 |
|
|
EXTERNAL_SUBNET_SIZE
|
| 57 |
|
|
MEMORY_QUOTA
|
| 58 |
|
|
NODE_STORAGE_OVERCOMMISSION
|
| 59 |
|
|
NODESTORAGE_QUOTA
|
| 60 |
|
|
PROXY_GW
|
| 61 |
|
|
PROXY_IP
|
| 62 |
|
|
PROXY_IP_RANGE_END
|
| 63 |
|
|
PROXY_IP_RANGE_START
|
| 64 |
|
|
PROXY_SUBNET_SIZE
|
| 65 |
|
|
RDIFF-BACKUP_ENABLED
|
| 66 |
|
|
RDIFF-BACKUP_USERS
|
| 67 |
6372a66e
|
hq
|
REMOTE_IP_ENABLED
|
| 68 |
|
|
REMOTE_IP_PROVIDER
|
| 69 |
95b003ff
|
Origo
|
RX_QUOTA
|
| 70 |
|
|
SHOW_COST
|
| 71 |
|
|
STORAGE_BACKUPDIR
|
| 72 |
|
|
STORAGE_POOLS_ADDRESS_PATHS
|
| 73 |
|
|
STORAGE_POOLS_DEFAULTS
|
| 74 |
|
|
STORAGE_POOLS_LOCAL_PATHS
|
| 75 |
|
|
STORAGE_POOLS_NAMES
|
| 76 |
|
|
STORAGE_POOLS_RDIFF-BACKUP_ENABLED
|
| 77 |
|
|
STORAGE_QUOTA
|
| 78 |
|
|
Z_IMAGE_RETENTION
|
| 79 |
|
|
Z_BACKUP_RETENTION
|
| 80 |
|
|
TX_QUOTA
|
| 81 |
|
|
VCPU_QUOTA
|
| 82 |
|
|
VLAN_RANGE_START
|
| 83 |
|
|
VLAN_RANGE_END
|
| 84 |
|
|
VERSION
|
| 85 |
|
|
)]);
|
| 86 |
|
|
|
| 87 |
|
|
$dbiuser = $Stabile::config->get('DBI_USER') || "irigo";
|
| 88 |
|
|
$dbipasswd = $Stabile::config->get('DBI_PASSWD') || "";
|
| 89 |
|
|
$dnsdomain = $Stabile::config->get('DNS_DOMAIN') || "stabile.io";
|
| 90 |
2a63870a
|
Christian Orellana
|
$appstoreurl = $Stabile::config->get('APPSTORE_URL') || "https://www.origo.io/registry";
|
| 91 |
c899e439
|
Origo
|
$appstores = $Stabile::config->get('APPSTORES') || "stabile.io"; # Used for publishing apps
|
| 92 |
95b003ff
|
Origo
|
$engineuser = $Stabile::config->get('ENGINEUSER') || "";
|
| 93 |
|
|
$imageretention = $Stabile::config->get('Z_IMAGE_RETENTION') || "";
|
| 94 |
|
|
$backupretention = $Stabile::config->get('Z_BACKUP_RETENTION') || "";
|
| 95 |
|
|
$enginelinked = $Stabile::config->get('ENGINE_LINKED') || "";
|
| 96 |
a2e0bc7e
|
hq
|
$downloadmasters = $Stabile::config->get('DOWNLOAD_MASTERS') || "";
|
| 97 |
95b003ff
|
Origo
|
$disablesnat = $Stabile::config->get('DISABLE_SNAT') || "";
|
| 98 |
d3805c61
|
hq
|
our $enforceiolimits = $Stabile::config->get('ENFORCE_IO_LIMITS') || "";
|
| 99 |
e9af6c24
|
Origo
|
our $engineid = $Stabile::config->get('ENGINEID') || "";
|
| 100 |
95b003ff
|
Origo
|
|
| 101 |
a2e0bc7e
|
hq
|
$Stabile::remoteipprovider = ($Stabile::config->get('REMOTE_IP_PROVIDER')) || "";
|
| 102 |
|
|
$Stabile::remoteipenabled = ($Stabile::config->get('REMOTE_IP_ENABLED') && $Stabile::config->get('ENGINE_LINKED')) || "";
|
| 103 |
|
|
$Stabile::engineuser = $Stabile::config->get('ENGINEUSER') || "";
|
| 104 |
|
|
|
| 105 |
95b003ff
|
Origo
|
$Stabile::dbopts = {db=>'mysql:steamregister', key=>'uuid', autocommit=>0, CLOBBER=>2, user=>$dbiuser, password=>$dbipasswd};
|
| 106 |
|
|
$Stabile::auth_tkt_conf = "/etc/apache2/conf-available/auth_tkt_cgi.conf";
|
| 107 |
|
|
|
| 108 |
|
|
my $base = "/var/www/stabile";
|
| 109 |
|
|
$base = `cat /etc/stabile/basedir` if (-e "/etc/stabile/basedir");
|
| 110 |
|
|
chomp $base;
|
| 111 |
|
|
$base =~ /(.+)/; $base = $1; #untaint
|
| 112 |
|
|
$main::logfile = "/var/log/stabile/steam.log";
|
| 113 |
|
|
|
| 114 |
|
|
$current_time = time;
|
| 115 |
|
|
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($current_time);
|
| 116 |
|
|
$year += 1900;
|
| 117 |
|
|
$month = substr("0" . ($mon+1), -2);
|
| 118 |
|
|
$pretty_time = sprintf "%4d-%02d-%02d@%02d:%02d:%02d",$year,$mon+1,$mday,$hour,$min,$sec;
|
| 119 |
|
|
|
| 120 |
d24d9a01
|
hq
|
if ($ENV{'HTTP_HOST'} && !($ENV{'HTTP_HOST'} =~ /^10\./) && $ENV{'HTTP_HOST'} ne 'localhost' && !($ENV{'HTTP_HOST'} =~ /^127/)) {
|
| 121 |
|
|
$baseurl = "https://$ENV{'HTTP_HOST'}/stabile";
|
| 122 |
|
|
`echo "$baseurl" > /tmp/baseurl` if ((! -e "/tmp/baseurl") && $baseurl);
|
| 123 |
2a63870a
|
Christian Orellana
|
} else {
|
| 124 |
|
|
if (!$baseurl && (-e "/tmp/baseurl" || -e "/etc/stabile/baseurl")) {
|
| 125 |
|
|
if (-e "/etc/stabile/baseurl") {
|
| 126 |
|
|
$baseurl = `cat /etc/stabile/baseurl`;
|
| 127 |
|
|
} else {
|
| 128 |
|
|
$baseurl = `cat /tmp/baseurl`;
|
| 129 |
|
|
chomp $baseurl;
|
| 130 |
|
|
`echo "$baseurl" >/etc/stabile/baseurl` unless (-e "/etc/stabile/baseurl");
|
| 131 |
|
|
}
|
| 132 |
|
|
}
|
| 133 |
|
|
}
|
| 134 |
|
|
if (!$baseurl) {
|
| 135 |
|
|
my $hostname = `hostname`; chomp $hostname;
|
| 136 |
|
|
$baseurl = "https://$hostname/stabile";
|
| 137 |
|
|
}
|
| 138 |
95b003ff
|
Origo
|
$baseurl = $1 if ($baseurl =~ /(.+)/); #untaint
|
| 139 |
|
|
|
| 140 |
|
|
$Stabile::basedir = "/var/www/stabile";
|
| 141 |
|
|
$Stabile::basedir = `cat /etc/stabile/basedir` if -e "/etc/stabile/basedir";
|
| 142 |
|
|
chomp $Stabile::basedir;
|
| 143 |
|
|
$Stabile::basedir = $1 if ($Stabile::basedir =~ /(.+)/); #untaint
|
| 144 |
|
|
|
| 145 |
|
|
$package = substr(lc __PACKAGE__, length "Stabile::");
|
| 146 |
|
|
$programname = "Stabile";
|
| 147 |
|
|
|
| 148 |
|
|
$sshcmd = qq|ssh -l irigo -i /var/www/.ssh/id_rsa_www -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no|;
|
| 149 |
|
|
|
| 150 |
|
|
$ENV{'REQUEST_METHOD'} = $ENV{'REQUEST_METHOD'} || 'GET';
|
| 151 |
|
|
|
| 152 |
|
|
preInit();
|
| 153 |
|
|
1;
|
| 154 |
|
|
|
| 155 |
|
|
$main::syslogit = sub {
|
| 156 |
|
|
my ($user, $p, $msg) = @_;
|
| 157 |
|
|
my $priority = ($p eq 'syslog')?'info':$p;
|
| 158 |
|
|
|
| 159 |
|
|
$current_time = time;
|
| 160 |
|
|
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($current_time);
|
| 161 |
|
|
$year += 1900;
|
| 162 |
|
|
$month = substr("0" . ($mon+1), -2);
|
| 163 |
|
|
my $pretty_time = sprintf "%4d-%02d-%02d@%02d:%02d:%02d",$year,$mon+1,$mday,$hour,$min,$sec;
|
| 164 |
|
|
|
| 165 |
|
|
my $loguser = (!$tktuser || $tktuser eq $user)?"$user":"$user ($tktuser)";
|
| 166 |
|
|
if ($msg && $msg ne '') {
|
| 167 |
|
|
utf8::decode($msg);
|
| 168 |
|
|
unless (open(TEMP3, ">>$main::logfile")) {$posterror .= "Status=Error log file '$main::logfile' could not be written";}
|
| 169 |
|
|
$msg =~ /(.+)/; $msg = $1; #untaint
|
| 170 |
|
|
print TEMP3 $pretty_time, " : $loguser : $msg\n";
|
| 171 |
|
|
close(TEMP3);
|
| 172 |
|
|
}
|
| 173 |
|
|
return 0 unless ($priority =~ /err|debug/);
|
| 174 |
|
|
setlogsock('unix');
|
| 175 |
|
|
# $programname is assumed to be a global. Also log the PID
|
| 176 |
|
|
# and to CONSole if there's a problem. Use facility 'user'.
|
| 177 |
|
|
openlog($programname, 'pid,cons', 'user');
|
| 178 |
|
|
syslog($priority, "($loguser) $msg");
|
| 179 |
|
|
closelog();
|
| 180 |
|
|
return 1;
|
| 181 |
|
|
};
|
| 182 |
|
|
|
| 183 |
|
|
|
| 184 |
|
|
$main::postToOrigo = sub {
|
| 185 |
|
|
my ($engineid, $postaction, $postcontent, $postkey, $callback) = @_;
|
| 186 |
|
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
| 187 |
|
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
| 188 |
|
|
my $ret;
|
| 189 |
|
|
|
| 190 |
|
|
if ($tktkey && $engineid) {
|
| 191 |
|
|
my $browser = LWP::UserAgent->new;
|
| 192 |
|
|
$browser->timeout(15);
|
| 193 |
|
|
$browser->agent('pressurecontrol/1.0b');
|
| 194 |
|
|
$browser->protocols_allowed( [ 'http','https'] );
|
| 195 |
|
|
|
| 196 |
|
|
my $postreq;
|
| 197 |
|
|
$postreq->{'engineid'} = $engineid;
|
| 198 |
|
|
$postreq->{'enginetkthash'} = sha512_hex($tktkey) if ($enginelinked);
|
| 199 |
|
|
$postreq->{'appuser'} = $user;
|
| 200 |
|
|
$postreq->{'callback'} .= $callback if ($callback);
|
| 201 |
|
|
$postkey = 'POSTDATA' unless ($postkey);
|
| 202 |
|
|
$postreq->{$postkey} = $postcontent;
|
| 203 |
|
|
my $posturl = "https://www.origo.io/irigo/engine.cgi?action=$postaction";
|
| 204 |
|
|
my $content = $browser->post($posturl, $postreq)->content();
|
| 205 |
|
|
my $ok = ($content =~ /OK: (.*)/i);
|
| 206 |
|
|
$ret .= $content;
|
| 207 |
|
|
} else {
|
| 208 |
|
|
$main::syslogit->('pressurecontrol', 'info', "Unable to get engine tktkey...");
|
| 209 |
|
|
$ret .= "Unable to get engine tktkey...";
|
| 210 |
|
|
}
|
| 211 |
|
|
return $ret;
|
| 212 |
|
|
};
|
| 213 |
|
|
|
| 214 |
48fcda6b
|
Origo
|
$main::uploadToOrigo = sub {
|
| 215 |
|
|
my ($engineid, $filepath, $force) = @_;
|
| 216 |
|
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
| 217 |
|
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
| 218 |
|
|
my $ret;
|
| 219 |
|
|
|
| 220 |
|
|
if (!$filepath || !(-e $filepath)) {
|
| 221 |
|
|
$ret = "Status=Error Invalid file path\n";
|
| 222 |
|
|
} elsif ($tktkey && $engineid) {
|
| 223 |
2a63870a
|
Christian Orellana
|
$HTTP::Request::Common::DYNAMIC_FILE_UPLOAD = 1;
|
| 224 |
48fcda6b
|
Origo
|
my $browser = LWP::UserAgent->new;
|
| 225 |
|
|
$browser->timeout(15 * 60); # 15 min
|
| 226 |
|
|
$browser->agent('pressurecontrol/1.0b');
|
| 227 |
|
|
$browser->protocols_allowed( [ 'http','https'] );
|
| 228 |
|
|
my $fname = $1 if ($filepath =~ /.*\/(.+\.qcow2)$/);
|
| 229 |
|
|
return "Status=Error Invalid file\n" unless ($fname);
|
| 230 |
|
|
my $posturl = "https://www.origo.io/irigo/engine.cgi?action=uploadimage";
|
| 231 |
2a63870a
|
Christian Orellana
|
|
| 232 |
|
|
# -- using ->post
|
| 233 |
|
|
# my $postreq = [
|
| 234 |
|
|
# 'file' => [ $filepath ],
|
| 235 |
|
|
# 'filename' => $fname,
|
| 236 |
|
|
# 'engineid' => $engineid,
|
| 237 |
|
|
# 'enginetkthash' => sha512_hex($tktkey),
|
| 238 |
|
|
# 'appuser' => $user,
|
| 239 |
|
|
# 'force' => $force
|
| 240 |
|
|
# ];
|
| 241 |
|
|
# my $content = $browser->post($posturl, $postreq, 'Content_Type' => 'form-data')->content;
|
| 242 |
|
|
# $ret .= $content;
|
| 243 |
|
|
|
| 244 |
|
|
# -- using ->request
|
| 245 |
|
|
my $req = POST $posturl,
|
| 246 |
|
|
Content_Type => 'form-data',
|
| 247 |
|
|
Content => [
|
| 248 |
|
|
'file' => [ $filepath ],
|
| 249 |
|
|
'filename' => $fname,
|
| 250 |
|
|
'engineid' => $engineid,
|
| 251 |
|
|
'enginetkthash' => sha512_hex($tktkey),
|
| 252 |
|
|
'appuser' => $user,
|
| 253 |
|
|
'force' => $force
|
| 254 |
|
|
];
|
| 255 |
|
|
my $total;
|
| 256 |
|
|
my $callback = $req->content;
|
| 257 |
|
|
if (ref($callback) eq "CODE") {
|
| 258 |
|
|
my $size = $req->header('content-length');
|
| 259 |
|
|
my $counter = 0;
|
| 260 |
|
|
my $progress = '';
|
| 261 |
|
|
$req->content(
|
| 262 |
|
|
sub {
|
| 263 |
|
|
my $chunk = $callback->();
|
| 264 |
|
|
if ($chunk) {
|
| 265 |
|
|
my $length = length $chunk;
|
| 266 |
|
|
$total += $length;
|
| 267 |
|
|
if ($total / $size * 100 > $counter) {
|
| 268 |
|
|
$counter = 1+ int $total / $size * 100;
|
| 269 |
|
|
$progress .= "#";
|
| 270 |
|
|
`echo "$progress$counter" >> /tmp/upload-$fname`;
|
| 271 |
|
|
}
|
| 272 |
|
|
# printf "%+5d = %5.1f%%\n", $length, $total / $size * 100;
|
| 273 |
|
|
# printf "%5.1f%%\n", $total / $size * 100;
|
| 274 |
|
|
|
| 275 |
|
|
} else {
|
| 276 |
|
|
# print "Done\n";
|
| 277 |
|
|
}
|
| 278 |
|
|
$chunk;
|
| 279 |
|
|
}
|
| 280 |
|
|
);
|
| 281 |
|
|
my $resp = $browser->request($req)->content();
|
| 282 |
|
|
$ret .= $resp;
|
| 283 |
|
|
$ret .= "Status=OK $progress\n";
|
| 284 |
|
|
} else {
|
| 285 |
|
|
$ret .= "Status=Error Did not get a callback";
|
| 286 |
|
|
}
|
| 287 |
48fcda6b
|
Origo
|
} else {
|
| 288 |
|
|
$ret .= "Status=Error Unable to get engine tktkey...";
|
| 289 |
|
|
}
|
| 290 |
|
|
return $ret;
|
| 291 |
|
|
};
|
| 292 |
|
|
|
| 293 |
95b003ff
|
Origo
|
$main::postAsyncToOrigo = sub {
|
| 294 |
|
|
my ($engineid, $postaction, $json_text) = @_;
|
| 295 |
|
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
| 296 |
|
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
| 297 |
|
|
my $ret;
|
| 298 |
|
|
|
| 299 |
|
|
if ($tktkey && $engineid) {
|
| 300 |
|
|
my $browser = LWP::UserAgent->new;
|
| 301 |
|
|
$browser->timeout(15);
|
| 302 |
|
|
$browser->agent('pressurecontrol/1.0b');
|
| 303 |
|
|
$browser->protocols_allowed( [ 'http','https'] );
|
| 304 |
|
|
|
| 305 |
|
|
$ret .= "Posting $postaction to origo.io\n";
|
| 306 |
|
|
|
| 307 |
|
|
my $postreq;
|
| 308 |
|
|
$postreq->{'engineid'} = $engineid;
|
| 309 |
|
|
$postreq->{'enginetkthash'} = sha512_hex($tktkey);
|
| 310 |
|
|
$postreq->{'POSTDATA'} = $json_text;
|
| 311 |
|
|
# my $content = $browser->post("https://www.origo.io/irigo/engine.cgi?action=$postaction", $postreq)->content();
|
| 312 |
|
|
# my $ok = ($content =~ /OK: (.*)/i);
|
| 313 |
|
|
# $ret .= $content;
|
| 314 |
|
|
|
| 315 |
|
|
my $async = HTTP::Async->new;
|
| 316 |
|
|
my $post = POST "https://www.origo.io/irigo/engine.cgi?action=$postaction",
|
| 317 |
|
|
[ engineid => $engineid,
|
| 318 |
|
|
enginetkthash => sha512_hex($tktkey),
|
| 319 |
|
|
POSTDATA => $json_text
|
| 320 |
|
|
];
|
| 321 |
|
|
$async->add( $post );
|
| 322 |
|
|
# while ( my $response = $async->wait_for_next_response ) {
|
| 323 |
|
|
# $ret .= $response->decoded_content;
|
| 324 |
|
|
# }
|
| 325 |
|
|
} else {
|
| 326 |
|
|
$main::syslogit->('pressurecontrol', 'info', "Unable to get engine tktkey...");
|
| 327 |
|
|
$ret .= "Unable to get engine tktkey...";
|
| 328 |
|
|
}
|
| 329 |
|
|
return $ret;
|
| 330 |
|
|
};
|
| 331 |
|
|
|
| 332 |
|
|
$main::dnsCreate = sub {
|
| 333 |
|
|
my ($engineid, $name, $value, $type, $username) = @_;
|
| 334 |
|
|
my $res;
|
| 335 |
e9af6c24
|
Origo
|
my $dnssubdomain = substr($engineid, 0, 8);
|
| 336 |
|
|
$type = uc $type;
|
| 337 |
|
|
$type || 'CNAME';
|
| 338 |
95b003ff
|
Origo
|
$name = $1 if ($name =~ /(.+)\.$dnsdomain/);
|
| 339 |
e9af6c24
|
Origo
|
# $name =$1 if ($name =~ /(.+)\.$dnssubdomain/);
|
| 340 |
|
|
if ($type eq 'A') { # Look for initial registrations and format correctly
|
| 341 |
|
|
if (!$name && $value) { # If no name provided assume we are creating initial A-record
|
| 342 |
|
|
$name = $value;
|
| 343 |
|
|
} elsif ($name =~ /^(\d+\.\d+\.\d+\.\d+)/) { # Looks like an IP address - must be same as value
|
| 344 |
|
|
if ($1 eq $value) { # Keep some order in registrations
|
| 345 |
|
|
$name = "$value.$dnssubdomain"; # The way we format initial registrations
|
| 346 |
|
|
} else {
|
| 347 |
|
|
$name = '';
|
| 348 |
|
|
}
|
| 349 |
|
|
}
|
| 350 |
|
|
}
|
| 351 |
95b003ff
|
Origo
|
# Only allow creation of records corresponding to user's own networks when username is supplied
|
| 352 |
|
|
# When username is not supplied, we assume checking has been done
|
| 353 |
|
|
if ($username) {
|
| 354 |
|
|
my $checkval = $value;
|
| 355 |
e9af6c24
|
Origo
|
# Remove any trailing period
|
| 356 |
95b003ff
|
Origo
|
$checkval = $1 if ($checkval =~ /(.+)\.$/);
|
| 357 |
6fdc8676
|
hq
|
if ($type eq 'TXT') {
|
| 358 |
|
|
$checkval = '';
|
| 359 |
|
|
} elsif ($type eq 'A') {
|
| 360 |
95b003ff
|
Origo
|
$checkval = $value;
|
| 361 |
|
|
} else {
|
| 362 |
e9af6c24
|
Origo
|
$checkval = $1 if ($checkval =~ /(\d+\.\d+\.\d+\.\d+)\.$dnssubdomain\.$dnsdomain$/);
|
| 363 |
95b003ff
|
Origo
|
$checkval = $1 if ($checkval =~ /(\d+\.\d+\.\d+\.\d+)\.$dnsdomain$/);
|
| 364 |
e9af6c24
|
Origo
|
$checkval = $1 if ($checkval =~ /(\d+\.\d+\.\d+\.\d+)$/);
|
| 365 |
95b003ff
|
Origo
|
}
|
| 366 |
|
|
if ($checkval) {
|
| 367 |
|
|
unless (tie %networkreg,'Tie::DBI', {
|
| 368 |
|
|
db=>'mysql:steamregister',
|
| 369 |
|
|
table=>'networks',
|
| 370 |
|
|
key=>'uuid',
|
| 371 |
|
|
autocommit=>0,
|
| 372 |
|
|
CLOBBER=>0,
|
| 373 |
|
|
user=>$dbiuser,
|
| 374 |
|
|
password=>$dbipasswd}) {throw Error::Simple("Error Register could not be accessed")};
|
| 375 |
|
|
my @regkeys = (tied %networkreg)->select_where("externalip = '$checkval'");
|
| 376 |
|
|
if (scalar @regkeys == 1) {
|
| 377 |
04c16f26
|
hq
|
if ($register{$regkeys[0]} && $register{$regkeys[0]}->{'user'} eq $username) {
|
| 378 |
95b003ff
|
Origo
|
; # OK
|
| 379 |
|
|
} else {
|
| 380 |
eb31fb38
|
hq
|
return qq|{"status": "Error", "message": "Invalid value $checkval, not allowed"}|;
|
| 381 |
95b003ff
|
Origo
|
}
|
| 382 |
|
|
} elsif (scalar @regkeys >1) {
|
| 383 |
eb31fb38
|
hq
|
return qq|{"status": "Error", "message": "Invalid value $checkval"}|;
|
| 384 |
95b003ff
|
Origo
|
}
|
| 385 |
|
|
untie %networkreg;
|
| 386 |
e9af6c24
|
Origo
|
if ($type eq 'A') {
|
| 387 |
6fdc8676
|
hq
|
# $name = "$checkval.$dnssubdomain"; # Only allow this type of A-records...?
|
| 388 |
e9af6c24
|
Origo
|
} else {
|
| 389 |
|
|
$value = "$checkval.$dnssubdomain";
|
| 390 |
|
|
}
|
| 391 |
95b003ff
|
Origo
|
}
|
| 392 |
|
|
}
|
| 393 |
|
|
|
| 394 |
6fdc8676
|
hq
|
if ($type ne 'MX' && $type ne 'TXT' && `host $name.$dnsdomain authns1.cabocomm.dk` =~ /has address/) {
|
| 395 |
eb31fb38
|
hq
|
return qq|{"status": "Error", "message": "$name is already registered"}|;
|
| 396 |
e9af6c24
|
Origo
|
};
|
| 397 |
|
|
|
| 398 |
95b003ff
|
Origo
|
if ($enginelinked && $name && $value) {
|
| 399 |
|
|
require LWP::Simple;
|
| 400 |
|
|
my $browser = LWP::UserAgent->new;
|
| 401 |
|
|
$browser->agent('Stabile/1.0b');
|
| 402 |
|
|
$browser->protocols_allowed( [ 'http','https'] );
|
| 403 |
|
|
$browser->timeout(10);
|
| 404 |
|
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
| 405 |
|
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
| 406 |
|
|
my $tkthash = sha512_hex($tktkey);
|
| 407 |
|
|
my $posturl = "https://www.origo.io/irigo/engine.cgi?action=dnscreate";
|
| 408 |
|
|
|
| 409 |
|
|
my $async = HTTP::Async->new;
|
| 410 |
|
|
my $post = POST $posturl,
|
| 411 |
6fdc8676
|
hq
|
[ engineid => $engineid,
|
| 412 |
95b003ff
|
Origo
|
enginetkthash => $tkthash,
|
| 413 |
6fdc8676
|
hq
|
name => $name,
|
| 414 |
|
|
domain => $dnsdomain,
|
| 415 |
|
|
value => $value,
|
| 416 |
|
|
type => $type,
|
| 417 |
|
|
username => $username || $user
|
| 418 |
95b003ff
|
Origo
|
];
|
| 419 |
|
|
# We fire this asynchronously and hope for the best. Waiting for an answer is just too erratic for now
|
| 420 |
|
|
$async->add( $post );
|
| 421 |
|
|
|
| 422 |
|
|
if ($username) {
|
| 423 |
|
|
my $response;
|
| 424 |
|
|
while ( $response = $async->wait_for_next_response ) {
|
| 425 |
|
|
$ret .= $response->decoded_content;
|
| 426 |
|
|
}
|
| 427 |
|
|
foreach my $line (split /\n/, $ret) {
|
| 428 |
|
|
$res .= $line unless ($line =~ /^\d/);
|
| 429 |
|
|
}
|
| 430 |
|
|
}
|
| 431 |
eb31fb38
|
hq
|
# $res =~ s/://g;
|
| 432 |
3657de20
|
Origo
|
return "$res\n";
|
| 433 |
95b003ff
|
Origo
|
|
| 434 |
|
|
} else {
|
| 435 |
eb31fb38
|
hq
|
return qq|{"status": "Error", "message": "Problem creating dns record with data $name, $value.| . ($enginelinked?"":" Engine is not linked!") . qq|"}|;
|
| 436 |
95b003ff
|
Origo
|
}
|
| 437 |
|
|
};
|
| 438 |
|
|
|
| 439 |
|
|
$main::dnsDelete = sub {
|
| 440 |
ca937547
|
hq
|
my ($engineid, $name, $value, $type, $username) = @_;
|
| 441 |
e9af6c24
|
Origo
|
my $dnssubdomain = substr($engineid, 0, 8);
|
| 442 |
afc024ef
|
hq
|
$name = $1 if ($name =~ /(.+)\.$dnsdomain$/);
|
| 443 |
|
|
# $name =$1 if ($name =~ /(.+)\.$dnssubdomain/);
|
| 444 |
ca937547
|
hq
|
if ($name =~ /^(\d+\.\d+\.\d+\.\d+)$/) {
|
| 445 |
|
|
$name = "$1.$dnssubdomain";
|
| 446 |
|
|
$type = $type || 'A';
|
| 447 |
95b003ff
|
Origo
|
}
|
| 448 |
|
|
|
| 449 |
ca937547
|
hq
|
$main::syslogit->($user, "info", "Deleting DNS entry $type $name $dnsdomain");
|
| 450 |
95b003ff
|
Origo
|
if ($enginelinked && $name) {
|
| 451 |
|
|
require LWP::Simple;
|
| 452 |
|
|
my $browser = LWP::UserAgent->new;
|
| 453 |
|
|
$browser->agent('Stabile/1.0b');
|
| 454 |
|
|
$browser->protocols_allowed( [ 'http','https'] );
|
| 455 |
|
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
| 456 |
|
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
| 457 |
|
|
my $tkthash = sha512_hex($tktkey);
|
| 458 |
|
|
my $posturl = "https://www.origo.io/irigo/engine.cgi?action=dnsdelete";
|
| 459 |
|
|
|
| 460 |
|
|
my $postreq = ();
|
| 461 |
|
|
$postreq->{'engineid'} = $engineid;
|
| 462 |
|
|
$postreq->{'enginetkthash'} = $tkthash;
|
| 463 |
|
|
$postreq->{'name'} = $name;
|
| 464 |
ca937547
|
hq
|
$postreq->{'value'} = $value;
|
| 465 |
|
|
$postreq->{'type'} = $type;
|
| 466 |
6fdc8676
|
hq
|
$postreq->{'username'} = $username || $user;
|
| 467 |
|
|
$postreq->{'domain'} = "$dnsdomain";
|
| 468 |
95b003ff
|
Origo
|
$content = $browser->post($posturl, $postreq)->content();
|
| 469 |
eb31fb38
|
hq
|
# $content =~ s/://g;
|
| 470 |
95b003ff
|
Origo
|
return $content;
|
| 471 |
|
|
} else {
|
| 472 |
|
|
return "ERROR Invalid data $name." . ($enginelinked?"":" Engine is not linked!") . "\n";
|
| 473 |
|
|
}
|
| 474 |
|
|
};
|
| 475 |
|
|
|
| 476 |
48fcda6b
|
Origo
|
$main::dnsUpdate = sub {
|
| 477 |
eb31fb38
|
hq
|
my ($engineid, $name, $value, $type, $oldname, $oldvalue, $username) = @_;
|
| 478 |
48fcda6b
|
Origo
|
$name = $1 if ($name =~ /(.+)\.$dnsdomain/);
|
| 479 |
eb31fb38
|
hq
|
$type = uc $type;
|
| 480 |
|
|
$type || 'CNAME';
|
| 481 |
48fcda6b
|
Origo
|
|
| 482 |
|
|
# Only allow deletion of records corresponding to user's own networks when username is supplied
|
| 483 |
|
|
# When username is not supplied, we assume checking has been done
|
| 484 |
eb31fb38
|
hq
|
# Obsolete
|
| 485 |
|
|
# my $checkval;
|
| 486 |
|
|
# if ($username) {
|
| 487 |
|
|
# if ($name =~ /\d+\.\d+\.\d+\.\d+/) {
|
| 488 |
|
|
# $checkval = $name;
|
| 489 |
|
|
# } else {
|
| 490 |
|
|
# my $checkname = $name;
|
| 491 |
|
|
# # Remove trailing period
|
| 492 |
|
|
# $checkname = $1 if ($checkname =~ /(.+)\.$/);
|
| 493 |
|
|
# $checkname = "$checkname.$dnsdomain" unless ($checkname =~ /(.+)\.$dnsdomain$/);
|
| 494 |
|
|
# $checkval = $1 if (`host $checkname authns1.cabocomm.dk` =~ /has address (\d+\.\d+\.\d+\.\d+)/);
|
| 495 |
|
|
# return "ERROR Invalid value $checkname\n" unless ($checkval);
|
| 496 |
|
|
# }
|
| 497 |
|
|
#
|
| 498 |
|
|
# unless (tie %networkreg,'Tie::DBI', {
|
| 499 |
|
|
# db=>'mysql:steamregister',
|
| 500 |
|
|
# table=>'networks',
|
| 501 |
|
|
# key=>'uuid',
|
| 502 |
|
|
# autocommit=>0,
|
| 503 |
|
|
# CLOBBER=>0,
|
| 504 |
|
|
# user=>$dbiuser,
|
| 505 |
|
|
# password=>$dbipasswd}) {throw Error::Simple("Error Register could not be accessed")};
|
| 506 |
|
|
# my @regkeys = (tied %networkreg)->select_where("externalip = '$checkval' OR internalip = '$checkval'");
|
| 507 |
|
|
# if ($isadmin || (scalar @regkeys == 1 && $register{$regkeys[0]}->{'user'} eq $username)) {
|
| 508 |
|
|
# ; # OK
|
| 509 |
|
|
# } else {
|
| 510 |
|
|
# return "ERROR Invalid user for $checkval, not allowed\n";
|
| 511 |
|
|
# }
|
| 512 |
|
|
# untie %networkreg;
|
| 513 |
|
|
# }
|
| 514 |
48fcda6b
|
Origo
|
|
| 515 |
|
|
$main::syslogit->($user, "info", "Updating DNS entries for $name $dnsdomain");
|
| 516 |
|
|
if ($enginelinked && $name) {
|
| 517 |
|
|
require LWP::Simple;
|
| 518 |
|
|
my $browser = LWP::UserAgent->new;
|
| 519 |
|
|
$browser->agent('Stabile/1.0b');
|
| 520 |
|
|
$browser->protocols_allowed( [ 'http','https'] );
|
| 521 |
|
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
| 522 |
|
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
| 523 |
|
|
my $tkthash = sha512_hex($tktkey);
|
| 524 |
|
|
my $posturl = "https://www.origo.io/irigo/engine.cgi?action=dnsupdate";
|
| 525 |
|
|
|
| 526 |
|
|
my $postreq = ();
|
| 527 |
|
|
$postreq->{'engineid'} = $engineid;
|
| 528 |
|
|
$postreq->{'enginetkthash'} = $tkthash;
|
| 529 |
|
|
$postreq->{'name'} = $name;
|
| 530 |
eb31fb38
|
hq
|
$postreq->{'value'} = $value;
|
| 531 |
|
|
$postreq->{'type'} = $type;
|
| 532 |
|
|
$postreq->{'oldname'} = $oldname if ($oldname);
|
| 533 |
|
|
$postreq->{'oldvalue'} = $oldvalue if ($oldvalue);
|
| 534 |
6fdc8676
|
hq
|
$postreq->{'username'} = $username || $user;
|
| 535 |
48fcda6b
|
Origo
|
$postreq->{'domain'} = $dnsdomain;
|
| 536 |
|
|
$content = $browser->post($posturl, $postreq)->content();
|
| 537 |
|
|
return $content;
|
| 538 |
|
|
} else {
|
| 539 |
|
|
return "ERROR Invalid data $name." . ($enginelinked?"":" Engine is not linked!") . "\n";
|
| 540 |
|
|
}
|
| 541 |
|
|
};
|
| 542 |
|
|
|
| 543 |
e9af6c24
|
Origo
|
$main::dnsList = sub {
|
| 544 |
eb31fb38
|
hq
|
my ($engineid, $username, $domain) = @_;
|
| 545 |
e9af6c24
|
Origo
|
if ($enginelinked) {
|
| 546 |
|
|
require LWP::Simple;
|
| 547 |
|
|
my $browser = LWP::UserAgent->new;
|
| 548 |
|
|
$browser->agent('Stabile/1.0b');
|
| 549 |
|
|
$browser->protocols_allowed( [ 'http','https'] );
|
| 550 |
|
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
| 551 |
|
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
| 552 |
|
|
my $tkthash = sha512_hex($tktkey);
|
| 553 |
|
|
my $posturl = "https://www.origo.io/irigo/engine.cgi?action=dnslist";
|
| 554 |
eb31fb38
|
hq
|
$domain = $domain || $dnsdomain;
|
| 555 |
e9af6c24
|
Origo
|
|
| 556 |
|
|
my $postreq = ();
|
| 557 |
|
|
$postreq->{'engineid'} = $engineid;
|
| 558 |
|
|
$postreq->{'enginetkthash'} = $tkthash;
|
| 559 |
eb31fb38
|
hq
|
$postreq->{'domain'} = $domain;
|
| 560 |
6fdc8676
|
hq
|
$postreq->{'username'} = $username || $user;
|
| 561 |
e9af6c24
|
Origo
|
$content = $browser->post($posturl, $postreq)->content();
|
| 562 |
eb31fb38
|
hq
|
# $content =~ s/://g;
|
| 563 |
e9af6c24
|
Origo
|
return $content;
|
| 564 |
|
|
} else {
|
| 565 |
|
|
return "ERROR Engine is not linked!\n";
|
| 566 |
|
|
}
|
| 567 |
|
|
};
|
| 568 |
|
|
|
| 569 |
|
|
$main::dnsClean = sub {
|
| 570 |
|
|
my ($engineid, $username) = @_;
|
| 571 |
|
|
if ($enginelinked) {
|
| 572 |
|
|
require LWP::Simple;
|
| 573 |
|
|
my $browser = LWP::UserAgent->new;
|
| 574 |
|
|
$browser->agent('Stabile/1.0b');
|
| 575 |
|
|
$browser->protocols_allowed( [ 'http','https'] );
|
| 576 |
|
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
| 577 |
|
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
| 578 |
|
|
my $tkthash = sha512_hex($tktkey);
|
| 579 |
|
|
my $posturl = "https://www.origo.io/irigo/engine.cgi?action=dnsclean";
|
| 580 |
|
|
my $postreq = ();
|
| 581 |
|
|
$postreq->{'engineid'} = $engineid;
|
| 582 |
|
|
$postreq->{'enginetkthash'} = $tkthash;
|
| 583 |
|
|
$postreq->{'domain'} = $dnsdomain;
|
| 584 |
|
|
$content = $browser->post($posturl, $postreq)->content();
|
| 585 |
|
|
$content =~ s/://g;
|
| 586 |
|
|
return $content;
|
| 587 |
|
|
} else {
|
| 588 |
|
|
return "ERROR Engine is not linked!\n";
|
| 589 |
|
|
}
|
| 590 |
|
|
};
|
| 591 |
|
|
|
| 592 |
95b003ff
|
Origo
|
$main::xmppSend = sub {
|
| 593 |
|
|
my ($to, $msg, $engineid, $sysuuid) = @_;
|
| 594 |
|
|
$engineid = `cat /etc/stabile/config.cfg | sed -n -e 's/^ENGINEID=//p'` unless ($engineid);
|
| 595 |
|
|
my $doxmpp = `cat /etc/stabile/config.cfg | sed -n -e 's/^DO_XMPP=//p'`;
|
| 596 |
|
|
if (!$doxmpp) {
|
| 597 |
|
|
return "INFO: DO_XMPP not enabled in config\n";
|
| 598 |
|
|
|
| 599 |
|
|
} elsif ($to && $msg) {
|
| 600 |
|
|
my $xdom;
|
| 601 |
|
|
$xdom = $1 if ($to =~ /\@(.+)$/);
|
| 602 |
|
|
if ($xdom && `host -t SRV _xmpp-server._tcp.$xdom` !~ /NXDOMAIN/) {
|
| 603 |
|
|
require LWP::Simple;
|
| 604 |
|
|
my $browser = LWP::UserAgent->new;
|
| 605 |
|
|
$browser->agent('Stabile/1.0b');
|
| 606 |
|
|
$browser->protocols_allowed( [ 'http','https'] );
|
| 607 |
|
|
$browser->timeout(10);
|
| 608 |
|
|
my $tktcfg = ConfigReader::Simple->new($Stabile::auth_tkt_conf, [qw(TKTAuthSecret)]);
|
| 609 |
|
|
my $tktkey = $tktcfg->get('TKTAuthSecret') || '';
|
| 610 |
|
|
my $tkthash = sha512_hex($tktkey);
|
| 611 |
|
|
my $posturl = "https://www.origo.io/irigo/engine.cgi?action=xmppsend";
|
| 612 |
|
|
|
| 613 |
|
|
my $async = HTTP::Async->new;
|
| 614 |
|
|
my $post = POST $posturl,
|
| 615 |
|
|
[ engineid => $engineid,
|
| 616 |
|
|
enginetkthash => $tkthash,
|
| 617 |
|
|
sysuuid => $sysuuid,
|
| 618 |
|
|
to => $to,
|
| 619 |
|
|
msg => $msg
|
| 620 |
|
|
];
|
| 621 |
|
|
$async->add( $post );
|
| 622 |
|
|
|
| 623 |
|
|
#my $postreq = ();
|
| 624 |
|
|
#$postreq->{'engineid'} = $engineid;
|
| 625 |
|
|
#$postreq->{'enginetkthash'} = $tkthash;
|
| 626 |
|
|
#$postreq->{'to'} = $to;
|
| 627 |
|
|
#$postreq->{'msg'} = $msg;
|
| 628 |
|
|
#$content = $browser->post($posturl, $postreq)->content();
|
| 629 |
|
|
|
| 630 |
|
|
return "Status=OK Sent xmpp message to $to\n";
|
| 631 |
|
|
} else {
|
| 632 |
|
|
return "Status=ERROR XMPP srv records not found for domain \"$xdom\"\n";
|
| 633 |
|
|
}
|
| 634 |
|
|
|
| 635 |
|
|
} else {
|
| 636 |
|
|
return "Status=ERROR Invalid xmpp data $to, $msg\n";
|
| 637 |
|
|
}
|
| 638 |
|
|
};
|
| 639 |
|
|
|
| 640 |
2a63870a
|
Christian Orellana
|
# Enumerate and return network interfaces
|
| 641 |
|
|
$main::getNics = sub {
|
| 642 |
|
|
my $internalnic = $Stabile::config->get('ENGINE_DATA_NIC');
|
| 643 |
|
|
my $externalnic = $Stabile::config->get('EXTERNAL_NIC');
|
| 644 |
|
|
if (!$externalnic) {
|
| 645 |
|
|
my $droute = `ip route show default`;
|
| 646 |
|
|
$externalnic = $1 if ($droute =~ /default via .+ dev (.+) proto/);
|
| 647 |
|
|
}
|
| 648 |
|
|
my @nics = ();
|
| 649 |
|
|
if (!$externalnic || !$internalnic) {
|
| 650 |
|
|
my $niclist = `ifconfig | grep flags= | sed -n -e 's/: .*//p'`;
|
| 651 |
|
|
if (-e "/mnt/stabile/tftp/bionic") { # If a piston root exists, assume we will be providing boot services over secondary NIC even if it has no link
|
| 652 |
|
|
$niclist = `ifconfig -a | grep flags= | sed -n -e 's/: .*//p'`;
|
| 653 |
|
|
}
|
| 654 |
|
|
# my $niclist = `netstat -in`;
|
| 655 |
|
|
push @nics, $externalnic if ($externalnic);
|
| 656 |
|
|
foreach my $line (split("\n", $niclist)) {
|
| 657 |
|
|
if ($line =~ /^(\w+)$/) {
|
| 658 |
|
|
my $nic = $1;
|
| 659 |
|
|
push(@nics, $nic) if ($nic ne 'lo' && $nic ne $externalnic && !($nic=~/^virbr/) && !($nic=~/^docker/) && !($nic=~/^br/) && !($nic=~/^vnet/) && !($nic=~/^Name/) && !($nic=~/^Kernel/) && !($nic=~/^Iface/) && !($nic=~/(\.|\:)/));
|
| 660 |
|
|
}
|
| 661 |
|
|
}
|
| 662 |
|
|
}
|
| 663 |
|
|
$externalnic = $externalnic || $nics[0];
|
| 664 |
|
|
$internalnic = $internalnic || $nics[1] || $externalnic;
|
| 665 |
f4b82f15
|
hq
|
if ($internalnic =~ /^wl/) {
|
| 666 |
|
|
# We're dealing with a wlan interface, we are probably on a laptop, use it as external network
|
| 667 |
|
|
my $tempnic = $internalnic;
|
| 668 |
|
|
$internalnic = $externalnic;
|
| 669 |
|
|
$externalnic = $tempnic;
|
| 670 |
|
|
}
|
| 671 |
2a63870a
|
Christian Orellana
|
return ($internalnic, $externalnic);
|
| 672 |
|
|
};
|
| 673 |
|
|
|
| 674 |
95b003ff
|
Origo
|
$main::updateUI = sub {
|
| 675 |
|
|
my @parslist = @_;
|
| 676 |
|
|
my $newtasks;
|
| 677 |
|
|
my $tab;
|
| 678 |
|
|
my $duser;
|
| 679 |
|
|
foreach my $pars (@parslist) {
|
| 680 |
|
|
my $type = $pars->{type};
|
| 681 |
|
|
my $duuid = $pars->{uuid};
|
| 682 |
|
|
my $domuuid = $pars->{domuuid};
|
| 683 |
|
|
my $dstatus = $pars->{status};
|
| 684 |
|
|
my $message = $pars->{message};
|
| 685 |
48fcda6b
|
Origo
|
$message =~ s/"/\\"/g;
|
| 686 |
|
|
$message =~ s/'/\\'/g;
|
| 687 |
95b003ff
|
Origo
|
my $newpath = $pars->{newpath};
|
| 688 |
|
|
my $displayip = $pars->{displayip};
|
| 689 |
|
|
my $displayport = $pars->{displayport};
|
| 690 |
|
|
my $name = $pars->{name};
|
| 691 |
|
|
my $master = $pars->{master};
|
| 692 |
|
|
my $mac = $pars->{mac};
|
| 693 |
|
|
my $macname = $pars->{macname};
|
| 694 |
|
|
my $progress = $pars->{progress};
|
| 695 |
|
|
my $title = $pars->{title};
|
| 696 |
|
|
my $managementlink = $pars->{managementlink};
|
| 697 |
|
|
my $backup = $pars->{backup};
|
| 698 |
2a63870a
|
Christian Orellana
|
my $download = $pars->{download};
|
| 699 |
|
|
my $size = $pars->{size};
|
| 700 |
95b003ff
|
Origo
|
my $sender = $pars->{sender};
|
| 701 |
|
|
my $path = $pars->{path};
|
| 702 |
|
|
my $snap1 = $pars->{snap1};
|
| 703 |
|
|
my $username = $pars->{username};
|
| 704 |
|
|
|
| 705 |
|
|
$tab = $pars->{tab};
|
| 706 |
|
|
$duser = $pars->{user};
|
| 707 |
|
|
$duser = "irigo" if ($duser eq "--");
|
| 708 |
|
|
$tab = $tab || substr(lc __PACKAGE__, 9);
|
| 709 |
|
|
$type = $type || ($message?'message':'update');
|
| 710 |
|
|
$sender = $sender || "stabile:$package";
|
| 711 |
|
|
|
| 712 |
|
|
if ($package eq 'users' && $pars->{'uuid'}) {
|
| 713 |
|
|
my %u = %{$register{$pars->{'uuid'}}};
|
| 714 |
|
|
delete $u{'password'};
|
| 715 |
|
|
$u{'user'} = $duser;
|
| 716 |
|
|
$u{'type'} = 'update';
|
| 717 |
|
|
$u{'status'} = ($u{'privileges'} =~ /d/)?'disabled':'enabled';
|
| 718 |
|
|
$u{'tab'} = $package;
|
| 719 |
|
|
$u{'timestamp'} = $current_time;
|
| 720 |
|
|
$newtasks .= to_json(\%u) . ", ";
|
| 721 |
|
|
} else {
|
| 722 |
|
|
$newtasks .= "{\"type\":\"$type\",\"tab\":\"$tab\",\"timestamp\":$current_time" .
|
| 723 |
|
|
($duuid?",\"uuid\":\"$duuid\"":"") .
|
| 724 |
|
|
($domuuid?",\"domuuid\":\"$domuuid\"":"") .
|
| 725 |
|
|
($duser?",\"user\":\"$duser\"":"") .
|
| 726 |
|
|
($dstatus?",\"status\":\"$dstatus\"":"") .
|
| 727 |
|
|
($message?",\"message\":\"$message\"":"") .
|
| 728 |
|
|
($newpath?",\"path\":\"$newpath\"":"") .
|
| 729 |
|
|
($displayip?",\"displayip\":\"$displayip\"":"") .
|
| 730 |
|
|
($displayport?",\"displayport\":\"$displayport\"":"") .
|
| 731 |
|
|
($name?",\"name\":\"$name\"":"") .
|
| 732 |
|
|
($backup?",\"backup\":\"$backup\"":"") .
|
| 733 |
2a63870a
|
Christian Orellana
|
($download?",\"download\":\"$download\"":"") .
|
| 734 |
|
|
($size?",\"size\":\"$size\"":"") .
|
| 735 |
95b003ff
|
Origo
|
($mac?",\"mac\":\"$mac\"":"") .
|
| 736 |
|
|
($macname?",\"macname\":\"$macname\"":"") .
|
| 737 |
|
|
($progress?",\"progress\":$progress":"") . # This must be a number between 0 and 100
|
| 738 |
|
|
($title?",\"title\":\"$title\"":"") .
|
| 739 |
|
|
($managementlink?",\"managementlink\":\"$managementlink\"":"") .
|
| 740 |
|
|
($master?",\"master\":\"$master\"":"") .
|
| 741 |
|
|
($snap1?",\"snap1\":\"$snap1\"":"") .
|
| 742 |
|
|
($username?",\"username\":\"$username\"":"") .
|
| 743 |
48fcda6b
|
Origo
|
($path?",\"path\":\"$path\"":"") .
|
| 744 |
|
|
",\"sender\":\"$sender\"}, ";
|
| 745 |
95b003ff
|
Origo
|
}
|
| 746 |
|
|
}
|
| 747 |
|
|
$newtasks = $1 if ($newtasks =~ /(.+)/); #untaint
|
| 748 |
|
|
my $res;
|
| 749 |
|
|
eval {
|
| 750 |
|
|
opendir my($dh), '/tmp' or die "Couldn't open '/tmp': $!";
|
| 751 |
|
|
my @files;
|
| 752 |
|
|
if ($tab eq 'nodes' || $duser eq 'irigo') {
|
| 753 |
|
|
# write tasks to all admin user's session task pipes
|
| 754 |
|
|
@files = grep { /.*~A-.*\.tasks$/ } readdir $dh;
|
| 755 |
|
|
} else {
|
| 756 |
|
|
# write tasks to all the user's session task pipes
|
| 757 |
|
|
@files = grep { /^$duser~.*\.tasks$/ } readdir $dh;
|
| 758 |
|
|
}
|
| 759 |
|
|
closedir $dh;
|
| 760 |
|
|
my $t = new Proc::ProcessTable;
|
| 761 |
|
|
my @ptable = @{$t->table};
|
| 762 |
|
|
my @pfiles;
|
| 763 |
|
|
my $cmnds;
|
| 764 |
|
|
foreach my $f (@files) {
|
| 765 |
|
|
# my $n = `pgrep -fc "$f"`;
|
| 766 |
|
|
# chomp $n;
|
| 767 |
|
|
foreach my $p ( @ptable ){
|
| 768 |
|
|
my $pcmd = $p->cmndline;
|
| 769 |
|
|
$cmnds .= $pcmd . "\n" if ($pcmd =~ /tmp/);
|
| 770 |
|
|
if ($pcmd =~ /\/tmp\/$f/) { # Only include pipes with active listeners
|
| 771 |
|
|
push @pfiles, "/tmp/$f";
|
| 772 |
|
|
last;
|
| 773 |
|
|
}
|
| 774 |
|
|
}
|
| 775 |
|
|
};
|
| 776 |
|
|
my $tasksfiles = join(' ', @pfiles);
|
| 777 |
|
|
$tasksfiles = $1 if ($tasksfiles =~ /(.+)/); #untaint
|
| 778 |
|
|
# Write to users named pipes if user is logged in and session file found
|
| 779 |
|
|
if ($tasksfiles) {
|
| 780 |
|
|
$res = `/bin/echo \'$newtasks\' | /usr/bin/tee $tasksfiles \&`;
|
| 781 |
|
|
} else {
|
| 782 |
|
|
# If session file not found, append to orphan tasks file wait a sec and reload
|
| 783 |
|
|
$res = `/bin/echo \'$newtasks\' >> /tmp/$duser.tasks`;
|
| 784 |
|
|
$res .= `chown www-data:www-data /tmp/$duser.tasks`;
|
| 785 |
|
|
# sleep 1;
|
| 786 |
|
|
eval {`/usr/bin/pkill -HUP -f ui_update`; 1;} or do {;};
|
| 787 |
ca937547
|
hq
|
# `echo "duh: $duser" >> /tmp/duh`;
|
| 788 |
95b003ff
|
Origo
|
}
|
| 789 |
|
|
# eval {`/usr/bin/pkill -HUP -f $duser~ui_update`; 1;} or do {;};
|
| 790 |
|
|
} or do {$e=1; $res .= "ERROR Problem writing to tasks pipe $@\n";};
|
| 791 |
|
|
return 1;
|
| 792 |
|
|
};
|
| 793 |
|
|
|
| 794 |
|
|
sub action {
|
| 795 |
|
|
my ($target, $action, $obj) = @_;
|
| 796 |
|
|
my $res;
|
| 797 |
|
|
my $func = ucfirst $action;
|
| 798 |
|
|
# If a function named $action (with first letter uppercased) exists, call it and return the result
|
| 799 |
|
|
if (defined &{$func}) {
|
| 800 |
|
|
$res .= &{$func}($target, $action, $obj);
|
| 801 |
|
|
}
|
| 802 |
|
|
return $res;
|
| 803 |
|
|
}
|
| 804 |
|
|
|
| 805 |
|
|
sub privileged_action {
|
| 806 |
|
|
my ($target, $action, $obj) = @_;
|
| 807 |
|
|
return "Status=ERROR Your account does not have the necessary privileges\n" if ($isreadonly);
|
| 808 |
|
|
return action($target, $action) if ($help);
|
| 809 |
|
|
my $res;
|
| 810 |
|
|
$obj = {} unless ($obj);
|
| 811 |
|
|
$obj->{'console'} = 1 if ($console || $options{c});
|
| 812 |
2a63870a
|
Christian Orellana
|
$obj->{'baseurl'} = $baseurl if ($baseurl);
|
| 813 |
95b003ff
|
Origo
|
my $client = Gearman::Client->new;
|
| 814 |
|
|
$client->job_servers('127.0.0.1:4730');
|
| 815 |
|
|
# Gearman server will try to call a method named "do_gear_$action"
|
| 816 |
|
|
$res = $client->do_task(steamexec => freeze({package=>$package, tktuser=>$tktuser, user=>$user, target=>$target, action=>$action, args=>$obj}));
|
| 817 |
|
|
$res = ${ $res };
|
| 818 |
|
|
return $res;
|
| 819 |
|
|
}
|
| 820 |
|
|
|
| 821 |
|
|
sub privileged_action_async {
|
| 822 |
|
|
my ($target, $action, $obj) = @_;
|
| 823 |
|
|
return "Status=ERROR Your account does not have the necessary privileges\n" if ($isreadonly);
|
| 824 |
|
|
return action($target, $action) if ($help);
|
| 825 |
|
|
my $client = Gearman::Client->new;
|
| 826 |
|
|
$client->job_servers('127.0.0.1:4730');
|
| 827 |
|
|
my $tasks = $client->new_task_set;
|
| 828 |
|
|
$obj = {} unless ($obj);
|
| 829 |
|
|
$obj->{'console'} = 1 if ($console || $options{c});
|
| 830 |
|
|
# Gearman server will try to call a method named "do_gear_$action"
|
| 831 |
a2e0bc7e
|
hq
|
if (scalar(keys %{$obj}) > 2) {
|
| 832 |
95b003ff
|
Origo
|
my $handle = $tasks->add_task(steamexec => freeze({package=>$package, tktuser=>$tktuser, user=>$user, target=>$target, action=>$action, args=>$obj}));
|
| 833 |
|
|
} else {
|
| 834 |
|
|
my $handle = $tasks->add_task(steamexec => freeze({package=>$package, tktuser=>$tktuser, user=>$user, target=>$target, action=>$action}));
|
| 835 |
|
|
}
|
| 836 |
|
|
my $regtarget = $register{$target};
|
| 837 |
|
|
my $imgregtarget = $imagereg{$target};
|
| 838 |
d24d9a01
|
hq
|
$uistatus = $regtarget->{status} || "$action".'ing';
|
| 839 |
95b003ff
|
Origo
|
$uistatus = 'cloning' if ($action eq 'clone');
|
| 840 |
|
|
$uistatus = 'snapshotting' if ($action eq 'snapshot');
|
| 841 |
|
|
$uistatus = 'unsnapping' if ($action eq 'unsnap');
|
| 842 |
|
|
$uistatus = 'mastering' if ($action eq 'master');
|
| 843 |
|
|
$uistatus = 'unmastering' if ($action eq 'unmaster');
|
| 844 |
|
|
$uistatus = 'backingup' if ($action eq 'backup');
|
| 845 |
|
|
$uistatus = 'restoring' if ($action eq 'restore');
|
| 846 |
|
|
$uistatus = 'saving' if ($action eq 'save');
|
| 847 |
|
|
$uistatus = 'venting' if ($action eq 'releasepressure');
|
| 848 |
04c16f26
|
hq
|
$uistatus = 'injecting' if ($action eq 'inject');
|
| 849 |
95b003ff
|
Origo
|
my $name = $regtarget->{name} || $imgregtarget->{name};
|
| 850 |
|
|
if ($action eq 'save') {
|
| 851 |
|
|
if ($package eq 'images') {
|
| 852 |
|
|
if ($obj->{status} eq 'new') {
|
| 853 |
|
|
$obj->{status} = 'unused';
|
| 854 |
|
|
}
|
| 855 |
|
|
elsif ($obj->{regstoragepool} ne $obj->{storagepool}) {
|
| 856 |
d24d9a01
|
hq
|
$obj->{'status'} = $uistatus = 'moving';
|
| 857 |
95b003ff
|
Origo
|
}
|
| 858 |
|
|
}
|
| 859 |
|
|
$postreply = to_json($obj, {pretty=>1});
|
| 860 |
|
|
$postreply = encode('utf8', $postreply);
|
| 861 |
|
|
$postreply =~ s/""/"--"/g;
|
| 862 |
|
|
$postreply =~ s/null/"--"/g;
|
| 863 |
|
|
$postreply =~ s/"notes" {0,1}: {0,1}"--"/"notes":""/g;
|
| 864 |
|
|
$postreply =~ s/"installable" {0,1}: {0,1}"(true|false)"/"installable":$1/g;
|
| 865 |
|
|
return $postreply;
|
| 866 |
|
|
} else {
|
| 867 |
|
|
return "Status=$uistatus OK $action $name (deferred)\n";
|
| 868 |
|
|
}
|
| 869 |
|
|
}
|
| 870 |
|
|
|
| 871 |
|
|
sub do_gear_action {
|
| 872 |
|
|
my ($target, $action ,$obj) = @_;
|
| 873 |
|
|
$target = encode("iso-8859-1", $target); # MySQL uses Latin1 as default charset
|
| 874 |
|
|
$action = $1 if ($action =~ /gear_(.+)/);
|
| 875 |
|
|
my $res;
|
| 876 |
|
|
return "This only works with elevated privileges\n" if ($>);
|
| 877 |
9d03439e
|
hq
|
if ($register{$target}
|
| 878 |
|
|
|| $action =~ /all$|save|^monitors|^packages|^changemonitoremail|^buildsystem|^removesystem|^updateaccountinfo|^updateengineinfo|^removeusersystems|^removeuserimages/
|
| 879 |
|
|
|| $action =~ /^updateamtinfo|^updatedownloads|^releasepressure|linkmaster$|activate$|engine$|^syncusers|^deletesystem|^getserverbackups|^listserverbackups|^fullstats/
|
| 880 |
14fd7cc5
|
hq
|
|| $action =~ /^zbackup|^updateallbtimes|^initializestorage|^liststoragedevices|^getbackupdevice|^getimagesdevice|^listbackupdevices|^listimagesdevices/
|
| 881 |
a2e0bc7e
|
hq
|
|| $action =~ /^setstoragedevice|^updateui|configurecgroups|backup|sync_backup|^snapshot|^unsnap/
|
| 882 |
95b003ff
|
Origo
|
|| ($action eq 'remove' && $package eq 'images' && $target =~ /\.master\.qcow2$/) # We allow removing master images by name only
|
| 883 |
d3805c61
|
hq
|
|| ($action eq 'remove' && $package eq 'images' && $target =~ /^(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})$/) # We allow removing images by uuid also
|
| 884 |
95b003ff
|
Origo
|
) {
|
| 885 |
|
|
my $func = ucfirst $action;
|
| 886 |
|
|
# If a function named $action (with first letter uppercased) exists, call it and return the result
|
| 887 |
|
|
if (defined &{$func}) {
|
| 888 |
|
|
if ($obj) {
|
| 889 |
|
|
$console = $obj->{'console'} if ($obj->{'console'});
|
| 890 |
|
|
$target = $obj->{uuid} if (!$target && $obj->{uuid}); # backwards compat with apps calling removesystem
|
| 891 |
|
|
$res .= &{$func}($target, $action, $obj);
|
| 892 |
|
|
} else {
|
| 893 |
|
|
$res .= &{$func}($target, $action);
|
| 894 |
|
|
}
|
| 895 |
|
|
} else {
|
| 896 |
|
|
$res .= "Status=ERROR Unable to $action $target - function not found in $package\n";
|
| 897 |
|
|
}
|
| 898 |
|
|
} else {
|
| 899 |
|
|
$res .= "Status=ERROR Unable to $action $target - target not found in $package\n";
|
| 900 |
|
|
}
|
| 901 |
|
|
return $res;
|
| 902 |
|
|
}
|
| 903 |
|
|
|
| 904 |
|
|
sub preInit {
|
| 905 |
|
|
# Set global vars: $user, $tktuser, $curuuid and if applicable: $curdomuuid, $cursysuuid, $curimg
|
| 906 |
|
|
# Identify and validate user, read user prefs from DB
|
| 907 |
48fcda6b
|
Origo
|
unless ( tie(%userreg,'Tie::DBI', Hash::Merge::merge({table=>'users', key=>'username'}, $Stabile::dbopts)) ) {throw Error::Simple("Status=Error User register could not be accessed")};
|
| 908 |
95b003ff
|
Origo
|
|
| 909 |
|
|
$user = $user || $Stabile::user || $ENV{'REMOTE_USER'};
|
| 910 |
|
|
$user = 'irigo' if ($package eq 'steamexec');
|
| 911 |
|
|
$remoteip = $ENV{'REMOTE_ADDR'};
|
| 912 |
|
|
# If request is coming from a running server from an internal ip, identify user requesting access
|
| 913 |
|
|
if (!$user && $remoteip && $remoteip =~ /^10\.\d+\.\d+\.\d+/) {
|
| 914 |
48fcda6b
|
Origo
|
unless ( tie(%networkreg,'Tie::DBI', Hash::Merge::merge({table=>'networks', CLOBBER=>3}, $Stabile::dbopts)) ) {throw Error::Simple("Status=Error Network register could not be accessed")};
|
| 915 |
|
|
unless ( tie(%domreg,'Tie::DBI', Hash::Merge::merge({table=>'domains', CLOBBER=>3}, $Stabile::dbopts)) ) {throw Error::Simple("Status=Error Domain register could not be accessed")};
|
| 916 |
95b003ff
|
Origo
|
my @regkeys = (tied %networkreg)->select_where("internalip = '$remoteip'");
|
| 917 |
|
|
foreach my $k (@regkeys) {
|
| 918 |
|
|
my $network = $networkreg{$k};
|
| 919 |
|
|
my @domregkeys = (tied %domreg)->select_where("networkuuid1 = '$network->{uuid}'");
|
| 920 |
|
|
my $dom = $domreg{$network->{'domains'}} || $domreg{$domregkeys[0]}; # Sometimes domains is lost in network - compensate
|
| 921 |
|
|
# Request is coming from a running server from an internal ip - accept
|
| 922 |
|
|
if ($network->{'internalip'} eq $remoteip) {
|
| 923 |
|
|
$user = $network->{'user'};
|
| 924 |
|
|
# my $dom = $domreg{$network->{'domains'}};
|
| 925 |
|
|
if ($package eq 'networks') {
|
| 926 |
|
|
$curuuid = $network->{'uuid'};
|
| 927 |
|
|
$curdomuuid = $network->{'domains'};
|
| 928 |
|
|
$cursysuuid = $dom->{'system'};
|
| 929 |
|
|
} elsif ($package eq 'images') {
|
| 930 |
|
|
$curimg = $dom->{'image'} unless ($curimg);
|
| 931 |
|
|
} elsif ($package eq 'systems') {
|
| 932 |
|
|
$curuuid = $dom->{'system'} || $dom->{'uuid'} unless ($curuuid);
|
| 933 |
|
|
$cursysuuid = $dom->{'system'};
|
| 934 |
|
|
$curdomuuid = $dom->{'uuid'};
|
| 935 |
|
|
} elsif ($package eq 'servers') {
|
| 936 |
|
|
$curuuid = $dom->{'uuid'} unless ($curuuid);
|
| 937 |
|
|
$cursysuuid = $dom->{'system'};
|
| 938 |
|
|
}
|
| 939 |
|
|
if (!$userreg{$user}->{'allowinternalapi'}) {
|
| 940 |
|
|
$user = ''; # Internal API access is not enabled, disallow access
|
| 941 |
|
|
}
|
| 942 |
|
|
last;
|
| 943 |
|
|
}
|
| 944 |
|
|
}
|
| 945 |
|
|
untie %networkreg;
|
| 946 |
|
|
untie %domreg;
|
| 947 |
705b5366
|
hq
|
} else { # Check authorized referers to mitigate CSRF attacks. If no referer in ENV we let it pass to allow API access.
|
| 948 |
|
|
if (-e "/etc/stabile/basereferers"
|
| 949 |
|
|
&& $ENV{HTTP_REFERER}
|
| 950 |
|
|
) {
|
| 951 |
|
|
my $basereferers = `cat /etc/stabile/basereferers`;
|
| 952 |
|
|
chomp $basereferers;
|
| 953 |
|
|
my @baserefs = split(/\s+/, $basereferers);
|
| 954 |
|
|
my $match = 0;
|
| 955 |
|
|
foreach my $ref (@baserefs) {
|
| 956 |
|
|
if ($ENV{HTTP_REFERER} =~ /$ref/) {
|
| 957 |
|
|
$match = 1;
|
| 958 |
|
|
last;
|
| 959 |
|
|
}
|
| 960 |
|
|
}
|
| 961 |
|
|
$user = '' unless ($match);
|
| 962 |
|
|
}
|
| 963 |
95b003ff
|
Origo
|
}
|
| 964 |
|
|
$user = $1 if $user =~ /(.+)/; #untaint
|
| 965 |
|
|
$tktuser = $user;
|
| 966 |
|
|
$Stabile::tktuser = $tktuser;
|
| 967 |
|
|
|
| 968 |
|
|
# Initalize CGI
|
| 969 |
|
|
$Stabile::q = new CGI;
|
| 970 |
|
|
|
| 971 |
|
|
# Load params
|
| 972 |
|
|
%params = $Stabile::q->Vars;
|
| 973 |
|
|
$uripath = URI::Escape::uri_unescape($ENV{'REQUEST_URI'});
|
| 974 |
|
|
if ($options{s}) {
|
| 975 |
|
|
$account = $options{s};
|
| 976 |
|
|
} else {
|
| 977 |
|
|
$account = $Stabile::q->cookie('steamaccount');
|
| 978 |
|
|
}
|
| 979 |
|
|
$user = 'guest' if (!$user && $params{'action'} eq 'help');
|
| 980 |
|
|
die "No active user. Please authenticate or provide user through REMOTE_USER environment variable." unless ($user);
|
| 981 |
|
|
|
| 982 |
|
|
my $u = $userreg{$user};
|
| 983 |
|
|
my @accounts = split(/,\s*/, $u->{'accounts'}) if ($u->{'accounts'});
|
| 984 |
|
|
my @accountsprivs = split(/,\s*/, $u->{'accountsprivileges'}) if ($u->{'accountsprivileges'});
|
| 985 |
|
|
for my $i (0 .. $#accounts)
|
| 986 |
|
|
{ $ahash{$accounts[$i]} = $accountsprivs[$i] || 'r'; }
|
| 987 |
|
|
|
| 988 |
|
|
$privileges = '';
|
| 989 |
|
|
# User is requesting access to another account - check privs
|
| 990 |
|
|
if ($account && $account ne $user) {
|
| 991 |
|
|
if ($ahash{$account}) {
|
| 992 |
|
|
$user = $account;
|
| 993 |
|
|
$main::account = $account;
|
| 994 |
|
|
# Only allow users whose base account is admin to get admin privs
|
| 995 |
|
|
$ahash{$account} =~ s/a// unless ($userreg{$tktuser}->{'privileges'} =~ /a/);
|
| 996 |
|
|
$privileges = $ahash{$account};
|
| 997 |
|
|
$u = $userreg{$account};
|
| 998 |
|
|
}
|
| 999 |
|
|
}
|
| 1000 |
|
|
|
| 1001 |
|
|
$Stabile::user = $user;
|
| 1002 |
|
|
|
| 1003 |
|
|
$defaultmemoryquota = $Stabile::config->get('MEMORY_QUOTA') + 0;
|
| 1004 |
|
|
$defaultstoragequota = $Stabile::config->get('STORAGE_QUOTA') + 0;
|
| 1005 |
|
|
$defaultnodestoragequota = $Stabile::config->get('NODESTORAGE_QUOTA') + 0;
|
| 1006 |
|
|
$defaultvcpuquota = $Stabile::config->get('VCPU_QUOTA') + 0;
|
| 1007 |
|
|
$defaultexternalipquota = $Stabile::config->get('EXTERNAL_IP_QUOTA') + 0;
|
| 1008 |
|
|
$defaultrxquota = $Stabile::config->get('RX_QUOTA') + 0;
|
| 1009 |
|
|
$defaulttxquota = $Stabile::config->get('TX_QUOTA') + 0;
|
| 1010 |
|
|
|
| 1011 |
|
|
# Read quotas and privileges from db
|
| 1012 |
|
|
$Stabile::userstoragequota = 0+ $u->{'storagequota'} if ($u->{'storagequota'});
|
| 1013 |
|
|
$Stabile::usernodestoragequota = 0+ $u->{'nodestoragequota'} if ($u->{'storagequota'});
|
| 1014 |
a2e0bc7e
|
hq
|
$Stabile::usermemoryquota = 0+ $u->{'memoryquota'} if ($u->{'memoryquota'});
|
| 1015 |
|
|
$Stabile::uservcpuquota = 0+ $u->{'vcpuquota'} if ($u->{'vcpuquota'});
|
| 1016 |
54401133
|
hq
|
$Stabile::userexternalipquota = 0+ $u->{'externalipquota'} if ($u->{'externalipquota'});
|
| 1017 |
|
|
$Stabile::userrxquota = 0+ $u->{'rxquota'} if ( $u->{'rxquota'});
|
| 1018 |
|
|
$Stabile::usertxquota = 0+ $u->{'txquota'} if ($u->{'txquota'});
|
| 1019 |
95b003ff
|
Origo
|
|
| 1020 |
|
|
$billto = $u->{'billto'};
|
| 1021 |
|
|
$Stabile::userprivileges = $u->{'privileges'};
|
| 1022 |
|
|
$privileges = $Stabile::userprivileges if (!$privileges && $Stabile::userprivileges);
|
| 1023 |
|
|
$isadmin = index($privileges,"a")!=-1;
|
| 1024 |
|
|
$ismanager = index($privileges,"m")!=-1;
|
| 1025 |
|
|
$isreadonly = index($privileges,"r")!=-1;
|
| 1026 |
d3805c61
|
hq
|
$Stabile::preserveimagesonremove = index($privileges,"p")!=-1;
|
| 1027 |
95b003ff
|
Origo
|
$fulllist = $options{f} && $isadmin;
|
| 1028 |
|
|
$fullupdate = $options{p} && $isadmin;
|
| 1029 |
|
|
|
| 1030 |
71b897d3
|
hq
|
my $bto = $userreg{$billto};
|
| 1031 |
|
|
my @bdnsdomains = split(/, ?/, $bto->{'dnsdomains'});
|
| 1032 |
|
|
my @udnsdomains = split(/, ?/, $u->{'dnsdomains'});
|
| 1033 |
23748604
|
hq
|
$dnsdomain = '' if ($dnsdomain eq '--'); # TODO - ugly
|
| 1034 |
|
|
$udnsdomains[0] = '' if ($udnsdomains[0] eq '--');
|
| 1035 |
|
|
$bdnsdomains[0] = '' if ($bdnsdomains[0] eq '--');
|
| 1036 |
45cc3024
|
hq
|
$dnsdomain = $udnsdomains[0] || $bdnsdomains[0] || $dnsdomain; # override config value
|
| 1037 |
|
|
|
| 1038 |
|
|
my $bstoreurl = $bto->{'appstoreurl'};
|
| 1039 |
23748604
|
hq
|
$bstoreurl = '' if ($bstoreurl eq '--');
|
| 1040 |
45cc3024
|
hq
|
my $ustoreurl = $u->{'appstoreurl'};
|
| 1041 |
23748604
|
hq
|
$ustoreurl = '' if ($ustoreurl eq '--');
|
| 1042 |
45cc3024
|
hq
|
$appstoreurl = $bstoreurl || $ustoreurl || $appstoreurl; # override config value
|
| 1043 |
71b897d3
|
hq
|
|
| 1044 |
95b003ff
|
Origo
|
$Stabile::sshcmd = $sshcmd;
|
| 1045 |
|
|
$Stabile::disablesnat = $disablesnat;
|
| 1046 |
|
|
$Stabile::privileges = $privileges;
|
| 1047 |
|
|
$Stabile::isadmin = $isadmin;
|
| 1048 |
|
|
|
| 1049 |
|
|
$storagepools = $u->{'storagepools'}; # Prioritized list of users storage pools as numbers, e.g. "0,2,1"
|
| 1050 |
|
|
my $dbuser = $u->{'username'};
|
| 1051 |
|
|
untie %userreg;
|
| 1052 |
|
|
|
| 1053 |
|
|
# If params are passed in URI for a POST og PUT request, we try to parse them out
|
| 1054 |
|
|
if (($ENV{'REQUEST_METHOD'} ne 'GET') && !$isreadonly) {
|
| 1055 |
|
|
$action = $1 if (!$action && $uripath =~ /action=(\w+)/);
|
| 1056 |
|
|
if ($uripath =~ /$package(\.cgi)?\/(.+)$/ && !$isreadonly) {
|
| 1057 |
|
|
my $uuid = $2;
|
| 1058 |
|
|
if (!(%params) && !$curuuid && $uuid =~ /^\?/) {
|
| 1059 |
|
|
%params = split /[=&]/, substr($uuid,1);
|
| 1060 |
|
|
$curuuid = $params{uuid};
|
| 1061 |
|
|
} else {
|
| 1062 |
|
|
$curuuid = $uuid;
|
| 1063 |
|
|
}
|
| 1064 |
|
|
$curuuid = $1 if ($curuuid =~ /\/(.+)/);
|
| 1065 |
|
|
}
|
| 1066 |
|
|
}
|
| 1067 |
|
|
|
| 1068 |
|
|
# Parse out params from g option if called from cmdline
|
| 1069 |
|
|
my $args = $options{g};
|
| 1070 |
|
|
if ($args && !%params) {
|
| 1071 |
|
|
my $obj = from_json( uri_unescape ($args));
|
| 1072 |
d3805c61
|
hq
|
if ( ref($obj) eq 'HASH' && $obj->{items} ) {
|
| 1073 |
|
|
%params = ();
|
| 1074 |
|
|
$params{'POSTDATA'} = $args;
|
| 1075 |
|
|
} elsif (ref($obj) eq 'HASH') {
|
| 1076 |
95b003ff
|
Origo
|
%params = %{$obj};
|
| 1077 |
|
|
} else {
|
| 1078 |
|
|
%params = {};
|
| 1079 |
|
|
$params{'POSTDATA'} = $args;
|
| 1080 |
|
|
}
|
| 1081 |
|
|
$console = $obj->{'console'} if ($obj->{'console'});
|
| 1082 |
|
|
$curuuid = $obj->{uuid} if (!$curuuid && $obj->{uuid}); # backwards compat with apps calling removesystem
|
| 1083 |
|
|
}
|
| 1084 |
|
|
|
| 1085 |
|
|
# Action may be via on command line switch -a
|
| 1086 |
|
|
if (!$action) {
|
| 1087 |
|
|
$action = $options{a};
|
| 1088 |
|
|
if ($action) { # Set a few options if we are called from command line
|
| 1089 |
|
|
$console = 1 unless ($options{v} && !$options{c});
|
| 1090 |
|
|
$Data::Dumper::Varname = $package;
|
| 1091 |
|
|
$Data::Dumper::Pair = ' : ';
|
| 1092 |
|
|
$Data::Dumper::Terse = 1;
|
| 1093 |
|
|
$Data::Dumper::Useqq = 1;
|
| 1094 |
|
|
}
|
| 1095 |
|
|
}
|
| 1096 |
|
|
# Parse out $action - i.e. find out what action is requested
|
| 1097 |
|
|
$action = $action || $params{'action'}; # $action may have been set above to 'remove' by DELETE request
|
| 1098 |
|
|
|
| 1099 |
|
|
# Handling of action given as part of addressable API
|
| 1100 |
|
|
# Special cases for systems, monitors, etc.
|
| 1101 |
|
|
if (!$action && $uripath =~ /$package\/(.+)(\/|\?)/ && !$params{'path'}) {
|
| 1102 |
|
|
$action = $1;
|
| 1103 |
|
|
$action = $1 if ($action =~ /([^\/]+)\/(.*)/);
|
| 1104 |
|
|
}
|
| 1105 |
|
|
$curuuid = $curuuid || $params{'uuid'} || $params{'id'} || $params{'system'} || $params{'serveruuid'};
|
| 1106 |
|
|
# Handling of target given as part of addressable API
|
| 1107 |
|
|
# if ($uripath =~ /$package(\.cgi)?\/($action\/)?(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})(:\w+)?/) {
|
| 1108 |
|
|
if ($uripath =~ /$package\/(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})(:\w+)?/) {
|
| 1109 |
|
|
$curuuid = "$1$2";
|
| 1110 |
|
|
} elsif ($package eq 'nodes' && $uripath =~ /$package\/(\w{12})(:\w+)?/) {
|
| 1111 |
|
|
$curuuid = "$1$2";
|
| 1112 |
|
|
}
|
| 1113 |
|
|
|
| 1114 |
|
|
$action = lc $action;
|
| 1115 |
|
|
if (!$params && $options{k}) {
|
| 1116 |
|
|
$params{'keywords'} = URI::Escape::uri_unescape($options{k});
|
| 1117 |
|
|
$console = 1 unless ($options{v} && !$options{c});
|
| 1118 |
|
|
}
|
| 1119 |
d3d1a2d4
|
Origo
|
$action = (($action)?$action.'_':'') . 'remove' if ($ENV{'REQUEST_METHOD'} eq 'DELETE' && $action ne 'remove');
|
| 1120 |
95b003ff
|
Origo
|
# -f should only set $fulllisting and not trigger any keyword actions
|
| 1121 |
|
|
delete $params{'keywords'} if ($params{'keywords'} eq '-f');
|
| 1122 |
|
|
|
| 1123 |
|
|
# Regular read - we send out JSON version of directory list
|
| 1124 |
|
|
if (!$action && (!$ENV{'REQUEST_METHOD'} || $ENV{'REQUEST_METHOD'} eq 'GET')) {
|
| 1125 |
|
|
if (!($package)) {
|
| 1126 |
|
|
; # If we get called as a library this is were we end - do nothing...
|
| 1127 |
|
|
} elsif ($params{'keywords'}) {
|
| 1128 |
|
|
; # If param keywords is provided treat as a post
|
| 1129 |
|
|
} else {
|
| 1130 |
|
|
$action = 'list';
|
| 1131 |
|
|
}
|
| 1132 |
|
|
}
|
| 1133 |
|
|
|
| 1134 |
|
|
### Main security check
|
| 1135 |
|
|
unless ($package eq 'pressurecontrol' || $dbuser || ($user eq 'common' && $action =~ /^updatebtime|^list/)) {throw Error::Simple("Status=Error $action: Unknown user $user [$remoteip]")};
|
| 1136 |
|
|
if (index($privileges,"d")!=-1 && $action ne 'help') {throw Error::Simple("Status=Error Disabled user")};
|
| 1137 |
|
|
|
| 1138 |
|
|
$curuuid = $curuuid || URI::Escape::uri_unescape($params{'uuid'}); # $curuuid may have been set above for DELETE requests
|
| 1139 |
|
|
$curuuid = "" if ($curuuid eq "--");
|
| 1140 |
|
|
$curuuid = $options{u} unless $curuuid;
|
| 1141 |
|
|
if ($package eq 'images') {
|
| 1142 |
|
|
$curimg = URI::Escape::uri_unescape($params{'image'} || $params{'path'}) unless ($action eq 'listfiles');
|
| 1143 |
|
|
$curimg = "" if ($curimg eq "--");
|
| 1144 |
|
|
$curimg = $1 if ($curimg =~ /(.*)\*$/); # Handle Dojo peculiarity
|
| 1145 |
|
|
$curimg = URI::Escape::uri_unescape($options{i}) unless $curimg;
|
| 1146 |
|
|
unless (tie(%imagereg,'Tie::DBI', Hash::Merge::merge({table=>'images', CLOBBER=>1}, $Stabile::dbopts)) ) {throw Error::Simple("Stroke=Error Image UUID register could not be accessed")};
|
| 1147 |
|
|
if ($curimg && !$curuuid && $curimg =~ /(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})/) {
|
| 1148 |
|
|
$curuuid = $curimg;
|
| 1149 |
|
|
$curimg = $imagereg{$curuuid}->{'path'} if ($imagereg{$curuuid});
|
| 1150 |
|
|
# } elsif ($target && !$curimg && !$curuuid) {
|
| 1151 |
|
|
# if ($target =~ /(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})/) {
|
| 1152 |
|
|
# $curuuid = $1;
|
| 1153 |
|
|
# $curimg = $imagereg{$curuuid}->{'path'};
|
| 1154 |
|
|
# } else {
|
| 1155 |
|
|
# $curimg = $target;
|
| 1156 |
|
|
# }
|
| 1157 |
|
|
} elsif (!$curimg && $curuuid) {
|
| 1158 |
|
|
$curimg = $imagereg{$curuuid}->{'path'} if ($imagereg{$curuuid});
|
| 1159 |
|
|
}
|
| 1160 |
|
|
untie %imagereg;
|
| 1161 |
|
|
}
|
| 1162 |
|
|
}
|
| 1163 |
|
|
|
| 1164 |
|
|
sub process {
|
| 1165 |
|
|
my $target = $params{'target'} || $options{t} || $curuuid;
|
| 1166 |
|
|
# We may receive utf8 strings either from browser or command line - convert them to native Perl to avoid double encodings
|
| 1167 |
|
|
utf8::decode($target) if ( $target =~ /[^\x00-\x7f]/ );# true if string contains any non-ascii character
|
| 1168 |
|
|
my $uipath;
|
| 1169 |
d24d9a01
|
hq
|
# my $uistatus;
|
| 1170 |
95b003ff
|
Origo
|
# Special handling
|
| 1171 |
|
|
if ($package eq 'images') {
|
| 1172 |
|
|
$target = $curimg || $params{'path'} || $params{'image'} || $target unless ($target =~ /^\/.+/);
|
| 1173 |
|
|
$params{'restorepath'} = $params{'path'} if ($action eq 'listfiles');
|
| 1174 |
2a63870a
|
Christian Orellana
|
$params{'baseurl'} = "https://$ENV{'HTTP_HOST'}/stabile" if ($action eq 'download' && $ENV{'HTTP_HOST'} && !($baseurl =~ /\./)); # send baseurl if configured value not valid
|
| 1175 |
95b003ff
|
Origo
|
} elsif ($package eq 'systems') {
|
| 1176 |
|
|
$target = $params{'id'} || $target if ($action =~ /^monitors_/);
|
| 1177 |
|
|
} elsif ($package eq 'nodes') {
|
| 1178 |
|
|
$target = $target || $params{'mac'};
|
| 1179 |
|
|
} elsif ($package eq 'users') {
|
| 1180 |
|
|
$target = $target || $params{'username'};
|
| 1181 |
|
|
}
|
| 1182 |
|
|
# Named action - we got a request for an action
|
| 1183 |
|
|
my $obj;
|
| 1184 |
d3805c61
|
hq
|
if ($action && (defined &{"do_$action"}) &&
|
| 1185 |
|
|
($ENV{'REQUEST_METHOD'} ne 'POST' || $action eq 'upload' || $action eq 'restorefiles')
|
| 1186 |
|
|
&& !($params{"keywords"} || $params{"POSTDATA"})
|
| 1187 |
|
|
) {
|
| 1188 |
95b003ff
|
Origo
|
# If a function named do_$action (only lowercase allowed) exists, call it and print the result
|
| 1189 |
|
|
if ($action =~ /^monitors/) {
|
| 1190 |
|
|
if ($params{'PUTDATA'}) {
|
| 1191 |
|
|
$obj = $params{'PUTDATA'};
|
| 1192 |
|
|
$action = 'monitors_save' unless ($action =~ /monitors_.+/);
|
| 1193 |
|
|
} else {
|
| 1194 |
|
|
$obj = { action => $action, id => $target };
|
| 1195 |
|
|
}
|
| 1196 |
|
|
} else {
|
| 1197 |
|
|
unless (%params) {
|
| 1198 |
|
|
if ($package eq 'images' && $target =~ /^\//) {
|
| 1199 |
|
|
%params = ("path", $target);
|
| 1200 |
|
|
delete $params{"uuid"};
|
| 1201 |
|
|
} else{
|
| 1202 |
|
|
%params = ("uuid", $target);
|
| 1203 |
|
|
}
|
| 1204 |
|
|
}
|
| 1205 |
|
|
if ($curuuid || $target) {
|
| 1206 |
|
|
$params{uuid} = $curuuid || $target unless ($params{uuid} || $params{path} || ($params{image} && $package eq 'images'));
|
| 1207 |
|
|
}
|
| 1208 |
|
|
$obj = getObj(\%params);
|
| 1209 |
|
|
}
|
| 1210 |
|
|
$obj->{'console'} = $console if ($console);
|
| 1211 |
2a63870a
|
Christian Orellana
|
$obj->{'baseurl'} = $params{baseurl} if ($params{baseurl});
|
| 1212 |
95b003ff
|
Origo
|
# Perform the action
|
| 1213 |
|
|
$postreply = &{"do_$action"}($target, $action, $obj);
|
| 1214 |
|
|
if (!$postreply) { # We expect some kind of reply
|
| 1215 |
6fdc8676
|
hq
|
$postreply .= header('text/plain', '500 Internal Server Error because no reply') unless ($console);
|
| 1216 |
95b003ff
|
Origo
|
$main::syslogit->($user, 'info', "Could not $action $target ($package)") unless ($action eq 'uuidlookup');
|
| 1217 |
|
|
} elsif (! ($postreply =~ /^(Content-type|Status|Location):/i) ) {
|
| 1218 |
|
|
if ($postreply =~ /Content-type:/) {
|
| 1219 |
|
|
;
|
| 1220 |
|
|
} elsif (!$postreply || $postreply =~ /Status=/ || $postreply =~ /^</ || $postreply =~ /^\w/) {
|
| 1221 |
|
|
$postreply = header('text/plain; charset=UTF8') . $postreply unless ($console);
|
| 1222 |
|
|
} else {
|
| 1223 |
|
|
$postreply = header('application/json; charset=UTF8') . $postreply unless ($console);
|
| 1224 |
|
|
}
|
| 1225 |
|
|
}
|
| 1226 |
|
|
print "$postreply";
|
| 1227 |
|
|
|
| 1228 |
|
|
} elsif (($params{'PUTDATA'} || $params{"keywords"} || $params{"POSTDATA"}) && !$isreadonly) {
|
| 1229 |
|
|
# We got a save post with JSON. Look for interesting stuff and perform action or save
|
| 1230 |
2a63870a
|
Christian Orellana
|
my @json_array;
|
| 1231 |
95b003ff
|
Origo
|
if ($params{'PUTDATA'}) {
|
| 1232 |
|
|
my $json_text = $params{'PUTDATA'};
|
| 1233 |
|
|
utf8::decode($json_text);
|
| 1234 |
|
|
$json_text =~ s/\x/ /g;
|
| 1235 |
|
|
$json_text =~ s/\[\]/\"\"/g;
|
| 1236 |
|
|
@json_array = from_json($json_text);
|
| 1237 |
|
|
} elsif ($params{"keywords"} || $params{"POSTDATA"}) {
|
| 1238 |
|
|
my $json_text = $params{"keywords"} || $params{'POSTDATA'};
|
| 1239 |
|
|
$json_text = uri_unescape($json_text);
|
| 1240 |
|
|
utf8::decode($json_text);
|
| 1241 |
|
|
$json_text =~ s/\x/ /g;
|
| 1242 |
|
|
$json_text =~ s/\[\]/\"\"/g;
|
| 1243 |
|
|
my $json_obj = from_json($json_text);
|
| 1244 |
|
|
if (ref $json_obj eq 'ARRAY') {
|
| 1245 |
|
|
@json_array = @$json_obj;
|
| 1246 |
|
|
} elsif (ref $json_obj eq 'HASH') {
|
| 1247 |
|
|
my %json_hash = %$json_obj;
|
| 1248 |
|
|
my $json_array_ref = [\%json_hash];
|
| 1249 |
|
|
if ($json_hash{"items"}) {
|
| 1250 |
|
|
$json_array_ref = $json_hash{"items"};
|
| 1251 |
|
|
}
|
| 1252 |
|
|
@json_array = @$json_array_ref;
|
| 1253 |
|
|
}
|
| 1254 |
|
|
}
|
| 1255 |
|
|
foreach (@json_array) {
|
| 1256 |
|
|
my %h = %$_;
|
| 1257 |
|
|
$console = 1 if $h{"console"};
|
| 1258 |
|
|
my $objaction = $h{'action'} || $action;
|
| 1259 |
|
|
$objaction = 'save' if (!$objaction || $objaction eq "--");
|
| 1260 |
|
|
$h{'action'} = $objaction = $action.'_'.$objaction if ($action eq "monitors" || $action eq "packages"); # Allow sending e.g. disable action to monitors by calling monitors_disable
|
| 1261 |
2a63870a
|
Christian Orellana
|
$h{'action'} = $objaction if ($objaction && !$h{'action'});
|
| 1262 |
95b003ff
|
Origo
|
my $obj = getObj(\%h);
|
| 1263 |
|
|
next unless $obj;
|
| 1264 |
|
|
$obj->{'console'} = $console if ($console);
|
| 1265 |
|
|
# Now build the requested action
|
| 1266 |
|
|
my $objfunc = "do_$objaction";
|
| 1267 |
|
|
# If a function named objfunc exists, call it
|
| 1268 |
|
|
if (defined &$objfunc) {
|
| 1269 |
|
|
$target = $h{'uuid'} || $h{'id'};
|
| 1270 |
|
|
$uiuuid = $target;
|
| 1271 |
|
|
my $targetimg = $imagereg{$target};
|
| 1272 |
|
|
# Special handling
|
| 1273 |
|
|
if ($package eq 'images') {
|
| 1274 |
|
|
$target = $targetimg->{'path'} || $h{'image'} || $h{'path'} || $target;
|
| 1275 |
|
|
}
|
| 1276 |
|
|
# Perform the action
|
| 1277 |
a2e0bc7e
|
hq
|
my $areply = &{$objfunc}($target, $objaction, $obj);
|
| 1278 |
|
|
$postreply .= $areply unless ($postreply eq $areply); # $postreply has been set
|
| 1279 |
|
|
|
| 1280 |
95b003ff
|
Origo
|
# Special handling
|
| 1281 |
|
|
if ($package eq 'images') {
|
| 1282 |
|
|
if ($h{'status'} eq 'new') {
|
| 1283 |
|
|
# $uistatus = 'new';
|
| 1284 |
|
|
# $uiuuid = ''; # Refresh entire view
|
| 1285 |
|
|
}
|
| 1286 |
|
|
}
|
| 1287 |
|
|
my $node = $nodereg{$mac};
|
| 1288 |
|
|
my $updateEntry = {
|
| 1289 |
|
|
tab=>$tab,
|
| 1290 |
|
|
user=>$user,
|
| 1291 |
|
|
uuid=>$uiuuid,
|
| 1292 |
|
|
status=>$uistatus,
|
| 1293 |
|
|
mac=>$mac,
|
| 1294 |
|
|
macname=>$node->{'name'},
|
| 1295 |
|
|
displayip=>$uidisplayip,
|
| 1296 |
|
|
displayport=>$uidisplayport,
|
| 1297 |
|
|
type=>$uiupdatetype,
|
| 1298 |
|
|
message=>$postmsg
|
| 1299 |
|
|
};
|
| 1300 |
|
|
# Special handling
|
| 1301 |
|
|
if ($package eq 'images') {
|
| 1302 |
|
|
$obj->{'uuid'} = '' if ($uistatus eq 'new');
|
| 1303 |
|
|
$uipath = $obj->{'path'};
|
| 1304 |
|
|
$updateEntry->{'path'} = $uipath;
|
| 1305 |
|
|
$uiname = $obj->{'name'};
|
| 1306 |
|
|
}
|
| 1307 |
|
|
if ($uiname) {
|
| 1308 |
|
|
$updateEntry->{'name'} = $uiname;
|
| 1309 |
|
|
}
|
| 1310 |
|
|
if ($uiuuid || $postmsg || $uistatus) {
|
| 1311 |
|
|
push (@updateList, $updateEntry);
|
| 1312 |
|
|
}
|
| 1313 |
|
|
} else {
|
| 1314 |
|
|
$postreply .= "Status=ERROR Unknown $package action: $objaction\n";
|
| 1315 |
|
|
}
|
| 1316 |
|
|
}
|
| 1317 |
a2e0bc7e
|
hq
|
if ($postreply && ! ($postreply =~ /^(Content-type|Status|Location):/i) ) {
|
| 1318 |
95b003ff
|
Origo
|
if (!$postreply || $postreply =~ /Status=/) {
|
| 1319 |
|
|
$postreply = header('text/plain; charset=UTF8') . $postreply unless ($console);
|
| 1320 |
|
|
} else {
|
| 1321 |
|
|
$postreply = header('application/json; charset=UTF8') . $postreply unless ($console);
|
| 1322 |
|
|
}
|
| 1323 |
|
|
}
|
| 1324 |
|
|
print $postreply;
|
| 1325 |
|
|
} else {
|
| 1326 |
|
|
$postreply .= "Status=Error Unknown $ENV{'REQUEST_METHOD'} $package action: $action\n";
|
| 1327 |
|
|
print header('text/html', '500 Internal Server Error') unless ($console);
|
| 1328 |
|
|
print $postreply;
|
| 1329 |
|
|
}
|
| 1330 |
|
|
# Functions called via aliases to privileged_action or privileged_action_async cannot update $postmsg or $uistatus
|
| 1331 |
|
|
# so updateUI must be called internally in these functions.
|
| 1332 |
|
|
if (@updateList) {
|
| 1333 |
|
|
$main::updateUI->(@updateList);
|
| 1334 |
|
|
}
|
| 1335 |
|
|
}
|
| 1336 |
|
|
|
| 1337 |
|
|
|
| 1338 |
|
|
# Print list of available actions
|
| 1339 |
|
|
sub Help {
|
| 1340 |
|
|
$help = 1;
|
| 1341 |
|
|
no strict 'refs';
|
| 1342 |
|
|
my %fdescriptions;
|
| 1343 |
|
|
my %fmethods;
|
| 1344 |
|
|
my %fparams;
|
| 1345 |
|
|
my @fnames;
|
| 1346 |
|
|
|
| 1347 |
|
|
my $res = header() unless ($console);
|
| 1348 |
|
|
# my $tempuuid = "484d7852-90d2-43f1-8bd6-e29e234848b0";
|
| 1349 |
|
|
my $tempuuid = "";
|
| 1350 |
|
|
unless ($console) {
|
| 1351 |
|
|
$res .= <<END
|
| 1352 |
|
|
<!DOCTYPE html>
|
| 1353 |
|
|
<html>
|
| 1354 |
|
|
<head>
|
| 1355 |
|
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
|
| 1356 |
|
|
<!-- script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script -->
|
| 1357 |
|
|
<!-- script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script -->
|
| 1358 |
|
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
|
| 1359 |
|
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
|
| 1360 |
|
|
<style>
|
| 1361 |
|
|
.form-control {display: inline-block; width: auto; margin: 2px; }
|
| 1362 |
|
|
input.form-control {width: 180px;}
|
| 1363 |
|
|
pre {
|
| 1364 |
|
|
overflow-x: auto;
|
| 1365 |
|
|
white-space: pre-wrap;
|
| 1366 |
|
|
white-space: -moz-pre-wrap;
|
| 1367 |
|
|
white-space: -pre-wrap;
|
| 1368 |
|
|
white-space: -o-pre-wrap;
|
| 1369 |
|
|
word-wrap: break-word;
|
| 1370 |
|
|
}
|
| 1371 |
|
|
</style>
|
| 1372 |
|
|
</head>
|
| 1373 |
|
|
<body style="margin:1.25rem;">
|
| 1374 |
|
|
<div>
|
| 1375 |
|
|
<table style="width:100%;"><tr><td>
|
| 1376 |
|
|
<select class="form-control" id="scopeaction" name="scopeaction" placeholder="action" onchange="data.scopeaction=this.value; dofields();" autocomplete="off"></select>
|
| 1377 |
|
|
<span id="scopeinputs">
|
| 1378 |
|
|
<input class="form-control" id="scopeuuid" name="scopeuuid" placeholder="uuid" onchange="data.scopedata.uuid=this.value; update();" value="$tempuuid" autocomplete="off" size="34">
|
| 1379 |
|
|
</span>
|
| 1380 |
|
|
<button class="btn btn-primary" href="#" onclick="doit();">Try it</button>
|
| 1381 |
|
|
<pre>
|
| 1382 |
|
|
\$.ajax({
|
| 1383 |
|
|
url: "<span class='scopeurl'>/stabile/$package?uuid=$tempuuid&action=activate</span>",
|
| 1384 |
|
|
type: "<span class='scopemethod'>GET</span>", <span id="dataspan" style="display:none;"><br /> data: "<span class="scopedata"></span>",</span>
|
| 1385 |
|
|
success: function(result) {\$("#scoperesult").text(result);}
|
| 1386 |
|
|
});
|
| 1387 |
|
|
</pre>
|
| 1388 |
|
|
</td><td width="50%"><textarea id="scoperesult" style="width:100%; height: 200px;"></textarea></td>
|
| 1389 |
|
|
</tr>
|
| 1390 |
|
|
</table>
|
| 1391 |
|
|
</div>
|
| 1392 |
|
|
<script>
|
| 1393 |
|
|
data = {"scopemethod": "GET", "scopeaction": "activate", "scopeuuid": "$tempuuid", "scopeurl": "/stabile/$package?uuid=$tempuuid&action=activate"};
|
| 1394 |
|
|
function doit() {
|
| 1395 |
|
|
var obj = {
|
| 1396 |
|
|
url: data.scopeurl,
|
| 1397 |
|
|
type: data.scopemethod,
|
| 1398 |
|
|
success: handleResult,
|
| 1399 |
|
|
error: handleResult
|
| 1400 |
|
|
}
|
| 1401 |
|
|
if (data.scopemethod != 'GET') obj.data = JSON.stringify(data.scopedata);
|
| 1402 |
|
|
\$.ajax(obj);
|
| 1403 |
27512919
|
Origo
|
\$("#scoperesult").text("");
|
| 1404 |
95b003ff
|
Origo
|
return true;
|
| 1405 |
|
|
function handleResult(data, textStatus, jqXHR) {
|
| 1406 |
|
|
if (jqXHR == 'Unauthorized') {
|
| 1407 |
|
|
\$("#scoperesult").text(jqXHR + ": You must log in before you can call API methods.");
|
| 1408 |
|
|
} else if (jqXHR.responseText) {
|
| 1409 |
|
|
\$("#scoperesult").text(jqXHR.responseText);
|
| 1410 |
|
|
} else {
|
| 1411 |
|
|
\$("#scoperesult").text("No result received");
|
| 1412 |
|
|
}
|
| 1413 |
|
|
}
|
| 1414 |
|
|
}
|
| 1415 |
|
|
function dofields() {
|
| 1416 |
|
|
if (scopeparams[data.scopeaction].length==0) {
|
| 1417 |
|
|
\$("#scopeinputs").hide();
|
| 1418 |
|
|
} else {
|
| 1419 |
|
|
var fields = "";
|
| 1420 |
|
|
\$.each(scopeparams[data.scopeaction], function (i, item) {
|
| 1421 |
|
|
var itemname = "scope" + item;
|
| 1422 |
|
|
if (\$("#"+itemname).val()) data[itemname] = \$("#"+itemname).val();
|
| 1423 |
|
|
fields += '<input class="form-control" id="' + itemname + '" placeholder="' + item + '" value="' + ((data[itemname])?data[itemname]:'') + '" size="34" onchange="update();"> ';
|
| 1424 |
|
|
});
|
| 1425 |
|
|
\$("#scopeinputs").empty();
|
| 1426 |
|
|
\$("#scopeinputs").append(fields);
|
| 1427 |
|
|
\$("#scopeinputs").show();
|
| 1428 |
|
|
}
|
| 1429 |
|
|
update();
|
| 1430 |
|
|
}
|
| 1431 |
|
|
function update() {
|
| 1432 |
|
|
data.scopemethod = scopemethods[data.scopeaction];
|
| 1433 |
|
|
if (data.scopemethod == "POST") {
|
| 1434 |
|
|
\$("#dataspan").show();
|
| 1435 |
|
|
data.scopeurl = "/stabile/$package";
|
| 1436 |
|
|
data.scopedata = {"items": [{"action":data.scopeaction}]};
|
| 1437 |
|
|
\$.each(scopeparams[data.scopeaction], function (i, item) {
|
| 1438 |
|
|
var val = \$("#scope"+item).val();
|
| 1439 |
|
|
if (val) data.scopedata.items[0][item] = val;
|
| 1440 |
|
|
});
|
| 1441 |
|
|
} else if (data.scopemethod == "PUT") {
|
| 1442 |
|
|
\$("#dataspan").show();
|
| 1443 |
|
|
data.scopeurl = "/stabile/$package";
|
| 1444 |
|
|
data.scopedata = [{"action":data.scopeaction}];
|
| 1445 |
|
|
\$.each(scopeparams[data.scopeaction], function (i, item) {
|
| 1446 |
|
|
var val = \$("#scope"+item).val();
|
| 1447 |
|
|
if (val) data.scopedata[0][item] = val;
|
| 1448 |
|
|
});
|
| 1449 |
|
|
} else {
|
| 1450 |
|
|
\$("#dataspan").hide();
|
| 1451 |
|
|
data.scopeurl = "/stabile/$package?action="+data.scopeaction;
|
| 1452 |
|
|
\$.each(scopeparams[data.scopeaction], function (i, item) {
|
| 1453 |
|
|
var val = \$("#scope"+item).val();
|
| 1454 |
|
|
if (val) data.scopeurl += "&" + item + "=" + val;
|
| 1455 |
|
|
});
|
| 1456 |
|
|
data.scopedata = '';
|
| 1457 |
|
|
}
|
| 1458 |
|
|
\$(".scopemethod").text(data.scopemethod);
|
| 1459 |
|
|
\$(".scopeurl").text(data.scopeurl);
|
| 1460 |
|
|
\$(".scopedata").text(JSON.stringify(data.scopedata, null, ' ').replace(/\\n/g,'').replace(/ /g,''));
|
| 1461 |
|
|
}
|
| 1462 |
|
|
\$( document ).ready(function() {
|
| 1463 |
|
|
data.scopeaction=\$("#scopeaction").val(); dofields()
|
| 1464 |
|
|
});
|
| 1465 |
|
|
END
|
| 1466 |
|
|
;
|
| 1467 |
|
|
$res .= qq|var scopeparams = {};\n|;
|
| 1468 |
|
|
$res .= qq|var scopemethods = {};\n|;
|
| 1469 |
|
|
$res .= qq|var package="$package"\n|;
|
| 1470 |
|
|
}
|
| 1471 |
|
|
my @entries;
|
| 1472 |
|
|
if ($package eq 'networks') {
|
| 1473 |
|
|
@entries = sort keys %Stabile::Networks::;
|
| 1474 |
|
|
} elsif ($package eq 'images') {
|
| 1475 |
|
|
@entries = sort keys %Stabile::Images::;
|
| 1476 |
|
|
} elsif ($package eq 'servers') {
|
| 1477 |
|
|
@entries = sort keys %Stabile::Servers::;
|
| 1478 |
|
|
} elsif ($package eq 'nodes') {
|
| 1479 |
|
|
@entries = sort keys %Stabile::Nodes::;
|
| 1480 |
|
|
} elsif ($package eq 'users') {
|
| 1481 |
|
|
@entries = sort keys %Stabile::Users::;
|
| 1482 |
|
|
} elsif ($package eq 'systems') {
|
| 1483 |
|
|
@entries = sort keys %Stabile::Systems::;
|
| 1484 |
|
|
}
|
| 1485 |
|
|
|
| 1486 |
|
|
foreach my $entry (@entries) {
|
| 1487 |
|
|
if (defined &{"$entry"} && $entry !~ /help/i && $entry =~ /^do_(.+)/) {
|
| 1488 |
|
|
my $fname = $1;
|
| 1489 |
|
|
# Ask function for help - $help is on
|
| 1490 |
|
|
my $helptext = &{"$entry"}(0, $fname);
|
| 1491 |
|
|
my @helplist = split(":", $helptext, 3);
|
| 1492 |
|
|
chomp $helptext;
|
| 1493 |
|
|
unless ($fname =~ /^gear_/) {
|
| 1494 |
|
|
$fmethods{$fname} = $helplist[0];
|
| 1495 |
|
|
$fparams{$fname} = $helplist[1];
|
| 1496 |
|
|
$fdescriptions{$fname} = $helplist[2];
|
| 1497 |
|
|
$fdescriptions{$fname} =~ s/\n// unless ($console);
|
| 1498 |
|
|
$fdescriptions{$fname} =~ s/\n/\n<br>/g unless ($console);
|
| 1499 |
|
|
my @plist = split(/, ?/, $fparams{$fname});
|
| 1500 |
|
|
unless ($console) {
|
| 1501 |
|
|
$res .= qq|scopeparams["$fname"] = |.to_json(\@plist).";\n";
|
| 1502 |
|
|
$res .= qq|\$("#scopeaction").append(new Option("$fname", "$fname"));\n|;
|
| 1503 |
|
|
$res .= qq|scopemethods["$fname"] = "$helplist[0]";\n|;
|
| 1504 |
|
|
}
|
| 1505 |
|
|
}
|
| 1506 |
|
|
}
|
| 1507 |
|
|
}
|
| 1508 |
|
|
@fnames = sort (keys %fdescriptions);
|
| 1509 |
|
|
|
| 1510 |
|
|
unless ($console) {
|
| 1511 |
|
|
$res .= "\n</script>\n";
|
| 1512 |
|
|
$res .= <<END
|
| 1513 |
|
|
<div class="table-responsive" style="margin-top:1.5rem; noheight: 65vh; overflow-y: scroll;">
|
| 1514 |
|
|
<table class="table table-striped table-sm">
|
| 1515 |
|
|
<thead>
|
| 1516 |
|
|
<tr>
|
| 1517 |
|
|
<th>Name</th>
|
| 1518 |
|
|
<th>Method</th>
|
| 1519 |
|
|
<th>Parameters</th>
|
| 1520 |
|
|
<th style="width:60%;">Description</th>
|
| 1521 |
|
|
</tr>
|
| 1522 |
|
|
</thead>
|
| 1523 |
|
|
<tbody>
|
| 1524 |
|
|
END
|
| 1525 |
|
|
;
|
| 1526 |
|
|
foreach my $fname (@fnames) {
|
| 1527 |
|
|
my $fp = ($fparams{$fname}) ? "$fparams{$fname}" : '';
|
| 1528 |
|
|
$res .= <<END
|
| 1529 |
|
|
<tr>
|
| 1530 |
|
|
<td><a href="#" onclick="data.scopeaction=this.text; \$('#scopeaction option[value=$fname]').prop('selected', true); dofields();">$fname</a></td>
|
| 1531 |
|
|
<td>$fmethods{$fname}</td>
|
| 1532 |
|
|
<td>$fp</td>
|
| 1533 |
|
|
<td>$fdescriptions{$fname}</td>
|
| 1534 |
|
|
</tr>
|
| 1535 |
|
|
END
|
| 1536 |
|
|
;
|
| 1537 |
|
|
}
|
| 1538 |
|
|
$res .= <<END
|
| 1539 |
|
|
</tbody>
|
| 1540 |
|
|
</table>
|
| 1541 |
|
|
</div>
|
| 1542 |
|
|
END
|
| 1543 |
|
|
;
|
| 1544 |
|
|
$res .= qq|</body>\n</html>|;
|
| 1545 |
|
|
} else {
|
| 1546 |
|
|
foreach my $fname (@fnames) {
|
| 1547 |
|
|
my $fp = ($fparams{$fname}) ? "[$fparams{$fname}]" : '';
|
| 1548 |
|
|
$res .= <<END
|
| 1549 |
|
|
* $fname ($fmethods{$fname}) $fp $fdescriptions{$fname}
|
| 1550 |
|
|
END
|
| 1551 |
|
|
;
|
| 1552 |
|
|
}
|
| 1553 |
|
|
}
|
| 1554 |
|
|
|
| 1555 |
|
|
return $res;
|
| 1556 |
|
|
}
|
| 1557 |
|
|
|
| 1558 |
8d7785ff
|
Origo
|
sub getBackupSize {
|
| 1559 |
|
|
my ($subdir, $img, $imguser) = @_; # $subdir, if specified, includes leading slash
|
| 1560 |
|
|
$imguser = $imguser || $user;
|
| 1561 |
|
|
my $backupsize = 0;
|
| 1562 |
|
|
my @bdirs = ("$backupdir/$imguser$subdir/$img");
|
| 1563 |
|
|
if ($backupdir =~ /^\/stabile-backup\//) { # ZFS backup is enabled - we need to scan more dirs
|
| 1564 |
|
|
@bdirs = (
|
| 1565 |
|
|
"/stabile-backup/*/$imguser$subdir/" . shell_esc_chars($img),
|
| 1566 |
|
|
"/stabile-backup/*/.zfs/snapshot/*/$imguser$subdir/". shell_esc_chars($img)
|
| 1567 |
|
|
);
|
| 1568 |
|
|
}
|
| 1569 |
|
|
foreach my $bdir (@bdirs) {
|
| 1570 |
|
|
my $bdu = `/usr/bin/du -bs $bdir 2>/dev/null`;
|
| 1571 |
|
|
my @blines = split("\n", $bdu);
|
| 1572 |
|
|
# only count size from last snapshot
|
| 1573 |
|
|
my $bline = pop @blines;
|
| 1574 |
|
|
# foreach my $bline (@blines) {
|
| 1575 |
|
|
$bline =~ /(\d+)\s+/;
|
| 1576 |
|
|
$backupsize += $1;
|
| 1577 |
|
|
# }
|
| 1578 |
|
|
}
|
| 1579 |
|
|
return $backupsize;
|
| 1580 |
|
|
}
|
| 1581 |
|
|
|
| 1582 |
95b003ff
|
Origo
|
sub shell_esc_chars {
|
| 1583 |
|
|
my $str = shift;
|
| 1584 |
|
|
$str =~ s/([;<>\*\|`&\$!#\(\)\[\]\{\}:'" ])/\\$1/g;
|
| 1585 |
|
|
return $str;
|
| 1586 |
|
|
}
|