diff options
| author | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-10-17 19:47:32 +0000 |
|---|---|---|
| committer | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-10-17 19:47:32 +0000 |
| commit | 86dae84dad4cd5688029c398109b15b6074cf3c4 (patch) | |
| tree | a7aaa43746bc6b100618e8ecc1acb1a79acba6c9 | |
| parent | 7e488b2dfb9e4c04a9be0c5f824947b55fd0226c (diff) | |
| download | puppet-86dae84dad4cd5688029c398109b15b6074cf3c4.tar.gz puppet-86dae84dad4cd5688029c398109b15b6074cf3c4.tar.xz puppet-86dae84dad4cd5688029c398109b15b6074cf3c4.zip | |
Fixing ports to now use a provider
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1800 980ebf18-57e1-0310-9a29-db15c13687c0
| -rwxr-xr-x | lib/puppet/provider/port/parsed.rb | 139 | ||||
| -rwxr-xr-x | lib/puppet/type/parsedtype.rb | 2 | ||||
| -rwxr-xr-x | lib/puppet/type/parsedtype/port.rb | 261 | ||||
| -rwxr-xr-x | lib/puppet/type/port.rb | 110 | ||||
| -rwxr-xr-x | test/providers/parsedport.rb | 57 | ||||
| -rwxr-xr-x | test/types/port.rb | 117 |
6 files changed, 337 insertions, 349 deletions
diff --git a/lib/puppet/provider/port/parsed.rb b/lib/puppet/provider/port/parsed.rb new file mode 100755 index 000000000..d65a0777f --- /dev/null +++ b/lib/puppet/provider/port/parsed.rb @@ -0,0 +1,139 @@ +require 'puppet/provider/parsedfile' + +Puppet::Type.type(:port).provide :parsed, :parent => Puppet::Provider::ParsedFile do + + @filetype = Puppet::FileType.filetype(:flat) + @path = "/etc/services" + + # Parse a services file + # + # This method also stores existing comments, and it stores all port + # info in order, mostly so that comments are retained in the order + # they were written and in proximity to the same ports. + def self.parse(text) + count = 0 + instances = [] + namehash = {} # For merging + text.chomp.split("\n").each { |line| + hash = {} + case line + when /^#/, /^\s*$/: + # add comments and blank lines to the list as they are + instances << line + else + if line.sub!(/^(\S+)\s+(\d+)\/(\w+)\s*/, '') + hash[:name] = $1 + hash[:number] = $2 + hash[:protocols] = [$3] + + unless line == "" + line.sub!(/^([^#]+)\s*/) do |value| + aliases = $1 + + # Remove any trailing whitespace + aliases.strip! + unless aliases =~ /^\s*$/ + hash[:alias] = aliases.split(/\s+/) + end + + "" + end + + line.sub!(/^\s*#\s*(.+)$/) do |value| + desc = $1 + unless desc =~ /^\s*$/ + hash[:description] = desc.sub(/\s*$/, '') + end + + "" + end + end + else + if line =~ /^\s+\d+/ and + Facter["operatingsystem"].value == "Darwin" + #Puppet.notice "Skipping wonky OS X port entry %s" % + # line.inspect + next + end + raise Puppet::Error, "Could not match '%s'" % line + end + + # If there's already a service with this name, then check + # to see if the only difference is the proto; if so, just + # add our proto and walk away + if obj = namehash[hash[:name]] + if portmerge(obj, hash) + next + end + end + + instances << hash + namehash[hash[:name]] = hash + + count += 1 + end + } + + return instances + end + + def self.portmerge(base, hash) + unless base.has_key?(:protocols) + return false + end + + # This method is only called from parsing, so we only worry + # about 'is' values. + proto = base[:protocols] + + if proto.nil? or proto == :absent + # We are an unitialized object; we've got 'should' + # values but no 'is' values + return false + end + + # If this is happening, our object exists + base[:ensure] = :present + + if hash[:protocols] + # The protocol can be a symbol, so... + if proto.is_a?(Symbol) + proto = [] + end + # Check to see if it already includes our proto + unless proto.include?(hash[:protocols]) + # We are missing their proto + proto += hash[:protocols] + base[:protocols] = proto + end + end + + if hash.include?(:description) and ! base.include?(:description) + base[:description] = hash[:description] + end + + return true + end + + # Convert the current object into one or more services entry. + def self.to_record(hash) + hash[:protocols].collect { |proto| + str = "%s\t%s/%s" % [hash[:name], hash[:number], proto] + + if value = hash[:alias] and value != :absent + str += "\t%s" % value.join(" ") + else + str += "\t" + end + + if value = hash[:description] and value != :absent + str += "\t# %s" % value + else + str += "\t" + end + str + }.join("\n") + end +end + +# $Id$ diff --git a/lib/puppet/type/parsedtype.rb b/lib/puppet/type/parsedtype.rb index ae7918819..a74ba02ac 100755 --- a/lib/puppet/type/parsedtype.rb +++ b/lib/puppet/type/parsedtype.rb @@ -165,7 +165,7 @@ module Puppet def exists? h = self.retrieve - if h[:ensure] == :absent + if h.nil? or h[:ensure] == :absent return false else return true diff --git a/lib/puppet/type/parsedtype/port.rb b/lib/puppet/type/parsedtype/port.rb deleted file mode 100755 index f8a08913f..000000000 --- a/lib/puppet/type/parsedtype/port.rb +++ /dev/null @@ -1,261 +0,0 @@ -require 'etc' -require 'facter' -require 'puppet/type/parsedtype' -require 'puppet/type/state' - -module Puppet - newtype(:port, Puppet::Type::ParsedType) do - newstate(:protocols) do - desc "The protocols the port uses. Valid values are *udp* and *tcp*. - Most services have both protocols, but not all. If you want - both protocols, you must specify that; Puppet replaces the - current values, it does not merge with them. If you specify - multiple protocols they must be as an array." - - def is=(value) - case value - when String - @is = value.split(/\s+/) - else - @is = value - end - end - - def is - @is - end - - # We actually want to return the whole array here, not just the first - # value. - def should - if defined? @should - if @should[0] == :absent - return :absent - else - return @should - end - else - return nil - end - end - - validate do |value| - valids = ["udp", "tcp", "ddp", :absent] - unless valids.include? value - raise Puppet::Error, - "Protocols can be either 'udp' or 'tcp', not %s" % value - end - end - end - - newstate(:number) do - desc "The port number." - end - - newstate(:description) do - desc "The port description." - isoptional - end - - newstate(:alias) do - desc "Any aliases the port might have. Multiple values must be - specified as an array. Note that this state has the same name as - one of the metaparams; using this state to set aliases will make - those aliases available in your Puppet scripts and also on disk." - - isoptional - - # We have to override the feeding mechanism; it might be nil or - # white-space separated - def is=(value) - # If it's just whitespace, ignore it - case value - when /^\s+$/ - @is = nil - when String - @is = value.split(/\s+/) - when Symbol - @is = value - else - raise Puppet::DevError, "Invalid value %s" % value.inspect - end - end - - # We actually want to return the whole array here, not just the first - # value. - def should - if defined? @should - if @should[0] == :absent - return :absent - else - return @should - end - else - return nil - end - end - - validate do |value| - if value.is_a? String and value =~ /\s/ - raise Puppet::Error, - "Aliases cannot have whitespace in them: %s" % - value.inspect - end - end - - munge do |value| - unless value == "absent" or value == :absent - # Add the :alias metaparam in addition to the state - @parent.newmetaparam( - @parent.class.metaparamclass(:alias), value - ) - end - value - end - end - - newparam(:name) do - desc "The port name." - - isnamevar - end - - @doc = "Installs and manages port entries. For most systems, these - entries will just be in /etc/services, but some systems (notably OS X) - will have different solutions." - - @path = "/etc/services" - @fields = [:ip, :name, :alias] - - @filetype = Puppet::FileType.filetype(:flat) - - # Parse a services file - # - # This method also stores existing comments, and it stores all port - # info in order, mostly so that comments are retained in the order - # they were written and in proximity to the same ports. - def self.parse(text) - count = 0 - hash = {} - text.chomp.split("\n").each { |line| - hash.clear - case line - when /^#/, /^\s*$/: - # add comments and blank lines to the list as they are - @instances << line - else - if line.sub!(/^(\S+)\s+(\d+)\/(\w+)\s*/, '') - hash[:name] = $1 - hash[:number] = $2 - hash[:protocols] = $3 - - unless line == "" - line.sub!(/^([^#]+)\s*/) do |value| - aliases = $1 - - # Remove any trailing whitespace - aliases.strip! - unless aliases =~ /^\s*$/ - hash[:alias] = aliases - end - - "" - end - - line.sub!(/^\s*#\s*(.+)$/) do |value| - desc = $1 - unless desc =~ /^\s*$/ - hash[:description] = desc.sub(/\s*$/, '') - end - - "" - end - end - else - if line =~ /^\s+\d+/ and - Facter["operatingsystem"].value == "Darwin" - #Puppet.notice "Skipping wonky OS X port entry %s" % - # line.inspect - next - end - raise Puppet::Error, "Could not match '%s'" % line - end - - # If there's already a service with this name, then check - # to see if the only difference is the proto; if so, just - # add our proto and walk away - if obj = self[hash[:name]] - if obj.portmerge(hash) - next - end - end - - hash2obj(hash) - - count += 1 - end - } - end - - def portmerge(hash) - unless @states.include?(:protocols) - return false - end - - # This method is only called from parsing, so we only worry - # about 'is' values. - proto = self.state(:protocols).is - - if proto.nil? or proto == :absent - # We are an unitialized object; we've got 'should' - # values but no 'is' values - return false - end - - # If this is happening, our object exists - self.is = [:ensure, :present] - - if hash[:protocols] - # The protocol can be a symbol, so... - if proto.is_a?(Symbol) - proto = [] - end - # Check to see if it already includes our proto - unless proto.include?(hash[:protocols]) - # We are missing their proto - proto << hash[:protocols] - @states[:protocols].is = proto - end - end - - if hash.include?(:description) and ! @states.include?(:description) - self.is = [:description, hash[:description]] - end - - return true - end - - # Convert the current object into one or more services entry. - def to_record - self.state(:protocols).value.collect { |proto| - str = "%s\t%s/%s" % [self[:name], self.value(:number), - proto] - - if value = self.value(:alias) and value != :absent - str += "\t%s" % value.join(" ") - else - str += "\t" - end - - if value = self.value(:description) and value != :absent - str += "\t# %s" % value - else - str += "\t" - end - str - }.join("\n") - end - end -end - -# $Id$ diff --git a/lib/puppet/type/port.rb b/lib/puppet/type/port.rb new file mode 100755 index 000000000..3e73e625f --- /dev/null +++ b/lib/puppet/type/port.rb @@ -0,0 +1,110 @@ +require 'puppet/type/parsedtype' + +module Puppet + newtype(:port, Puppet::Type::ParsedType) do + @doc = "Installs and manages port entries. For most systems, these + entries will just be in /etc/services, but some systems (notably OS X) + will have different solutions." + + newstate(:protocols) do + desc "The protocols the port uses. Valid values are *udp* and *tcp*. + Most services have both protocols, but not all. If you want + both protocols, you must specify that; Puppet replaces the + current values, it does not merge with them. If you specify + multiple protocols they must be as an array." + + def is=(value) + case value + when String + @is = value.split(/\s+/) + else + @is = value + end + end + + def is + @is + end + + # We actually want to return the whole array here, not just the first + # value. + def should + if defined? @should + if @should[0] == :absent + return :absent + else + return @should + end + else + return nil + end + end + + validate do |value| + valids = ["udp", "tcp", "ddp", :absent] + unless valids.include? value + raise Puppet::Error, + "Protocols can be either 'udp' or 'tcp', not %s" % value + end + end + end + + newstate(:number) do + desc "The port number." + end + + newstate(:description) do + desc "The port description." + isoptional + end + + newstate(:alias) do + desc "Any aliases the port might have. Multiple values must be + specified as an array. Note that this state has the same name as + one of the metaparams; using this state to set aliases will make + those aliases available in your Puppet scripts and also on disk." + + isoptional + + # We actually want to return the whole array here, not just the first + # value. + def should + if defined? @should + if @should[0] == :absent + return :absent + else + return @should + end + else + return nil + end + end + + validate do |value| + if value.is_a? String and value =~ /\s/ + raise Puppet::Error, + "Aliases cannot have whitespace in them: %s" % + value.inspect + end + end + + munge do |value| + unless value == "absent" or value == :absent + # Add the :alias metaparam in addition to the state + @parent.newmetaparam( + @parent.class.metaparamclass(:alias), value + ) + end + value + end + end + + newparam(:name) do + desc "The port name." + + isnamevar + end + end +end + +# $Id$ diff --git a/test/providers/parsedport.rb b/test/providers/parsedport.rb new file mode 100755 index 000000000..ff3977dd5 --- /dev/null +++ b/test/providers/parsedport.rb @@ -0,0 +1,57 @@ +#!/usr/bin/env ruby + +$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/ + +require 'puppettest' +require 'puppet' +require 'puppet/type/port' +require 'test/unit' +require 'facter' + +class TestParsedPort < Test::Unit::TestCase + include PuppetTest + + def setup + super + @provider = Puppet.type(:port).provider(:parsed) + + @oldfiletype = @provider.filetype + end + + def teardown + Puppet::FileType.filetype(:ram).clear + @provider.filetype = @oldfiletype + super + end + + # Parse our sample data and make sure we regenerate it correctly. + def test_portsparse + fakedata("data/types/ports").each do |file| + @provider.path = file + instances = nil + assert_nothing_raised { + instances = @provider.retrieve + } + + text = @provider.fileobj.read.gsub(/\s+/, ' ') + text.gsub!(/ #.+$/, '') + + dest = tempfile() + @provider.path = dest + + # Now write it back out + assert_nothing_raised { + @provider.store(instances) + } + + newtext = @provider.fileobj.read.gsub(/\s+/, ' ') + + newtext.gsub!(/ #.+$/, '') + + # Don't worry about difference in whitespace + assert_equal(text.gsub(/\s+/, ' '), newtext.gsub(/\s+/, ' ')) + end + end +end + +# $Id$ diff --git a/test/types/port.rb b/test/types/port.rb index 1537dc921..d80c242a8 100755 --- a/test/types/port.rb +++ b/test/types/port.rb @@ -2,32 +2,32 @@ $:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/ -# Test host job creation, modification, and destruction - require 'puppettest' require 'puppet' -require 'puppet/type/parsedtype/port' +require 'test/unit' require 'facter' class TestPort < Test::Unit::TestCase include PuppetTest + def setup super @porttype = Puppet.type(:port) - @oldfiletype = @porttype.filetype - end - def teardown - @porttype.filetype = @oldfiletype - Puppet.type(:file).clear - super - end + @provider = @porttype.defaultprovider - # Here we just create a fake host type that answers to all of the methods - # but does not modify our actual system. - def mkfaketype - pfile = tempfile() - @porttype.path = pfile + # Make sure they aren't using something funky like netinfo + unless @provider.name == :parsed + @porttype.defaultprovider = @porttype.provider(:parsed) + end + + cleanup do @porttype.defaultprovider = nil end + + oldpath = @provider.path + cleanup do + @provider.path = oldpath + end + @provider.path = tempfile() end def mkport @@ -52,51 +52,29 @@ class TestPort < Test::Unit::TestCase end def test_simpleport - mkfaketype host = nil assert_nothing_raised { - assert_nil(Puppet.type(:port).retrieve) + Puppet.type(:port).defaultprovider.retrieve + + count = 0 + @porttype.each do |h| + count += 1 + end + + assert_equal(0, count, "Found hosts in empty file somehow") } port = mkport + assert_apply(port) assert_nothing_raised { - Puppet.type(:port).store + port.retrieve } - assert_nothing_raised { - assert( - Puppet.type(:port).to_file.include?( - Puppet.type(:port).fileobj.read - ), - "File does not include all of our objects" - ) - } - end - - def test_portsparse - fakedata("data/types/ports").each { |file| - @porttype.path = file - Puppet.info "Parsing %s" % file - assert_nothing_raised { - @porttype.retrieve - } - - # Now just make we've got some ports we know will be there - dns = @porttype["domain"] - assert(dns, "Could not retrieve DNS port") - - assert_equal("53", dns.is(:number), "DNS number was wrong") - %w{udp tcp}.each { |v| - assert(dns.is(:protocols).include?(v), "DNS did not include proto %s" % v) - } - - @porttype.clear - } + assert(port.exists?, "Port did not get created") end def test_moddingport - mkfaketype port = nil port = mkport @@ -120,59 +98,24 @@ class TestPort < Test::Unit::TestCase end def test_removal - mkfaketype port = mkport() assert_nothing_raised { port[:ensure] = :present } assert_events([:port_created], port) + assert_events([], port) - port.retrieve - assert(port.insync?) + assert(port.exists?, "port was not created") assert_nothing_raised { port[:ensure] = :absent } assert_events([:port_removed], port) - port.retrieve + assert(! port.exists?, "port was not removed") assert_events([], port) end - def test_modifyingfile - mkfaketype() - - ports = [] - names = [] - 3.times { - k = mkport() - ports << k - names << k.name - } - assert_apply(*ports) - ports.clear - Puppet.type(:port).clear - newport = mkport() - #newport[:ensure] = :present - names << newport.name - assert_apply(newport) - Puppet.type(:port).clear - # Verify we can retrieve that info - assert_nothing_raised("Could not retrieve after second write") { - newport.retrieve - } - - # And verify that we have data for everything - names.each { |name| - port = Puppet.type(:port)[name] - assert(port) - port.retrieve - assert(port[:number], "port %s has no number" % name) - } - end - def test_addingstates - mkfaketype - port = mkport() assert_events([:port_created], port) @@ -186,7 +129,7 @@ class TestPort < Test::Unit::TestCase assert_equal(:present, port.is(:ensure)) - assert(port.state(:alias).is == :absent) + assert_equal(:absent, port.is(:alias)) port[:alias] = "yaytest" assert_events([:port_changed], port) |
