prometheus-splash 0.0.3 → 0.1.0

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.
Binary file
Binary file
data/bin/splash CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby -W:no-deprecated
2
-
2
+ require 'socket'
3
+ require 'yaml'
4
+ require 'thread'
3
5
 
4
6
  begin
5
7
  require 'prometheus/client'
@@ -20,6 +22,7 @@ require 'splash/helpers'
20
22
  require 'splash/config'
21
23
  require 'splash/templates'
22
24
  require 'splash/backends'
25
+ require 'splash/transports'
23
26
 
24
27
  require 'splash/commands'
25
28
  require 'splash/logs'
@@ -38,21 +41,24 @@ module CLISplash
38
41
  include Splash::Config
39
42
  include Splash::Backends
40
43
 
41
- desc "run NAME", "run for command/sequence or ack result"
44
+ desc "execute NAME", "run for command/sequence or ack result"
42
45
  long_desc <<-LONGDESC
43
- Running command or sequence or ack result
44
- with --no-trace prevent storing execution trace in TRACE_PATH (see config file)
46
+ execute command or sequence or ack result
47
+ with --no-trace prevent storing execution trace in configured backend (see config file)
45
48
  with --ack, notify errorcode=0 to Prometheus PushGateway
46
49
  with --no-notify, bypass Prometheus notification
50
+ with --no-callback, never execute callback (:on_failure, :on_success)
51
+ never follow sequences
47
52
  LONGDESC
48
53
  option :trace, :type => :boolean, :default => true
49
54
  option :ack, :type => :boolean, negate: false
50
55
  option :notify, :type => :boolean, :default => true
51
- def wrap(name)
56
+ option :callback, :type => :boolean, :default => true
57
+ def execute(name)
52
58
  if is_root? then
53
59
  command = Splash::CommandWrapper::new(name)
54
60
  command.ack if options[:ack]
55
- command.call_and_notify trace: options[:trace], notify: options[:notify]
61
+ command.call_and_notify trace: options[:trace], notify: options[:notify], callback: options[:callback]
56
62
  else
57
63
  $stderr.puts "Command wrapping need to be run as root"
58
64
  exit 60
@@ -124,14 +130,24 @@ module CLISplash
124
130
 
125
131
 
126
132
  desc "lastrun COMMAND", "Show last running result for specific configured command COMMAND"
133
+ long_desc <<-LONGDESC
134
+ Show last running result for specific configured command COMMAND
135
+ with --hostname <HOSTNAME>, an other Splash monitored server (only with Redis backend configured)
136
+ LONGDESC
137
+ option :hostname, :type => :string
127
138
  def lastrun(command)
139
+ backend = get_backend :execution_trace
140
+ redis = (backend.class == Splash::Backends::Redis)? true : false
141
+ if not redis and options[:hostname] then
142
+ $stderr.puts "Remote execution report request only possible with Redis backend"
143
+ end
128
144
  list = get_config.commands
129
145
  if list.keys.include? command.to_sym then
130
146
  print "Splash command #{command} previous execution report:\n\n"
131
- backend = get_backend :execution_trace
132
- key = "#{command}_trace.last"
133
- if backend.exist? key: key then
134
- print backend.get key: key
147
+ req = { :key => command}
148
+ req[:hostname] = options[:hostname] if options[:hostname]
149
+ if backend.exist? req then
150
+ print backend.get req
135
151
  else
136
152
  puts "Command not already runned."
137
153
  end
@@ -141,11 +157,58 @@ module CLISplash
141
157
  end
142
158
  end
143
159
 
