How to create a plugin for openQRM ?

by Matt Rechenburg

This how to is dedicated for all people who would like to integrate their own, custom services within the openQRM data-center management console. It provides an overview about the openQRM plugin API and gives detailed examples how to use it.

The explanations in this howto will be based on an integration of an example service working in a common client-server way. This service which is going to be integrated will be called "myservice" and has a "myservice-server" and a "myservice-client". An example of such a service already integrated in openQRM is the Nagios-plugin. The Nagios-plugin automatically starts the Nagios-server part on the openQRM-server and the Nagios-client (NRPE) on the managed nodes. In this howto the integration of the example service "myservice" will work in a similar way.

Contents

Plugin basics (part 1)

Requirements
Creating the plugin skeleton
Creating the plugin definition and function file
Creating the init-scripts for "myservice"
Test-building the "myservice" plugin
Completing the "myservice" plugin
Connecting the "myservice-server" to openQRM
Connecting the "myservice-client" to the systems manged by openQRM
Building and installing the "myservice" plugin
Building rpm-packages for the "myservice" plugin
Summary (part 1)
Get the files used for part 1

Web-page and menu integration (part 2)

Adding a web-page to the plugin
Creating a menu entry for the custom web-page
Adding a simple web-service to "myservice-serverd"
Re-compiling and re-installing the "myservice" plugin
Screenshot of the web-page and menu integration
Summary (part 2)
Get the files used for part 2

Listening and responding to events (part 3)

Events in openQRM
Adding a custom event to openQRM via the "myservice" plugin
Adding an EventListerner to the "myservice" plugin
Creating the command-script connected to the EventListener
Re-compiling and re-installing the "myservice" plugin
Calling the custom event via the qrm-cli
Summary (part 3)
Get the files used for part 3


Using Java to communicate with internal openQRM-server objects (part 4)

Adding java code to the "myservice" plugin
Adding maven build configuration
Interfacing with the openQRM-server objects
Example: Creating a "id to ip" calculator
Adding the menu item for the "id to ip" calculator
Re-compiling and re-installing the "myservice" plugin
Screenshot of the "id to ip" calculator
Summary (part 4)
Get the files used for part 4


Urls
Revision History


Plugin basics (part 1)

Requirements

For starting with plugin-development you need to have openQRM compiled from its sources. It is recommended to use the sources from the cvs because this will generally include the latest fixes.
To compile openQRM you need to have the development components installed on your build-system (gcc/c++, make, autoconf, etc.).
Also the java jdk version 1.5 from sun and maven version 1.0.2 is required.
Please notice that you need to have exactly this java + maven versions !
Hint: to speed up compilation please remove all plugin-directories from the .../src/plugins/ directory expect e.g. the dhcpd plugin.

Creating the plugin skeleton

First create a new directory in the openQRM sources at ../src/plugins/ called "myservice". This directory should be named as plugin you plan to create.
	cd ../src/plugins/
	mkdir -p myservice
	
Then create a basic Makefile with the following content :
#
# openQRM plugin myservice Makefile
#
# standard definitions for the build- packaging-system
export PACKAGE_PREFIX = openqrm
export PACKAGE_GROUP = plugin
export PACKAGE_NAME = myservice
export PACKAGE_F_NAME = $(PACKAGE_PREFIX)-$(PACKAGE_GROUP)-$(PACKAGE_NAME)

# defining the output directory :
# ../../../out for general compilation
# ../../../pkg_stage/$(PACKAGE_F_NAME)/opt for rpm/deb packaging
ifdef PACKAGE_TYPE
OUTDIR := ../../../pkg_stage/$(PACKAGE_F_NAME)/opt
else
OUTDIR := ../../../out
endif

# define which files should be installed
INSTALL_FILES := etc/myservice.xml, -m 0700 etc/init.d, include
INSTALL_FILES_DEST := $(OUTDIR)/qrm/plugins/myservice

# include the build- and packaging-functions
INC_NAME := ../../build/Makefile.inc
include $(INC_NAME)
RPM_INC_NAME := ../../build/Makefile.rpm.inc
include $(RPM_INC_NAME)


all:    check
        # Do everything here to e.g. create the binaries for
        # your plugin. This section should compile your component "myservice"
        @echo "making the myservice-plugin"

install:
        # Do everything here additional needed to install "myservice" in
        # the OUT_DIR. No compilation should be done in this step
        # Files in INSTALL_FILES will be installed automatically
        @echo "installing myservice for Qrm"

clean:  check
        # Do everything here to clean-up the install-section
        @echo "cleaning up myservice"
        @/bin/rm -rf $(OUTDIR)/qrm/plugins/myservice

realclean:      clean
        @echo "making realclean for the myservice plugin"
        @/bin/rm -rf $(QRM_CACHE_DIR)/myservice \
                $(QRM_ROOT_DIR)/build/myservice

configure:
        # You can use this section to pre-configure your plugin build

check:
        @if [ -z $(QRM_CACHE_DIR) ]; then echo "ERROR: QRM_CACHE_DIR not set!"; exit 1; fi
        @if [ -z $(QRM_ROOT_DIR) ]; then echo "ERROR: QRM_ROOT_DIR not set!"; exit 1; fi
This Makefile-skeleton should be self-explaining from the included comments.
Now let's create the directory structure for the "myservice" plugin. Since plugins in openQRM should be separated from the base-server the concept is that each plugin will "work" within its plugin directory and not put and/or use files on system-level. Based on that a plugin should have a directory layout similar to the system-director layout and manage their services within their own directories. This mechanism makes it also easy to find plugin components e.g. in our example the init-script for the "myservice-server" should be located at ../src/plugins/myservice/etc/init.d/.
For now we just create an etc/init.d and include dir :
	cd myservice
	mkdir -p etc/init.d include
	

Creating the plugin definition and function file

For giving openQRM some initial informations about the "myservice" plugin we create its definition in a xml-file at ../src/plugins/myservice/etc/myservice.xml :


<?xml version="1.0"?>
<plugin>
  <properties>
    <name>myservice</name>
    <version>0.1</version>
    <description>Provides the myservice-service</description>
    <schema_version>1.0</schema_version>
  </properties>
 <config/>
</plugin>


For building and automatic packaging later we also need to create an info.xml file for the "myservice" plugin at ../src/plugins/myservice/include/myservice-info.xml with the following content :

<?xml version="1.0"?>
<package>
  <module>
    <name>myservice</name>
    <version>0</version>
    <type>plugin</type>
    <release>1</release>
    <license>Qlusters Public License Version 1.1</license>
    <group>QRM/plugins</group>
    <arch>i386</arch>
    <summary>myservice plugin</summary>
    <description>Provides myservice for openQRM</description>
  </module>
 </package>

To let openQRM know what to do when installing, uninstalling, starting and stopping the "myservice" plugin a functions file needs to be created at ../src/plugins/myservice/include/myservice-functions. It should look like :
#!/bin/bash

function myservice_install() {
        echo "installing the myservice plugin"
        # this function will run during installing the plugin
}

function myservice_uninstall() {
        echo "uninstalling the myservice plugin"
        # this function will run during uninstallation
}

function myservice_start() {
        echo "starting the myservice plugin"
        # this function will run immeadiatly after the openQRM-server is started
        # to start the myservice plugin service
}

function myservice_stop() {
        echo "stopping the myservice plugin"
        # this function will run before the openQRM-server gets stopped
        # to stop the myservice plugin service
}

