Updated mcollective.init according to OSCI-658
[packages/precise/mcollective.git] / lib / mcollective / discovery.rb
diff --git a/lib/mcollective/discovery.rb b/lib/mcollective/discovery.rb
new file mode 100644 (file)
index 0000000..824baf6
--- /dev/null
@@ -0,0 +1,143 @@
+module MCollective
+  class Discovery
+    def initialize(client)
+      @known_methods = find_known_methods
+      @default_method = Config.instance.default_discovery_method
+      @client = client
+    end
+
+    def find_known_methods
+      PluginManager.find("discovery")
+    end
+
+    def has_method?(method)
+      @known_methods.include?(method)
+    end
+
+    def force_direct_mode?
+      discovery_method != "mc"
+    end
+
+    def discovery_method
+      method = "mc"
+
+      if @client.options[:discovery_method]
+        method = @client.options[:discovery_method]
+      else
+        method = @default_method
+      end
+
+      raise "Unknown discovery method %s" % method unless has_method?(method)
+
+      unless method == "mc"
+        raise "Custom discovery methods require direct addressing mode" unless Config.instance.direct_addressing
+      end
+
+      return method
+    end
+
+    def discovery_class
+      method = discovery_method.capitalize
+
+      PluginManager.loadclass("MCollective::Discovery::#{method}") unless self.class.const_defined?(method)
+
+      self.class.const_get(method)
+    end
+
+    def ddl
+      @ddl ||= DDL.new(discovery_method, :discovery)
+
+      # if the discovery method got changed we might have an old DDL cached
+      # this will detect that and reread the correct DDL from disk
+      unless @ddl.meta[:name] == discovery_method
+        @ddl = DDL.new(discovery_method, :discovery)
+      end
+
+      return @ddl
+    end
+
+    # Agent filters are always present no matter what, so we cant raise an error if the capabilities
+    # suggest the discovery method cant do agents we just have to rely on the discovery plugin to not
+    # do stupid things in the presense of a agent filter
+    def check_capabilities(filter)
+      capabilities = ddl.discovery_interface[:capabilities]
+
+      unless capabilities.include?(:classes)
+        raise "Cannot use class filters while using the '%s' discovery method" % discovery_method unless filter["cf_class"].empty?
+      end
+
+      unless capabilities.include?(:facts)
+        raise "Cannot use fact filters while using the '%s' discovery method" % discovery_method unless filter["fact"].empty?
+      end
+
+      unless capabilities.include?(:identity)
+        raise "Cannot use identity filters while using the '%s' discovery method" % discovery_method unless filter["identity"].empty?
+      end
+
+      unless capabilities.include?(:compound)
+        raise "Cannot use compound filters while using the '%s' discovery method" % discovery_method unless filter["compound"].empty?
+      end
+    end
+
+    # checks if compound filters are used and then forces the 'mc' discovery plugin
+    def force_discovery_method_by_filter(filter)
+      unless discovery_method == "mc"
+        unless filter["compound"].empty?
+          Log.info "Switching to mc discovery method because compound filters are used"
+          @client.options[:discovery_method] = "mc"
+
+          return true
+        end
+      end
+
+      return false
+    end
+
+    # if a compound filter is specified and it has any function
+    # then we read the DDL for each of those plugins and sum up
+    # the timeout declared in the DDL
+    def timeout_for_compound_filter(compound_filter)
+      return 0 if compound_filter.nil? || compound_filter.empty?
+
+      timeout = 0
+
+      compound_filter.each do |filter|
+        filter.each do |statement|
+          if statement["fstatement"]
+            pluginname = Data.pluginname(statement["fstatement"]["name"])
+            ddl = DDL.new(pluginname, :data)
+            timeout += ddl.meta[:timeout]
+          end
+        end
+      end
+
+      timeout
+    end
+
+    def discovery_timeout(timeout, filter)
+      timeout = ddl.meta[:timeout] unless timeout
+
+      unless (filter["compound"] && filter["compound"].empty?)
+        timeout + timeout_for_compound_filter(filter["compound"])
+      else
+        timeout
+      end
+    end
+
+    def discover(filter, timeout, limit)
+      raise "Limit has to be an integer" unless limit.is_a?(Fixnum)
+
+      force_discovery_method_by_filter(filter)
+
+      check_capabilities(filter)
+
+      discovered = discovery_class.discover(filter, discovery_timeout(timeout, filter), limit, @client)
+
+      if limit > 0
+        return discovered[0,limit]
+      else
+        return discovered
+      end
+    end
+  end
+end