aboutsummaryrefslogtreecommitdiff
path: root/lib/puppet
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet')
-rw-r--r--lib/puppet/provider/host/parsed.rb49
-rw-r--r--lib/puppet/type/host.rb92
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