prometheus-splash 0.6.0 → 0.8.2

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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -1
  3. data/README.md +1 -1
  4. data/config/splash.yml +97 -11
  5. data/lib/splash/backends.rb +1 -0
  6. data/lib/splash/cli.rb +4 -1
  7. data/lib/splash/cli/commands.rb +127 -7
  8. data/lib/splash/cli/daemon.rb +41 -1
  9. data/lib/splash/cli/logs.rb +111 -47
  10. data/lib/splash/cli/process.rb +112 -52
  11. data/lib/splash/cli/sequences.rb +114 -0
  12. data/lib/splash/cli/transfers.rb +213 -0
  13. data/lib/splash/cli/webadmin.rb +3 -3
  14. data/lib/splash/commands.rb +89 -22
  15. data/lib/splash/config.rb +149 -62
  16. data/lib/splash/config/sanitycheck.rb +1 -1
  17. data/lib/splash/constants.rb +11 -11
  18. data/lib/splash/daemon/controller.rb +10 -10
  19. data/lib/splash/daemon/metrics.rb +8 -8
  20. data/lib/splash/daemon/orchestrator.rb +96 -35
  21. data/lib/splash/daemon/orchestrator/grammar.rb +16 -1
  22. data/lib/splash/dependencies.rb +8 -0
  23. data/lib/splash/exiter.rb +1 -1
  24. data/lib/splash/helpers.rb +22 -4
  25. data/lib/splash/loggers/cli.rb +2 -10
  26. data/lib/splash/logs.rb +91 -17
  27. data/lib/splash/processes.rb +88 -17
  28. data/lib/splash/sequences.rb +105 -0
  29. data/lib/splash/transfers.rb +229 -0
  30. data/lib/splash/transports/rabbitmq.rb +0 -1
  31. data/lib/splash/webadmin.rb +4 -4
  32. data/lib/splash/webadmin/api/routes/commands.rb +2 -2
  33. data/lib/splash/webadmin/api/routes/config.rb +53 -2
  34. data/lib/splash/webadmin/api/routes/logs.rb +32 -17
  35. data/lib/splash/webadmin/api/routes/process.rb +4 -4
  36. data/lib/splash/webadmin/api/routes/sequences.rb +28 -0
  37. data/lib/splash/webadmin/main.rb +3 -2
  38. data/lib/splash/webadmin/portal/controllers/commands.rb +2 -0
  39. data/lib/splash/webadmin/portal/controllers/documentation.rb +3 -1
  40. data/lib/splash/webadmin/portal/controllers/home.rb +24 -0
  41. data/lib/splash/webadmin/portal/controllers/logs.rb +44 -1
  42. data/lib/splash/webadmin/portal/controllers/processes.rb +2 -0
  43. data/lib/splash/webadmin/portal/controllers/proxy.rb +15 -8
  44. data/lib/splash/webadmin/portal/controllers/restclient.rb +7 -2
  45. data/lib/splash/webadmin/portal/controllers/sequences.rb +9 -0
  46. data/lib/splash/webadmin/portal/init.rb +2 -2
  47. data/lib/splash/webadmin/portal/public/css/ultragreen.css +6 -0
  48. data/lib/splash/webadmin/portal/public/favicon.ico +0 -0
  49. data/lib/splash/webadmin/portal/views/commands.slim +1 -1
  50. data/lib/splash/webadmin/portal/views/documentation.slim +1 -1
  51. data/lib/splash/webadmin/portal/views/home.slim +53 -9
  52. data/lib/splash/webadmin/portal/views/layout.slim +2 -2
  53. data/lib/splash/webadmin/portal/views/logs.slim +68 -21
  54. data/lib/splash/webadmin/portal/views/logs_form.slim +24 -0
  55. data/lib/splash/webadmin/portal/views/nav.slim +1 -1
  56. data/lib/splash/webadmin/portal/views/not_found.slim +1 -1
  57. data/lib/splash/webadmin/portal/views/processes.slim +1 -1
  58. data/lib/splash/webadmin/portal/views/proxy.slim +5 -2
  59. data/lib/splash/webadmin/portal/views/restclient.slim +7 -4
  60. data/lib/splash/webadmin/portal/views/restclient_result.slim +24 -20
  61. data/lib/splash/webadmin/portal/views/sequences.slim +50 -0
  62. data/prometheus-splash.gemspec +5 -2
  63. metadata +69 -4
