[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, experimental, updated. debian/2.6.8-1-844-g7ec39d5

Hector Rivas Gandara hrivas at caixagalicia.es
Tue May 10 08:03:30 UTC 2011


The following commit has been merged in the experimental branch:
commit 52f8dddf75e4611de4ea60cb29f4de3e11739226
Author: Hector Rivas Gandara <keymon at gmail.com>
Date:   Mon Dec 27 11:20:26 2010 +0100

    (#5432) Use AIX native commands to manage users and groups
    
    Refactorized the aixobject.rb to allow new providers using commands with colon separated output.

diff --git a/lib/puppet/provider/aixobject.rb b/lib/puppet/provider/aixobject.rb
index 98ac13b..ae5180d 100755
--- a/lib/puppet/provider/aixobject.rb
+++ b/lib/puppet/provider/aixobject.rb
@@ -12,20 +12,13 @@ class Puppet::Provider::AixObject < Puppet::Provider
   # TODO:: add a type parameter to change this
   class << self 
     attr_accessor :ia_module
-  end
-  
-   
-  # AIX attributes to properties mapping. Subclasses should rewrite them
-  # It is a list with of hash
-  #  :aix_attr      AIX command attribute name
-  #  :puppet_prop   Puppet propertie name
-  #  :to            Method to adapt puppet property to aix command value. Optional.
-  #  :from            Method to adapt aix command value to puppet property. Optional
-  class << self
-    attr_accessor :attribute_mapping
+  end  
+
+  # The real provider must implement these functions.
+  def lscmd(value=@resource[:name])
+    raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: #{detail}"
   end
 
-  # Provider must implement these functions.
   def lscmd(value=@resource[:name])
     raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: #{detail}"
   end
@@ -42,7 +35,13 @@ class Puppet::Provider::AixObject < Puppet::Provider
     raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: #{detail}"
   end
 
-  # attribute_mapping class variable, 
+
+  # Valid attributes to be managed by this provider.
+  # It is a list of hashes
+  #  :aix_attr      AIX command attribute name
+  #  :puppet_prop   Puppet propertie name
+  #  :to            Optional. Method name that adapts puppet property to aix command value. 
+  #  :from          Optional. Method to adapt aix command line value to puppet property. Optional
   class << self 
     attr_accessor :attribute_mapping
   end
@@ -70,12 +69,12 @@ class Puppet::Provider::AixObject < Puppet::Provider
     end
     @attribute_mapping_from
   end
-
+  
   # This functions translates a key and value using the given mapping.
   # Mapping can be nil (no translation) or a hash with this format
   # {:key => new_key, :method => translate_method}
   # It returns a list [key, value]
-  def self.translate_attr(key, value, mapping)
+  def translate_attr(key, value, mapping)
     return [key, value] unless mapping
     return nil unless mapping[key]
     
@@ -86,18 +85,103 @@ class Puppet::Provider::AixObject < Puppet::Provider
     end
     [mapping[key][:key], new_value]
   end
+
+  # Gets the given command line argument for the given key, value and mapping.
+  def get_arg(key, value, mapping)
+    arg = nil
+    if ret = self.translate_attr(key, val, mapping)
+      new_key = ret[0]
+      new_val = ret[1]
+      
+      # Arrays are separated by commas
+      if new_val.is_a? Array
+        value = new_val.join(",")
+      else
+        value = new_val.to_s
+      end
+      
+      # Get the needed argument
+      if mapping[key][:to_arg]
+        arg = method(mapping[key][:to_arg]).call(new_key, value)
+      else
+        arg = (new_key.to_s + "=" + value )
+      end
+    end
+    return arg
+  end
+  
   
-  #-----
-  # Convert a pair key-value using the 
+  # Reads and attribute.
+  # Here we implement the default behaviour.
+  # Subclasses must reimplement this.
+  def load_attribute(key, value, mapping, objectinfo)
+    if mapping.nil?
+      objectinfo[key] = value
+    elsif mapping[key].nil?
+      # is not present in mapping, ignore it.
+      true
+    elsif mapping[key][:method].nil?
+      objectinfo[mapping[key][:key]] = value
+    elsif 
+      objectinfo[mapping[key][:key]] = method(mapping[key][:method]).call(value)
+    end
+    
+    return objectinfo
+  end
   
-  # Parse AIX command attributes (string) and return provider hash
+  def get_arguments(key, value, mapping, objectinfo)
+    if mapping.nil?
+      new_key = key
+      new_value = value
+    elsif mapping[key].nil?
+      # is not present in mapping, ignore it.
+      new_key = nil
+      new_value = nil
+    elsif mapping[key][:method].nil?
+      new_key = mapping[key][:key]
+      new_value = value
+    elsif 
+      new_key = mapping[key][:key]
+      new_value = method(mapping[key][:method]).call(value)
+    end
+
+    # convert it to string
+    if new_val.is_a? Array
+      new_val = new_val.join(",")
+    else
+      new_val = new_val.to_s
+    end
+
+    if new_key?
+      return [ "#{new_key}=#{new_value}" ] 
+    else
+      return []
+    end
+  end
+  
+  # Convert the provider properties to AIX command arguments (string)
+  # This function will translate each value/key and generate the argument.
+  # By default, arguments are created as aix_key=aix_value
+  def hash2args(hash, mapping=self.class.attribute_mapping_to)
+    return "" unless hash 
+    arg_list = []
+    hash.each {|key, val|
+      arg_list += self.get_arguments(key, val, mapping, hash)
+    }
+    arg_list
+  end
+
+  # Parse AIX command attributes in a format of space separated of key=value
+  # pairs: "uid=100 groups=a,b,c"
+  # It returns and return provider hash.
+  #
   # If a mapping is provided, the keys are translated as defined in the
   # mapping hash. Only values included in mapping will be added
   # NOTE: it will ignore the items not including '='
-  def self.attr2hash(str, mapping=attribute_mapping_from)
+  def parse_attr_list(str, mapping=self.class.attribute_mapping_from)
     properties = {}
     attrs = []
-    if !str or (attrs = str.split()[0..-1]).empty?
+    if !str or (attrs = str.split()).empty?
       return nil
     end 
 
@@ -109,40 +193,53 @@ class Puppet::Provider::AixObject < Puppet::Provider
           info "Empty key in string 'i'?"
           continue
         end
-        key = key_str.to_sym
+        key = key_str.downcase.to_sym
        
-        if ret = self.translate_attr(key, val, mapping)
-          new_key = ret[0]
-          new_val = ret[1]
-          
-          properties[new_key] = new_val
-        end
+        properties = self.load_attribute(key, val, mapping, properties)
       end
     }
     properties.empty? ? nil : properties
   end
 
-  # Convert the provider properties to AIX command attributes (string)
-  def self.hash2attr(hash, mapping=attribute_mapping_to)
-    return "" unless hash 
-    attr_list = []
-    hash.each {|key, val|
-      
-      if ret = self.translate_attr(key, val, mapping)
-        new_key = ret[0]
-        new_val = ret[1]
-        
-        # Arrays are separated by commas
-        if new_val.is_a? Array
-          value = new_val.join(",")
-        else
-          value = new_val.to_s
-        end
-        
-        attr_list << (new_key.to_s + "=" + value )
-      end
+  # Parse AIX colon separated list of attributes, using given list of keys
+  # to name the attributes. This function is useful to parse the output
+  # of commands like lsfs -c:
+  #   #MountPoint:Device:Vfs:Nodename:Type:Size:Options:AutoMount:Acct
+  #   /:/dev/hd4:jfs2::bootfs:557056:rw:yes:no
+  #   /home:/dev/hd1:jfs2:::2129920:rw:yes:no
+  #   /usr:/dev/hd2:jfs2::bootfs:9797632:rw:yes:no
+  #
+  # If a mapping is provided, the keys are translated as defined in the
+  # mapping hash. Only values included in mapping will be added
+  # NOTE: it will ignore the items not including '='
+  def parse_colon_list(str, key_list, mapping=self.class.attribute_mapping_from)
+    properties = {}
+    attrs = []
+    if !str or (attrs = str.split(':')).empty?
+      return nil
+    end 
+
+    attrs.each { |val|
+      key = key_list.shift.downcase.to_sym
+      properties = self.load_attribute(key, val, mapping, properties)
     }
-    attr_list
+    properties.empty? ? nil : properties
+    
+  end
+  
+  # Default parsing function for colon separated list or attributte list
+  # (key=val pairs). It will choose the method depending of the first line.
+  # For the colon separated list it will:
+  #  1. Get keys from first line.
+  #  2. Parse next line.
+  def parse_command_output(output)
+    lines = output.split("\n")
+    # if it begins with #something:... is a colon separated list.
+    if lines[0] =~ /^#.*:/ 
+      self.parse_colon_list(lines[1], lines[0][1..-1].split(':'))
+    else
+      self.parse_attr_list(lines[0])
+    end
   end
 
   # Retrieve what we can about our object
@@ -150,17 +247,33 @@ class Puppet::Provider::AixObject < Puppet::Provider
     if @objectinfo.nil? or refresh == true
       # Execute lsuser, split all attributes and add them to a dict.
       begin
-        attrs = execute(self.lscmd).split("\n")[0]
-        @objectinfo = self.class.attr2hash(attrs)
+        @objectinfo = self.parse_command_output(execute(self.lscmd))
       rescue Puppet::ExecutionFailure => detail
-        # Print error if needed
-        Puppet.debug "aix.getinfo(): Could not find #{@resource.class.name} #{@resource.name}: #{detail}" \
-          unless detail.to_s.include? "User \"#{@resource.name}\" does not exist."
+        # Print error if needed. FIXME: Do not check the user here.
+        Puppet.debug "aix.getinfo(): Could not find #{@resource.class.name} #{@resource.name}: #{detail}" 
       end
     end
     @objectinfo
   end
 
+  # List all elements of given type. It works for colon separated commands and
+  # list commands. 
+  def list_all
+    names = []
+    begin
+      output = execute(self.lsallcmd()).split('\n')
+      (output.select{ |l| l != /^#/ }).each { |v|
+        name = v.split(/[ :]/)
+        names << name if not name.empty?
+      }
+    rescue Puppet::ExecutionFailure => detail
+      # Print error if needed
+      Puppet.debug "aix.list_all(): Could not get all resources of type #{@resource.class.name}: #{detail}" 
+    end
+    names
+  end
+
+
   #-------------
   # Provider API
   # ------------
@@ -168,7 +281,7 @@ class Puppet::Provider::AixObject < Puppet::Provider
   # Clear out the cached values.
   def flush
     @property_hash.clear if @property_hash
-    @object_info.clear if @object_info
+    @objectinfo.clear if @objectinfo
   end
 
   # Check that the user exists
@@ -192,9 +305,9 @@ class Puppet::Provider::AixObject < Puppet::Provider
   # The method for returning a list of provider instances.  Note that it returns
   # providers, preferably with values already filled in, not resources.
   def self.instances
-    objects = []
-    execute(lscmd("ALL")).each { |entry|
-      objects << new(:name => entry.split(" ")[0], :ensure => :present)
+    objects=[]
+    self.list_all().each { |entry|
+      objects << new(:name => entry, :ensure => :present)
     }
     objects
   end
@@ -254,17 +367,23 @@ class Puppet::Provider::AixObject < Puppet::Provider
   # Set a property.
   def set(param, value)
     @property_hash[symbolize(param)] = value
-    # If value does not change, do not update.    
+    
+    if getinfo().nil?
+      # This is weird...
+      raise Puppet::Error, "Trying to update parameter '#{param}' to '#{value}' for a resource that does not exists #{@resource.class.name} #{@resource.name}: #{detail}"
+    end
     if value == getinfo()[param.to_sym]
       return
     end
     
     #self.class.validate(param, value)
-    cmd = modifycmd({param => value})
-    begin
-      execute(cmd)
-    rescue Puppet::ExecutionFailure  => detail
-      raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}"
+    
+    if cmd = modifycmd({param =>value})
+      begin
+        execute(cmd)
+      rescue Puppet::ExecutionFailure  => detail
+        raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}"
+      end
     end
     
     # Refresh de info.  
@@ -279,4 +398,3 @@ class Puppet::Provider::AixObject < Puppet::Provider
   end  
 
 end
-#end
diff --git a/lib/puppet/provider/group/aix.rb b/lib/puppet/provider/group/aix.rb
index bcb81e1..0c3122b 100755
--- a/lib/puppet/provider/group/aix.rb
+++ b/lib/puppet/provider/group/aix.rb
@@ -69,7 +69,7 @@ Puppet::Type.type(:group).provide :aix, :parent => Puppet::Provider::AixObject d
   end
 
   # Force convert users it a list.
-  def self.users_from_attr(value)
+  def users_from_attr(value)
     (value.is_a? String) ? value.split(',') : value
   end
 
diff --git a/lib/puppet/provider/user/aix.rb b/lib/puppet/provider/user/aix.rb
index ba95dbc..4a5c4ee 100755
--- a/lib/puppet/provider/user/aix.rb
+++ b/lib/puppet/provider/user/aix.rb
@@ -26,23 +26,24 @@ Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do
   # Constants
   # Default extra attributes to add when element is created
   # registry=compat SYSTEM=compat: Needed if you are using LDAP by default.
-  @DEFAULT_EXTRA_ATTRS = [ "registry=compat", " SYSTEM=compat" ]
+  @DEFAULT_EXTRA_ATTRS = [ "registry=compat", "SYSTEM=compat" ]
 
   # This will the the default provider for this platform
   defaultfor :operatingsystem => :aix
   confine :operatingsystem => :aix
 
   # Commands that manage the element
-  commands :lsgroup    => "/usr/sbin/lsgroup"
-  
   commands :list      => "/usr/sbin/lsuser"
   commands :add       => "/usr/bin/mkuser"
   commands :delete    => "/usr/sbin/rmuser"
   commands :modify    => "/usr/bin/chuser"
+  
+  commands :lsgroup   => "/usr/sbin/lsgroup"
   commands :chpasswd  => "/bin/chpasswd"
 
   # Provider features
-  has_features :manages_homedir, :manages_passwords, :manages_expiry, :manages_password_age
+  has_features :manages_homedir, :manages_passwords
+  has_features :manages_expiry,  :manages_password_age
 
   # Attribute verification (TODO)
   #verify :gid, "GID must be an string or int of a valid group" do |value|
@@ -78,14 +79,18 @@ Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do
   #--------------
   # Command lines
   
-  def self.lsgroupscmd(value=@resource[:name])
-    [command(:lsgroup),"-R", ia_module, "-a", "id", value]
+  def lsgroupscmd(value=@resource[:name])
+    [command(:lsgroup),"-R", self.class.ia_module, "-a", "id", value]
   end
 
   def lscmd(value=@resource[:name])
     [self.class.command(:list), "-R", self.class.ia_module , value]
   end
 
+  def lsallcmd()
+    lscmd("ALL")
+  end
+
   def addcmd(extra_attrs = [])
     # Here we use the @resource.to_hash to get the list of provided parameters
     # Puppet does not call to self.<parameter>= method if it does not exists.
@@ -118,7 +123,7 @@ Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do
   def self.groupname_by_id(gid)
     groupname=nil
     execute(lsgroupscmd("ALL")).each { |entry|
-      attrs = attr2hash(entry, nil)
+      attrs = parse_attr_list(entry, nil)
       if attrs and attrs.include? :id and gid == attrs[:id].to_i
         groupname = entry.split(" ")[0]
       end
@@ -127,13 +132,13 @@ Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do
   end
 
   # Get the groupname from its id
-  def self.groupid_by_name(groupname)
-    attrs = attr2hash(execute(lsgroupscmd(groupname)).split("\n")[0], nil)
+  def groupid_by_name(groupname)
+    attrs = parse_attr_list(execute(lsgroupscmd(groupname)).split("\n")[0], nil)
     attrs ? attrs[:id].to_i : nil
   end
 
   # Check that a group exists and is valid
-  def self.verify_group(value)
+  def verify_group(value)
     if value.is_a? Integer or value.is_a? Fixnum  
       groupname = self.groupname_by_id(value)
       raise ArgumentError, "AIX group must be a valid existing group" unless groupname
@@ -145,17 +150,17 @@ Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do
   end
   
   # The user's primary group.  Can be specified numerically or by name.
-  def self.gid_to_attr(value)
+  def gid_to_attr(value)
     verify_group(value)
   end
 
-  def self.gid_from_attr(value)
+  def gid_from_attr(value)
     groupid_by_name(value)
   end
 
   # The expiry date for this user. Must be provided in
   # a zero padded YYYY-MM-DD HH:MM format 
-  def self.expiry_to_attr(value)
+  def expiry_to_attr(value)
     # For chuser the expires parameter is a 10-character string in the MMDDhhmmyy format
     # that is,"%m%d%H%M%y"
     newdate = '0'
@@ -166,7 +171,7 @@ Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do
     newdate
   end
   
-  def self.expiry_from_attr(value)
+  def expiry_from_attr(value)
     if value =~ /(..)(..)(..)(..)(..)/
       #d= DateTime.parse("20#{$5}-#{$1}-#{$2} #{$3}:#{$4}")
       #expiry_date = d.strftime("%Y-%m-%d %H:%M")

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list