choria-mcorpc-support 2.21.0 → 2.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mcollective.rb +1 -1
  3. data/lib/mcollective/agent/bolt_tasks.ddl +235 -0
  4. data/lib/mcollective/agent/bolt_tasks.json +347 -0
  5. data/lib/mcollective/agent/bolt_tasks.rb +176 -0
  6. data/lib/mcollective/agent/choria_util.ddl +152 -0
  7. data/lib/mcollective/agent/choria_util.json +244 -0
  8. data/lib/mcollective/agent/rpcutil.ddl +7 -3
  9. data/lib/mcollective/agent/rpcutil.json +333 -0
  10. data/lib/mcollective/agent/scout.ddl +169 -0
  11. data/lib/mcollective/agent/scout.json +224 -0
  12. data/lib/mcollective/agents.rb +7 -6
  13. data/lib/mcollective/aggregate.rb +4 -4
  14. data/lib/mcollective/aggregate/average.rb +2 -2
  15. data/lib/mcollective/aggregate/base.rb +2 -2
  16. data/lib/mcollective/aggregate/result.rb +3 -3
  17. data/lib/mcollective/aggregate/result/collection_result.rb +2 -2
  18. data/lib/mcollective/aggregate/result/numeric_result.rb +2 -2
  19. data/lib/mcollective/aggregate/sum.rb +2 -2
  20. data/lib/mcollective/aggregate/summary.rb +3 -4
  21. data/lib/mcollective/application.rb +57 -21
  22. data/lib/mcollective/application/choria.rb +249 -0
  23. data/lib/mcollective/application/completion.rb +6 -6
  24. data/lib/mcollective/application/describe_filter.rb +20 -20
  25. data/lib/mcollective/application/facts.rb +19 -11
  26. data/lib/mcollective/application/federation.rb +239 -0
  27. data/lib/mcollective/application/find.rb +4 -4
  28. data/lib/mcollective/application/help.rb +3 -3
  29. data/lib/mcollective/application/inventory.rb +3 -341
  30. data/lib/mcollective/application/ping.rb +3 -77
  31. data/lib/mcollective/application/playbook.rb +207 -0
  32. data/lib/mcollective/application/plugin.rb +106 -106
  33. data/lib/mcollective/application/rpc.rb +3 -108
  34. data/lib/mcollective/application/tasks.rb +416 -0
  35. data/lib/mcollective/applications.rb +11 -10
  36. data/lib/mcollective/audit/choria.rb +33 -0
  37. data/lib/mcollective/cache.rb +2 -4
  38. data/lib/mcollective/client.rb +11 -10
  39. data/lib/mcollective/config.rb +21 -34
  40. data/lib/mcollective/connector/base.rb +2 -1
  41. data/lib/mcollective/connector/nats.ddl +9 -0
  42. data/lib/mcollective/connector/nats.rb +450 -0
  43. data/lib/mcollective/data.rb +8 -3
  44. data/lib/mcollective/data/agent_data.rb +1 -1
  45. data/lib/mcollective/data/base.rb +6 -5
  46. data/lib/mcollective/data/bolt_task_data.ddl +90 -0
  47. data/lib/mcollective/data/bolt_task_data.rb +32 -0
  48. data/lib/mcollective/data/collective_data.rb +1 -1
  49. data/lib/mcollective/data/fact_data.rb +6 -6
  50. data/lib/mcollective/data/fstat_data.rb +2 -4
  51. data/lib/mcollective/data/result.rb +7 -2
  52. data/lib/mcollective/ddl/agentddl.rb +5 -17
  53. data/lib/mcollective/ddl/base.rb +10 -13
  54. data/lib/mcollective/discovery.rb +12 -26
  55. data/lib/mcollective/discovery/choria.ddl +11 -0
  56. data/lib/mcollective/discovery/choria.rb +223 -0
  57. data/lib/mcollective/discovery/flatfile.rb +7 -8
  58. data/lib/mcollective/discovery/mc.rb +2 -2
  59. data/lib/mcollective/discovery/stdin.rb +17 -18
  60. data/lib/mcollective/exceptions.rb +13 -0
  61. data/lib/mcollective/facts/base.rb +9 -9
  62. data/lib/mcollective/facts/yaml_facts.rb +12 -12
  63. data/lib/mcollective/generators.rb +3 -3
  64. data/lib/mcollective/generators/agent_generator.rb +3 -4
  65. data/lib/mcollective/generators/base.rb +14 -15
  66. data/lib/mcollective/generators/data_generator.rb +5 -6
  67. data/lib/mcollective/log.rb +2 -2
  68. data/lib/mcollective/logger/base.rb +3 -2
  69. data/lib/mcollective/logger/console_logger.rb +10 -10
  70. data/lib/mcollective/logger/file_logger.rb +7 -7
  71. data/lib/mcollective/logger/syslog_logger.rb +11 -15
  72. data/lib/mcollective/matcher.rb +14 -14
  73. data/lib/mcollective/matcher/parser.rb +31 -41
  74. data/lib/mcollective/matcher/scanner.rb +69 -74
  75. data/lib/mcollective/message.rb +10 -17
  76. data/lib/mcollective/monkey_patches.rb +2 -4
  77. data/lib/mcollective/optionparser.rb +1 -0
  78. data/lib/mcollective/pluginmanager.rb +3 -5
  79. data/lib/mcollective/pluginpackager.rb +1 -3
  80. data/lib/mcollective/pluginpackager/agent_definition.rb +3 -8
  81. data/lib/mcollective/pluginpackager/forge_packager.rb +7 -9
  82. data/lib/mcollective/pluginpackager/standard_definition.rb +1 -2
  83. data/lib/mcollective/registration/base.rb +18 -16
  84. data/lib/mcollective/rpc.rb +2 -4
  85. data/lib/mcollective/rpc/actionrunner.rb +16 -18
  86. data/lib/mcollective/rpc/agent.rb +26 -43
  87. data/lib/mcollective/rpc/audit.rb +1 -0
  88. data/lib/mcollective/rpc/client.rb +67 -85
  89. data/lib/mcollective/rpc/helpers.rb +55 -62
  90. data/lib/mcollective/rpc/progress.rb +2 -2
  91. data/lib/mcollective/rpc/reply.rb +17 -19
  92. data/lib/mcollective/rpc/request.rb +7 -5
  93. data/lib/mcollective/rpc/result.rb +6 -8
  94. data/lib/mcollective/rpc/stats.rb +49 -58
  95. data/lib/mcollective/security/base.rb +29 -36
  96. data/lib/mcollective/security/choria.rb +765 -0
  97. data/lib/mcollective/shell.rb +9 -4
  98. data/lib/mcollective/signer/base.rb +28 -0
  99. data/lib/mcollective/signer/choria.rb +185 -0
  100. data/lib/mcollective/ssl.rb +8 -6
  101. data/lib/mcollective/util.rb +58 -55
  102. data/lib/mcollective/util/bolt_support.rb +176 -0
  103. data/lib/mcollective/util/bolt_support/plan_runner.rb +167 -0
  104. data/lib/mcollective/util/bolt_support/task_result.rb +94 -0
  105. data/lib/mcollective/util/bolt_support/task_results.rb +128 -0
  106. data/lib/mcollective/util/choria.rb +1103 -0
  107. data/lib/mcollective/util/indifferent_hash.rb +12 -0
  108. data/lib/mcollective/util/natswrapper.rb +242 -0
  109. data/lib/mcollective/util/playbook.rb +435 -0
  110. data/lib/mcollective/util/playbook/data_stores.rb +201 -0
  111. data/lib/mcollective/util/playbook/data_stores/base.rb +99 -0
  112. data/lib/mcollective/util/playbook/data_stores/consul_data_store.rb +88 -0
  113. data/lib/mcollective/util/playbook/data_stores/environment_data_store.rb +33 -0
  114. data/lib/mcollective/util/playbook/data_stores/etcd_data_store.rb +42 -0
  115. data/lib/mcollective/util/playbook/data_stores/file_data_store.rb +106 -0
  116. data/lib/mcollective/util/playbook/data_stores/shell_data_store.rb +103 -0
  117. data/lib/mcollective/util/playbook/inputs.rb +265 -0
  118. data/lib/mcollective/util/playbook/nodes.rb +207 -0
  119. data/lib/mcollective/util/playbook/nodes/mcollective_nodes.rb +86 -0
  120. data/lib/mcollective/util/playbook/nodes/pql_nodes.rb +40 -0
  121. data/lib/mcollective/util/playbook/nodes/shell_nodes.rb +55 -0
  122. data/lib/mcollective/util/playbook/nodes/terraform_nodes.rb +65 -0
  123. data/lib/mcollective/util/playbook/nodes/yaml_nodes.rb +47 -0
  124. data/lib/mcollective/util/playbook/playbook_logger.rb +47 -0
  125. data/lib/mcollective/util/playbook/puppet_logger.rb +51 -0
  126. data/lib/mcollective/util/playbook/report.rb +152 -0
  127. data/lib/mcollective/util/playbook/task_result.rb +55 -0
  128. data/lib/mcollective/util/playbook/tasks.rb +196 -0
  129. data/lib/mcollective/util/playbook/tasks/base.rb +45 -0
  130. data/lib/mcollective/util/playbook/tasks/graphite_event_task.rb +64 -0
  131. data/lib/mcollective/util/playbook/tasks/mcollective_task.rb +356 -0
  132. data/lib/mcollective/util/playbook/tasks/shell_task.rb +93 -0
  133. data/lib/mcollective/util/playbook/tasks/slack_task.rb +105 -0
  134. data/lib/mcollective/util/playbook/tasks/webhook_task.rb +136 -0
  135. data/lib/mcollective/util/playbook/template_util.rb +98 -0
  136. data/lib/mcollective/util/playbook/uses.rb +169 -0
  137. data/lib/mcollective/util/tasks_support.rb +733 -0
  138. data/lib/mcollective/util/tasks_support/cli.rb +260 -0
  139. data/lib/mcollective/util/tasks_support/default_formatter.rb +138 -0
  140. data/lib/mcollective/util/tasks_support/json_formatter.rb +108 -0
  141. data/lib/mcollective/validator.rb +6 -1
  142. data/lib/mcollective/validator/bolt_task_name_validator.ddl +7 -0
  143. data/lib/mcollective/validator/bolt_task_name_validator.rb +11 -0
  144. data/lib/mcollective/validator/length_validator.rb +1 -3
  145. metadata +65 -2
