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, which omitted '-c'. --- puppet-3.8.6/lib/puppet/provider/zone/solaris.rb.orig 2016-04-19 14:19:58.810028057 -0700 +++ puppet-3.8.6/lib/puppet/provider/zone/solaris.rb 2016-04-19 14:20:32.183269425 -0700 @@ -1,5 +1,9 @@ +####################################################################### +# Oracle has modified the originally distributed contents of this file. +####################################################################### + 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 +12,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 || 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 +97,35 @@ # 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] + if @resource[:config_profile] + zoneadm :clone, @resource[:install_args].split(" "), @resource[:clone] + else + zoneadm :clone, @resource[:clone] + end + elsif @resource[:install_args] zoneadm :install, @resource[:install_args].split(" ") else zoneadm :install @@ -182,11 +145,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 +178,6 @@ # def getconfig output = zonecfg :info - name = nil current = nil hash = {} @@ -244,14 +207,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 +230,9 @@ end end end + end + # Boots the zone zoneadm :boot end @@ -285,64 +245,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