824baf68c07a7b7697edf0246117c35ad6c641fb
[packages/precise/mcollective.git] / lib / mcollective / discovery.rb
1 module MCollective
2   class Discovery
3     def initialize(client)
4       @known_methods = find_known_methods
5       @default_method = Config.instance.default_discovery_method
6       @client = client
7     end
8
9     def find_known_methods
10       PluginManager.find("discovery")
11     end
12
13     def has_method?(method)
14       @known_methods.include?(method)
15     end
16
17     def force_direct_mode?
18       discovery_method != "mc"
19     end
20
21     def discovery_method
22       method = "mc"
23
24       if @client.options[:discovery_method]
25         method = @client.options[:discovery_method]
26       else
27         method = @default_method
28       end
29
30       raise "Unknown discovery method %s" % method unless has_method?(method)
31
32       unless method == "mc"
33         raise "Custom discovery methods require direct addressing mode" unless Config.instance.direct_addressing
34       end
35
36       return method
37     end
38
39     def discovery_class
40       method = discovery_method.capitalize
41
42       PluginManager.loadclass("MCollective::Discovery::#{method}") unless self.class.const_defined?(method)
43
44       self.class.const_get(method)
45     end
46
47     def ddl
48       @ddl ||= DDL.new(discovery_method, :discovery)
49
50       # if the discovery method got changed we might have an old DDL cached
51       # this will detect that and reread the correct DDL from disk
52       unless @ddl.meta[:name] == discovery_method
53         @ddl = DDL.new(discovery_method, :discovery)
54       end
55
56       return @ddl
57     end
58
59     # Agent filters are always present no matter what, so we cant raise an error if the capabilities
60     # suggest the discovery method cant do agents we just have to rely on the discovery plugin to not
61     # do stupid things in the presense of a agent filter
62     def check_capabilities(filter)
63       capabilities = ddl.discovery_interface[:capabilities]
64
65       unless capabilities.include?(:classes)
66         raise "Cannot use class filters while using the '%s' discovery method" % discovery_method unless filter["cf_class"].empty?
67       end
68
69       unless capabilities.include?(:facts)
70         raise "Cannot use fact filters while using the '%s' discovery method" % discovery_method unless filter["fact"].empty?
71       end
72
73       unless capabilities.include?(:identity)
74         raise "Cannot use identity filters while using the '%s' discovery method" % discovery_method unless filter["identity"].empty?
75       end
76
77       unless capabilities.include?(:compound)
78         raise "Cannot use compound filters while using the '%s' discovery method" % discovery_method unless filter["compound"].empty?
79       end
80     end
81
82     # checks if compound filters are used and then forces the 'mc' discovery plugin
83     def force_discovery_method_by_filter(filter)
84       unless discovery_method == "mc"
85         unless filter["compound"].empty?
86           Log.info "Switching to mc discovery method because compound filters are used"
87           @client.options[:discovery_method] = "mc"
88
89           return true
90         end
91       end
92
93       return false
94     end
95
96     # if a compound filter is specified and it has any function
97     # then we read the DDL for each of those plugins and sum up
98     # the timeout declared in the DDL
99     def timeout_for_compound_filter(compound_filter)
100       return 0 if compound_filter.nil? || compound_filter.empty?
101
102       timeout = 0
103
104       compound_filter.each do |filter|
105         filter.each do |statement|
106           if statement["fstatement"]
107             pluginname = Data.pluginname(statement["fstatement"]["name"])
108             ddl = DDL.new(pluginname, :data)
109             timeout += ddl.meta[:timeout]
110           end
111         end
112       end
113
114       timeout
115     end
116
117     def discovery_timeout(timeout, filter)
118       timeout = ddl.meta[:timeout] unless timeout
119
120       unless (filter["compound"] && filter["compound"].empty?)
121         timeout + timeout_for_compound_filter(filter["compound"])
122       else
123         timeout
124       end
125     end
126
127     def discover(filter, timeout, limit)
128       raise "Limit has to be an integer" unless limit.is_a?(Fixnum)
129
130       force_discovery_method_by_filter(filter)
131
132       check_capabilities(filter)
133
134       discovered = discovery_class.discover(filter, discovery_timeout(timeout, filter), limit, @client)
135
136       if limit > 0
137         return discovered[0,limit]
138       else
139         return discovered
140       end
141     end
142   end
143 end