@@ -0,0 +1,249 @@
1
+ module MCollective
2
+ class Application
3
+ class Choria < Application
4
+ description "Choria Orchestrator Management and Configuration"
5
+
6
+ usage <<-USAGE
7
+ mco choria [OPTIONS] <ACTION>
8
+
9
+ The ACTION can be one of the following:
10
+
11
+ request_cert - requests a certificate from the Puppet CA
12
+ show_config - shows the active configuration parameters
13
+
14
+ The environment is chosen using --environment and the concurrent
15
+ runs may be limited using --batch.
16
+
17
+ The batching works a bit different than typical, it will only batch
18
+ based on a sorted list of certificate names, this means the batches
19
+ will always run in predictable order.
20
+ USAGE
21
+
22
+ exclude_argument_sections "common", "filter", "rpc"
23
+
24
+ option :ca,
25
+ :arguments => ["--ca SERVER"],
26
+ :description => "Address of your Puppet CA",
27
+ :type => String
28
+
29
+ option :certname,
30
+ :arguments => ["--certname CERTNAME"],
31
+ :description => "Override the default certificate name",
32
+ :type => String
33
+
34
+ def post_option_parser(configuration)
35
+ if ARGV.length >= 1
36
+ configuration[:command] = ARGV.shift
37
+ else
38
+ abort("Please specify a command, valid commands are: %s" % valid_commands.join(", "))
39
+ end
40
+
41
+ ENV["MCOLLECTIVE_CERTNAME"] = configuration[:certname] if configuration[:certname]
42
+ end
43
+
44
+ # Validates the configuration
45
+ #
46
+ # @return [void]
47
+ def validate_configuration(configuration)
48
+ Util.loadclass("MCollective::Util::Choria")
49
+
50
+ abort("Unknown command %s, valid commands are: %s" % [configuration[:command], valid_commands.join(", ")]) unless valid_commands.include?(configuration[:command])
51
+
52
+ if !choria.has_client_public_cert? && !["request_cert", "show_config"].include?(configuration[:command])
53
+ abort("A certificate is needed from the Puppet CA for `%s`, please use the `request_cert` command" % choria.certname)
54
+ end
55
+ end
56
+
57
+ def main
58
+ send("%s_command" % configuration[:command])
59
+ rescue Util::Choria::UserError
60
+ warn("Encountered a critical error: %s" % Util.colorize(:red, $!.to_s))
61
+ rescue Util::Choria::Abort
62
+ exit(1)
63
+ end
64
+
65
+ # Requests a certificate from the CA
66
+ #
67
+ # @return [void]
68
+ def request_cert_command
69
+ disconnect
70
+
71
+ raise(Util::Choria::UserError, "Cannot only request certificates in Puppet security mode") unless choria.puppet_security?
72
+
73
+ raise(Util::Choria::UserError, "Already have a certificate '%s', cannot request a new one" % choria.client_public_cert) if choria.has_client_public_cert?
74
+
75
+ choria.ca = configuration[:ca] if configuration[:ca]
76
+
77
+ certname = choria.client_public_cert
78
+
79
+ choria.make_ssl_dirs
80
+ choria.fetch_ca
81
+
82
+ if choria.waiting_for_cert?
83
+ puts("Certificate %s has already been requested, attempting to retrieve it" % certname)
84
+ else
85
+ puts("Requesting certificate for '%s'" % certname)
86
+ choria.request_cert
87
+ end
88
+
89
+ puts("Waiting up to 240 seconds for it to be signed")
90
+ puts
91
+
92
+ puts("Key fingerprint: %s" % choria.csr_fingerprint)
93
+ puts
94
+
95
+ 24.times do |time|
96
+ print "Attempting to download certificate %s: %d / 24\r" % [certname, time]
97
+
98
+ break if choria.attempt_fetch_cert
99
+
100
+ sleep 10
101
+ end
102
+
103
+ unless choria.has_client_public_cert?
104
+ raise(Util::Choria::UserError, "Could not fetch the certificate after 240 seconds, please ensure it gets signed and rerun this command")
105
+ end
106
+
107
+ puts("Certificate %s has been stored in %s" % [certname, choria.ssl_dir])
108
+ end
109
+
110
+ def show_config_command # rubocop:disable Metrics/MethodLength
111
+ disconnect
112
+
113
+ puts "Active Choria configuration:"
114
+ puts
115
+ puts "The active configuration used in Choria comes from using Puppet AIO defaults, querying SRV"
116
+ puts "records and reading configuration files. The below information shows the completely resolved"
117
+ puts "configuration that will be used when running MCollective commands"
118
+ puts
119
+ puts "MCollective related:"
120
+ puts
121
+ puts " MCollective Version: %s" % MCollective::VERSION
122
+ puts " Choria Version: %s" % Util::Choria::VERSION
123
+ puts " Client Config File: %s" % Util.config_file_for_user
124
+ puts " Active Config File: %s" % Config.instance.configfile
125
+ puts " Plugin Config Dir: %s" % File.join(Config.instance.configdir, "plugin.d")
126
+ puts " Using SRV Records: %s" % choria.should_use_srv?
127
+ puts " Federated: %s" % choria.federated?
128
+ puts " SRV Domain: %s" % choria.srv_domain
129
+ puts " NATS NGS: %s" % choria.ngs?
130
+
131
+ middleware_servers = choria.middleware_servers("puppet", 4222).map {|s, p| "%s:%s" % [s, p]}.join(", ")
132
+
133
+ puts " Middleware Servers: %s" % middleware_servers
134
+
135
+ if choria.federated?
136
+ fed_servers = choria.federation_middleware_servers
137
+ if fed_servers
138
+ puts " Federation Servers: %s" % fed_servers.map {|s, p| "%s:%s" % [s, p]}.join(", ")
139
+ else
140
+ puts " Federation Servers: %s (fallback to normal client settings)" % middleware_servers
141
+ end
142
+ puts " Federation Collectives: %s" % choria.federation_collectives.join(", ")
143
+ end
144
+
145
+ puts
146
+
147
+ puppet_server = choria.puppet_server
148
+ puppetca_server = choria.puppetca_server
149
+ puppetdb_server = choria.puppetdb_server
150
+
151
+ puts "Puppet related:"
152
+ puts
153
+ puts " Puppet Server: %s:%s" % [puppet_server[:target], puppet_server[:port]]
154
+ puts " PuppetCA Server: %s:%s" % [puppetca_server[:target], puppetca_server[:port]]
155
+ puts " PuppetDB Server: %s:%s" % [puppetdb_server[:target], puppetdb_server[:port]]
156
+
157
+ if choria.proxied_discovery?
158
+ proxy_server = choria.discovery_server
159
+ puts " Discovery Proxy: %s:%s" % [proxy_server[:target], proxy_server[:port]]
160
+ else
161
+ puts " Discovery Proxy: not using a proxy"
162
+ end
163
+
164
+ puts " Facter Command: %s" % choria.facter_cmd
165
+ puts " Facter Domain: %s" % choria.facter_domain
166
+
167
+ puts
168
+
169
+ puts "Security setup:"
170
+ puts
171
+
172
+ valid_ssl = choria.check_ssl_setup(false) rescue false
173
+
174
+ if valid_ssl
175
+ puts " Valid SSL Setup: %s" % [Util.colorize(:green, "yes")]
176
+ else
177
+ puts " Valid SSL Setup: %s try running 'mco choria request_cert'" % [Util.colorize(:red, "no")]
178
+ end
179
+
180
+ puts " Security Provider: %s" % [choria.security_provider]
181
+ puts " Certname: %s" % [choria.certname]
182
+ puts " SSL Directory: %s (%s)" % [choria.ssl_dir, File.exist?(choria.ssl_dir) ? Util.colorize(:green, "found") : Util.colorize(:red, "absent")]
183
+ puts " Client Public Cert: %s (%s)" % [choria.client_public_cert, choria.has_client_public_cert? ? Util.colorize(:green, "found") : Util.colorize(:red, "absent")]
184
+ puts " Client Private Key: %s (%s)" % [choria.client_private_key, choria.has_client_private_key? ? Util.colorize(:green, "found") : Util.colorize(:red, "absent")]
185
+ puts " CA Path: %s (%s)" % [choria.ca_path, choria.has_ca? ? Util.colorize(:green, "found") : Util.colorize(:red, "absent")]
186
+ puts " CSR Path: %s (%s)" % [choria.csr_path, choria.has_csr? ? Util.colorize(:green, "found") : Util.colorize(:red, "absent")]
187
+
188
+ if choria.has_client_public_cert?
189
+ cn = choria.valid_certificate?(File.read(choria.client_public_cert), choria.certname, false)
190
+
191
+ puts " Public Cert CN: %s (%s)" % [cn, cn == choria.certname ? Util.colorize(:green, "match") : Util.colorize(:red, "does not match certname")]
192
+ end
193
+
194
+ if choria.credential_file?
195
+ puts " NATS Credentials: %s (%s)" % [
196
+ choria.credential_file,
197
+ File.exist?(choria.credential_file) ? Util.colorize(:green, "exit") : Util.colorize(:red, "does not exist")
198
+ ]
199
+ puts " 'nkeys' gem: %s" % choria.nkeys?
200
+ end
201
+
202
+ puts
203
+
204
+ puts "Active Choria configuration settings as found in configuration files:"
205
+ puts
206
+
207
+ choria_settings = Config.instance.pluginconf.select {|k, _| k.start_with?("choria")}
208
+ padding = choria_settings.empty? ? 2 : choria_settings.keys.map(&:length).max + 2
209
+
210
+ if choria_settings.empty?
211
+ puts " No custom Choria settings found in your configuration files"
212
+ else
213
+ choria_settings.each do |k, v|
214
+ puts "%#{padding}s: %s" % [k, v]
215
+ end
216
+ end
217
+
218
+ puts
219
+ end
220
+
221
+ # Creates and cache a Choria helper class
222
+ #
223
+ # @return [Util::Choria]
224
+ def choria
225
+ @_choria ||= Util::Choria.new(false)
226
+ end
227
+
228
+ # List of valid commands this application respond to
229
+ #
230
+ # @return [Array<String>] like `plan` and `run`
231
+ def valid_commands
232
+ methods.grep(/_command$/).map {|c| c.to_s.gsub("_command", "")}
233
+ end
234
+
235
+ # Asks the user to confirm something on the CLI
236
+ #
237
+ # @note exits the application on no
238
+ # @param msg [String] the message to ask
239
+ # @return [void]
240
+ def confirm(msg)
241
+ print("%s (y/n) " % msg)
242
+
243
+ $stdout.flush
244
+
245
+ exit(1) unless $stdin.gets.strip.match?(/^(?:y|yes)$/i)
246
+ end
247
+ end
248
+ end
249
+ end
@@ -1,5 +1,5 @@
1
1
  module MCollective
