2016-04-22 09:55:09 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Icinga\Module\Director\Objects;
|
|
|
|
|
2018-10-08 06:25:38 +02:00
|
|
|
use Icinga\Exception\NotFoundError;
|
2019-09-17 15:06:39 +02:00
|
|
|
use Icinga\Module\Director\Daemon\Logger;
|
2016-04-22 09:55:09 +02:00
|
|
|
use Icinga\Module\Director\Data\Db\DbObjectWithSettings;
|
2018-10-08 06:25:38 +02:00
|
|
|
use Icinga\Module\Director\Db;
|
|
|
|
use Icinga\Module\Director\DirectorObject\Automation\ExportInterface;
|
|
|
|
use Icinga\Module\Director\Exception\DuplicateKeyException;
|
2016-05-25 11:55:18 +02:00
|
|
|
use Icinga\Module\Director\Hook\JobHook;
|
2016-06-16 14:25:35 +02:00
|
|
|
use Exception;
|
2018-10-04 06:52:35 +02:00
|
|
|
use InvalidArgumentException;
|
2016-04-22 09:55:09 +02:00
|
|
|
|
2018-10-08 06:25:38 +02:00
|
|
|
class DirectorJob extends DbObjectWithSettings implements ExportInterface
|
2016-04-22 09:55:09 +02:00
|
|
|
{
|
2017-11-08 15:31:56 +01:00
|
|
|
/** @var JobHook */
|
2016-05-25 11:55:18 +02:00
|
|
|
protected $job;
|
|
|
|
|
2016-04-22 09:55:09 +02:00
|
|
|
protected $table = 'director_job';
|
|
|
|
|
2018-10-08 06:25:38 +02:00
|
|
|
protected $keyName = 'job_name';
|
2016-04-22 09:55:09 +02:00
|
|
|
|
|
|
|
protected $autoincKeyName = 'id';
|
|
|
|
|
2018-10-08 08:37:02 +02:00
|
|
|
protected $protectAutoinc = false;
|
|
|
|
|
2018-05-29 12:34:18 +02:00
|
|
|
protected $defaultProperties = [
|
2016-04-22 09:55:09 +02:00
|
|
|
'id' => null,
|
|
|
|
'job_name' => null,
|
|
|
|
'job_class' => null,
|
|
|
|
'disabled' => null,
|
|
|
|
'run_interval' => null,
|
|
|
|
'last_attempt_succeeded' => null,
|
|
|
|
'ts_last_attempt' => null,
|
|
|
|
'ts_last_error' => null,
|
|
|
|
'last_error_message' => null,
|
2016-05-25 11:55:18 +02:00
|
|
|
'timeperiod_id' => null,
|
2018-05-29 12:34:18 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
protected $stateProperties = [
|
|
|
|
'last_attempt_succeeded',
|
|
|
|
'last_error_message',
|
|
|
|
'ts_last_attempt',
|
|
|
|
'ts_last_error',
|
|
|
|
];
|
2016-04-22 09:55:09 +02:00
|
|
|
|
|
|
|
protected $settingsTable = 'director_job_setting';
|
2016-04-22 14:30:52 +02:00
|
|
|
|
|
|
|
protected $settingsRemoteId = 'job_id';
|
|
|
|
|
2018-10-08 06:25:38 +02:00
|
|
|
public function getUniqueIdentifier()
|
|
|
|
{
|
|
|
|
return $this->get('job_name');
|
|
|
|
}
|
|
|
|
|
2018-05-29 12:34:18 +02:00
|
|
|
/**
|
|
|
|
* @return JobHook
|
|
|
|
*/
|
2016-05-25 11:55:18 +02:00
|
|
|
public function job()
|
|
|
|
{
|
|
|
|
if ($this->job === null) {
|
2018-10-04 06:52:35 +02:00
|
|
|
$class = $this->get('job_class');
|
2016-05-25 11:55:18 +02:00
|
|
|
$this->job = new $class;
|
|
|
|
$this->job->setDb($this->connection);
|
2018-05-29 12:34:18 +02:00
|
|
|
$this->job->setDefinition($this);
|
2016-05-25 11:55:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $this->job;
|
|
|
|
}
|
|
|
|
|
2018-05-29 12:34:18 +02:00
|
|
|
/**
|
2018-10-04 06:52:35 +02:00
|
|
|
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
|
2018-05-29 12:34:18 +02:00
|
|
|
*/
|
2016-05-25 11:55:18 +02:00
|
|
|
public function run()
|
|
|
|
{
|
2018-05-29 12:34:18 +02:00
|
|
|
$job = $this->job();
|
2018-10-04 06:52:35 +02:00
|
|
|
$this->set('ts_last_attempt', date('Y-m-d H:i:s'));
|
2016-06-16 14:25:35 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
$job->run();
|
2018-10-04 06:52:35 +02:00
|
|
|
$this->set('last_attempt_succeeded', 'y');
|
2019-09-17 15:06:39 +02:00
|
|
|
$success = true;
|
2016-06-16 14:25:35 +02:00
|
|
|
} catch (Exception $e) {
|
2019-09-17 15:06:39 +02:00
|
|
|
Logger::error($e->getMessage());
|
2018-10-04 06:52:35 +02:00
|
|
|
$this->set('ts_last_error', date('Y-m-d H:i:s'));
|
|
|
|
$this->set('last_error_message', $e->getMessage());
|
|
|
|
$this->set('last_attempt_succeeded', 'n');
|
2019-09-17 15:06:39 +02:00
|
|
|
$success = false;
|
2016-06-16 14:25:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->hasBeenModified()) {
|
|
|
|
$this->store();
|
|
|
|
}
|
2019-09-17 15:06:39 +02:00
|
|
|
|
|
|
|
return $success;
|
2016-06-16 14:25:35 +02:00
|
|
|
}
|
|
|
|
|
2018-05-29 12:34:18 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2016-06-16 14:25:35 +02:00
|
|
|
public function shouldRun()
|
|
|
|
{
|
|
|
|
return (! $this->hasBeenDisabled()) && $this->isPending();
|
|
|
|
}
|
|
|
|
|
2018-05-29 12:34:18 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2017-11-08 15:31:56 +01:00
|
|
|
public function isOverdue()
|
|
|
|
{
|
|
|
|
if (! $this->shouldRun()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2018-10-04 06:52:35 +02:00
|
|
|
strtotime($this->get('ts_last_attempt')) + $this->get('run_interval') * 2
|
2017-11-08 15:31:56 +01:00
|
|
|
) < time();
|
|
|
|
}
|
|
|
|
|
2016-06-16 14:25:35 +02:00
|
|
|
public function hasBeenDisabled()
|
|
|
|
{
|
2018-10-04 06:52:35 +02:00
|
|
|
return $this->get('disabled') === 'y';
|
2016-05-25 11:55:18 +02:00
|
|
|
}
|
|
|
|
|
2018-05-29 12:34:18 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2016-04-22 14:30:52 +02:00
|
|
|
public function isPending()
|
|
|
|
{
|
2018-10-04 06:52:35 +02:00
|
|
|
if ($this->get('ts_last_attempt') === null) {
|
2016-05-25 11:55:18 +02:00
|
|
|
return $this->isWithinTimeperiod();
|
2016-04-22 14:30:52 +02:00
|
|
|
}
|
|
|
|
|
2018-10-04 06:52:35 +02:00
|
|
|
if (strtotime($this->get('ts_last_attempt')) + $this->get('run_interval') < time()) {
|
2016-05-25 11:55:18 +02:00
|
|
|
return $this->isWithinTimeperiod();
|
2016-04-22 14:30:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-29 12:34:18 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2016-05-25 11:55:18 +02:00
|
|
|
public function isWithinTimeperiod()
|
|
|
|
{
|
|
|
|
if ($this->hasTimeperiod()) {
|
2016-06-17 14:23:03 +02:00
|
|
|
return $this->timeperiod()->isActive();
|
2016-05-25 11:55:18 +02:00
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-22 14:30:52 +02:00
|
|
|
public function lastAttemptSucceeded()
|
|
|
|
{
|
2018-10-04 06:52:35 +02:00
|
|
|
return $this->get('last_attempt_succeeded') === 'y';
|
2016-04-22 14:30:52 +02:00
|
|
|
}
|
2016-05-25 11:55:18 +02:00
|
|
|
|
2019-10-16 14:47:33 +02:00
|
|
|
public function lastAttemptFailed()
|
|
|
|
{
|
|
|
|
return $this->get('last_attempt_succeeded') === 'n';
|
|
|
|
}
|
|
|
|
|
2016-05-25 11:55:18 +02:00
|
|
|
public function hasTimeperiod()
|
|
|
|
{
|
2018-10-04 06:52:35 +02:00
|
|
|
return $this->get('timeperiod_id') !== null;
|
2016-05-25 11:55:18 +02:00
|
|
|
}
|
|
|
|
|
2018-05-29 12:34:18 +02:00
|
|
|
/**
|
|
|
|
* @param $timeperiod
|
|
|
|
* @return $this
|
2018-10-04 06:52:35 +02:00
|
|
|
* @throws \Icinga\Exception\NotFoundError
|
2018-05-29 12:34:18 +02:00
|
|
|
*/
|
|
|
|
public function setTimeperiod($timeperiod)
|
|
|
|
{
|
|
|
|
if (is_string($timeperiod)) {
|
|
|
|
$timeperiod = IcingaTimePeriod::load($timeperiod, $this->connection);
|
|
|
|
} elseif (! $timeperiod instanceof IcingaTimePeriod) {
|
2018-10-04 06:52:35 +02:00
|
|
|
throw new InvalidArgumentException('TimePeriod expected');
|
2018-05-29 12:34:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$this->set('timeperiod_id', $timeperiod->get('id'));
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return object
|
|
|
|
* @throws \Icinga\Exception\NotFoundError
|
|
|
|
*/
|
|
|
|
public function export()
|
|
|
|
{
|
|
|
|
$plain = (object) $this->getProperties();
|
|
|
|
$plain->originalId = $plain->id;
|
|
|
|
unset($plain->id);
|
|
|
|
unset($plain->timeperiod_id);
|
|
|
|
if ($this->hasTimeperiod()) {
|
|
|
|
$plain->timeperiod = $this->timeperiod()->getObjectName();
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->stateProperties as $key) {
|
|
|
|
unset($plain->$key);
|
|
|
|
}
|
|
|
|
$plain->settings = $this->job()->exportSettings();
|
|
|
|
|
|
|
|
return $plain;
|
|
|
|
}
|
|
|
|
|
2018-10-08 06:25:38 +02:00
|
|
|
/**
|
|
|
|
* @param $plain
|
|
|
|
* @param Db $db
|
|
|
|
* @param bool $replace
|
|
|
|
* @return DirectorJob
|
|
|
|
* @throws DuplicateKeyException
|
|
|
|
* @throws NotFoundError
|
|
|
|
*/
|
|
|
|
public static function import($plain, Db $db, $replace = false)
|
|
|
|
{
|
|
|
|
$dummy = new static;
|
|
|
|
$idCol = $dummy->autoincKeyName;
|
|
|
|
$keyCol = $dummy->keyName;
|
|
|
|
$properties = (array) $plain;
|
|
|
|
if (isset($properties['originalId'])) {
|
|
|
|
$id = $properties['originalId'];
|
|
|
|
unset($properties['originalId']);
|
|
|
|
} else {
|
|
|
|
$id = null;
|
|
|
|
}
|
|
|
|
$name = $properties[$keyCol];
|
|
|
|
|
|
|
|
if ($replace && static::existsWithNameAndId($name, $id, $db)) {
|
|
|
|
$object = static::loadWithAutoIncId($id, $db);
|
|
|
|
} elseif ($replace && static::exists($name, $db)) {
|
|
|
|
$object = static::load($name, $db);
|
|
|
|
} elseif (static::exists($name, $db)) {
|
|
|
|
throw new DuplicateKeyException(
|
|
|
|
'Director Job "%s" already exists',
|
|
|
|
$name
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$object = static::create([], $db);
|
|
|
|
}
|
|
|
|
|
|
|
|
$object->setProperties($properties);
|
|
|
|
if ($id !== null) {
|
|
|
|
$object->reallySet($idCol, $id);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $object;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $name
|
|
|
|
* @param int $id
|
|
|
|
* @param Db $connection
|
|
|
|
* @api internal
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected static function existsWithNameAndId($name, $id, Db $connection)
|
|
|
|
{
|
|
|
|
$db = $connection->getDbAdapter();
|
|
|
|
$dummy = new static;
|
|
|
|
$idCol = $dummy->autoincKeyName;
|
|
|
|
$keyCol = $dummy->keyName;
|
|
|
|
|
|
|
|
return (string) $id === (string) $db->fetchOne(
|
|
|
|
$db->select()
|
|
|
|
->from($dummy->table, $idCol)
|
|
|
|
->where("$idCol = ?", $id)
|
|
|
|
->where("$keyCol = ?", $name)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-05-29 12:34:18 +02:00
|
|
|
/**
|
|
|
|
* @return IcingaTimePeriod
|
|
|
|
*/
|
2016-05-25 11:55:18 +02:00
|
|
|
protected function timeperiod()
|
|
|
|
{
|
2019-09-01 14:27:48 +02:00
|
|
|
try {
|
|
|
|
return IcingaTimePeriod::loadWithAutoIncId($this->get('timeperiod_id'), $this->connection);
|
|
|
|
} catch (NotFoundError $e) {
|
|
|
|
throw new \RuntimeException(sprintf(
|
|
|
|
'The TimePeriod configured for Job "%s" could not have been found',
|
|
|
|
$this->get('name')
|
|
|
|
));
|
|
|
|
}
|
2016-05-25 11:55:18 +02:00
|
|
|
}
|
2016-04-22 09:55:09 +02:00
|
|
|
}
|