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.4.1/lib/puppet/provider/zone/solaris.rb.orig	2013-07-29 15:48:58.118553584 -0600
+++ puppet-3.4.1/lib/puppet/provider/zone/solaris.rb	2013-07-29 15:49:21.053204412 -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,20 +8,20 @@
 
   # 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
     # LAK:NOTE See http://snurl.com/21zf8  [groups_google_com]
     x = adm(:list, "-cp").split("\n").collect do |line|
@@ -29,109 +29,64 @@
     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
@@ -139,31 +94,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
@@ -183,11 +138,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]
@@ -215,7 +171,6 @@
   #
   def getconfig
     output = zonecfg :info
-
     name = nil
     current = nil
     hash = {}
@@ -245,14 +200,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")
@@ -273,7 +223,9 @@
         end
       end
     end
+   end
 
+    # Boots the zone
     zoneadm :boot
   end
 
@@ -286,64 +238,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 "Could not #{cmd[0]} zone: #{detail}"