2
- class Application::Completion<MCollective::Application
2
+ class Application::Completion < MCollective::Application # rubocop:disable Style/ClassAndModuleChildren
3
3
  description "Helper for shell completion systems"
4
4
 
5
5
  exclude_argument_sections "common", "filter", "rpc"
@@ -43,8 +43,8 @@ module MCollective
43
43
  PluginManager.find(:agent, "ddl").each do |agent|
44
44
  begin
45
45
  ddl = DDL.new(agent)
46
- puts "%s:%s" % [ agent, ddl.meta[:description] ]
47
- rescue
46
+ puts "%s:%s" % [agent, ddl.meta[:description]]
47
+ rescue # rubocop:disable Lint/SuppressedException
48
48
  end
49
49
  end
50
50
  else
@@ -64,7 +64,7 @@ module MCollective
64
64
  else
65
65
  DDL.new(configuration[:agent], :agent).actions.sort.each {|a| puts a}
66
66
  end
67
- rescue
67
+ rescue # rubocop:disable Lint/SuppressedException
68
68
  end
69
69
 
70
70
  def list_inputs
@@ -79,7 +79,7 @@ module MCollective
79
79
  else
80
80
  DDL.new(configuration[:agent], :agent).action_interface(configuration[:action])[:input].keys.sort.each {|i| puts i}
