[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, master, updated. debian/0.24.6-1-356-g5718585

James Turnbull james at lovedthanlost.net
Fri Jan 23 14:21:50 UTC 2009


The following commit has been merged in the master branch:
commit 0a40668b348d210cb6cb9e9c25320ccc981ae422
Author: Andrew Shafer <andrew at reductivelabs.com>
Date:   Sun Nov 30 23:54:57 2008 -0700

    Feature #1783 - Add ZFS support
    
    Types and providers to manage zfs and zpool

diff --git a/lib/puppet/provider/zfs/solaris.rb b/lib/puppet/provider/zfs/solaris.rb
new file mode 100644
index 0000000..4d382cf
--- /dev/null
+++ b/lib/puppet/provider/zfs/solaris.rb
@@ -0,0 +1,56 @@
+Puppet::Type.type(:zfs).provide(:solaris) do
+    desc "Provider for Solaris zfs."
+
+    commands :zfs => "/usr/sbin/zfs"
+    defaultfor :operatingsystem => :solaris
+
+    def add_properties
+        properties = []
+        Puppet::Type.type(:zfs).validproperties.each do |property|
+            next if property == :ensure
+            if value = @resource[property] and value != ""
+                properties << "-o" << "#{property}=#{value}"
+            end
+        end
+        properties
+    end
+
+    def arrayify_second_line_on_whitespace(text)
+        if second_line = text.split("\n")[1]
+            second_line.split("\s")
+        else
+            []
+        end
+    end
+
+    def create
+        zfs *([:create] + add_properties + [@resource[:name]])
+    end
+
+    def delete
+        zfs(:destroy, @resource[:name])
+    end
+
+    def exists?
+        if zfs(:list).split("\n").detect { |line| line.split("\s")[0] == @resource[:name] }
+            true
+        else
+            false
+        end
+    end
+
+    [:mountpoint, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir].each do |field|
+        define_method(field) do
+            #special knowledge of format
+            #the command returns values in this format with the header
+            #NAME PROPERTY VALUE SOURCE
+            arrayify_second_line_on_whitespace(zfs(:get, field, @resource[:name]))[2]
+        end
+
+        define_method(field.to_s + "=") do |should|
+            zfs(:set, "#{field}=#{should}", @resource[:name])
+        end
+    end
+
+end
+
diff --git a/lib/puppet/provider/zpool/solaris.rb b/lib/puppet/provider/zpool/solaris.rb
new file mode 100644
index 0000000..d680a5f
--- /dev/null
+++ b/lib/puppet/provider/zpool/solaris.rb
@@ -0,0 +1,112 @@
+Puppet::Type.type(:zpool).provide(:solaris) do
+    desc "Provider for Solaris zpool."
+
+    commands :zpool => "/usr/sbin/zpool"
+    defaultfor :operatingsystem => :solaris
+
+    def process_zpool_data(pool_array)
+        if pool_array == []
+            return Hash.new(:absent)
+        end
+        #get the name and get rid of it
+        pool = Hash.new([])
+        pool[:pool] = pool_array[0]
+        pool_array.shift
+
+        #order matters here :(
+        tmp = []
+
+        pool_array.reverse.each_with_index do |value, i|
+            case value
+            when "spares": pool[:spare] = tmp.reverse and tmp.clear
+            when "logs": pool[:log] = tmp.reverse and tmp.clear
+            when "mirror", "raidz1", "raidz2":
+                sym = value == "mirror" ? :mirror : :raidz
+                pool[sym].unshift(tmp.reverse.join(' '))
+                pool[:raid_parity] = "raidz2" if value == "raidz2"
+                tmp.clear
+            else
+                tmp << value
+                pool[:disk] = tmp.reverse if i == 0
+            end
+        end
+
+        pool
+    end
+
+    def get_pool_data
+        #this is all voodoo dependent on the output from zpool
+        zpool_data = %x{ zpool status #{@resource[:pool]}}.split("\n").select { |line| line.index("\t") == 0 }.collect { |l| l.strip.split("\s")[0] }
+        zpool_data.shift
+        zpool_data
+    end
+
+    def current_pool
+        unless (defined?(@current_pool) and @current_pool)
+            @current_pool = process_zpool_data(get_pool_data)
+        end
+        @current_pool
+    end
+
+    def flush
+        @current_pool= nil
+    end
+
+    #Adds log and spare
+    def build_named(name)
+        if prop = @resource[name.intern]
+            [name] + prop.collect { |p| p.split(' ') }.flatten
+        else
+            []
+        end
+    end
+
+    #query for parity and set the right string
+    def raidzarity
+        @resource[:raid_parity] ? @resource[:raid_parity] : "raidz1"
+    end
+
+    #handle mirror or raid
+    def handle_multi_arrays(prefix, array)
+        array.collect{ |a| [prefix] +  a.split(' ') }.flatten
+    end
+
+    #builds up the vdevs for create command
+    def build_vdevs
+        if disk = @resource[:disk]
+            disk.collect { |d| d.split(' ') }.flatten
+        elsif mirror = @resource[:mirror]
+            handle_multi_arrays("mirror", mirror)
+        elsif raidz = @resource[:raidz]
+            handle_multi_arrays(raidzarity, raidz)
+        end
+    end
+
+    def create
+        zpool(*([:create, @resource[:pool]] + build_vdevs + build_named("spare") + build_named("log")))
+    end
+
+    def delete
+        zpool :destroy, @resource[:pool]
+    end
+
+    def exists?
+        if current_pool[:pool] == :absent
+            false
+        else
+            true
+        end
+    end
+
+    [:disk, :mirror, :raidz, :log, :spare].each do |field|
+        define_method(field) do
+            current_pool[field]
+        end
+
+        define_method(field.to_s + "=") do |should|
+            Puppet.warning "NO CHANGES BEING MADE: zpool %s does not match, should be '%s' currently is '%s'" % [field, should, current_pool[field]]
+        end
+    end
+
+end
+
diff --git a/lib/puppet/type/zfs.rb b/lib/puppet/type/zfs.rb
new file mode 100755
index 0000000..d3af3a4
--- /dev/null
+++ b/lib/puppet/type/zfs.rb
@@ -0,0 +1,45 @@
+module Puppet
+    newtype(:zfs) do
+        @doc = "Manage zfs. Create destroy and set properties on zfs instances."
+
+        ensurable
+
+        newparam(:name) do
+            desc "The full name for this filesystem. (including the zpool)"
+        end
+
+        newproperty(:mountpoint) do
+            desc "The mountpoint property."
+        end
+
+        newproperty(:compression) do
+            desc "The compression property."
+        end
+
+        newproperty(:copies) do
+            desc "The copies property."
+        end
+
+        newproperty(:quota) do
+            desc "The quota property."
+        end
+
+        newproperty(:reservation) do
+            desc "The reservation property."
+        end
+
+        newproperty(:sharenfs) do
+            desc "The sharenfs property."
+        end
+
+        newproperty(:snapdir) do
+            desc "The sharenfs property."
+        end
+
+        autorequire(:zpool) do
+            #strip the zpool off the zfs name and autorequire it
+            [@parameters[:name].value.split('/')[0]]
+        end
+    end
+end
+
diff --git a/lib/puppet/type/zpool.rb b/lib/puppet/type/zpool.rb
new file mode 100755
index 0000000..6d589a0
--- /dev/null
+++ b/lib/puppet/type/zpool.rb
@@ -0,0 +1,60 @@
+module Puppet
+    newtype(:zpool) do
+        @doc = "Manage zpools. Create and delete zpools. The provider WILL NOT SYNC, only report differences.
+
+                Supports vdevs with mirrors, raidz, logs and spares."
+
+        ensurable
+
+        newproperty(:disk, :array_matching => :all) do
+            desc "The disk(s) for this pool. Can be an array or space separated string"
+        end
+
+        newproperty(:mirror, :array_matching => :all) do
+            desc "List of all the devices to mirror for this pool. Each mirror should be a space separated string.
+                  mirror => [\"disk1 disk2\", \"disk3 disk4\"]"
+
+            validate do |value|
+                if value.include?(",")
+                    raise ArgumentError, "mirror names must be provided as string separated, not a comma-separated list"
+                end
+            end
+        end
+
+        newproperty(:raidz, :array_matching => :all) do
+            desc "List of all the devices to raid for this pool. Should be an array of space separated strings.
+                  raidz => [\"disk1 disk2\", \"disk3 disk4\"]"
+
+            validate do |value|
+                if value.include?(",")
+                    raise ArgumentError, "raid names must be provided as string separated, not a comma-separated list"
+                end
+            end
+        end
+
+        newproperty(:spare, :array_matching => :all) do
+            desc "Spare disk(s) for this pool."
+        end
+
+        newproperty(:log, :array_matching => :all) do
+            desc "Log disks for this pool. (doesn't support mirroring yet)"
+        end
+
+        newparam(:pool) do
+            desc "The name for this pool."
+            isnamevar
+       end
+
+        newparam(:raid_parity) do
+            desc "Determines parity when using raidz property."
+        end
+
+        validate do
+            has_should = [:disk, :mirror, :raidz].select { |prop| self.should(prop) }
+            if has_should.length > 1
+                self.fail "You cannot specify %s on this type (only one)" % has_should.join(" and ")
+            end
+        end
+    end
+end
+
diff --git a/spec/unit/provider/zfs/solaris.rb b/spec/unit/provider/zfs/solaris.rb
new file mode 100755
index 0000000..63aefcd
--- /dev/null
+++ b/spec/unit/provider/zfs/solaris.rb
@@ -0,0 +1,87 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+provider_class = Puppet::Type.type(:zfs).provider(:solaris)
+
+describe provider_class do
+    before do
+        @resource = stub("resource", :name => "myzfs")
+        @resource.stubs(:[]).with(:name).returns "myzfs"
+        @resource.stubs(:[]).returns "shouldvalue"
+        @provider = provider_class.new(@resource)
+    end
+
+    describe "when calling add_properties" do
+        it "should add -o and the key=value for each properties with a value" do
+            @resource.stubs(:[]).with(:quota).returns ""
+            @resource.stubs(:[]).with(:mountpoint).returns "/foo"
+            properties = @provider.add_properties
+            properties.include?("-o").should == true
+            properties.include?("mountpoint=/foo").should == true
+            properties.detect { |a| a.include?("quota") }.should == nil
+        end
+    end
+
+    describe "when calling create" do
+        it "should call add_properties" do
+            @provider.stubs(:zfs)
+            @provider.expects(:add_properties).returns([])
+            @provider.create
+        end
+
+        it "should call zfs with create, properties and this zfs" do
+            @provider.stubs(:add_properties).returns(%w{a b})
+            @provider.expects(:zfs).with(:create, "a", "b", @resource[:name])
+            @provider.create
+        end
+    end
+
+    describe "when calling delete" do
+        it "should call zfs with :destroy and this zfs" do
+            @provider.expects(:zfs).with(:destroy, @resource[:name])
+            @provider.delete
+        end
+    end
+   
+    describe "when calling exist?" do
+        it "should call zfs with :list" do
+            #return stuff because we have to slice and dice it
+            @provider.expects(:zfs).with(:list).returns("NAME USED AVAIL REFER MOUNTPOINT\nmyzfs 100K 27.4M /myzfs")
+            @provider.exists?
+        end
+        
+        it "should return true if returned values match the name" do 
+            @provider.stubs(:zfs).with(:list).returns("NAME USED AVAIL REFER MOUNTPOINT\n#{@resource[:name]} 100K 27.4M /myzfs")
+            @provider.exists?.should == true
+        end
+
+        it "should return false if returned values don't match the name" do
+            @provider.stubs(:zfs).with(:list).returns("no soup for you")
+            @provider.exists?.should == false
+        end
+            
+    end
+
+    [:mountpoint, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir].each do |prop|
+        describe "when getting the #{prop} value" do
+            it "should call zfs with :get, #{prop} and this zfs" do
+                @provider.expects(:zfs).with(:get, prop, @resource[:name]).returns("NAME PROPERTY VALUE SOURCE\nmyzfs name value blah")
+                @provider.send(prop)
+            end
+
+            it "should get the third value of the second line from the output" do
+                @provider.stubs(:zfs).with(:get, prop, @resource[:name]).returns("NAME PROPERTY VALUE SOURCE\nmyzfs name value blah")
+                @provider.send(prop).should == "value"
+            end
+        end
+
+        describe "when setting the #{prop} value" do
+            it "should call zfs with :set, #{prop}=value and this zfs" do
+                @provider.expects(:zfs).with(:set, "#{prop}=value", @resource[:name])
+                @provider.send("#{prop}=".intern, "value")
+            end
+        end
+    end
+
+end
diff --git a/spec/unit/provider/zpool/solaris.rb b/spec/unit/provider/zpool/solaris.rb
new file mode 100755
index 0000000..af4db88
--- /dev/null
+++ b/spec/unit/provider/zpool/solaris.rb
@@ -0,0 +1,158 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+provider_class = Puppet::Type.type(:zpool).provider(:solaris)
+
+describe provider_class do
+    before do
+        @resource = stub("resource", :name => "mypool")
+        @resource.stubs(:[]).returns "shouldvalue"
+        @provider = provider_class.new(@resource)
+    end
+
+    describe "when getting the instance" do
+        it "should call process_zpool_data with the result of get_pool_data only once" do
+            @provider.stubs(:get_pool_data).returns(["foo", "disk"])
+            @provider.expects(:process_zpool_data).with(["foo", "disk"]).returns("stuff").once
+            @provider.current_pool
+            @provider.current_pool
+        end
+    end
+
+    describe "when calling flush" do
+        it "should need to reload the pool" do
+            @provider.stubs(:get_pool_data)
+            @provider.expects(:process_zpool_data).returns("stuff").times(2)
+            @provider.current_pool
+            @provider.flush
+            @provider.current_pool
+        end
+    end
+
+    describe "when procesing zpool data" do
+        before do
+            @zpool_data = ["foo", "disk"]
+        end
+
+        describe "when there is no data" do
+            it "should return a hash with ensure=>:absent" do
+                @provider.process_zpool_data([])[:ensure].should == :absent
+            end
+        end
+
+        describe "when there is a spare" do
+            it "should add the spare disk to the hash and strip the array" do
+                @zpool_data += ["spares", "spare_disk"]
+                @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk"]
+            end
+        end
+
+        describe "when there is a log" do
+            it "should add the log disk to the hash and strip the array" do
+                @zpool_data += ["logs", "log_disk"]
+                @provider.process_zpool_data(@zpool_data)[:log].should == ["log_disk"]
+            end
+        end
+
+        describe "when the vdev is a mirror" do
+            it "should call create_multi_array with mirror" do
+                @zpool_data = ["mirrorpool", "mirror", "disk1", "disk2", "mirror", "disk3", "disk4"]
+                @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2", "disk3 disk4"]
+            end
+        end
+
+        describe "when the vdev is a raidz1" do
+            it "should call create_multi_array with raidz1" do
+                @zpool_data = ["mirrorpool", "raidz1", "disk1", "disk2"]
+                @provider.process_zpool_data(@zpool_data)[:raidz].should == ["disk1 disk2"]
+            end
+        end
+
+        describe "when the vdev is a raidz2" do
+            it "should call create_multi_array with raidz2 and set the raid_parity" do
+                @zpool_data = ["mirrorpool", "raidz2", "disk1", "disk2"]
+                pool = @provider.process_zpool_data(@zpool_data)
+                pool[:raidz].should == ["disk1 disk2"]
+                pool[:raid_parity].should == "raidz2"
+            end
+        end
+    end
+
+    describe "when calling the getters and setters" do
+        [:disk, :mirror, :raidz, :log, :spare].each do |field|
+            describe "when calling %s" % field do
+                it "should get the %s value from the current_pool hash" % field do
+                    pool_hash = mock "pool hash"
+                    pool_hash.expects(:[]).with(field)
+                    @provider.stubs(:current_pool).returns(pool_hash)
+                    @provider.send(field)
+                end
+            end
+
+            describe "when setting the %s" % field do
+                it "should warn the %s values were not in sync" % field do
+                    Puppet.expects(:warning).with("NO CHANGES BEING MADE: zpool %s does not match, should be 'shouldvalue' currently is 'currentvalue'" % field)
+                    @provider.stubs(:current_pool).returns(Hash.new("currentvalue"))
+                    @provider.send((field.to_s + "=").intern, "shouldvalue")
+                end
+            end
+        end
+    end
+
+    describe "when calling create" do
+        before do
+            @resource.stubs(:[]).with(:pool).returns("mypool")
+            @provider.stubs(:zpool)
+        end
+
+
+        it "should call build_vdevs" do
+            @provider.expects(:build_vdevs).returns([])
+            @provider.create
+        end
+
+        it "should call build_named with 'spares' and 'log" do
+            @provider.expects(:build_named).with("spare").returns([])
+            @provider.expects(:build_named).with("log").returns([])
+            @provider.create
+        end
+
+        it "should call zpool with arguments from build_vdevs and build_named" do
+            @provider.expects(:zpool).with(:create, 'mypool', 'shouldvalue', 'spare', 'shouldvalue', 'log', 'shouldvalue')
+            @provider.create
+        end
+    end
+
+    describe "when calling delete" do
+        it "should call zpool with destroy and the pool name" do
+            @resource.stubs(:[]).with(:pool).returns("poolname")
+            @provider.expects(:zpool).with(:destroy, "poolname")
+            @provider.delete
+        end
+    end
+
+    describe "when calling exists?" do
+        before do
+            @current_pool = Hash.new(:absent)
+            @provider.stubs(:get_pool_data).returns([])
+            @provider.stubs(:process_zpool_data).returns(@current_pool)
+        end
+
+        it "should get the current pool" do
+            @provider.expects(:process_zpool_data).returns(@current_pool)
+            @provider.exists?
+        end
+
+        it "should return false if the current_pool is absent" do
+            #the before sets it up
+            @provider.exists?.should == false
+        end
+
+        it "should return true if the current_pool has values" do
+            @current_pool[:pool] = "mypool"
+            @provider.exists?.should == true
+        end
+    end
+
+end
diff --git a/spec/unit/type/zfs.rb b/spec/unit/type/zfs.rb
new file mode 100755
index 0000000..434415e
--- /dev/null
+++ b/spec/unit/type/zfs.rb
@@ -0,0 +1,28 @@
+#!/usr/bin/env ruby
+
+Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
+
+zpool = Puppet::Type.type(:zfs)
+
+describe zpool do
+    before do
+        @provider = stub 'provider'
+        @resource = stub 'resource', :resource => nil, :provider => @provider, :line => nil, :file => nil
+    end
+
+    properties = [:ensure, :mountpoint, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir]
+
+    properties.each do |property|
+        it "should have a %s property" % property do
+            zpool.attrclass(property).ancestors.should be_include(Puppet::Property)
+        end
+    end
+
+    parameters = [:name]
+
+    parameters.each do |parameter|
+        it "should have a %s parameter" % parameter do
+            zpool.attrclass(parameter).ancestors.should be_include(Puppet::Parameter)
+        end
+    end
+end
diff --git a/spec/unit/type/zpool.rb b/spec/unit/type/zpool.rb
new file mode 100755
index 0000000..6477d06
--- /dev/null
+++ b/spec/unit/type/zpool.rb
@@ -0,0 +1,28 @@
+#!/usr/bin/env ruby
+
+Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
+
+zpool = Puppet::Type.type(:zpool)
+
+describe zpool do
+    before do
+        @provider = stub 'provider'
+        @resource = stub 'resource', :resource => nil, :provider => @provider, :line => nil, :file => nil
+    end
+
+    properties = [:ensure, :disk, :mirror, :raidz, :spare, :log]
+
+    properties.each do |property|
+        it "should have a %s property" % property do
+            zpool.attrclass(property).ancestors.should be_include(Puppet::Property)
+        end
+    end
+
+    parameters = [:pool, :raid_parity]
+
+    parameters.each do |parameter|
+        it "should have a %s parameter" % parameter do
+            zpool.attrclass(parameter).ancestors.should be_include(Puppet::Parameter)
+        end
+    end
+end

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list