summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorJesse Wolfe <jes5199@gmail.com>2011-03-29 14:38:17 -0700
committerJesse Wolfe <jes5199@gmail.com>2011-03-29 14:38:17 -0700
commit1a7f0dc6c4bfe4ce716ddfd918be181c8d682229 (patch)
treee8d9471db41ce820c29e5f2c49693b0fd1166e09 /lib
parent54f15b6eae233c291ef14c4224ee1280aa09761e (diff)
parentfb339cbbc17cd14ba34df624fe9a2b9c74640eb5 (diff)
downloadpuppet-1a7f0dc6c4bfe4ce716ddfd918be181c8d682229.tar.gz
puppet-1a7f0dc6c4bfe4ce716ddfd918be181c8d682229.tar.xz
puppet-1a7f0dc6c4bfe4ce716ddfd918be181c8d682229.zip
Merge branch 'feature/master/5432' of https://github.com/keymon/puppet into next
Diffstat (limited to 'lib')
-rwxr-xr-xlib/puppet/provider/aixobject.rb393
-rwxr-xr-xlib/puppet/provider/group/aix.rb141
-rwxr-xr-xlib/puppet/provider/user/aix.rb353
-rwxr-xr-xlib/puppet/type/group.rb36
-rwxr-xr-xlib/puppet/type/user.rb39
5 files changed, 961 insertions, 1 deletions
diff --git a/lib/puppet/provider/aixobject.rb b/lib/puppet/provider/aixobject.rb
new file mode 100755
index 000000000..9506c67a2
--- /dev/null
+++ b/lib/puppet/provider/aixobject.rb
@@ -0,0 +1,393 @@
+#
+# Common code for AIX providers. This class implements basic structure for
+# AIX resources.
+# Author:: Hector Rivas Gandara <keymon@gmail.com>
+#
+class Puppet::Provider::AixObject < Puppet::Provider
+ desc "Generic AIX resource provider"
+
+ # 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
+
+ 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
+
+ # 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
+
+ # Mapping from Puppet property to AIX attribute.
+ 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
+
+ # Mapping from AIX attribute to Puppet property.
+ 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 with the pair [key, value]
+ def 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
+
+ # Loads an AIX attribute (key=value) and stores it in the given hash with
+ # puppet semantics. It translates the pair using the given mapping.
+ #
+ # This operation works with each property one by one,
+ # subclasses must reimplement this if more complex operations are needed
+ 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
+
+ # Gets the given command line argument for the given key and value,
+ # using the given mapping to translate key and value.
+ # All the objectinfo hash (@resource or @property_hash) is passed.
+ #
+ # This operation works with each property one by one,
+ # and default behaviour is return the arguments as key=value pairs.
+ # Subclasses must reimplement this if more complex operations/arguments
+ # are needed
+ #
+ 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
+ new_value = Array(new_value).join(',')
+
+ if new_key
+ return [ "#{new_key}=#{new_value}" ]
+ else
+ return []
+ end
+ end
+
+ # Convert the provider properties (hash) to AIX command arguments
+ # (list of strings)
+ # This function will translate each value/key and generate the argument using
+ # the get_arguments function.
+ 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 from the output of an AIX command, that
+ # which format is a list of space separated of key=value pairs:
+ # "uid=100 groups=a,b,c".
+ # It returns an hash.
+ #
+ # If a mapping is provided, the keys are translated as defined in the
+ # mapping hash. And only values included in mapping will be added
+ #
+ # NOTE: it will ignore the items not including '='
+ def parse_attr_list(str, mapping=self.class.attribute_mapping_from)
+ properties = {}
+ attrs = []
+ if !str or (attrs = str.split()).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
+
+ properties = self.load_attribute(key, val, mapping, properties)
+ end
+ }
+ properties.empty? ? nil : properties
+ end
+
+ # Parse AIX command output in a colon separated list of 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. And only values included in mapping will be added
+ 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)
+ }
+ properties.empty? ? nil : properties
+
+ end
+
+ # Default parsing function for AIX commands.
+ # 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, mapping=self.class.attribute_mapping_from)
+ 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(':'), mapping)
+ else
+ self.parse_attr_list(lines[0], mapping)
+ end
+ end
+
+ # Retrieve all the information of an existing resource.
+ # It will execute 'lscmd' command and parse the output, using the mapping
+ # 'attribute_mapping_from' to translate the keys and values.
+ def getinfo(refresh = false)
+ if @objectinfo.nil? or refresh == true
+ # Execute lsuser, split all attributes and add them to a dict.
+ begin
+ output = execute(self.lscmd)
+ @objectinfo = self.parse_command_output(execute(self.lscmd))
+ # All attributtes without translation
+ @objectosinfo = self.parse_command_output(execute(self.lscmd), nil)
+ rescue Puppet::ExecutionFailure => detail
+ # 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
+
+ # Like getinfo, but it will not use the mapping to translate the keys and values.
+ # It might be usefult to retrieve some raw information.
+ def getosinfo(refresh = false)
+ if @objectosinfo .nil? or refresh == true
+ getinfo(refresh)
+ end
+ @objectosinfo
+ end
+
+
+ # List all elements of given type. It works for colon separated commands and
+ # list commands.
+ # It returns a list of names.
+ 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
+ # ------------
+
+ # Clear out the cached values.
+ def flush
+ @property_hash.clear if @property_hash
+ @objectinfo.clear if @objectinfo
+ end
+
+ # Check that the user exists
+ def exists?
+ !!getinfo(true) # !! => converts to bool
+ 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=[]
+ self.list_all().each { |entry|
+ objects << new(:name => entry, :ensure => :present)
+ }
+ objects
+ 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
+
+ # Create a new instance of the resource
+ 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
+
+ # Delete this instance of the resource
+ 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.
+ # It creates 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 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)
+ 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.
+ hash = getinfo(true)
+ end
+
+ def initialize(resource)
+ super
+ @objectinfo = nil
+ @objectosinfo = nil
+ end
+
+end
diff --git a/lib/puppet/provider/group/aix.rb b/lib/puppet/provider/group/aix.rb
new file mode 100755
index 000000000..ecdef6070
--- /dev/null
+++ b/lib/puppet/provider/group/aix.rb
@@ -0,0 +1,141 @@
+#
+# Group Puppet provider for AIX. It uses standard commands to manage groups:
+# mkgroup, rmgroup, lsgroup, chgroup
+#
+# Author:: Hector Rivas Gandara <keymon@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"
+
+ # This will the the default provider for this platform
+ defaultfor :operatingsystem => :aix
+ confine :operatingsystem => :aix
+
+ # Provider features
+ has_features :manages_aix_lam
+ 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"
+
+ # Group attributes to ignore
+ def self.attribute_ignore
+ []
+ 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 => :id, :puppet_prop => :gid },
+ {:aix_attr => :users, :puppet_prop => :members,
+ :from => :users_from_attr},
+ {:aix_attr => :attributes, :puppet_prop => :attributes},
+ ]
+
+ #--------------
+ # Command definition
+
+ # Return the IA module arguments based on the resource param ia_load_module
+ def get_ia_module_args
+ if @resource[:ia_load_module]
+ ["-R", @resource[:ia_load_module].to_s]
+ else
+ []
+ end
+ end
+
+ def lscmd(value=@resource[:name])
+ [self.class.command(:list)] +
+ self.get_ia_module_args +
+ [ 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.
+ #
+ # It gets an extra list of arguments to add to the user.
+ [self.class.command(:add) ] +
+ self.get_ia_module_args +
+ self.hash2args(@resource.to_hash) +
+ extra_attrs + [@resource[:name]]
+ end
+
+ def modifycmd(hash = property_hash)
+ args = self.hash2args(hash)
+ return nil if args.empty?
+
+ [self.class.command(:modify)] +
+ self.get_ia_module_args +
+ args + [@resource[:name]]
+ end
+
+ def deletecmd
+ [self.class.command(:delete)] +
+ self.get_ia_module_args +
+ [@resource[:name]]
+ end
+
+
+ #--------------
+ # Overwrite get_arguments to add the attributes arguments
+ def get_arguments(key, value, mapping, objectinfo)
+ # In the case of attributes, return a list of key=vlaue
+ if key == :attributes
+ raise Puppet::Error, "Attributes must be a list of pairs key=value on #{@resource.class.name}[#{@resource.name}]" \
+ unless value and value.is_a? Hash
+ return value.select { |k,v| true }.map { |pair| pair.join("=") }
+ end
+ super(key, value, mapping, objectinfo)
+ end
+
+ def filter_attributes(hash)
+ # Return only not managed attributtes.
+ hash.select {
+ |k,v| !self.class.attribute_mapping_from.include?(k) and
+ !self.class.attribute_ignore.include?(k)
+ }.inject({}) {
+ |hash, array| hash[array[0]] = array[1]; hash
+ }
+ end
+
+ def attributes
+ filter_attributes(getosinfo(refresh = false))
+ end
+
+ def attributes=(attr_hash)
+ #self.class.validate(param, value)
+ param = :attributes
+ cmd = modifycmd({param => filter_attributes(attr_hash)})
+ if cmd
+ begin
+ execute(cmd)
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}"
+ end
+ end
+ end
+
+ # Force convert users it a list.
+ def 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 000000000..032d2b536
--- /dev/null
+++ b/lib/puppet/provider/user/aix.rb
@@ -0,0 +1,353 @@
+#
+# User Puppet provider for AIX. It uses standard 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
+#
+# See http://projects.puppetlabs.com/projects/puppet/wiki/Development_Provider_Development
+# for more information
+#
+# Author:: Hector Rivas Gandara <keymon@gmail.com>
+#
+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"
+
+ # This will the the default provider for this platform
+ defaultfor :operatingsystem => :aix
+ confine :operatingsystem => :aix
+
+ # Commands that manage the element
+ 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_aix_lam
+ 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|
+ # value.is_a? String || value.is_a? Integer
+ #end
+ #
+ #verify :groups, "Groups must be comma-separated" do |value|
+ # value !~ /\s/
+ #end
+
+ # User attributes to ignore from AIX output.
+ def self.attribute_ignore
+ []
+ 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},
+ {:aix_attr => :attributes, :puppet_prop => :attributes},
+ ]
+
+ #--------------
+ # Command definition
+
+ # Return the IA module arguments based on the resource param ia_load_module
+ def get_ia_module_args
+ if @resource[:ia_load_module]
+ ["-R", @resource[:ia_load_module].to_s]
+ else
+ []
+ end
+ end
+
+ # List groups and Ids
+ def lsgroupscmd(value=@resource[:name])
+ [command(:lsgroup)] +
+ self.get_ia_module_args +
+ ["-a", "id", value]
+ end
+
+ def lscmd(value=@resource[:name])
+ [self.class.command(:list)] + self.get_ia_module_args + [ 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.
+ #
+ # It gets an extra list of arguments to add to the user.
+ [self.class.command(:add)] + self.get_ia_module_args +
+ self.hash2args(@resource.to_hash) +
+ extra_attrs + [@resource[:name]]
+ end
+
+ # Get modify command. Set translate=false if no mapping must be used.
+ # Needed for special properties like "attributes"
+ def modifycmd(hash = property_hash)
+ args = self.hash2args(hash)
+ return nil if args.empty?
+
+ [self.class.command(:modify)] + self.get_ia_module_args +
+ args + [@resource[:name]]
+ end
+
+ def deletecmd
+ [self.class.command(:delete)] + self.get_ia_module_args + [@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
+
+
+ def get_arguments(key, value, mapping, objectinfo)
+ # In the case of attributes, return a list of key=vlaue
+ if key == :attributes
+ raise Puppet::Error, "Attributes must be a list of pairs key=value on #{@resource.class.name}[#{@resource.name}]" \
+ unless value and value.is_a? Hash
+ return value.select { |k,v| true }.map { |pair| pair.join("=") }
+ end
+
+ super(key, value, mapping, objectinfo)
+ end
+
+ # Get the groupname from its id
+ def self.groupname_by_id(gid)
+ groupname=nil
+ execute(lsgroupscmd("ALL")).each { |entry|
+ attrs = self.parse_attr_list(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 groupid_by_name(groupname)
+ attrs = self.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 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 gid_to_attr(value)
+ verify_group(value)
+ end
+
+ # Get the group gid from its name
+ 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 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 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", self.class.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
+
+ def filter_attributes(hash)
+ # Return only not managed attributtes.
+ hash.select {
+ |k,v| !self.class.attribute_mapping_from.include?(k) and
+ !self.class.attribute_ignore.include?(k)
+ }.inject({}) {
+ |hash, array| hash[array[0]] = array[1]; hash
+ }
+ end
+
+ def attributes
+ filter_attributes(getosinfo(refresh = false))
+ end
+
+ def attributes=(attr_hash)
+ #self.class.validate(param, value)
+ param = :attributes
+ cmd = modifycmd({param => filter_attributes(attr_hash)})
+ if cmd
+ begin
+ execute(cmd)
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}"
+ end
+ 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
diff --git a/lib/puppet/type/group.rb b/lib/puppet/type/group.rb
index cde1cfd65..aa96bd9c3 100755
--- a/lib/puppet/type/group.rb
+++ b/lib/puppet/type/group.rb
@@ -15,6 +15,9 @@ module Puppet
feature :manages_members,
"For directories where membership is an attribute of groups not users."
+ feature :manages_aix_lam,
+ "The provider can manage AIX Loadable Authentication Module (LAM) system."
+
ensurable do
desc "Create or remove the group."
@@ -95,5 +98,38 @@ module Puppet
defaultto false
end
+
+ newparam(:ia_load_module, :required_features => :manages_aix_lam) do
+ desc "The name of the I&A module to use to manage this user"
+
+ defaultto "compat"
+ end
+
+ newproperty(:attributes, :parent => Puppet::Property::KeyValue, :required_features => :manages_aix_lam) do
+ desc "Specify group AIX attributes in an array of keyvalue pairs"
+
+ def membership
+ :attribute_membership
+ end
+
+ def delimiter
+ " "
+ end
+
+ validate do |value|
+ raise ArgumentError, "Attributes value pairs must be seperated by an =" unless value.include?("=")
+ end
+ end
+
+ newparam(:attribute_membership) do
+ desc "Whether specified attribute value pairs should be treated as the only attributes
+ of the user or whether they should merely
+ be treated as the minimum list."
+
+ newvalues(:inclusive, :minimum)
+
+ defaultto :minimum
+ end
+
end
end
diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb
index f74e4266f..767959308 100755
--- a/lib/puppet/type/user.rb
+++ b/lib/puppet/type/user.rb
@@ -13,7 +13,7 @@ module Puppet
This resource type uses the prescribed native tools for creating
groups and generally uses POSIX APIs for retrieving information
about them. It does not directly modify `/etc/passwd` or anything.
-
+
**Autorequires:** If Puppet is managing the user's primary group (as provided in the `gid` attribute), the user resource will autorequire that group. If Puppet is managing any role accounts corresponding to the user's roles, the user resource will autorequire those role accounts."
feature :allows_duplicates,
@@ -39,6 +39,9 @@ module Puppet
feature :system_users,
"The provider allows you to create system users with lower UIDs."
+ feature :manages_aix_lam,
+ "The provider can manage AIX Loadable Authentication Module (LAM) system."
+
newproperty(:ensure, :parent => Puppet::Property::Ensure) do
newvalue(:present, :event => :user_created) do
provider.create
@@ -445,5 +448,39 @@ module Puppet
newproperty(:project, :required_features => :manages_solaris_rbac) do
desc "The name of the project associated with a user"
end
+
+ newparam(:ia_load_module, :required_features => :manages_aix_lam) do
+ desc "The name of the I&A module to use to manage this user"
+
+ defaultto "compat"
+ end
+
+ newproperty(:attributes, :parent => Puppet::Property::KeyValue, :required_features => :manages_aix_lam) do
+ desc "Specify user AIX attributes in an array of keyvalue pairs"
+
+ def membership
+ :attribute_membership
+ end
+
+ def delimiter
+ " "
+ end
+
+ validate do |value|
+ raise ArgumentError, "Attributes value pairs must be seperated by an =" unless value.include?("=")
+ end
+ end
+
+ newparam(:attribute_membership) do
+ desc "Whether specified attribute value pairs should be treated as the only attributes
+ of the user or whether they should merely
+ be treated as the minimum list."
+
+ newvalues(:inclusive, :minimum)
+
+ defaultto :minimum
+ end
+
+
end
end