prometheus-splash 0.7.0 → 0.8.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.
@@ -7,7 +7,7 @@ module Splash
7
7
  module Constants
8
8
 
9
9
  # Current splash version
10
- VERSION = "0.7.0"
10
+ VERSION = "0.8.0"
11
11
 
12
12
  # the path to th config file, not overridable by config
13
13
  CONFIG_FILE = "/etc/splash.yml"
@@ -43,7 +43,10 @@ module Splash
43
43
  LICENSE="BSD-2-Clause"
44
44
 
45
45
  # the default prometheus pushgateway URL
46
- PROMETHEUS_PUSHGATEWAY_URL = 'http://localhost:9090/'
46
+ PROMETHEUS_PUSHGATEWAY_URL = 'http://localhost:9091/'
47
+
48
+ # the default prometheus Alertmanager URL
49
+ PROMETHEUS_ALERTMANAGER_URL = 'http://localhost:9092/'
47
50
 
48
51
  # the default prometheus URL
49
52
  PROMETHEUS_URL = "http://localhost:9090/"
@@ -84,6 +87,8 @@ module Splash
84
87
  # the default sdterr trace file
85
88
  WEBADMIN_STDERR_TRACE="stderr.txt"
86
89
 
90
+ # default transfer retention for trace
91
+ TRANSFER_DEFAULT_RETENTION=1
87
92
 
88
93
  end
89
94
  end
@@ -17,8 +17,8 @@ module Splash
17
17
 
18
18
  # metrics manager factory
19
19
  # @return [Splash::Daemon::Metrics::Manager]
20
- def get_metrics_manager
21
- return @@manager ||= Manager::new
20
+ def get_metrics_manager(session)
21
+ return @@manager ||= Manager::new(:session => session)
22
22
  end
23
23
 
24
24
  # Metrics Manager (collect and sending to Prometheus)
@@ -32,7 +32,8 @@ module Splash
32
32
  attr_reader :monitoring_processes_count
33
33
 
34
34
  # Constructor prepare prometheus-client, defined metrics and init attributes
35
- def initialize
35
+ def initialize(options ={})
36
+ @session = options[:session]
36
37
  @config = get_config
37
38
  @starttime = Time.now
38
39
  @execution_count = 0
@@ -75,12 +76,11 @@ module Splash
75
76
  # @return [Hash] Exiter case ( :service_dependence_missing , :quiet_exit)
76
77
  def notify
77
78
  log = get_logger
78
- session = get_session
79
79
  unless verify_service url: @config.prometheus_pushgateway_url then
80
80
  return { :case => :service_dependence_missing, :more => "Prometheus Notification not send." }
81
81
  end
82
82
 
83
- log.debug "Sending Splash self metrics to PushGateway." , session
83
+ log.ok "Sending Splash self metrics to PushGateway." , @session
84
84
  @metric_uptime.set uptime
85
85
  @metric_execution.set execution_count
86
86
  @metric_logs_monitoring.set monitoring_logs_count
@@ -89,7 +89,7 @@ module Splash
89
89
  hostname = Socket.gethostname
90
90
  url = @config.prometheus_pushgateway_url
91
91
  Prometheus::Client::Push.new('Splash',hostname, url).add(@registry)
92
- log.debug "Sending to Prometheus PushGateway done.", session
92
+ log.debug "Sending to Prometheus PushGateway done.", @session
93
93
  return {:case => :quiet_exit }
94
94
  end
95
95
 
@@ -32,20 +32,69 @@ module Splash
32
32
  def initialize(options = {})
33
33
  @log = get_logger
34
34
  self.extend Splash::Daemon::Metrics
35
- @metric_manager = get_metrics_manager
35
+ @session = get_session
36
+ @metric_manager = get_metrics_manager(@session)
36
37
  $stdout.sync = true
37
38
  $stderr.sync = true
38
39
  @server = Rufus::Scheduler::new
39
40
  @server.extend SchedulerHooks
40
41
  @config = get_config
42
+ @scheduling = options[:scheduling]
41
43
 
42
44
  @log.info "Splash Orchestrator starting :"
43
- if options[:scheduling] then
44
- @log.item "Initializing commands Scheduling."
45
+ if @scheduling then
46
+ @log.item "Initializing Sequences & commands Scheduling."
45
47
  init_commands_scheduling
46
48
  init_sequences_scheduling
47
49
  end
48
50
 
