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 ⅔ 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