summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS9
-rw-r--r--COPYING339
-rw-r--r--README.md90
-rw-r--r--application/controllers/ShowController.php94
-rw-r--r--application/views/scripts/show/map.phtml18
-rw-r--r--configuration.php41
-rw-r--r--doc/01-authorization.md15
-rw-r--r--library/Nagvis/RestrictionHelper.php41
-rw-r--r--library/nagvis-includes/CoreAuthModIcingaweb2.php129
-rw-r--r--library/nagvis-includes/CoreAuthorisationModIcingaweb2.php183
-rw-r--r--library/nagvis-includes/CoreLogonIcingaweb2.php22
-rw-r--r--library/nagvis-includes/GlobalBackendicingaweb2.php1242
-rw-r--r--library/nagvis-includes/TODO.md1
-rw-r--r--library/nagvis-includes/init.inc.php3
-rw-r--r--module.info12
-rw-r--r--public/css/icingaweb-nagvis-integration.css7
-rw-r--r--public/css/module.less3
-rw-r--r--public/js/module.js54
18 files changed, 2303 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..0a4c29b
--- /dev/null
+++ b/AUTHORS
@@ -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>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..ecbc059
--- /dev/null
+++ b/COPYING
@@ -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) (&gt;= 2.0.0)
+* [NagVis](http://www.nagvis.org/) (&gt;= 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));
+