Class: Puppet::Provider::AixObject

Inherits:
Puppet::Provider show all
Defined in:
lib/puppet/provider/aixobject.rb

Overview

Common code for AIX providers. This class implements basic structure for AIX resources.

Author

Hector Rivas Gandara <[email protected]>

API:

  • public

Constant Summary

Constants inherited from Puppet::Provider

Confine

Constants included from Util::Logging

Util::Logging::FILE_AND_LINE, Util::Logging::FILE_NO_LINE, Util::Logging::MM, Util::Logging::NO_FILE_LINE

Constants included from Util

Util::AbsolutePathPosix, Util::AbsolutePathWindows, Util::DEFAULT_POSIX_MODE, Util::DEFAULT_WINDOWS_MODE

Constants included from Util::POSIX

Util::POSIX::LOCALE_ENV_VARS, Util::POSIX::USER_ENV_VARS

Constants included from Util::SymbolicFileMode

Util::SymbolicFileMode::SetGIDBit, Util::SymbolicFileMode::SetUIDBit, Util::SymbolicFileMode::StickyBit, Util::SymbolicFileMode::SymbolicMode, Util::SymbolicFileMode::SymbolicSpecialToBit

Constants included from Util::Docs

Util::Docs::HEADER_LEVELS

Class Attribute Summary collapse

Attributes inherited from Puppet::Provider

#resource

Attributes included from Util::Docs

#doc, #nodoc

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Puppet::Provider

#<=>, #clear, command, #command, commands, declared_feature?, default?, default_match, defaultfor, execfail, #execfail, execpipe, #execpipe, execute, #execute, fact_match, feature_match, has_command, initvars, #inspect, #name, optional_commands, post_resource_eval, prefetch, specificity, supports_parameter?, #to_s

Methods included from Util::Logging

#clear_deprecation_warnings, #debug, #deprecation_warning, #format_exception, #get_deprecation_offender, #log_and_raise, #log_deprecations_to_file, #log_exception, #puppet_deprecation_warning, #send_log, setup_facter_logging!, #warn_once

Methods included from Util

absolute_path?, benchmark, chuser, clear_environment, default_env, deterministic_rand, deterministic_rand_int, exit_on_fail, get_env, get_environment, logmethods, merge_environment, path_to_uri, pretty_backtrace, replace_file, safe_posix_fork, set_env, symbolizehash, thinmark, uri_to_path, which, withenv, withumask

Methods included from Util::POSIX

#get_posix_field, #gid, #idfield, #methodbyid, #methodbyname, #search_posix_field, #uid

Methods included from Util::SymbolicFileMode

#normalize_symbolic_mode, #symbolic_mode_to_int, #valid_symbolic_mode?

Methods included from Util::Docs

#desc, #dochook, #doctable, #markdown_definitionlist, #markdown_header, #nodoc?, #pad, scrub

Methods included from Util::Warnings

clear_warnings, debug_once, notice_once, warnonce

Methods included from Confiner

#confine, #confine_collection, #suitable?

Methods included from Util::Errors

#adderrorcontext, #devfail, #error_context, #exceptwrap, #fail

Constructor Details

#initialize(resource) ⇒ AixObject

Returns a new instance of AixObject.

API:

  • public



387
388
389
390
391
# File 'lib/puppet/provider/aixobject.rb', line 387

def initialize(resource)
  super
  @objectinfo = nil
  @objectosinfo = nil
end

Class Attribute Details

.attribute_mappingObject

API:

  • public



33
34
35
# File 'lib/puppet/provider/aixobject.rb', line 33

def attribute_mapping
  @attribute_mapping
end

Class Method Details

.attribute_mapping_fromObject

Mapping from AIX attribute to Puppet property.

API:

  • public



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/puppet/provider/aixobject.rb', line 51

def self.attribute_mapping_from
  if ! @attribute_mapping_from
    @attribute_mapping_from = {}
    attribute_mapping.each { |elem|
      attribute_mapping_from[elem[:aix_attr]] = {
        :key => elem[:puppet_prop],
        :method => elem[:from]
      }
    }
  end
  @attribute_mapping_from
end

.attribute_mapping_toObject

Mapping from Puppet property to AIX attribute.

API:

  • public



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/puppet/provider/aixobject.rb', line 37

