choria-mcorpc-support 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/bin/mco +64 -0
  3. data/lib/mcollective.rb +63 -0
  4. data/lib/mcollective/agent.rb +5 -0
  5. data/lib/mcollective/agents.rb +149 -0
  6. data/lib/mcollective/aggregate.rb +85 -0
  7. data/lib/mcollective/aggregate/average.ddl +33 -0
  8. data/lib/mcollective/aggregate/average.rb +29 -0
  9. data/lib/mcollective/aggregate/base.rb +40 -0
  10. data/lib/mcollective/aggregate/result.rb +9 -0
  11. data/lib/mcollective/aggregate/result/base.rb +25 -0
  12. data/lib/mcollective/aggregate/result/collection_result.rb +19 -0
  13. data/lib/mcollective/aggregate/result/numeric_result.rb +13 -0
  14. data/lib/mcollective/aggregate/sum.ddl +33 -0
  15. data/lib/mcollective/aggregate/sum.rb +18 -0
  16. data/lib/mcollective/aggregate/summary.ddl +33 -0
  17. data/lib/mcollective/aggregate/summary.rb +53 -0
  18. data/lib/mcollective/application.rb +365 -0
  19. data/lib/mcollective/application/completion.rb +104 -0
  20. data/lib/mcollective/application/describe_filter.rb +87 -0
  21. data/lib/mcollective/application/facts.rb +62 -0
  22. data/lib/mcollective/application/find.rb +23 -0
  23. data/lib/mcollective/application/help.rb +28 -0
  24. data/lib/mcollective/application/inventory.rb +344 -0
  25. data/lib/mcollective/application/ping.rb +82 -0
  26. data/lib/mcollective/application/plugin.rb +369 -0
  27. data/lib/mcollective/application/rpc.rb +111 -0
  28. data/lib/mcollective/applications.rb +134 -0
  29. data/lib/mcollective/cache.rb +145 -0
  30. data/lib/mcollective/client.rb +353 -0
  31. data/lib/mcollective/config.rb +245 -0
  32. data/lib/mcollective/connector.rb +18 -0
  33. data/lib/mcollective/connector/base.rb +26 -0
  34. data/lib/mcollective/data.rb +91 -0
  35. data/lib/mcollective/data/agent_data.ddl +22 -0
  36. data/lib/mcollective/data/agent_data.rb +17 -0
  37. data/lib/mcollective/data/base.rb +67 -0
  38. data/lib/mcollective/data/collective_data.ddl +20 -0
  39. data/lib/mcollective/data/collective_data.rb +9 -0
  40. data/lib/mcollective/data/fact_data.ddl +28 -0
  41. data/lib/mcollective/data/fact_data.rb +55 -0
  42. data/lib/mcollective/data/fstat_data.ddl +89 -0
  43. data/lib/mcollective/data/fstat_data.rb +56 -0
  44. data/lib/mcollective/data/result.rb +45 -0
  45. data/lib/mcollective/ddl.rb +113 -0
  46. data/lib/mcollective/ddl/agentddl.rb +253 -0
  47. data/lib/mcollective/ddl/base.rb +217 -0
  48. data/lib/mcollective/ddl/dataddl.rb +56 -0
  49. data/lib/mcollective/ddl/discoveryddl.rb +52 -0
  50. data/lib/mcollective/ddl/validatorddl.rb +6 -0
  51. data/lib/mcollective/discovery.rb +143 -0
  52. data/lib/mcollective/discovery/flatfile.ddl +11 -0
  53. data/lib/mcollective/discovery/flatfile.rb +48 -0
  54. data/lib/mcollective/discovery/mc.ddl +11 -0
  55. data/lib/mcollective/discovery/mc.rb +30 -0
  56. data/lib/mcollective/discovery/stdin.ddl +11 -0
  57. data/lib/mcollective/discovery/stdin.rb +68 -0
  58. data/lib/mcollective/exceptions.rb +28 -0
  59. data/lib/mcollective/facts.rb +39 -0
  60. data/lib/mcollective/facts/base.rb +100 -0
  61. data/lib/mcollective/facts/yaml_facts.rb +65 -0
  62. data/lib/mcollective/generators.rb +7 -0
  63. data/lib/mcollective/generators/agent_generator.rb +51 -0
  64. data/lib/mcollective/generators/base.rb +46 -0
  65. data/lib/mcollective/generators/data_generator.rb +51 -0
  66. data/lib/mcollective/generators/templates/action_snippet.erb +13 -0
  67. data/lib/mcollective/generators/templates/data_input_snippet.erb +7 -0
  68. data/lib/mcollective/generators/templates/ddl.erb +8 -0
  69. data/lib/mcollective/generators/templates/plugin.erb +7 -0
  70. data/lib/mcollective/log.rb +118 -0
  71. data/lib/mcollective/logger.rb +5 -0
  72. data/lib/mcollective/logger/base.rb +77 -0
  73. data/lib/mcollective/logger/console_logger.rb +61 -0
  74. data/lib/mcollective/logger/file_logger.rb +53 -0
  75. data/lib/mcollective/logger/syslog_logger.rb +53 -0
  76. data/lib/mcollective/matcher.rb +224 -0
  77. data/lib/mcollective/matcher/parser.rb +128 -0
  78. data/lib/mcollective/matcher/scanner.rb +241 -0
  79. data/lib/mcollective/message.rb +248 -0
  80. data/lib/mcollective/monkey_patches.rb +152 -0
  81. data/lib/mcollective/optionparser.rb +197 -0
  82. data/lib/mcollective/pluginmanager.rb +180 -0
  83. data/lib/mcollective/pluginpackager.rb +98 -0
  84. data/lib/mcollective/pluginpackager/agent_definition.rb +94 -0
  85. data/lib/mcollective/pluginpackager/debpackage_packager.rb +237 -0
  86. data/lib/mcollective/pluginpackager/modulepackage_packager.rb +127 -0
  87. data/lib/mcollective/pluginpackager/ospackage_packager.rb +59 -0
  88. data/lib/mcollective/pluginpackager/rpmpackage_packager.rb +180 -0
  89. data/lib/mcollective/pluginpackager/standard_definition.rb +69 -0
  90. data/lib/mcollective/pluginpackager/templates/debian/Makefile.erb +7 -0
  91. data/lib/mcollective/pluginpackager/templates/debian/changelog.erb +5 -0
  92. data/lib/mcollective/pluginpackager/templates/debian/compat.erb +1 -0
  93. data/lib/mcollective/pluginpackager/templates/debian/control.erb +15 -0
  94. data/lib/mcollective/pluginpackager/templates/debian/copyright.erb +8 -0
  95. data/lib/mcollective/pluginpackager/templates/debian/rules.erb +6 -0
  96. data/lib/mcollective/pluginpackager/templates/module/Modulefile.erb +5 -0
  97. data/lib/mcollective/pluginpackager/templates/module/README.md.erb +37 -0
  98. data/lib/mcollective/pluginpackager/templates/module/_manifest.pp.erb +9 -0
  99. data/lib/mcollective/pluginpackager/templates/redhat/rpm_spec.erb +63 -0
  100. data/lib/mcollective/registration/base.rb +91 -0
  101. data/lib/mcollective/rpc.rb +182 -0
  102. data/lib/mcollective/rpc/actionrunner.rb +158 -0
  103. data/lib/mcollective/rpc/agent.rb +374 -0
  104. data/lib/mcollective/rpc/audit.rb +38 -0
  105. data/lib/mcollective/rpc/client.rb +1066 -0
  106. data/lib/mcollective/rpc/helpers.rb +321 -0
  107. data/lib/mcollective/rpc/progress.rb +63 -0
  108. data/lib/mcollective/rpc/reply.rb +87 -0
  109. data/lib/mcollective/rpc/request.rb +86 -0
  110. data/lib/mcollective/rpc/result.rb +90 -0
  111. data/lib/mcollective/rpc/stats.rb +294 -0
  112. data/lib/mcollective/runnerstats.rb +90 -0
  113. data/lib/mcollective/security.rb +26 -0
  114. data/lib/mcollective/security/base.rb +244 -0
  115. data/lib/mcollective/shell.rb +126 -0
  116. data/lib/mcollective/ssl.rb +285 -0
  117. data/lib/mcollective/util.rb +579 -0
  118. data/lib/mcollective/validator.rb +85 -0
  119. data/lib/mcollective/validator/array_validator.ddl +7 -0
  120. data/lib/mcollective/validator/array_validator.rb +9 -0
  121. data/lib/mcollective/validator/ipv4address_validator.ddl +7 -0
  122. data/lib/mcollective/validator/ipv4address_validator.rb +16 -0
  123. data/lib/mcollective/validator/ipv6address_validator.ddl +7 -0
  124. data/lib/mcollective/validator/ipv6address_validator.rb +16 -0
  125. data/lib/mcollective/validator/length_validator.ddl +7 -0
  126. data/lib/mcollective/validator/length_validator.rb +11 -0
  127. data/lib/mcollective/validator/regex_validator.ddl +7 -0
  128. data/lib/mcollective/validator/regex_validator.rb +9 -0
  129. data/lib/mcollective/validator/shellsafe_validator.ddl +7 -0
  130. data/lib/mcollective/validator/shellsafe_validator.rb +13 -0
  131. data/lib/mcollective/validator/typecheck_validator.ddl +7 -0
  132. data/lib/mcollective/validator/typecheck_validator.rb +28 -0
  133. metadata +215 -0
