choria-mcorpc-support 2.21.1 → 2.23.1

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 -2
  3. data/lib/mcollective/agent/bolt_tasks.ddl +253 -0
  4. data/lib/mcollective/agent/bolt_tasks.json +365 -0
  5. data/lib/mcollective/agent/bolt_tasks.rb +178 -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 +8 -4
  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 +189 -0
  23. data/lib/mcollective/application/completion.rb +6 -6
  24. data/lib/mcollective/application/facts.rb +11 -11
  25. data/lib/mcollective/application/federation.rb +237 -0
  26. data/lib/mcollective/application/find.rb +4 -4
  27. data/lib/mcollective/application/help.rb +3 -3
  28. data/lib/mcollective/application/inventory.rb +3 -341
  29. data/lib/mcollective/application/ping.rb +5 -51
  30. data/lib/mcollective/application/playbook.rb +207 -0
  31. data/lib/mcollective/application/plugin.rb +106 -106
  32. data/lib/mcollective/application/rpc.rb +3 -108
  33. data/lib/mcollective/application/tasks.rb +425 -0
  34. data/lib/mcollective/applications.rb +11 -10
  35. data/lib/mcollective/audit/choria.rb +33 -0
  36. data/lib/mcollective/cache.rb +2 -4
  37. data/lib/mcollective/client.rb +11 -10
  38. data/lib/mcollective/config.rb +21 -34
  39. data/lib/mcollective/connector/base.rb +2 -1
  40. data/lib/mcollective/connector/nats.ddl +9 -0
  41. data/lib/mcollective/connector/nats.rb +450 -0
  42. data/lib/mcollective/data.rb +8 -3
  43. data/lib/mcollective/data/agent_data.rb +1 -1
  44. data/lib/mcollective/data/base.rb +6 -5
  45. data/lib/mcollective/data/bolt_task_data.ddl +90 -0
  46. data/lib/mcollective/data/bolt_task_data.rb +32 -0
  47. data/lib/mcollective/data/collective_data.rb +1 -1
  48. data/lib/mcollective/data/fact_data.rb +6 -6
  49. data/lib/mcollective/data/fstat_data.rb +2 -4
  50. data/lib/mcollective/data/result.rb +7 -2
  51. data/lib/mcollective/ddl/agentddl.rb +5 -17
  52. data/lib/mcollective/ddl/base.rb +10 -13
  53. data/lib/mcollective/discovery.rb +24 -39
  54. data/lib/mcollective/discovery/choria.ddl +11 -0
  55. data/lib/mcollective/discovery/choria.rb +223 -0
  56. data/lib/mcollective/discovery/flatfile.rb +7 -8
  57. data/lib/mcollective/discovery/mc.rb +2 -2
  58. data/lib/mcollective/discovery/stdin.rb +17 -18
  59. data/lib/mcollective/exceptions.rb +13 -0
  60. data/lib/mcollective/facts/base.rb +9 -9
  61. data/lib/mcollective/facts/yaml_facts.rb +12 -12
  62. data/lib/mcollective/generators.rb +3 -3
  63. data/lib/mcollective/generators/agent_generator.rb +3 -4
  64. data/lib/mcollective/generators/base.rb +14 -15
  65. data/lib/mcollective/generators/data_generator.rb +5 -6
  66. data/lib/mcollective/log.rb +2 -2
  67. data/lib/mcollective/logger/base.rb +3 -2
  68. data/lib/mcollective/logger/console_logger.rb +10 -10
  69. data/lib/mcollective/logger/file_logger.rb +7 -7
  70. data/lib/mcollective/logger/syslog_logger.rb +11 -15
  71. data/lib/mcollective/message.rb +8 -39
  72. data/lib/mcollective/monkey_patches.rb +2 -4
  73. data/lib/mcollective/optionparser.rb +2 -1
  74. data/lib/mcollective/pluginmanager.rb +3 -5
  75. data/lib/mcollective/pluginpackager.rb +1 -3
  76. data/lib/mcollective/pluginpackager/agent_definition.rb +3 -8
  77. data/lib/mcollective/pluginpackager/forge_packager.rb +7 -9
  78. data/lib/mcollective/pluginpackager/standard_definition.rb +1 -2
  79. data/lib/mcollective/registration/base.rb +18 -16
  80. data/lib/mcollective/rpc.rb +2 -4
  81. data/lib/mcollective/rpc/actionrunner.rb +16 -18
  82. data/lib/mcollective/rpc/agent.rb +26 -43
  83. data/lib/mcollective/rpc/audit.rb +1 -0
  84. data/lib/mcollective/rpc/client.rb +67 -85
  85. data/lib/mcollective/rpc/helpers.rb +55 -62
  86. data/lib/mcollective/rpc/progress.rb +2 -2
  87. data/lib/mcollective/rpc/reply.rb +17 -19
  88. data/lib/mcollective/rpc/request.rb +7 -5
  89. data/lib/mcollective/rpc/result.rb +6 -8
  90. data/lib/mcollective/rpc/stats.rb +49 -58
  91. data/lib/mcollective/security/base.rb +13 -56
  92. data/lib/mcollective/security/choria.rb +765 -0
  93. data/lib/mcollective/shell.rb +9 -4
  94. data/lib/mcollective/signer/base.rb +28 -0
  95. data/lib/mcollective/signer/choria.rb +185 -0
  96. data/lib/mcollective/ssl.rb +8 -6
  97. data/lib/mcollective/util.rb +58 -55
  98. data/lib/mcollective/util/bolt_support.rb +176 -0
  99. data/lib/mcollective/util/bolt_support/plan_runner.rb +167 -0
  100. data/lib/mcollective/util/bolt_support/task_result.rb +94 -0
  101. data/lib/mcollective/util/bolt_support/task_results.rb +128 -0
  102. data/lib/mcollective/util/choria.rb +946 -0
  103. data/lib/mcollective/util/indifferent_hash.rb +12 -0
  104. data/lib/mcollective/util/natswrapper.rb +242 -0
  105. data/lib/mcollective/util/playbook.rb +435 -0
  106. data/lib/mcollective/util/playbook/data_stores.rb +201 -0
  107. data/lib/mcollective/util/playbook/data_stores/base.rb +99 -0
  108. data/lib/mcollective/util/playbook/data_stores/consul_data_store.rb +88 -0
  109. data/lib/mcollective/util/playbook/data_stores/environment_data_store.rb +33 -0
  110. data/lib/mcollective/util/playbook/data_stores/etcd_data_store.rb +42 -0
  111. data/lib/mcollective/util/playbook/data_stores/file_data_store.rb +106 -0
  112. data/lib/mcollective/util/playbook/data_stores/shell_data_store.rb +103 -0
  113. data/lib/mcollective/util/playbook/inputs.rb +265 -0
  114. data/lib/mcollective/util/playbook/nodes.rb +207 -0
  115. data/lib/mcollective/util/playbook/nodes/mcollective_nodes.rb +86 -0
  116. data/lib/mcollective/util/playbook/nodes/pql_nodes.rb +40 -0
  117. data/lib/mcollective/util/playbook/nodes/shell_nodes.rb +55 -0
  118. data/lib/mcollective/util/playbook/nodes/terraform_nodes.rb +65 -0
  119. data/lib/mcollective/util/playbook/nodes/yaml_nodes.rb +47 -0
  120. data/lib/mcollective/util/playbook/playbook_logger.rb +47 -0
  121. data/lib/mcollective/util/playbook/puppet_logger.rb +51 -0
  122. data/lib/mcollective/util/playbook/report.rb +152 -0
  123. data/lib/mcollective/util/playbook/task_result.rb +55 -0
  124. data/lib/mcollective/util/playbook/tasks.rb +196 -0
  125. data/lib/mcollective/util/playbook/tasks/base.rb +45 -0
  126. data/lib/mcollective/util/playbook/tasks/graphite_event_task.rb +64 -0
  127. data/lib/mcollective/util/playbook/tasks/mcollective_task.rb +356 -0
  128. data/lib/mcollective/util/playbook/tasks/shell_task.rb +93 -0
  129. data/lib/mcollective/util/playbook/tasks/slack_task.rb +105 -0
  130. data/lib/mcollective/util/playbook/tasks/webhook_task.rb +136 -0
  131. data/lib/mcollective/util/playbook/template_util.rb +98 -0
  132. data/lib/mcollective/util/playbook/uses.rb +169 -0
  133. data/lib/mcollective/util/tasks_support.rb +751 -0
  134. data/lib/mcollective/util/tasks_support/cli.rb +260 -0
  135. data/lib/mcollective/util/tasks_support/default_formatter.rb +138 -0
  136. data/lib/mcollective/util/tasks_support/json_formatter.rb +108 -0
  137. data/lib/mcollective/validator.rb +6 -1
  138. data/lib/mcollective/validator/bolt_task_name_validator.ddl +7 -0
  139. data/lib/mcollective/validator/bolt_task_name_validator.rb +11 -0
  140. data/lib/mcollective/validator/length_validator.rb +1 -3
  141. metadata +65 -6
  142. data/lib/mcollective/application/describe_filter.rb +0 -87
  143. data/lib/mcollective/matcher.rb +0 -220
  144. data/lib/mcollective/matcher/parser.rb +0 -128
  145. data/lib/mcollective/matcher/scanner.rb +0 -241
