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,484 @@
1
+ require 'stomp'
2
+
3
+ module MCollective
4
+ module Connector
5
+ class Rabbitmq<Base
6
+ attr_reader :connection
7
+
8
+ class EventLogger
9
+ def on_connecting(params=nil)
10
+ Log.info("TCP Connection attempt %d to %s" % [params[:cur_conattempts], stomp_url(params)])
11
+ rescue
12
+ end
13
+
14
+ def on_connected(params=nil)
15
+ Log.info("Connected to #{stomp_url(params)}")
16
+ rescue
17
+ end
18
+
19
+ def on_disconnect(params=nil)
20
+ Log.info("Disconnected from #{stomp_url(params)}")
21
+ rescue
22
+ end
23
+
24
+ def on_connectfail(params=nil)
25
+ Log.info("TCP Connection to #{stomp_url(params)} failed on attempt #{params[:cur_conattempts]}")
26
+ rescue
27
+ end
28
+
29
+ def on_miscerr(params, errstr)
30
+ Log.error("Unexpected error on connection #{stomp_url(params)}: #{errstr}")
31
+ rescue
32
+ end
33
+
34
+ def on_ssl_connecting(params)
35
+ Log.info("Establishing SSL session with #{stomp_url(params)}")
36
+ rescue
37
+ end
38
+
39
+ def on_ssl_connected(params)
40
+ Log.info("SSL session established with #{stomp_url(params)}")
41
+ rescue
42
+ end
43
+
44
+ def on_ssl_connectfail(params)
45
+ Log.error("SSL session creation with #{stomp_url(params)} failed: #{params[:ssl_exception]}")
46
+ end
47
+
48
+ # Stomp 1.1+ - heart beat read (receive) failed.
49
+ def on_hbread_fail(params, ticker_data)
50
+ if ticker_data["lock_fail"]
51
+ if params[:max_hbrlck_fails] == 0
52
+ # failure is disabled
53
+ Log.debug("Heartbeat failed to acquire readlock for '%s': %s" % [stomp_url(params), ticker_data.inspect])
54
+ elsif ticker_data['lock_fail_count'] >= params[:max_hbrlck_fails]
55
+ # we're about to force a disconnect
56
+ Log.error("Heartbeat failed to acquire readlock for '%s': %s" % [stomp_url(params), ticker_data.inspect])
57
+ else
58
+ Log.warn("Heartbeat failed to acquire readlock for '%s': %s" % [stomp_url(params), ticker_data.inspect])
59
+ end
60
+ else
61
+ if params[:max_hbread_fails] == 0
62
+ # failure is disabled
63
+ Log.debug("Heartbeat read failed from '%s': %s" % [stomp_url(params), ticker_data.inspect])
64
+ elsif ticker_data['read_fail_count'] >= params[:max_hbread_fails]
65
+ # we're about to force a reconnect
66
+ Log.error("Heartbeat read failed from '%s': %s" % [stomp_url(params), ticker_data.inspect])
67
+ else
68
+ Log.warn("Heartbeat read failed from '%s': %s" % [stomp_url(params), ticker_data.inspect])
69
+ end
70
+ end
71
+ rescue Exception => e
72
+ end
73
+
74
+ # Stomp 1.1+ - heart beat send (transmit) failed.
75
+ def on_hbwrite_fail(params, ticker_data)
76
+ Log.error("Heartbeat write failed from '%s': %s" % [stomp_url(params), ticker_data.inspect])
77
+ rescue Exception => e
78
+ end
79
+
80
+ # Log heart beat fires
81
+ def on_hbfire(params, srind, curt)
82
+ case srind
83
+ when "receive_fire"
84
+ Log.debug("Received heartbeat from %s: %s, %s" % [stomp_url(params), srind, curt])
85
+ when "send_fire"
86
+ Log.debug("Publishing heartbeat to %s: %s, %s" % [stomp_url(params), srind, curt])
87
+ end
88
+ rescue Exception => e
89
+ end
90
+
91
+ def stomp_url(params)
92
+ "%s://%s@%s:%d" % [ params[:cur_ssl] ? "stomp+ssl" : "stomp", params[:cur_login], params[:cur_host], params[:cur_port]]
93
+ end
94
+ end
95
+
96
+ def initialize
97
+ @config = Config.instance
98
+ @subscriptions = []
99
+ @base64 = false
100
+ @use_exponential_back_off = get_bool_option("rabbitmq.use_exponential_back_off", "true")
101
+ @initial_reconnect_delay = Float(get_option("rabbitmq.initial_reconnect_delay", 0.01))
102
+ @back_off_multiplier = Integer(get_option("rabbitmq.back_off_multiplier", 2))
103
+ @max_reconnect_delay = Float(get_option("rabbitmq.max_reconnect_delay", 30.0))
104
+ @reconnect_delay = @initial_reconnect_delay
105
+
106
+ Log.info("RabbitMQ connector initialized. Using stomp-gem #{stomp_version}")
107
+ end
108
+
109
+ # Connects to the RabbitMQ middleware
110
+ def connect(connector = ::Stomp::Connection)
111
+ if @connection
112
+ Log.debug("Already connection, not re-initializing connection")
113
+ return
114
+ end
115
+
116
+ begin
117
+ @base64 = get_bool_option("rabbitmq.base64", "false")
118
+
119
+ pools = Integer(get_option("rabbitmq.pool.size"))
120
+ hosts = []
121
+
122
+ 1.upto(pools) do |poolnum|
123
+ host = {}
124
+
125
+ host[:host] = get_option("rabbitmq.pool.#{poolnum}.host")
126
+ host[:port] = get_option("rabbitmq.pool.#{poolnum}.port", 61613).to_i
127
+ host[:login] = get_env_or_option("STOMP_USER", "rabbitmq.pool.#{poolnum}.user", '')
128
+ host[:passcode] = get_env_or_option("STOMP_PASSWORD", "rabbitmq.pool.#{poolnum}.password", '')
129
+ host[:ssl] = get_bool_option("rabbitmq.pool.#{poolnum}.ssl", "false")
130
+
131
+ # if ssl is enabled set :ssl to the hash of parameters
132
+ if host[:ssl]
133
+ host[:ssl] = ssl_parameters(poolnum, get_bool_option("rabbitmq.pool.#{poolnum}.ssl.fallback", "false"))
134
+ end
135
+
136
+ Log.debug("Adding #{host[:host]}:#{host[:port]} to the connection pool")
137
+ hosts << host
138
+ end
139
+
140
+ raise "No hosts found for the RabbitMQ connection pool" if hosts.size == 0
141
+
142
+ connection = {:hosts => hosts}
143
+
144
+ # Various STOMP gem options, defaults here matches defaults for 1.1.6 the meaning of
145
+ # these can be guessed, the documentation isn't clear
146
+ connection[:use_exponential_back_off] = @use_exponential_back_off
147
+ connection[:initial_reconnect_delay] = @initial_reconnect_delay
148
+ connection[:back_off_multiplier] = @back_off_multiplier
149
+ connection[:max_reconnect_delay] = @max_reconnect_delay
150
+ connection[:max_reconnect_attempts] = Integer(get_option("rabbitmq.max_reconnect_attempts", 0))
151
+ connection[:randomize] = get_bool_option("rabbitmq.randomize", "false")
152
+ connection[:backup] = get_bool_option("rabbitmq.backup", "false")
153
+
154
+ connection[:timeout] = Integer(get_option("rabbitmq.timeout", -1))
155
+ connection[:connect_timeout] = Integer(get_option("rabbitmq.connect_timeout", 30))
156
+ connection[:reliable] = true
157
+ connection[:max_hbrlck_fails] = Integer(get_option("rabbitmq.max_hbrlck_fails", 0))
158
+ connection[:max_hbread_fails] = Integer(get_option("rabbitmq.max_hbread_fails", 2))
159
+
160
+ connection[:connect_headers] = connection_headers
161
+
162
+ connection[:logger] = EventLogger.new
163
+
164
+ @connection = connector.new(connection)
165
+
166
+ rescue ClientTimeoutError => e
167
+ raise e
168
+ rescue Exception => e
169
+ raise("Could not connect to RabbitMQ Server: #{e}")
170
+ end
171
+ end
172
+
173
+ def connection_headers
174
+ headers = {:"accept-version" => "1.0"}
175
+
176
+ heartbeat_interval = Integer(get_option("rabbitmq.heartbeat_interval", 0))
177
+ stomp_1_0_fallback = get_bool_option("rabbitmq.stomp_1_0_fallback", true)
178
+
179
+ headers[:host] = get_option("rabbitmq.vhost", "/")
180
+
181
+ if heartbeat_interval > 0
182
+ unless stomp_version_supports_heartbeat?
183
+ raise("Setting STOMP 1.1 properties like heartbeat intervals require at least version 1.2.10 of the STOMP gem")
184
+ end
185
+
186
+ if heartbeat_interval < 30
187
+ Log.warn("Connection heartbeat is set to %d, forcing to minimum value of 30s")
188
+ heartbeat_interval = 30
189
+ end
190
+
191
+ heartbeat_interval = heartbeat_interval * 1000
192
+ headers[:"heart-beat"] = "%d,%d" % [heartbeat_interval + 500, heartbeat_interval - 500]
193
+
194
+ if stomp_1_0_fallback
195
+ headers[:"accept-version"] = "1.1,1.0"
196
+ else
197
+ headers[:"accept-version"] = "1.1"
198
+ end
199
+ else
200
+ if stomp_version_supports_heartbeat?
201
+ Log.info("Connecting without STOMP 1.1 heartbeats, consider setting plugin.rabbitmq.heartbeat_interval")
202
+ end
203
+ end
204
+
205
+ headers
206
+ end
207
+
208
+ def stomp_version
209
+ ::Stomp::Version::STRING
210
+ end
211
+
212
+ def stomp_version_supports_heartbeat?
213
+ return Util.versioncmp(stomp_version, "1.2.10") >= 0
214
+ end
215
+
216
+ # Sets the SSL paramaters for a specific connection
217
+ def ssl_parameters(poolnum, fallback)
218
+ params = {
219
+ :cert_file => get_cert_file(poolnum),
220
+ :key_file => get_key_file(poolnum),
221
+ :ts_files => get_option("rabbitmq.pool.#{poolnum}.ssl.ca", false),
222
+ :ciphers => get_option("rabbitmq.pool.#{poolnum}.ssl.ciphers", false),
223
+ }
224
+
225
+ raise "cert, key and ca has to be supplied for verified SSL mode" unless params[:cert_file] && params[:key_file] && params[:ts_files]
226
+
227
+ raise "Cannot find certificate file #{params[:cert_file]}" unless File.exist?(params[:cert_file])
228
+ raise "Cannot find key file #{params[:key_file]}" unless File.exist?(params[:key_file])
229
+
230
+ params[:ts_files].split(",").each do |ca|
231
+ raise "Cannot find CA file #{ca}" unless File.exist?(ca)
232
+ end
233
+
234
+ begin
235
+ ::Stomp::SSLParams.new(params)
236
+ rescue NameError
237
+ raise "Stomp gem >= 1.2.2 is needed"
238
+ end
239
+
240
+ rescue Exception => e
241
+ if fallback
242
+ Log.warn("Failed to set full SSL verified mode, falling back to unverified: #{e.class}: #{e}")
243
+ return true
244
+ else
245
+ Log.error("Failed to set full SSL verified mode: #{e.class}: #{e}")
246
+ raise(e)
247
+ end
248
+ end
249
+
250
+ # Returns the name of the private key file used by RabbitMQ
251
+ # Will first check if an environment variable MCOLLECTIVE_RABBITMQ_POOLX_SSL_KEY exists,
252
+ # where X is the RabbitMQ pool number.
253
+ # If the environment variable doesn't exist, it will try and load the value from the config.
254
+ def get_key_file(poolnum)
255
+ ENV["MCOLLECTIVE_RABBITMQ_POOL%s_SSL_KEY" % poolnum] || get_option("rabbitmq.pool.#{poolnum}.ssl.key", false)
256
+ end
257
+
258
+ # Returns the name of the certificate file used by RabbitMQ
259
+ # Will first check if an environment variable MCOLLECTIVE_RABBITMQ_POOLX_SSL_CERT exists,
260
+ # where X is the RabbitMQ pool number.
261
+ # If the environment variable doesn't exist, it will try and load the value from the config.
262
+ def get_cert_file(poolnum)
263
+ ENV["MCOLLECTIVE_RABBITMQ_POOL%s_SSL_CERT" % poolnum] || get_option("rabbitmq.pool.#{poolnum}.ssl.cert", false)
264
+ end
265
+
266
+ # Calculate the exponential backoff needed
267
+ def exponential_back_off
268
+ if !@use_exponential_back_off
269
+ return nil
270
+ end
271
+
272
+ backoff = @reconnect_delay
273
+
274
+ # calculate next delay
275
+ @reconnect_delay = @reconnect_delay * @back_off_multiplier
276
+
277
+ # cap at max reconnect delay
278
+ if @reconnect_delay > @max_reconnect_delay
279
+ @reconnect_delay = @max_reconnect_delay
280
+ end
281
+
282
+ return backoff
283
+ end
284
+
285
+ # Receives a message from the RabbitMQ connection
286
+ def receive
287
+ Log.debug("Waiting for a message from RabbitMQ")
288
+
289
+ # When the Stomp library > 1.2.0 is mid reconnecting due to its reliable connection
290
+ # handling it sets the connection to closed. If we happen to be receiving at just
291
+ # that time we will get an exception warning about the closed connection so handling
292
+ # that here with a sleep and a retry.
293
+ begin
294
+ msg = @connection.receive
295
+ rescue ::Stomp::Error::NoCurrentConnection
296
+ sleep 1
297
+ retry
298
+ end
299
+
300
+ # In older stomp gems an attempt to receive after failed authentication can return nil
301
+ if msg.nil?
302
+ raise MessageNotReceived.new(exponential_back_off), "No message received from RabbitMQ."
303
+ end
304
+
305
+ raise "Received a processing error from RabbitMQ: '%s'" % msg.body.chomp if msg.body =~ /Processing error/
306
+
307
+ # We expect all messages we get to be of STOMP frame type MESSAGE, raise on unexpected types
308
+ if msg.command != 'MESSAGE'
309
+ Log.debug("Unexpected '#{msg.command}' frame. Headers: #{msg.headers.inspect} Body: #{msg.body.inspect}")
310
+ raise UnexpectedMessageType.new(exponential_back_off),
311
+ "Received frame of type '#{msg.command}' expected 'MESSAGE'"
312
+ end
313
+
314
+ Message.new(msg.body, msg, :base64 => @base64, :headers => msg.headers)
315
+ end
316
+
317
+ # Sends a message to the RabbitMQ connection
318
+ def publish(msg)
319
+ msg.base64_encode! if @base64
320
+
321
+ if msg.type == :direct_request
322
+ msg.discovered_hosts.each do |node|
323
+ target = target_for(msg, node)
324
+
325
+ Log.debug("Sending a direct message to RabbitMQ target '#{target[:name]}' with headers '#{target[:headers].inspect}'")
326
+
327
+ @connection.publish(target[:name], msg.payload, target[:headers])
328
+ end
329
+ else
330
+ target = target_for(msg)
331
+
332
+ Log.debug("Sending a broadcast message to RabbitMQ target '#{target[:name]}' with headers '#{target[:headers].inspect}'")
333
+
334
+ @connection.publish(target[:name], msg.payload, target[:headers])
335
+ end
336
+ end
337
+
338
+ def target_for(msg, node=nil)
339
+ if msg.type == :reply
340
+ target = {:name => msg.request.headers["reply-to"], :headers => {}, :id => ""}
341
+
342
+ elsif [:request, :direct_request].include?(msg.type)
343
+ target = make_target(msg.agent, msg.type, msg.collective, msg.reply_to, node)
344
+
345
+ else
346
+ raise "Don't now how to create a target for message type #{msg.type}"
347
+
348
+ end
349
+
350
+ # marks messages as valid for ttl + 10 seconds, we do this here
351
+ # rather than in make_target as this should only be set on publish
352
+ target[:headers]["expiration"] = ((msg.ttl + 10) * 1000).to_s
353
+
354
+ target[:headers]["mc_sender"] = Config.instance.identity
355
+
356
+ return target
357
+ end
358
+
359
+ def make_target(agent, type, collective, reply_to=nil, node=nil)
360
+ raise("Unknown target type #{type}") unless [:directed, :broadcast, :reply, :request, :direct_request].include?(type)
361
+ raise("Unknown collective '#{collective}' known collectives are '#{@config.collectives.join ', '}'") unless @config.collectives.include?(collective)
362
+
363
+ target = {:name => "", :headers => {}, :id => nil}
364
+
365
+ if reply_to
366
+ reply_path = reply_to
367
+ elsif get_bool_option("rabbitmq.use_reply_exchange", false)
368
+ reply_path = "/exchange/mcollective_reply/%s_%s_%s" % [ @config.identity, $$, Client.request_sequence ]
369
+ else
370
+ reply_path = "/temp-queue/mcollective_reply_%s" % agent
371
+ end
372
+ case type
373
+ when :reply # receiving replies on a temp queue
374
+ target[:name] = reply_path
375
+ target[:id] = "mcollective_%s_replies" % agent
376
+
377
+ when :broadcast, :request # publishing a request to all nodes with an agent
378
+ target[:name] = "/exchange/%s_broadcast/%s" % [collective, agent]
379
+ if reply_to
380
+ target[:headers]["reply-to"] = reply_to
381
+ else
382
+ target[:headers]["reply-to"] = reply_path
383
+ end
384
+ target[:id] = "%s_broadcast_%s" % [collective, agent]
385
+
386
+ when :direct_request # a request to a specific node
387
+ raise "Directed requests need to have a node identity" unless node
388
+
389
+ target[:name] = "/exchange/%s_directed/%s" % [ collective, node]
390
+ target[:headers]["reply-to"] = reply_path
391
+
392
+ when :directed # subscribing to directed messages
393
+ target[:name] = "/exchange/%s_directed/%s" % [ collective, @config.identity ]
394
+ target[:id] = "%s_%s_directed_to_identity" % [ collective, @config.identity ]
395
+ end
396
+
397
+ target
398
+ end
399
+
400
+ # Subscribe to a topic or queue
401
+ def subscribe(agent, type, collective)
402
+ if type == :reply
403
+ # On rabbitmq if you send a message with a reply-to: header set to
404
+ # '/temp-queue/*' it automatically creates a private queue, munges
405
+ # the reply-to: header to point to this private queue, and
406
+ # subscribes you to it. As such you should never attempt to
407
+ # SUBSCRIBE or UNSUBSCRIBE to '/temp-queue/*' directly as that'll
408
+ # cause great pain and suffering.
409
+ # https://www.rabbitmq.com/stomp.html#d.tqd
410
+
411
+ # The exception to this is in 'use_reply_exchange' mode, when the
412
+ # reply-to will be set to a queue in an explicit exchange.
413
+ if !get_bool_option("rabbitmq.use_reply_exchange", false)
414
+ # We aren't in 'use_reply_exchange' mode, don't subscribe.
415
+ return
416
+ end
417
+ end
418
+
419
+ source = make_target(agent, type, collective)
420
+
421
+ unless @subscriptions.include?(source[:id])
422
+ Log.debug("Subscribing to #{source[:name]} with headers #{source[:headers].inspect.chomp}")
423
+ @connection.subscribe(source[:name], source[:headers], source[:id])
424
+ @subscriptions << source[:id]
425
+ end
426
+ rescue ::Stomp::Error::DuplicateSubscription
427
+ Log.error("Received subscription request for #{source.inspect.chomp} but already had a matching subscription, ignoring")
428
+ end
429
+
430
+ # Subscribe to a topic or queue
431
+ def unsubscribe(agent, type, collective)
432
+ if type == :reply
433
+ # For a more detailed discussion of this logic, please see #subscribe
434
+ if !get_bool_option("rabbitmq.use_reply_exchange", false)
435
+ # We shouldn't try to unsubscribe from a '/temp-queue/*' queue.
436
+ return
437
+ end
438
+ end
439
+
440
+ source = make_target(agent, type, collective)
441
+
442
+ Log.debug("Unsubscribing from #{source[:name]}")
443
+ @connection.unsubscribe(source[:name], source[:headers], source[:id])
444
+ @subscriptions.delete(source[:id])
445
+ end
446
+
447
+ # Disconnects from the RabbitMQ connection
448
+ def disconnect
449
+ Log.debug("Disconnecting from RabbitMQ")
450
+ @connection.disconnect
451
+ @connection = nil
452
+ end
453
+
454
+ # looks in the environment first then in the config file
455
+ # for a specific option, accepts an optional default.
456
+ #
457
+ # raises an exception when it cant find a value anywhere
458
+ def get_env_or_option(env, opt, default=nil)
459
+ return ENV[env] if ENV.include?(env)
460
+ return @config.pluginconf[opt] if @config.pluginconf.include?(opt)
461
+ return default if default
462
+
463
+ raise("No #{env} environment or plugin.#{opt} configuration option given")
464
+ end
465
+
466
+ # looks for a config option, accepts an optional default
467
+ #
468
+ # raises an exception when it cant find a value anywhere
469
+ def get_option(opt, default=nil)
470
+ return @config.pluginconf[opt] if @config.pluginconf.include?(opt)
471
+ return default unless default.nil?
472
+
473
+ raise("No plugin.#{opt} configuration option given")
474
+ end
475
+
476
+ # looks up a boolean value in the config
477
+ def get_bool_option(val, default)
478
+ Util.str_to_bool(@config.pluginconf.fetch(val, default))
479
+ end
480
+ end
481
+ end
482
+ end
483
+
484
+ # vi:tabstop=4:expandtab:ai
@@ -13,6 +13,6 @@ module MCollective
13
13
  # with something else that supports the above, see MCollective::Connector::Stomp
