dtk-node-agent 0.5.10

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