81
81
  end
82
- rescue
82
+ rescue # rubocop:disable Lint/SuppressedException
83
83
  end
84
84
 
85
85
  def list_applications
@@ -93,7 +93,7 @@ module MCollective
93
93
  end
94
94
 
95
95
  def main
96
- actions = configuration.keys.map{|k| k.to_s}.grep(/^list_/)
96
+ actions = configuration.keys.map(&:to_s).grep(/^list_/)
97
97
 
98
98
  abort "Please choose either --list-[agents|actions|inputs|applications]" if actions.empty?
99
99
  abort "Please choose only one of --list-[agents|actions|inputs|applications]" if actions.size > 1
@@ -1,5 +1,5 @@
1
1
  module MCollective
2
- class Application::Describe_filter<Application
2
+ class Application::Describe_filter < Application # rubocop:disable Style/ClassAndModuleChildren
3
3
  exclude_argument_sections "common", "rpc"
4
4
 
5
5
  description "Display human readable interpretation of filters"
@@ -12,30 +12,29 @@ module MCollective
12
12
  puts "-S Query expands to the following instructions:"
13
13
  puts
14
14
  stack.each do |token|
15
- if token.keys[0] == "statement"
15
+ case token.keys[0]
16
+ when "statement"
16
17
  if token.values[0] =~ /(<=|>=|=|=~|=)/
