	How to write a Good Handler

-- Write simple handlers.  The goal of a handler is to sync a change to a
CCE object into system configuration files and _possibly_ system state.
Maybe you need to really write yet another tiny tool to do some really hard
stuff.  Maybe your handler should really just do some minimal checks, write
a configuration file, and something else on the system should go, parse the
file, and do the actual operations.
CCE is a great place to get notification of configuration events.  The
filesystem is a great place to store configuration files.  And a separate
program is a great place to actually do complex changes.

-- Don't verify that the entire system is the same way you left it.
If the goal is to add a user to the passwd file, and they already exist,
you succeeded.  If the goal is to set the timeout value on deferred
mail, don't parse sendmail.cf and verify that the whole file is exactly
the same as the CCE object that it refers to.  Verify that sendmail.cf
exists, and that it has some line that refers to delivery timeouts, modify
it, and that's a successful handler.

-- Write multiple handlers if this makes them simpler.

--- If your handler is getting too large and complex, split the handler up
into separate handlers to attach to separate properties.  If your handler
has to modify the same configuration file for each property, obviously this
advice doesn't apply.  But in general, simple handlers attached to a couple
of properties is the best way.

--- Handler stages
---- If you're writing a complex handler that is doing many verification
stages (just to make sure it is possible to add the user.. as I said above,
only verify what you have to), write that as a separate handler, and put it
in the VALIDATE stage.  If it requires parsing a config file twice or three
times to separate it into stages, then don't.  Put it in EXECUTE, or leave
it as unspecified.  Here's the design for the stages.

---- VALIDATE: read-only checks of system files, state, and CCE objects to
verify that the requested operation is permitted.
---- CONFIGURE: modify other CCE objects.
---- EXECUTE: modify the system state.  By the end of the EXECUTE stage,
everything on the system should now have the correct values.
---- TEST: run any testing of the system state that is necessary to verify
that your changes did complete.  Also run programs in "test" mode to check
their configuration files.  eg. postfix check.
---- CLEANUP: remove backup files if success, replace backup files if
rollback.  MUST NOT FAIL.  How is this handler notified of commit/rollback?
Still TBD.  Also TBD is the handler you use for "library commit/rollback"
functionality.  So if in doubt, leave it blank for now.

-- INFO, and WARN.
INFO and WARN are direct lines to the user.  Or at least to the top level
client code.  They are for things that you would (in a normal unix system)
syslog at the LOG_INFO or LOG_WARNING levels.

-- Keep CCE future functionality goals in mind.

We've got big ideas for CCE... if you know what they are, we can find out
if they're not going to work for the code you're writing.  The goal is to
make everything just work... as long as you have nice clean code.

--- Disconnected transactions.  There is going to come a time when your
handler is actually doing slow stuff while the UI has gone on to other
things.  Think about how your handler is operating and the fact that the
system is still running.  Try and do things in an atomic fashion.  (The
library functions will do this)

--- Handlers should be able to handle multiple operations in a single call.
It's possible that quite a few objects/properties have been modified, and
the handler only needs to be called once.  Modifying everything in a single
config file SHOULD only require that you parse and rewrite it only once.
Write the handler such that you can do this right now, or possibly do this
easily in the future.  (This functionality exists right now in CCE.
However it is easiest to see in operation in the devel branch when using
BEGIN/COMMIT)

--- Handlers need to be able to commit/roll-back.  If you use common
library functions, I'm going to make this just work.  But a handler HAS
to open the file itself and feed in a whole bunch of data, or perhaps
delete entire directories.. make sure it does something reasonable if it
successfully completes and then CCE says that it has to go back to the
previous state.  The method and syntax to do the rollback is TBD
But you can still figure out HOW you're going to do it.

--- It may be a future goal to do handler-level-locking.  So handlers
should do a very well defined set of things to the system.  They might need
complicated logic, but they should be very clear in what they actually
MODIFY.  If you can document this at the top, that's even better.  It's
also very desirable to know what the handlers are actually trying to do.
eg: "This handler modifies /etc/http/apache.conf, and /var/packages/"

--- Use the libraries.  Rely on the design to get better.  Don't try and
second-guess it.  PLEASE, CHECK RETURN VALUES FROM FUNCTIONS.  If we change
the editing functions to verify the file, and the verification fails, you
really really shouldn't "succeed" in your failed modification.  Even if you
"know" your modification should always succeed.


-- Use common functions from the libraries available.
Yes, this is the hardest thing to do, and the thing that really needs to be
documented the soonest.  This is very hard to do considering that the
libraries are being rewritten... which depends on having CCE functionality
improved..  And everyone needs to write handlers immediately.

If you need some kind of common functionality, please look in the files in
/usr/sausalito/perl/Sauce, or in cvs in the
sauce-base.mod module, in the src/perl-handler-utils directory.

Here are a couple of APIs that are in common use right now in Qube3.
Obviously they are the most tested, and will be maintained the best.
They are all from Util.pm

--- editfile($filename, $function, @args
This gives you a function that will be called as
$function($inputfd, $outputfd, @args).  The library handles all the
locking/backup semantics of moving the file into place.
It'll return 0 if the operation fails.

--- editblock($filename, $fn, $start_line, $end_line, @args).
This gives you a delimited block of text in a filehandle, passed to a
function in the same way as editfile.  Returns 0 on error.

--- replaceblock($filename, $start_line, $data, $endline, $filemode)
This is slightly faster and easier than editing a block using a function.
This says you know what you want in there, and whatever is there right now
is obsolete.  Returns 0 on error.

With all of these, some future enhancements are possibly things like "did
the sysadmin modify it?" Make sure you check the return code.


