Subscribe to feed
Blog | About

Archive for April, 2007

Zenoss Core

I recently installed Zenoss Core (available at zenoss.com) at the office for the purpose of monitoring the handful of machines we have there. My goal with this post was to be exhaustive but I decided instead to give a quick summary and some tips as the documentation is quite good already. A community site with forums, a blog, and a wiki can be found at community.zenoss.com and it is pretty lively. I’ve asked 2 questions thus far and received prompt and helpful responses and discussion in return.

In the past my tools for monitoring machines have consisted of Nagios and patchworks of shell scripts. Shell scripts aren’t particularly manageable for a large number of machines and I found Nagios to be too difficult to configure and learn. Nagios is a really powerful tool used by a lot of people though so don’t discount it based solely on my unqualified opinion. To learn more about Nagios go here.

That said, I was trying to find a new approach and slashdot recently ran a post about Zenoss. After giving it a shot and finally getting it installed I have to say I really love this thing for monitoring network devices and servers. It is fairly easy to install and learn, has a great interface and charting out of the box, is incredibly configurable and customizable, has an active and helpful community, great documentation, and if needed can run Nagios plugins.

Installing on OpenSuse 10.2 64bit

As of a few days ago 64bit rpms are not available for installing Zenoss so you will need to go with the source tarball found on the download page here. Once you have the source where you want it on the machine that will do your monitoring you can use the instructions here to perform the installation. There really isn’t anything missing from their instructions, but I didn’t take everything literally and it made things take longer. If you have trouble with the install be sure to check the zenbuild.log file. Here are the issues I faced.

  • If during the install you get messages along the lines of wrong ELF class: ELFCLASS32 then you are probably trying to use the 32bit rpm to install Zenoss on a 64bit OS. Go grab the source tarball instead.
  • You need MySQL 5.0.22 or greater. Though this post doesn’t cover the rpm installation in depth if you are able to use the rpm and it complains about this dependency even when you are 100% certain you have a qualified MySQL version installed (such as happened for me on a 32bit OpenSuse box) just force it with --nodeps and you should be fine.
  • You really do need Python 2.3.6 or 2.4. If you use Yast with OpenSuse you will not be able to use the Python it provides (its 2.5). If this is the case go to the 2.3.6 download page on python.org, grab the tarball and do a ./configure, make, make install. You should now be set. Make sure running which python points you to the 2.3.6 version. You should not need to uninstall Python 2.5 from your machine. When I tried to do this Yast complained about all sorts of dependencies.
  • If during the install you get messages related to mkzopeinstance.py then you have Python 2.5 installed and not 2.3.6 or 2.4. You need to go grab and install the correct version.
  • If during the install you get messages containing undefined symbol: Py_InitModule4 it probably means the Zenoss installer is seeing and running a Python 2.5 interpreter and trying to load Python 2.3.6 or 2.4 modules. Make sure any symlinks or references to Python 2.5 are converted.

Other Tips

  • If you get stuck during the install feel free to post here or better yet ask in the community forums for Zenoss.
  • Once everything is up and running you will want to work through the quick start guide on the Zenoss documentation page - obviously skipping the vmware related stuff if you’ve just installed your own copy.
  • If when adding hosts to monitor Zenoss gives you a no snmp found for ip = x.x.x.x you need to make sure an snmp agent is installed and accessible on the host. I found these instructions to be quite helpful to make it work quickly. Skip down to step 19 and remember those instructions are for Ubuntu so you’ll probably be using yast on OpenSuse instead of apt-get.
  • One thing that threw me briefly with the snmpconf tool was the snmpd.conf file it generates is written to whatever directory you ran the tool from. Make sure you cd to /etc/snmp/ before running the tool or copy the generated .conf file to its correct place.
  • After changing your snmp configuration you will want to do a /sbin/service snmpd restart.
  • To monitor windows machines through snmp you will need to enable it as described here.
  • The only other tip I can offer is to think about the monitoring as being done from inside of the device or machine. Through snmp Zenoss will be able to check for actual processes, disk usage, and machine information. My experience with monitoring applications like this was so limited that I had the mindset of wanting to monitor everything through IP ports. This isn’t the way to think about it - though Zenoss allows this as well. By setting up some processes and alerting rules you can have some pretty effective monitoring in place very quickly and you will have more options doing it this way.

    No matter how small your office or how limited your production environments your customers (whether internal or external) should never be able to surprise you with news of down time. Zenoss is a smart, effective tool that can be used to monitor 1 or tons of devices and I have been very pleased with it thus far.

