bmc-daemon-lib 0.2.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cd041b445612d4933a4f5f69ccb0158051892ed7
4
- data.tar.gz: 5a4a9aa4dd013fb2369498d46380d5d815ebdaba
3
+ metadata.gz: 51d2fdee16ca885f005c34f7c8397a9d9204b43d
4
+ data.tar.gz: 4ec68d9f87a93f171cd02057af392500b11be85a
5
5
  SHA512:
6
- metadata.gz: b98b2147d8abeb9b4f6e5214b363a14142c3beb7feff0d673b5b387a05d395f0b986f06df2735e7ea86b6dcb96c5994b01c5cd3568581a426fa2b4e1d552a813
7
- data.tar.gz: 454958f16430d0be771750e5b88244d9619d3eb7e425b9c7f23bdce810fc2988c5ea22d01efc1a5b37d7c43c2c04be16894608009d61fc6ba525142860229d39
6
+ metadata.gz: 84f136ee87bec96835b716df93dc1faf3f3d49c9094f173928ac8b083bfb29b45cd582f7747430f2af5a2754d9c8da1ba6d8729ad54be1cbf2042a5969fa3855
7
+ data.tar.gz: f578421f1565e0a1cdbf15bb39eefa039e11e49dfa8173f0d825fd7a32ffacc6e703dfeb63538162989b362e864cfd9be730282ab1f37ecbe6e4cace6983963b
data/Gemfile.lock CHANGED
@@ -1,13 +1,17 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bmc-daemon-lib (0.2.0)
4
+ bmc-daemon-lib (0.3.0)
5
+ bunny
5
6
  chamber (~> 2.9)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
11
+ amq-protocol (2.0.1)
10
12
  ast (2.3.0)
13
+ bunny (2.5.1)
14
+ amq-protocol (>= 2.0.1)
11
15
  chamber (2.9.0)
12
16
  hashie (~> 3.3)
13
17
  thor (~> 0.19.1)
@@ -22,7 +26,7 @@ GEM
22
26
  rspec-core (~> 3.5.0)
23
27
  rspec-expectations (~> 3.5.0)
24
28
  rspec-mocks (~> 3.5.0)
25
- rspec-core (3.5.1)
29
+ rspec-core (3.5.2)
26
30
  rspec-support (~> 3.5.0)
27
31
  rspec-expectations (3.5.0)
28
32
  diff-lcs (>= 1.2.0, < 2.0)
@@ -31,7 +35,7 @@ GEM
31
35
  diff-lcs (>= 1.2.0, < 2.0)
32
36
  rspec-support (~> 3.5.0)
33
37
  rspec-support (3.5.0)
34
- rubocop (0.41.2)
38
+ rubocop (0.42.0)
35
39
  parser (>= 2.3.1.1, < 3.0)
36
40
  powerpack (~> 0.1)
37
41
  rainbow (>= 1.99.1, < 3.0)
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  Gem::Specification.new do |spec|
3
3
  # Project version
4
- spec.version = "0.2.0"
4
+ spec.version = "0.3.0"
5
5
 
6
6
  # Project description
7
7
  spec.name = "bmc-daemon-lib"
@@ -27,4 +27,5 @@ Gem::Specification.new do |spec|
27
27
 
28
28
  # Runtime dependencies
29
29
  spec.add_runtime_dependency "chamber", "~> 2.9"
30
+ spec.add_runtime_dependency "bunny"
30
31
  end
@@ -10,3 +10,4 @@ require_relative "bmc-daemon-lib/logger_formatter"
10
10
  require_relative "bmc-daemon-lib/logger_helper"
11
11
  require_relative "bmc-daemon-lib/logger_pool"
12
12
  require_relative "bmc-daemon-lib/worker_base"
13
+ require_relative "bmc-daemon-lib/mq_consumer"
@@ -81,7 +81,9 @@ module BmcDaemonLib
81
81
  Encoding.default_external = "utf-8"
82
82
 
83
83
  # Init New Relic
84
- newrelic_logfile = File.expand_path(Conf[:logs][:newrelic].to_s, Conf[:logs][:path].to_s)
84
+ logs_newrelic = Conf.at :logs, :newrelic
85
+ logs_path = Conf.at :logs, :path
86
+ newrelic_logfile = File.expand_path logs_newrelic.to_s, logs_path.to_s
85
87
  prepare_newrelic self[:newrelic], newrelic_logfile