51
+ init_logs_monitoring_scheduling
52
+ init_process_monitoring_scheduling
53
+ init_metrics_scheduling
54
+ init_daemon_subscriber
55
+
56
+ end
57
+
58
+
59
+
60
+
61
+
62
+ # Stop the Splash daemon gracefully
63
+ # @return [hash] Exiter Case :quiet_exit
64
+ def terminate
65
+ @log.info "Splash daemon shutdown"
66
+ @server.shutdown
67
+ change_logger logger: :cli
68
+ splash_exit case: :quiet_exit
69
+ end
70
+
71
+ private
72
+
73
+ #prepare main daemon subscriber
74
+ def init_daemon_subscriber
75
+ hostname = Socket.gethostname
76
+ transport = get_default_subscriber queue: "splash.#{hostname}.input"
77
+ if transport.class == Hash and transport.include? :case then
78
+ splash_exit transport
79
+ end
80
+ transport.subscribe(:block => true) do |delivery_info, properties, body|
81
+ content = YAML::load(body)
82
+ session = get_session
83
+ content[:session] = session
84
+ if VERBS.include? content[:verb]
85
+ @log.receive "Valid remote order, verb : #{content[:verb].to_s}", session
86
+ res = self.send content[:verb], content
87
+ get_default_client.publish queue: content[:return_to], message: res.to_yaml
88
+ @log.send "Result to #{content[:return_to]}.", session
89
+ else
90
+ @log.receive "INVALID remote order, verb : #{content[:verb].to_s}", session
91
+ get_default_client.publish queue: content[:return_to], message: "Unkown verb #{content[:verb]}".to_yaml
92
+ end
93
+ end
94
+ end
95
+
96
+ #prepare logs monitoring sheduling
97
+ def init_logs_monitoring_scheduling
49
98
  if @config.logs.empty? then
50
99
  @log.item "No logs to monitor"
51
100
  else
@@ -64,7 +113,10 @@ module Splash
64
113
  end
65
114
  end
66
115
  end
116
+ end
67
117
 
118
+ #prepare process monitoring sheduling
119
+ def init_process_monitoring_scheduling
68
120
  if @config.processes.empty? then
69
121
  @log.item "No processes to monitor"
70
122
  else
@@ -83,49 +135,24 @@ module Splash
83
135
  end
84
136
  end
85
137
  end
138
+ end
86
139
 
87
140
 
141
+ #prepare metrics sheduling
142
+ def init_metrics_scheduling
88
143
  sched,value = @config.daemon_metrics_scheduling.flatten
89
144
  @log.item "Initializing Splash metrics notifications."
90
145
  @server.send sched,value do
91
146
  begin
147
+ @log.trigger "Splash Metrics monitoring for Scheduling : #{sched.to_s} #{value.to_s}", @session
92
148
  @metric_manager.notify
93
149
  rescue Errno::ECONNREFUSED
94
150
  @log.error "PushGateway seems to be done, please start it."
95
151
  end
96
152
  end
97
-
98
- hostname = Socket.gethostname
99
- transport = get_default_subscriber queue: "splash.#{hostname}.input"
100
- if transport.class == Hash and transport.include? :case then
101
- splash_exit transport
102
- end
103
- transport.subscribe(:block => true) do |delivery_info, properties, body|
104
- content = YAML::load(body)
105
- session = get_session
106
- content[:session] = session
107
- if VERBS.include? content[:verb]
108
- @log.receive "Valid remote order, verb : #{content[:verb].to_s}", session
109
- res = self.send content[:verb], content
110
- get_default_client.publish queue: content[:return_to], message: res.to_yaml
111
- @log.send "Result to #{content[:return_to]}.", session
112
- else
113
- @log.receive "INVALID remote order, verb : #{content[:verb].to_s}", session
114
- get_default_client.publish queue: content[:return_to], message: "Unkown verb #{content[:verb]}".to_yaml
115
- end
116
- end
117
153
  end
118
154
 
119
- # Stop the Splash daemon gracefully
120
- # @return [hash] Exiter Case :quiet_exit
121
- def terminate
122
- @log.info "Splash daemon shutdown"
123
- @server.shutdown
124
- change_logger logger: :cli
125
- splash_exit case: :quiet_exit
126
- end
127
155
 
128
- private
129
156
  # prepare commands Scheduling
130
157
  def init_commands_scheduling
131
158
  config = get_config.commands
@@ -139,7 +166,6 @@ module Splash
139
166
  execute command: command.to_s, session: session
