icingaweb2/library/Icinga/Backend/Ido.php

293 lines
9.5 KiB
PHP
Executable File

<?php
/**
* Icinga IDO Backend
*
* @package Icinga\Backend
*/
namespace Icinga\Backend;
/**
* This class provides an easy-to-use interface to the IDO database
*
* You should usually not directly use this class but go through Icinga\Backend.
*
* New MySQL indexes:
* <code>
* CREATE INDEX web2_index ON icinga_scheduleddowntime (object_id, is_in_effect);
* CREATE INDEX web2_index ON icinga_comments (object_id);
* CREATE INDEX web2_index ON icinga_objects (object_id, is_active); -- (not sure yet)
* </code>
*
* Other possible (history-related) indexes, still subject to tests:
* CREATE INDEX web2_index ON icinga_statehistory (object_id, state_time DESC);
* CREATE INDEX web2_index ON icinga_notifications (object_id, instance_id, start_time DESC);
* CREATE INDEX web2_index ON icinga_downtimehistory (object_id, actual_start_time, actual_end_time);
*
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
* @author Icinga-Web Team <info@icinga.org>
* @package Icinga\Application
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
*/
class Ido extends AbstractBackend
{
protected $db;
protected $dbtype;
protected $prefix = 'icinga_';
/**
* Backend initialization starts here
*
* return void
*/
protected function init()
{
$this->connect();
}
/**
* Get our Zend_Db connection
*
* return \Zend_Db_Adapter_Abstract
*/
public function getAdapter()
{
return $this->db;
}
public function getDbType()
{
return $this->dbtype;
}
/**
* Get our IDO table prefix
*
* return string
*/
public function getPrefix()
{
return $this->prefix;
}
// TODO: Move elsewhere. Really? Reasons may be: other backends need IDO
// access, even in environments running state details without IDO
protected function connect()
{
$this->dbtype = $this->config->get('dbtype', 'mysql');
$options = array(
\Zend_Db::AUTO_QUOTE_IDENTIFIERS => false,
\Zend_Db::CASE_FOLDING => \Zend_Db::CASE_LOWER
);
$drv_options = array(
\PDO::ATTR_TIMEOUT => 2,
// TODO: Check whether LC is useful. Zend_Db does fetchNum for Oci:
\PDO::ATTR_CASE => \PDO::CASE_LOWER
// TODO: ATTR_ERRMODE => ERRMODE_EXCEPTION vs ERRMODE_SILENT
);
switch ($this->dbtype) {
case 'mysql':
$adapter = 'Pdo_Mysql';
$drv_options[\PDO::MYSQL_ATTR_INIT_COMMAND] =
"SET SESSION SQL_MODE='STRICT_ALL_TABLES,NO_ZERO_IN_DATE,"
. "NO_ZERO_DATE,NO_ENGINE_SUBSTITUTION';";
// Not using ONLY_FULL_GROUP_BY as of performance impact
// TODO: NO_ZERO_IN_DATE as been added with 5.1.11. Is it
// ignored by other versions?
$port = $this->config->get('port', 3306);
break;
case 'pgsql':
$adapter = 'Pdo_Pgsql';
$port = $this->config->get('port', 5432);
break;
case 'oracle':
$adapter = 'Pdo_Oci';
// $adapter = 'Oracle';
$port = $this->config->get('port', 1521);
// $drv_options[\PDO::ATTR_STRINGIFY_FETCHES] = true;
if ($adapter === 'Oracle') {
putenv('ORACLE_SID=XE');
putenv('ORACLE_HOME=/u01/app/oracle/product/11.2.0/xe');
putenv('PATH=$PATH:$ORACLE_HOME/bin');
putenv('ORACLE_BASE=/u01/app/oracle');
putenv('NLS_LANG=AMERICAN_AMERICA.UTF8');
}
$this->prefix = '';
break;
default:
throw new \Exception(sprintf(
'Backend "%s" is not supported', $type
));
}
$attributes = array(
'host' => $this->config->host,
'port' => $port,
'username' => $this->config->user,
'password' => $this->config->pass,
'dbname' => $this->config->db,
'options' => $options,
'driver_options' => $drv_options
);
if ($this->dbtype === 'oracle') {
$attributes['persistent'] = true;
}
$this->db = \Zend_Db::factory($adapter, $attributes);
if ($adapter === 'Oracle') {
$this->db->setLobAsString(false);
}
$this->db->setFetchMode(\Zend_Db::FETCH_OBJ);
// $this->db->setFetchMode(\Zend_Db::FETCH_ASSOC);
}
/// *** TODO: EVERYTHING BELOW THIS LINE WILL BE MOVED AWAY *** ///
// UGLY temporary host fetch
public function fetchHost($host)
{
$object = \Icinga\Objects\Service::fromBackend(
$this->select()
->from('servicelist')
->where('so.name1 = ?', $host)
->fetchRow()
);
$object->customvars = $this->fetchCustomvars($host);
return $object;
}
// UGLY temporary service fetch
public function fetchService($host, $service)
{
$object = \Icinga\Objects\Service::fromBackend(
$this->select()
->from('servicelist')
->where('so.name1 = ?', $host)
->where('so.name2 = ?', $service)
->fetchRow()
);
$object->customvars = $this->fetchCustomvars($host, $service);
return $object;
}
public function fetchCustomvars($host, $service = null)
{
if ($this->dbtype === 'oracle') return (object) array();
$select = $this->db->select()->from(
array('cv' => $this->prefix . 'customvariablestatus'),
array(
// 'host_name' => 'cvo.name1',
// 'service_description' => 'cvo.name2',
'name' => 'cv.varname',
'value' => 'cv.varvalue',
)
)->join(
array('cvo' => $this->prefix . 'objects'),
'cvo.object_id = cv.object_id',
array()
);
$select->where('name1 = ?', $host);
if ($service === null) {
$select->where('objecttype_id = 1');
} else {
$select->where('objecttype_id = 1');
$select->where('name2 = ?', $service);
}
$select->where('is_active = 1')->order('cv.varname');
return (object) $this->db->fetchPairs($select);
}
// TODO: Move to module!
public function fetchHardStatesForBpHosts($hosts)
{
return $this->fetchStatesForBp($hosts, 'last_hard_state');
}
public function fetchSoftStatesForBpHosts($hosts)
{
return $this->fetchStatesForBp($hosts, 'current_state');
}
public function fetchStatesForBp($hosts, $state_column = 'last_hard_state')
{
$select_hosts = $this->db->select()->from(
array('hs' => $this->prefix . 'hoststatus'),
array(
'state' => 'hs.' . $state_column,
'ack' => 'hs.problem_has_been_acknowledged',
'in_downtime' => 'CASE WHEN (d.object_id IS NULL) THEN 0 ELSE 1 END',
'combined' => 'hs.current_state << 2 + hs.problem_has_been_acknowledged << 1 + CASE WHEN (d.object_id IS NULL) THEN 0 ELSE 1 END'
)
)->joinRight(
array('o' => $this->prefix . 'objects'),
'hs.host_object_id = o.object_id',
array(
'object_id' => 'o.object_id',
'hostname' => 'o.name1',
'service' => '(NULL)'
)
)->joinLeft(
array('d' => $this->prefix . 'scheduleddowntime'),
'o.object_id = d.object_id'
. ' AND d.was_started = 1'
. ' AND d.scheduled_end_time > NOW()'
. ' AND d.actual_start_time < NOW()',
array()
)->where('o.name1 IN (?)', $hosts)
->where('o.objecttype_id = 1')
->where('o.is_active = 1');
$select_services = $this->db->select()->from(
array('ss' => $this->prefix . 'servicestatus'),
array(
'state' => 'ss.' . $state_column,
'ack' => 'ss.problem_has_been_acknowledged',
'in_downtime' => 'CASE WHEN (d.object_id IS NULL) THEN 0 ELSE 1 END',
'combined' => 'ss.current_state << 2 + ss.problem_has_been_acknowledged << 1 + CASE WHEN (d.object_id IS NULL) THEN 0 ELSE 1 END'
)
)->joinRight(
array('o' => $this->prefix . 'objects'),
'ss.service_object_id = o.object_id',
array(
'object_id' => 'o.object_id',
'hostname' => 'o.name1',
'service' => 'o.name2'
)
)->joinLeft(
array('d' => $this->prefix . 'scheduleddowntime'),
'o.object_id = d.object_id'
. ' AND d.was_started = 1'
. ' AND d.scheduled_end_time > NOW()'
. ' AND d.actual_start_time < NOW()',
array()
)->where('o.name1 IN (?)', $hosts)
->where('o.is_active = 1')
->where('o.objecttype_id = 2');
$union = $this->db->select()->union(
array(
'(' . $select_hosts . ')', // ZF-4338 :-(
'(' . $select_services . ')',
),
// At least on MySQL UNION ALL seems to be faster than UNION in
// most situations, as it doesn't care about duplicates
\Zend_Db_Select::SQL_UNION_ALL
)->order('hostname')->order('service');
return $this->db->fetchAll($union);
}
}