160
+ desc "getreportlist COMMAND", "list all executions report results "
161
+ long_desc <<-LONGDESC
162
+ Show configured commands
163
+ with --pattern <SEARCH>, search type string, wilcard * (group) ? (char)
164
+ with --hostname <HOSTNAME>, an other Splash monitored server (only with Redis backend configured)
165
+ with --all, get all execution report for all servers (only with Redis backend configured)
166
+ --all and --hostname are exclusives
167
+ LONGDESC
168
+ option :pattern, :type => :string
169
+ option :hostname, :type => :string
170
+ option :all, :type => :boolean, :negate => false
171
+ def getreportlist
172
+ if options[:hostname] and options[:all] then
173
+ $stderr.puts "--all option imcompatible with --hostname"
174
+ exit 40
175
+ end
176
+ backend = get_backend :execution_trace
177
+ redis = (backend.class == Splash::Backends::Redis)? true : false
178
+ if not redis and (options[:hostname] or options[:all]) then
179
+ $stderr.puts "Remote execution report request only possible with Redis backend"
180
+ exit 40
181
+ end
182
+ pattern = (options[:pattern])? options[:pattern] : '*'
183
+ if options[:all] then
184
+ res = backend.listall pattern
185
+ elsif options[:hostname]
186
+ res = backend.list pattern, options[:hostname]
187
+ else
188
+ res = backend.list pattern
189
+ end
190
+ print "List of Executions reports :\n\n"
191
+ puts "Not reports found" if res.empty?
192
+ res.each do |item|
193
+ if options[:all]
194
+ host,command = item.split('#')
195
+ puts " * Command : #{command} @ host : #{host}"
196
+ else
197
+ puts " * Command : #{item}"
198
+ end
199
+ end
200
+ end
201
+
202
+
203
+
144
204
  end
145
205
 
146
206
 
207
+
208
+
147
209
  class CLIController < Thor
148
210
  include Splash::LogsMonitor::DaemonController
211
+ include Splash::Transports
149
212
 
150
213
  option :foreground, :type => :boolean
151
214
  desc "start", "Starting Logs Monitor Daemon"
@@ -166,6 +229,26 @@ module CLISplash
166
229
  exit errorcode
167
230
  end
168
231
 
232
+ desc "ping HOSTNAME", "send a ping to HOSTNAME daemon over transport (need an active tranport), Typicallly RabbitMQ"
233
+ def ping(hostname=Socket.gethostname)
234
+ puts "ctrl+c for interrupt"
235
+ queue = "splash.#{Socket.gethostname}.returncli"
236
+ order = {:verb => :ping, :payload => {:hostname => Socket.gethostname}, :return_to => queue}
237
+
238
+ lock = Mutex.new
239
+ condition = ConditionVariable.new
240
+ begin
241
+ get_default_subscriber(queue: queue).subscribe(timeout: 10) do |delivery_info, properties, payload|
242
+ puts YAML::load(payload)
243
+ lock.synchronize { condition.signal }
244
+ end
245
+ get_default_client.publish queue: "splash.#{hostname}.input", message: order.to_yaml
246
+ lock.synchronize { condition.wait(lock) }
247
+ rescue Interrupt
248
+ puts "Splash : ping : Interrupted by user. "
249
+ exit 33
250
+ end
251
+ end
169
252
 
170
253
  end
171
254
 
@@ -177,6 +260,11 @@ module CLISplash
177
260
  include Splash::Helpers
178
261
 
179
262
  desc "setup", "Setup installation fo Splash"
263
+ long_desc <<-LONGDESC
264
+ Setup installation fo Splash
265
+ with --preserve, preserve from reinstallation of the config
266
+ LONGDESC
267
+ option :preserve, :type => :boolean
180
268
  def setup
181
269
  errorcode = run_as_root :setupsplash
182
270
  exit errorcode
data/config/splash.yml CHANGED
@@ -5,19 +5,7 @@
5
5
  :templates:
6
6
  :execution:
7
7
  :path: /etc/splash_execution_report.tpl
8
- :tokens:
9
- - :date
10
- - :cmd_name
11
- - :cmd_line
12
- - :stdout
13
- - :stderr
14
- - :desc
15
- - :status
16
- - :exec_time
17
8
  :backends:
18
- :list:
19
- - :file
20
- - :redis
21
9
  :stores:
22
10
  :execution_trace:
23
11
  :type: :file
@@ -94,5 +82,5 @@
94
82
  :logs:
95
83
  - :log: /tmp/test
96
84
  :pattern: ERROR
97
- - :log: /var/log/message
98
- :pattern: error
85
+ - :log: /tmp/test2
86
+ :pattern: ERROR
@@ -8,25 +8,31 @@ module Splash
8
8
  end
9
9
 
10
10
  def list(pattern='*')
11
- return Dir.glob(pattern)
11
+ pattern = suffix_trace(pattern)
12
+ return Dir.glob("#{@path}/#{pattern}").map{|item| ::File.basename(item,".trace") }
12
13
  end
13
14
 
14
15
  def get(options)