function myservice_run_once() {
        echo "running run_once for the myservice plugin"
        # this function will run once after the plugins installation
        # phase and can be used to e.g. register new components or
        # apply any updates/changes to the configuration
}

Creating the init-scripts for "myservice"

For this howto we defined that "myservice" is based on a common client-server model. That means we need to create one init-script for the "myservice-server" which will be running on the openQRM-server and one "myservice-client" init-script for the starting the client component on the systems managed by openQRM.
First let's create the "myservice-server" init-script ../src/plugins/myservice/etc/init.d/myservice-server as following :
#!/bin/bash
# init script for the myservice plugin managing myservice-server

QRM_SERVER_BASE_DIR=$(pushd $(dirname $0)/../../../../.. > /dev/null; echo $PWD; popd > /dev/null)
PLUGIN_BASE_DIR=$QRM_SERVER_BASE_DIR/qrm/plugins/myservice

LOCKFILE=$QRM_SERVER_BASE_DIR/qrm/plugins/myservice/var/lock/subsys/myservice-server
mkdir -p $QRM_SERVER_BASE_DIR/qrm/plugins/myservice/var/lock/subsys/

function start() {
	echo "starting openQRM myservice plugin"
	$QRM_SERVER_BASE_DIR/qrm/plugins/myservice/sbin/myservice-serverd &
	touch $LOCKFILE
}


function stop() {
	echo "Stopping openQRM myservice plugin"
	killall myservice-serverd 1>/dev/null 2>&1
	rm -f $LOCKFILE
}

function status() {
	if [ -s $LOCKFILE ]; then
		echo "openQRM myservice-server is running"
	else
		echo "openQRM myservice-server is stopped"
	fi
}


case "$1" in
	start)
		start
		;;
	stop)
		stop
		;;
	status)
		status
		;;
	restart)
		stop
		sleep 1
		start
		;;
	*)
		echo "Usage: $0 {start|stop|status|restart}"
		exit 1

esac
exit $?

Then create the "myservice-client" init-script at ../src/plugins/myservice/etc/init.d/myservice-client as following :
#!/bin/bash
# openQRM init script for the myservice plugin managing myservice-client
#
# chkconfig: 345 97 18
# description: openQRM is the next generation Linux Data Center management. \
#              Please see www.openQRM.org for more details.

export `eval cat /proc/cmdline`
. /var/qrm/conf/qrm.conf.$id
. $QRM_NODES_BASE_DIR/qrm/include/qrm-nodes-functions

LOCKFILE=$QRM_NODES_BASE_DIR/qrm/plugins/myservice/var/lock/subsys/myservice-client
mkdir -p $QRM_NODES_BASE_DIR/qrm/plugins/myservice/var/lock/subsys/


function start() {
	echo "starting openQRM myservice plugin (myservice-client)"
	$QRM_NODES_BASE_DIR/qrm/plugins/myservice/sbin/myservice-clientd &
	touch $LOCKFILE
}


function stop() {
	echo "Stopping openQRM myservice plugin (myservice-client)"
	killall myservice-clientd 1>/dev/null 2>&1
	rm -f $LOCKFILE
}


function status() {
	if [ -s $LOCKFILE ]; then
		echo "openQRM myservice-client is running"
	else
		echo "openQRM myservice-client is stopped"
	fi
}

case "$1" in
	start)
		start
		;;
	stop)
		stop
		;;
	status)
		status
		;;
	restart)
		stop
		sleep 1
		start
		;;
	*)
		echo "Usage: $0 {start|stop|status|restart}"
		exit 1

esac
exit $?

Test-building the "myservice" plugin

We created the initial skeleton of the "myservice" plugin and we have now the following files :
	./etc/init.d/myservice-client
	./etc/init.d/myservice-server
	./etc/myservice.xml
	./include/myservice-functions
	./include/myservice-info.xml
	./Makefile
We can now attempt to build it for testing. Here an example output of the build procedure :

[root@demo myservice]# make clean
# Do everything here to clean-up the install-section
cleaning up myservice
[root@demo myservice]# make
# Do everything here to e.g. create the binaries for
# your plugin. This section should compile your component "myservice"
making the myservice-plugin
[root@demo myservice]# make install
/usr/bin/install -D   etc/myservice.xml ../../../out/qrm/plugins/myservice/etc/myservice.xml
/usr/bin/install -D  -m 0700 etc/init.d/myservice-client ../../../out/qrm/plugins/myservice/etc/init.d/myservice-client
/usr/bin/install -D  -m 0700 etc/init.d/myservice-server ../../../out/qrm/plugins/myservice/etc/init.d/myservice-server
/usr/bin/install -D   include/myservice-functions ../../../out/qrm/plugins/myservice/include/myservice-functions
/usr/bin/install -D   include/myservice-info.xml ../../../out/qrm/plugins/myservice/include/myservice-info.xml
touch ../../../out/qrm/plugins/myservice/include/.files_installed
# Do everything here additional needed to install "myservice" in
# the OUT_DIR. No compilation should be done in this step
# Files in INSTALL_FILES will be installed automatically
installing myservice for Qrm
[root@demo myservice]#           

Completing the "myservice" plugin

We have now created the initial directory structure and files for the "myservice" plugin. Still we are missing the actual "myservice-server/client" daemons and for now almost all functions are still empty. For this howto we will create very simply scripts for the "myservice-server" and the "myservice-client". In a real integration those daemons/binaries will be created through the plugins compile procedure defined in the plugins Makefiles "all" section. The "myservice-serverd" and "myservice-clientd" script should be located in the plugins sbin directory so let's create it by running :
	mkdir -p sbin
	
Here the "myservice-serverd" script which should be saved at ../src/plugins/myservice/sbin/myservice-serverd :
#!/bin/bash

# this is a simple example server daemon for the "howto create an openQRM plugin" howto
echo "myservice-server started"
while (true); do
        sleep 1000
done
And a "myservice-clientd" script which should be saved at ../src/plugins/myservice/sbin/myservice-clientd :
#!/bin/bash

# this is a simple example client daemon for the "howto create an openQRM plugin" howto
echo "myservice-client started"
while (true); do
        sleep 1000
done

To automatically add those two new file to the plugins installation please add them to the INSTALL_FILES variable in the Makefile. It should now look like :
INSTALL_FILES := etc/myservice.xml, -m 0700 etc/init.d, include, -m 700 sbin

Connecting the "myservice-server" to openQRM

Starting the "myservice-server" daemon as an additional plugin service is easy. Just add its start (and stop) commands to the "myservice_start" and "myservice_stop" functions in ../src/plugins/myservice/include/myservice-functions. Those two functions should now look like :
function myservice_start() {
	echo "starting the myservice plugin"
	# this function will run immeadiatly after the openQRM-server is started
	# to start the myservice plugin service
	$QRM_SERVER_BASE_DIR/qrm/plugins/myservice/etc/init.d/myservice-server start
}

function myservice_stop() {
	echo "stopping the myservice plugin"
	# this function will run before the openQRM-server gets stopped
	# to stop the myservice plugin service
	$QRM_SERVER_BASE_DIR/qrm/plugins/myservice/etc/init.d/myservice-server stop
}

Connecting the "myservice-client" to the systems manged by openQRM