def self.attribute_mapping_to
  if ! @attribute_mapping_to
    @attribute_mapping_to = {}
    attribute_mapping.each { |elem|
      attribute_mapping_to[elem[:puppet_prop]] = {
        :key => elem[:aix_attr],
        :method => elem[:to]
      }
    }
  end
  @attribute_mapping_to
end

.instancesObject

Return all existing instances The method for returning a list of provider instances. Note that it returns providers, preferably with values already filled in, not resources.

API:

  • public



289
290
291
292
293
294
295
# File 'lib/puppet/provider/aixobject.rb', line 289

def self.instances
  objects=[]
  list_all.each { |entry|
    objects << new(:name => entry, :ensure => :present)
  }
  objects
end

.list_allObject

List all elements of given type. It works for colon separated commands and list commands. It returns a list of names.

API:

  • public



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/puppet/provider/aixobject.rb', line 252

def self.list_all
  names = []
  begin
    output = execute([self.command(:list), 'ALL'])

    output = output.split("\n").select{ |line| line != /^#/ }

    output.each do |line|
      name = line.split(/[ :]/)[0]
      names << name if not name.empty?
    end
  rescue Puppet::ExecutionFailure => detail
    # Print error if needed
    Puppet.debug "aix.list_all(): Could not get all resources of type #{@resource.class.name}: #{detail}"
  end
  names
end

.mk_resource_methodsObject


Call this method when the object is initialized. It creates getter/setter methods for each property our resource type supports. If setter or getter already defined it will not be overwritten

API:

  • public



343
344
345
346
347
348
349
# File 'lib/puppet/provider/aixobject.rb', line 343

def self.mk_resource_methods
  [resource_type.validproperties, resource_type.parameters].flatten.each do |prop|
    next if prop == :ensure
    define_method(prop) { get(prop) || :absent} unless public_method_defined?(prop)
    define_method(prop.to_s + "=") { |*vals| set(prop, *vals) } unless public_method_defined?(prop.to_s + "=")
  end
end

.resource_type=(resource_type) ⇒ Object

Define the needed getters and setters as soon as we know the resource type

API:

  • public



352
353
354
355
# File 'lib/puppet/provider/aixobject.rb', line 352

def self.resource_type=(resource_type)
  super
  mk_resource_methods
end

Instance Method Details

#addcmd(_extra_attrs = []) ⇒ Object

Raises:

API:

  • public



14
15
16
# File 'lib/puppet/provider/aixobject.rb', line 14

def addcmd( _extra_attrs = [] )
  raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: Base AixObject provider doesn't implement addcmd"
end

#createObject

Create a new instance of the resource

API:

  • public



310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/puppet/provider/aixobject.rb', line 310

def create
  if exists?
    info "already exists"
    # The object already exists
    return nil
  end

  begin
    execute(self.addcmd)
  rescue Puppet::ExecutionFailure => detail
    raise Puppet::Error, "Could not create #{@resource.class.name} #{@resource.name}: #{detail}", detail.backtrace
  end
end

#deleteObject

Delete this instance of the resource

API:

  • public



325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/puppet/provider/aixobject.rb', line 325

def delete
  unless exists?
    info "already absent"
    # the object already doesn't exist
    return nil
  end

  begin
    execute(self.deletecmd)
  rescue Puppet::ExecutionFailure => detail
    raise Puppet::Error, "Could not delete #{@resource.class.name} #{@resource.name}: #{detail}", detail.backtrace
  end
end

#deletecmdObject

Raises:

API:

  • public



22
23
24
# File 'lib/puppet/provider/aixobject.rb', line 22

def deletecmd
  raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: Base AixObject provider doesn't implement deletecmd"
end

#ensureObject

  • ensure

    The basic state that the object should be in.  Valid values are
    `present`, `absent`, `role`.
    

From ensurable: exists?, create, delete

API:

  • public



301
302
303
304
305
306
307
# File 'lib/puppet/provider/aixobject.rb', line 301

def ensure
  if exists?
    :present
  else
    :absent
  end
end

#exists?Boolean

Check that the user exists

Returns:

API:

  • public



282
283
284
# File 'lib/puppet/provider/aixobject.rb', line 282

def exists?
  !!getinfo(true) # !! => converts to bool
end

#flushObject

Clear out the cached values.

API:

  • public



276
277
278
279
# File 'lib/puppet/provider/aixobject.rb', line 276

def flush
  @property_hash.clear if @property_hash
  @objectinfo.clear if @objectinfo
end

#get(param) ⇒ Object

Retrieve a specific value by name.

API:

  • public



358
359
360
# File 'lib/puppet/provider/aixobject.rb', line 358

def get(param)
  (hash = getinfo(false)) ? hash[param] : nil
end

#get_arguments(key, value, mapping, objectinfo) ⇒ Object

Gets the given command line argument for the given key and value, using the given mapping to translate key and value. All the objectinfo hash (@resource or @property_hash) is passed.

This operation works with each property one by one, and default behaviour is return the arguments as key=value pairs. Subclasses must reimplement this if more complex operations/arguments are needed

API:

  • public



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/puppet/provider/aixobject.rb', line 109

def get_arguments(key, value, mapping, objectinfo)
  if mapping.nil?
    new_key = key
    new_value = value
  elsif mapping[key].nil?
    # is not present in mapping, ignore it.
    new_key = nil
    new_value = nil
  elsif mapping[key][:method].nil?
    new_key = mapping[key][:key]
    new_value = value
  else
    new_key = mapping[key][:key]
    new_value = method(mapping[key][:method]).call(value)
  end

  # convert it to string
  new_value = Array(new_value).join(',')

  if new_key
    return [ "#{new_key}=#{new_value}" ]
  else
    return []
  end
end

#getinfo(refresh = false) ⇒ Object

Retrieve all the information of an existing resource. It will execute ‘lscmd’ command and parse the output, using the mapping ‘attribute_mapping_from’ to translate the keys and values.

API:

  • public



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/puppet/provider/aixobject.rb', line 223

def getinfo(refresh = false)
  if @objectinfo.nil? or refresh == true
    # Execute lsuser, split all attributes and add them to a dict.
    begin
      output = execute(self.lscmd)
      @objectinfo = self.parse_command_output(output)
      # All attributtes without translation
      @objectosinfo = self.parse_command_output(output, nil)
    rescue Puppet::ExecutionFailure => detail
      # Print error if needed. FIXME: Do not check the user here.
      Puppet.debug "aix.getinfo(): Could not find #{@resource.class.name} #{@resource.name}: #{detail}"
    end
  end
  @objectinfo
end

#getosinfo(refresh = false) ⇒ Object

Like getinfo, but it will not use the mapping to translate the keys and values. It might be usefult to retrieve some raw information.

API:

  • public



241
242
243
244
245
246
# File 'lib/puppet/provider/aixobject.rb', line 241

def getosinfo(refresh = false)
  if @objectosinfo.nil? or refresh == true
    getinfo(refresh)
  end
  @objectosinfo || Hash.new
end

#hash2args(hash, mapping = self.class.attribute_mapping_to) ⇒ Object

Convert the provider properties (hash) to AIX command arguments (list of strings) This function will translate each value/key and generate the argument using the get_arguments function.

API:

  • public



139
140
141
142
143
144
145
146
# File 'lib/puppet/provider/aixobject.rb', line 139

def hash2args(hash, mapping=self.class.attribute_mapping_to)
  return "" unless hash
  arg_list = []
  hash.each {|key, val|
    arg_list += self.get_arguments(key, val, mapping, hash)
  }
  arg_list
end

#load_attribute(key, value, mapping, objectinfo) ⇒ Object

Loads an AIX attribute (key=value) and stores it in the given hash with puppet semantics. It translates the pair using the given mapping.

This operation works with each property one by one, subclasses must reimplement this if more complex operations are needed

API:

  • public



85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/puppet/provider/aixobject.rb', line 85

def load_attribute(key, value, mapping, objectinfo)
  if mapping.nil?
    objectinfo[key] = value
  elsif mapping[key].nil?
    # is not present in mapping, ignore it.
    true
  elsif mapping[key][:method].nil?
    objectinfo[mapping[key][:key]] = value
  else
    objectinfo[mapping[key][:key]] = method(mapping[key][:method]).call(value)
  end

  return objectinfo
end

#lscmd(_value = @resource[:name]) ⇒ Object

The real provider must implement these functions.

Raises:

API:

  • public



10
11
12
# File 'lib/puppet/provider/aixobject.rb', line 10

def lscmd( _value = @resource[:name] )
  raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: Base AixObject provider doesn't implement lscmd"
end

#modifycmd(_attributes_hash = {}) ⇒ Object

Raises:

API:

  • public



18
19
20
# File 'lib/puppet/provider/aixobject.rb', line 18

def modifycmd( _attributes_hash = {} )
  raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: Base AixObject provider doesn't implement modifycmd"
end

#parse_attr_list(str, mapping = self.class.attribute_mapping_from) ⇒ Object

Parse AIX command attributes from the output of an AIX command, that which format is a list of space separated of key=value pairs: “uid=100 groups=a,b,c”. It returns a hash.

If a mapping is provided, the keys are translated as defined in the mapping hash. And only values included in mapping will be added

NOTE: it will ignore the items not including ‘=’

API:

  • public



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/puppet/provider/aixobject.rb', line 157

def parse_attr_list(str, mapping=self.class.attribute_mapping_from)
  properties = {}
  attrs = []
  if str.nil? or (attrs = str.split()).empty?
    return nil
  end

  attrs.each { |i|
    if i.include? "=" # Ignore if it does not include '='
      (key_str, val) = i.split('=')
      # Check the key
      if key_str.nil? or key_str.empty?
        info "Empty key in string 'i'?"
        continue
      end
      key_str.strip!
      key = key_str.to_sym
      val.strip! if val

      properties = self.load_attribute(key, val, mapping, properties)
    end
  }
  properties.empty? ? nil : properties
end

#parse_colon_list(str, key_list, mapping = self.class.attribute_mapping_from) ⇒ Object

Parse AIX command output in a colon separated list of attributes, This function is useful to parse the output of commands like lsfs -c:

#MountPoint:Device:Vfs:Nodename:Type:Size:Options:AutoMount:Acct
/:/dev/hd4:jfs2::bootfs:557056:rw:yes:no
/home:/dev/hd1:jfs2:::2129920:rw:yes:no
/usr:/dev/hd2:jfs2::bootfs:9797632:rw:yes:no

If a mapping is provided, the keys are translated as defined in the mapping hash. And only values included in mapping will be added

API:

  • public



191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/puppet/provider/aixobject.rb', line 191

def parse_colon_list(str, key_list, mapping=self.class.attribute_mapping_from)
  properties = {}
  attrs = []
  if str.nil? or (attrs = str.split(':')).empty?
    return nil
  end

  attrs.each { |val|
    key = key_list.shift.to_sym
    properties = self.load_attribute(key, val, mapping, properties)
  }
  properties.empty? ? nil : properties
end

#parse_command_output(output, mapping = self.class.attribute_mapping_from) ⇒ Object

Default parsing function for AIX commands. It will choose the method depending of the first line. For the colon separated list it will:

1. Get keys from first line.
2. Parse next line.

API:

  • public



210
211
212
213
214
215
216
217
218
# File 'lib/puppet/provider/aixobject.rb', line 210

def parse_command_output(output, mapping=self.class.attribute_mapping_from)
  lines = output.split("\n")
  # if it begins with #something:... is a colon separated list.
  if lines[0] =~ /^#.*:/
    self.parse_colon_list(lines[1], lines[0][1..-1].split(':'), mapping)
  else
    self.parse_attr_list(lines[0], mapping)
  end
end

#set(param, value) ⇒ Object

Set a property.

API:

  • public



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/puppet/provider/aixobject.rb', line 363

def set(param, value)
  @property_hash[param.intern] = value

  if getinfo().nil?
    # This is weird...
    raise Puppet::Error, "Trying to update parameter '#{param}' to '#{value}' for a resource that does not exists #{@resource.class.name} #{@resource.name}: #{detail}"
  end
  if value == getinfo()[param.to_sym]
    return
  end

  #self.class.validate(param, value)
  if cmd = modifycmd({param =>value})
    begin
      execute(cmd)
    rescue Puppet::ExecutionFailure  => detail
      raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}", detail.backtrace
    end
  end

  # Refresh de info.
  getinfo(true)
end

#translate_attr(key, value, mapping) ⇒ Object

This functions translates a key and value using the given mapping. Mapping can be nil (no translation) or a hash with this format => new_key, :method => translate_method It returns a list with the pair [key, value]

API:

  • public



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/puppet/provider/aixobject.rb', line 68

def translate_attr(key, value, mapping)
  return [key, value] unless mapping
  return nil unless mapping[key]

  if mapping[key][:method]
    new_value = method(mapping[key][:method]).call(value)
  else
    new_value = value
  end
  [mapping[key][:key], new_value]
end