| 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 |
|
|
|
| 9 |
|
|
# https://valve001.irigo.com/stabile/piston/stats.cgi?1.timestamp=1277045903&1.blk.hda.errs=-1&1.if.vnet1.tx_drop=0&1.if.vnet1.tx_packets=582932&1.if.vnet1.tx_errs=0&1.domain.nrVirtCpu=1&1.if.vnet1.tx_bytes=83963279&1.uuid=5eff51c7-f398-4eb9-8495-567cbb2bad77&1.if.vnet1.rx_packets=2898355&1.blk.hda.wr_req=183467&1.blk.hda.rd_req=206233&1.domain.cpuTime=26164280000000&1.domain.state=1&1.blk.hda.wr_bytes=3615029248&1.if.vnet1.rx_drop=0&1.domain.maxMem=2097152&1.if.vnet1.rx_bytes=1029898520&1.domain.memory=2097152&1.if.vnet1.rx_errs=0&1.blk.hda.rd_bytes=4088299520
|
| 10 |
|
|
# https://valve001.irigo.com/stabile/piston/stats.cgi?5.blk.hda.errs=-1&5.if.vnet1.tx_drop=0&5.if.vnet1.tx_packets=582932&5.if.vnet1.tx_errs=0&5.domain.nrVirtCpu=1&5.if.vnet1.tx_bytes=83963279&5.uuid=5eff51c7-f398-4eb9-8495-567cbb2bad77&5.if.vnet1.rx_packets=2898355&5.blk.hda.wr_req=183467&5.blk.hda.rd_req=206233&5.domain.cpuTime=26164280000000&5.domain.state=1&5.blk.hda.wr_bytes=3615029248&5.if.vnet1.rx_drop=0&5.domain.maxMem=2097152&5.if.vnet1.rx_bytes=1029898520&5.domain.memory=2097152&5.if.vnet1.rx_errs=0&5.blk.hda.rd_bytes=4088299520
|
| 11 |
|
|
#
|
| 12 |
|
|
# rrdtool dump 0de6349b-e10b-4465-90e4-d5a314263649.rrd | grep -v NaN|less
|
| 13 |
|
|
#
|
| 14 |
|
|
|
| 15 |
|
|
# Modifications made to valve001.irigo.com:
|
| 16 |
|
|
# apt-get install apt-get install rrdtool librrd4 librrds-perl
|
| 17 |
|
|
# mkdir /var/cache/rrdtool/
|
| 18 |
|
|
# chown www-data:www-data /var/cache/rrdtool/
|
| 19 |
|
|
#
|
| 20 |
|
|
|
| 21 |
|
|
use CGI::Carp qw(fatalsToBrowser);
|
| 22 |
|
|
#use CGI 3.47 ':standard';
|
| 23 |
|
|
use CGI ':standard';
|
| 24 |
|
|
use JSON;
|
| 25 |
|
|
use URI::Escape;
|
| 26 |
|
|
use Error qw(:try);
|
| 27 |
|
|
use Sys::Syslog qw( :DEFAULT setlogsock);
|
| 28 |
|
|
use Data::Dumper;
|
| 29 |
|
|
use IO::Socket::INET;
|
| 30 |
|
|
use Python::Serialise::Pickle;
|
| 31 |
|
|
|
| 32 |
|
|
# Clear up tainted environment
|
| 33 |
|
|
$ENV{PATH} = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin';
|
| 34 |
|
|
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
|
| 35 |
|
|
|
| 36 |
|
|
my $q = new CGI;
|
| 37 |
|
|
my $params = $q->Vars;
|
| 38 |
|
|
|
| 39 |
|
|
$statsnap = 10; # time in seconds between stats updates - this must be coordinated with values in movepiston
|
| 40 |
|
|
|
| 41 |
|
|
|
| 42 |
|
|
print header;
|
| 43 |
|
|
#
|
| 44 |
|
|
# Put posted information into a hashtable for easy retrieval...
|
| 45 |
|
|
#
|
| 46 |
|
|
$hash = ();
|
| 47 |
|
|
|
| 48 |
|
|
while (my($key,$value) = each(%$params)) {
|
| 49 |
|
|
@keysplit = split('\.', $key);
|
| 50 |
|
|
$domainnumber = shift(@keysplit);
|
| 51 |
|
|
$type = shift(@keysplit);
|
| 52 |
|
|
$dev = shift(@keysplit);
|
| 53 |
|
|
$param = shift(@keysplit);
|
| 54 |
|
|
|
| 55 |
|
|
if ($type eq "domain") {
|
| 56 |
|
|
$hash->{$domainnumber}->{$type}->{$dev} = $value;
|
| 57 |
|
|
} elsif ($type eq "blk" or $type eq "if") {
|
| 58 |
|
|
$hash->{$domainnumber}->{$type}->{$dev}->{$param} = $value;
|
| 59 |
|
|
}
|
| 60 |
|
|
}
|
| 61 |
|
|
|
| 62 |
|
|
# print(Dumper($hash));
|
| 63 |
|
|
|
| 64 |
|
|
|
| 65 |
|
|
#
|
| 66 |
|
|
# Find number of domains...
|
| 67 |
|
|
#
|
| 68 |
|
|
$numberOfDomainsSubmitted = getNumberOfDomainsSubmitted($params);
|
| 69 |
|
|
|
| 70 |
|
|
#
|
| 71 |
|
|
# Loop through all domain statistics, putting it in the database...
|
| 72 |
|
|
#
|
| 73 |
|
|
for ($i=1; $i <= $numberOfDomainsSubmitted; $i++) {
|
| 74 |
|
|
my $uuid = $params->{$i.".uuid"};
|
| 75 |
|
|
my $timestamp = int(getParam($params, "$i.timestamp"));
|
| 76 |
|
|
print("Loop $i [".$uuid."]...\n");
|
| 77 |
|
|
|
| 78 |
|
|
#
|
| 79 |
|
|
# Make sure RRD for the uuid exists...
|
| 80 |
|
|
#
|
| 81 |
|
|
checkRRD($uuid, $timestamp);
|
| 82 |
|
|
|
| 83 |
|
|
#
|
| 84 |
|
|
# Update RRD buckets...
|
| 85 |
|
|
#
|
| 86 |
|
|
# my $cpuTime = getParam($params, "$i.domain.cpuTime") / getParam($params, "$i.domain.nrVirtCpu");
|
| 87 |
|
|
# $cpuTime = 1 if ($cpuTime > 1);
|
| 88 |
71b897d3
|
hq
|
my $cpuTime = getParam($params, "$i.domain.cpuTime")+0;
|
| 89 |
|
|
my $cpuLoad = getParam($params, "$i.domain.cpuLoad")+0;
|
| 90 |
|
|
my $rd_bytes = getParam($params, "$i.blk.hd.rd_bytes")+0;
|
| 91 |
|
|
my $wr_bytes = getParam($params, "$i.blk.hd.wr_bytes")+0;
|
| 92 |
|
|
my $rd_kbytes_s = getParam($params, "$i.blk.hd.rd_kbytes_s")+0;
|
| 93 |
|
|
my $wr_kbytes_s = getParam($params, "$i.blk.hd.wr_kbytes_s")+0;
|
| 94 |
|
|
my $rx_bytes = getParam($params, "$i.if.vnet.rx_bytes")+0;
|
| 95 |
|
|
my $tx_bytes = getParam($params, "$i.if.vnet.tx_bytes")+0;
|
| 96 |
|
|
my $rx_kbytes_s = getParam($params, "$i.if.vnet.rx_kbytes_s")+0;
|
| 97 |
|
|
my $tx_kbytes_s = getParam($params, "$i.if.vnet.tx_kbytes_s")+0;
|
| 98 |
|
|
my $nrVirtCpu = getParam($params, "$i.domain.nrVirtCpu")+0;
|
| 99 |
|
|
my $memory = getParam($params, "$i.domain.memory")+0;
|
| 100 |
|
|
my $maxMem = getParam($params, "$i.domain.maxMem")+0;
|
| 101 |
95b003ff
|
Origo
|
|
| 102 |
|
|
my $rrd_data = "$timestamp".
|
| 103 |
|
|
":".$cpuTime.
|
| 104 |
|
|
":".$rd_bytes.
|
| 105 |
|
|
":".$wr_bytes.
|
| 106 |
|
|
":".$rx_bytes.
|
| 107 |
|
|
":".$tx_bytes.
|
| 108 |
|
|
":".$nrVirtCpu.
|
| 109 |
|
|
":".$memory.
|
| 110 |
|
|
":".$maxMem.
|
| 111 |
|
|
"";
|
| 112 |
|
|
print "\t$rrd_data\n";
|
| 113 |
|
|
|
| 114 |
|
|
# Update highres rrd...
|
| 115 |
|
|
my $rrd_file_highres = "/var/cache/rrdtool/".$uuid."_highres.rrd";
|
| 116 |
|
|
$rrd_file_highres = $1 if ($rrd_file_highres =~ /(.+)/);
|
| 117 |
|
|
$rrd_data = $1 if ($rrd_data =~ /(.+)/);
|
| 118 |
|
|
print("\tUpdating $rrd_file_highres...\n");
|
| 119 |
|
|
print `/usr/bin/rrdtool update $rrd_file_highres $rrd_data`;
|
| 120 |
|
|
|
| 121 |
|
|
my $pickle_data = [
|
| 122 |
|
|
["domains.$uuid.cpuLoad", [$timestamp, $cpuLoad]],
|
| 123 |
|
|
["domains.$uuid.rx_kbytes_s", [$timestamp, $rx_kbytes_s]],
|
| 124 |
|
|
["domains.$uuid.tx_kbytes_s", [$timestamp, $tx_kbytes_s]],
|
| 125 |
|
|
["domains.$uuid.rd_kbytes_s", [$timestamp, $rd_kbytes_s]],
|
| 126 |
|
|
["domains.$uuid.wr_kbytes_s", [$timestamp, $wr_kbytes_s]],
|
| 127 |
|
|
["domains.$uuid.rd_bytes", [$timestamp, $rd_bytes]],
|
| 128 |
|
|
["domains.$uuid.wr_bytes", [$timestamp, $wr_bytes]],
|
| 129 |
|
|
["domains.$uuid.rx_bytes", [$timestamp, $rx_bytes]],
|
| 130 |
|
|
["domains.$uuid.tx_bytes", [$timestamp, $tx_bytes]],
|
| 131 |
|
|
["domains.$uuid.nrVirtCpu", [$timestamp, $nrVirtCpu]],
|
| 132 |
|
|
["domains.$uuid.memory", [$timestamp, $memory]],
|
| 133 |
|
|
["domains.$uuid.maxMem", [$timestamp, $maxMem]]
|
| 134 |
|
|
];
|
| 135 |
|
|
print pickle_it($pickle_data);
|
| 136 |
|
|
|
| 137 |
|
|
}
|
| 138 |
|
|
|
| 139 |
|
|
print "---\nGot it, thanks!\n";
|
| 140 |
|
|
|
| 141 |
|
|
0;
|
| 142 |
|
|
|
| 143 |
|
|
sub getNumberOfDomainsSubmitted {
|
| 144 |
|
|
my($parms) = @_;
|
| 145 |
|
|
my $numberofdomains = 0;
|
| 146 |
|
|
foreach my $dom (keys %$parms) {
|
| 147 |
|
|
my @splits = split('\.', $dom);
|
| 148 |
|
|
my $domnum = shift(@splits);
|
| 149 |
|
|
$numberofdomains = $domnum if ($domnum > $numberofdomains);
|
| 150 |
|
|
}
|
| 151 |
|
|
return($numberofdomains);
|
| 152 |
|
|
}
|
| 153 |
|
|
|
| 154 |
|
|
sub getParam {
|
| 155 |
|
|
my($parms, $key) = @_;
|
| 156 |
|
|
my $value = $parms->{$key};
|
| 157 |
|
|
return $value;
|
| 158 |
|
|
# return $value || 0;
|
| 159 |
|
|
}
|
| 160 |
|
|
|
| 161 |
|
|
#
|
| 162 |
|
|
# checkRRD: Checks if an RRD for the specified uuid exists. If not, a new RRD is created.
|
| 163 |
|
|
#
|
| 164 |
|
|
# @param uuid The uuid for which to look for an RRD for
|
| 165 |
|
|
#
|
| 166 |
|
|
sub checkRRD {
|
| 167 |
|
|
my($uuid, $data_timestamp) = @_;
|
| 168 |
|
|
my @rrd_order_counter = (
|
| 169 |
|
|
"domain-cpuTime",
|
| 170 |
|
|
"hda-rd_bytes",
|
| 171 |
|
|
"hda-wr_bytes",
|
| 172 |
|
|
"vnet0-rx_bytes",
|
| 173 |
|
|
"vnet0-tx_bytes"
|
| 174 |
|
|
);
|
| 175 |
|
|
my @rrd_order_gauge = (
|
| 176 |
|
|
"domain-nrVirtCpu",
|
| 177 |
|
|
"domain-memory",
|
| 178 |
|
|
"domain-maxMem",
|
| 179 |
|
|
);
|
| 180 |
|
|
my $rrd_datastores = "";
|
| 181 |
|
|
foreach $dsname (@rrd_order_counter) {
|
| 182 |
|
|
$rrd_datastores .= " DS:$dsname:COUNTER:30:U:U";
|
| 183 |
|
|
}
|
| 184 |
|
|
foreach $dsname (@rrd_order_gauge) {
|
| 185 |
|
|
$rrd_datastores .= " DS:$dsname:GAUGE:30:U:U";
|
| 186 |
|
|
}
|
| 187 |
|
|
|
| 188 |
|
|
my $rrd_file_highres = "/var/cache/rrdtool/".$uuid."_highres.rrd";
|
| 189 |
|
|
if ((not -e $rrd_file_highres) and ($uuid)) {
|
| 190 |
|
|
#
|
| 191 |
|
|
# RRD file for specified uuid not found, so create a new one...
|
| 192 |
|
|
#
|
| 193 |
|
|
|
| 194 |
|
|
my @rows_time_span = (
|
| 195 |
|
|
10, # archive for next 10 mins
|
| 196 |
|
|
30, # next 30 ...
|
| 197 |
|
|
60, # next 60
|
| 198 |
|
|
120, # next 2hrs
|
| 199 |
|
|
720, # 12 hrs
|
| 200 |
|
|
1440, # 24 hrs
|
| 201 |
|
|
2880, # 2 days
|
| 202 |
|
|
14 * 24 * 60, # 14 days
|
| 203 |
|
|
28 * 24 * 60, # 28 days
|
| 204 |
|
|
6 * 30 * 24 * 60, # 6 months
|
| 205 |
|
|
);
|
| 206 |
|
|
my $extra = 5;
|
| 207 |
|
|
my $rows = 30;
|
| 208 |
|
|
my $rows_with_buffer = $rows + $extra;
|
| 209 |
|
|
my @archives = ();
|
| 210 |
|
|
|
| 211 |
|
|
foreach my $total_span ( @rows_time_span ) {
|
| 212 |
|
|
# time span for each slot.
|
| 213 |
|
|
$steps = ($total_span * 60) / $rows; # when sampling rate is pr. second
|
| 214 |
|
|
$steps = $steps / $statsnap; # since we are sampling every 10s
|
| 215 |
|
|
# 0.9 XFiles Factor 90% of measurements can be UNKNOWN
|
| 216 |
|
|
push @archives, "RRA:AVERAGE:0.9:$steps:$rows_with_buffer";
|
| 217 |
|
|
}
|
| 218 |
|
|
|
| 219 |
|
|
my $exec = "/usr/bin/rrdtool create $rrd_file_highres --start $data_timestamp --step $statsnap $rrd_datastores " . join(" ", @archives);
|
| 220 |
|
|
print("Creating new highres RRD for $uuid...\n$exec\n");
|
| 221 |
|
|
print `$exec` . "\n";
|
| 222 |
|
|
}
|
| 223 |
|
|
}
|
| 224 |
|
|
|
| 225 |
|
|
sub pickle_it {
|
| 226 |
|
|
my $data = shift;
|
| 227 |
|
|
# example: [ ["path.mytest", [1332444075,27893687]], ["path.mytest", [1332444076,938.435]], ];
|
| 228 |
|
|
my($carbon_server) = '127.0.0.1';
|
| 229 |
|
|
my($carbon_port) = 2004;
|
| 230 |
|
|
|
| 231 |
|
|
|
| 232 |
|
|
my($message) = pack("N/a*", pickle_dumps($data));
|
| 233 |
|
|
|
| 234 |
|
|
my($sock) = IO::Socket::INET->new (
|
| 235 |
|
|
PeerAddr => $carbon_server,
|
| 236 |
|
|
PeerPort => $carbon_port,
|
| 237 |
|
|
Proto => 'tcp'
|
| 238 |
|
|
);
|
| 239 |
|
|
return "Unable to connect to pickle socket: $!\n" unless ($sock->connected);
|
| 240 |
|
|
|
| 241 |
|
|
$sock->send($message);
|
| 242 |
|
|
$sock->shutdown(2);
|
| 243 |
|
|
return '';
|
| 244 |
|
|
}
|
| 245 |
|
|
|
| 246 |
|
|
# Work around P::S::Pickle 0.01's extremely limiting interface.
|
| 247 |
|
|
sub pickle_dumps {
|
| 248 |
|
|
open(my $fh, '>', \my $s) or die $!;
|
| 249 |
|
|
my $pickle = bless({ _fh => $fh }, 'Python::Serialise::Pickle');
|
| 250 |
|
|
$pickle->dump($_[0]);
|
| 251 |
|
|
$pickle->close();
|
| 252 |
|
|
return $s;
|
| 253 |
|
|
}
|