require 'puppet'
# This is the parent class of all NSS classes. They're very different in
# their backend, but they're pretty similar on the front-end. This class
# provides a way for them all to be as similar as possible.
class Puppet::Provider::NameService < Puppet::Provider
class << self
def autogen_default(param)
if defined? @autogen_defaults
return @autogen_defaults[symbolize(param)]
else
return nil
end
end
def autogen_defaults(hash)
@autogen_defaults ||= {}
hash.each do |param, value|
@autogen_defaults[symbolize(param)] = value
end
end
def initvars
@checks = {}
super
end
def instances
objects = []
listbyname do |name|
objects << new(:name => name, :ensure => :present)
end
objects
end
def option(name, option)
name = name.intern if name.is_a? String
if defined? @options and @options.include? name and @options[name].include? option
return @options[name][option]
else
return nil
end
end
def options(name, hash)
unless resource_type.validattr?(name)
raise Puppet::DevError, "%s is not a valid attribute for %s" %
[name, resource_type.name]
end
@options ||= {}
@options[name] ||= {}
# Set options individually, so we can call the options method
# multiple times.
hash.each do |param, value|
@options[name][param] = value
end
end
# List everything out by name. Abstracted a bit so that it works
# for both users and groups.
def listbyname
names = []
Etc.send("set%sent" % section())
begin
while ent = Etc.send("get%sent" % section())
names << ent.name
if block_given?
yield ent.name
end
end
ensure
Etc.send("end%sent" % section())
end
return names
end
def resource_type=(resource_type)
super
@resource_type.validproperties.each do |prop|
next if prop == :ensure
unless public_method_defined?(prop)
define_method(prop) { get(prop) || :absent}
end
unless public_method_defined?(prop.to_s + "=")
define_method(prop.to_s + "=") { |*vals| set(prop, *vals) }
end
end
end
# This is annoying, but there really aren't that many options,
# and this *is* built into Ruby.
def section
unless defined? @resource_type
raise Puppet::DevError,
"Cannot determine Etc section without a resource type"
end
if @resource_type.name == :group
"gr"
else
"pw"
end
end
def validate(name, value)
name = name.intern if name.is_a? String
if @checks.include? name
block = @checks[name][:block]
unless block.call(value)
raise ArgumentError, "Invalid value %s: %s" %
[value, @checks[name][:error]]
end
end
end
def verify(name, error, &block)
name = name.intern if name.is_a? String
@checks[name] = {:error => error, :block => block}
end
private
def op(property)
@ops[property.name] || ("-" + property.name)
end
end
# Autogenerate a value. Mostly used for uid/gid, but also used heavily
# with netinfo, because netinfo is stupid.
def autogen(field)
field = symbolize(field)
id_generators = {:user => :uid, :group => :gid}
if id_generators[@resource.class.name] == field
return autogen_id(field)
else
if value = self.class.autogen_default(field)
return value
elsif respond_to?("autogen_%s" % [field])
return send("autogen_%s" % field)
else
return nil
end
end
end
# Autogenerate either a uid or a gid. This is hard-coded: we can only
# generate one field type per class.
def autogen_id(field)
highest = 0
group = method = nil
case @resource.class.name
when :user: group = :passwd; method = :uid
when :group: group = :group; method = :gid
else
raise Puppet::DevError, "Invalid resource name %s" % resource
end
# Make sure we don't use the same value multiple times
if defined? @@prevauto
@@prevauto += 1
else
Etc.send(group) { |obj|
if obj.gid > highest
unless obj.send(method) > 65000
highest = obj.send(method)
end
end
}
@@prevauto = highest + 1
end
return @@prevauto
end
def create
self.ensure = :present
end
def delete
self.ensure = :absent
end
def ensure
if exists?
:present
else
:absent
end
end
# This is only used when creating or destroying the object.
def ensure=(value)
cmd = nil
event = nil
case value
when :absent
# we need to remove the object...
unless exists?
info "already absent"
# the object already doesn't exist
return nil
end
# again, needs to be set by the ind. property or its
# parent
cmd = self.deletecmd
type = "delete"
when :present
if exists?
info "already exists"
# The object already exists
return nil
end
# blah blah, define elsewhere, blah blah
cmd = self.addcmd
type = "create"
end
begin
execute(cmd)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not %s %s %s: %s" %
[type, @resource.class.name, @resource.name, detail]
end
end
# Does our object exist?
def exists?
if getinfo(true)
return true
else
return false
end
end
# Retrieve a specific value by name.
def get(param)
if hash = getinfo(false)
return hash[param]
else
return nil
end
end
# Retrieve what we can about our object
def getinfo(refresh)
if @objectinfo.nil? or refresh == true
@etcmethod ||= ("get" + self.class.section().to_s + "nam").intern
begin
@objectinfo = Etc.send(@etcmethod, @resource[:name])
rescue ArgumentError => detail
@objectinfo = nil
end
end
# Now convert our Etc struct into a hash.
if @objectinfo
return info2hash(@objectinfo)
else
return nil
end
end
# The list of all groups the user is a member of. Different
# user mgmt systems will need to override this method.
def groups
groups = []
# Reset our group list
Etc.setgrent
user = @resource[:name]
# Now iterate across all of the groups, adding each one our
# user is a member of
while group = Etc.getgrent
members = group.mem
if members.include? user
groups << group.name
end
end
# We have to close the file, so each listing is a separate
# reading of the file.
Etc.endgrent
groups.join(",")
end
# Convert the Etc struct into a hash.
def info2hash(info)
hash = {}
self.class.resource_type.validproperties.each do |param|
method = posixmethod(param)
if info.respond_to? method
hash[param] = info.send(posixmethod(param))
end
end
return hash
end
def initialize(resource)
super
@objectinfo = nil
end
def set(param, value)
self.class.validate(param, value)
cmd = modifycmd(param, value)
unless cmd.is_a?(Array)
raise Puppet::DevError, "Nameservice command must be an array"
end
begin
execute(cmd)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not set %s on %s[%s]: %s" %
[param, @resource.class.name, @resource.name, detail]
end
end
end
# $Id: nameservice.rb 2555 2007-06-08 17:20:00Z luke $
syntax highlighted by Code2HTML, v. 0.9.1