Developers Guide to OMF

Prerequisites

Set up your environment

Please please make sure you followed the INSTALLATION GUIDE and get thing installed and configured properly.

If somehow you experienced problems regarding running examples listed in this guide, please first check that you indeed followed the INSTALLATION GUIDE and have things installed and configured properly.

Communication layer test and debug tools

To quickly send a message or monitor incoming messages on a topic, we provide a few utilities that come with the omf_common gem.

Monitor a topic:

$ omf_monitor_topic -h
Usage: omf_monitor_topic [options] topic1 topic2 ...

Monitor a set of resources (topics) and print all observed messages.

If the 'follow-children' flag is set, automatically add all resources
created by the monitored resources to the monitor set. Please note
that there will be a delay until the new monitors are in place which
can result in missed messages.

-c, --comms-url URL              URL of communication server (e.g. xmpp://my.server.com) []
-f, --[no-]follow-children       Follow all newly created resources [true]
-d, --debug                      Set log level to DEBUG
-h, --help                       Show this message

Send a CREATE message:

$ omf_send_create -h
Usage: omf_send_create [options] property1:value1 property2:value2 ...

Send a create message to a specific resource (topic) and print out any replies.

Any additional command line arguments are interpreted as property:value and are
sent with the create message.

-r, --resource-url URL           URL of resource (e.g. xmpp://my.server.com/topic1)
-t, --type TYPE                  Type of resource to create (e.g. node)
-y, --yaml YAML_FILE             Read type and property from YAML file
-d, --debug                      Set log level to DEBUG
-h, --help                       Show this message

Send a REQUEST message:

$ omf_send_request -h
Usage: omf_send_request [options] prop1 prop2 ...

Send a request to a specific resource (topic) and print out any replies.

Any additional command line arguments are interpreted as limiting the request
to those, otherwise all properties are requested.

    -r, --resource-url URL           URL of resource (e.g. xmpp://my.server.com/topic1)
    -d, --debug                      Set log level to DEBUG
    -h, --help                       Show this message

Background

FRCP protocol

If you are more towards implementing FRCP protocol without using Ruby nor our library, you could skip this guide and go to check out FRCP protocol documents directly.

Resource controller system

One of the biggest changes we are trying to make in version 6 resource controller system is to focus on the core features, and instead of trying to implement all the functionalities and hardware support, we want to provide an abstract entity acts as the proxy, processing the resource related messages based on the new FRCP protocol, and decides what type of the actions to perform according to the operation defined by the message and constrained by the proxy's capabilities which could be easily defined and extended by the resource providers.

In our design, users interact with the resource proxies purely via pubsub messages, they publish certain operation (create, request, configure, and release) messages to the pubsub topics, and subscribe to the PubSub topics for inform messages published by the resource proxies based on the outcome of the these requested operations. The resource proxy instances are actually doing the same, but the opposite, they are subscribing to the PubSub topics, react when new operation messages appeared by calling the internal methods corresponding to the content of the operation messages.

Interaction with proxy

We created a little tutorial will give a brief example demonstrating the work flow and how to implement these resource proxy definitions.

Tutorial

Suppose we are doing some kind of engine testing in a racing team garage, and using OMF to build a garage controller to handle interaction messages issued by the staff.

This tutorial will utilise DSL methods provided by our RC library, and the full API documentation can be accessed here:

Resource proxy DSL API doucumentation

This will be a good reference you feel puzzled about certain keywords, methods in the tutorial code we provide here.

The Ruby source code provided in this tutorial can be copied and pasted to your favourite editor, and execute as standard Ruby scripts.

Step 1: Garage and engine proxy definition

We will build a garage resource controller acts as the proxy to the garage and engines, while the staff use scripts to interact and guide garage controller to perform certain operations.

A resource proxy definition module (Mixin) represents the functionalities the resource could provide, including internal property state, what properties can be configured or requested, what operations to perform based on stage of the work flow. The features defined in the proxy definition module will be available to the engine proxy instance by the time it is created by the resource factory.

We start by defining a garage proxy and a engine proxy, where garage can create engines (parent child relationship).

Garage controller skeletion

# Need omf_rc gem to be required
#
require 'omf_rc'

# By using default namespace OmfRc::ResourceProxy, the module defined could be loaded automatically.
#
module OmfRc::ResourceProxy::Garage
  # Include DSL module, which provides all DSL helper methods
  #
  include OmfRc::ResourceProxyDSL

  # DSL method register_proxy will register this module definition,
  # where :garage become the :type of the proxy.
  #
  register_proxy :garage
end

module OmfRc::ResourceProxy::Engine
  include OmfRc::ResourceProxyDSL

  # You can specify what kind of proxy can create it, this case, :garage
  #
  register_proxy :engine, :create_by => :garage

  # DSL method property will define proxy's internal properties,
  # and you can provide initial default value.
  #
  property :manufacturer, :default => "Cosworth"
  property :max_rpm, :default => 12500
  property :rpm, :default => 1000
end

# This init method will set up your run time environment,
# communication, eventloop, logging etc. We will explain that later.
#
OmfCommon.init(:development, communication: { url: 'xmpp://localhost' }) do
  OmfCommon.comm.on_connected do |comm|
    info "Garage controoler >> Connected to XMPP server"
    garage = OmfRc::ResourceFactory.create(:garage, uid: 'garage')
    comm.on_interrupted { garage.disconnect }
  end
end

Now save the script to a file, then run the script using:

ruby <filename>

You can verify it is running by compare its output to the following

Garage controller console output

18:17:46  INFO XMPP::Communicator: Connecting to 'localhost' ...
18:17:47  INFO Object: Garage controller >> Connected to XMPP server
18:17:47 DEBUG XMPP::Topic: New topic: garage
18:17:47 DEBUG XMPP::Communicator: _subscribe >> garage SUCCEED
18:17:47 DEBUG XMPP::Communicator: publish >> garage SUCCEED

Interact with garage controller

Step 2: Request information of the garage

Now since the garage controller is running, we can query its information by sending FRCP request message. We will omf_common library's communicator to achieve this.

Request information of the garage

Test script, querying garage

# Use omf_common communicator directly
#
require 'omf_common'

# As seen previously, this init will set up various run time options for you.
#
# First line simply indicates:
# * Use :development as default environment,
#   this will use Eventmachine by default, set logging level to :debug
# * Use XMPP as default communication layer and XMPP server to connect to is localhost
# * By default username:password will be auto generated
#
# OmfCommon.comm returns a communicator instance,
# and this will be your entry point to interact with XMPP server.
#
# OmfCommon.eventloop returns Eventmachine runtime instance since it is default.
#
OmfCommon.init(:development, communication: { url: 'xmpp://localhost' }) do
  # Event :on_connected will be triggered when connected to XMPP server
  #
  OmfCommon.comm.on_connected do |comm|
    info "Engine test script >> Connected to XMPP"

    # Subscribe to a XMPP topic represents :garage, the name was set in the controller code if you wonder.
    # Once triggered, it will yield a Topic object.
    #
    comm.subscribe('garage') do |garage|
      unless garage.error?
        # Request two properties from garage, :uid and :type
        #
        # This is asynchronous, the reply_msg will only get processed when garage received the request
        # and we actually received the inform message it issued.
        #
        # Once we got the reply, simply iterate two properties and print them
        #
        garage.request([:uid, :type]) do |reply_msg|
          reply_msg.each_property do |k, v|
            info "#{k} >> #{v}"
          end
        end
      else
        error garage.inspect
      end
    end

    # Eventloop allows to control the flow, in this case, we disconnect after 5 seconds.
    #
    OmfCommon.eventloop.after(5) { comm.disconnect }
    # If you hit ctrl-c, we will disconnect too.
    #
    comm.on_interrupted { comm.disconnect }
  end
end

While engine is running, execute this script, you should be able to see garage's properties in the output.

Console output

13:08:25  INFO XMPP::Communicator: Connecting to 'localhost' ...
13:08:25  INFO Object: Engine test script >> Connected to XMPP
13:08:25 DEBUG XMPP::Topic: New topic: garage
13:08:25 DEBUG XMPP::Communicator: _subscribe >> garage SUCCEED
13:08:25 DEBUG XMPP::Topic: (garage) create_message_and_publish 'request': [:uid, :type]
13:08:25 DEBUG XMPP::Communicator: publish >> garage SUCCEED
13:08:25  INFO Object: uid >> garage
13:08:25  INFO Object: type >> garage
13:08:25  INFO Object: hrn >>
13:08:30  INFO XMPP::Communicator: Disconnecting ...

Step 3: Create & release engine

Now we will let garage to create an engine proxy instance, and once it is created, we will then release it after certain period. This will demonstrate the resource life-cycle described in the FRCP protocol.

Create & release engine

Create engine and then release it

require 'omf_common'

# We define a create_engine method here to contain all the logic around engine creation
#
def create_engine(garage)
  # We create an engine instance with a human readable name 'my_engine'
  #
  garage.create(:engine, hrn: 'my_engine') do |reply_msg|
    # This reply_msg will be the inform message issued by garage controller
    #
    if reply_msg.success?
      # Since we need to interact with engine's PubSub topic,
      # we call #resource method to construct a topic from the FRCP message content.
      #
      engine = reply_msg.resource

      # Because of the asynchronous nature, we need to use this on_subscribed callback
      # to make sure the operation in the block executed only when subscribed to the newly created engine's topic
      engine.on_subscribed do
        info ">>> Connected to newly created engine #{reply_msg[:hrn]}(id: #{reply_msg[:res_id]})"
      end

      # Then later on, we will ask garage again to release this engine.
      #
      OmfCommon.eventloop.after(3) do
        release_engine(garage, engine)
      end
    else
      error ">>> Resource creation failed - #{reply_msg[:reason]}"
    end
  end
end

def release_engine(garage, engine)
  info ">>> Release engine"
  # Only parent (garage) can release its child (engine)
  #
  garage.release(engine) do |reply_msg|
    info "Engine #{reply_msg[:res_id]} released"
    OmfCommon.comm.disconnect
  end
end

OmfCommon.init(:development, communication: { url: 'xmpp://localhost' }) do
  OmfCommon.comm.on_connected do |comm|
    info "Engine test script >> Connected to XMPP"

    comm.subscribe('garage') do |garage|
      unless garage.error?
        # Now calling create_engine method we defined, with newly created garage topic object
        #
        create_engine(garage)
      else
        error garage.inspect
      end
    end

    OmfCommon.eventloop.after(10) { comm.disconnect }
    comm.on_interrupted { comm.disconnect }
  end
end

While garage controller is running, execute this script and check the output

Console output

13:20:50  INFO XMPP::Communicator: Connecting to 'localhost' ...
13:20:50  INFO Object: Engine test script >> Connected to XMPP
13:20:50 DEBUG XMPP::Topic: New topic: garage
13:20:50 DEBUG XMPP::Communicator: _subscribe >> garage SUCCEED
13:20:50 DEBUG XMPP::Topic: Create resource of type 'engine'
13:20:50 DEBUG XMPP::Topic: (garage) create_message_and_publish 'create': {:hrn=>"my_engine", :type=>:engine}
13:20:50 DEBUG XMPP::Communicator: publish >> garage SUCCEED
13:20:50 DEBUG XMPP::Topic: New topic: xmpp://[email protected]
13:20:50 DEBUG XMPP::Communicator: _subscribe >> 1e1c5fd6-6d7a-4375-a48c-9dddbdf05394 SUCCEED
13:20:50  INFO Object: >>> Connected to newly created engine my_engine(id: xmpp://[email protected])
13:20:53  INFO Object: >>> Release engine
13:20:53 DEBUG XMPP::Communicator: publish >> garage SUCCEED
13:20:59  INFO Object: Engine xmpp://[email protected] released
13:20:59  INFO XMPP::Communicator: Disconnecting ...

Step 4: Add more features to garage engine proxies

Now we are going to add a feature of controlling throttle to the engine proxy, so that we can apply different level of throttle and monitor engine's RPM value.

Configure engine property

To allow this, we need to update our garage controller to allow configuring throttle, and provide some simple RPM calculation.

Modified garage proxy

require 'omf_rc'

module OmfRc::ResourceProxy::Garage
  include OmfRc::ResourceProxyDSL

  register_proxy :garage
end

module OmfRc::ResourceProxy::Engine
  include OmfRc::ResourceProxyDSL

  register_proxy :engine, :create_by => :garage

  property :manufacturer, :default => "Cosworth"
  property :max_rpm, :default => 12500
  # Add additional property to store rpm and throttle
  #
  property :rpm, :default => 1000
  property :throttle, :default => 0

  hook :before_ready do |engine|
    # Constantly calculate RPM value, rules are:
    #
    # * Applying 100% throttle will increase RPM by 5000 per second
    # * Engine will reduce RPM by 500 per second when no throttle applied
    #
    OmfCommon.eventloop.every(2) do
      engine.property.rpm += (engine.property.throttle * 5000 - 500).to_i
      engine.property.rpm = 1000 if engine.property.rpm < 1000
    end
  end

  # Then we simply register a configure property handler for throttle,
  # We expect a percentage value received and convert into decimal value
  #
  configure :throttle do |engine, value|
    engine.property.throttle = value.to_f / 100.0
  end
end

OmfCommon.init(:development, communication: { url: 'xmpp://localhost' }) do
  OmfCommon.comm.on_connected do |comm|
    info "Garage controoler >> Connected to XMPP server"
    garage = OmfRc::ResourceFactory.create(:garage, uid: 'garage')
    comm.on_interrupted { garage.disconnect }
  end
end

We then modify the engine test script to configure throttle, and request RPM values.

Engine test

require 'omf_common'

def create_engine(garage)
  garage.create(:engine, hrn: 'my_engine') do |reply_msg|
    if reply_msg.success?
      engine = reply_msg.resource

      engine.on_subscribed do
        info ">>> Connected to newly created engine #{reply_msg[:hrn]}(id: #{reply_msg[:res_id]})"
        on_engine_created(engine)
      end

      OmfCommon.eventloop.after(10) do
        release_engine(garage, engine)
      end
    else
      error ">>> Resource creation failed - #{reply_msg[:reason]}"
    end
  end
end

def on_engine_created(engine)
  info "> Now we will apply 50% throttle to the engine"
  engine.configure(throttle: 50)

  # Every 2 seconds, we send a request to engine, request its RPM value
  #
  OmfCommon.eventloop.every(2) do
    engine.request([:rpm]) do |reply_msg|
      info "RPM >> #{reply_msg[:rpm]}"
    end
  end

  # Some time later, we configure the throttle back to 0
  #
  OmfCommon.eventloop.after(5) do
    info "> We want to reduce the throttle to 0"
    engine.configure(throttle: 0)
  end
end

def release_engine(garage, engine)
  info ">>> Release engine"
  garage.release(engine) do |reply_msg|
    info "Engine #{reply_msg[:res_id]} released"
    OmfCommon.comm.disconnect
  end
end

OmfCommon.init(:development, communication: { url: 'xmpp://localhost' }) do
  OmfCommon.comm.on_connected do |comm|
    info "Engine test script >> Connected to XMPP"

    comm.subscribe('garage') do |garage|
      unless garage.error?
        create_engine(garage)
      else
        error garage.inspect
      end
    end

    OmfCommon.eventloop.after(20) { comm.disconnect }
    comm.on_interrupted { comm.disconnect }
  end
end

Now in the output you could notice that RPM value increased and then decreased.

Engine test output

15:52:32  INFO XMPP::Communicator: Connecting to 'localhost' ...
15:52:32  INFO Object: Engine test script >> Connected to XMPP
15:52:32 DEBUG XMPP::Topic: New topic: garage
15:52:32 DEBUG XMPP::Communicator: _subscribe >> garage SUCCEED
15:52:32 DEBUG XMPP::Topic: Create resource of type 'engine'
15:52:32 DEBUG XMPP::Topic: (garage) create_message_and_publish 'create': {:hrn=>"my_engine", :type=>:engine}
15:52:32 DEBUG XMPP::Communicator: publish >> garage SUCCEED
15:52:32 DEBUG XMPP::Topic: New topic: xmpp://[email protected]
15:52:32 DEBUG XMPP::Communicator: _subscribe >> f9288cef-ae76-4561-a199-276800efe029 SUCCEED
15:52:32  INFO Object: >>> Connected to newly created engine my_engine(id: xmpp://[email protected])
15:52:32  INFO Object: > Now we will apply 50% throttle to the engine
15:52:32 DEBUG XMPP::Topic: (f9288cef-ae76-4561-a199-276800efe029) create_message_and_publish 'configure': {:throttle=>50}
15:52:32 DEBUG XMPP::Communicator: publish >> f9288cef-ae76-4561-a199-276800efe029 SUCCEED
15:52:34 DEBUG XMPP::Topic: (f9288cef-ae76-4561-a199-276800efe029) create_message_and_publish 'request': [:rpm]
15:52:34 DEBUG XMPP::Communicator: publish >> f9288cef-ae76-4561-a199-276800efe029 SUCCEED
15:52:34  INFO Object: RPM >> 3000
15:52:36 DEBUG XMPP::Topic: (f9288cef-ae76-4561-a199-276800efe029) create_message_and_publish 'request': [:rpm]
15:52:36 DEBUG XMPP::Communicator: publish >> f9288cef-ae76-4561-a199-276800efe029 SUCCEED
15:52:36  INFO Object: RPM >> 5000
15:52:37  INFO Object: > We want to reduce the throttle to 0
15:52:37 DEBUG XMPP::Topic: (f9288cef-ae76-4561-a199-276800efe029) create_message_and_publish 'configure': {:throttle=>0}
15:52:37 DEBUG XMPP::Communicator: publish >> f9288cef-ae76-4561-a199-276800efe029 SUCCEED
15:52:38 DEBUG XMPP::Topic: (f9288cef-ae76-4561-a199-276800efe029) create_message_and_publish 'request': [:rpm]
15:52:38 DEBUG XMPP::Communicator: publish >> f9288cef-ae76-4561-a199-276800efe029 SUCCEED
15:52:38  INFO Object: RPM >> 4500
15:52:40 DEBUG XMPP::Topic: (f9288cef-ae76-4561-a199-276800efe029) create_message_and_publish 'request': [:rpm]
15:52:40 DEBUG XMPP::Communicator: publish >> f9288cef-ae76-4561-a199-276800efe029 SUCCEED
15:52:40  INFO Object: RPM >> 4000
15:52:42  INFO Object: >>> Release engine
15:52:42 DEBUG XMPP::Communicator: publish >> garage SUCCEED
15:52:42 DEBUG XMPP::Topic: (f9288cef-ae76-4561-a199-276800efe029) create_message_and_publish 'request': [:rpm]
15:52:42 DEBUG XMPP::Communicator: publish >> f9288cef-ae76-4561-a199-276800efe029 SUCCEED
15:52:42  INFO Object: RPM >> 3500
15:52:44 DEBUG XMPP::Topic: (f9288cef-ae76-4561-a199-276800efe029) create_message_and_publish 'request': [:rpm]
15:52:44 DEBUG XMPP::Communicator: publish >> f9288cef-ae76-4561-a199-276800efe029 SUCCEED
15:52:44  INFO Object: RPM >> 3000
15:52:46 DEBUG XMPP::Topic: (f9288cef-ae76-4561-a199-276800efe029) create_message_and_publish 'request': [:rpm]
15:52:46 DEBUG XMPP::Communicator: publish >> f9288cef-ae76-4561-a199-276800efe029 SUCCEED
15:52:46  INFO Object: RPM >> 2500
15:52:47  INFO Object: Engine xmpp://[email protected] released
15:52:47  INFO XMPP::Communicator: Disconnecting ...

Step 5: Hooks

OMF allow you to define hook callbacks, which basically can be called at certain stage of the operation.

Currently the system supports these hooks:

Execute in parent resource

  • before_create, called before parent creates the child resource. (in the context of parent resource)
  • after_create, called after parent creates the child resource.

Execute in child resource

  • before_ready, called when a resource created, before creating an associated pubsub topic
  • before_release, called before a resource released
  • after_initial_configured, called after child resource created, and initial set of properties have been configured.

Please also refer to DSL hook method for more information.

Hooks

Now modify the garage controller to test these hooks.

Proxy with hooks

require 'omf_rc'

module OmfRc::ResourceProxy::Garage
  include OmfRc::ResourceProxyDSL

  register_proxy :garage

  # before_create allows you access the current garage instance, the type of new resource it is going to create,
  # and initial options passed to be used for new resource
  #
  hook :before_create do |garage, new_resource_type, new_resource_opts|
    # Can check existing engines already created
    #
    info "Garage has #{garage.children.size} engine(s)"

    # Can verify new resource's options
    #
    info "You asked me to create a new #{new_resource_type} with options: #{new_resource_opts}"
  end

  # after_create hook has access to the current garage instance and newly created engine instance
  #
  hook :after_create do |garage, engine|
    # Can inspect or update newly created resource
    #
    info "Engine #{engine.uid} created"
  end
end

module OmfRc::ResourceProxy::Engine
  include OmfRc::ResourceProxyDSL

  register_proxy :engine, :create_by => :garage

  property :serial_number, :default => "0000"
  property :rpm, :default => 0

  # Use this to do initialisation/bootstrap
  #
  hook :before_ready do |engine|
    engine.property.rpm = 1000
    # Notice that now serial number hasn't been configured yet.
    #
    info "Engine serial number is #{engine.property.serial_number}"
  end

  # Since now new resource has been created and configured properly,
  # additional logic can be applied based on configured properties' state.
  #
  hook :after_initial_configured do |engine|
    # Notice now serial number is configured.
    #
    info "Engine serial number is #{engine.property.serial_number}"
  end

  # before_release hook will be called before the resource is fully released, shut down the engine in this case.
  #
  hook :before_release do |engine|
    engine.property.rpm = 0
  end
end

OmfCommon.init(:development, communication: { url: 'xmpp://localhost' }) do
  OmfCommon.comm.on_connected do |comm|
    info "Garage controoler >> Connected to XMPP server"
    garage = OmfRc::ResourceFactory.create(:garage, uid: 'garage', hrn: 'my_garage')
    comm.on_interrupted { garage.disconnect }
  end
end

Then use this simple engine test script to run through the resource cycle, from create to release.

Engine test

require 'omf_common'

def create_engine(garage)
  garage.create(:engine, hrn: 'my_engine', serial_number: '1234') do |reply_msg|
    if reply_msg.success?
      engine = reply_msg.resource

      engine.on_subscribed do
        info ">>> Connected to newly created engine #{reply_msg[:hrn]}(id: #{reply_msg[:res_id]})"
      end

      OmfCommon.eventloop.after(3) do
        release_engine(garage, engine)
      end
    else
      error ">>> Resource creation failed - #{reply_msg[:reason]}"
    end
  end
end

def release_engine(garage, engine)
  info ">>> Release engine"
  garage.release(engine) do |reply_msg|
    info "Engine #{reply_msg[:res_id]} released"
    OmfCommon.comm.disconnect
  end
end

OmfCommon.init(:development, communication: { url: 'xmpp://localhost' }) do
  OmfCommon.comm.on_connected do |comm|
    info "Engine test script >> Connected to XMPP"

    comm.subscribe('garage') do |garage|
      unless garage.error?
        create_engine(garage)
      else
        error garage.inspect
      end
    end

    OmfCommon.eventloop.after(10) { comm.disconnect }
    comm.on_interrupted { comm.disconnect }
  end
end

While you are running garage controller and test script, you should be able to see the following log message on the garage controller side.

Please check the 'info' messages and you could notice the order of how these hooks got executed. Verify the order against the diagram we showed earlier.

Proxy output

15:33:03  INFO XMPP::Communicator: Connecting to 'localhost' ...
15:33:03  INFO Object: Garage controoler >> Connected to XMPP server
15:33:03 DEBUG XMPP::Topic: New topic: garage
15:33:03 DEBUG XMPP::Communicator: _subscribe >> garage SUCCEED
15:33:03 DEBUG XMPP::Communicator: publish >> garage SUCCEED
15:33:12  INFO ResourceProxy::Garage: Garage has 0 engine(s)
15:33:12  INFO ResourceProxy::Garage: You asked me to create a new engine with options: {:hrn=>"my_engine"}
15:33:12 DEBUG XMPP::Topic: New topic: 9ae92796-bcd7-4770-a698-78df44d63fe2
15:33:12  INFO ResourceProxy::Engine: Engine serial number is 0000
15:33:12  INFO ResourceProxy::Garage: Engine 9ae92796-bcd7-4770-a698-78df44d63fe2 created
15:33:12 DEBUG XMPP::Communicator: _create >> 9ae92796-bcd7-4770-a698-78df44d63fe2 SUCCEED
15:33:12 DEBUG XMPP::Communicator: _subscribe >> 9ae92796-bcd7-4770-a698-78df44d63fe2 SUCCEED
15:33:12  INFO ResourceProxy::Engine: Engine serial number is 1234
15:33:12 DEBUG XMPP::Communicator: publish >> garage SUCCEED
15:33:12 DEBUG XMPP::Communicator: publish >> 9ae92796-bcd7-4770-a698-78df44d63fe2 SUCCEED
15:33:15  INFO ResourceProxy::AbstractResource: Releasing hrn: my_engine, uid: 9ae92796-bcd7-4770-a698-78df44d63fe2

That concludes our little tutorial for you.

Proxies included in the official RC gem

OMF RC has included some resource proxies to support network configuration and running OMF enabled applications. For more details, refer to OmfRc::ResourceProxy::AbstractResource.

Application Proxy

Application proxy has been provided to set up and control applications. We include a separate guide explaining how it works. See How to use the Application Proxy. It is also a good example demonstrating how to write a non-trivial resource proxy.

Logging

Given controlling logging is important for developing and debugging, we created a dedicated guide.

Logging system in OMF 6

Advanced topics

Organise resource proxy modules

Define inline

If you have a rather simple resource controller, with minimal set of features, like the ones described in this tutorial, you could just define these modules as part of the RC script.

Include resource proxy modules in the default package

The default location of resource proxy definition files are located in the directory omf_rc/lib/omf_rc/resource_proxy.

If you wish your feature set could be available as part of the default package, save them under this default directory, following this naming convention: OmfRc::ResourceProxy::Engine will register a proxy named :engine, and saved to file omf_rc/lib/omf_rc/resource_proxy/engine.rb

To load these default resource proxies, simple call a load method provided by ResourceFactory class in your resource controller script (e.g. engine_control.rb)

OmfRc::ResourceFactory.load_default_resource_proxies

Commit your definition files into the git repository and simply send us a pull request.

Package your proxy definition files as OMF extension gem

You could also package your proxy definition files into separate gems, if you feel they should not go into the default RC package.

This process is rather simple, take a look at this third party rc gem of openflow integration.

https://github.com/kohoumas/omf_rc_openflow

Refactor common features into resource utilities

If a set of features can be shared among different types of resources, it is a good idea to refactor them into resource utilities.

Take this engine test example, if we have more than one type of engine needs to be tested, and they could all be able to adjust throttle, we can create a utility for this.

module OmfRc::Util::Throttle
  include OmfRc::ResourceProxyDSL

  configure :throttle do |resource, value|
    resource.property.throttle = value.to_f / 100.0
  end
end

Then include this utility inside the engine resource proxy file by using:

utility :throttle

You could also overwrite a property definition provided by the utility, by registering it again using the same name.

Contributing to OMF

Ready to be part of OMF project? Please refer to this document for some guidelines:

CONTRIBUTING