For the "myservice-client" which should be running automatically on the systems managed by openQRM we have to do some more work. An openQRM plugin extension callled "BootService" is used to make a system, associated with this boot-service, do the following steps during init : download a specified boot-service package from the openQRM-server (this is a zip file containing all required files to start the boot-service) unpack the boot-service package run a specified init-script Let's first create the boot-service package for the "myservice" plugin. For starting the "myservice-client" we basically just need the daemon file and its init-script. To pack them during the "make install" stage of the plugin build please update the Makefile's "install" section to look like :
install:
	# Do everything here additional needed to install "myservice" in
	# the OUT_DIR. No compilation should be done in this step
	# Files in INSTALL_FILES will be installed automatically
	@echo "installing myservice for Qrm"
	mkdir -p $(OUTDIR)/qrm/plugins/myservice/client
	tar -C $(OUTDIR)/qrm/ --exclude CVS --exclude Makefile -czf $(OUTDIR)/qrm/plugins/myservice/client/myservice-client.tgz plugins/myservice/etc/init.d/myservice-client plugins/myservice/sbin/myservice-clientd

Next we need to create the boot-service definition which is done via a simple xml configuration file. Please create ../src/plugins/myservice/etc/myservice-plugin.xml with the following content :

<?xml version="1.0"?>
<!DOCTYPE plugin PUBLIC "-//JPF//Java Plug-in Manifest 0.2" "http://jpf.sourceforge.net/plugin_0_2.dtd">
<plugin id="com.qlusters.qrm.plugins.puppet-plugin" version="0.0.1" >
	<requires>
		<import plugin-id="com.qlusters.qrm.plugins.core"/>
	</requires>
	<extension plugin-id="com.qlusters.qrm.plugins.core" point-id="BootService" id="myservice">
		<parameter id="name" value="myservice"/>
		<parameter id="runlevel" value="35"/>
		<parameter id="package" value="http://$QRM_SERVER_IP_ADDRESS/unsecure/myservice/myservice-client.tgz"/>
		<parameter id="init" value="plugins/myservice/etc/init.d/myservice-client"/>
	</extension>
</plugin>


This definition creates a new boot-service in openQRM named "myservice". All systems associated with this boot-service will download the myservice-client.tgz package, unpack it and run its init-script at init 3 or 5. Please add this file to the INSTALL_FILES variable in the Makefile. INSTALL_FILES should now look like :
INSTALL_FILES := etc/myservice.xml, -m 0700 etc/init.d, include, -m 700 sbin, etc/myservice-plugin.xml
To make the myservice-client.tgz package available for download we copy it to a public section of the webserver. This step should happen during the installation of the plugin so the "myservice_install" and "myservice_uninstall" functions in ../src/plugins/myservice/include/myservice-functions should be updated as following :
function myservice_install() {
	echo "installing the myservice plugin"
	# this function will run during installing the plugin
	mkdir -p $QRM_SERVER_BASE_DIR/qrm/usr/tomcat/webapps/ROOT/unsecure/myservice
	/bin/cp -f $QRM_SERVER_BASE_DIR/qrm/plugins/myservice/client/myservice-client.tgz $QRM_SERVER_BASE_DIR/qrm/usr/tomcat/webapps/ROOT/unsecure/myservice/
}

function myservice_uninstall() {
	echo "uninstalling the myservice plugin"
	# this function will run during uninstallation
	/bin/rm -rf $QRM_SERVER_BASE_DIR/qrm/usr/tomcat/webapps/ROOT/unsecure/myservice
}

Last step for the boot-service setup is to associate it with the systems managed by openQRM. This step should happen once during installation so the "myservice_run_once" function (also in ../src/plugins/myservice/include/myservice-functions) should be used and updated as following :

function myservice_run_once() {
	echo "running run_once for the myservice plugin"
	# this function will run once after the plugins installation
	# phase and can be used to e.g. register new components or
	# apply any updates/changes to the configuration
	# boot-services for images
	$QRM_SERVER_BASE_DIR/qrm/bin/qrm-cli boot associate	-k myservice -t image
}
Not to forget is that this boot-service should be removed during uninstallation of the "myservice" plugin. Please update again the "myservice_uninstall" function and add the line to disassociate the boot-service. It should look like :
function myservice_uninstall() {
	echo "uninstalling the myservice plugin"
	# this function will run during uninstallation
	/bin/rm -rf $QRM_SERVER_BASE_DIR/qrm/usr/tomcat/webapps/ROOT/unsecure/myservice
	$QRM_SERVER_BASE_DIR/qrm/bin/qrm-cli boot dissociate -k myservice -t image
}

Building and installing the "myservice" plugin

The "myservice" plugin is now ready to be build and installed. To build/rebuild it please run "make clean && make && make install" in the "myservice" plugin directory. Here an example output :
[root@demo myservice]# make clean && make && make install
# Do everything here to clean-up the install-section
cleaning up myservice
# Do everything here to e.g. create the binaries for
# your plugin. This section should compile your component "myservice"
making the myservice-plugin
/usr/bin/install -D   etc/myservice.xml ../../../out/qrm/plugins/myservice/etc/myservice.xml
/usr/bin/install -D  -m 0700 etc/init.d/myservice-client ../../../out/qrm/plugins/myservice/etc/init.d/myservice-client
/usr/bin/install -D  -m 0700 etc/init.d/myservice-server ../../../out/qrm/plugins/myservice/etc/init.d/myservice-server
/usr/bin/install -D   include/myservice-functions ../../../out/qrm/plugins/myservice/include/myservice-functions
/usr/bin/install -D  -m 700 sbin/myservice-clientd ../../../out/qrm/plugins/myservice/sbin/myservice-clientd
/usr/bin/install -D  -m 700 sbin/myservice-serverd ../../../out/qrm/plugins/myservice/sbin/myservice-serverd
/usr/bin/install -D   etc/myservice-plugin.xml ../../../out/qrm/plugins/myservice/etc/myservice-plugin.xml
touch ../../../out/qrm/plugins/myservice/include/.files_installed
# Do everything here additional needed to install "myservice" in
# the OUT_DIR. No compilation should be done in this step
# Files in INSTALL_FILES will be installed automatically
installing myservice for Qrm
mkdir -p ../../../out/qrm/plugins/myservice/client
tar -C ../../../out/qrm/ --exclude CVS --exclude Makefile -czf ../../../out/qrm/plugins/myservice/client/myservice-client.tgz plugins/myservice/etc/init.d/myservice-client plugins/myservice/sbin/myservice-clientd
[root@demo myservice]# 
This compile procedure now created the "installable" plugin at ../../../out/qrm/plugins/myservice. To install the "myservice" plugin please copy the ../../../out/qrm/plugins/myservice directory into the plugins directory of your running openQRM-server and enable the "myservice" plugin via the qrm-installer :
[root@demo myservice]# /bin/cp -aR ../../../out/qrm/plugins/myservice /opt/qrm/plugins/
[root@demo myservice]# cd /opt/qrm
[root@demo qrm]# ./qrm-installer -i -m myservice
#####################################################
#                                                   #
#                                                   #
#               Welcome to OpenQRM 3.1.4            #
#                 installation procedure            #
#                                                   #
#                                  by Qlusters Inc  #
#####################################################
(logfile for the installation at /var/log/qrm/qrm-installer.log)
Components will be installed in following order:
    1.  myservice


Please notice
QRM will be installed with the following configuration :

General setup:
--------------
    QRM server interface       :     eth0
    QRM server base directory  :     /opt
    QRM server log directory   :     /var/log/qrm
    QRM nodes base directory   :     /opt
    QRM nodes logs directory   :     syslog
    QRM default kernel         :

