Mastering Automation Addendum for CloudForms 4.6 a
  • Introduction
  • Preface
  • Updates
    • New Service Dialog Editor
    • New Custom Button Functionality
    • Ansible Tower Provider Changes
    • Notifications
    • Miscellaneous Updates
  • Embedded Ansible
    • Introduction to Embedded Ansible
    • Ansible Playbook Services
    • Playbook Service Provision & Retirement State Machines
    • Running an Ansible Playbook Service Non-Interactively
    • Running a Playbook Service from a Button
    • ManageIQ Ansible Modules
    • Ansible Playbook Methods
    • Calling a Playbook Method from the VM Provision State Machine
    • Using Cloud Credentials
    • Embedded Ansible Automation Objects
    • Troubleshooting
  • Generic Objects
    • Introduction to Generic Objects
    • Generic Object Example
    • Managing Associations Between Generic Objects from Ansible
  • Embedded Methods
    • Introduction to Embedded Methods
    • Embedded Method Example
  • Expression Methods
Powered by GitBook
On this page
  • Calling an Embedded Method
  • Writing an Embedded Method
  • Class With Initializer
  • Class Without Initializer
  • Mixin
  • Bare Methods
  • Multiple Embedded Methods
  • Logging
  • Summary
  • Further Reading
  1. Embedded Methods

Introduction to Embedded Methods

PreviousEmbedded MethodsNextEmbedded Method Example

Last updated 6 years ago

A useful new feature with CloudForms 4.6 (ManageIQ Gaprindashvili) is the ability to be able to create reusable libraries of Ruby automation methods, and include (embed) these into other Ruby methods to use. This promotes code re-use, makes methods smaller, avoids code duplication, and simplifies testing.

Calling an Embedded Method

An embedded method must be added to a calling method's definition in the WebUI before it can be used. In the Embedded Methods section of the Automate method editor screen, click the Add Method button and browse to the location of the method to embed (see screenshot ).

Once the embedded method has been added, any of its contained methods are available for the calling method to use.

Writing an Embedded Method

Class With Initializer

Encapsulating the embedded methods in a class with its own initializer allows the class to be unit tested by allowing the injection of a mock $evm into the @handle variable, as follows:

module Bit63
  module Automate
    module Library
      module Utils
        class Logger

          def initialize(handle = $evm)
            @handle = handle
          end

          def log(level, msg)
            @handle.log(level, "(location: #{caller_locations(1,1)[0].label}) #{msg}")
          end

          def dump_root
            log("info", "Listing $evm.root Attributes - Begin")
            @handle.root.attributes.sort.each { |k, v| log("info", "   Attribute - #{k}: #{v}") }
            log("info", "Listing $evm.root Attributes - End")
          end

        end
      end
    end
  end
end

Embedded methods written in this way can be invoked in a calling method as follows:

logger = Bit63::Automate::Library::Utils::Logger.new()
logger.log(:info, "Some text")
logger.dump_root

Class Without Initializer

Alternatively the embedded method can be written as a straightforward class method, as follows:

module Bit63
  module Automate
    module Library
      module Utils
        class Logger

          def self.log(level, msg)
            $evm.log(level, "(location: #{caller_locations(1,1)[0].label}) #{msg}")
          end

          def self.dump_root
            log("info", "Listing $evm.root Attributes - Begin")
            $evm.root.attributes.sort.each { |k, v| log("info", "   Attribute - #{k}: #{v}") }
            log("info", "Listing $evm.root Attributes - End")
          end

        end
      end
    end
  end
end

These embedded methods can be invoked in the following way in a calling method:

Bit63::Automate::Library::Utils::Logger.log(:info, "Some text")
Bit63::Automate::Library::Utils::Logger.dump_root

Note

For unit testing, class methods can also optionally take a handle, for example:

def self.log_and_exit(msg, exit_code, handle = $evm)

Mixin

The code could be written as a mixin without a class, as follows:

module Bit63
  module Automate
    module Library
      module Utils

        def log(level, msg)
          $evm.log(level, "(location: #{caller_locations(1,1)[0].label}) #{msg}")
        end

        def dump_root
          log("info", "Listing $evm.root Attributes - Begin")
          $evm.root.attributes.sort.each { |k, v| log("info", "   Attribute - #{k}: #{v}") }
          log("info", "Listing $evm.root Attributes - End")
        end

      end
    end
  end
end

The calling method must include the embedded module's module path, which imports the embedded methods into its own namespace. The embedded methods can then be invoked without specifying their module path, for example:

include Bit63::Automate::Library::Utils
log(:info, "Some text")
dump_root

Bare Methods

Alternatively the embedded methods can be written as simple bare method definitions, as follows:

def log(level, msg)
   $evm.log(level, "(location: #{caller_locations(1,1)[0].label}) #{msg}")
end

def self.dump_root
  log("info", "Listing $evm.root Attributes - Begin")
  $evm.root.attributes.sort.each { |k, v| log("info", "   Attribute - #{k}: #{v}") }
  log("info", "Listing $evm.root Attributes - End")
end

These embedded methods can be invoked in the following way in a calling method:

log(:info, "Some text")
dump_root

Multiple Embedded Methods

Note

Embedded methods cannot themselves use embedded methods as there is currently no support for nesting (as of CloudForms 4.6.4 and ManageIQ Gaprindashvili-4). This capability will however be added in a future release.

Logging

The use of an embedded method can be traced in automation.log. Before an Automate method is launched the Automation Engine will log Loading embedded method if any are defined, as follows:

...  INFO -- : Invoking [inline] method [/Bit63/Stuff/Methods/test30] with inputs [{}]
-->  INFO -- : Updated namespace [Library/Utils/logger  Bit63/Library]
-->  INFO -- : Loading embedded method Bit63/Library/Utils/logger
...  INFO -- : <AEMethod [/Bit63/Stuff/Methods/test]> Starting
...  INFO -- : <AEMethod test> (location: <main>) Some text
...  INFO -- : <AEMethod test> (location: dump_root) Listing $evm.root Attributes - Begin
...  INFO -- : <AEMethod test> (location: block in dump_root)    Attribute - ae_provider_category: unknown

If an embedded method throws an error, the fully qualified method name (Domain/Namespace/Class/Method) and the line number in the method that threw the exception is logged for ease of debugging.

Summary

The chapter has introduced embedded methods, and shown how useful they can be to promote code re-use. They can be written in several different styles to suit the coding and unit testing standards in use in an organisation.

The next chapter shows an example of an embedded method that defines two small reusable methods that each use the RHV SDK to connect to a Red Hat Virtualization Manager.

Further Reading

Embedded methods (often called library methods) can be written in several different ways, depending on their intended use. Some use the nested module structure described in , and as classes containing class methods, although this is not mandatory. Each style of writing has its advantages. The most common styles are as follows.

More than one embedded method can be added to an Automate method definition, allowing various library methods to be written in separate modules. The Automation Engine combines all of the embedded methods into a single preamble injected into to the Automate method at run-time. Code will execute in the order that it was listed in the embedded methods list (see screenshot ).

Automate Embedded Methods
Multiple Embedded Methods
Adding an Embedded Method
Defining Automate Methods as Classes
Adding an Embedded Method
Multiple Embedded Methods