diff --git a/lib/puppet/provider/group/aix.rb b/lib/puppet/provider/group/aix.rb index ecdef6070..a7ae72cf0 100755 --- a/lib/puppet/provider/group/aix.rb +++ b/lib/puppet/provider/group/aix.rb @@ -1,141 +1,141 @@ # # Group Puppet provider for AIX. It uses standard commands to manage groups: # mkgroup, rmgroup, lsgroup, chgroup # # Author:: Hector Rivas Gandara -# +# 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" + desc "Group management for AIX." # 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.= 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 + 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/group/groupadd.rb b/lib/puppet/provider/group/groupadd.rb index bcc08d9f7..ecbfb28b3 100644 --- a/lib/puppet/provider/group/groupadd.rb +++ b/lib/puppet/provider/group/groupadd.rb @@ -1,32 +1,30 @@ require 'puppet/provider/nameservice/objectadd' Puppet::Type.type(:group).provide :groupadd, :parent => Puppet::Provider::NameService::ObjectAdd do - desc "Group management via `groupadd` and its ilk. - - The default for most platforms + desc "Group management via `groupadd` and its ilk. The default for most platforms. " commands :add => "groupadd", :delete => "groupdel", :modify => "groupmod" has_feature :system_groups verify :gid, "GID must be an integer" do |value| value.is_a? Integer end def addcmd cmd = [command(:add)] if gid = @resource.should(:gid) unless gid == :absent cmd << flag(:gid) << gid end end cmd << "-o" if @resource.allowdupe? cmd << "-r" if @resource.system? cmd << @resource[:name] cmd end end diff --git a/lib/puppet/provider/group/ldap.rb b/lib/puppet/provider/group/ldap.rb index 86c72a5d3..78333f176 100644 --- a/lib/puppet/provider/group/ldap.rb +++ b/lib/puppet/provider/group/ldap.rb @@ -1,47 +1,45 @@ require 'puppet/provider/ldap' Puppet::Type.type(:group).provide :ldap, :parent => Puppet::Provider::Ldap do - desc "Group management via `ldap`. + desc "Group management via LDAP. - This provider requires that you have valid values for all of the - ldap-related settings, including `ldapbase`. You will also almost - definitely need settings for `ldapuser` and `ldappassword`, so that - your clients can write to ldap. + This provider requires that you have valid values for all of the + LDAP-related settings in `puppet.conf`, including `ldapbase`. You will + almost definitely need settings for `ldapuser` and `ldappassword` in order + for your clients to write to LDAP. - Note that this provider will automatically generate a GID for you if you do - not specify one, but it is a potentially expensive operation, as it - iterates across all existing groups to pick the appropriate next one. - - " + Note that this provider will automatically generate a GID for you if you do + not specify one, but it is a potentially expensive operation, as it + iterates across all existing groups to pick the appropriate next one." confine :true => Puppet.features.ldap?, :false => (Puppet[:ldapuser] == "") # We're mapping 'members' here because we want to make it # easy for the ldap user provider to manage groups. This # way it can just use the 'update' method in the group manager, # whereas otherwise it would need to replicate that code. manages(:posixGroup).at("ou=Groups").and.maps :name => :cn, :gid => :gidNumber, :members => :memberUid # Find the next gid after the current largest gid. provider = self manager.generates(:gidNumber).with do largest = 500 if existing = provider.manager.search existing.each do |hash| next unless value = hash[:gid] num = value[0].to_i largest = num if num > largest end end largest + 1 end # Convert a group name to an id. def self.name2id(group) return nil unless result = manager.search("cn=#{group}") and result.length > 0 # Only use the first result. group = result[0] gid = group[:gid][0] end end diff --git a/lib/puppet/provider/package/aix.rb b/lib/puppet/provider/package/aix.rb index 134a84526..088099d45 100644 --- a/lib/puppet/provider/package/aix.rb +++ b/lib/puppet/provider/package/aix.rb @@ -1,128 +1,128 @@ require 'puppet/provider/package' require 'puppet/util/package' Puppet::Type.type(:package).provide :aix, :parent => Puppet::Provider::Package do - desc "Installation from AIX Software directory" + desc "Installation from the AIX software directory." # The commands we are using on an AIX box are installed standard # (except nimclient) nimclient needs the bos.sysmgt.nim.client fileset. commands :lslpp => "/usr/bin/lslpp", :installp => "/usr/sbin/installp" # AIX supports versionable packages with and without a NIM server has_feature :versionable confine :operatingsystem => [ :aix ] defaultfor :operatingsystem => :aix attr_accessor :latest_info def self.srclistcmd(source) [ command(:installp), "-L", "-d", source ] end def self.prefetch(packages) raise Puppet::Error, "The aix provider can only be used by root" if Process.euid != 0 return unless packages.detect { |name, package| package.should(:ensure) == :latest } sources = packages.collect { |name, package| package[:source] }.uniq updates = {} sources.each do |source| execute(self.srclistcmd(source)).each do |line| if line =~ /^[^#][^:]*:([^:]*):([^:]*)/ current = {} current[:name] = $1 current[:version] = $2 current[:source] = source if updates.key?(current[:name]) previous = updates[current[:name]] updates[ current[:name] ] = current unless Puppet::Util::Package.versioncmp(previous[:version], current[:version]) == 1 else updates[current[:name]] = current end end end end packages.each do |name, package| if info = updates[package[:name]] package.provider.latest_info = info[0] end end end def uninstall # Automatically process dependencies when installing/uninstalling # with the -g option to installp. installp "-gu", @resource[:name] end def install(useversion = true) unless source = @resource[:source] self.fail "A directory is required which will be used to find packages" end pkg = @resource[:name] pkg << " #{@resource.should(:ensure)}" if (! @resource.should(:ensure).is_a? Symbol) and useversion installp "-acgwXY", "-d", source, pkg end def self.pkglist(hash = {}) cmd = [command(:lslpp), "-qLc"] if name = hash[:pkgname] cmd << name end begin list = execute(cmd).scan(/^[^#][^:]*:([^:]*):([^:]*)/).collect { |n,e| { :name => n, :ensure => e, :provider => self.name } } rescue Puppet::ExecutionFailure => detail if hash[:pkgname] return nil else raise Puppet::Error, "Could not list installed Packages: #{detail}" end end if hash[:pkgname] return list.shift else return list end end def self.instances pkglist.collect do |hash| new(hash) end end def latest upd = latest_info unless upd.nil? return "#{upd[:version]}" else raise Puppet::DevError, "Tried to get latest on a missing package" if properties[:ensure] == :absent return properties[:ensure] end end def query self.class.pkglist(:pkgname => @resource[:name]) end def update self.install(false) end end diff --git a/lib/puppet/provider/package/macports.rb b/lib/puppet/provider/package/macports.rb index 6e5526647..22fe6d930 100755 --- a/lib/puppet/provider/package/macports.rb +++ b/lib/puppet/provider/package/macports.rb @@ -1,105 +1,105 @@ require 'puppet/provider/package' Puppet::Type.type(:package).provide :macports, :parent => Puppet::Provider::Package do desc "Package management using MacPorts on OS X. Supports MacPorts versions and revisions, but not variants. - Variant preferences may be specified using the MacPorts variants.conf file - http://guide.macports.org/chunked/internals.configuration-files.html#internals.configuration-files.variants-conf + Variant preferences may be specified using + [the MacPorts variants.conf file](http://guide.macports.org/chunked/internals.configuration-files.html#internals.configuration-files.variants-conf). - When specifying a version in the Puppet DSL, only specify the version, not the revision + When specifying a version in the Puppet DSL, only specify the version, not the revision. Revisions are only used internally for ensuring the latest version/revision of a port. " confine :operatingsystem => :darwin commands :port => "/opt/local/bin/port" has_feature :installable has_feature :uninstallable has_feature :upgradeable has_feature :versionable def self.parse_installed_query_line(line) regex = /(\S+)\s+@(\S+)_(\S+)\s+\(active\)/ fields = [:name, :ensure, :revision] hash_from_line(line, regex, fields) end def self.parse_info_query_line(line) regex = /(\S+)\s+(\S+)/ fields = [:version, :revision] hash_from_line(line, regex, fields) end def self.hash_from_line(line, regex, fields) hash = {} if match = regex.match(line) fields.zip(match.captures) { |field, value| hash[field] = value } hash[:provider] = self.name return hash end nil end def self.instances packages = [] port("-q", :installed).each do |line| if hash = parse_installed_query_line(line) packages << new(hash) end end packages end def install should = @resource.should(:ensure) if [:latest, :installed, :present].include?(should) output = port("-q", :install, @resource[:name]) else output = port("-q", :install, @resource[:name], "@#{should}") end # MacPorts now correctly exits non-zero with appropriate errors in # situations where a port cannot be found or installed. end def query return self.class.parse_installed_query_line(port("-q", :installed, @resource[:name])) end def latest # We need both the version and the revision to be confident # we've got the latest revision of a specific version # Note we're still not doing anything with variants here. info_line = port("-q", :info, "--line", "--version", "--revision", @resource[:name]) return nil if info_line == "" if newest = self.class.parse_info_query_line(info_line) current = query # We're doing some fiddling behind the scenes here to cope with updated revisions. # If we're already at the latest version/revision, then just return the version # so the current and desired values match. Otherwise return version and revision # to trigger an upgrade to the latest revision. if newest[:version] == current[:ensure] and newest[:revision] == current[:revision] return current[:ensure] else return "#{newest[:version]}_#{newest[:revision]}" end end nil end def uninstall port("-q", :uninstall, @resource[:name]) end def update if query[:name] == @resource[:name] # 'port upgrade' cannot install new ports port("-q", :upgrade, @resource[:name]) else install end end end diff --git a/lib/puppet/provider/package/nim.rb b/lib/puppet/provider/package/nim.rb index 8f52016db..49d3c8b30 100644 --- a/lib/puppet/provider/package/nim.rb +++ b/lib/puppet/provider/package/nim.rb @@ -1,35 +1,35 @@ require 'puppet/provider/package' require 'puppet/util/package' Puppet::Type.type(:package).provide :nim, :parent => :aix, :source => :aix do - desc "Installation from NIM LPP source" + desc "Installation from NIM LPP source." # The commands we are using on an AIX box are installed standard # (except nimclient) nimclient needs the bos.sysmgt.nim.client fileset. commands :nimclient => "/usr/sbin/nimclient" # If NIM has not been configured, /etc/niminfo will not be present. # However, we have no way of knowing if the NIM server is not configured # properly. confine :exists => "/etc/niminfo" has_feature :versionable attr_accessor :latest_info def self.srclistcmd(source) [ command(:nimclient), "-o", "showres", "-a", "installp_flags=L", "-a", "resource=#{source}" ] end def install(useversion = true) unless source = @resource[:source] self.fail "An LPP source location is required in 'source'" end pkg = @resource[:name] pkg << " " << @resource.should(:ensure) if (! @resource.should(:ensure).is_a? Symbol) and useversion nimclient "-o", "cust", "-a", "installp_flags=acgwXY", "-a", "lpp_source=#{source}", "-a", "filesets='#{pkg}'" end end diff --git a/lib/puppet/provider/package/pkgdmg.rb b/lib/puppet/provider/package/pkgdmg.rb index b24514c44..4bdb2d38a 100644 --- a/lib/puppet/provider/package/pkgdmg.rb +++ b/lib/puppet/provider/package/pkgdmg.rb @@ -1,129 +1,129 @@ # # Motivation: DMG files provide a true HFS file system # and are easier to manage and .pkg bundles. # # Note: the 'apple' Provider checks for the package name # in /L/Receipts. Since we install multiple pkg's from a single # source, we treat the source .pkg.dmg file as the package name. # As a result, we store installed .pkg.dmg file names # in /var/db/.puppet_pkgdmg_installed_ require 'puppet/provider/package' require 'facter/util/plist' Puppet::Type.type(:package).provide :pkgdmg, :parent => Puppet::Provider::Package do desc "Package management based on Apple's Installer.app and DiskUtility.app. This package works by checking the contents of a DMG image for Apple pkg or mpkg files. Any number of pkg or mpkg - files may exist in the root directory of the DMG file system. Sub - directories are not checked for packages. See `the wiki docs - ` + files may exist in the root directory of the DMG file system. + Subdirectories are not checked for packages. See + [the wiki docs on this provider](http://projects.puppetlabs.com/projects/puppet/wiki/Package_Management_With_Dmg_Patterns) for more detail." confine :operatingsystem => :darwin defaultfor :operatingsystem => :darwin commands :installer => "/usr/sbin/installer" commands :hdiutil => "/usr/bin/hdiutil" commands :curl => "/usr/bin/curl" # JJM We store a cookie for each installed .pkg.dmg in /var/db def self.instance_by_name Dir.entries("/var/db").find_all { |f| f =~ /^\.puppet_pkgdmg_installed_/ }.collect do |f| name = f.sub(/^\.puppet_pkgdmg_installed_/, '') yield name if block_given? name end end def self.instances instance_by_name.collect do |name| new( :name => name, :provider => :pkgdmg, :ensure => :installed ) end end def self.installpkg(source, name, orig_source) installer "-pkg", source, "-target", "/" # Non-zero exit status will throw an exception. File.open("/var/db/.puppet_pkgdmg_installed_#{name}", "w") do |t| t.print "name: '#{name}'\n" t.print "source: '#{orig_source}'\n" end end def self.installpkgdmg(source, name) unless source =~ /\.dmg$/i || source =~ /\.pkg$/i raise Puppet::Error.new("Mac OS X PKG DMG's must specificy a source string ending in .dmg or flat .pkg file") end require 'open-uri' cached_source = source if %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ cached_source cached_source = "/tmp/#{name}" begin curl "-o", cached_source, "-C", "-", "-k", "-s", "--url", source Puppet.debug "Success: curl transfered [#{name}]" rescue Puppet::ExecutionFailure Puppet.debug "curl did not transfer [#{name}]. Falling back to slower open-uri transfer methods." cached_source = source end end begin if source =~ /\.dmg$/i File.open(cached_source) do |dmg| xml_str = hdiutil "mount", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", "/tmp", dmg.path hdiutil_info = Plist::parse_xml(xml_str) raise Puppet::Error.new("No disk entities returned by mount at #{dmg.path}") unless hdiutil_info.has_key?("system-entities") mounts = hdiutil_info["system-entities"].collect { |entity| entity["mount-point"] }.compact begin mounts.each do |mountpoint| Dir.entries(mountpoint).select { |f| f =~ /\.m{0,1}pkg$/i }.each do |pkg| installpkg("#{mountpoint}/#{pkg}", name, source) end end ensure mounts.each do |mountpoint| hdiutil "eject", mountpoint end end end elsif source =~ /\.pkg$/i installpkg(cached_source, name, source) else raise Puppet::Error.new("Mac OS X PKG DMG's must specificy a source string ending in .dmg or flat .pkg file") end ensure # JJM Remove the file if open-uri didn't already do so. File.unlink(cached_source) if File.exist?(cached_source) end end def query if FileTest.exists?("/var/db/.puppet_pkgdmg_installed_#{@resource[:name]}") Puppet.debug "/var/db/.puppet_pkgdmg_installed_#{@resource[:name]} found" return {:name => @resource[:name], :ensure => :present} else return nil end end def install source = nil unless source = @resource[:source] raise Puppet::Error.new("Mac OS X PKG DMG's must specify a package source.") end unless name = @resource[:name] raise Puppet::Error.new("Mac OS X PKG DMG's must specify a package name.") end self.class.installpkgdmg(source,name) end end diff --git a/lib/puppet/provider/package/ports.rb b/lib/puppet/provider/package/ports.rb index 6a8d6a77b..62c65c8b8 100755 --- a/lib/puppet/provider/package/ports.rb +++ b/lib/puppet/provider/package/ports.rb @@ -1,94 +1,94 @@ Puppet::Type.type(:package).provide :ports, :parent => :freebsd, :source => :freebsd do - desc "Support for FreeBSD's ports. Again, this still mixes packages and ports." + desc "Support for FreeBSD's ports. Note that this, too, mixes packages and ports." commands :portupgrade => "/usr/local/sbin/portupgrade", :portversion => "/usr/local/sbin/portversion", :portuninstall => "/usr/local/sbin/pkg_deinstall", :portinfo => "/usr/sbin/pkg_info" defaultfor :operatingsystem => :freebsd # I hate ports %w{INTERACTIVE UNAME}.each do |var| ENV.delete(var) if ENV.include?(var) end def install # -N: install if the package is missing, otherwise upgrade # -M: yes, we're a batch, so don't ask any questions cmd = %w{-N -M BATCH=yes} << @resource[:name] output = portupgrade(*cmd) if output =~ /\*\* No such / raise Puppet::ExecutionFailure, "Could not find package #{@resource[:name]}" end end # If there are multiple packages, we only use the last one def latest cmd = ["-v", @resource[:name]] begin output = portversion(*cmd) rescue Puppet::ExecutionFailure raise Puppet::Error.new(output) end line = output.split("\n").pop unless line =~ /^(\S+)\s+(\S)\s+(.+)$/ # There's no "latest" version, so just return a placeholder return :latest end pkgstuff = $1 match = $2 info = $3 unless pkgstuff =~ /^(\S+)-([^-\s]+)$/ raise Puppet::Error, "Could not match package info '#{pkgstuff}'" end name, version = $1, $2 if match == "=" or match == ">" # we're up to date or more recent return version end # Else, we need to be updated; we need to pull out the new version unless info =~ /\((\w+) has (.+)\)/ raise Puppet::Error, "Could not match version info '#{info}'" end source, newversion = $1, $2 debug "Newer version in #{source}" newversion end def query # support portorigin_glob such as "mail/postfix" name = self.name if name =~ /\// name = self.name.split(/\//).slice(1) end self.class.instances.each do |instance| if instance.name == name return instance.properties end end nil end def uninstall portuninstall @resource[:name] end def update install end end diff --git a/lib/puppet/provider/service/base.rb b/lib/puppet/provider/service/base.rb index 211e7f964..380bd5944 100755 --- a/lib/puppet/provider/service/base.rb +++ b/lib/puppet/provider/service/base.rb @@ -1,144 +1,144 @@ Puppet::Type.type(:service).provide :base do desc "The simplest form of service support. You have to specify enough about your service for this to work; the minimum you can specify is a binary for starting the process, and this same binary will be searched for in the process table to stop the - service. It is preferable to specify start, stop, and status commands, - akin to how you would do so using `init`. + service. As with `init`-style services, it is preferable to specify start, + stop, and status commands. " commands :kill => "kill" def self.instances [] end # Get the process ID for a running process. Requires the 'pattern' # parameter. def getpid @resource.fail "Either stop/status commands or a pattern must be specified" unless @resource[:pattern] ps = Facter["ps"].value @resource.fail "You must upgrade Facter to a version that includes 'ps'" unless ps and ps != "" regex = Regexp.new(@resource[:pattern]) self.debug "Executing '#{ps}'" IO.popen(ps) { |table| table.each { |line| if regex.match(line) ary = line.sub(/^\s+/, '').split(/\s+/) return ary[1] end } } nil end # How to restart the process. def restart if @resource[:restart] or restartcmd ucommand(:restart) else self.stop self.start end end # There is no default command, which causes other methods to be used def restartcmd end # Check if the process is running. Prefer the 'status' parameter, # then 'statuscmd' method, then look in the process table. We give # the object the option to not return a status command, which might # happen if, for instance, it has an init script (and thus responds to # 'statuscmd') but does not have 'hasstatus' enabled. def status if @resource[:status] or statuscmd # Don't fail when the exit status is not 0. ucommand(:status, false) # Expicitly calling exitstatus to facilitate testing if $CHILD_STATUS.exitstatus == 0 return :running else return :stopped end elsif pid = self.getpid self.debug "PID is #{pid}" return :running else return :stopped end end # There is no default command, which causes other methods to be used def statuscmd end # Run the 'start' parameter command, or the specified 'startcmd'. def start ucommand(:start) end # The command used to start. Generated if the 'binary' argument # is passed. def startcmd if @resource[:binary] return @resource[:binary] else raise Puppet::Error, "Services must specify a start command or a binary" end end # Stop the service. If a 'stop' parameter is specified, it # takes precedence; otherwise checks if the object responds to # a 'stopcmd' method, and if so runs that; otherwise, looks # for the process in the process table. # This method will generally not be overridden by submodules. def stop if @resource[:stop] or stopcmd ucommand(:stop) else pid = getpid unless pid self.info "#{self.name} is not running" return false end begin output = kill pid rescue Puppet::ExecutionFailure => detail @resource.fail "Could not kill #{self.name}, PID #{pid}: #{output}" end return true end end # There is no default command, which causes other methods to be used def stopcmd end # A simple wrapper so execution failures are a bit more informative. def texecute(type, command, fof = true) begin # #565: Services generally produce no output, so squelch them. execute(command, :failonfail => fof, :squelch => true) rescue Puppet::ExecutionFailure => detail @resource.fail "Could not #{type} #{@resource.ref}: #{detail}" end nil end # Use either a specified command or the default for our provider. def ucommand(type, fof = true) if c = @resource[type] cmd = [c] else cmd = [send("#{type}cmd")].flatten end texecute(type, cmd, fof) end end diff --git a/lib/puppet/provider/service/bsd.rb b/lib/puppet/provider/service/bsd.rb index e2a0e35f7..0c019fdd0 100644 --- a/lib/puppet/provider/service/bsd.rb +++ b/lib/puppet/provider/service/bsd.rb @@ -1,48 +1,49 @@ # Manage FreeBSD services. Puppet::Type.type(:service).provide :bsd, :parent => :init do - desc "FreeBSD's (and probably NetBSD?) form of `init`-style service management. + desc <<-EOT + FreeBSD's (and probably NetBSD's?) form of `init`-style service management. - Uses `rc.conf.d` for service enabling and disabling. + Uses `rc.conf.d` for service enabling and disabling. -" + EOT confine :operatingsystem => [:freebsd, :netbsd, :openbsd] @@rcconf_dir = '/etc/rc.conf.d' def self.defpath superclass.defpath end # remove service file from rc.conf.d to disable it def disable rcfile = File.join(@@rcconf_dir, @model[:name]) File.delete(rcfile) if File.exists?(rcfile) end # if the service file exists in rc.conf.d then it's already enabled def enabled? rcfile = File.join(@@rcconf_dir, @model[:name]) return :true if File.exists?(rcfile) :false end # enable service by creating a service file under rc.conf.d with the # proper contents def enable Dir.mkdir(@@rcconf_dir) if not File.exists?(@@rcconf_dir) rcfile = File.join(@@rcconf_dir, @model[:name]) open(rcfile, 'w') { |f| f << "%s_enable=\"YES\"\n" % @model[:name] } end # Override stop/start commands to use one's and the avoid race condition # where provider trys to stop/start the service before it is enabled def startcmd [self.initscript, :onestart] end def stopcmd [self.initscript, :onestop] end end diff --git a/lib/puppet/provider/service/daemontools.rb b/lib/puppet/provider/service/daemontools.rb index f5a073329..bb94c43f5 100644 --- a/lib/puppet/provider/service/daemontools.rb +++ b/lib/puppet/provider/service/daemontools.rb @@ -1,194 +1,194 @@ # Daemontools service management # # author Brice Figureau Puppet::Type.type(:service).provide :daemontools, :parent => :base do - desc "Daemontools service management. + desc <<-EOT + Daemontools service management. - This provider manages daemons running supervised by D.J.Bernstein daemontools. - It tries to detect the service directory, with by order of preference: + This provider manages daemons supervised by D.J. Bernstein daemontools. + When detecting the service directory it will check, in order of preference: - * /service - * /etc/service - * /var/lib/svscan + * `/service` + * `/etc/service` + * `/var/lib/svscan` - The daemon directory should be placed in a directory that can be - by default in: + The daemon directory should be in one of the following locations: - * /var/lib/service - * /etc + * `/var/lib/service` + * `/etc` - or this can be overriden in the service resource parameters:: + ...or this can be overriden in the resource's attributes: - service { \"myservice\": - provider => \"daemontools\", - path => \"/path/to/daemons\", - } + service { "myservice": + provider => "daemontools", + path => "/path/to/daemons", + } - This provider supports out of the box: + This provider supports out of the box: - * start/stop (mapped to enable/disable) - * enable/disable - * restart - * status + * start/stop (mapped to enable/disable) + * enable/disable + * restart + * status - If a service has `ensure => \"running\"`, it will link /path/to/daemon to - /path/to/service, which will automatically enable the service. + If a service has `ensure => "running"`, it will link /path/to/daemon to + /path/to/service, which will automatically enable the service. - If a service has `ensure => \"stopped\"`, it will only down the service, not - remove the /path/to/service link. + If a service has `ensure => "stopped"`, it will only shut down the service, not + remove the `/path/to/service` link. - " + EOT commands :svc => "/usr/bin/svc", :svstat => "/usr/bin/svstat" class << self attr_writer :defpath # Determine the daemon path. def defpath(dummy_argument=:work_arround_for_ruby_GC_bug) unless @defpath ["/var/lib/service", "/etc"].each do |path| if FileTest.exist?(path) @defpath = path break end end raise "Could not find the daemon directory (tested [/var/lib/service,/etc])" unless @defpath end @defpath end end attr_writer :servicedir # returns all providers for all existing services in @defpath # ie enabled or not def self.instances path = self.defpath unless FileTest.directory?(path) Puppet.notice "Service path #{path} does not exist" return end # reject entries that aren't either a directory # or don't contain a run file Dir.entries(path).reject { |e| fullpath = File.join(path, e) e =~ /^\./ or ! FileTest.directory?(fullpath) or ! FileTest.exist?(File.join(fullpath,"run")) }.collect do |name| new(:name => name, :path => path) end end # returns the daemon dir on this node def self.daemondir self.defpath end # find the service dir on this node def servicedir unless @servicedir ["/service", "/etc/service","/var/lib/svscan"].each do |path| if FileTest.exist?(path) @servicedir = path break end end raise "Could not find service directory" unless @servicedir end @servicedir end # returns the full path of this service when enabled # (ie in the service directory) def service File.join(self.servicedir, resource[:name]) end # returns the full path to the current daemon directory # note that this path can be overriden in the resource # definition def daemon File.join(resource[:path], resource[:name]) end def status begin output = svstat self.service if output =~ /:\s+up \(/ return :running end rescue Puppet::ExecutionFailure => detail raise Puppet::Error.new( "Could not get status for service #{resource.ref}: #{detail}" ) end :stopped end def setupservice if resource[:manifest] Puppet.notice "Configuring #{resource[:name]}" command = [ resource[:manifest], resource[:name] ] #texecute("setupservice", command) rv = system("#{command}") end rescue Puppet::ExecutionFailure => detail raise Puppet::Error.new( "Cannot config #{self.service} to enable it: #{detail}" ) end def enabled? case self.status when :running # obviously if the daemon is running then it is enabled return :true else # the service is enabled if it is linked return FileTest.symlink?(self.service) ? :true : :false end end def enable if ! FileTest.directory?(self.daemon) Puppet.notice "No daemon dir, calling setupservice for #{resource[:name]}" self.setupservice end if self.daemon if ! FileTest.symlink?(self.service) Puppet.notice "Enabling #{self.service}: linking #{self.daemon} -> #{self.service}" File.symlink(self.daemon, self.service) end end rescue Puppet::ExecutionFailure => detail raise Puppet::Error.new( "No daemon directory found for #{self.service}") end def disable begin if ! FileTest.directory?(self.daemon) Puppet.notice "No daemon dir, calling setupservice for #{resource[:name]}" self.setupservice end if self.daemon if FileTest.symlink?(self.service) Puppet.notice "Disabling #{self.service}: removing link #{self.daemon} -> #{self.service}" File.unlink(self.service) end end rescue Puppet::ExecutionFailure => detail raise Puppet::Error.new( "No daemon directory found for #{self.service}") end self.stop end def restart svc "-t", self.service end def start enable unless enabled? == :true svc "-u", self.service end def stop svc "-d", self.service end end diff --git a/lib/puppet/provider/service/debian.rb b/lib/puppet/provider/service/debian.rb index 58b808a8e..6030da1e9 100755 --- a/lib/puppet/provider/service/debian.rb +++ b/lib/puppet/provider/service/debian.rb @@ -1,52 +1,54 @@ # Manage debian services. Start/stop is the same as InitSvc, but enable/disable # is special. Puppet::Type.type(:service).provide :debian, :parent => :init do - desc "Debian's form of `init`-style management. + desc <<-EOT + Debian's form of `init`-style management. - The only difference is that this supports service enabling and disabling - via `update-rc.d` and determines enabled status via `invoke-rc.d`. + The only differences from `init` are support for enabling and disabling + services via `update-rc.d` and the ability to determine enabled status via + `invoke-rc.d`. - " + EOT commands :update_rc => "/usr/sbin/update-rc.d" # note this isn't being used as a command until # http://projects.reductivelabs.com/issues/2538 # is resolved. commands :invoke_rc => "/usr/sbin/invoke-rc.d" defaultfor :operatingsystem => [:debian, :ubuntu] def self.defpath superclass.defpath end # Remove the symlinks def disable 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? # TODO: Replace system call when Puppet::Util.execute gives us a way # to determine exit status. http://projects.reductivelabs.com/issues/2538 system("/usr/sbin/invoke-rc.d", "--quiet", "--query", @resource[:name], "start") # 104 is the exit status when you query start an enabled service. # 106 is the exit status when the policy layer supplies a fallback action # See x-man-page://invoke-rc.d if [104, 106].include?($CHILD_STATUS.exitstatus) return :true else return :false end end def enable update_rc "-f", @resource[:name], "remove" update_rc @resource[:name], "defaults" end end diff --git a/lib/puppet/provider/service/freebsd.rb b/lib/puppet/provider/service/freebsd.rb index f8c7134f0..5e1a36d46 100644 --- a/lib/puppet/provider/service/freebsd.rb +++ b/lib/puppet/provider/service/freebsd.rb @@ -1,139 +1,139 @@ Puppet::Type.type(:service).provide :freebsd, :parent => :init do - desc "Provider for FreeBSD. Makes use of rcvar argument of init scripts and parses/edits rc files." + desc "Provider for FreeBSD. Uses the `rcvar` argument of init scripts and parses/edits rc files." confine :operatingsystem => [:freebsd] defaultfor :operatingsystem => [:freebsd] @@rcconf = '/etc/rc.conf' @@rcconf_local = '/etc/rc.conf.local' @@rcconf_dir = '/etc/rc.conf.d' def self.defpath superclass.defpath end # Executing an init script with the 'rcvar' argument returns # the service name, rcvar name and whether it's enabled/disabled def rcvar rcvar = execute([self.initscript, :rcvar], :failonfail => true, :squelch => false) rcvar = rcvar.split("\n") rcvar.delete_if {|str| str =~ /^#\s*$/} rcvar[1] = rcvar[1].gsub(/^\$/, '') rcvar end # Extract service name def service_name name = self.rcvar[0] self.error("No service name found in rcvar") if name.nil? name = name.gsub!(/# (.*)/, '\1') self.error("Service name is empty") if name.nil? self.debug("Service name is #{name}") name end # Extract rcvar name def rcvar_name name = self.rcvar[1] self.error("No rcvar name found in rcvar") if name.nil? name = name.gsub!(/(.*)_enable=(.*)/, '\1') self.error("rcvar name is empty") if name.nil? self.debug("rcvar name is #{name}") name end # Extract rcvar value def rcvar_value value = self.rcvar[1] self.error("No rcvar value found in rcvar") if value.nil? value = value.gsub!(/(.*)_enable="?(\w+)"?/, '\2') self.error("rcvar value is empty") if value.nil? self.debug("rcvar value is #{value}") value end # Edit rc files and set the service to yes/no def rc_edit(yesno) service = self.service_name rcvar = self.rcvar_name self.debug("Editing rc files: setting #{rcvar} to #{yesno} for #{service}") self.rc_add(service, rcvar, yesno) if not self.rc_replace(service, rcvar, yesno) end # Try to find an existing setting in the rc files # and replace the value def rc_replace(service, rcvar, yesno) success = false # Replace in all files, not just in the first found with a match [@@rcconf, @@rcconf_local, @@rcconf_dir + "/#{service}"].each do |filename| if File.exists?(filename) s = File.read(filename) if s.gsub!(/(#{rcvar}_enable)=\"?(YES|NO)\"?/, "\\1=\"#{yesno}\"") File.open(filename, File::WRONLY) { |f| f << s } self.debug("Replaced in #{filename}") success = true end end end success end # Add a new setting to the rc files def rc_add(service, rcvar, yesno) append = "\# Added by Puppet\n#{rcvar}_enable=\"#{yesno}\"\n" # First, try the one-file-per-service style if File.exists?(@@rcconf_dir) File.open(@@rcconf_dir + "/#{service}", File::WRONLY | File::APPEND | File::CREAT, 0644) { |f| f << append self.debug("Appended to #{f.path}") } else # Else, check the local rc file first, but don't create it if File.exists?(@@rcconf_local) File.open(@@rcconf_local, File::WRONLY | File::APPEND) { |f| f << append self.debug("Appended to #{f.path}") } else # At last use the standard rc.conf file File.open(@@rcconf, File::WRONLY | File::APPEND | File::CREAT, 0644) { |f| f << append self.debug("Appended to #{f.path}") } end end end def enabled? if /YES$/ =~ self.rcvar_value self.debug("Is enabled") return :true end self.debug("Is disabled") :false end def enable self.debug("Enabling") self.rc_edit("YES") end def disable self.debug("Disabling") self.rc_edit("NO") end def startcmd [self.initscript, :onestart] end def stopcmd [self.initscript, :onestop] end def statuscmd [self.initscript, :onestatus] end end diff --git a/lib/puppet/provider/service/gentoo.rb b/lib/puppet/provider/service/gentoo.rb index 20f5d77e6..8928d4459 100644 --- a/lib/puppet/provider/service/gentoo.rb +++ b/lib/puppet/provider/service/gentoo.rb @@ -1,50 +1,51 @@ # Manage gentoo services. Start/stop is the same as InitSvc, but enable/disable # is special. Puppet::Type.type(:service).provide :gentoo, :parent => :init do - desc "Gentoo's form of `init`-style service management. + desc <<-EOT + Gentoo's form of `init`-style service management. - Uses `rc-update` for service enabling and disabling. + Uses `rc-update` for service enabling and disabling. - " + EOT commands :update => "/sbin/rc-update" confine :operatingsystem => :gentoo defaultfor :operatingsystem => :gentoo def self.defpath superclass.defpath end def disable output = update :del, @resource[:name], :default rescue Puppet::ExecutionFailure raise Puppet::Error, "Could not disable #{self.name}: #{output}" end def enabled? begin output = update :show rescue Puppet::ExecutionFailure return :false end line = output.split(/\n/).find { |l| l.include?(@resource[:name]) } return :false unless line # If it's enabled then it will print output showing service | runlevel if output =~ /^\s*#{@resource[:name]}\s*\|\s*(boot|default)/ return :true else return :false end end def enable output = update :add, @resource[:name], :default rescue Puppet::ExecutionFailure raise Puppet::Error, "Could not enable #{self.name}: #{output}" end end diff --git a/lib/puppet/provider/service/init.rb b/lib/puppet/provider/service/init.rb index 447c01aa5..f13e9c8e1 100755 --- a/lib/puppet/provider/service/init.rb +++ b/lib/puppet/provider/service/init.rb @@ -1,141 +1,134 @@ # The standard init-based service type. Many other service types are # customizations of this module. Puppet::Type.type(:service).provide :init, :parent => :base do - desc "Standard init service management. - - This provider assumes that the init script has no `status` command, - because so few scripts do, so you need to either provide a status - command or specify via `hasstatus` that one already exists in the - init script. - -" + desc "Standard `init`-style service management." class << self attr_accessor :defpath end case Facter["operatingsystem"].value when "FreeBSD" @defpath = ["/etc/rc.d", "/usr/local/etc/rc.d"] when "HP-UX" @defpath = "/sbin/init.d" else @defpath = "/etc/init.d" end # We can't confine this here, because the init path can be overridden. #confine :exists => @defpath # List all services of this type. def self.instances get_services(self.defpath) end def self.get_services(defpath, exclude=[]) defpath = [defpath] unless defpath.is_a? Array instances = [] defpath.each do |path| unless FileTest.directory?(path) Puppet.debug "Service path #{path} does not exist" next end check = [:ensure] check << :enable if public_method_defined? :enabled? Dir.entries(path).each do |name| fullpath = File.join(path, name) next if name =~ /^\./ next if exclude.include? name next if not FileTest.executable?(fullpath) instances << new(:name => name, :path => path, :hasstatus => true) end end instances end # Mark that our init script supports 'status' commands. def hasstatus=(value) case value when true, "true"; @parameters[:hasstatus] = true when false, "false"; @parameters[:hasstatus] = false else raise Puppet::Error, "Invalid 'hasstatus' value #{value.inspect}" end end # Where is our init script? def initscript @initscript ||= self.search(@resource[:name]) end def paths @paths ||= @resource[:path].find_all do |path| if File.directory?(path) true else if File.exist?(path) and ! File.directory?(path) self.debug "Search path #{path} is not a directory" else self.debug "Search path #{path} does not exist" end false end end end def search(name) paths.each { |path| fqname = File.join(path,name) begin stat = File.stat(fqname) rescue # should probably rescue specific errors... self.debug("Could not find #{name} in #{path}") next end # if we've gotten this far, we found a valid script return fqname } paths.each { |path| fqname_sh = File.join(path,"#{name}.sh") begin stat = File.stat(fqname_sh) rescue # should probably rescue specific errors... self.debug("Could not find #{name}.sh in #{path}") next end # if we've gotten this far, we found a valid script return fqname_sh } raise Puppet::Error, "Could not find init script for '#{name}'" end # The start command is just the init scriptwith 'start'. def startcmd [initscript, :start] end # The stop command is just the init script with 'stop'. def stopcmd [initscript, :stop] end def restartcmd (@resource[:hasrestart] == :true) && [initscript, :restart] end # If it was specified that the init script has a 'status' command, then # we just return that; otherwise, we return false, which causes it to # fallback to other mechanisms. def statuscmd (@resource[:hasstatus] == :true) && [initscript, :status] end end diff --git a/lib/puppet/provider/service/launchd.rb b/lib/puppet/provider/service/launchd.rb index 4caa1420c..73d4b3c07 100644 --- a/lib/puppet/provider/service/launchd.rb +++ b/lib/puppet/provider/service/launchd.rb @@ -1,296 +1,299 @@ require 'facter/util/plist' Puppet::Type.type(:service).provide :launchd, :parent => :base do - desc "launchd service management framework. + desc <<-EOT + This provider manages jobs with `launchd`, which is the default service + framework for Mac OS X (and may be available for use on other platforms). - This provider manages jobs with launchd, which is the default service framework for - Mac OS X and is potentially available for use on other platforms. + For `launchd` documentation, see: - See: - * http://developer.apple.com/macosx/launchd.html - * http://launchd.macosforge.org/ + * + * - This provider reads plists out of the following directories: - * /System/Library/LaunchDaemons - * /System/Library/LaunchAgents - * /Library/LaunchDaemons - * /Library/LaunchAgents + This provider reads plists out of the following directories: - ...and builds up a list of services based upon each plist's \"Label\" entry. + * `/System/Library/LaunchDaemons` + * `/System/Library/LaunchAgents` + * `/Library/LaunchDaemons` + * `/Library/LaunchAgents` - This provider supports: - * ensure => running/stopped, - * enable => true/false - * status - * restart + ...and builds up a list of services based upon each plist's "Label" entry. - Here is how the Puppet states correspond to launchd states: - * stopped --- job unloaded - * started --- job loaded - * enabled --- 'Disable' removed from job plist file - * disabled --- 'Disable' added to job plist file + This provider supports: - Note that this allows you to do something launchctl can't do, which is to - be in a state of \"stopped/enabled\ or \"running/disabled\". + * ensure => running/stopped, + * enable => true/false + * status + * restart - " + Here is how the Puppet states correspond to `launchd` states: + + * stopped --- job unloaded + * started --- job loaded + * enabled --- 'Disable' removed from job plist file + * disabled --- 'Disable' added to job plist file + + Note that this allows you to do something `launchctl` can't do, which is to + be in a state of "stopped/enabled" or "running/disabled". + + EOT include Puppet::Util::Warnings commands :launchctl => "/bin/launchctl" commands :sw_vers => "/usr/bin/sw_vers" commands :plutil => "/usr/bin/plutil" defaultfor :operatingsystem => :darwin confine :operatingsystem => :darwin has_feature :enableable mk_resource_methods Launchd_Paths = [ "/Library/LaunchAgents", "/Library/LaunchDaemons", "/System/Library/LaunchAgents", "/System/Library/LaunchDaemons"] Launchd_Overrides = "/var/db/launchd.db/com.apple.launchd/overrides.plist" # Caching is enabled through the following three methods. Self.prefetch will # call self.instances to create an instance for each service. Self.flush will # clear out our cache when we're done. def self.prefetch(resources) instances.each do |prov| if resource = resources[prov.name] resource.provider = prov end end end # Self.instances will return an array with each element being a hash # containing the name, provider, path, and status of each service on the # system. def self.instances jobs = self.jobsearch @job_list ||= self.job_list jobs.keys.collect do |job| job_status = @job_list.has_key?(job) ? :running : :stopped new(:name => job, :provider => :launchd, :path => jobs[job], :status => job_status) end end # Sets a class instance variable with a hash of all launchd plist files that # are found on the system. The key of the hash is the job id and the value # is the path to the file. If a label is passed, we return the job id and # path for that specific job. def self.jobsearch(label=nil) @label_to_path_map ||= {} if @label_to_path_map.empty? Launchd_Paths.each do |path| Dir.glob(File.join(path,'*')).each do |filepath| next if ! File.file?(filepath) job = read_plist(filepath) if job.has_key?("Label") and job["Label"] == label return { label => filepath } else @label_to_path_map[job["Label"]] = filepath end end end end if label if @label_to_path_map.has_key? label return { label => @label_to_path_map[label] } else raise Puppet::Error.new("Unable to find launchd plist for job: #{label}") end else @label_to_path_map end end # This status method lists out all currently running services. # This hash is returned at the end of the method. def self.job_list @job_list = Hash.new begin output = launchctl :list raise Puppet::Error.new("launchctl list failed to return any data.") if output.nil? output.split("\n").each do |line| @job_list[line.split(/\s/).last] = :running end rescue Puppet::ExecutionFailure raise Puppet::Error.new("Unable to determine status of #{resource[:name]}") end @job_list end # Launchd implemented plist overrides in version 10.6. # This method checks the major_version of OS X and returns true if # it is 10.6 or greater. This allows us to implement different plist # behavior for versions >= 10.6 def has_macosx_plist_overrides? @product_version ||= self.class.get_macosx_version_major return true unless /^10\.[0-5]/.match(@product_version) return false end # Read a plist, whether its format is XML or in Apple's "binary1" # format. def self.read_plist(path) Plist::parse_xml(plutil('-convert', 'xml1', '-o', '/dev/stdout', path)) end # Clean out the @property_hash variable containing the cached list of services def flush @property_hash.clear end def exists? Puppet.debug("Puppet::Provider::Launchd:Ensure for #{@property_hash[:name]}: #{@property_hash[:ensure]}") @property_hash[:ensure] != :absent end def self.get_macosx_version_major return @macosx_version_major if @macosx_version_major begin # Make sure we've loaded all of the facts Facter.loadfacts if Facter.value(:macosx_productversion_major) product_version_major = Facter.value(:macosx_productversion_major) else # TODO: remove this code chunk once we require Facter 1.5.5 or higher. warnonce("DEPRECATION WARNING: Future versions of the launchd provider will require Facter 1.5.5 or newer.") product_version = Facter.value(:macosx_productversion) fail("Could not determine OS X version from Facter") if product_version.nil? product_version_major = product_version.scan(/(\d+)\.(\d+)./).join(".") end fail("#{product_version_major} is not supported by the launchd provider") if %w{10.0 10.1 10.2 10.3}.include?(product_version_major) @macosx_version_major = product_version_major return @macosx_version_major rescue Puppet::ExecutionFailure => detail fail("Could not determine OS X version: #{detail}") end end # finds the path for a given label and returns the path and parsed plist # as an array of [path, plist]. Note plist is really a Hash here. def plist_from_label(label) job = self.class.jobsearch(label) job_path = job[label] if FileTest.file?(job_path) job_plist = self.class.read_plist(job_path) else raise Puppet::Error.new("Unable to parse launchd plist at path: #{job_path}") end [job_path, job_plist] end # start the service. To get to a state of running/enabled, we need to # conditionally enable at load, then disable by modifying the plist file # directly. def start job_path, job_plist = plist_from_label(resource[:name]) did_enable_job = false cmds = [] cmds << :launchctl << :load if self.enabled? == :false # launchctl won't load disabled jobs cmds << "-w" did_enable_job = true end cmds << job_path begin execute(cmds) rescue Puppet::ExecutionFailure raise Puppet::Error.new("Unable to start service: #{resource[:name]} at path: #{job_path}") end # As load -w clears the Disabled flag, we need to add it in after self.disable if did_enable_job and resource[:enable] == :false end def stop job_path, job_plist = plist_from_label(resource[:name]) did_disable_job = false cmds = [] cmds << :launchctl << :unload if self.enabled? == :true # keepalive jobs can't be stopped without disabling cmds << "-w" did_disable_job = true end cmds << job_path begin execute(cmds) rescue Puppet::ExecutionFailure raise Puppet::Error.new("Unable to stop service: #{resource[:name]} at path: #{job_path}") end # As unload -w sets the Disabled flag, we need to add it in after self.enable if did_disable_job and resource[:enable] == :true end # launchd jobs are enabled by default. They are only disabled if the key # "Disabled" is set to true, but it can also be set to false to enable it. # Starting in 10.6, the Disabled key in the job plist is consulted, but only # if there is no entry in the global overrides plist. # We need to draw a distinction between undefined, true and false for both # locations where the Disabled flag can be defined. def enabled? job_plist_disabled = nil overrides_disabled = nil job_path, job_plist = plist_from_label(resource[:name]) job_plist_disabled = job_plist["Disabled"] if job_plist.has_key?("Disabled") if has_macosx_plist_overrides? if FileTest.file?(Launchd_Overrides) and overrides = self.class.read_plist(Launchd_Overrides) if overrides.has_key?(resource[:name]) overrides_disabled = overrides[resource[:name]]["Disabled"] if overrides[resource[:name]].has_key?("Disabled") end end end if overrides_disabled.nil? if job_plist_disabled.nil? or job_plist_disabled == false return :true end elsif overrides_disabled == false return :true end :false end # enable and disable are a bit hacky. We write out the plist with the appropriate value # rather than dealing with launchctl as it is unable to change the Disabled flag # without actually loading/unloading the job. # Starting in 10.6 we need to write out a disabled key to the global # overrides plist, in earlier versions this is stored in the job plist itself. def enable if has_macosx_plist_overrides? overrides = self.class.read_plist(Launchd_Overrides) overrides[resource[:name]] = { "Disabled" => false } Plist::Emit.save_plist(overrides, Launchd_Overrides) else job_path, job_plist = plist_from_label(resource[:name]) if self.enabled? == :false job_plist.delete("Disabled") Plist::Emit.save_plist(job_plist, job_path) end end end def disable if has_macosx_plist_overrides? overrides = self.class.read_plist(Launchd_Overrides) overrides[resource[:name]] = { "Disabled" => true } Plist::Emit.save_plist(overrides, Launchd_Overrides) else job_path, job_plist = plist_from_label(resource[:name]) job_plist["Disabled"] = true Plist::Emit.save_plist(job_plist, job_path) end end end diff --git a/lib/puppet/provider/service/redhat.rb b/lib/puppet/provider/service/redhat.rb index e851a488d..b600e1b2e 100755 --- a/lib/puppet/provider/service/redhat.rb +++ b/lib/puppet/provider/service/redhat.rb @@ -1,76 +1,75 @@ # Manage Red Hat services. Start/stop uses /sbin/service and enable/disable uses chkconfig Puppet::Type.type(:service).provide :redhat, :parent => :init, :source => :init do - desc "Red Hat's (and probably many others) form of `init`-style service management: - - Uses `chkconfig` for service enabling and disabling. + desc "Red Hat's (and probably many others') form of `init`-style service + management. Uses `chkconfig` for service enabling and disabling. " commands :chkconfig => "/sbin/chkconfig", :service => "/sbin/service" defaultfor :operatingsystem => [:redhat, :fedora, :suse, :centos, :sles, :oel, :ovm] def self.instances # this exclude list is all from /sbin/service (5.x), but I did not exclude kudzu self.get_services(['/etc/init.d'], ['functions', 'halt', 'killall', 'single', 'linuxconf']) end def self.defpath superclass.defpath end # Remove the symlinks def disable output = chkconfig(@resource[:name], :off) rescue Puppet::ExecutionFailure raise Puppet::Error, "Could not disable #{self.name}: #{output}" end def enabled? begin output = chkconfig(@resource[:name]) rescue Puppet::ExecutionFailure return :false end # If it's disabled on SuSE, then it will print output showing "off" # at the end if output =~ /.* off$/ return :false end :true end # Don't support them specifying runlevels; always use the runlevels # in the init scripts. def enable output = chkconfig(@resource[:name], :on) rescue Puppet::ExecutionFailure => detail raise Puppet::Error, "Could not enable #{self.name}: #{detail}" end def initscript raise Puppet::Error, "Do not directly call the init script for '#{@resource[:name]}'; use 'service' instead" end # use hasstatus=>true when its set for the provider. def statuscmd ((@resource.provider.get(:hasstatus) == true) || (@resource[:hasstatus] == :true)) && [command(:service), @resource[:name], "status"] end def restartcmd (@resource[:hasrestart] == :true) && [command(:service), @resource[:name], "restart"] end def startcmd [command(:service), @resource[:name], "start"] end def stopcmd [command(:service), @resource[:name], "stop"] end end diff --git a/lib/puppet/provider/service/runit.rb b/lib/puppet/provider/service/runit.rb index 736e3db71..c923bd28e 100644 --- a/lib/puppet/provider/service/runit.rb +++ b/lib/puppet/provider/service/runit.rb @@ -1,103 +1,103 @@ # Daemontools service management # # author Brice Figureau Puppet::Type.type(:service).provide :runit, :parent => :daemontools do - desc "Runit service management. + desc <<-EOT + Runit service management. - This provider manages daemons running supervised by Runit. - It tries to detect the service directory, with by order of preference: + This provider manages daemons running supervised by Runit. + When detecting the service directory it will check, in order of preference: - * /service - * /var/service - * /etc/service + * `/service` + * `/var/service` + * `/etc/service` - The daemon directory should be placed in a directory that can be - by default in: + The daemon directory should be in one of the following locations: - * /etc/sv + * `/etc/sv` - or this can be overriden in the service resource parameters:: + or this can be overriden in the service resource parameters:: - service { \"myservice\": - provider => \"runit\", - path => \"/path/to/daemons\", - } + service { "myservice": + provider => "runit", + path => "/path/to/daemons", + } - This provider supports out of the box: + This provider supports out of the box: - * start/stop - * enable/disable - * restart - * status + * start/stop + * enable/disable + * restart + * status -" + EOT commands :sv => "/usr/bin/sv" class << self # this is necessary to autodetect a valid resource # default path, since there is no standard for such directory. def defpath(dummy_argument=:work_arround_for_ruby_GC_bug) unless @defpath ["/etc/sv", "/var/lib/service"].each do |path| if FileTest.exist?(path) @defpath = path break end end raise "Could not find the daemon directory (tested [/var/lib/service,/etc])" unless @defpath end @defpath end end # find the service dir on this node def servicedir unless @servicedir ["/service", "/etc/service","/var/service"].each do |path| if FileTest.exist?(path) @servicedir = path break end end raise "Could not find service directory" unless @servicedir end @servicedir end def status begin output = sv "status", self.daemon return :running if output =~ /^run: / rescue Puppet::ExecutionFailure => detail unless detail.message =~ /(warning: |runsv not running$)/ raise Puppet::Error.new( "Could not get status for service #{resource.ref}: #{detail}" ) end end :stopped end def stop sv "stop", self.service end def start enable unless enabled? == :true sv "start", self.service end def restart sv "restart", self.service end # disable by removing the symlink so that runit # doesn't restart our service behind our back # note that runit doesn't need to perform a stop # before a disable def disable # unlink the daemon symlink to disable it File.unlink(self.service) if FileTest.symlink?(self.service) end end diff --git a/lib/puppet/provider/service/smf.rb b/lib/puppet/provider/service/smf.rb index f6f221a7c..c8cc262e9 100755 --- a/lib/puppet/provider/service/smf.rb +++ b/lib/puppet/provider/service/smf.rb @@ -1,103 +1,104 @@ # Solaris 10 SMF-style services. Puppet::Type.type(:service).provide :smf, :parent => :base do - desc "Support for Sun's new Service Management Framework. + desc <<-EOT + Support for Sun's new Service Management Framework. - Starting a service is effectively equivalent to enabling it, so there is - only support for starting and stopping services, which also enables and - disables them, respectively. + Starting a service is effectively equivalent to enabling it, so there is + only support for starting and stopping services, which also enables and + disables them, respectively. - By specifying manifest => \"/path/to/service.xml\", the SMF manifest will - be imported if it does not exist. + By specifying `manifest => "/path/to/service.xml"`, the SMF manifest will + be imported if it does not exist. - " + EOT defaultfor :operatingsystem => :solaris confine :operatingsystem => :solaris commands :adm => "/usr/sbin/svcadm", :svcs => "/usr/bin/svcs" commands :svccfg => "/usr/sbin/svccfg" def setupservice if resource[:manifest] [command(:svcs), "-l", @resource[:name]] if $CHILD_STATUS.exitstatus == 1 Puppet.notice "Importing #{@resource[:manifest]} for #{@resource[:name]}" svccfg :import, resource[:manifest] end end rescue Puppet::ExecutionFailure => detail raise Puppet::Error.new( "Cannot config #{self.name} to enable it: #{detail}" ) end def enable self.start end def enabled? case self.status when :running return :true else return :false end end def disable self.stop end def restartcmd [command(:adm), :restart, @resource[:name]] end def startcmd self.setupservice case self.status when :maintenance [command(:adm), :clear, @resource[:name]] else [command(:adm), :enable, @resource[:name]] end end def status if @resource[:status] super return end begin # get the current state and the next state, and if the next # state is set (i.e. not "-") use it for state comparison states = svcs("-H", "-o", "state,nstate", @resource[:name]).chomp.split state = states[1] == "-" ? states[0] : states[1] rescue Puppet::ExecutionFailure info "Could not get status on service #{self.name}" return :stopped end case state when "online" #self.warning "matched running #{line.inspect}" return :running when "offline", "disabled", "uninitialized" #self.warning "matched stopped #{line.inspect}" return :stopped when "maintenance" return :maintenance when "legacy_run" raise Puppet::Error, "Cannot manage legacy services through SMF" else raise Puppet::Error, "Unmanageable state '#{state}' on service #{self.name}" end end def stopcmd [command(:adm), :disable, @resource[:name]] end end diff --git a/lib/puppet/provider/service/src.rb b/lib/puppet/provider/service/src.rb index 2bd643c0b..eced271d4 100755 --- a/lib/puppet/provider/service/src.rb +++ b/lib/puppet/provider/service/src.rb @@ -1,87 +1,86 @@ # AIX System Resource controller (SRC) Puppet::Type.type(:service).provide :src, :parent => :base do desc "Support for AIX's System Resource controller. - Services are started/stopped based on the stopsrc and startsrc - commands, and some services can be refreshed with refresh command. + Services are started/stopped based on the `stopsrc` and `startsrc` + commands, and some services can be refreshed with `refresh` command. - * Enabling and disableing services is not supported, as it requires - modifications to /etc/inittab. - - * Starting and stopping groups of subsystems is not yet supported + Enabling and disabling services is not supported, as it requires + modifications to `/etc/inittab`. Starting and stopping groups of subsystems + is not yet supported. " defaultfor :operatingsystem => :aix confine :operatingsystem => :aix commands :stopsrc => "/usr/bin/stopsrc" commands :startsrc => "/usr/bin/startsrc" commands :refresh => "/usr/bin/refresh" commands :lssrc => "/usr/bin/lssrc" has_feature :refreshable def startcmd [command(:startsrc), "-s", @resource[:name]] end def stopcmd [command(:stopsrc), "-s", @resource[:name]] end def restart execute([command(:lssrc), "-Ss", @resource[:name]]).each do |line| args = line.split(":") next unless args[0] == @resource[:name] # Subsystems with the -K flag can get refreshed (HUPed) # While subsystems with -S (signals) must be stopped/started method = args[11] do_refresh = case method when "-K" then :true when "-S" then :false else self.fail("Unknown service communication method #{method}") end begin if do_refresh == :true execute([command(:refresh), "-s", @resource[:name]]) else self.stop self.start end return :true rescue Puppet::ExecutionFailure => detail raise Puppet::Error.new("Unable to restart service #{@resource[:name]}, error was: #{detail}" ) end end self.fail("No such service found") rescue Puppet::ExecutionFailure => detail raise Puppet::Error.new("Cannot get status of #{@resource[:name]}, error was: #{detail}" ) end def status execute([command(:lssrc), "-s", @resource[:name]]).each do |line| args = line.split # This is the header line next unless args[0] == @resource[:name] # PID is the 3rd field, but inoperative subsystems # skip this so split doesn't work right state = case args[-1] when "active" then :running when "inoperative" then :stopped end Puppet.debug("Service #{@resource[:name]} is #{args[-1]}") return state end self.fail("No such service found") rescue Puppet::ExecutionFailure => detail raise Puppet::Error.new("Cannot get status of #{@resource[:name]}, error was: #{detail}" ) end end diff --git a/lib/puppet/provider/service/systemd.rb b/lib/puppet/provider/service/systemd.rb index b6b3b386f..c67a437c5 100755 --- a/lib/puppet/provider/service/systemd.rb +++ b/lib/puppet/provider/service/systemd.rb @@ -1,64 +1,64 @@ # Manage systemd services using /bin/systemctl Puppet::Type.type(:service).provide :systemd, :parent => :base do - desc "Manage systemd services using /bin/systemctl" + desc "Manages `systemd` services using `/bin/systemctl`." commands :systemctl => "/bin/systemctl" #defaultfor :operatingsystem => [:redhat, :fedora, :suse, :centos, :sles, :oel, :ovm] def self.instances i = [] output = `systemctl list-units --full --all --no-pager` output.scan(/^(\S+)\s+(loaded|error)\s+(active|inactive)\s+(active|waiting|running|plugged|mounted|dead|exited|listening|elapsed)\s*?(\S.*?)?$/i).each do |m| i << m[0] end return i rescue Puppet::ExecutionFailure return [] end def disable output = systemctl(:disable, @resource[:name]) rescue Puppet::ExecutionFailure raise Puppet::Error, "Could not disable #{self.name}: #{output}" end def enabled? begin systemctl("is-enabled", @resource[:name]) rescue Puppet::ExecutionFailure return :false end :true end def status begin output = systemctl("is-active", @resource[:name]) rescue Puppet::ExecutionFailure return :stopped end return :running end def enable output = systemctl("enable", @resource[:name]) rescue Puppet::ExecutionFailure raise Puppet::Error, "Could not enable #{self.name}: #{output}" end def restartcmd [command(:systemctl), "restart", @resource[:name]] end def startcmd [command(:systemctl), "start", @resource[:name]] end def stopcmd [command(:systemctl), "stop", @resource[:name]] end end diff --git a/lib/puppet/provider/service/upstart.rb b/lib/puppet/provider/service/upstart.rb index 54971eeac..5249a914c 100755 --- a/lib/puppet/provider/service/upstart.rb +++ b/lib/puppet/provider/service/upstart.rb @@ -1,73 +1,71 @@ Puppet::Type.type(:service).provide :upstart, :parent => :init do - desc "Ubuntu service manager upstart. + desc "Ubuntu service management with `upstart`. - This provider manages upstart jobs which have replaced initd. - - See: - * http://upstart.ubuntu.com/ + This provider manages `upstart` jobs, which have replaced `initd` services + on Ubuntu. For `upstart` documentation, see . " # confine to :ubuntu for now because I haven't tested on other platforms confine :operatingsystem => :ubuntu #[:ubuntu, :fedora, :debian] commands :start => "/sbin/start", :stop => "/sbin/stop", :restart => "/sbin/restart", :status_exec => "/sbin/status", :initctl => "/sbin/initctl" # upstart developer haven't implemented initctl enable/disable yet: # http://www.linuxplanet.com/linuxplanet/tutorials/7033/2/ # has_feature :enableable def self.instances instances = [] execpipe("#{command(:initctl)} list") { |process| process.each { |line| # needs special handling of services such as network-interface: # initctl list: # network-interface (lo) start/running # network-interface (eth0) start/running # network-interface-security start/running name = \ if matcher = line.match(/^(network-interface)\s\(([^\)]+)\)/) "#{matcher[1]} INTERFACE=#{matcher[2]}" else line.split.first end instances << new(:name => name) } } instances end def startcmd [command(:start), @resource[:name]] end def stopcmd [command(:stop), @resource[:name]] end def restartcmd (@resource[:hasrestart] == :true) && [command(:restart), @resource[:name]] end def status # allows user override of status command if @resource[:status] ucommand(:status, false) if $?.exitstatus == 0 return :running else return :stopped end else output = status_exec(@resource[:name].split) if (! $?.nil?) && (output =~ /start\//) return :running else return :stopped end end end end diff --git a/lib/puppet/provider/service/windows.rb b/lib/puppet/provider/service/windows.rb index 162dc8381..e773fa546 100644 --- a/lib/puppet/provider/service/windows.rb +++ b/lib/puppet/provider/service/windows.rb @@ -1,110 +1,110 @@ # Windows Service Control Manager (SCM) provider require 'win32/service' if Puppet.features.microsoft_windows? Puppet::Type.type(:service).provide :windows do - desc "Support for Windows Service Control Manager (SCM). + desc <<-EOT + Support for Windows Service Control Manager (SCM). - Services are controlled according to win32-service gem. - - * All SCM operations (start/stop/enable/disable/query) are supported. - - * Control of service groups (dependencies) is not yet supported." + Services are controlled according to the capabilities of the `win32-service` + gem. All SCM operations (start/stop/enable/disable/query) are supported. + Control of service groups (dependencies) is not yet supported. + EOT defaultfor :operatingsystem => :windows confine :operatingsystem => :windows has_feature :refreshable def enable w32ss = Win32::Service.configure( 'service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_AUTO_START ) raise Puppet::Error.new("Win32 service enable of #{@resource[:name]} failed" ) if( w32ss.nil? ) rescue Win32::Service::Error => detail raise Puppet::Error.new("Cannot enable #{@resource[:name]}, error was: #{detail}" ) end def disable w32ss = Win32::Service.configure( 'service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_DISABLED ) raise Puppet::Error.new("Win32 service disable of #{@resource[:name]} failed" ) if( w32ss.nil? ) rescue Win32::Service::Error => detail raise Puppet::Error.new("Cannot disable #{@resource[:name]}, error was: #{detail}" ) end def manual_start w32ss = Win32::Service.configure( 'service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_DEMAND_START ) raise Puppet::Error.new("Win32 service manual enable of #{@resource[:name]} failed" ) if( w32ss.nil? ) rescue Win32::Service::Error => detail raise Puppet::Error.new("Cannot enable #{@resource[:name]} for manual start, error was: #{detail}" ) end def enabled? w32ss = Win32::Service.config_info( @resource[:name] ) raise Puppet::Error.new("Win32 service query of #{@resource[:name]} failed" ) unless( !w32ss.nil? && w32ss.instance_of?( Struct::ServiceConfigInfo ) ) debug("Service #{@resource[:name]} start type is #{w32ss.start_type}") case w32ss.start_type when Win32::Service.get_start_type(Win32::Service::SERVICE_AUTO_START), Win32::Service.get_start_type(Win32::Service::SERVICE_BOOT_START), Win32::Service.get_start_type(Win32::Service::SERVICE_SYSTEM_START) :true when Win32::Service.get_start_type(Win32::Service::SERVICE_DEMAND_START) :manual when Win32::Service.get_start_type(Win32::Service::SERVICE_DISABLED) :false else raise Puppet::Error.new("Unknown start type: #{w32ss.start_type}") end rescue Win32::Service::Error => detail raise Puppet::Error.new("Cannot get start type for #{@resource[:name]}, error was: #{detail}" ) end def start if enabled? == :false # If disabled and not managing enable, respect disabled and fail. if @resource[:enable].nil? raise Puppet::Error, "Will not start disabled service #{@resource[:name]} without managing enable. Specify 'enable => false' to override." # Otherwise start. If enable => false, we will later sync enable and # disable the service again. elsif @resource[:enable] == :true enable else manual_start end end Win32::Service.start( @resource[:name] ) rescue Win32::Service::Error => detail raise Puppet::Error.new("Cannot start #{@resource[:name]}, error was: #{detail}" ) end def stop Win32::Service.stop( @resource[:name] ) rescue Win32::Service::Error => detail raise Puppet::Error.new("Cannot stop #{@resource[:name]}, error was: #{detail}" ) end def restart self.stop self.start end def status w32ss = Win32::Service.status( @resource[:name] ) raise Puppet::Error.new("Win32 service query of #{@resource[:name]} failed" ) unless( !w32ss.nil? && w32ss.instance_of?( Struct::ServiceStatus ) ) state = case w32ss.current_state when "stopped", "pause pending", "stop pending", "paused" then :stopped when "running", "continue pending", "start pending" then :running else raise Puppet::Error.new("Unknown service state '#{w32ss.current_state}' for service '#{@resource[:name]}'") end debug("Service #{@resource[:name]} is #{w32ss.current_state}") return state rescue Win32::Service::Error => detail raise Puppet::Error.new("Cannot get status of #{@resource[:name]}, error was: #{detail}" ) end # returns all providers for all existing services and startup state def self.instances Win32::Service.services.collect { |s| new(:name => s.service_name) } end end diff --git a/lib/puppet/provider/user/aix.rb b/lib/puppet/provider/user/aix.rb index 032d2b536..a33ed677c 100755 --- a/lib/puppet/provider/user/aix.rb +++ b/lib/puppet/provider/user/aix.rb @@ -1,353 +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 # 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" + desc "User management for AIX." # 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.= 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 + 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 + 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 + 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 + # 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*$/ + 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 + 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 + 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 + 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/user/hpux.rb b/lib/puppet/provider/user/hpux.rb index 983970935..f0c8f7e8f 100644 --- a/lib/puppet/provider/user/hpux.rb +++ b/lib/puppet/provider/user/hpux.rb @@ -1,29 +1,31 @@ Puppet::Type.type(:user).provide :hpuxuseradd, :parent => :useradd do - desc "User management for hp-ux! Undocumented switch to special usermod because HP-UX regular usermod is TOO STUPID to change stuff while the user is logged in." + desc "User management for HP-UX. This provider uses the undocumented `-F` + switch to HP-UX's special `usermod` binary to work around the fact that + its standard `usermod` cannot make changes while the user is logged in." defaultfor :operatingsystem => "hp-ux" confine :operatingsystem => "hp-ux" commands :modify => "/usr/sam/lbin/usermod.sam", :delete => "/usr/sam/lbin/userdel.sam", :add => "/usr/sbin/useradd" options :comment, :method => :gecos options :groups, :flag => "-G" options :home, :flag => "-d", :method => :dir verify :gid, "GID must be an integer" do |value| value.is_a? Integer end verify :groups, "Groups must be comma-separated" do |value| value !~ /\s/ end has_features :manages_homedir, :allows_duplicates def deletecmd super.insert(1,"-F") end def modifycmd(param,value) super.insert(1,"-F") end end diff --git a/lib/puppet/provider/user/ldap.rb b/lib/puppet/provider/user/ldap.rb index 75a9667b3..2601fdb20 100644 --- a/lib/puppet/provider/user/ldap.rb +++ b/lib/puppet/provider/user/ldap.rb @@ -1,129 +1,129 @@ require 'puppet/provider/ldap' Puppet::Type.type(:user).provide :ldap, :parent => Puppet::Provider::Ldap do - desc "User management via `ldap`. This provider requires that you - have valid values for all of the ldap-related settings, - including `ldapbase`. You will also almost definitely need settings - for `ldapuser` and `ldappassword`, so that your clients can write - to ldap. + desc "User management via LDAP. + + This provider requires that you have valid values for all of the + LDAP-related settings in `puppet.conf`, including `ldapbase`. You will + almost definitely need settings for `ldapuser` and `ldappassword` in order + for your clients to write to LDAP. Note that this provider will automatically generate a UID for you if you do not specify one, but it is a potentially expensive operation, - as it iterates across all existing users to pick the appropriate next - one." + as it iterates across all existing users to pick the appropriate next one." confine :feature => :ldap, :false => (Puppet[:ldapuser] == "") has_feature :manages_passwords manages(:posixAccount, :person).at("ou=People").named_by(:uid).and.maps :name => :uid, :password => :userPassword, :comment => :cn, :uid => :uidNumber, :gid => :gidNumber, :home => :homeDirectory, :shell => :loginShell # Use the last field of a space-separated array as # the sn. LDAP requires a surname, for some stupid reason. manager.generates(:sn).from(:cn).with do |cn| x = 1 cn[0].split(/\s+/)[-1] end # Find the next uid after the current largest uid. provider = self manager.generates(:uidNumber).with do largest = 500 if existing = provider.manager.search existing.each do |hash| next unless value = hash[:uid] num = value[0].to_i largest = num if num > largest end end largest + 1 end # Convert our gid to a group name, if necessary. def gid=(value) value = group2id(value) unless [Fixnum, Bignum].include?(value.class) @property_hash[:gid] = value end # Find all groups this user is a member of in ldap. def groups # We want to cache the current result, so we know if we # have to remove old values. unless @property_hash[:groups] unless result = group_manager.search("memberUid=#{name}") return @property_hash[:groups] = :absent end return @property_hash[:groups] = result.collect { |r| r[:name] }.sort.join(",") end @property_hash[:groups] end # Manage the list of groups this user is a member of. def groups=(values) should = values.split(",") if groups == :absent is = [] else is = groups.split(",") end modes = {} [is, should].flatten.uniq.each do |group| # Skip it when they're in both next if is.include?(group) and should.include?(group) # We're adding a group. modes[group] = :add and next unless is.include?(group) # We're removing a group. modes[group] = :remove and next unless should.include?(group) end modes.each do |group, form| self.fail "Could not find ldap group #{group}" unless ldap_group = group_manager.find(group) current = ldap_group[:members] if form == :add if current.is_a?(Array) and ! current.empty? new = current + [name] else new = [name] end else new = current - [name] new = :absent if new.empty? end group_manager.update(group, {:ensure => :present, :members => current}, {:ensure => :present, :members => new}) end end # Convert a gropu name to an id. def group2id(group) Puppet::Type.type(:group).provider(:ldap).name2id(group) end private def group_manager Puppet::Type.type(:group).provider(:ldap).manager end def group_properties(values) if values.empty? or values == :absent {:ensure => :present} else {:ensure => :present, :members => values} end end end diff --git a/lib/puppet/provider/user/user_role_add.rb b/lib/puppet/provider/user/user_role_add.rb index 6e18b7079..5bed1ee59 100644 --- a/lib/puppet/provider/user/user_role_add.rb +++ b/lib/puppet/provider/user/user_role_add.rb @@ -1,196 +1,196 @@ require 'puppet/util/user_attr' Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd, :source => :useradd do - desc "User management inherits `useradd` and adds logic to manage roles on Solaris using roleadd." + desc "User and role management on Solaris, via `useradd` and `roleadd`." defaultfor :operatingsystem => :solaris commands :add => "useradd", :delete => "userdel", :modify => "usermod", :password => "passwd", :role_add => "roleadd", :role_delete => "roledel", :role_modify => "rolemod" options :home, :flag => "-d", :method => :dir options :comment, :method => :gecos options :groups, :flag => "-G" options :roles, :flag => "-R" options :auths, :flag => "-A" options :profiles, :flag => "-P" options :password_min_age, :flag => "-n" options :password_max_age, :flag => "-x" verify :gid, "GID must be an integer" do |value| value.is_a? Integer end verify :groups, "Groups must be comma-separated" do |value| value !~ /\s/ end has_features :manages_homedir, :allows_duplicates, :manages_solaris_rbac, :manages_passwords, :manages_password_age #must override this to hand the keyvalue pairs def add_properties cmd = [] Puppet::Type.type(:user).validproperties.each do |property| #skip the password because we can't create it with the solaris useradd next if [:ensure, :password, :password_min_age, :password_max_age].include?(property) # 1680 Now you can set the hashed passwords on solaris:lib/puppet/provider/user/user_role_add.rb # the value needs to be quoted, mostly because -c might # have spaces in it if value = @resource.should(property) and value != "" if property == :keys cmd += build_keys_cmd(value) else cmd << flag(property) << value end end end cmd end def user_attributes @user_attributes ||= UserAttr.get_attributes_by_name(@resource[:name]) end def flush @user_attributes = nil end def command(cmd) cmd = ("role_#{cmd}").intern if is_role? or (!exists? and @resource[:ensure] == :role) super(cmd) end def is_role? user_attributes and user_attributes[:type] == "role" end def run(cmd, msg) execute(cmd) rescue Puppet::ExecutionFailure => detail raise Puppet::Error, "Could not #{msg} #{@resource.class.name} #{@resource.name}: #{detail}" end def transition(type) cmd = [command(:modify)] cmd << "-K" << "type=#{type}" cmd += add_properties cmd << @resource[:name] end def create if is_role? run(transition("normal"), "transition role to") else run(addcmd, "create") if cmd = passcmd run(cmd, "change password policy for") end end # added to handle case when password is specified self.password = @resource[:password] if @resource[:password] end def destroy run(deletecmd, "delete "+ (is_role? ? "role" : "user")) end def create_role if exists? and !is_role? run(transition("role"), "transition user to") else run(addcmd, "create role") end end def roles user_attributes[:roles] if user_attributes end def auths user_attributes[:auths] if user_attributes end def profiles user_attributes[:profiles] if user_attributes end def project user_attributes[:project] if user_attributes end def managed_attributes [:name, :type, :roles, :auths, :profiles, :project] end def remove_managed_attributes managed = managed_attributes user_attributes.select { |k,v| !managed.include?(k) }.inject({}) { |hash, array| hash[array[0]] = array[1]; hash } end def keys if user_attributes #we have to get rid of all the keys we are managing another way remove_managed_attributes end end def build_keys_cmd(keys_hash) cmd = [] keys_hash.each do |k,v| cmd << "-K" << "#{k}=#{v}" end cmd end def keys=(keys_hash) run([command(:modify)] + build_keys_cmd(keys_hash) << @resource[:name], "modify attribute key pairs") end #Read in /etc/shadow, find the line for this user (skipping comments, because who knows) and return it #No abstraction, all esoteric knowledge of file formats, yay def shadow_entry return @shadow_entry if defined? @shadow_entry @shadow_entry = File.readlines("/etc/shadow").reject { |r| r =~ /^[^\w]/ }.collect { |l| l.chomp.split(':') }.find { |user, _| user == @resource[:name] } end def password shadow_entry[1] if shadow_entry end def password_min_age shadow_entry ? shadow_entry[3] : :absent end def password_max_age shadow_entry ? shadow_entry[4] : :absent end #Read in /etc/shadow, find the line for our used and rewrite it with the new pw #Smooth like 80 grit def password=(cryptopw) begin File.open(shadow_file, "r") do |shadow| File.open("#{shadow_file}_tmp", "w", 0600) do |shadow_tmp| while line = shadow.gets line_arr = line.split(':') if line_arr[0] == @resource[:name] line_arr[1] = cryptopw line_arr[2] = Time.now().to_i / 86400 line = line_arr.join(':') end shadow_tmp.print line end end end File.rename("#{shadow_file}_tmp", shadow_file) rescue => detail fail "Could not write temporary shadow file: #{detail}" ensure # Make sure this *always* gets deleted File.unlink("#{shadow_file}_tmp") if File.exist?("#{shadow_file}_tmp") end end private def shadow_file; '/etc/shadow'; end end diff --git a/lib/puppet/provider/user/useradd.rb b/lib/puppet/provider/user/useradd.rb index b87971738..e3749057f 100644 --- a/lib/puppet/provider/user/useradd.rb +++ b/lib/puppet/provider/user/useradd.rb @@ -1,114 +1,116 @@ require 'puppet/provider/nameservice/objectadd' Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameService::ObjectAdd do - desc "User management via `useradd` and its ilk. Note that you will need to install the `Shadow Password` Ruby library often known as ruby-libshadow to manage user passwords." + desc "User management via `useradd` and its ilk. Note that you will need to + install Ruby's shadow password library (often known as `ruby-libshadow`) + if you wish to manage user passwords." commands :add => "useradd", :delete => "userdel", :modify => "usermod", :password => "chage" options :home, :flag => "-d", :method => :dir options :comment, :method => :gecos options :groups, :flag => "-G" options :password_min_age, :flag => "-m" options :password_max_age, :flag => "-M" verify :gid, "GID must be an integer" do |value| value.is_a? Integer end verify :groups, "Groups must be comma-separated" do |value| value !~ /\s/ end has_features :manages_homedir, :allows_duplicates, :manages_expiry, :system_users has_features :manages_passwords, :manages_password_age if Puppet.features.libshadow? def check_allow_dup @resource.allowdupe? ? ["-o"] : [] end def check_manage_home cmd = [] if @resource.managehome? cmd << "-m" elsif %w{Fedora RedHat CentOS OEL OVS}.include?(Facter.value("operatingsystem")) cmd << "-M" end cmd end def check_manage_expiry cmd = [] if @resource[:expiry] cmd << "-e #{@resource[:expiry]}" end cmd end def check_system_users @resource.system? ? ["-r"] : [] end def add_properties cmd = [] Puppet::Type.type(:user).validproperties.each do |property| next if property == :ensure next if property.to_s =~ /password_.+_age/ # the value needs to be quoted, mostly because -c might # have spaces in it if value = @resource.should(property) and value != "" cmd << flag(property) << value end end cmd end def addcmd cmd = [command(:add)] cmd += add_properties cmd += check_allow_dup cmd += check_manage_home cmd += check_manage_expiry cmd += check_system_users cmd << @resource[:name] end def passcmd age_limits = [:password_min_age, :password_max_age].select { |property| @resource.should(property) } if age_limits.empty? nil else [command(:password),age_limits.collect { |property| [flag(property), @resource.should(property)]}, @resource[:name]].flatten end end def password_min_age if Puppet.features.libshadow? if ent = Shadow::Passwd.getspnam(@resource.name) return ent.sp_min end end :absent end def password_max_age if Puppet.features.libshadow? if ent = Shadow::Passwd.getspnam(@resource.name) return ent.sp_max end end :absent end # Retrieve the password using the Shadow Password library def password if Puppet.features.libshadow? if ent = Shadow::Passwd.getspnam(@resource.name) return ent.sp_pwdp end end :absent end end diff --git a/lib/puppet/provider/user/windows_adsi.rb b/lib/puppet/provider/user/windows_adsi.rb index 3a5c716b7..6b0a9bce7 100644 --- a/lib/puppet/provider/user/windows_adsi.rb +++ b/lib/puppet/provider/user/windows_adsi.rb @@ -1,87 +1,87 @@ require 'puppet/util/adsi' Puppet::Type.type(:user).provide :windows_adsi do - desc "User management for Windows" + desc "User management for Windows." defaultfor :operatingsystem => :windows confine :operatingsystem => :windows has_features :manages_homedir, :manages_passwords def user @user ||= Puppet::Util::ADSI::User.new(@resource[:name]) end def groups user.groups.join(',') end def groups=(groups) user.set_groups(groups, @resource[:membership] == :minimum) end def create @user = Puppet::Util::ADSI::User.create(@resource[:name]) @user.commit [:comment, :home, :groups].each do |prop| send("#{prop}=", @resource[prop]) if @resource[prop] end end def exists? Puppet::Util::ADSI::User.exists?(@resource[:name]) end def delete Puppet::Util::ADSI::User.delete(@resource[:name]) end # Only flush if we created or modified a user, not deleted def flush @user.commit if @user end def comment user['Description'] end def comment=(value) user['Description'] = value end def home user['HomeDirectory'] end def home=(value) user['HomeDirectory'] = value end def password user.password_is?( @resource[:password] ) ? @resource[:password] : :absent end def password=(value) user.password = value end def uid Puppet::Util::ADSI.sid_for_account(@resource[:name]) end def uid=(value) fail "uid is read-only" end [:gid, :shell].each do |prop| define_method(prop) { nil } define_method("#{prop}=") do |v| fail "No support for managing property #{prop} of user #{@resource[:name]} on Windows" end end def self.instances Puppet::Util::ADSI::User.map { |u| new(:ensure => :present, :name => u.name) } end end diff --git a/lib/puppet/reference/type.rb b/lib/puppet/reference/type.rb index b423387e9..65dd2538c 100644 --- a/lib/puppet/reference/type.rb +++ b/lib/puppet/reference/type.rb @@ -1,113 +1,113 @@ type = Puppet::Util::Reference.newreference :type, :doc => "All Puppet resource types and all their details" do types = {} Puppet::Type.loadall Puppet::Type.eachtype { |type| next if type.name == :puppet next if type.name == :component next if type.name == :whit types[type.name] = type } str = %{ ## Resource Types - The *namevar* is the parameter used to uniquely identify a type instance. This is the parameter that gets assigned when a string is provided before the colon in a type declaration. In general, only developers will need to worry about which parameter is the `namevar`. In the following code: file { "/etc/passwd": owner => root, group => root, - mode => 644 + mode => 644 } `/etc/passwd` is considered the title of the file object (used for things like dependency handling), and because `path` is the namevar for `file`, that string is assigned to the `path` parameter. - *Parameters* determine the specific configuration of the instance. They either directly modify the system (internally, these are called properties) or they affect how the instance behaves (e.g., adding a search path for `exec` instances or determining recursion on `file` instances). - *Providers* provide low-level functionality for a given resource type. This is usually in the form of calling out to external commands. When required binaries are specified for providers, fully qualifed paths indicate that the binary must exist at that specific path and unqualified binaries indicate that Puppet will search for the binary using the shell path. - *Features* are abilities that some providers might not support. You can use the list of supported features to determine how a given provider can be used. Resource types define features they can use, and providers can be tested to see which features they provide. } types.sort { |a,b| a.to_s <=> b.to_s }.each { |name,type| str += " ---------------- " str += h(name, 3) str += scrub(type.doc) + "\n\n" # Handle the feature docs. if featuredocs = type.featuredocs str += h("Features", 4) str += featuredocs end docs = {} type.validproperties.sort { |a,b| a.to_s <=> b.to_s }.reject { |sname| property = type.propertybyname(sname) property.nodoc }.each { |sname| property = type.propertybyname(sname) raise "Could not retrieve property #{sname} on type #{type.name}" unless property doc = nil unless doc = property.doc $stderr.puts "No docs for #{type}[#{sname}]" next end doc = doc.dup tmp = doc tmp = scrub(tmp) docs[sname] = tmp } str += h("Parameters", 4) + "\n" type.parameters.sort { |a,b| a.to_s <=> b.to_s }.each { |name,param| #docs[name] = indent(scrub(type.paramdoc(name)), $tab) docs[name] = scrub(type.paramdoc(name)) } additional_key_attributes = type.key_attributes - [:name] docs.sort { |a, b| a[0].to_s <=> b[0].to_s }.each { |name, doc| str += paramwrap(name, doc, :namevar => additional_key_attributes.include?(name)) } str += "\n" } str end diff --git a/lib/puppet/type/augeas.rb b/lib/puppet/type/augeas.rb index f02cfe6e2..cc2ca6b1d 100644 --- a/lib/puppet/type/augeas.rb +++ b/lib/puppet/type/augeas.rb @@ -1,184 +1,218 @@ # # Copyright 2011 Bryan Kearney # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. Puppet::Type.newtype(:augeas) do include Puppet::Util feature :parse_commands, "Parse the command string" feature :need_to_run?, "If the command should run" feature :execute_changes, "Actually make the changes" @doc = <<-EOT - Apply the changes (single or array of changes) to the filesystem - via the augeas tool. + Apply a change or an array of changes to the filesystem + using the augeas tool. Requires: - - augeas to be installed (http://www.augeas.net) - - ruby-augeas bindings + - [Augeas](http://www.augeas.net) + - The ruby-augeas bindings Sample usage with a string: augeas{"test1" : context => "/files/etc/sysconfig/firstboot", changes => "set RUN_FIRSTBOOT YES", onlyif => "match other_value size > 0", } Sample usage with an array and custom lenses: augeas{"jboss_conf": - context => "/files", - changes => [ - "set etc/jbossas/jbossas.conf/JBOSS_IP $ipaddress", - "set etc/jbossas/jbossas.conf/JAVA_HOME /usr", - ], + context => "/files", + changes => [ + "set etc/jbossas/jbossas.conf/JBOSS_IP $ipaddress", + "set etc/jbossas/jbossas.conf/JAVA_HOME /usr", + ], load_path => "$/usr/share/jbossas/lenses", } EOT newparam (:name) do - desc "The name of this task. Used for uniqueness" + desc "The name of this task. Used for uniqueness." isnamevar end newparam (:context) do - desc "Optional context path. This value is prepended to the paths of all changes if the path is relative. If INCL is set, defaults to '/files' + INCL, otherwise the empty string" + desc "Optional context path. This value is prepended to the paths of all + changes if the path is relative. If the `incl` parameter is set, + defaults to `/files + incl`; otherwise, defaults to the empty string." defaultto "" munge do |value| if value.empty? and resource[:incl] "/files" + resource[:incl] else value end end end newparam (:onlyif) do desc "Optional augeas command and comparisons to control the execution of this type. Supported onlyif syntax: - get [AUGEAS_PATH] [COMPARATOR] [STRING] - match [MATCH_PATH] size [COMPARATOR] [INT] - match [MATCH_PATH] include [STRING] - match [MATCH_PATH] not_include [STRING] - match [MATCH_PATH] == [AN_ARRAY] - match [MATCH_PATH] != [AN_ARRAY] + * `get ` + * `match size ` + * `match include ` + * `match not_include ` + * `match == ` + * `match != ` where: - AUGEAS_PATH is a valid path scoped by the context - MATCH_PATH is a valid match synatx scoped by the context - COMPARATOR is in the set [> >= != == <= <] - STRING is a string - INT is a number - AN_ARRAY is in the form ['a string', 'another']" + * `AUGEAS_PATH` is a valid path scoped by the context + * `MATCH_PATH` is a valid match synatx scoped by the context + * `COMPARATOR` is one of `>, >=, !=, ==, <=,` or `<` + * `STRING` is a string + * `INT` is a number + * `AN_ARRAY` is in the form `['a string', 'another']`" defaultto "" end newparam(:changes) do desc "The changes which should be applied to the filesystem. This - can be either a string which contains a command or an array of commands. - Commands supported are: - - set [PATH] [VALUE] Sets the value VALUE at loction PATH - setm [PATH] [SUB] [VALUE] Sets multiple nodes matching SUB relative to PATH, to VALUE - rm [PATH] Removes the node at location PATH - remove [PATH] Synonym for rm - clear [PATH] Sets the node at PATH to NULL, creating it if needed - ins [LABEL] [WHERE] [PATH] Inserts an empty node LABEL either [WHERE={before|after}] PATH. - insert [LABEL] [WHERE] [PATH] Synonym for ins - mv [PATH] [PATH] Moves a node at PATH to the new location PATH - move [PATH] [PATH] Synonym for mv - defvar [NAME] [PATH] Sets Augeas variable $NAME to PATH - defnode [NAME] [PATH] [VALUE] Sets Augeas variable $NAME to PATH, creating it with VALUE if needed - - If the parameter 'context' is set that value is prepended to a relative PATH" + can be a command or an array of commands. The following commands are supported: + + `set ` + : Sets the value `VALUE` at loction `PATH` + + + `setm ` + : Sets multiple nodes (matching `SUB` relative to `PATH`) to `VALUE` + + + `rm ` + : Removes the node at location `PATH` + + + `remove ` + : Synonym for `rm` + + + `clear ` + : Sets the node at `PATH` to `NULL`, creating it if needed + + + `ins