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