[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 9032898eb32080bfae66a707d55976e9f984a512
Author: Hector Rivas Gandara <keymon at gmail.com>
Date: Wed Dec 1 13:47:57 2010 +0100
(#5432) Use AIX native commands to manage users and groups
Specific providers should be created for AIX to manage users and groups.
AIX bases the authentication management on a set of commands: mkuser, rmuser, chuser, lsuser, mkgroup, rmgroup, chgroup, lsgroup, etc.
This commit implements such providers.
Notes::
- AIX users can have expiry date defined with minute granularity,
but puppet does not allow it. There is a ticket open for that (#5431)
- AIX maximum password age is in WEEKs, not days.
- I force the compat IA module.
TODO::
- Add new AIX specific attributes, specilly registry and SYSTEM.
diff --git a/lib/puppet/provider/aixobject.rb b/lib/puppet/provider/aixobject.rb
new file mode 100755
index 0000000..98ac13b
--- /dev/null
+++ b/lib/puppet/provider/aixobject.rb
@@ -0,0 +1,282 @@
+#
+# Common code for AIX providers
+#
+# Author:: Hector Rivas Gandara <keymon at gmail.com>
+#
+#
+class Puppet::Provider::AixObject < Puppet::Provider
+ desc "User management for AIX! Users are managed with mkuser, rmuser, chuser, lsuser"
+
+ # Constants
+ # Loadable AIX I/A module for users and groups. By default we manage compat.
+ # 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
+
+ # Provider must implement these functions.
+ def lscmd(value=@resource[:name])
+ raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: #{detail}"
+ end
+
+ def addcmd(extra_attrs = [])
+ raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: #{detail}"
+ end
+
+ def modifycmd(attributes_hash)
+ raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: #{detail}"
+ end
+
+ def deletecmd
+ raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: #{detail}"
+ end
+
+ # attribute_mapping class variable,
+ class << self
+ attr_accessor :attribute_mapping
+ end
+ def self.attribute_mapping_to
+ if ! @attribute_mapping_to
+ @attribute_mapping_to = {}
+ attribute_mapping.each { |elem|
+ attribute_mapping_to[elem[:puppet_prop]] = {
+ :key => elem[:aix_attr],
+ :method => elem[:to]
+ }
+ }
+ end
+ @attribute_mapping_to
+ end
+ def self.attribute_mapping_from
+ if ! @attribute_mapping_from
+ @attribute_mapping_from = {}
+ attribute_mapping.each { |elem|
+ attribute_mapping_from[elem[:aix_attr]] = {
+ :key => elem[:puppet_prop],
+ :method => elem[:from]
+ }
+ }
+ 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)
+ return [key, value] unless mapping
+ return nil unless mapping[key]
+
+ if mapping[key][:method]
+ new_value = method(mapping[key][:method]).call(value)
+ else
+ new_value = value
+ end
+ [mapping[key][:key], new_value]
+ end
+
+ #-----
+ # Convert a pair key-value using the
+
+ # Parse AIX command attributes (string) 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)
+ properties = {}
+ attrs = []
+ if !str or (attrs = str.split()[0..-1]).empty?
+ return nil
+ end
+
+ attrs.each { |i|
+ if i.include? "=" # Ignore if it does not include '='
+ (key_str, val) = i.split('=')
+ # Check the key
+ if !key_str or key_str.empty?
+ info "Empty key in string 'i'?"
+ continue
+ end
+ key = key_str.to_sym
+
+ if ret = self.translate_attr(key, val, mapping)
+ new_key = ret[0]
+ new_val = ret[1]
+
+ properties[new_key] = new_val
+ end
+ 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
+ }
+ attr_list
+ end
+
+ # Retrieve what we can about our object
+ def getinfo(refresh = false)
+ 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)
+ 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."
+ end
+ end
+ @objectinfo
+ end
+
+ #-------------
+ # Provider API
+ # ------------
+
+ # Clear out the cached values.
+ def flush
+ @property_hash.clear if @property_hash
+ @object_info.clear if @object_info
+ end
+
+ # Check that the user exists
+ def exists?
+ !!getinfo(true) # !! => converts to bool
+ end
+
+ #- **ensure**
+ # The basic state that the object should be in. Valid values are
+ # `present`, `absent`, `role`.
+ # From ensurable: exists?, create, delete
+ def ensure
+ if exists?
+ :present
+ else
+ :absent
+ end
+ end
+
+ # Return all existing instances
+ # 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
+ end
+
+ def create
+ if exists?
+ info "already exists"
+ # The object already exists
+ return nil
+ end
+
+ begin
+ execute(self.addcmd)
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error, "Could not create #{@resource.class.name} #{@resource.name}: #{detail}"
+ end
+ end
+
+ def delete
+ unless exists?
+ info "already absent"
+ # the object already doesn't exist
+ return nil
+ end
+
+ begin
+ execute(self.deletecmd)
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error, "Could not delete #{@resource.class.name} #{@resource.name}: #{detail}"
+ end
+ end
+
+ #--------------------------------
+ # Call this method when the object is initialized,
+ # create getter/setter methods for each property our resource type supports.
+ # If setter or getter already defined it will not be overwritten
+ def self.mk_resource_methods
+ [resource_type.validproperties, resource_type.parameters].flatten.each do |prop|
+ next if prop == :ensure
+ define_method(prop) { get(prop) || :absent} unless public_method_defined?(prop)
+ define_method(prop.to_s + "=") { |*vals| set(prop, *vals) } unless public_method_defined?(prop.to_s + "=")
+ end
+ end
+ #
+
+ # Define the needed getters and setters as soon as we know the resource type
+ def self.resource_type=(resource_type)
+ super
+ mk_resource_methods
+ end
+
+ # Retrieve a specific value by name.
+ def get(param)
+ (hash = getinfo(false)) ? hash[param] : nil
+ end
+
+ # Set a property.
+ def set(param, value)
+ @property_hash[symbolize(param)] = value
+ # If value does not change, do not update.
+ 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}"
+ end
+
+ # Refresh de info.
+ hash = getinfo(true)
+ end
+
+ def initialize(resource)
+ super
+ @objectinfo = nil
+ # FIXME: Initiallize this properly.
+ self.class.ia_module="compat"
+ end
+
+end
+#end
diff --git a/lib/puppet/provider/group/aix.rb b/lib/puppet/provider/group/aix.rb
new file mode 100755
index 0000000..bcb81e1
--- /dev/null
+++ b/lib/puppet/provider/group/aix.rb
@@ -0,0 +1,76 @@
+#
+# Group Puppet provider for AIX. It uses standard commands to manage groups:
+# mkgroup, rmgroup, lsgroup, chgroup
+#
+# Author:: Hector Rivas Gandara <keymon at gmail.com>
+#
+require 'puppet/provider/aixobject'
+
+Puppet::Type.type(:group).provide :aix, :parent => Puppet::Provider::AixObject do
+ desc "Group management for AIX! Users are managed with mkgroup, rmgroup, lsgroup, chgroup"
+
+ # Constants
+ # Default extra attributes to add when element is created
+ # registry=compat: Needed if you are using LDAP by default.
+ @DEFAULT_EXTRA_ATTRS = [ "registry=compat", ]
+
+
+ # This will the the default provider for this platform
+ defaultfor :operatingsystem => :aix
+ confine :operatingsystem => :aix
+
+ # Provider features
+ has_features :manages_members
+
+ # Commands that manage the element
+ commands :list => "/usr/sbin/lsgroup"
+ commands :add => "/usr/bin/mkgroup"
+ commands :delete => "/usr/sbin/rmgroup"
+ commands :modify => "/usr/bin/chgroup"
+
+ # AIX attributes to properties mapping.
+ #
+ # Valid attributes to be managed by this provider.
+ # 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
+ self.attribute_mapping = [
+ #:name => :name,
+ {:aix_attr => :id, :puppet_prop => :gid },
+ {:aix_attr => :users, :puppet_prop => :members,
+ :from => :users_from_attr},
+ ]
+
+ #--------------
+ # Command lines
+ def lscmd(value=@resource[:name])
+ [self.class.command(:list), "-R", self.class.ia_module , value]
+ 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.
+ #
+ # It gets an extra list of arguments to add to the user.
+ [self.class.command(:add), "-R", self.class.ia_module ]+
+ self.class.hash2attr(@resource.to_hash) +
+ extra_attrs + [@resource[:name]]
+ end
+
+ def modifycmd(hash = property_hash)
+ [self.class.command(:modify), "-R", self.class.ia_module ]+
+ self.class.hash2attr(hash) + [@resource[:name]]
+ end
+
+ def deletecmd
+ [self.class.command(:delete),"-R", self.class.ia_module, @resource[:name]]
+ end
+
+ # Force convert users it a list.
+ def self.users_from_attr(value)
+ (value.is_a? String) ? value.split(',') : value
+ end
+
+end
diff --git a/lib/puppet/provider/user/aix.rb b/lib/puppet/provider/user/aix.rb
new file mode 100755
index 0000000..ba95dbc
--- /dev/null
+++ b/lib/puppet/provider/user/aix.rb
@@ -0,0 +1,294 @@
+#
+# User Puppet provider for AIX. It uses standar commands to manage users:
+# mkuser, rmuser, lsuser, chuser
+#
+# Notes:
+# - AIX users can have expiry date defined with minute granularity,
+# but puppet does not allow it. There is a ticket open for that (#5431)
+# - AIX maximum password age is in WEEKs, not days
+# - I force the compat IA module.
+#
+# See http://projects.puppetlabs.com/projects/puppet/wiki/Development_Provider_Development
+# for more information
+#
+# Author:: Hector Rivas Gandara <keymon at gmail.com>
+#
+# TODO::
+# - Add new AIX specific attributes, specilly registry and SYSTEM.
+#
+require 'puppet/provider/aixobject'
+require 'tempfile'
+require 'date'
+
+Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do
+ desc "User management for AIX! Users are managed with mkuser, rmuser, chuser, lsuser"
+
+ # 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" ]
+
+ # 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 :chpasswd => "/bin/chpasswd"
+
+ # Provider features
+ has_features :manages_homedir, :manages_passwords, :manages_expiry, :manages_password_age
+
+ # Attribute verification (TODO)
+ #verify :gid, "GID must be an string or int of a valid group" do |value|
+ # value.is_a? String || value.is_a? Integer
+ #end
+ #
+ #verify :groups, "Groups must be comma-separated" do |value|
+ # value !~ /\s/
+ #end
+
+ # AIX attributes to properties mapping.
+ #
+ # Valid attributes to be managed by this provider.
+ # 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
+ self.attribute_mapping = [
+ #:name => :name,
+ {:aix_attr => :pgrp, :puppet_prop => :gid,
+ :to => :gid_to_attr, :from => :gid_from_attr},
+ {:aix_attr => :id, :puppet_prop => :uid},
+ {:aix_attr => :groups, :puppet_prop => :groups},
+ {:aix_attr => :home, :puppet_prop => :home},
+ {:aix_attr => :shell, :puppet_prop => :shell},
+ {:aix_attr => :expires, :puppet_prop => :expiry,
+ :to => :expiry_to_attr, :from => :expiry_from_attr},
+ {:aix_attr => :maxage, :puppet_prop => :password_max_age},
+ {:aix_attr => :minage, :puppet_prop => :password_min_age},
+ ]
+
+ #--------------
+ # Command lines
+
+ def self.lsgroupscmd(value=@resource[:name])
+ [command(:lsgroup),"-R", ia_module, "-a", "id", value]
+ end
+
+ def lscmd(value=@resource[:name])
+ [self.class.command(:list), "-R", self.class.ia_module , value]
+ 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.
+ #
+ # It gets an extra list of arguments to add to the user.
+ [self.class.command(:add), "-R", self.class.ia_module ]+
+ self.class.hash2attr(@resource.to_hash) +
+ extra_attrs + [@resource[:name]]
+ end
+
+ def modifycmd(hash = property_hash)
+ [self.class.command(:modify), "-R", self.class.ia_module ]+
+ self.class.hash2attr(hash) + [@resource[:name]]
+ end
+
+ def deletecmd
+ [self.class.command(:delete),"-R", self.class.ia_module, @resource[:name]]
+ end
+
+ #--------------
+ # We overwrite the create function to change the password after creation.
+ def create
+ super
+ # Reset the password if needed
+ self.password = @resource[:password] if @resource[:password]
+ end
+
+
+ # Get the groupname from its id
+ def self.groupname_by_id(gid)
+ groupname=nil
+ execute(lsgroupscmd("ALL")).each { |entry|
+ attrs = attr2hash(entry, nil)
+ if attrs and attrs.include? :id and gid == attrs[:id].to_i
+ groupname = entry.split(" ")[0]
+ end
+ }
+ groupname
+ end
+
+ # Get the groupname from its id
+ def self.groupid_by_name(groupname)
+ attrs = attr2hash(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)
+ 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
+ else
+ raise ArgumentError, "AIX group must be a valid existing group" unless groupid_by_name(value)
+ groupname = value
+ end
+ groupname
+ end
+
+ # The user's primary group. Can be specified numerically or by name.
+ def self.gid_to_attr(value)
+ verify_group(value)
+ end
+
+ def self.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)
+ # For chuser the expires parameter is a 10-character string in the MMDDhhmmyy format
+ # that is,"%m%d%H%M%y"
+ newdate = '0'
+ if value.is_a? String and value!="0000-00-00"
+ d = DateTime.parse(value, "%Y-%m-%d %H:%M")
+ newdate = d.strftime("%m%d%H%M%y")
+ end
+ newdate
+ end
+
+ def self.expiry_from_attr(value)
+ if value =~ /(..)(..)(..)(..)(..)/
+ #d= DateTime.parse("20#{$5}-#{$1}-#{$2} #{$3}:#{$4}")
+ #expiry_date = d.strftime("%Y-%m-%d %H:%M")
+ #expiry_date = d.strftime("%Y-%m-%d")
+ expiry_date = "20#{$5}-#{$1}-#{$2}"
+ else
+ Puppet.warn("Could not convert AIX expires date '#{value}' on #{@resource.class.name}[#{@resource.name}]") \
+ unless value == '0'
+ expiry_date = :absent
+ end
+ expiry_date
+ end
+
+ #--------------------------------
+ # Getter and Setter
+ # When the provider is initialized, create getter/setter methods for each
+ # property our resource type supports.
+ # If setter or getter already defined it will not be overwritten
+
+ #- **password**
+ # The user's password, in whatever encrypted format the local machine
+ # requires. Be sure to enclose any value that includes a dollar sign ($)
+ # in single quotes ('). Requires features manages_passwords.
+ #
+ # Retrieve the password parsing directly the /etc/security/passwd
+ def password
+ password = :absent
+ user = @resource[:name]
+ f = File.open("/etc/security/passwd", 'r')
+ # Skip to the user
+ f.each { |l| break if l =~ /^#{user}:\s*$/ }
+ if ! f.eof?
+ f.each { |l|
+ # If there is a new user stanza, stop
+ break if l =~ /^\S*:\s*$/
+ # If the password= entry is found, return it
+ if l =~ /^\s*password\s*=\s*(.*)$/
+ password = $1; break;
+ end
+ }
+ end
+ f.close()
+ return password
+ end
+
+ def password=(value)
+ user = @resource[:name]
+
+ # Puppet execute does not support strings as input, only files.
+ tmpfile = Tempfile.new('puppet_#{user}_pw')
+ tmpfile << "#{user}:#{value}\n"
+ tmpfile.close()
+
+ # Options '-e', '-c', use encrypted password and clear flags
+ # Must receibe "user:enc_password" as input
+ # command, arguments = {:failonfail => true, :combine => true}
+ cmd = [self.class.command(:chpasswd),"-R", ia_module,
+ '-e', '-c', user]
+ begin
+ execute(cmd, {:failonfail => true, :combine => true, :stdinfile => tmpfile.path })
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}"
+ ensure
+ tmpfile.delete()
+ end
+ end
+
+ #- **comment**
+ # A description of the user. Generally is a user's full name.
+ #def comment=(value)
+ #end
+ #
+ #def comment
+ #end
+ # UNSUPPORTED
+ #- **profile_membership**
+ # Whether specified roles should be treated as the only roles
+ # of which the user is a member or whether they should merely
+ # be treated as the minimum membership list. Valid values are
+ # `inclusive`, `minimum`.
+ # UNSUPPORTED
+ #- **profiles**
+ # The profiles the user has. Multiple profiles should be
+ # specified as an array. Requires features manages_solaris_rbac.
+ # UNSUPPORTED
+ #- **project**
+ # The name of the project associated with a user Requires features
+ # manages_solaris_rbac.
+ # UNSUPPORTED
+ #- **role_membership**
+ # Whether specified roles should be treated as the only roles
+ # of which the user is a member or whether they should merely
+ # be treated as the minimum membership list. Valid values are
+ # `inclusive`, `minimum`.
+ # UNSUPPORTED
+ #- **roles**
+ # The roles the user has. Multiple roles should be
+ # specified as an array. Requires features manages_solaris_rbac.
+ # UNSUPPORTED
+ #- **key_membership**
+ # Whether specified key value pairs should be treated as the only
+ # attributes
+ # of the user or whether they should merely
+ # be treated as the minimum list. Valid values are `inclusive`,
+ # `minimum`.
+ # UNSUPPORTED
+ #- **keys**
+ # Specify user attributes in an array of keyvalue pairs Requires features
+ # manages_solaris_rbac.
+ # UNSUPPORTED
+ #- **allowdupe**
+ # Whether to allow duplicate UIDs. Valid values are `true`, `false`.
+ # UNSUPPORTED
+ #- **auths**
+ # The auths the user has. Multiple auths should be
+ # specified as an array. Requires features manages_solaris_rbac.
+ # UNSUPPORTED
+ #- **auth_membership**
+ # Whether specified auths should be treated as the only auths
+ # of which the user is a member or whether they should merely
+ # be treated as the minimum membership list. Valid values are
+ # `inclusive`, `minimum`.
+ # UNSUPPORTED
+
+end
--
Puppet packaging for Debian
More information about the Pkg-puppet-devel
mailing list