CLI interface: initial import
This commit is contained in:
parent
d1e61a1826
commit
a25cd80ec0
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Clicommands;
|
||||
|
||||
use Icinga\Cli\Command;
|
||||
use Icinga\Cli\Documentation;
|
||||
|
||||
/**
|
||||
* Help for modules, commands and actions
|
||||
*
|
||||
* The help command shows help for a given command, module and also for a
|
||||
* given module's command or a specific command's action.
|
||||
*
|
||||
* Usage: icingaweb help [<module>] [<command> [<action>]]
|
||||
*/
|
||||
class HelpCommand extends Command
|
||||
{
|
||||
protected $defaultActionName = 'show';
|
||||
|
||||
/**
|
||||
* Show help for modules, commands and actions [default]
|
||||
*
|
||||
* The help command shows help for a given command, module and also for a
|
||||
* given module's command or a specific command's action.
|
||||
*
|
||||
* Usage: icingaweb help [<module>] [<command> [<action>]]
|
||||
*/
|
||||
public function showAction()
|
||||
{
|
||||
$module = null;
|
||||
$command = null;
|
||||
$action = null;
|
||||
$loader = $this->app->cliLoader();
|
||||
$command = $this->params->shift();
|
||||
|
||||
if ($loader->hasCommand($command)) {
|
||||
$action = $this->params->shift();
|
||||
if (! $loader->getCommandInstance($command)->hasActionName($action)) {
|
||||
$action = null;
|
||||
}
|
||||
} else {
|
||||
if ($loader->hasModule($command)) {
|
||||
$module = $command;
|
||||
$command = $this->params->shift();
|
||||
if ($loader->hasModuleCommand($module, $command)) {
|
||||
$action = $this->params->shift();
|
||||
$mod = $loader->getModuleCommandInstance($module, $command);
|
||||
if (! $mod->hasActionName($action)) {
|
||||
$action = null;
|
||||
}
|
||||
} else {
|
||||
$command = null;
|
||||
}
|
||||
} else {
|
||||
$command = null;
|
||||
}
|
||||
}
|
||||
echo $this->docs()->usage($module, $command, $action);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Clicommands;
|
||||
|
||||
use Icinga\Cli\Command;
|
||||
|
||||
/**
|
||||
* List and handle modules
|
||||
*
|
||||
* The module command allows you to handle your IcingaWeb modules
|
||||
*
|
||||
* Usage: icingaweb module [<action>] [<modulename>]
|
||||
*/
|
||||
class ModuleCommand extends Command
|
||||
{
|
||||
protected $modules;
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->modules = $this->app->getModuleManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* List all enabled modules
|
||||
*
|
||||
* If you are interested in all installed modules pass 'installed' (or
|
||||
* even --installed) as a command parameter. If you enable --verbose even
|
||||
* more details will be shown
|
||||
*
|
||||
* Usage: icingaweb module list [installed] [--verbose]
|
||||
*/
|
||||
public function listAction()
|
||||
{
|
||||
if ($type = $this->params->shift()) {
|
||||
if (! in_array($type, array('enabled', 'installed'))) {
|
||||
return $this->showUsage();
|
||||
}
|
||||
} else {
|
||||
$type = 'enabled';
|
||||
$this->params->shift('enabled');
|
||||
if ($this->params->shift('installed')) {
|
||||
$type = 'installed';
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->hasRemainingParams()) {
|
||||
return $this->showUsage();
|
||||
}
|
||||
|
||||
if ($type === 'enabled') {
|
||||
$modules = $this->modules->listEnabledModules();
|
||||
} else {
|
||||
$modules = $this->modules->listInstalledModules();
|
||||
}
|
||||
if (empty($modules)) {
|
||||
echo "There are no modules installed\n";
|
||||
return;
|
||||
}
|
||||
if ($this->isVerbose) {
|
||||
printf("%-14s %-9s DIRECTORY\n", 'MODULE', 'STATE');
|
||||
} else {
|
||||
printf("%-14s %-9s\n", 'MODULE', 'STATE');
|
||||
}
|
||||
foreach ($modules as $module) {
|
||||
if ($this->isVerbose) {
|
||||
$dir = ' ' . $this->modules->getModuleDir($module);
|
||||
} else {
|
||||
$dir = '';
|
||||
}
|
||||
printf(
|
||||
"%-14s %-9s%s\n",
|
||||
$module,
|
||||
($type === 'enabled' || $this->modules->hasEnabled($module))
|
||||
? 'enabled'
|
||||
: 'disabled',
|
||||
$dir
|
||||
);
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a given module
|
||||
*
|
||||
* Usage: icingaweb module enable <module-name>
|
||||
*/
|
||||
public function enableAction()
|
||||
{
|
||||
if (! $module = $this->params->shift()) {
|
||||
$module = $this->params->shift('module');
|
||||
}
|
||||
if (! $module || $this->hasRemainingParams()) {
|
||||
return $this->showUsage();
|
||||
}
|
||||
$this->modules->enableModule($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a given module
|
||||
*
|
||||
* Usage: icingaweb module disable <module-name>
|
||||
*/
|
||||
public function disableAction()
|
||||
{
|
||||
if (! $module = $this->params->shift()) {
|
||||
$module = $this->params->shift('module');
|
||||
}
|
||||
if (! $module || $this->hasRemainingParams()) {
|
||||
return $this->showUsage();
|
||||
}
|
||||
$this->modules->disableModule($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a given module
|
||||
*
|
||||
* Does a lookup against your configured IcingaWeb app stores and tries to
|
||||
* find modules matching your search string
|
||||
*
|
||||
* Usage: icingaweb module search <search-string>
|
||||
*/
|
||||
public function searchAction()
|
||||
{
|
||||
$this->fail("Not implemented yet");
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a given module
|
||||
*
|
||||
* Downloads a given module or installes a module from a given archive
|
||||
*
|
||||
* Usage: icingaweb module install <module-name>
|
||||
* icingaweb module install </path/to/archive.tar.gz>
|
||||
*/
|
||||
public function installAction()
|
||||
{
|
||||
$this->fail("Not implemented yet");
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a given module
|
||||
*
|
||||
* Removes the given module from your disk. Module configuration will be
|
||||
* preserved
|
||||
*
|
||||
* Usage: icingaweb module remove <module-name>
|
||||
*/
|
||||
public function removeAction()
|
||||
{
|
||||
$this->fail("Not implemented yet");
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge a given module
|
||||
*
|
||||
* Removes the given module from your disk. Also wipes configuration files
|
||||
* and other data stored and/or generated by this module
|
||||
*
|
||||
* Usage: icingaweb module remove <module-name>
|
||||
*/
|
||||
public function purgeAction()
|
||||
{
|
||||
$this->fail("Not implemented yet");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Clicommands;
|
||||
|
||||
use Icinga\Cli\Command;
|
||||
use Icinga\Application\TranslationHelper;
|
||||
|
||||
/**
|
||||
* Translation command
|
||||
*
|
||||
* This command provides different utilities useful for translators. It
|
||||
* allows to add new languages and also to refresh existing translations. All
|
||||
* functionality is available for core components and for modules.
|
||||
*
|
||||
* This is another parapragh.
|
||||
*/
|
||||
class TranslationCommand extends Command
|
||||
{
|
||||
protected $translator;
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->translator = new TranslationHelper(
|
||||
$this->application,
|
||||
$this->params->get('locale', 'C'),
|
||||
$this->params->get('module', 'monitoring') // bullshit. NULL?
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh translation catalogs
|
||||
*
|
||||
* Extracts all translatable strings for a given module (or core) from the
|
||||
* Icingaweb source code, adds those to the existing catalog for the given
|
||||
* locale and marks obsolete translations.
|
||||
*
|
||||
* Usage: icingaweb translation refresh --module <modulename> --locale <lc_LC>
|
||||
*/
|
||||
public function refreshAction()
|
||||
{
|
||||
$this->translator->createTemporaryFileList()->extractTexts();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
use Icinga\Application\Cli;
|
||||
use Icinga\Cli\Params as CliParams;
|
||||
use Icinga\Application\Benchmark;
|
||||
|
||||
set_include_path(
|
||||
realpath(dirname(__FILE__) . '/../library/')
|
||||
. ':' . get_include_path()
|
||||
);
|
||||
|
||||
require_once 'Icinga/Application/Cli.php';
|
||||
$app = Cli::start(dirname(__FILE__) . '/../config/')->dispatch();
|
||||
|
|
@ -1,67 +1,158 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga 2 Web.
|
||||
*
|
||||
* Icinga 2 Web - Head for multiple monitoring backends.
|
||||
* Copyright (C) 2013 Icinga Development Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* @copyright 2013 Icinga Development Team <info@icinga.org>
|
||||
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
||||
* @author Icinga Development Team <info@icinga.org>
|
||||
*/
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Application;
|
||||
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Application\ApplicationBootstrap;
|
||||
use Icinga\Application\Modules\Manager as ModuleManager;
|
||||
use Icinga\Cli\Params;
|
||||
use Icinga\Cli\Loader;
|
||||
use Icinga\Cli\Screen;
|
||||
use Icinga\Application\Benchmark;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
require_once dirname(__FILE__). '/ApplicationBootstrap.php';
|
||||
require_once dirname(__FILE__) . '/ApplicationBootstrap.php';
|
||||
require_once dirname(__FILE__). '/../Exception/ProgrammingError.php';
|
||||
// @codingStandardsIgnoreStop
|
||||
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
/**
|
||||
* Bootstrapping on cli environment
|
||||
*/
|
||||
class Cli extends ApplicationBootstrap
|
||||
{
|
||||
protected $isCli = true;
|
||||
|
||||
protected $params;
|
||||
|
||||
protected $showBenchmark = false;
|
||||
|
||||
protected $watchTimeout;
|
||||
|
||||
protected $cliLoader;
|
||||
|
||||
protected function bootstrap()
|
||||
{
|
||||
$this->assertRunningOnCli();
|
||||
|
||||
return $this->setupConfig()
|
||||
->setupErrorHandling()
|
||||
->setupTimezone();
|
||||
->setupErrorHandling()
|
||||
->setupResourceFactory()
|
||||
->setupModules()
|
||||
->parseParams();
|
||||
}
|
||||
|
||||
public function cliLoader()
|
||||
{
|
||||
if ($this->cliLoader === null) {
|
||||
$this->cliLoader = new Loader($this);
|
||||
}
|
||||
return $this->cliLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup module loader
|
||||
*
|
||||
* TODO: This can be removed once broken bootstrapping has been fixed
|
||||
* Loading the module manager and enabling all modules have former
|
||||
* been two different tasks. CLI does NOT enable any module by default.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function setupModules()
|
||||
{
|
||||
$this->moduleManager = new ModuleManager($this, $this->getConfigDir('enabledModules'));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for module manager
|
||||
*
|
||||
* TODO: This can also be removed once fixed. Making everything private
|
||||
* made this duplication necessary
|
||||
*
|
||||
* @return ModuleManager
|
||||
*/
|
||||
public function getModuleManager()
|
||||
{
|
||||
return $this->moduleManager;
|
||||
}
|
||||
|
||||
protected function parseParams()
|
||||
{
|
||||
$this->params = Params::parse();
|
||||
if ($this->params->shift('help')) {
|
||||
$this->params->unshift('help');
|
||||
}
|
||||
$watch = $this->params->shift('watch');
|
||||
if ($watch === true) {
|
||||
$watch = 5;
|
||||
}
|
||||
if (preg_match('~^\d+$~', $watch)) {
|
||||
$this->watchTimeout = (int) $watch;
|
||||
}
|
||||
|
||||
$this->showBenchmark = (bool) $this->params->shift('benchmark');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getParams()
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
public function dispatch()
|
||||
{
|
||||
Benchmark::measure('Dispatching CLI command');
|
||||
|
||||
if ($this->watchTimeout === null) {
|
||||
$this->dispatchOnce();
|
||||
} else {
|
||||
$this->dispatchEndless();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected function dispatchOnce()
|
||||
{
|
||||
$loader = new Loader($this);
|
||||
$loader->parseParams();
|
||||
$loader->dispatch();
|
||||
Benchmark::measure('All done');
|
||||
if ($this->showBenchmark) {
|
||||
Benchmark::dump();
|
||||
}
|
||||
}
|
||||
|
||||
protected function dispatchEndless()
|
||||
{
|
||||
$loader = new Loader($this);
|
||||
$loader->parseParams();
|
||||
$screen = Screen::instance();
|
||||
while (true) {
|
||||
Benchmark::measure('Watch mode - loop begins');
|
||||
echo $screen->clear();
|
||||
$params = clone($this->params);
|
||||
$loader->dispatch();
|
||||
Benchmark::measure('Dispatch done');
|
||||
if ($this->showBenchmark) {
|
||||
Benchmark::dump();
|
||||
}
|
||||
Benchmark::reset();
|
||||
$this->params = $params;
|
||||
sleep($this->watchTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail if Icinga has not been called on CLI
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws ProgrammingError
|
||||
* @return void
|
||||
*/
|
||||
private function assertRunningOnCli()
|
||||
{
|
||||
if (Platform::isCli()) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new ProgrammingError('Icinga is not running on CLI');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Cli;
|
||||
|
||||
use Icinga\Cli\Loader;
|
||||
use Icinga\Cli\Screen;
|
||||
use Icinga\Application\ApplicationBootstrap as App;
|
||||
use Exception;
|
||||
|
||||
abstract class Command
|
||||
{
|
||||
protected $app;
|
||||
protected $docs;
|
||||
protected $params;
|
||||
protected $screen;
|
||||
protected $isVerbose;
|
||||
protected $isDebugging;
|
||||
|
||||
protected $moduleName;
|
||||
protected $commandName;
|
||||
protected $actionName;
|
||||
|
||||
protected $defaultActionName = 'default';
|
||||
|
||||
public function __construct(App $app, $moduleName, $commandName, $actionName, $initialize = true)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->moduleName = $moduleName;
|
||||
$this->commandName = $commandName;
|
||||
$this->actionName = $actionName;
|
||||
$this->params = $app->getParams();
|
||||
$this->screen = Screen::instance($app);
|
||||
$this->isVerbose = $this->params->shift('verbose', false);
|
||||
$this->isDebuging = $this->params->shift('debug', false);
|
||||
if ($initialize) {
|
||||
$this->init();
|
||||
}
|
||||
}
|
||||
|
||||
public function hasRemainingParams()
|
||||
{
|
||||
return $this->params->count() > 0;
|
||||
}
|
||||
|
||||
public function fail($msg)
|
||||
{
|
||||
throw new Exception($msg);
|
||||
}
|
||||
|
||||
public function getDefaultActionName()
|
||||
{
|
||||
return $this->defaultActionName;
|
||||
}
|
||||
|
||||
public function hasDefaultActionName()
|
||||
{
|
||||
return $this->hasActionName($this->defaultActionName);
|
||||
}
|
||||
|
||||
public function hasActionName($name)
|
||||
{
|
||||
$actions = $this->listActions();
|
||||
return in_array($name, $actions);
|
||||
}
|
||||
|
||||
public function listActions()
|
||||
{
|
||||
$actions = array();
|
||||
foreach (get_class_methods($this) as $method) {
|
||||
if (preg_match('~^([A-Za-z0-9]+)Action$~', $method, $m)) {
|
||||
$actions[] = $m[1];
|
||||
}
|
||||
}
|
||||
sort($actions);
|
||||
return $actions;
|
||||
}
|
||||
|
||||
public function docs()
|
||||
{
|
||||
if ($this->docs === null) {
|
||||
$this->docs = new Documentation($this->app);
|
||||
}
|
||||
return $this->docs;
|
||||
}
|
||||
|
||||
public function showUsage($action = null)
|
||||
{
|
||||
if ($action === null) {
|
||||
$action = $this->actionName;
|
||||
}
|
||||
echo $this->docs()->usage(
|
||||
$this->moduleName,
|
||||
$this->commandName,
|
||||
$action
|
||||
);
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Cli;
|
||||
|
||||
use Icinga\Application\ApplicationBootstrap as App;
|
||||
use Icinga\Cli\Documentation\CommentParser;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
|
||||
class Documentation
|
||||
{
|
||||
protected $icinga;
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->loader = $app->cliLoader();
|
||||
}
|
||||
|
||||
public function usage($module = null, $command = null, $action = null)
|
||||
{
|
||||
if ($module !== null) {
|
||||
return $this->moduleUsage($module, $command, $action);
|
||||
}
|
||||
if ($command !== null) {
|
||||
return $this->commandUsage($command, $action);
|
||||
}
|
||||
return $this->globalUsage();
|
||||
}
|
||||
|
||||
public function globalUsage()
|
||||
{
|
||||
$d = "USAGE: icingaweb [module] <command> [action] [options]\n\n"
|
||||
. "Available commands:\n\n";
|
||||
foreach ($this->loader->listCommands() as $command) {
|
||||
$obj = $this->loader->getCommandInstance($command);
|
||||
$d .= sprintf(
|
||||
" %-14s %s\n",
|
||||
$command,
|
||||
$this->getClassTitle($obj)
|
||||
);
|
||||
}
|
||||
$d .= "\nAvailable modules:\n\n";
|
||||
foreach ($this->loader->listModules() as $module) {
|
||||
$d .= ' ' . $module . "\n";
|
||||
}
|
||||
$d .= "\nGlobal options:\n\n"
|
||||
. " --verbose Be verbose\n"
|
||||
. " --debug Show debug output\n"
|
||||
. " --benchmark Show benchmark summary\n"
|
||||
. " --watch [s] Refresh output each <s> seconds (default: 5)\n"
|
||||
;
|
||||
$d .= "\nShow help on a specific command : icingaweb help <command>"
|
||||
. "\nShow help on a specific module : icingaweb help <module>"
|
||||
. "\n";
|
||||
return $d;
|
||||
}
|
||||
|
||||
public function moduleUsage($module, $command = null, $action = null)
|
||||
{
|
||||
$commands = $this->loader->listModuleCommands($module);
|
||||
|
||||
if (empty($commands)) {
|
||||
return "The '$module' module does not provide any CLI commands\n";
|
||||
}
|
||||
$d = '';
|
||||
if ($command) {
|
||||
$obj = $this->loader->getModuleCommandInstance($module, $command);
|
||||
}
|
||||
if ($command === null) {
|
||||
$d = "USAGE: icingaweb $module <command> [<action>] [options]\n\n"
|
||||
. "Available commands:\n\n";
|
||||
foreach ($commands as $command) {
|
||||
$d .= ' ' . $command . "\n";
|
||||
}
|
||||
$d .= "\nShow help on a specific command: icingaweb help $module <command>\n";
|
||||
} elseif ($action === null) {
|
||||
$d .= $this->showCommandActions($obj, $command);
|
||||
} else {
|
||||
$d .= $this->getMethodDocumentation($obj, $action);
|
||||
}
|
||||
return $d;
|
||||
}
|
||||
|
||||
protected function showCommandActions($command, $name)
|
||||
{
|
||||
$actions = $command->listActions();
|
||||
$d = $this->getClassDocumentation($command)
|
||||
. "Available actions:\n\n";
|
||||
foreach ($actions as $action) {
|
||||
$d .= sprintf(
|
||||
" %-14s %s\n",
|
||||
$action,
|
||||
$this->getMethodTitle($command, $action)
|
||||
);
|
||||
}
|
||||
$d .= "\nShow help on a specific action: icingaweb help $name <action>\n";
|
||||
return $d;
|
||||
}
|
||||
|
||||
public function commandUsage($command, $action = null)
|
||||
{
|
||||
$obj = $this->loader->getCommandInstance($command);
|
||||
$d = "\n";
|
||||
if ($action === null) {
|
||||
$d .= $this->showCommandActions($obj, $command);
|
||||
} else {
|
||||
$d .= $this->getMethodDocumentation($obj, $action);
|
||||
}
|
||||
return $d;
|
||||
}
|
||||
|
||||
protected function getClassTitle($class)
|
||||
{
|
||||
$ref = new ReflectionClass($class);
|
||||
$comment = new CommentParser($ref->getDocComment());
|
||||
return $comment->getTitle();
|
||||
}
|
||||
|
||||
protected function getClassDocumentation($class)
|
||||
{
|
||||
$ref = new ReflectionClass($class);
|
||||
$comment = new CommentParser($ref->getDocComment());
|
||||
return $comment->dump();
|
||||
}
|
||||
|
||||
protected function getMethodTitle($class, $method)
|
||||
{
|
||||
$ref = new ReflectionMethod($class, $method . 'Action');
|
||||
$comment = new CommentParser($ref->getDocComment());
|
||||
return $comment->getTitle();
|
||||
}
|
||||
|
||||
protected function getMethodDocumentation($class, $method)
|
||||
{
|
||||
$ref = new ReflectionMethod($class, $method . 'Action');
|
||||
$comment = new CommentParser($ref->getDocComment());
|
||||
return $comment->dump();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Cli\Documentation;
|
||||
|
||||
class CommentParser
|
||||
{
|
||||
protected $raw;
|
||||
protected $plain;
|
||||
protected $title;
|
||||
protected $paragraphs = array();
|
||||
|
||||
public function __construct($raw)
|
||||
{
|
||||
$this->raw = $raw;
|
||||
if ($raw) {
|
||||
$this->parse();
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
protected function parse()
|
||||
{
|
||||
$plain = $this->raw;
|
||||
|
||||
// Strip comment start /**
|
||||
$plain = preg_replace('~^/\s*\*\*\n~s', '', $plain);
|
||||
|
||||
// Strip comment end */
|
||||
$plain = preg_replace('~\n\s*\*/\s*~s', "\n", $plain);
|
||||
$p = null;
|
||||
foreach (preg_split('~\n~', $plain) as $line) {
|
||||
|
||||
// Strip * at line start
|
||||
$line = preg_replace('~^\s*\*\s?~', '', $line);
|
||||
$line = rtrim($line);
|
||||
if ($this->title === null) {
|
||||
$this->title = $line;
|
||||
continue;
|
||||
}
|
||||
if ($p === null && empty($this->paragraphs)) {
|
||||
$p = & $this->paragraphs[];
|
||||
}
|
||||
|
||||
if ($line === '') {
|
||||
if ($p !== null) {
|
||||
$p = & $this->paragraphs[];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ($p === null) {
|
||||
$p = $line;
|
||||
} else {
|
||||
if (substr($line, 0, 2) === ' ') {
|
||||
$p .= "\n" . $line;
|
||||
} else {
|
||||
$p .= ' ' . $line;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($p === null) {
|
||||
array_pop($this->paragraphs);
|
||||
}
|
||||
}
|
||||
|
||||
public function dump()
|
||||
{
|
||||
$res = $this->title . "\n" . str_repeat('=', strlen($this->title)) . "\n\n";
|
||||
foreach ($this->paragraphs as $p) {
|
||||
$res .= wordwrap($p, 72) . "\n\n";
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,399 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Cli;
|
||||
|
||||
use Icinga\Application\ApplicationBootstrap as App;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Cli\Params;
|
||||
use Icinga\Cli\Screen;
|
||||
use Icinga\Cli\Documentation;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class Loader
|
||||
{
|
||||
protected $app;
|
||||
|
||||
protected $docs;
|
||||
|
||||
protected $commands;
|
||||
|
||||
protected $modules;
|
||||
|
||||
protected $moduleCommands = array();
|
||||
|
||||
protected $coreAppDir;
|
||||
|
||||
protected $screen;
|
||||
|
||||
protected $moduleName;
|
||||
|
||||
protected $commandName;
|
||||
|
||||
protected $actionName; // Should this better be moved to the Command?
|
||||
|
||||
/**
|
||||
* [$command] = $class;
|
||||
*/
|
||||
protected $commandClassMap = array();
|
||||
|
||||
/**
|
||||
* [$command] = $file;
|
||||
*/
|
||||
protected $commandFileMap = array();
|
||||
|
||||
/**
|
||||
* [$module][$command] = $class;
|
||||
*/
|
||||
protected $moduleClassMap = array();
|
||||
|
||||
/**
|
||||
* [$module][$command] = $file;
|
||||
*/
|
||||
protected $moduleFileMap = array();
|
||||
|
||||
protected $commandInstances = array();
|
||||
|
||||
protected $moduleInstances = array();
|
||||
|
||||
protected $lastSuggestions = array();
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->coreAppDir = ICINGA_APPDIR . '/clicommands';
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen shortcut
|
||||
*
|
||||
* @return Screen
|
||||
*/
|
||||
protected function screen()
|
||||
{
|
||||
if ($this->screen === null) {
|
||||
$this->screen = Screen::instance();
|
||||
}
|
||||
return $this->screen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Documentation shortcut
|
||||
*
|
||||
* @return Documentation
|
||||
*/
|
||||
protected function docs()
|
||||
{
|
||||
if ($this->docs === null) {
|
||||
$this->docs = new Documentation($this->app);
|
||||
}
|
||||
return $this->docs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show given message and exit
|
||||
*
|
||||
* @param string $msg message to show
|
||||
*/
|
||||
public function fail($msg)
|
||||
{
|
||||
printf("%s: %s\n", $this->screen()->colorize('ERROR', 'red'), $msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
public function getCommandInstance($command)
|
||||
{
|
||||
if (! array_key_exists($command, $this->commandInstances)) {
|
||||
$this->assertCommandExists($command);
|
||||
require_once $this->commandFileMap[$command];
|
||||
$className = $this->commandClassMap[$command];
|
||||
$this->commandInstances[$command] = new $className(
|
||||
$this->app,
|
||||
null,
|
||||
$command,
|
||||
null,
|
||||
false
|
||||
);
|
||||
}
|
||||
return $this->commandInstances[$command];
|
||||
}
|
||||
|
||||
public function getModuleCommandInstance($module, $command)
|
||||
{
|
||||
if (! array_key_exists($command, $this->moduleInstances[$module])) {
|
||||
$this->assertModuleCommandExists($module, $command);
|
||||
require_once $this->moduleFileMap[$module][$command];
|
||||
$className = $this->moduleClassMap[$module][$command];
|
||||
$this->moduleInstances[$module][$command] = new $className(
|
||||
$this->app,
|
||||
$module,
|
||||
$command,
|
||||
null,
|
||||
false
|
||||
);
|
||||
}
|
||||
return $this->moduleInstances[$module][$command];
|
||||
}
|
||||
|
||||
public function showLastSuggestions()
|
||||
{
|
||||
if (! empty($this->lastSuggestions)) {
|
||||
foreach ($this->lastSuggestions as & $s) {
|
||||
$s = $this->screen()->colorize($s, 'lightblue');
|
||||
}
|
||||
printf(
|
||||
"Did you mean %s?\n",
|
||||
implode(" or ", $this->lastSuggestions)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function parseParams(Params $params = null)
|
||||
{
|
||||
if ($params === null) {
|
||||
$params = $this->app->getParams();
|
||||
}
|
||||
$first = $params->shift();
|
||||
if (! $first) {
|
||||
return;
|
||||
}
|
||||
$found = $this->resolveName($first);
|
||||
if (! $found) {
|
||||
$msg = "There is no such module or command: '$first'";
|
||||
printf("%s: %s\n", $this->screen()->colorize('ERROR', 'red'), $msg);
|
||||
$this->showLastSuggestions();
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
$obj = null;
|
||||
if ($this->hasCommand($found)) {
|
||||
$this->commandName = $found;
|
||||
$obj = $this->getCommandInstance($this->commandName);
|
||||
} elseif ($this->hasModule($found)) {
|
||||
$this->moduleName = $found;
|
||||
$command = $this->resolveModuleCommandName($found, $params->shift());
|
||||
if ($command) {
|
||||
$this->commandName = $command;
|
||||
$obj = $this->getModuleCommandInstance(
|
||||
$this->moduleName,
|
||||
$this->commandName
|
||||
);
|
||||
}
|
||||
}
|
||||
if ($obj !== null) {
|
||||
$action = $this->resolveObjectActionName(
|
||||
$obj,
|
||||
$params->getStandalone()
|
||||
);
|
||||
if ($obj->hasActionName($action)) {
|
||||
$this->actionName = $action;
|
||||
$params->shift();
|
||||
} elseif ($obj->hasDefaultActionName()) {
|
||||
$this->actionName = $obj->getDefaultActionName();
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function handleParams(Params $params = null)
|
||||
{
|
||||
$this->parseParams($params);
|
||||
$this->dispatch();
|
||||
}
|
||||
|
||||
public function dispatch()
|
||||
{
|
||||
if ($this->commandName === null) {
|
||||
echo $this->docs()->usage($this->moduleName);
|
||||
return false;
|
||||
} elseif ($this->actionName === null) {
|
||||
echo $this->docs()->usage($this->moduleName, $this->commandName);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($this->moduleName) {
|
||||
$this->app->getModuleManager()->loadModule($this->moduleName);
|
||||
$obj = $this->getModuleCommandInstance(
|
||||
$this->moduleName,
|
||||
$this->commandName
|
||||
);
|
||||
} else {
|
||||
$obj = $this->getCommandInstance($this->commandName);
|
||||
}
|
||||
$obj->init();
|
||||
return $obj->{$this->actionName . 'Action'}();
|
||||
} catch (Exception $e) {
|
||||
$this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected function searchMatch($needle, $haystack)
|
||||
{
|
||||
$stack = $haystack;
|
||||
$search = $needle;
|
||||
$this->lastSuggestions = array();
|
||||
while (strlen($search) > 0) {
|
||||
$len = strlen($search);
|
||||
foreach ($stack as & $s) {
|
||||
$s = substr($s, 0, $len);
|
||||
}
|
||||
|
||||
$res = array_keys($stack, $search, true);
|
||||
if (count($res) === 1) {
|
||||
$found = $haystack[$res[0]];
|
||||
if (substr($found, 0, strlen($needle)) === $needle) {
|
||||
return $found;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} elseif (count($res) > 1) {
|
||||
foreach ($res as $key) {
|
||||
$this->lastSuggestions[] = $haystack[$key];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$search = substr($search, 0, -1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function resolveName($name)
|
||||
{
|
||||
return $this->searchMatch(
|
||||
$name,
|
||||
array_merge($this->listCommands(), $this->listModules())
|
||||
);
|
||||
}
|
||||
|
||||
public function resolveCommandName($name)
|
||||
{
|
||||
return $this->searchMatch($name, $this->listCommands());
|
||||
}
|
||||
|
||||
public function resolveModuleName($name)
|
||||
{
|
||||
return $this->searchMatch($name, $this->listModules());
|
||||
}
|
||||
|
||||
public function resolveModuleCommandName($module, $name)
|
||||
{
|
||||
return $this->searchMatch($name, $this->listModuleCommands($module));
|
||||
}
|
||||
|
||||
public function resolveObjectActionName($obj, $name)
|
||||
{
|
||||
return $this->searchMatch($name, $obj->listActions());
|
||||
}
|
||||
|
||||
protected function assertModuleExists($module)
|
||||
{
|
||||
if (! $this->hasModule($module)) {
|
||||
throw new ProgrammingError(
|
||||
sprintf('There is no such module: %s', $module)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function assertCommandExists($command)
|
||||
{
|
||||
if (! $this->hasCommand($command)) {
|
||||
throw new ProgrammingError(
|
||||
sprintf('There is no such command: %s', $command)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function assertModuleCommandExists($module, $command)
|
||||
{
|
||||
$this->assertModuleExists($module);
|
||||
if (! $this->hasModuleCommand($module, $command)) {
|
||||
throw new ProgrammingError(
|
||||
sprintf("The module '%s' has no such command: %s", $module, $command)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function hasCommand($name)
|
||||
{
|
||||
return in_array($name, $this->listCommands());
|
||||
}
|
||||
|
||||
public function hasModule($name)
|
||||
{
|
||||
return in_array($name, $this->listModules());
|
||||
}
|
||||
|
||||
public function hasModuleCommand($module, $name)
|
||||
{
|
||||
return in_array($name, $this->listModuleCommands($module));
|
||||
}
|
||||
|
||||
public function listModules()
|
||||
{
|
||||
if ($this->modules === null) {
|
||||
$this->modules = array();
|
||||
$this->modules = $this->app->getModuleManager()->listEnabledModules();
|
||||
sort($this->modules);
|
||||
}
|
||||
return $this->modules;
|
||||
}
|
||||
|
||||
protected function retrieveCommandsFromDir($dirname)
|
||||
{
|
||||
$commands = array();
|
||||
if (! @file_exists($dirname) || ! is_readable($dirname)) {
|
||||
return $commands;
|
||||
}
|
||||
|
||||
$base = opendir($dirname);
|
||||
if ($base === false) {
|
||||
return $commands;
|
||||
}
|
||||
while (false !== ($dir = readdir($base))) {
|
||||
if ($dir[0] === '.') {
|
||||
continue;
|
||||
}
|
||||
if (preg_match('~^([A-Za-z0-9]+)Command\.php$~', $dir, $m)) {
|
||||
$cmd = strtolower($m[1]);
|
||||
$commands[] = $cmd;
|
||||
}
|
||||
}
|
||||
sort($commands);
|
||||
return $commands;
|
||||
}
|
||||
|
||||
public function listCommands()
|
||||
{
|
||||
if ($this->commands === null) {
|
||||
$this->commands = array();
|
||||
$ns = 'Icinga\\Clicommands\\';
|
||||
$this->commands = $this->retrieveCommandsFromDir($this->coreAppDir);
|
||||
foreach ($this->commands as $cmd) {
|
||||
$this->commandClassMap[$cmd] = $ns . ucfirst($cmd) . 'Command';
|
||||
$this->commandFileMap[$cmd] = $this->coreAppDir . '/' . ucfirst($cmd) . 'Command.php';
|
||||
}
|
||||
}
|
||||
return $this->commands;
|
||||
}
|
||||
|
||||
public function listModuleCommands($module)
|
||||
{
|
||||
if (! array_key_exists($module, $this->moduleCommands)) {
|
||||
$ns = 'Icinga\\Module\\' . ucfirst($module) . '\\Clicommands\\';
|
||||
$this->assertModuleExists($module);
|
||||
$manager = $this->app->getModuleManager();
|
||||
$manager->enableModule($module);
|
||||
$dir = $manager->getModuleDir($module) . '/application/clicommands';
|
||||
$this->moduleCommands[$module] = $this->retrieveCommandsFromDir($dir);
|
||||
$this->moduleInstances[$module] = array();
|
||||
foreach ($this->moduleCommands[$module] as $cmd) {
|
||||
$this->moduleClassMap[$module][$cmd] = $ns . ucfirst($cmd) . 'Command';
|
||||
$this->moduleFileMap[$module][$cmd] = $dir . '/' . ucfirst($cmd) . 'Command.php';
|
||||
}
|
||||
}
|
||||
return $this->moduleCommands[$module];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Cli;
|
||||
|
||||
class Params
|
||||
{
|
||||
protected $program;
|
||||
protected $standalone = array();
|
||||
protected $params = array();
|
||||
|
||||
public function __construct($argv)
|
||||
{
|
||||
$this->program = array_shift($argv);
|
||||
for ($i = 0; $i < count($argv); $i++) {
|
||||
if (substr($argv[$i], 0, 2) === '--') {
|
||||
$key = substr($argv[$i], 2);
|
||||
if (! isset($argv[$i + 1]) || substr($argv[$i + 1], 0, 2) === '--') {
|
||||
$this->params[$key] = true;
|
||||
} else {
|
||||
$this->params[$key] = $argv[++$i];
|
||||
}
|
||||
} else {
|
||||
$this->standalone[] = $argv[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getStandalone($pos = 0, $default = null)
|
||||
{
|
||||
if (isset($this->standalone[$pos])) {
|
||||
return $this->standalone[$pos];
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->standalone) + count($this->params);
|
||||
}
|
||||
|
||||
public function getParams()
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->get($key);
|
||||
}
|
||||
|
||||
public function has($key)
|
||||
{
|
||||
return array_key_exists($key, $this->params);
|
||||
}
|
||||
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
if ($this->has($key)) {
|
||||
return $this->params[$key];
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->params[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function remove($keys = array())
|
||||
{
|
||||
if (! is_array($keys)) {
|
||||
$keys = array($keys);
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $this->params)) {
|
||||
unset($this->params[$key]);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function without($keys = array())
|
||||
{
|
||||
$params = clone($this);
|
||||
return $params->remove($keys);
|
||||
}
|
||||
|
||||
public function shift($key = null, $default = null)
|
||||
{
|
||||
if ($key === null) {
|
||||
if (count($this->standalone) > 0) {
|
||||
return array_shift($this->standalone);
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
$result = $this->get($key, $default);
|
||||
$this->remove($key);
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function unshift($key)
|
||||
{
|
||||
array_unshift($this->standalone, $key);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function parse($argv = null)
|
||||
{
|
||||
if ($argv === null) {
|
||||
$argv = $GLOBALS['argv'];
|
||||
}
|
||||
$params = new self($argv);
|
||||
return $params;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Cli;
|
||||
|
||||
use Icinga\Cli\Screen\AnsiScreen;
|
||||
|
||||
class Screen
|
||||
{
|
||||
protected static $instance;
|
||||
|
||||
public function instance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new AnsiScreen();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Cli\Screen;
|
||||
|
||||
use Icinga\Cli\Screen;
|
||||
|
||||
// @see http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
|
||||
class AnsiScreen extends Screen
|
||||
{
|
||||
protected $isUtf8;
|
||||
|
||||
protected $fgColors = array(
|
||||
'black' => '30',
|
||||
'darkgray' => '1;30',
|
||||
'red' => '31',
|
||||
'lightred' => '1;31',
|
||||
'green' => '32',
|
||||
'lightgreen' => '1;32',
|
||||
'brown' => '33',
|
||||
'yellow' => '1;33',
|
||||
'blue' => '34',
|
||||
'lightblue' => '1;34',
|
||||
'purple' => '35',
|
||||
'lightpurple' => '1;35',
|
||||
'cyan' => '36',
|
||||
'lightcyan' => '1;36',
|
||||
'lightgray' => '37',
|
||||
'white' => '1;37',
|
||||
);
|
||||
|
||||
protected $bgColors = array(
|
||||
'black' => '40',
|
||||
'red' => '41',
|
||||
'green' => '42',
|
||||
'brown' => '43',
|
||||
'blue' => '44',
|
||||
'purple' => '45',
|
||||
'cyan' => '46',
|
||||
'lightgray' => '47',
|
||||
);
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function getColumns()
|
||||
{
|
||||
$cols = (int) getenv('COLUMNS');
|
||||
if (! $cols) {
|
||||
// stty -a ?
|
||||
$cols = (int) exec('tput cols');
|
||||
}
|
||||
if (! $cols) {
|
||||
$cols = 80;
|
||||
}
|
||||
return $cols;
|
||||
}
|
||||
|
||||
public function getRows()
|
||||
{
|
||||
$rows = (int) getenv('ROWS');
|
||||
if (! $rows) {
|
||||
// stty -a ?
|
||||
$rows = (int) exec('tput rows');
|
||||
}
|
||||
if (! $rows) {
|
||||
$rows = 25;
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
public function hasUtf8()
|
||||
{
|
||||
if ($this->isUtf8 === null) {
|
||||
// null should equal 0 here, however seems to equal '' on some systems:
|
||||
$current = setlocale(LC_ALL, 0);
|
||||
|
||||
$parts = preg_split('/;/', $current);
|
||||
$lc_parts = array();
|
||||
foreach ($parts as $part) {
|
||||
if (strpos($part, '=') === false) {
|
||||
continue;
|
||||
}
|
||||
list($key, $val) = preg_split('/=/', $part, 2);
|
||||
$lc_parts[$key] = $val;
|
||||
}
|
||||
|
||||
$this->isUtf8 = array_key_exists('LC_CTYPE', $lc_parts)
|
||||
&& preg_match('~\.UTF-8$~i', $lc_parts['LC_CTYPE']);
|
||||
}
|
||||
return $this->isUtf8;
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
return "\033[2J" // Clear the whole screen
|
||||
. "\033[1;1H" // Move the cursor to row 1, column 1
|
||||
. "\033[1S"; // Scroll whole page up by 1 line (why?)
|
||||
}
|
||||
|
||||
protected function fgColor($color)
|
||||
{
|
||||
if (! array_key_exists($color, $this->fgColors)) {
|
||||
throw new \Exception(sprintf('There is no such foreground color: %s', $color));
|
||||
}
|
||||
return $this->fgColors[$color];
|
||||
}
|
||||
|
||||
protected function bgColor($color)
|
||||
{
|
||||
if (! array_key_exists($color, $this->bgColors)) {
|
||||
throw new \Exception(sprintf('There is no such background color: %s', $color));
|
||||
}
|
||||
return $this->bgColors[$color];
|
||||
}
|
||||
|
||||
protected function startColor($fgColor = null, $bgColor = null)
|
||||
{
|
||||
$escape = "ESC[";
|
||||
$parts = array();
|
||||
if ($fgColor !== null
|
||||
&& $bgColor !== null
|
||||
&& ! array_key_exists($bgColor, $this->bgColors)
|
||||
&& array_key_exists($bgColor, $this->fgColors)
|
||||
&& array_key_exists($fgColor, $this->bgColors)
|
||||
) {
|
||||
$parts[] = '7'; // reverse video, negative image
|
||||
$parts[] = $this->bgColor($fgColor);
|
||||
$parts[] = $this->fgColor($bgColor);
|
||||
} else {
|
||||
if ($fgColor !== null) {
|
||||
$parts[] = $this->fgColor($fgColor);
|
||||
}
|
||||
if ($bgColor !== null) {
|
||||
$parts[] = $this->bgColor($bgColor);
|
||||
}
|
||||
}
|
||||
if (empty($parts)) {
|
||||
return '';
|
||||
}
|
||||
return "\033[" . implode(';', $parts) . 'm';
|
||||
}
|
||||
|
||||
public function underline($text)
|
||||
{
|
||||
return "\033[4m"
|
||||
. $text
|
||||
. "\033[0m"; // Reset color codes
|
||||
}
|
||||
|
||||
public function colorize($text, $fgColor = null, $bgColor = null)
|
||||
{
|
||||
return $this->startColor($fgColor, $bgColor)
|
||||
. $text
|
||||
. "\033[0m"; // Reset color codes
|
||||
}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Monitoring\Clicommands;
|
||||
|
||||
use Icinga\Module\Monitoring\Backend;
|
||||
use Icinga\Module\Monitoring\Cli\CliUtils;
|
||||
use Icinga\Util\Format;
|
||||
use Icinga\Cli\Command;
|
||||
use Icinga\File\Csv;
|
||||
|
||||
/**
|
||||
* List and filter monitored objects
|
||||
*
|
||||
* This command allows you to search and visualize your monitored objects in
|
||||
* different ways.
|
||||
*
|
||||
* USAGE
|
||||
*
|
||||
* icingaweb monitoring list [<type>] [options]
|
||||
*
|
||||
* OPTIONS
|
||||
*
|
||||
* --verbose Show detailled output
|
||||
* --showsql Dump generated SQL query (DB backend only)
|
||||
*
|
||||
* --format <csv|json|<custom>>
|
||||
* Dump columns in the given format. <custom> format allows $column$
|
||||
* placeholders, e.g. --format '$host$: $service$'
|
||||
*
|
||||
* --<column> [filter]
|
||||
* Filter given column by optional filter. Boolean (1/0) columns are true
|
||||
* if no filter value is given.
|
||||
*
|
||||
* EXAMPLES
|
||||
*
|
||||
* icingaweb monitoring list --unhandled
|
||||
* icingaweb monitoring list --host local* --service *disk*
|
||||
* icingaweb monitoring list --format '$host_name$: $service_description$'
|
||||
*/
|
||||
class ListCommand extends Command
|
||||
{
|
||||
protected $backend;
|
||||
protected $dumpSql;
|
||||
protected $defaultActionName = 'status';
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->backend = Backend::createBackend($this->params->shift('backend'));
|
||||
$this->dumpSql = $this->params->shift('showsql');
|
||||
}
|
||||
|
||||
protected function getQuery($table, $columns)
|
||||
{
|
||||
$limit = $this->params->shift('limit');
|
||||
$format = $this->params->shift('format');
|
||||
if ($format !== null) {
|
||||
if ($this->params->has('columns')) {
|
||||
$columnParams = preg_split(
|
||||
'/,/',
|
||||
$this->params->shift('columns')
|
||||
);
|
||||
$columns = array();
|
||||
foreach ($columnParams as $col) {
|
||||
if (false !== ($pos = strpos($col, '='))) {
|
||||
$columns[substr($col, 0, $pos)] = substr($col, $pos + 1);
|
||||
} else {
|
||||
$columns[] = $col;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$query = $this->backend->select()->from($table, $columns);
|
||||
if ($limit) {
|
||||
$query->limit($limit, $this->params->shift('offset'));
|
||||
}
|
||||
foreach ($this->params->getParams() as $col => $filter) {
|
||||
$query->where($col, $filter);
|
||||
}
|
||||
// $query->applyFilters($this->params->getParams());
|
||||
if ($this->dumpSql) {
|
||||
echo wordwrap($query->dump(), 72);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($format !== null) {
|
||||
$this->showFormatted($query, $format, $columns);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function showFormatted($query, $format, $columns)
|
||||
{
|
||||
switch($format) {
|
||||
case 'json':
|
||||
echo json_encode($query->fetchAll());
|
||||
break;
|
||||
case 'csv':
|
||||
Csv::fromQuery($query)->dump();
|
||||
break;
|
||||
default:
|
||||
preg_match_all('~\$([a-z0-9_-]+)\$~', $format, $m);
|
||||
$words = array();
|
||||
foreach ($columns as $key => $col) {
|
||||
if (is_numeric($key)) {
|
||||
if (in_array($col, $m[1])) {
|
||||
$words[] = $col;
|
||||
}
|
||||
} else {
|
||||
if (in_array($key, $m[1])) {
|
||||
$words[] = $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($query->fetchAll() as $row) {
|
||||
$output = $format;
|
||||
foreach ($words as $word) {
|
||||
$output = preg_replace(
|
||||
'~\$' . $word . '\$~',
|
||||
$row->{$word},
|
||||
$output
|
||||
);
|
||||
}
|
||||
echo $output . "\n";
|
||||
}
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
public function statusAction()
|
||||
{
|
||||
$columns = array(
|
||||
'host_name',
|
||||
'host_state',
|
||||
'host_output',
|
||||
'host_handled',
|
||||
'host_acknowledged',
|
||||
'host_in_downtime',
|
||||
'service_description',
|
||||
'service_state',
|
||||
'service_acknowledged',
|
||||
'service_in_downtime',
|
||||
'service_handled',
|
||||
'service_output',
|
||||
'service_last_state_change'
|
||||
);
|
||||
$query = $this->getQuery('status', $columns)
|
||||
->order('host_name');
|
||||
echo $this->renderQuery($query);
|
||||
}
|
||||
|
||||
protected function renderQuery($query)
|
||||
{
|
||||
$out = '';
|
||||
$last_host = null;
|
||||
$screen = $this->screen;
|
||||
$utils = new CliUtils($screen);
|
||||
$maxCols = $screen->getColumns();
|
||||
$rows = $query->fetchAll();
|
||||
$count = $query->count();
|
||||
$count = count($rows);
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$row = & $rows[$i];
|
||||
|
||||
$utils->setHostState($row->host_state);
|
||||
if (! array_key_exists($i + 1, $rows)
|
||||
|| $row->host_name !== $rows[$i + 1]->host_name
|
||||
) {
|
||||
$lastService = true;
|
||||
} else {
|
||||
$lastService = false;
|
||||
}
|
||||
|
||||
$hostUnhandled = ! ($row->host_state == 0 || $row->host_handled);
|
||||
|
||||
if ($row->host_name !== $last_host) {
|
||||
if (isset($row->service_description)) {
|
||||
$out .= "\n";
|
||||
}
|
||||
|
||||
$hostTxt = $utils->shortHostState();
|
||||
if ($hostUnhandled) {
|
||||
$out .= $utils->hostStateBackground(
|
||||
sprintf(' %s ', $utils->shortHostState())
|
||||
);
|
||||
} else {
|
||||
$out .= sprintf(
|
||||
'%s %s ',
|
||||
$utils->hostStateBackground(' '),
|
||||
$utils->shortHostState()
|
||||
);
|
||||
}
|
||||
$out .= sprintf(
|
||||
" %s%s: %s\n",
|
||||
$screen->underline($row->host_name),
|
||||
$screen->colorize($utils->objectStateFlags('host', $row), 'lightblue'),
|
||||
$row->host_output
|
||||
);
|
||||
|
||||
if (isset($row->services_ok)) {
|
||||
$out .= sprintf(
|
||||
"%d services, %d problems (%d unhandled), %d OK\n",
|
||||
$row->services_cnt,
|
||||
$row->services_problem,
|
||||
$row->services_problem_unhandled,
|
||||
$row->services_ok
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$last_host = $row->host_name;
|
||||
if (! isset($row->service_description)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$utils->setServiceState($row->service_state);
|
||||
$serviceUnhandled = ! (
|
||||
$row->service_state == 0 || $row->service_handled
|
||||
);
|
||||
|
||||
if ($lastService) {
|
||||
$straight = ' ';
|
||||
$leaf = '└';
|
||||
} else {
|
||||
$straight = '│';
|
||||
$leaf = '├';
|
||||
}
|
||||
$out .= $utils->hostStateBackground(' ');
|
||||
|
||||
if ($serviceUnhandled) {
|
||||
$out .= $utils->serviceStateBackground(
|
||||
sprintf(' %s ', $utils->shortServiceState())
|
||||
);
|
||||
$emptyBg = ' ';
|
||||
$emptySpace = '';
|
||||
} else {
|
||||
$out .= sprintf(
|
||||
'%s %s ',
|
||||
$utils->serviceStateBackground(' '),
|
||||
$utils->shortServiceState()
|
||||
);
|
||||
$emptyBg = ' ';
|
||||
$emptySpace = ' ';
|
||||
}
|
||||
|
||||
$emptyLine = "\n"
|
||||
. $utils->hostStateBackground(' ')
|
||||
. $utils->serviceStateBackground($emptyBg)
|
||||
. $emptySpace
|
||||
. ' ' . $straight . ' ';
|
||||
|
||||
$wrappedOutput = wordwrap(
|
||||
preg_replace('~\@{3,}~', '@@@', $row->service_output),
|
||||
$maxCols - 13
|
||||
) . "\n";
|
||||
$out .= sprintf(
|
||||
" %1s─ %s%s (since %s)",
|
||||
$leaf,
|
||||
$screen->underline($row->service_description),
|
||||
$screen->colorize($utils->objectStateFlags('service', $row), 'lightblue'),
|
||||
Format::timeSince($row->service_last_state_change)
|
||||
);
|
||||
if ($this->isVerbose) {
|
||||
$out .= $emptyLine . preg_replace(
|
||||
'/\n/',
|
||||
$emptyLine,
|
||||
$wrappedOutput
|
||||
) . "\n";
|
||||
} else {
|
||||
$out .= "\n";
|
||||
}
|
||||
}
|
||||
|
||||
$out .= "\n";
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Monitoring\Clicommands;
|
||||
|
||||
use Icinga\Protocol\Nrpe\Connection;
|
||||
use Icinga\Cli\Command;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* NRPE
|
||||
*/
|
||||
class NrpeCommand extends Command
|
||||
{
|
||||
protected $defaultActionName = 'check';
|
||||
|
||||
/**
|
||||
* Execute an NRPE command
|
||||
*
|
||||
* This command will execute an NRPE check, fire it against the given host
|
||||
* and also pass through all your parameters. Output will be shown, exit
|
||||
* code respected.
|
||||
*
|
||||
* USAGE
|
||||
*
|
||||
* icingaweb monitoring nrpe <host> <command> [--ssl] [nrpe options]
|
||||
*
|
||||
* EXAMPLE
|
||||
*
|
||||
* icingaweb monitoring nrpe 127.0.0.1 CheckMEM --ssl --MaxWarn 80% \
|
||||
* --MaxCrit 90% --type physical
|
||||
*/
|
||||
public function checkAction()
|
||||
{
|
||||
$host = $this->params->shift();
|
||||
if (! $host) {
|
||||
echo $this->showUsage();
|
||||
exit(3);
|
||||
}
|
||||
$command = $this->params->shift(null, '_NRPE_CHECK');
|
||||
$port = $this->params->shift('port', 5666);
|
||||
try {
|
||||
$nrpe = new Connection($host, $port);
|
||||
if ($this->params->shift('ssl')) {
|
||||
$nrpe->useSsl();
|
||||
}
|
||||
$args = array();
|
||||
foreach ($this->params->getParams() as $k => $v) {
|
||||
$args[] = $k . '=' . $v;
|
||||
}
|
||||
echo $nrpe->sendCommand($command, $args) . "\n";
|
||||
exit($nrpe->getLastReturnCode());
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
exit(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Monitoring\Cli;
|
||||
|
||||
use Icinga\Cli\Screen;
|
||||
|
||||
class CliUtils
|
||||
{
|
||||
protected $hostColors = array(
|
||||
0 => array('black', 'lightgreen'),
|
||||
1 => array('black', 'lightred'),
|
||||
2 => array('black', 'brown'),
|
||||
99 => array('black', 'lightgray'),
|
||||
);
|
||||
protected $serviceColors = array(
|
||||
0 => array('black', 'lightgreen'),
|
||||
1 => array('black', 'yellow'),
|
||||
2 => array('black', 'lightred'),
|
||||
3 => array('black', 'lightpurple'),
|
||||
99 => array('black', 'lightgray'),
|
||||
);
|
||||
protected $hostStates = array(
|
||||
0 => 'UP',
|
||||
1 => 'DOWN',
|
||||
2 => 'UNREACHABLE',
|
||||
99 => 'PENDING',
|
||||
);
|
||||
|
||||
protected $serviceStates = array(
|
||||
0 => 'OK',
|
||||
1 => 'WARNING',
|
||||
2 => 'CRITICAL',
|
||||
3 => 'UNKNOWN',
|
||||
99 => 'PENDING',
|
||||
);
|
||||
|
||||
protected $screen;
|
||||
protected $hostState;
|
||||
protected $serviceState;
|
||||
|
||||
public function __construct(Screen $screen)
|
||||
{
|
||||
$this->screen = $screen;
|
||||
}
|
||||
|
||||
public function setHostState($state)
|
||||
{
|
||||
$this->hostState = $state;
|
||||
}
|
||||
|
||||
public function setServiceState($state)
|
||||
{
|
||||
$this->serviceState = $state;
|
||||
}
|
||||
|
||||
public function shortHostState($state = null)
|
||||
{
|
||||
if ($state === null) {
|
||||
$state = $this->hostState;
|
||||
}
|
||||
return sprintf('%-4s', substr($this->hostStates[$state], 0, 4));
|
||||
}
|
||||
|
||||
public function shortServiceState($state = null)
|
||||
{
|
||||
if ($state === null) {
|
||||
$state = $this->serviceState;
|
||||
}
|
||||
return sprintf('%-4s', substr($this->serviceStates[$state], 0, 4));
|
||||
}
|
||||
|
||||
public function hostStateBackground($text, $state = null)
|
||||
{
|
||||
if ($state === null) {
|
||||
$state = $this->hostState;
|
||||
}
|
||||
return $this->screen->colorize(
|
||||
$text,
|
||||
$this->hostColors[$state][0],
|
||||
$this->hostColors[$state][1]
|
||||
);
|
||||
}
|
||||
|
||||
public function serviceStateBackground($text, $state = null)
|
||||
{
|
||||
if ($state === null) {
|
||||
$state = $this->serviceState;
|
||||
}
|
||||
return $this->screen->colorize(
|
||||
$text,
|
||||
$this->serviceColors[$state][0],
|
||||
$this->serviceColors[$state][1]
|
||||
);
|
||||
}
|
||||
|
||||
public function objectStateFlags($type, & $row)
|
||||
{
|
||||
$extra = array();
|
||||
if ($row->{$type . '_in_downtime'}) {
|
||||
if ($this->screen->hasUtf8()) {
|
||||
$extra[] = 'DOWNTIME ⌚';
|
||||
} else {
|
||||
$extra[] = 'DOWNTIME';
|
||||
}
|
||||
}
|
||||
if ($row->{$type . '_acknowledged'}) {
|
||||
if ($this->screen->hasUtf8()) {
|
||||
$extra[] = 'ACK ✓';
|
||||
} else {
|
||||
$extra[] = 'ACK';
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($extra)) {
|
||||
$extra = '';
|
||||
} else {
|
||||
$extra = sprintf(' [ %s ]', implode(', ', $extra));
|
||||
}
|
||||
return $extra;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue