diff options
Diffstat (limited to 'lib/puppet')
-rw-r--r-- | lib/puppet/provider/host/parsed.rb | 49 | ||||
-rw-r--r-- | lib/puppet/type/host.rb | 92 |
2 files changed, 141 insertions, 0 deletions
diff --git a/lib/puppet/provider/host/parsed.rb b/lib/puppet/provider/host/parsed.rb new file mode 100644 index 0000000..1f82237 --- /dev/null +++ b/lib/puppet/provider/host/parsed.rb @@ -0,0 +1,49 @@ +require 'puppet/provider/parsedfile' + +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 + @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." + + confine exists: hosts + + text_line :comment, match: %r{^#} + text_line :blank, match: %r{^\s*$} + hosts_pattern = '^([0-9a-f:]\S+)\s+([^#\s+]\S+)\s*(.*?)?(?:\s*#\s*(.*))?$' + record_line :parsed, fields: ['ip', 'name', 'host_aliases', 'comment'], + optional: ['host_aliases', 'comment'], + match: %r{#{hosts_pattern}}, + post_parse: proc { |hash| + # An absent comment should match "comment => ''" + hash[:comment] = '' if hash[:comment].nil? || hash[:comment] == :absent + unless hash[:host_aliases].nil? || hash[:host_aliases] == :absent + hash[:host_aliases].gsub!(%r{\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] && hash[n] != :absent + end + str = "#{hash[:ip]}\t#{hash[:name]}" + if hash.include?(:host_aliases) && !hash[:host_aliases].nil? && hash[:host_aliases] != :absent + str += "\t#{hash[:host_aliases]}" + end + if hash.include?(:comment) && !hash[:comment].empty? + str += "\t# #{hash[:comment]}" + end + str + } + + text_line :incomplete, match: %r{(?! (#{hosts_pattern}))} +end diff --git a/lib/puppet/type/host.rb b/lib/puppet/type/host.rb new file mode 100644 index 0000000..03059b9 --- /dev/null +++ b/lib/puppet/type/host.rb @@ -0,0 +1,92 @@ +require 'puppet/property/ordered_list' + +Puppet::Type.newtype(:host) do + @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." + + ensurable + + newproperty(:ip) do + desc "The host's IP address, IPv4 or IPv6." + + def valid_v4?(addr) + data = addr.match(%r{^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$}) + data && data.captures.map(&:to_i).all? { |i| i >= 0 && i <= 255 } + end + + def valid_v6?(addr) + # http://forums.dartware.com/viewtopic.php?t=452 + # ...and, yes, it is this hard. Doing it programmatically is harder. + addr =~ %r{^\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*$} + end + + def valid_newline?(addr) + addr !~ %r{\n} && addr !~ %r{\r} + 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 =~ %r{\s} + raise Puppet::Error, _('Host aliases cannot be an empty string. Use an empty array to delete all host_aliases ') if value =~ %r{^\s*$} + end + end + + newproperty(:comment) do + desc 'A comment that will be attached to the line with a # character.' + validate do |value| + if value =~ %r{\n} || value =~ %r{\r} + raise Puppet::Error, _('Comment cannot include newline') + end + 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 do + if @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile) + @resource.class.defaultprovider.default_target + else + nil + end + end + end + + newparam(:name) do + desc 'The host name.' + + isnamevar + + validate do |value| + value.split('.').each do |hostpart| + if hostpart !~ %r{^([\w]+|[\w][\w\-]+[\w])$} + raise Puppet::Error, _('Invalid host name') + end + end + if value =~ %r{\n} || value =~ %r{\r} + raise Puppet::Error, _('Hostname cannot include newline') + end + end + end +end |