@@ -0,0 +1,104 @@
1
+ module MCollective
2
+ class Application::Completion<MCollective::Application
3
+ description "Helper for shell completion systems"
4
+
5
+ exclude_argument_sections "common", "filter", "rpc"
6
+
7
+ option :list_agents,
8
+ :description => "List all known agents",
9
+ :arguments => "--list-agents",
10
+ :required => false,
11
+ :type => :boolean
12
+
13
+ option :list_actions,
14
+ :description => "List all actions for an agent",
15
+ :arguments => "--list-actions",
16
+ :required => false,
17
+ :type => :boolean
18
+
19
+ option :list_inputs,
20
+ :description => "List all inputs for an action",
21
+ :arguments => "--list-inputs",
22
+ :required => false,
23
+ :type => :boolean
24
+
25
+ option :list_applications,
26
+ :description => "List all known applications",
27
+ :arguments => "--list-applications",
28
+ :required => false,
29
+ :type => :boolean
30
+
31
+ option :agent,
32
+ :description => "The agent to operate on",
33
+ :arguments => "--agent AGENT",
34
+ :required => false
35
+
36
+ option :action,
37
+ :description => "The action to operate on",
38
+ :arguments => "--action ACTION",
39
+ :required => false
40
+
41
+ def list_agents
42
+ if options[:verbose]
43
+ PluginManager.find(:agent, "ddl").each do |agent|
44
+ begin
45
+ ddl = DDL.new(agent)
46
+ puts "%s:%s" % [ agent, ddl.meta[:description] ]
47
+ rescue
48
+ end
49
+ end
50
+ else
51
+ PluginManager.find(:agent, "ddl").each {|p| puts p}
52
+ end
53
+ end
54
+
55
+ def list_actions
56
+ abort "Please specify an agent to list actions for" unless configuration[:agent]
57
+
58
+ if options[:verbose]
59
+ ddl = DDL.new(configuration[:agent], :agent)
60
+
61
+ ddl.actions.sort.each do |action|
62
+ puts "%s:%s" % [action, ddl.action_interface(action)[:description]]
63
+ end
64
+ else
65
+ DDL.new(configuration[:agent], :agent).actions.sort.each {|a| puts a}
66
+ end
67
+ rescue
68
+ end
69
+
70
+ def list_inputs
71
+ abort "Please specify an action and agent to list inputs for" unless configuration[:agent] && configuration[:action]
72
+
73
+ if options[:verbose]
74
+ ddl = DDL.new(configuration[:agent], :agent)
75
+ action = ddl.action_interface(configuration[:action])
76
+ action[:input].keys.sort.each do |input|
77
+ puts "%s:%s" % [input, action[:input][input][:description]]
78
+ end
79
+ else
80
+ DDL.new(configuration[:agent], :agent).action_interface(configuration[:action])[:input].keys.sort.each {|i| puts i}
81
+ end
82
+ rescue
83
+ end
84
+
85
+ def list_applications
86
+ if options[:verbose]
87
+ Applications.list.each do |app|
88
+ puts "%s:%s" % [app, Applications[app].application_description]
89
+ end
90
+ else
91
+ Applications.list.each {|a| puts a}
92
+ end
93
+ end
94
+
95
+ def main
96
+ actions = configuration.keys.map{|k| k.to_s}.grep(/^list_/)
97
+
98
+ abort "Please choose either --list-[agents|actions|inputs|applications]" if actions.empty?
99
+ abort "Please choose only one of --list-[agents|actions|inputs|applications]" if actions.size > 1
100
+
101
+ send actions.first
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,87 @@
1
+ module MCollective
2
+ class Application::Describe_filter<Application
3
+ exclude_argument_sections "common", "rpc"
4
+
5
+ description "Display human readable interpretation of filters"
6
+
7
+ usage "mco describe_filter -S <filter> -F <filter> -C <filter>"
8
+
9
+ def describe_s_filter(stack)
10
+ indent = " "
11
+ old_indent = " "
12
+ puts "-S Query expands to the following instructions:"
13
+ puts
14
+ stack.each do |token|
15
+ if token.keys[0] == "statement"
16
+ if token.values[0] =~ /(<=|>=|=|=~|=)/
17
+ op = $1
18
+ k,v = token.values[0].split(op)
19
+ puts indent + get_fact_string(k, v, op)
20
+ else
21
+ puts indent + get_class_string(token.values[0])
22
+ end
23
+ elsif token.keys[0] == "fstatement"
24
+ 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
29
+ result_string += ". "
30
+ result_string += "Check if the query's '#{v["value"]}' value #{v["operator"]} '#{v["r_compare"]}' "
31
+ puts result_string
32
+ elsif token.keys[0] == "("
33
+ puts indent + "("
34
+ old_indent = indent
35
+ indent *= 2
36
+ elsif token.keys[0] == ")"
37
+ indent = old_indent
38
+ puts indent + ")"
39
+ else
40
+ puts indent + token.keys[0].upcase
41
+ end
42
+ end
43
+ end
44
+
45
+ def describe_f_filter(facts)
46
+ puts "-F filter expands to the following fact comparisons:"
47
+ puts
48
+ facts.each do |f|
49
+ puts " " + get_fact_string(f[:fact], f[:value], f[:operator])
50
+ end
51
+ end
52
+
53
+ def describe_c_filter(classes)
54
+ puts "-C filter expands to the following class checks:"
55
+ puts
56
+ classes.each do |c|
57
+ puts " " + get_class_string(c)
58
+ end
59
+ end
60
+
61
+ def main
62
+ if !(@options[:filter]["fact"].empty?)
63
+ describe_f_filter(@options[:filter]["fact"])
64
+ puts
65
+ end
66
+
67
+ if !(@options[:filter]["cf_class"].empty?)
68
+ describe_c_filter(@options[:filter]["cf_class"])
69
+ puts
70
+ end
71
+
72
+ if !(@options[:filter]["compound"].empty?)
73
+ describe_s_filter(@options[:filter]["compound"][0])
74
+ puts
75
+ end
76
+ end
77
+
78
+ private
79
+ def get_fact_string(fact, value, op)
80
+ "Check if fact '#{fact}' #{op} '#{value}'"
81
+ end
82
+
83
+ def get_class_string(classname)
84
+ "Check if class '#{classname}' is present on the host"
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,62 @@
1
+ class MCollective::Application::Facts<MCollective::Application
2
+ description "Reports on usage for a specific fact"
3
+
4
+ def post_option_parser(configuration)
5
+ configuration[:fact] = ARGV.shift if ARGV.size > 0
6
+ end
7
+
8
+ def validate_configuration(configuration)
9
+ raise "Please specify a fact to report for" unless configuration.include?(:fact)
10
+ end
11
+
12
+ def show_single_fact_report(fact, facts, verbose=false)
13
+ puts("Report for fact: #{fact}\n\n")
14
+
15
+ field_size = MCollective::Util.field_size(facts.keys)
16
+ facts.keys.sort.each do |k|
17
+ printf(" %-#{field_size}s found %d times\n", k, facts[k].size)
18
+
19
+ if verbose
20
+ puts
21
+
22
+ facts[k].sort.each do |f|
23
+ puts(" #{f}")
24
+ end
25
+
26
+ puts
27
+ end
28
+ end
29
+ end
30
+
31
+ def main
32
+ rpcutil = rpcclient("rpcutil")
33
+ rpcutil.progress = false
34
+
35
+ facts = {}
36
+
37
+ rpcutil.get_fact(:fact => configuration[:fact]) do |resp|
38
+ begin
39
+ value = resp[:body][:data][:value]
40
+ if value
41
+ if facts.include?(value)
42
+ facts[value] << resp[:senderid]
43
+ else
44
+ facts[value] = [ resp[:senderid] ]
45
+ end
46
+ end
47
+ rescue Exception => e
48
+ STDERR.puts "Could not parse facts for #{resp[:senderid]}: #{e.class}: #{e}"
49
+ end
50
+ end
51
+
52
+ if facts.empty?
53
+ puts "No values found for fact #{configuration[:fact]}\n"
54
+ else
55
+ show_single_fact_report(configuration[:fact], facts, options[:verbose])
56
+ end
57
+
58
+ printrpcstats
59
+
60
+ halt rpcutil.stats
61
+ end
62
+ end
@@ -0,0 +1,23 @@
1
+ class MCollective::Application::Find<MCollective::Application
2
+ description "Find hosts using the discovery system matching filter criteria"
3
+
4
+ def main
5
+ mc = rpcclient("rpcutil")
6
+
7
+ starttime = Time.now
8
+
9
+ mc.detect_and_set_stdin_discovery
10
+
11
+ nodes = mc.discover
12
+
13
+ discoverytime = Time.now - starttime
14
+
15
+ STDERR.puts if options[:verbose]
16
+
17
+ nodes.each {|c| puts c}
18
+
19
+ STDERR.puts "\nDiscovered %s nodes in %.2f seconds using the %s discovery plugin" % [nodes.size, discoverytime, mc.client.discoverer.discovery_method] if options[:verbose]
20
+
21
+ nodes.size > 0 ? exit(0) : exit(1)
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ module MCollective
2
+ class Application::Help<Application
3
+ description "Application list and help"
4
+ usage "rpc help [application name]"
5
+
6
+ def post_option_parser(configuration)
7
+ configuration[:application] = ARGV.shift if ARGV.size > 0
8
+ end
9
+
10
+ def main
11
+ if configuration.include?(:application)
12
+ puts Applications[configuration[:application]].help
13
+ else
14
+ puts "The Marionette Collective version #{MCollective.version}"
15
+ puts
16
+
17
+ Applications.list.sort.each do |app|
18
+ begin
19
+ puts " %-15s %s" % [app, Applications[app].application_description]
20
+ rescue
21
+ end
22
+ end
23
+
24
+ puts
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,344 @@
1
+ class MCollective::Application::Inventory<MCollective::Application
2
+ description "General reporting tool for nodes, collectives and subcollectives"
3
+
4
+ option :script,
5
+ :description => "Script to run",
6
+ :arguments => ["--script SCRIPT"]
7
+
8
+ option :collectives,
9
+ :description => "List all known collectives",
10
+ :arguments => ["--list-collectives", "--lc"],
11
+ :default => false,
12
+ :type => :bool
13
+
14
+ option :collectivemap,
15
+ :description => "Create a DOT graph of all collectives",
16
+ :arguments => ["--collective-graph MAP", "--cg MAP", "--map MAP"]
17
+
18
+ def post_option_parser(configuration)
19
+ configuration[:node] = ARGV.shift if ARGV.size > 0
20
+ end
21
+
22
+ def validate_configuration(configuration)
23
+ unless configuration[:node] || configuration[:script] || configuration[:collectives] || configuration[:collectivemap]
24
+ raise "Need to specify either a node name, script to run or other options"
25
+ end
26
+ end
27
+
28
+ # Get all the known collectives and nodes that belong to them
29
+ def get_collectives
30
+ util = rpcclient("rpcutil")
31
+ util.progress = false
32
+
33
+ collectives = {}
34
+ nodes = 0
35
+ total = 0
36
+
37
+ util.collective_info do |r, cinfo|
38
+ begin
39
+ if cinfo[:data] && cinfo[:data][:collectives]
40
+ cinfo[:data][:collectives].each do |collective|
41
+ collectives[collective] ||= []
42
+ collectives[collective] << cinfo[:sender]
43
+ end
44
+
45
+ nodes += 1
46
+ total += 1
47
+ end
48
+ end
49
+ end
50
+
51
+ {:collectives => collectives, :nodes => nodes, :total_nodes => total}
52
+ end
53
+
54
+ # Writes a crude DOT graph to a file
55
+ def collectives_map(file)
56
+ File.open(file, "w") do |graph|
57
+ puts "Retrieving collective info...."
58
+ collectives = get_collectives
59
+
60
+ graph.puts 'graph {'
61
+
62
+ collectives[:collectives].keys.sort.each do |collective|
63
+ graph.puts ' subgraph "%s" {' % [ collective ]
64
+
65
+ collectives[:collectives][collective].each do |member|
66
+ graph.puts ' "%s" -- "%s"' % [ member, collective ]
67
+ end
68
+
69
+ graph.puts ' }'
70
+ end
71
+
72
+ graph.puts '}'
73
+
74
+ puts "Graph of #{collectives[:total_nodes]} nodes has been written to #{file}"
75
+ end
76
+ end
77
+
78
+ # Prints a report of all known sub collectives
79
+ def collectives_report
80
+ collectives = get_collectives
81
+
82
+ puts " %-30s %s" % [ "Collective", "Nodes" ]
83
+ puts " %-30s %s" % [ "==========", "=====" ]
84
+
85
+ collectives[:collectives].sort_by {|key,count| count.size}.each do |collective|
86
+ puts " %-30s %d" % [ collective[0], collective[1].size ]
87
+ end
88
+
89
+ puts
90
+ puts " %30s %d" % [ "Total nodes:", collectives[:nodes] ]
91
+ puts
92
+ end
93
+
94
+ def node_inventory
95
+ node = configuration[:node]
96
+
97
+ util = rpcclient("rpcutil")
98
+ util.identity_filter node
99
+ util.progress = false
100
+
101
+ nodestats = util.custom_request("daemon_stats", {}, node, {"identity" => node}).first
102
+
103
+ unless nodestats
104
+ STDERR.puts "Did not receive any results from node #{node}"
105
+ exit 1
106
+ end
107
+
108
+ unless nodestats[:statuscode] == 0
109
+ STDERR.puts "Failed to retrieve daemon_stats from #{node}: #{nodestats[:statusmsg]}"
110
+ else
111
+ util.custom_request("inventory", {}, node, {"identity" => node}).each do |resp|
112
+ unless resp[:statuscode] == 0
113
+ STDERR.puts "Failed to retrieve inventory for #{node}: #{resp[:statusmsg]}"
114
+ next
115
+ end
116
+
117
+ data = resp[:data]
118
+
119
+ begin
120
+ puts "Inventory for #{resp[:sender]}:"
121
+ puts
122
+
123
+ nodestats = nodestats[:data]
124
+
125
+ puts " Server Statistics:"
126
+ puts " Version: #{nodestats[:version]}"
127
+ puts " Start Time: #{Time.at(nodestats[:starttime])}"
128
+ puts " Config File: #{nodestats[:configfile]}"
129
+ puts " Collectives: #{data[:collectives].join(', ')}" if data.include?(:collectives)
130
+ puts " Main Collective: #{data[:main_collective]}" if data.include?(:main_collective)
131
+ puts " Process ID: #{nodestats[:pid]}"
132
+ puts " Total Messages: #{nodestats[:total]}"
133
+ puts " Messages Passed Filters: #{nodestats[:passed]}"
134
+ puts " Messages Filtered: #{nodestats[:filtered]}"
135
+ puts " Expired Messages: #{nodestats[:ttlexpired]}"
136
+ puts " Replies Sent: #{nodestats[:replies]}"
137
+ puts " Total Processor Time: #{nodestats[:times][:utime]} seconds"
138
+ puts " System Time: #{nodestats[:times][:stime]} seconds"
139
+
140
+ puts
141
+
142
+ puts " Agents:"
143
+ if data[:agents].size > 0
144
+ data[:agents].sort.in_groups_of(3, "") do |agents|
145
+ puts " %-15s %-15s %-15s" % agents
146
+ end
147
+ else
148
+ puts " No agents installed"
149
+ end
150
+
151
+ puts
152
+
153
+ puts " Data Plugins:"
154
+ if data[:data_plugins].size > 0
155
+ data[:data_plugins].sort.in_groups_of(3, "") do |plugins|
156
+ puts " %-15s %-15s %-15s" % plugins.map{|p| p.gsub("_data", "")}
157
+ end
158
+ else
159
+ puts " No data plugins installed"
160
+ end
161
+
162
+ puts
163
+
164
+ puts " Configuration Management Classes:"
165
+ if data[:classes].size > 0
166
+ field_size = MCollective::Util.field_size(data[:classes], 30)
167
+ fields_num = MCollective::Util.field_number(field_size)
168
+ format = " " + (" %-#{field_size}s" * fields_num)
169
+
170
+ data[:classes].sort.in_groups_of(fields_num, "") do |klasses|
171
+ puts format % klasses
172
+ end
173
+ else
174
+ puts " No classes applied"
175
+ end
176
+
177
+ puts
178
+
179
+ puts " Facts:"
180
+ if data[:facts].size > 0
181
+ data[:facts].sort_by{|f| f[0]}.each do |f|
182
+ puts " #{f[0]} => #{f[1]}"
183
+ end
184
+ else
185
+ puts " No facts known"
186
+ end
187
+
188
+ break
189
+ rescue Exception => e
190
+ STDERR.puts "Failed to display node inventory: #{e.class}: #{e}"
191
+ end
192
+ end
193
+ end
194
+
195
+ halt util.stats
196
+ end
197
+
198
+ # Helpers to create a simple DSL for scriptlets
199
+ def format(fmt)
200
+ @fmt = fmt
201
+ end
202
+
203
+ def fields(&blk)
204
+ @flds = blk
205
+ end
206
+
207
+ def identity
208
+ @node[:identity]
209
+ end
210
+
211
+ def facts
212
+ @node[:facts]
213
+ end
214
+
215
+ def classes
216
+ @node[:classes]
217
+ end
218
+
219
+ def agents
220
+ @node[:agents]
221
+ end
222
+
223
+ def page_length(len)
224
+ @page_length = len
225
+ end
226
+
227
+ def page_heading(fmt)
228
+ @page_heading = fmt
229
+ end
230
+
231
+ def page_body(fmt)
232
+ @page_body = fmt
233
+ end
234
+
235
+ # Expects a simple printf style format and apply it to
236
+ # each node:
237
+ #
238
+ # inventory do
239
+ # format "%s:\t\t%s\t\t%s"
240
+ #
241
+ # fields { [ identity, facts["serialnumber"], facts["productname"] ] }
242
+ # end
243
+ def inventory(&blk)
244
+ raise "Need to give a block to inventory" unless block_given?
245
+
246
+ blk.call if block_given?
247
+
248
+ raise "Need to define a format" if @fmt.nil?
249
+ raise "Need to define inventory fields" if @flds.nil?
250
+
251
+ util = rpcclient("rpcutil")
252
+ util.progress = false
253
+
254
+ util.inventory do |t, resp|
255
+ @node = {:identity => resp[:sender],
256
+ :facts => resp[:data][:facts],
257
+ :classes => resp[:data][:classes],
258
+ :agents => resp[:data][:agents]}
259
+
260
+ puts @fmt % @flds.call
261
+ end
262
+ end
263
+
264
+ # Use the ruby formatr gem to build reports using Perls formats
265
+ #
266
+ # It is kind of ugly but brings a lot of flexibility in report
267
+ # writing without building an entire reporting language.
268
+ #
269
+ # You need to have formatr installed to enable reports like:
270
+ #
271
+ # formatted_inventory do
272
+ # page_length 20
273
+ #
274
+ # page_heading <<TOP
275
+ #
276
+ # Node Report @<<<<<<<<<<<<<<<<<<<<<<<<<
277
+ # time
278
+ #
279
+ # Hostname: Customer: Distribution:
280
+ # -------------------------------------------------------------------------
281
+ # TOP
282
+ #
283
+ # page_body <<BODY
284
+ #
285
+ # @<<<<<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
286
+ # identity, facts["customer"], facts["lsbdistdescription"]
287
+ # @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
288
+ # facts["processor0"]
289
+ # BODY
290
+ # end
291
+ def formatted_inventory(&blk)
292
+ require 'formatr'
293
+
294
+ raise "Need to give a block to formatted_inventory" unless block_given?
295
+
296
+ blk.call if block_given?
297
+
298
+ raise "Need to define page body format" if @page_body.nil?
299
+
300
+ body_fmt = FormatR::Format.new(@page_heading, @page_body)
301
+ body_fmt.setPageLength(@page_length)
302
+ time = Time.now
303
+
304
+ util = rpcclient("rpcutil")
305
+ util.progress = false
306
+
307
+ util.inventory do |t, resp|
308
+ @node = {:identity => resp[:sender],
309
+ :facts => resp[:data][:facts],
310
+ :classes => resp[:data][:classes],
311
+ :agents => resp[:data][:agents]}
312
+
313
+ body_fmt.printFormat(binding)
314
+ end
315
+ rescue Exception => e
316
+ STDERR.puts "Could not create report: #{e.class}: #{e}"
317
+ exit 1
318
+ end
319
+
320
+ @fmt = nil
321
+ @flds = nil
322
+ @page_heading = nil
323
+ @page_body = nil
324
+ @page_length = 40
325
+
326
+ def main
327
+ if configuration[:script]
328
+ if File.exist?(configuration[:script])
329
+ eval(File.read(configuration[:script]))
330
+ else
331
+ raise "Could not find script to run: #{configuration[:script]}"
332
+ end
333
+
334
+ elsif configuration[:collectivemap]
335
+ collectives_map(configuration[:collectivemap])
336
+
337
+ elsif configuration[:collectives]
338
+ collectives_report
339
+
340
+ else
341
+ node_inventory
342
+ end
343
+ end
344
+ end