@@ -0,0 +1,189 @@
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
+ show_config - shows the active configuration parameters
12
+
13
+ USAGE
14
+
15
+ exclude_argument_sections "common", "filter", "rpc"
16
+
17
+ def post_option_parser(configuration)
18
+ if ARGV.length >= 1
19
+ configuration[:command] = ARGV.shift
20
+ else
21
+ abort("Please specify a command, valid commands are: %s" % valid_commands.join(", "))
22
+ end
23
+ end
24
+
25
+ # Validates the configuration
26
+ #
27
+ # @return [void]
28
+ def validate_configuration(configuration)
29
+ Util.loadclass("MCollective::Util::Choria")
30
+
31
+ abort("Unknown command %s, valid commands are: %s" % [configuration[:command], valid_commands.join(", ")]) unless valid_commands.include?(configuration[:command])
32
+ end
33
+
34
+ def main
35
+ send("%s_command" % configuration[:command])
36
+ rescue Util::Choria::UserError
37
+ warn("Encountered a critical error: %s" % Util.colorize(:red, $!.to_s))
38
+ rescue Util::Choria::Abort
39
+ exit(1)
40
+ end
41
+
42
+ # Requests a certificate from the CA
43
+ #
44
+ # @return [void]
45
+ def request_cert_command
46
+ puts("Please use 'choria enroll' to enroll in the security subsystem")
47
+ raise(Util::Choria::Abort, "1")
48
+ end
49
+
50
+ def show_config_command # rubocop:disable Metrics/MethodLength
51
+ disconnect
52
+
53
+ puts "Active Choria configuration:"
54
+ puts
55
+ puts "The active configuration used in Choria comes from using Puppet AIO defaults, querying SRV"
56
+ puts "records and reading configuration files. The below information shows the completely resolved"
57
+ puts "configuration that will be used when running MCollective commands"
58
+ puts
59
+ puts "MCollective related:"
60
+ puts
61
+ puts " MCollective Version: %s" % MCollective::VERSION
62
+ puts " Choria Version: %s" % Util::Choria::VERSION
63
+ puts " Client Config File: %s" % Util.config_file_for_user
64
+ puts " Active Config File: %s" % Config.instance.configfile
65
+ puts " Plugin Config Dir: %s" % File.join(Config.instance.configdir, "plugin.d")
66
+ puts " Using SRV Records: %s" % choria.should_use_srv?
67
+ puts " Federated: %s" % choria.federated?
68
+ puts " SRV Domain: %s" % choria.srv_domain
69
+ puts " NATS NGS: %s" % choria.ngs?
70
+
71
+ middleware_servers = choria.middleware_servers("puppet", 4222).map {|s, p| "%s:%s" % [s, p]}.join(", ")
72
+
73
+ puts " Middleware Servers: %s" % middleware_servers
74
+
75
+ if choria.federated?
76
+ fed_servers = choria.federation_middleware_servers
77
+ if fed_servers
78
+ puts " Federation Servers: %s" % fed_servers.map {|s, p| "%s:%s" % [s, p]}.join(", ")
79
+ else
80
+ puts " Federation Servers: %s (fallback to normal client settings)" % middleware_servers
81
+ end
82
+ puts " Federation Collectives: %s" % choria.federation_collectives.join(", ")
83
+ end
84
+
85
+ puts
86
+
87
+ puppet_server = choria.puppet_server
88
+ puppetca_server = choria.puppetca_server
89
+ puppetdb_server = choria.puppetdb_server
90
+
91
+ puts "Puppet related:"
92
+ puts
93
+ puts " Puppet Server: %s:%s" % [puppet_server[:target], puppet_server[:port]]
94
+ puts " PuppetCA Server: %s:%s" % [puppetca_server[:target], puppetca_server[:port]]
95
+ puts " PuppetDB Server: %s:%s" % [puppetdb_server[:target], puppetdb_server[:port]]
96
+
97
+ if choria.proxied_discovery?
98
+ proxy_server = choria.discovery_server
99
+ puts " Discovery Proxy: %s:%s" % [proxy_server[:target], proxy_server[:port]]
100
+ else
101
+ puts " Discovery Proxy: not using a proxy"
102
+ end
103
+
104
+ puts " Facter Command: %s" % choria.facter_cmd
105
+ puts " Facter Domain: %s" % choria.facter_domain
106
+
107
+ puts
108
+
109
+ puts "Security setup:"
110
+ puts
111
+
112
+ valid_ssl = choria.check_ssl_setup(false) rescue false
113
+
114
+ if valid_ssl
115
+ puts " Valid SSL Setup: %s" % [Util.colorize(:green, "yes")]
116
+ else
117
+ puts " Valid SSL Setup: %s try running 'choria enroll'" % [Util.colorize(:red, "no")]
118
+ end
119
+
120
+ puts " Security Provider: %s" % [choria.security_provider]
121
+ puts " Certname: %s" % [choria.certname]
122
+ puts " SSL Directory: %s (%s)" % [choria.ssl_dir, File.exist?(choria.ssl_dir) ? Util.colorize(:green, "found") : Util.colorize(:red, "absent")]
123
+ puts " Client Public Cert: %s (%s)" % [choria.client_public_cert, choria.has_client_public_cert? ? Util.colorize(:green, "found") : Util.colorize(:red, "absent")]
124
+ puts " Client Private Key: %s (%s)" % [choria.client_private_key, choria.has_client_private_key? ? Util.colorize(:green, "found") : Util.colorize(:red, "absent")]
125
+ puts " CA Path: %s (%s)" % [choria.ca_path, choria.has_ca? ? Util.colorize(:green, "found") : Util.colorize(:red, "absent")]
126
+ puts " CSR Path: %s (%s)" % [choria.csr_path, choria.has_csr? ? Util.colorize(:green, "found") : Util.colorize(:red, "absent")]
127
+
128
+ if choria.has_client_public_cert?
129
+ cn = choria.valid_certificate?(File.read(choria.client_public_cert), choria.certname, false)
130
+
131
+ puts " Public Cert CN: %s (%s)" % [cn, cn == choria.certname ? Util.colorize(:green, "match") : Util.colorize(:red, "does not match certname")]
132
+ end
133
+
134
+ if choria.credential_file?
135
+ puts " NATS Credentials: %s (%s)" % [
136
+ choria.credential_file,
137
+ File.exist?(choria.credential_file) ? Util.colorize(:green, "exit") : Util.colorize(:red, "does not exist")
138
+ ]
139
+ puts " 'nkeys' gem: %s" % choria.nkeys?
140
+ end
141
+
142
+ puts
143
+
144
+ puts "Active Choria configuration settings as found in configuration files:"
145
+ puts
146
+
147
+ choria_settings = Config.instance.pluginconf.select {|k, _| k.start_with?("choria")}
148
+ padding = choria_settings.empty? ? 2 : choria_settings.keys.map(&:length).max + 2
149
+
150
+ if choria_settings.empty?
151
+ puts " No custom Choria settings found in your configuration files"
152
+ else
153
+ choria_settings.each do |k, v|
154
+ puts "%#{padding}s: %s" % [k, v]
155
+ end
156
+ end
157
+
158
+ puts
159
+ end
160
+
161
+ # Creates and cache a Choria helper class
162
+ #
163
+ # @return [Util::Choria]
164
+ def choria
165
+ @_choria ||= Util::Choria.new(false)
166
+ end
167
+
168
+ # List of valid commands this application respond to
169
+ #
170
+ # @return [Array<String>] like `plan` and `run`
171
+ def valid_commands
172
+ methods.grep(/_command$/).map {|c| c.to_s.gsub("_command", "")}
173
+ end
174
+
175
+ # Asks the user to confirm something on the CLI
176
+ #
177
+ # @note exits the application on no
178
+ # @param msg [String] the message to ask
179
+ # @return [void]
180
+ def confirm(msg)
181
+ print("%s (y/n) " % msg)
182
+
183
+ $stdout.flush
184
+
185
+ exit(1) unless $stdin.gets.strip.match?(/^(?:y|yes)$/i)
186
+ end
187
+ end
188
+ end
189
+ 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,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)
@@ -18,15 +18,15 @@ class MCollective::Application::Facts<MCollective::Application
18
18
  facts.keys.sort.each do |k|
