summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/provider/interface/sunos.rb178
-rwxr-xr-xspec/unit/ral/provider/interface/sunos.rb214
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