summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
authorJeffrey McCune <mccune.jeff@gmail.com>2008-11-28 22:39:22 -0500
committerJames Turnbull <james@lovedthanlost.net>2008-12-01 18:05:06 +1100
commit9329c95d6fbb2df5e8b754620427645f6eae69b9 (patch)
tree5694255e239a405e9418be9dce4166afc354053d /lib/puppet
parent83b3a1ec2a98edeab12a5eb9c4b107a6fe08613c (diff)
downloadpuppet-9329c95d6fbb2df5e8b754620427645f6eae69b9.tar.gz
puppet-9329c95d6fbb2df5e8b754620427645f6eae69b9.tar.xz
puppet-9329c95d6fbb2df5e8b754620427645f6eae69b9.zip
type/mcx.rb Feature #1026 - MCX Type
Added new MCX type and base test. This type manages MCX settings on DirectoryService nodes. These settings take the form of plist XML documents attached to Users, Groups, and Computers in DirectoryService.
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