summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet')
-rw-r--r--lib/puppet/provider/computer/computer.rb22
-rw-r--r--lib/puppet/provider/group/directoryservice.rb5
-rw-r--r--lib/puppet/provider/nameservice/directoryservice.rb109
-rw-r--r--lib/puppet/type/computer.rb59
-rwxr-xr-xlib/puppet/type/group.rb38
5 files changed, 198 insertions, 35 deletions
diff --git a/lib/puppet/provider/computer/computer.rb b/lib/puppet/provider/computer/computer.rb
new file mode 100644
index 000000000..76d0f1883
--- /dev/null
+++ b/lib/puppet/provider/computer/computer.rb
@@ -0,0 +1,22 @@
+require 'puppet/provider/nameservice/directoryservice'
+
+Puppet::Type.type(:computer).provide :directoryservice, :parent => Puppet::Provider::NameService::DirectoryService do
+ desc "Computer object management using DirectoryService on OS X.
+
+ Note that these are distinctly different kinds of objects to 'hosts',
+ as they require a MAC address and can have all sorts of policy attached to
+ them.
+
+ This provider only manages Computer objects in the local directory service
+ domain, not in remote directories.
+
+ If you wish to manage /etc/hosts on Mac OS X, then simply use the host
+ type as per other platforms.
+ "
+
+ confine :operatingsystem => :darwin
+ defaultfor :operatingsystem => :darwin
+
+ # hurray for abstraction. The nameservice directoryservice provider can
+ # handle everything we need. super.
+end \ No newline at end of file
diff --git a/lib/puppet/provider/group/directoryservice.rb b/lib/puppet/provider/group/directoryservice.rb
index 406622224..2f393052b 100644
--- a/lib/puppet/provider/group/directoryservice.rb
+++ b/lib/puppet/provider/group/directoryservice.rb
@@ -16,8 +16,9 @@ require 'puppet/provider/nameservice/directoryservice'
Puppet::Type.type(:group).provide :directoryservice, :parent => Puppet::Provider::NameService::DirectoryService do
desc "Group management using DirectoryService on OS X."
-
+
commands :dscl => "/usr/bin/dscl"
confine :operatingsystem => :darwin
- #defaultfor :operatingsystem => :darwin
+ defaultfor :operatingsystem => :darwin
+ has_feature :manages_members
end
diff --git a/lib/puppet/provider/nameservice/directoryservice.rb b/lib/puppet/provider/nameservice/directoryservice.rb
index ecd5fa6f4..0e8002c31 100644
--- a/lib/puppet/provider/nameservice/directoryservice.rb
+++ b/lib/puppet/provider/nameservice/directoryservice.rb
@@ -16,6 +16,7 @@ require 'puppet'
require 'puppet/provider/nameservice'
require 'facter/util/plist'
+
class Puppet::Provider::NameService
class DirectoryService < Puppet::Provider::NameService
# JJM: Dive into the eigenclass
@@ -27,6 +28,7 @@ class DirectoryService < Puppet::Provider::NameService
attr_writer :ds_path
end
+
# JJM 2007-07-24: Not yet sure what initvars() does. I saw it in netinfo.rb
# I do know, however, that it makes methods "work" =)
# e.g. addcmd isn't available if this method call isn't present.
@@ -37,6 +39,7 @@ class DirectoryService < Puppet::Provider::NameService
initvars()
commands :dscl => "/usr/bin/dscl"
+ commands :dseditgroup => "/usr/sbin/dseditgroup"
confine :operatingsystem => :darwin
defaultfor :operatingsystem => :darwin
@@ -56,6 +59,9 @@ class DirectoryService < Puppet::Provider::NameService
'RealName' => :comment,
'Password' => :password,
'GeneratedUID' => :guid,
+ 'IPAddress' => :ip_address,
+ 'ENetAddress' => :en_address,
+ 'GroupMembership' => :members,
}
# JJM The same table as above, inverted.
@@ns_to_ds_attribute_map = {
@@ -67,6 +73,9 @@ class DirectoryService < Puppet::Provider::NameService
:comment => 'RealName',
:password => 'Password',
:guid => 'GeneratedUID',
+ :en_address => 'ENetAddress',
+ :ip_address => 'IPAddress',
+ :members => 'GroupMembership',
}
@@password_hash_dir = "/var/db/shadow/hash"
@@ -137,7 +146,10 @@ class DirectoryService < Puppet::Provider::NameService
dscl_plist.keys().each do |key|
ds_attribute = key.sub("dsAttrTypeStandard:", "")
next unless (@@ds_to_ns_attribute_map.keys.include?(ds_attribute) and type_properties.include? @@ds_to_ns_attribute_map[ds_attribute])
- ds_value = dscl_plist[key][0] # only care about the first entry...
+ ds_value = dscl_plist[key]
+ if not @@ds_to_ns_attribute_map[ds_attribute] == :members # only members uses arrays so far
+ ds_value = ds_value[0]
+ end
attribute_hash[@@ds_to_ns_attribute_map[ds_attribute]] = ds_value
end
@@ -229,7 +241,6 @@ class DirectoryService < Puppet::Provider::NameService
if ensure_value == :present
@resource.class.validproperties.each do |name|
next if name == :ensure
-
# LAK: We use property.sync here rather than directly calling
# the settor method because the properties might do some kind
# of conversion. In particular, the user gid property might
@@ -261,18 +272,36 @@ class DirectoryService < Puppet::Provider::NameService
end
end
- def modifycmd(property, value)
- # JJM: This method will assemble a exec vector which modifies
- # a single property and it's value using dscl.
- # JJM: With /usr/bin/dscl, the -create option will destroy an
- # existing property record if it exists
- exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
- # JJM: The following line just maps the NS name to the DS name
- # e.g. { :uid => 'UniqueID' }
- exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(property)]
- # JJM: The following line sends the actual value to set the property to
- exec_arg_vector << value.to_s
- return exec_arg_vector
+ # NBK: we override @parent.set as we need to execute a series of commands
+ # to deal with array values, rather than the single command nameservice.rb
+ # expects to be returned by modifycmd. Thus we don't bother defining modifycmd.
+
+ def set(param, value)
+ self.class.validate(param, value)
+ current_members = @property_value_cache_hash[:members]
+ if param == :members
+ # If we are meant to be authoritative for the group membership
+ # then remove all existing members who haven't been specified
+ # in the manifest.
+ if @resource[:auth_membership] and not current_members.nil?
+ remove_unwanted_members(current_members, value)
+ end
+
+ # if they're not a member, make them one.
+ add_members(current_members, value)
+ else
+ exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
+ # JJM: The following line just maps the NS name to the DS name
+ # e.g. { :uid => 'UniqueID' }
+ exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(param)]
+ # JJM: The following line sends the actual value to set the property to
+ exec_arg_vector << value.to_s
+ begin
+ execute(exec_arg_vector)
+ 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
# NBK: we override @parent.create as we need to execute a series of commands
@@ -307,20 +336,50 @@ class DirectoryService < Puppet::Provider::NameService
end
# Now we create all the standard properties
- Puppet::Type.type(:user).validproperties.each do |property|
+ Puppet::Type.type(@resource.class.name).validproperties.each do |property|
next if property == :ensure
if value = @resource.should(property) and value != ""
- exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
- exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(property)]
- next if property == :password # skip setting the password here
- exec_arg_vector << value.to_s
+ if property == :members
+ add_members(nil, value)
+ else
+ exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
+ exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(property)]
+ next if property == :password # skip setting the password here
+ exec_arg_vector << value.to_s
+ begin
+ execute(exec_arg_vector)
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error, "Could not create %s %s: %s" %
+ [@resource.class.name, @resource.name, detail]
+ end
+ end
+ end
+ end
+ end
+
+ def remove_unwanted_members(current_members, new_members)
+ current_members.each do |member|
+ if not value.include?(member)
+ cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-d", member, @resource[:name]]
begin
- execute(exec_arg_vector)
+ execute(cmd)
rescue Puppet::ExecutionFailure => detail
- raise Puppet::Error, "Could not create %s %s: %s" %
- [@resource.class.name, @resource.name, detail]
- end
- end
+ raise Puppet::Error, "Could not set %s on %s[%s]: %s" % [param, @resource.class.name, @resource.name, detail]
+ end
+ end
+ end
+ end
+
+ def add_members(current_members, new_members)
+ new_members.each do |user|
+ if current_members.nil? or not current_members.include?(user)
+ cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-a", user, @resource[:name]]
+ 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
end
@@ -376,4 +435,4 @@ class DirectoryService < Puppet::Provider::NameService
return @property_value_cache_hash
end
end
-end
+end \ No newline at end of file
diff --git a/lib/puppet/type/computer.rb b/lib/puppet/type/computer.rb
new file mode 100644
index 000000000..0c0a70900
--- /dev/null
+++ b/lib/puppet/type/computer.rb
@@ -0,0 +1,59 @@
+Puppet::Type.newtype(:computer) do
+
+ @doc = "Computer object management using DirectoryService on OS X.
+
+ Note that these are distinctly different kinds of objects to 'hosts',
+ as they require a MAC address and can have all sorts of policy attached to
+ them.
+
+ This provider only manages Computer objects in the local directory service
+ domain, not in remote directories.
+
+ If you wish to manage /etc/hosts on Mac OS X, then simply use the host
+ type as per other platforms.
+
+ This type primarily exists to create localhost Computer objects that MCX
+ policy can then be attached to."
+
+ # ensurable
+
+ # We autorequire the computer object in case it is being managed at the
+ # file level by Puppet.
+
+ autorequire(:file) do
+ if self[:name]
+ "/var/db/dslocal/nodes/Default/computers/#{self[:name]}.plist"
+ else
+ nil
+ end
+ end
+
+ newproperty(:ensure, :parent => Puppet::Property::Ensure) do
+ newvalue(:present) do
+ provider.create
+ end
+
+ newvalue(:absent) do
+ Puppet.notice "prop ensure = absent"
+ provider.delete
+ end
+ end
+
+ newparam(:name) do
+ desc "The "
+ isnamevar
+ end
+
+ newparam(:realname) do
+ desc "realname"
+ end
+
+ newproperty(:en_address) do
+ desc "The MAC address of the primary network interface. Must match en0."
+ end
+
+ newproperty(:ip_address) do
+ desc "The IP Address of the Computer object."
+ end
+
+end \ No newline at end of file
diff --git a/lib/puppet/type/group.rb b/lib/puppet/type/group.rb
index 29486d3f0..1167962fe 100755
--- a/lib/puppet/type/group.rb
+++ b/lib/puppet/type/group.rb
@@ -11,15 +11,22 @@ require 'facter'
module Puppet
newtype(:group) do
- @doc = "Manage groups. This type can only create groups. Group
- membership must be managed on individual users. This resource type
- uses the prescribed native tools for creating groups and generally
- uses POSIX APIs for retrieving information about them. It does
- not directly modify ``/etc/group`` or anything.
+ @doc = "Manage groups. On most platforms this can only create groups.
+ Group membership must be managed on individual users.
+
+ On OS X, group membership is managed as an attribute of the group.
+ This resource type uses the prescribed native tools for creating
+ groups and generally uses POSIX APIs for retrieving information
+ about them. It does not directly modify ``/etc/group`` or anything.
For most platforms, the tools used are ``groupadd`` and its ilk;
- for Mac OS X, NetInfo is used. This is currently unconfigurable,
- but if you desperately need it to be so, please contact us."
+ for Mac OS X, dscl/dseditgroup are used.
+
+ This is currently unconfigurable, but if you desperately need it
+ to be so, please contact us."
+
+ feature :manages_members,
+ "For directories where membership is an attribute of groups not users."
ensurable do
desc "Create or remove the group."
@@ -73,13 +80,28 @@ module Puppet
return gid
end
end
+
+ newproperty(:members, :array_matching => :all, :required_features => :manages_members) do
+ desc "The members of the group. For directory services where group
+ membership is stored in the group objects, not the users."
+
+ def change_to_s(currentvalue, newvalue)
+ currentvalue = currentvalue.join(",") if currentvalue != :absent
+ newvalue = newvalue.join(",")
+ super(currentvalue, newvalue)
+ end
+ end
+
+ newparam(:auth_membership) do
+ desc "whether the provider is authoritative for group membership."
+ defaultto true
+ end
newparam(:name) do
desc "The group name. While naming limitations vary by
system, it is advisable to keep the name to the degenerate
limitations, which is a maximum of 8 characters beginning with
a letter."
-
isnamevar
end