summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/puppet/defaults.rb12
-rw-r--r--lib/puppet/indirector/node/ldap.rb29
-rw-r--r--lib/puppet/parser/compiler.rb2
-rw-r--r--lib/puppet/parser/functions.rb2
-rw-r--r--lib/puppet/parser/interpreter.rb2
-rw-r--r--lib/puppet/parser/templatewrapper.rb9
-rw-r--r--lib/puppet/provider.rb104
-rw-r--r--lib/puppet/provider/confine.rb77
-rw-r--r--lib/puppet/provider/confine/exists.rb22
-rw-r--r--lib/puppet/provider/confine/facter.rb37
-rw-r--r--lib/puppet/provider/confine/false.rb19
-rw-r--r--lib/puppet/provider/confine/feature.rb17
-rw-r--r--lib/puppet/provider/confine/true.rb20
-rw-r--r--lib/puppet/provider/confine_collection.rb47
-rw-r--r--lib/puppet/provider/confiner.rb20
-rwxr-xr-xlib/puppet/provider/cron/crontab.rb2
-rw-r--r--lib/puppet/provider/group/ldap.rb37
-rw-r--r--lib/puppet/provider/ldap.rb137
-rw-r--r--lib/puppet/provider/nameservice/objectadd.rb2
-rwxr-xr-xlib/puppet/provider/package/rpm.rb2
-rw-r--r--lib/puppet/provider/package/urpmi.rb6
-rwxr-xr-xlib/puppet/provider/service/base.rb8
-rwxr-xr-xlib/puppet/provider/service/init.rb6
-rwxr-xr-xlib/puppet/provider/service/redhat.rb29
-rw-r--r--lib/puppet/provider/ssh_authorized_key/parsed.rb49
-rw-r--r--lib/puppet/provider/user/ldap.rb115
-rw-r--r--lib/puppet/reference/providers.rb2
-rwxr-xr-xlib/puppet/type/mount.rb4
-rw-r--r--lib/puppet/type/ssh_authorized_key.rb44
-rw-r--r--lib/puppet/util/ldap.rb5
-rw-r--r--lib/puppet/util/ldap/connection.rb57
-rw-r--r--lib/puppet/util/ldap/generator.rb45
-rw-r--r--lib/puppet/util/ldap/manager.rb281
-rw-r--r--lib/puppet/util/settings.rb2
-rw-r--r--lib/puppet/util/storage.rb4
-rw-r--r--lib/puppet/util/variables.rb38
36 files changed, 1133 insertions, 161 deletions
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb
index 78cf7c47d..d77ec0486 100644
--- a/lib/puppet/defaults.rb
+++ b/lib/puppet/defaults.rb
@@ -516,9 +516,11 @@ module Puppet
# Central fact information.
self.setdefaults(:main,
- :factpath => ["$vardir/facts",
- "Where Puppet should look for facts. Multiple directories should
- be colon-separated, like normal PATH variables."],
+ :factpath => {:default => "$vardir/facts",
+ :desc => "Where Puppet should look for facts. Multiple directories should
+ be colon-separated, like normal PATH variables.",
+ :call_on_define => true, # Call our hook with the default value, so we always get the value added to facter.
+ :hook => proc { |value| Facter.search(value) if Facter.respond_to?(:search) }},
:factdest => ["$vardir/facts",
"Where Puppet should store facts that it pulls down from the central
server."],
@@ -633,6 +635,10 @@ module Puppet
:ldapclassattrs => ["puppetclass",
"The LDAP attributes to use to define Puppet classes. Values
should be comma-separated."],
+ :ldapstackedattrs => ["puppetvar",
+ "The LDAP attributes that should be stacked to arrays by adding
+ the values in all hierarchy elements of the tree. Values
+ should be comma-separated."],
:ldapattrs => ["all",
"The LDAP attributes to include when querying LDAP for nodes. All
returned attributes are set as variables in the top-level scope.
diff --git a/lib/puppet/indirector/node/ldap.rb b/lib/puppet/indirector/node/ldap.rb
index 6c41c18d4..bc58908fd 100644
--- a/lib/puppet/indirector/node/ldap.rb
+++ b/lib/puppet/indirector/node/ldap.rb
@@ -19,6 +19,8 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap
node = Puppet::Node.new(name)
+ information[:stacked_parameters] = {}
+
parent_info = nil
parent = information[:parent]
parents = [name]
@@ -34,6 +36,10 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap
raise Puppet::Error.new("Could not find parent node '%s'" % parent)
end
information[:classes] += parent_info[:classes]
+ parent_info[:stacked].each do |value|
+ param = value.split('=', 2)
+ information[:stacked_parameters][param[0]] = param[1]
+ end
parent_info[:parameters].each do |param, value|
# Specifically test for whether it's set, so false values are handled
# correctly.
@@ -45,6 +51,15 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap
parent = parent_info[:parent]
end
+ information[:stacked].each do |value|
+ param = value.split('=', 2)
+ information[:stacked_parameters][param[0]] = param[1]
+ end
+
+ information[:stacked_parameters].each do |param, value|
+ information[:parameters][param] = value unless information[:parameters].include?(param)
+ end
+
node.classes = information[:classes].uniq unless information[:classes].empty?
node.parameters = information[:parameters] unless information[:parameters].empty?
node.environment = information[:environment] if information[:environment]
@@ -62,6 +77,12 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap
end
end
+ # The attributes that Puppet will stack as array over the full
+ # hierarchy.
+ def stacked_attributes
+ Puppet[:ldapstackedattrs].split(/\s*,\s*/)
+ end
+
# Process the found entry. We assume that we don't just want the
# ldap object.
def process(name, entry)
@@ -85,6 +106,14 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap
end
}
+ result[:stacked] = []
+ stacked_attributes.each { |attr|
+ if values = entry.vals(attr)
+ result[:stacked] = result[:stacked] + values
+ end
+ }
+
+
result[:parameters] = entry.to_hash.inject({}) do |hash, ary|
if ary[1].length == 1
hash[ary[0]] = ary[1].shift
diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb
index 8fba41121..d67b3d275 100644
--- a/lib/puppet/parser/compiler.rb
+++ b/lib/puppet/parser/compiler.rb
@@ -332,7 +332,7 @@ class Puppet::Parser::Compiler
unless remaining.empty?
fail Puppet::ParseError,
- "Could not find object(s) %s" % remaining.collect { |o|
+ "Could not find resource(s) %s for overriding" % remaining.collect { |o|
o.ref
}.join(", ")
end
diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb
index e0b60e161..93991275c 100644
--- a/lib/puppet/parser/functions.rb
+++ b/lib/puppet/parser/functions.rb
@@ -165,7 +165,7 @@ module Functions
type is defined, either as a native type or a defined type, or whether a class is defined.
This is useful for checking whether a class is defined and only including it if it is.
This function can also test whether a resource has been defined, using resource references
- (e.g., ``if defined(File['/tmp/myfile'] { ... }``). This function is unfortunately
+ (e.g., ``if defined(File['/tmp/myfile']) { ... }``). This function is unfortunately
dependent on the parse order of the configuration when testing whether a resource is defined.") do |vals|
result = false
vals.each do |val|
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
index f27c1c5c8..04ca41494 100644
--- a/lib/puppet/parser/interpreter.rb
+++ b/lib/puppet/parser/interpreter.rb
@@ -62,7 +62,7 @@ class Puppet::Parser::Interpreter
# exception elsewhere and reuse the parser. If one doesn't
# exist, then reraise.
if @parsers[environment]
- Puppet.err detail
+ Puppet.err(detail.to_s + "; using previously parsed manifests")
else
raise detail
end
diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb
index 7a8f74156..4790cea30 100644
--- a/lib/puppet/parser/templatewrapper.rb
+++ b/lib/puppet/parser/templatewrapper.rb
@@ -20,6 +20,15 @@ class Puppet::Parser::TemplateWrapper
end
end
+ # Should return true if a variable is defined, false if it is not
+ def has_variable?(name)
+ if @scope.lookupvar(name.to_s, false) != :undefined
+ true
+ else
+ false
+ end
+ end
+
# Ruby treats variables like methods, so we can cheat here and
# trap missing vars like they were missing methods.
def method_missing(name, *args)
diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb
index e73bb0cb6..c02e15029 100644
--- a/lib/puppet/provider.rb
+++ b/lib/puppet/provider.rb
@@ -5,6 +5,10 @@ class Puppet::Provider
include Puppet::Util::Warnings
extend Puppet::Util::Warnings
+ require 'puppet/provider/confiner'
+
+ extend Puppet::Provider::Confiner
+
Puppet::Util.logmethods(self, true)
class << self
@@ -40,27 +44,13 @@ class Puppet::Provider
[name, self.name]
end
- if command == :missing
- return nil
- end
-
- command
+ return binary(command)
end
# Define commands that are not optional.
def self.commands(hash)
optional_commands(hash) do |name, path|
- confine :exists => path
- end
- end
-
- def self.confine(hash)
- hash.each do |p,v|
- if v.is_a? Array
- @confines[p] += v
- else
- @confines[p] << v
- end
+ confine :exists => path, :for_binary => true
end
end
@@ -108,10 +98,6 @@ class Puppet::Provider
def self.initvars
@defaults = {}
@commands = {}
- @origcommands = {}
- @confines = Hash.new do |hash, key|
- hash[key] = []
- end
end
# The method for returning a list of provider instances. Note that it returns providers, preferably with values already
@@ -180,16 +166,7 @@ class Puppet::Provider
def self.optional_commands(hash)
hash.each do |name, path|
name = symbolize(name)
- @origcommands[name] = path
-
- # Try to find the full path (or verify already-full paths); otherwise
- # store that the command is missing so we know it's defined but absent.
- if tmp = binary(path)
- path = tmp
- @commands[name] = path
- else
- @commands[name] = :missing
- end
+ @commands[name] = path
if block_given?
yield(name, path)
@@ -208,69 +185,6 @@ class Puppet::Provider
@source
end
- # Check whether this implementation is suitable for our platform.
- def self.suitable?(short = true)
- # A single false result is sufficient to turn the whole thing down.
- # We don't return 'true' until the very end, though, so that every
- # confine is tested.
- missing = {}
- @confines.each do |check, values|
- case check
- when :exists:
- values.each do |value|
- unless value and FileTest.exists? value
- debug "Not suitable: missing %s" % value
- return false if short
- missing[:exists] ||= []
- missing[:exists] << value
- end
- end
- when :true:
- values.each do |v|
- debug "Not suitable: false value"
- unless v
- return false if short
- missing[:true] ||= 0
- missing[:true] += 1
- end
- end
- when :false:
- values.each do |v|
- debug "Not suitable: true value"
- if v and short
- return false if short
- missing[:false] ||= 0
- missing[:false] += 1
- end
- end
- else # Just delegate everything else to facter
- if result = Facter.value(check)
- result = result.to_s.downcase.intern
-
- found = values.find do |v|
- result == v.to_s.downcase.intern
- end
- unless found
- debug "Not suitable: %s not in %s" % [check, values]
- return false if short
- missing[:facter] ||= {}
- missing[:facter][check] = values
- end
- else
- return false if short
- missing[:facter] ||= {}
- missing[:facter][check] = values
- end
- end
- end
-
- if short
- return true
- else
- return missing
- end
- end
-
# Does this provider support the specified parameter?
def self.supports_parameter?(param)
if param.is_a?(Class)
@@ -309,8 +223,8 @@ class Puppet::Provider
end
dochook(:commands) do
- if @origcommands.length > 0
- return " Required binaries: " + @origcommands.collect do |n, c|
+ if @commands.length > 0
+ return " Required binaries: " + @commands.collect do |n, c|
"``#{c}``"
end.join(", ") + "."
end
diff --git a/lib/puppet/provider/confine.rb b/lib/puppet/provider/confine.rb
new file mode 100644
index 000000000..35b80fdcf
--- /dev/null
+++ b/lib/puppet/provider/confine.rb
@@ -0,0 +1,77 @@
+# The class that handles testing whether our providers
+# actually work or not.
+require 'puppet/util'
+
+class Puppet::Provider::Confine
+ include Puppet::Util
+
+ @tests = {}
+
+ class << self
+ attr_accessor :name
+ end
+
+ def self.inherited(klass)
+ name = klass.to_s.split("::").pop.downcase.to_sym
+ raise "Test %s is already defined" % name if @tests.include?(name)
+
+ klass.name = name
+
+ @tests[name] = klass
+ end
+
+ def self.test(name)
+ unless @tests[name]
+ begin
+ require "puppet/provider/confine/%s" % name
+ rescue LoadError => detail
+ unless detail.to_s.include?("no such file")
+ warn "Could not load confine test '%s': %s" % [name, detail]
+ end
+ # Could not find file
+ end
+ end
+ return @tests[name]
+ end
+
+ attr_reader :values
+
+ # Mark that this confine is used for testing binary existence.
+ attr_accessor :for_binary
+ def for_binary?
+ for_binary
+ end
+
+ def initialize(values)
+ values = [values] unless values.is_a?(Array)
+ @values = values
+ end
+
+ # Provide a hook for the message when there's a failure.
+ def message(value)
+ ""
+ end
+
+ # Collect the results of all of them.
+ def result
+ values.collect { |value| pass?(value) }
+ end
+
+ # Test whether our confine matches.
+ def valid?
+ values.each do |value|
+ unless pass?(value)
+ Puppet.debug message(value)
+ return false
+ end
+ end
+
+ return true
+ ensure
+ reset
+ end
+
+ # Provide a hook for subclasses.
+ def reset
+ end
+end
diff --git a/lib/puppet/provider/confine/exists.rb b/lib/puppet/provider/confine/exists.rb
new file mode 100644
index 000000000..1d1ed8c84
--- /dev/null
+++ b/lib/puppet/provider/confine/exists.rb
@@ -0,0 +1,22 @@
+require 'puppet/provider/confine'
+
+class Puppet::Provider::Confine::Exists < Puppet::Provider::Confine
+ def self.summarize(confines)
+ confines.inject([]) { |total, confine| total + confine.summary }
+ end
+
+ def pass?(value)
+ if for_binary?
+ return false unless value = binary(value)
+ end
+ value and FileTest.exist?(value)
+ end
+
+ def message(value)
+ "file %s does not exist" % value
+ end
+
+ def summary
+ result.zip(values).inject([]) { |array, args| val, f = args; array << f unless val; array }
+ end
+end
diff --git a/lib/puppet/provider/confine/facter.rb b/lib/puppet/provider/confine/facter.rb
new file mode 100644
index 000000000..9bb66c058
--- /dev/null
+++ b/lib/puppet/provider/confine/facter.rb
@@ -0,0 +1,37 @@
+require 'puppet/provider/confine'
+
+class Puppet::Provider::Confine::Facter < Puppet::Provider::Confine
+ def self.summarize(confines)
+ result = Hash.new { |hash, key| hash[key] = [] }
+ confines.inject(result) { |total, confine| total[confine.fact] += confine.values unless confine.valid?; total }
+ end
+
+ attr_accessor :fact
+
+ # Are we a facter comparison?
+ def facter?
+ defined?(@facter)
+ end
+
+ # Retrieve the value from facter
+ def facter_value
+ unless defined?(@facter_value) and @facter_value
+ @facter_value = ::Facter.value(@fact).to_s.downcase
+ end
+ @facter_value
+ end
+
+ def message(value)
+ "facter value '%s' for '%s' not in required list '%s'" % [value, self.fact, values.join(",")]
+ end
+
+ def pass?(value)
+ facter_value == value.to_s.downcase
+ end
+
+ def reset
+ # Reset the cache. We want to cache it during a given
+ # run, but across runs.
+ @facter_value = nil
+ end
+end
diff --git a/lib/puppet/provider/confine/false.rb b/lib/puppet/provider/confine/false.rb
new file mode 100644
index 000000000..b5b2b51c8
--- /dev/null
+++ b/lib/puppet/provider/confine/false.rb
@@ -0,0 +1,19 @@
+require 'puppet/provider/confine'
+
+class Puppet::Provider::Confine::False < Puppet::Provider::Confine
+ def self.summarize(confines)
+ confines.inject(0) { |count, confine| count + confine.summary }
+ end
+
+ def pass?(value)
+ ! value
+ end
+
+ def message(value)
+ "true value when expecting false"
+ end
+
+ def summary
+ result.find_all { |v| v == false }.length
+ end
+end
diff --git a/lib/puppet/provider/confine/feature.rb b/lib/puppet/provider/confine/feature.rb
new file mode 100644
index 000000000..1d92b001a
--- /dev/null
+++ b/lib/puppet/provider/confine/feature.rb
@@ -0,0 +1,17 @@
+require 'puppet/provider/confine'
+
+class Puppet::Provider::Confine::Feature < Puppet::Provider::Confine
+ def self.summarize(confines)
+ confines.collect { |c| c.values }.flatten.uniq.find_all { |value| ! confines[0].pass?(value) }
+ end
+
+ # Is the named feature available?
+ def pass?(value)
+ Puppet.features.send(value.to_s + "?")
+ end
+
+ def message(value)
+ "feature %s is missing" % value
+ end
+end
+
diff --git a/lib/puppet/provider/confine/true.rb b/lib/puppet/provider/confine/true.rb
new file mode 100644
index 000000000..86b3b144f
--- /dev/null
+++ b/lib/puppet/provider/confine/true.rb
@@ -0,0 +1,20 @@
+require 'puppet/provider/confine'
+
+class Puppet::Provider::Confine::True < Puppet::Provider::Confine
+ def self.summarize(confines)
+ confines.inject(0) { |count, confine| count + confine.summary }
+ end
+
+ def pass?(value)
+ # Double negate, so we only get true or false.
+ ! ! value
+ end
+
+ def message(value)
+ "false value when expecting true"
+ end
+
+ def summary
+ result.find_all { |v| v == true }.length
+ end
+end
diff --git a/lib/puppet/provider/confine_collection.rb b/lib/puppet/provider/confine_collection.rb
new file mode 100644
index 000000000..0c80086c9
--- /dev/null
+++ b/lib/puppet/provider/confine_collection.rb
@@ -0,0 +1,47 @@
+# Manage a collection of confines, returning a boolean or
+# helpful information.
+require 'puppet/provider/confine'
+
+class Puppet::Provider::ConfineCollection
+ def confine(hash)
+ if hash.include?(:for_binary)
+ for_binary = true
+ hash.delete(:for_binary)
+ else
+ for_binary = false
+ end
+ hash.each do |test, values|
+ if klass = Puppet::Provider::Confine.test(test)
+ @confines << klass.new(values)
+ @confines[-1].for_binary = true if for_binary
+ else
+ confine = Puppet::Provider::Confine.test(:facter).new(values)
+ confine.fact = test
+ @confines << confine
+ end
+ end
+ end
+
+ def initialize
+ @confines = []
+ end
+
+ # Return a hash of the whole confine set, used for the Provider
+ # reference.
+ def summary
+ confines = Hash.new { |hash, key| hash[key] = [] }
+ @confines.each { |confine| confines[confine.class] << confine }
+ result = {}
+ confines.each do |klass, list|
+ value = klass.summarize(list)
+ next if (value.respond_to?(:length) and value.length == 0) or (value == 0)
+ result[klass.name] = value
+
+ end
+ result
+ end
+
+ def valid?
+ ! @confines.detect { |c| ! c.valid? }
+ end
+end
diff --git a/lib/puppet/provider/confiner.rb b/lib/puppet/provider/confiner.rb
new file mode 100644
index 000000000..4605523e8
--- /dev/null
+++ b/lib/puppet/provider/confiner.rb
@@ -0,0 +1,20 @@
+require 'puppet/provider/confine_collection'
+
+module Puppet::Provider::Confiner
+ def confine(hash)
+ confine_collection.confine(hash)
+ end
+
+ def confine_collection
+ unless defined?(@confine_collection)
+ @confine_collection = Puppet::Provider::ConfineCollection.new
+ end
+ @confine_collection
+ end
+
+ # Check whether this implementation is suitable for our platform.
+ def suitable?(short = true)
+ return confine_collection.valid? if short
+ return confine_collection.summary
+ end
+end
diff --git a/lib/puppet/provider/cron/crontab.rb b/lib/puppet/provider/cron/crontab.rb
index 7ddcc0505..e2228b15e 100755
--- a/lib/puppet/provider/cron/crontab.rb
+++ b/lib/puppet/provider/cron/crontab.rb
@@ -30,7 +30,7 @@ Puppet::Type.type(:cron).provide(:crontab,
}
crontab = record_line :crontab, :fields => %w{minute hour monthday month weekday command},
- :match => %r{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)$},
+ :match => %r{^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)$},
:optional => %w{minute hour weekday month monthday}, :absent => "*"
class << crontab
diff --git a/lib/puppet/provider/group/ldap.rb b/lib/puppet/provider/group/ldap.rb
new file mode 100644
index 000000000..632358ff1
--- /dev/null
+++ b/lib/puppet/provider/group/ldap.rb
@@ -0,0 +1,37 @@
+require 'puppet/provider/ldap'
+
+Puppet::Type.type(:group).provide :ldap, :parent => Puppet::Provider::Ldap do
+ 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.
+
+ 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?
+
+ # 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 = 0
+ provider.manager.search.each do |hash|
+ next unless value = hash[:gid]
+ num = value[0].to_i
+ if num > largest
+ largest = num
+ end
+ end
+ largest + 1
+ end
+
+end
diff --git a/lib/puppet/provider/ldap.rb b/lib/puppet/provider/ldap.rb
new file mode 100644
index 000000000..76834f94d
--- /dev/null
+++ b/lib/puppet/provider/ldap.rb
@@ -0,0 +1,137 @@
+require 'puppet/provider'
+
+# The base class for LDAP providers.
+class Puppet::Provider::Ldap < Puppet::Provider
+ require 'puppet/util/ldap/manager'
+
+ class << self
+ attr_reader :manager
+ end
+
+ # Look up all instances at our location. Yay.
+ def self.instances
+ return [] unless list = manager.search
+
+ list.collect { |entry| new(entry) }
+ end
+
+ # Specify the ldap manager for this provider, which is
+ # used to figure out how we actually interact with ldap.
+ def self.manages(*args)
+ @manager = Puppet::Util::Ldap::Manager.new
+ @manager.manages(*args)
+
+ # Set up our getter/setter methods.
+ mk_resource_methods
+ return @manager
+ end
+
+ # Query all of our resources from ldap.
+ def self.prefetch(resources)
+ resources.each do |name, resource|
+ if result = manager.find(name)
+ result[:ensure] = :present
+ resource.provider = new(result)
+ else
+ resource.provider = new(:ensure => :absent)
+ end
+ end
+ end
+
+ attr_reader :ldap_properties
+
+ def manager
+ self.class.manager
+ end
+
+ def create
+ @property_hash[:ensure] = :present
+ self.class.resource_type.validproperties.each do |property|
+ if val = resource.should(property)
+ @property_hash[property] = val
+ end
+ end
+ end
+
+ def delete
+ @property_hash[:ensure] = :absent
+ end
+
+ def exists?
+ @property_hash[:ensure] != :absent
+ end
+
+ # Apply our changes to ldap, yo.
+ def flush
+ # Just call the manager's update() method.
+ @property_hash.delete(:groups)
+ @ldap_properties.delete(:groups)
+ manager.update(name, ldap_properties, properties)
+ @property_hash.clear
+ @ldap_properties.clear
+ end
+
+ def initialize(*args)
+ raise(Puppet::DevError, "No LDAP Configuration defined for %s" % self.class) unless self.class.manager
+ raise(Puppet::DevError, "Invalid LDAP Configuration defined for %s" % self.class) unless self.class.manager.valid?
+ super
+
+ @property_hash = @property_hash.inject({}) do |result, ary|
+ param, values = ary
+
+ # Skip any attributes we don't manage.
+ next result unless self.class.resource_type.validattr?(param)
+
+ paramclass = self.class.resource_type.attrclass(param)
+
+ unless values.is_a?(Array)
+ result[param] = values
+ next result
+ end
+
+ # Only use the first value if the attribute class doesn't manage
+ # arrays of values.
+ if paramclass.superclass == Puppet::Parameter or paramclass.array_matching == :first
+ result[param] = values[0]
+ else
+ result[param] = values
+ end
+ result
+ end
+
+ # Make a duplicate, so that we have a copy for comparison
+ # at the end.
+ @ldap_properties = @property_hash.dup
+ end
+
+ # Return the current state of ldap.
+ def ldap_properties
+ @ldap_properties.dup
+ end
+
+ # Return (and look up if necessary) the desired state.
+ def properties
+ if @property_hash.empty?
+ @property_hash = query || {:ensure => :absent}
+ if @property_hash.empty?
+ @property_hash[:ensure] = :absent
+ end
+ end
+ @property_hash.dup
+ end
+
+ # Collect the current attributes from ldap. Returns
+ # the results, but also stores the attributes locally,
+ # so we have something to compare against when we update.
+ # LAK:NOTE This is normally not used, because we rely on prefetching.
+ def query
+ # Use the module function.
+ unless attributes = manager.find(name)
+ @ldap_properties = {}
+ return nil
+ end
+
+ @ldap_properties = attributes
+ return @ldap_properties.dup
+ end
+end
diff --git a/lib/puppet/provider/nameservice/objectadd.rb b/lib/puppet/provider/nameservice/objectadd.rb
index 8ebf8924b..4682b5169 100644
--- a/lib/puppet/provider/nameservice/objectadd.rb
+++ b/lib/puppet/provider/nameservice/objectadd.rb
@@ -25,7 +25,7 @@ class ObjectAdd < Puppet::Provider::NameService
cmd = [command(:modify),
flag(param),
value]
- if @resource[:allowdupe] == :true
+ if @resource[:allowdupe] == :true && param == :uid
cmd << "-o"
end
cmd << @resource[:name]
diff --git a/lib/puppet/provider/package/rpm.rb b/lib/puppet/provider/package/rpm.rb
index 98ca1efa6..35684e11d 100755
--- a/lib/puppet/provider/package/rpm.rb
+++ b/lib/puppet/provider/package/rpm.rb
@@ -67,7 +67,7 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr
end
cmd = [command(:rpm), "-q", "--qf", "#{NEVRAFORMAT}\n", "-p", "#{@resource[:source]}"]
- h = nevra_to_hash(execfail(cmd, Puppet::Error))
+ h = self.class.nevra_to_hash(execfail(cmd, Puppet::Error))
return h[:ensure]
end
diff --git a/lib/puppet/provider/package/urpmi.rb b/lib/puppet/provider/package/urpmi.rb
index 8adc62ab4..a95835284 100644
--- a/lib/puppet/provider/package/urpmi.rb
+++ b/lib/puppet/provider/package/urpmi.rb
@@ -1,6 +1,6 @@
Puppet::Type.type(:package).provide :urpmi, :parent => :rpm, :source => :rpm do
desc "Support via ``urpmi``."
- commands :urpmi => "urpmi", :rpm => "rpm"
+ commands :urpmi => "urpmi", :urpmq => "urpmq", :rpm => "rpm"
if command('rpm')
confine :true => begin
@@ -41,9 +41,9 @@ Puppet::Type.type(:package).provide :urpmi, :parent => :rpm, :source => :rpm do
# What's the latest package version available?
def latest
- output = urpmi "-S", :available, @resource[:name]
+ output = urpmq "-S", @resource[:name]
- if output =~ /^#{@resource[:name]}\S+\s+(\S+)\s/
+ if output =~ /^#{@resource[:name]}\s+:\s+.*\(\s+(\S+)\s+\)/
return $1
else
# urpmi didn't find updates, pretend the current
diff --git a/lib/puppet/provider/service/base.rb b/lib/puppet/provider/service/base.rb
index 254b4fe4c..8964322b6 100755
--- a/lib/puppet/provider/service/base.rb
+++ b/lib/puppet/provider/service/base.rb
@@ -1,10 +1,10 @@
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``."
+ 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``."
commands :kill => "kill"
diff --git a/lib/puppet/provider/service/init.rb b/lib/puppet/provider/service/init.rb
index 274c334a3..3081d0eb8 100755
--- a/lib/puppet/provider/service/init.rb
+++ b/lib/puppet/provider/service/init.rb
@@ -2,9 +2,9 @@
# 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 not ``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."
+ 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."
class << self
attr_accessor :defpath
diff --git a/lib/puppet/provider/service/redhat.rb b/lib/puppet/provider/service/redhat.rb
index b013c34dc..e2d6ac947 100755
--- a/lib/puppet/provider/service/redhat.rb
+++ b/lib/puppet/provider/service/redhat.rb
@@ -1,11 +1,11 @@
-# Manage debian services. Start/stop is the same as InitSvc, but enable/disable
-# is special.
+# Manage Red Hat services. Start/stop uses /sbin/service and enable/disable uses chkconfig
+
Puppet::Type.type(:service).provide :redhat, :parent => :init do
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"
-
+ commands :chkconfig => "/sbin/chkconfig", :service => "/sbin/service"
+
defaultfor :operatingsystem => [:redhat, :fedora, :suse, :centos]
def self.defpath
@@ -16,7 +16,6 @@ Puppet::Type.type(:service).provide :redhat, :parent => :init do
def disable
begin
output = chkconfig(@resource[:name], :off)
- output += chkconfig("--del", @resource[:name])
rescue Puppet::ExecutionFailure
raise Puppet::Error, "Could not disable %s: %s" %
[self.name, output]
@@ -43,12 +42,28 @@ Puppet::Type.type(:service).provide :redhat, :parent => :init do
# in the init scripts.
def enable
begin
- output = chkconfig("--add", @resource[:name])
- output += chkconfig(@resource[:name], :on)
+ output = chkconfig(@resource[:name], :on)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not enable %s: %s" %
[self.name, detail]
end
end
+
+ def restart
+ if @resource[:hasrestart] == true
+ service(@resource[:name], "restart")
+ else
+ return false
+ end
+ end
+
+ def start
+ service(@resource[:name], "start")
+ end
+
+ def stop
+ service(@resource[:name], "stop")
+ end
+
end
diff --git a/lib/puppet/provider/ssh_authorized_key/parsed.rb b/lib/puppet/provider/ssh_authorized_key/parsed.rb
new file mode 100644
index 000000000..7cb6626de
--- /dev/null
+++ b/lib/puppet/provider/ssh_authorized_key/parsed.rb
@@ -0,0 +1,49 @@
+require 'puppet/provider/parsedfile'
+
+Puppet::Type.type(:ssh_authorized_key).provide(:parsed,
+ :parent => Puppet::Provider::ParsedFile,
+ :filetype => :flat,
+ :default_target => ''
+) do
+ desc "Parse and generate authorized_keys files for SSH."
+
+ text_line :comment, :match => /^#/
+ text_line :blank, :match => /^\s+/
+
+ record_line :parsed,
+ :fields => %w{options type key name},
+ :optional => %w{options},
+ :rts => /^\s+/,
+ :match => /^(?:([^ ]+) )?(ssh-dss|ssh-rsa) ([^ ]+)(?: (.+))?$/,
+ :post_parse => proc { |record|
+ if record[:options].nil?
+ record[:options] = [:absent]
+ else
+ record[:options] = record[:options].split(',')
+ end
+ },
+ :pre_gen => proc { |record|
+ if record[:options].include?(:absent)
+ record[:options] = ""
+ else
+ record[:options] = record[:options].join(',')
+ end
+ }
+
+ def prefetch
+ if not @resource.should(:target)
+ #
+ # Set default target when user is given
+ if val = @resource.should(:user)
+ target = File.expand_path("~%s/.ssh/authorized_keys" % val)
+ Puppet::debug("Setting target to %s" % target)
+ @resource[:target] = target
+ else
+ raise Puppet::Error, "Missing attribute 'user' or 'target'"
+ end
+ end
+
+ super
+ end
+end
+
diff --git a/lib/puppet/provider/user/ldap.rb b/lib/puppet/provider/user/ldap.rb
new file mode 100644
index 000000000..ba91a871e
--- /dev/null
+++ b/lib/puppet/provider/user/ldap.rb
@@ -0,0 +1,115 @@
+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.
+
+ 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."
+
+ confine :true => Puppet.features.ldap?
+
+ 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 = 0
+ provider.manager.search.each do |hash|
+ next unless value = hash[:uid]
+ num = value[0].to_i
+ if num > largest
+ largest = num
+ end
+ end
+ largest + 1
+ 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=%s" % name)
+ return @property_hash[:groups] = :absent
+ end
+
+ return @property_hash[:groups] = result.collect { |r| r[:name] }.join(",")
+ end
+ return @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 %s" % 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
+
+ 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/reference/providers.rb b/lib/puppet/reference/providers.rb
index da815ddf1..610c7550d 100644
--- a/lib/puppet/reference/providers.rb
+++ b/lib/puppet/reference/providers.rb
@@ -71,6 +71,8 @@ providers = Puppet::Util::Reference.newreference :providers, :title => "Provider
details += " - Got %s true tests that should have been false\n" % values
when :false:
details += " - Got %s false tests that should have been true\n" % values
+ when :feature:
+ details += " - Missing features %s\n" % values.collect { |f| f.to_s }.join(",")
end
end
notes << details
diff --git a/lib/puppet/type/mount.rb b/lib/puppet/type/mount.rb
index e18630cc8..1679b73a3 100755
--- a/lib/puppet/type/mount.rb
+++ b/lib/puppet/type/mount.rb
@@ -141,7 +141,9 @@ module Puppet
newproperty(:dump) do
desc "Whether to dump the mount. Not all platforms
- support this."
+ support this. Valid values are ``1`` or ``0``. Default is ``0``."
+
+ newvalue(%r{(0|1)}) { }
defaultto {
if @resource.managed?
diff --git a/lib/puppet/type/ssh_authorized_key.rb b/lib/puppet/type/ssh_authorized_key.rb
new file mode 100644
index 000000000..e28fb7cda
--- /dev/null
+++ b/lib/puppet/type/ssh_authorized_key.rb
@@ -0,0 +1,44 @@
+module Puppet
+ newtype(:ssh_authorized_key) do
+ @doc = "Manages ssh authorized keys."
+
+ ensurable
+
+ newparam(:name) do
+ desc "The ssh key comment."
+
+ isnamevar
+ end
+
+ newproperty(:type) do
+ desc "The encryption type used. Probably ssh-dss or ssh-rsa for
+ ssh version 2. Not used for ssh version 1."
+
+ newvalue("ssh-dss")
+ newvalue("ssh-rsa")
+
+ aliasvalue(:dsa, "ssh-dss")
+ aliasvalue(:rsa, "ssh-rsa")
+ end
+
+ newproperty(:key) do
+ desc "The key itself; generally a long string of hex digits."
+ end
+
+ newproperty(:user) do
+ desc "The user account in which the ssh key should be installed."
+ end
+
+ newproperty(:target) do
+ desc "The file in which to store the ssh key."
+ end
+
+ newproperty(:options, :array_matching => :all) do
+ desc "Key options, see sshd(8) for possible values. Multiple values
+ should be specified as an array."
+
+ defaultto do :absent end
+ end
+ end
+end
+
diff --git a/lib/puppet/util/ldap.rb b/lib/puppet/util/ldap.rb
new file mode 100644
index 000000000..33f01f789
--- /dev/null
+++ b/lib/puppet/util/ldap.rb
@@ -0,0 +1,5 @@
+#
+# Created by Luke Kanies on 2008-3-23.
+# Copyright (c) 2008. All rights reserved.
+module Puppet::Util::Ldap
+end
diff --git a/lib/puppet/util/ldap/connection.rb b/lib/puppet/util/ldap/connection.rb
new file mode 100644
index 000000000..abcc07ecb
--- /dev/null
+++ b/lib/puppet/util/ldap/connection.rb
@@ -0,0 +1,57 @@
+#
+# Created by Luke Kanies on 2008-3-23.
+# Copyright (c) 2008. All rights reserved.
+require 'puppet/util/ldap'
+
+class Puppet::Util::Ldap::Connection
+ attr_accessor :host, :port, :user, :password, :reset, :ssl
+
+ attr_reader :connection
+
+ def close
+ connection.unbind if connection.bound?
+ end
+
+ def initialize(host, port, options = {})
+ raise Puppet::Error, "Could not set up LDAP Connection: Missing ruby/ldap libraries" unless Puppet.features.ldap?
+
+ @host, @port = host, port
+
+ options.each do |param, value|
+ begin
+ send(param.to_s + "=", value)
+ rescue
+ raise ArgumentError, "LDAP connections do not support %s parameters" % param
+ end
+ end
+ end
+
+ # Create a per-connection unique name.
+ def name
+ [host, port, user, password, ssl].collect { |p| p.to_s }.join("/")
+ end
+
+ # Should we reset the connection?
+ def reset?
+ reset
+ end
+
+ # Start our ldap connection.
+ def start
+ begin
+ case ssl
+ when :tls:
+ @connection = LDAP::SSLConn.new(host, port, true)
+ when true:
+ @connection = LDAP::SSLConn.new(host, port)
+ else
+ @connection = LDAP::Conn.new(host, port)
+ end
+ @connection.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
+ @connection.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)
+ @connection.simple_bind(user, password)
+ rescue => detail
+ raise Puppet::Error, "Could not connect to LDAP: %s" % detail
+ end
+ end
+end
diff --git a/lib/puppet/util/ldap/generator.rb b/lib/puppet/util/ldap/generator.rb
new file mode 100644
index 000000000..2a868b0d9
--- /dev/null
+++ b/lib/puppet/util/ldap/generator.rb
@@ -0,0 +1,45 @@
+#
+# Created by Luke Kanies on 2008-3-28.
+# Copyright (c) 2008. All rights reserved.
+require 'puppet/util/ldap'
+
+class Puppet::Util::Ldap::Generator
+ # Declare the attribute we'll use to generate the value.
+ def from(source)
+ @source = source
+ return self
+ end
+
+ # Actually do the generation.
+ def generate(value = nil)
+ if value.nil?
+ @generator.call
+ else
+ @generator.call(value)
+ end
+ end
+
+ # Initialize our generator with the name of the parameter
+ # being generated.
+ def initialize(name)
+ @name = name
+ end
+
+ def name
+ @name.to_s
+ end
+
+ def source
+ if defined?(@source) and @source
+ @source.to_s
+ else
+ nil
+ end
+ end
+
+ # Provide the code that does the generation.
+ def with(&block)
+ @generator = block
+ return self
+ end
+end
diff --git a/lib/puppet/util/ldap/manager.rb b/lib/puppet/util/ldap/manager.rb
new file mode 100644
index 000000000..9761fc753
--- /dev/null
+++ b/lib/puppet/util/ldap/manager.rb
@@ -0,0 +1,281 @@
+require 'puppet/util/ldap'
+require 'puppet/util/ldap/connection'
+require 'puppet/util/ldap/generator'
+
+# The configuration class for LDAP providers, plus
+# connection handling for actually interacting with ldap.
+class Puppet::Util::Ldap::Manager
+ attr_reader :objectclasses, :puppet2ldap, :location, :rdn
+
+ # A null-op that just returns the config.
+ def and
+ return self
+ end
+
+ # Set the offset from the search base and return the config.
+ def at(location)
+ @location = location
+ return self
+ end
+
+ # The basic search base.
+ def base
+ [location, Puppet[:ldapbase]].join(",")
+ end
+
+ # Convert the name to a dn, then pass the args along to
+ # our connection.
+ def create(name, attributes)
+ attributes = attributes.dup
+
+ # Add the objectclasses
+ attributes["objectClass"] = objectclasses.collect { |o| o.to_s }
+ attributes["objectClass"] << "top" unless attributes["objectClass"].include?("top")
+
+ attributes[rdn.to_s] = [name]
+
+ # Generate any new values we might need.
+ generate(attributes)
+
+ # And create our resource.
+ connect { |conn| conn.add dn(name), attributes }
+ end
+
+ # Open, yield, and close the connection. Cannot be left
+ # open, at this point.
+ def connect
+ raise ArgumentError, "You must pass a block to #connect" unless block_given?
+
+ unless defined?(@connection) and @connection
+ if Puppet[:ldaptls]
+ ssl = :tls
+ elsif Puppet[:ldapssl]
+ ssl = true
+ else
+ ssl = false
+ end
+ options = {:ssl => ssl}
+ if user = Puppet[:ldapuser] and user != ""
+ options[:user] = user
+ end
+ if password = Puppet[:ldappassword] and password != ""
+ options[:password] = password
+ end
+ @connection = Puppet::Util::Ldap::Connection.new(Puppet[:ldapserver], Puppet[:ldapport], options)
+ end
+ @connection.start
+ begin
+ yield @connection.connection
+ ensure
+ @connection.close
+ end
+ return nil
+ end
+
+ # Convert the name to a dn, then pass the args along to
+ # our connection.
+ def delete(name)
+ connect { |connection| connection.delete dn(name) }
+ end
+
+ # Calculate the dn for a given resource.
+ def dn(name)
+ ["#{rdn.to_s}=%s" % name, base].join(",")
+ end
+
+ # Convert an ldap-style entry hash to a provider-style hash.
+ def entry2provider(entry)
+ raise ArgumentError, "Could not get dn from ldap entry" unless entry["dn"]
+
+ # DN is always a single-entry array. Strip off the bits before the
+ # first comma, then the bits after the remaining equal sign. This is the
+ # name.
+ name = entry["dn"].dup.pop.split(",").shift.split("=").pop
+
+ result = {:name => name}
+
+ @ldap2puppet.each do |ldap, puppet|
+ result[puppet] = entry[ldap.to_s] || :absent
+ end
+
+ result
+ end
+
+ # Create our normal search filter.
+ def filter
+ return "objectclass=%s" % objectclasses[0] if objectclasses.length == 1
+ return "(&(objectclass=" + objectclasses.join(")(objectclass=") + "))"
+ end
+
+ # Find the associated entry for a resource. Returns a hash, minus
+ # 'dn', or nil if the entry cannot be found.
+ def find(name)
+ result = nil
+ connect do |conn|
+ begin
+ conn.search2(dn(name), 0, "objectclass=*") do |result|
+ # Convert to puppet-appropriate attributes
+ return entry2provider(result)
+ end
+ rescue => detail
+ return nil
+ end
+ end
+ end
+
+ # Declare a new attribute generator.
+ def generates(parameter)
+ @generators << Puppet::Util::Ldap::Generator.new(parameter)
+ @generators[-1]
+ end
+
+ # Generate any extra values we need to make the ldap entry work.
+ def generate(values)
+ return unless @generators.length > 0
+
+ @generators.each do |generator|
+ # Don't override any values that might exist.
+ next if values[generator.name]
+
+ if generator.source
+ unless value = values[generator.source]
+ raise ArgumentError, "%s must be defined to generate %s" % [generator.source, generator.name]
+ end
+ result = generator.generate(value)
+ else
+ result = generator.generate
+ end
+
+ result = [result] unless result.is_a?(Array)
+ result = result.collect { |r| r.to_s }
+
+ values[generator.name] = result
+ end
+ end
+
+ def initialize
+ @rdn = :cn
+ @generators = []
+ end
+
+ # Specify what classes this provider models.
+ def manages(*classes)
+ @objectclasses = classes
+ return self
+ end
+
+ # Specify the attribute map. Assumes the keys are the puppet
+ # attributes, and the values are the ldap attributes, and creates a map
+ # for each direction.
+ def maps(attributes)
+ # The map with the puppet attributes as the keys
+ @puppet2ldap = attributes
+
+ # and the ldap attributes as the keys.
+ @ldap2puppet = attributes.inject({}) { |map, ary| map[ary[1]] = ary[0]; map }
+
+ return self
+ end
+
+ # Return the ldap name for a puppet attribute.
+ def ldap_name(attribute)
+ @puppet2ldap[attribute].to_s
+ end
+
+ # Convert the name to a dn, then pass the args along to
+ # our connection.
+ def modify(name, mods)
+ connect { |connection| connection.modify dn(name), mods }
+ end
+
+ # Specify the rdn that we use to build up our dn.
+ def named_by(attribute)
+ @rdn = attribute
+ self
+ end
+
+ # Return the puppet name for an ldap attribute.
+ def puppet_name(attribute)
+ @ldap2puppet[attribute]
+ end
+
+ # Search for all entries at our base. A potentially expensive search.
+ def search(sfilter = nil)
+ sfilter ||= filter()
+
+ result = []
+ connect do |conn|
+ conn.search2(base, 1, sfilter) do |entry|
+ result << entry2provider(entry)
+ end
+ end
+ return nil if result.empty?
+ return result
+ end
+
+ # Update the ldap entry with the desired state.
+ def update(name, is, should)
+ if should[:ensure] == :absent
+ Puppet.info "Removing %s from ldap" % dn(name)
+ delete(name)
+ return
+ end
+
+ # We're creating a new entry
+ if is.empty? or is[:ensure] == :absent
+ Puppet.info "Creating %s in ldap" % dn(name)
+ # Remove any :absent params and :ensure, then convert the names to ldap names.
+ attrs = ldap_convert(should)
+ create(name, attrs)
+ return
+ end
+
+ # We're modifying an existing entry. Yuck.
+
+ mods = []
+ # For each attribute we're deleting that is present, create a
+ # modify instance for deletion.
+ [is.keys, should.keys].flatten.uniq.each do |property|
+ # They're equal, so do nothing.
+ next if is[property] == should[property]
+
+ attributes = ldap_convert(should)
+
+ prop_name = ldap_name(property).to_s
+
+ # We're creating it.
+ if is[property] == :absent or is[property].nil?
+ mods << LDAP::Mod.new(LDAP::LDAP_MOD_ADD, prop_name, attributes[prop_name])
+ next
+ end
+
+ # We're deleting it
+ if should[property] == :absent or should[property].nil?
+ mods << LDAP::Mod.new(LDAP::LDAP_MOD_DELETE, prop_name, [])
+ next
+ end
+
+ # We're replacing an existing value
+ mods << LDAP::Mod.new(LDAP::LDAP_MOD_REPLACE, prop_name, attributes[prop_name])
+ end
+
+ modify(name, mods)
+ end
+
+ # Is this a complete ldap configuration?
+ def valid?
+ location and objectclasses and ! objectclasses.empty? and puppet2ldap
+ end
+
+ private
+
+ # Convert a hash of attributes to ldap-like forms. This mostly means
+ # getting rid of :ensure and making sure everything's an array of strings.
+ def ldap_convert(attributes)
+ attributes.reject { |param, value| value == :absent or param == :ensure }.inject({}) do |result, ary|
+ value = (ary[1].is_a?(Array) ? ary[1] : [ary[1]]).collect { |v| v.to_s }
+ result[ldap_name(ary[0])] = value
+ result
+ end
+ end
+end
diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb
index 1b953c95e..eec86625d 100644
--- a/lib/puppet/util/settings.rb
+++ b/lib/puppet/util/settings.rb
@@ -123,7 +123,7 @@ class Puppet::Util::Settings
if pval = self.value(varname)
pval
else
- raise Puppet::DevError, "Could not find value for %s" % parent
+ raise Puppet::DevError, "Could not find value for %s" % value
end
end
diff --git a/lib/puppet/util/storage.rb b/lib/puppet/util/storage.rb
index 9358a28e9..dc4e9cd71 100644
--- a/lib/puppet/util/storage.rb
+++ b/lib/puppet/util/storage.rb
@@ -6,6 +6,10 @@ class Puppet::Util::Storage
include Singleton
include Puppet::Util
+ def self.state
+ return @@state
+ end
+
def initialize
self.class.load
end
diff --git a/lib/puppet/util/variables.rb b/lib/puppet/util/variables.rb
deleted file mode 100644
index 1a78ef5c1..000000000
--- a/lib/puppet/util/variables.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-module Puppet::Util::Variables
- def inithooks
- @instance_init_hooks.dup
- end
-
- def initvars
- return unless defined? @class_init_hooks
- self.inithooks.each do |var, value|
- if value.is_a?(Class)
- instance_variable_set("@" + var.to_s, value.new)
- else
- instance_variable_set("@" + var.to_s, value)
- end
- end
- end
-
- def instancevar(hash)
- @instance_init_hooks ||= {}
-
- unless method_defined?(:initvars)
- define_method(:initvars) do
- self.class.inithooks.each do |var, value|
- if value.is_a?(Class)
- instance_variable_set("@" + var.to_s, value.new)
- else
- instance_variable_set("@" + var.to_s, value)
- end
- end
- end
- end
- hash.each do |var, value|
- raise("Already initializing %s" % var) if @instance_init_hooks[var]
-
- @instance_init_hooks[var] = value
- end
- end
-end
-