dtk-node-agent 0.5.10

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.
Files changed (62) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +18 -0
  4. data/README.md +42 -0
  5. data/bin/dtk-node-agent +16 -0
  6. data/dtk-node-agent.gemspec +38 -0
  7. data/lib/config/install.config +12 -0
  8. data/lib/dtk-node-agent/installer.rb +142 -0
  9. data/lib/dtk-node-agent/version.rb +3 -0
  10. data/mcollective_additions/plugins/README.md +1 -0
  11. data/mcollective_additions/plugins/v1.2/agent/discovery.rb +39 -0
  12. data/mcollective_additions/plugins/v1.2/agent/get_log_fragment.ddl +15 -0
  13. data/mcollective_additions/plugins/v1.2/agent/get_log_fragment.rb +79 -0
  14. data/mcollective_additions/plugins/v1.2/agent/git_access.ddl +9 -0
  15. data/mcollective_additions/plugins/v1.2/agent/git_access.rb +79 -0
  16. data/mcollective_additions/plugins/v1.2/agent/netstat.ddl +9 -0
  17. data/mcollective_additions/plugins/v1.2/agent/netstat.rb +34 -0
  18. data/mcollective_additions/plugins/v1.2/agent/puppet_apply.ddl +9 -0
  19. data/mcollective_additions/plugins/v1.2/agent/puppet_apply.rb +630 -0
  20. data/mcollective_additions/plugins/v1.2/agent/rpcutil.ddl +204 -0
  21. data/mcollective_additions/plugins/v1.2/agent/rpcutil.rb +101 -0
  22. data/mcollective_additions/plugins/v1.2/facts/pbuilder_facts.rb +35 -0
  23. data/mcollective_additions/plugins/v2.2/agent/dev_manager.ddl +9 -0
  24. data/mcollective_additions/plugins/v2.2/agent/dev_manager.rb +69 -0
  25. data/mcollective_additions/plugins/v2.2/agent/discovery.rb +39 -0
  26. data/mcollective_additions/plugins/v2.2/agent/dtk_node_agent_git_client.rb +94 -0
  27. data/mcollective_additions/plugins/v2.2/agent/execute_tests.ddl +9 -0
  28. data/mcollective_additions/plugins/v2.2/agent/execute_tests.rb +64 -0
  29. data/mcollective_additions/plugins/v2.2/agent/get_log_fragment.ddl +15 -0
  30. data/mcollective_additions/plugins/v2.2/agent/get_log_fragment.rb +79 -0
  31. data/mcollective_additions/plugins/v2.2/agent/git_access.ddl +9 -0
  32. data/mcollective_additions/plugins/v2.2/agent/git_access.rb +72 -0
  33. data/mcollective_additions/plugins/v2.2/agent/netstat.ddl +9 -0
  34. data/mcollective_additions/plugins/v2.2/agent/netstat.rb +34 -0
  35. data/mcollective_additions/plugins/v2.2/agent/ps.ddl +9 -0
  36. data/mcollective_additions/plugins/v2.2/agent/ps.rb +37 -0
  37. data/mcollective_additions/plugins/v2.2/agent/puppet_apply.ddl +9 -0
  38. data/mcollective_additions/plugins/v2.2/agent/puppet_apply.rb +633 -0
  39. data/mcollective_additions/plugins/v2.2/agent/puppet_cancel.ddl +10 -0
  40. data/mcollective_additions/plugins/v2.2/agent/puppet_cancel.rb +78 -0
  41. data/mcollective_additions/plugins/v2.2/agent/rpcutil.ddl +204 -0
  42. data/mcollective_additions/plugins/v2.2/agent/rpcutil.rb +101 -0
  43. data/mcollective_additions/plugins/v2.2/agent/sync_agent_code.ddl +10 -0
  44. data/mcollective_additions/plugins/v2.2/agent/sync_agent_code.rb +85 -0
  45. data/mcollective_additions/plugins/v2.2/agent/tail.ddl +11 -0
  46. data/mcollective_additions/plugins/v2.2/agent/tail.rb +67 -0
  47. data/mcollective_additions/plugins/v2.2/connector/r8stomp.rb +238 -0
  48. data/mcollective_additions/plugins/v2.2/connector/stomp.rb +349 -0
  49. data/mcollective_additions/plugins/v2.2/connector/stomp_em.rb +191 -0
  50. data/mcollective_additions/plugins/v2.2/facts/pbuilder_facts.rb +35 -0
  51. data/mcollective_additions/plugins/v2.2/security/sshkey.ddl +9 -0
  52. data/mcollective_additions/plugins/v2.2/security/sshkey.rb +362 -0
  53. data/mcollective_additions/server.cfg +22 -0
  54. data/puppet_additions/modules/r8/lib/puppet/type/r8_export_file.rb +53 -0
  55. data/puppet_additions/modules/r8/manifests/export_variable.rb +10 -0
  56. data/puppet_additions/puppet_lib_base/puppet/indirector/catalog/r8_storeconfig_backend.rb +48 -0
  57. data/puppet_additions/puppet_lib_base/puppet/indirector/r8_storeconfig_backend.rb +4 -0
  58. data/puppet_additions/puppet_lib_base/puppet/indirector/resource/r8_storeconfig_backend.rb +72 -0
  59. data/src/etc/init.d/ec2-run-user-data +95 -0
  60. data/src/etc/logrotate.d/mcollective +10 -0
  61. data/src/etc/logrotate.d/puppet +7 -0
  62. metadata +189 -0
