require 'facter/util/resolution' module Facter module Util ## # Provide a set of utility methods to interact with Solaris zones. This class # is expected to be instantiated once per set of resolutions in order to # cache the output of the zoneadm command, which can be quite expensive. # # @api private class SolarisZones attr_reader :zone_hash attr_reader :zoneadm_cmd attr_reader :zoneadm_output attr_reader :zoneadm_keys ## # add_facts defines all of the facts for solaris zones, for example `zones`, # `zone_global_id`, `zone_global_status`, etc... This method defines the # static fact named `zones`. The value of this fact is the numver of zones # reported by the zoneadm system command. The `zones` fact also defines # all of the dynamic facts describing the following seven attribute values # for each zone. # # Zones may be added to the system while Facter is loaded. In order to # define new dynamic facts that reflect this new information, the `virtual` # will define new facts as a side effect of refreshing it's own value. # # @api private def self.add_facts model = new model.refresh model.add_dynamic_facts Facter.add("zones") do setcode do model.refresh if model.flushed? model.add_dynamic_facts model.count end on_flush do model.flush! end end end ## # @param [Hash] opts the options to create the instance with # @option opts [String] :zoneadm_cmd ('/usr/sbin/zoneadm list -cp') the # system command to inspect zones # @option opts [String] :zoneadm_output (nil) the cached output of the # zoneadm_cmd def initialize(opts = {}) @zoneadm_keys = [:id, :name, :status, :path, :uuid, :brand, :iptype] @zoneadm_cmd = opts[:zoneadm_cmd] || '/usr/sbin/zoneadm list -cp' if opts[:zoneadm_output] @zoneadm_output = opts[:zoneadm_output] end end ## # add_dynamic_facts defines all of the dynamic facts derived from parsing # the output of the zoneadm command. The zone facts are dynamic, so this # method has the behavior of figuring out what dynamic zone facts need to # be defined and how they should be resolved. # # @param model [SolarisZones] the model used to store data from the system # # @api private def add_dynamic_facts model = self zone_hash.each_pair do |zone, attr_hsh| attr_hsh.keys.each do |attr| Facter.add("zone_#{zone}_#{attr}") do setcode do model.refresh if model.flushed? # Don't resolve if the zone has since been deleted if zone_hsh = model.zone_hash[zone] zone_hsh[attr] # the value end end on_flush do model.flush! end end end end end ## # refresh executes the zoneadm_cmd and stores the output data. # # @api private # # @return [Hash] the parsed output of the zoneadm command def refresh @zoneadm_output = Facter::Util::Resolution.exec(zoneadm_cmd) parse! end ## # parse! parses the string stored in {@zoneadm_output} and stores the # resulting Hash data structure in {@zone_hash} # # @api private def parse! rows = @zoneadm_output.split("\n").collect { |line| line.split(':') } @zone_hash = rows.inject({}) do |memo, fields| zone = fields[1].intern # Transform the row into a hash with keys named by the column names memo[zone] = Hash[*@zoneadm_keys.zip(fields).flatten] memo end end private :parse! ## # count returns the number of running zones, including the global zone. # This method is intended to be used from the setcode block of the `zones` # fact. # # @api private # # @return [Fixnum, nil] the number of running zones or nil if the number # could not be determined. def count if @zone_hash @zone_hash.size end end ## # flush! purges the saved data from the zoneadm_cmd output # # @api private def flush! @zoneadm_output = nil @zone_hash = nil end ## # flushed? returns true if the instance has no parsed data accessible via # the {zone_hash} method. # # @api private # # @return [Boolean] true if there is no parsed data, false otherwise def flushed? !@zone_hash end end end end