Developer's Guide

  • Docs Home
  • Community Home

5. Developing a Daemon

5.1. Command-line Options

Each daemon should support:

$ mydaemon start

This should daemon-ize the new daemon, running it forever in the background.

$ mydaemon stop

This should find the collector and stop it with a graceful shutdown.

$ mydaemon run

The new daemon should run for one cycle (if it has a cycle), and should not daemon-ize and log to stderr.

Thankfully most of this infrastructure is taken care of for you. Should you require more command-line options, here's how you should take advantage of the existing code:

from Products.ZenHub.PBDaemon import PBDaemon
class myclass(PBDaemon)

    ...

    def buildOptions(self):
        """Build our list of command-line options
        """
        PBDaemon.buildOptions(self)
        self.parser.add_option( '--newoption',
                dest='dest_var', action="store_true", default=False,
                help="Do something really interesting")

The option formats are as specified in the Python optparse library.

Other features taken care of with the Zenoss daemon infrastructure is reading from configuration files, the --genconf flag (which produces a configuration file populated with all options, comments and default values) as well as the --genxmltable flag (which produces a DocBook XML table showing command-line switches). As other features can be added to the base class, if you follow this recommendation there are more things your daemon gets for free.

Note

The code to allow commands to get command-line option values out of a config file in $ZENHOME/etc/ currently can only set values on lower-case options. Please be aware of this when you create new command-line options.

5.2. Add the Daemon Control Script

The daemons directory should contain a file with the name of your daemon (the one that should appear under the Daemons tab under Settings). This file is an executable shell script which should contain the following:

#! /usr/bin/env bash

. $ZENHOME/bin/zenfunctions

MYPATH=`python -c "import os.path; print os.path.realpath('$0')"`
THISDIR=`dirname $MYPATH`
PRGHOME=`dirname $THISDIR`
PRGNAME=mydaemon.py
CFGFILE=$CFGDIR/mydaemon.conf

generic "$@"

Of course, the PRGNAME and CFGFILE variables don't necessarily need to be contain the same name as the daemon. However, keeping the same name will certainly make things much less confusing.

The mydaemon.py file is assumed to live at the base of the ZenPack.

5.3. Set Up ZenHub Communications

The basics of daemon communications are these.

Procedure 10.1. Daemon to ZenHub Communication Steps

  1. A daemon connects to ZenHub. The raw mechanics of this are handled by the PBDaemon classes so we don't need to explicitly code anything.

  2. The daemon requests specific Services by name from ZenHub. The Services are classes either already known to ZenHub or classes provided in the services directory in a ZenPack and are loaded by ZenHub at runtime.

  3. The daemon calls remote_ methods on the Service objects from ZenHub to receive configuration information or perform other work.

  4. The Services can also call remote_ methods on the daemon to provide updates, etc.

5.3.1. Registering Services with the Hub

The services directory needs to be created at the base directory of your ZenPack. Included in this directory is the __init__.py file. The __init__.py can be empty, but it must exist or any service class files cannot be loaded by zenhub.

zenhub imports Services (a daemon-to-Hub interface class) and the daemons can then use their own Service to perform actions. Look for the example closest to your needs from the $ZENHOME/Products/ZenHub/services/ directory as well as from other ZenPacks (such as HelloWorldZenPack or ZenJMX).

A basic Service class can be found in the Products.ZenHub.HubService.HubService class. More complex daemons doing data collection may want to subclass Products.ZenHub.PerformanceConfig.PerformanceConfig instead to take advantage of some additional infrastructure there.