Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
124 lines (104 sloc) 4.76 KB
require 'ipaddress'
###############################################################################################
# This hiera backend will lookup subnet specific hiera data. #
# By passing a :datadir: to the backend in hiera.yaml, you can then #
# populate a dir with yaml files of the form '0_0_0_0-0.yaml', '192_168_0_0-24.yaml' etc. #
# #
# The backend will recurse, using '0_0_0_0-0.yaml' as an equivilent of common.yaml in #
# typical hiera backends. Using more specific information where given in the normal hiera way #
###############################################################################################
class Hiera
module Backend
class Subnet_backend
def initialize(cache=nil)
Hiera.debug("Hiera Subnet backend starting")
@cache = cache || Filecache.new
end
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Lookup hook, called from puppet code
def lookup(key, scope, order_override, resolution_type)
answer = nil
Hiera.debug("Looking up #{key} in Subnet backend")
datadir = Backend.datadir(:subnet, scope)
# Convert our '192.168.56.0' puppet fact into a cidr form
ip_of_caller = scope['ipaddress'] + '/32'
yamls = get_relevant_yaml_files(datadir, ip_of_caller)
yamls = order_yamls(yamls)
# Perform actual lookup in each file
yamls.each do |source|
Hiera.debug("Searching yaml file #{source}")
source = source.sub('.yaml', '')
yamlfile = Backend.datafile(:subnet, scope, source, 'yaml') || next
next unless File.exist?(yamlfile)
data = @cache.read(yamlfile, Hash, {}) do |data|
YAML.load(data)
end
next if data.empty?
next unless data.include?(key)
new_answer = data[key] # This might want to be 'new_answer = Backend.parse_answer(data[key], scope)' for further interpolation
Hiera.debug("Entry of #{new_answer} from #{source} yaml file matches lookup, adding to / as returned value(s)")
case resolution_type
when :array
raise Exception, "Hiera type mismatch: expected Array and got #{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of? String
answer ||= []
answer << new_answer
when :hash
raise Exception, "Hiera type mismatch: expected Hash and got #{new_answer.class}" unless new_answer.kind_of? Hash
answer ||= {}
answer = Backend.merge_answer(new_answer,answer)
else
answer = new_answer
break
end
end
return answer
end
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Order the list returned from get_relevant_yaml_files()
# by subnet size (smallest subnet to largest /32 -> /0)
def order_yamls(valid_yamls)
if valid_yamls.size < 2
return valid_yamls
end
(1..valid_yamls.size-1).each do |i|
j = i
while j > 0 and str_as_subnet(valid_yamls[j-1]).split('/')[1].to_i >
str_as_subnet(valid_yamls[j]).split('/')[1].to_i do
temp = valid_yamls[j]
valid_yamls[j] = valid_yamls[j-1]
valid_yamls[j-1] = temp
j = j - 1
end
end
# Reverse the order so smallest subnet (highest cidr number) is actually first
valid_yamls = valid_yamls.reverse
return valid_yamls
end
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# Transform a string like '192_168_3_0-24.yaml' into '192.168.3.0/24'
def str_as_subnet (filename)
filename = filename.sub('.yaml', '')
filename.gsub!('_', '.')
filename.sub!('-', '/')
return filename
end
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# Look for yaml files which are exactly for, or a superset of
# the subnet of the calling puppet node's ip. Return an unsorted list
def get_relevant_yaml_files(datadir, ip_of_caller)
valid_yamls = []
Dir.glob("#{datadir}*.yaml") do |filepath|
filename = File.basename(filepath)
filename_as_subnet = str_as_subnet(filename)
file_subnet = IPAddress(filename_as_subnet)
given_subnet = IPAddress(ip_of_caller)
if file_subnet.include?(given_subnet)
valid_yamls.push(filename)
end
end
return valid_yamls
end
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
end
end
end