mcollective-client 2.7.0 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. data/lib/mcollective/agent/discovery.rb +37 -0
  2. data/lib/mcollective/agent/rpcutil.ddl +220 -0
  3. data/lib/mcollective/agent/rpcutil.rb +108 -0
  4. data/lib/mcollective/aggregate/average.ddl +33 -0
  5. data/lib/mcollective/aggregate/average.rb +29 -0
  6. data/lib/mcollective/aggregate/result.rb +3 -3
  7. data/lib/mcollective/aggregate/sum.ddl +33 -0
  8. data/lib/mcollective/aggregate/sum.rb +18 -0
  9. data/lib/mcollective/aggregate/summary.ddl +33 -0
  10. data/lib/mcollective/aggregate/summary.rb +53 -0
  11. data/lib/mcollective/aggregate.rb +2 -2
  12. data/lib/mcollective/application/completion.rb +104 -0
  13. data/lib/mcollective/application/facts.rb +62 -0
  14. data/lib/mcollective/application/find.rb +21 -0
  15. data/lib/mcollective/application/help.rb +28 -0
  16. data/lib/mcollective/application/inventory.rb +344 -0
  17. data/lib/mcollective/application/ping.rb +77 -0
  18. data/lib/mcollective/application/plugin.rb +369 -0
  19. data/lib/mcollective/application/rpc.rb +121 -0
  20. data/lib/mcollective/application.rb +2 -0
  21. data/lib/mcollective/audit/logfile.rb +26 -0
  22. data/lib/mcollective/config.rb +16 -9
  23. data/lib/mcollective/connector/activemq.ddl +9 -0
  24. data/lib/mcollective/connector/activemq.rb +572 -0
  25. data/lib/mcollective/connector/rabbitmq.ddl +9 -0
  26. data/lib/mcollective/connector/rabbitmq.rb +484 -0
  27. data/lib/mcollective/connector.rb +1 -1
  28. data/lib/mcollective/data/agent_data.ddl +22 -0
  29. data/lib/mcollective/data/agent_data.rb +17 -0
  30. data/lib/mcollective/data/collective_data.ddl +20 -0
  31. data/lib/mcollective/data/collective_data.rb +9 -0
  32. data/lib/mcollective/data/fact_data.ddl +28 -0
  33. data/lib/mcollective/data/fact_data.rb +55 -0
  34. data/lib/mcollective/data/fstat_data.ddl +89 -0
  35. data/lib/mcollective/data/fstat_data.rb +56 -0
  36. data/lib/mcollective/data.rb +2 -2
  37. data/lib/mcollective/ddl.rb +4 -4
  38. data/lib/mcollective/discovery/flatfile.ddl +11 -0
  39. data/lib/mcollective/discovery/flatfile.rb +48 -0
  40. data/lib/mcollective/discovery/mc.ddl +11 -0
  41. data/lib/mcollective/discovery/mc.rb +30 -0
  42. data/lib/mcollective/discovery/stdin.ddl +11 -0
  43. data/lib/mcollective/discovery/stdin.rb +66 -0
  44. data/lib/mcollective/facts/yaml_facts.rb +61 -0
  45. data/lib/mcollective/facts.rb +1 -1
  46. data/lib/mcollective/generators.rb +3 -3
  47. data/lib/mcollective/logger.rb +1 -1
  48. data/lib/mcollective/matcher/scanner.rb +1 -1
  49. data/lib/mcollective/matcher.rb +2 -2
  50. data/lib/mcollective/pluginpackager/debpackage_packager.rb +237 -0
  51. data/lib/mcollective/pluginpackager/modulepackage_packager.rb +127 -0
  52. data/lib/mcollective/pluginpackager/ospackage_packager.rb +59 -0
  53. data/lib/mcollective/pluginpackager/rpmpackage_packager.rb +180 -0
  54. data/lib/mcollective/pluginpackager/templates/debian/Makefile.erb +7 -0
  55. data/lib/mcollective/pluginpackager/templates/debian/changelog.erb +5 -0
  56. data/lib/mcollective/pluginpackager/templates/debian/compat.erb +1 -0
  57. data/lib/mcollective/pluginpackager/templates/debian/control.erb +15 -0
  58. data/lib/mcollective/pluginpackager/templates/debian/copyright.erb +8 -0
  59. data/lib/mcollective/pluginpackager/templates/debian/rules.erb +6 -0
  60. data/lib/mcollective/pluginpackager/templates/module/Modulefile.erb +5 -0
  61. data/lib/mcollective/pluginpackager/templates/module/README.md.erb +37 -0
  62. data/lib/mcollective/pluginpackager/templates/module/_manifest.pp.erb +9 -0
  63. data/lib/mcollective/pluginpackager/templates/redhat/rpm_spec.erb +63 -0
  64. data/lib/mcollective/pluginpackager.rb +2 -2
  65. data/lib/mcollective/registration/agentlist.rb +10 -0
  66. data/lib/mcollective/registration.rb +1 -1
  67. data/lib/mcollective/rpc/stats.rb +0 -1
  68. data/lib/mcollective/rpc.rb +11 -11
  69. data/lib/mcollective/security/aes_security.rb +394 -0
  70. data/lib/mcollective/security/psk.rb +117 -0
  71. data/lib/mcollective/security/ssl.rb +328 -0
  72. data/lib/mcollective/security.rb +1 -1
  73. data/lib/mcollective/util.rb +18 -18
  74. data/lib/mcollective/validator/array_validator.ddl +7 -0
  75. data/lib/mcollective/validator/array_validator.rb +9 -0
  76. data/lib/mcollective/validator/ipv4address_validator.ddl +7 -0
  77. data/lib/mcollective/validator/ipv4address_validator.rb +16 -0
  78. data/lib/mcollective/validator/ipv6address_validator.ddl +7 -0
  79. data/lib/mcollective/validator/ipv6address_validator.rb +16 -0
  80. data/lib/mcollective/validator/length_validator.ddl +7 -0
  81. data/lib/mcollective/validator/length_validator.rb +11 -0
  82. data/lib/mcollective/validator/regex_validator.ddl +7 -0
  83. data/lib/mcollective/validator/regex_validator.rb +9 -0
  84. data/lib/mcollective/validator/shellsafe_validator.ddl +7 -0
  85. data/lib/mcollective/validator/shellsafe_validator.rb +13 -0
  86. data/lib/mcollective/validator/typecheck_validator.ddl +7 -0
  87. data/lib/mcollective/validator/typecheck_validator.rb +28 -0
  88. data/lib/mcollective.rb +31 -31
  89. data/spec/spec_helper.rb +0 -5
  90. data/spec/unit/{plugins/mcollective → mcollective}/agent/rpcutil_spec.rb +1 -1
  91. data/spec/unit/{agents_spec.rb → mcollective/agents_spec.rb} +0 -0
  92. data/spec/unit/{plugins/mcollective → mcollective}/aggregate/average_spec.rb +1 -1
  93. data/spec/unit/{aggregate → mcollective/aggregate}/base_spec.rb +0 -0
  94. data/spec/unit/{aggregate → mcollective/aggregate}/result/base_spec.rb +0 -0
  95. data/spec/unit/{aggregate → mcollective/aggregate}/result/collection_result_spec.rb +0 -0
  96. data/spec/unit/{aggregate → mcollective/aggregate}/result/numeric_result_spec.rb +0 -0
  97. data/spec/unit/{plugins/mcollective → mcollective}/aggregate/sum_spec.rb +1 -1
  98. data/spec/unit/{plugins/mcollective → mcollective}/aggregate/summary_spec.rb +1 -1
  99. data/spec/unit/{aggregate_spec.rb → mcollective/aggregate_spec.rb} +0 -0
  100. data/spec/unit/{plugins/mcollective → mcollective}/application/plugin_spec.rb +1 -1
  101. data/spec/unit/{application_spec.rb → mcollective/application_spec.rb} +0 -0
  102. data/spec/unit/{applications_spec.rb → mcollective/applications_spec.rb} +1 -1
  103. data/spec/unit/{array_spec.rb → mcollective/array_spec.rb} +0 -0
  104. data/spec/unit/{plugins/mcollective → mcollective}/audit/logfile_spec.rb +1 -1
  105. data/spec/unit/{cache_spec.rb → mcollective/cache_spec.rb} +0 -0
  106. data/spec/unit/{client_spec.rb → mcollective/client_spec.rb} +0 -0
  107. data/spec/unit/{config_spec.rb → mcollective/config_spec.rb} +12 -7
  108. data/spec/unit/{plugins/mcollective → mcollective}/connector/activemq_spec.rb +1 -1
  109. data/spec/unit/{connector → mcollective/connector}/base_spec.rb +0 -0
  110. data/spec/unit/{plugins/mcollective → mcollective}/connector/rabbitmq_spec.rb +1 -1
  111. data/spec/unit/{plugins/mcollective → mcollective}/data/agent_data_spec.rb +1 -1
  112. data/spec/unit/{data → mcollective/data}/base_spec.rb +0 -0
  113. data/spec/unit/{plugins/mcollective → mcollective}/data/collective_data_spec.rb +1 -1
  114. data/spec/unit/{plugins/mcollective → mcollective}/data/fact_data_spec.rb +1 -1
  115. data/spec/unit/{plugins/mcollective → mcollective}/data/fstat_data_spec.rb +1 -1
  116. data/spec/unit/{data → mcollective/data}/result_spec.rb +0 -0
  117. data/spec/unit/{data_spec.rb → mcollective/data_spec.rb} +0 -0
  118. data/spec/unit/{ddl → mcollective/ddl}/agentddl_spec.rb +0 -0
  119. data/spec/unit/{ddl → mcollective/ddl}/base_spec.rb +0 -0
  120. data/spec/unit/{ddl → mcollective/ddl}/dataddl_spec.rb +0 -0
  121. data/spec/unit/{ddl → mcollective/ddl}/discoveryddl_spec.rb +0 -0
  122. data/spec/unit/{ddl_spec.rb → mcollective/ddl_spec.rb} +0 -0
  123. data/spec/unit/{plugins/mcollective → mcollective}/discovery/flatfile_spec.rb +1 -1
  124. data/spec/unit/{plugins/mcollective → mcollective}/discovery/mc_spec.rb +1 -1
  125. data/spec/unit/{plugins/mcollective → mcollective}/discovery/stdin_spec.rb +1 -1
  126. data/spec/unit/{discovery_spec.rb → mcollective/discovery_spec.rb} +0 -0
  127. data/spec/unit/{facts → mcollective/facts}/base_spec.rb +0 -0
  128. data/spec/unit/{plugins/mcollective → mcollective}/facts/yaml_facts_spec.rb +1 -1
  129. data/spec/unit/{facts_spec.rb → mcollective/facts_spec.rb} +0 -0
  130. data/spec/unit/{generators → mcollective/generators}/agent_generator_spec.rb +0 -0
  131. data/spec/unit/{generators → mcollective/generators}/base_spec.rb +0 -0
  132. data/spec/unit/{generators → mcollective/generators}/data_generator_spec.rb +0 -0
  133. data/spec/unit/{generators → mcollective/generators}/snippets/agent_ddl +0 -0
  134. data/spec/unit/{generators → mcollective/generators}/snippets/data_ddl +0 -0
  135. data/spec/unit/{log_spec.rb → mcollective/log_spec.rb} +0 -0
  136. data/spec/unit/{logger → mcollective/logger}/base_spec.rb +0 -0
  137. data/spec/unit/{logger → mcollective/logger}/console_logger_spec.rb +0 -0
  138. data/spec/unit/{logger → mcollective/logger}/file_logger_spec.rb +0 -0
  139. data/spec/unit/{logger → mcollective/logger}/syslog_logger_spec.rb +0 -0
  140. data/spec/unit/{matcher → mcollective/matcher}/parser_spec.rb +0 -0
  141. data/spec/unit/{matcher → mcollective/matcher}/scanner_spec.rb +0 -0
  142. data/spec/unit/{matcher_spec.rb → mcollective/matcher_spec.rb} +0 -0
  143. data/spec/unit/{message_spec.rb → mcollective/message_spec.rb} +0 -0
  144. data/spec/unit/{monkey_patches_spec.rb → mcollective/monkey_patches_spec.rb} +0 -0
  145. data/spec/unit/{optionparser_spec.rb → mcollective/optionparser_spec.rb} +0 -0
  146. data/spec/unit/{plugins/mcollective → mcollective}/packagers/debpackage_packager_spec.rb +1 -1
  147. data/spec/unit/{plugins/mcollective → mcollective}/packagers/modulepackage_packager_spec.rb +1 -1
  148. data/spec/unit/{plugins/mcollective → mcollective}/packagers/ospackage_spec.rb +1 -1
  149. data/spec/unit/{plugins/mcollective → mcollective}/packagers/rpmpackage_packager_spec.rb +1 -1
  150. data/spec/unit/{pluginmanager_spec.rb → mcollective/pluginmanager_spec.rb} +0 -0
  151. data/spec/unit/{pluginpackager → mcollective/pluginpackager}/agent_definition_spec.rb +0 -0
  152. data/spec/unit/{pluginpackager → mcollective/pluginpackager}/standard_definition_spec.rb +0 -0
  153. data/spec/unit/{pluginpackager_spec.rb → mcollective/pluginpackager_spec.rb} +0 -0
  154. data/spec/unit/{registration → mcollective/registration}/base_spec.rb +0 -0
  155. data/spec/unit/{rpc → mcollective/rpc}/actionrunner_spec.rb +0 -0
  156. data/spec/unit/{rpc → mcollective/rpc}/agent_spec.rb +0 -0
  157. data/spec/unit/{rpc → mcollective/rpc}/client_spec.rb +0 -0
  158. data/spec/unit/{rpc → mcollective/rpc}/helpers_spec.rb +0 -0
  159. data/spec/unit/{rpc → mcollective/rpc}/reply_spec.rb +0 -0
  160. data/spec/unit/{rpc → mcollective/rpc}/request_spec.rb +0 -0
  161. data/spec/unit/{rpc → mcollective/rpc}/result_spec.rb +0 -0
  162. data/spec/unit/{rpc → mcollective/rpc}/stats_spec.rb +0 -0
  163. data/spec/unit/{rpc_spec.rb → mcollective/rpc_spec.rb} +0 -0
  164. data/spec/unit/{runner_spec.rb → mcollective/runner_spec.rb} +0 -0
  165. data/spec/unit/{runnerstats_spec.rb → mcollective/runnerstats_spec.rb} +0 -0
  166. data/spec/unit/{plugins/mcollective → mcollective}/security/aes_security_spec.rb +1 -1
  167. data/spec/unit/{security → mcollective/security}/base_spec.rb +0 -0
  168. data/spec/unit/{plugins/mcollective → mcollective}/security/psk_spec.rb +1 -1
  169. data/spec/unit/{shell_spec.rb → mcollective/shell_spec.rb} +0 -0
  170. data/spec/unit/{ssl_spec.rb → mcollective/ssl_spec.rb} +12 -12
  171. data/spec/unit/{string_spec.rb → mcollective/string_spec.rb} +0 -0
  172. data/spec/unit/{symbol_spec.rb → mcollective/symbol_spec.rb} +0 -0
  173. data/spec/unit/{unix_daemon_spec.rb → mcollective/unix_daemon_spec.rb} +0 -0
  174. data/spec/unit/{util_spec.rb → mcollective/util_spec.rb} +28 -6
  175. data/spec/unit/{plugins/mcollective → mcollective}/validator/array_validator_spec.rb +1 -1
  176. data/spec/unit/{plugins/mcollective → mcollective}/validator/ipv4address_validator_spec.rb +1 -1
  177. data/spec/unit/{plugins/mcollective → mcollective}/validator/ipv6address_validator_spec.rb +1 -1
  178. data/spec/unit/{plugins/mcollective → mcollective}/validator/length_validator_spec.rb +1 -1
  179. data/spec/unit/{plugins/mcollective → mcollective}/validator/regex_validator_spec.rb +1 -1
  180. data/spec/unit/{plugins/mcollective → mcollective}/validator/shellsafe_validator_spec.rb +1 -1
  181. data/spec/unit/{plugins/mcollective → mcollective}/validator/typecheck_validator_spec.rb +1 -1
  182. data/spec/unit/{validator_spec.rb → mcollective/validator_spec.rb} +1 -1
  183. data/spec/unit/{vendor_spec.rb → mcollective/vendor_spec.rb} +2 -2
  184. data/spec/unit/{windows_daemon_spec.rb → mcollective/windows_daemon_spec.rb} +0 -0
  185. metadata +361 -284
  186. checksums.yaml +0 -7