17
18
  op = $1
18
- k,v = token.values[0].split(op)
19
+ k, v = token.values[0].split(op)
19
20
  puts indent + get_fact_string(k, v, op)
20
21
  else
21
22
  puts indent + get_class_string(token.values[0])
22
23
  end
23
- elsif token.keys[0] == "fstatement"
24
+ when "fstatement"
24
25
  v = token.values[0]
25
- result_string = indent + "Execute the Data Query '#{v["name"]}'"
26
- if v["params"]
27
- result_string += " with parameters (#{v["params"]})"
28
- end
26
+ result_string = indent + "Execute the Data Query '#{v['name']}'"
27
+ result_string += " with parameters (#{v['params']})" if v["params"]
29
28
  result_string += ". "
30
- result_string += "Check if the query's '#{v["value"]}' value #{v["operator"]} '#{v["r_compare"]}' "
29
+ result_string += "Check if the query's '#{v['value']}' value #{v['operator']} '#{v['r_compare']}' "
31
30
  puts result_string
32
- elsif token.keys[0] == "("
33
- puts indent + "("
31
+ when "("
32
+ puts "#{indent}("
34
33
  old_indent = indent
35
34
  indent *= 2
36
- elsif token.keys[0] == ")"
35
+ when ")"
37
36
  indent = old_indent
