summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet')
-rw-r--r--lib/puppet/provider/mcx/mcxcontent.rb199
-rw-r--r--lib/puppet/type/mcx.rb114
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