@@ -0,0 +1,572 @@
1
+ require 'stomp'
2
+
3
+ module MCollective
4
+ module Connector
5
+ # Handles sending and receiving messages over the Stomp protocol for ActiveMQ
6
+ # servers specifically, we take advantages of ActiveMQ specific features and
7
+ # enhancements to the Stomp protocol. For best results in a clustered environment
8
+ # use ActiveMQ 5.5.0 at least.
9
+ #
10
+ # This plugin takes an entirely different approach to dealing with ActiveMQ
11
+ # from the more generic stomp connector.
12
+ #
13
+ # - Agents use /topic/<collective>.<agent>.agent
14
+ # - Replies use temp-topics so they are private and transient.
15
+ # - Point to Point messages using topics are supported by subscribing to
16
+ # /queue/<collective>.nodes with a selector "mc_identity = 'identity'
17
+ #
18
+ # The use of temp-topics for the replies is a huge improvement over the old style.
19
+ # In the old way all clients got replies for all clients that were active at that
20
+ # time, this would mean that they would need to decrypt, validate etc in order to
21
+ # determine if they need to ignore the message, this was computationally expensive
22
+ # and on large busy networks the messages were being sent all over the show cross
23
+ # broker boundaries.
24
+ #
25
+ # The new way means the messages go point2point back to only whoever requested the
26
+ # message, they only get their own replies and this is ap private channel that
27
+ # casual observers cannot just snoop into.
28
+ #
29
+ # This plugin supports 1.1.6 and newer of the Stomp rubygem.
30
+ #
31
+ # connector = activemq
32
+ # plugin.activemq.pool.size = 2
33
+ #
34
+ # plugin.activemq.pool.1.host = stomp1.your.net
35
+ # plugin.activemq.pool.1.port = 61613
36
+ # plugin.activemq.pool.1.user = you
37
+ # plugin.activemq.pool.1.password = secret
38
+ # plugin.activemq.pool.1.ssl = true
39
+ # plugin.activemq.pool.1.ssl.cert = /path/to/your.cert
40
+ # plugin.activemq.pool.1.ssl.key = /path/to/your.key
41
+ # plugin.activemq.pool.1.ssl.ca = /path/to/your.ca
42
+ # plugin.activemq.pool.1.ssl.fallback = true
43
+ # plugin.activemq.pool.1.ssl.ciphers = TLSv1:!MD5:!LOW:!EXPORT
44
+ #
45
+ # plugin.activemq.pool.2.host = stomp2.your.net
46
+ # plugin.activemq.pool.2.port = 61613
47
+ # plugin.activemq.pool.2.user = you
48
+ # plugin.activemq.pool.2.password = secret
49
+ # plugin.activemq.pool.2.ssl = false
50
+ #
51
+ # Using this method you can supply just STOMP_USER and STOMP_PASSWORD. The port will
52
+ # default to 61613 if not specified.
53
+ #
54
+ # The ssl options are only usable in version of the Stomp gem newer than 1.2.2 where these
55
+ # will imply full SSL validation will be done and you'll only be able to connect to a
56
+ # ActiveMQ server that has a cert signed by the same CA. If you only set ssl = true
57
+ # and do not supply the cert, key and ca properties or if you have an older gem it
58
+ # will fall back to unverified mode only if ssl.fallback is true
59
+ #
60
+ # In addition you can set the following options for the rubygem:
61
+ #
62
+ # plugin.activemq.initial_reconnect_delay = 0.01
63
+ # plugin.activemq.max_reconnect_delay = 30.0
64
+ # plugin.activemq.use_exponential_back_off = true
65
+ # plugin.activemq.back_off_multiplier = 2
66
+ # plugin.activemq.max_reconnect_attempts = 0
67
+ # plugin.activemq.randomize = false
68
+ # plugin.activemq.timeout = -1
69
+ #
70
+ # You can set the initial connetion timeout - this is when your stomp server is simply
71
+ # unreachable - after which it would failover to the next in the pool:
72
+ #
73
+ # plugin.activemq.connect_timeout = 30
74
+ #
75
+ # ActiveMQ JMS message priorities can be set:
76
+ #
77
+ # plugin.activemq.priority = 4
78
+ #
79
+ # This plugin supports Stomp protocol 1.1 when combined with the stomp gem version
80
+ # 1.2.10 or newer. To enable network heartbeats which will help keep the connection
81
+ # alive over NAT connections and aggresive session tracking firewalls you can set:
82
+ #
83
+ # plugin.activemq.heartbeat_interval = 30
84
+ #
85
+ # which will cause a heartbeat to be sent on 30 second intervals and one to be expected
86
+ # from the broker every 30 seconds. The shortest supported period is 30 seconds, if
87
+ # you set it lower it will get forced to 30 seconds.
88
+ #
89
+ # After 2 failures to receive a heartbeat the connection will be reset via the normal
90
+ # failover mechanism.
91
+ #
92
+ # By default if heartbeat_interval is set it will request Stomp 1.1 but support fallback
93
+ # to 1.0, but you can enable strict Stomp 1.1 only operation
94
+ #
95
+ # plugin.activemq.stomp_1_0_fallback = 0
96
+ class Activemq<Base
97
+ attr_reader :connection
98
+
99
+ # Older stomp gems do not have these error classes, in order to be able to
100
+ # handle these exceptions if they are present and still support older gems
101
+ # we're assigning the constants to a dummy exception that will never be thrown
102
+ # by us. End result is that the code catching these exceptions become noops on
103
+ # older gems but on newer ones they become usable and handle those new errors
104
+ # intelligently
105
+ class DummyError<RuntimeError; end
106
+
107
+ ::Stomp::Error = DummyError unless defined?(::Stomp::Error)
108
+ ::Stomp::Error::NoCurrentConnection = DummyError unless defined?(::Stomp::Error::NoCurrentConnection)
109
+ ::Stomp::Error::DuplicateSubscription = DummyError unless defined?(::Stomp::Error::DuplicateSubscription)
110
+
111
+ # Class for Stomp 1.1.9 callback based logging
112
+ class EventLogger
113
+ def on_connecting(params=nil)
114
+ Log.info("TCP Connection attempt %d to %s" % [params[:cur_conattempts], stomp_url(params)])
115
+ rescue
116
+ end
117
+
118
+ def on_connected(params=nil)
119
+ Log.info("Connected to #{stomp_url(params)}")
120
+ rescue
121
+ end
122
+
123
+ def on_disconnect(params=nil)
124
+ Log.info("Disconnected from #{stomp_url(params)}")
125
+ rescue
126
+ end
127
+
128
+ def on_connectfail(params=nil)
129
+ Log.info("TCP Connection to #{stomp_url(params)} failed on attempt #{params[:cur_conattempts]}")
130
+ rescue
131
+ end
132
+
133
+ def on_miscerr(params, errstr)
134
+ Log.error("Unexpected error on connection #{stomp_url(params)}: #{errstr}")
135
+ rescue
136
+ end
137
+
138
+ def on_ssl_connecting(params)
139
+ Log.info("Establishing SSL session with #{stomp_url(params)}")
140
+ rescue
141
+ end
142
+
143
+ def on_ssl_connected(params)
144
+ Log.info("SSL session established with #{stomp_url(params)}")
145
+ rescue
146
+ end
147
+
148
+ def on_ssl_connectfail(params)
149
+ Log.error("SSL session creation with #{stomp_url(params)} failed: #{params[:ssl_exception]}")
150
+ end
151
+
152
+ # Stomp 1.1+ - heart beat read (receive) failed.
153
+ def on_hbread_fail(params, ticker_data)
154
+ if ticker_data["lock_fail"]
155
+ if params[:max_hbrlck_fails] == 0
156
+ # failure is disabled
157
+ Log.debug("Heartbeat failed to acquire readlock for '%s': %s" % [stomp_url(params), ticker_data.inspect])
158
+ elsif ticker_data['lock_fail_count'] >= params[:max_hbrlck_fails]
159
+ # we're about to force a disconnect
160
+ Log.error("Heartbeat failed to acquire readlock for '%s': %s" % [stomp_url(params), ticker_data.inspect])
161
+ else
162
+ Log.warn("Heartbeat failed to acquire readlock for '%s': %s" % [stomp_url(params), ticker_data.inspect])
163
+ end
164
+ else
165
+ if params[:max_hbread_fails] == 0
166
+ # failure is disabled
167
+ Log.debug("Heartbeat read failed from '%s': %s" % [stomp_url(params), ticker_data.inspect])
168
+ elsif ticker_data['read_fail_count'] >= params[:max_hbread_fails]
169
+ # we're about to force a reconnect
170
+ Log.error("Heartbeat read failed from '%s': %s" % [stomp_url(params), ticker_data.inspect])
171
+ else
172
+ Log.warn("Heartbeat read failed from '%s': %s" % [stomp_url(params), ticker_data.inspect])
173
+ end
174
+ end
175
+ rescue Exception => e
176
+ end
177
+
178
+ # Stomp 1.1+ - heart beat send (transmit) failed.
179
+ def on_hbwrite_fail(params, ticker_data)
180
+ Log.error("Heartbeat write failed from '%s': %s" % [stomp_url(params), ticker_data.inspect])
181
+ rescue Exception => e
182
+ end
183
+
184
+ # Log heart beat fires
185
+ def on_hbfire(params, srind, curt)
186
+ case srind
187
+ when "receive_fire"
188
+ Log.debug("Received heartbeat from %s: %s, %s" % [stomp_url(params), srind, curt])
189
+ when "send_fire"
190
+ Log.debug("Publishing heartbeat to %s: %s, %s" % [stomp_url(params), srind, curt])
191
+ end
192
+ rescue Exception => e
193
+ end
194
+
195
+ def stomp_url(params)
196
+ "%s://%s@%s:%d" % [ params[:cur_ssl] ? "stomp+ssl" : "stomp", params[:cur_login], params[:cur_host], params[:cur_port]]
197
+ end
198
+ end
199
+
200
+ def initialize
201
+ @config = Config.instance
202
+ @subscriptions = []
203
+ @msgpriority = 0
204
+ @base64 = false
205
+ @use_exponential_back_off = get_bool_option("activemq.use_exponential_back_off", "true")
206
+ @initial_reconnect_delay = Float(get_option("activemq.initial_reconnect_delay", 0.01))
207
+ @back_off_multiplier = Integer(get_option("activemq.back_off_multiplier", 2))
208
+ @max_reconnect_delay = Float(get_option("activemq.max_reconnect_delay", 30.0))
209
+ @reconnect_delay = @initial_reconnect_delay
210
+
211
+ Log.info("ActiveMQ connector initialized. Using stomp-gem #{stomp_version}")
212
+ end
213
+
214
+ # Connects to the ActiveMQ middleware
215
+ def connect(connector = ::Stomp::Connection)
216
+ if @connection
217
+ Log.debug("Already connection, not re-initializing connection")
218
+ return
219
+ end
220
+
221
+ begin
222
+ @base64 = get_bool_option("activemq.base64", "false")
223
+ @msgpriority = get_option("activemq.priority", 0).to_i
224
+
225
+ pools = Integer(get_option("activemq.pool.size"))
226
+ hosts = []
227
+
228
+ 1.upto(pools) do |poolnum|
229
+ host = {}
230
+
231
+ host[:host] = get_option("activemq.pool.#{poolnum}.host")
232
+ host[:port] = get_option("activemq.pool.#{poolnum}.port", 61613).to_i
233
+ host[:login] = get_env_or_option("STOMP_USER", "activemq.pool.#{poolnum}.user", '')
234
+ host[:passcode] = get_env_or_option("STOMP_PASSWORD", "activemq.pool.#{poolnum}.password", '')
235
+ host[:ssl] = get_bool_option("activemq.pool.#{poolnum}.ssl", "false")
236
+
237
+ # if ssl is enabled set :ssl to the hash of parameters
238
+ if host[:ssl]
239
+ host[:ssl] = ssl_parameters(poolnum, get_bool_option("activemq.pool.#{poolnum}.ssl.fallback", "false"))
240
+ end
241
+
242
+ Log.debug("Adding #{host[:host]}:#{host[:port]} to the connection pool")
243
+ hosts << host
244
+ end
245
+
246
+ raise "No hosts found for the ActiveMQ connection pool" if hosts.size == 0
247
+
248
+ connection = {:hosts => hosts}
249
+
250
+ # Various STOMP gem options, defaults here matches defaults for 1.1.6 the meaning of
251
+ # these can be guessed, the documentation isn't clear
252
+ connection[:use_exponential_back_off] = @use_exponential_back_off
253
+ connection[:initial_reconnect_delay] = @initial_reconnect_delay
254
+ connection[:back_off_multiplier] = @back_off_multiplier
255
+ connection[:max_reconnect_delay] = @max_reconnect_delay
256
+ connection[:max_reconnect_attempts] = Integer(get_option("activemq.max_reconnect_attempts", 0))
257
+ connection[:randomize] = get_bool_option("activemq.randomize", "false")
258
+ connection[:backup] = get_bool_option("activemq.backup", "false")
259
+ connection[:timeout] = Integer(get_option("activemq.timeout", -1))
260
+ connection[:connect_timeout] = Integer(get_option("activemq.connect_timeout", 30))
261
+ connection[:reliable] = true
262
+ connection[:connect_headers] = connection_headers
263
+ connection[:max_hbrlck_fails] = Integer(get_option("activemq.max_hbrlck_fails", 0))
264
+ connection[:max_hbread_fails] = Integer(get_option("activemq.max_hbread_fails", 2))
265
+
266
+ connection[:logger] = EventLogger.new
267
+
268
+ @connection = connector.new(connection)
269
+
270
+ rescue ClientTimeoutError => e
271
+ raise e
272
+ rescue Exception => e
273
+ raise("Could not connect to ActiveMQ Server: #{e}")
274
+ end
275
+ end
276
+
277
+ def stomp_version
278
+ ::Stomp::Version::STRING
279
+ end
280
+
281
+ def stomp_version_supports_heartbeat?
282
+ return Util.versioncmp(stomp_version, "1.2.10") >= 0
283
+ end
284
+
285
+ def connection_headers
286
+ headers = {:"accept-version" => "1.0"}
287
+
288
+ heartbeat_interval = Integer(get_option("activemq.heartbeat_interval", 0))
289
+ stomp_1_0_fallback = get_bool_option("activemq.stomp_1_0_fallback", true)
290
+
291
+ headers[:host] = get_option("activemq.vhost", "mcollective")
292
+
293
+ if heartbeat_interval > 0
294
+ unless stomp_version_supports_heartbeat?
295
+ raise("Setting STOMP 1.1 properties like heartbeat intervals require at least version 1.2.10 of the STOMP gem")
296
+ end
297
+
298
+ if heartbeat_interval < 30
299
+ Log.warn("Connection heartbeat is set to %d, forcing to minimum value of 30s")
300
+ heartbeat_interval = 30
301
+ end
302
+
303
+ heartbeat_interval = heartbeat_interval * 1000
304
+ headers[:"heart-beat"] = "%d,%d" % [heartbeat_interval + 500, heartbeat_interval - 500]
305
+
306
+ if stomp_1_0_fallback
307
+ headers[:"accept-version"] = "1.1,1.0"
308
+ else
309
+ headers[:"accept-version"] = "1.1"
310
+ end
311
+ else
312
+ if stomp_version_supports_heartbeat?
313
+ Log.info("Connecting without STOMP 1.1 heartbeats, if you are using ActiveMQ 5.8 or newer consider setting plugin.activemq.heartbeat_interval")
314
+ end
315
+ end
316
+
317
+ headers
318
+ end
319
+
320
+ # Sets the SSL paramaters for a specific connection
321
+ def ssl_parameters(poolnum, fallback)
322
+ params = {
323
+ :cert_file => get_cert_file(poolnum),
324
+ :key_file => get_key_file(poolnum),
325
+ :ts_files => get_option("activemq.pool.#{poolnum}.ssl.ca", false),
326
+ :ciphers => get_option("activemq.pool.#{poolnum}.ssl.ciphers", false),
327
+ }
328
+
329
+ raise "cert, key and ca has to be supplied for verified SSL mode" unless params[:cert_file] && params[:key_file] && params[:ts_files]
330
+
331
+ raise "Cannot find certificate file #{params[:cert_file]}" unless File.exist?(params[:cert_file])
332
+ raise "Cannot find key file #{params[:key_file]}" unless File.exist?(params[:key_file])
333
+
334
+ params[:ts_files].split(",").each do |ca|
335
+ raise "Cannot find CA file #{ca}" unless File.exist?(ca)
336
+ end
337
+
338
+ begin
339
+ ::Stomp::SSLParams.new(params)
340
+ rescue NameError
341
+ raise "Stomp gem >= 1.2.2 is needed"
342
+ end
343
+
344
+ rescue Exception => e
345
+ if fallback
346
+ Log.warn("Failed to set full SSL verified mode, falling back to unverified: #{e.class}: #{e}")
347
+ return true
348
+ else
349
+ Log.error("Failed to set full SSL verified mode: #{e.class}: #{e}")
350
+ raise(e)
351
+ end
352
+ end
353
+
354
+ # Returns the name of the private key file used by ActiveMQ
355
+ # Will first check if an environment variable MCOLLECTIVE_ACTIVEMQ_POOLX_SSL_KEY exists,
356
+ # where X is the ActiveMQ pool number.
357
+ # If the environment variable doesn't exist, it will try and load the value from the config.
358
+ def get_key_file(poolnum)
359
+ ENV["MCOLLECTIVE_ACTIVEMQ_POOL%s_SSL_KEY" % poolnum] || get_option("activemq.pool.#{poolnum}.ssl.key", false)
360
+ end
361
+
362
+ # Returns the name of the certficate file used by ActiveMQ
363
+ # Will first check if an environment variable MCOLLECTIVE_ACTIVEMQ_POOLX_SSL_CERT exists,
364
+ # where X is the ActiveMQ pool number.
365
+ # If the environment variable doesn't exist, it will try and load the value from the config.
366
+ def get_cert_file(poolnum)
367
+ ENV["MCOLLECTIVE_ACTIVEMQ_POOL%s_SSL_CERT" % poolnum] || get_option("activemq.pool.#{poolnum}.ssl.cert", false)
368
+ end
369
+
370
+ # Calculate the exponential backoff needed
371
+ def exponential_back_off
372
+ if !@use_exponential_back_off
373
+ return nil
374
+ end
375
+
376
+ backoff = @reconnect_delay
377
+
378
+ # calculate next delay
379
+ @reconnect_delay = @reconnect_delay * @back_off_multiplier
380
+
381
+ # cap at max reconnect delay
382
+ if @reconnect_delay > @max_reconnect_delay
383
+ @reconnect_delay = @max_reconnect_delay
384
+ end
385
+
386
+ return backoff
387
+ end
388
+
389
+ # Receives a message from the ActiveMQ connection
390
+ def receive
391
+ Log.debug("Waiting for a message from ActiveMQ")
392
+
393
+ # When the Stomp library > 1.2.0 is mid reconnecting due to its reliable connection
394
+ # handling it sets the connection to closed. If we happen to be receiving at just
395
+ # that time we will get an exception warning about the closed connection so handling
396
+ # that here with a sleep and a retry.
397
+ begin
398
+ msg = @connection.receive
399
+ rescue ::Stomp::Error::NoCurrentConnection
400
+ sleep 1
401
+ retry
402
+ end
403
+
404
+ # In older stomp gems an attempt to receive after failed authentication can return nil
405
+ if msg.nil?
406
+ raise MessageNotReceived.new(exponential_back_off), "No message received from ActiveMQ."
407
+
408
+ end
409
+
410
+ # We expect all messages we get to be of STOMP frame type MESSAGE, raise on unexpected types
411
+ if msg.command != 'MESSAGE'
412
+ Log.debug("Unexpected '#{msg.command}' frame. Headers: #{msg.headers.inspect} Body: #{msg.body.inspect}")
413
+ raise UnexpectedMessageType.new(exponential_back_off),
414
+ "Received frame of type '#{msg.command}' expected 'MESSAGE'"
415
+ end
416
+
417
+ Message.new(msg.body, msg, :base64 => @base64, :headers => msg.headers)
418
+ end
419
+
420
+ # Sends a message to the ActiveMQ connection
421
+ def publish(msg)
422
+ msg.base64_encode! if @base64
423
+
424
+ target = target_for(msg)
425
+
426
+ if msg.type == :direct_request
427
+ msg.discovered_hosts.each do |node|
428
+ target[:headers] = headers_for(msg, node)
429
+
430
+ Log.debug("Sending a direct message to ActiveMQ target '#{target[:name]}' with headers '#{target[:headers].inspect}'")
431
+
432
+ @connection.publish(target[:name], msg.payload, target[:headers])
433
+ end
434
+ else
435
+ target[:headers].merge!(headers_for(msg))
436
+
437
+ Log.debug("Sending a broadcast message to ActiveMQ target '#{target[:name]}' with headers '#{target[:headers].inspect}'")
438
+
439
+ @connection.publish(target[:name], msg.payload, target[:headers])
440
+ end
441
+ end
442
+
443
+ # Subscribe to a topic or queue
444
+ def subscribe(agent, type, collective)
445
+ source = make_target(agent, type, collective)
446
+
447
+ unless @subscriptions.include?(source[:id])
448
+ Log.debug("Subscribing to #{source[:name]} with headers #{source[:headers].inspect.chomp}")
449
+ @connection.subscribe(source[:name], source[:headers], source[:id])
450
+ @subscriptions << source[:id]
451
+ end
452
+ rescue ::Stomp::Error::DuplicateSubscription
453
+ Log.error("Received subscription request for #{source.inspect.chomp} but already had a matching subscription, ignoring")
454
+ end
455
+
456
+ # UnSubscribe to a topic or queue
457
+ def unsubscribe(agent, type, collective)
458
+ source = make_target(agent, type, collective)
459
+
460
+ Log.debug("Unsubscribing from #{source[:name]}")
461
+ @connection.unsubscribe(source[:name], source[:headers], source[:id])
462
+ @subscriptions.delete(source[:id])
463
+ end
464
+
465
+ def target_for(msg)
466
+ if msg.type == :reply
467
+ target = {:name => msg.request.headers["reply-to"], :headers => {}}
468
+ elsif [:request, :direct_request].include?(msg.type)
469
+ target = make_target(msg.agent, msg.type, msg.collective)
470
+ else
471
+ raise "Don't now how to create a target for message type #{msg.type}"
472
+ end
473
+
474
+ return target
475
+ end
476
+
477
+ # Disconnects from the ActiveMQ connection
478
+ def disconnect
479
+ Log.debug("Disconnecting from ActiveMQ")
480
+ @connection.disconnect
481
+ @connection = nil
482
+ end
483
+
484
+ def headers_for(msg, identity=nil)
485
+ headers = {}
486
+
487
+ headers = {"priority" => @msgpriority} if @msgpriority > 0
488
+
489
+ headers["timestamp"] = (Time.now.utc.to_i * 1000).to_s
490
+
491
+ # set the expires header based on the TTL, we build a small additional
492
+ # timeout of 10 seconds in here to allow for network latency etc
493
+ headers["expires"] = ((Time.now.utc.to_i + msg.ttl + 10) * 1000).to_s
494
+
495
+ if [:request, :direct_request].include?(msg.type)
496
+ target = make_target(msg.agent, :reply, msg.collective)
497
+
498
+ if msg.reply_to
499
+ headers["reply-to"] = msg.reply_to
500
+ else
501
+ headers["reply-to"] = target[:name]
502
+ end
503
+
504
+ headers["mc_identity"] = identity if msg.type == :direct_request
505
+ end
506
+
507
+ headers["mc_sender"] = Config.instance.identity
508
+
509
+ return headers
510
+ end
511
+
512
+ def make_target(agent, type, collective)
513
+ raise("Unknown target type #{type}") unless [:directed, :broadcast, :reply, :request, :direct_request].include?(type)
514
+ raise("Unknown collective '#{collective}' known collectives are '#{@config.collectives.join ', '}'") unless @config.collectives.include?(collective)
515
+
516
+ target = {:name => nil, :headers => {}}
517
+
518
+ case type
519
+ when :reply
520
+ target[:name] = ["/queue/" + collective, :reply, "#{Config.instance.identity}_#{$$}", Client.request_sequence].join(".")
521
+
522
+ when :broadcast
523
+ target[:name] = ["/topic/" + collective, agent, :agent].join(".")
524
+
525
+ when :request
526
+ target[:name] = ["/topic/" + collective, agent, :agent].join(".")
527
+
528
+ when :direct_request
529
+ target[:name] = ["/queue/" + collective, :nodes].join(".")
530
+
531
+ when :directed
532
+ target[:name] = ["/queue/" + collective, :nodes].join(".")
533
+ target[:headers]["selector"] = "mc_identity = '#{@config.identity}'"
534
+ target[:id] = "%s_directed_to_identity" % collective
535
+ end
536
+
537
+ target[:id] = target[:name] unless target[:id]
538
+
539
+ target
540
+ end
541
+
542
+ # looks in the environment first then in the config file
543
+ # for a specific option, accepts an optional default.
544
+ #
545
+ # raises an exception when it cant find a value anywhere
546
+ def get_env_or_option(env, opt, default=nil)
547
+ return ENV[env] if ENV.include?(env)
548
+ return @config.pluginconf[opt] if @config.pluginconf.include?(opt)
549
+ return default if default
550
+
551
+ raise("No #{env} environment or plugin.#{opt} configuration option given")
552
+ end
553
+
554
+ # looks for a config option, accepts an optional default
555
+ #
556
+ # raises an exception when it cant find a value anywhere
557
+ def get_option(opt, default=nil)
558
+ return @config.pluginconf[opt] if @config.pluginconf.include?(opt)
559
+ return default unless default.nil?
560
+
561
+ raise("No plugin.#{opt} configuration option given")
562
+ end
563
+
564
+ # looks up a boolean value in the config
565
+ def get_bool_option(val, default)
566
+ Util.str_to_bool(@config.pluginconf.fetch(val, default))
567
+ end
568
+ end
569
+ end
570
+ end
571
+
572
+ # vi:tabstop=4:expandtab:ai
@@ -0,0 +1,9 @@
1
+ metadata :name => "RabbitMQ Connector",
2
+ :description => "Connector plugin for RabbitMQ middleware",
3
+ :author => "Puppet Labs",
4
+ :license => "ASL 2.0",
5
+ :version => "1.0.0",
6
+ :url => "http://projects.puppetlabs.com/projects/mcollective-plugins/wiki",
7
+ :timeout => 60
8
+
9
+ requires :mcollective => "2.6.0"