kstats-node 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|