140
167
  end
141
168
  end
142
-
143
169
  end
144
170
 
145
171
 
@@ -156,9 +182,25 @@ module Splash
156
182
  run_seq name: sequence.to_s, session: session
157
183
  end
158
184
  end
159
-
160
185
  end
161
186
 
187
+ # reset the orchestrator
188
+ # @return [Hash] Exiter case
189
+ def reset_orchestrator
190
+ @server.shutdown
191
+ @server = Rufus::Scheduler::new
192
+ @server.extend SchedulerHooks
193
+ @config = rehash_config
194
+ @log.info "Splash Orchestrator re-hashing :"
195
+ if @scheduling then
196
+ @log.item "Re-Initializing Sequences & commands Scheduling."
197
+ init_commands_scheduling
198
+ init_sequences_scheduling
199
+ end
200
+ init_logs_monitoring_scheduling
201
+ init_process_monitoring_scheduling
202
+ init_metrics_scheduling
203
+ end
162
204
 
163
205
 
164
206
  # execute_command verb : execute command specified in payload
@@ -174,9 +216,7 @@ module Splash
174
216
  return command.call_and_notify trace: true, notify: true, callback: true, session: options[:session]
175
217
  end
176
218
  end
177
-
178
219
  end
179
-
180
220
  end
181
221
  end
182
222
  end
@@ -16,7 +16,7 @@ module Splash
16
16
  include Splash::Loggers
17
17
 
18
18
  # list of known verbs for Splash orchestrator
19
- VERBS=[:ping,:list_commands,:execute_command,:ack_command, :shutdown]
19
+ VERBS=[:ping,:list_commands,:execute_command,:ack_command, :shutdown, :get_jobs, :reset]
20
20
 
21
21
  # shutdown verb : stop the Splash daemon gracefully
22
22
  # @param [Hash] content message content Hash Structure, ignored
@@ -46,6 +46,21 @@ module Splash
46
46
  return execute command: content[:payload][:name], ack: true
47
47
  end
48
48
 
49
+
50
+ # get_jobs verb : return list of scheduled jobs for internal scheduler
51
+ # @param [Hash] content message content Hash Structure, ignored
52
+ # @return [String] YAML dataset
53
+ def get_jobs(content)
54
+ return @server.jobs.to_yaml
55
+ end
56
+
57
+ # reset verb : reset the internal scheduler
58
+ # @param [Hash] content message content Hash Structure, ignored
59
+ # @return [String] "Scheduler reset" static
60
+ def reset(content)
61
+ return "Scheduler reset" if reset_orchestrator
62
+ end
63
+
49
64
  # execute_command verb : execute command specified in payload
50
65
  # @param [Hash] content message content Hash Structure, include mandatory payload[:name]
51
66
  # @return [Hash] Exiter case
@@ -17,8 +17,12 @@ module Splash
17
17
 
18
18
 
19
19
 
20
+
21
+
20
22
  # Rubygems
21
23
  begin
24
+ require 'net/ssh'
25
+ require 'net/scp'
22
26
  require 'prometheus/client'
23
27
  require 'prometheus/client/push'
24
28
  require 'thor'
@@ -35,6 +39,7 @@ module Splash
35
39
  require 'rest-client'
36
40
  require 'kramdown'
37
41
  require 'rack/reverse_proxy'
42
+ require 'tty-table'
38
43
 
39
44
 
40
45
  rescue Gem::GemNotFoundException
@@ -57,9 +62,9 @@ module Splash
57
62
 
58
63
  require 'splash/commands'
59
64
  require 'splash/sequences'
60
-
61
65
  require 'splash/logs'
62
66
  require 'splash/processes'
67
+ require 'splash/transfers'
63
68
 
64
69
  require 'splash/daemon'
65
70
  require 'splash/webadmin'
@@ -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
@@ -336,7 +336,6 @@ module Splash
336
336
  end
337
337
  #!@endgroup
338
338
 
339
-
340
339
  def format_response(data, format)
341
340
  response = case format
342
341
  when 'application/json' then JSON.pretty_generate(data)
@@ -355,6 +354,16 @@ module Splash
355
354
  return result[extension]
356
355
  end
357
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
358
367
 
359
368
  end
360
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
 
