diff options
-rw-r--r-- | lib/puppet/provider/interface/sunos.rb | 178 | ||||
-rwxr-xr-x | spec/unit/ral/provider/interface/sunos.rb | 214 |
2 files changed, 264 insertions, 128 deletions
diff --git a/lib/puppet/provider/interface/sunos.rb b/lib/puppet/provider/interface/sunos.rb index 99ddcd2f4..eda21ca3d 100644 --- a/lib/puppet/provider/interface/sunos.rb +++ b/lib/puppet/provider/interface/sunos.rb @@ -1,88 +1,22 @@ require 'puppet/provider/parsedfile' require 'erb' -Puppet::Type.type(:interface).provide(:sunos, - :default_target => "/etc/hostname.lo0", - :parent => Puppet::Provider::ParsedFile, - :filetype => :flat -) do - +Puppet::Type.type(:interface).provide(:sunos) do confine :kernel => "SunOS" - # Two types of lines: - # the first line does not start with 'addif' - # the rest do - record_line :sunos, :fields => %w{interface_type name ifopts onboot}, :rts => true, :absent => "", :block_eval => :instance do - # Parse our interface line - def process(line) - details = {:ensure => :present} - - values = line.split(/\s+/) - - # Are we the primary interface? - if values[0] == "addif" - details[:interface_type] = :alias - values.shift - else - details[:interface_type] = :normal - end - - # Should the interface be up by default? - if values[-1] == "up" - details[:onboot] = :true - values.pop - else - details[:onboot] = :false - end - - # Set the interface name. - details[:name] = values.shift - - # Handle any interface options - unless values.empty? - details[:ifopts] = values.join(" ") - end - - return details - end - - # Turn our record into a line. - def to_line(details) - ret = [] - if details[:interface_type] != :normal - ret << "addif" - end - ret << details[:name] - - if details[:ifopts] and details[:ifopts] != :absent - if details[:ifopts].is_a?(Array) - ret << details[:ifopts].join(" ") - else - ret << details[:ifopts] - end - end - - if details[:onboot] and details[:onboot] != :false - ret << "up" - end - - return ret.join(" ") - end - end - - def self.header - # over-write the default puppet behaviour of adding a header - # because on further investigation this breaks the solaris - # init scripts - %{} - end + # Add accessor/getter methods for each property/parameter; these methods + # modify @property_hash. + mk_resource_methods # Get a list of interface instances. def self.instances Dir.glob("/etc/hostname.*").collect do |file| - # We really only expect one record from each file - parse(file).shift - end.collect { |record| new(record) } + device = File.basename(file).split(".").pop + + instance = new(:interface => device) + instance.parse + instance + end end def self.match(hash) @@ -103,13 +37,97 @@ Puppet::Type.type(:interface).provide(:sunos, end end + def initialize(*args) + @property_hash = {} + super + end + + def create + self.class.resource_type.validproperties.each do |property| + if value = resource.should(property) + @property_hash[property] = value + end + end + @property_hash[:name] = resource.name + + return (@resource.class.name.to_s + "_created").intern + end + + def destroy + File.unlink(file_path) + @property_hash[:ensure] = :absent + end + + def exists? + FileTest.exist?(file_path) + end + # Where should the file be written out? Can be overridden by setting # :target in the model. def file_path - unless resource[:interface] - raise ArgumentError, "You must provide the interface name on Solaris" + self.fail("Could not determine interface") unless interface = @property_hash[:interface] || (resource and resource[:interface]) + return File.join("/etc", "hostname." + interface) + end + + def flush + return if self.ensure == :absent + File.open(file_path, "w") { |f| f.print generate() + "\n" } + end + + # Turn our record into a line. + def generate + ret = [] + if self.interface_type == :alias + ret << "addif" + end + ret << self.name + + if self.ifopts != :absent + if @property_hash[:ifopts].is_a?(Array) + ret << @property_hash[:ifopts].join(" ") + else + ret << @property_hash[:ifopts] + end + end + + if self.onboot and ! [:absent, :false].include?(self.onboot) + ret << "up" end - return File.join("/etc", "hostname." + resource[:interface]) + + return ret.join(" ") end -end + # Parse our interface file. + def parse + (@property_hash = {:ensure => :absent} and return) unless FileTest.exist?(file_path) + + values = File.read(file_path).chomp.split(/\s+/) + + @property_hash[:ensure] = :present + #@property_hash = {:ensure => :present} + + # Are we the primary interface? + if values[0] == "addif" + @property_hash[:interface_type] = :alias + values.shift + else + @property_hash[:interface_type] = :normal + end + + # Should the interface be up by default? + if values[-1] == "up" + @property_hash[:onboot] = :true + values.pop + else + @property_hash[:onboot] = :false + end + + # Set the interface name. + @property_hash[:name] = values.shift + + # Handle any interface options + unless values.empty? + @property_hash[:ifopts] = values.join(" ") + end + end +end diff --git a/spec/unit/ral/provider/interface/sunos.rb b/spec/unit/ral/provider/interface/sunos.rb index b48cf8e22..7b9f462e6 100755 --- a/spec/unit/ral/provider/interface/sunos.rb +++ b/spec/unit/ral/provider/interface/sunos.rb @@ -21,101 +21,219 @@ describe provider_class do provider_class.should be_suitable end - it "should be a subclass of ParsedFile" do - provider_class.superclass.should equal(Puppet::Provider::ParsedFile) + it "should pick its file path by combining '/etc/hostname.' with the interface if one is set" do + provider = provider_class.new(:record_type => :sunos, :interface_type => :normal, :name => "testing", :interface => 'eth0') + provider.file_path.should == "/etc/hostname.eth0" end - it "should use /etc/hostname.lo0 as the default target" do - provider_class.default_target.should == "/etc/hostname.lo0" + it "should pick its file path by combining '/etc/hostname.' with the resource's interface if one is not set in the provider" do + provider = provider_class.new(:record_type => :sunos, :interface_type => :normal, :name => "testing") + resource = mock 'resource' + resource.stubs(:[]).with(:interface).returns("eth0") + provider.resource = resource + provider.file_path.should == "/etc/hostname.eth0" end - it "should use the :flat filetype" do - provider_class.filetype.name.should == :flat + it "should fail when picking its file path if there is no resource nor an interface set in the provider" do + provider = provider_class.new(:record_type => :sunos, :interface_type => :normal, :name => "testing") + proc { provider.file_path }.should raise_error(Puppet::Error) end +end - it "should return an instance for every file matching /etc/hostname.*" do - Dir.expects(:glob).with("/etc/hostname.*").returns(%w{one two}) - one_record = mock 'one_record' - two_record = mock 'two_record' - provider_class.expects(:parse).with("one").returns([one_record]) - provider_class.expects(:parse).with("two").returns([two_record]) - one_instance = mock 'one_instance' - two_instance = mock 'two_instance' - provider_class.expects(:new).with(one_record).returns(one_instance) - provider_class.expects(:new).with(two_record).returns(two_instance) +describe provider_class, " when listing interfaces" do + it "should return an instance for every file matching /etc/hostname.*, created with the interface name set from the file" do + Dir.expects(:glob).with("/etc/hostname.*").returns(%w{/etc/hostname.one /etc/hostname.two}) + one_instance = stub 'one_instance', :parse => nil + two_instance = stub 'two_instance', :parse => nil + provider_class.expects(:new).with(:interface => "one").returns(one_instance) + provider_class.expects(:new).with(:interface => "two").returns(two_instance) provider_class.instances.should == [one_instance, two_instance] end + + it "should call parse on each instance being returned" do + Dir.expects(:glob).with("/etc/hostname.*").returns(%w{/etc/hostname.one}) + one_instance = mock 'one_instance' + provider_class.expects(:new).with(:interface => "one").returns(one_instance) + + one_instance.expects(:parse) + + provider_class.instances + end + + it "should assign matching providers to any prefetched instances" do + Dir.expects(:glob).with("/etc/hostname.*").returns(%w{one two}) + one_instance = stub 'one_instance', :name => "one", :parse => nil + two_instance = stub 'two_instance', :name => "two", :parse => nil + provider_class.expects(:new).with(:interface => "one").returns(one_instance) + provider_class.expects(:new).with(:interface => "two").returns(two_instance) + + resources = {"one" => mock("one"), "three" => mock('three')} + resources["one"].expects(:provider=).with(one_instance) + + provider_class.prefetch(resources) + end +end + +describe provider_class, " when creating and destroying" do + before do + @provider = provider_class.new(:interface => "eth0", :name => "testing") + end + + it "should consider the interface present if the file exists" do + FileTest.expects(:exist?).with("/etc/hostname.eth0").returns(true) + @provider.should be_exists + end + + it "should consider the interface absent if the file does not exist" do + FileTest.expects(:exist?).with("/etc/hostname.eth0").returns(false) + @provider.should_not be_exists + end + + it "should remove the file if the interface is being destroyed" do + File.expects(:unlink).with("/etc/hostname.eth0") + @provider.destroy + end + + it "should mark :ensure as :absent if the interface is destroyed" do + File.stubs(:unlink) + @provider.destroy + @provider.ensure.should == :absent + end + + it "should mark :ensure as :present if the interface is being created" do + resource = stub 'resource', :name => 'testing' + resource.stubs(:should).with { |name| name == :ensure }.returns(:present) + resource.stubs(:should).with { |name| name != :ensure }.returns(nil) + @provider.resource = resource + @provider.create + @provider.ensure.should == :present + end + + it "should write the generated text to disk when the interface is flushed" do + fh = mock("filehandle") + File.expects(:open).yields(fh) + fh.expects(:print).with("testing\n") + resource = stub 'resource', :name => 'testing' + resource.stubs(:should).with { |name| name == :ensure }.returns(:present) + resource.stubs(:should).with { |name| name != :ensure }.returns(nil) + @provider.resource = resource + @provider.create + @provider.flush + end + + it "should not write the generated text to disk when the interface is flushed if :ensure == :absent" do + @provider.ensure = :absent + @provider.flush + end end -describe provider_class, " when parsing" do +describe provider_class, " when parsing a non-existant file" do + it "should mark the interface as absent" do + @provider = provider_class.new(:interface => "eth0", :name => "testing") + FileTest.expects(:exist?).with("/etc/hostname.eth0").returns(false) + @provider.parse + @provider.ensure.should == :absent + end +end + +describe provider_class, " when parsing an existing file" do + before do + @provider = provider_class.new(:interface => "eth0", :name => "testing") + FileTest.stubs(:exist?).with("/etc/hostname.eth0").returns(true) + end + + def set_text(text) + File.stubs(:read).with("/etc/hostname.eth0").returns(text) + end + + it "should retain the interface name" do + set_text "testing" + @provider.parse + @provider.ensure.should == :present + @provider.interface.should == "eth0" + end + it "should mark the interface as present" do - provider_class.parse("testing")[0][:ensure].should == :present + set_text "testing" + @provider.parse + @provider.ensure.should == :present end it "should mark the interface as an alias if the first word is 'addif'" do - provider_class.parse("addif testing")[0][:interface_type].should == :alias + set_text "addif testing" + @provider.parse + @provider.interface_type.should == :alias end it "should not mark the interface as normal if the first word is not 'addif'" do - provider_class.parse("testing")[0][:interface_type].should == :normal + set_text "testing" + @provider.parse + @provider.interface_type.should == :normal end it "should start the interface on boot of the last word is 'up'" do - provider_class.parse("testing up")[0][:onboot].should == :true + set_text "testing up" + @provider.parse + @provider.onboot.should == :true end it "should not start the interface on boot of the last word is not 'up'" do - provider_class.parse("testing")[0][:onboot].should == :false + set_text "testing" + @provider.parse + @provider.onboot.should == :false end it "should set the interface to the first non-behavioural word" do - provider_class.parse("addif testing up")[0][:name].should == "testing" + set_text "addif testing up" + @provider.parse + @provider.name.should == "testing" end it "should consider any remaining terms to be interface options" do - provider_class.parse("addif testing -O up")[0][:ifopts].should == "-O" - end - - it "should pick its file path by combining '/etc/hostname.' with the resource's interface" do - provider = provider_class.new(:record_type => :sunos, :interface_type => :normal, :name => "testing") - resource = stub 'resource' - resource.stubs(:[]).with(:interface).returns("eth0") - provider.resource = resource - provider.file_path.should == "/etc/hostname.eth0" + set_text "addif testing -O up" + @provider.parse + @provider.ifopts.should == "-O" end end describe provider_class, " when generating" do - it "should prefix the text with 'addif' if the interface is an alias" do - provider_class.to_file([{:record_type => :sunos, :interface_type => :alias, :name => "testing"}]).should =~ /^addif / + before do + @provider = provider_class.new(:interface => "eth0", :name => "testing") end - it "should not prefix the text with 'addif' if the interface is not an alias" do - provider_class.to_file([{:record_type => :sunos, :interface_type => :normal, :name => "testing"}]).should !~ /^testing/ + it "should prefix the text with 'addif' if the interface is an alias" do + @provider.interface_type = :alias + @provider.generate.should == "addif testing" end - it "should put the name first if the interface is not an alias" do - provider_class.to_file([{:record_type => :sunos, :interface_type => :normal, :name => "testing"}]).should =~ /^testing/ + it "should not prefix the text with 'addif' if the interface is not an alias" do + @provider.generate.should == "testing" end - it "should put the name after the 'addif' if the interface is an alias" do - provider_class.to_file([{:record_type => :sunos, :interface_type => :alias, :name => "testing"}]).should =~ /^addif testing/ + it "should put the ifopts after the name if they are present" do + @provider.ifopts = "-O" + @provider.generate.should == "testing -O" end - it "should put the ifopts after the name if they are present" do - provider_class.to_file([{:record_type => :sunos, :interface_type => :normal, :ifopts => "-O", :name => "testing"}]).should =~ /testing -O/ + it "should mark the interface up if onboot is enabled" do + @provider.onboot = :true + @provider.generate.should == "testing up" end - it "should not put the ifopts after the name if they are marked :absent" do - provider_class.to_file([{:record_type => :sunos, :interface_type => :normal, :ifopts => :absent, :name => "testing"}]).should == "testing\n" + it "should use the resource name if no provider name is present" do + provider = provider_class.new(:interface => "eth0") + resource = stub 'resource', :name => "rtest" + provider.resource = resource + provider.generate.should == "rtest" end - it "should mark the interface up if onboot is enabled" do - provider_class.to_file([{:record_type => :sunos, :onboot => :true, :interface_type => :normal, :name => "testing"}]).should == "testing up\n" + it "should use the provider name if present" do + @provider.generate.should == "testing" end - it "should not include a commented header" do - provider_class.to_file([{:record_type => :sunos, :interface_type => :normal, :name => "testing"}]).should == "testing\n" + it "should fail if neither a resource nor the provider name is present" do + provider = provider_class.new(:interface => "eth0") + proc { provider.generate }.should raise_error end end |