Sunday, May 18, 2008

Writing a RHQ plugin Part 4

Welcome back to the fourth part of the trilogy :)

In the last three parts we have been building our first RHQ plugin. This was working great, but hardcoding the target URL is not really elegant. In this posting I will show you how to make the target URLs configurable from the GUI.

To do this we need to reshuffle things a little:
We will have a generic Server 'HttpCheck' that servers as parent for the individual http-servers that we want to monitor. Those will live as Services under that Server. In the Server inventory we will add the possibility to manually add new http servers on the go.



As you may have already guessed, most of this is done in the plugin descriptor. We also need some small code changes, but those are mostly to separate the concerns of the various files. Lets start with the changed plugin descriptor

Changed plugin descriptor



The boilerplate code is the same as before and will thus not be shown again.


<server name="HttpCheck"
description="Httpserver pinging"
discovery="HttpDiscoveryComponent"
class="HttpComponent">


I have changed the name of the Server to HttpCheck, as this is nicer in the GUI. Now the interesting part starts:


<service name="HttpServer"
discovery="HttpServiceDiscoveryComponent"
class="HttpServiceComponent"
description="One remote Http Server"
supportsManualAdd="true"
>


Here we introduce a Service as child of the above Server. It has its own Plugin Component and Discovery classes (the name of the classes reflect that they belong to this Service). Technically they could have gone into the existing classes, but this way it is more obvious who does what. The attribute supportsManualAdd tells RHQ that those HttpServer Services can be added by the operator in the GUI - just what we want.


<plugin-configuration>
<c:simple-property name="url"
type="string"
required="true" />
</plugin-configuration>


The plugin-configuration tells RHQ that this service can be configured with one simple property, the URL of the remote, which is required. I'll talk a bit more about properties in a minute.

Last but not least, we have moved the two metrics into the service tag (so I don't show them in detail again:


<metric property="responseTime" ...
 
<metric property="status" ...
</service>
</server>


A word about configuration and properties



The configuration type presented here, can be used in two forms within a plugin descriptor: plugin-configuration and resource-configuration. Check the structure diagram in part 2 to see where they belong.

A configuration can consist of a number of sub-elements - notably properties that are children of the abstract configurationType. This is described below.


(Diagram created with http://x2svg.sf.net/)

In addition it is possible to group properties together in the group element. The GUI will show those in their own collapsable section. Allowed child elements of group are one description element and instances of the abstract configuration-property. Templates allow you to preset some configuration properties, so the user has only to fill in stuff that is needed or that they want to change. The template itself is of the configuration type and thus no shown again.

Properties



Properties allow you to specify individual apsects of a configuration. There are three types of properties:

  • simple-property: for one key value pair, as shown above

  • map-property: for a bunch of key value pairs, following the java.util.Map concept

  • list-property: for a list of properties.




(Diagram created with http://x2svg.sf.net/)


As you can see from the structural diagram, it is possible to nest configuration properties within list-property and map-property elements to compose more complex configurations.

If we would want to allow our Services to add multiple remote servers with properties of 'host', 'port', 'protocol' it could look like this:


<plugin-configuration>
<c:list-property name="Servers">
<c:map-property name="OneServer">
<c:simple-property name="host"/>
<c:simple-property name="port">
<c:integer-constraint
minimum="0"
maximum="65535"/>
</c:simple-property>
<c:simple-property name="protocol">
<c:property-options>
<c:option value="http" default="true"/>
<c:option value="https"/>
</c:property-options>
</c:simple-property>
</c:map-property>
</c:list-property>
</plugin-configuration>


This example also shows a few more possibilities we have here:
The port has a constraint so, the GUI can validate the input being between 0 and 2^16-1. For the protocol, we offer the user a drop down list / radio buttons to choose the protocol from. It defaults to 'http', as indicated on the option element.

Change in discovery components



These changes are - as already indicated - more or less just for clarity reasons and to clearly separate out the concerns of each component.

Server level: HttpDiscoveryComponent



The HttpDiscoveryComponent from part 3 of the series only got some minor changes to cater for the change in naming, so I am not showing it here - have a look at the provided sources archive for details.

Service level: HttpServiceDiscoveryComponent



The HttpServiceDiscoveryComponent is more interesting, as we no longer have the hard coded keys, but we get the URL passed in from the GUI when the user is adding a new one.

public class HttpServiceDiscoveryComponent
implements ResourceDiscoveryComponent<HttpServiceComponent>
{
public Set<DiscoveredResourceDetails> discoverResources
(ResourceDiscoveryContext<HttpServiceComponent> context)
throws InvalidPluginConfigurationException, Exception
{
Set<DiscoveredResourceDetails> result =
new HashSet<DiscoveredResourceDetails>();
ResourceType resourceType = context.getResourceType();


This basically the same code that we already know. The interesting part starts now:


List<Configuration> childConfigs =
context.getPluginConfigurations();
for (Configuration childConfig : childConfigs) {
String key = childConfig.getSimpleValue("url", null);
if (key == null)
throw new InvalidPluginConfigurationException(
"No URL provided");


We get a list of plugin configurations passed from the context through which we loop
to determine the passed parameters. As we have only one - the url - this is simple.
If there is no url provided provided we complain (actually that should never happen, as we
marked the property as required in the plugin descriptor above).


String name = key;
String description = "Http server at " + key;
DiscoveredResourceDetails detail =
new DiscoveredResourceDetails(
resourceType, key, name, null,
description, childConfig, null
);
result.add(detail);
}
return result;
}


The remainder is the same as we know it already from the previous part.


Change in plugin components



The change in plugin components in basically that the old HttpComponent got
renamed to HttpServiceComponent and that we have a new "pseudo" HttpComponent on server level.

Server level - HttpComponent



Ok, this one is - as just described - a dummy implementation, as it just provides
placeholder methods from the ResourceComponent interface.


public AvailabilityType getAvailability() {
return AvailabilityType.UP;
}


We set the Availability to being always UP so the component can successfully start. We leave the other two methods just as empty implementations.

Service level - HttpServiceComponent



As indicated this is more or less the old HttpComponent except for one change:


public void start(ResourceContext context)
throws InvalidPluginConfigurationException, Exception
{
url = new URL(context.getResourceKey());
// Provide an initial status, so
// getAvailability() returns up
status = "200";
}


We are now setting the URL when the component is starting be reading it from the passed ResourceContext.

Building the plugin



The updated plugin can be built as shown in the previous part by calling mvn -Pdev install in the root of plugin source tree.

There is currently as of RHQ 1.0 a drawback though: Changes like those above may not be recognized by the system (or worse even throw an Exception in the server at plugin update time) - in this case you have to clean out the database and start with an empty one. We are looking into fixing this.

Summary



You have just seen, how easy it is to pass plugin configuration parameters from the GUI to a plugin by expressing the parameters in the plugin descriptor. Our plugin is now able to have an arbitrary number of child services that each monitor a different remote http server. The changes needed are basically a few more lines of XML and a little bit more Java code.

The sources are again available as zip archive. Just install it like the previous one (overwrite the previous one).

In the next posting I will talk a little more about the OperationFacet and the ConfigurationFacet and will show an example around using process scans for discovery of servers and services.


 


Part 1
Part 2
Part 3
Part 5








Technorati Tags:
, ,

No comments: