diff options
author | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2005-09-16 21:53:03 +0000 |
---|---|---|
committer | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2005-09-16 21:53:03 +0000 |
commit | e0d1d311d9d8335b938bd0312f6a712ce2c5127f (patch) | |
tree | e3c6a46a81268e88082bfd2e7bf704f71b99e9b7 /lib/puppet | |
parent | 29c291a0e8bf55be0835fcf11df595b762686db6 (diff) | |
download | puppet-e0d1d311d9d8335b938bd0312f6a712ce2c5127f.tar.gz puppet-e0d1d311d9d8335b938bd0312f6a712ce2c5127f.tar.xz puppet-e0d1d311d9d8335b938bd0312f6a712ce2c5127f.zip |
temporary commit so i can move to my workstation; groups and users are way b0rked
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@682 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/puppet')
-rwxr-xr-x | lib/puppet/type/group.rb | 323 | ||||
-rw-r--r-- | lib/puppet/type/nameservice/netinfo.rb | 138 | ||||
-rw-r--r-- | lib/puppet/type/nameservice/objectadd.rb | 135 | ||||
-rw-r--r-- | lib/puppet/type/nameservice/posix.rb | 128 | ||||
-rwxr-xr-x | lib/puppet/type/user.rb | 252 |
5 files changed, 512 insertions, 464 deletions
diff --git a/lib/puppet/type/group.rb b/lib/puppet/type/group.rb index 0654a15f6..80b83f000 100755 --- a/lib/puppet/type/group.rb +++ b/lib/puppet/type/group.rb @@ -1,4 +1,10 @@ -# $Id$ +# Manage Unix groups. This class is annoyingly complicated; There +# is some variety in whether systems use 'groupadd' or 'addgroup', but OS X +# significantly complicates the picture by using NetInfo. Eventually we +# will also need to deal with systems that have their groups hosted elsewhere +# (e.g., in LDAP). That will likely only be a problem for OS X, since it +# currently does not use the POSIX interfaces, since lookupd's cache screws +# things up. require 'etc' require 'facter' @@ -6,44 +12,17 @@ require 'puppet/type/state' module Puppet class State - class GroupState < Puppet::State - attr_accessor :xaddflag, :ninfoarg, :infomethod - class << self - [:infomethod, :ninfokey, :xaddflag].each { |method| - self.send(:define_method, method) { - # *shudder* - if eval("defined? @%s" % method) and eval("@%s" % method) - return eval("@%s" % method) - else - return @name - end - } - } + module GroupGID + def self.doc + "The group ID. Must be specified numerically. If not + specified, a number will be picked, which can result in ID + differences across systems and thus is not recommended. The + GID is picked according to local system standards." end - # we use the POSIX interfaces to retrieve all information, - # so we don't have to worry about abstracting that across - # the system - def retrieve - if obj = @parent.getinfo(true) - - method = self.class.infomethod - @is = obj.send(method) - else - @is = :notfound - end - + def self.name + :gid end - end - - class GroupGID < GroupState - @doc = "The group ID. Must be specified numerically. If not - specified, a number will be picked, which can result in ID - differences across systems and thus is not recommended. The - method for picking GIDs is basically to find the next GID above - the highest existing GID excluding those above 65000." - @name = :gid - @xaddflag = "-g" def should=(gid) if gid.is_a?(String) @@ -63,279 +42,27 @@ module Puppet @should = gid end end - - class GroupName < GroupState - @doc = "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." - @name = :name - - def should=(name) - Puppet.info "Setting group name to %s" % name - - @should = name - end - end - - module GroupXAdd - def self.test - system("which groupadd > /dev/null 2>&1") - - if $? == 0 - return true - else - Puppet.err "Could not find groupadd" - return false - end - end - - def sync - obj = @parent.getinfo - - if @is == :notfound or @should == :notfound - return syncname() - end - - obj = @parent.getinfo - - if obj.nil? - raise Puppet::DevError, - "Group does not exist; cannot set gid" - end - - if @should == :notfound - # we have to depend on the 'name' state doing the deletion - return nil - end - cmd = [ - "groupmod", self.class.xaddflag, "'%s'" % @should, @parent.name - ].join(" ") - - output = %x{#{cmd} 2>&1} - - unless $? == 0 - raise Puppet::Error, "Could not modify %s on group %s: %s" % - [self.class.name, @parent.name, output] - end - - return :group_modified - end - - private - def syncname - obj = @parent.getinfo - - cmd = nil - event = nil - if @should == :notfound - # we need to remove the object... - if obj.nil? - # the group already doesn't exist - return nil - end - - cmd = ["groupdel", @parent.name] - type = "delete" - else - unless obj.nil? - raise Puppet::DevError, - "Got told to create a group that already exists" - end - # we're creating the group - - # i can just tell i'm going to regret this - # why doesn't POSIX include interfaces for adding users - # and groups? it's stupid - cmd = ["groupadd"] - if gid = @parent.should(:gid) - cmd << "-g" << gid - end - cmd << @parent.name - type = "create" - end - - output = %x{#{cmd.join(" ")} 2>&1} - - unless $? == 0 - raise Puppet::Error, "Could not %s group %s: %s" % - [type, @parent.name, output] - end - - return "group_#{type}d".intern - end - end - - module GroupNInfo - def self.test - system("which niutil > /dev/null 2>&1") - - if $? == 0 - return true - else - Puppet.err "Could not find niutil" - return false - end - end - - def self.flush - output = %x{lookupd -flushcache 2>&1} - - if $? != 0 - Puppet.err "Could not flush lookupd cache: %s" % output - end - end - - def retrieve - cmd = %w{nireport / /groups name} - - if key = self.class.ninfokey - cmd << key.to_s - else - raise Puppet::DevError, - "Could not find ninfokey for state %s" % - self.class.name - end - - output = %x{#{cmd.join(" ")} 2>&1}.split("\n").each { |line| - name, value = line.chomp.split(/\s+/) - - if name == @parent.name - if value =~ /^\d+$/ - @is = Integer(value) - else - @is = value - end - end - } - - unless defined? @is - @is = :notfound - end - end - - def sync - events = [] - - unless @parent.exists? - events << syncname() - end - - if @should == :notfound - return syncname() - end - obj = @parent.getinfo - - if obj.nil? - raise Puppet::DevError, - "Group does not exist; cannot set gid" - end - - cmd = ["niutil"] - - cmd << "-createprop" << "/" << "/groups/%s" % @parent.name - - if key = self.class.ninfokey - cmd << key << "'%s'" % @should - else - raise Puppet::DevError, - "Could not find ninfokey for state %s" % - self.class.name - end - - output = %x{#{cmd.join(" ")} 2>&1} - - unless $? == 0 - raise Puppet::Error, "Could not set %s on group %s: %s" % - [self.class.name, @parent.name, output] - end - - GroupNInfo.flush() - - events << :group_modified - return events - end - - private - def syncname - - cmd = ["niutil"] - event = nil - if @should == :notfound - # we need to remove the object... - unless @parent.exists? - # the group already doesn't exist - return nil - end - - cmd << "-destroy" - type = "delete" - else - if @parent.exists? - raise Puppet::DevError, - "Got told to create a group that already exists" - end - # we're creating the group - - # i can just tell i'm going to regret this - # why doesn't POSIX include interfaces for adding users - # and groups? it's stupid - cmd << "-create" - type = "create" - end - - cmd << "/" << "/groups/%s" % @parent.name - - output = %x{#{cmd.join(" ")} 2>&1} - - unless $? == 0 - raise Puppet::Error, "Could not %s group %s: %s" % - [type, @parent.name, output] - end - - GroupNInfo.flush() - - return "group_#{type}d".intern - end - end end class Type class Group < Type - @states = [ - Puppet::State::GroupGID - ] - - @@extender = nil + statemodule = nil case Facter["operatingsystem"].value when "Darwin": - @@extender = "NInfo" + statemodule = Puppet::NameService::NetInfo::NetInfoGroup else - @@extender = "XAdd" + statemodule = Puppet::NameService::ObjectAdd::ObjectAddGroup end + @states = statemodule.substates + @name = :group @namevar = :name - # all of the states are very similar, but syncing is different - # for each _type_ of state - @states.each { |state| - begin - klass = eval("Puppet::State::Group" + @@extender) - if klass.test - state.send(:include, klass) - else - Puppet.err "Cannot sync %s on %s" % - [state.name, @name] - end - rescue NameError - Puppet.notice "No %s extender for %s" % - [@@extender, state.name] - end - } - @parameters = [:name] + @netinfodir = "groups" + @paramdoc[:name] = "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 @@ -344,7 +71,6 @@ module Puppet @doc = "Manage groups. This type can only create groups. Group membership must be managed on individual users." - def exists? case @@extender when "NInfo": @@ -367,9 +93,6 @@ module Puppet def getinfo(refresh = false) if @groupinfo.nil? or refresh == true begin - #GroupNInfo.flush() - system("lookupd -flushcache") - #sleep(4) @groupinfo = Etc.getgrnam(self.name) rescue ArgumentError => detail @groupinfo = nil @@ -415,3 +138,5 @@ module Puppet end end end + +# $Id$ diff --git a/lib/puppet/type/nameservice/netinfo.rb b/lib/puppet/type/nameservice/netinfo.rb new file mode 100644 index 000000000..3a86bb9e4 --- /dev/null +++ b/lib/puppet/type/nameservice/netinfo.rb @@ -0,0 +1,138 @@ +# Manage NetInfo POSIX objects. Probably only used on OS X, but I suppose +# it could be used elsewhere. + +module Puppet + class Type + # Return the NetInfo directory in which a given object type is stored. + # Defaults to the type's name if @netinfodir is unset. + def self.netinfodir + if defined? @netinfodir and @netinfodir + return @netinfodir + else + return @name + end + end + end + + module NameService + module NetInfo + def self.test + system("which niutil > /dev/null 2>&1") + + if $? == 0 + return true + else + Puppet.err "Could not find niutil" + return false + end + + system("which nireport > /dev/null 2>&1") + + if $? == 0 + return true + else + Puppet.err "Could not find nireport" + return false + end + end + + def self.flush + output = %x{lookupd -flushcache 2>&1} + + if $? != 0 + Puppet.err "Could not flush lookupd cache: %s" % output + end + end + + class NetInfoState < POSIX::POSIXState + def self.netinfokey + if defined? @netinfokey and @netinfokey + return @netinfokey + else + return @name + end + end + + def retrieve + dir = @parent.class.netinfodir + cmd = ["nireport", "/" "/%s" % dir, "name"] + + if key = self.class.netinfokey + cmd << key.to_s + else + raise Puppet::DevError, + "Could not find netinfokey for state %s" % + self.class.name + end + + output = %x{#{cmd.join(" ")} 2>&1}.split("\n").each { |line| + name, value = line.chomp.split(/\s+/) + + if name == @parent.name + if value =~ /^\d+$/ + @is = Integer(value) + else + @is = value + end + end + } + + unless defined? @is + @is = :notfound + end + end + + def addcmd + creatorcmd("-create") + end + + def creatorcmd(arg) + cmd = ["niutil"] + cmd << arg + + cmd << "/" << "/%s/%s" % + [@parent.class.netinfodir, @parent.name] + + cmd.join(" ") + end + + def deletecmd + creatorcmd("-destroy") + end + + def modifycmd + cmd = ["niutil"] + + cmd << "-createprop" << "/" << "/%s/%s" % + [@parent.class.netinfodir, @parent.name] + + if key = self.class.netinfokey + cmd << key << "'%s'" % @should + else + raise Puppet::DevError, + "Could not find netinfokey for state %s" % + self.class.name + end + cmd.join(" " + end + end + + class NetInfoGroup < NetInfoState + @subs = [] + def self.inherited(sub) + @subs << sub + mod = "Puppet::State::%s" % + sub.to_s.sub(/.+::/,'') + sub.include(eval(mod)) + end + + def self.substates + @subs + end + + end + + class NetInfoGID < NetInfoState; end + end + end +end diff --git a/lib/puppet/type/nameservice/objectadd.rb b/lib/puppet/type/nameservice/objectadd.rb new file mode 100644 index 000000000..f58ce7739 --- /dev/null +++ b/lib/puppet/type/nameservice/objectadd.rb @@ -0,0 +1,135 @@ +module Puppet + class Type + def self.objectaddflag + if defined? @objectaddflag and @objectaddflag + return @objectaddflag + else + return @name + end + end + end + + module NameService + module ObjectAdd + # Verify that we've got the commands necessary to manage flat files. + def self.test + system("which groupadd > /dev/null 2>&1") + + if $? == 0 + return true + else + Puppet.err "Could not find groupadd" + return false + end + end + + class ObjectAddGroup < POSIX::POSIXState + def addcmd + cmd = ["groupadd"] + if gid = @parent.should(:gid) + cmd << "-g" << gid + end + + return cmd.join(" ") + end + + def deletecmd + "groupdel %s" % @parent.name + end + + def modifycmd + [ + "groupmod", + self.class.xaddflag, + "'%s'" % @should, + @parent.name + ].join(" ") + end + end + + class GroupGID < ObjectAddGroup + @objectaddflag = "-g" + end + + class ObjectAddUser < POSIX::POSIXState + class << self + attr_accessor :extender + end + + @subs = [] + def self.inherited(sub) + @subs << sub + mod = "Puppet::State::%s" % + sub.to_s.sub(/.+::/,'') + begin + modklass = eval(mod) + rescue NameError + raise Puppet::Error, + "Could not find extender module for %s" % sub.to_s + end + sub.include(modklass) + + sub.extender = modklass + end + + def self.substates + @subs + end + + def addcmd + cmd = ["useradd"] + @parent.eachstate { |state| + # the value needs to be quoted, mostly because -c might + # have spaces in it + cmd << state.class.objectaddflag << "'%s'" % state.should + } + + cmd.join(" ") + end + + def deletecmd + ["userdel", @parent.name].join(" ") + end + + def modifycmd + cmd = [ + "usermod", + self.class.objectaddflag, + "'%s'" % @should, + @parent.name + ].join(" ") + end + end + class UserUID < ObjectAddUser + @objectaddflag = "-u" + end + + class UserGID < ObjectAddUser + @objectaddflag = "-g" + end + + class UserComment < ObjectAddUser + @objectaddflag = "-d" + end + + class UserHome < ObjectAddUser + @objectaddflag = "-d" + end + + class UserShell < ObjectAddUser + @objectaddflag = "-s" + end + + class UserLocked < ObjectAddUser + end + + class UserExpire < ObjectAddUser + @objectaddflag = "-e" + end + + class UserInactive < ObjectAddUser + @objectaddflag = "-f" + end + end + end +end diff --git a/lib/puppet/type/nameservice/posix.rb b/lib/puppet/type/nameservice/posix.rb new file mode 100644 index 000000000..c2faefa74 --- /dev/null +++ b/lib/puppet/type/nameservice/posix.rb @@ -0,0 +1,128 @@ +module Puppet + class Type + def self.posixmethod + if defined? @posixmethod and @posixmethod + return @posixmethod + else + return @name + end + end + end + + module NameService + # This is the base module for basically all of the NSS stuff. It + # should be able to retrieve the info for almost any system, but + # it can't create any information on its own. You need to define + # a subclass of these classes to actually modify the system. + module POSIX + class POSIXState < Puppet::State + def self.doc + if defined? @extender + @extender.doc + else + nil + end + end + + def self.posixmethod + if defined? @extender + if @extender.respond_to?(:posixmethod) + else + return @name + end + else + return @name + end + end + + def self.name + @extender.name + end + # we use the POSIX interfaces to retrieve all information, + # so we don't have to worry about abstracting that across + # the system + def retrieve + if obj = @parent.getinfo(true) + + method = self.class.posixmethod + @is = obj.send(method) + else + @is = :notfound + end + + end + def sync + obj = @parent.getinfo + + # if the object needs to be created or deleted, + # depend on another method to do it all at once + if @is == :notfound or @should == :notfound + return syncname() + end + + if obj.nil? + raise Puppet::DevError, + "%s %s does not exist; cannot set %s" % + [@parent.class.name, @parent.name, self.class.name] + end + + # this needs to be set either by the individual state + # or its parent class + cmd = self.modifycmd + + output = %x{#{cmd} 2>&1} + + unless $? == 0 + raise Puppet::Error, "Could not modify %s on %s %s: %s" % + [self.class.name, @parent.class.name, + @parent.name, output] + end + + return "#{@parent.class.name}_modified".intern + end + + private + def syncname + obj = @parent.getinfo + + cmd = nil + event = nil + if @should == :notfound + # we need to remove the object... + if obj.nil? + # the group already doesn't exist + return nil + end + + # again, needs to be set by the ind. state or its + # parent + cmd = self.deletecmd + type = "delete" + else + unless obj.nil? + raise Puppet::DevError, + "Got told to create a %s that already exists" % + @parent.class.name + end + + # blah blah, define elsewhere, blah blah + cmd = self.addcmd + type = "create" + end + + output = %x{#{cmd} 2>&1} + + unless $? == 0 + raise Puppet::Error, "Could not %s %s %s: %s" % + [type, @parent.class.name, @parent.name, output] + end + + return "#{@parent.class.name}_#{type}d".intern + end + + class POSIXGID + end + end + end + end +end diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb index 15c6a3d03..d42e7c078 100755 --- a/lib/puppet/type/user.rb +++ b/lib/puppet/type/user.rb @@ -6,112 +6,29 @@ require 'puppet/type/state' module Puppet class State - class UserState < Puppet::State - class << self - attr_accessor :flag - def infomethod - if defined? @infomethod and @infomethod - return @infomethod - else - return @name - end - end + module UserUID + def self.doc + "The user ID. Must be specified numerically. For new users + being created, if no user ID is specified then one will be + chosen automatically, which will likely result in the same user + having different IDs on different systems, which is not + recommended." end - def create - obj = @parent.getinfo - - cmd = nil - event = nil - if @should == :notfound - # we need to remove the object... - if obj.nil? - # the user already doesn't exist - return nil - end - - cmd = ["userdel", @parent.name] - type = "delete" - else - unless obj.nil? - raise Puppet::DevError, - "Got told to create a user that already exists" - end - # we're creating the user - - # i can just tell i'm going to regret this - # why doesn't POSIX include interfaces for adding users - # and groups? it's stupid - cmd = ["useradd"] - @parent.eachstate { |state| - # the value needs to be quoted, mostly because -c might - # have spaces in it - cmd << state.class.flag << "'%s'" % state.should - } - cmd << @parent.name - type = "create" - end - - output = %x{#{cmd.join(" ")} 2>&1} - - unless $? == 0 - raise Puppet::Error, "Could not %s group %s: %s" % - [type, @parent.name, output] - end - - return "user_#{type}d".intern + def self.name + :uid end + end - def retrieve - if info = @parent.getinfo(true) - @is = info.send(self.class.infomethod) - else - @is = :notfound - end + module UserGID + def self.doc + "The user's primary group. Can be specified numerically or + by name." end - def sync - obj = @parent.getinfo - - # if the user either does not or should not exist... - # yes, it's a badly named method - if obj.nil? or @should == :notfound - return self.create - end - - # there's a possibility that we created the user in this session - # so verify that we're actually out of sync - if self.insync? - return nil - end - cmd = [ - "usermod", self.class.flag, "'%s'" % @should, @parent.name - ].join(" ") - - output = %x{#{cmd} 2>&1} - - unless $? == 0 - raise Puppet::Error, "Could not modify %s on user %s: %s" % - [self.class.name, @parent.name, output] - end - - return :user_modified + def self.name + :gid end - end - - class UserUID < UserState - @doc = "The user ID. Must be specified numerically. For new users being - created, if no user ID is specified then one will be chosen - automatically, which will likely result in the same user having - different IDs on different systems, which is not recommended." - @name = :uid - @flag = "-u" - end - - class UserGID < UserState - @doc = "The user's primary group. Can be specified numerically or by name." - @name = :gid - @flag = "-g" def should=(gid) method = :getgrgid @@ -123,120 +40,125 @@ module Puppet end end - # FIXME this should really check to see if we already have a group - # ready to be managed; if so, then we should just mark it as a prereq + # FIXME this should really check to see if we already have a + # group ready to be managed; if so, then we should just mark it + # as a prereq begin ginfo = Etc.send(method, gid) rescue ArgumentError => detail - raise Puppet::Error, "Could not find group %s: %s" % [gid, detail] + raise Puppet::Error, "Could not find group %s: %s" % + [gid, detail] end @should = ginfo.gid end end - class UserComment < UserState - @doc = "A description of the user. Generally is a user's full name." - @name = :comment - @infomethod = :gecos - @flag = "-c" + module UserComment + def self.doc + "A description of the user. Generally is a user's full name." + end + + def self.name + :comment + end + + def self.posixmethod + :gecos + end end - class UserHome < UserState - @doc = "The home directory of the user. The directory must be created + module UserHome + def self.doc + "The home directory of the user. The directory must be created separately and is not currently checked for existence." - @name = :home - @infomethod = :dir - @flag = "-d" + end + + def self.name + :home + end + + def self.posixmethod + :dir + end end - class UserShell < UserState - @doc = "The user's login shell. The shell must exist and be + module UserShell + def self.doc + "The user's login shell. The shell must exist and be executable." - @name = :shell - @flag = "-s" + def self.name + :shell + end end - # these three states are all implemented differently on each platform, so i'm - # disabling them for now + # these three states are all implemented differently on each platform, + # so i'm disabling them for now # FIXME Puppet::State::UserLocked is currently non-functional - class UserLocked < UserState - @doc = "The expected return code. An error will be returned if the + module UserLocked + def self.doc + "The expected return code. An error will be returned if the executed command returns something else." - @name = :locked + end + + def self.name + :locked + end end # FIXME Puppet::State::UserExpire is currently non-functional - class UserExpire < UserState - @doc = "The expected return code. An error will be returned if the + module UserExpire + def self.doc + "The expected return code. An error will be returned if the executed command returns something else." - @name = :expire - @flag = "-e" + end + + def self.name; :expire; end end # FIXME Puppet::State::UserInactive is currently non-functional - class UserInactive < UserState - @doc = "The expected return code. An error will be returned if the + module UserInactive + def self.doc + "The expected return code. An error will be returned if the executed command returns something else." - @name = :inactive - @flag = "-f" + end + + def self.name; :inactive; end end end class Type class User < Type - @states = [ - Puppet::State::UserUID, - Puppet::State::UserGID, - Puppet::State::UserComment, - Puppet::State::UserHome, - Puppet::State::UserShell - ] - - @parameters = [ - :name - ] - + statemodule = nil case Facter["operatingsystem"].value when "Darwin": - @@extender = "NInfo" + statemodule = Puppet::NameService::NetInfo::NetInfoUser else - @@extender = "XAdd" + statemodule = Puppet::NameService::ObjectAdd::ObjectAddUser end - @name = :group - @namevar = :name + @states = statemodule.substates - # all of the states are very similar, but syncing is different - # for each _type_ of state - @states.each { |state| -# begin -# klass = eval("Puppet::State::UserState" + @@extender) -# if klass.test -# state.send(:include, klass) -# else -# Puppet.err "Cannot sync %s on %s" % -# [state.name, @name] -# end -# rescue NameError -# Puppet.notice "No %s extender for %s" % -# [@@extender, state.name] -# end - } + @parameters = [ + :name + ] @paramdoc[:name] = "User name. While limitations are determined for each operating system, it is generally a good idea to keep to the degenerate 8 characters, beginning with a letter." - @doc = "Manage users. Currently can create and modify users, but cannot - delete them. Theoretically all of the parameters are optional, - but if no parameters are specified the comment will be set to the - user name in order to make the internals work out correctly." + @doc = "Manage users. Currently can create and modify users, but + cannot delete them. Theoretically all of the parameters are + optional, but if no parameters are specified the comment will + be set to the user name in order to make the internals work out + correctly." @name = :user @namevar = :name + @netinfodir = "users" + def getinfo(refresh = false) if @userinfo.nil? or refresh == true begin |