Database setup:
---------------

    IP        :     localhost
    name      :     qrm
    port      :     3306
    socket    :     /var/lib/mysql/mysql.sock
    username  :     root
    password  :

Component myservice Configuration
------------------------------------------
There is no configurable parameters for this component

Is the configuration correct? (y - continue, n - cancel , c - configure)? (y/n/c) [y]
==================================
Running components sanity checks
==================================

myservice........
                                                           [  OK  ]
==================================
Running install stage for components
==================================
myservice..............

Installing myservice
installing the myservice plugin
                                                           [  OK  ]
Do you want to restart QRM server? (y/n) [y]
Stopping the Qrm-server ------------/                      [  OK  ]
Starting the Qrm-server ---------/                         [  OK  ]

#################################################################
           Installation finished
 Components with status SUCCESS : myservice

#################################################################
[root@demo qrm]# 

The "myservice" plugin is now installed and enabled on the openQRM-server. To check the "myservice-server" plugin service came up correctly we check that the "myservice-serverd" is running by "ps" :
[root@demo ~]# ps -C myservice-serverd
  PID TTY          TIME CMD
10021 pts/2    00:00:00 myservice-serve
[root@demo ~]#
To check the "myservice-client" plugin service came up correctly we can quickly deploy a VE and check that the "myservice-clientd" is running on that system :
[root@fc4_test_small_nfs-1 ~]# ps -C myservice-clientd
  PID TTY          TIME CMD
 1992 ?        00:00:00 myservice-clien
[root@fc4_test_small_nfs-1 ~]#

Building rpm-packages for the "myservice" plugin

The "myservice" plugin is now fully integrated into the openQRM build-system. To create rpm-packages for the "myservice" plugin you can simply run :
	make clean PACKAGE_TYPE=rpm
	make all PACKAGE_TYPE=rpm
	make install PACKAGE_TYPE=rpm
	make pack_rpm PACKAGE_TYPE=rpm
from the main source dir. This will create rpm-packages for the openQRM-base and all plugin available in the plugins source directory. To create Debian (.deb) packages you can use the following make commands :

	make clean PACKAGE_TYPE=deb
	make all PACKAGE_TYPE=deb
	make install PACKAGE_TYPE=deb
	make pack_rpm PACKAGE_TYPE=deb

Summary (part 1)

We have now successfully integrated the example "myservice" client-server based service into openQRM via a custom plugin.

How to go on from here ?
We (the openQRM-Team) are working on further parts of this openQRM plugin-howto which will cover :
- working with custom Resource-, Image- and VE-parameter
- adding custom menu-items and web-pages
- interacting with the objects in the openQRM-server via java
- and more

Get the files used for part 1

You can download all files used in for this howto (part 1) packed in a .tgz at :

http://downloads.openqrm.net/contrib/plugin-howto/myservice-plugin-1.0.tgz



Web-page and menu integration (part 2)

Part 1 of this howto explained how to integrate a custom, client-server based service into openQRM as a plugin. This, part 2, will give an overview and example of the web-page and menu integration capabilities of the openQRM plugin API. It will go into detail how to add a custom web-page and menu-entry to openQRM.

Adding a web-page to the plugin

To add a custom web-page we first create a web directory within the sources of the "myservice" plugin :
	cd ../src/plugins/myservice
	mkdir -p web
Since we added a new directory we must also add it to the INSTALL_FILES variable in the plugins Makefile. It should now look like :
INSTALL_FILES := etc/myservice.xml, -m 0700 etc/init.d, include, -m 700 sbin, etc/myservice-plugin.xml, web
Web-pages in openQRM are based on templates and tiles which can be populated via iframes. The template sets up how the tiles are displayed and with which content they are filled. For this example we create a template for the custom, myservice web-page at ../src/plugins/myservice/web/myservice.jsp with the following content :
<%@ taglib uri='/WEB-INF/tld/struts-tiles-1.2.tld' prefix='tiles' %>
<tiles:insert page='/templates/qrm_template.jsp'>
  <tiles:put name="title" value="MyService" />
  <tiles:put name="onPageLoad" value="javascript:showAlerts();" />
  <tiles:put name="calendar" value="" />
  <tiles:put name='header' value='/action/header.do' />
  <tiles:put name='sidebar' value='/action/sidebar.do' />
  <tiles:put name='content' value='/unsecure/myservice/web/myservice_int.jsp'/>
  <tiles:put name='footer' value='/include/footer.jsp'/>
</tiles:insert>
This template for the myservice web-page sets up all basic tiles and forwards the "content" tile to a custom (jsp) web-page which we create in the next step by creating ../src/plugins/myservice/web/myservice_int.jsp with the following content :
<% pageContext.setAttribute("myservice", "http://"+ request.getServerName() + ":8090"); %>
<iframe
scrolling="auto"
marginwidth="0"
marginheight="0"
frameborder="0"
vspace="0" hspace="0"
width="100%"
height="100%"
src="${myservice}">
</iframe>

This creates the iframe for the "content" tile filled with the server-output on port 8090 of the openQRM-server system. We will care to create a simple service on this port for this example later in this howto.

To populate the web-directory of the plugin during its installation please add a line in the ../src/plugins/myservice/include/myservice-functions "myservice_install" function to copy the web content into the web-directory of the tomcat server (the openQRM-server).
After your editing the myservice_install function should look like this :
function myservice_install() {
	echo "installing the myservice plugin"
	# this function will run during installing the plugin
	mkdir -p $QRM_SERVER_BASE_DIR/qrm/usr/tomcat/webapps/ROOT/unsecure/myservice
	/bin/cp -f $QRM_SERVER_BASE_DIR/qrm/plugins/myservice/client/myservice-client.tgz $QRM_SERVER_BASE_DIR/qrm/usr/tomcat/webapps/ROOT/unsecure/myservice/
	/bin/cp -aR $QRM_SERVER_BASE_DIR/qrm/plugins/myservice/web $QRM_SERVER_BASE_DIR/qrm/usr/tomcat/webapps/ROOT/unsecure/myservice/
}

Creating a menu entry for the custom web-page

Now let's create the menu-entry for this custom web-page within the main openQRM-menu. We just need to define it in the myservice plugins xml-configuration file at ../src/plugins/myservice/etc/myservice-plugin.xml by adding a second MenuItem extension (next to our previous configured BootService extentsion). After editing the file should look like this :

<?xml version="1.0" ?>
<!DOCTYPE plugin PUBLIC "-//JPF//Java Plug-in Manifest 0.2" "http://jpf.sourceforge.net/plugin_0_2.dtd">
<plugin id="com.qlusters.qrm.plugins.puppet-plugin" version="0.0.1" >
	<requires>
		<import plugin-id="com.qlusters.qrm.plugins.core"/>
	</requires>
	<extension plugin-id="com.qlusters.qrm.plugins.core" point-id="BootService" id="myservice">
		<parameter id="name" value="myservice"/>
		<parameter id="runlevel" value="35"/>
		<parameter id="package" value="http://$QRM_SERVER_IP_ADDRESS/unsecure/myservice/myservice-client.tgz"/>
		<parameter id="init" value="plugins/myservice/etc/init.d/myservice-client"/>
	</extension>

	<extension plugin-id="com.qlusters.qrm.plugins.core" point-id="MenuItem" id="MyService">
		<parameter id="hierarchy" value="&lt;strong&gt;Management Tools&lt;/strong&gt;" />
		<parameter id="key" value="navigation"/>
		<parameter id="title" value="MyService"/>
		<parameter id="description" value="Myservice web-page"/>
		<parameter id="url" value="/unsecure/myservice/web/myservice.jsp"/>
		<parameter id="composite" value="false"/>
		<parameter id="item-id" value="Myservice web-page"/>
	</extension>
