Class: Puppet::Provider::AixObject

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

Overview

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

Author

Hector Rivas Gandara <[email protected]>

Constant Summary

Constants included from Util

Util::AbsolutePathPosix, Util::AbsolutePathWindows

Constants included from Util::Docs

Util::Docs::HEADER_LEVELS

Class Attribute Summary collapse

Attributes inherited from Puppet::Provider

#model, #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?, defaultfor, initvars, make_command_methods, mkmodelmethods, #name, optional_commands, specificity, supports_parameter?, #to_s

Methods included from Util::Logging

#clear_deprecation_warnings, #deprecation_warning, #send_log

Methods included from Util

absolute_path?, activerecord_version, benchmark, binread, chuser, classproxy, #execfail, #execpipe, execute, execute_posix, execute_windows, logmethods, memory, path_to_uri, proxy, replace_file, safe_posix_fork, symbolize, symbolizehash, symbolizehash!, synchronize_on, thinmark, #threadlock, uri_to_path, wait_for_output, which, withumask

Methods included from Util::POSIX

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

Methods included from Util::Docs

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

Methods included from Util::Warnings

clear_warnings, 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.



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

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

Class Attribute Details

.attribute_mappingObject

Returns the value of attribute attribute_mapping.



37
38
39
# File 'lib/vendor/puppet/provider/aixobject.rb', line 37

def attribute_mapping
  @attribute_mapping
end

Class Method Details

.attribute_mapping_fromObject

Mapping from AIX attribute to Puppet property.



55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/vendor/puppet/provider/aixobject.rb', line 55

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.



41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/vendor/puppet/provider/aixobject.rb', line 41

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.



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

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



343
344
345
346
347
348
349
# File 'lib/vendor/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



352
353
354
355
# File 'lib/vendor/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:



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

def addcmd(extra_attrs = [])
  raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: #{detail}"
end

#createObject

Create a new instance of the resource



310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/vendor/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}"
  end
end

#deleteObject

Delete this instance of the resource



325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/vendor/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}"
  end
end

#deletecmdObject

Raises:



26
27
28
# File 'lib/vendor/puppet/provider/aixobject.rb', line 26

def deletecmd
  raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: #{detail}"
end

#ensureObject

  • ensure

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

From ensurable: exists?, create, delete



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

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

#exists?Boolean

Check that the user exists

Returns:

  • (Boolean)


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

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

#flushObject

Clear out the cached values.



276
277
278
279
# File 'lib/vendor/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.



358
359
360
# File 'lib/vendor/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



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/vendor/puppet/provider/aixobject.rb', line 113

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
  elsif 
    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.



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/vendor/puppet/provider/aixobject.rb', line 226

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(execute(self.lscmd))
      # All attributtes without translation
      @objectosinfo = self.parse_command_output(execute(self.lscmd), 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.



244
245
246
247
248
249
# File 'lib/vendor/puppet/provider/aixobject.rb', line 244

def getosinfo(refresh = false)
  if @objectosinfo .nil? or refresh == true
    getinfo(refresh)
  end
  @objectosinfo
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.



143
144
145
146
147
148
149
150
# File 'lib/vendor/puppet/provider/aixobject.rb', line 143

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

#list_allObject

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



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

def list_all
  names = []
  begin
    output = execute(self.lsallcmd()).split('\n')
    (output.select{ |l| l != /^#/ }).each { |v|
      name = v.split(/[ :]/)
      names << name if not name.empty?
    }
  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

#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



89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/vendor/puppet/provider/aixobject.rb', line 89

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
  elsif 
    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:



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

def lscmd(value=@resource[:name])
  raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: #{detail}"
end

#modifycmd(attributes_hash) ⇒ Object

Raises:



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

def modifycmd(attributes_hash)
  raise Puppet::Error, "Method not defined #{@resource.class.name} #{@resource.name}: #{detail}"
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 an 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 ‘=’



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

def parse_attr_list(str, mapping=self.class.attribute_mapping_from)
  properties = {}
  attrs = []
  if !str 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 or key_str.empty?
        info "Empty key in string 'i'?"
        continue
      end
      key = key_str.to_sym
     
      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



193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/vendor/puppet/provider/aixobject.rb', line 193

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

  attrs.each { |val|
    key = key_list.shift.downcase.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.


213
214
215
216
217
218
219
220
221
# File 'lib/vendor/puppet/provider/aixobject.rb', line 213

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.



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/vendor/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}"
    end
  end
  
  # Refresh de info.  
  hash = 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]



72
73
74
75
76
77
78
79
80
81
82
# File 'lib/vendor/puppet/provider/aixobject.rb', line 72

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