class MCollective::Application::Inventory

Public Instance Methods

agents() click to toggle source
    # File lib/mcollective/application/inventory.rb
219 def agents
220   @node[:agents]
221 end
classes() click to toggle source
    # File lib/mcollective/application/inventory.rb
215 def classes
216   @node[:classes]
217 end
collectives_map(file) click to toggle source

Writes a crude DOT graph to a file

   # File lib/mcollective/application/inventory.rb
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
collectives_report() click to toggle source

Prints a report of all known sub collectives

   # File lib/mcollective/application/inventory.rb
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
facts() click to toggle source
    # File lib/mcollective/application/inventory.rb
211 def facts
212   @node[:facts]
213 end
fields(&blk) click to toggle source
    # File lib/mcollective/application/inventory.rb
203 def fields(&blk)
204   @flds = blk
205 end
format(fmt) click to toggle source

Helpers to create a simple DSL for scriptlets

    # File lib/mcollective/application/inventory.rb
199 def format(fmt)
200   @fmt = fmt
201 end
formatted_inventory(&blk) click to toggle source

Use the ruby formatr gem to build reports using Perls formats

It is kind of ugly but brings a lot of flexibility in report writing without building an entire reporting language.

You need to have formatr installed to enable reports like:

formatted_inventory do
    page_length 20

    page_heading <<TOP

            Node Report @<<<<<<<<<<<<<<<<<<<<<<<<<
                        time

Hostname:         Customer:     Distribution:
-------------------------------------------------------------------------
TOP

    page_body <<BODY

@<<<<<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
identity,    facts["customer"], facts["lsbdistdescription"]
                                @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                facts["processor0"]
BODY
end
    # File lib/mcollective/application/inventory.rb
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
get_collectives() click to toggle source

Get all the known collectives and nodes that belong to them

   # File lib/mcollective/application/inventory.rb
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
identity() click to toggle source
    # File lib/mcollective/application/inventory.rb
207 def identity
208   @node[:identity]
209 end
inventory(&blk) click to toggle source

Expects a simple printf style format and apply it to each node:

inventory do
    format "%s:\t\t%s\t\t%s"

    fields { [ identity, facts["serialnumber"], facts["productname"] ] }
end
    # File lib/mcollective/application/inventory.rb
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
main() click to toggle source
    # File lib/mcollective/application/inventory.rb
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
node_inventory() click to toggle source
    # File lib/mcollective/application/inventory.rb
 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
page_body(fmt) click to toggle source
    # File lib/mcollective/application/inventory.rb
231 def page_body(fmt)
232   @page_body = fmt
233 end
page_heading(fmt) click to toggle source
    # File lib/mcollective/application/inventory.rb
227 def page_heading(fmt)
228   @page_heading = fmt
229 end
page_length(len) click to toggle source
    # File lib/mcollective/application/inventory.rb
223 def page_length(len)
224   @page_length = len
225 end
post_option_parser(configuration) click to toggle source
   # File lib/mcollective/application/inventory.rb
18 def post_option_parser(configuration)
19   configuration[:node] = ARGV.shift if ARGV.size > 0
20 end
validate_configuration(configuration) click to toggle source
   # File lib/mcollective/application/inventory.rb
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