622 lines
17 KiB
PHP
622 lines
17 KiB
PHP
<?php
|
|
|
|
namespace Icinga\Module\Director\Objects;
|
|
|
|
use Icinga\Data\Db\DbConnection;
|
|
use Icinga\Exception\NotFoundError;
|
|
use Icinga\Module\Director\Data\PropertiesFilter;
|
|
use Icinga\Module\Director\Db;
|
|
use Icinga\Module\Director\DirectorObject\Automation\ExportInterface;
|
|
use Icinga\Module\Director\Exception\DuplicateKeyException;
|
|
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
|
use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1;
|
|
use Icinga\Module\Director\Objects\Extension\FlappingSupport;
|
|
use InvalidArgumentException;
|
|
use RuntimeException;
|
|
|
|
class IcingaHost extends IcingaObject implements ExportInterface
|
|
{
|
|
use FlappingSupport;
|
|
|
|
protected $table = 'icinga_host';
|
|
|
|
protected $defaultProperties = array(
|
|
'id' => null,
|
|
'object_name' => null,
|
|
'object_type' => null,
|
|
'disabled' => 'n',
|
|
'display_name' => null,
|
|
'address' => null,
|
|
'address6' => null,
|
|
'check_command_id' => null,
|
|
'max_check_attempts' => null,
|
|
'check_period_id' => null,
|
|
'check_interval' => null,
|
|
'retry_interval' => null,
|
|
'check_timeout' => null,
|
|
'enable_notifications' => null,
|
|
'enable_active_checks' => null,
|
|
'enable_passive_checks' => null,
|
|
'enable_event_handler' => null,
|
|
'enable_flapping' => null,
|
|
'enable_perfdata' => null,
|
|
'event_command_id' => null,
|
|
'flapping_threshold_high' => null,
|
|
'flapping_threshold_low' => null,
|
|
'volatile' => null,
|
|
'zone_id' => null,
|
|
'command_endpoint_id' => null,
|
|
'notes' => null,
|
|
'notes_url' => null,
|
|
'action_url' => null,
|
|
'icon_image' => null,
|
|
'icon_image_alt' => null,
|
|
'has_agent' => null,
|
|
'master_should_connect' => null,
|
|
'accept_config' => null,
|
|
'api_key' => null,
|
|
'template_choice_id' => null,
|
|
);
|
|
|
|
protected $relations = array(
|
|
'check_command' => 'IcingaCommand',
|
|
'event_command' => 'IcingaCommand',
|
|
'check_period' => 'IcingaTimePeriod',
|
|
'command_endpoint' => 'IcingaEndpoint',
|
|
'zone' => 'IcingaZone',
|
|
'template_choice' => 'IcingaTemplateChoiceHost',
|
|
);
|
|
|
|
protected $booleans = array(
|
|
'enable_notifications' => 'enable_notifications',
|
|
'enable_active_checks' => 'enable_active_checks',
|
|
'enable_passive_checks' => 'enable_passive_checks',
|
|
'enable_event_handler' => 'enable_event_handler',
|
|
'enable_flapping' => 'enable_flapping',
|
|
'enable_perfdata' => 'enable_perfdata',
|
|
'volatile' => 'volatile',
|
|
'has_agent' => 'has_agent',
|
|
'master_should_connect' => 'master_should_connect',
|
|
'accept_config' => 'accept_config',
|
|
);
|
|
|
|
protected $intervalProperties = array(
|
|
'check_interval' => 'check_interval',
|
|
'check_timeout' => 'check_timeout',
|
|
'retry_interval' => 'retry_interval',
|
|
);
|
|
|
|
protected $supportsCustomVars = true;
|
|
|
|
protected $supportsGroups = true;
|
|
|
|
protected $supportsImports = true;
|
|
|
|
protected $supportsFields = true;
|
|
|
|
protected $supportsChoices = true;
|
|
|
|
protected $supportedInLegacy = true;
|
|
|
|
/** @var HostGroupMembershipResolver */
|
|
protected $hostgroupMembershipResolver;
|
|
|
|
public static function enumProperties(
|
|
DbConnection $connection = null,
|
|
$prefix = '',
|
|
$filter = null
|
|
) {
|
|
$hostProperties = array();
|
|
if ($filter === null) {
|
|
$filter = new PropertiesFilter();
|
|
}
|
|
$realProperties = array_merge(['templates'], static::create()->listProperties());
|
|
sort($realProperties);
|
|
|
|
if ($filter->match(PropertiesFilter::$HOST_PROPERTY, 'name')) {
|
|
$hostProperties[$prefix . 'name'] = 'name';
|
|
}
|
|
foreach ($realProperties as $prop) {
|
|
if (!$filter->match(PropertiesFilter::$HOST_PROPERTY, $prop)) {
|
|
continue;
|
|
}
|
|
|
|
if (substr($prop, -3) === '_id') {
|
|
if ($prop === 'template_choice_id') {
|
|
continue;
|
|
}
|
|
$prop = substr($prop, 0, -3);
|
|
}
|
|
|
|
$hostProperties[$prefix . $prop] = $prop;
|
|
}
|
|
|
|
$hostVars = array();
|
|
|
|
if ($connection instanceof Db) {
|
|
foreach ($connection->fetchDistinctHostVars() as $var) {
|
|
if ($filter->match(PropertiesFilter::$CUSTOM_PROPERTY, $var->varname, $var)) {
|
|
if ($var->datatype) {
|
|
$hostVars[$prefix . 'vars.' . $var->varname] = sprintf(
|
|
'%s (%s)',
|
|
$var->varname,
|
|
$var->caption
|
|
);
|
|
} else {
|
|
$hostVars[$prefix . 'vars.' . $var->varname] = $var->varname;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//$properties['vars.*'] = 'Other custom variable';
|
|
ksort($hostVars);
|
|
|
|
|
|
$props = mt('director', 'Host properties');
|
|
$vars = mt('director', 'Custom variables');
|
|
|
|
$properties = array();
|
|
if (!empty($hostProperties)) {
|
|
$properties[$props] = $hostProperties;
|
|
$properties[$props][$prefix . 'groups'] = 'Groups';
|
|
}
|
|
|
|
if (!empty($hostVars)) {
|
|
$properties[$vars] = $hostVars;
|
|
}
|
|
|
|
return $properties;
|
|
}
|
|
|
|
public function getCheckCommand()
|
|
{
|
|
$id = $this->getSingleResolvedProperty('check_command_id');
|
|
return IcingaCommand::loadWithAutoIncId(
|
|
$id,
|
|
$this->getConnection()
|
|
);
|
|
}
|
|
|
|
public function hasCheckCommand()
|
|
{
|
|
return $this->getSingleResolvedProperty('check_command_id') !== null;
|
|
}
|
|
|
|
public function renderToConfig(IcingaConfig $config)
|
|
{
|
|
parent::renderToConfig($config);
|
|
|
|
// TODO: We might alternatively let the whole config fail in case we have
|
|
// used use_agent together with a legacy config
|
|
if (! $config->isLegacy()) {
|
|
$this->renderAgentZoneAndEndpoint($config);
|
|
}
|
|
}
|
|
|
|
public function renderAgentZoneAndEndpoint(IcingaConfig $config = null)
|
|
{
|
|
if (!$this->isObject()) {
|
|
return;
|
|
}
|
|
|
|
if ($this->isDisabled()) {
|
|
return;
|
|
}
|
|
|
|
if ($this->getRenderingZone($config) === self::RESOLVE_ERROR) {
|
|
return;
|
|
}
|
|
|
|
if ($this->getSingleResolvedProperty('has_agent') !== 'y') {
|
|
return;
|
|
}
|
|
|
|
$name = $this->object_name;
|
|
if (IcingaEndpoint::exists($name, $this->connection)) {
|
|
return;
|
|
}
|
|
|
|
$props = array(
|
|
'object_name' => $name,
|
|
'object_type' => 'object',
|
|
'log_duration' => 0
|
|
);
|
|
|
|
if ($this->getSingleResolvedProperty('master_should_connect') === 'y') {
|
|
$props['host'] = $this->getSingleResolvedProperty('address');
|
|
}
|
|
|
|
$props['zone_id'] = $this->getSingleResolvedProperty('zone_id');
|
|
|
|
$endpoint = IcingaEndpoint::create($props, $this->connection);
|
|
|
|
$zone = IcingaZone::create(array(
|
|
'object_name' => $name,
|
|
), $this->connection)->setEndpointList(array($name));
|
|
|
|
if ($props['zone_id']) {
|
|
$zone->parent_id = $props['zone_id'];
|
|
} else {
|
|
$zone->parent = $this->connection->getMasterZoneName();
|
|
}
|
|
|
|
$pre = 'zones.d/' . $this->getRenderingZone($config) . '/';
|
|
$config->configFile($pre . 'agent_endpoints')->addObject($endpoint);
|
|
$config->configFile($pre . 'agent_zones')->addObject($zone);
|
|
}
|
|
|
|
public function getAgentListenPort()
|
|
{
|
|
$conn = $this->connection;
|
|
$name = $this->getObjectName();
|
|
if (IcingaEndpoint::exists($name, $conn)) {
|
|
return IcingaEndpoint::load($name, $conn)->getResolvedPort();
|
|
} else {
|
|
return 5665;
|
|
}
|
|
}
|
|
|
|
public function getUniqueIdentifier()
|
|
{
|
|
if ($this->isTemplate()) {
|
|
return $this->getObjectName();
|
|
} else {
|
|
throw new RuntimeException(
|
|
'getUniqueIdentifier() is supported by Host Templates only'
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return object
|
|
* @throws \Icinga\Exception\NotFoundError
|
|
*/
|
|
public function export()
|
|
{
|
|
// TODO: ksort in toPlainObject?
|
|
$props = (array) $this->toPlainObject();
|
|
$props['fields'] = $this->loadFieldReferences();
|
|
ksort($props);
|
|
|
|
return (object) $props;
|
|
}
|
|
|
|
/**
|
|
* @param $plain
|
|
* @param Db $db
|
|
* @param bool $replace
|
|
* @return IcingaHost
|
|
* @throws DuplicateKeyException
|
|
* @throws \Icinga\Exception\NotFoundError
|
|
*/
|
|
public static function import($plain, Db $db, $replace = false)
|
|
{
|
|
$properties = (array) $plain;
|
|
$name = $properties['object_name'];
|
|
if ($properties['object_type'] !== 'template') {
|
|
throw new InvalidArgumentException(sprintf(
|
|
'Can import only Templates, got "%s" for "%s"',
|
|
$properties['object_type'],
|
|
$name
|
|
));
|
|
}
|
|
$key = $name;
|
|
|
|
if ($replace && static::exists($key, $db)) {
|
|
$object = static::load($key, $db);
|
|
} elseif (static::exists($key, $db)) {
|
|
throw new DuplicateKeyException(
|
|
'Service Template "%s" already exists',
|
|
$name
|
|
);
|
|
} else {
|
|
$object = static::create([], $db);
|
|
}
|
|
|
|
// $object->newFields = $properties['fields'];
|
|
unset($properties['fields']);
|
|
$object->setProperties($properties);
|
|
|
|
return $object;
|
|
}
|
|
|
|
protected function loadFieldReferences()
|
|
{
|
|
$db = $this->getDb();
|
|
|
|
$res = $db->fetchAll(
|
|
$db->select()->from([
|
|
'hf' => 'icinga_host_field'
|
|
], [
|
|
'hf.datafield_id',
|
|
'hf.is_required',
|
|
'hf.var_filter',
|
|
])->join(['df' => 'director_datafield'], 'df.id = hf.datafield_id', [])
|
|
->where('host_id = ?', $this->get('id'))
|
|
->order('varname ASC')
|
|
);
|
|
|
|
if (empty($res)) {
|
|
return [];
|
|
} else {
|
|
foreach ($res as $field) {
|
|
$field->datafield_id = (int) $field->datafield_id;
|
|
}
|
|
return $res;
|
|
}
|
|
}
|
|
|
|
public function beforeDelete()
|
|
{
|
|
foreach ($this->fetchServices() as $service) {
|
|
$service->delete();
|
|
}
|
|
foreach ($this->fetchServiceSets() as $set) {
|
|
$set->delete();
|
|
}
|
|
|
|
parent::beforeDelete();
|
|
}
|
|
|
|
public function hasAnyOverridenServiceVars()
|
|
{
|
|
$varname = $this->getServiceOverrivesVarname();
|
|
return isset($this->vars()->$varname);
|
|
}
|
|
|
|
public function getAllOverriddenServiceVars()
|
|
{
|
|
if ($this->hasAnyOverridenServiceVars()) {
|
|
$varname = $this->getServiceOverrivesVarname();
|
|
return $this->vars()->$varname->getValue();
|
|
} else {
|
|
return (object) array();
|
|
}
|
|
}
|
|
|
|
public function hasOverriddenServiceVars($service)
|
|
{
|
|
$all = $this->getAllOverriddenServiceVars();
|
|
return property_exists($all, $service);
|
|
}
|
|
|
|
public function getOverriddenServiceVars($service)
|
|
{
|
|
if ($this->hasOverriddenServiceVars($service)) {
|
|
$all = $this->getAllOverriddenServiceVars();
|
|
return $all->$service;
|
|
} else {
|
|
return (object) array();
|
|
}
|
|
}
|
|
|
|
public function overrideServiceVars($service, $vars)
|
|
{
|
|
// For PHP < 5.5.0:
|
|
$array = (array) $vars;
|
|
if (empty($array)) {
|
|
return $this->unsetOverriddenServiceVars($service);
|
|
}
|
|
|
|
$all = $this->getAllOverriddenServiceVars();
|
|
$all->$service = $vars;
|
|
$varname = $this->getServiceOverrivesVarname();
|
|
$this->vars()->$varname = $all;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function unsetOverriddenServiceVars($service)
|
|
{
|
|
if ($this->hasOverriddenServiceVars($service)) {
|
|
$all = (array) $this->getAllOverriddenServiceVars();
|
|
unset($all[$service]);
|
|
|
|
$varname = $this->getServiceOverrivesVarname();
|
|
if (empty($all)) {
|
|
unset($this->vars()->$varname);
|
|
} else {
|
|
$this->vars()->$varname = (object) $all;
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
protected function notifyResolvers()
|
|
{
|
|
$resolver = $this->getHostGroupMembershipResolver();
|
|
$resolver->addObject($this);
|
|
$resolver->refreshDb();
|
|
|
|
return $this;
|
|
}
|
|
|
|
protected function getHostGroupMembershipResolver()
|
|
{
|
|
if ($this->hostgroupMembershipResolver === null) {
|
|
$this->hostgroupMembershipResolver = new HostGroupMembershipResolver(
|
|
$this->getConnection()
|
|
);
|
|
}
|
|
|
|
return $this->hostgroupMembershipResolver;
|
|
}
|
|
|
|
public function setHostGroupMembershipResolver(HostGroupMembershipResolver $resolver)
|
|
{
|
|
$this->hostgroupMembershipResolver = $resolver;
|
|
return $this;
|
|
}
|
|
|
|
protected function getServiceOverrivesVarname()
|
|
{
|
|
return $this->connection->settings()->override_services_varname;
|
|
}
|
|
|
|
/**
|
|
* Internal property, will not be rendered
|
|
*
|
|
* Avoid complaints for method names with underscore:
|
|
* @codingStandardsIgnoreStart
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function renderHas_Agent()
|
|
{
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Internal property, will not be rendered
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function renderMaster_should_connect()
|
|
{
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Internal property, will not be rendered
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function renderApi_key()
|
|
{
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Internal property, will not be rendered
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function renderTemplate_choice_id()
|
|
{
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Internal property, will not be rendered
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function renderAccept_config()
|
|
{
|
|
// @codingStandardsIgnoreEnd
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* @codingStandardsIgnoreStart
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function renderLegacyDisplay_Name()
|
|
{
|
|
// @codingStandardsIgnoreEnd
|
|
return c1::renderKeyValue('display_name', $this->display_name);
|
|
}
|
|
|
|
protected function renderLegacyVolatile()
|
|
{
|
|
// not available for hosts in Icinga 1.x
|
|
return;
|
|
}
|
|
|
|
protected function renderLegacyCustomExtensions()
|
|
{
|
|
$str = parent::renderLegacyCustomExtensions();
|
|
|
|
if (($alias = $this->vars()->get('alias')) !== null) {
|
|
$str .= c1::renderKeyValue('alias', $alias->getValue());
|
|
}
|
|
|
|
return $str;
|
|
}
|
|
|
|
/**
|
|
* @return IcingaService[]
|
|
*/
|
|
public function fetchServices()
|
|
{
|
|
$connection = $this->getConnection();
|
|
$db = $connection->getDbAdapter();
|
|
|
|
/** @var IcingaService[] $services */
|
|
$services = IcingaService::loadAll(
|
|
$connection,
|
|
$db->select()->from('icinga_service')
|
|
->where('host_id = ?', $this->get('id'))
|
|
);
|
|
|
|
return $services;
|
|
}
|
|
|
|
/**
|
|
* @return IcingaServiceSet[]
|
|
*/
|
|
public function fetchServiceSets()
|
|
{
|
|
$connection = $this->getConnection();
|
|
$db = $connection->getDbAdapter();
|
|
|
|
/** @var IcingaServiceSet[] $sets */
|
|
$sets = IcingaServiceSet::loadAll(
|
|
$connection,
|
|
$db->select()->from('icinga_service_set')
|
|
->where('host_id = ?', $this->get('id'))
|
|
);
|
|
|
|
return $sets;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function generateApiKey()
|
|
{
|
|
$key = sha1(
|
|
(string) microtime(false)
|
|
. $this->getObjectName()
|
|
. rand(1, 1000000)
|
|
);
|
|
|
|
if ($this->dbHasApiKey($key)) {
|
|
$key = $this->generateApiKey();
|
|
}
|
|
|
|
$this->set('api_key', $key);
|
|
|
|
return $key;
|
|
}
|
|
|
|
protected function dbHasApiKey($key)
|
|
{
|
|
$db = $this->getDb();
|
|
$query = $db->select()->from(
|
|
['o' => $this->getTableName()],
|
|
'o.api_key'
|
|
)->where('api_key = ?', $key);
|
|
|
|
return $db->fetchOne($query) === $key;
|
|
}
|
|
|
|
public static function loadWithApiKey($key, Db $db)
|
|
{
|
|
$query = $db->getDbAdapter()
|
|
->select()
|
|
->from('icinga_host')
|
|
->where('api_key = ?', $key);
|
|
|
|
$result = self::loadAll($db, $query);
|
|
if (count($result) !== 1) {
|
|
throw new NotFoundError('Got invalid API key "%s"', $key);
|
|
}
|
|
|
|
return current($result);
|
|
}
|
|
}
|