diff options
Diffstat (limited to 'lib/puppet')
-rw-r--r-- | lib/puppet/provider/computer/computer.rb | 22 | ||||
-rw-r--r-- | lib/puppet/provider/group/directoryservice.rb | 5 | ||||
-rw-r--r-- | lib/puppet/provider/nameservice/directoryservice.rb | 109 | ||||
-rw-r--r-- | lib/puppet/type/computer.rb | 59 | ||||
-rwxr-xr-x | lib/puppet/type/group.rb | 38 |
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 |