Code Structure

General Code Structure

Copying existing code

Before using an existing piece of code as an example or template, check the age of the code in SVN. There are many places in the code where we have found better and more efficient ways of doing things as time goes on, but we don't always have time to update the old code.

A good example is in the graphing system, please only copy the newer graph definitions which use the graph templates, not the old ones which define the graph manually.

This is especially important in the poller and discovery modules, where the same thing is done in several different ways depending upon when it was written.

If in doubt, ask on the mailing list or IRC to find out what the current recommended way of doing a task is.

RRDtool API

We have created generic functions for rrdtool access. This allows us to globally support specific paths, options, or features such as rrdcached, and easy debugging.

The functions are in includes/rrdtool.inc.php.

The most important functions include rrdtool_create(), rrdtool_update() and rrdtool_get_path()

Database API

We have incorporated and modified dbFacile, which is a "frontend" for MySQL queries with automatic escaping etc. This means we need to be less bothered by SQL injection, and allows us to easily print queries when debugging, without needing code for that anywhere.

Commonly used functions include dbFetchRows(), dbUpdate(), dbInsert() and dbDelete(). In many instances we have further abstracted data retrieval from the database with per-entity build_*_query() functions which will build a query to fetch data from MySQL from a $var array.

dbFetchRows($sql, $parameters = array())
dbFetchCell($sql, $parameters = array())
dbUpdate($data, $table, $where = null, $parameters = array())
dbInsert($data, $table)
dbDelete($table, $where = null, $parameters = array())

dbFacile's parameters array is an array of values substituted into the $where string during processing. A value can be passed to MySQL without escaping by wrapping it in an array(), for example array(array('NULL')) would pass the keyword NULL, rather than the text string 'NULL'.

Here are some example queries :

dbFetchRows('SELECT * FROM ports WHERE device_id = ?', array($device['device_id']));
dbUpdate(array('last_discovered' => array('NULL')), 'devices', '`device_id` = ?', array($device['device_id']));
dbDelete('device_graphs', "`device_id` = ? AND `graph` = ?", array($device['device_id'], $graph));
dbInsert(array('device_id' => $device['device_id'], 'graph' => $graph, 'enabled' => $value), 'device_graphs');
dbFetchCell("SELECT COUNT(*) FROM `ports` WHERE `device_id` = ? AND `ifType` = 'adsl'", array($device['device_id']));

The database API has recently been modified to support PHP's mysqli module alongside the now-deprecated mysql module.

SNMP API

We have created generic functions for SNMP calls. This allows us to transparantly run v1, 2c or 3 queries, do bulkwalk or not, set specific retries or timeouts, depending on the device configuration, and parse the results into different sorts of arrays, without needing to write code for this time and time again.

The functions are in includes/snmp.inc.php.

Configuration

  • includes/defaults.inc.php

This file gets included first, and contains all configuration statements you can change. Do not change this file directly! It'll mess up your upgrades. Add entries you wish to override to config.inc.php instead.

  • config.inc.php

This file starts out small with things you need to configure, and will grow whenever you want to change one of the defaults.inc.php settings away from its default. Note that if something is an array in defaults, just adding a field to it (eg snmp community setting) will not erase the defaults, but just add to them.

  • includes/definitions.inc.php

This file should not be changed unless developing. It contains definitions, settings and some compatibility setting code for deprecated settings. Graph titles, modules to be loaded, etc. are configured here, as are settings (icon, group, description, etc) for device types.

Discovery

Discovery is started from discovery.php in the Observium root directory. It runs discovery modules, which finds various things on configured devices. For some modules, such as IPMI, UNIX Agent, OSPF and others, discovery is done during the polling, as the data is coming in anyway, so there is no point in having separate discovery code.

In a lot of instances actually discovering entities during discovery is an anachronism, as it's more convenient and efficient to add/remove things from the database in the poller because it walks the entire SNMP relevant table regardless and and we're slowly modifying the code to do this, like the ports poller and netscaler pollers.

Module system

The discovery system has an array $config['discovery_modules'] which defines all modules to run during discovery, if the value is 1. You can override this for the entire Observium system in config.inc.php, or per-device in the settings tab. A module needs to be in this array to be able to run.

The key in the array is the filename in includes/discovery/ (followed by .inc.php).

Finding new things

TBD: discover_* functions

Polling

Polling is done through poller.php, which calls poll_device() for every device specified to be polled through the commandline options.

Module system

Similarly to the discovery module system (see above), the poller also has modules which can be enabled or disabled globally and per-device.

Ports

TBD: explain ports poller, also discovered here, etc.

Port type classification

See Interface Description Parsing - which file is included to to this depends on the configuration setting $config['port_descr_parser'].

Agent

After connecting to the agent, the poller receives a block of data, set into different sections indicated by <<xx>> or <<<xxx>>>.

Applications

We parse some section names in includes/polling/unix-agent.inc.php, or when the section name matches app-appname, we include includes/polling/applications/appname.inc.php if it exists.

Sensors

When a sensor is found, discover_sensor() is run, just like through discovery. The sensors are added to $agent_sensors, which is later used to check which sensors can be removed (if they're not in agent_sensors, but they are in the database, they're gone and should be deleted).

IPMI

Similar to the agent, IPMI sensors are discovered at polling time, and are stored in $ipmi_sensors, also to be able to delete sensors that are gone at the end of the polling cycle.

Web interface

The Observium web user interface is primarily constructed from a heavily modified hybrid Bootstrap 2/3 CSS framework. We've modified and extended the original Bootstrap 2 framework to include additional styles and partially merged Bootstrap 3 changes where they were beneficial to us.

Main interface

TBD: top bar, menu, caching queries etc

Authentication

TBD: auth modules usage, link to auth modules page, configuration and consequences

Graphs

TBD: paths, includes, etc. debug

Graph Authentication

TBD