86
88
 
87
89
  # Try to access any key to force parsing of the files
@@ -171,16 +173,15 @@ module BmcDaemonLib
171
173
  ENV["NEW_RELIC_MONITOR_MODE"] = "true"
172
174
 
173
175
  # Build NewRelic app_name if not provided as-is
174
- if section[:app_name]
175
- ENV["NEW_RELIC_APP_NAME"] = section[:app_name].to_s
176
- else
176
+ if !section[:app_name]
177
177
  stack = []
178
178
  stack << (section[:prefix] || @app_name)
179
179
  stack << section[:platform] if section[:platform]
180
180
  stack << @app_env
181
181
  text = stack.join('-')
182
- ENV["NEW_RELIC_APP_NAME"] = "#{text}-#{host};#{text}"
182
+ section[:app_name] = "#{text}; #{text}-#{host}"
183
183
  end
184
+ ENV["NEW_RELIC_APP_NAME"] = section[:app_name].to_s
184
185
 
185
186
  # Enable module
186
187
  ENV["NEWRELIC_AGENT_ENABLED"] = "true"
@@ -25,16 +25,25 @@ module BmcDaemonLib
25
25
 
26
26
  # Builds prefix if LOG_PREFIX_FORMAT defined and caller has log_prefix method to provide values
27
27
  def build_prefix
28
+ # Skip if no format defined
29
+ return unless defined?('LOG_PREFIX_FORMAT')
30
+ return unless LOG_PREFIX_FORMAT.is_a? String
31
+
28
32
  # Skip if no values from user class
29
33
  return unless respond_to?(:log_prefix, true)
30
34
  values = log_prefix
31
35
 
32
- # Skip if no format defined
33
- return unless defined?('LOG_PREFIX_FORMAT')
34
- return unless LOG_PREFIX_FORMAT.is_a? String
36
+ # Change to an array if not already
37
+ values = [values] if values.is_a? String
38
+
39
+ # Stop if still not an array
40
+ return unless values.is_a? Array
41
+
42
+ # Finally format the string
43
+ return LOG_PREFIX_FORMAT % values.map(&:to_s)
35
44
 
36
- # Build prefix string
37
- LOG_PREFIX_FORMAT % values.map(&:to_s)
45
+ rescue ArgumentError
46
+ return "INVALID_FORMAT"
38
47
  end
39
48
 
40
49
  def build_messages severity, message, details = nil
@@ -6,7 +6,9 @@ module BmcDaemonLib
6
6
  class LoggerPool
7
7
  include Singleton
8
8
 
9
- def get pipe
9
+ def get pipe = nil
10
+ pipe = :default if pipe.to_s.blank?
11
+
10
12
  @loggers ||= {}
11
13
  @loggers[pipe] ||= create(pipe)
12
14
  end
@@ -31,7 +33,7 @@ module BmcDaemonLib
31
33
 
32
34
  def logfile pipe
33
35
  # Disabled if no valid config
34
- return nil unless Conf[:logs].is_a?(Hash)
36
+ return nil unless Conf[:logs].is_a?(Hash) && Conf.at(:logs, pipe)
35
37
 
36
38
  # Compute logfile and check if we can write there
37
39
  logfile = File.expand_path(Conf[:logs][pipe].to_s, Conf[:logs][:path].to_s)