38
- puts indent + ")"
37
+ puts "#{indent})"
39
38
  else
40
39
  puts indent + token.keys[0].upcase
41
40
  end
@@ -46,7 +45,7 @@ module MCollective
46
45
  puts "-F filter expands to the following fact comparisons:"
47
46
  puts
48
47
  facts.each do |f|
49
- puts " " + get_fact_string(f[:fact], f[:value], f[:operator])
48
+ puts " #{get_fact_string(f[:fact], f[:value], f[:operator])}"
50
49
  end
51
50
  end
52
51
 
@@ -54,30 +53,31 @@ module MCollective
54
53
  puts "-C filter expands to the following class checks:"
55
54
  puts
56
55
  classes.each do |c|
57
- puts " " + get_class_string(c)
56
+ puts " #{get_class_string(c)}"
58
57
  end
59
58
  end
60
59
 
61
60
  def main
62
- if !(@options[:filter]["fact"].empty?)
61
+ unless @options[:filter]["fact"].empty?
63
62
  describe_f_filter(@options[:filter]["fact"])
64
63
  puts
65
64
  end
66
65
 
67
- if !(@options[:filter]["cf_class"].empty?)
66
+ unless @options[:filter]["cf_class"].empty?
68
67
  describe_c_filter(@options[:filter]["cf_class"])
69
68
  puts
70
69
  end
71
70
 
72
- if !(@options[:filter]["compound"].empty?)
71
+ unless @options[:filter]["compound"].empty?
73
72
  describe_s_filter(@options[:filter]["compound"][0])
74
73
  puts
75
74
  end
76
75
  end
77
76
 
78
77
  private
79
- def get_fact_string(fact, value, op)
80
- "Check if fact '#{fact}' #{op} '#{value}'"
78
+
79
+ def get_fact_string(fact, value, oper)
80
+ "Check if fact '#{fact}' #{oper} '#{value}'"
81
81
  end
82
82
 
83
83
  def get_class_string(classname)
@@ -1,8 +1,8 @@
1
- class MCollective::Application::Facts<MCollective::Application
1
+ class MCollective::Application::Facts < MCollective::Application # rubocop:disable Style/ClassAndModuleChildren
2
2
  description "Reports on usage for a specific fact"
3
3
 
4
4
  def post_option_parser(configuration)
5
- configuration[:fact] = ARGV.shift if ARGV.size > 0
5
+ configuration[:fact] = ARGV.shift unless ARGV.empty?
6
6
  end
7
7
 
8
8
  def validate_configuration(configuration)
@@ -12,22 +12,30 @@ class MCollective::Application::Facts<MCollective::Application
12
12
  def show_single_fact_report(fact, facts, verbose=false)
13
13
  puts("Report for fact: #{fact}\n\n")
14
14
 
15
+ facts = stringify_facts_hash(facts)
16
+
15
17
  field_size = MCollective::Util.field_size(facts.keys)
16
18
  facts.keys.sort.each do |k|
17
19
  printf(" %-#{field_size}s found %d times\n", k, facts[k].size)
18
20
 
19
- if verbose
20
- puts
21
+ next unless verbose
21
22
 
22
- facts[k].sort.each do |f|
23
- puts(" #{f}")
24
- end
23
+ puts
25
24
 
26
- puts
25
+ facts[k].sort.each do |f|
26
+ puts(" #{f}")
27
27
  end
28
+
29
+ puts
28
30
  end
29
31
  end
30
32
 
33
+ def stringify_facts_hash(facts)
34
+ res = Hash.new([])
35
+ facts.each { |k, v| res[k.to_s] += v }
36
+ res
37
+ end
38
+
31
39
  def main
32
40
  rpcutil = rpcclient("rpcutil")
33
41
  rpcutil.progress = false
@@ -42,11 +50,11 @@ class MCollective::Application::Facts<MCollective::Application
42
50
  if facts.include?(value)
43
51
  facts[value] << resp[:senderid]
44
52
  else
