prometheus-splash 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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/'} }