[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