1
|
#!/usr/bin/perl
|
2
|
|
3
|
# All rights reserved and Copyright (c) 2020 Origo Systems ApS.
|
4
|
# This file is provided with no warranty, and is subject to the terms and conditions defined in the license file LICENSE.md.
|
5
|
# The license file is part of this source code package and its content is also available at:
|
6
|
# https://www.origo.io/info/stabiledocs/licensing/stabile-open-source-license
|
7
|
|
8
|
|
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
|
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
|
|
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
|
}
|