kstats-node 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/bin/kstats-node +84 -0
- data/kstats-node.gemspec +1 -0
- data/lib/kstats/node/config.rb +5 -0
- data/lib/kstats/node/database.rb +43 -0
- data/lib/kstats/node/helper.rb +42 -0
- data/lib/kstats/node/probe.rb +165 -0
- data/lib/kstats/node/version.rb +1 -1
- data/lib/kstats/node/worker.rb +46 -0
- data/test/conf.yml +2 -0
- data/test/probes/cpu.rb +32 -0
- data/test/probes/disk.rb +35 -0
- data/test/probes/memory.rb +37 -0
- metadata +18 -3
data/.gitignore
CHANGED
data/bin/kstats-node
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
lib = File.expand_path('../../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
|
6
|
+
class Time
|
7
|
+
def to_db
|
8
|
+
strftime "%Y%m%d%H%M%S"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'optparse'
|
13
|
+
require 'yaml'
|
14
|
+
require 'kstats/node'
|
15
|
+
require 'json'
|
16
|
+
|
17
|
+
options = {}
|
18
|
+
optparse = OptionParser.new do |opts|
|
19
|
+
opts.banner = "Usage: kstats-node [options]"
|
20
|
+
|
21
|
+
opts.on('-h', '--help', 'Show help screen'){ puts opts; exit }
|
22
|
+
|
23
|
+
opts.on('-c', '--config CONF', 'Set configuration file') do |config|
|
24
|
+
options[:config] = config
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
optparse.parse!
|
29
|
+
require 'sinatra'
|
30
|
+
|
31
|
+
#Parse the configuration file:
|
32
|
+
config = YAML::load(File.read(options[:config]))
|
33
|
+
Kstats::Node::CONFIG.merge!(config)
|
34
|
+
|
35
|
+
Kstats::Node::Database.init
|
36
|
+
|
37
|
+
get '/probes/list' do
|
38
|
+
Kstats::Node::Probe.list.to_json
|
39
|
+
end
|
40
|
+
|
41
|
+
#Display data for daily infos
|
42
|
+
get '/probe/:id/:target' do
|
43
|
+
now = Time.now
|
44
|
+
|
45
|
+
case params[:target]
|
46
|
+
when 'tick'
|
47
|
+
start = now - (60*60*24)
|
48
|
+
when 'weekly'
|
49
|
+
start = now - 7*(60*60*24)
|
50
|
+
when 'monthly'
|
51
|
+
start = now - 30*(60*60*24)
|
52
|
+
when 'yearly'
|
53
|
+
start = now - 365*(60*60*24)
|
54
|
+
else
|
55
|
+
return 403
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
data =Kstats::Node::Database.db.execute(
|
60
|
+
"SELECT probe_key, date, probe_value FROM probe_data WHERE period_type=? AND probe_id = ? AND date >= ? ORDER BY date ASC",
|
61
|
+
params[:target],
|
62
|
+
params[:id],
|
63
|
+
start.to_s
|
64
|
+
)
|
65
|
+
|
66
|
+
datas = {}
|
67
|
+
data.each do |record|
|
68
|
+
key, date, value = record
|
69
|
+
datas[key] ||= []
|
70
|
+
datas[key] << [Time.parse(date), value]
|
71
|
+
end
|
72
|
+
|
73
|
+
datas.each do |key, value|
|
74
|
+
datas[key] = Kstats::Node::Helper.generate_array(value, start, (now-start)/60, 288)
|
75
|
+
end
|
76
|
+
|
77
|
+
{
|
78
|
+
start_at: start,
|
79
|
+
format: Kstats::Node::Probe.get_format(params[:id]),
|
80
|
+
data: datas
|
81
|
+
}.to_json
|
82
|
+
end
|
83
|
+
|
84
|
+
Kstats::Node::Worker.launch!
|
data/kstats-node.gemspec
CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.description = %q{Provide a simple way to make probe on system values, and monitorign theses values.}
|
13
13
|
spec.homepage = ""
|
14
14
|
spec.license = "MIT"
|
15
|
+
spec.executables << 'kstats-node'
|
15
16
|
|
16
17
|
spec.files = `git ls-files -z`.split("\x0")
|
17
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'sqlite3'
|
2
|
+
|
3
|
+
module Kstats
|
4
|
+
module Node
|
5
|
+
module Database
|
6
|
+
class << self
|
7
|
+
attr_reader :db
|
8
|
+
|
9
|
+
def init
|
10
|
+
@db = SQLite3::Database.new( Kstats::Node::CONFIG['db_dir'] )
|
11
|
+
@db.execute [
|
12
|
+
"CREATE TABLE IF NOT EXISTS probe_data (id INTEGER PRIMARY KEY ASC, date DATETIME, period_type STRING, probe_id STRING, probe_key STRING, probe_value NUMBER)",
|
13
|
+
"CREATE INDEX IF NOT EXISTS probe_data_period_type ON probe_data(period_type)",
|
14
|
+
"CREATE INDEX IF NOT EXISTS probe_data_date ON probe_data(date)",
|
15
|
+
"CREATE INDEX IF NOT EXISTS probe_data_probe_id ON probe_data(probe_id)",
|
16
|
+
"CREATE INDEX IF NOT EXISTS probe_data_probe_key ON probe_data(probe_key)"
|
17
|
+
].join(";")
|
18
|
+
end
|
19
|
+
|
20
|
+
def execute query
|
21
|
+
@db.execute(query)
|
22
|
+
end
|
23
|
+
|
24
|
+
def save_probes_data probes, type
|
25
|
+
time = Time.now
|
26
|
+
|
27
|
+
probes.each do |name, values|
|
28
|
+
@db.close
|
29
|
+
@db = SQLite3::Database.new( Kstats::Node::CONFIG['db_dir'] )
|
30
|
+
|
31
|
+
sql = <<SQL
|
32
|
+
INSERT INTO probe_data (date, period_type, probe_id, probe_key, probe_value)
|
33
|
+
VALUES (?, ?, ?, ?, ?)
|
34
|
+
SQL
|
35
|
+
values.each do |probe_key, probe_value|
|
36
|
+
@db.execute(sql, time.to_s, type.to_s, name.to_s, probe_key.to_s, probe_value)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end #<< self
|
41
|
+
end #Class
|
42
|
+
end #Node
|
43
|
+
end #Kstats
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Kstats
|
2
|
+
module Node
|
3
|
+
module Helper
|
4
|
+
#Entry:
|
5
|
+
# Data from probe with:
|
6
|
+
# 0: Date of the pick
|
7
|
+
# 1: Value of the pick
|
8
|
+
# Output:
|
9
|
+
# An array with value of average of each points
|
10
|
+
def self.generate_array data, start, period, nb_points
|
11
|
+
out_array = Array.new nb_points
|
12
|
+
|
13
|
+
for i in 0...nb_points
|
14
|
+
out_array[i] = [0.0, 0.0] # value and coef.
|
15
|
+
end
|
16
|
+
|
17
|
+
data.each do |x|
|
18
|
+
#Position of the data over the period:
|
19
|
+
poz = (x[0] - start)/60 # In minutes
|
20
|
+
poz = poz / period.to_f # In percent between [0..1]
|
21
|
+
|
22
|
+
next if poz < 0.0 || poz > 1.0
|
23
|
+
|
24
|
+
p1,p2,f = (poz * nb_points).floor, (poz*nb_points).ceil, (poz*nb_points).abs.modulo(1)
|
25
|
+
|
26
|
+
if f>0.5
|
27
|
+
output = out_array[p1]
|
28
|
+
else
|
29
|
+
output = out_array[p2]
|
30
|
+
end
|
31
|
+
|
32
|
+
unless output.nil?
|
33
|
+
output[0] = (x[1] + output[1]*output[0]) / ( output[1] + 1)
|
34
|
+
output[1] += 1
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
out_array.map{ |x| x[0].round(3) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module Kstats
|
2
|
+
module Node
|
3
|
+
class Probe
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_accessor :registered
|
7
|
+
|
8
|
+
def register name, &block
|
9
|
+
@registered << Probe.new(name).from_dsl(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_format probe
|
13
|
+
probe = @fixed_registered.select{|x| x.id == probe }.first
|
14
|
+
if probe.nil?
|
15
|
+
return nil
|
16
|
+
else
|
17
|
+
{
|
18
|
+
type: probe.type,
|
19
|
+
variables: probe.variables.inject({}){ |h, x|
|
20
|
+
h[x.name] = {}
|
21
|
+
h[x.name][:desc] = x.desc
|
22
|
+
h[x.name][:color] = x.color
|
23
|
+
h
|
24
|
+
}
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def reload_and_test!
|
30
|
+
@fixed_registered = @registered
|
31
|
+
@registered = []
|
32
|
+
|
33
|
+
dir = Kstats::Node::CONFIG['probes_dir']
|
34
|
+
|
35
|
+
Dir[File.join(dir, "*.rb")].each do |probe_file|
|
36
|
+
begin
|
37
|
+
load(probe_file)
|
38
|
+
rescue => e
|
39
|
+
puts "Unable to load #{probe_file}: #{e}"
|
40
|
+
puts "*\t#{e.backtrace.join("\n*\t")}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
probes = {}
|
45
|
+
|
46
|
+
@registered.each do |probe|
|
47
|
+
begin
|
48
|
+
probes[probe.id] = probe.test
|
49
|
+
rescue => e
|
50
|
+
puts "Exception for probe #{probe.id} : #{e.message}"
|
51
|
+
puts "*\t#{e.backtrace.join("\n*\t")}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
@fixed_registered = @registered
|
56
|
+
|
57
|
+
probes
|
58
|
+
end
|
59
|
+
|
60
|
+
def list
|
61
|
+
probe_categories = {}
|
62
|
+
@fixed_registered.each do |probe|
|
63
|
+
probe_categories[probe.category] ||= []
|
64
|
+
probe_categories[probe.category] << { name: probe.name, id: probe.id }
|
65
|
+
end
|
66
|
+
|
67
|
+
probe_categories
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class Variable
|
72
|
+
attr_reader :name
|
73
|
+
attr_accessor :desc, :color, :probe
|
74
|
+
|
75
|
+
def initialize name, parent
|
76
|
+
@name = name
|
77
|
+
@parent = parent
|
78
|
+
end
|
79
|
+
|
80
|
+
def command
|
81
|
+
@parent.command
|
82
|
+
end
|
83
|
+
|
84
|
+
def test
|
85
|
+
instance_eval(&probe)
|
86
|
+
end
|
87
|
+
|
88
|
+
def from_dsl &block
|
89
|
+
dsl = DSL.new(self)
|
90
|
+
dsl.instance_eval(&block)
|
91
|
+
|
92
|
+
return self
|
93
|
+
end
|
94
|
+
|
95
|
+
class DSL
|
96
|
+
def initialize variable
|
97
|
+
@variable = variable
|
98
|
+
end
|
99
|
+
|
100
|
+
def desc val
|
101
|
+
@variable.desc = val
|
102
|
+
end
|
103
|
+
|
104
|
+
def color val
|
105
|
+
@variable.color = val
|
106
|
+
end
|
107
|
+
|
108
|
+
def probe &val
|
109
|
+
@variable.probe = val
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
attr_reader :id
|
115
|
+
attr_accessor :variables, :category, :name, :type, :command, :command_block
|
116
|
+
|
117
|
+
def initialize id
|
118
|
+
@variables = []
|
119
|
+
@id = id
|
120
|
+
end
|
121
|
+
|
122
|
+
def from_dsl &block
|
123
|
+
DSL.new(self).instance_eval(&block)
|
124
|
+
|
125
|
+
return self
|
126
|
+
end
|
127
|
+
|
128
|
+
def add_variable var
|
129
|
+
@variables << var
|
130
|
+
end
|
131
|
+
|
132
|
+
def test
|
133
|
+
@command = self.command_block.call unless self.command_block.nil?
|
134
|
+
|
135
|
+
@variables.inject({}){|h, x| h[x.name] = x.test; h }
|
136
|
+
end
|
137
|
+
|
138
|
+
class DSL
|
139
|
+
def initialize probe
|
140
|
+
@probe = probe
|
141
|
+
end
|
142
|
+
|
143
|
+
def category val
|
144
|
+
@probe.category = val
|
145
|
+
end
|
146
|
+
|
147
|
+
def name val
|
148
|
+
@probe.name = val
|
149
|
+
end
|
150
|
+
|
151
|
+
def command &block
|
152
|
+
@probe.command_block = block
|
153
|
+
end
|
154
|
+
|
155
|
+
def type val
|
156
|
+
@probe.type = val
|
157
|
+
end
|
158
|
+
|
159
|
+
def variable name, &block
|
160
|
+
@probe.add_variable Variable.new(name, @probe).from_dsl(&block)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
data/lib/kstats/node/version.rb
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Kstats
|
2
|
+
module Node
|
3
|
+
class Worker
|
4
|
+
def self.launch!
|
5
|
+
insert_month = 0
|
6
|
+
insert_week = 0
|
7
|
+
insert_year = 0
|
8
|
+
|
9
|
+
thread = Thread.new do
|
10
|
+
begin
|
11
|
+
begin
|
12
|
+
puts "New probe tick."
|
13
|
+
probes = Kstats::Node::Probe.reload_and_test!
|
14
|
+
|
15
|
+
if insert_month == 0
|
16
|
+
insert_month = 30
|
17
|
+
Database.save_probes_data(probes, :monthly)
|
18
|
+
end
|
19
|
+
|
20
|
+
if insert_week == 0
|
21
|
+
insert_week = 7
|
22
|
+
Database.save_probes_data(probes, :weekly)
|
23
|
+
end
|
24
|
+
|
25
|
+
if insert_year == 0
|
26
|
+
insert_year = 365
|
27
|
+
Database.save_probes_data(probes, :yearly)
|
28
|
+
end
|
29
|
+
|
30
|
+
insert_month -= 1
|
31
|
+
insert_week -= 1
|
32
|
+
insert_year -= 1
|
33
|
+
|
34
|
+
Database.save_probes_data(probes, :tick)
|
35
|
+
rescue Exception => e
|
36
|
+
puts e.message
|
37
|
+
puts e.backtrace
|
38
|
+
end
|
39
|
+
sleep(300)
|
40
|
+
end while true
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/test/conf.yml
ADDED
data/test/probes/cpu.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
Kstats::Node::Probe.register 'cpu' do
|
2
|
+
category 'Hardware'
|
3
|
+
name 'CPU usage'
|
4
|
+
|
5
|
+
type :curve
|
6
|
+
|
7
|
+
command{ `cat /proc/loadavg`.split(/\s/).map(&:to_f) }
|
8
|
+
|
9
|
+
variable :load_avg_1mn do
|
10
|
+
desc 'Load average (1mn)'
|
11
|
+
color '#0000FF'
|
12
|
+
probe do
|
13
|
+
command[0]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
variable :load_avg_5mn do
|
18
|
+
desc 'Load average (5mn)'
|
19
|
+
color '#00FF00'
|
20
|
+
probe do
|
21
|
+
command[1]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
variable :load_avg_15mn do
|
26
|
+
desc 'Load average (15mn)'
|
27
|
+
color '#FF0000'
|
28
|
+
probe do
|
29
|
+
command[2]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/test/probes/disk.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
Kstats::Node::Probe.register 'disk' do
|
2
|
+
category 'Hardware'
|
3
|
+
name 'Disk usage'
|
4
|
+
|
5
|
+
type :curve
|
6
|
+
|
7
|
+
_self = self
|
8
|
+
instance_eval do
|
9
|
+
def parsedf
|
10
|
+
disks = `df`.split(/\n/).map do |x|
|
11
|
+
x.split(/\s+/)
|
12
|
+
end
|
13
|
+
|
14
|
+
disks.reject!{|x| x[0] == 'udev' || x[0] == 'none' || x[0] == 'tmpfs' || x[0] == 'Filesystem' }
|
15
|
+
disks.map!{|x| {name: x[0], space: 100*(x[2].to_f/x[1].to_f) }}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
parsedf.each do |infos|
|
20
|
+
var = infos[:name]
|
21
|
+
|
22
|
+
variable var do
|
23
|
+
desc "Filling of `#{infos[:name]}`"
|
24
|
+
color "#0000FF"
|
25
|
+
probe do
|
26
|
+
val = _self.parsedf.select{|x| x[:name] == var }.first[:space]
|
27
|
+
|
28
|
+
puts val
|
29
|
+
|
30
|
+
val
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
Kstats::Node::Probe.register 'hwmem' do
|
2
|
+
category 'Hardware'
|
3
|
+
name 'Memory usage'
|
4
|
+
|
5
|
+
type :curve
|
6
|
+
|
7
|
+
command do
|
8
|
+
cmd = `cat /proc/meminfo`
|
9
|
+
lines = cmd.split(/\n/)
|
10
|
+
|
11
|
+
memtotal = lines.select{|x| x =~ /MemTotal/ }.first.gsub(/[^0-9]/, '').to_f
|
12
|
+
active = lines.select{|x| x =~ /Active/ }.first.gsub(/[^0-9]/, '').to_f
|
13
|
+
inactive = lines.select{|x| x =~ /Inactive/ }.first.gsub(/[^0-9]/, '').to_f
|
14
|
+
memfree = lines.select{|x| x =~ /MemFree/ }.first.gsub(/[^0-9]/, '').to_f
|
15
|
+
swaptotal = lines.select{|x| x =~ /SwapTotal/ }.first.gsub(/[^0-9]/, '').to_f
|
16
|
+
swapfree = lines.select{|x| x =~ /SwapFree/ }.first.gsub(/[^0-9]/, '').to_f
|
17
|
+
|
18
|
+
[memtotal, active, inactive, memfree, swaptotal, swapfree]
|
19
|
+
end
|
20
|
+
|
21
|
+
variable :mem_load do
|
22
|
+
desc 'Memory load (%)'
|
23
|
+
color '#0000FF'
|
24
|
+
probe do
|
25
|
+
1-(command[3]/command[0])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
variable :swap_load do
|
30
|
+
desc 'Swap load (%)'
|
31
|
+
color '#FF0000'
|
32
|
+
|
33
|
+
probe do
|
34
|
+
1-(command[5]/command[4])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kstats-node
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -79,7 +79,8 @@ description: Provide a simple way to make probe on system values, and monitorign
|
|
79
79
|
values.
|
80
80
|
email:
|
81
81
|
- yacine@kosmogo.com
|
82
|
-
executables:
|
82
|
+
executables:
|
83
|
+
- kstats-node
|
83
84
|
extensions: []
|
84
85
|
extra_rdoc_files: []
|
85
86
|
files:
|
@@ -88,9 +89,19 @@ files:
|
|
88
89
|
- LICENSE.txt
|
89
90
|
- README.md
|
90
91
|
- Rakefile
|
92
|
+
- bin/kstats-node
|
91
93
|
- kstats-node.gemspec
|
92
94
|
- lib/kstats/node.rb
|
95
|
+
- lib/kstats/node/config.rb
|
96
|
+
- lib/kstats/node/database.rb
|
97
|
+
- lib/kstats/node/helper.rb
|
98
|
+
- lib/kstats/node/probe.rb
|
93
99
|
- lib/kstats/node/version.rb
|
100
|
+
- lib/kstats/node/worker.rb
|
101
|
+
- test/conf.yml
|
102
|
+
- test/probes/cpu.rb
|
103
|
+
- test/probes/disk.rb
|
104
|
+
- test/probes/memory.rb
|
94
105
|
homepage: ''
|
95
106
|
licenses:
|
96
107
|
- MIT
|
@@ -116,5 +127,9 @@ rubygems_version: 1.8.28
|
|
116
127
|
signing_key:
|
117
128
|
specification_version: 3
|
118
129
|
summary: Node for the project kstats
|
119
|
-
test_files:
|
130
|
+
test_files:
|
131
|
+
- test/conf.yml
|
132
|
+
- test/probes/cpu.rb
|
133
|
+
- test/probes/disk.rb
|
134
|
+
- test/probes/memory.rb
|
120
135
|
has_rdoc:
|