Investigative Debugging

As seen in Working with Automation Objects, there is a lot of useful information in the various Service Models such as attributes, virtual columns, and associations, that we can use in our own Automation scripts. The challenge is sometimes knowing how and where to find it. Fortunately there are several ways of dumping or searching for the objects that we work with in the Automation Engine.

InspectMe

InspectMe is an Instance/Method combination supplied out-of-the-box that we can call to dump some attributes of $evm.root and its associated objects. As an example we can call InspectMe from a button on a VM and Instance object as we did when running our AddCustomAttribute Instance in A More Advanced Example. As both the Instance and Method are in the ManageIQ/System/Request namespace, we can call InspectMe directly rather than calling Call_Instance as an intermediary.

We can view the results of the InspectMe dump in automation.log:

[root@cloudforms ~]# vmdb
[root@cloudforms vmdb]# grep inspectme log/automation.log | awk 'FS="INFO -- :" {print $2}'
 <AEMethod inspectme> Root:<$evm.root> Attributes - Begin
 <AEMethod inspectme>   Attribute - ae_provider_category: infrastructure
 <AEMethod inspectme>   Attribute - miq_server: #<MiqAeMethodService::MiqAeServiceMiqServer:0x0000000b85ac90>
 <AEMethod inspectme>   Attribute - miq_server_id: 1000000000001
 <AEMethod inspectme>   Attribute - object_name: Request
 <AEMethod inspectme>   Attribute - request: InspectMe
 <AEMethod inspectme>   Attribute - user: #<MiqAeMethodService::MiqAeServiceUser:0x0000000b86b540>
 <AEMethod inspectme>   Attribute - user_id: 1000000000001
 <AEMethod inspectme>   Attribute - vm: rhel7srv001
 <AEMethod inspectme>   Attribute - vm_id: 1000000000025
 <AEMethod inspectme>   Attribute - vmdb_object_type: vm
 <AEMethod inspectme> Root:<$evm.root> Attributes - End
 <AEMethod inspectme>
 <AEMethod inspectme> key:<miq_server>  object:<#<MiqAeMethodService::MiqAeServiceMiqServer:0x0000000b85ac90>>
 <AEMethod inspectme>   Begin Attributes [object.attributes]
 <AEMethod inspectme>     build = "20150108100920_387a856"
 ...

Kevin Morey has written a greatly enhanced version of InspectMe, available from here.

object_walker

object_walker is a slightly more dynamic tool that walks and dumps the objects, attributes and virtual columns of $evm.root and its immediate objects, but also recursively traverses associations to walk and dump any objects that it finds. It prints the output in a Ruby-like syntax that can be copied and pasted directly into an Automation script to access or walk the same path.

The script is available here, along with instructions for use.

Black or Whitelisting Associations

One of the features of object_walker is the ability to be able to selectively choose which associations to "walk" to limit the output. This is selected by setting a @walk_association_policy to :whitelist or :blacklist, and then defining a @walk_association_whitelist or @walk_association_blacklist to list the associations to be walked (whitelist), or not walked (blacklist).

In practice a @walk_association_policy of :blacklist produces so much output that it's rarely used, and so a :whitelist is more often defined, e.g.

@walk_association_whitelist = { "MiqAeServiceVmRedhat"    => ["hardware", "host", "storage"],
                                "MiqAeServiceVmVmware"    => ["hardware", "host", "storage"],
                                "MiqAeServiceHardware"    => ["nics", "ports", "storage_adapters" ],
                                "MiqAeServiceGuestDevice" => ["hardware", "lan", "network"] }

object_walker_reader

There is a companion object_walker_reader script that can be copied to the CloudForms appliance to extract the object_walker dumps from automation.log, list the dumps, and even diff two dumps - useful when running object_walker before and after a built-in method (for example in a State Machine) to see what the method has changed.