Comments (2)

New Blog Setup

Welcome to the new blog. It hopefully is identical to the last as everything appears to have imported cleanly. I went ahead and hosted the latest from wordpress.org so that I can take more control over the pages, plugins, and themes. This should mean better code formatting in the future.

I am using https://www.nearlyfreespeech.net/ for the host and would highly recommend them for hosting a wordpress blog. It only took about 30 minutes to get everything installed, active, and imported.

A real post about Zenoss Core should be showing up real soon. It is a pretty fantastic tool I recently setup to monitor our servers and devices and I want to share the experience and offer some tips and tricks.

Comments

Flex2 & DWR

In my last post I mentioned in passing some code we wrote to allow DWR calls inside Flex2 code. My goal with this post is to provide a short, clear example of doing this and then describe how we applied that to our project. This doesn’t handle timeouts, call batching, reverse Ajax/Comet, and probably some other DWR features I am forgetting. There are many improvements that could be made to it and it was only used in FireFox. You may want to be familiar with the ExternalInterface of Flash (which hopefully will have its bug fixed soon) and with Javascript and DWR before reading on. I apologize for any faulty formatting - still using vanilla, hosted wordpress and it doesn’t make code especially easy to work with.

There are 3 pieces to making it work:

  • An Html/JS container that your swfs must be run in.
  • An AS3 class that talks to the Html/JS container.
  • A layer of JS to make the DWR calls and route callbacks to AS3.

The Html/JS Container

For the sake of brevity here is a wrapper that will work. For a more robust wrapper take a look at the Html/JS wrappers that FlexBuilder generates and base your custom DWR wrapper files off of those. You need 2 wrappers - one for run mode and one for debug.

DWRContainer.html & DWRContainer-debug.html

<html>
<head>
  <!– Core JS Stuff –>
  <script type=”text/javascript” src=”dwrWrapper.js”>
  </script>
  <script type=”text/javascript” src=”/ajax/engine.js”>
  </script>

  <!– DWR Controllers –>
  <script type=”text/javascript” 
    src=”/ajax/someDWRController.js”></script>
</head>
<body style=”padding: 0px; margin: 0px”>
  <object id=’flexObject’
    classid=”clsid:d27cdb6e-ae6d-11cf-96b8-444553540000″
    codebase=’http://download.macromedia.com/pub/
      shockwave/cabs/flash/swflash.cab#
      version=9,0,0,0′ 
    height=’100%’
    width=’100%’>
  <param name=’src’ value=’MyFlexApp.swf’/>
  <param name=”allowScriptAccess” value=”always”/>
  <embed name=’flexObject’ src=’MyFlexApp.swf’
    allowScriptAccess=’always’ pluginspage=’http://
      www.macromedia.com/shockwave/download/
      index.cgi?P1_Prod_Version=ShockwaveFlash’
    height=’100%’
    width=’100%’/>
  </object>
</body>
</html>

The details to notice are the inclusion of dwrWrapper.js, the DWR engine.js file, and then any number of DWR controllers that you want to use in Flex2. Ideally these DWR controller JS files would have their content injected dynamically but they are small files so I don’t think it is a big issue to include them all and this makes the example simpler. Now to create the debug version just copy the above contents but use MyFlexApp-debug.swf for the src values. Once you have your wrappers built, you will need to tell FlexBuilder to use these when running/debugging. To set this go to the Run menu, click either the Run or Debug option that is expandable, then click Other. In the “Url or path to launch” section you can enter your run and debug wrapper locations.

Now lets look at the JS file.

The Javascript Layer

dwrWrapper.js


/**
 * Receives requests from AS3 for a Dwr call
 * @param wrapUUID The unique identifier for the wrapper
 *   used to make the call
 * @param jsMethodName Name of JS method to call
 * @param jsMethodArguments Arguments for JS method
 * @param asCallbacks The callback object containing AS3
 *   function UUIDs
 */
var dwrCall = function(wrapUUID, jsMethodName,
    jsMethodArguments, asCallbacks){

  var methodToCall = eval(jsMethodName);

  // add callback to arguments
  jsMethodArguments.push(buildDwrCallback(
    wrapUUID, asCallbacks.callback,
    asCallbacks.errorHandler));

  // call the desired JS method
  methodToCall.apply(null, jsMethodArguments);
}