</plugin>

The second, new MenuItem extension adds a "MyService" menu entry under the Managetement Tools menu in openQRM and points it to /unsecure/myservice/web/myservice.jsp, our custom web-page for this howto.

Adding a simple web-service to "myservice-serverd"

For this howto we enhance our example "myservice-serverd" daemon with a simple web-service on port 8090 vi the "nc" utility (netcat). This is easy. Just replace the line "sleep 1000" in the myservice-serverd script located the myservice plugins sbin directory with a nc command. It should look like this :
#!/bin/bash

# this is a simple example server daemon for the "howto create an openQRM plugin" howto
echo "myservice-server started"

while (true); do
	`dirname $0`/../../../sbin/nc  -l -p 8090 -e `which uptime`
done
This is enough to report the uptime and load average of the system on port 8090.

Re-compiling and re-installing the "myservice" plugin

We are finished with the custom web-page integration and will now :
- uninstall the previous version of the myservice plugin
- re-compile the updated myservice plugin
- re-install the updated myservice plugin

First let's uninstall. Here the sample output of a developement systems :
[root@demo ~]# cd /opt/qrm
[root@demo qrm]# ./qrm-installer -u -m myservice
#####################################################
#                                                   #
#                                                   #
#               Welcome to OpenQRM 3.1.4            #
#                 uninstall procedure               #
#                                                   #
#                                  by Qlusters Inc  #
#####################################################
Uninstalling myservice
uninstalling the myservice plugin
Plugin myservice un-installed successfully.
                                                           [  OK  ]
Do you want to restart QRM server ? (y/n) [n] y
Stopping the Qrm-server -------------/                     [  OK  ]
Starting the Qrm-server --------|                          [  OK  ]
[root@demo qrm]#
Then we re-compile the updated myservice plugin in the source directory :
	cd ../src/plugins/myservice/
	make clean && make && make install
This compiles the myservice plugin to ../out/qrm/plugins/myservice.
We now need to update the myservice plugin directory on the installed openQRM server with the content of the updated ../out/qrm/plugins/myservice directory.
	rm -rf /opt/qrm/plugins/myservice
	/bin/cp -aRv ../../../out/qrm/plugins/myservice/ /opt/qrm/plugins/
Now we just need to re-install the plugin via the qrm-installer. Here again the output of one of the development servers :

[root@demo qrm]# ./qrm-installer -i -m myservice
#####################################################
#                                                   #
#                                                   #
#               Welcome to OpenQRM 3.1.4            #
#                 installation procedure            #
#                                                   #
#                                  by Qlusters Inc  #
#####################################################
(logfile for the installation at /var/log/qrm/qrm-installer.log)

Components will be installed in following order:
    1.  myservice


Please notice
QRM will be installed with the following configuration :

General setup:
--------------
    QRM server interface       :     eth0
    QRM server base directory  :     /opt
    QRM server log directory   :     /var/log/qrm
    QRM nodes base directory   :     /opt
    QRM nodes logs directory   :     syslog
    QRM default kernel         :

Database setup:
---------------

    IP        :     localhost
    name      :     qrm
    port      :     3306
    socket    :     /var/lib/mysql/mysql.sock
    username  :     root
    password  :

Component myservice Configuration
------------------------------------------
There is no configurable parameters for this component

Is the configuration correct? (y - continue, n - cancel , c - configure)? (y/n/c) [y] y
==================================
Running components sanity checks
==================================

myservice........
                                                           [  OK  ]
==================================
Running install stage for components
==================================
myservice..............

Installing myservice
installing the myservice plugin
                                                           [  OK  ]
Do you want to restart QRM server? (y/n) [y] y
Stopping the Qrm-server ------------                       [  OK  ]
Starting the Qrm-server ---------\                         [  OK  ]
[root@demo qrm]#
#################################################################
           Installation finished
 Components with status SUCCESS : myservice

#################################################################

[root@demo qrm]#


Screenshot of the web-page and menu integration

Here a screenshot of the myservice plugin including the integrated web-page and menu entry :

Web-page integration with openQRM

Summary (part 2)

Part 2 of this howto explained how custom web-page can be integrated within openQRM in an easy way using the plugin API and its extension points. Similar to the myservice plugin used in this howto there are lots of possible tools providing web-ouput of their results which can be easily combined and even connected by integrating them within openQRM's single-management data-center console.

We (the openQRM-Team) will continue working on further parts of this howto to cover details about mechanism to connect events from external tools (integrated via a plugin) to openQRM-actions and how to interact with the internal objects in the openQRM-server via java.

.. to be continued

Get the files used for part 2

You can download all files used in for this howto (part 2) packed in a .tgz at :

http://downloads.openqrm.net/contrib/plugin-howto/myservice-plugin-1.1.tgz


Listening and responding to events (part 3)

Part 3 of this HowTo will give an overview and example how to listen to events in openQRM and connect them to custom commands. This mechanism is often used by plugins to execute custom commands on specific events.

Events in openQRM

In openQRM every action results in a specific event. Also error conditions are causing events within the openQRM-server.
The EventListener extension provided by the openQRM plugin API supports to hook custom commands to every event happening in the openQRM-server. It also allows plugins to define their own events.
As an example the Xen-plugin is using an EventListern listening for the "Resource.State.Operation.ShuttingSystem" and "Resource.State.Role.Deassigning" event to check if it needs to re-write the systems pxe-configuration file.

Please find a list of the possible events in the openQRM-server at :

http://openqrm.cvs.sourceforge.net/openqrm/base/openqrm-3.1.4/src/base/java/main/resources/events.properties?revision=1.1&view=markup

This file can also be found on an installed openQRM-server at :

/opt/qrm/java/webapp/WEB-INF/classes/events.properties

Adding a custom event to openQRM via the "myservice" plugin

Plugins can add their own events to the openQRM-server in an easy way by simply providing an "events.properties" file within the plugins /etc directory.
For the example "myservice" plugin we add a custom "MyService.Custom.Notice" event by creating ../src/plugins/myservice/etc/events.properties with the following content :
	MyService.Custom.Notice$level=Event
	MyService.Custom.Notice$message=Custom MyService event arrived.

Please do not forget to add this new file to the INSTALL_FILES variable in the Makefile. The INSTALL_FILES in the Makefile should look like this :
INSTALL_FILES := etc/myservice.xml, -m 0700 etc/init.d, include, -m 700 sbin, etc/myservice-plugin.xml, etc/events.properties, web

Adding an EventListerner to the "myservice" plugin

Now we would like to listen for this "MyService.Custom.Notice" event. We simply add an EventListern to the plugins xml-configuration file to defines for which event to check and which command to run when the event happens.
Please edit ../src/plugins/myservice/etc/myservice-plugin.xml and add the following section :
	<extension id="MyServiceCustomListener" plugin-id="com.qlusters.qrm.plugins.core" point-id="EventListener">
		<parameter id="eventKey" value="MyService.Custom.Notice"/>
		<parameter id="execution">
		<parameter id="type" value="exec"/>
		<parameter id="resource" value="path=$QRM_SERVER_BASE_DIR/qrm/plugins/myservice/sbin/myservice-custom-cmd"/>
		</parameter>
		<parameter id="inTransaction" value="false"/>
	</extension>