45
- facts[value] = [ resp[:senderid] ]
53
+ facts[value] = [resp[:senderid]]
46
54
  end
47
55
  end
48
- rescue Exception => e
49
- STDERR.puts "Could not parse facts for #{resp[:senderid]}: #{e.class}: #{e}"
56
+ rescue Exception => e # rubocop:disable Lint/RescueException
57
+ warn "Could not parse facts for #{resp[:senderid]}: #{e.class}: #{e}"
50
58
  end
51
59
  end
52
60
 
@@ -0,0 +1,239 @@
1
+ module MCollective
2
+ class Application
3
+ class Federation < Application
4
+ description "Choria Federation Broker Utilities"
5
+
6
+ usage <<-USAGE
7
+ mco federation [OPTIONS] <ACTION>
8
+
9
+ The ACTION can be one of the following:
10
+
11
+ trace - trace the path to a client
12
+
13
+ USAGE
14
+
15
+ exclude_argument_sections "common", "filter", "rpc"
16
+
17
+ option :collective,
18
+ :description => "Target messages to a specific sub collective",
19
+ :arguments => ["-T", "--target COLLECTIVE"],
20
+ :type => String,
21
+ :required => false
22
+
23
+ # Publish a specially crafted 'ping' with seen-by headers
24
+ # embedded, this signals to the entire collective to record
25
+ # their route
26
+ def trace_node(node)
27
+ options[:filter] = Util.empty_filter
28
+
29
+ request = Message.new("ping", nil,
30
+ :agent => "discovery",
31
+ :filter => options[:filter],
32
+ :collective => options[:collective],
33
+ :type => :request,
34
+ :headers => {"seen-by" => []},
35
+ :options => options)
36
+
37
+ request.discovered_hosts = [node]
38
+
39
+ client = Client.new(options)
40
+ found_time = 0
41
+ route = []
42
+
43
+ stats = client.req(request) do |reply, message|
44
+ abort("Tracing requests requires MCollective 2.10.3 or newer") unless message
45
+
46
+ found_time = Time.now.to_f
47
+
48
+ abort("Received a response from %s while expecting a response from %s" % [reply[:senderid], node]) unless reply[:senderid] == node
49
+
50
+ route = message.headers["seen-by"]
51
+ end
52
+
53
+ raise("Did not receive any responses to trace request") if stats[:responses] == 0
54
+
55
+ {:route => route, :stats => stats, :response_time => (found_time - stats[:starttime]), :request => request.requestid}
56
+ end
57
+
58
+ def route_to(route, destination)
59
+ route.take_while {|hop| hop[1] != destination}
60
+ end
61
+
62
+ def route_back(route, destination)
63
+ route.reverse.take_while {|hop| hop.size == 2 || hop[1] != destination}.reverse
64
+ end
65
+
66
+ def destination_hop(route)
67
+ route[route_to(route, destination).size]
68
+ end
69
+
70
+ def extract_federation_brokers(route, destination)
71
+ abort("Unexpected route size %d found" % route.size) unless route.size == 5
72
+
73
+ [route_to(route, destination).last[1], route_back(route, destination).first[1]]
74
+ end
75
+
76
+ def draw_cluster(left, right, direction, indent)
77
+ if left.last == right.first && direction == :out
78
+ "%s└── %s" % [" " * indent, left.last]
79
+ elsif left.last == right.first
80
+ "%s┌── %s" % [" " * indent, left.last]
81
+ elsif direction == :out
82
+ [
83
+ "%s└─┐ %s" % [" " * indent, left.last],
84
+ "%s └ %s" % [" " * indent, right.first]
85
+ ].join("\n")
86
+ else
87
+ [
88
+ "%s ┌ %s" % [" " * indent, left.last],
89
+ "%s┌─┘ %s" % [" " * indent, right.first]
90
+ ].join("\n")
91
+ end
92
+ end
93
+
94
+ # @todo this is aweful brute force rubbish while I dont know what I want, make some algo
95
+ def display_unfederated_route(route)
96
+ c_out, node, c_in = route
97
+
98
+ if c_out[1] == node[0] && node[2] == c_out[1] && node[0] == c_out[1]
99
+ puts " Shared Middleware"
100
+ puts
101
+ puts " %s" % Util.colorize(:cyan, c_out[0])
102
+ puts draw_cluster(c_out, node, :out, 7)
103
+ puts " └── %s" % Util.colorize(:yellow, node[1])
104
+
105
+ elsif c_out[1] == c_in[0] && node[0] == node[2]
106
+ puts " NATS Cluster with Symetrical Path"
107
+ puts
108
+ puts " %s" % Util.colorize(:cyan, c_out[0])
109
+ puts draw_cluster(c_out, node, :out, 7)
110
+ puts " └── %s" % Util.colorize(:yellow, node[1])
111
+
112
+ else
113
+ puts " NATS Cluster with Asymmetrical Path"
114
+ puts
115
+ puts " %s" % Util.colorize(:cyan, c_out[0])
116
+ puts draw_cluster(c_out, node, :out, 5)
117
+ puts " ├── %s" % Util.colorize(:yellow, node[1])
118
+ puts draw_cluster(node, c_in, :in, 5)
119
+ puts " %s" % Util.colorize(:cyan, c_in[1])
120
+ end
121
+ puts
122
+ puts "[%s] Client [%s] Server [%s] Middleware" % [
123
+ Util.colorize(:cyan, "█"), Util.colorize(:yellow, "█"), "█"
124
+ ]
125
+ puts
126
+ end
127
+
128
+ # @todo this is aweful brute force rubbish while I dont know what I want, make some algo
129
+ def display_federated_route(route)
130
+ c_out, fb1, node, fb2, c_in = route
131
+
132
+ puts " %s" % Util.colorize(:bold, "Request:")
133
+ puts " %s" % Util.colorize(:cyan, c_out[0])
134
+ puts draw_cluster(c_out, fb1, :out, 7)
135
+ puts " └─ %s" % Util.colorize(:green, fb1[1])
136
+ puts draw_cluster(fb1, node, :out, 15)
137
+ puts " └── %s" % Util.colorize(:yellow, node[1])
138
+ puts
139
+ puts " %s" % Util.colorize(:bold, "Reply:")
140
+ puts " ┌── %s" % Util.colorize(:yellow, node[1])
141
+ puts draw_cluster(node, fb2, :int, 15)
142
+ puts " ┌─ %s" % Util.colorize(:green, fb2[1])
143
+ puts draw_cluster(fb2, c_in, :in, 7)
144
+ puts " %s" % Util.colorize(:cyan, c_in[1])
145
+ puts
146
+ puts "[%s] Client [%s] Federation Broker [%s] Server [%s] Middleware" % [
147
+ Util.colorize(:cyan, "█"), Util.colorize(:green, "█"), Util.colorize(:yellow, "█"), "█"
148
+ ]
149
+ puts
150
+ end
151
+
152
+ def trace_command
153
+ result = trace_node(configuration[:host])
154
+
155
+ abort("No routes were reported, are your nodes running a supported version?") if result[:route].empty?
156
+
157
+ puts "Received response from %s in %.2fms for message %s" % [configuration[:host], result[:response_time] * 1000, result[:request]]
158
+ puts
159
+
160
+ route = result[:route]
161
+ puts "Reported Route:"
162
+ puts
163
+ case route.size
164
+ when 5
165
+ display_federated_route(route)
166
+ when 3
167
+ display_unfederated_route(route)
168
+ end
169
+
170
+ puts
171
+
172
+ puts "Federation Brokers Instances:"
173
+ puts
174
+ if choria.federated?
175
+ extract_federation_brokers(result[:route], configuration[:host]).sort.uniq.each do |broker|
176
+ puts " %s" % broker
177
+ end
178
+ else
179
+ puts " Unfederated"
180
+ end
181
+ puts
182
+
183
+ puts "Known Federation Broker Clusters:"
184
+ puts
185
+ if choria.federated?
186
+ puts " %s" % choria.federation_collectives.join(", ")
187
+ else
188
+ puts " Unfederated"
189
+ end
190
+ end
191
+
192
+ # Creates and cache a Choria helper class
193
+ #
194
+ # @return [Util::Choria]
195
+ def choria
196
+ @_choria ||= Util::Choria.new
197
+ end
198
+
199
+ def post_option_parser(configuration)
200
+ if ARGV.length >= 1
201
+ configuration[:command] = ARGV.shift
202
+ else
203
+ abort("Please specify a command, valid commands are: %s" % valid_commands.join(", "))
204
+ end
205
+
206
+ if configuration[:command] == "trace"
207
+ if ARGV.length >= 1
208
+ configuration[:host] = ARGV.shift
209
+ else
210
+ abort("Please specify a host to trace, example: mco federation trace node1.prod.example.net")
211
+ end
212
+ end
213
+ end
214
+
215
+ def validate_configuration(configuration)
216
+ Util.loadclass("MCollective::Util::Choria")
217
+
218
+ abort("Unknown command %s, valid commands are: %s" % [configuration[:command], valid_commands.join(", ")]) unless valid_commands.include?(configuration[:command])
219
+
220
+ if !choria.has_client_public_cert? && !["request_cert", "show_config"].include?(configuration[:command])
221
+ abort("A certificate is needed from the Puppet CA for `%s`, please use the `request_cert` command" % choria.certname)
222
+ end
223
+ end
224
+
225
+ def main
226
+ send("%s_command" % configuration[:command])
227
+ rescue Interrupt
228
+ exit
229
+ end
230
+
231
+ # List of valid commands this application respond to
232
+ #
233
+ # @return [Array<String>] like `plan` and `run`
234
+ def valid_commands
235
+ methods.grep(/_command$/).map {|c| c.to_s.gsub("_command", "")}
236
+ end
237
+ end
238
+ end
239
+ end