@@ -0,0 +1,11 @@
1
+ metadata :name => "tailing log",
2
+ :description => "Agent to tail log",
3
+ :author => "Reactor8",
4
+ :license => "",
5
+ :version => "",
6
+ :url => "",
7
+ :timeout => 2
8
+ action "get_log", :description => "returns log content" do
9
+ end
10
+ action "grep", :description => "returns log content from multiple nodes" do
11
+ end
@@ -0,0 +1,67 @@
1
+ module MCollective
2
+ module Agent
3
+ class Tail < RPC::Agent
4
+
5
+ # number of lines that will be returned on first request
6
+ BATCH_SIZE_OF_LOG = 50
7
+
8
+ action "get_log" do
9
+ begin
10
+ unless File.exists? request[:log_path]
11
+ reply[:data] = { :error => "File #{request[:log_path]} not found on given node."}
12
+ reply[:pbuilderid] = Facts["pbuilderid"]
13
+ reply[:status] = :ok
14
+ return
15
+ end
16
+
17
+ # returns total number of lines in file, one is to start next iteration with new line
18
+ last_line = `wc -l #{request[:log_path]} | awk '{print $1}'`.to_i + 1
19
+ # if there is start line from CLI request we use it, if not we take last BATCH_SIZE_OF_LOG lines
20
+ if request[:start_line].empty?
21
+ # If BATCH_SIZE_OF_LOG is bigger than last_line, then start line will be 0
22
+ start_line = (last_line > BATCH_SIZE_OF_LOG) ? last_line-BATCH_SIZE_OF_LOG : 0
23
+ else
24
+ start_line = request[:start_line]
25
+ end
26
+
27
+ # returns needed lines
28
+ if (request[:grep_option].nil? || request[:grep_option].empty?)
29
+ output = `tail -n +#{start_line} #{request[:log_path]}`
30
+ else
31
+ output = `tail -n +#{start_line} #{request[:log_path]} | grep #{request[:grep_option]}`
32
+ end
33
+
34
+ reply[:data] = { :output => output, :last_line => last_line }
35
+ reply[:pbuilderid] = Facts["pbuilderid"]
36
+ reply[:status] = :ok
37
+ rescue Exception => e
38
+ Log.error e
39
+ end
40
+ end
41
+
42
+ action "grep" do
43
+ begin
44
+ unless File.exists? request[:log_path]
45
+ reply[:data] = { :error => "File #{request[:log_path]} not found on given node."}
46
+ reply[:pbuilderid] = Facts["pbuilderid"]
47
+ reply[:status] = :ok
48
+ return
49
+ end
50
+
51
+ # returns needed lines
52
+ if (request[:stop_on_first_match].empty? || request[:stop_on_first_match].nil? || request[:stop_on_first_match].eql?('false'))
53
+ output = `more #{request[:log_path]} | grep #{request[:grep_pattern]}`
54
+ else
55
+ output = `more #{request[:log_path]} | grep #{request[:grep_pattern]} | tail -1`
56
+ end
57
+ reply[:data] = { :output => output}
58
+ reply[:pbuilderid] = Facts["pbuilderid"]
59
+ reply[:status] = :ok
60
+ rescue Exception => e
61
+ Log.error e
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,238 @@
1
+ require 'stomp'
2
+
3
+ module MCollective
4
+ module Connector
5
+ # Handles sending and receiving messages over the Stomp protocol
6
+ #
7
+ # This plugin supports version 1.1 or 1.1.6 and newer of the Stomp rubygem
8
+ # the versions between those had multi threading issues.
9
+ #
10
+ # For all versions you can configure it as follows:
11
+ #
12
+ # connector = stomp
13
+ # plugin.stomp.host = stomp.your.net
14
+ # plugin.stomp.port = 6163
15
+ # plugin.stomp.user = you
16
+ # plugin.stomp.password = secret
17
+ #
18
+ # All of these can be overriden per user using environment variables:
19
+ #
20
+ # STOMP_SERVER, STOMP_PORT, STOMP_USER, STOMP_PASSWORD
21
+ #
22
+ # Version 1.1.6 onward support supplying multiple connections and it will
23
+ # do failover between these servers, you can configure it as follows:
24
+ #
25
+ # connector = stomp
26
+ # plugin.stomp.pool.size = 2
27
+ #
28
+ # plugin.stomp.pool.host1 = stomp1.your.net
29
+ # plugin.stomp.pool.port1 = 6163
30
+ # plugin.stomp.pool.user1 = you
31
+ # plugin.stomp.pool.password1 = secret
32
+ # plugin.stomp.pool.ssl1 = true
33
+ #
34
+ # plugin.stomp.pool.host2 = stomp2.your.net
35
+ # plugin.stomp.pool.port2 = 6163
36
+ # plugin.stomp.pool.user2 = you
37
+ # plugin.stomp.pool.password2 = secret
38
+ # plugin.stomp.pool.ssl2 = false
39
+ #
40
+ # Using this method you can supply just STOMP_USER and STOMP_PASSWORD
41
+ # you have to supply the hostname for each pool member in the config.
42
+ # The port will default to 6163 if not specified.
43
+ #
44
+ # In addition you can set the following options but only when using
45
+ # pooled configuration:
46
+ #
47
+ # plugin.stomp.pool.initial_reconnect_delay = 0.01
48
+ # plugin.stomp.pool.max_reconnect_delay = 30.0
49
+ # plugin.stomp.pool.use_exponential_back_off = true
50
+ # plugin.stomp.pool.back_off_multiplier = 2
51
+ # plugin.stomp.pool.max_reconnect_attempts = 0
52
+ # plugin.stomp.pool.randomize = false
53
+ # plugin.stomp.pool.timeout = -1
54
+ #
55
+ # For versions of ActiveMQ that supports message priorities
56
+ # you can set a priority, this will cause a "priority" header
57
+ # to be emitted if present:
58
+ #
59
+ # plugin.stomp.priority = 4
60
+ #
61
+ class Stomp<Base
62
+ attr_reader :connection
63
+
64
+ #modifications so connect at initialization and there is not multiple connections per thread
65
+ def initialize
66
+ @config = Config.instance
67
+ @subscriptions = []
68
+ ###BEGIN R8 CUSTOMIZATION
69
+ if Thread.current[:stomp_client]
70
+ @connection = Thread.current[:stomp_client]
71
+ else
72
+ connect()
73
+ Thread.current[:stomp_client] = @connection
74
+ end
75
+ ###END R8 CUSTOMIZATION
76
+ end
77
+
78
+ def disconnect
79
+ Log.debug("Disconnecting from Stomp")
80
+ @connection.disconnect
81
+ end
82
+
83
+ # Connects to the Stomp middleware
84
+ def connect
85
+ if @connection
86
+ Log.debug("Already connection, not re-initializing connection")
87
+ return
88
+ end
89
+ begin
90
+ host = nil
91
+ port = nil
92
+ user = nil
93
+ password = nil
94
+ @base64 = get_bool_option("stomp.base64", false)
95
+ @msgpriority = get_option("stomp.priority", 0).to_i
96
+
97
+ # Maintain backward compat for older stomps
98
+ unless @config.pluginconf.include?("stomp.pool.size")
99
+ host = get_env_or_option("STOMP_SERVER", "stomp.host")
100
+ port = get_env_or_option("STOMP_PORT", "stomp.port", 6163).to_i
101
+ user = get_env_or_option("STOMP_USER", "stomp.user")
102
+ password = get_env_or_option("STOMP_PASSWORD", "stomp.password")
103
+
104
+ Log.debug("Connecting to #{host}:#{port}")
105
+ @connection = ::Stomp::Connection.new(user, password, host, port, true)
106
+ else
107
+ pools = @config.pluginconf["stomp.pool.size"].to_i
108
+ hosts = []
109
+
110
+ 1.upto(pools) do |poolnum|
111
+ host = {}
112
+
113
+ host[:host] = get_option("stomp.pool.host#{poolnum}")
114
+ host[:port] = get_option("stomp.pool.port#{poolnum}", 6163).to_i
115
+ host[:login] = get_env_or_option("STOMP_USER", "stomp.pool.user#{poolnum}")
116
+ host[:passcode] = get_env_or_option("STOMP_PASSWORD", "stomp.pool.password#{poolnum}")
117
+ host[:ssl] = get_bool_option("stomp.pool.ssl#{poolnum}", false)
118
+
119
+ Log.debug("Adding #{host[:host]}:#{host[:port]} to the connection pool")
120
+ hosts << host
121
+ end
122
+
123
+ raise "No hosts found for the STOMP connection pool" if hosts.size == 0
124
+
125
+ connection = {:hosts => hosts}
126
+
127
+ # Various STOMP gem options, defaults here matches defaults for 1.1.6 the meaning of
128
+ # these can be guessed, the documentation isn't clear
129
+ connection[:initial_reconnect_delay] = get_option("stomp.pool.initial_reconnect_delay", 0.01).to_f
130
+ connection[:max_reconnect_delay] = get_option("stomp.pool.max_reconnect_delay", 30.0).to_f
131
+ connection[:use_exponential_back_off] = get_bool_option("stomp.pool.use_exponential_back_off", true)
132
+ connection[:back_off_multiplier] = get_bool_option("stomp.pool.back_off_multiplier", 2).to_i
133
+ connection[:max_reconnect_attempts] = get_option("stomp.pool.max_reconnect_attempts", 0)
134
+ connection[:randomize] = get_bool_option("stomp.pool.randomize", false)
135
+ connection[:backup] = get_bool_option("stomp.pool.backup", false)
136
+ connection[:timeout] = get_option("stomp.pool.timeout", -1).to_i
137
+
138
+ @connection = ::Stomp::Connection.new(connection)
139
+ end
140
+ rescue Exception => e
141
+ raise("Could not connect to Stomp Server: #{e}")
142
+ end
143
+ end
144
+
145
+ # Receives a message from the Stomp connection
146
+ def receive
147
+ Log.debug("Waiting for a message from Stomp")
148
+ msg = @connection.receive
149
+
150
+ # STOMP puts the payload in the body variable, pass that
151
+ # into the payload of MCollective::Request and discard all the
152
+ # other headers etc that stomp provides
153
+ if @base64
154
+ Request.new(SSL.base64_decode(msg.body))
155
+ else
156
+ Request.new(msg.body)
157
+ end
158
+ end
159
+
160
+ # Sends a message to the Stomp connection
161
+ def send(target, msg)
162
+ Log.debug("Sending a message to Stomp target '#{target}'")
163
+
164
+ msg = SSL.base64_encode(msg) if @base64
165
+
166
+ # deal with deprecation warnings in newer stomp gems
167
+ if @connection.respond_to?("publish")
168
+ @connection.publish(target, msg, msgheaders)
169
+ else
170
+ @connection.send(target, msg, msgheaders)
171
+ end
172
+ end
173
+
174
+ # Subscribe to a topic or queue
175
+ def subscribe(source)
176
+ unless @subscriptions.include?(source)
177
+ Log.debug("Subscribing to #{source}")
178
+ @connection.subscribe(source)
179
+ @subscriptions << source
180
+ end
181
+ end
182
+
183
+ # Subscribe to a topic or queue
184
+ def unsubscribe(source)
185
+ Log.debug("Unsubscribing from #{source}")
186
+ @connection.unsubscribe(source)
187
+ @subscriptions.delete(source)
188
+ end
189
+
190
+ private
191
+ def msgheaders
192
+ headers = {}
193
+ headers = {"priority" => @msgpriority} if (@msgpriority and @msgpriority > 0)
194
+
195
+ return headers
196
+ end
197
+
198
+ # looks in the environment first then in the config file
199
+ # for a specific option, accepts an optional default.
200
+ #
201
+ # raises an exception when it cant find a value anywhere
202
+ def get_env_or_option(env, opt, default=nil)
203
+ return ENV[env] if ENV.include?(env)
204
+ return @config.pluginconf[opt] if @config.pluginconf.include?(opt)
205
+ return default if default
206
+
207
+ raise("No #{env} environment or plugin.#{opt} configuration option given")
208
+ end
209
+
210
+ # looks for a config option, accepts an optional default
211
+ #
212
+ # raises an exception when it cant find a value anywhere
213
+ def get_option(opt, default=nil)
214
+ return @config.pluginconf[opt] if @config.pluginconf.include?(opt)
215
+ return default if default
216
+
217
+ raise("No plugin.#{opt} configuration option given")
218
+ end
219
+
220
+ # gets a boolean option from the config, supports y/n/true/false/1/0
221
+ def get_bool_option(opt, default)
222
+ return default unless @config.pluginconf.include?(opt)
223
+
224
+ val = @config.pluginconf[opt]
225
+
226
+ if val =~ /^1|yes|true/
227
+ return true
228
+ elsif val =~ /^0|no|false/
229
+ return false
230
+ else
231
+ return default
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
237
+
238
+ # vi:tabstop=4:expandtab:ai
@@ -0,0 +1,349 @@
1
+ require 'stomp'
2
+
3
+ module MCollective
4
+ module Connector
5
+ # Handles sending and receiving messages over the Stomp protocol
6
+ #
7
+ # This plugin supports version 1.1 or 1.1.6 and newer of the Stomp rubygem
8
+ # the versions between those had multi threading issues.
9
+ #
10
+ # For all versions you can configure it as follows:
11
+ #
12
+ # connector = stomp
13
+ # plugin.stomp.host = stomp.your.net
14
+ # plugin.stomp.port = 6163
15
+ # plugin.stomp.user = you
16
+ # plugin.stomp.password = secret
17
+ #
18
+ # All of these can be overriden per user using environment variables:
19
+ #
20
+ # STOMP_SERVER, STOMP_PORT, STOMP_USER, STOMP_PASSWORD
21
+ #
22
+ # Version 1.1.6 onward support supplying multiple connections and it will
23
+ # do failover between these servers, you can configure it as follows:
24
+ #
25
+ # connector = stomp
26
+ # plugin.stomp.pool.size = 2
27
+ #
28
+ # plugin.stomp.pool.host1 = stomp1.your.net
29
+ # plugin.stomp.pool.port1 = 6163
30
+ # plugin.stomp.pool.user1 = you
31
+ # plugin.stomp.pool.password1 = secret
32
+ # plugin.stomp.pool.ssl1 = true
33
+ #
34
+ # plugin.stomp.pool.host2 = stomp2.your.net
35
+ # plugin.stomp.pool.port2 = 6163
36
+ # plugin.stomp.pool.user2 = you
37
+ # plugin.stomp.pool.password2 = secret
38
+ # plugin.stomp.pool.ssl2 = false
39
+ #
40
+ # Using this method you can supply just STOMP_USER and STOMP_PASSWORD
41
+ # you have to supply the hostname for each pool member in the config.
42
+ # The port will default to 6163 if not specified.
43
+ #
44
+ # In addition you can set the following options but only when using
45
+ # pooled configuration:
46
+ #
47
+ # plugin.stomp.pool.initial_reconnect_delay = 0.01
48
+ # plugin.stomp.pool.max_reconnect_delay = 30.0
49
+ # plugin.stomp.pool.use_exponential_back_off = true
50
+ # plugin.stomp.pool.back_off_multiplier = 2
51
+ # plugin.stomp.pool.max_reconnect_attempts = 0
52
+ # plugin.stomp.pool.randomize = false
53
+ # plugin.stomp.pool.timeout = -1
54
+ #
55
+ # You can set the initial connetion timeout - this is when your stomp server is simply
56
+ # unreachable - after which it would failover to the next in the pool:
57
+ #
58
+ # plugin.stomp.pool.connect_timeout = 30
59
+ #
60
+ # For versions of ActiveMQ that supports message priorities
61
+ # you can set a priority, this will cause a "priority" header
62
+ # to be emitted if present:
63
+ #
64
+ # plugin.stomp.priority = 4
65
+ #
66
+ class Stomp<Base
67
+ # Older stomp gems do not have these error classes, in order to be able to
68
+ # handle these exceptions if they are present and still support older gems
69
+ # we're assigning the constants to a dummy exception that will never be thrown
70
+ # by us. End result is that the code catching these exceptions become noops on
71
+ # older gems but on newer ones they become usable and handle those new errors
72
+ # intelligently
73
+ class DummyError<RuntimeError; end
74
+
75
+ ::Stomp::Error = DummyError unless defined?(::Stomp::Error)
76
+ ::Stomp::Error::NoCurrentConnection = DummyError unless defined?(::Stomp::Error::NoCurrentConnection)
77
+ ::Stomp::Error::DuplicateSubscription = DummyError unless defined?(::Stomp::Error::DuplicateSubscription)
78
+
79
+ # Class for Stomp 1.1.9 callback based logging
80
+ class EventLogger
81
+ def on_connecting(params=nil)
82
+ Log.info("Connection attempt %d to %s" % [params[:cur_conattempts], stomp_url(params)])
83
+ rescue
84
+ end
85
+
86
+ def on_connected(params=nil)
87
+ Log.info("Conncted to #{stomp_url(params)}")
88
+ rescue
89
+ end
90
+
91
+ def on_disconnect(params=nil)
92
+ Log.info("Disconnected from #{stomp_url(params)}")
93
+ rescue
94
+ end
95
+
96
+ def on_connectfail(params=nil)
97
+ Log.info("Connection to #{stomp_url(params)} failed on attempt #{params[:cur_conattempts]}")
98
+ rescue
99
+ end
100
+
101
+ def on_miscerr(params, errstr)
102
+ Log.error("Unexpected error on connection #{stomp_url(params)}: #{errstr}")
103
+ rescue
104
+ end
105
+
106
+ def on_ssl_connecting(params)
107
+ Log.info("Performing SSL connection to #{stomp_url(params)}")
108
+ rescue
109
+ end
110
+
111
+ def on_ssl_connected(params)
112
+ Log.info("Connected SSL socket #{stomp_url(params)}")
113
+ rescue
114
+ end
115
+
116
+ def stomp_url(params)
117
+ "%s://%s@%s:%d" % [ params[:cur_ssl] ? "stomp+ssl" : "stomp", params[:cur_login], params[:cur_host], params[:cur_port]]
118
+ end
119
+ end
120
+
121
+ attr_reader :connection
122
+
123
+ def initialize
124
+ Log.info("MCollective 2.2.x will be the last to fully support the 'stomp' connector, please migrate to the 'activemq' or 'rabbitmq' connector")
125
+
126
+ @config = Config.instance
127
+ @subscriptions = []
128
+ end
129
+
130
+ # Connects to the Stomp middleware
131
+ def connect(connector = ::Stomp::Connection)
132
+ if @connection
133
+ Log.debug("Already connection, not re-initializing connection")
134
+ return
135
+ end
136
+
137
+ begin
138
+ host = nil
139
+ port = nil
140
+ user = nil
141
+ password = nil
142
+ @base64 = get_bool_option("stomp.base64", false)
143
+ @msgpriority = get_option("stomp.priority", 0).to_i
144
+
145
+ # Maintain backward compat for older stomps
146
+ unless @config.pluginconf.include?("stomp.pool.size")
147
+ host = get_env_or_option("STOMP_SERVER", "stomp.host")
148
+ port = get_env_or_option("STOMP_PORT", "stomp.port", 6163).to_i
149
+ user = get_env_or_option("STOMP_USER", "stomp.user")
150
+ password = get_env_or_option("STOMP_PASSWORD", "stomp.password")
151
+
152
+ Log.debug("Connecting to #{host}:#{port}")
153
+ @connection = connector.new(user, password, host, port, true)
154
+ else
155
+ pools = @config.pluginconf["stomp.pool.size"].to_i
156
+ hosts = []
157
+
158
+ 1.upto(pools) do |poolnum|
159
+ host = {}
160
+
161
+ host[:host] = get_option("stomp.pool.host#{poolnum}")
162
+ host[:port] = get_option("stomp.pool.port#{poolnum}", 6163).to_i
163
+ host[:login] = get_env_or_option("STOMP_USER", "stomp.pool.user#{poolnum}")
164
+ host[:passcode] = get_env_or_option("STOMP_PASSWORD", "stomp.pool.password#{poolnum}")
165
+ host[:ssl] = get_bool_option("stomp.pool.ssl#{poolnum}", false)
166
+
167
+ Log.debug("Adding #{host[:host]}:#{host[:port]} to the connection pool")
168
+ hosts << host
169
+ end
170
+
171
+ raise "No hosts found for the STOMP connection pool" if hosts.size == 0
172
+
173
+ connection = {:hosts => hosts}
174
+
175
+ # Various STOMP gem options, defaults here matches defaults for 1.1.6 the meaning of
176
+ # these can be guessed, the documentation isn't clear
177
+ connection[:initial_reconnect_delay] = get_option("stomp.pool.initial_reconnect_delay", 0.01).to_f
178
+ connection[:max_reconnect_delay] = get_option("stomp.pool.max_reconnect_delay", 30.0).to_f
179
+ connection[:use_exponential_back_off] = get_bool_option("stomp.pool.use_exponential_back_off", true)
180
+ connection[:back_off_multiplier] = get_bool_option("stomp.pool.back_off_multiplier", 2).to_i
181
+ connection[:max_reconnect_attempts] = get_option("stomp.pool.max_reconnect_attempts", 0).to_i
182
+ connection[:randomize] = get_bool_option("stomp.pool.randomize", false)
183
+ connection[:backup] = get_bool_option("stomp.pool.backup", false)
184
+ connection[:timeout] = get_option("stomp.pool.timeout", -1).to_i
185
+ connection[:connect_timeout] = Integer(get_option("stomp.pool.connect_timeout", 30))
186
+ connection[:reliable] = true
187
+
188
+ connection[:logger] = EventLogger.new
189
+
190
+ @connection = connector.new(connection)
191
+ end
192
+ rescue Exception => e
193
+ raise("Could not connect to Stomp Server: #{e}")
194
+ end
195
+ end
196
+
197
+ # Receives a message from the Stomp connection
198
+ def receive
199
+ Log.debug("Waiting for a message from Stomp")
200
+
201
+ # When the Stomp library > 1.2.0 is mid reconnecting due to its reliable connection
202
+ # handling it sets the connection to closed. If we happen to be receiving at just
203
+ # that time we will get an exception warning about the closed connection so handling
204
+ # that here with a sleep and a retry.
205
+ begin
206
+ msg = @connection.receive
207
+ rescue ::Stomp::Error::NoCurrentConnection
208
+ sleep 1
209
+ retry
210
+ end
211
+
212
+ Message.new(msg.body, msg, :base64 => @base64, :headers => msg.headers)
213
+ end
214
+
215
+ # Sends a message to the Stomp connection
216
+ def publish(msg)
217
+ msg.base64_encode! if @base64
218
+
219
+ raise "Cannot set specific reply to targets with the STOMP plugin" if msg.reply_to
220
+
221
+ if msg.type == :direct_request
222
+ msg.discovered_hosts.each do |node|
223
+ target = make_target(msg.agent, msg.type, msg.collective, node)
224
+
225
+ Log.debug("Sending a direct message to STOMP target '#{target}'")
226
+
227
+ publish_msg(target, msg.payload)
228
+ end
229
+ else
230
+ target = make_target(msg.agent, msg.type, msg.collective)
231
+
232
+ Log.debug("Sending a broadcast message to STOMP target '#{target}'")
233
+
234
+ publish_msg(target, msg.payload)
235
+ end
236
+ end
237
+
238
+ # Subscribe to a topic or queue
239
+ def subscribe(agent, type, collective)
240
+ source = make_target(agent, type, collective)
241
+
242
+ unless @subscriptions.include?(source)
243
+ Log.debug("Subscribing to #{source}")
244
+ @connection.subscribe(source)
245
+ @subscriptions << source
246
+ end
247
+ rescue ::Stomp::Error::DuplicateSubscription
248
+ Log.debug("Received subscription for #{source[:name]} but already had a subscription, ignoring")
249
+ end
250
+
251
+ # Actually sends the message to the middleware
252
+ def publish_msg(target, msg)
253
+ # deal with deprecation warnings in newer stomp gems
254
+ if @connection.respond_to?("publish")
255
+ @connection.publish(target, msg, msgheaders)
256
+ else
257
+ @connection.send(target, msg, msgheaders)
258
+ end
259
+ end
260
+
261
+ # Subscribe to a topic or queue
262
+ def unsubscribe(agent, type, collective)
263
+ source = make_target(agent, type, collective)
264
+
265
+ Log.debug("Unsubscribing from #{source}")
266
+ @connection.unsubscribe(source)
267
+ @subscriptions.delete(source)
268
+ end
269
+
270
+ # Disconnects from the Stomp connection
271
+ def disconnect
272
+ Log.debug("Disconnecting from Stomp")
273
+ @connection.disconnect
274
+ end
275
+
276
+ def msgheaders
277
+ headers = {}
278
+ headers = {"priority" => @msgpriority} if @msgpriority > 0
279
+
280
+ return headers
281
+ end
282
+
283
+ # looks in the environment first then in the config file
284
+ # for a specific option, accepts an optional default.
285
+ #
286
+ # raises an exception when it cant find a value anywhere
287
+ def get_env_or_option(env, opt, default=nil)
288
+ return ENV[env] if ENV.include?(env)
289
+ return @config.pluginconf[opt] if @config.pluginconf.include?(opt)
290
+ return default if default
291
+
292
+ raise("No #{env} environment or plugin.#{opt} configuration option given")
293
+ end
294
+
295
+ # looks for a config option, accepts an optional default
296
+ #
297
+ # raises an exception when it cant find a value anywhere
298
+ def get_option(opt, default=nil)
299
+ return @config.pluginconf[opt] if @config.pluginconf.include?(opt)
300
+ return default if default
301
+
302
+ raise("No plugin.#{opt} configuration option given")
303
+ end
304
+
305
+ # gets a boolean option from the config, supports y/n/true/false/1/0
306
+ def get_bool_option(opt, default)
307
+ return default unless @config.pluginconf.include?(opt)
308
+
309
+ val = @config.pluginconf[opt]
310
+
311
+ if val =~ /^1|yes|true/
312
+ return true
313
+ elsif val =~ /^0|no|false/
314
+ return false
315
+ else
316
+ return default
317
+ end
318
+ end
319
+
320
+ def make_target(agent, type, collective, target_node=nil)
321
+ raise("Unknown target type #{type}") unless [:directed, :broadcast, :reply, :request, :direct_request].include?(type)
322
+ raise("Unknown collective '#{collective}' known collectives are '#{@config.collectives.join ', '}'") unless @config.collectives.include?(collective)
323
+
324
+ prefix = @config.topicprefix
325
+
326
+ case type
327
+ when :reply
328
+ suffix = :reply
329
+ when :broadcast
330
+ suffix = :command
331
+ when :request
332
+ suffix = :command
333
+ when :direct_request
334
+ agent = nil
335
+ prefix = @config.queueprefix
336
+ suffix = Digest::MD5.hexdigest(target_node)
337
+ when :directed
338
+ agent = nil
339
+ prefix = @config.queueprefix
340
+ # use a md5 since hostnames might have illegal characters that
341
+ # the middleware dont understand
342
+ suffix = Digest::MD5.hexdigest(@config.identity)
343
+ end
344
+
345
+ ["#{prefix}#{collective}", agent, suffix].compact.join(@config.topicsep)
346
+ end
347
+ end
348
+ end
349
+ end