developer-overview

Path: doc/src/developer-overview
Last Update: Fri Oct 13 15:35:37 +0200 2006

dsadmin Developer Documentation

Architectural Overview

Dsadmin is divided into two main components: admind, the distributed daemon running on the hosts to be administered (the part that does the real work) and webfe, the user friendly web frontend. In addition to that a few commandline utilities are bundled (most notably admindctl, and for testing / debugging purposes admindcmd).

The different parts communicate by sending requests via a custom protocol over TCP/IP connections.

admind (Distributed Daemon)

Master and Slaves

The communication architecture between admind instances is a mix of master/slave and peer-to-peer. All instances can communicate equally with each other, but a designated instance (the "master") has a few special properties:

  • It is the only instance that accepts connections from the "outside", i.e. from something other than admind instances (e.g. the web frontend, cmdline tools etc).
  • It is the only instance with an active internal task scheduler (see class Dsadmin::Admind::Cron).
  • It is the only instance with an active "meta" module (handling requests controlling/querying the admind system itself, see Dsadmin::Admind::MetaController).

Startup and Shutdown

The "main class" or "entry point" for admind is class Dsadmin::Admind::AdminDaemon. It

  • detaches the process (if desired),
  • sets up signal handlers (right now only for SIGTERM and SIGINT),
  • starts all core services (Dsadmin::Admind::ConfigManager, Dsadmin::Logger and (on the master instance) Dsadmin::Admind::Cron),
  • executes the startup requests specified in the configfile (section modules/meta/startup-request, only on master),
  • starts to listen for incoming requests,
  • drops to an unprivileged EUID/EGID and

Shutdown and restart are also initiated here (see Dsadmin::Admind::AdminDaemon#shutdown). Note that for a restart, admind simply exec()s itself after some cleanup, so the process ID as well as stdout / stderr are not affected. On the master instance it is also possible to trigger a system-wide shutdown / restart (i.e. affecting all admind instances).

A note about admin‘s (E)UID: admind has always to be started as UID 0 (root). Immediately after startup (after starting to listen on its — potentially privileged — TCP port to be precise) it drops to EUID/EGID nobody/nogroup, and only changes this for operations that specifically need to be run as different user (see Dsadmin::System#runAs). While admind is running as different user/group, all threading is disabled to prevent accidents.

Controllers and Actions

The actual processing of requests in admind is implemented in so-called "controllers" and their "actions". This is modeled after rails’ ActionController, with Dsadmin::Admind::AdmindController taking on the role of the base class.

webfe (Web Frontend)

rails application, with some custom twists.

Common Facilities

Configuration Files

Most aspects of dsadmin can (and often have to) be customized in a set of configuration files. The document ./files/doc/src/config.html describes the general mechanisms that are and/or can be used in these files, and class Dsadmin::BaseConfigManager has info on how to access them from the code. For info on their content, have a look at the (well-commented) example files in config/.

Please note that all dsadmin components only read their config file(s) once - at program startup. So every configfile change requires a restart of the respective component (admind or webfe). In the case of admind, all instances also have to use the same configfiles (i.e. identical contents). On a properly configured and running system, script/admindctl.rb —sync will take care of both the configfile distribution and the subsequent restart of all instances.

Logging

Class Dsadmin::Logger

Error Handling

In most cases, error handling (recovery from unexpected problems) is done via a rather simple mechanism:

An exception is raised, cascading upwards until either

  1. it reaches a point where the code can nicely recover from it (e.g. by retrying the (macro-) operation) or
  2. it is caught in a catch-all at a central point near the top of the call stack (e.g. in Dsadmin::Admind::RequestProcessor#process).

The first case is obviously the desirable, but less common than the second one.

If an exception is caught by a high-level catch-all, this obviously means something truly unexpected happened, so the system logs it as a "bug" (see Dsadmin::Logger), aborts the current operation and returns an "internal error" response to the client. This means that an error (including bugs in the code) should only affect the current request (never other requests or even the entire system!) and result in an at least halfway graceful aborting of that operation.

The code also makes frequent use of the assertions provided by Dsadmin::Contractor, implementing a simple variant of Design by Contract. Since the assertions simply throw an exception on failure (Test::Unit::AssertionFailedError), this integrates nicely into the core error handling described above.

Core Classes and Modules

Project Database

See ./files/doc/src/projectdb.html

The Test Environment

FIXME: To be written. Until then, look at the following:

  • lib/tasks/unittest.rake
  • test/test_helper.rb
  • test/unit/testrunner.rb
  • config/testenv.xml
  • db/ldap/test_setup.ldif

Major Subsystems

Task Scheduler ("Cron")

All shown classes are in module Dsadmin::Admind.

Mirror Updating

All shown classes are in module Dsadmin::Admind. And there‘s of course also the Mirror and MirrorUpdate classes.

[Validate]