15
- return ::File.readlines("#{@path}/#{options[:key]}").join
16
+ return ::File.readlines("#{@path}/#{suffix_trace(options[:key])}").join
16
17
  end
17
18
 
18
19
  def put(options)
19
- ::File.open("#{@path}/#{options[:key]}", 'w') { |file|
20
+ ::File.open("#{@path}/#{suffix_trace(options[:key])}", 'w') { |file|
20
21
  file.write options[:value]
21
22
  }
22
23
  end
23
24
 
24
25
  def del(options)
25
- ::File.unlink("#{@path}/#{options[:key]}") if File.exist?("#{@path}/#{options[:key]}")
26
+ ::File.unlink("#{@path}/#{suffix_trace(options[:key])}") if File.exist?("#{@path}/#{suffix_trace(options[:key])}")
26
27
  end
27
28
 
28
29
  def exist?(options)
29
- return ::File.exist?("#{@path}/#{options[:key]}")
30
+ return ::File.exist?("#{@path}/#{suffix_trace(options[:key])}")
31
+ end
32
+
33
+ private
34
+ def suffix_trace(astring)
35
+ return "#{astring}.trace"
30
36
  end
31
37
 
32
38
  end
@@ -1,30 +1,39 @@
1
1
  require "redis"
2
+ require "socket"
2
3
 
3
4
  module Splash
4
5
  module Backends
5
6
  class Redis
6
7
  include Splash::Config
7
8
  def initialize(store)
9
+ @hostname = Socket.gethostname
8
10
  @config = get_config[:backends][:stores][store]
9
11
  @store = ::Redis.new :host => @config[:host], :port => @config[:port], :db => @config[:base].to_i
10
12
  @redis_cli_cmd = `which redis-cli`
11
13
  @store.auth(@config[:auth]) if @config[:auth]
12
14
  end
13
15
 
14
- def list(pattern='*')
15
- return @store.keys pattern
16
+ def list(pattern='*', hostname = @hostname)
17
+ return @store.keys("#{hostname}##{pattern}").map{|item| item = remove_hostname(item)}
18
+ end
19
+
20
+ def listall(pattern='*')
21
+ return @store.keys(pattern)
16
22
  end
17
23
 
18
24
  def get(options)
19
- return @store.get(options[:key])
25
+ hostname = (options[:hostname])? options[:hostname] : @hostname
26
+ return @store.get(prefix_hostname(options[:key],hostname))
20
27
  end
21
28
 
22
29
  def put(options)
23
- @store.set options[:key], options[:value]
30
+ hostname = (options[:hostname])? options[:hostname] : @hostname
31
+ @store.set prefix_hostname(options[:key],hostname), options[:value]
24
32
  end
25
33
 
26
34
  def del(options)
27
- @store.del options[:key]
35
+ hostname = (options[:hostname])? options[:hostname] : @hostname
36
+ @store.del prefix_hostname(options[:key],hostname)
28
37
  end
29
38
 
30
39
  def flush
@@ -33,7 +42,20 @@ module Splash
33
42
  end
34
43
 
35
44
  def exist?(options)
36
- return ( not @store.get(options[:key]).nil?)
45
+ hostname = (options[:hostname])? options[:hostname] : @hostname
46
+ return ( not @store.get(prefix_hostname(options[:key],hostname)).nil?)
47
+ end
48
+
49
+ private
50
+ def prefix_hostname(key,hostname)
51
+ return "#{hostname}##{key}"
52
+ end
53
+
54
+
55
+ def remove_hostname(astring)
56
+ result = astring.split("#")
57
+ result.shift
58
+ return result.join("#")
37
59
  end
38
60
 
39
61
  end
@@ -47,6 +47,7 @@ module Splash
47
47
  def call_and_notify(options)
48
48
  puts "Executing command : '#{@name}' "
49
49
  start = Time.now
50
+ start_date = DateTime.now.to_s
50
51
  unless options[:trace] then
51
52
  puts " * Traceless execution"
52
53
  if @config.commands[@name.to_sym][:user] then
@@ -70,7 +71,8 @@ module Splash
70
71
  list_token: @config.execution_template_tokens,
71
72
  template_file: @config.execution_template_path)
72
73
 
73
- tp.date = DateTime.now.to_s
74
+ tp.start_date = start_date
75
+ tp.end_date = DateTime.now.to_s
74
76
  tp.cmd_name = @name
