Each daemon should support:
This should daemon-ize the new daemon, running it forever in the background.
This should find the collector and stop it with a graceful shutdown.
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
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.
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.
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
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.
file is assumed to live at the base of the ZenPack.
The basics of daemon communications are these.
Procedure 10.1. Daemon to ZenHub Communication Steps
A daemon connects to ZenHub. The raw mechanics of this are handled by the
PBDaemonclasses so we don't need to explicitly code anything.
The daemon requests specific Services by name from ZenHub. The Services are classes either already known to ZenHub or classes provided in the
servicesdirectory in a ZenPack and are loaded by ZenHub at runtime.
The daemon calls
remote_methods on the Service objects from ZenHub to receive configuration information or perform other work.
The Services can also call
remote_methods on the daemon to provide updates, etc.
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.