The whole ../src/plugins/myservice/etc/myservice-plugin.xml file should now look like this :

<?xml version="1.0" ?>
<!DOCTYPE plugin PUBLIC "-//JPF//Java Plug-in Manifest 0.2" "http://jpf.sourceforge.net/plugin_0_2.dtd">
<plugin id="com.qlusters.qrm.plugins.puppet-plugin" version="0.0.1" >
	<requires>
		<import plugin-id="com.qlusters.qrm.plugins.core"/>
	</requires>
	<extension plugin-id="com.qlusters.qrm.plugins.core" point-id="BootService" id="myservice">
		<parameter id="name" value="myservice"/>
		<parameter id="runlevel" value="35"/>
		<parameter id="package" value="http://$QRM_SERVER_IP_ADDRESS/unsecure/myservice/myservice-client.tgz"/>
		<parameter id="init" value="plugins/myservice/etc/init.d/myservice-client"/>
	</extension>

	<extension plugin-id="com.qlusters.qrm.plugins.core" point-id="MenuItem" id="MyService">
		<parameter id="hierarchy" value="&lt;strong&gt;Management Tools&lt;/strong&gt;" />
		<parameter id="key" value="navigation"/>
		<parameter id="title" value="MyService"/>
		<parameter id="description" value="Myservice web-page"/>
		<parameter id="url" value="/unsecure/myservice/web/myservice.jsp"/>
		<parameter id="composite" value="false"/>
		<parameter id="item-id" value="Myservice web-page"/>
	</extension>

	<extension id="MyServiceCustomListener" plugin-id="com.qlusters.qrm.plugins.core" point-id="EventListener">
		<parameter id="eventKey" value="MyService.Custom.Notice"/>
		<parameter id="execution">
		<parameter id="type" value="exec"/>
		<parameter id="resource" value="path=$QRM_SERVER_BASE_DIR/qrm/plugins/myservice/sbin/myservice-custom-cmd"/>
		</parameter>
		<parameter id="inTransaction" value="false"/>
	</extension>

</plugin>

This defines to listen for the "MyService.Custom.Notice" (set in "eventkey") and then run the command "$QRM_SERVER_BASE_DIR/qrm/plugins/myservice/sbin/myservice-custom-cmd" (set by "resource").

Creating the command-script connected to the EventListener

Now that we have created the EventListener we care about creating the custom command connected to our custom event. Please create ../src/plugins/myservice/sbin/myservice-custom-cmd with the following content :
#!/bin/bash

# this is a simple example custom command for the "howto create an openQRM plugin" howto
echo "MyService.Custom.Notice event happened in the openQRM-server"

Re-compiling and re-installing the "myservice" plugin

Please re-compile and re-install the "myservice" plugin in the same way as after part 2

Calling the custom event via the qrm-cli

After re-compiling and re-installing the "myservice" plugin the new created "MyService.Custom.Notice" event can be activated/called by adding it to the openQRM-servers event-list via the qrm-cli.

Adding a "MyService.Custom.Notice" event as a global event :
./qrm-cli -u qrm -p qrm event add -g -k "MyService.Custom.Notice"
in /var/log/qrm/qrmlog :
...
 2007-11-06 12:20:18,478 DEBUG [EventsManager] (http-443-Processor1:) Event: [49] Custom MyService event arrived. Entities are: QRM
 2007-11-06 12:20:18,478 DEBUG [EventListenerManager] (http-443-Processor1:) Calling to fireEventCreated with event [id=49:creationTime=Tue Nov 06 12:20:18 CET 2007:levelInt=4:key=MyService.Custom.Notice:]:
 2007-11-06 12:20:18,485 DEBUG [EventListenerManager] (http-443-Processor1:) Calling to afterCommit with event [id=49:creationTime=Tue Nov 06 12:20:18 CET 2007:levelInt=4:key=MyService.Custom.Notice:]:
 2007-11-06 12:20:19,093 DEBUG [ExecutableDelegate] (http-443-Processor1:) Run of /opt/qrm/sbin/exec_wrapper.sh QRM_SESSION="0.20774404329788887" -- $QRM_SERVER_BASE_DIR/qrm/plugins/myservice/sbin/myservice-custom-cmd returned:
Stdout:
MyService.Custom.Notice event happened in the openQRM-server

Stderr:
...
Adding a "MyService.Custom.Notice" event on behalf of a resource :
./qrm-cli -u qrm -p qrm event add --internal_cr_id 1 -k "MyService.Custom.Notice"
in /var/log/qrm/qrmlog :
...
 2007-11-06 12:20:52,763 DEBUG [EventsManager] (http-443-Processor2:) Event: [50] Custom MyService event arrived. Entities are: Node 1
 2007-11-06 12:20:52,763 DEBUG [EventListenerManager] (http-443-Processor2:) Calling to fireEventCreated with event [id=50:creationTime=Tue Nov 06 12:20:52 CET 2007:levelInt=4:key=MyService.Custom.Notice:]:
 2007-11-06 12:20:52,771 DEBUG [EventListenerManager] (http-443-Processor2:) Calling to afterCommit with event [id=50:creationTime=Tue Nov 06 12:20:52 CET 2007:levelInt=4:key=MyService.Custom.Notice:]:
 2007-11-06 12:20:53,492 DEBUG [ExecutableDelegate] (http-443-Processor2:) Run of /opt/qrm/sbin/exec_wrapper.sh QRM_SESSION="0.8713933908322177" -- $QRM_SERVER_BASE_DIR/qrm/plugins/myservice/sbin/myservice-custom-cmd returned:
Stdout:
MyService.Custom.Notice event happened in the openQRM-server

Stderr:
...
Please notice that when the event was added to openQRM on behalf of a resource it is possible to use additional commandline parameters for the custom script. Those extra parameters are defined in the ../src/plugins/myservice/etc/myservice-plugin.xml file and can be used by simply changing the parameter line :
  <parameter id="resource" value="path=$QRM_SERVER_BASE_DIR/qrm/plugins/myservice/sbin/myservice-custom-cmd"/>

to
  <parameter id="resource" value="path=$QRM_SERVER_BASE_DIR/qrm/plugins/myservice/sbin/myservice-custom-cmd ${resource.id} ${resource.ipString}"/>

Then the myservice-custom-cmd will be executed with the resource ip and id as the first 2 commandline parameters.

Summary (part 3)

Part 3 of this howto explained how to listen to events in the openQRM-server, how to create custom events via a plugin and how to connect specific events to custom commands by the example of a "MyService.Custom.Notice" event connected to a plugin command (myservice-custom-cmd). This mechanism is especially useful for the integration of third-party components because it makes it easy to map third-party-events to openQRM-events which then can be handled automatically.

.. to be continued

Get the files used for part 3

You can download all files used in for this howto (part 3) packed in a .tgz at :

http://downloads.openqrm.net/contrib/plugin-howto/myservice-plugin-1.2.tgz




Using Java to communicate with internal openQRM-server objects (part 4)

Part 4 of this HowTo will give an overview and example how to interface directly with the internal objects of the openQRM-server using the java-language. It desribes how to add and integrate custom logic within openQRM using its well-defined plugin API.

In the example for this part 4 we will add a custom java class to the "myservice" plugin which will act as a "id to ip" calculator. It will list all "idle" resources with internal id and ip-address and execute a method from the custom java class. This example is useful for integrating all kinds of client-server based components within openQRM.

