diff options
| author | Max Martin <max@puppetlabs.com> | 2011-03-30 13:02:19 -0700 |
|---|---|---|
| committer | Max Martin <max@puppetlabs.com> | 2011-03-30 13:02:19 -0700 |
| commit | 9c06fbd762cddcc41a7185a36f2a8e72879125eb (patch) | |
| tree | bfd37ac5887307857d0b0d79a1db034010eed424 /lib/puppet/provider | |
| parent | 4196699f5fbb90ceecbb709c8502622eaad39062 (diff) | |
| parent | 11309a214148abb2167c1bd0adba0e035b2d8384 (diff) | |
Merge branch 'next'
* next: (39 commits)
(#5908) Add support for new update-rc.d disable API
(#6830) Fix tests that depended on special inherited behavior
(#6830) Fix overly stubbed tests
(#6830) Fix instance_variables now comes back as symbols
(#6830) Fix badly stubbed Time object in test
(#6830) Fix MD5 handling to work with Ruby 1.9
(#6830) Fix File class scoping
(#6830) Handle case where array is actually a string
(#6830) Fix case where instance_variables returns symbols in Ruby 1.9
(#6862) Add a default subject for the mail_patches rake task
Fixed #6256 - Creation of rrd directory.
(#6855) ResourceType#search now accepts a regex
(#5477) Allow watch_file to watch non-existent files, especially site.pp
(#5477) Allow watch_file to watch non-existent files, especially site.pp
Fixing #6851 - ResourceType#find/search loads types
Fixing Module#path detection
Fixed #6850 - Clean up ResourceType#to_pson
(#6830) Fix stat method calls to not use an unneeded argument
(#4576) Raise an error when a node is classified into a non-existent class
Update CHANGELOG for 2.6.7
...
Diffstat (limited to 'lib/puppet/provider')
| -rwxr-xr-x | lib/puppet/provider/aixobject.rb | 393 | ||||
| -rw-r--r-- | lib/puppet/provider/augeas/augeas.rb | 21 | ||||
| -rw-r--r-- | lib/puppet/provider/file/posix.rb | 2 | ||||
| -rw-r--r-- | lib/puppet/provider/file/win32.rb | 2 | ||||
| -rwxr-xr-x | lib/puppet/provider/group/aix.rb | 141 | ||||
| -rwxr-xr-x | lib/puppet/provider/service/debian.rb | 8 | ||||
| -rwxr-xr-x | lib/puppet/provider/service/smf.rb | 6 | ||||
| -rwxr-xr-x | lib/puppet/provider/user/aix.rb | 353 | ||||
| -rw-r--r-- | lib/puppet/provider/zfs/solaris.rb | 2 |
9 files changed, 920 insertions, 8 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/augeas/augeas.rb b/lib/puppet/provider/augeas/augeas.rb index 461968245..5488c6674 100644 --- a/lib/puppet/provider/augeas/augeas.rb +++ b/lib/puppet/provider/augeas/augeas.rb @@ -32,10 +32,14 @@ Puppet::Type.type(:augeas).provide(:augeas) do COMMANDS = { "set" => [ :path, :string ], + "setm" => [ :path, :string, :string ], "rm" => [ :path ], "clear" => [ :path ], + "mv" => [ :path, :path ], "insert" => [ :string, :string, :path ], "get" => [ :path, :comparator, :string ], + "defvar" => [ :string, :path ], + "defnode" => [ :string, :path, :string ], "match" => [ :path, :glob ], "size" => [:comparator, :int], "include" => [:string], @@ -46,6 +50,7 @@ Puppet::Type.type(:augeas).provide(:augeas) do COMMANDS["ins"] = COMMANDS["insert"] COMMANDS["remove"] = COMMANDS["rm"] + COMMANDS["move"] = COMMANDS["mv"] attr_accessor :aug @@ -334,6 +339,10 @@ Puppet::Type.type(:augeas).provide(:augeas) do debug("sending command '#{command}' with params #{cmd_array.inspect}") rv = aug.set(cmd_array[0], cmd_array[1]) fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (!rv) + when "setm" + debug("sending command '#{command}' with params #{cmd_array.inspect}") + rv = aug.setm(cmd_array[0], cmd_array[1], cmd_array[2]) + fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (rv == -1) when "rm", "remove" debug("sending command '#{command}' with params #{cmd_array.inspect}") rv = aug.rm(cmd_array[0]) @@ -354,6 +363,18 @@ Puppet::Type.type(:augeas).provide(:augeas) do debug("sending command '#{command}' with params #{[label, where, path].inspect}") rv = aug.insert(path, label, before) fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (rv == -1) + when "defvar" + debug("sending command '#{command}' with params #{cmd_array.inspect}") + rv = aug.defvar(cmd_array[0], cmd_array[1]) + fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (!rv) + when "defnode" + debug("sending command '#{command}' with params #{cmd_array.inspect}") + rv = aug.defnode(cmd_array[0], cmd_array[1], cmd_array[2]) + fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (!rv) + when "mv", "move" + debug("sending command '#{command}' with params #{cmd_array.inspect}") + rv = aug.mv(cmd_array[0], cmd_array[1]) + fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (rv == -1) else fail("Command '#{command}' is not supported") end rescue SystemExit,NoMemoryError diff --git a/lib/puppet/provider/file/posix.rb b/lib/puppet/provider/file/posix.rb index f7b8c9797..7b7336b9d 100644 --- a/lib/puppet/provider/file/posix.rb +++ b/lib/puppet/provider/file/posix.rb @@ -54,7 +54,7 @@ Puppet::Type.type(:file).provide :posix do end def retrieve(resource) - unless stat = resource.stat(false) + unless stat = resource.stat return :absent end diff --git a/lib/puppet/provider/file/win32.rb b/lib/puppet/provider/file/win32.rb index 21e7ca974..9423e8f00 100644 --- a/lib/puppet/provider/file/win32.rb +++ b/lib/puppet/provider/file/win32.rb @@ -49,7 +49,7 @@ Puppet::Type.type(:file).provide :microsoft_windows do end def retrieve(resource) - unless stat = resource.stat(false) + unless stat = resource.stat return :absent 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/service/debian.rb b/lib/puppet/provider/service/debian.rb index 3d09e2849..58b808a8e 100755 --- a/lib/puppet/provider/service/debian.rb +++ b/lib/puppet/provider/service/debian.rb @@ -22,8 +22,12 @@ Puppet::Type.type(:service).provide :debian, :parent => :init do # Remove the symlinks def disable - update_rc "-f", @resource[:name], "remove" - update_rc @resource[:name], "stop", "00", "1", "2", "3", "4", "5", "6", "." + if `dpkg --compare-versions $(dpkg-query -W --showformat '${Version}' sysv-rc) ge 2.88 ; echo $?`.to_i == 0 + update_rc @resource[:name], "disable" + else + update_rc "-f", @resource[:name], "remove" + update_rc @resource[:name], "stop", "00", "1", "2", "3", "4", "5", "6", "." + end end def enabled? diff --git a/lib/puppet/provider/service/smf.rb b/lib/puppet/provider/service/smf.rb index 3efb2eb37..f6f221a7c 100755 --- a/lib/puppet/provider/service/smf.rb +++ b/lib/puppet/provider/service/smf.rb @@ -27,7 +27,7 @@ Puppet::Type.type(:service).provide :smf, :parent => :base do end end rescue Puppet::ExecutionFailure => detail - raise Puppet::Error.new( "Cannot config #{self.service} to enable it: #{detail}" ) + raise Puppet::Error.new( "Cannot config #{self.name} to enable it: #{detail}" ) end def enable @@ -54,10 +54,10 @@ Puppet::Type.type(:service).provide :smf, :parent => :base do def startcmd self.setupservice case self.status - when :stopped - [command(:adm), :enable, @resource[:name]] when :maintenance [command(:adm), :clear, @resource[:name]] + else + [command(:adm), :enable, @resource[:name]] 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/provider/zfs/solaris.rb b/lib/puppet/provider/zfs/solaris.rb index 9aec9d801..b783f9e01 100644 --- a/lib/puppet/provider/zfs/solaris.rb +++ b/lib/puppet/provider/zfs/solaris.rb @@ -31,7 +31,7 @@ Puppet::Type.type(:zfs).provide(:solaris) do end end - [:mountpoint, :recordsize, :aclmode, :aclinherit, :primarycache, :secondarycache, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir].each do |field| + [:aclinherit, :aclmode, :atime, :canmount, :checksum, :compression, :copies, :devices, :exec, :logbias, :mountpoint, :nbmand, :primarycache, :quota, :readonly, :recordsize, :refquota, :refreservation, :reservation, :secondarycache, :setuid, :shareiscsi, :sharenfs, :sharesmb, :snapdir, :version, :volsize, :vscan, :xattr, :zoned, :vscan].each do |field| define_method(field) do zfs(:get, "-H", "-o", "value", field, @resource[:name]).strip end |