75
77
  tp.cmd_line = @config.commands[@name.to_sym][:command]
76
78
  tp.desc = @config.commands[@name.to_sym][:desc]
@@ -79,7 +81,7 @@ module Splash
79
81
  tp.stderr = stderr
80
82
  tp.exec_time = time.to_s
81
83
  backend = get_backend :execution_trace
82
- key = "#{@name}_trace.last"
84
+ key = @name
83
85
  backend.put key: key, value: tp.output
84
86
  exit_code = status.exitstatus
85
87
 
@@ -91,29 +93,32 @@ module Splash
91
93
  else
92
94
  puts " * Without Prometheus notification"
93
95
  end
94
- on_failure = (@config.commands[@name.to_sym][:on_failure])? @config.commands[@name.to_sym][:on_failure] : false
95
- on_success = (@config.commands[@name.to_sym][:on_success])? @config.commands[@name.to_sym][:on_success] : false
96
+ if options[:callback] then
97
+ on_failure = (@config.commands[@name.to_sym][:on_failure])? @config.commands[@name.to_sym][:on_failure] : false
98
+ on_success = (@config.commands[@name.to_sym][:on_success])? @config.commands[@name.to_sym][:on_success] : false
96
99
 
97
- if on_failure and exit_code > 0 then
98
- puts " * On failure callback : #{on_failure}"
99
- if @config.commands.keys.include? on_failure then
100
- @name = on_failure.to_s
101
- call_and_notify options
102
- else
103
- $stderr.puts "on_failure call error : configuration mistake : #{on_failure} command inexistant."
100
+ if on_failure and exit_code > 0 then
101
+ puts " * On failure callback : #{on_failure}"
102
+ if @config.commands.keys.include? on_failure then
103
+ @name = on_failure.to_s
104
+ call_and_notify options
105
+ else
106
+ $stderr.puts "on_failure call error : configuration mistake : #{on_failure} command inexistant."
107
+ end
104
108
  end
105
- end
106
- if on_success and exit_code == 0 then
107
- puts " * On success callback : #{on_success}"
108
- if @config.commands.keys.include? on_success then
109
- @name = on_success.to_s
110
- call_and_notify options
111
- else
112
- $stderr.puts "on_success call error : configuration mistake : #{on_success} command inexistant."
109
+ if on_success and exit_code == 0 then
110
+ puts " * On success callback : #{on_success}"
111
+ if @config.commands.keys.include? on_success then
112
+ @name = on_success.to_s
113
+ call_and_notify options
114
+ else
115
+ $stderr.puts "on_success call error : configuration mistake : #{on_success} command inexistant."
116
+ end
113
117
  end
118
+ else
119
+ puts " * Without callbacks sequences"
114
120
  end
115
121
 
116
-
117
122
  exit exit_code
118
123
  end
119
124
  end
data/lib/splash/config.rb CHANGED
@@ -18,7 +18,7 @@ module Splash
18
18
  self[:prometheus_pushgateway_port] = (config_from_file[:prometheus][:pushgateway][:port])? config_from_file[:prometheus][:pushgateway][:port] : PROMETHEUS_PUSHGATEWAY_PORT
19
19
  self[:daemon_process_name] = (config_from_file[:daemon][:process_name])? config_from_file[:daemon][:process_name] : DAEMON_PROCESS_NAME
20
20
  self[:daemon_logmon_scheduling] = (config_from_file[:daemon][:logmon_scheduling])? config_from_file[:daemon][:logmon_scheduling] : DAEMON_LOGMON_SCHEDULING
21
- self[:execution_template_tokens] = (config_from_file[:templates][:execution][:tokens])? config_from_file[:templates][:execution][:tokens] : EXECUTION_TEMPLATE_TOKENS_LIST
21
+ self[:execution_template_tokens] = EXECUTION_TEMPLATE_TOKENS_LIST
22
22
  self[:execution_template_path] = (config_from_file[:templates][:execution][:path])? config_from_file[:templates][:execution][:path] : EXECUTION_TEMPLATE
23
23
  self[:pid_path] = (config_from_file[:daemon][:paths][:pid_path])? config_from_file[:daemon][:paths][:pid_path] : DAEMON_PID_PATH
24
24
  self[:trace_path] = (config_from_file[:daemon][:paths][:trace_path])? config_from_file[:daemon][:paths][:trace_path] : TRACE_PATH
