diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:45:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:45:29 +0000 |
commit | 0fa6f7723cca9bf7edc8af84112e8429e82dea84 (patch) | |
tree | 52c73718c9e2b6e4aa3f997791786aa2ee6ebcc9 | |
parent | Initial commit. (diff) | |
download | icingaweb2-module-nagvis-0fa6f7723cca9bf7edc8af84112e8429e82dea84.tar.xz icingaweb2-module-nagvis-0fa6f7723cca9bf7edc8af84112e8429e82dea84.zip |
Adding upstream version 1.1.1.upstream/1.1.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | AUTHORS | 9 | ||||
-rw-r--r-- | COPYING | 339 | ||||
-rw-r--r-- | README.md | 90 | ||||
-rw-r--r-- | application/controllers/ShowController.php | 94 | ||||
-rw-r--r-- | application/views/scripts/show/map.phtml | 18 | ||||
-rw-r--r-- | configuration.php | 41 | ||||
-rw-r--r-- | doc/01-authorization.md | 15 | ||||
-rw-r--r-- | library/Nagvis/RestrictionHelper.php | 41 | ||||
-rw-r--r-- | library/nagvis-includes/CoreAuthModIcingaweb2.php | 129 | ||||
-rw-r--r-- | library/nagvis-includes/CoreAuthorisationModIcingaweb2.php | 183 | ||||
-rw-r--r-- | library/nagvis-includes/CoreLogonIcingaweb2.php | 22 | ||||
-rw-r--r-- | library/nagvis-includes/GlobalBackendicingaweb2.php | 1242 | ||||
-rw-r--r-- | library/nagvis-includes/TODO.md | 1 | ||||
-rw-r--r-- | library/nagvis-includes/init.inc.php | 3 | ||||
-rw-r--r-- | module.info | 12 | ||||
-rw-r--r-- | public/css/icingaweb-nagvis-integration.css | 7 | ||||
-rw-r--r-- | public/css/module.less | 3 | ||||
-rw-r--r-- | public/js/module.js | 54 |
18 files changed, 2303 insertions, 0 deletions
@@ -0,0 +1,9 @@ +Alexander A. Klimov <alexander.klimov@icinga.com> +Dirk Goetz <dirk.goetz@netways.de> +Eric Lippmann <eric.lippmann@icinga.com> +Marius Hein <marius.hein@netways.de> +Markus Waldmueller <markus.waldmueller@netways.de> +Max Stephan <max.stephan@btc-it-services.com> +Michael Friedrich <michael.friedrich@netways.de> +Michael Newton <miken32@gmail.com> +Thomas Gelf <thomas@gelf.net> @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License.
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..bd67a97 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +# Icinga Web 2 Nagvis Module +## Requirements + +This module glues NagVis and Icinga Web 2 together. Both of them are required +to be installed and configured: + +* [Icinga Web 2](https://github.com/icinga/icingaweb2) (>= 2.0.0) +* [NagVis](http://www.nagvis.org/) (>= 1.8) + +## NagVis Configuration +This module provides everything for a complete Icinga Web 2 NagVis +integration, including authentication and authorisation. To get +everything working as expected, your NagVis configuration should contain +the following settings: + +```ini +[global] +authmodule="CoreAuthModIcingaweb2" +authorisationmodule="CoreAuthorisationModIcingaweb2" +logonmodule="LogonIcingaweb2" + +[paths] +htmlcgi = "/icingaweb2" + +[defaults] +backend = "ndomy_1" +urltarget = "_top" +hosturl="[htmlcgi]/monitoring/host/show?host=[host_name]" +hostgroupurl="[htmlcgi]/monitoring/list/hostgroups?hostgroup_name=[hostgroup_name]" +serviceurl="[htmlcgi]/monitoring/service/show?host=[host_name]&service=[service_description]" +servicegroupurl="[htmlcgi]/monitoring/list/servicegroups?servicegroup=[servicegroup_name]" +mapurl="[htmlcgi]/nagvis/show/map?map=[map_name]" +headermenu="0" +stylesheet="icingaweb-nagvis-integration.css" +``` + +The CSS file `public/css/icingaweb-nagvis-integration.css` must be copied to +`<nagvisdir>/share/userfiles/styles`. + +## Module Configuration +For many environments the module needs no special configuration. Usually +you might want to add a bunch of main maps directly to your menu - this +can be done in `<ICINGAWEB_CONFIGDIR>/modules/nagvis/config.ini` like in +the following example: + +```ini +[global] +default-map = demo-overview + +[menu] +demo-germany = Germany +demo-ham-racks = Hamburg +``` + +## Icinga Web 2 Configuration + +Since Icinga Web 2.2.0 session cookies are restricted to `/icingaweb2` or +your Icinga Web 2 base path. This means that your browser would not send +your session cookie to `/nagvis` and you would not be granted access to +NagVis. To solve this please add a section as follows to your Icinga Web 2 +configuration in `/etc/icingaweb2/config.ini`: + +```ini +[cookie] +path = / +``` + +Before doing so please log out from Icinga Web 2 and close your browser, +just to be on the safe side. You could otherwise lock your browser in a +redirection loop. + +## PHP code integration +To get the integration running and to allow NagVis to find the configured +handlers you need to add a short piece of code to +`<nagvisdir>/share/server/core/functions/index.php`: + +```php +/** + * Icinga Web 2 integration + */ +use Icinga\Application\EmbeddedWeb; + +require_once 'Icinga/Application/EmbeddedWeb.php'; +require_once EmbeddedWeb::start('/usr/share/icingaweb2', '/etc/icingaweb2') + ->getModuleManager() + ->getModule('nagvis') + ->getLibDir() . '/nagvis-includes/init.inc.php'; +``` + +This has to sit on top of the page, but after the `<?php` line. diff --git a/application/controllers/ShowController.php b/application/controllers/ShowController.php new file mode 100644 index 0000000..0a36023 --- /dev/null +++ b/application/controllers/ShowController.php @@ -0,0 +1,94 @@ +<?php + +namespace Icinga\Module\Nagvis\Controllers; + +use Icinga\Module\Nagvis\RestrictionHelper; +use Icinga\Security\SecurityException; +use Icinga\Web\Controller; +use Icinga\Web\Url; +use Icinga\Web\Widget\Tabextension\DashboardAction; +use Icinga\Web\Widget\Tabextension\MenuAction; + +class ShowController extends Controller +{ + public function getTabs() + { + $tabs = parent::getTabs (); + $tabs->add ( + 'index', + array ( + 'title' => 'Nagvis', + 'url' => Url::fromRequest ()->getRelativeUrl () + ) + )->extend( + new DashboardAction() + )->extend( + new MenuAction() + ); + + $menu_param = $this->params->get('showMenu'); + if (isset($menu_param) && $menu_param == 1) { + $menu_text = "Hide NagVis Menu"; + $menu_url = $this->getRequest()->getUrl()->without('showMenu'); + $icon = 'eye-off'; + } else { + $menu_text = "Show NagVis Menu"; + $menu_url = $this->getRequest()->getUrl()->with('showMenu', 1); + $icon = 'eye'; + } + + $tabs->addAsDropdown( + 'nagvis-menu-entry', + array( + 'icon' => $icon, + 'label' => t($menu_text), + 'url' => $menu_url + ) + ); + + return $tabs; + } + + public function mapAction() + { + // TODO: I'd prefer to have mod=Overview as a default, that would also + // work with no enabled map. Unfortunately Overview doesn't seem + // to respect header_menu=0 + $map = $this->params->get( + 'map', + $this->Config()->get( + 'global', + 'default-map', + 'demo-overview' + ) + ); + + $restriction = RestrictionHelper::getRegex(); + if ($restriction !== null && ! preg_match($restriction, $map)) { + throw new SecurityException('You\'re not allowed to view map "%s"', $map); + } + + $baseurl = $this->Config()->get('global', 'baseurl', '/nagvis'); + + $url = $baseurl . '/frontend/nagvis-js/index.php'; + $url .= '?mod=Map&act=view&show=' . urlencode($map); + + if ($this->params->get('showMenu')) { + $url .= '&header_menu=1'; + } else { + $url .= '&header_menu=0'; + } + + $zoom = $this->params->shift('zoom', $this->view->compact ? 47 : null); + if ($zoom) { + $url .= '&zoom=' . (int) $zoom; + } + + if ($height = $this->params->shift('height')) { + $this->view->height = (int) $height; + } + + $this->view->nagvisUrl = $url; + $this->getTabs ()->activate('index'); + } +} diff --git a/application/views/scripts/show/map.phtml b/application/views/scripts/show/map.phtml new file mode 100644 index 0000000..f59aad3 --- /dev/null +++ b/application/views/scripts/show/map.phtml @@ -0,0 +1,18 @@ +<?php if (! $this->compact): ?> +<div class="controls"> + <?= $tabs; ?> +</div> +<?php endif ?> + +<?php + +if ($this->height) { + $attr = sprintf(' height="%d"', $height); +} elseif (! $this->compact) { + $attr = ' height="99%"'; +} else { + $attr = ''; +} + +?> +<iframe id="nagvis-iframe" src="<?= $this->nagvisUrl ?>" width="100%"<?= $attr ?> marginheight=0 marginwidth=0 wspace=0 frameborder=0 ></iframe><!-- onload="nagvis_frame_reload();" -->
\ No newline at end of file diff --git a/configuration.php b/configuration.php new file mode 100644 index 0000000..f6dcf64 --- /dev/null +++ b/configuration.php @@ -0,0 +1,41 @@ +<?php + +use Icinga\Application\Config; +use Icinga\Module\Nagvis\RestrictionHelper; + +$section = $this->menuSection(N_('Maps')) + ->setUrl('nagvis/show/map') + ->setIcon('globe'); + +$prio = 0; +$restriction = RestrictionHelper::getRegex(); +foreach (Config::module('nagvis')->getSection('menu') as $name => $caption) { + if ($restriction !== null && ! preg_match($restriction, $name)) { + continue; + } + $section->add($caption, array( + 'url' => 'nagvis/show/map', + 'urlParameters' => array('map' => $name), + 'priority' => ++$prio + )); +} + +$this->providePermission( + 'nagvis/edit', + $this->translate('Modify NagVis maps') +); + +$this->providePermission( + 'nagvis/admin', + $this->translate('Nagvis administration') +); + +$this->providePermission( + 'nagvis/overview', + $this->translate('NagVis general overview') +); + +$this->provideRestriction( + 'nagvis/map/filter', + $this->translate('Filter NagVis maps') +); diff --git a/doc/01-authorization.md b/doc/01-authorization.md new file mode 100644 index 0000000..3945479 --- /dev/null +++ b/doc/01-authorization.md @@ -0,0 +1,15 @@ +# Authorization + +## Permissions + +Name | Description +----------------|------------ +nagvis/overview | NagVis general overview +nagvis/edit | Modify all NagVis maps +nagvis/admin | NagVis administration + +## Restrictions + +Name | Description +------------------|------------ +nagvis/map/filter | Comma-separated set of case insensitive map name patterns (wildcard: \*) a role is allowed to view diff --git a/library/Nagvis/RestrictionHelper.php b/library/Nagvis/RestrictionHelper.php new file mode 100644 index 0000000..a757b9d --- /dev/null +++ b/library/Nagvis/RestrictionHelper.php @@ -0,0 +1,41 @@ +<?php + +namespace Icinga\Module\Nagvis; + +use Icinga\Authentication\Auth; + +/** + * NagVis restriction helper + */ +class RestrictionHelper +{ + /** + * Get the regular expression for validating map names + * + * @return string|null + */ + public static function getRegex() + { + $mapFilters = array(); + foreach (Auth::getInstance()->getRestrictions('nagvis/map/filter') as $mapFilter) { + if ($mapFilter !== '') { + $mapFilters = array_merge($mapFilters, array_map('trim', explode(',', $mapFilter))); + } + } + + if (! empty($mapFilters)) { + $mapRegexParts = array(); + foreach (array_unique($mapFilters) as $mapFilter) { + $nonWildcards = array(); + foreach (explode('*', $mapFilter) as $nonWildcard) { + $nonWildcards[] = preg_quote($nonWildcard, '/'); + } + $mapRegexParts[] = implode('.*', $nonWildcards); + } + + return '/^(?:' . implode('|', $mapRegexParts) . ')$/i'; + } + + return null; + } +} diff --git a/library/nagvis-includes/CoreAuthModIcingaweb2.php b/library/nagvis-includes/CoreAuthModIcingaweb2.php new file mode 100644 index 0000000..da04298 --- /dev/null +++ b/library/nagvis-includes/CoreAuthModIcingaweb2.php @@ -0,0 +1,129 @@ +<?php + +use Icinga\Application\Icinga; +use Icinga\Authentication\Auth; + +class CoreAuthModIcingaweb2 extends CoreAuthModule +{ + private $app; + private $auth; + private $user; + + private $iUserId = -1; + private $sUsername = ''; + private $sPassword = ''; + private $sPasswordnew = ''; + private $sPasswordHash = ''; + + public function __construct() + { + parent::$aFeatures = array( + // General functions for authentication + 'passCredentials' => false, + 'getCredentials' => false, + 'isAuthenticated' => true, + 'getUser' => true, + 'getUserId' => true, + + // Changing passwords + 'passNewPassword' => false, + 'changePassword' => false, + 'passNewPassword' => false, + + // Managing users + 'createUser' => false, + ); + + $oldname = session_name(); + session_write_close(); + $old_path = ini_get('session.cookie_path'); + $old_id = session_id(); + $cacheLimiter = ini_get('session.cache_limiter'); + ini_set('session.use_cookies', false); + ini_set('session.use_only_cookies', false); + ini_set('session.cache_limiter', null); + ini_set('session.cookie_path', '/'); + $icookie = 'Icingaweb2'; + if (isset($_COOKIE[$icookie])) { + session_id($_COOKIE[$icookie]); + } + + $this->app = Icinga::app(); + $this->auth = Auth::getInstance(); + if ($this->auth->isAuthenticated()) { + $this->user = $this->auth->getUser(); + } + ini_set('session.cookie_path', $old_path); + session_id($old_id); + session_name($oldname); + ini_set('session.use_cookies', true); + ini_set('session.use_only_cookies', true); + ini_set('session.cache_limiter', $cacheLimiter); + } + + public function getAllUsers() + { + die('getAllUsers'); + return array(); + // assoc -> userId, name + } + + public function checkUserExists($name) + { + return true; + } + + private function updatePassword() + { + return true; + } + + private function addUser($user, $hash) + { + return true; + } + + public function passCredentials($aData) + { + // die('pass ' . print_r($aData, 1)); + // eventually has user, password and passwordHash + } + + public function passNewPassword($aData) + { + // die('new pass'); + // eventually has user, password and passwordHash + } + + public function getCredentials() + { + return Array('user' => $this->getUser(), + 'passwordHash' => null, + 'userId' => $this->getUserId()); + } + + public function createUser($user, $password) + { + return false; + } + + public function changePassword() + { + return false; + } + + public function isAuthenticated($bTrustUsername = AUTH_NOT_TRUST_USERNAME) + { + return $this->user !== null; + } + + public function getUser() + { + return $this->user->getUsername(); + } + + public function getUserId() + { + return $this->getUser(); + } +} diff --git a/library/nagvis-includes/CoreAuthorisationModIcingaweb2.php b/library/nagvis-includes/CoreAuthorisationModIcingaweb2.php new file mode 100644 index 0000000..111ecba --- /dev/null +++ b/library/nagvis-includes/CoreAuthorisationModIcingaweb2.php @@ -0,0 +1,183 @@ +<?php + +use Icinga\Application\Icinga; +use Icinga\Authentication\Auth; +use Icinga\Module\Nagvis\RestrictionHelper; + +class CoreAuthorisationModIcingaweb2 extends CoreAuthorisationModule +{ + public $rolesConfigurable = false; + + private $auth; + + public function __construct() + { + $this->auth = Auth::getInstance(); + } + + public function parsePermissions($sUsername = null) + { + global $CORE; + + if ($sUsername !== null) { + die('parsePermissions() with username is not supported'); + } + + $perms = array( + 'General' => array('*' => array('*' => true)), + 'User' => array('setOption' => array('*' => true)), + 'Search' => array('view' => array('*' => true)), + 'Rotation' => array('view' => array('*' => true)) + ); + + $restriction = RestrictionHelper::getRegex(); + if ($restriction !== null) { + $maps = array(); + foreach ($CORE->getAvailableMaps($restriction) as $map) { + $maps[$map] = true; + } + if (! empty($maps)) { + $perms['Map'] = array('view' => $maps); + } + } else { + $perms['Map'] = array('view' => array('*' => true)); + } + + if ($this->auth->hasPermission('nagvis/overview')) { + $perms['Overview'] = array('view' => array('*' => true)); + } + + // Never allowed: + // ChangePassword - change + // Auth - logout + // UserMgmt - manage + // RoleMgmt - manage + // Action - perform - * + + if ($this->auth->hasPermission('nagvis/edit')) { + $perms['ManageShapes'] = array('manage' => array('*' => true)); + $perms['ManageBackgrounds'] = array('manage' => array('*' => true)); + $perms['Overview']['edit'] = array('*' => true); + $perms['Map']['add'] = array('*' => true); + $perms['Map']['edit'] = array('*' => true); + $perms['Map']['manage'] = array('*' => true); + } + + if ($this->auth->hasPermission('nagvis/admin')) { + $perms['MainCfg'] = array('edit' => array('*' => true)); + } + + return $perms; + } + + public function getUserRoles($userId) + { + // $userId is now the username + return array(); + die("getUserRoles($userId)"); + return array(0 => array('name' => 'Administrators')); + // [ { roleId, name }, ... ] + } + + public function getAllRoles() + { + // die('getAllRoles'); + // User menu -> Manage Users + return array(); + // [ { roleId, name }, ... ] + } + + + // I want to get rid of those :( + + public function isPermitted($sModule, $sAction, $sObj = null) + { + die("isPermitted($sModule, $sAction, $sObj) - should never be called"); + return false; + } + + public function deletePermission($mod, $name) + { + return false; + // $mod -> Map, Rotation + } + + public function createPermission($mod, $name) + { + return false; + } + + public function roleUsedBy($roleId) + { + die("roleUsedBy($roleId)"); + return array(); + // [ name , ... ] + } + + public function deleteRole($roleId) + { + return false; + } + + public function deleteUser($userId) + { + return false; + } + + public function updateUserRoles($userId, $roles) + { + return false; + // roles = [roleId, ...] + } + + + public function getRoleId($sRole) + { + die("getRoleId($sRole)"); + return 0; + } + + public function getAllPerms() + { + die('getAllPerms'); + return array(); + // [ { permId, mod, act, obj }, ... ] + } + + public function getRolePerms($roleId) { + die("getRolePerms($roleId)"); + return array(); + // [ permId => true, ... ] + } + + public function updateRolePerms($roleId, $perms) + { + // $perms = [ permId, ... ] + return false; + } + + public function checkRoleExists($name) + { + die("checkRoleExists($name)"); + return false; + } + + public function createRole($name) + { + die("createRole($name)"); + return true; + } + + + private function checkUserExistsById($id) + { + die('checkUserExistsById'); + return false; + } + + public function getUserId($sUsername) + { + die('getUserId'); + return 0; + } +} diff --git a/library/nagvis-includes/CoreLogonIcingaweb2.php b/library/nagvis-includes/CoreLogonIcingaweb2.php new file mode 100644 index 0000000..4602bba --- /dev/null +++ b/library/nagvis-includes/CoreLogonIcingaweb2.php @@ -0,0 +1,22 @@ +<?php + +class CoreLogonIcingaweb2 extends CoreLogonModule +{ + public function check($printErr = true) + { + global $AUTH, $CORE; + if ($AUTH->isAuthenticated()) { + $AUTH->setTrustUsername(true); + $AUTH->setLogoutPossible(false); + return true; + } else { + // ??? ... + die('not authenticated'); + // ... + header('Location: /icingaweb'); + exit; + // ... ??? + return false; + } + } +} diff --git a/library/nagvis-includes/GlobalBackendicingaweb2.php b/library/nagvis-includes/GlobalBackendicingaweb2.php new file mode 100644 index 0000000..1168e0a --- /dev/null +++ b/library/nagvis-includes/GlobalBackendicingaweb2.php @@ -0,0 +1,1242 @@ +<?php + +class GlobalBackendicingaweb2 implements GlobalBackendInterface { + private $CONN; + private $backendId; + private $dbName; + private $dbUser; + private $dbPass; + private $dbHost; + private $dbPrefix; + private $dbInstanceName; + private $dbInstanceId; + private $objConfigType; + + private $hostCache; + private $serviceCache; + private $hostAckCache; + + // Define the backend local configuration options + private static $validConfig = Array( + 'dbhost' => Array('must' => 1, + 'editable' => 1, + 'default' => 'localhost', + 'match' => MATCH_STRING_NO_SPACE), + 'dbport' => Array('must' => 0, + 'editable' => 1, + 'default' => '3306', + 'match' => MATCH_INTEGER), + 'dbname' => Array('must' => 1, + 'editable' => 1, + 'default' => 'nagios', + 'match' => MATCH_STRING_NO_SPACE), + 'dbuser' => Array('must' => 1, + 'editable' => 1, + 'default' => 'root', + 'match' => MATCH_STRING_NO_SPACE), + 'dbpass' => Array('must' => 0, + 'editable' => 1, + 'default' => '', + 'match' => MATCH_STRING_EMPTY), + 'dbprefix' => Array('must' => 0, + 'editable' => 1, + 'default' => 'nagios_', + 'match' => MATCH_STRING_NO_SPACE_EMPTY), + 'dbinstancename' => Array('must' => 0, + 'editable' => 1, + 'default' => 'default', + 'match' => MATCH_STRING_NO_SPACE), + 'maxtimewithoutupdate' => Array('must' => 0, + 'editable' => 1, + 'default' => '180', + 'match' => MATCH_INTEGER)); + + /** + * Constructor + * Reads needed configuration parameters, connects to the Database + * and checks that Nagios is running + * + * @param config $MAINCFG + * @param String $backendId + * @author Andreas Husch <downanup@nagios-wiki.de> + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public function __construct($backendId) { + $this->backendId = $backendId; + + $this->hostCache = Array(); + $this->serviceCache = Array(); + $this->hostAckCache = Array(); + + $this->dbName = cfg('backend_'.$backendId, 'dbname'); + $this->dbUser = cfg('backend_'.$backendId, 'dbuser'); + $this->dbPass = cfg('backend_'.$backendId, 'dbpass'); + $this->dbHost = cfg('backend_'.$backendId, 'dbhost'); + $this->dbPort = cfg('backend_'.$backendId, 'dbport'); + $this->dbPrefix = cfg('backend_'.$backendId, 'dbprefix'); + $this->dbInstanceName = cfg('backend_'.$backendId, 'dbinstancename'); + + if($this->checkMysqlSupport() && $this->connectDB()) { + // Set the instanceId + $this->dbInstanceId = $this->getInstanceId(); + + // Do some checks to make sure that Nagios is running and the Data at the DB is ok + $QUERYHANDLE = $this->mysqlQuery('SELECT is_currently_running, UNIX_TIMESTAMP(status_update_time) AS status_update_time FROM '.$this->dbPrefix.'programstatus WHERE instance_id='.$this->dbInstanceId); + $nagiosstate = mysql_fetch_array($QUERYHANDLE); + + // Free memory + mysql_free_result($QUERYHANDLE); + + // Check that Nagios reports itself as running + if ($nagiosstate['is_currently_running'] != 1) { + throw new BackendConnectionProblem(l('nagiosNotRunning', Array('BACKENDID' =>$this->backendId))); + } + + // Be suspicious and check that the data at the db is not older that "maxTimeWithoutUpdate" too + if($_SERVER['REQUEST_TIME'] - $nagiosstate['status_update_time'] > cfg('backend_'.$backendId, 'maxtimewithoutupdate')) { + throw new BackendConnectionProblem(l('nagiosDataNotUpToDate', Array('BACKENDID' => $this->backendId, 'TIMEWITHOUTUPDATE' => cfg('backend_'.$backendId, 'maxtimewithoutupdate')))); + } + + /** + * It looks like there is a problem with the config_type value at some + * installations. The NDO docs and mailinglist say that the flag + * config_type marks the objects as being read from retention data or read + * from configuration. Until NagVis 1.3b3 only objects with config_type=1 + * were queried. + * Cause of some problem reports that there are NO objects with + * config_type=1 in the DB this check was added. If there is at least one + * object with config_type=1 NagVis only recognizes objects with that + * value set. If there is no object with config_type=1 all objects with + * config_type=0 are recognized. + * + * http://www.nagios-portal.de/wbb/index.php?page=Thread&threadID=9269 + */ + if($this->checkConfigTypeObjects()) { + $this->objConfigType = 1; + } else { + $this->objConfigType = 0; + } + } else { + return FALSE; + } + + return TRUE; + } + + /** + * PUBLIC Method getValidConfig + * + * Returns the valid config for this backend + * + * @return Array + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public static function getValidConfig() { + return self::$validConfig; + } + + /** + * PUBLIC Method getObjects + * + * Return the objects configured at Nagios which are matching the given pattern. + * This is needed for WUI, e.g. to populate drop down lists. + * + * @param string $type, string $name1Pattern, string $name2Pattern + * @return array $ret + * @author Lars Michelsen <lars@vertical-visions.de> + * @author Andreas Husch <downanup@nagios-wiki.de> + */ + public function getObjects($type,$name1Pattern='',$name2Pattern='') { + $ret = Array(); + $filter = ''; + $query = $this->backend->select(); + switch($type) { + case 'host': + $query->from('hostStatus', array( + 'name1' => 'host_name' + )); + $objectType = 1; + if($name1Pattern != '') { + $filter = ' name1=\''.$name1Pattern.'\' AND '; + } + break; + case 'service': + $objectType = 2; + + if($name1Pattern != '' && $name2Pattern != '') { + $filter = ' name1=\''.$name1Pattern.'\' AND name2=\''.$name2Pattern.'\' AND '; + } else if($name1Pattern != '') { + $filter = ' name1=\''.$name1Pattern.'\' AND '; + } + break; + case 'hostgroup': + $objectType = 3; + + if($name1Pattern != '') { + $filter = ' name1=\''.$name1Pattern.'\' AND '; + } + break; + case 'servicegroup': + $objectType = 4; + + if($name1Pattern != '') { + $filter = ' name1=\''.$name1Pattern.'\' AND '; + } + break; + default: + return Array(); + break; + } + + /** + * is_active default value is 0. + * When broker option is -1 or BROKER_RETENTION_DATA is activated the + * current objects have is_active=1 and the deprecated object have + * is_active=0. Workaround: Check if there is any is_active=1, then use the + * is_active filter. + * + * For details see: + * https://sourceforge.net/tracker/index.php?func=detail&aid=1839631&group_id=132019&atid=725179 + * http://www.nagios-portal.de/forum/thread.php?postid=59971#post59971 + */ + if($this->checkForIsActiveObjects()) { + $isActiveFilter = ' is_active=1 AND'; + } else { + $isActiveFilter = ''; + } + + $QUERYHANDLE = $this->mysqlQuery('SELECT name1,name2 FROM '.$this->dbPrefix.'objects WHERE objecttype_id='.$objectType.' AND '.$filter.$isActiveFilter.' instance_id='.$this->dbInstanceId.' ORDER BY name1'); + while($data = mysql_fetch_array($QUERYHANDLE)) { + $ret[] = Array('name1' => $data['name1'],'name2' => $data['name2']); + } + + // Free memory + mysql_free_result($QUERYHANDLE); + + return $ret; + } + + /** + * PUBLIC Method getObjectsEx + * + * Return all objects configured at Nagios plus some additional information. + * This is needed for gmap, e.g. to populate lists. + * + * @param string $type + * @return array $ret + * @author Roman Kyrylych <rkyrylych@op5.com> + */ + public function getObjectsEx($type) { + $ret = Array(); + + return $ret; + } + + /** + * PRIVATE Method checkForIsActiveObjects + * + * Checks if there are some object records with is_active=1 + * + * @return Boolean + * @author Lars Michelsen <lars@vertical-visions.de> + */ + private function checkForIsActiveObjects() { + if(mysql_num_rows($this->mysqlQuery('SELECT object_id FROM '.$this->dbPrefix.'objects WHERE is_active=1')) > 0) { + return TRUE; + } else { + return FALSE; + } + } + + /** + * PRIVATE Method checkConfigTypeObjects + * + * Checks if there are some object records with config_type=1 + * + * @return Boolean + * @author Lars Michelsen <lars@vertical-visions.de> + */ + private function checkConfigTypeObjects() { + if(mysql_num_rows($this->mysqlQuery('SELECT host_id FROM '.$this->dbPrefix.'hosts WHERE config_type=1 AND instance_id='.$this->dbInstanceId.' LIMIT 1')) > 0) { + return TRUE; + } else { + return FALSE; + } + } + + /** + * PRIVATE parseFilter() + * + * Parses the filter array to backend + * + * @param Array List of objects to query + * @param Array List of filters to apply + * @param String Table to use for filtering + * @param Boolean Split the filter by options + * @return String Parsed filters + * @author Lars Michelsen <lars@vertical-visions.de> + */ + private function parseFilter($objects, $filters, $table, $childTable, $isMemberQuery = false, + $isCountQuery = false, $isHostQuery = true) { + $aFilters = Array(); + foreach($objects AS $OBJS) { + $objFilters = Array(); + foreach($filters AS $filter) { + // Array('key' => 'host_name', 'operator' => '=', 'name'), + switch($filter['key']) { + case 'host_name': + case 'host_groups': + case 'service_groups': + case 'hostgroup_name': + case 'group_name': + case 'groups': + case 'servicegroup_name': + case 'service_description': + if($filter['key'] != 'service_description') + $val = $OBJS[0]->getName(); + else + $val = $OBJS[0]->getServiceDescription(); + + // Translate field names + if($filter['key'] == 'service_description') + $filter['key'] = 'name2'; + else + $filter['key'] = 'name1'; + + if($filter['op'] == '>=') + $filter['op'] = '='; + + $objFilters[] = ' '.$table.'.'.$filter['key']." ".$filter['op']." binary '".$val."' "; + break; + default: + throw new BackendConnectionProblem('Invalid filter key ('.$filter['key'].')'); + break; + } + } + + // Are there child exclude filters defined for this object?\ + // The objTupe is the type of the objects to query the data for + if($isMemberQuery && $OBJS[0]->hasExcludeFilters($isCountQuery)) { + $filter = $OBJS[0]->getExcludeFilter($isCountQuery); + $objType = $OBJS[0]->getType(); + + if($objType == 'host') { + $parts = explode('~~', $filter); + if(!isset($parts[1])) + $objFilters[] = " ".$childTable.".name2 NOT REGEXP BINARY \"".$filter."\""; + + } elseif($objType == 'hostgroup' && $isHostQuery) { + $parts = explode('~~', $filter); + if(!isset($parts[1])) + $objFilters[] = " ".$childTable.".name1 NOT REGEXP BINARY \"".$parts[0]."\""; + + } elseif(($objType == 'hostgroup' && !$isHostQuery) || $objType == 'servicegroup') { + $parts = explode('~~', $filter); + if(isset($parts[1])) + $objFilters[] = " NOT (".$childTable.".name1 REGEXP BINARY \"".$parts[0]."\" " + ." AND ".$childTable.".name2 REGEXP BINARY \"".$parts[1]."\")"; + else + $objFilters[] = " ".$childTable.".name1 NOT REGEXP BINARY \"".$parts[0]."\""; + } + } + + $aFilters[] = implode(' AND ', $objFilters); + } + + return implode(' OR ', $aFilters); + } + + + /** + * PRIVATE Method getHostAckByHostname + * + * Returns if a host state has been acknowledged. The method doesn't check + * if the host is in OK/DOWN, it only checks the has_been_acknowledged flag. + * + * @param string $hostName + * @return bool $ack + * @author Lars Michelsen <lars@vertical-visions.de> + */ + private function getHostAckByHostname($hostName) { + $return = 0; + + // Read from cache or fetch from NDO + if(isset($this->hostAckCache[$hostName])) { + $return = $this->hostAckCache[$hostName]; + } else { + $QUERYHANDLE = $this->mysqlQuery('SELECT problem_has_been_acknowledged + FROM '.$this->dbPrefix.'objects AS o,'.$this->dbPrefix.'hoststatus AS h + WHERE (o.objecttype_id=1 AND o.name1 = binary \''.$hostName.'\' AND o.instance_id='.$this->dbInstanceId.') AND h.host_object_id=o.object_id LIMIT 1'); + + $data = mysql_fetch_assoc($QUERYHANDLE); + + // Free memory + mysql_free_result($QUERYHANDLE); + + // It's unnessecary to check if the value is 0, everything not equal to 1 is FALSE + if(isset($data['problem_has_been_acknowledged']) && $data['problem_has_been_acknowledged'] == '1') { + $return = 1; + } else { + $return = 0; + } + + // Save to cache + $this->hostAckCache[$hostName] = $return; + } + + return $return; + } + + /** + * PUBLIC getHostState() + * + * Returns the Nagios state and additional information for the requested host + * + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public function getHostState($objects, $options, $filters, $isMemberQuery = false) { + $arrReturn = Array(); + + $QUERYHANDLE = $this->mysqlQuery('SELECT + o.object_id, alias, display_name, address, o.name1, + has_been_checked, + last_hard_state, + UNIX_TIMESTAMP(last_hard_state_change) AS last_hard_state_change, + UNIX_TIMESTAMP(last_state_change) AS last_state_change, + current_state, + output, perfdata, + h.notes, + problem_has_been_acknowledged, + UNIX_TIMESTAMP(last_check) AS last_check, UNIX_TIMESTAMP(next_check) AS next_check, + hs.state_type, hs.current_check_attempt, hs.max_check_attempts, hs.check_command, + UNIX_TIMESTAMP(dh.scheduled_start_time) AS downtime_start, UNIX_TIMESTAMP(dh.scheduled_end_time) AS downtime_end, + dh.author_name AS downtime_author, dh.comment_data AS downtime_data + FROM + '.$this->dbPrefix.'hosts AS h, + '.$this->dbPrefix.'objects AS o + LEFT JOIN + '.$this->dbPrefix.'hoststatus AS hs + ON hs.host_object_id=o.object_id + LEFT JOIN + '.$this->dbPrefix.'scheduleddowntime AS dh + ON dh.object_id=o.object_id AND NOW()>dh.scheduled_start_time AND NOW()<dh.scheduled_end_time + WHERE + (o.objecttype_id=1 AND ('.$this->parseFilter($objects, $filters, 'o', 'o', $isMemberQuery, false, HOST_QUERY).') + AND o.instance_id='.$this->dbInstanceId.') + AND (h.config_type='.$this->objConfigType.' AND h.instance_id='.$this->dbInstanceId.' AND h.host_object_id=o.object_id)'); + + while($data = mysql_fetch_assoc($QUERYHANDLE)) { + + // If there is a downtime for this object, save the data + $in_downtime = 0; + $dt_details = array(null, null, null, null); + if(isset($data['downtime_start']) && $data['downtime_start'] != '') { + $in_downtime = 1; + $dt_details = array($data['downtime_author'], $data['downtime_data'], + $data['downtime_start'], $data['downtime_end']); + } + + /** + * Only recognize hard states. There was a discussion about the implementation + * This is a new handling of only_hard_states. For more details, see: + * http://www.nagios-portal.de/wbb/index.php?page=Thread&threadID=8524 + * + * Thanks to Andurin and fredy82 + */ + if($options & 1) + if($data['state_type'] != '0') + $data['current_state'] = $data['current_state']; + else + $data['current_state'] = $data['last_hard_state']; + + $acknowledged = 0; + + if($data['has_been_checked'] == '0' || $data['current_state'] == '') { + $state = UNCHECKED; + $output = l('hostIsPending', Array('HOST' => $data['name1'])); + } elseif($data['current_state'] == '0') { + // Host is UP + $state = UP; + $output = $data['output']; + } else { + // Host is DOWN/UNREACHABLE/UNKNOWN + + $acknowledged = intval($data['problem_has_been_acknowledged']); + + // Store state and output in array + switch($data['current_state']) { + case '1': + $state = DOWN; + $output = $data['output']; + break; + case '2': + $state = UNREACHABLE; + $output = $data['output']; + break; + case '3': + $state = UNKNOWN; + $output = $data['output']; + break; + default: + $state = UNKNOWN; + $output = 'GlobalBackendndomy::getHostState: Undefined state!'; + break; + } + } + + $arrReturn[$data['name1']] = array( + $state, + $output, + $acknowledged, + $in_downtime, + 0, // staleness + $data['state_type'], + $data['current_check_attempt'], + $data['max_check_attempts'], + $data['last_check'], + $data['next_check'], + $data['last_hard_state_change'], + $data['last_state_change'], + $data['perfdata'], + $data['display_name'], + $data['alias'], + $data['address'], + $data['notes'], + $data['check_command'], + null, // custom_vars + $dt_details[0], // downtime author + $dt_details[1], // downtime comment + $dt_details[2], // downtime start + $dt_details[3], // downtime end + ); + } + + // Free memory + mysql_free_result($QUERYHANDLE); + + return $arrReturn; + } + + /** + * PUBLIC getServiceState() + * + * Returns the state and additional information of the requested service + * + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public function getServiceState($objects, $options, $filters, $isMemberQuery = false) { + $arrReturn = Array(); + + $QUERYHANDLE = $this->mysqlQuery('SELECT + o.object_id, o.name1, o.name2, + s.display_name, + s.notes, + h.address, + ss.has_been_checked, ss.last_hard_state, ss.current_state, + UNIX_TIMESTAMP(ss.last_hard_state_change) AS last_hard_state_change, + UNIX_TIMESTAMP(ss.last_state_change) AS last_state_change, + ss.output, ss.perfdata, ss.problem_has_been_acknowledged, + UNIX_TIMESTAMP(ss.last_check) AS last_check, UNIX_TIMESTAMP(ss.next_check) AS next_check, + ss.state_type, ss.current_check_attempt, ss.max_check_attempts, ss.check_command, + UNIX_TIMESTAMP(dh.scheduled_start_time) AS downtime_start, UNIX_TIMESTAMP(dh.scheduled_end_time) AS downtime_end, + dh.author_name AS downtime_author, dh.comment_data AS downtime_data + FROM + '.$this->dbPrefix.'services AS s, + '.$this->dbPrefix.'hosts AS h, + '.$this->dbPrefix.'objects AS o + LEFT JOIN + '.$this->dbPrefix.'servicestatus AS ss + ON ss.service_object_id=o.object_id + LEFT JOIN + '.$this->dbPrefix.'scheduleddowntime AS dh + ON dh.object_id=o.object_id AND NOW()>dh.scheduled_start_time AND NOW()<dh.scheduled_end_time + WHERE + (o.objecttype_id=2 AND ('.$this->parseFilter($objects, $filters, 'o', 'o', $isMemberQuery, false, !HOST_QUERY).')) + AND (s.config_type='.$this->objConfigType.' AND s.instance_id='.$this->dbInstanceId.' AND s.service_object_id=o.object_id) + AND (h.config_type='.$this->objConfigType.' AND h.instance_id='.$this->dbInstanceId.' AND h.host_object_id=s.host_object_id) + '); + + while($data = mysql_fetch_assoc($QUERYHANDLE)) { + $arrTmpReturn = Array(); + + if(isset($objects[$data['name1'].'~~'.$data['name2']])) { + $specific = true; + $key = $data['name1'].'~~'.$data['name2']; + } else { + $specific = false; + $key = $data['name1']; + } + + // If there is a downtime for this object, save the data + $in_downtime = 0; + $dt_details = array(null, null, null, null); + if(isset($data['downtime_start']) && $data['downtime_start'] != '') { + $in_downtime = 1; + $dt_details = array($data['downtime_author'], $data['downtime_data'], + $data['downtime_start'], $data['downtime_end']); + } + + /** + * Only recognize hard states. There was a discussion about the implementation + * This is a new handling of only_hard_states. For more details, see: + * http://www.nagios-portal.de/wbb/index.php?page=Thread&threadID=8524 + * + * Thanks to Andurin and fredy82 + */ + if($options & 1) + if($data['state_type'] != '0') + $data['current_state'] = $data['current_state']; + else + $data['current_state'] = $data['last_hard_state']; + + $acknowledged = 0; + if($data['has_been_checked'] == '0' || $data['current_state'] == '') { + $state = PENDING; + $output = l('serviceNotChecked', Array('SERVICE' => $data['name2'])); + } elseif($data['current_state'] == '0') { + // Host is UP + $state = OK; + $output = $data['output']; + } else { + // Host is DOWN/UNREACHABLE/UNKNOWN + + /** + * If state is not OK (=> WARN, CRIT, UNKNOWN) and service is not + * acknowledged => check for acknowledged host + */ + if($data['problem_has_been_acknowledged'] != 1) { + $acknowledged = $this->getHostAckByHostname($data['name1']); + } else { + $acknowledged = intval($data['problem_has_been_acknowledged']); + } + + // Store state and output in array + switch($data['current_state']) { + case '1': + $state = WARNING; + $output = $data['output']; + break; + case '2': + $state = CRITICAL; + $output = $data['output']; + break; + case '3': + $state = UNKNOWN; + $output = $data['output']; + break; + default: + $state = UNKNOWN; + $output = 'GlobalBackendndomy::getServiceState: Undefined state!'; + break; + } + } + + $svc = array( + $state, + $output, + $acknowledged, + $in_downtime, + 0, // staleness + $data['state_type'], + $data['current_check_attempt'], + $data['max_check_attempts'], + $data['last_check'], + $data['next_check'], + $data['last_hard_state_change'], + $data['last_state_change'], + $data['perfdata'], + $data['display_name'], + $data['display_name'], + $data['address'], + $data['notes'], + $data['check_command'], + null, //custom_vars + $dt_details[0], // dt author + $dt_details[1], // dt data + $dt_details[2], // dt start + $dt_details[3], // dt end + $data['name2'] + ); + + if($specific) { + $arrReturn[$key] = $svc; + } else { + if(!isset($arrReturn[$key])) + $arrReturn[$key] = Array(); + + $arrReturn[$key][] = $svc; + } + } + + // Free memory + mysql_free_result($QUERYHANDLE); + + return $arrReturn; + } + + /** + * PUBLIC getHostMemberCounts() + * + * @param Array List of objects to query + * @param Array List of filters to apply + * @return Array List of states and counts + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public function getHostMemberCounts($objects, $options, $filters) { + if($options & 1) + $stateAttr = 'IF((ss.state_type = 0), ss.last_hard_state, ss.current_state)'; + else + $stateAttr = 'ss.current_state'; + + $QUERYHANDLE = $this->mysqlQuery('SELECT + o.name1, h.alias, + SUM(IF(ss.has_been_checked=0,1,0)) AS pending, + SUM(IF(('.$stateAttr.'=0 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth=0 AND hs.scheduled_downtime_depth=0),1,0)) AS ok, + SUM(IF(('.$stateAttr.'=0 AND ss.has_been_checked!=0 AND (ss.scheduled_downtime_depth!=0 OR hs.scheduled_downtime_depth!=0)),1,0)) AS ok_downtime, + SUM(IF(('.$stateAttr.'=1 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth=0 AND hs.scheduled_downtime_depth=0 AND ss.problem_has_been_acknowledged=0 AND hs.problem_has_been_acknowledged=0),1,0)) AS warning, + SUM(IF(('.$stateAttr.'=1 AND ss.has_been_checked!=0 AND (ss.scheduled_downtime_depth!=0 OR hs.scheduled_downtime_depth!=0)),1,0)) AS warning_downtime, + SUM(IF(('.$stateAttr.'=1 AND ss.has_been_checked!=0 AND (ss.problem_has_been_acknowledged=1 OR hs.problem_has_been_acknowledged=1)),1,0)) AS warning_ack, + SUM(IF(('.$stateAttr.'=2 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth=0 AND hs.scheduled_downtime_depth=0) AND ss.problem_has_been_acknowledged=0 AND hs.problem_has_been_acknowledged=0,1,0)) AS critical, + SUM(IF(('.$stateAttr.'=2 AND ss.has_been_checked!=0 AND (ss.scheduled_downtime_depth!=0 OR hs.scheduled_downtime_depth!=0)),1,0)) AS critical_downtime, + SUM(IF(('.$stateAttr.'=2 AND ss.has_been_checked!=0 AND (ss.problem_has_been_acknowledged=1 OR hs.problem_has_been_acknowledged=1)),1,0)) AS critical_ack, + SUM(IF(('.$stateAttr.'=3 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth=0 AND hs.scheduled_downtime_depth=0 AND ss.problem_has_been_acknowledged=0 AND hs.problem_has_been_acknowledged=0),1,0)) AS unknown, + SUM(IF(('.$stateAttr.'=3 AND ss.has_been_checked!=0 AND (ss.scheduled_downtime_depth!=0 OR hs.scheduled_downtime_depth!=0)),1,0)) AS unknown_downtime, + SUM(IF(('.$stateAttr.'=3 AND ss.has_been_checked!=0 AND (ss.problem_has_been_acknowledged=1 OR hs.problem_has_been_acknowledged=1)),1,0)) AS unknown_ack + FROM + '.$this->dbPrefix.'hoststatus AS hs, + '.$this->dbPrefix.'services AS s, + '.$this->dbPrefix.'hosts AS h, + '.$this->dbPrefix.'objects AS o + LEFT JOIN + '.$this->dbPrefix.'servicestatus AS ss + ON ss.service_object_id=o.object_id + WHERE + (o.objecttype_id=2 AND ('.$this->parseFilter($objects, $filters, 'o', 'o', MEMBER_QUERY, COUNT_QUERY, !HOST_QUERY).')) + AND (s.config_type='.$this->objConfigType.' AND s.instance_id='.$this->dbInstanceId.' AND s.service_object_id=o.object_id) + AND (h.config_type='.$this->objConfigType.' AND h.instance_id='.$this->dbInstanceId.' AND h.host_object_id=s.host_object_id) + AND (hs.host_object_id=h.host_object_id) + GROUP BY h.host_object_id'); + + $arrReturn = Array(); + while($data = mysql_fetch_assoc($QUERYHANDLE)) { + $arrReturn[$data['name1']] = Array( + //'details' => Array('alias' => $data['alias']), + 'counts' => Array( + PENDING => Array( + 'normal' => intval($data['pending']), + ), + OK => Array( + 'normal' => intval($data['ok']), + 'stale' => 0, + 'downtime' => intval($data['ok_downtime']), + ), + WARNING => Array( + 'normal' => intval($data['warning']), + 'stale' => 0, + 'ack' => intval($data['warning_ack']), + 'downtime' => intval($data['warning_downtime']), + ), + CRITICAL => Array( + 'normal' => intval($data['critical']), + 'stale' => 0, + 'ack' => intval($data['critical_ack']), + 'downtime' => intval($data['critical_downtime']), + ), + UNKNOWN => Array( + 'normal' => intval($data['unknown']), + 'stale' => 0, + 'ack' => intval($data['unknown_ack']), + 'downtime' => intval($data['unknown_downtime']), + ), + ) + ); + } + + // Free memory + mysql_free_result($QUERYHANDLE); + + return $arrReturn; + } + + public function getHostgroupStateCounts($objects, $options, $filters) { + if($options & 1) + $stateAttr = 'IF((hs.state_type = 0), hs.last_hard_state, hs.current_state)'; + else + $stateAttr = 'hs.current_state'; + + $QUERYHANDLE = $this->mysqlQuery('SELECT + o.name1, hg.alias, + SUM(IF(hs.has_been_checked=0,1,0)) AS unchecked, + SUM(IF(('.$stateAttr.'=0 AND hs.has_been_checked!=0 AND hs.scheduled_downtime_depth=0),1,0)) AS up, + SUM(IF(('.$stateAttr.'=0 AND hs.has_been_checked!=0 AND hs.scheduled_downtime_depth!=0),1,0)) AS up_downtime, + SUM(IF(('.$stateAttr.'=1 AND hs.has_been_checked!=0 AND hs.scheduled_downtime_depth=0 AND hs.problem_has_been_acknowledged=0),1,0)) AS down, + SUM(IF(('.$stateAttr.'=1 AND hs.has_been_checked!=0 AND hs.scheduled_downtime_depth!=0),1,0)) AS down_downtime, + SUM(IF(('.$stateAttr.'=1 AND hs.has_been_checked!=0 AND hs.problem_has_been_acknowledged=1),1,0)) AS down_ack, + SUM(IF(('.$stateAttr.'=2 AND hs.has_been_checked!=0 AND hs.scheduled_downtime_depth=0 AND hs.problem_has_been_acknowledged=0),1,0)) AS unreachable, + SUM(IF(('.$stateAttr.'=2 AND hs.has_been_checked!=0 AND hs.scheduled_downtime_depth!=0),1,0)) AS unreachable_downtime, + SUM(IF(('.$stateAttr.'=2 AND hs.has_been_checked!=0 AND hs.problem_has_been_acknowledged=1),1,0)) AS unreachable_ack, + SUM(IF(('.$stateAttr.'=3 AND hs.has_been_checked!=0 AND hs.scheduled_downtime_depth=0 AND hs.problem_has_been_acknowledged=0),1,0)) AS unknown, + SUM(IF(('.$stateAttr.'=3 AND hs.has_been_checked!=0 AND hs.scheduled_downtime_depth!=0),1,0)) AS unknown_downtime, + SUM(IF(('.$stateAttr.'=3 AND hs.has_been_checked!=0 AND hs.problem_has_been_acknowledged=1),1,0)) AS unknown_ack + FROM + '.$this->dbPrefix.'objects AS o, + '.$this->dbPrefix.'hostgroups AS hg, + '.$this->dbPrefix.'hostgroup_members AS hgm, + '.$this->dbPrefix.'objects AS o2 + LEFT JOIN + '.$this->dbPrefix.'hoststatus AS hs + ON hs.host_object_id=o2.object_id + WHERE + (o.objecttype_id=3 AND ('.$this->parseFilter($objects, $filters, 'o', 'o2', MEMBER_QUERY, COUNT_QUERY, HOST_QUERY).') + AND o.instance_id='.$this->dbInstanceId.') + AND (hg.config_type='.$this->objConfigType.' AND hg.instance_id='.$this->dbInstanceId.' AND hg.hostgroup_object_id=o.object_id) + AND hgm.hostgroup_id=hg.hostgroup_id + AND (o2.objecttype_id=1 AND o2.object_id=hgm.host_object_id) + GROUP BY o.object_id'); + + $arrReturn = Array(); + while($data = mysql_fetch_assoc($QUERYHANDLE)) { + $arrReturn[$data['name1']] = Array( + 'details' => Array(ALIAS => $data['alias']), + 'counts' => Array( + UNCHECKED => Array( + 'normal' => intval($data['unchecked']), + ), + UP => Array( + 'normal' => intval($data['up']), + 'stale' => 0, + 'downtime' => intval($data['up_downtime']), + ), + DOWN => Array( + 'normal' => intval($data['down']), + 'stale' => 0, + 'ack' => intval($data['down_ack']), + 'downtime' => intval($data['down_downtime']), + ), + UNREACHABLE => Array( + 'normal' => intval($data['unreachable']), + 'stale' => 0, + 'ack' => intval($data['unreachable_ack']), + 'downtime' => intval($data['unreachable_downtime']), + ), + ), + ); + } + + if($options & 1) + $stateAttr = 'IF((ss.state_type = 0), ss.last_hard_state, ss.current_state)'; + else + $stateAttr = 'ss.current_state'; + + // If recognize_services are disabled don't fetch service information + if($options & 2) + return $arrReturn; + + // FIXME: Does not handle host downtimes/acks + $QUERYHANDLE = $this->mysqlQuery('SELECT + o.name1, + SUM(IF(ss.has_been_checked=0,1,0)) AS pending, + SUM(IF(('.$stateAttr.'=0 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth=0),1,0)) AS ok, + SUM(IF(('.$stateAttr.'=0 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth!=0),1,0)) AS ok_downtime, + SUM(IF(('.$stateAttr.'=1 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth=0 AND ss.problem_has_been_acknowledged=0),1,0)) AS warning, + SUM(IF(('.$stateAttr.'=1 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth!=0),1,0)) AS warning_downtime, + SUM(IF(('.$stateAttr.'=1 AND ss.has_been_checked!=0 AND ss.problem_has_been_acknowledged=1),1,0)) AS warning_ack, + SUM(IF(('.$stateAttr.'=2 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth=0 AND ss.problem_has_been_acknowledged=0),1,0)) AS critical, + SUM(IF(('.$stateAttr.'=2 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth!=0),1,0)) AS critical_downtime, + SUM(IF(('.$stateAttr.'=2 AND ss.has_been_checked!=0 AND ss.problem_has_been_acknowledged=1),1,0)) AS critical_ack, + SUM(IF(('.$stateAttr.'=3 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth=0 AND ss.problem_has_been_acknowledged=0),1,0)) AS unknown, + SUM(IF(('.$stateAttr.'=3 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth!=0),1,0)) AS unknown_downtime, + SUM(IF(('.$stateAttr.'=3 AND ss.has_been_checked!=0 AND ss.problem_has_been_acknowledged=1),1,0)) AS unknown_ack + FROM + '.$this->dbPrefix.'objects AS o, + '.$this->dbPrefix.'hostgroups AS hg, + '.$this->dbPrefix.'hostgroup_members AS hgm, + '.$this->dbPrefix.'services AS s, + '.$this->dbPrefix.'objects AS o2 + LEFT JOIN + '.$this->dbPrefix.'servicestatus AS ss + ON ss.service_object_id=o2.object_id + WHERE + (o.objecttype_id=3 AND ('.$this->parseFilter($objects, $filters, 'o', 'o2', MEMBER_QUERY, COUNT_QUERY, !HOST_QUERY).') + AND o.instance_id='.$this->dbInstanceId.') + AND (hg.config_type='.$this->objConfigType.' AND hg.instance_id='.$this->dbInstanceId.' AND hg.hostgroup_object_id=o.object_id) + AND hgm.hostgroup_id=hg.hostgroup_id + AND (s.config_type='.$this->objConfigType.' AND s.instance_id='.$this->dbInstanceId.' AND s.host_object_id=hgm.host_object_id) + AND (o2.objecttype_id=2 AND s.service_object_id=o2.object_id) + GROUP BY o.object_id'); + + while($data = mysql_fetch_assoc($QUERYHANDLE)) { + $arrReturn[$data['name1']]['counts'][PENDING]['normal'] = intval($data['pending']); + $arrReturn[$data['name1']]['counts'][OK]['normal'] = intval($data['ok']); + $arrReturn[$data['name1']]['counts'][OK]['stale'] = 0; + $arrReturn[$data['name1']]['counts'][OK]['downtime'] = intval($data['ok_downtime']); + $arrReturn[$data['name1']]['counts'][WARNING]['normal'] = intval($data['warning']); + $arrReturn[$data['name1']]['counts'][WARNING]['stale'] = 0; + $arrReturn[$data['name1']]['counts'][WARNING]['ack'] = intval($data['warning_ack']); + $arrReturn[$data['name1']]['counts'][WARNING]['downtime'] = intval($data['warning_downtime']); + $arrReturn[$data['name1']]['counts'][CRITICAL]['normal'] = intval($data['critical']); + $arrReturn[$data['name1']]['counts'][CRITICAL]['stale'] = 0; + $arrReturn[$data['name1']]['counts'][CRITICAL]['ack'] = intval($data['critical_ack']); + $arrReturn[$data['name1']]['counts'][CRITICAL]['downtime'] = intval($data['critical_downtime']); + $arrReturn[$data['name1']]['counts'][UNKNOWN]['normal'] = intval($data['unknown']); + $arrReturn[$data['name1']]['counts'][UNKNOWN]['stale'] = 0; + $arrReturn[$data['name1']]['counts'][UNKNOWN]['ack'] = intval($data['unknown_ack']); + $arrReturn[$data['name1']]['counts'][UNKNOWN]['downtime'] = intval($data['unknown_downtime']); + } + + // Free memory + mysql_free_result($QUERYHANDLE); + + return $arrReturn; + } + + public function getServicegroupStateCounts($objects, $options, $filters) { + if($options & 1) + $stateAttr = 'IF((ss.state_type = 0), ss.last_hard_state, ss.current_state)'; + else + $stateAttr = 'ss.current_state'; + + // FIXME: Recognize host ack/downtime + $QUERYHANDLE = $this->mysqlQuery('SELECT + o.name1, sg.alias, + SUM(IF(ss.has_been_checked=0,1,0)) AS pending, + SUM(IF(('.$stateAttr.'=0 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth=0 AND ss.scheduled_downtime_depth=0),1,0)) AS ok, + SUM(IF(('.$stateAttr.'=0 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth!=0),1,0)) AS ok_downtime, + SUM(IF(('.$stateAttr.'=1 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth=0 AND ss.problem_has_been_acknowledged=0),1,0)) AS warning, + SUM(IF(('.$stateAttr.'=1 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth!=0),1,0)) AS warning_downtime, + SUM(IF(('.$stateAttr.'=1 AND ss.has_been_checked!=0 AND ss.problem_has_been_acknowledged=1),1,0)) AS warning_ack, + SUM(IF(('.$stateAttr.'=2 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth=0 AND ss.problem_has_been_acknowledged=0),1,0)) AS critical, + SUM(IF(('.$stateAttr.'=2 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth!=0),1,0)) AS critical_downtime, + SUM(IF(('.$stateAttr.'=2 AND ss.has_been_checked!=0 AND ss.problem_has_been_acknowledged=1),1,0)) AS critical_ack, + SUM(IF(('.$stateAttr.'=3 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth=0 AND ss.problem_has_been_acknowledged=0),1,0)) AS unknown, + SUM(IF(('.$stateAttr.'=3 AND ss.has_been_checked!=0 AND ss.scheduled_downtime_depth!=0),1,0)) AS unknown_downtime, + SUM(IF(('.$stateAttr.'=3 AND ss.has_been_checked!=0 AND ss.problem_has_been_acknowledged=1),1,0)) AS unknown_ack + FROM + '.$this->dbPrefix.'objects AS o, + '.$this->dbPrefix.'servicegroups AS sg, + '.$this->dbPrefix.'servicegroup_members AS sgm, + '.$this->dbPrefix.'services AS s, + '.$this->dbPrefix.'objects AS o2 + LEFT JOIN + '.$this->dbPrefix.'servicestatus AS ss + ON ss.service_object_id=o2.object_id + WHERE + (o.objecttype_id=4 AND ('.$this->parseFilter($objects, $filters, 'o', 'o2', MEMBER_QUERY, COUNT_QUERY, !HOST_QUERY).') + AND o.instance_id='.$this->dbInstanceId.') + AND (sg.config_type='.$this->objConfigType.' AND sg.instance_id='.$this->dbInstanceId.' AND sg.servicegroup_object_id=o.object_id) + AND sgm.servicegroup_id=sg.servicegroup_id + AND (s.config_type='.$this->objConfigType.' AND s.instance_id='.$this->dbInstanceId.' AND s.service_object_id=sgm.service_object_id) + AND (o2.objecttype_id=2 AND s.service_object_id=o2.object_id) + GROUP BY o.object_id'); + + $arrReturn = Array(); + while($data = mysql_fetch_assoc($QUERYHANDLE)) { + $arrReturn[$data['name1']] = Array( + 'details' => Array(ALIAS => $data['alias']), + 'counts' => Array( + PENDING => Array( + 'normal' => intval($data['pending']), + ), + OK => Array( + 'normal' => intval($data['ok']), + 'stale' => 0, + 'downtime' => intval($data['ok_downtime']), + ), + WARNING => Array( + 'normal' => intval($data['warning']), + 'stale' => 0, + 'ack' => intval($data['warning_ack']), + 'downtime' => intval($data['warning_downtime']), + ), + CRITICAL => Array( + 'normal' => intval($data['critical']), + 'stale' => 0, + 'ack' => intval($data['critical_ack']), + 'downtime' => intval($data['critical_downtime']), + ), + UNKNOWN => Array( + 'normal' => intval($data['unknown']), + 'stale' => 0, + 'ack' => intval($data['unknown_ack']), + 'downtime' => intval($data['unknown_downtime']), + ), + ), + ); + } + + // Free memory + mysql_free_result($QUERYHANDLE); + + return $arrReturn; + } + + /** + * PUBLIC Method getHostNamesWithNoParent + * + * Gets all hosts with no parent host. This method is needed by the automap + * to get the root host. + * + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public function getHostNamesWithNoParent() { + $arrReturn = Array(); + + $QUERYHANDLE = $this->mysqlQuery('SELECT o1.name1 + FROM + `'.$this->dbPrefix.'objects` AS o1, + `'.$this->dbPrefix.'hosts` AS h1 + LEFT OUTER JOIN `'.$this->dbPrefix.'host_parenthosts` AS ph1 ON h1.host_id=ph1.host_id + WHERE o1.objecttype_id=1 + AND (h1.config_type='.$this->objConfigType.' AND h1.instance_id='.$this->dbInstanceId.' AND h1.host_object_id=o1.object_id) + AND ph1.parent_host_object_id IS null'); + + while($data = mysql_fetch_array($QUERYHANDLE)) { + $arrReturn[] = $data['name1']; + } + + // Free memory + mysql_free_result($QUERYHANDLE); + + return $arrReturn; + } + + /** + * PUBLIC getDirectParentNamesByHostName() + * + * Gets the names of all parent hosts. New in 1.5. Showing parent layers on + * the automap is only possible when the backend provides this method. + * + * @param String Name of host to get the parents of + * @return Array Array with hostnames + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public function getDirectParentNamesByHostName($hostName) { + $aParentNames = Array(); + + $QUERYHANDLE = $this->mysqlQuery('SELECT o2.name1 + FROM + `'.$this->dbPrefix.'objects` AS o1, + `'.$this->dbPrefix.'hosts` AS h1, + `'.$this->dbPrefix.'host_parenthosts` AS ph1, + `'.$this->dbPrefix.'objects` AS o2 + WHERE o1.objecttype_id=1 AND o1.name1=\''.$hostName.'\' + AND (h1.config_type='.$this->objConfigType.' AND h1.instance_id='.$this->dbInstanceId.' AND h1.host_object_id=o1.object_id) + AND h1.host_id=ph1.host_id + AND o2.objecttype_id=1 AND o2.object_id=ph1.parent_host_object_id'); + while($data = mysql_fetch_array($QUERYHANDLE)) { + $aParentNames[] = $data['name1']; + } + + // Free memory + mysql_free_result($QUERYHANDLE); + + return $aParentNames; + } + + /** + * PUBLIC Method getDirectChildNamesByHostName + * + * Gets the names of all child hosts + * + * @param String Name of host to get the children of + * @return Array Array with hostnames + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public function getDirectChildNamesByHostName($hostName) { + $arrChildNames = Array(); + + $QUERYHANDLE = $this->mysqlQuery('SELECT o2.name1 + FROM + `'.$this->dbPrefix.'objects` AS o1, + `'.$this->dbPrefix.'hosts` AS h1, + `'.$this->dbPrefix.'host_parenthosts` AS ph1, + `'.$this->dbPrefix.'hosts` AS h2, + `'.$this->dbPrefix.'objects` AS o2 + WHERE o1.objecttype_id=1 AND o1.name1=\''.$hostName.'\' + AND (h1.config_type='.$this->objConfigType.' AND h1.instance_id='.$this->dbInstanceId.' AND h1.host_object_id=o1.object_id) + AND o1.object_id=ph1.parent_host_object_id + AND (h2.config_type='.$this->objConfigType.' AND h2.instance_id='.$this->dbInstanceId.' AND h2.host_id=ph1.host_id) + AND o2.objecttype_id=1 AND h2.host_object_id=o2.object_id'); + while($data = mysql_fetch_array($QUERYHANDLE)) { + $arrChildNames[] = $data['name1']; + } + + // Free memory + mysql_free_result($QUERYHANDLE); + + return $arrChildNames; + } + + /** + * PUBLIC Method getHostsByHostgroupName + * + * Gets all hosts of a hostgroup + * + * @param String Name of hostgroup to get the hosts of + * @return Array Array with hostnames + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public function getHostsByHostgroupName($hostgroupName) { + $arrReturn = Array(); + + $QUERYHANDLE = $this->mysqlQuery('SELECT + o2.name1 + FROM + '.$this->dbPrefix.'objects AS o, + '.$this->dbPrefix.'hostgroups AS hg, + '.$this->dbPrefix.'hostgroup_members AS hgm, + '.$this->dbPrefix.'objects AS o2 + WHERE + (o.objecttype_id=3 AND o.name1 = binary \''.$hostgroupName.'\' AND o.instance_id='.$this->dbInstanceId.') + AND (hg.config_type='.$this->objConfigType.' AND hg.instance_id='.$this->dbInstanceId.' AND hg.hostgroup_object_id=o.object_id) + AND hgm.hostgroup_id=hg.hostgroup_id + AND (o2.objecttype_id=1 AND o2.object_id=hgm.host_object_id)'); + + while($data = mysql_fetch_array($QUERYHANDLE)) { + // Assign actual dataset to return array + $arrReturn[] = $data['name1']; + } + + // Free memory + mysql_free_result($QUERYHANDLE); + + return $arrReturn; + } + + /** + * PUBLIC Method getServicesByServicegroupName + * + * Gets all services of a servicegroup + * + * @param String Name of servicegroup to get the services of + * @return Array Array with hostnames and service descriptions + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public function getServicesByServicegroupName($servicegroupName) { + $arrReturn = Array(); + + $QUERYHANDLE = $this->mysqlQuery('SELECT + o2.name1, o2.name2 + FROM + '.$this->dbPrefix.'objects AS o, + '.$this->dbPrefix.'servicegroups AS sg, + '.$this->dbPrefix.'servicegroup_members AS sgm, + '.$this->dbPrefix.'objects AS o2 + WHERE + (o.objecttype_id=4 AND o.name1 = binary \''.$servicegroupName.'\' AND o.instance_id='.$this->dbInstanceId.') + AND (sg.config_type='.$this->objConfigType.' AND sg.instance_id='.$this->dbInstanceId.' AND sg.servicegroup_object_id=o.object_id) + AND sgm.servicegroup_id=sg.servicegroup_id + AND (o2.objecttype_id=2 AND o2.object_id=sgm.service_object_id)'); + + while($data = mysql_fetch_array($QUERYHANDLE)) { + // Assign actual dataset to return array + $arrReturn[] = Array('host_name' => $data['name1'], 'service_description' => $data['name2']); + } + + // Free memory + mysql_free_result($QUERYHANDLE); + + return $arrReturn; + } + + /** + * PUBLIC Method getServicegroupInformations + * + * Gets information like the alias for a servicegroup + * + * @param String Name of servicegroup + * @return Array Array with object information + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public function getServicegroupInformations($servicegroupName) { + $arrReturn = Array(); + + $QUERYHANDLE = $this->mysqlQuery('SELECT + o.object_id, sg.alias + FROM + '.$this->dbPrefix.'objects AS o, + '.$this->dbPrefix.'servicegroups AS sg + WHERE + (o.objecttype_id=4 AND o.name1 = binary \''.$servicegroupName.'\' AND o.instance_id='.$this->dbInstanceId.') + AND (sg.config_type='.$this->objConfigType.' AND sg.instance_id='.$this->dbInstanceId.' AND sg.servicegroup_object_id=o.object_id) + LIMIT 1'); + + $data = mysql_fetch_array($QUERYHANDLE); + + // Free memory + mysql_free_result($QUERYHANDLE); + + $arrReturn['alias'] = $data['alias']; + + return $arrReturn; + } + + /** + * PUBLIC Method getHostgroupInformations + * + * Gets information like the alias for a hostgroup + * + * @param String Name of group + * @return Array Array with object information + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public function getHostgroupInformations($groupName) { + $arrReturn = Array(); + + $QUERYHANDLE = $this->mysqlQuery('SELECT + o.object_id, g.alias + FROM + '.$this->dbPrefix.'objects AS o, + '.$this->dbPrefix.'hostgroups AS g + WHERE + (o.objecttype_id=3 AND o.name1 = binary \''.$groupName.'\' AND o.instance_id='.$this->dbInstanceId.') + AND (g.config_type='.$this->objConfigType.' AND g.instance_id='.$this->dbInstanceId.' AND g.hostgroup_object_id=o.object_id) + LIMIT 1'); + + $data = mysql_fetch_array($QUERYHANDLE); + + // Free memory + mysql_free_result($QUERYHANDLE); + + $arrReturn['alias'] = $data['alias']; + + return $arrReturn; + } + + /** + * PUBLIC getDirectChildDependenciesNamesByHostName() + * + * @param String Hostname + * @return Array List of hostnames + * @author Thibault Cohen <thibault.cohen@savoirfairelinux.com> + */ + public function getDirectChildDependenciesNamesByHostName($hostName, $min_business_impact=false) { + return $this->getDirectChildNamesByHostName($hostName); + } + + /* + * PUBLIC getDirectParentNamesByHostName() + * + * @param String Hostname + * @return Array List of hostnames + * @author Mathias Kettner <mk@mathias-kettner.de> + * @author Lars Michelsen <lars@vertical-visions.de> + */ + public function getDirectParentDependenciesNamesByHostName($hostName, $min_business_impact=false) { + return $this->getDirectParentNamesByHostName($hostName); + } + + public function getProgramStart() { + $QUERYHANDLE = $this->mysqlQuery('SELECT UNIX_TIMESTAMP(program_start_time) AS program_start ' + .'FROM '.$this->dbPrefix.'programstatus WHERE instance_id='.$this->dbInstanceId); + $data = mysql_fetch_array($QUERYHANDLE); + mysql_free_result($QUERYHANDLE); + if(isset($data[0])) + return $data[0]; + else + return -1; + } + + +} +?> diff --git a/library/nagvis-includes/TODO.md b/library/nagvis-includes/TODO.md new file mode 100644 index 0000000..ea17bcc --- /dev/null +++ b/library/nagvis-includes/TODO.md @@ -0,0 +1 @@ +GlobalBackendicingaweb2.php diff --git a/library/nagvis-includes/init.inc.php b/library/nagvis-includes/init.inc.php new file mode 100644 index 0000000..6eb4d46 --- /dev/null +++ b/library/nagvis-includes/init.inc.php @@ -0,0 +1,3 @@ +<?php + +set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path()); diff --git a/module.info b/module.info new file mode 100644 index 0000000..d0f2af2 --- /dev/null +++ b/module.info @@ -0,0 +1,12 @@ +Name: NagVis +Version: 1.1.1 +Depends: monitoring>=2.0.0 +Description: NagVis integration + NagVis is a visualization addon for Icinga or Nagios. It can be used + to visualize monitoring data in combination with all kinds of pictures + or maps. This allows to show the state of your infrastructure in many + different creative ways. + + This module requires NagVis to be configured and installed. It provides + an additional authentication-, authorisation and logon implementation + for NagVis. diff --git a/public/css/icingaweb-nagvis-integration.css b/public/css/icingaweb-nagvis-integration.css new file mode 100644 index 0000000..8fa0890 --- /dev/null +++ b/public/css/icingaweb-nagvis-integration.css @@ -0,0 +1,7 @@ +#header ul.head > li:first-child a { + display: none; +} + +#header ul.head.right li.dropdown { + display: none; +} diff --git a/public/css/module.less b/public/css/module.less new file mode 100644 index 0000000..b430555 --- /dev/null +++ b/public/css/module.less @@ -0,0 +1,3 @@ +.compact iframe { + height: 40em; +}
\ No newline at end of file diff --git a/public/js/module.js b/public/js/module.js new file mode 100644 index 0000000..1d30129 --- /dev/null +++ b/public/js/module.js @@ -0,0 +1,54 @@ + +(function(Icinga) { + + var Nagvis = function(module) { + + this.module = module; + + this.idCache = {}; + + this.initialize(); + + this.module.icinga.logger.debug('Nagvis module loaded'); + }; + + Nagvis.prototype = { + + initialize: function() + { + $('#nagvis-iframe').on('load', this.frameLoaded.bind(this)); + }, + + frameLoaded: function (event) { + var currentMap; + var icinga = this.module.icinga; + var $iframe = $('#nagvis-iframe'); + var matchNagvis = /[\?&]show=([^\&]+)/; + var matchIcinga = /[\?&]map=([^\&]+)/; + var currentMap = null; + var shownMap = null; + + icinga.logger.debug('Nagvis frame loaded'); + if (currentMap = $iframe.contents()[0].location.search.match(matchNagvis)) { + currentMap = currentMap[1]; + } + if (shownMap = document.location.search.match(matchIcinga)) { + shownMap = shownMap[1]; + } + if (currentMap !== null && shownMap !== currentMap) { + this.setCurrentMap(currentMap); + } + }, + + setCurrentMap: function (map) { +//icinga.logger.info(icinga.utils.addUrlParams(document.location.pathname + '?' + document.location.search, { map: 'asd' })) +// var url = parseUrl + this.module.icinga.logger.info("Setting current map", map); + } + + }; + + Icinga.availableModules.nagvis = Nagvis; + +}(Icinga)); + |