19
19
  printf(" %-#{field_size}s found %d times\n", k, facts[k].size)
20
20
 
21
- if verbose
22
- puts
21
+ next unless verbose
23
22
 
24
- facts[k].sort.each do |f|
25
- puts(" #{f}")
26
- end
23
+ puts
27
24
 
28
- puts
25
+ facts[k].sort.each do |f|
26
+ puts(" #{f}")
29
27
  end
28
+
29
+ puts
30
30
  end
31
31
  end
32
32
 
@@ -50,11 +50,11 @@ class MCollective::Application::Facts<MCollective::Application
50
50
  if facts.include?(value)
51
51
  facts[value] << resp[:senderid]
52
52
  else
53
- facts[value] = [ resp[:senderid] ]
53
+ facts[value] = [resp[:senderid]]
54
54
  end
55
55
  end
56
- rescue Exception => e
57
- 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}"
58
58
  end
59
59
  end
60
60
 
@@ -0,0 +1,237 @@
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
+ abort("A certificate is needed from the Puppet CA for `%s`, please use the `choria enroll` command" % choria.certname) unless choria.has_client_public_cert?
221
+ end
222
+
223
+ def main
224
+ send("%s_command" % configuration[:command])
225
+ rescue Interrupt
226
+ exit
227
+ end
228
+
229
+ # List of valid commands this application respond to
230
+ #
231
+ # @return [Array<String>] like `plan` and `run`
232
+ def valid_commands
233
+ methods.grep(/_command$/).map {|c| c.to_s.gsub("_command", "")}
234
+ end
235
+ end
236
+ end
237
+ end