@@ -58,7 +58,7 @@ module Splash
58
58
  end
59
59
 
60
60
  def transports
61
- return self[:transport]
61
+ return self[:transports]
62
62
  end
63
63
 
64
64
  def daemon_logmon_scheduling
@@ -137,12 +137,16 @@ module Splash
137
137
  conf_in_path = search_file_in_gem "prometheus-splash", "config/splash.yml"
138
138
  full_res = 0
139
139
  puts "Splash -> setup : "
140
- print "* Installing Configuration file : #{CONFIG_FILE} : "
141
- if install_file source: conf_in_path, target: CONFIG_FILE, mode: "644", owner: Configuration.user_root, group: Configuration.group_root then
142
- puts "[OK]"
140
+ unless options[:preserve] then
141
+ print "* Installing Configuration file : #{CONFIG_FILE} : "
142
+ if install_file source: conf_in_path, target: CONFIG_FILE, mode: "644", owner: Configuration.user_root, group: Configuration.group_root then
143
+ puts "[OK]"
144
+ else
145
+ full_res =+ 1
146
+ puts "[KO]"
147
+ end
143
148
  else
144
- full_res =+ 1
145
- puts "[KO]"
149
+ puts "Config file preservation."
146
150
  end
147
151
  config = get_config
148
152
  report_in_path = search_file_in_gem "prometheus-splash", "templates/report.txt"
@@ -1,30 +1,50 @@
1
1
  module Splash
2
2
  module Constants
3
- VERSION = "0.0.3"
3
+ VERSION = "0.1.0"
4
4
 
5
+ # the path to th config file, not overridable by config
5
6
  CONFIG_FILE = "/etc/splash.yml"
7
+ # the default execution trace_path if backend file
6
8
  TRACE_PATH="/var/run/splash"
7
9
 
10
+
11
+ # default scheduling criteria for log monitoring
8
12
  DAEMON_LOGMON_SCHEDULING={ :every => '20s'}
13
+ # the display name of daemon in proc info (ps/top)
9
14
  DAEMON_PROCESS_NAME="Splash : daemon."
15
+ # the default pid file path
10
16
  DAEMON_PID_PATH="/var/run"
17
+ # the default pid file name
11
18
  DAEMON_PID_FILE="splash.pid"
19
+ # the default sdtout trace file
12
20
  DAEMON_STDOUT_TRACE="stdout.txt"
21
+ # the default sdterr trace file
13
22
  DAEMON_STDERR_TRACE="stderr.txt"
14
23
 
24
+ # the Author name
15
25
  AUTHOR="Romain GEORGES"
26
+ # the maintainer mail
16
27
  EMAIL = "gems@ultragreen.net"
28
+ # legal Copyright (c) 2020 Copyright Utragreen All Rights Reserved.
17
29
  COPYRIGHT="Ultragreen (c) 2020"
30
+ # type of licence
18
31
  LICENSE="BSD-2-Clause"
19
32
 
33
+ # the default prometheus pushgateway host
20
34
  PROMETHEUS_PUSHGATEWAY_HOST = "localhost"
35
+ # the default prometheus pushgateway port
21
36
  PROMETHEUS_PUSHGATEWAY_PORT = "9091"
22
37
 
38
+ # the default path fo execution report template
23
39
  EXECUTION_TEMPLATE="/etc/splash_execution_report.tpl"
24
- EXECUTION_TEMPLATE_TOKENS_LIST = [:date,:cmd_name,:cmd_line,:stdout,:stderr,:desc,:status,:exec_time]
25
40
 
41
+ # the list of authorized tokens for template, carefull override,
42
+ EXECUTION_TEMPLATE_TOKENS_LIST = [:end_date,:start_date,:cmd_name,:cmd_line,:stdout,:stderr,:desc,:status,:exec_time]
43
+
44
+ # backends default settings
26
45
  BACKENDS_STRUCT = { :list => [:file,:redis],
27
46
  :stores => { :execution_trace => { :type => :file, :path => "/var/run/splash" }}}
47
+ # transports default settings
28
48
  TRANSPORTS_STRUCT = { :list => [:rabbitmq],
29
49
  :active => :rabbitmq,
30
50
  :rabbitmq => { :url => 'amqp://localhost/'} }