@@ -0,0 +1,224 @@
1
+ # coding: utf-8
2
+
3
+ # base Splash Module
4
+ module Splash
5
+
6
+ # Transfers module
7
+ module Transfers
8
+
9
+ include Splash::Config
10
+ include Splash::Loggers
11
+ include Splash::Helpers
12
+
13
+
14
+
15
+ class TxNotifier
16
+ @@registry = Prometheus::Client::Registry::new
17
+ @@metric_nbfiles = Prometheus::Client::Gauge.new(:txnbfiles, docstring: 'SPLASH metric transfer number of files')
18
+ @@metric_nbfiles_failed = Prometheus::Client::Gauge.new(:txnbfilesfailed, docstring: 'SPLASH metric transfer number of failed files')
19
+ @@metric_time = Prometheus::Client::Gauge.new(:txtime, docstring: 'SPLASH metric transfer execution time')
20
+ @@registry.register(@@metric_nbfiles)
21
+ @@registry.register(@@metric_nbfiles_failed)
22
+ @@registry.register(@@metric_time)
23
+
24
+ def initialize(options={})
25
+ @config = get_config
26
+ @url = @config.prometheus_pushgateway_url
27
+ @name = "tx_#{options[:name].to_s}"
28
+ @nbfiles = options[:nbfiles]
29
+ @nbfiles_failed = options[:nbfiles_failed]
30
+ @time = options[:time]
31
+ end
32
+
33
+ # send metrics to Prometheus PushGateway
34
+ # @return [Bool]
35
+ def notify
36
+ unless verify_service url: @url then
37
+ return { :case => :service_dependence_missing, :more => "Prometheus Notification not send."}
38
+ end
39
+ @@metric_nbfiles.set(@nbfiles)
40
+ @@metric_nbfiles_failed.set(@nbfiles_failed)
41
+ @@metric_time.set(@time)
42
+ hostname = Socket.gethostname
43
+ return Prometheus::Client::Push.new(@name, hostname, @url).add(@@registry)
44
+ end
45
+
46
+ end
47
+
48
+
49
+
50
+ class TxRecords
51
+ include Splash::Backends
52
+ def initialize(name)
53
+ @name = name
54
+ @backend = get_backend :transferts_trace
55
+ end
56
+
57
+ def purge(retention)
58
+ if retention.include? :hours then
59
+ adjusted_datetime = DateTime.now - retention[:hours].to_f / 24
60
+ elsif retention.include? :hours then
61
+ adjusted_datetime = DateTime.now - retention[:days].to_i
62
+ else
63
+ retention = TRANSFER_DEFAULT_RETENTION
64
+ end
65
+
66
+ data = get_all_records
67
+
68
+ data.delete_if { |item,value|
69
+ DateTime.parse(item) <= (adjusted_datetime) and value[:status] != :prepared}
70
+ @backend.put key: @name, value: data.to_yaml
71
+ end
72
+
73
+ def add_record(record)
74
+ data = get_all_records
75
+ data[DateTime.now.to_s] = record
76
+ @backend.put key: @name, value: data.to_yaml
77
+ end
78
+
79
+ def get_all_records
80
+ return (@backend.exist?({key: @name}))? YAML::load(@backend.get({key: @name})) : {}
81
+ end
82
+
83
+ def check_prepared
84
+ return :never_run_prepare unless @backend.exist?({key: @name})
85
+ return :never_prepare unless YAML::load(@backend.get({key: @name})).select {|item,value|
86
+ value[:status] == :prepared
87
+ }.count > 0
88
+ return :prepared
89
+ end
90
+
91
+ end
92
+
93
+ def run_txs(options = {})
94
+ log = get_logger
95
+ log.info 'Running Transfers'
96
+ count=0
97
+ get_config.transfers.each do |record|
98
+ txrec = TxRecords::new record[:name]
99
+ txrec.purge(record[:retention])
100
+ log.item "Execute : #{record[:name]}, #{record[:desc]}"
101
+ case txrec.check_prepared
102
+ when :prepared
103
+ if record[:type] == :push then
104
+ unless push record
105
+ count += 1
106
+ end
107
+ elsif record[:type] == :pull then
108
+ unless pull record
109
+ count += 1
110
+ end
111
+ else
112
+ log.ko "Transfer type unkown"
113
+ count += 1
114
+ end
115
+ when :never_prepare
116
+ log.ko "#{record[:name]} : Never prepared, ignored"
117
+ txrec.add_record :status => :never_prepared
118
+ count += 1
119
+ when :never_run_prepare
120
+ log.ko "#{record[:name]} : Never Executed and never prepared, ignored"
121
+ txrec.add_record :status => :never_prepared
122
+ count += 1
123
+ end
124
+ end
125
+ return {:case => :error_exit, :more => "#{count} Transfer(s) failed"} if count > 0
126
+ return {:case => :quiet_exit }
127
+ end
128
+
129
+
130
+
131
+ def prepare_tx(name)
132
+ log = get_logger
133
+ record = get_config.transfers.select { |item| item[:name] == name.to_sym }.first
134
+ home = Etc.getpwnam(record[:local][:user]).dir
135
+ identity = ::File::readlines("#{home}/.ssh/id_rsa.pub").first.chomp
136
+ folder = {:mode => "755",
137
+ :owner => record[:local][:user] ,
138
+ :group => Etc.getgrgid(Etc.getpwnam(record[:local][:user]).gid).name,
139
+ :name => record[:local][:path],
140
+ :path => record[:local][:path]}
141
+ log.info "Ensure local folder : #{record[:local][:path]}"
142
+ make_folder(folder) unless verify_folder(folder).empty?
143
+ begin
144
+ log.info "Ensure RSA Key sharing for local user : #{record[:local][:user]} to remote user : #{record[:remote][:user]}@#{record[:remote][:host]}"
145
+ ssh = Net::SSH.start(record[:remote][:host],record[:remote][:user])
146
+ output = ssh.exec!(%[
147
+ /bin/bash -cl '
148
+ umask 077;
149
+ mkdir #{record[:remote][:path]};
150
+ test -d ~/.ssh || mkdir ~/.ssh;
151
+ if [ ! -f ~/.ssh/authorized_keys -o `grep "#{identity}" ~/.ssh/authorized_keys 2> /dev/null | wc -l` -eq 0 ]; then echo "#{identity}" >> ~/.ssh/authorized_keys
152
+ fi'])
153
+ log.info "Prepare remote folder : #{record[:remote][:path]}"
154
+ log.info "Prepare data file for transfer : #{record[:name]}"
155
+ txrec = TxRecords::new record[:name]
156
+ txrec.add_record :status => :prepared
157
+ log.ok "Transfer : #{record[:name]} prepared successfully"
158
+ return {:case => :quiet_exit }
159
+ rescue Interrupt
160
+ splash_exit case: :interrupt, more: "Remote command exection"
161
+ rescue TTY::Reader::InputInterrupt
162
+ splash_exit case: :interrupt, more: "Remote command exection"
163
+ end
164
+ end
165
+
166
+
167
+
168
+ def save_data
169
+
170
+ end
171
+
172
+
173
+
174
+ def push(record)
175
+ config = get_config
176
+ log = get_logger
177
+ txrec = TxRecords::new record[:name]
178
+ start = Time.now
179
+ res = true
180
+ count = 0
181
+ done =[]
182
+ start_date = DateTime.now.to_s
183
+ list = Dir.glob("#{record[:local][:path]}/#{record[:pattern]}")
184
+ count = list.count
185
+ log.arrow "Transfering #{count} file(s)"
186
+
187
+ begin
188
+ scp = Net::SCP.start(record[:remote][:host],record[:remote][:user])
189
+ list.each do|f|
190
+ log.arrow "Copy file : #{f} to #{record[:remote][:user]}@#{record[:remote][:host]}:#{record[:remote][:path]}"
191
+ scp.upload! f, record[:remote][:path]
192
+ done.push f
193
+ if record[:backup] then
194
+ log.arrow "File #{f} backuped"
195
+ FileUtils::mv(f, "#{f}.#{Time.now.getutc.to_i}")
196
+ else
197
+ FileUtils::unlink(f)
198
+ end
199
+ end
200
+
201
+ rescue
202
+ res = false
203
+ end
204
+
205
+ end_date = DateTime.now.to_s
206
+ time = Time.now - start
207
+ status = (res)? :success : :failure
208
+ txrec.add_record :status => status,
209
+ :end_date => end_date,
210
+ :time => time,
211
+ :count => count,
212
+ :wanted => list,
213
+ :done => done
214
+ count_failed = list.count - done.count
215
+ txmonitor = TxNotifier::new({name: record[:name], nbfiles: count,nbfiles_failed: count_failed, time: time})
216
+ if txmonitor.notify then
217
+ log.ok "Sending metrics to Prometheus Pushgateway"
218
+ else
219
+ log.ko "Failed to send metrics to Prometheus Pushgateway"
220
+ end
221
+ return res
222
+ end
223
+ end
224
+ end