14
14
  # for the default connector.
15
15
  module Connector
16
- autoload :Base, "mcollective/connector/base"
16
+ require "mcollective/connector/base"
17
17
  end
18
18
  end
@@ -0,0 +1,22 @@
1
+ metadata :name => "Agent",
2
+ :description => "Meta data about installed MColletive Agents",
3
+ :author => "R.I.Pienaar <rip@devco.net>",
4
+ :license => "ASL 2.0",
5
+ :version => "1.0",
6
+ :url => "http://marionette-collective.org/",
7
+ :timeout => 1
8
+
9
+ dataquery :description => "Agent Meta Data" do
10
+ input :query,
11
+ :prompt => "Agent Name",
12
+ :description => "Valid agent name",
13
+ :type => :string,
14
+ :validation => /^[\w\_]+$/,
15
+ :maxlength => 20
16
+
17
+ [:license, :timeout, :description, :url, :version, :author].each do |item|
18
+ output item,
19
+ :description => "Agent #{item}",
20
+ :display_as => item.to_s.capitalize
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ module MCollective
2
+ module Data
3
+ class Agent_data<Base
4
+ query do |plugin|
5
+ raise "No agent called #{plugin} found" unless PluginManager.include?("#{plugin}_agent")
6
+
7
+ agent = PluginManager["#{plugin}_agent"]
8
+
9
+ result[:agent] = plugin
10
+
11
+ [:license, :timeout, :description, :url, :version, :author].each do |item|
12
+ result[item] = agent.meta[item]
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ metadata :name => "Collective",
2
+ :description => "Collective membership",
3
+ :author => "Puppet Labs",
4
+ :license => "ASL 2.0",
5
+ :version => "1.0",
6
+ :url => "http://marionette-collective.org/",
7
+ :timeout => 1
8
+
9
+ dataquery :description => "Collective" do
10
+ input :query,
11
+ :prompt => 'Collective',
12
+ :description => 'Collective name to ask about, eg mcollective',
13
+ :type => :string,
14
+ :validation => /./,
15
+ :maxlength => 256
16
+
17
+ output :member,
18
+ :description => 'Node is a member of the named collective',
19
+ :display_as => 'member'
20
+ end
@@ -0,0 +1,9 @@
1
+ module MCollective
2
+ module Data
3
+ class Collective_data<Base
4
+ query do |collective|
5
+ result[:member] = Config.instance.collectives.include?(collective)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ metadata :name => "Fact",
2
+ :description => "Structured fact query",
3
+ :author => "Puppet Labs",
4
+ :license => "ASL 2.0",
5
+ :version => "1.0",
6
+ :url => "http://marionette-collective.org/",
7
+ :timeout => 1
8
+
9
+ dataquery :description => "Fact" do
10
+ input :query,
11
+ :prompt => 'Fact Path',
12
+ :description => 'Path to a fact, eg network.eth0.address',
13
+ :type => :string,
14
+ :validation => /./,
15
+ :maxlength => 256
16
+
17
+ output :exists,
18
+ :description => 'Fact is present',
19
+ :display_as => 'exists'
20
+
21
+ output :value,
22
+ :description => 'Fact value',
23
+ :display_as => 'value'
24
+
25
+ output :value_encoding,
26
+ :description => 'Encoding of the fact value (text/plain or application/json)',
27
+ :display_as => 'value_encoding'
28
+ end
@@ -0,0 +1,55 @@
1
+ module MCollective
2
+ module Data
3
+ class Fact_data<Base
4
+ query do |path|
5
+ parts = path.split /\./
6
+ walk_path(parts)
7
+ end
8
+
9
+ private
10
+
11
+ def walk_path(path)
12
+ # Set up results as though we didn't find the value
13
+ result[:exists] = false
14
+ result[:value] = false
15
+ result[:value_encoding] = false
16
+
17
+ facts = PluginManager['facts_plugin'].get_facts
18
+
19
+ path.each do |level|
20
+ case facts
21
+ when Array
22
+ level = Integer(level)
23
+ if level >= facts.size
24
+ # array index out would be out of bounds, so we don't have the value
25
+ return
26
+ end
27
+ when Hash
28
+ if !facts.include?(level)
29
+ # we don't have the key for the next level, so give up
30
+ return
31
+ end
32
+ else
33
+ # this isn't a container data type, so we can't walk into it
34
+ return
35
+ end
36
+
37
+ facts = facts[level]
38
+ end
39
+
40
+ result[:exists] = true
41
+ case facts
42
+ when Array, Hash
43
+ # Currently data plugins cannot return structured data, so until
44
+ # this is fixed flatten the data with json and flag that we have
45
+ # munged the data
46
+ result[:value] = facts.to_json
47
+ result[:value_encoding] = 'application/json'
48
+ else
49
+ result[:value] = facts
50
+ result[:value_encoding] = 'text/plain'
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end