Enhance the zone provider to configure zones using a zonecfg export file format.
Enhance the output of puppet resource zone, and fix zone clone functionality.
--- puppet-3.6.2/lib/puppet/provider/zone/solaris.rb.orig 2014-06-18 09:28:43.538680821 -0600
+++ puppet-3.6.2/lib/puppet/provider/zone/solaris.rb 2014-06-16 10:47:24.834849592 -0600
@@ -1,5 +1,5 @@
Puppet::Type.type(:zone).provide(:solaris) do
- desc "Provider for Solaris Zones."
+ desc "Provider for Solaris zones."
commands :adm => "/usr/sbin/zoneadm", :cfg => "/usr/sbin/zonecfg"
defaultfor :osfamily => :solaris
@@ -8,129 +8,84 @@
# Convert the output of a list into a hash
def self.line2hash(line)
- fields = [:id, :name, :ensure, :path, :uuid, :brand, :iptype]
+ fields = [:id, :name, :ensure, :zonepath, :uuid, :brand, :iptype ]
properties = Hash[fields.zip(line.split(':'))]
- del_id = [:brand, :uuid]
+ del_id = [:id, :uuid]
+
# Configured but not installed zones do not have IDs
del_id << :id if properties[:id] == "-"
del_id.each { |p| properties.delete(p) }
-
properties[:ensure] = properties[:ensure].intern
- properties[:iptype] = 'exclusive' if properties[:iptype] == 'excl'
properties
end
+
def self.instances
adm(:list, "-cp").split("\n").collect do |line|
new(line2hash(line))
end
end
- def multi_conf(name, should, &action)
- has = properties[name]
- has = [] if has == :absent
- rms = has - should
- adds = should - has
- (rms.map{|o| action.call(:rm,o)} + adds.map{|o| action.call(:add,o)}).join("\n")
- end
-
- def self.def_prop(var, str)
- define_method('%s_conf' % var.to_s) do |v|
- str % v
- end
- define_method('%s=' % var.to_s) do |v|
- setconfig self.send( ('%s_conf'% var).intern, v)
- end
- end
+
+ # Read in the zone configuration parameters and properties and
+ # perform the zone configuration. Options are cloning the zone,
+ # which needs a zonecfg_export file, configuring an archive, which
+ # takes optional zonecfg_export and archived_zonename parameters,
+ # or performing a zone configuration, which requires a zonecfg_export
+ # file or string.
+ def configure
- def self.def_multiprop(var, &conf)
- define_method(var.to_s) do |v|
- o = properties[var]
- return '' if o.nil? or o == :absent
- o.join(' ')
- end
- define_method('%s=' % var.to_s) do |v|
- setconfig self.send( ('%s_conf'% var).intern, v)
- end
- define_method('%s_conf' % var.to_s) do |v|
- multi_conf(var, v, &conf)
+ if @resource[:archive].nil? && @resource[:zonecfg_export].nil?
+ raise Puppet::Error, "No configuration resource is defined."
end
- end
- def_prop :iptype, "set ip-type=%s"
- def_prop :autoboot, "set autoboot=%s"
- def_prop :path, "set zonepath=%s"
- def_prop :pool, "set pool=%s"
- def_prop :shares, "add rctl\nset name=zone.cpu-shares\nadd value (priv=privileged,limit=%s,action=none)\nend"
-
- def_multiprop :ip do |action, str|
- interface, ip, defrouter = str.split(':')
- case action
- when :add
- cmd = ["add net"]
- cmd << "set physical=#{interface}" if interface
- cmd << "set address=#{ip}" if ip
- cmd << "set defrouter=#{defrouter}" if defrouter
- cmd << "end"
- cmd.join("\n")
- when :rm
- if ip
- "remove net address=#{ip}"
- elsif interface
- "remove net physical=#{interface}"
- else
- raise ArgumentError, "can not remove network based on default router"
+ command = String.new
+ if @resource[:clone]
+ if !@resource[:zonecfg_export]
+ raise Puppet::Error, "A zone configuration must be defined to
+ clone a zone."
+ end
+ command = "#{command(:cfg)} -z #{@resource[:name]} -f #{@resource[:zonecfg_export]}"
+ else
+ unless @resource[:zonecfg_export].nil? || @resource[:zonecfg_export].empty?
+ begin
+ file = File.open(@resource[:zonecfg_export], "rb")
+ str = file.read.gsub(/[\n]\n*\s*/, "; ")
+ rescue
+ str = @resource[:zonecfg_export].gsub(/[\n]\n*\s*/, "; ")
+ ensure
+ file.close unless file.nil?
+ end
+ @property_hash.clear
end
- else self.fail action
- end
- end
- def_multiprop :dataset do |action, str|
- case action
- when :add; ['add dataset',"set name=#{str}",'end'].join("\n")
- when :rm; "remove dataset name=#{str}"
- else self.fail action
- end
- end
+ unless @resource[:archive].nil? || @resource[:archive].empty?
+ if !str.nil?
+ command = "#{command(:cfg)} -z #{@resource[:name]} \'create -a #{@resource[:archive]};#{str}\'"
+ else
+ command = "#{command(:cfg)} -z #{@resource[:name]} create -a #{@resource[:archive]} "
+ end
+ if @resource[:archived_zonename]
+ command << " -z #{@resource[:archived_zonename]}"
+ end
+ end
- def_multiprop :inherit do |action, str|
- case action
- when :add; ['add inherit-pkg-dir', "set dir=#{str}",'end'].join("\n")
- when :rm; "remove inherit-pkg-dir dir=#{str}"
- else self.fail action
+ if !@resource[:zonecfg_export].nil? && @resource[:archive].nil?
+ command = "#{command(:cfg)} -z #{@resource[:name]} \'#{str}\'"
+ end
end
- end
- def my_properties
- [:path, :iptype, :autoboot, :pool, :shares, :ip, :dataset, :inherit]
- end
-
- # Perform all of our configuration steps.
- def configure
- self.fail "Path is required" unless @resource[:path]
- arr = ["create -b #{@resource[:create_args]}"]
-
- # Then perform all of our configuration steps. It's annoying
- # that we need this much internal info on the resource.
- self.resource.properties.each do |property|
- next unless my_properties.include? property.name
- method = (property.name.to_s + '_conf').intern
- arr << self.send(method ,@resource[property.name]) unless property.safe_insync?(properties[property.name])
+ if command
+ r = exec_cmd(:cmd => command)
end
- setconfig(arr.join("\n"))
end
def destroy
zonecfg :delete, "-F"
end
- def add_cmd(cmd)
- @cmds = [] if @cmds.nil?
- @cmds << cmd
- end
-
def exists?
properties[:ensure] != :absent
end
@@ -138,31 +93,31 @@
# We cannot use the execpipe in util because the pipe is not opened in
# read/write mode.
def exec_cmd(var)
- # In bash, the exit value of the last command is the exit value of the
- # entire pipeline
- out = execute("echo \"#{var[:input]}\" | #{var[:cmd]}", :failonfail => false, :combine => true)
- st = $?.exitstatus
- {:out => out, :exit => st}
- end
-
- # Clear out the cached values.
- def flush
- return if @cmds.nil? || @cmds.empty?
- str = (@cmds << "commit" << "exit").join("\n")
- @cmds = []
- @property_hash.clear
-
- command = "#{command(:cfg)} -z #{@resource[:name]} -f -"
- r = exec_cmd(:cmd => command, :input => str)
- if r[:exit] != 0 or r[:out] =~ /not allowed/
- raise ArgumentError, "Failed to apply configuration"
+ if var[:input]
+ execute("echo \"#{var[:input]}\" | #{var[:cmd]}", :failonfail => true, :combine => true)
+ else
+ execute("#{var[:cmd]}", :failonfail => true, :combine => true)
end
end
+
def install(dummy_argument=:work_arround_for_ruby_GC_bug)
+ if ['5.11', '5.12'].include? Facter.value(:kernelrelease)
+ if !@resource[:install_args] and @resource[:config_profile]
+ @resource[:install_args] = " -c " + @resource[:config_profile]
+ elsif !@resource[:install_args] and @resource[:archive]
+ @resource[:install_args] = " -a " + @resource[:archive]
+ if @resource[:archived_zonename]
+ @resource[:install_args] << " -z " + @resource[:archived_zonename]
+ end
+ elsif @resource[:config_profile]
+ @resource[:install_args] << " -c " + @resource[:config_profile]
+ end
+ end
+
if @resource[:clone] # TODO: add support for "-s snapshot"
- zoneadm :clone, @resource[:clone]
- elsif @resource[:install_args]
+ zoneadm :clone, @resource[:clone]
+ elsif @resource[:install_args]
zoneadm :install, @resource[:install_args].split(" ")
else
zoneadm :install
@@ -182,11 +137,12 @@
end
end
@property_hash.dup
+
end
# We need a way to test whether a zone is in process. Our 'ensure'
# property models the static states, but we need to handle the temporary ones.
- def processing?
+ def processing?
hash = status
return false unless hash
["incomplete", "ready", "shutting_down"].include? hash[:ensure]
@@ -214,7 +170,6 @@
#
def getconfig
output = zonecfg :info
-
name = nil
current = nil
hash = {}
@@ -244,14 +199,9 @@
hash
end
- # Execute a configuration string. Can't be private because it's called
- # by the properties.
- def setconfig(str)
- add_cmd str
- end
-
def start
# Check the sysidcfg stuff
+ if ['5.10'].include? Facter.value(:kernelrelease)
if cfg = @resource[:sysidcfg]
self.fail "Path is required" unless @resource[:path]
zoneetc = File.join(@resource[:path], "root", "etc")
@@ -272,7 +222,9 @@
end
end
end
+ end
+ # Boots the zone
zoneadm :boot
end
@@ -285,64 +237,35 @@
end
main = self.class.line2hash(output.chomp)
-
- # Now add in the configuration information
- config_status.each do |name, value|
- main[name] = value
- end
-
main
end
def ready
+ # Prepare the zone
zoneadm :ready
end
def stop
- zoneadm :halt
+ # Shutdown the zone
+ zoneadm :halt
end
+
def unconfigure
+ # Unconfigure and delete the zone
zonecfg :delete, "-F"
end
def uninstall
+ # Uninstall the zone
zoneadm :uninstall, "-F"
end
private
- # Turn the results of getconfig into status information.
- def config_status
- config = getconfig
- result = {}
-
- result[:autoboot] = config[:autoboot] ? config[:autoboot].intern : :true
- result[:pool] = config[:pool]
- result[:shares] = config[:shares]
- if dir = config["inherit-pkg-dir"]
- result[:inherit] = dir.collect { |dirs| dirs[:dir] }
- end
- if datasets = config["dataset"]
- result[:dataset] = datasets.collect { |dataset| dataset[:name] }
- end
- result[:iptype] = config[:'ip-type'] if config[:'ip-type']
- if net = config["net"]
- result[:ip] = net.collect do |params|
- if params[:defrouter]
- "#{params[:physical]}:#{params[:address]}:#{params[:defrouter]}"
- elsif params[:address]
- "#{params[:physical]}:#{params[:address]}"
- else
- params[:physical]
- end
- end
- end
-
- result
- end
-
def zoneadm(*cmd)
+ # Execute the zoneadm command with the arguments
+ # provided
adm("-z", @resource[:name], *cmd)
rescue Puppet::ExecutionFailure => detail
self.fail Puppet::Error, "Could not #{cmd[0]} zone: #{detail}", detail