class Puppet::Provider::NameService
This is the parent class of all NSS classes. They're very different in their backend, but they're pretty similar on the front-end. This class provides a way for them all to be as similar as possible.
Public Class Methods
# File lib/puppet/provider/nameservice.rb 8 def autogen_default(param) 9 defined?(@autogen_defaults) ? @autogen_defaults[param.intern] : nil 10 end
# File lib/puppet/provider/nameservice.rb 12 def autogen_defaults(hash) 13 @autogen_defaults ||= {} 14 hash.each do |param, value| 15 @autogen_defaults[param.intern] = value 16 end 17 end
Autogenerate either a uid or a gid. This is not very flexible: we can only generate one field type per class, and get kind of confused if asked for both.
# File lib/puppet/provider/nameservice.rb 123 def self.autogen_id(field, resource_type) 124 # Figure out what sort of value we want to generate. 125 case resource_type 126 when :user; database = :passwd; method = :uid 127 when :group; database = :group; method = :gid 128 else 129 #TRANSLATORS "autogen_id()" is a method name and should not be translated 130 raise Puppet::DevError, _("autogen_id() does not support auto generation of id for resource type %{resource_type}") % { resource_type: resource_type } 131 end 132 133 # Initialize from the data set, if needed. 134 unless @prevauto 135 # Sadly, Etc doesn't return an enumerator, it just invokes the block 136 # given, or returns the first record from the database. There is no 137 # other, more convenient enumerator for these, so we fake one with this 138 # loop. Thanks, Ruby, for your awesome abstractions. --daniel 2012-03-23 139 highest = [] 140 Puppet::Etc.send(database) {|entry| highest << entry.send(method) } 141 highest = highest.reject {|x| x > 65000 }.max 142 143 @prevauto = highest || 1000 144 end 145 146 # ...and finally increment and return the next value. 147 @prevauto += 1 148 end
Puppet::Provider::initvars
# File lib/puppet/provider/nameservice.rb 19 def initvars 20 @checks = {} 21 @options = {} 22 super 23 end
# File lib/puppet/provider/nameservice.rb 25 def instances 26 objects = [] 27 begin 28 method = Puppet::Etc.method(:"get#{section}ent") 29 while ent = method.call #rubocop:disable Lint/AssignmentInCondition 30 objects << new(:name => ent.name, :canonical_name => ent.canonical_name, :ensure => :present) 31 end 32 ensure 33 Puppet::Etc.send("end#{section}ent") 34 end 35 objects 36 end
Puppet::Provider::new
# File lib/puppet/provider/nameservice.rb 250 def initialize(resource) 251 super 252 @custom_environment = {} 253 @objectinfo = nil 254 if resource.is_a?(Hash) && !resource[:canonical_name].nil? 255 @canonical_name = resource[:canonical_name] 256 else 257 @canonical_name = resource[:name] 258 end 259 end
# File lib/puppet/provider/nameservice.rb 38 def option(name, option) 39 name = name.intern if name.is_a? String 40 (defined?(@options) and @options.include? name and @options[name].include? option) ? @options[name][option] : nil 41 end
# File lib/puppet/provider/nameservice.rb 43 def options(name, hash) 44 unless resource_type.valid_parameter?(name) 45 raise Puppet::DevError, _("%{name} is not a valid attribute for %{resource_type}") % { name: name, resource_type: resource_type.name } 46 end 47 @options ||= {} 48 @options[name] ||= {} 49 50 # Set options individually, so we can call the options method 51 # multiple times. 52 hash.each do |param, value| 53 @options[name][param] = value 54 end 55 end
# File lib/puppet/provider/nameservice.rb 57 def resource_type=(resource_type) 58 super 59 @resource_type.validproperties.each do |prop| 60 next if prop == :ensure 61 define_method(prop) { get(prop) || :absent} unless public_method_defined?(prop) 62 define_method(prop.to_s + "=") { |*vals| set(prop, *vals) } unless public_method_defined?(prop.to_s + "=") 63 end 64 end
This is annoying, but there really aren't that many options, and this is built into Ruby.
# File lib/puppet/provider/nameservice.rb 68 def section 69 unless resource_type 70 raise Puppet::DevError, 71 "Cannot determine Etc section without a resource type" 72 end 73 74 if @resource_type.name == :group 75 "gr" 76 else 77 "pw" 78 end 79 end
# File lib/puppet/provider/nameservice.rb 81 def validate(name, value) 82 name = name.intern if name.is_a? String 83 if @checks.include? name 84 block = @checks[name][:block] 85 raise ArgumentError, _("Invalid value %{value}: %{error}") % { value: value, error: @checks[name][:error] } unless block.call(value) 86 end 87 end
# File lib/puppet/provider/nameservice.rb 89 def verify(name, error, &block) 90 name = name.intern if name.is_a? String 91 @checks[name] = {:error => error, :block => block} 92 end
Private Class Methods
# File lib/puppet/provider/nameservice.rb 96 def op(property) 97 @ops[property.name] || ("-#{property.name}") 98 end
Public Instance Methods
Autogenerate a value. Mostly used for uid/gid, but also used heavily with DirectoryServices
# File lib/puppet/provider/nameservice.rb 103 def autogen(field) 104 field = field.intern 105 id_generators = {:user => :uid, :group => :gid} 106 if id_generators[@resource.class.name] == field 107 return self.class.autogen_id(field, @resource.class.name) 108 else 109 value = self.class.autogen_default(field) 110 if value 111 return value 112 elsif respond_to?("autogen_#{field}") 113 return send("autogen_#{field}") 114 else 115 return nil 116 end 117 end 118 end
From overriding Puppet::Property#insync?
Ruby Etc::getpwnam < 2.1.0 always returns a struct with binary encoded string values, and >= 2.1.0 will return binary encoded strings for values incompatible with current locale charset, or Encoding.default_external if compatible. Compare a “should” value with encoding of “current” value, to avoid unnecessary property syncs and comparison of strings with different encodings. (PUP-6777)
return basic string comparison after re-encoding (same as Puppet::Property#property_matches)
# File lib/puppet/provider/nameservice.rb 287 def comments_insync?(current, should) 288 # we're only doing comparison here so don't mutate the string 289 desired = should[0].to_s.dup 290 current == desired.force_encoding(current.encoding) 291 end
# File lib/puppet/provider/nameservice.rb 150 def create 151 if exists? 152 info _("already exists") 153 # The object already exists 154 return nil 155 end 156 157 begin 158 sensitive = has_sensitive_data? 159 execute(self.addcmd, {:failonfail => true, :combine => true, :custom_environment => @custom_environment, :sensitive => sensitive}) 160 if feature?(:manages_password_age) && (cmd = passcmd) 161 execute(cmd, {:failonfail => true, :combine => true, :custom_environment => @custom_environment, :sensitive => sensitive}) 162 end 163 rescue Puppet::ExecutionFailure => detail 164 raise Puppet::Error, _("Could not create %{resource} %{name}: %{detail}") % { resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace 165 end 166 end
# File lib/puppet/provider/nameservice.rb 168 def delete 169 unless exists? 170 info _("already absent") 171 # the object already doesn't exist 172 return nil 173 end 174 175 begin 176 execute(self.deletecmd, {:failonfail => true, :combine => true, :custom_environment => @custom_environment}) 177 rescue Puppet::ExecutionFailure => detail 178 raise Puppet::Error, _("Could not delete %{resource} %{name}: %{detail}") % { resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace 179 end 180 end
# File lib/puppet/provider/nameservice.rb 182 def ensure 183 if exists? 184 :present 185 else 186 :absent 187 end 188 end
Does our object exist?
# File lib/puppet/provider/nameservice.rb 191 def exists? 192 !!getinfo(true) 193 end
Retrieve a specific value by name.
# File lib/puppet/provider/nameservice.rb 196 def get(param) 197 (hash = getinfo(false)) ? unmunge(param, hash[param]) : nil 198 end
Retrieve what we can about our object
# File lib/puppet/provider/nameservice.rb 219 def getinfo(refresh) 220 if @objectinfo.nil? or refresh == true 221 @etcmethod ||= ("get" + self.class.section.to_s + "nam").intern 222 begin 223 @objectinfo = Puppet::Etc.send(@etcmethod, @canonical_name) 224 rescue ArgumentError 225 @objectinfo = nil 226 end 227 end 228 229 # Now convert our Etc struct into a hash. 230 @objectinfo ? info2hash(@objectinfo) : nil 231 end
The list of all groups the user is a member of. Different user mgmt systems will need to override this method.
# File lib/puppet/provider/nameservice.rb 235 def groups 236 Puppet::Util::POSIX.groups_of(@resource[:name]).join(',') 237 end
Derived classes can override to declare sensitive data so a flag can be passed to execute
# File lib/puppet/provider/nameservice.rb 274 def has_sensitive_data?(property = nil) 275 false 276 end
Convert the Etc struct into a hash.
# File lib/puppet/provider/nameservice.rb 240 def info2hash(info) 241 hash = {} 242 self.class.resource_type.validproperties.each do |param| 243 method = posixmethod(param) 244 hash[param] = info.send(posixmethod(param)) if info.respond_to? method 245 end 246 247 hash 248 end
# File lib/puppet/provider/nameservice.rb 200 def munge(name, value) 201 block = self.class.option(name, :munge) 202 if block and block.is_a? Proc 203 block.call(value) 204 else 205 value 206 end 207 end
# File lib/puppet/provider/nameservice.rb 261 def set(param, value) 262 self.class.validate(param, value) 263 cmd = modifycmd(param, munge(param, value)) 264 raise Puppet::DevError, _("Nameservice command must be an array") unless cmd.is_a?(Array) 265 sensitive = has_sensitive_data?(param) 266 begin 267 execute(cmd, {:failonfail => true, :combine => true, :custom_environment => @custom_environment, :sensitive => sensitive}) 268 rescue Puppet::ExecutionFailure => detail 269 raise Puppet::Error, _("Could not set %{param} on %{resource}[%{name}]: %{detail}") % { param: param, resource: @resource.class.name, name: @resource.name, detail: detail }, detail.backtrace 270 end 271 end
# File lib/puppet/provider/nameservice.rb 209 def unmunge(name, value) 210 block = self.class.option(name, :unmunge) 211 if block and block.is_a? Proc 212 block.call(value) 213 else 214 value 215 end 216 end