@@ -252,7 +252,7 @@ module Splash
252
252
  # check folder
253
253
  # @return [Array] of Symbol with error type : [:inexistant,:mode,:owner,:group]
254
254
  # @param [Hash] options
255
- # @option options [String] :path folder path (relative or absolute)
255
+ # @option options [String] :name folder path (relative or absolute)
256
256
  # @option options [String] :mode String for OCTAL rights like "644", optionnal
257
257
  # @option options [String] :owner file owner for folder, optionnal
258
258
  # @option options [String] :group file group for folder, optionnal
@@ -310,11 +310,20 @@ module Splash
310
310
  # @param [Hash] options
311
311
  # @option options [String] :host hostname
312
312
  # @option options [String] :port TCP port
313
+ # @option options [String] :url full URL, priority on :host and :port
313
314
  def verify_service(options ={})
314
315
  begin
316
+ if options[:url] then
317
+ uri = URI.parse(options[:url])
318
+ host = uri.host
319
+ port = uri.port
320
+ else
321
+ host = options[:host]
322
+ port = options[:port]
323
+ end
315
324
  Timeout::timeout(1) do
316
325
  begin
317
- s = TCPSocket.new(options[:host], options[:port])
326
+ s = TCPSocket.new(host, port)
318
327
  s.close
319
328
  return true
320
329
  rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
@@ -327,12 +336,11 @@ module Splash
327
336
  end
328
337
  #!@endgroup
329
338
 
330
-
331
339
  def format_response(data, format)
332
340
  response = case format
333
341
  when 'application/json' then JSON.pretty_generate(data)
334
342
  when 'text/x-yaml' then data.to_yaml
335
- else JSON.pretty_generate(data)
343
+ else data.to_yaml
336
344
  end
337
345
  return response
338
346
  end
@@ -346,6 +354,16 @@ module Splash
346
354
  return result[extension]
347
355
  end
348
356
 
357
+ # check if unicode must be used with term ENV
358
+ # @return [Boolean]
359
+ def check_unicode_term
360
+ return false unless ENV.include? "TERM"
361
+ if ENV.values_at("LC_ALL","LC_CTYPE","LANG").compact.first.include?("UTF-8") and ENV.values_at('TERM').first.include? "xterm" then
362
+ return true
363
+ else
364
+ return false
365
+ end
366
+ end
349
367
 
350
368
  end
351
369
  end
@@ -10,6 +10,7 @@ module Splash
10
10
  class Cli < Splash::Loggers::LoggerTemplate
11
11
 
12
12
  include Splash::Config
13
+ include Splash::Helpers
13
14
 
14
15
  # mapping of UTf-8 emoji for log levels or alias