Adding java code to the "myservice" plugin

Adding custom java classes to the openQRM-server engine is a simple task for a plugin.
For this HowTo we create a java class "MyServiceManager.java" at ../src/plugins/myservice/main/code/java/com/qlusters/qrm/plugins/myservice/MyServiceManager.java with the following content :
package com.qlusters.qrm.plugins.myservice;
import java.util.*;
import java.io.*;

public class MyServiceManager {

	public static String MyService(String ip) {
		String ret;
    	ret = "Executing MyService on " + ip;
    	return ret;
    }
}

A very simple java class with one method which accepts a string as parameter and returns a string when being executed.

Of course this java class can use all internal objects provided by the openQRM-server by importing their packages in the class definition. For now we will keep it simple.

Adding maven build configuration

Now let's benefit from the automated build-system of openQRM. To compile the java class created in the previous step please create the following 3 maven configuration files in the "myservice" plugin base directory :
(some of those config files are quite long so we provide links for them below)

../src/plugins/myservice/maven.xml
../src/plugins/myservice/project.properties
../src/plugins/myservice/project.xml

After adding the maven configuration files we just need to add a few lines into the "myservice" plugin Makefile. First we add a new target "make java" and then we run this target during "make install". Here the updated section of the Makefile :

...

install:
	# Do everything here additional needed to install "myservice" in
	# the OUT_DIR. No compilation should be done in this step
	# Files in INSTALL_FILES will be installed automatically
	@echo "installing myservice for Qrm"
	mkdir -p $(OUTDIR)/qrm/plugins/myservice/client
	tar -C $(OUTDIR)/qrm/ --exclude CVS --exclude Makefile -czf $(OUTDIR)/qrm/plugins/myservice/client/myservice-client.tgz plugins/myservice/etc/init.d/myservice-client plugins/myservice/sbin/myservice-clientd
	make java

java:
	../../build/maven-helper.sh install -c $(QRM_CACHE_DIR)/java -t $(QRM_ROOT_DIR) -d $(INSTALL_FILES_DEST)/include -m myservice -w $(TOMCAT_DIR) -- $(MAVEN_OPTS)

....



The "make java" target will automatically build a myservice-plugin-1.0.jar to ../out/qrm/plugins/myservice/include/myservice-plugin-1.0.jar which then will be plug'ed into the openQRM-server engine during the plugin installation.

One thing left to do is to add to the myservice_install/uninstall functions in ../src/plugins/myservice/include/myservice-functions to link the myservice-plugin-1.0.jar file to the openQRM-servers lib directory. The functions should now look like below :

function myservice_install() {
	echo "installing the myservice plugin"
	# this function will run during installing the plugin
	mkdir -p $QRM_SERVER_BASE_DIR/qrm/usr/tomcat/webapps/ROOT/unsecure/myservice
	/bin/cp -f $QRM_SERVER_BASE_DIR/qrm/plugins/myservice/client/myservice-client.tgz $QRM_SERVER_BASE_DIR/qrm/usr/tomcat/webapps/ROOT/unsecure/myservice/
	/bin/cp -aR $QRM_SERVER_BASE_DIR/qrm/plugins/myservice/web $QRM_SERVER_BASE_DIR/qrm/usr/tomcat/webapps/ROOT/unsecure/myservice/
	/bin/ln -sf $QRM_SERVER_BASE_DIR/qrm/plugins/myservice/include/myservice-plugin-1.0.jar ${QRM_SERVER_BASE_DIR}/qrm/java/webapp/WEB-INF/lib/myservice-plugin-1.0.jar
}

function myservice_uninstall() {
	echo "uninstalling the myservice plugin"
	# this function will run during uninstallation
	/bin/rm -rf $QRM_SERVER_BASE_DIR/qrm/usr/tomcat/webapps/ROOT/unsecure/myservice
	$QRM_SERVER_BASE_DIR/qrm/bin/qrm-cli boot dissociate -k myservice -t image
	/bin/rm -f ${QRM_SERVER_BASE_DIR}/qrm/java/webapp/WEB-INF/lib/myservice-plugin-1.0.jar
}

The additional last line in the "myservice_install" function makes the custom jar file available for the openQRM-server which will get access to the included classes and methods after a restart (which is the default after the installation of addtional plugins).
In the "myservice_uninstall" function the symbolic link to the custom jar file simply gets removed again during uninstallation of the plugin.

Interfacing with the openQRM-server objects

Basically there are two ways to inteface and communicate with the internal objects in the openQRM-server :

- jsp (java server pages)
- pure java code

In section Adding java code to the "myservice" plugin we already added some custom java code to the "myservice" plugin which gets plug'ed into openQRM via a addtional jar file. To get access to the openQRM objects simply import their classes into the plugins java source code and use them according their API.

To easily find out how the openQRM objects are working and how they can be used we recommend to use Eclipse which has a nice built-in search functions for classes. Of course your favourite editor and grep will do the same job.

Before going into more details let's see how to find informations about some useful objects. In the following example we will create a "id to ip" calculator which is using the "ComputeResourceData" object. This object represents a resource in openQRM so it contains all informations about a managed system e.g. id, ip, mem, cpu, ...
In the sources of openQRM you find this class at :
/openqrm-3.1.4/src/base/java/main/code/com/qlusters/qrm/server/api/objects/ComputeResourceData.java

Please look into it a bit to find out how easy it is to e.g. get the systems ip-address via the "getIpString()" method.

Now let's start with the "id to ip" calculator example.

Example: Creating a "id to ip" calculator

First we create a new webpage template at ../src/plugins/myservice/web/myservice-calculator.jsp with the following content :

<%@ taglib uri='/WEB-INF/tld/struts-tiles-1.2.tld' prefix='tiles' %>
<tiles:insert page='/templates/qrm_template.jsp'>
  <tiles:put name="title" value="MyService" />
  <tiles:put name="onPageLoad" value="javascript:showAlerts();" />
  <tiles:put name="calendar" value="" />
  <tiles:put name='header' value='/action/header.do' />
  <tiles:put name='sidebar' value='/action/sidebar.do' />
  <tiles:put name='content' value='/unsecure/myservice/web/myservice-calculator_int.jsp'/>
  <tiles:put name='footer' value='/include/footer.jsp'/>
</tiles:insert>

As described in the section Adding a web-page to the plugin webpages in openQRM are based on tiles which get populated via Iframes. This template definition loads the content of "/unsecure/myservice/web/myservice-calculator_int.jsp" in the main frame.

As the next step we create this content by creating ../src/plugins/myservice/web/myservice-calculator_int.jsp as below :

<%@ taglib uri='/WEB-INF/tld/c-1.1.2.tld' prefix='c' %>

<%@include file="/include/metaData.inc"%>
<%@include file="/include/content.inc"%>

<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>

<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>
<%@ page import="com.qlusters.qrm.utils.StringUtils" %>
<%@ page import="com.qlusters.qrm.server.api.services.ApiServicesLocator" %>
<%@ page import="com.qlusters.qrm.server.api.objects.resources.*" %>
<%@ page import="com.qlusters.qrm.server.api.objects.*" %>
<%@ page import="com.qlusters.qrm.server.api.services.nodes.*" %>
<%@ page import="com.qlusters.qrm.server.api.objects.Paging" %>
<%@ page import="com.qlusters.qrm.server.api.objects.SortOrder" %>
<%@ page import="com.qlusters.qrm.server.api.objects.Sorter" %>
<%@ page import="com.qlusters.qrm.server.api.objects.filters.*" %>
<%@ page import="com.qlusters.qrm.plugins.myservice.*" %>

