prometheus-splash 0.7.0 → 0.8.0

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