/**
 * Constructs a callback map for a Dwr call.
 * @param wrapUUID The UUID of the AS3 wrapper
 * @param asCallback UUID of the AS3 function to call
 *   upon successful callback
 * @param asErrorHandler The UUID of the AS3 function to
 *   call if there is an error
 * @returns A JS callback object for a DWR call
 */
var buildDwrCallback = function(wrapUUID, asCallback,
    asErrorHandler){
  var jsCallback = handleDwrCallback;
  var jsErrorHandler = handleDwrError;

  var wrappedJsCallback = jsCallback;
  var wrappedJsErrorHandler = jsErrorHandler;

  // wrap the callbacks in functions that call them
  // with dwr response + AS3 callback info
  jsCallback = function(dwrResponse){
    return wrappedJsCallback.call(null, wrapUUID,
      dwrResponse, asCallback);
  }
  jsErrorHandler = function(dwrResponse){
    return wrappedJsErrorHandler.call(null, wrapUUID,
      dwrResponse, asErrorHandler);
  }

  return {callback: jsCallback,
    errorHandler: jsErrorHandler};
}

/**
 * Called when a DWR function returns -
 *   sends response to AS3 code
 * @param wrapUUID UUID for the wrapper
 * @param dwrResponse The actual response received
 *   from the server through Dwr
 * @param asCallback UUID for the AS3 function to call
 */
var handleDwrCallback = function(wrapUUID, dwrResponse,
    asCallback, extraArguments){

  var asCallbackCall = “document['flexObject'].
    handleJsCallback” + wrapUUID.replace(/-/gi, “”) +
    “(dwrResponse, asCallback)”;

  eval(asCallbackCall);
}

/**
 * Called when a DWR call fails -
 *   sends error to AS3 code
 * @param wrapUUID UUID for the wrapper
 * @param dwrResponse The actual error response received
 *   from the server through Dwr
 * @param asErrorHandler UUID for the AS3 function
 */
var handleDwrError = function(wrapUUID, dwrResponse,
    asErrorHandler){

  var asErrorCall = “document['flexObject'].
    handleJsError” + wrapUUID.replace(/-/gi, “”) +
    “(dwrResponse, asErrorHandler)”;

  eval(asErrorCall);
}

Some comments on this JS code:

  • Instead of passing the string names for the AS3 functions to use for callbacks and errors I used UUIDs. On the AS3 side a mapping from UUID to function reference is maintained.
  • I use eval above to call the AS3 functions registered on ExternalInterface because the apply function isn’t available.
  • I assigned each usage of the wrapper a UUID as well that is taken into account when calling back into AS3. By appending these wrapper UUIDs to the callback and errorHandler AS3 functions registered on the ExternalInterface we can ensure the Dwr callbacks are going to the correct AS3 code. Each DWR controller will have a separate wrapper instance on the AS3 side each with its own UUID.
  • In the buildDwrCallback function we are using the technique described here to pass the AS3 callback/errorhandler information through the DWR call such that we can use it in the handleDwrCallback/handleDwrError functions. If the above makes sense (and it will look weird to someone new to JS) you should be able to see how extra arguments could be passed on top of the callback info for the benefit of your AS3 code.
  • I strip the dashes from the wrapper UUIDs because they were causing problems with ExternalInterface and calling the registered AS3 functions. You will notice below that the dashes are stripped to match on the AS3 side of things.

So that covers the Html/JS wrappers for running your Flex2 code against DWR. The last step is the AS3 class that knows how to communicate with this wrapper.

The Actionscript Layer

DwrWrap.as

