diff options
Diffstat (limited to 'lib/puppet')
-rw-r--r-- | lib/puppet/provider/mcx/mcxcontent.rb | 199 | ||||
-rw-r--r-- | lib/puppet/type/mcx.rb | 114 |
2 files changed, 313 insertions, 0 deletions
diff --git a/lib/puppet/provider/mcx/mcxcontent.rb b/lib/puppet/provider/mcx/mcxcontent.rb new file mode 100644 index 000000000..fdcc8cc5d --- /dev/null +++ b/lib/puppet/provider/mcx/mcxcontent.rb @@ -0,0 +1,199 @@ +#-- +# Copyright (C) 2008 Jeffrey J McCune. + +# This program and entire repository is free software; you can +# redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software +# Foundation; either version 2 of the License, or any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Author: Jeff McCune <mccune.jeff@gmail.com> + +require 'tempfile' + +Puppet::Type.type(:mcx).provide :mcxcontent, :parent => Puppet::Provider do + + desc "MCX Settings management using DirectoryService on OS X. + +This provider manages the entire MCXSettings attribute available +to some directory services nodes. This management is 'all or nothing' +in that discrete application domain key value pairs are not managed +by this provider. + +It is recommended to use WorkGroup Manager to configure Users, Groups, +Computers, or ComputerLists, then use 'ralsh mcx' to generate a puppet +manifest from the resulting configuration. + +Original Author: Jeff McCune (mccune.jeff@gmail.com)" + + # This provides a mapping of puppet types to DirectoryService + # type strings. + TypeMap = { + :user => "Users", + :group => "Groups", + :computer => "Computers", + :computergroup => "ComputerGroups", + } + + class MCXContentProviderException < Exception + + end + + commands :dscl => "/usr/bin/dscl" + confine :operatingsystem => :darwin + defaultfor :operatingsystem => :darwin + + # self.instances is all important. + # This is the only class method, it returns + # an array of instances of this class. + def self.instances + mcx_list = [] + for ds_type in TypeMap.keys + ds_path = "/Local/Default/#{TypeMap[ds_type]}" + output = dscl 'localhost', '-list', ds_path + member_list = output.split + for ds_name in member_list + content = mcxexport(ds_type, ds_name) + if content.empty? + Puppet.debug "/#{TypeMap[ds_type]}/#{ds_name} has no MCX data." + else + # This node has MCX data. + rsrc = self.new(:name => "/#{TypeMap[ds_type]}/#{ds_name}", + :ds_type => ds_type, + :ds_name => ds_name, + :content => content) + mcx_list << rsrc + end + end + end + return mcx_list + end + + private + + # mcxexport is used by instances, and therefore + # a class method. + def self.mcxexport(ds_type, ds_name) + ds_t = TypeMap[ds_type] + ds_n = ds_name.to_s + ds_path = "/Local/Default/#{ds_t}/#{ds_n}" + + dscl 'localhost', '-mcxexport', ds_path + end + + def mcximport(ds_type, ds_name, val) + ds_t = TypeMap[ds_type] + ds_n = ds_name.to_s + ds_path = "/Local/Default/#{ds_t}/#{ds_name}" + + tmp = Tempfile.new('puppet_mcx') + begin + tmp << val + tmp.flush + dscl 'localhost', '-mcximport', ds_path, tmp.path + ensure + tmp.close + tmp.unlink + end + end + + # Given the resource name string, parse ds_type out. + def parse_type(name) + tmp = name.split('/')[1] + if ! tmp.is_a? String + raise MCXContentProviderException, + "Coult not parse ds_type from resource name '#{name}'. Specify with ds_type parameter." + end + # De-pluralize and downcase. + tmp = tmp.chop.downcase.to_sym + if not TypeMap.keys.member? tmp + raise MCXContentProviderException, + "Coult not parse ds_type from resource name '#{name}'. Specify with ds_type parameter." + end + return tmp + end + + # Given the resource name string, parse ds_name out. + def parse_name(name) + ds_name = name.split('/')[2] + if ! ds_name.is_a? String + raise MCXContentProviderException, + "Could not parse ds_name from resource name '#{name}'. Specify with ds_name parameter." + end + return ds_name + end + + # Gather ds_type and ds_name from resource or + # parse it out of the name. + # This is a private instance method, not a class method. + def get_dsparams + ds_type = resource[:ds_type] + if ds_type.nil? + ds_type = parse_type(resource[:name]) + end + + ds_name = resource[:ds_name] + if ds_name.nil? + ds_name = parse_name(resource[:name]) + end + + rval = { + :ds_type => ds_type.to_sym, + :ds_name => ds_name, + } + + return rval + + end + + public + + def create + self.content=(resource[:content]) + end + + def destroy + ds_parms = get_dsparams + ds_t = TypeMap[ds_parms[:ds_type]] + ds_n = ds_parms[:ds_name].to_s + ds_path = "/Local/Default/#{ds_t}/#{ds_n}" + + dscl 'localhost', '-mcxdelete', ds_path + end + + def exists? + # JJM Just re-use the content method and see if it's empty. + begin + mcx = content + rescue Puppet::ExecutionFailure => e + return false + end + has_mcx = ! mcx.empty? + return has_mcx + end + + def content + ds_parms = get_dsparams + mcx = self.class.mcxexport(ds_parms[:ds_type], + ds_parms[:ds_name]) + return mcx + end + + def content=(value) + # dscl localhost -mcximport + ds_parms = get_dsparams + mcx = mcximport(ds_parms[:ds_type], + ds_parms[:ds_name], + resource[:content]) + return mcx + end + +end diff --git a/lib/puppet/type/mcx.rb b/lib/puppet/type/mcx.rb new file mode 100644 index 000000000..ec33afd13 --- /dev/null +++ b/lib/puppet/type/mcx.rb @@ -0,0 +1,114 @@ +#-- +# Copyright (C) 2008 Jeffrey J McCune. + +# This program and entire repository is free software; you can +# redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software +# Foundation; either version 2 of the License, or any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Author: Jeff McCune <mccune.jeff@gmail.com> + +Puppet::Type.newtype(:mcx) do + + @doc = "MCX object management using DirectoryService on OS X. + +Original Author: Jeff McCune <mccune.jeff@gmail.com> + +The default provider of this type merely manages the XML plist as +reported by the dscl -mcxexport command. This is similar to the +content property of the file type in Puppet. + +The recommended method of using this type is to use Work Group Manager +to manage users and groups on the local computer, record the resulting +puppet manifest using the command 'ralsh mcx' then deploying this +to other machines. +" + feature :manages_content, \ + "The provider can manage MCXSettings as a string.", + :methods => [:content, :content=] + + ensurable do + desc "Create or remove the MCX setting." + + newvalue(:present) do + provider.create + end + + newvalue(:absent) do + provider.destroy + end + + end + + newparam(:name) do + desc "The name of the resource being managed. + The default naming convention follows Directory Service paths: + '/Computers/localhost' + '/Groups/admin' + '/Users/localadmin' + + The ds_type and ds_name type parameters are not necessary if the + default naming convention is followed." + isnamevar + end + + newparam(:ds_type) do + + desc "The DirectoryService type this MCX setting attaches to." + + newvalues(:user, :group, :computer, :computerlist) + + end + + newparam(:ds_name) do + desc "The name to attach the MCX Setting to. + e.g. 'localhost' when ds_type => computer. This setting is not + required, as it may be parsed so long as the resource name is + parseable. e.g. /Groups/admin where 'group' is the dstype." + end + + newproperty(:content, :required_features => :manages_content) do + desc "The XML Plist. The value of MCXSettings in DirectoryService. + This is the standard output from the system command: + dscl localhost -mcxexport /Local/Default/<ds_type>/<ds_name> + Note that ds_type is capitalized and plural in the dscl command." + end + + # JJM Yes, this is not DRY at all. Because of the code blocks + # autorequire must be done this way. I think. + + def setup_autorequire(type) + # value returns a Symbol + name = value(:name) + ds_type = value(:ds_type) + ds_name = value(:ds_name) + if ds_type == type + rval = [ ds_name.to_s ] + else + rval = [ ] + end + rval + end + + autorequire(:user) do + setup_autorequire(:user) + end + + autorequire(:group) do + setup_autorequire(:group) + end + + autorequire(:computer) do + setup_autorequire(:computer) + end + +end |