diff --git a/library/Icinga/Protocol/Commandpipe/Acknowledgement.php b/library/Icinga/Protocol/Commandpipe/Acknowledgement.php index 56a3be240..9001997fe 100644 --- a/library/Icinga/Protocol/Commandpipe/Acknowledgement.php +++ b/library/Icinga/Protocol/Commandpipe/Acknowledgement.php @@ -32,33 +32,42 @@ use \Icinga\Protocol\Commandpipe\Exception\InvalidCommandException; use \Icinga\Protocol\Commandpipe\Comment; /** - * Class Acknowledgement - * @package Icinga\Protocol\Commandpipe + * Container for a host/service Acknowledgement */ class Acknowledgement implements IComment { /** + * The expire time of this acknowledgement or -1 if no expire time is used + * * @var int */ - public $expireTime = -1; + private $expireTime = -1; /** + * Whether to set the notify flag of the acknowledgment + * * @var bool */ - public $notify = false; + private $notify = false; /** - * @var Comment|null + * The comment text of this acknowledgment + * + * @var Comment */ - public $comment = null; + private $comment; /** + * true if this is a sticky acknowledgment + * * @var bool */ public $sticky; /** - * @param int $time + * Set the expire time of this acknowledgment to $time + * + * @param int $time The new expire time as a UNIX timestamp */ public function setExpireTime($time) { @@ -66,7 +75,9 @@ class Acknowledgement implements IComment } /** - * @param boolean $bool + * Set the notify flag of this object + * + * @param boolean $bool True if notify should be set, otherwise false */ public function setNotify($bool) { @@ -74,10 +85,12 @@ class Acknowledgement implements IComment } /** - * @param Comment $comment - * @param bool $notify - * @param $expire - * @param bool $sticky + * Create a new acknowledgment container + * + * @param Comment $comment The comment to use for the acknowledgement + * @param bool $notify Whether to set the notify flag + * @param int $expire The expire time or -1 of not expiring + * @param bool $sticky Whether to set the sticky flag */ public function __construct(Comment $comment, $notify = false, $expire = -1, $sticky = false) { @@ -88,9 +101,11 @@ class Acknowledgement implements IComment } /** - * @param $type - * @return string - * @throws Exception\InvalidCommandException + * Return the ACKNOWLEDGE_?_PROBLEM string to be used for submitting an external icinga command + * + * @param string $type Either CommandPipe::TYPE_HOST or CommandPipe::TYPE_SERVICE + * @return string The command string to be submitted to the command pipe + * @throws InvalidCommandException */ public function getFormatString($type) { diff --git a/library/Icinga/Protocol/Commandpipe/CommandPipe.php b/library/Icinga/Protocol/Commandpipe/CommandPipe.php index 77aedc83f..2c4392294 100644 --- a/library/Icinga/Protocol/Commandpipe/CommandPipe.php +++ b/library/Icinga/Protocol/Commandpipe/CommandPipe.php @@ -30,141 +30,121 @@ namespace Icinga\Protocol\Commandpipe; use Icinga\Application\Logger as IcingaLogger; +use Icinga\Protocol\Commandpipe\Transport\Transport; +use Icinga\Protocol\Commandpipe\Transport\LocalPipe; +use Icinga\Protocol\Commandpipe\Transport\SecureShell; + /** - * Class CommandPipe - * @package Icinga\Protocol\Commandpipe + * Class to the access icinga CommandPipe via a @see Icinga\Protocol\Commandpipe\Transport.php + * + * Will be configured using the instances.ini */ class CommandPipe { /** - * @var mixed - */ - private $path; - - /** - * @var mixed - */ - private $name; - - /** - * @var bool|mixed - */ - private $user = false; - - /** - * @var bool|mixed - */ - private $host = false; - - /** - * @var int|mixed - */ - private $port = 22; - - /** + * The name of this class as defined in the instances.ini + * * @var string */ - public $fopen_mode = "w"; + private $name = ""; /** + * The underlying @see Icinga\Protocol\Commandpipe\Transport.php class handling communication with icinga * + * @var Icinga\Protocol\Commandpipe\Transport + */ + private $transport = null; + + /** + * Constant identifying a monitoring object as host */ const TYPE_HOST = "HOST"; /** - * + * Constant identifying a monitoring object as service */ const TYPE_SERVICE = "SVC"; /** - * + * Constant identifying a monitoring object as hostgroup */ const TYPE_HOSTGROUP = "HOSTGROUP"; /** - * + * Constant identifying a monitoring object as servicegroups */ const TYPE_SERVICEGROUP = "SERVICEGROUP"; /** + * Notification option (use logical OR for combination) + * + * Broadcast (send notification to all normal and all escalated contacts for the service) + */ + const NOTIFY_BROADCAST = 1; + + /** + * Notification option (use logical OR for combination) + * + * notification is sent out regardless of current time, whether or not notifications are enabled, etc. + */ + const NOTIFY_FORCED = 2; + + /** + * Notification option (use logical OR for combination) + * + * Increment current notification # for the service(this is not done by default for custom notifications) + */ + const NOTIFY_INCREMENT = 4; + + /** + * Create a new CommandPipe class which accesses the icinga.cmd pipe as defined in $config + * * @param \Zend_Config $config */ public function __construct(\Zend_Config $config) { - $this->path = $config->path; + $this->getTransportForConfiguration($config); $this->name = $config->name; + } + + /** + * Setup the @see Icinga\Protocol\Commandpipe\Transport.php class that will be used for accessing the command pipe + * + * Currently this method uses SecureShell when a host is given, otherwise it assumes the pipe is accessible + * via the machines filesystem + * + * @param \Zend_Config $config The configuration as defined in the instances.ini + */ + private function getTransportForConfiguration(\Zend_Config $config) + { if (isset($config->host)) { - $this->host = $config->host; - } - if (isset($config->port)) { - $this->port = $config->port; - } - if (isset($config->user)) { - $this->user = $config->user; + $this->transport = new SecureShell(); + $this->transport->setEndpoint($config); + } else { + $this->transport = new LocalPipe(); + $this->transport->setEndpoint($config); } } /** - * @param $command - * @throws \RuntimeException + * Send the command string $command to the icinga pipe + * + * This method just delegates the send command to the underlying transport + * + * @param String $command The command string to send, without the timestamp */ public function send($command) { - if (!$this->host) { - IcingaLogger::debug( - "Attempting to send external icinga command $command to local command file {$this->path}" - ); - $file = @fopen($this->path, $this->fopen_mode); - if (!$file) { - throw new \RuntimeException("Could not open icinga pipe at $file : " . print_r(error_get_last(), true)); - } - fwrite($file, "[" . time() . "] " . $command . PHP_EOL); - IcingaLogger::debug('Writing [' . time() . '] ' . $command . PHP_EOL); - fclose($file); - } else { - // send over ssh - $retCode = 0; - $output = array(); - IcingaLogger::debug( - 'Icinga instance is on different host, attempting to send command %s via ssh to %s:%s/%s', - $command, - $this->host, - $this->port, - $this->path - ); - $hostConnector = $this->user ? $this->user . "@" . $this->host : $this->host; - exec( - "ssh $hostConnector -p{$this->port} \"echo '[" . time() . "] " - . escapeshellcmd( - $command - ) - . "' > {$this->path}\"", - $output, - $retCode - ); - IcingaLogger::debug( - "$:ssh $hostConnector -p{$this->port} \"echo '[" . time() . "] " . escapeshellcmd( - $command - ) . "' > {$this->path}\"" - ); - IcingaLogger::debug("Code code %s: %s ", $retCode, $output); - - if ($retCode != 0) { - throw new \RuntimeException( - 'Could not send command to remote icinga host: ' - . implode( - "\n", - $output - ) - . " (returncode $retCode)" - ); - } - } + $this->transport->send($command); } /** - * @param $objects - * @param IComment $acknowledgementOrComment + * Acknowledge a set of monitoring objects + * + * $objects can be a mixed array of host and service objects + * + * @param array $objects An array of host and service objects + * @param IComment $acknowledgementOrComment An acknowledgement or comment object to use as the comment */ public function acknowledge($objects, IComment $acknowledgementOrComment) { @@ -184,7 +164,9 @@ class CommandPipe } /** - * @param $objects + * Remove the acknowledgements of the provided objects + * + * @param array $objects An array of mixed service and host objects whose acknowledgments will be removed */ public function removeAcknowledge($objects) { @@ -198,15 +180,23 @@ class CommandPipe } /** - * @param $objects - * @param $state - * @param $output + * Submit passive check result for all provided objects + * + * @param array $objects An array of hosts and services to submit the passive check result to + * @param int $state The state to set for the monitoring objects + * @param string $output The output string to set as the check result + * @param string $perfdata The optional perfdata to submit as the check result */ - public function submitCheckResult($objects, $state, $output) + public function submitCheckResult($objects, $state, $output, $perfdata = "") { + if ($perfdata) { + $output = $output."|".$perfdata; + } foreach ($objects as $object) { if (isset($object->service_description)) { - $this->send("PROCESS_SVC_CHECK_RESULT;$object->host_name;$object->service_description;$state;$output"); + $this->send( + "PROCESS_SERVICE_CHECK_RESULT;$object->host_name;$object->service_description;$state;$output" + ); } else { $this->send("PROCESS_HOST_CHECK_RESULT;$object->host_name;$state;$output"); } @@ -214,9 +204,11 @@ class CommandPipe } /** - * @param $objects - * @param bool $time - * @param bool $withChilds + * Reschedule a forced check for all provided objects + * + * @param array $objects An array of hosts and services to reschedule + * @param int|bool $time The time to submit, if empty time() will be used + * @param bool $withChilds Whether only childs should be rescheduled */ public function scheduleForcedCheck($objects, $time = false, $withChilds = false) { @@ -234,9 +226,11 @@ class CommandPipe } /** - * @param $objects - * @param bool $time - * @param bool $withChilds + * Reschedule a check for all provided objects + * + * @param array $objects An array of hosts and services to reschedule + * @param int|bool $time The time to submit, if empty time() will be used + * @param bool $withChilds Whether only childs should be rescheduled */ public function scheduleCheck($objects, $time = false, $withChilds = false) { @@ -254,8 +248,10 @@ class CommandPipe } /** - * @param array $objects - * @param Comment $comment + * Add a comment to all submitted objects + * + * @param array $objects An array of hosts and services to add a comment for + * @param Comment $comment The comment object to add */ public function addComment(array $objects, Comment $comment) { @@ -272,7 +268,10 @@ class CommandPipe } /** - * @param $objectsOrComments + * Removes the submitted comments + * + * @param array $objectsOrComments An array of hosts and services (to remove all their comments) + * or single comment objects to remove */ public function removeComment($objectsOrComments) { @@ -300,6 +299,7 @@ class CommandPipe } /** + * Globally enable notifications for this instance * */ public function enableGlobalNotifications() @@ -308,6 +308,7 @@ class CommandPipe } /** + * Globally disable notifications for this instance * */ public function disableGlobalNotifications() @@ -316,8 +317,10 @@ class CommandPipe } /** - * @param $object - * @return string + * Return the object type of the provided object (TYPE_SERVICE or TYPE_HOST) + * + * @param $object The object to identify + * @return string TYPE_SERVICE or TYPE_HOST */ private function getObjectType($object) { @@ -329,8 +332,10 @@ class CommandPipe } /** - * @param $objects - * @param Downtime $downtime + * Schedule a downtime for all provided objects + * + * @param array $objects An array of monitoring objects to schedule the downtime for + * @param Downtime $downtime The downtime object to schedule */ public function scheduleDowntime($objects, Downtime $downtime) { @@ -347,8 +352,10 @@ class CommandPipe } /** - * @param $objects - * @param int $starttime + * Remove downtimes for objects + * + * @param array $objects An array containing hosts, service or downtime objects + * @param int $starttime An optional starttime to use for the DEL_DOWNTIME_BY_HOST_NAME command */ public function removeDowntime($objects, $starttime = 0) { @@ -370,6 +377,7 @@ class CommandPipe } /** + * Restart the icinga instance * */ public function restartIcinga() @@ -378,8 +386,10 @@ class CommandPipe } /** - * @param $objects - * @param PropertyModifier $flags + * Modify monitoring flags for the provided objects + * + * @param array $objects An arry of service and/or host objects to modify + * @param PropertyModifier $flags The Monitoring attributes to modify */ public function setMonitoringProperties($objects, PropertyModifier $flags) { @@ -396,7 +406,9 @@ class CommandPipe } /** - * @param $objects + * Enable active checks for all provided objects + * + * @param array $objects An array containing services and hosts to enable active checks for */ public function enableActiveChecks($objects) { @@ -411,11 +423,13 @@ class CommandPipe } /** - * @param $objects + * Disable active checks for all provided objects + * + * @param array $objects An array containing services and hosts to disable active checks */ public function disableActiveChecks($objects) { - $this->modifyMonitoringProperties( + $this->setMonitoringProperties( $objects, new PropertyModifier( array( @@ -426,7 +440,9 @@ class CommandPipe } /** - * @param $objects + * Enable passive checks for all provided objects + * + * @param array $objects An array containing services and hosts to enable passive checks for */ public function enablePassiveChecks($objects) { @@ -441,11 +457,13 @@ class CommandPipe } /** - * @param $objects + * Enable passive checks for all provided objects + * + * @param array $objects An array containing services and hosts to enable passive checks for */ public function disablePassiveChecks($objects) { - $this->modifyMonitoringProperties( + $this->setMonitoringProperties( $objects, new PropertyModifier( array( @@ -456,7 +474,10 @@ class CommandPipe } /** - * @param $objects + * Enable flap detection for all provided objects + * + * @param array $objects An array containing services and hosts to enable flap detection + * */ public function enableFlappingDetection($objects) { @@ -471,7 +492,10 @@ class CommandPipe } /** - * @param $objects + * Disable flap detection for all provided objects + * + * @param array $objects An array containing services and hosts to disable flap detection + * */ public function disableFlappingDetection($objects) { @@ -486,7 +510,10 @@ class CommandPipe } /** - * @param $objects + * Enable notifications for all provided objects + * + * @param array $objects An array containing services and hosts to enable notification + * */ public function enableNotifications($objects) { @@ -501,7 +528,10 @@ class CommandPipe } /** - * @param $objects + * Disable flap detection for all provided objects + * + * @param array $objects An array containing services and hosts to disable notifications + * */ public function disableNotifications($objects) { @@ -516,7 +546,9 @@ class CommandPipe } /** - * @param $objects + * Enable freshness checks for all provided objects + * + * @param array $objects An array of hosts and/or services */ public function enableFreshnessChecks($objects) { @@ -531,7 +563,9 @@ class CommandPipe } /** - * @param $objects + * Disable freshness checks for all provided objects + * + * @param array $objects An array of hosts and/or services */ public function disableFreshnessChecks($objects) { @@ -546,7 +580,9 @@ class CommandPipe } /** - * @param $objects + * Enable event handler for all provided objects + * + * @param array $objects An array of hosts and/or services */ public function enableEventHandler($objects) { @@ -561,7 +597,9 @@ class CommandPipe } /** - * @param $objects + * Disable event handler for all provided objects + * + * @param array $objects An array of hosts and/or services */ public function disableEventHandler($objects) { @@ -576,7 +614,9 @@ class CommandPipe } /** - * @param $objects + * Enable performance data parsing for all provided objects + * + * @param array $objects An array of hosts and/or services */ public function enablePerfdata($objects) { @@ -590,6 +630,11 @@ class CommandPipe ); } + /** + * Disable performance data parsing for all provided objects + * + * @param array $objects An array of hosts and/or services + */ public function disablePerfdata($objects) { $this->setMonitoringProperties( @@ -601,4 +646,202 @@ class CommandPipe ) ); } + + /** + * Start obsessing over provided services/hosts + * + * @param array $objects An array of hosts and/or services + */ + public function startObsessing($objects) + { + foreach ($objects as $object) { + $type = $this->getObjectType($object); + $msg = "START_OBSESSING_OVER_". (($type == self::TYPE_SERVICE) ? 'SVC' : 'HOST'); + $msg .= ';'.$object->host_name; + if ($type == self::TYPE_SERVICE) { + $msg .= ';'.$object->service_description; + } + $this->send($msg); + } + } + + /** + * Stop obsessing over provided services/hosts + * + * @param array $objects An array of hosts and/or services + */ + public function stopObsessing($objects) + { + foreach ($objects as $object) { + $type = $this->getObjectType($object); + $msg = "STOP_OBSESSING_OVER_". (($type == self::TYPE_SERVICE) ? 'SVC' : 'HOST'); + $msg .= ';'.$object->host_name; + if ($type == self::TYPE_SERVICE) { + $msg .= ';'.$object->service_description; + } + $this->send($msg); + } + } + + /** + * Start obsessing over provided services/hosts + * + * @param array $objects An array of hosts and/or services + */ + public function startObsessing($objects) + { + foreach ($objects as $object) { + $type = $this->getObjectType($object); + $msg = "START_OBSESSING_OVER_". (($type == self::TYPE_SERVICE) ? 'SVC' : 'HOST'); + $msg .= ';'.$object->host_name; + if ($type == self::TYPE_SERVICE) { + $msg .= ';'.$object->service_description; + } + $this->send($msg); + } + } + + /** + * Stop obsessing over provided services/hosts + * + * @param array $objects An array of hosts and/or services + */ + public function stopObsessing($objects) + { + foreach ($objects as $object) { + $type = $this->getObjectType($object); + $msg = "STOP_OBSESSING_OVER_". (($type == self::TYPE_SERVICE) ? 'SVC' : 'HOST'); + $msg .= ';'.$object->host_name; + if ($type == self::TYPE_SERVICE) { + $msg .= ';'.$object->service_description; + } + $this->send($msg); + } + } + + /** + * Send a custom host or service notification + * + * @param $objects monitoring objects to send this notification to + * @param Comment $comment comment to use in the notification + * @param int [$...] Optional list of Notification flags which will be used as the option parameter + */ + public function sendCustomNotification($objects, Comment $comment, $optionsVarList = 0/*, ...*/) + { + $args = func_get_args(); + // logical OR for all notification options + for ($i = 3; $i < count($args); $i++) { + $optionsVarList |= $args[$i]; + } + + foreach ($objects as $object) { + $type = $this->getObjectType($object); + $msg = 'SEND_CUSTOM_'.(($type == self::TYPE_SERVICE) ? 'SVC' : 'HOST' ).'_NOTIFICATION'; + $msg .= ';'.$object->host_name; + if ($type == self::TYPE_SERVICE) { + $msg .= ';'.$object->service_description; + } + $msg .= ';'.$optionsVarList; + $msg .= ';'.$comment->author; + $msg .= ';'.$comment->comment; + $this->send($msg); + } + } + + /** + * Disable notifications for all services of the provided hosts + * + * @param array $objects An array of hosts + */ + public function disableNotificationsForServices($objects) + { + foreach ($objects as $host) { + $msg = 'DISABLE_HOST_SVC_NOTIFICATIONS;'.$host->host_name; + $this->send($msg); + } + } + + /** + * Enable notifications for all services of the provided hosts + * + * @param array $objects An array of hosts + */ + public function enableNotificationsForServices($objects) + { + foreach ($objects as $host) { + $msg = 'ENABLE_HOST_SVC_NOTIFICATIONS;'.$host->host_name; + $this->send($msg); + } + } + + /** + * Disable active checks for all services of the provided hosts + * + * @param array $objects An array of hosts + */ + public function disableActiveChecksWithChildren($objects) + { + foreach ($objects as $host) { + $msg = 'DISABLE_HOST_SVC_CHECKS;'.$host->host_name; + $this->send($msg); + } + } + + /** + * Enable active checks for all services of the provided hosts + * + * @param array $objects An array of hosts + */ + public function enableActiveChecksWithChildren($objects) + { + foreach ($objects as $host) { + $msg = 'ENABLE_HOST_SVC_CHECKS;'.$host->host_name; + $this->send($msg); + } + } + + /** + * Reset modified attributes for all provided objects + * + * @param array $objects An array of hosts and services + */ + public function resetAttributes($objects) + { + foreach ($objects as $object) { + $type = $this->getObjectType($object); + if ($type === self::TYPE_SERVICE) { + $this->send('CHANGE_SVC_MODATTR;'.$object->host_name.';'.$object->service_description.';0'); + } else { + $this->send('CHANGE_HOST_MODATTR;'.$object->host_name.';0'); + } + } + } + + /** + * Delay notifications for all provided hosts and services for $time seconds + * + * @param array $objects An array of hosts and services + * @param int $time The number of seconds to delay notifications for + */ + public function delayNotification($objects, $time) + { + foreach ($objects as $object) { + $type = $this->getObjectType($object); + if ($type === self::TYPE_SERVICE) { + $this->send('DELAY_SVC_NOTIFICATION;'.$object->host_name.';'.$object->service_description.';'.$time); + } else { + $this->send('DELAY_HOST_NOTIFICATION;'.$object->host_name.';'.$time); + } + } + } + + /** + * Return the transport handler that handles actual sending of commands + * + * @return Transport + */ + public function getTransport() + { + return $this->transport; + } } diff --git a/library/Icinga/Protocol/Commandpipe/Comment.php b/library/Icinga/Protocol/Commandpipe/Comment.php index bc9b71302..4b4103d74 100644 --- a/library/Icinga/Protocol/Commandpipe/Comment.php +++ b/library/Icinga/Protocol/Commandpipe/Comment.php @@ -29,30 +29,38 @@ namespace Icinga\Protocol\Commandpipe; /** - * Class Comment - * @package Icinga\Protocol\Commandpipe + * Container for comment information that can be send to icinga's external command pipe + * */ class Comment implements IComment { /** + * Whether the persistent flag should be submitted with this command + * * @var bool */ public $persistent = false; /** - * @var string + * The author of this comment + * + * @var string */ public $author = ""; /** - * @var string + * The comment text to use + * + * @var string */ public $comment = ""; /** - * @param $author - * @param $comment - * @param bool $persistent + * Create a new comment object + * + * @param string $author The author name to use for this object + * @param string $comment The comment text to use + * @param bool $persistent Whether this comment should persist icinga restarts */ public function __construct($author, $comment, $persistent = false) { @@ -62,9 +70,13 @@ class Comment implements IComment } /** - * @param $type - * @return string - * @throws InvalidCommandException + * Return this comment as an ADD_?_COMMENT external command string that can directly be send to the command pipe + * + * @param string $type either CommandPipe::TYPE_HOST or CommandPipe::TYPE_SERVICE + * + * @return string The ADD_HOST_COMMENT or ADD_SVC_COMMENT command, without the timestamp + * + * @throws InvalidCommandException When $type is unknown */ public function getFormatString($type) { diff --git a/library/Icinga/Protocol/Commandpipe/Downtime.php b/library/Icinga/Protocol/Commandpipe/Downtime.php index 0e05995b9..3e5f7a3d7 100644 --- a/library/Icinga/Protocol/Commandpipe/Downtime.php +++ b/library/Icinga/Protocol/Commandpipe/Downtime.php @@ -29,63 +29,135 @@ namespace Icinga\Protocol\Commandpipe; /** - * Class Downtime - * @package Icinga\Protocol\Commandpipe + * Container class containing downtime information + * */ class Downtime { /** - * @var mixed + * Propagate this downtime for all child objects + */ + const TYPE_WITH_CHILDREN = 'AND_PROPAGATE_'; + + /** + * Propagate this downtime for all child objects as triggered downtime + */ + const TYPE_WITH_CHILDREN_TRIGGERED = 'AND_PROPAGATE_TRIGGERED_'; + + /** + * Schedule downtime for the services of the given hos + */ + const TYPE_HOST_SVC = 'HOST_SVC'; + + /** + * Timestamp representing the downtime's start + * + * @var int */ public $startTime; /** - * @var mixed + * Timestamp representing the downtime's end + * + * @var int */ public $endTime; /** - * @var mixed + * Whether this is a fixed downtime + * + * @var boolean */ private $fixed = false; /** - * @var mixed + * The duration of the downtime in seconds if flexible + * + * @var int */ public $duration; /** - * @var mixed + * The comment object of the downtime + * + * @var Comment */ public $comment; /** - * @param $start - * @param $end - * @param Comment $comment - * @param int $duration + * The downtime id that triggers this downtime (0 = no triggered downtime) + * + * @var int */ - public function __construct($start, $end, Comment $comment, $duration = 0) + public $trigger_id = 0; + + /** + * Internal information for the exact type of the downtime + * + * E.g. with children, with children and triggered, services etc. + * + * @var string + */ + private $subtype = ''; + + /** + * Create a new downtime container + * + * @param int $start A timestamp that defines the downtime's start time + * @param int $end A timestamp that defines the downtime's end time + * @param Comment $comment A comment that will be used when scheduling the downtime + * @param int $duration The duration of this downtime in seconds. + * Duration > 0 will make this a flexible downtime + * @param int $trigger_id An id of the downtime that triggers this downtime. + * 0 means this is not a triggered downtime + */ + public function __construct($start, $end, Comment $comment, $duration = 0, $trigger_id = 0) { $this->startTime = $start; $this->endTime = $end; $this->comment = $comment; - if ($duration != 0) { + if ($duration == 0) { $this->fixed = true; } $this->duration = intval($duration); + $this->trigger_id = intval($trigger_id); } /** - * @param $type - * @return string + * Return the SCHEDULE_?_DOWNTIME representing this class for the given $type + * + * @param string $type CommandPipe::TYPE_SERVICE to trigger a service downtime or CommandPipe::TYPE_HOST to + * trigger a host downtime + * @return string A schedule downtime command representing the state of this class + * */ public function getFormatString($type) { - return 'SCHEDULE_' . $type . '_DOWNTIME;%s' - . ($type == CommandPipe::TYPE_SERVICE ? ';%s;' : ';') - . $this->startTime . ';' . $this->endTime - . ';' . ($this->fixed ? '1' : '0') . ';' . $this->duration . ';0;' - . $this->comment->author . ';' . $this->comment->comment; + if ($this->subtype == self::TYPE_HOST_SVC) { + $type = ""; + } + return 'SCHEDULE_' + . $this->subtype + . $type + . '_DOWNTIME;' + . '%s;' + . ($type == CommandPipe::TYPE_SERVICE ? '%s;' : '') + . $this->startTime . ';' + . $this->endTime . ';' + . ($this->fixed ? '1' : '0') . ';' + . $this->trigger_id . ';' + . $this->duration . ';' + . $this->comment->author . ';' + . $this->comment->comment; + } + + /** + * Set the exact type of this downtime (see the TYPE_ constants) + * + * @param $type The type of to use for this downtime + */ + public function setType($type) + { + $this->subtype = $type; } } diff --git a/library/Icinga/Protocol/Commandpipe/Exception/InvalidCommandException.php b/library/Icinga/Protocol/Commandpipe/Exception/InvalidCommandException.php index 8e4ec241d..8bf8cc4cd 100644 --- a/library/Icinga/Protocol/Commandpipe/Exception/InvalidCommandException.php +++ b/library/Icinga/Protocol/Commandpipe/Exception/InvalidCommandException.php @@ -29,8 +29,7 @@ namespace Icinga\Protocol\Commandpipe\Exception; /** - * Class InvalidCommandException - * @package Icinga\Protocol\Commandpipe\Exception + * Exception class for unknown/invalid external commands */ class InvalidCommandException extends \Exception { diff --git a/library/Icinga/Protocol/Commandpipe/IComment.php b/library/Icinga/Protocol/Commandpipe/IComment.php index 9e5e2a81c..a374a4ff2 100644 --- a/library/Icinga/Protocol/Commandpipe/IComment.php +++ b/library/Icinga/Protocol/Commandpipe/IComment.php @@ -29,8 +29,8 @@ namespace Icinga\Protocol\Commandpipe; /** - * Class IComment - * @package Icinga\Protocol\Commandpipe + * Interface flagging a class as being a comment + * */ interface IComment { diff --git a/library/Icinga/Protocol/Commandpipe/PropertyModifier.php b/library/Icinga/Protocol/Commandpipe/PropertyModifier.php index b45b0cb57..8dd72a1a0 100644 --- a/library/Icinga/Protocol/Commandpipe/PropertyModifier.php +++ b/library/Icinga/Protocol/Commandpipe/PropertyModifier.php @@ -29,60 +29,62 @@ namespace Icinga\Protocol\Commandpipe; /** - * Class PropertyModifier - * @package Icinga\Protocol\Commandpipe + * Container class to modify a few monitoring attributes at oncee + * */ class PropertyModifier { /** - * + * Set an attribute to be enabled in the command */ const STATE_ENABLE = 1; /** - * + * Set an attribute to be disabled in the command */ const STATE_DISABLE = 0; /** - * + * Set an attribute to not be modified in the command */ const STATE_KEEP = -1; /** - * + * Template for enabling/disabling flap detection */ const FLAPPING = "%s_FLAP_DETECTION"; /** - * + * Template for enabling/disabling active checks */ const ACTIVE = "%s_CHECK"; /** - * + * Template for enabling/disabling passive checks */ const PASSIVE = "PASSIVE_%s_CHECKS"; /** - * + * Template for enabling/disabling notification */ const NOTIFICATIONS = "%s_NOTIFICATIONS"; /** - * + * Template for enabling/disabling freshness checks */ const FRESHNESS = "%s_FRESHNESS_CHECKS"; /** - * + * Template for enabling/disabling event handler */ const EVENTHANDLER = "%s_EVENT_HANDLER"; /** + * The state that will be applied when fetching this container for an object + * * @var array */ - public $flags = array( + private $flags = array( self::FLAPPING => self::STATE_KEEP, self::ACTIVE => self::STATE_KEEP, self::PASSIVE => self::STATE_KEEP, @@ -92,7 +94,9 @@ class PropertyModifier ); /** - * @param array $flags + * Create a new PropertyModified object using the given flags + * + * @param array $flags Flags to enable/disable/keep different monitoring attributes */ public function __construct(array $flags) { @@ -104,8 +108,10 @@ class PropertyModifier } /** - * @param $type - * @return array + * Return this object as a template for the given object type + * + * @param $type Either CommandPipe::TYPE_HOST or CommandPipe::TYPE_SERVICE + * @return array An array of external command templates for the given type representing the containers state */ public function getFormatString($type) { diff --git a/library/Icinga/Protocol/Commandpipe/Transport/LocalPipe.php b/library/Icinga/Protocol/Commandpipe/Transport/LocalPipe.php new file mode 100644 index 000000000..79b297f11 --- /dev/null +++ b/library/Icinga/Protocol/Commandpipe/Transport/LocalPipe.php @@ -0,0 +1,84 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Protocol\Commandpipe\Transport; + +use Icinga\Application\Logger; + +/** + * CommandPipe Transport class that writes to a file accessible by the filesystem + */ +class LocalPipe implements Transport +{ + /** + * The path of the icinga commandpipe + * + * @var String + */ + private $path; + + /** + * The mode to use for fopen() + * + * @var string + */ + private $openMode = "w"; + + /** + * @see Transport::setEndpoint() + */ + public function setEndpoint(\Zend_Config $config) + { + $this->path = isset($config->path) ? $config->path : '/usr/local/icinga/var/rw/icinga.cmd'; + } + + /** + * @see Transport::send() + */ + public function send($message) + { + Logger::debug('Attempting to send external icinga command %s to local command file ', $message, $this->path); + $file = @fopen($this->path, $this->openMode); + if (!$file) { + throw new \RuntimeException('Could not open icinga pipe at $file : ' . print_r(error_get_last(), true)); + } + fwrite($file, '[' . time() . '] ' . $message . PHP_EOL); + Logger::debug('Writing [' . time() . '] ' . $message . PHP_EOL); + fclose($file); + } + + /** + * Overwrite the open mode (useful for testing) + * + * @param string $mode A open mode supported by fopen() + */ + public function setOpenMode($mode) + { + $this->openMode = $mode; + } +} diff --git a/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php b/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php new file mode 100644 index 000000000..272b4b34d --- /dev/null +++ b/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php @@ -0,0 +1,124 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Protocol\Commandpipe\Transport; + +use Icinga\Application\Logger; + +/** + * Command pipe transport class that uses ssh for connecting to a remote filesystem with the icinga.cmd pipe + * The remote host must have KeyAuth enabled for this user + * + */ +class SecureShell implements Transport +{ + /** + * The remote host to connect to + * + * @var string + */ + private $host = 'localhost'; + + /** + * The location of the icinga pipe on the remote host + * + * @var string + */ + private $path = "/usr/local/icinga/var/rw/icinga.cmd"; + + /** + * The SSH port of the remote host + * + * @var int + */ + private $port = 22; + + /** + * The user to authenticate with on the remote host + * + * @var String + */ + private $user = null; + + /** + * @see Transport::setEndpoint() + * + */ + public function setEndpoint(\Zend_Config $config) + { + $this->host = isset($config->host) ? $config->host : "localhost"; + $this->port = isset($config->port) ? $config->port : 22; + $this->user = isset($config->user) ? $config->user : null; + $this->password = isset($config->password) ? $config->password : null; + $this->path = isset($config->path) ? $config->path : "/usr/local/icinga/var/rw/icinga.cmd"; + } + + /** + * @see Transport::send() + * + */ + public function send($command) + { + $retCode = 0; + $output = array(); + Logger::debug( + 'Icinga instance is on different host, attempting to send command %s via ssh to %s:%s/%s', + $command, + $this->host, + $this->port, + $this->path + ); + $hostConnector = $this->user ? $this->user . "@" . $this->host : $this->host; + exec( + 'ssh -o BatchMode=yes -o KbdInteractiveAuthentication=no' + . $hostConnector.' -p'.$this->port.' "echo \'['. time() .'] ' + . escapeshellcmd( + $command + ) + . '\' > '.$this->path.'" > /dev/null 2> /dev/null & ', + $output, + $retCode + ); + Logger::debug( + 'ssh '.$hostConnector.' -p'.$this->port.' "echo \'['. time() .'] ' + . escapeshellcmd( + $command + ) + . '\' > '.$this->path.'"' + ); + Logger::debug("Return code %s: %s ", $retCode, $output); + + if ($retCode != 0) { + $msg = 'Could not send command to remote icinga host: ' + . implode(PHP_EOL, $output) + . " (returncode $retCode)"; + Logger::error($msg); + throw new \RuntimeException($msg); + } + } +} diff --git a/library/Icinga/Protocol/Commandpipe/Transport/Transport.php b/library/Icinga/Protocol/Commandpipe/Transport/Transport.php new file mode 100644 index 000000000..d249c86e0 --- /dev/null +++ b/library/Icinga/Protocol/Commandpipe/Transport/Transport.php @@ -0,0 +1,50 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Protocol\Commandpipe\Transport; + +/** + * Interface for Transport classes handling the concrete access to the command pipe + * + */ +interface Transport +{ + /** + * Overwrite the target file of this Transport class using the given config from instances.ini + * + * @param \Zend_Config $config A configuration file containing a 'path' setting + */ + public function setEndpoint(\Zend_Config $config); + + /** + * Write the given external command to the command pipe + * + * @param string $message The command to send, without the timestamp (this will be added here) + */ + public function send($message); +} diff --git a/modules/monitoring/application/controllers/CommandController.php b/modules/monitoring/application/controllers/CommandController.php index 043a49c14..0346bd7b6 100644 --- a/modules/monitoring/application/controllers/CommandController.php +++ b/modules/monitoring/application/controllers/CommandController.php @@ -29,14 +29,15 @@ // {{{ICINGA_LICENSE_HEADER}}} use Icinga\Application\Benchmark; +use Icinga\Application\Icinga; use Icinga\Backend; use Icinga\Application\Config; +use Icinga\Application\Logger; use Icinga\Authentication\Manager; use Icinga\Web\Form; use Icinga\Web\ModuleActionController; use Icinga\Protocol\Commandpipe\Comment; use Icinga\Protocol\Commandpipe\CommandPipe; -use Icinga\Protocol\Commandpipe\Acknowledgement; use Icinga\Exception\ConfigurationError; use Icinga\Exception\MissingParameterException; use Monitoring\Form\Command\AcknowledgeForm; @@ -97,18 +98,19 @@ class Monitoring_CommandController extends ModuleActionController { if ($this->issetForm()) { if ($this->form->isPostAndValid()) { + $this->_helper->viewRenderer->setNoRender(true); $this->_helper->layout()->disableLayout(); } $this->view->form = $this->form; } - parent::postDispatch(); } /** * Controller configuration + * * @throws Icinga\Exception\ConfigurationError */ public function init() @@ -116,9 +118,7 @@ class Monitoring_CommandController extends ModuleActionController if ($this->_request->isPost()) { $instance = $this->_request->getPost("instance"); - $targetConfig = Config::module('monitoring', 'instances'); - if ($instance) { if ($targetConfig->get($instance)) { $this->target = new CommandPipe($targetConfig->get($instance)); @@ -137,37 +137,60 @@ class Monitoring_CommandController extends ModuleActionController } if ($this->getRequest()->getActionName() !== 'list') { - // Reduce template writing mess $this->_helper->viewRenderer->setRender(self::DEFAULT_VIEW_SCRIPT); } } /** * Retrieve all existing targets for host- and service combination - * @param string $hostname - * @param string $servicename - * @return array + * + * @param $hostOnly Ignore the service parameters + * (for example when using commands that only make sense for hosts) + * + * @return array Array of monitoring objects + * * @throws Icinga\Exception\MissingParameterException */ - private function selectCommandTargets($hostname, $servicename = null) + private function selectCommandTargets($hostOnly = false) { - $target = "hostlist"; - $filter = array(); - if (!$hostname && !$servicename) { - throw new MissingParameterException("Missing host and service definition"); + $query = null; + $fields = array( + 'host_name', + 'host_state' + ); + try { + $hostname = $this->getParam('host', null); + $servicename = $this->getParam('service', null); + $filter = array(); + if (!$hostname && !$servicename) { + throw new MissingParameterException("No target given for this command"); + } + if ($hostname) { + $filter["host_name"] = $hostname; + } + if ($servicename && !$hostOnly) { + $filter["service_description"] = $servicename; + $fields[] = "service_description"; + $fields[] = "service_state"; + } + ; + $query = Backend::getInstance()->select()->from("status", $fields); + return $query->applyFilters($filter)->fetchAll(); + } catch (\Exception $e) { + Logger::error( + "CommandController: SQL Query '%s' failed (message %s) ", + $query ? (string) $query->getQuery()->dump() : '--', $e->getMessage() + ); + return array(); } - if ($hostname) { - $filter["host_name"] = $hostname; - } - if ($servicename) { - $filter["service_description"] = $servicename; - $target = "servicelist"; - } - return Backend::getInstance()->select()->from($target)->applyFilters($filter)->fetchAll(); } /** * Displays a list of all commands + * + * This method uses reflection on the sourcecode to determine all *Action classes and return + * a list of them (ignoring the listAction) + * */ public function listAction() { @@ -183,16 +206,48 @@ class Monitoring_CommandController extends ModuleActionController $this->view->commands = $commands; } + /** + * Tell the controller that at least one of the parameters in $supported is required to be availabe + * + * @param array $supported An array of properties to check for existence in the POST or GET parameter list + * + * @throws Exception When non of the supported parameters is given + */ + private function setSupportedParameters(array $supported) + { + $given = array_intersect_key(array_flip($supported), $this->getRequest()->getParams()); + if (empty($given)) { + throw new \Exception('Missing parameter, supported: '.implode(', ', $supported)); + } + if (isset($given["host"])) { + $this->view->objects = $this->selectCommandTargets(!in_array("service", $supported)); + if (empty($this->view->objects)) { + throw new \Exception("No objects found for your command"); + } + + } else if (in_array("downtimeid", $supported)) { + $this->view->objects = array(); + $downtimes = $this->getParam("downtimeid"); + if (!is_array($downtimes)) { + $downtimes = array($downtimes); + } + foreach ($downtimes as $downtimeId) { + $this->view->objects[] = (object) array("downtime_id" => $downtimeId); + } + } + } + // ------------------------------------------------------------------------ // Commands for hosts / services // ------------------------------------------------------------------------ /** * Handle command disableactivechecks - * @throws Icinga\Exception\ProgrammingError + * */ public function disableactivechecksAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Disable active checks')); @@ -200,16 +255,17 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->disableActiveChecks($this->view->objects); } } /** * Handle command enableactivechecks - * @throws Icinga\Exception\ProgrammingError + * */ public function enableactivechecksAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Enable active checks')); @@ -217,32 +273,34 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->enableActiveChecks($this->view->objects); } } /** * Handle command reschedulenextcheck - * @throws Icinga\Exception\ProgrammingError + * */ public function reschedulenextcheckAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new RescheduleNextCheckForm(); $form->setRequest($this->getRequest()); $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->scheduleCheck($this->view->objects); } } /** * Handle command submitpassivecheckresult - * @throws Icinga\Exception\ProgrammingError + * */ public function submitpassivecheckresultAction() { + $this->setSupportedParameters(array('host', 'service')); $type = SubmitPassiveCheckResultForm::TYPE_SERVICE; $form = new SubmitPassiveCheckResultForm(); @@ -252,16 +310,17 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->submitCheckResult($this->view->objects, $form->getState(), $form->getOutput(), $form->getPerformancedata()); } } /** * Handle command stopobsessing - * @throws Icinga\Exception\ProgrammingError + * */ public function stopobsessingAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Stop obsessing')); @@ -269,16 +328,17 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->stopObsessing($this->view->objects); } } /** * Handle command startobsessing - * @throws Icinga\Exception\ProgrammingError + * */ public function startobsessingAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Start obsessing')); @@ -286,16 +346,17 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->startObsessing($this->view->objects); } } /** * Handle command stopacceptingpassivechecks - * @throws Icinga\Exception\ProgrammingError + * */ public function stopacceptingpassivechecksAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Stop accepting passive checks')); @@ -303,16 +364,17 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->disablePassiveChecks($this->view->objects); } } /** * Handle command startacceptingpassivechecks - * @throws Icinga\Exception\ProgrammingError + * */ public function startacceptingpassivechecksAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Start accepting passive checks')); @@ -320,16 +382,17 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->enableActiveChecks($this->view->objects); } } /** * Handle command disablenotifications - * @throws Icinga\Exception\ProgrammingError + * */ public function disablenotificationsAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Disable notifications')); @@ -337,13 +400,13 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->disableNotifications($this->view->objects); } } /** * Handle command enablenotifications - * @throws Icinga\Exception\ProgrammingError + * */ public function enablenotificationsAction() { @@ -354,63 +417,73 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->enableNotifications($this->view->objects); } } /** * Handle command sendcustomnotification - * @throws Icinga\Exception\ProgrammingError + * */ public function sendcustomnotificationAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new CustomNotificationForm(); $form->setRequest($this->getRequest()); $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $author = $this->getRequest()->getUser()->getUsername(); + $this->target->sendCustomNotification( + $this->view->objects, + new Comment($author, $form->getComment()), + $form->getOptions() + ); } } /** * Handle command scheduledowntime - * @throws Icinga\Exception\ProgrammingError + * */ public function scheduledowntimeAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ScheduleDowntimeForm(); $form->setRequest($this->getRequest()); $form->setWithChildren(false); $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->scheduleDowntime($this->view->objects, $form->getDowntime()); } } /** * Handle command scheduledowntimeswithchildren + * * @throws Icinga\Exception\ProgrammingError */ public function scheduledowntimeswithchildrenAction() { + $this->setSupportedParameters(array('host')); $form = new ScheduleDowntimeForm(); $form->setRequest($this->getRequest()); $form->setWithChildren(true); $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->scheduleDowntime($this->view->objects, $form->getDowntime()); } } /** * Handle command removedowntimeswithchildren - * @throws Icinga\Exception\ProgrammingError + * */ public function removedowntimeswithchildrenAction() { + $this->setSupportedParameters(array('host')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Remove downtime(s)')); @@ -418,16 +491,17 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->removeDowntime($this->view->objects); } } /** * Handle command disablenotificationswithchildren - * @throws Icinga\Exception\ProgrammingError + * */ public function disablenotificationswithchildrenAction() { + $this->setSupportedParameters(array('host')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Disable notifications')); @@ -435,16 +509,18 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->disableNotifications($this->view->objects); + $this->target->disableNotificationsForServices($this->view->objects); } } /** * Handle command enablenotificationswithchildren - * @throws Icinga\Exception\ProgrammingError + * */ public function enablenotificationswithchildrenAction() { + $this->setSupportedParameters(array('host')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Enable notifications')); @@ -452,16 +528,18 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->enableNotifications($this->view->objects); + $this->target->enableNotificationsForServices($this->view->objects); } } /** * Handle command reschedulenextcheckwithchildren - * @throws Icinga\Exception\ProgrammingError + * */ public function reschedulenextcheckwithchildrenAction() { + $this->setSupportedParameters(array('host')); $form = new RescheduleNextCheckForm(); $form->setRequest($this->getRequest()); @@ -470,16 +548,23 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + if ($form->isForced()) { + $this->target->scheduleForcedCheck($this->view->objects, time()); + $this->target->scheduleForcedCheck($this->view->objects, time(), true); + } else { + $this->target->scheduleCheck($this->view->objects, time()); + $this->target->scheduleCheck($this->view->objects, time(), true); + } } } /** * Handle command disableactivecheckswithchildren - * @throws Icinga\Exception\ProgrammingError + * */ public function disableactivecheckswithchildrenAction() { + $this->setSupportedParameters(array('host')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Disable active checks')); @@ -487,16 +572,18 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->disableActiveChecks($this->view->objects); + $this->target->disableActiveChecksWithChildren($this->view->objects); } } /** * Handle command enableactivecheckswithchildren - * @throws Icinga\Exception\ProgrammingError + * */ public function enableactivecheckswithchildrenAction() { + $this->setSupportedParameters(array('host')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Enable active checks')); @@ -504,16 +591,18 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->enableActiveChecks($this->view->objects); + $this->target->enableActiveChecksWithChildren($this->view->objects); } } /** * Handle command disableeventhandler - * @throws Icinga\Exception\ProgrammingError + * */ public function disableeventhandlerAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Disable event handler')); @@ -521,16 +610,17 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->disableEventHandler($this->view->objects); } } /** * Handle command enableeventhandler - * @throws Icinga\Exception\ProgrammingError + * */ public function enableeventhandlerAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Enable event handler')); @@ -538,16 +628,17 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->enableEventHandler($this->view->objects); } } /** * Handle command disableflapdetection - * @throws Icinga\Exception\ProgrammingError + * */ public function disableflapdetectionAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Disable flapping detection')); @@ -555,16 +646,17 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->disableFlappingDetection($this->view->objects); } } /** * Handle command enableflapdetection - * @throws Icinga\Exception\ProgrammingError + * */ public function enableflapdetectionAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Enable flapping detection')); @@ -572,32 +664,34 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->enableFlappingDetection($this->view->objects); } } /** * Handle command addcomment - * @throws Icinga\Exception\ProgrammingError + * */ public function addcommentAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new CommentForm(); $form->setRequest($this->getRequest()); $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->addComment($this->view->objects, $form->getComment()); } } /** * Handle command resetattributes - * @throws Icinga\Exception\ProgrammingError + * */ public function resetattributesAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Reset attributes')); @@ -605,32 +699,34 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->resetAttributes($this->view->objects); } } /** * Handle command acknowledgeproblem - * @throws Icinga\Exception\ProgrammingError + * */ public function acknowledgeproblemAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new AcknowledgeForm(); $form->setRequest($this->getRequest()); $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->acknowledge($this->view->objects, $form->getAcknowledgement()); } } /** * Handle command removeacknowledgement - * @throws Icinga\Exception\ProgrammingError + * */ public function removeacknowledgementAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new ConfirmationForm(); $form->setRequest($this->getRequest()); $form->setSubmitLabel(t('Remove problem acknowledgement')); @@ -638,32 +734,34 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->removeAcknowledge($this->view->objects); } } /** * Handle command delaynotification - * @throws Icinga\Exception\ProgrammingError + * */ public function delaynotificationAction() { + $this->setSupportedParameters(array('host', 'service')); $form = new DelayNotificationForm(); $form->setRequest($this->getRequest()); $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->delayNotification($this->view->objects, $form->getDelayTime()); } } /** * Handle command removedowntime - * @throws Icinga\Exception\ProgrammingError + * */ public function removedowntimeAction() { + $this->setSupportedParameters(array('downtimeid')); $form = new ConfirmationWithIdentifierForm(); $form->setRequest($this->getRequest()); @@ -675,7 +773,7 @@ class Monitoring_CommandController extends ModuleActionController $this->setForm($form); if ($form->isPostAndValid() === true) { - throw new \Icinga\Exception\ProgrammingError('Command sender not implemented: '. __FUNCTION__); + $this->target->removeDowntime($this->view->objects); } } } diff --git a/modules/monitoring/application/forms/Command/AcknowledgeForm.php b/modules/monitoring/application/forms/Command/AcknowledgeForm.php index 36deee5db..61e9bb46e 100644 --- a/modules/monitoring/application/forms/Command/AcknowledgeForm.php +++ b/modules/monitoring/application/forms/Command/AcknowledgeForm.php @@ -32,7 +32,8 @@ use Icinga\Web\Form\Element\DateTime; use \DateTime as PhpDateTime; use \DateInterval; use Icinga\Web\Form\Element\Note; - +use Icinga\Protocol\Commandpipe\Acknowledgement; +use Icinga\Protocol\Commandpipe\Comment; /** * Form for acknowledge commands */ @@ -142,4 +143,24 @@ class AcknowledgeForm extends ConfirmationForm $expireTime->addValidator($this->createDateTimeValidator(), true); } } + + public function getAcknowledgement() + { + $expireTime = -1; + if ($this->getValue('expire')) { + $time = new PhpDateTime($this->getValue('expiretime')); + $expireTime = $time->getTimestamp(); + } + return new Acknowledgement( + new Comment( + $this->getAuthorName(), + $this->getValue('comment'), + $this->getValue('persistent') + ), + $this->getValue('notify'), + $expireTime, + $this->getValue('sticky') + ); + + } } diff --git a/modules/monitoring/application/forms/Command/CommentForm.php b/modules/monitoring/application/forms/Command/CommentForm.php index 7d76e07dd..51d16f840 100644 --- a/modules/monitoring/application/forms/Command/CommentForm.php +++ b/modules/monitoring/application/forms/Command/CommentForm.php @@ -28,6 +28,7 @@ namespace Monitoring\Form\Command; +use Icinga\Protocol\Commandpipe\Comment; /** * Form for adding comment commands */ @@ -64,4 +65,9 @@ class CommentForm extends ConfirmationForm parent::create(); } + + public function getComment() + { + return new Comment($this->getAuthorName(), $this->getValue('comment'), $this->getValue('persistent')); + } } diff --git a/modules/monitoring/application/forms/Command/ConfirmationForm.php b/modules/monitoring/application/forms/Command/ConfirmationForm.php index 48de09d84..de24650d3 100644 --- a/modules/monitoring/application/forms/Command/ConfirmationForm.php +++ b/modules/monitoring/application/forms/Command/ConfirmationForm.php @@ -68,12 +68,14 @@ class ConfirmationForm extends Form /** * Array of messages + * * @var string[] */ private $notes = array(); /** * Setter for cancel label + * * @param string $cancelLabel */ public function setCancelLabel($cancelLabel) @@ -83,6 +85,7 @@ class ConfirmationForm extends Form /** * Getter for cancel label + * * @return string */ public function getCancelLabel() @@ -92,6 +95,7 @@ class ConfirmationForm extends Form /** * Setter for submit label + * * @param string $submitLabel */ public function setSubmitLabel($submitLabel) @@ -101,6 +105,7 @@ class ConfirmationForm extends Form /** * Getter for submit label + * * @return string */ public function getSubmitLabel() @@ -110,6 +115,7 @@ class ConfirmationForm extends Form /** * Add message to stack + * * @param string $message */ public function addNote($message) @@ -119,6 +125,7 @@ class ConfirmationForm extends Form /** * Purge messages from stack + * */ public function clearNotes() { @@ -127,6 +134,7 @@ class ConfirmationForm extends Form /** * Getter for notes + * * @return string[] */ public function getNotes() @@ -148,6 +156,7 @@ class ConfirmationForm extends Form /** * Add elements to this form (used by extending classes) + * * @see Form::create */ protected function create() @@ -191,15 +200,19 @@ class ConfirmationForm extends Form /** * Get the author name - * TODO(mh): This should work on the request, at present it's fix + */ protected function getAuthorName() { - return 'Iwan IV. Wassiljewitsch, der Schreckliche'; + if (is_a($this->getRequest(), "Zend_Controller_Request_HttpTestCase")) { + return "Test user"; + } + return $this->getRequest()->getUser()->getUsername(); } /** * Creator for author field + * * @return Zend_Form_Element_Hidden */ protected function createAuthorField() diff --git a/modules/monitoring/application/forms/Command/CustomNotificationForm.php b/modules/monitoring/application/forms/Command/CustomNotificationForm.php index 38579923d..f3217a50e 100644 --- a/modules/monitoring/application/forms/Command/CustomNotificationForm.php +++ b/modules/monitoring/application/forms/Command/CustomNotificationForm.php @@ -73,4 +73,21 @@ class CustomNotificationForm extends ConfirmationForm parent::create(); } + + public function getComment() + { + return $this->getValue('comment'); + } + + public function getOptions() + { + $value = 0; + if ($this->getValue('force')) { + $value |= 2; + } + if ($this->getValue('broadcast')) { + $value |= 1; + } + return $value; + } } diff --git a/modules/monitoring/application/forms/Command/DelayNotificationForm.php b/modules/monitoring/application/forms/Command/DelayNotificationForm.php index e7afa6ec4..59ec290da 100644 --- a/modules/monitoring/application/forms/Command/DelayNotificationForm.php +++ b/modules/monitoring/application/forms/Command/DelayNotificationForm.php @@ -70,4 +70,9 @@ class DelayNotificationForm extends ConfirmationForm parent::create(); } + + public function getDelayTime() + { + return $this->getValue('minutes')*60; + } } diff --git a/modules/monitoring/application/forms/Command/RescheduleNextCheckForm.php b/modules/monitoring/application/forms/Command/RescheduleNextCheckForm.php index e587ed05e..3d5ea4730 100644 --- a/modules/monitoring/application/forms/Command/RescheduleNextCheckForm.php +++ b/modules/monitoring/application/forms/Command/RescheduleNextCheckForm.php @@ -79,4 +79,9 @@ class RescheduleNextCheckForm extends WithChildrenCommandForm parent::create(); } + + public function isForced() + { + return $this->getValue('forcecheck') == true; + } } diff --git a/modules/monitoring/application/forms/Command/ScheduleDowntimeForm.php b/modules/monitoring/application/forms/Command/ScheduleDowntimeForm.php index c0840982e..cab8fae0e 100644 --- a/modules/monitoring/application/forms/Command/ScheduleDowntimeForm.php +++ b/modules/monitoring/application/forms/Command/ScheduleDowntimeForm.php @@ -29,6 +29,8 @@ namespace Monitoring\Form\Command; use Icinga\Web\Form\Element\DateTime; +use Icinga\Protocol\Commandpipe\Downtime; +use Icinga\Protocol\Commandpipe\Comment; use \DateTime as PhpDateTime; use \DateInterval; use \Zend_Form_Element_Text; @@ -58,6 +60,7 @@ class ScheduleDowntimeForm extends WithChildrenCommandForm /** * Build an array of timestamps + * * @return string[] */ private function generateDefaultTimestamps() @@ -76,6 +79,7 @@ class ScheduleDowntimeForm extends WithChildrenCommandForm /** * Generate translated multi options based on type constants + * * @return array */ private function getDowntimeTypeOptions() @@ -88,6 +92,7 @@ class ScheduleDowntimeForm extends WithChildrenCommandForm /** * Interface method to build the form + * * @see ConfirmationForm::create */ protected function create() @@ -104,6 +109,10 @@ class ScheduleDowntimeForm extends WithChildrenCommandForm ) ); + /** + * @TODO: Display downtime list (Bug #4496) + * + */ $this->addElement( 'text', 'triggered', @@ -242,6 +251,7 @@ class ScheduleDowntimeForm extends WithChildrenCommandForm /** * Change validators at runtime + * * @see Form::preValidation * @param array $data */ @@ -265,4 +275,45 @@ class ScheduleDowntimeForm extends WithChildrenCommandForm $minutes->addValidator($greaterThanValidator, true); } } + + /** + * Return the downtime submitted in this form + * + * @return Downtime + */ + public function getDowntime() + { + + $comment = new Comment( + $this->getRequest()->getUser()->getUsername(), + $this->getValue('comment') + ); + $duration = 0; + if ($this->getValue('type') === self::TYPE_FLEXIBLE) { + $duration = ($this->getValue('hours')*3600) + ($this->getValue('minutes')*60); + } + $starttime = new PhpDateTime($this->getValue('starttime')); + $endtime = new PhpDateTime($this->getValue('endtime')); + + $downtime = new Downtime( + $starttime->getTimestamp(), + $endtime->getTimestamp(), + $comment, + $duration, + $this->getValue('triggered') + ); + if (! $this->getWithChildren()) { + switch ($this->getValue('childobjects')) { + case 1: + $downtime->setType(Downtime::TYPE_WITH_CHILDREN_TRIGGERED); + break; + case 2: + $downtime->setType(Downtime::TYPE_WITH_CHILDREN); + break; + } + } else { + $downtime->setType(Downtime::TYPE_HOST_SVC); + } + return $downtime; + } } diff --git a/modules/monitoring/application/forms/Command/SubmitPassiveCheckResultForm.php b/modules/monitoring/application/forms/Command/SubmitPassiveCheckResultForm.php index 7ad872e3a..f1e8861ea 100644 --- a/modules/monitoring/application/forms/Command/SubmitPassiveCheckResultForm.php +++ b/modules/monitoring/application/forms/Command/SubmitPassiveCheckResultForm.php @@ -168,4 +168,20 @@ class SubmitPassiveCheckResultForm extends ConfirmationForm parent::create(); } + + public function getState() + { + return intval($this->getValue('pluginstate')); + } + + public function getOutput() + { + return $this->getValue('checkoutput'); + } + + public function getPerformancedata() + { + return $this->getValue('performancedata'); + } + } diff --git a/modules/monitoring/application/views/helpers/MonitoringFlags.php b/modules/monitoring/application/views/helpers/MonitoringFlags.php index d6b7d367f..b13b4562c 100644 --- a/modules/monitoring/application/views/helpers/MonitoringFlags.php +++ b/modules/monitoring/application/views/helpers/MonitoringFlags.php @@ -39,7 +39,7 @@ class Zend_View_Helper_MonitoringFlags extends Zend_View_Helper_Abstract private static $keys = array( 'passive_checks_enabled' => 'Passive checks', 'active_checks_enabled' => 'Active checks', - 'obsess_over_host' => 'Obsessing', + 'obsessing' => 'Obsessing', 'notifications_enabled' => 'Notifications', 'event_handler_enabled' => 'Event handler', 'flap_detection_enabled' => 'Flap detection', diff --git a/modules/monitoring/application/views/scripts/command/renderform.phtml b/modules/monitoring/application/views/scripts/command/renderform.phtml index eb10f07da..08c726625 100644 --- a/modules/monitoring/application/views/scripts/command/renderform.phtml +++ b/modules/monitoring/application/views/scripts/command/renderform.phtml @@ -1,4 +1,30 @@ + +objects) && !empty($this->objects) && isset($this->objects[0]->host_name)): ?> + + Affected objects + + + + + + + +objects as $object): ?> + + + + + + + +
Host nameService name
host_name; ?>service_description) ? $object->service_description : ''); ?>
+ + + +form): ?>
form; ?>
-
\ No newline at end of file + + + diff --git a/modules/monitoring/docs/instances.md b/modules/monitoring/docs/instances.md new file mode 100644 index 000000000..2cb1eebc9 --- /dev/null +++ b/modules/monitoring/docs/instances.md @@ -0,0 +1,37 @@ +# The instance.ini configuration file + +## Abstract + +The instance.ini defines how icingaweb accesses the command pipe of your icinga process in order to submit external +commands. When you are at the root of your icingaweb installation you can find it under ./config/modules/monitoring/instances.ini. + +## Syntax + +You can define multiple instances in the instances.ini, icingaweb will use the first one as the default instance. + +Every instance starts with a section header containing the name of the instance, followed by the config directives for +this instance in the standard ini format used by icingaweb. + +## Using a local icinga pipe + +A local icinga instance can be easily setup and only requires the 'path' parameter: + + [icinga] + path=/usr/local/icinga/var/rw/icinga.cmd + +When sending commands to the icinga instance, icingaweb just opens the file found underneath 'path' and writes the external +command to it. + +## Using ssh for accessing an icinga pipe + +When providing at least a host directive to the instances.ini, SSH will be used for accessing the pipe. You must have +setup key authentication at the endpoint and allow your icingweb's user to access the machine without a password at this time: + + [icinga] + path=/usr/local/icinga/var/rw/icinga.cmd ; the path on the remote machine where the icinga.cmd can be found + host=my.remote.machine.com ; the hostname or address of the remote machine + port=22 ; the port to use (22 if none is given) + user=jdoe ; the user to authenticate with + + + diff --git a/modules/monitoring/library/Monitoring/Backend/AbstractBackend.php b/modules/monitoring/library/Monitoring/Backend/AbstractBackend.php index 38aa8d3e3..1c38fd2f8 100644 --- a/modules/monitoring/library/Monitoring/Backend/AbstractBackend.php +++ b/modules/monitoring/library/Monitoring/Backend/AbstractBackend.php @@ -149,7 +149,7 @@ class AbstractBackend implements DatasourceInterface 'host_scheduled_downtime_depth', 'host_failure_prediction_enabled', 'host_process_performance_data', - 'host_obsess_over_host', + 'host_obsessing', 'host_modified_host_attributes', 'host_event_handler', 'host_check_command', @@ -230,7 +230,7 @@ class AbstractBackend implements DatasourceInterface 'service_scheduled_downtime_depth', 'service_failure_prediction_enabled', 'service_process_performance_data', - 'service_obsess_over_service', + 'service_obsessing', 'service_modified_service_attributes', 'service_event_handler', 'service_check_command', diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php index 0bc50617b..a37e7203f 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php @@ -60,7 +60,7 @@ class StatusQuery extends AbstractQuery 'host_scheduled_downtime_depth' => 'hs.scheduled_downtime_depth', 'host_failure_prediction_enabled' => 'hs.failure_prediction_enabled', 'host_process_performance_data' => 'hs.process_performance_data', - 'host_obsess_over_host' => 'hs.obsess_over_host', + 'host_obsessing' => 'hs.obsess_over_host', 'host_modified_host_attributes' => 'hs.modified_host_attributes', 'host_event_handler' => 'hs.event_handler', 'host_check_command' => 'hs.check_command', @@ -160,7 +160,7 @@ class StatusQuery extends AbstractQuery 'service_scheduled_downtime_depth' => 'ss.scheduled_downtime_depth', 'service_failure_prediction_enabled' => 'ss.failure_prediction_enabled', 'service_process_performance_data' => 'ss.process_performance_data', - 'service_obsess_over_service' => 'ss.obsess_over_service', + 'service_obsessing' => 'ss.obsess_over_service', 'service_modified_service_attributes' => 'ss.modified_service_attributes', 'service_event_handler' => 'ss.event_handler', 'service_check_command' => 'ss.check_command', diff --git a/modules/monitoring/library/Monitoring/Command/Meta.php b/modules/monitoring/library/Monitoring/Command/Meta.php index 053867d8a..fc0abc41a 100644 --- a/modules/monitoring/library/Monitoring/Command/Meta.php +++ b/modules/monitoring/library/Monitoring/Command/Meta.php @@ -729,8 +729,8 @@ class Meta } else { unset($commands[self::CMD_START_ACCEPTING_PASSIVE_CHECKS]); } - $obsess = 'obsess_over_'.$type; - if ($object->$obsess === '1') { + + if ($object->obsessing === '1') { unset($commands[self::CMD_START_OBSESSING]); } else { unset($commands[self::CMD_STOP_OBSESSING]); diff --git a/modules/monitoring/test/php/application/views/helpers/MonitoringCommandsTest.php b/modules/monitoring/test/php/application/views/helpers/MonitoringCommandsTest.php index b4649402c..542a42eb5 100644 --- a/modules/monitoring/test/php/application/views/helpers/MonitoringCommandsTest.php +++ b/modules/monitoring/test/php/application/views/helpers/MonitoringCommandsTest.php @@ -54,7 +54,7 @@ class HostStruct extends \stdClass public $host_scheduled_downtime_depth = '1'; public $host_failure_prediction_enabled = '1'; public $host_process_performance_data = '1'; - public $host_obsess_over_host = '1'; + public $host_obsessing = '1'; public $host_modified_host_attributes = '14'; public $host_event_handler = ''; public $host_normal_check_interval = '5'; diff --git a/modules/monitoring/test/php/application/views/helpers/MonitoringFlagsTest.php b/modules/monitoring/test/php/application/views/helpers/MonitoringFlagsTest.php index b03eff55f..4821df3b8 100644 --- a/modules/monitoring/test/php/application/views/helpers/MonitoringFlagsTest.php +++ b/modules/monitoring/test/php/application/views/helpers/MonitoringFlagsTest.php @@ -13,7 +13,7 @@ class MonitoringFlagsTest extends \PHPUnit_Framework_TestCase $testArray = array( 'host_passive_checks_enabled' => '0', 'host_active_checks_enabled' => '0', - 'host_obsess_over_host' => '1', + 'host_obsessing' => '1', 'host_notifications_enabled' => '0', 'host_event_handler_enabled' => '1', 'host_flap_detection_enabled' => '1', @@ -41,7 +41,7 @@ class MonitoringFlagsTest extends \PHPUnit_Framework_TestCase $testArray = array( 'service_passive_checks_enabled' => '0', 'service_active_checks_enabled' => '1', - 'service_obsess_over_host' => '0', + 'service_obsessing' => '0', 'service_notifications_enabled' => '1', 'service_event_handler_enabled' => '1', 'service_flap_detection_enabled' => '0', @@ -68,7 +68,7 @@ class MonitoringFlagsTest extends \PHPUnit_Framework_TestCase { $testArray = array( 'service_active_checks_enabled' => '1', - 'service_obsess_over_host' => '1', + 'service_obsessing' => '1', 'DING DING' => '$$$', 'DONG DONG' => '###' ); diff --git a/modules/monitoring/test/php/application/views/helpers/MonitoringPropertiesTest.php b/modules/monitoring/test/php/application/views/helpers/MonitoringPropertiesTest.php index a1bab2bbf..e33d9657f 100644 --- a/modules/monitoring/test/php/application/views/helpers/MonitoringPropertiesTest.php +++ b/modules/monitoring/test/php/application/views/helpers/MonitoringPropertiesTest.php @@ -50,7 +50,7 @@ class HostStruct4Properties extends \stdClass public $host_scheduled_downtime_depth = '1'; public $host_failure_prediction_enabled = '1'; public $host_process_performance_data = '1'; - public $host_obsess_over_host = '1'; + public $host_obsessing = '1'; public $host_modified_host_attributes = '14'; public $host_event_handler = ''; public $host_normal_check_interval = '5'; diff --git a/modules/monitoring/test/php/library/Command/MetaTest.php b/modules/monitoring/test/php/library/Command/MetaTest.php index e2c2f155a..c86f24efa 100644 --- a/modules/monitoring/test/php/library/Command/MetaTest.php +++ b/modules/monitoring/test/php/library/Command/MetaTest.php @@ -51,7 +51,7 @@ class HostStruct extends \stdClass public $host_scheduled_downtime_depth = '1'; public $host_failure_prediction_enabled = '1'; public $host_process_performance_data = '1'; - public $host_obsess_over_host = '1'; + public $host_obsessing = '1'; public $host_modified_host_attributes = '14'; public $host_event_handler = ''; public $host_normal_check_interval = '5'; @@ -158,7 +158,7 @@ class MetaTest extends \PHPUnit_Framework_TestCase $object = new HostStruct(); - $object->host_obsess_over_host = '0'; + $object->host_obsessing = '0'; $object->host_flap_detection_enabled = '0'; $object->host_active_checks_enabled = '0'; diff --git a/test/php/library/Icinga/Protocol/Commandpipe/CommandPipeLoader.php b/test/php/library/Icinga/Protocol/Commandpipe/CommandPipeLoader.php new file mode 100644 index 000000000..5a5ca1831 --- /dev/null +++ b/test/php/library/Icinga/Protocol/Commandpipe/CommandPipeLoader.php @@ -0,0 +1,34 @@ +getPipeName(); $this->cleanup(); @@ -45,26 +55,76 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase return $pipe; } + /** + * Return a @see Icinga\Protocal\CommandPipe\CommandPipe instance set up for the local test pipe, but with ssh as the transport layer + * + * @return Commandpipe + */ + private function getSSHTestPipe() + { + $tmpPipe = $this->getPipeName(); + $this->cleanup(); + touch($tmpPipe); + + $cfg = new \Zend_Config(array( + "path" => $tmpPipe, + "user" => "vagrant", + "password" => "vagrant", + "host" => 'localhost', + "port" => 22, + "name" => "test" + )); + $comment = new Comment("Autor","Comment"); + $pipe = new Commandpipe($cfg); + + return $pipe; + } + + /** + * Remove the testpipe if it exists + * + */ private function cleanup() { if(file_exists($this->getPipeName())) unlink($this->getPipeName()); } + /** + * Query the extcmd_test script with $command or the command pipe and test whether the result is $exceptedString and + * has a return code of 0. + * + * Note: + * - if no string is given, only the return code is tested + * - if no command is given, the content of the test commandpipe is used + * + * This helps testing whether commandpipe serialization works correctly + * + * @param bool $expectedString The string that is expected to be returned from the extcmd_test binary + * (optional, leave it to just test for the return code) + * @param bool $command The commandstring to send (optional, leave it for using the command pipe content) + */ private function assertCommandSucceeded($expectedString = false,$command = false) { $resultCode = null; $resultArr = array(); $receivedCmd = exec(EXTCMD_TEST_BIN." ".escapeshellarg($command ? $command : file_get_contents($this->getPipeName())),$resultArr,$resultCode); - $this->assertEquals(0,$resultCode,"Submit of external command returned error : ".$receivedCmd); + $this->assertEquals(0, $resultCode, "Submit of external command returned error : ".$receivedCmd); if (!$expectedString) return; $this->assertEquals( $expectedString, - $receivedCmd + $receivedCmd, + 'Asserting that the command icinga received matches the command we send' ); } + + /** + * Test whether a single host acknowledgment is serialized and send correctly + * + * @throws \Exception|Exception + */ public function testAcknowledgeSingleHost() { - $pipe = $this->getTestPipe(); + $pipe = $this->getLocalTestPipe(); try { $ack = new Acknowledgement(new Comment("I can","sends teh ack")); $pipe->acknowledge(array( @@ -80,12 +140,17 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase $this->cleanup(); } + /** + * Test whether multiple host and service acknowledgments are serialized and send correctly + * + * @throws \Exception|Exception + */ public function testAcknowledgeMultipleObjects() { - $pipe = $this->getTestPipe(); + $pipe = $this->getLocalTestPipe(); try { $ack = new Comment("I can","sends teh ack"); - $pipe->fopen_mode = "a"; + $pipe->getTransport()->setOpenMode("a"); $pipe->acknowledge(array( (object) array( "host_name" => "hostA" @@ -100,8 +165,7 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase ),$ack); $result = explode("\n",file_get_contents($this->getPipeName())); - - $this->assertCount(5,$result); + $this->assertCount(5, $result, "Asserting the correct number of commands being written to the command pipe"); $this->assertCommandSucceeded("ACKNOWLEDGE_HOST_PROBLEM;hostA;0;0;0;I can;sends teh ack",$result[0]); $this->assertCommandSucceeded("ACKNOWLEDGE_HOST_PROBLEM;hostB;0;0;0;I can;sends teh ack",$result[1]); @@ -115,9 +179,14 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase $this->cleanup(); } + /** + * Test whether a single host comment is correctly serialized and send to the command pipe + * + * @throws \Exception|Exception + */ public function testAddHostComment() { - $pipe = $this->getTestPipe(); + $pipe = $this->getLocalTestPipe(); try { $pipe->addComment(array((object) array("host_name" => "hostA")), new Comment("Autor","Comment") @@ -130,9 +199,14 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase $this->cleanup(); } + /** + * Test whether removing all hostcomments is correctly serialized and send to the command pipe + * + * @throws \Exception|Exception + */ public function testRemoveAllHostComment() { - $pipe = $this->getTestPipe(); + $pipe = $this->getLocalTestPipe(); try { $pipe->removeComment(array( (object) array( @@ -147,9 +221,14 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase $this->cleanup(); } + /** + * Test whether removing a single host comment is correctly serialized and send to the command pipe + * + * @throws \Exception|Exception + */ public function testRemoveSpecificComment() { - $pipe = $this->getTestPipe(); + $pipe = $this->getLocalTestPipe(); try { $pipe->removeComment(array((object) array("comment_id"=>34,"host_name"=>"test"))); $this->assertCommandSucceeded("DEL_HOST_COMMENT;34"); @@ -160,11 +239,16 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase $this->cleanup(); } + /** + * Test whether a multiple reschedules for services and hosts are correctly serialized and send to the commandpipe + * + * @throws \Exception|Exception + */ public function testScheduleChecks() { - $pipe = $this->getTestPipe(); + $pipe = $this->getLocalTestPipe(); try { - $pipe->fopen_mode = "a"; // append so we have multiple results + $pipe->getTransport()->setOpenMode("a"); // append so we have multiple results $t = time(); // normal reschedule $pipe->scheduleCheck(array( @@ -182,7 +266,7 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase ),$t,true); $result = explode("\n",file_get_contents($this->getPipeName())); - $this->assertCount(6,$result); + $this->assertCount(6,$result, "Asserting a correct number of commands being written to the commandpipe"); $this->assertCommandSucceeded("SCHEDULE_HOST_CHECK;test;".$t,$result[0]); $this->assertCommandSucceeded("SCHEDULE_SVC_CHECK;test;svc1;".$t,$result[1]); @@ -198,11 +282,16 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase $this->cleanup(); } + /** + * Test whether modifying monitoringflags of a host and service is correctly serialized and send to the command pipe + * + * @throws \Exception|Exception + */ public function testObjectStateModifications() { - $pipe = $this->getTestPipe(); + $pipe = $this->getLocalTestPipe(); try { - $pipe->fopen_mode = "a"; + $pipe->getTransport()->setOpenMode("a"); $pipe->setMonitoringProperties(array( (object) array( "host_name" => "Testhost" @@ -223,7 +312,7 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase $result = explode("\n",file_get_contents($this->getPipeName())); array_pop($result); // remove empty last line - $this->assertCount(12,$result); + $this->assertCount(12,$result, "Asserting a correct number of commands being written to the commandpipe"); foreach ($result as $command) { $this->assertCommandSucceeded(false,$command); } @@ -235,9 +324,14 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase $this->cleanup(); } + /** + * Test whether enabling and disabling global notifications are send correctly to the pipe + * + * @throws \Exception|Exception + */ public function testGlobalNotificationTrigger() { - $pipe = $this->getTestPipe(); + $pipe = $this->getLocalTestPipe(); try { $pipe->enableGlobalNotifications(); $this->assertCommandSucceeded("ENABLE_NOTIFICATIONS;"); @@ -250,9 +344,14 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase $this->cleanup(); } + /** + * Test whether host and servicedowntimes are correctly scheduled + * + * @throws \Exception|Exception + */ public function testScheduleDowntime() { - $pipe = $this->getTestPipe(); + $pipe = $this->getLocalTestPipe(); try { $downtime = new Downtime(25,26,new Comment("me","test")); $pipe->scheduleDowntime(array( @@ -260,7 +359,7 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase "host_name" => "Testhost" ) ),$downtime); - $this->assertCommandSucceeded("SCHEDULE_HOST_DOWNTIME;Testhost;25;26;0;0;0;me;test"); + $this->assertCommandSucceeded("SCHEDULE_HOST_DOWNTIME;Testhost;25;26;1;0;0;me;test"); $pipe->scheduleDowntime(array( (object) array( @@ -268,7 +367,7 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase "service_description" => "svc" ) ),$downtime); - $this->assertCommandSucceeded("SCHEDULE_SVC_DOWNTIME;Testhost;svc;25;26;0;0;0;me;test"); + $this->assertCommandSucceeded("SCHEDULE_SVC_DOWNTIME;Testhost;svc;25;26;1;0;0;me;test"); } catch (Exception $e) { $this->cleanup(); @@ -277,11 +376,16 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase $this->cleanup(); } + /** + * Test whether the removal of downtimes is correctly serialized and send to the commandpipe for hosts and services + * + * @throws \Exception|Exception + */ public function testRemoveDowntime() { - $pipe = $this->getTestPipe(); + $pipe = $this->getLocalTestPipe(); try { - $pipe->fopen_mode = "a"; + $pipe->getTransport()->setOpenMode("a"); $pipe->removeDowntime(array( (object) array( "host_name" => "Testhost" @@ -298,7 +402,7 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase )); $result = explode("\n",file_get_contents($this->getPipeName())); array_pop($result); // remove empty last line - $this->assertCount(3,$result); + $this->assertCount(3,$result, "Asserting a correct number of commands being written to the commandpipe"); $this->assertCommandSucceeded("DEL_DOWNTIME_BY_HOST_NAME;Testhost",$result[0]); $this->assertCommandSucceeded("DEL_DOWNTIME_BY_HOST_NAME;host;svc",$result[1]); $this->assertCommandSucceeded("DEL_SVC_DOWNTIME;123",$result[2]); @@ -310,4 +414,82 @@ class CommandPipeTest extends \PHPUnit_Framework_TestCase $this->cleanup(); } + /** + * Test whether custom servicenotifications are correctly send to the commandpipe without options + * + * @throws \Exception + */ + public function testSendCustomServiceNotification() + { + $pipe = $this->getLocalTestPipe(); + try { + $comment = new Comment("author", "commenttext"); + $pipe->sendCustomNotification(array( + (object) array( + "host_name" => "host1", + "service_description" => "service1" + ) + ), $comment); + $this->assertCommandSucceeded( + "SEND_CUSTOM_SVC_NOTIFICATION;host1;service1;0;author;commenttext" + ); + } catch (Exception $e) { + $this->cleanup(); + throw $e; + } + $this->cleanup(); + } + + /** + * Test whether custom hostnotifications are correctly send to the commandpipe with a varlist of options + * + * @throws \Exception + */ + public function testSendCustomHostNotificationWithOptions() + { + $pipe = $this->getLocalTestPipe(); + try { + $comment = new Comment('author', 'commenttext'); + $pipe->sendCustomNotification(array( + (object) array( + 'host_name' => 'host' + ) + ), $comment, Commandpipe::NOTIFY_FORCED, Commandpipe::NOTIFY_BROADCAST, Commandpipe::NOTIFY_INCREMENT); + + $this->assertCommandSucceeded( + 'SEND_CUSTOM_HOST_NOTIFICATION;host;7;author;commenttext' + ); + } catch (Exception $e) { + $this->cleanup(); + throw $e; + } + $this->cleanup(); + } + + /** + * Test sending of commands via SSH (currently disabled) + * + * @throws \Exception|Exception + */ + public function testSSHCommands() + { + $this->markTestSkipped("This test assumes running in a vagrant VM with key-auth"); + + if (!is_dir("/vagrant")) { + } + $pipe = $this->getSSHTestPipe(); + try { + $ack = new Acknowledgement(new Comment("I can","sends teh ack")); + $pipe->acknowledge(array( + (object) array( + "host_name" => "hostA" + ) + ),$ack); + $this->assertCommandSucceeded("ACKNOWLEDGE_HOST_PROBLEM;hostA;0;0;0;I can;sends teh ack"); + } catch(Exception $e) { + $this->cleanup(); + throw $e; + } + $this->cleanup(); + } }