<html>
<body>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr class="h1Row">
<h1>
<img src="/images/ind_resources.gif" align="middle"/>
Myservice Id2Ip Calculator

</h1>
</td>
</table>			


<h2>Idle Resources
<table width="100%" border="0" cellspacing="0" cellpadding="0">
	<tr>
	<fieldset>
	<legend>Resources
		<table width="100%" border="2" cellspacing="0" cellpadding="1" class="dataTable">
			<tr>

		<%

		ComputeResourceFilterData allfilter = new ComputeResourceFilterData();
		allfilter.setIdle();
		allfilter.setMaintenance(false);
	
		Paging allpage = new Paging();
		Sorter allsort = new Sorter("id", SortOrder.DESC);
		List<ComputeResourceData> alldata = ApiServicesLocator.getInstance().getResourcesServices().getResources(allfilter, allpage, allsort);
        ListIterator iter = alldata.listIterator();
        while (iter.hasNext()) {
            ComputeResourceData resource = (ComputeResourceData) iter.next();

			out.println("<td>");
            out.print("node " +resource.getExternalId());
			out.println("</td><td>");
            out.print(resource.getIpString());
			out.println("</td>");
			out.println("<td>");
		%>
			<%= (com.qlusters.qrm.plugins.myservice.MyServiceManager.MyService(resource.getIpString())) %>
		<%
			out.println("</td>");
		}
		%>
		
			</tr>
		</table>			
		</td></tr>
</table>

</body>
</html>



We will now go over each interesting step one by one.

1) Imports of the openQRM classes

<%@ taglib uri='/WEB-INF/tld/c-1.1.2.tld' prefix='c' %>

<%@include file="/include/metaData.inc"%>
<%@include file="/include/content.inc"%>

<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>

<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>
<%@ page import="com.qlusters.qrm.utils.StringUtils" %>
<%@ page import="com.qlusters.qrm.server.api.services.ApiServicesLocator" %>
<%@ page import="com.qlusters.qrm.server.api.objects.resources.*" %>
<%@ page import="com.qlusters.qrm.server.api.objects.*" %>
<%@ page import="com.qlusters.qrm.server.api.services.nodes.*" %>
<%@ page import="com.qlusters.qrm.server.api.objects.Paging" %>
<%@ page import="com.qlusters.qrm.server.api.objects.SortOrder" %>
<%@ page import="com.qlusters.qrm.server.api.objects.Sorter" %>
<%@ page import="com.qlusters.qrm.server.api.objects.filters.*" %>
<%@ page import="com.qlusters.qrm.plugins.myservice.*" %>

This section imports all needed openQRM classes for this example "id to ip" calculator. It also imports the custom class "myservice" we created at Adding java code to the "myservice" plugin.
After importing those classes all included objects and methods are available within the jsp web-page.

2) Using a filter for listing resources
After some pure html we find :
		ComputeResourceFilterData allfilter = new ComputeResourceFilterData();
		allfilter.setIdle();
		allfilter.setMaintenance(false);

This creates a new ComputeResourceFilterData object named allfilter which is then being set to filter only "idle" resources which are not in "maintainance mode".

3) Applying the sort order
Then a new page order is being created by the following lines :

		Paging allpage = new Paging();
		Sorter allsort = new Sorter("id", SortOrder.DESC);

It defines the order DESC by the objects "id".

4) Get a list of resource objects
The following line creates a list of ComputeResourceData objects using the previously created filter and paging order :
		List<ComputeResourceData> alldata = ApiServicesLocator.getInstance().getResourcesServices().getResources(allfilter, allpage, allsort);

So we now have a list of all "idle" resources which are not in maintainance, ordered DESC by their "id" in the "alldata" object.

5) Iterating over the results
Now we simply create a new ListIterator object starting at the beginning of our "alldata" object to iterate over the results and fetch some data per resource.
        ListIterator iter = alldata.listIterator();
        while (iter.hasNext()) {
            ComputeResourceData resource = (ComputeResourceData) iter.next();
        ....

For each entry in the "alldata" list of our resources a new ComputeResourceData object is being derived which then contains all informations for a resource in openQRM.

6) Fetching the results
To fetch information from the ComputeResourceData object we now can use its "get" methods :
..
out.print("node " +resource.getExternalId());
..
out.print(resource.getIpString());
..


7) Calling custom method from the plugins java class
The following line calls our method "MyService(String ip)" from the custom java class we created for the "myservice" plugin at Adding java code to the "myservice" plugin for each resource in the list.
			<%= (com.qlusters.qrm.plugins.myservice.MyServiceManager.MyService(resource.getIpString())) %>


The rest is pure html.


Adding the menu item for the "id to ip" calculator

One thing left to do to complete our "id to ip" calculator example is to add a menu entry for the calculator web-page as described at Creating a menu entry for the custom web-page.
Please add the following section to the "myservice" plugin xml-definition at ../src/plugins/myservice/etc/myservice-plugin.xml :

	<extension plugin-id="com.qlusters.qrm.plugins.core" point-id="MenuItem" id="MyServiceCalc">
		<parameter id="hierarchy" value="<strong>Management Tools</strong>" />
		<parameter id="key" value="navigation"/>
		<parameter id="title" value="MyServiceCalc"/>
		<parameter id="description" value="Myservice Calculator"/>
		<parameter id="url" value="/unsecure/myservice/web/myservice-calculator.jsp"/>
		<parameter id="composite" value="false"/>
		<parameter id="item-id" value="Myservice Calculator"/>
	</extension>


Ready to re-compile and re-install it.

Re-compiling and re-installing the "myservice" plugin

Please re-compile and re-install the "myservice" plugin in the same way as after part 2

Screenshot of the "id to ip" calculator

openQRM myservice plugin - id to ip calculator

Summary (part 4)

This part 4 of the openQRM plugin HowTo explained how to integrate custom logic into openQRM and how to interface with the internal server objects via the java language. It also gave an overview of how to access the API of the openQRM-server, where to find the class definitions and how to use those objects based on a simple "id to ip" calculator.

We hope it will encourage even more people (especially java developers) to look into openQRM's pluggable architecture and to contribute by developing additional useful plugins for openQRM. Enjoy !

Get the files used for part 4

You can download all files used in for this howto (part 4) packed in a .tgz at :

http://downloads.openqrm.net/contrib/plugin-howto/myservice-plugin-1.3.tgz

or you can directly check them out from the openQRM cvs at sourceforge.net by :
cvs -d:pserver:anonymous@openqrm.cvs.sourceforge.net:/cvsroot/openqrm login
# (just press ENTER when it asks for a password)
cvs -z3 -d:pserver:anonymous@openqrm.cvs.sourceforge.net:/cvsroot/openqrm co -P plugins/myservice



Urls

openQRM website - http://www.openqrm.org/

openQRM project - http://sourceforge.net/projects/openqrm

Qlusters - http://qlusters.com/

Revision History

1.0 22.10.2007 - initial release (part 1)
1.1 30.10.2007 - added part 2 about web-page and menu integration
1.1 06.11.2007 - added part 3 about listening to events
1.2 20.11.2007 - added part 4 about interfacing with the internal openQRM-server objects via java