Object Walker 1.6 Starting
     --- $evm.current_* details ---
     $evm.current_namespace = bit63   (type: String)
     $evm.current_class = methods   (type: String)
     $evm.current_instance = objectwalker   (type: String)
     $evm.current_message = create   (type: String)
     $evm.current_object = /bit63/methods/objectwalker   (type: DRb::DRbObject, URI: druby://127.0.0.1:56498)
     $evm.current_object.current_field_name = Execute   (type: String)
     $evm.current_object.current_field_type = method   (type: String)
     $evm.current_method = object_walker   (type: String)
     --- object hierarchy ---
     $evm.root = /ManageIQ/SYSTEM/PROCESS/Request
       $evm.parent = /ManageIQ/System/Request/call_instance
         $evm.object = /bit63/methods/objectwalker
     --- walking $evm.root ---
     $evm.root = /ManageIQ/SYSTEM/PROCESS/Request   (type: DRb::DRbObject, URI: druby://127.0.0.1:56498)
     |    --- attributes follow ---
     |    $evm.root['ae_provider_category'] = infrastructure   (type: String)
     |    $evm.root.class = DRb::DRbObject   (type: Class)
     |    $evm.root['dialog_walk_association_whitelist'] =    (type: String)
     |    $evm.root['instance'] = objectwalker   (type: String)
     |    $evm.root['miq_server'] => #<MiqAeMethodService::MiqAeServiceMiqServer:0x0000000d2b9370>   (type: DRb::DRbObject, URI: druby://127.0.0.1:56498)
     |    |    --- attributes follow ---
     |    |    $evm.root['miq_server'].build = 20150820153254_83e434d   (type: String)
     |    |    $evm.root['miq_server'].capabilities = {:vixDisk=>true, :concurrent_miqproxies=>2}   (type: Hash)
     |    |    $evm.root['miq_server'].cpu_time = 9884.0   (type: Float)
...skipping...
     |    |    $evm.root['miq_server'].version = 5.4.2.0   (type: String)
     |    |    $evm.root['miq_server'].vm_id = nil
     |    |    $evm.root['miq_server'].zone_id = 1000000000001   (type: Fixnum)
     |    |    --- end of attributes ---
     |    |    --- virtual columns follow ---
     |    |    $evm.root['miq_server'].region_description = Region 1   (type: String)
     |    |    $evm.root['miq_server'].region_number = 1   (type: Fixnum)
     |    |    $evm.root['miq_server'].zone_description = Default Zone   (type: String)
     |    |    --- end of virtual columns ---
     |    |    --- no associations ---
     |    |    --- methods follow ---
     |    |    $evm.root['miq_server'].region_name
     |    |    $evm.root['miq_server'].zone
     |    |    --- end of methods ---
...skipping...
     |    |    |    hardware.ipaddresses = ["192.168.12.171"]   (type: Array)
     |    |    |    hardware.mac_addresses = ["00:50:56:b8:00:02"]   (type: Array)
     |    |    |    hardware.region_description = Region 1   (type: String)
     |    |    |    hardware.region_number = 1   (type: Fixnum)
     |    |    |    --- end of virtual columns ---
     |    |    |    --- associations follow ---
     |    |    |    hardware.guest_devices (type: Association)
     |    |    |    hardware.guest_devices.each do |guest_device|
     |    |    |    |    (object type: MiqAeServiceGuestDevice, object ID: 1000000000016)
     |    |    |    |    --- attributes follow ---
     |    |    |    |    guest_device.address = 00:50:56:b8:00:02   (type: String)
     |    |    |    |    guest_device.auto_detect = nil
     |    |    |    |    guest_device.chap_auth_enabled = nil
     |    |    |    |    guest_device.controller_type = ethernet   (type: String)
     |    |    |    |    guest_device.device_name = Network adapter 1   (type: String)
     |    |    |    |    guest_device.device_type = ethernet   (type: String)

Rails console

We can connect to the Rails console to have a look around. On the CloudForms appliance itself:

[root@cloudforms ~]# vmdb   # alias vmdb='cd /var/www/miq/vmdb/' is defined on the appliance
[root@cloudforms vmdb]# source /etc/default/evm
[root@cloudforms vmdb]# bin/rails c
Loading production environment (Rails 3.2.17)
irb(main):001:0>


Once in the Rails console there are a number of things that we can do, such as use Rails object syntax to look at all Host Active Records:

irb(main):002:0> Host.all
   (3.6ms)  SELECT version()
  Host Load (0.7ms)  SELECT "hosts".* FROM "hosts"
  Host Inst (85.2ms - 2rows)
=> [#<HostRedhat id: 1000000000002, name: "rhelh02.bit63.net", hostname: "192.168.12.22", ipaddress: "192.168.12.22", ...

irb(main):003:0>


We can even generate our own $evm variable that matches the Automation Engine default:

$evm = MiqAeMethodService::MiqAeService.new(MiqAeEngine::MiqAeWorkspaceRuntime.new)

i.e.

irb(main):001:0> $evm = MiqAeMethodService::MiqAeService.new(MiqAeEngine::MiqAeWorkspaceRuntime.new)
   (41.6ms)  SELECT version()
  SQL (1.1ms)  SELECT "miq_ae_namespaces"."name" FROM "miq_ae_namespaces" WHERE "miq_ae_namespaces"."parent_id" IS NULL AND "miq_ae_namespaces"."enabled" = 't' AND ("miq_ae_namespaces"."name" != '$') ORDER BY priority DESC
=> #<MiqAeMethodService::MiqAeService:0x00000004a1f5c8 @drb_server_references=[], @inputs={}, @workspace=#<MiqAeEngine::MiqAeWorkspaceRuntime:0x000000048b66c8 @readonly=false, @graph=#<MiqAeEngine::MiqAeDigraph:0x000000048b6650 @v={}, @from={}, @lastid=-1>, @current=[], @num_drb_methods=0, @datastore_cache={}, @class_methods={}, @dom_search=#<MiqAeEngine::MiqAeDomainSearch:0x000000048b6538 @sorted_domains=["Tutorial", "Bit63", "RedHat", "ManageIQ"], @fqns_id_cache={}, @fqns_id_class_cache={}, @partial_ns=[]>, @persist_state_hash={}>, @preamble_lines=0, @body=[], @persist_state_hash={}>


George Goh has created a really useful .irbrc file that defines a $evm for us as soon as we go into the Rails Console:

# limit output size in IRB console.
class IRB::Context
   attr_accessor :max_output_size

   alias initialize_before_max_output_size initialize
   def initialize(*args)
     initialize_before_max_output_size(*args)
     @max_output_size = IRB.conf[:MAX_OUTPUT_SIZE] || 300
   end
end

class IRB::Irb
   def output_value
     text =
       if @context.inspect?
         sprintf @context.return_format, @context.last_value.inspect
       else
         sprintf @context.return_format, @context.last_value
       end
     max = @context.max_output_size
     if text.size < max
       puts text
     else
       puts text[0..max-1] + "..." + text[-2..-1]
     end
   end
end

# Retrieve a new EVM object and set it to the $evm attribute.
def get_evm
    MiqAeMethodService::MiqAeService.new(MiqAeEngine::MiqAeWorkspaceRuntime.new)
end

$evm = get_evm

More details are available here

Rails db

It is occasionaly useful to be able to examine some of the database tables (such as to look for column headers that we can find_by_* on). We can connect to Rails db, which puts us directly into a psql session:

[root@cloudforms ~]# vmdb
[root@cloudforms vmdb]# source /etc/default/evm
[root@cloudforms vmdb]# bin/rails db
psql (9.2.8)
Type "help" for help.

vmdb_production=#
vmdb_production=# \d guest_devices
                                      Table "public.guest_devices"
      Column       |          Type          |                         Modifiers
-------------------+------------------------+------------------------------------------------------------
 id                | bigint                 | not null default nextval('guest_devices_id_seq'::regclass)
 device_name       | character varying(255) |
 device_type       | character varying(255) |
 location          | character varying(255) |
 filename          | character varying(255) |
 hardware_id       | bigint                 |
 mode              | character varying(255) |
 controller_type   | character varying(255) |
 size              | bigint                 |
 free_space        | bigint                 |
 size_on_disk      | bigint                 |
 address           | character varying(255) |
 switch_id         | bigint                 |
 lan_id            | bigint                 |
...

results matching ""

    No results matching ""