@@ -0,0 +1,168 @@
1
+ require "bunny"
2
+
3
+ module BmcDaemonLib
4
+ # class ShouterResponseError < StandardError; end
5
+ # class ShouterChannelClosed < StandardError; end
6
+ # class ShouterPreconditionFailed < StandardError; end
7
+ # class ShouterInterrupted < StandardError; end
8
+ # class EndpointTopicContext < StandardError; end
9
+ class EndpointConnexionContext < StandardError; end
10
+ class EndpointConnectionError < StandardError; end
11
+ class EndpointSubscribeContext < StandardError; end
12
+ class EndpointSubscribeError < StandardError; end
13
+
14
+ class MqConsumer
15
+ include LoggerHelper
16
+ attr_reader :logger
17
+
18
+ protected
19
+
20
+ def log_prefix
21
+ self.class.name.split('::').last
22
+ end
23
+
24
+ def subscribe_on_queue name
25
+ info "use_queue [#{name}]"
26
+
27
+ # Queue for this rule
28
+ @queue = @channel.queue(name, auto_delete: false, durable: true)
29
+
30
+ # Create consumer on this queue
31
+ @queue.subscribe(manual_ack: AMQP_MANUAL_ACK, on_cancellation: :consumer_cancelled) do |delivery_info, metadata, payload|
32
+ # Prepare data
33
+ msg_exchange = delivery_info.exchange
34
+ msg_rkey = delivery_info.routing_key.force_encoding('UTF-8')
35
+ msg_tag = delivery_info.delivery_tag
36
+
37
+ msg_headers = metadata.headers || {}
38
+
39
+ # Extract payload
40
+ msg_data = payload_parse payload, metadata.content_type
41
+
42
+ # Announce
43
+ announce msg_rkey, msg_tag, msg_data, metadata, msg_exchange, payload.bytesize
44
+
45
+ # Hand to the callback
46
+ receive msg_rkey, msg_tag, msg_data, metadata, delivery_info
47
+ end
48
+ end
49
+
50
+ def announce msg_rkey, msg_tag, msg_data, metadata, msg_exchange, payload_bytesize
51
+ # Prepare data
52
+ msg_headers = metadata.headers || {}
53
+
54
+ # Announce match
55
+ log_message MSG_RECV, msg_exchange, msg_rkey, msg_data, {
56
+ 'channel.dtag' => "#{@channel.id}.#{msg_tag}",
57
+ 'app-id' => metadata.app_id,
58
+ 'content-type' => metadata.content_type,
59
+ 'delay (ms)' => extract_delay(msg_headers),
60
+ 'body size' => format_bytes(payload_bytesize, "B"),
61
+ }
62
+ end
63
+
64
+ def bind_on topic, route
65
+ # Exchange to this rule
66
+ exchange = @channel.topic(topic, durable: true, persistent: false)
67
+
68
+ info "bind_on [#{topic}] [#{route}] > [#{@queue.name}]"
69
+ @queue.bind exchange, routing_key: route
70
+ end
71
+
72
+ def consumer_cancelled all={}
73
+ error "consumer cancelled remotely: #{all.inspect}"
74
+ end
75
+
76
+ # Start connexion to RabbitMQ
77
+ def connect_to busconf
78
+ fail PushyDaemon::EndpointConnexionContext, "invalid bus host/port" unless busconf
79
+ info "connecting to bus", {
80
+ broker: busconf,
81
+ recover: AMQP_RECOVERY_INTERVAL,
82
+ heartbeat: AMQP_HEARTBEAT_INTERVAL,
83
+ prefetch: AMQP_PREFETCH
84
+ }
85
+ conn = Bunny.new busconf.to_s,
86
+ logger: @logger,
87
+ # heartbeat: :server,
88
+ automatically_recover: true,
89
+ network_recovery_interval: AMQP_RECOVERY_INTERVAL,
90
+ heartbeat_interval: AMQP_HEARTBEAT_INTERVAL,
91
+ read_write_timeout: AMQP_HEARTBEAT_INTERVAL*2
92
+ conn.start
93
+
94
+ rescue Bunny::TCPConnectionFailedForAllHosts, Bunny::AuthenticationFailureError, AMQ::Protocol::EmptyResponseError => e
95
+ fail PushyDaemon::EndpointConnectionError, "error connecting (#{e.class})"
96
+ rescue StandardError => e
97
+ fail PushyDaemon::EndpointConnectionError, "unknow (#{e.inspect})"
98
+ else
99
+ return conn
100
+ end
101
+
102
+ def identifier len
103
+ rand(36**len).to_s(36)
104
+ end
105
+
106
+ def log_message msg_way, msg_exchange, msg_key, msg_body = [], msg_attrs = {}
107
+ # Message header
108
+ info sprintf("%3s %-15s %s", msg_way, msg_exchange, msg_key)
109
+
110
+ # Body lines
111
+ if msg_body.is_a?(Enumerable) && !msg_body.empty?
112
+ body_json = JSON.pretty_generate(msg_body)
113
+ log_debug nil, body_json.lines
114
+ end
115
+
116
+ # Attributes lines
117
+ log_debug nil, msg_attrs if msg_attrs
118
+ end
119
+
120
+ def extract_delay msg_headers
121
+ return unless msg_headers['sent_at']
122
+
123
+ # Extract sent_at header
124
+ sent_at = Time.iso8601(msg_headers['sent_at']) rescue nil
125
+ # log_info "sent_at : #{sent_at.to_f}"
126
+ # log_info "timenow : #{Time.now.to_f}"
127
+
128
+ # Compute delay
129
+ return ((Time.now - sent_at)*1000).round(2)
130
+ end
131
+
132
+ def format_bytes number, unit="", decimals = 0
133
+ return "&Oslash;" if number.nil? || number.to_f.zero?
134
+
135
+ units = ["", "k", "M", "G", "T", "P" ]
136
+ index = ( Math.log(number) / Math.log(2) ).to_i / 10
137
+ converted = number.to_f / (1024 ** index)
138
+
139
+ truncated = converted.round(decimals)
140
+ return "#{truncated} #{units[index]}#{unit}"
141
+ end
142
+
143
+ def receive delivery_info, metadata, payload
144
+ debug "MqConsumer.receive"
145
+ end
146
+
147
+ def payload_parse payload, content_type #, fields = []
148
+ # Force encoding (pftop...)
149
+ utf8payload = payload.to_s.force_encoding('UTF-8')
150
+
151
+ # Parse payload if content-type provided
152
+ case content_type
153
+ when "application/json"
154
+ return JSON.parse utf8payload rescue nil
155
+ when "text/plain"
156
+ return utf8payload.to_s
157
+ else
158
+ return utf8payload
159
+ end
160
+
161
+ # Handle body parse errors
162
+ rescue Encoding::UndefinedConversionError => e
163
+ log_error "parse: JSON PARSE ERROR: #{e.inspect}"
164
+ return {}
165
+ end
166
+
167
+ end
168
+ end
@@ -16,10 +16,8 @@ module BmcDaemonLib
16
16
  @config = {}
