From 2ac89832b7e065df4490d81b3080b2b570a172ad Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 9 Jul 2018 17:15:39 -0700 Subject: Initial host import from puppet#ee7cf4d28077be7d1bdbbe934ea012d41d33deff --- lib/puppet/provider/host/parsed.rb | 46 ++++++++++++++++++ lib/puppet/type/host.rb | 95 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 lib/puppet/provider/host/parsed.rb create mode 100644 lib/puppet/type/host.rb (limited to 'lib/puppet') diff --git a/lib/puppet/provider/host/parsed.rb b/lib/puppet/provider/host/parsed.rb new file mode 100644 index 0000000..d3d0039 --- /dev/null +++ b/lib/puppet/provider/host/parsed.rb @@ -0,0 +1,46 @@ +require 'puppet/provider/parsedfile' + +hosts = nil +case Facter.value(:osfamily) +when "Solaris"; hosts = "/etc/inet/hosts" +when "windows" + require 'win32/resolv' + hosts = Win32::Resolv.get_hosts_path +else + hosts = "/etc/hosts" +end + + +Puppet::Type.type(:host).provide(:parsed,:parent => Puppet::Provider::ParsedFile, + :default_target => hosts,:filetype => :flat) do + confine :exists => hosts + + text_line :comment, :match => /^#/ + text_line :blank, :match => /^\s*$/ + hosts_pattern = '^([0-9a-f:]\S+)\s+([^#\s+]\S+)\s*(.*?)?(?:\s*#\s*(.*))?$' + record_line :parsed, :fields => %w{ip name host_aliases comment}, + :optional => %w{host_aliases comment}, + :match => /#{hosts_pattern}/, + :post_parse => proc { |hash| + # An absent comment should match "comment => ''" + hash[:comment] = '' if hash[:comment].nil? or hash[:comment] == :absent + unless hash[:host_aliases].nil? or hash[:host_aliases] == :absent + hash[:host_aliases].gsub!(/\s+/,' ') # Change delimiter + end + }, + :to_line => proc { |hash| + [:ip, :name].each do |n| + raise ArgumentError, _("%{attr} is a required attribute for hosts") % { attr: n } unless hash[n] and hash[n] != :absent + end + str = "#{hash[:ip]}\t#{hash[:name]}" + if hash.include? :host_aliases and !hash[:host_aliases].nil? and hash[:host_aliases] != :absent + str += "\t#{hash[:host_aliases]}" + end + if hash.include? :comment and !hash[:comment].empty? + str += "\t# #{hash[:comment]}" + end + str + } + + text_line :incomplete, :match => /(?! (#{hosts_pattern}))/ +end diff --git a/lib/puppet/type/host.rb b/lib/puppet/type/host.rb new file mode 100644 index 0000000..3bfcc7e --- /dev/null +++ b/lib/puppet/type/host.rb @@ -0,0 +1,95 @@ +require 'puppet/property/ordered_list' + +module Puppet + Type.newtype(:host) do + ensurable + + newproperty(:ip) do + desc "The host's IP address, IPv4 or IPv6." + + + def valid_v4?(addr) + if /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ =~ addr + return $~.captures.all? {|i| i = i.to_i; i >= 0 and i <= 255 } + end + return false + end + + def valid_v6?(addr) + # http://forums.dartware.com/viewtopic.php?t=452 + # ...and, yes, it is this hard. Doing it programmatically is harder. + return true if addr =~ /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/ + + return false + end + def valid_newline?(addr) + return false if (addr =~ /\n/ || addr =~ /\r/) + return true + end + + validate do |value| + return true if ((valid_v4?(value) || valid_v6?(value)) && (valid_newline?(value))) + raise Puppet::Error, _("Invalid IP address %{value}") % { value: value.inspect } + end + end + + # for now we use OrderedList to indicate that the order does matter. + newproperty(:host_aliases, :parent => Puppet::Property::OrderedList) do + desc "Any aliases the host might have. Multiple values must be + specified as an array." + + def delimiter + " " + end + + def inclusive? + true + end + + validate do |value| + # This regex already includes newline check. + raise Puppet::Error, _("Host aliases cannot include whitespace") if value =~ /\s/ + raise Puppet::Error, _("Host aliases cannot be an empty string. Use an empty array to delete all host_aliases ") if value =~ /^\s*$/ + end + + end + + newproperty(:comment) do + desc "A comment that will be attached to the line with a # character." + validate do |value| + raise Puppet::Error, _("Comment cannot include newline") if (value =~ /\n/ || value =~ /\r/) + end + end + + newproperty(:target) do + desc "The file in which to store service information. Only used by + those providers that write to disk. On most systems this defaults to `/etc/hosts`." + + defaultto { if @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile) + @resource.class.defaultprovider.default_target + else + nil + end + } + end + + newparam(:name) do + desc "The host name." + + isnamevar + + validate do |value| + value.split('.').each do |hostpart| + unless hostpart =~ /^([\w]+|[\w][\w\-]+[\w])$/ + raise Puppet::Error, _("Invalid host name") + end + end + raise Puppet::Error, _("Hostname cannot include newline") if (value =~ /\n/ || value =~ /\r/) + end + end + + @doc = "Installs and manages host entries. For most systems, these + entries will just be in `/etc/hosts`, but some systems (notably OS X) + will have different solutions." + end +end -- cgit v1.2.3