Permalink
Browse files

add a POC puppetdb discovery plugin

  • Loading branch information...
1 parent 9393ba7 commit 76c4cca6d500c7336f2f8cc89e589e48161284f2 @ripienaar committed Jun 13, 2012
Showing with 117 additions and 0 deletions.
  1. +11 −0 discovery/puppetdb/discovery/puppetdb.ddl
  2. +106 −0 discovery/puppetdb/discovery/puppetdb.rb
@@ -0,0 +1,11 @@
+metadata :name => "puppetdb",
+ :description => "PuppetDB based discovery",
+ :author => "R.I.Pienaar <rip@devco.net>",
+ :license => "ASL 2.0",
+ :version => "0.1",
+ :url => "http://marionette-collective.org/",
+ :timeout => 0
+
+discovery do
+ capabilities [:identity, :classes, :facts]
+end
@@ -0,0 +1,106 @@
+require 'net/http'
+require 'net/https'
+
+module MCollective
+ class Discovery
+ class Puppetdb
+ def self.discover(filter, timeout, limit=0, client=nil)
+ http = Net::HTTP.new('puppetdb.devco.net', 443)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+
+ found = []
+
+ filter.keys.each do |key|
+ case key
+ when "identity"
+ identity_search(filter["identity"], http, found)
+
+ when "cf_class"
+ class_search(filter["cf_class"], http, found)
+
+ when "fact"
+ fact_search(filter["fact"], http, found)
+ end
+ end
+
+ # filters are combined so we get the intersection of values across
+ # all matches found using fact, agent and identity filters
+ found = found.inject(found[0]){|x, y| x & y}
+
+ found.flatten.map do |node|
+ if node =~ /^(.+?)\.\w+\.net/
+ $1
+ else
+ node
+ end
+ end
+ end
+
+ def self.fact_search(filter, http, found)
+ return if filter.empty?
+
+ selected_hosts = []
+
+ filter.each do |fact|
+ raise "Can only do == matches using the PuppetDB discovery" unless fact[:operator] == "=="
+
+ query = ["and", ["=", ["fact", fact[:fact]], fact[:value]]]
+
+ resp, data = http.get("/nodes?query=%s" % URI.escape(query.to_json), {"accept" => "application/json"})
+ raise "Failed to retrieve nodes from PuppetDB: %s: %s" % [resp.code, resp.message] unless resp.code == "200"
+
+ found << JSON.parse(data)
+ end
+ end
+
+ def self.class_search(filter, http, found)
+ return if filter.empty?
+
+ selected_hosts = []
+
+ filter.each do |klass|
+ klass = klass.split("::").map{|i| i.capitalize}.join("::")
+ raise "Can not do regular expression matches for classes using the PuppetDB discovery method" if regexy_string(klass).is_a?(Regexp)
+
+ query = ["and", ["=", "type", "Class"], ["=", "title", klass]]
+
+ resp, data = http.get("/resources?query=%s" % URI.escape(query.to_json), {"accept" => "application/json"})
+ raise "Failed to retrieve nodes from PuppetDB: %s: %s" % [resp.code, resp.message] unless resp.code == "200"
+
+ found << JSON.parse(data).map{|found| found["certname"]}
+ end
+ end
+
+ def self.identity_search(filter, http, found)
+ return if filter.empty?
+
+ resp, data = http.get("/nodes", {"accept" => "application/json"})
+ raise "Failed to retrieve nodes from PuppetDB: %s: %s" % [resp.code, resp.message] unless resp.code == "200"
+
+ all_hosts = JSON.parse(data)
+ selected_hosts = []
+
+ filter.each do |identity|
+ identity = regexy_string(identity)
+
+ if identity.is_a?(Regexp)
+ selected_hosts << all_hosts.grep(identity)
+ else
+ selected_hosts << identity if all_hosts.include?(identity)
+ end
+ end
+
+ found << selected_hosts
+ end
+
+ def self.regexy_string(string)
+ if string.match("^/")
+ Regexp.new(string.gsub("\/", ""))
+ else
+ string
+ end
+ end
+ end
+ end
+end

0 comments on commit 76c4cca

Please sign in to comment.