17
17
 
18
18
  # Set thread context
19
- @pool = pool
20
- @wid = wid
21
- Thread.current.thread_variable_set :pool, pool
22
- Thread.current.thread_variable_set :wid, wid
19
+ Thread.current.thread_variable_set :pool, (@pool = pool)
20
+ Thread.current.thread_variable_set :wid, (@wid = wid)
23
21
  Thread.current.thread_variable_set :started_at, Time.now
24
22
  worker_status WORKER_STATUS_STARTING
25
23
 
@@ -78,11 +76,11 @@ module BmcDaemonLib
78
76
  return unless @log_worker_status_changes
79
77
 
80
78
  # Log this status change
81
- if job.is_a?(Job)
82
- log_info "status [#{status}] on job[#{job.id}] status[#{job.status}] error[#{job.error}]"
83
- else
84
- log_info "status [#{status}]"
85
- end
79
+ # if defined?'Job' && job.is_a?(Job)
80
+ # log_info "status [#{status}] on job[#{job.id}] status[#{job.status}] error[#{job.error}]"
81
+ # else
82
+ log_info "status [#{status}]"
83
+ # end
86
84
  end
87
85
 
88
86
  def worker_jid jid
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bmc-daemon-lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bruno MEDICI
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-21 00:00:00.000000000 Z
11
+ date: 2016-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '2.9'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bunny
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  description: 'Shared utilities to build a daemon: logger, configuration, helpers'
84
98
  email: bmc-daemon-lib@bmconseil.com
85
99
  executables: []
@@ -98,6 +112,7 @@ files:
98
112
  - lib/bmc-daemon-lib/logger_formatter.rb
99
113
  - lib/bmc-daemon-lib/logger_helper.rb
100
114
  - lib/bmc-daemon-lib/logger_pool.rb
115
+ - lib/bmc-daemon-lib/mq_consumer.rb
101
116
  - lib/bmc-daemon-lib/worker_base.rb
102
117
  homepage: http://github.com/bmedici/bmc-daemon-lib
103
118
  licenses: