From: Hector Rivas Gandara <[email protected]>

Refactorized the aixobject.rb to allow new providers using commands with colon 
separated output.

Signed-off-by: Hector Rivas Gandara <[email protected]>
---
Local-branch: feature/master/5432
 lib/puppet/provider/aixobject.rb |  244 ++++++++++++++++++++++++++++----------
 lib/puppet/provider/group/aix.rb |    2 +-
 lib/puppet/provider/user/aix.rb  |   33 +++--
 3 files changed, 201 insertions(+), 78 deletions(-)

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(val...@resource[:name])
+    raise Puppet::Error, "Method not defined #[email protected]} 
#[email protected]}: #{detail}"
   end

-  # Provider must implement these functions.
   def lscmd(val...@resource[:name])
     raise Puppet::Error, "Method not defined #[email protected]} 
#[email protected]}: #{detail}"
   end
@@ -42,7 +35,13 @@ class Puppet::Provider::AixObject < Puppet::Provider
     raise Puppet::Error, "Method not defined #[email protected]} 
#[email protected]}: #{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 #[email protected]} 
#[email protected]}: #{detail}" \
-          unless detail.to_s.include? "User \"#[email protected]}\" does not 
exist."
+        # Print error if needed. FIXME: Do not check the user here.
+        Puppet.debug "aix.getinfo(): Could not find #[email protected]} 
#[email protected]}: #{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 
#[email protected]}: #{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 #[email protected]} 
#[email protected]}: #{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 
#[email protected]}[#{@resource.name}]: #{detail}"
+
+    if cmd = modifycmd({param =>value})
+      begin
+        execute(cmd)
+      rescue Puppet::ExecutionFailure  => detail
+        raise Puppet::Error, "Could not set #{param} on 
#[email protected]}[#{@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(val...@resource[:name])

-    [command(:lsgroup),"-R", ia_module, "-a", "id", value]

+  def lsgroupscmd(val...@resource[:name])

+    [command(:lsgroup),"-R", self.class.ia_module, "-a", "id", value]

   end



   def lscmd(val...@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")

--
1.7.1




______________________________________________________________________
This email has been scanned by the MessageLabs Email Security System.
For more information please visit http://www.messagelabs.com/email 
______________________________________________________________________

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to