15
16
  EMOJI = { :unknown => "\u{1F4A5}",
@@ -82,16 +83,7 @@ module Splash
82
83
  get_config.loggers[:cli][:color] = status
83
84
  end
84
85
 
85
- # check if unicode must be used with term ENV
86
- # @return [Boolean]
87
- def check_unicode_term
88
- return false unless ENV.include? "TERM"
89
- if ENV.values_at("LC_ALL","LC_CTYPE","LANG").compact.first.include?("UTF-8") and ENV.values_at('TERM').first.include? "xterm" then
90
- return true
91
- else
92
- return false
93
- end
94
- end
86
+
95
87
 
96
88
  end
97
89
 
@@ -6,6 +6,79 @@ module Splash
6
6
  # Logs namespace
7
7
  module Logs
8
8
 
9
+ class LogsNotifier
10
+
11
+ @@registry = Prometheus::Client::Registry::new
12
+ @@metric_missing = Prometheus::Client::Gauge.new(:logmissing, docstring: 'SPLASH metric log missing', labels: [:log ])
13
+ @@metric_count = Prometheus::Client::Gauge.new(:logerrors, docstring: 'SPLASH metric log error', labels: [:log ])
14
+ @@metric_lines = Prometheus::Client::Gauge.new(:loglines, docstring: 'SPLASH metric log lines numbers', labels: [:log ])
15
+ @@registry.register(@@metric_count)
16
+ @@registry.register(@@metric_missing)
17
+ @@registry.register(@@metric_lines)
18
+
19
+ def initialize(options={})
20
+ @config = get_config
21
+ @url = @config.prometheus_pushgateway_url
22
+ @name = options[:name]
23
+ @missing = options[:missing]
24
+ @lines = options[:lines]
25
+ @errors = options[:errors]
26
+ end
27
+
28
+ # send metrics to Prometheus PushGateway
29
+ # @return [Bool]
30
+ def notify
31
+ unless verify_service url: @url then
32
+ return { :case => :service_dependence_missing, :more => "Prometheus Notification not send."}
33
+ end
34
+ @@metric_missing.set(@missing, labels: { log: @name })
35
+ @@metric_count.set(@errors, labels: { log: @name })
36
+ @@metric_lines.set(@lines, labels: { log: @name })
37
+ hostname = Socket.gethostname
38
+ return Prometheus::Client::Push.new("Splash", hostname, @url).add(@@registry)
39
+ end
40
+
41
+ end
42
+
43
+ class LogsRecords
44
+ include Splash::Backends
45
+ include Splash::Constants
46
+ def initialize(name)
47
+ @name = name
48
+ @backend = get_backend :logs_trace
49
+ end
50
+
51
+ def purge(retention)
52
+ retention = {} if retention.nil?
53
+ if retention.include? :hours then
54
+ adjusted_datetime = DateTime.now - retention[:hours].to_f / 24
55
+ elsif retention.include? :hours then
56
+ adjusted_datetime = DateTime.now - retention[:days].to_i
57
+ else
58
+ adjusted_datetime = DateTime.now - DEFAULT_RETENTION
59
+ end
60
+
61
+ data = get_all_records
62
+
63
+ data.delete_if { |item|
64
+ DateTime.parse(item.keys.first) <= (adjusted_datetime)}
65
+ @backend.put key: @name, value: data.to_yaml
66
+ end
67
+
68
+ def add_record(record)
69
+ data = get_all_records
70
+ data.push({ DateTime.now.to_s => record })
71
+ @backend.put key: @name, value: data.to_yaml
72
+ end
73
+
74
+ def get_all_records(options={})
75
+ return (@backend.exist?({key: @name}))? YAML::load(@backend.get({key: @name})) : []
76
+ end
77
+
78
+ end
79
+
80
+
81
+
9
82
  # Log scanner and notifier
10
83
  class LogScanner
11
84
  include Splash::Constants
@@ -17,13 +90,7 @@ module Splash
17
90
  def initialize
18
91
  @logs_target = Marshal.load(Marshal.dump(get_config.logs))
19
92
  @config = get_config
20
- @registry = Prometheus::Client::Registry::new
21
- @metric_count = Prometheus::Client::Gauge.new(:logerrors, docstring: 'SPLASH metric log error', labels: [:log ])
22
- @metric_missing = Prometheus::Client::Gauge.new(:logmissing, docstring: 'SPLASH metric log missing', labels: [:log ])
23
- @metric_lines = Prometheus::Client::Gauge.new(:loglines, docstring: 'SPLASH metric log lines numbers', labels: [:log ])
24
- @registry.register(@metric_count)
25
- @registry.register(@metric_missing)
26
- @registry.register(@metric_lines)
93
+
27
94
  end
28
95
 
29
96
 
@@ -56,23 +123,30 @@ module Splash
56
123
  # @return [Hash] Exiter case :quiet_exit
57
124
  def notify(options = {})
58
125
  log = get_logger
59
- unless verify_service host: @config.prometheus_pushgateway_host ,port: @config.prometheus_pushgateway_port then
126
+ unless verify_service url: @config.prometheus_pushgateway_url then
60
127
  return { :case => :service_dependence_missing, :more => "Prometheus Notification not send." }
61
128
  end
62
129
  session = (options[:session]) ? options[:session] : log.get_session
63
130
  log.info "Sending metrics to Prometheus Pushgateway", session
64
131
  @logs_target.each do |item|
65
- missing = (item[:status] == :missing)? 1 : 0
66
- log.item "Sending metrics for #{item[:log]}", session
67
- @metric_count.set(item[:count], labels: { log: item[:log] })
68
- @metric_missing.set(missing, labels: { log: item[:log] })
132
+ logsrec = LogsRecords::new item[:label]
133
+ errors = (item[:count])? item[:count] : 0
69
134
  lines = (item[:lines])? item[:lines] : 0
70
- @metric_lines.set(lines, labels: { log: item[:log] })
135
+ missing = (item[:status] = :missing)? 1 : 0
136
+ file = item[:log]
137
+ logsrec.purge(item[:retention])
138
+ logsrec.add_record :status => item[:status],
139
+ :errors => errors,
140
+ :lines => lines,
141
+ :file => file
142
+
143
+ logsmonitor = LogsNotifier::new({name: item[:label], missing: missing, file: file, errors: errors, lines: lines})
144
+ if logsmonitor.notify then
145
+ log.ok "Sending metrics for log #{file} to Prometheus Pushgateway", session
146
+ else
147
+ log.ko "Failed to send metrics for log #{file} to Prometheus Pushgateway", session
148
+ end
71
149
  end
72
- hostname = Socket.gethostname
73
- url = "http://#{@config.prometheus_pushgateway_host}:#{@config.prometheus_pushgateway_port}/#{@config.prometheus_pushgateway_path}"
74
- Prometheus::Client::Push.new('Splash',hostname, url).add(@registry)
75
- log.ok "Sending to Prometheus PushGateway done.", session
76
150
  return {:case => :quiet_exit }
77
151
  end
78
152
 
@@ -6,6 +6,81 @@ module Splash
6
6
  # Processes namespace
7
7
  module Processes
8
8
 
9
+
10
+ class ProcessNotifier
11
+
12
+ @@registry = Prometheus::Client::Registry::new
13
+ @@metric_status = Prometheus::Client::Gauge.new(:process_status, docstring: 'SPLASH metric process status', labels: [:process ])
14
+ @@metric_cpu_percent = Prometheus::Client::Gauge.new(:process_cpu_percent, docstring: 'SPLASH metric process CPU usage in percent', labels: [:process ])
15
+ @@metric_mem_percent = Prometheus::Client::Gauge.new(:process_mem_percent, docstring: 'SPLASH metric process MEM usage in percent', labels: [:process ])
16
+ @@registry.register(@@metric_status)
17
+ @@registry.register(@@metric_cpu_percent)
18
+ @@registry.register(@@metric_mem_percent)
19
+
20
+
21
+ def initialize(options={})
22
+ @config = get_config
23
+ @url = @config.prometheus_pushgateway_url
24
+ @name = options[:name]
25
+ @status = options[:status]
26
+ @cpu_percent = options[:cpu_percent]
27
+ @mem_percent = options[:mem_percent]
28
+ end
29
+
30
+ # send metrics to Prometheus PushGateway
31
+ # @return [Bool]
32
+ def notify
33
+ unless verify_service url: @url then
34
+ return { :case => :service_dependence_missing, :more => "Prometheus Notification not send."}
35
+ end
36
+ @@metric_mem_percent.set(@mem_percent, labels: { process: @name })
37
+ @@metric_cpu_percent.set(@cpu_percent, labels: { process: @name })
38
+ @@metric_status.set(@status, labels: { process: @name })
39
+ hostname = Socket.gethostname
40
+ return Prometheus::Client::Push.new("Splash", hostname, @url).add(@@registry)
41
+ end
42
+
43
+ end
44
+
45
+
46
+ class ProcessRecords
47
+ include Splash::Backends
48
+ include Splash::Constants
49
+
50
+ def initialize(name)
51
+ @name = name
52
+ @backend = get_backend :process_trace
53
+ end
54
+
55
+ def purge(retention)
56
+ retention = {} if retention.nil?
57
+ if retention.include? :hours then
58
+ adjusted_datetime = DateTime.now - retention[:hours].to_f / 24
59
+ elsif retention.include? :hours then
60
+ adjusted_datetime = DateTime.now - retention[:days].to_i
61
+ else
62
+ adjusted_datetime = DateTime.now - DEFAULT_RETENTION
63
+ end
64
+
65
+ data = get_all_records
66
+
67
+ data.delete_if { |item|
68
+ DateTime.parse(item.keys.first) <= (adjusted_datetime)}
69
+ @backend.put key: @name, value: data.to_yaml
70
+ end
71
+
72
+ def add_record(record)
73
+ data = get_all_records
74
+ data.push({ DateTime.now.to_s => record })
75
+ @backend.put key: @name, value: data.to_yaml
76
+ end
77
+
78
+ def get_all_records(options={})
79
+ return (@backend.exist?({key: @name}))? YAML::load(@backend.get({key: @name})) : []
80
+ end
81
+
82
+ end
83
+
9
84
  # Processes scanner and notifier
10
85
  class ProcessScanner
11
86
  include Splash::Constants
@@ -17,14 +92,6 @@ module Splash
17
92
  def initialize
18
93
  @processes_target = Marshal.load(Marshal.dump(get_config.processes))
19
94
  @config = get_config
20
- @registry = Prometheus::Client::Registry::new
21
- @metric_status = Prometheus::Client::Gauge.new(:process_status, docstring: 'SPLASH metric process status', labels: [:process ])
22
- @metric_cpu_percent = Prometheus::Client::Gauge.new(:process_cpu_percent, docstring: 'SPLASH metric process CPU usage in percent', labels: [:process ])
23
- @metric_mem_percent = Prometheus::Client::Gauge.new(:process_mem_percent, docstring: 'SPLASH metric process MEM usage in percent', labels: [:process ])
24
- @registry.register(@metric_status)
25
- @registry.register(@metric_cpu_percent)
26
- @registry.register(@metric_mem_percent)
27
-
28
95
  end
29
96
 
30
97
 
@@ -58,23 +125,27 @@ module Splash
58
125
  # @return [Hash] Exiter case :quiet_exit
59
126
  def notify(options = {})
60
127
  log = get_logger
61
- unless verify_service host: @config.prometheus_pushgateway_host ,port: @config.prometheus_pushgateway_port then
128
+ unless verify_service url: @config.prometheus_pushgateway_url then
62
129
  return { :case => :service_dependence_missing, :more => "Prometheus Notification not send." }
63
130
  end
64
131
  session = (options[:session]) ? options[:session] : log.get_session
65
132
  log.info "Sending metrics to Prometheus Pushgateway", session
66
133
  @processes_target.each do |item|
134
+ processrec = ProcessRecords::new item[:process]
67
135
  missing = (item[:status] == :missing)? 1 : 0
68
- log.item "Sending metrics for #{item[:process]}", session
69
136
  val = (item[:status] == :running )? 1 : 0
70
- @metric_status.set(val, labels: { process: item[:process] })
71
- @metric_cpu_percent.set(item[:cpu], labels: { process: item[:process] })
72
- @metric_mem_percent.set(item[:mem], labels: { process: item[:process] })
137
+ processrec.purge(item[:retention])
138
+ processrec.add_record :status => item[:status],
139
+ :cpu_percent => item[:cpu],
140
+ :mem_percent => item[:mem] ,
141
+ :process => item[:process]
142
+ processmonitor = ProcessNotifier::new({name: item[:process], status: val , cpu_percent: item[:cpu], mem_percent: item[:mem]})
143
+ if processmonitor.notify then
144
+ log.ok "Sending metrics for process #{item[:process]} to Prometheus Pushgateway", session
145
+ else
146
+ log.ko "Failed to send metrics for process #{item[:process]} to Prometheus Pushgateway", session
147
+ end
73
148
  end
74
- hostname = Socket.gethostname
75
- url = "http://#{@config.prometheus_pushgateway_host}:#{@config.prometheus_pushgateway_port}/#{@config.prometheus_pushgateway_path}"
76
- Prometheus::Client::Push.new('Splash',hostname, url).add(@registry)
77
- log.ok "Sending to Prometheus PushGateway done.", session
78
149
  return {:case => :quiet_exit }
79
150
  end
80
151
 
@@ -0,0 +1,105 @@
1
+ # coding: utf-8
2
+
3
+ # base Splash module
4
+ module Splash
5
+
6
+ # Splash Commands module/namespace
7
+ module Sequences
8
+
9
+ include Splash::Commands
10
+ include Splash::Config
11
+ include Splash::Loggers
12
+ include Splash::Exiter
13
+ include Splash::Transports
14
+
15
+ def run_seq(params = {})
16
+ list = get_config.sequences
17
+ name = params[:name].to_sym
18
+ acase = {}
19
+ seq_res = []
20
+ log = get_logger
21
+ log.info "beginnning Sequence execution : #{name}"
22
+ session = (params[:session])? params[:session] : get_session
23
+ if list.include? name then
24
+ seq_options = (list[name][:options].nil?)? {} : list[name][:options]
25
+ list[name][:definition].each do |step|
26
+ log.info "STEP : #{step[:step]}",session
27
+ if step[:on_host].nil? then
28
+ command = CommandWrapper::new(step[:command].to_s)
29
+ step[:callback] = true if step[:callback].nil?
30
+ step[:trace] = true if step[:trace].nil?
31
+ step[:notify] = true if step[:notify].nil?
32
+ step[:session] = session
33
+ acase = command.call_and_notify step
34
+ else
35
+ log.info "Remote execution of #{step[:command]} on #{step[:on_host]}", session
36
+ begin
37
+ transport = get_default_client
38
+ if transport.class == Hash and transport.include? :case then
39
+ return transport
40
+ else
41
+ acase = transport.execute({ :verb => :execute_command,
42
+ payload: {:name => step[:command].to_s},
43
+ :return_to => "splash.#{Socket.gethostname}.return",
44
+ :queue => "splash.#{step[:on_host]}.input" })
45
+ log.receive "return with exitcode #{acase[:exit_code]}", session
46
+ end
47
+ rescue Interrupt
48
+ acase = { case: :interrupt, more: "Remote command exection", exit_code: 33}
49
+ end
50
+
51
+ end
52
+ seq_res.push acase[:exit_code]
53
+ continue = (seq_options[:continue].nil?)? true : seq_options[:continue]
54
+ if acase[:exit_code] > 0 and continue == false then
55
+ acase[:more] = "Break execution on error, continue = false"
56
+ break
57
+ end
58
+ end
59
+ else
60
+ return { :case => :not_found, :more => "Sequence #{name} not configured" }
61
+ end
62
+
63
+ if seq_res.select {|res| res > 0}.count == seq_res.count then
64
+ acase = { case: :status_ko, more: "all execution failed" }
65
+ elsif seq_res.select {|res| res > 0}.count > 0 then
66
+ acase = { case: :status_ko, more: "some execution failed" }
67
+ else
68
+ acase = { case: :status_ok, more: "all execution successed" }
69
+ end
70
+ acase[:more] << " with remote call interrupt" if seq_res.include?(33)
71
+ return acase
72
+ end
73
+
74
+
75
+ def list_seq(options = {})
76
+ list = get_config.sequences
77
+ unless list.empty?
78
+ acase = { :case => :quiet_exit , :more => "List configured sequences"}
79
+ acase[:data] = list
80
+ return acase
81
+ else
82
+ return { :case => :not_found, :more => 'No sequences configured' }
83
+ end
84
+ end
85
+
86
+ def show_seq(options = {})
87
+ list = get_config.sequences
88
+ name = options[:name].to_sym
89
+ if list.include? name then
90
+ acase = { :case => :quiet_exit, :more => "Show specific sequence : #{name}"}
91
+ acase[:data] = list[name]
92
+ else
93
+ return { :case => :not_found, :more => "Sequence #{name} not configured" }
94
+ end
95
+ return acase
96
+ end
97
+
98
+ def schedule_seq(options = {})
99
+ acase = { :case => :quiet_exit, :more => "schedule" }
100
+ return acase
101
+
102
+ end
103
+
104
+ end
105
+ end