package someLib
{
  import flash.external.ExternalInterface;
  import mx.collections.ArrayCollection;
  import mx.utils.UIDUtil;

  /**
   * This class provides access to Dwr.  It assumes
   * that flex is being used in an Html container that
   * has access to dwrWrapper.js.
   * Each instance wraps a single Dwr controller.
   */
  public class DwrWrap
    private var functionMap:Array;
    private var wrapUID:String;

    public var controllerName:String;

    /**
     * Constructor for DwrWrap.  Initializes function
     * map and registers ExternalInterface callbacks
     * @param controllerName The name of the
     *   JS/Dwr controller to use
     */
    public function DwrWrap(controllerName:String){
      this.controllerName = controllerName;
      this.functionMap = new Array();
      this.wrapUID = UIDUtil.getUID(this);

      // register AS3 callbacks as name + UUID
      ExternalInterface.addCallback(”handleJsCallback” +
        this.wrapUID.replace(/-/gi, “”),
        handleJsCallback);
      ExternalInterface.addCallback(”handleJsError” +
        this.wrapUID.replace(/-/gi, “”),
        handleJsError);
    }

    /**
     * Method that calls JS using ExternalInterface.
     * @param jsMethodName JS/Dwr function to call
     * @param jsArgs A single argument or an
     *   ArrayCollection of arguments to use in JS call
     * @param callback An untyped object with callback
     *   and errorHandler properties
     */
    public function serverCall(jsMethodName:String,
        jsArgs:*, callback:Object = null):void{

      var jsCallback:Object = new Object();

      // map UUIDs to AS3 callback function references
      var callbackUID:String =
        UIDUtil.getUID(callback.callback);
      jsCallback.callback = callbackUID;
      functionMap[callbackUID] = callback.callback;

      var errorUID:String =
        UIDUtil.getUID(callback.errorHandler);
      jsCallback.errorHandler = errorUID;
      functionMap[errorUID] = callback.errorHandler;

      // make sure jsArgs is an array - assumption in JS
      if (jsArgs is ArrayCollection){
        jsArgs = jsArgs.toArray();
      }
      else if (!(jsArgs is Array)){
        var argArray:Array = new Array();
        argArray.push(jsArgs);
        jsArgs = argArray;
      }
      else if (jsArgs == null){
        jsArgs = new Array();
      }

      ExternalInterface.call(”dwrCall”, this.wrapUID,
        (controllerName + “.” + jsMethodName), jsArgs,
        jsCallback);
    }  

    /**
     * This method receives the callback from JS and
     * then calls the appropriate AS3 callback
     * @param dwrResponse The response from Dwr
     * @param asCallback UUID of AS3 function to call
     */
    public function handleJsCallback(dwrResponse:*,
        asCallback:String):void{

      // use the UUID to look up the function reference
      var callbackFunction:Function =
        functionMap[asCallback];

      if (callbackFunction != null){
        delete functionMap[asCallback];
        callbackFunction(dwrResponse);
      }
    }

    /**
     * This method receives errors from JS and
     * then calls the appropriate AS3 errorHandler
     * @param dwrResponse The error response from Dwr
     * @param asErrorHandler UUID of the AS3 function
     */		
    public function handleJsError(dwrResponse:*,
        asErrorHandler:String):void{

      // use the UUID to look up the function reference
      var errorFunction:Function =
        functionMap[asErrorHandler];

      if (errorFunction != null){
        delete functionMap[asErrorHandler];
        errorFunction(dwrResponse);
      }
    }
  }
}

Some comments about the AS3 code:

  • Notice that by routing all callbacks and errorHandlers through 2 AS3 functions we only have to register those 2 function on the ExternalInterface. These two functions need to be registered for each DWR controller so we append a UUID to the names to ensure uniqueness of function names.
  • AS3 functions to use for DWR callbacks and errors are sent to JS as UUID strings. When we get calls back from the JS we can then look up the actual function references and call them directly. This is nice because it allows the AS3 callback object to contain actual references and not strings.

Finally, you can make DWR calls from AS3 that look like this. Assume you have a DWR controller exposed as someDWRController.js and that this controller has an addNumbers method that takes 2 arguments and returns the sum.

Usage Example

// instantiate a wrapper for the DWR controller
var wrap : DwrWrap = new DwrWrap(”someDWRController”);
// call a function on that controller
wrap.serverCall(”addNumbers”, 5, 6,
  {callback: someASCallback, errorHandler: someASErrorHandler});

// the callback and errorHandler
public function someASCallback(theSum): void{
  Alert.show(”Should be 11: ” + theSum);
}
public function someASErrorHandler(errorInfo): void{
  Alert.show(”Stuff broke”);
}

This should work for making basic DWR calls. In our project Mark cleaned this code up, allowed extra passthrough arguments to be passed to callback functions, injected the DWR Controller JS file content dynamically, and then integrated this logic into our Cairngorm-based application. DWR services were then listed and looked up from a Services.mxml file and executed by delegates like any other service. The callback and errorhandler functions for DWR calls were bound to the result/fault methods of our command objects and upon return the results from Dwr were translated into typed objects. I won’t share that code because I didn’t write it.

Hopefully just this will help someone stuck with Flex2 in an environment where DWR is being used. If you haven’t already read my opinions on Flex2 and you are trying to decide between Flex2 and Html/JS you should.

If you are determined to use Flex2 with DWR you really need to read about the ExternalInterface bug that may force you to refactor your value objects depending on their complexity.

Comments (6)