pynag-0.9.1+dfsg.orig/0000775000175000017500000000000012370025176013526 5ustar clintclintpynag-0.9.1+dfsg.orig/Makefile0000664000175000017500000000403512370025102015155 0ustar clintclintVERSION = 0.9.1 RELEASE = 1 DATE = $(shell date) NEWRELEASE = $(shell echo $$(($(RELEASE) + 1))) PYTHON = /usr/bin/python TOPDIR = $(shell pwd) DIRS = build docs contrib etc examples pynag scripts debian.upstream PYDIRS = pynag scripts examples debian EXAMPLEDIR = examples MANPAGES = pynag all: rpms versionfile: echo "version:" $(VERSION) > etc/version echo "release:" $(RELEASE) >> etc/version echo "source build date:" $(DATE) >> etc/version build: clean $(PYTHON) setup.py build -f clean: -rm -f MANIFEST -rm -rf dist/ build/ -rm -rf *~ -rm -rf rpm-build/ -rm -rf deb-build/ -rm -rf docs/*.1 -rm -f etc/version clean_hard: -rm -rf $(shell $(PYTHON) -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")/pynag clean_hardest: clean_rpms install: build $(PYTHON) setup.py install -f install_hard: clean_hard install install_harder: clean_harder install install_hardest: clean_harder clean_rpms rpms install_rpm install_rpm: -rpm -Uvh rpm-build/pynag-$(VERSION)-$(NEWRELEASE)$(shell rpm -E "%{?dist}").noarch.rpm recombuild: install_harder clean_rpms: -rpm -e pynag sdist: $(PYTHON) setup.py sdist pychecker: -for d in $(PYDIRS); do ($(MAKE) -C $$d pychecker ); done pyflakes: -for d in $(PYDIRS); do ($(MAKE) -C $$d pyflakes ); done money: clean -sloccount --addlang "makefile" $(TOPDIR) $(PYDIRS) $(EXAMPLEDIR) testit: clean -cd test; sh test-it.sh unittest: -nosetests -v -w test/unittest rpms: build sdist mkdir -p rpm-build cp dist/*.gz rpm-build/ rpmbuild --define "_topdir %(pwd)/rpm-build" \ --define "_builddir %{_topdir}" \ --define "_rpmdir %{_topdir}" \ --define "_srcrpmdir %{_topdir}" \ --define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' \ --define "_specdir %{_topdir}" \ --define "_sourcedir %{_topdir}" \ -ba pynag.spec debs: build sdist mkdir -p deb-build cp dist/*gz deb-build/pynag_${VERSION}.orig.tar.gz cd deb-build/ ; \ tar -zxvf pynag_${VERSION}.orig.tar.gz ; \ cd pynag-${VERSION} ;\ cp -r debian.upstream debian ;\ debuild pynag-0.9.1+dfsg.orig/pynag.spec0000664000175000017500000000770212370025102015513 0ustar clintclint%if 0%{?rhel} <= 5 %{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} %{!?python_version: %global python_version %(%{__python} -c "from distutils.sysconfig import get_python_version; print get_python_version()")} %endif # RHEL6 and newer has unittest2 # All other distributions assume that we have access to unittest2 %define unittest2 0 %if 0%{?rhel} %if 0%{?rhel} >= 6 %define unittest2 1 %endif %else %define unittest2 1 %endif %define release 1 Summary: Python modules and utilities for Nagios plugins and configuration Name: pynag Version: 0.9.1 Release: %{release}%{?dist} Source0: http://pynag.googlecode.com/files/%{name}-%{version}.tar.gz License: GPLv2 Group: System Environment/Libraries BuildRequires: python-devel BuildRequires: python-setuptools BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) Url: http://pynag.org/ BuildArch: noarch %if 0%{?unittest2} BuildRequires: python-unittest2 %endif %description Python modules and utilities for pragmatically handling Nagios configuration file maintenance, status information, log file parsing and plug-in development. %package examples Group: System Environment/Libraries Summary: Example scripts which manipulate Nagios configuration Requires: pynag %description examples Example scripts which manipulate Nagios configuration files. Provided are scripts which list services, do network discovery among other tasks. %prep %setup -q %build %{__python} setup.py build %if 0%{?unittest2} %{__python} setup.py test %endif %install test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT %{__python} setup.py install --prefix=/usr --root=$RPM_BUILD_ROOT install -m 755 -d $RPM_BUILD_ROOT/%{_datadir}/%{name}/examples install -m 755 -d $RPM_BUILD_ROOT/%{_datadir}/%{name}/examples/Model install -m 755 -d $RPM_BUILD_ROOT/%{_datadir}/%{name}/examples/Utils install -m 755 -d $RPM_BUILD_ROOT/%{_datadir}/%{name}/examples/Parsers install -m 755 -d $RPM_BUILD_ROOT/%{_datadir}/%{name}/examples/Plugins install -m 755 examples/Model/* $RPM_BUILD_ROOT/%{_datadir}/%{name}/examples/Model/ install -m 755 examples/Parsers/* $RPM_BUILD_ROOT/%{_datadir}/%{name}/examples/Parsers/ install -m 755 examples/Plugins/* $RPM_BUILD_ROOT/%{_datadir}/%{name}/examples/Plugins/ %clean rm -fr $RPM_BUILD_ROOT %files %defattr(-, root, root, -) %if "%{python_version}" >= "2.5" %{python_sitelib}/pynag*.egg-info %endif %{python_sitelib}/pynag/ %{_bindir}/pynag %{_mandir}/man1/pynag.1.gz %doc AUTHORS README.md LICENSE CHANGES %dir %{_datadir}/%{name} %files examples %defattr(-, root, root, -) %{_datadir}/%{name}/examples %doc examples/README %changelog * Wed Aug 30 2013 Pall Sigurdsson 0.6.1-1 - New upstream version * Tue Apr 30 2013 Tomas Edwardsson 0.4.9-1 - New upstream version * Wed Dec 12 2012 Pall Sigurdsson 0.4.8-1 - New upstream version * Tue Aug 21 2012 Pall Sigurdsson 0.4.5-1 - New upstream version * Fri Aug 17 2012 Pall Sigurdsson 0.4.4-1 - New upstream version * Mon Jul 23 2012 Tomas Edwardsson 0.4.3-1 - New upstream version * Fri Jun 29 2012 Pall Sigurdsson 0.4.2-1 - pynag script added to spec file. Other scripts removed (palli@opensource.is) * Tue May 9 2012 Tomas Edwardsson 0.4.1-6 - Simplified spec file, threw out lots of legacy conditionals - Added Requires parent for pynag-examples * Mon Jul 4 2011 Pall Sigurdsson - 0.4-1 - New upstream version - Config refactoring - New Model module * Wed Apr 27 2011 Tomas Edwardsson - 0.3-3 - Added examples package and moved example files there * Fri Jan 26 2011 Tomas Edwardsson - 0.3-2 - Fixes for spelling and some issues reported by rpmlint * Fri Jan 22 2011 Tomas Edwardsson - 0.3-1 - Initial RPM Creation, based heavily on the func spec file pynag-0.9.1+dfsg.orig/LICENSE0000664000175000017500000004310412310325103014520 0ustar clintclint 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. Copyright (C) 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. , 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. pynag-0.9.1+dfsg.orig/pynag/0000775000175000017500000000000012370025176014644 5ustar clintclintpynag-0.9.1+dfsg.orig/pynag/Utils/0000775000175000017500000000000012370025176015744 5ustar clintclintpynag-0.9.1+dfsg.orig/pynag/Utils/misc.py0000664000175000017500000005515012354645173017266 0ustar clintclint# -*- coding: utf-8 -*- """ miscellaneous utils classes and function. The reason they don't live in in pynag.Utils is that they have more requirements, and they do on occation import pynag.Model og pynag.Parsers """ import tempfile import os import time import pynag.Parsers import pynag.Model from pynag.Utils import PynagError class FakeNagiosEnvironment(object): """ Creates a fake nagios environment with minimal configs in /tmp/ Example: >>> nagios = FakeNagiosEnvironment() >>> nagios.create_minimal_environment() # Create temporary director with minimal config >>> nagios.update_model() # Update the global variables in pynag.Model >>> nagios.configure_livestatus() # Configure a livestatus socket >>> result, stdout, stderr = nagios.start() # Start up nagios >>> config = nagios.get_config() # Returns Parsers.Config instance >>> livestatus = nagios.get_livestatus() # Returns Parsers.Livestatus instance >>> result, stdout, sdterr = nagios.stop() # Stop nagios >>> nagios.terminate() # Stops nagios and cleans up everything """ def __init__(self, global_config_file=None, p1_file=None, livestatus=False): self.global_config_file = global_config_file self.p1_file = p1_file self._model_is_dirty = False self.livestatus_module_path = livestatus self.livestatus_object = None self.config = None self.tempdir = tempfile.mkdtemp('nagios-environment') path_to_socket = os.path.join(self.tempdir, "livestatus.socket") self.livestatus_socket_path = path_to_socket def get_config(self): if not self.config: self.create_minimal_environment() return self.config def get_livestatus(self): if not self.livestatus_object: self.configure_livestatus() return self.livestatus_object def update_model(self): """ Update the global variables in pynag.Model to point to our config """ self.original_objects_dir = pynag.Model.pynag_directory self.original_cfg_file = pynag.Model.cfg_file self.original_config = pynag.Model.config pynag.Model.config = self.get_config() pynag.Model.cfg_file = self.config.cfg_file pynag.Model.pynag_directory = self.objects_dir pynag.Model.eventhandlers = [] self._model_is_dirty = True def open_decorator(self, func): """ Safety decorator aroundaround self.config.open() It wraps around self.config.open() and raises error if the fake nagios environment tries to open file outside the sandbox. """ def wrap(filename, *args, **kwargs): if filename.startswith(self.tempdir): return func(filename, *args, **kwargs) else: raise PynagError("FakeNagiosEnvironment tried to open file outside its sandbox: %s" % (filename, )) wrap.__name__ = func.__name__ wrap.__module__ = func.__module__ return wrap def restore_model(self): """ Restores the global variables in pynag.Model """ pynag.Model.config = self.original_config pynag.Model.cfg_file = self.original_cfg_file pynag.Model.pynag_directory = self.original_objects_dir self._model_is_dirty = False def create_minimal_environment(self): """ Starts a nagios server with empty config in an isolated environment """ t = self.tempdir cfg_file = self.cfg_file = os.path.join(t, "nagios.cfg") open(cfg_file, 'w').write('') objects_dir = self.objects_dir = os.path.join(t, "conf.d") os.mkdir(objects_dir) check_result_path = os.path.join(self.tempdir, 'checkresults') os.mkdir(check_result_path) log_dir = os.path.join(self.tempdir, 'log') archive_dir = os.path.join(log_dir, 'archive') os.mkdir(log_dir) os.mkdir(archive_dir) with open(objects_dir + "/minimal_config.cfg", 'w') as f: f.write(minimal_config) config = self.config = pynag.Parsers.config(cfg_file=cfg_file) self.config.open = self.open_decorator(self.config.open) config.parse() config._edit_static_file(attribute='log_archive_path', new_value=os.path.join(t, "log/archive")) config._edit_static_file(attribute='log_file', new_value=os.path.join(t, "log/nagios.log")) config._edit_static_file(attribute='object_cache_file', new_value=os.path.join(t, "objects.cache")) config._edit_static_file(attribute='precached_object_file', new_value=os.path.join(t, "/objects.precache")) config._edit_static_file(attribute='lock_file', new_value=os.path.join(t, "nagios.pid")) config._edit_static_file(attribute='command_file', new_value=os.path.join(t, "nagios.cmd")) config._edit_static_file(attribute='state_retention_file', new_value=os.path.join(t, "retention.dat")) config._edit_static_file(attribute='status_file', new_value=os.path.join(t, "status.dat")) config._edit_static_file(attribute='cfg_dir', new_value=objects_dir) config._edit_static_file(attribute='log_initial_states', new_value="1") config._edit_static_file(attribute='enable_embedded_perl', new_value='0') config._edit_static_file(attribute='event_broker_options', new_value='-1') config._edit_static_file(attribute='illegal_macro_output_chars', new_value='''~$&|<>''') config._edit_static_file(attribute='check_result_path', new_value=check_result_path) config._edit_static_file(attribute='temp_path', new_value=log_dir) config._edit_static_file(attribute='temp_file', new_value=os.path.join(t, "nagios.tmp")) def clean_up(self): """ Clean up all temporary directories """ command = ['rm', '-rf', self.tempdir] pynag.Utils.runCommand(command=command, shell=False) def terminate(self): """ Stop the nagios environment and remove all temporary files """ self.stop() if self._model_is_dirty: self.restore_model() self.clean_up() def start(self, start_command=None, timeout=10): self.configure_p1_file() if not start_command: nagios_binary = self.config.guess_nagios_binary() start_command = "%s -d %s" % (nagios_binary, self.config.cfg_file) result = pynag.Utils.runCommand(command=start_command) code, stdout, stderr = result pid_file = os.path.join(self.tempdir, "nagios.pid") while not os.path.exists(pid_file) and timeout: timeout -= 1 time.sleep(1) start_error = None if not os.path.exists(pid_file): start_error = "Nagios pid file did not materialize" if result[0] != 0: start_error = "Nagios did not start, bad return code" if start_error: if os.path.exists(os.path.join(self.tempdir, "nagios.log")): log_file_output = open(os.path.join(self.tempdir, "nagios.log")).read() else: log_file_output = "No log file found." message = start_error message += "Command: {start_command}\n" message += "Exit Code: {code}\n" message += "============\nStandard out\n{stdout}\n" message += "=============\nStandard Err\n{stderr}\n" message += "=============\nLog File output\n{log_file_output}\n" message = message.format(**locals()) raise Exception(message) return result def stop(self, stop_command=None): pid_file = os.path.join(self.tempdir, "nagios.pid") if not os.path.exists(pid_file): return pid = open(pid_file).read() if not stop_command: stop_command = "kill -9 %s" % pid result = pynag.Utils.runCommand(stop_command) time.sleep(0.5) return result def configure_livestatus(self): if not self.livestatus_module_path: self.livestatus_module_path = self.guess_livestatus_path() line = "%s %s" % (self.livestatus_module_path, self.livestatus_socket_path) config = self.get_config() config._edit_static_file(attribute="broker_module", new_value=line) self.livestatus_object = pynag.Parsers.Livestatus( nagios_cfg_file=config.cfg_file, ) def guess_p1_file(self): global_config = pynag.Parsers.config(cfg_file=self.global_config_file) global_config.parse_maincfg() for k, v in global_config.maincfg_values: if k == 'p1_file': return v def configure_p1_file(self): if not self.p1_file: self.p1_file = self.guess_p1_file() config = self.get_config() config._edit_static_file(attribute='p1_file', new_value=self.p1_file) def guess_livestatus_path(self): """ Tries to guess a path to mk-livestatus Returns: string containing full path to mk-livestatus broker_module """ # Find mk_livestatus broker module global_config = pynag.Parsers.config(cfg_file=self.global_config_file) global_config.parse_maincfg() for k, v in global_config.maincfg_values: if k == 'broker_module' and 'livestatus' in v: livestatus_module = v.split()[0] return livestatus_module def import_config(self, path): """ Copies any file or directory into our environment and include it in object configuration Args: path: full path to a nagios cfg file or a directory of cfg files Raises: Exception if path is not found """ destination = self.objects_dir command = "cp -r '{path}' '{destination}/'".format(**locals()) return os.system(command) minimal_config = r""" define timeperiod { alias 24 Hours A Day, 7 Days A Week friday 00:00-24:00 monday 00:00-24:00 saturday 00:00-24:00 sunday 00:00-24:00 thursday 00:00-24:00 timeperiod_name 24x7 tuesday 00:00-24:00 wednesday 00:00-24:00 } define timeperiod { alias 24x7 Sans Holidays friday 00:00-24:00 monday 00:00-24:00 saturday 00:00-24:00 sunday 00:00-24:00 thursday 00:00-24:00 timeperiod_name 24x7_sans_holidays tuesday 00:00-24:00 use us-holidays ; Get holiday exceptions from other timeperiod wednesday 00:00-24:00 } define contactgroup { alias Nagios Administrators contactgroup_name admins members nagiosadmin } define command { command_line $USER1$/check_ping -H $HOSTADDRESS$ -w 3000.0,80% -c 5000.0,100% -p 5 command_name check-host-alive } define command { command_line $USER1$/check_dhcp $ARG1$ command_name check_dhcp } define command { command_line $USER1$/check_ftp -H $HOSTADDRESS$ $ARG1$ command_name check_ftp } define command { command_line $USER1$/check_hpjd -H $HOSTADDRESS$ $ARG1$ command_name check_hpjd } define command { command_line $USER1$/check_http -I $HOSTADDRESS$ $ARG1$ command_name check_http } define command { command_line $USER1$/check_imap -H $HOSTADDRESS$ $ARG1$ command_name check_imap } define command { command_line $USER1$/check_disk -w $ARG1$ -c $ARG2$ -p $ARG3$ command_name check_local_disk } define command { command_line $USER1$/check_load -w $ARG1$ -c $ARG2$ command_name check_local_load } define command { command_line $USER1$/check_mrtgtraf -F $ARG1$ -a $ARG2$ -w $ARG3$ -c $ARG4$ -e $ARG5$ command_name check_local_mrtgtraf } define command { command_line $USER1$/check_procs -w $ARG1$ -c $ARG2$ -s $ARG3$ command_name check_local_procs } define command { command_line $USER1$/check_swap -w $ARG1$ -c $ARG2$ command_name check_local_swap } define command { command_line $USER1$/check_users -w $ARG1$ -c $ARG2$ command_name check_local_users } define command { command_line $USER1$/check_nt -H $HOSTADDRESS$ -p 12489 -v $ARG1$ $ARG2$ command_name check_nt } define command { command_line $USER1$/check_ping -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -p 5 command_name check_ping } define command { command_line $USER1$/check_pop -H $HOSTADDRESS$ $ARG1$ command_name check_pop } define command { command_line $USER1$/check_smtp -H $HOSTADDRESS$ $ARG1$ command_name check_smtp } define command { command_line $USER1$/check_snmp -H $HOSTADDRESS$ $ARG1$ command_name check_snmp } define command { command_line $USER1$/check_ssh $ARG1$ $HOSTADDRESS$ command_name check_ssh } define command { command_line $USER1$/check_tcp -H $HOSTADDRESS$ -p $ARG1$ $ARG2$ command_name check_tcp } define command { command_line $USER1$/check_udp -H $HOSTADDRESS$ -p $ARG1$ $ARG2$ command_name check_udp } define contact { name generic-contact host_notification_commands notify-host-by-email host_notification_options d,u,r,f,s host_notification_period 24x7 register 0 service_notification_commands notify-service-by-email service_notification_options w,u,c,r,f,s service_notification_period 24x7 } define host { name generic-host event_handler_enabled 1 failure_prediction_enabled 1 flap_detection_enabled 1 notification_period 24x7 notifications_enabled 1 process_perf_data 1 register 0 retain_nonstatus_information 1 retain_status_information 1 max_check_attempts 3 } define host { name generic-printer use generic-host check_command check-host-alive check_interval 5 check_period 24x7 contact_groups admins max_check_attempts 10 notification_interval 30 notification_options d,r notification_period workhours register 0 retry_interval 1 statusmap_image printer.png } define host { name generic-router use generic-switch register 0 statusmap_image router.png } define service { name generic-service action_url /pnp4nagios/graph?host=$HOSTNAME$&srv=$SERVICEDESC$ active_checks_enabled 1 check_freshness 0 check_period 24x7 event_handler_enabled 1 failure_prediction_enabled 1 flap_detection_enabled 1 icon_image unknown.gif is_volatile 0 max_check_attempts 3 normal_check_interval 10 notes_url /adagios/objectbrowser/edit_object/object_type=service/shortname=$HOSTNAME$/$SERVICEDESC$ notification_interval 60 notification_options w,u,c,r notification_period 24x7 notifications_enabled 1 obsess_over_service 1 parallelize_check 1 passive_checks_enabled 1 process_perf_data 1 register 0 retain_nonstatus_information 1 retain_status_information 1 retry_check_interval 2 } define host { name generic-switch use generic-host check_command check-host-alive check_interval 5 check_period 24x7 contact_groups admins max_check_attempts 10 notification_interval 30 notification_options d,r notification_period 24x7 register 0 retry_interval 1 statusmap_image switch.png } define host { name linux-server use generic-host check_command check-host-alive check_interval 5 check_period 24x7 contact_groups admins max_check_attempts 10 notification_interval 120 notification_options d,u,r notification_period workhours register 0 retry_interval 1 } define service { name local-service use generic-service max_check_attempts 4 normal_check_interval 5 register 0 retry_check_interval 1 } define contact { use generic-contact alias Nagios Admin contact_name nagiosadmin email nagios@localhost } define timeperiod { alias No Time Is A Good Time timeperiod_name none } define command { command_line /usr/bin/printf "%b" "***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n" | /bin/mail -s "** $NOTIFICATIONTYPE$ Host Alert: $HOSTNAME$ is $HOSTSTATE$ **" $CONTACTEMAIL$ command_name notify-host-by-email } define command { command_line /usr/bin/printf "%b" "***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\n\nService: $SERVICEDESC$\nHost: $HOSTALIAS$\nAddress: $HOSTADDRESS$\nState: $SERVICESTATE$\n\nDate/Time: $LONGDATETIME$\n\nAdditional Info:\n\n$SERVICEOUTPUT$\n" | /bin/mail -s "** $NOTIFICATIONTYPE$ Service Alert: $HOSTALIAS$/$SERVICEDESC$ is $SERVICESTATE$ **" $CONTACTEMAIL$ command_name notify-service-by-email } define command { command_line /usr/bin/printf "%b" "$LASTHOSTCHECK$\t$HOSTNAME$\t$HOSTSTATE$\t$HOSTATTEMPT$\t$HOSTSTATETYPE$\t$HOSTEXECUTIONTIME$\t$HOSTOUTPUT$\t$HOSTPERFDATA$\n" >> /var/log/nagios/host-perfdata.out command_name process-host-perfdata } define command { command_line /usr/bin/printf "%b" "$LASTSERVICECHECK$\t$HOSTNAME$\t$SERVICEDESC$\t$SERVICESTATE$\t$SERVICEATTEMPT$\t$SERVICESTATETYPE$\t$SERVICEEXECUTIONTIME$\t$SERVICELATENCY$\t$SERVICEOUTPUT$\t$SERVICEPERFDATA$\n" >> /var/log/nagios/service-perfdata.out command_name process-service-perfdata } define timeperiod { alias U.S. Holidays december 25 00:00-00:00 ; Christmas january 1 00:00-00:00 ; New Years july 4 00:00-00:00 ; Independence Day monday -1 may 00:00-00:00 ; Memorial Day (last Monday in May) monday 1 september 00:00-00:00 ; Labor Day (first Monday in September) name us-holidays thursday 4 november 00:00-00:00 ; Thanksgiving (4th Thursday in November) timeperiod_name us-holidays } define host { name windows-server use generic-host check_command check-host-alive check_interval 5 check_period 24x7 contact_groups admins hostgroups max_check_attempts 10 notification_interval 30 notification_options d,r notification_period 24x7 register 0 retry_interval 1 } define hostgroup { alias Windows Servers hostgroup_name windows-servers } define timeperiod { alias Normal Work Hours friday 09:00-17:00 monday 09:00-17:00 thursday 09:00-17:00 timeperiod_name workhours tuesday 09:00-17:00 wednesday 09:00-17:00 } define command { command_name check_dummy command_line $USER1$/check_dummy!$ARG1$!$ARG2$ } define host { host_name ok_host use generic-host address ok_host max_check_attempts 1 check_command check_dummy!0!Everything seems to be okay } define service { host_name ok_host use generic-service service_description ok service 1 check_command check_dummy!0!Everything seems to be okay } """ pynag-0.9.1+dfsg.orig/pynag/Utils/checkresult.py0000664000175000017500000001155412367401646020646 0ustar clintclint# !/usr/bin/python # # NagiosCheckResult- Class that creates Nagios checkresult file and # writes Passive Host and Service checks to it # # 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 3 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, see . # # import os import tempfile import time service_state = ['OK', 'WARNING', 'CRITICAL', 'UNKNOWN'] host_state = ['UP', 'DOWN', 'DOWN', 'DOWN'] class CheckResult(object): """ Methods for creating host and service checkresults for nagios processing """ def __init__(self, nagios_result_dir, file_time=None): if file_time is not None: self.file_time = file_time else: self.file_time = time.time() # Nagios is quite fussy about the filename, it must be # a 7 character name starting with 'c' self.fh, self.cmd_file = tempfile.mkstemp(prefix='c', dir=nagios_result_dir) os.write(self.fh, "### Active Check Result File ###\n") os.write(self.fh, "file_time=" + str(self.file_time) + "\n") def service_result(self, host_name, service_description, **kwargs): """ Create a service checkresult Any kwarg will be added to the checkresult Args: host_name (str) service_descritpion (str) Kwargs: check_type (int): active(0) or passive(1) check_options (int) scheduled_check (int) reschedule_check (int) latency (float) start_time (float) finish_time (float) early_timeout (int) exited_ok (int) return_code (int) output (str): plugin output """ kwargs.update({ 'host_name': host_name, 'service_description': service_description }) return self.__output_result(**kwargs) def host_result(self, host_name, **kwargs): """ Create a service checkresult Any kwarg will be added to the checkresult Args: host_name (str) service_descritpion (str) Kwargs: check_type (int): active(0) or passive(1) check_options (int) scheduled_check (int) reschedule_check (int) latency (float) start_time (float) finish_time (float) early_timeout (int) exited_ok (int) return_code (int) output (str): plugin output """ kwargs.update({ 'host_name': host_name, }) return self.__output_result(**kwargs) def __output_result(self, **kwargs): """ Create a checkresult Kwargs: host_name (str) service_descritpion (str) check_type (int): active(0) or passive(1) check_options (int) scheduled_check (int) reschedule_check (int) latency (float) start_time (float) finish_time (float) early_timeout (int) exited_ok (int) return_code (int) output (str): plugin output """ parms = { 'check_type': 0, # Active 'check_options': 0, 'scheduled_check': 0, 'reschedule_check': 0, 'latency': 0.0, 'start_time': time.time(), 'finish_time': time.time(), 'early_timeout': 0, 'exited_ok': 0, 'return_code': 0 } parms.update(**kwargs) object_type = 'host' if 'service_description' in parms: object_type = 'service' if 'output' not in parms: if object_type == 'host': parms['output'] = host_state[int(parms['return_code'])] else: parms['output'] = service_state[int(parms['return_code'])] parms['output'].replace('\n', '\\n') os.write(self.fh, """ ### Nagios {1} Check Result ### # Time: {0}\n""".format(self.file_time, object_type.capitalize())) for key, value in parms.items(): os.write(self.fh, key + "=" + str(value) + "\n") def submit(self): """Submits the results to nagios""" os.close(self.fh) ok_filename = self.cmd_file + ".ok" ok_fh = file(ok_filename, 'a') ok_fh.close() return self.cmd_file pynag-0.9.1+dfsg.orig/pynag/Utils/__init__.py0000664000175000017500000013203312355767315020072 0ustar clintclint# -*- coding: utf-8 -*- # # pynag - Python Nagios plug-in and configuration environment # Copyright (C) 2010 Drew Stinnet # # 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. """ Misc utility classes and helper functions for pynag This module contains misc classes and conveninence functions that are used throughout the pynag library. """ import subprocess import re import shlex import threading from os import getenv, environ, listdir, path from platform import node from getpass import getuser import datetime import pynag.Plugins import sys from pynag.Utils.checkresult import CheckResult rlock = threading.RLock() class PynagError(Exception): """ The default pynag exception. Exceptions raised within the pynag library should aim to inherit this one. """ def __init__(self, message, errorcode=None, errorstring=None, *args, **kwargs): self.errorcode = errorcode self.message = message self.errorstring = errorstring try: super(self.__class__, self).__init__(message, *args, **kwargs) except TypeError: # Python 2.4 is fail Exception.__init__(self, message, *args, **kwargs) def runCommand(command, raise_error_on_fail=False, shell=True, env=None): """ Run command from the shell prompt. Wrapper around subprocess. Args: command (str): string containing the command line to run raise_error_on_fail (bool): Raise PynagError if returncode > 0 Returns: str: stdout/stderr of the command run Raises: PynagError if returncode > 0 """ run_env = environ.copy() # Merge dict into environ if env: run_env.update(env) proc = subprocess.Popen(command, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=run_env) stdout, stderr = proc.communicate('through stdin to stdout') result = proc.returncode, stdout, stderr if proc.returncode > 0 and raise_error_on_fail: error_string = "* Could not run command (return code= %s)\n" % proc.returncode error_string += "* Error was:\n%s\n" % (stderr.strip()) error_string += "* Command was:\n%s\n" % command error_string += "* Output was:\n%s\n" % (stdout.strip()) if proc.returncode == 127: # File not found, lets print path path = getenv("PATH") error_string += "Check if y/our path is correct: %s" % path raise PynagError(error_string) else: return result class GitRepo(object): def __init__(self, directory, auto_init=True, author_name="Pynag User", author_email=None): """ Python Wrapper around Git command line. Args: Directory (str): Path to the directory does the git repo reside in (i.e. '/etc/nagios') auto_init (bool): If True and directory does not contain a git repo, create it automatically author_name (str): Full name of the author making changes author_email (str): Email used for commit messages, if None, then use username@hostname """ self.directory = directory # Who made the change if author_name is None: author_name = "Pynag User" if author_email is None: author_email = "<%s@%s>" % (getuser(), node()) self.author_name = author_name self.author_email = author_email # Which program did the change #self.source = source # Every string in self.messages indicated a line in the eventual commit # message self.messages = [] self.ignore_errors = False self._update_author() if auto_init: try: self._run_command('git status --short') except PynagError: t, e = sys.exc_info()[:2] if e.errorcode == 128: self.init() #self._run_command('git status --short') self._is_dirty = self.is_dirty # Backwards compatibility def _update_author(self): """ Updates environment variables GIT_AUTHOR_NAME and EMAIL Returns: None """ environ['GIT_AUTHOR_NAME'] = self.author_name environ['GIT_AUTHOR_EMAIL'] = self.author_email.strip('<').strip('>') def _run_command(self, command): """ Run a specified command from the command line. Return stdout Args: command (str): command to execute Returns: stdout of the executed command """ import subprocess import os cwd = self.directory proc = subprocess.Popen(command, cwd=cwd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,) stdout, stderr = proc.communicate('through stdin to stdout') returncode = proc.returncode if returncode > 0 and self.ignore_errors is False: errorstring = "Command '%s' returned exit status %s.\n stdout: %s \n stderr: %s\n Current user: %s" errorstring = errorstring % (command, returncode, stdout, stderr, getuser()) raise PynagError(errorstring, errorcode=returncode, errorstring=stderr) return stdout def is_up_to_date(self): """ Returns True if all files in git repo are fully commited Returns: bool. Git repo is up-to-date True -- All files are commited False -- At least one file is not commited """ return len(self.get_uncommited_files()) == 0 def get_valid_commits(self): """ Returns a list of all commit ids from git log Returns: List of all valid commit hashes """ return map(lambda x: x.get('hash'), self.log()) def get_uncommited_files(self): """ Returns a list of files that are have unstaged changes Returns: List. All files that have unstaged changes. """ output = self._run_command("git status --porcelain") result = [] for line in output.split('\n'): line = line.split(None, 1) if len(line) < 2: continue status, filename = line[0], line[1] # If file has been renamed, git status shows output in the form of: # R nrpe.cfg -> nrpe.cfg~ # We want only the last part of the filename if status == 'R': filename = filename.split('->')[1].strip() # If there are special characters in the name, git will double-quote the output # We will remove those quotes, but we cannot use strip because it will damage: # files like this: "\"filename with actual doublequotes\"" if filename.startswith('"') and filename.endswith('"'): filename = filename[1:-1] result.append({'status': status, 'filename': filename}) return result def log(self, **kwargs): """ Returns a log of previous commits. Log is is a list of dict objects. Any arguments provided will be passed directly to pynag.Utils.grep() to filter the results. Args: kwargs: Arguments passed to pynag.Utils.grep() Returns: List of dicts. Log of previous commits. Examples: self.log(author_name='nagiosadmin') self.log(comment__contains='localhost') """ raw_log = self._run_command("git log --pretty='%H\t%an\t%ae\t%at\t%s'") result = [] for line in raw_log.splitlines(): hash, author, authoremail, authortime, comment = line.split("\t", 4) result.append({ "hash": hash, "author_name": author, "author_email": authoremail, "author_time": datetime.datetime.fromtimestamp(float(authortime)), "timestamp": float(authortime), "comment": comment, }) return grep(result, **kwargs) def diff(self, commit_id_or_filename=None): """ Returns diff (as outputted by "git diff") for filename or commit id. If commit_id_or_filename is not specified. show diff against all uncommited files. Args: commit_id_or_filename (str): git commit id or file to diff with Returns: str. git diff for filename or commit id Raises: PynagError: Invalid commit id or filename was given """ if commit_id_or_filename in ('', None): command = "git diff" elif path.exists(commit_id_or_filename): commit_id_or_filename = commit_id_or_filename.replace("'", r"\'") command = "git diff '%s'" % commit_id_or_filename elif commit_id_or_filename in self.get_valid_commits(): commit_id_or_filename = commit_id_or_filename.replace("'", r"\'") command = "git diff '%s'" % commit_id_or_filename else: raise PynagError("%s is not a valid commit id or filename" % commit_id_or_filename) # Clean single quotes from parameters: return self._run_command(command) def show(self, commit_id,): """ Returns output from "git show" for a specified commit_id Args: commit_id (str): Commit id of the commit to display (``git show``) Returns: str. Output of ``git show commit_id`` Raises: PynagError: Invalid commit_id was given """ if commit_id not in self.get_valid_commits(): raise PynagError("%s is not a valid commit id" % commit_id) command = "git show %s" % commit_id return self._run_command(command) def init(self): """ Initilizes a new git repo (i.e. run "git init") """ self._update_author() command = "git init" self._run_command("git init") # Only do initial commit if there are files in the directory if not listdir(self.directory) == ['.git']: self.commit(message='Initial Commit') def _git_add(self, filename): """ Deprecated, use self.add() instead. """ return self.add(filename) def _git_commit(self, filename, message, filelist=None): """ Deprecated. Use self.commit() instead.""" if filelist is None: filelist = [] if not filename is None: filelist.append(filename) return self.commit(message=message, filelist=filelist) def pre_save(self, object_definition, message): """ Commits object_definition.get_filename() if it has any changes. This function is called by :py:class:`pynag.Model.EventHandlers` before calling :py:meth:`pynag.Utils.GitRepo.save` Args: object_definition (pynag.Model.ObjectDefinition): object to commit changes message (str): git commit message as specified in ``git commit -m`` A message from the authors: *"Since this is still here, either i forgot to remove it, or because it is here for backwards compatibility, palli"* """ filename = object_definition.get_filename() if self.is_dirty(filename): self._git_add(filename) self._git_commit(filename, message="External changes commited in %s '%s'" % (object_definition.object_type, object_definition.get_shortname())) def save(self, object_definition, message): """ Commits object_definition.get_filename() if it has any changes. This function is called by :py:class:`pynag.Model.EventHandlers` Args: object_definition (pynag.Model.ObjectDefinition): object to commit changes message (str): git commit message as specified in ``git commit -m`` """ filename = object_definition.get_filename() if len(self.messages) > 0: message = [message, '\n'] + self.messages message = '\n'.join(message) self._git_add(filename) if self.is_dirty(filename): self._git_commit(filename, message) self.messages = [] def is_dirty(self, filename): """ Returns True if filename needs to be committed to git Args: filename (str): file to check """ command = "git status --porcelain '%s'" % filename output = self._run_command(command) # Return True if there is any output return len(output) > 0 def write(self, object_definition, message): """ This method is called whenever :py:class:`pynag.Model.EventHandlers` is called. Args: object_definition (pynag.Model.ObjectDefinition): Object to write to file. message (str): git commit message as specified in ``git commit -m`` """ # When write is called ( something was written to file ) # We will log it in a buffer, and commit when save() is called. self.messages.append(" * %s" % message) def revert(self, commit): """ Revert some existing commits works like "git revert" """ commit = commit.replace(r"'", r"\'") command = "git revert --no-edit -- '%s'" % commit return self._run_command(command) def commit(self, message='commited by pynag', filelist=None, author=None): """ Commit files with "git commit" Args: message (str): Message used for the git commit filelist (list of strings): List of filenames to commit (if None, then commit all files in the repo) author (str): Author to use for git commit. If any is specified, overwrite self.author_name and self.author_email Returns: stdout from the "git commit" shell command. """ # Lets escape all single quotes from the message message = message.replace("'", r"\'") if author is None: author = "%s <%s>" % (self.author_name, self.author_email) # Escape all single quotes in author: author = author.replace("'", r"\'") if filelist is None: # If no files provided, commit everything self.add('.') command = "git commit -a -m '%s' --author='%s'" % (message, author) return self._run_command(command=command) elif isinstance(filelist, str): # in case filelist was provided as a string, consider to be only # one file filelist = [filelist] # Remove from commit list files that have not changed: filelist = filter(lambda x: self.is_dirty(x), filelist) # Run "git add" on every file. Just in case they are untracked self.ignore_errors = True for i in filelist: self.add(i) self.ignore_errors = True # Change ['file1','file2'] into the string """ 'file1' 'file2' """ filestring = '' # Escape all single quotes in filenames filelist = map(lambda x: x.replace("'", r"\'"), filelist) # Wrap filename inside single quotes: filelist = map(lambda x: "'%s'" % x, filelist) # If filelist is empty, we have nothing to commit and we will return as # opposed to throwing error if not filelist: return # Create a space seperated string with the filenames filestring = ' '.join(filelist) command = "git commit -m '%s' --author='%s' -- %s" % (message, author, filestring) return self._run_command(command=command) def add(self, filename): """ Run git add on filename Args: filename (str): name of one file to add, Returns: str. The stdout from "git add" shell command. """ # Escape all single quotes in filename: filename = filename.replace("'", r"\'") command = "git add -- '%s'" % filename return self._run_command(command) class PerfData(object): """ Data Structure for a nagios perfdata string with multiple perfdata metric Example string: >>> perf = PerfData("load1=10 load2=10 load3=20 'label with spaces'=5") >>> perf.metrics ['load1'=10;;;;, 'load2'=10;;;;, 'load3'=20;;;;, 'label with spaces'=5;;;;] >>> for i in perf.metrics: print("%s %s" % (i.label, i.value)) load1 10 load2 10 load3 20 label with spaces 5 """ def __init__(self, perfdatastring=""): """ >>> perf = PerfData("load1=10 load2=10 load3=20") """ self.metrics = [] self.invalid_metrics = [] # Hack: For some weird reason livestatus sometimes delivers perfdata in # utf-32 encoding. perfdatastring = perfdatastring.replace('\x00', '') try: perfdata = shlex.split(perfdatastring) for metric in perfdata: try: self.add_perfdatametric(metric) except Exception: self.invalid_metrics.append(metric) except ValueError: return def is_valid(self): """ Returns True if the every metric in the string is valid Example usage: >>> PerfData("load1=10 load2=10 load3=20").is_valid() True >>> PerfData("10b").is_valid() False >>> PerfData("load1=").is_valid() False >>> PerfData("load1=10 10").is_valid() False """ for i in self.metrics: if not i.is_valid(): return False # If we get here, all tests passed return True def add_perfdatametric(self, perfdatastring="", label="", value="", warn="", crit="", min="", max="", uom=""): """ Add a new perfdatametric to existing list of metrics. Args: perfdatastring (str): Complete perfdata string label (str): Label section of the perfdata string value (str): Value section of the perfdata string warn (str): WARNING threshold crit (str): CRITICAL threshold min (str): Minimal value of control max (str): Maximal value of control uom (str): Measure unit (octets, bits/s, volts, ...) Example: >>> s = PerfData() >>> s.add_perfdatametric("a=1") >>> s.add_perfdatametric(label="utilization",value="10",uom="%") """ metric = PerfDataMetric(perfdatastring=perfdatastring, label=label, value=value, warn=warn, crit=crit, min=min, max=max, uom=uom) self.metrics.append(metric) def get_perfdatametric(self, metric_name): """ Get one specific perfdatametric Args: metric_name (str): Name of the metric to return Example: >>> s = PerfData("cpu=90% memory=50% disk_usage=20%") >>> my_metric = s.get_perfdatametric('cpu') >>> my_metric.label, my_metric.value ('cpu', '90') """ for i in self.metrics: if i.label == metric_name: return i def reconsile_thresholds(self): """ Convert all thresholds in new_threshold_syntax to the standard one """ for i in self.metrics: i.reconsile_thresholds() def __str__(self): metrics = map(lambda x: x.__str__(), self.metrics) return ' '.join(metrics) class PerfDataMetric(object): """ Data structure for one single Nagios Perfdata Metric Attributes: perfdatastring (str): Complete perfdata string label (str): Label section of the perfdata string value (str): Value section of the perfdata string warn (str): WARNING threshold crit (str): CRITICAL threshold min (str): Minimal value of control max (str): Maximal value of control uom (str): Measure unit (octets, bits/s, volts, ...) """ label = "" # : str. Label section of the perfdata string value = "" # : str. Value section of the perfdata string warn = "" # : str. WARNING threshold crit = "" # : str CRITICAL threshold min = "" # : str. Minimal value of control max = "" # : str. Maximal value of control uom = "" # : str. Measure unit (octets, bits/s, volts, ...) def __repr__(self): return "'%s'=%s%s;%s;%s;%s;%s" % ( self.label, self.value, self.uom, self.warn, self.crit, self.min, self.max, ) def __str__(self): return self.__repr__() def __init__(self, perfdatastring="", label="", value="", warn="", crit="", min="", max="", uom=""): """ >>> p = PerfData(perfdatastring="size=10M;20M;;;") >>> metric = p.get_perfdatametric('size') >>> print(metric.label) size >>> print(metric.value) 10 >>> print(metric.uom) M >>> p = PerfDataMetric(perfdatastring="'with spaces'=10") >>> print(p.label) with spaces >>> print(p.value) 10 """ self.label = label self.value = value self.warn = warn self.crit = crit self.min = min self.max = max self.uom = uom perfdatastring = str(perfdatastring) # Hack: For some weird reason livestatus sometimes delivers perfdata in # utf-32 encoding. perfdatastring = perfdatastring.replace('\x00', '') if len(perfdatastring) == 0: return # If label is single quoted, there might be any symbol in the label # including other single quotes and the = sign. Therefore, we take # special precautions if it is so if perfdatastring.startswith("'"): tmp = perfdatastring.split("'") everything_but_label = tmp.pop() tmp.pop(0) label = "'".join(tmp) else: # Split into label=perfdata tmp = perfdatastring.split('=', 1) # If no = sign, then we just take in a label if tmp: label = tmp.pop(0) if tmp: everything_but_label = tmp.pop() else: everything_but_label = '' self.label = label # Next split string into value;warning;critical;min;max tmp = everything_but_label.split(';') if len(tmp) > 0: val = tmp.pop(0).strip('=') self.value, self.uom = self.split_value_and_uom(val) if len(tmp) > 0: self.warn = tmp.pop(0) if len(tmp) > 0: self.crit = tmp.pop(0) if len(tmp) > 0: self.min = tmp.pop(0) if len(tmp) > 0: self.max = tmp.pop(0) def get_status(self): """ Return nagios-style exit code (int 0-3) by comparing Example: self.value with self.warn and self.crit >>> PerfDataMetric("label1=10;20;30").get_status() 0 >>> PerfDataMetric("label2=25;20;30").get_status() 1 >>> PerfDataMetric("label3=35;20;30").get_status() 2 Invalid metrics always return unknown >>> PerfDataMetric("label3=35;invalid_metric").get_status() 3 """ try: status = pynag.Plugins.check_threshold(self.value, warning=self.warn, critical=self.crit) except pynag.Utils.PynagError: status = 3 return status def is_valid(self): """ Returns True if all Performance data is valid. Otherwise False Example Usage: >>> PerfDataMetric("load1=2").is_valid() True >>> PerfDataMetric("load1").is_valid() False >>> PerfDataMetric('').is_valid() False >>> PerfDataMetric('invalid_value=invalid').is_valid() False >>> PerfDataMetric('invalid_min=0;0;0;min;0').is_valid() False >>> PerfDataMetric('invalid_min=0;0;0;0;max').is_valid() False >>> PerfDataMetric('label with spaces=0').is_valid() False >>> PerfDataMetric("'label with spaces=0'").is_valid() False """ if self.label in (None, ''): return False if self.value in (None, ''): return False try: float(self.value) except ValueError: return False try: self.min == '' or float(self.min) except ValueError: return False try: self.max == '' or float(self.max) except ValueError: return False if self.label.find(' ') > -1 and not self.label.startswith("'") and not self.label.endswith("'"): return False # If we get here, we passed all tests return True def reconsile_thresholds(self): """ Convert threshold from new threshold syntax to current one. For backwards compatibility """ self.warn = reconsile_threshold(self.warn) self.crit = reconsile_threshold(self.crit) def split_value_and_uom(self, value): """ Example: get value="10M" and return (10,"M") >>> p = PerfDataMetric() >>> p.split_value_and_uom( "10" ) ('10', '') >>> p.split_value_and_uom( "10c" ) ('10', 'c') >>> p.split_value_and_uom( "10B" ) ('10', 'B') >>> p.split_value_and_uom( "10MB" ) ('10', 'MB') >>> p.split_value_and_uom( "10KB" ) ('10', 'KB') >>> p.split_value_and_uom( "10TB" ) ('10', 'TB') >>> p.split_value_and_uom( "10%" ) ('10', '%') >>> p.split_value_and_uom( "10s" ) ('10', 's') >>> p.split_value_and_uom( "10us" ) ('10', 'us') >>> p.split_value_and_uom( "10ms" ) ('10', 'ms') """ tmp = re.findall(r"([-]*[\d.]*\d+)(.*)", value) if len(tmp) == 0: return '', '' return tmp[0] def get_dict(self): """ Returns a dictionary which contains this class' attributes. Returned dict example:: { 'label': self.label, 'value': self.value, 'uom': self.uom, 'warn': self.warn, 'crit': self.crit, 'min': self.min, 'max': self.max, } """ return { 'label': self.label, 'value': self.value, 'uom': self.uom, 'warn': self.warn, 'crit': self.crit, 'min': self.min, 'max': self.max, } def grep(objects, **kwargs): """ Returns all the elements from array that match the keywords in **kwargs See documentation for pynag.Model.ObjectDefinition.objects.filter() for example how to use this. Arguments: objects (list of dict): list to be searched kwargs (str): Any search argument provided will be checked against every dict Examples:: array = [ {'host_name': 'examplehost', 'state':0}, {'host_name': 'example2', 'state':1}, ] grep_dict(array, state=0) # should return [{'host_name': 'examplehost', 'state':0},] """ # Input comes to us as a key/value dict. # We will flatten this out into a tuble, because if value # is a list, it means the calling function is doing multible search on # the same key search = [] for k, v in kwargs.items(): # We need the actual array in "v" for __in and __notin if isinstance(v, type([])) and not (k.endswith('__in') or k.endswith('__notin')): for i in v: search.append((k, i)) else: search.append((k, v)) matching_objects = objects for k, v in search: #v = str(v) v_str = str(v) if k.endswith('__contains'): k = k[:-len('__contains')] expression = lambda x: x.get(k) and v_str in str(x.get(k)) elif k.endswith('__notcontains'): k = k[:-len('__notcontains')] expression = lambda x: not v_str in str(x.get(k)) elif k.endswith('__startswith'): k = k[:-len('__startswith')] expression = lambda x: str(x.get(k)).startswith(v_str) elif k.endswith('__notstartswith'): k = k[:-len('__notstartswith')] expression = lambda x: not str(x.get(k)).startswith(v_str) elif k.endswith('__endswith'): k = k[:-len('__endswith')] expression = lambda x: str(x.get(k)).endswith(v_str) elif k.endswith('__notendswith'): k = k[:-len('__notendswith')] expression = lambda x: not str(x.get(k)).endswith(v_str) elif k.endswith('__exists'): k = k[:-len('__exists')] expression = lambda x: str(k in x) == v_str elif k.endswith('__isnot'): k = k[:-len('__isnot')] expression = lambda x: v_str != str(x.get(k)) elif k.endswith('__regex'): k = k[:-len('__regex')] regex = re.compile(str(v)) expression = lambda x: regex.search(str(x.get(k))) elif k.endswith('__in'): k = k[:-len('__in')] expression = lambda x: str(x.get(k)) in v elif k.endswith('__notin'): k = k[:-len('__notin')] expression = lambda x: str(x.get(k)) not in v elif k.endswith('__has_field'): k = k[:-len('__has_field')] expression = lambda x: v_str in AttributeList(x.get(k)).fields elif k == 'register' and str(v) == '1': # in case of register attribute None is the same as "1" expression = lambda x: x.get(k) in (v, None) elif k in ('search', 'q'): expression = lambda x: v_str in str(x) else: # If all else fails, assume they are asking for exact match v_is_str = isinstance(v, str) expression = lambda obj: (lambda objval: str(objval) == v_str or (v_is_str and isinstance(objval, list) and v in objval))(obj.get(k)) matching_objects = filter(expression, matching_objects) return matching_objects def grep_to_livestatus(*args, **kwargs): """ Converts from pynag style grep syntax to livestatus filter syntax. Example: >>> grep_to_livestatus(host_name='test') ['Filter: host_name = test'] >>> grep_to_livestatus(service_description__contains='serv') ['Filter: service_description ~ serv'] >>> grep_to_livestatus(service_description__isnot='serv') ['Filter: service_description != serv'] >>> grep_to_livestatus(service_description__contains=['serv','check']) ['Filter: service_description ~ serv'] >>> grep_to_livestatus(service_description__contains='foo', contacts__has_field='admin') ['Filter: contacts >= admin', 'Filter: service_description ~ foo'] >>> grep_to_livestatus(service_description__has_field='foo') ['Filter: service_description >= foo'] >>> grep_to_livestatus(service_description__startswith='foo') ['Filter: service_description ~ ^foo'] >>> grep_to_livestatus(service_description__endswith='foo') ['Filter: service_description ~ foo$'] """ result = list(args) # Args go unchanged back into results for k, v in kwargs.items(): if isinstance(v, list) and len(v) > 0: v = v[0] if k.endswith('__contains'): k = k[:-len('__contains')] my_string = "Filter: %s ~ %s" % (k, v) elif k.endswith('__has_field'): k = k[:-len('__has_field')] my_string = "Filter: %s >= %s" % (k, v) elif k.endswith('__isnot'): k = k[:-len('__isnot')] my_string = "Filter: %s != %s" % (k, v) elif k.endswith('__startswith'): k = k[:-len('__startswith')] my_string = "Filter: %s ~ ^%s" % (k, v) elif k.endswith('__endswith'): k = k[:-len('__endswith')] my_string = "Filter: %s ~ %s$" % (k, v) elif k == 'WaitObject': my_string = "WaitObject: %s" % (v,) elif k == 'WaitCondition': my_string = "WaitCondition: %s" % (v,) elif k == 'WaitTrigger': my_string = "WaitTrigger: %s" % (v,) elif k == 'WaitTimeout': my_string = "WaitTimeout: %s" % (v,) elif k in ('Limit', 'limit'): my_string = "Limit: %s" % (v,) elif k in ('Filter', 'filter'): my_string = "Filter: %s" % (v,) else: my_string = "Filter: %s = %s" % (k, v) result.append(my_string) return result class AttributeList(object): """ Parse a list of nagios attributes into a parsable format. (e. contact_groups) This makes it handy to mangle with nagios attribute values that are in a comma seperated format. Typical comma-seperated format in nagios configuration files looks something like this:: contact_groups +group1,group2,group3 Example:: >>> i = AttributeList('+group1,group2,group3') >>> i.operator '+' >>> i.fields ['group1', 'group2', 'group3'] # if your data is already in a list format you can use it directly: >>> i = AttributeList(['group1', 'group2', 'group3']) >>> i.fields ['group1', 'group2', 'group3'] # white spaces will be stripped from all fields >>> i = AttributeList('+group1, group2') >>> i +group1,group2 >>> i.fields ['group1', 'group2'] """ def __init__(self, value=None): self.operator = '' self.fields = [] # this is easy to do if attribue_name is unset if not value or value == 'null': return # value in this case should usually be a comma seperated string, but sometimes # (like when working with livestatus) we have the luxury of getting lists if isinstance(value, list): # Remove empty fields self.fields = filter(lambda x: len(x) > 0, value) return possible_operators = '+-!' if value[0] in possible_operators: self.operator = value[0] value = value[1:] else: self.operator = '' # Strip leading and trailing commas value = value.strip(',') # Split value into a comma seperated list self.fields = value.split(',') # Strip whitespaces from each field self.fields = map(lambda x: x.strip(), self.fields) def __str__(self): return self.operator + ','.join(self.fields) def __repr__(self): return self.__str__() def insert(self, index, object): """ Same as list.insert() Args: object: Any object that will be inserted into self.fields (usually a string) Example:: >>> i = AttributeList('group1,group2,group3') >>> i.insert(1, 'group4') >>> i.fields ['group1', 'group4', 'group2', 'group3'] """ return self.fields.insert(index, object) def append(self, object): """ Same as list.append(): Args: object: Item to append into self.fields (typically a string) Example: >>> i = AttributeList('group1,group2,group3') >>> i.append('group5') >>> i.fields ['group1', 'group2', 'group3', 'group5'] """ return self.fields.append(object) def count(self, value): """ Same as list.count() Args: value: Any object that might exist in self.fields (string) Returns: The number of occurances that 'value' has in self.fields Example: >>> i = AttributeList('group1,group2,group3') >>> i.count('group3') 1 """ return self.fields.count(value) def extend(self, iterable): """ Same as list.extend() Args: iterable: Any iterable that list.extend() supports Example: >>> i = AttributeList('group1,group2,group3') >>> i.extend(['group4', 'group5']) >>> i.fields ['group1', 'group2', 'group3', 'group4', 'group5'] """ return self.fields.extend(iterable) def index(self, value, start=0, stop=None): """ Same as list.index() Args: value: object to look for in self.fields start: start at this index point stop: stop at this index point Returns: The index of 'value' (integer) Examples: >>> i = AttributeList('group1,group2,group3') >>> i.index('group2') 1 >>> i.index('group3', 2, 5) 2 """ if stop is None: stop = len(self.fields) return self.fields.index(value, start, stop) def reverse(self): """ Same as list.reverse() Examples: >>> i = AttributeList('group1,group2,group3') >>> i.reverse() >>> i.fields ['group3', 'group2', 'group1'] """ return self.fields.reverse() def sort(self): """ Same as list.sort() Examples: >>> i = AttributeList('group3,group1,group2') >>> i.sort() >>> print(i.fields) ['group1', 'group2', 'group3'] """ return self.fields.sort() def remove(self, value): """ Same as list.remove() Args: value: The object that is to be removed Examples: >>> i = AttributeList('group1,group2,group3') >>> i.remove('group3') >>> i.fields ['group1', 'group2'] """ return self.fields.remove(value) def __iter__(self): """ Same as list.__iter__() >>> mylist = AttributeList('group1,group2,group3') >>> for i in mylist: print(i) group1 group2 group3 """ return self.fields.__iter__() class PluginOutput: """ This class parses a typical stdout from a nagios plugin It splits the output into the following fields: * Summary * Long Output * Perfdata Attributes: summary (str): Summary returned by the plugin check long_output (str) perfdata (str): Data returned by the plugin as a string parsed_perfdata: perfdata parsed and split Example Usage: >>> p = PluginOutput("Everything is ok | load1=15 load2=10") >>> p.summary 'Everything is ok ' >>> p.long_output '' >>> p.perfdata 'load1=15 load2=10' >>> p.parsed_perfdata.metrics ['load1'=15;;;;, 'load2'=10;;;;] """ summary = None # : str. Summary returned by the plugin check long_output = None perfdata = None # : str. Data returned by the plugin as a string parsed_perfdata = None # : Perfdata parsed and split def __init__(self, stdout): if not stdout: return long_output = [] perfdata = [] summary = None lines = stdout.splitlines() for i in lines: i = i.split('|', 1) if summary is None: summary = i.pop(0) else: long_output.append(i.pop(0)) if i: perfdata.append(i.pop()) perfdata = ' '.join(perfdata) self.summary = summary self.long_output = '\n'.join(long_output) self.perfdata = perfdata.strip() self.parsed_perfdata = PerfData(perfdatastring=perfdata) class defaultdict(dict): """ This is an alternative implementation of collections.defaultdict. Used as a fallback if using python 2.4 or older. Usage:: try: from collections import defaultdict except ImportError: from pynag.Utils import defaultdict """ def __init__(self, default_factory=None, *a, **kw): if (default_factory is not None and not hasattr(default_factory, '__call__')): raise TypeError('first argument must be callable') dict.__init__(self, *a, **kw) self.default_factory = default_factory def __getitem__(self, key): try: return dict.__getitem__(self, key) except KeyError: return self.__missing__(key) def __missing__(self, key): if self.default_factory is None: raise KeyError(key) self[key] = value = self.default_factory() return value def __reduce__(self): if self.default_factory is None: args = tuple() else: args = self.default_factory, return type(self), args, None, None, self.items() def copy(self): return self.__copy__() def __copy__(self): return type(self)(self.default_factory, self) def __deepcopy__(self, memo): import copy return type(self)(self.default_factory, copy.deepcopy(self.items())) def __repr__(self): return 'defaultdict(%s, %s)' % (self.default_factory, dict.__repr__(self)) def reconsile_threshold(threshold_range): """ Take threshold string as and normalize it to the format supported by plugin development team The input (usually a string in the form of 'the new threshold syntax') is a string in the form of x..y The output will be a compatible string in the older nagios plugin format @x:y Examples: >>> reconsile_threshold("0..5") '@0:5' >>> reconsile_threshold("inf..5") '5:' >>> reconsile_threshold("5..inf") '~:5' >>> reconsile_threshold("inf..inf") '@~:' >>> reconsile_threshold("^0..5") '0:5' >>> reconsile_threshold("10..20") '@10:20' >>> reconsile_threshold("10..inf") '~:10' """ threshold_range = str(threshold_range) if not '..' in threshold_range: return threshold_range threshold_range = threshold_range.strip() if threshold_range.startswith('^'): operator = '' threshold_range = threshold_range[1:] else: operator = '@' start, end = threshold_range.split('..', 1) start = start.replace('-inf', '~').replace('inf', '~') end = end.replace('-inf', '').replace('inf', '') if not start: start = '0' # Lets convert the common case of @0:x into x: if operator == '@' and start == '~' and end not in ('', '~'): result = "%s:" % end # Convert the common case of @x: into 0:x elif operator == '@' and end in ('', '~') and start != '~': result = '~:%s' % start else: result = '%s%s:%s' % (operator, start, end) #result = '%s%s:%s' % (operator, start, end) return result def synchronized(lock): """ Synchronization decorator Use this to make a multi-threaded method synchronized and thread-safe. Use the decorator like so:: @pynag.Utils.synchronized(pynag.Utils.rlock) """ def wrap(f): def newFunction(*args, **kw): lock.acquire() try: return f(*args, **kw) finally: lock.release() newFunction.__name__ = f.__name__ newFunction.__module__ = f.__module__ return newFunction return wrap def cache_only(func): def wrap(*args, **kwargs): pynag.Model.ObjectFetcher._cache_only = True try: return func(*args, **kwargs) finally: pynag.Model.ObjectFetcher._cache_only = False wrap.__name__ = func.__name__ wrap.__module__ = func.__module__ return wrap def send_nsca(code, message, nscahost, hostname=None, service=None, nscabin="send_nsca", nscaconf=None): """ Send data via send_nsca for passive service checks Args: code (int): Return code of plugin. message (str): Message to pass back. nscahost (str): Hostname or IP address of NSCA server. hostname (str): Hostname the check results apply to. service (str): Service the check results apply to. nscabin (str): Location of send_nsca binary. If none specified whatever is in the path will be used. nscaconf (str): Location of the NSCA configuration to use if any. Returns: [result,stdout,stderr] of the command being run """ if not hostname: hostname = node() # Build command command = [nscabin, '-H', nscahost] if nscaconf: command += ['-c', nscaconf] # Just in case, status code was sent in as an integer: code = str(code) # Build the input string if service: input_string = '\t'.join([hostname, service, code, message]) + '\n' else: input_string = '\t'.join([hostname, code, message]) + '\n' # Execute command proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) stdout, stderr = proc.communicate(input=input_string) result = proc.returncode, stdout, stderr return result pynag-0.9.1+dfsg.orig/pynag/Utils/importer.py0000664000175000017500000001046112363713622020163 0ustar clintclint#!/usr/bin/env python # -*- coding: utf-8 -*- """ General Utilities from importing nagios objects. Currently .csv files are supported Either execute this script standalone from the command line or use it as a python library like so: >>> from pynag.Utils import importer >>> pynag_objects = importer.import_from_csv_file(filename='foo', seperator=',') # doctest: +SKIP >>> for i in pynag_objects: # doctest: +SKIP ... i.save() # doctest: +SKIP """ import optparse import os import sys import pynag.Model # Default host is used when you try to add a service to a # Host that does not exist DEFAULT_HOST_ATTRIBUTES = { 'use': 'generic-host', 'check_command': 'check-host-alive', 'check_interval': '1', 'max_check_attempts': '3', 'retry_interval': '2', } options = optparse.OptionParser() options.add_option( '--destination_filename', dest='destination_filename', default=None, help='Destination file where objects will be saved', ) options.add_option( '--seperator', dest='seperator', default=',', help='Use this as seperator for columns (default: ,)', ) options.add_option( '--dry_run', dest='dry_run', default=False, action='store_true', help='If specified, only print object definitions to screen', ) options.add_option( '--object_type', dest='object_type', default=None, help='Assume this object type (e.g. "host") if its not specified in file' ) def parse_arguments(): """ Parse command line arguments """ (opts, args) = options.parse_args() return opts, args def main(): (opts, args) = parse_arguments() if not args: options.error("You must specify at least one .csv file as an argument") for i in args: if not os.path.isfile(i): options.error("Could not find file: %s" % i) pynag_objects = [] for i in args: tmp = import_from_csv_file(filename=i, seperator=opts.seperator, object_type=opts.object_type) pynag_objects += tmp if opts.dry_run: print len(pynag_objects) for i in pynag_objects: print i else: for i in pynag_objects: i.save(filename=opts.destination_filename) def dict_to_pynag_objects(dict_list, object_type=None): """Take a list of dictionaries, return a list of pynag.Model objects. Args: dict_list: List of dictionaries that represent pynag objects object_type: Use this object type as default, if it is not specified in dict_list Returns: List of pynag objects """ result = [] for i in dict_list: dictionary = i.copy() object_type = dictionary.pop("object_type", object_type) if not object_type: raise ValueError('One column needs to specify object type') Class = pynag.Model.string_to_class[object_type] pynag_object = Class(**dictionary) result.append(pynag_object) return result def parse_csv_file(filename, seperator=','): """ Parse filename and return a dict representing its contents """ with open(filename) as f: data = f.read() return parse_csv_string(data, seperator=seperator) def parse_csv_string(csv_string, seperator=','): """ Parse csv string and return a dict representing its contents """ result = [] lines = csv_string.splitlines() headers = None for line in lines: # Skip empty lines if not line: continue # If this is first line if not headers: headers = map(lambda x: x.strip(), line.split(seperator)) continue mydict = {} result.append(mydict) columns = map(lambda x: x.strip(), line.split(seperator)) for i, column in enumerate(columns): header = headers[i] mydict[header] = column return result def import_from_csv_file(filename, seperator=',', object_type=None): """ Parses filename and returns a list of pynag objects. Args: filename: Path to a file seperator: use this symbol to seperate columns in the file object_type: Assume this object_type if there is no object_type column """ dicts = parse_csv_file(filename=filename, seperator=seperator) pynag_objects = dict_to_pynag_objects(dicts, object_type=object_type) return pynag_objects if __name__ == '__main__': main() pynag-0.9.1+dfsg.orig/pynag/Model/0000775000175000017500000000000012370025176015704 5ustar clintclintpynag-0.9.1+dfsg.orig/pynag/Model/EventHandlers/0000775000175000017500000000000012370025176020446 5ustar clintclintpynag-0.9.1+dfsg.orig/pynag/Model/EventHandlers/__init__.py0000664000175000017500000002414712354645173022576 0ustar clintclint# -*- coding: utf-8 -*- # # pynag - Python Nagios plug-in and configuration environment # Copyright (C) 2010 Pall Sigurdsson # # 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. """ This module is experimental. The idea is to create a mechanism that allows you to hook your own events into an ObjectDefinition instance. This enables you for example to log to file every time an object is rewritten. """ import time from platform import node from os.path import dirname import subprocess import shlex from os import environ from getpass import getuser class BaseEventHandler: def __init__(self, debug=False): self._debug = debug def debug(self, object_definition, message): """Used for any particual debug notifications""" raise NotImplementedError() def write(self, object_definition, message): """Called whenever a modification has been written to file""" raise NotImplementedError() def pre_save(self, object_definition, message): """ Called at the beginning of save() """ def save(self, object_definition, message): """Called when objectdefinition.save() has finished""" raise NotImplementedError() class PrintToScreenHandler(BaseEventHandler): """Handler that prints everything to stdout""" def debug(self, object_definition, message): """Used for any particual debug notifications""" if self._debug: print "%s: %s" % (time.asctime(), message) def write(self, object_definition, message): """Called whenever a modification has been written to file""" print "%s: file='%s' %s" % (time.asctime(), object_definition['meta']['filename'], message) def save(self, object_definition, message): """Called when objectdefinition.save() has finished""" print "%s: %s" % (time.asctime(), message) class FileLogger(BaseEventHandler): """Handler that logs everything to file""" def __init__(self, logfile='/var/log/pynag.log', debug=False): BaseEventHandler.__init__(self) self.file = logfile self._debug = debug def _append_to_file(self, message): f = open(self.file, 'a') if not message.endswith('\n'): message += '\n' f.write(message) f.close() def debug(self, object_definition, message): """Used for any particular debug notifications""" if self.debug: message = "%s: %s" % (time.asctime(), message) self._append_to_file(message) def write(self, object_definition, message): """Called whenever a modification has been written to file""" message = "%s: file='%s' %s" % (time.asctime(), object_definition['meta']['filename'], message) self._append_to_file(message) def save(self, object_definition, message): """Called when objectdefinition.save() has finished""" message = "%s: %s" % (time.asctime(), message) self._append_to_file(message) class GitEventHandler(BaseEventHandler): def __init__(self, gitdir, source, modified_by, auto_init=False, ignore_errors=False): """ Commits to git repo rooted in nagios configuration directory It automatically raises an exception if the configuration directory is not a git repository. source = prepended to git commit messages modified_by = is the username in username@ for commit messages ignore_errors = if True, do not raise exceptions on git errors auto_init = If True, run git init if no git repository is found. """ BaseEventHandler.__init__(self) # Git base is the nagios config directory self.gitdir = gitdir # Who made the change self.modified_by = modified_by # Which program did the change self.source = source # Every string in self.messages indicated a line in the eventual commit message self.messages = [] self.ignore_errors = ignore_errors if auto_init: try: self._run_command('git status --short') except EventHandlerError as e: if e.errorcode == 128: self._git_init() #self._run_command('git status --short') self._update_author() def debug(self, object_definition, message): pass def _update_author(self): """ Updates environment variables GIT_AUTHOR_NAME and EMAIL Returns: None """ environ['GIT_AUTHOR_NAME'] = self.modified_by environ['GIT_AUTHOR_EMAIL'] = "%s@%s" % (self.source, node()) def _run_command(self, command): """ Run a specified command from the command line. Return stdout """ cwd = self.gitdir proc = subprocess.Popen(command, cwd=cwd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,) stdout, stderr = proc.communicate('through stdin to stdout') returncode = proc.returncode if returncode > 0 and self.ignore_errors is False: errorstring = "Command '%s' returned exit status %s.\n stdout: %s \n stderr: %s\n Current user: %s" errorstring = errorstring % (command, returncode, stdout, stderr, getuser()) raise EventHandlerError(errorstring, errorcode=returncode, errorstring=stderr) return stdout def is_commited(self): """ Returns True if all files in git repo are fully commited """ return self.get_uncommited_files() == 0 def get_uncommited_files(self): """ Returns a list of files that are have unstaged changes """ output = self._run_command("git status --porcelain") result = [] for line in output.split('\n'): line = line.split() if len(line) < 2: continue result.append({'status': line[0], 'filename': " ".join(line[1:])}) return result def _git_init(self, directory=None): """ Initilizes a new git repo in directory. If directory is none, use self.gitdir """ self._update_author() self._run_command("git init") self._run_command("git add .") self._run_command("git commit -a -m 'Initial Commit'") def _git_add(self, filename): """ Wrapper around git add command """ self._update_author() command = "git add '%s'" % filename return self._run_command(command) def _git_commit(self, filename, message, filelist=None): """ Wrapper around git commit command """ if filelist is None: filelist = [] self._update_author() # Lets strip out any single quotes from the message: message = message.replace("'", '"') if len(filelist) > 0: filename = "' '".join(filelist) command = "git commit '%s' -m '%s'" % (filename, message) return self._run_command(command=command) def pre_save(self, object_definition, message): """ Commits object_definition.get_filename() if it has any changes """ filename = object_definition.get_filename() if self._is_dirty(filename): self._git_add(filename) self._git_commit(filename, message="External changes commited in %s '%s'" % (object_definition.object_type, object_definition.get_shortname())) def save(self, object_definition, message): filename = object_definition.get_filename() if len(self.messages) > 0: message = [message, '\n'] + self.messages message = '\n'.join(message) self._git_add(filename) if self._is_dirty(filename): self._git_commit(filename, message) self.messages = [] def _is_dirty(self, filename): """ Returns True if filename needs to be committed to git """ command = "git status --porcelain '%s'" % filename output = self._run_command(command) # Return True if there is any output return len(output) > 0 def write(self, object_definition, message): # When write is called ( something was written to file ) # We will log it in a buffer, and commit when save() is called. self.messages.append(" * %s" % message) class NagiosReloadHandler(BaseEventHandler): """ This handler reloads nagios every time that a change is made. This is only meant for small environments """ def __init__(self, nagios_init, *args, **kwargs): import pynag.Control BaseEventHandler.__init__(self, *args, **kwargs) self.daemon = pynag.Control.daemon(nagios_init=nagios_init) self.in_transaction = False def _reload(self): """ Reload nagios """ self.daemon.reload() def debug(self, object_definition, message): """Used for any particual debug notifications""" pass def write(self, object_definition, message): """Called whenever a modification has been written to file""" if not self.in_transaction: self._reload() def pre_save(self, object_definition, message): """ Called at the beginning of save() """ self.in_transaction = True def save(self, object_definition, message): """Called when objectdefinition.save() has finished""" self.in_transaction = False self._reload() class EventHandlerError(Exception): def __init__(self, message, errorcode=None, errorstring=None): self.message = message self.errorcode = errorcode self.errorstring = errorstring def __str__(self): return self.errorstring pynag-0.9.1+dfsg.orig/pynag/Model/__init__.py0000664000175000017500000033075712354645173020043 0ustar clintclint# -*- coding: utf-8 -*- # # pynag - Python Nagios plug-in and configuration environment # Copyright (C) 2011 Pall Sigurdsson # # 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. """ This module provides a high level Object-Oriented wrapper around pynag.Parsers.config. Example: >>> from pynag.Model import Service, Host >>> >>> all_services = Service.objects.all >>> my_service = all_services[0] >>> print my_service.host_name # doctest: +SKIP localhost >>> >>> example_host = Host.objects.filter(host_name="host.example.com") >>> canadian_hosts = Host.objects.filter(host_name__endswith=".ca") >>> >>> for i in canadian_hosts: ... i.alias = "this host is located in Canada" ... i.save() # doctest: +SKIP """ import os import re import subprocess import time import getpass from pynag import Parsers import pynag.Control.Command import pynag.Utils from macros import _standard_macros import all_attributes # Path To Nagios configuration file cfg_file = None # '/etc/nagios/nagios.cfg' # Were new objects are written by default pynag_directory = None # This is the config parser that we use internally, if cfg_file is changed, then config # will be recreated whenever a parse is called. config = Parsers.config(cfg_file=cfg_file) #: eventhandlers -- A list of Model.EventHandlers object. # Event handler is responsible for passing notification whenever something # important happens in the model. # # For example FileLogger class is an event handler responsible for logging to # file whenever something has been written. eventhandlers = [] try: from collections import defaultdict except ImportError: from pynag.Utils import defaultdict class ObjectRelations(object): """ Static container for objects and their respective neighbours """ # c['contact_name'] = [host1.get_id(),host2.get_id()] contact_hosts = defaultdict(set) # c['contact_name'] = ['contactgroup1','contactgroup2'] contact_contactgroups = defaultdict(set) # c['contact_name'] = ['service1.get_id()','service2.get_id()'] contact_services = defaultdict(set) # c['contactgroup_name'] = ['contact1.contact_name','contact2.contact_name','contact3.contact_name'] contactgroup_contacts = defaultdict(set) # c['contactgroup_name'] = ['contactgroup1','contactgroup2','contactgroup3'] contactgroup_contactgroups = defaultdict(set) # c['contactgroup_name'] = ['host1.get_id()', 'host2.get_id()'] contactgroup_hosts = defaultdict(set) # c['contactgroup_name'] = ['service1.get_id()', 'service2.get_id()'] contactgroup_services = defaultdict(set) # c['host_name'] = [service1.id, service2.id,service3.id] host_services = defaultdict(set) # c['host_name'] = ['contactgroup1', 'contactgroup2'] host_contact_groups = defaultdict(set) # c['host_name'] = ['contact1','contact2'] host_contacts = defaultdict(set) # c['host_name'] = '['hostgroup1.hostgroup_name','hostgroup2.hostgroup_name'] host_hostgroups = defaultdict(set) # c['host_name'] = '['service1.get_id()','service2.get_id()'] host_services = defaultdict(set) # c['hostgroup_name'] = ['host_name1','host_name2'] hostgroup_hosts = defaultdict(set) # c['hostgroup_name'] = ['hostgroup1','hostgroup2'] hostgroup_hostgroups = defaultdict(set) # c['hostgroup_name'] = ['service1.get_id()','service2.get_id()'] hostgroup_services = defaultdict(set) # c['service.get_id()'] = '['contactgroup_name1','contactgroup_name2'] service_contact_groups = defaultdict(set) # c['service.get_id()'] = '['contact_name1','contact_name2'] service_contacts = defaultdict(set) # c['service.get_id()'] = '['hostgroup_name1','hostgroup_name2'] service_hostgroups = defaultdict(set) # c['service.get_id()'] = '['servicegroup_name1','servicegroup_name2'] service_servicegroups = defaultdict(set) # c['service.get_id()'] = ['host_name1','host_name2'] service_hosts = defaultdict(set) # c['servicegroup_name'] = ['service1.get_id()', ['service2.get_id()'] servicegroup_services = defaultdict(set) # c['servicegroup_name'] = ['servicegroup1','servicegroup2','servicegroup3'] servicegroup_servicegroups = defaultdict(set) # c[command_name] = '['service.get_id()','service.get_id()'] command_service = defaultdict(set) # c[command_name] = '['host_name1','host_name2'] command_host = defaultdict(set) # use['host']['host_name1'] = ['host_name2','host_name3'] # use['contact']['contact_name1'] = ['contact_name2','contact_name3'] _defaultdict_set = lambda: defaultdict(set) use = defaultdict(_defaultdict_set) # contactgroup_subgroups['contactgroup_name'] = ['group1_name','group2_name'] contactgroup_subgroups = defaultdict(set) # hostgroup_subgroups['hostgroup_name'] = ['group1_name','group2_name'] hostgroup_subgroups = defaultdict(set) # servicegroup_subgroups['servicegroup_name'] = ['servicegroup1_name','servicegroup2_name'] servicegroup_subgroups = defaultdict(set) # servicegroup_members['servicegroup_name'] = ['service1_shortname','service2_shortname'] servicegroup_members = defaultdict(set) @staticmethod def reset(): """ Runs clear() on every member attribute in ObjectRelations """ for k, v in ObjectRelations.__dict__.items(): if isinstance(v, defaultdict): v.clear() @staticmethod def _get_subgroups(group_name, dictname): """ Helper function that lets you get all sub-group members of a particular group For example this call: _get_all_group-members('admins', ObjectRelations.contactgroup_contacgroups) Will return recursively go through contactgroup_members of 'admins' and return a list of all subgroups """ subgroups = dictname[group_name].copy() checked_groups = set() while len(subgroups) > 0: i = subgroups.pop() if i not in checked_groups: checked_groups.add(i) subgroups.update(dictname[i]) return checked_groups @staticmethod def resolve_regex(): """ If any object relations are a regular expression, then expand them into a full list """ self = ObjectRelations expand = self._expand_regex shortnames = ObjectFetcher._cached_shortnames host_names = shortnames['host'].keys() hostgroup_names = shortnames['hostgroup'].keys() expand(self.hostgroup_hosts, host_names) expand(self.host_hostgroups, hostgroup_names) expand(self.service_hostgroups, hostgroup_names) @staticmethod def _expand_regex(dictionary, full_list): """ Replaces any regex found in dictionary.values() or dictionary.keys() **INPLACE** Example with ObjectRelations.hostgroup_hosts >>> hostnames = set(['localhost','remotehost', 'not_included']) >>> hostgroup_hosts = {'hostgroup1': set([ '.*host' ]), 'hostgroup2' : set(['localhost','remotehost']), } >>> ObjectRelations._expand_regex(dictionary=hostgroup_hosts, full_list=hostnames) >>> hostgroup_hosts['hostgroup1'] == hostgroup_hosts['hostgroup2'] True >>> hostgroup_hosts['hostgroup1'] == set(['localhost','remotehost']) True """ if config.get_cfg_value('use_true_regexp_matching') == "1": always_use_regex = True else: always_use_regex = False is_regex = lambda x: x is not None and (always_use_regex or '*' in x or '?' in x or '+' in x or '\.' in x) # Strip None entries from full_list full_list = filter(lambda x: x is not None, full_list) # Strip None entries from dictionary # If any keys in the dictionary are regex, expand it, i.e.: # if dictionary = { '.*':[1], 'localhost':[],'remotehost':[] } # then do: # dictionary['localhost'].update( [1] ) # dictionary['remotehost'].update( [1] ) # del dictionary['.*'] regex_keys = filter(is_regex, dictionary.keys()) for key in regex_keys: if key == '*': expanded_list = regex_keys else: regex = re.compile(key) expanded_list = filter(regex.search, regex_keys) for i in expanded_list: if i == key: # No need to react if regex resolved to itself continue dictionary[i].update(dictionary[key]) if key not in expanded_list: # Only remove the regex if it did not resolve to itself. del dictionary[key] # If dictionary.values() has any regex, expand it like so: # full_list = [1,2,3] # if dictionary = {'localhost':[ '.*' ]} # then change it so that: # dictionary = { 'localhost':[1,2,3] } for key, value in dictionary.items(): regex_members = filter(is_regex, value) if len(regex_members) == 0: continue # no changes need to be made if isinstance(value, list): value = set(value) #new_value = value.copy() for i in regex_members: if i == '*': # Nagios allows * instead of a valid regex, lets adjust to that expanded_list = full_list else: regex = re.compile(i) expanded_list = filter(regex.search, full_list) value.remove(i) value.update(expanded_list) #dictionary[key] = new_value @staticmethod def resolve_contactgroups(): """ Update all contactgroup relations to take into account contactgroup.contactgroup_members """ groups = ObjectRelations.contactgroup_contactgroups.keys() for group in groups: subgroups = ObjectRelations._get_subgroups(group, ObjectRelations.contactgroup_contactgroups) ObjectRelations.contactgroup_subgroups[group] = subgroups # Loop through every subgroup and apply its attributes to ours for subgroup in subgroups: for i in ObjectRelations.contactgroup_contacts[subgroup]: ObjectRelations.contact_contactgroups[i].add(group) ObjectRelations.contactgroup_contacts[group].update(ObjectRelations.contactgroup_contacts[subgroup]) @staticmethod def resolve_hostgroups(): """ Update all hostgroup relations to take into account hostgroup.hostgroup_members """ groups = ObjectRelations.hostgroup_hostgroups.keys() for group in groups: subgroups = ObjectRelations._get_subgroups(group, ObjectRelations.hostgroup_hostgroups) ObjectRelations.hostgroup_subgroups[group] = subgroups # Loop through every subgroup and apply its attributes to ours for subgroup in subgroups: for i in ObjectRelations.hostgroup_hosts[subgroup]: ObjectRelations.host_hostgroups[i].add(group) ObjectRelations.hostgroup_hosts[group].update(ObjectRelations.hostgroup_hosts[subgroup]) @staticmethod def resolve_servicegroups(): """ Update all servicegroup relations to take into account servicegroup.servicegroup_members """ # Before we do anything, resolve servicegroup.members into actual services ObjectRelations._resolve_servicegroup_members() groups = ObjectRelations.servicegroup_servicegroups.keys() for group in groups: subgroups = ObjectRelations._get_subgroups(group, ObjectRelations.servicegroup_servicegroups) ObjectRelations.servicegroup_subgroups[group] = subgroups # Loop through every subgroup and apply its attributes to ours for subgroup in subgroups: for i in ObjectRelations.servicegroup_services[subgroup]: ObjectRelations.service_servicegroups[i].add(group) ObjectRelations.servicegroup_services[group].update(ObjectRelations.servicegroup_services[subgroup]) @staticmethod def _resolve_servicegroup_members(): """ Iterates through all servicegroup.members, and updates servicegroup_services and service_servicegroups. This happens post-parse (instead of inside Servicegroup._do_relations() because when parsing Servicegroup you only know host_name/service_description of the service that belongs to the group. However the relations we update work on Service.get_id() because not all services that belong to servicegroups have a valid host_name/service_description pair (templates) """ for servicegroup, members in ObjectRelations.servicegroup_members.items(): for shortname in members: try: service = Service.objects.get_by_shortname(shortname, cache_only=True) service_id = service.get_id() ObjectRelations.servicegroup_services[servicegroup].add(service_id) ObjectRelations.service_servicegroups[service_id].add(servicegroup) except Exception: # If there is an error looking up any service, we ignore it and # don't display it in a list of related services pass class ObjectFetcher(object): """ This class is a wrapper around pynag.Parsers.config. Is responsible for fetching dict objects from config.data and turning into high ObjectDefinition objects Internal variables: * _cached_objects = List of every ObjectDefinition * _cached_id[o.get_id()] = o * _cached_shortnames[o.object_type][o.get_shortname()] = o * _cached_names[o.object_type][o.name] = o * _cached_object_type[o.object_type].append( o ) """ _cached_objects = [] _cached_ids = {} _cached_shortnames = defaultdict(dict) _cached_names = defaultdict(dict) _cached_object_type = defaultdict(list) _cache_only = False def __init__(self, object_type): self.object_type = object_type @pynag.Utils.synchronized(pynag.Utils.rlock) def get_all(self, cache_only=False): """ Return all object definitions of specified type""" if not cache_only and self.needs_reload(): self.reload_cache() if self.object_type is not None: return ObjectFetcher._cached_object_type[self.object_type] else: return ObjectFetcher._cached_objects all = property(get_all) @pynag.Utils.synchronized(pynag.Utils.rlock) def reload_cache(self): """Reload configuration cache""" # clear object list ObjectFetcher._cached_objects = [] ObjectFetcher._cached_ids = {} ObjectFetcher._cached_shortnames = defaultdict(dict) ObjectFetcher._cached_names = defaultdict(dict) ObjectFetcher._cached_object_type = defaultdict(list) global config # If global variable cfg_file has been changed, lets create a new ConfigParser object if config is None or config.cfg_file != cfg_file: config = Parsers.config(cfg_file) if config.needs_reparse(): config.parse() # Reset our list of how objects are related to each other ObjectRelations.reset() # Fetch all objects from Parsers.config for object_type, objects in config.data.items(): # change "all_host" to just "host" object_type = object_type[len("all_"):] Class = string_to_class.get(object_type, ObjectDefinition) for i in objects: i = Class(item=i) ObjectFetcher._cached_objects.append(i) ObjectFetcher._cached_object_type[object_type].append(i) ObjectFetcher._cached_ids[i.get_id()] = i ObjectFetcher._cached_shortnames[i.object_type][i.get_shortname()] = i if i.name is not None: ObjectFetcher._cached_names[i.object_type][i.name] = i i._do_relations() ObjectRelations.resolve_contactgroups() ObjectRelations.resolve_hostgroups() ObjectRelations.resolve_servicegroups() ObjectRelations.resolve_regex() return True @pynag.Utils.synchronized(pynag.Utils.rlock) def needs_reload(self): """ Returns true if configuration files need to be reloaded/reparsed """ if not ObjectFetcher._cached_objects: return True if config is None: return True if self._cache_only: return False return config.needs_reparse() def get_by_id(self, id, cache_only=False): """ Get one specific object :returns: ObjectDefinition :raises: ValueError if object is not found """ if not cache_only and self.needs_reload(): self.reload_cache() str_id = str(id).strip() return ObjectFetcher._cached_ids[str_id] def get_by_shortname(self, shortname, cache_only=False): """ Get one specific object by its shortname (i.e. host_name for host, etc) :param shortname: shortname of the object. i.e. host_name, command_name, etc. :param cache_only: If True, dont check if configuration files have changed since last parse :returns: ObjectDefinition :raises: ValueError if object is not found """ if cache_only is False and self.needs_reload(): self.reload_cache() shortname = str(shortname).strip() return ObjectFetcher._cached_shortnames[self.object_type][shortname] def get_by_name(self, object_name, cache_only=False): """ Get one specific object by its object_name (i.e. name attribute) :returns: ObjectDefinition :raises: ValueError if object is not found """ if not cache_only and self.needs_reload(): self.reload_cache() object_name = str(object_name).strip() return ObjectFetcher._cached_names[self.object_type][object_name] def get_object_types(self): """ Returns a list of all discovered object types """ if config is None or config.needs_reparse(): self.reload_cache() return config.get_object_types() def filter(self, **kwargs): """ Returns all objects that match the selected filter Example: Get all services where host_name is examplehost.example.com >>> Service.objects.filter(host_name='examplehost.example.com') # doctest: +SKIP Get service with host_name=examplehost.example.com and service_description='Ping' >>> Service.objects.filter(host_name='examplehost.example.com', ... service_description='Ping') # doctest: +SKIP Get all services that are registered but without a host_name >>> Service.objects.filter(host_name=None,register='1') # doctest: +SKIP Get all hosts that start with 'exampleh' >>> Host.objects.filter(host_name__startswith='exampleh') # doctest: +SKIP Get all hosts that end with 'example.com' >>> Service.objects.filter(host_name__endswith='example.com') # doctest: +SKIP Get all contactgroups that contain 'dba' >>> Contactgroup.objects.filter(host_name__contains='dba') # doctest: +SKIP Get all hosts that are not in the 'testservers' hostgroup >>> Host.objects.filter(hostgroup_name__notcontains='testservers') # doctest: +SKIP Get all services with non-empty name >>> Service.objects.filter(name__isnot=None) # doctest: +SKIP Get all hosts that have an address: >>> Host.objects.filter(address_exists=True) # doctest: +SKIP """ return pynag.Utils.grep(self.all, **kwargs) class ObjectDefinition(object): """ Holds one instance of one particular Object definition Example: >>> objects = ObjectDefinition.objects.all >>> my_object = ObjectDefinition( dict ) # doctest: +SKIP """ object_type = None objects = ObjectFetcher(None) def __init__(self, item=None, filename=None, **kwargs): self.__object_id__ = None # When we are saving, it is useful to know if we are already expecting # This object to exist in file or not. self._filename_has_changed = False # if item is empty, we are creating a new object if item is None: item = config.get_new_item(object_type=self.object_type, filename=filename) self.is_new = True else: self.is_new = False # store the object_type (i.e. host,service,command, etc) self.object_type = item['meta']['object_type'] # self.data -- This dict stores all effective attributes of this objects self._original_attributes = item #: _changes - This dict contains any changed (but yet unsaved) attributes of this object self._changes = {} #: _defined_attributes - All attributes that this item has defined self._defined_attributes = item['meta']['defined_attributes'] #: _inherited_attributes - All attributes that this object has inherited via 'use' self._inherited_attributes = item['meta']['inherited_attributes'] #: _meta - Various metadata about the object self._meta = item['meta'] #: _macros - A dict object that resolves any particular Nagios Macro (i.e. $HOSTADDR$) self._macros = {} #: __argument_macros - A dict object that resolves $ARG* macros self.__argument_macros = {} # Any kwargs provided will be added to changes: for k, v in kwargs.items(): self[k] = v def get_attribute(self, attribute_name): """Get one attribute from our object definition :param attribute_name: A attribute such as *host_name* """ return self[attribute_name] def set_attribute(self, attribute_name, attribute_value): """ Set (but does not save) one attribute in our object :param attribute_name: A attribute such as *host_name* :param attribute_value: The value you would like to set """ self[attribute_name] = attribute_value def attribute_is_empty(self, attribute_name): """ Check if the attribute is empty :param attribute_name: A attribute such as *host_name* :returns: True or False """ attr = self.get_attribute(attribute_name) if not attr or attr.strip() in '+-!': return True else: return False def is_dirty(self): """Returns true if any attributes has been changed on this object, and therefore it needs saving""" return len(self._changes.keys()) != 0 def is_registered(self): """ Returns true if object is enabled (registered) """ if not 'register' in self: return True if str(self['register']) == "1": return True return False def is_defined(self, attribute_name): """ Returns True if attribute_name is defined in this object """ return attribute_name in self._defined_attributes def __cmp__(self, other): return cmp(self.get_description(), other.get_description()) def __lt__(self, other): return self.get_description() < other.get_description() def __gt__(self, other): return self.get_description() > other.get_description() def __setitem__(self, key, item): # Special handle for macros if key.startswith('$') and key.endswith('$'): self.set_macro(key, item) elif self[key] != item: self._changes[key] = item self._event(level="debug", message="attribute changed: %s = %s" % (key, item)) def __getitem__(self, key): if key == 'id': return self.get_id() elif key == 'description': return self.get_description() elif key == 'shortname': return self.get_shortname() elif key == 'effective_command_line': return self.get_effective_command_line() elif key == 'register' and key not in self: return "1" elif key == 'meta': return self._meta elif key in self._changes: return self._changes[key] elif key in self._defined_attributes: return self._defined_attributes[key] elif key in self._inherited_attributes: return self._inherited_attributes[key] elif key in self._meta: return self._meta[key] else: return None def __contains__(self, item): """ Returns true if item is in ObjectDefinition """ if item in self.keys(): return True if item in self._meta.keys(): return True return False def has_key(self, key): """ Same as key in self """ return key in self def keys(self): all_keys = ['meta', 'id', 'shortname', 'effective_command_line'] for k in self._changes.keys(): if k not in all_keys: all_keys.append(k) for k in self._defined_attributes.keys(): if k not in all_keys: all_keys.append(k) for k in self._inherited_attributes.keys(): if k not in all_keys: all_keys.append(k) # for k in self._meta.keys(): # if k not in all_keys: all_keys.append(k) return all_keys def items(self): return map(lambda x: (x, self[x]), self.keys()) def get_id(self): """ Return a unique ID for this object""" #object_type = self['object_type'] #shortname = self.get_description() #object_name = self['name'] if not self.__object_id__: filename = self._original_attributes['meta']['filename'] object_id = (filename, sorted(frozenset(self._defined_attributes.items()))) object_id = str(object_id) self.__object_id__ = str(hash(object_id)) # this is good when troubleshooting ID issues: # definition = self._original_attributes['meta']['raw_definition'] # object_id = str((filename, definition)) #self.__object_id__ = object_id return self.__object_id__ def get_suggested_filename(self): """Get a suitable configuration filename to store this object in :returns: filename, eg str('/etc/nagios/pynag/templates/hosts.cfg') """ # Invalid characters that might potentially mess with our path # | / ' " are all invalid. So is any whitespace invalid_chars = '[/\s\'\"\|]' object_type = re.sub(invalid_chars, '', self.object_type) description = re.sub(invalid_chars, '', self.get_description()) # if pynag_directory is undefined, use "/pynag" dir under nagios.cfg global pynag_directory if pynag_directory is None: from os.path import dirname pynag_directory = dirname(config.cfg_file) + "/pynag" # By default assume this is the filename path = "%s/%ss/%s.cfg" % (pynag_directory, object_type, description) # Services go to same file as their host if object_type == "service" and self.get('host_name'): try: host = Host.objects.get_by_shortname(self.host_name) return host.get_filename() except Exception: pass # templates go to the template directory if not self.is_registered(): path = "%s/templates/%ss.cfg" % (pynag_directory, object_type) # Filename of services should match service description or name elif object_type == 'service': filename = self.name or self.service_description or "untitled" filename = re.sub(invalid_chars, '', filename) path = "%s/%ss/%s.cfg" % (pynag_directory, object_type, filename) return path @pynag.Utils.synchronized(pynag.Utils.rlock) def save(self, filename=None): """Saves any changes to the current object to its configuration file :param filename: * If filename is provided, save a copy of this object in that file. * If filename is None, either save to current file (in case of existing objects) or let pynag guess a location for it in case of new objects. :returns: * In case of existing objects, return number of attributes changed. * In case of new objects, return True """ # Let event-handlers know we are about to save an object self._event(level='pre_save', message="%s '%s'." % (self.object_type, self['shortname'])) number_of_changes = len(self._changes.keys()) filename = filename or self.get_filename() or self.get_suggested_filename() self.set_filename(filename) # If this is a new object, we save it with config.item_add() if self.is_new is True or self._filename_has_changed: for k, v in self._changes.items(): if v is not None: # Dont save anything if attribute is None self._defined_attributes[k] = v self._original_attributes[k] = v del self._changes[k] self.is_new = False self._filename_has_changed = False return config.item_add(self._original_attributes, self.get_filename()) # If we get here, we are making modifications to an object else: number_of_changes = 0 for field_name, new_value in self._changes.items(): save_result = config.item_edit_field( item=self._original_attributes, field_name=field_name, new_value=new_value ) if save_result is True: del self._changes[field_name] self._event(level='write', message="%s changed from '%s' to '%s'" % (field_name, self[field_name], new_value)) # Setting new_value to None, is a signal to remove the attribute # Therefore we remove it from our internal data structure if new_value is None: self._defined_attributes.pop(field_name, None) self._original_attributes.pop(field_name, None) else: self._defined_attributes[field_name] = new_value self._original_attributes[field_name] = new_value number_of_changes += 1 else: raise Exception( "Failure saving object. filename=%s, object=%s" % (self.get_filename(), self['shortname'])) # this piece of code makes sure that when we current object contains all current info self.reload_object() self._event(level='save', message="%s '%s' saved." % (self.object_type, self['shortname'])) return number_of_changes def reload_object(self): """ Re-applies templates to this object (handy when you have changed the use attribute """ old_me = config.get_new_item(self.object_type, self.get_filename()) old_me['meta']['defined_attributes'] = self._defined_attributes for k, v in self._defined_attributes.items(): old_me[k] = v for k, v in self._changes.items(): old_me[k] = v i = config._apply_template(old_me) new_me = self.__class__(item=i) self._defined_attributes = new_me._defined_attributes self._original_attributes = new_me._original_attributes self._inherited_attributes = new_me._inherited_attributes self._meta = new_me._meta self.__object_id__ = None @pynag.Utils.synchronized(pynag.Utils.rlock) def rewrite(self, str_new_definition=None): """Rewrites this Object Definition in its configuration files. :param str_new_definition: The actual string that will be written in the configuration file. If str_new_definition is *None*, then we will use *self.__str__()* :returns: True on success """ self._event(level='pre_save', message="Object definition is being rewritten") if self.is_new is True: self.save() if str_new_definition is None: str_new_definition = str(self) config.item_rewrite(self._original_attributes, str_new_definition) self['meta']['raw_definition'] = str_new_definition self._event(level='write', message="Object definition rewritten") # this piece of code makes sure that when we current object contains all current info new_me = config.parse_string(str_new_definition) if new_me: new_me = new_me[0] self._defined_attributes = new_me['meta']['defined_attributes'] self.reload_object() self._event(level='save', message="Object definition was rewritten") return True def delete(self, recursive=False, cleanup_related_items=True): """ Deletes this object definition from its configuration files. :param recursive: If True, look for items that depend on this object and delete them as well (for example, if you delete a host, delete all its services as well) :param cleanup_related_items: If True, look for related items and remove references to this one. (for example, if you delete a host, remove its name from all hostgroup.members entries) """ self._event(level="pre_save", message="%s '%s' will be deleted." % (self.object_type, self.get_shortname())) if recursive is True: # Recursive does not have any meaning for a generic object, this should subclassed. pass result = config.item_remove(self._original_attributes) self._event(level="write", message="%s '%s' was deleted." % (self.object_type, self.get_shortname())) self._event(level="save", message="%s '%s' was deleted." % (self.object_type, self.get_shortname())) return result def move(self, filename): """Move this object definition to a new file. It will be deleted from current file. This is the same as running: >>> self.copy(filename=filename) # doctest: +SKIP >>> self.delete() # doctest: +SKIP :returns: The new object definition """ new_me = self.copy(filename=filename) self.delete() return new_me def copy(self, recursive=False, filename=None, **args): """ Copies this object definition with any unsaved changes to a new configuration object Arguments: filename: If specified, new object will be saved in this file. recursive: If true, also find any related children objects and copy those **args: Any argument will be treated a modified attribute in the new definition. Examples: myhost = Host.objects.get_by_shortname('myhost.example.com') # Copy this host to a new one myhost.copy( host_name="newhost.example.com", address="127.0.0.1") # Copy this host and all its services: myhost.copy(recursive=True, host_name="newhost.example.com", address="127.0.0.1") Returns: * A copy of the new ObjectDefinition * A list of all copies objects if recursive is True """ if args == {} and filename is None: raise ValueError('To copy an object definition you need at least one new attribute') new_object = string_to_class[self.object_type](filename=filename) for k, v in self._defined_attributes.items(): new_object[k] = v for k, v in self._changes.items(): new_object[k] = v for k, v in args.items(): new_object[k] = v new_object.save() return new_object def rename(self, shortname): """ Change the shortname of this object Most objects that inherit this one, should also be responsible for updating related objects about the rename. Args: shortname: New name for this object Returns: None """ if not self.object_type: raise Exception("Don't use this object on ObjectDefinition. Only sub-classes") if not shortname: raise Exception("You must provide a valid shortname if you intend to rename this object") attribute = '%s_name' % self.object_type self.set_attribute(attribute, shortname) self.save() def get_related_objects(self): """ Returns a list of ObjectDefinition that depend on this object Object can "depend" on another by a 'use' or 'host_name' or similar attribute Returns: List of ObjectDefinition objects """ result = [] if self['name'] is not None: tmp = ObjectDefinition.objects.filter(use__has_field=self['name'], object_type=self['object_type']) for i in tmp: result.append(i) return result def __str__(self): return_buffer = "define %s {\n" % self.object_type fields = self._defined_attributes.keys() for i in self._changes.keys(): if i not in fields: fields.append(i) fields.sort() interesting_fields = ['service_description', 'use', 'name', 'host_name'] for i in interesting_fields: if i in fields: fields.remove(i) fields.insert(0, i) for key in fields: if key == 'meta' or key in self['meta'].keys(): continue value = self[key] return_buffer += " %-30s %s\n" % (key, value) return_buffer += "}\n" return return_buffer def __repr__(self): return "%s: %s" % (self['object_type'], self.get_description()) def get(self, value, default=None): """ self.get(x) == self[x] """ result = self[value] if result is None: return default else: return result def get_description(self): """ Returns a human friendly string describing current object. It will try the following in order: * return self.name (get the generic name) * return self get_shortname() * return "Untitled $object_type" """ return self.name or self.get_shortname() or "Untitled %s" % self.object_type def get_shortname(self): """ Returns shortname of an object in string format. For the confused, nagios documentation refers to shortnames usually as _name. * In case of Host it returns host_name * In case of Command it returns command_name * etc * Special case for services it returns "host_name/service_description" Returns None if no attribute can be found to use as a shortname """ return self.get("%s_name" % self.object_type, None) def get_filename(self): """ Get name of the config file which defines this object """ if self._meta['filename'] is None: return None return os.path.normpath(self._meta['filename']) def set_filename(self, filename): """ Set name of the config file which this object will be written to on next save. """ if filename != self.get_filename(): self._filename_has_changed = True if filename is None: self._meta['filename'] = filename else: self._meta['filename'] = os.path.normpath(filename) def get_macro(self, macroname, host_name=None, contact_name=None): """ Take macroname (e.g. $USER1$) and return its actual value Arguments: macroname -- Macro that is to be resolved. For example $HOSTADDRESS$ host_name -- Optionally specify host (use this for services that -- don't define host specifically for example ones that only -- define hostgroups Returns: (str) Actual value of the macro. For example "$HOSTADDRESS$" becomes "127.0.0.1" """ if macroname.startswith('$ARG'): # Command macros handled in a special function return self._get_command_macro(macroname, host_name=host_name) if macroname.startswith('$USER'): # $USERx$ macros are supposed to be private, but we will display them anyway return config.get_resource(macroname) if macroname.startswith('$HOST') or macroname.startswith('$_HOST'): return self._get_host_macro(macroname, host_name=host_name) if macroname.startswith('$SERVICE') or macroname.startswith('$_SERVICE'): return self._get_service_macro(macroname) if macroname.startswith('$CONTACT') or macroname.startswith('$_CONTACT'): return self._get_contact_macro(macroname, contact_name=contact_name) if macroname in _standard_macros: attr = _standard_macros[macroname] return self[attr] return '' def set_macro(self, macroname, new_value): """ Update a macro (custom variable) like $ARG1$ intelligently Returns: None Notes: You are responsible for calling .save() after modifying the object Examples: >>> s = Service() >>> s.check_command = 'okc-execute!arg1!arg2' >>> s.set_macro('$ARG1$', 'modified1') >>> s.check_command 'okc-execute!modified1!arg2' >>> s.set_macro('$ARG5$', 'modified5') >>> s.check_command 'okc-execute!modified1!arg2!!!modified5' >>> s.set_macro('$_SERVICE_TEST$', 'test') >>> s['__TEST'] 'test' """ if not macroname.startswith('$') or not macroname.endswith('$'): raise ValueError("Macros must be of the format $$") if macroname.startswith('$ARG'): if self.check_command is None: raise ValueError("cant save %s, when there is no check_command defined" % macroname) # split check command into an array # in general the following will apply: # $ARG0$ = c[0] # $ARG1$ = c[1] # etc... c = self._split_check_command_and_arguments(self.check_command) arg_number = int(macroname[4:-1]) # Special hack, if c array is to short for our value, we will make the array longer while arg_number >= len(c): c.append('') # Lets save our attribute c[arg_number] = new_value self.check_command = '!'.join(c) elif macroname.startswith('$_HOST'): macroname = "_" + macroname[6:-1] self[macroname] = new_value elif macroname.startswith('$_SERVICE'): macroname = "_" + macroname[9:-1] self[macroname] = new_value else: raise ValueError("No support for macro %s" % macroname) def get_all_macros(self): """Returns {macroname:macrovalue} hash map of this object's macros""" if self['check_command'] is None: return {} c = self['check_command'] c = self._split_check_command_and_arguments(c) command_name = c.pop(0) command = Command.objects.get_by_shortname(command_name) regex = re.compile("(\$\w+\$)") macronames = regex.findall(command['command_line']) # Add all custom macros to our list: for i in self.keys(): if not i.startswith('_'): continue if self.object_type == 'service': i = '$_SERVICE%s$' % (i[1:]) elif self.object_type == 'host': i = '$_HOST%s$' % (i[1:]) macronames.append(i) result = {} for i in macronames: result[i] = self.get_macro(i) return result def get_effective_command_line(self, host_name=None): """Return a string of this objects check_command with all macros (i.e. $HOSTADDR$) resolved""" if self['check_command'] is None: return None c = self['check_command'] c = self._split_check_command_and_arguments(c) command_name = c.pop(0) try: command = Command.objects.get_by_shortname(command_name, cache_only=True) except ValueError: return None return self._resolve_macros(command.command_line, host_name=host_name) def get_effective_notification_command_line(self, host_name=None, contact_name=None): """Get this objects notifications with all macros (i.e. $HOSTADDR$) resolved :param host_name: Simulate notification using this host. If None: Use first valid host (used for services) :param contact_name: Simulate notification for this contact. If None: use first valid contact for the service :returns: string of this objects notifications """ if contact_name is None: contacts = self.get_effective_contacts() if len(contacts) == 0: raise pynag.Utils.PynagError('Cannot calculate notification command for object with no contacts') else: contact = contacts[0] else: contact = Contact.objects.get_by_shortname(contact_name) notification_command = contact.service_notification_commands if not notification_command: return None command_name = notification_command.split('!').pop(0) try: command = Command.objects.get_by_shortname(command_name) except ValueError: return None return self._resolve_macros(command.command_line, host_name=host_name) def _resolve_macros(self, string, host_name=None): """Resolves every $NAGIOSMACRO$ within the string :param string: Arbitary string that contains macros :param host_name: Optionally supply host_name if this service does not define it :returns: string with every $NAGIOSMACRO$ resolved to actual value Example: >>> host = Host() >>> host.address = "127.0.0.1" >>> host._resolve_macros('check_ping -H $HOSTADDRESS$') 'check_ping -H 127.0.0.1' """ if not string: return None regex = re.compile("(\$\w+\$)") get_macro = lambda x: self.get_macro(x.group(), host_name=host_name) result = regex.sub(get_macro, string) return result def run_check_command(self, host_name=None): """Run the check_command defined by this service. Returns return_code,stdout,stderr""" command = self.get_effective_command_line(host_name=host_name) if command is None: return None proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, stderr = proc.communicate('through stdin to stdout') return proc.returncode, stdout, stderr def _split_check_command_and_arguments(self, check_command): """ Split a nagios "check_command" string into a tuple >>> check_command = "check_ping!warning!critical" >>> o = ObjectDefinition() >>> o._split_check_command_and_arguments(check_command) ['check_ping', 'warning', 'critical'] >>> complex_check_command = "check_ping!warning with \! in it!critical" >>> o._split_check_command_and_arguments(complex_check_command) ['check_ping', 'warning with \\\\! in it', 'critical'] """ if check_command in (None, ''): return [] if '\!' in check_command: check_command = check_command.replace('\!', 'ESCAPE_EXCL_MARK') tmp = check_command.split('!') result = map(lambda x: x.replace('ESCAPE_EXCL_MARK', '\!'), tmp) return result def _get_command_macro(self, macroname, check_command=None, host_name=None): """Resolve any command argument ($ARG1$) macros from check_command""" if check_command is None: check_command = self.check_command if check_command is None: return '' all_args = {} c = self._split_check_command_and_arguments(check_command) c.pop(0) # First item is the command, we dont need it for i, v in enumerate(c): name = '$ARG%s$' % str(i + 1) all_args[name] = v result = all_args.get(macroname, '') # Our $ARGx$ might contain macros on its own, so lets resolve macros in it: result = self._resolve_macros(result, host_name=host_name) return result def _get_service_macro(self, macroname): if macroname.startswith('$_SERVICE'): # If this is a custom macro name = macroname[9:-1] return self["_%s" % name] elif macroname in _standard_macros: attr = _standard_macros[macroname] return self[attr] elif macroname.startswith('$SERVICE'): name = macroname[8:-1].lower() return self.get(name) or '' return '' def _get_host_macro(self, macroname, host_name=None): if macroname.startswith('$_HOST'): # if this is a custom macro name = macroname[6:-1] return self["_%s" % name] elif macroname == '$HOSTADDRESS$' and not self.address: return self.get("host_name") elif macroname in _standard_macros: attr = _standard_macros[macroname] return self[attr] elif macroname.startswith('$HOST'): name = macroname[5:-1].lower() return self.get(name) return '' def _get_contact_macro(self, macroname, contact_name=None): # If contact_name is not specified, get first effective contact and resolve macro for that contact if not contact_name: contacts = self.get_effective_contacts() if len(contacts) == 0: return None contact = contacts[0] else: contact = Contact.objects.get_by_shortname(contact_name) return contact._get_contact_macro(macroname) def get_effective_children(self, recursive=False): """ Get a list of all objects that inherit this object via "use" attribute :param recursive: If true, include grandchildren as well :returns: A list of ObjectDefinition objects """ if not self.name: return [] name = self.name children = self.objects.filter(use__has_field=name) if recursive is True: for i in children: grandchildren = i.get_effective_children(recursive) for grandchild in grandchildren: if grandchild not in children: children.append(grandchild) return children def get_effective_parents(self, recursive=False, cache_only=False): """ Get all objects that this one inherits via "use" attribute Arguments: recursive - If true include grandparents in list to be returned Returns: a list of ObjectDefinition objects """ if not self.use: return [] results = [] use = pynag.Utils.AttributeList(self.use) for parent_name in use: parent = self.objects.get_by_name(parent_name, cache_only=cache_only) if parent not in results: results.append(parent) if recursive is True: for i in results: grandparents = i.get_effective_parents(recursive=True, cache_only=cache_only) for gp in grandparents: if gp not in results: results.append(gp) return results def get_attribute_tuple(self): """ Returns all relevant attributes in the form of: (attribute_name,defined_value,inherited_value) """ result = [] for k in self.keys(): inher = defin = None if k in self._inherited_attributes: inher = self._inherited_attributes[k] if k in self._defined_attributes: defin = self._defined_attributes[k] result.append((k, defin, inher)) return result def get_parents(self): """ Out-dated, use get_effective_parents instead. Kept here for backwards compatibility """ return self.get_effective_parents() def unregister(self, recursive=True): """ Short for self['register'] = 0 ; self.save() """ self['register'] = 0 self.save() if recursive is True: for i in self.get_related_objects(): i.unregister() def attribute_appendfield(self, attribute_name, value): """Convenient way to append value to an attribute with a comma seperated value Example: >>> myservice = Service() >>> myservice.attribute_appendfield(attribute_name="contact_groups", value="alladmins") >>> myservice.contact_groups '+alladmins' >>> myservice.attribute_appendfield(attribute_name="contact_groups", value='webmasters') >>> print myservice.contact_groups +alladmins,webmasters """ aList = AttributeList(self[attribute_name]) # If list was empty before, add a + to it so we are appending to parent if len(aList.fields) == 0: aList.operator = '+' if value not in aList.fields: aList.fields.append(value) self[attribute_name] = str(aList) return def attribute_removefield(self, attribute_name, value): """Convenient way to remove value to an attribute with a comma seperated value Example: >>> myservice = Service() >>> myservice.contact_groups = "+alladmins,localadmins" >>> myservice.attribute_removefield(attribute_name="contact_groups", value='localadmins') >>> print myservice.contact_groups +alladmins >>> myservice.attribute_removefield(attribute_name="contact_groups", value="alladmins") >>> print myservice.contact_groups None """ aList = AttributeList(self[attribute_name]) if value in aList.fields: aList.fields.remove(value) if not aList.fields: # If list is empty, lets remove the attribute self[attribute_name] = None else: self[attribute_name] = str(aList) return def attribute_replacefield(self, attribute_name, old_value, new_value): """Convenient way to replace field within an attribute with a comma seperated value Example: >>> myservice = Service() >>> myservice.contact_groups = "+alladmins,localadmins" >>> myservice.attribute_replacefield(attribute_name="contact_groups", old_value='localadmins', new_value="webmasters") >>> print myservice.contact_groups +alladmins,webmasters """ aList = AttributeList(self[attribute_name]) if old_value in aList.fields: i = aList.fields.index(old_value) aList.fields[i] = new_value self[attribute_name] = str(aList) return def _get_effective_attribute(self, attribute_name): """This helper function returns specific attribute, from this object or its templates This is handy for fields that effectively are many_to_many values. for example, "contactroups +group1,group2,group3" Fields that are known to use this format are: contacts, contactgroups, hostgroups, servicegroups, members,contactgroup_members """ result = [] tmp = self[attribute_name] if tmp is not None: result.append(tmp) if tmp is None or tmp.startswith('+'): for parent in self.get_parents(): result.append(parent._get_effective_attribute(attribute_name)) if parent[attribute_name] is not None and not parent[attribute_name].startswith('+'): break return_value = [] for value in result: value = value.strip('+') if value == '': continue if value not in return_value: return_value.append(value) tmp = ','.join(return_value) tmp = tmp.replace(',,', ',') return tmp def _event(self, level=None, message=None): """ Pass informational message about something that has happened within the Model """ for i in eventhandlers: if level == 'write': i.write(object_definition=self, message=message) elif level == 'save': i.save(object_definition=self, message=message) elif level == 'pre_save': i.pre_save(object_definition=self, message=message) else: i.debug(object_definition=self, message=message) def _do_relations(self): """ Discover all related objects (f.e. services that belong to this host, etc ObjectDefinition only has relations via 'use' paramameter. Subclasses should extend this. """ parents = AttributeList(self.use) for i in parents.fields: ObjectRelations.use[self.object_type][i].add(self.get_id()) class Host(ObjectDefinition): object_type = 'host' objects = ObjectFetcher('host') def acknowledge(self, sticky=1, notify=1, persistent=0, author='pynag', comment='acknowledged by pynag', recursive=False, timestamp=None): if timestamp is None: timestamp = int(time.time()) if recursive is True: pass # Its here for compatibility but we are not using recursive so far. pynag.Control.Command.acknowledge_host_problem(host_name=self.host_name, sticky=sticky, notify=notify, persistent=persistent, author=author, comment=comment, timestamp=timestamp, command_file=config.get_cfg_value('command_file') ) def downtime(self, start_time=None, end_time=None, trigger_id=0, duration=7200, author=None, comment='Downtime scheduled by pynag', recursive=False): """ Put this object in a schedule downtime. Arguments: start_time -- When downtime should start. If None, use time.time() (now) end_time -- When scheduled downtime should end. If None use start_time + duration duration -- Alternative to end_time, downtime lasts for duration seconds. Default 7200 seconds. trigger_id -- trigger_id>0 means that this downtime should trigger another downtime with trigger_id. author -- name of the contact scheduling downtime. If None, use current system user comment -- Comment that will be put in with the downtime recursive -- Also schedule same downtime for all service of this host. Returns: None because commands sent to nagios have no return values Raises: PynagError if this does not look an active object. """ if self.register == '0': raise pynag.Utils.PynagError('Cannot schedule a downtime for unregistered object') if not self.host_name: raise pynag.Utils.PynagError('Cannot schedule a downtime for host with no host_name') if start_time is None: start_time = time.time() if duration is None: duration = 7200 duration = int(duration) if end_time is None: end_time = start_time + duration if author is None: author = getpass.getuser() arguments = { 'host_name': self.host_name, 'start_time': start_time, 'end_time': end_time, 'fixed': '1', 'trigger_id': trigger_id, 'duration': duration, 'author': author, 'comment': comment, } if recursive is True: pynag.Control.Command.schedule_host_svc_downtime(**arguments) else: pynag.Control.Command.schedule_host_downtime(**arguments) def get_effective_services(self): """ Returns a list of all Service that belong to this Host """ get_object = lambda x: Service.objects.get_by_id(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.host_services[self.host_name]) services = map(get_object, list_of_shortnames) # Look for services that define hostgroup_name that we belong to for hg in self.get_effective_hostgroups(): services += hg.get_effective_services() return services def get_effective_contacts(self): """ Returns a list of all Contact that belong to this Host """ get_object = lambda x: Contact.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.host_contacts[self.host_name]) return map(get_object, list_of_shortnames) def get_effective_contact_groups(self): """ Returns a list of all Contactgroup that belong to this Host """ get_object = lambda x: Contactgroup.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.host_contact_groups[self.host_name]) return map(get_object, list_of_shortnames) def get_effective_hostgroups(self): """ Returns a list of all Hostgroup that belong to this Host """ get_object = lambda x: Hostgroup.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.host_hostgroups[self.host_name]) return map(get_object, list_of_shortnames) def get_effective_network_parents(self, recursive=False): """ Get all objects this one depends on via "parents" attribute Arguments: recursive - If true include grandparents in list to be returned Returns: a list of ObjectDefinition objects """ if self['parents'] is None: return [] results = [] parents = self['parents'].split(',') for parent_name in parents: results.append(self.objects.get_by_name(parent_name, cache_only=True)) if recursive is True: grandparents = [] for i in results: grandparents.append(i.get_effective_network_parents(recursive=True)) results += grandparents return results def get_effective_network_children(self, recursive=False): """ Get all objects that depend on this one via "parents" attribute Arguments: recursive - If true include grandchildren in list to be returned Returns: a list of ObjectDefinition objects """ if self.host_name is None: return [] children = self.objects.filter(parents__has_field=self.host_name) if recursive is True: for child in children: children += child.get_effective_network_children(recursive=True) return children def delete(self, recursive=False, cleanup_related_items=True): """ Delete this host and optionally its services Works like ObjectDefinition.delete() except for: Arguments: cleanup_related_items -- If True, remove references found in hostgroups and escalations recursive -- If True, also delete all services of this host """ if recursive is True and self.host_name: for i in Service.objects.filter(host_name__has_field=self.host_name, hostgroup_name__exists=False): # delete only services where only this host_name and no hostgroups are defined i.delete(recursive=recursive, cleanup_related_items=cleanup_related_items) if cleanup_related_items is True and self.host_name: hostgroups = Hostgroup.objects.filter(members__has_field=self.host_name) dependenciesAndEscalations = ObjectDefinition.objects.filter( host_name__has_field=self.host_name, object_type__isnot='host') for i in hostgroups: # remove host from hostgroups i.attribute_removefield('members', self.host_name) i.save() for i in dependenciesAndEscalations: # remove from host/service escalations/dependencies i.attribute_removefield('host_name', self.host_name) if ((i.get_attribute('object_type').endswith("escalation") or i.get_attribute('object_type').endswith("dependency")) and recursive is True and i.attribute_is_empty("host_name") and i.attribute_is_empty("hostgroup_name")): i.delete(recursive=recursive, cleanup_related_items=cleanup_related_items) else: i.save() # get these here as we might have deleted some in the block above dependencies = ObjectDefinition.objects.filter(dependent_host_name__has_field=self.host_name) for i in dependencies: # remove from host/service escalations/dependencies i.attribute_removefield('dependent_host_name', self.host_name) if (i.get_attribute('object_type').endswith("dependency") and recursive is True and i.attribute_is_empty("dependent_host_name") and i.attribute_is_empty("dependent_hostgroup_name")): i.delete(recursive=recursive, cleanup_related_items=cleanup_related_items) else: i.save() # Call parent to get delete myself return super(self.__class__, self).delete(recursive=recursive, cleanup_related_items=cleanup_related_items) def get_related_objects(self): result = super(self.__class__, self).get_related_objects() if self['host_name'] is not None: tmp = Service.objects.filter(host_name=self['host_name']) for i in tmp: result.append(i) return result def get_effective_check_command(self): """ Returns a Command object as defined by check_command attribute Raises KeyError if check_command is not found or not defined. """ c = self.check_command if not c or c == '': raise KeyError(None) check_command = c.split('!')[0] return Command.objects.get_by_shortname(check_command, cache_only=True) def get_current_status(self): """ Returns a dictionary with status data information for this object """ status = pynag.Parsers.StatusDat(cfg_file=cfg_file) host = status.get_hoststatus(self.host_name) return host def copy(self, recursive=False, filename=None, **args): """ Same as ObjectDefinition.copy() except can recursively copy services """ copies = [ObjectDefinition.copy(self, recursive=recursive, filename=filename, **args)] if recursive is True and 'host_name' in args: for i in self.get_effective_services(): copies.append(i.copy(filename=filename, host_name=args.get('host_name'))) return copies def _do_relations(self): super(self.__class__, self)._do_relations() # Do hostgroups hg = AttributeList(self.hostgroups) for i in hg.fields: ObjectRelations.host_hostgroups[self.host_name].add(i) ObjectRelations.hostgroup_hosts[i].add(self.host_name) # Contactgroups cg = AttributeList(self.contact_groups) for i in cg.fields: ObjectRelations.host_contact_groups[self.host_name].add(i) ObjectRelations.contactgroup_hosts[i].add(self.get_id()) contacts = AttributeList(self.contacts) for i in contacts.fields: ObjectRelations.host_contacts[self.host_name].add(i) ObjectRelations.contact_hosts[i].add(self.get_id()) if self.check_command: command_name = self.check_command.split('!')[0] ObjectRelations.command_service[self.host_name].add(command_name) def add_to_hostgroup(self, hostgroup_name): """ Add host to a hostgroup """ hostgroup = Hostgroup.objects.get_by_shortname(hostgroup_name) return _add_object_to_group(self, hostgroup) def remove_from_hostgroup(self, hostgroup_name): """ Removes host from specified hostgroup """ hostgroup = Hostgroup.objects.get_by_shortname(hostgroup_name) return _remove_object_from_group(self, hostgroup) def add_to_contactgroup(self, contactgroup): return _add_to_contactgroup(self, contactgroup) def remove_from_contactgroup(self, contactgroup): return _remove_from_contactgroup(self, contactgroup) def rename(self, shortname): """ Rename this host, and modify related objects """ old_name = self.get_shortname() super(Host, self).rename(shortname) for i in Service.objects.filter(host_name__has_field=old_name): i.attribute_replacefield('host_name', old_name, shortname) i.save() for i in Hostgroup.objects.filter(members__has_field=old_name): i.attribute_replacefield('members', old_name, shortname) i.save() class Service(ObjectDefinition): object_type = 'service' objects = ObjectFetcher('service') def get_shortname(self): host_name = self.host_name service_description = self.service_description if host_name and service_description: return "%s/%s" % (host_name, service_description) elif service_description: return "%s" % (service_description, ) else: return None def _get_host_macro(self, macroname, host_name=None): if not host_name: host_name = self['host_name'] if not host_name: return None try: myhost = Host.objects.get_by_shortname(host_name) return myhost._get_host_macro(macroname) except Exception: return None def _do_relations(self): super(self.__class__, self)._do_relations() # Do hostgroups hg = AttributeList(self.hostgroup_name) for i in hg.fields: ObjectRelations.service_hostgroups[self.get_id()].add(i) ObjectRelations.hostgroup_services[i].add(self.get_id()) # Contactgroups cg = AttributeList(self.contact_groups) for i in cg.fields: ObjectRelations.service_contact_groups[self.get_id()].add(i) ObjectRelations.contactgroup_services[i].add(self.get_id()) contacts = AttributeList(self.contacts) for i in contacts.fields: ObjectRelations.service_contacts[self.get_id()].add(i) ObjectRelations.contact_services[i].add(self.get_id()) sg = AttributeList(self.servicegroups) for i in sg.fields: ObjectRelations.service_servicegroups[self.get_id()].add(i) ObjectRelations.servicegroup_services[i].add(self.get_id()) if self.check_command: command_name = self.check_command.split('!')[0] ObjectRelations.command_service[self.get_id()].add(command_name) hosts = AttributeList(self.host_name) for i in hosts.fields: ObjectRelations.service_hosts[self.get_id()].add(i) ObjectRelations.host_services[i].add(self.get_id()) def acknowledge(self, sticky=1, notify=1, persistent=0, author='pynag', comment='acknowledged by pynag', timestamp=None): if timestamp is None: timestamp = int(time.time()) pynag.Control.Command.acknowledge_svc_problem(host_name=self.host_name, service_description=self.service_description, sticky=sticky, notify=notify, persistent=persistent, author=author, comment=comment, timestamp=timestamp, command_file=config.get_cfg_value('command_file') ) def downtime(self, start_time=None, end_time=None, trigger_id=0, duration=7200, author=None, comment='Downtime scheduled by pynag', recursive=False): """ Put this object in a schedule downtime. Arguments: start_time -- When downtime should start. If None, use time.time() (now) end_time -- When scheduled downtime should end. If None use start_time + duration duration -- Alternative to end_time, downtime lasts for duration seconds. Default 7200 seconds. trigger_id -- trigger_id>0 means that this downtime should trigger another downtime with trigger_id. author -- name of the contact scheduling downtime. If None, use current system user comment -- Comment that will be put in with the downtime recursive -- Here for compatibility. Has no effect on a service. Returns: None because commands sent to nagios have no return values Raises: PynagError if this does not look an active object. """ if recursive is True: pass # Only for compatibility, it has no effect. if self.register == '0': raise pynag.Utils.PynagError('Cannot schedule a downtime for unregistered object') if not self.host_name: raise pynag.Utils.PynagError('Cannot schedule a downtime for service with no host_name') if not self.service_description: raise pynag.Utils.PynagError('Cannot schedule a downtime for service with service_description') if start_time is None: start_time = time.time() if duration is None: duration = 7200 duration = int(duration) if end_time is None: end_time = start_time + duration if author is None: author = getpass.getuser() pynag.Control.Command.schedule_svc_downtime( host_name=self.host_name, service_description=self.service_description, start_time=start_time, end_time=end_time, fixed='1', trigger_id=trigger_id, duration=duration, author=author, comment=comment, ) def get_effective_hosts(self): """ Returns a list of all Host that belong to this Service """ get_object = lambda x: Host.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.service_hosts[self.get_id()]) hosts = map(get_object, list_of_shortnames) for hg in self.get_effective_hostgroups(): hosts += hg.get_effective_hosts() return hosts def get_effective_contacts(self): """ Returns a list of all Contact that belong to this Service """ get_object = lambda x: Contact.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.service_contacts[self.get_id()]) return map(get_object, list_of_shortnames) def get_effective_contact_groups(self): """ Returns a list of all Contactgroup that belong to this Service """ get_object = lambda x: Contactgroup.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.service_contact_groups[self.get_id()]) return map(get_object, list_of_shortnames) def get_effective_hostgroups(self): """ Returns a list of all Hostgroup that belong to this Service """ get_object = lambda x: Hostgroup.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.service_hostgroups[self.get_id()]) return map(get_object, list_of_shortnames) def get_effective_servicegroups(self): """ Returns a list of all Servicegroup that belong to this Service """ get_object = lambda x: Servicegroup.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.service_servicegroups[self.get_id()]) return map(get_object, list_of_shortnames) def get_effective_check_command(self): """ Returns a Command object as defined by check_command attribute Raises KeyError if check_command is not found or not defined. """ c = self.check_command if not c or c == '': raise KeyError(None) check_command = c.split('!')[0] return Command.objects.get_by_shortname(check_command, cache_only=True) def get_current_status(self): """ Returns a dictionary with status data information for this object """ status = pynag.Parsers.StatusDat(cfg_file=cfg_file) service = status.get_servicestatus(self.host_name, service_description=self.service_description) return service def add_to_servicegroup(self, servicegroup_name): """ Add this service to a specific servicegroup """ sg = Servicegroup.objects.get_by_shortname(servicegroup_name) return _add_object_to_group(self, sg) def remove_from_servicegroup(self, servicegroup_name): """ remove this service from a specific servicegroup """ sg = Servicegroup.objects.get_by_shortname(servicegroup_name) return _remove_object_from_group(self, sg) def add_to_contactgroup(self, contactgroup): return _add_to_contactgroup(self, contactgroup) def remove_from_contactgroup(self, contactgroup): return _remove_from_contactgroup(self, contactgroup) def merge_with_host(self): """ Moves a service from its original file to the same file as the first effective host """ if not self.host_name: return else: host = Host.objects.get_by_shortname(self.host_name) host_filename = host.get_filename() if host_filename != self.get_filename(): new_serv = self.move(host_filename) new_serv.save() def rename(self, shortname): """ Not implemented. Do not use. """ raise Exception("Not implemented for service.") class Command(ObjectDefinition): object_type = 'command' objects = ObjectFetcher('command') def rename(self, shortname): """ Rename this command, and reconfigure all related objects """ old_name = self.get_shortname() super(Command, self).rename(shortname) objects = ObjectDefinition.objects.filter(check_command=old_name) # TODO: Do something with objects that have check_command!ARGS!ARGS #objects += ObjectDefinition.objects.filter(check_command__startswith="%s!" % old_name) for i in objects: # Skip objects that are inheriting this from a template if not i.is_defined("check_command"): continue i.check_command = shortname i.save() class Contact(ObjectDefinition): object_type = 'contact' objects = ObjectFetcher('contact') def get_effective_contactgroups(self): """ Get a list of all Contactgroup that are hooked to this contact """ get_object = lambda x: Contactgroup.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.contact_contactgroups[self.contact_name]) return map(get_object, list_of_shortnames) def get_effective_hosts(self): """ Get a list of all Host that are hooked to this Contact """ result = set() # First add all hosts that name this contact specifically get_object = lambda x: Host.objects.get_by_id(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.contact_hosts[self.contact_name]) result.update(map(get_object, list_of_shortnames)) # Next do the same for all contactgroups this contact belongs in for i in self.get_effective_contactgroups(): result.update(i.get_effective_hosts()) return result def get_effective_services(self): """ Get a list of all Service that are hooked to this Contact """ result = set() # First add all services that name this contact specifically get_object = lambda x: Service.objects.get_by_id(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.contact_services[self.contact_name]) result.update(map(get_object, list_of_shortnames)) return result def _get_contact_macro(self, macroname, contact_name=None): if macroname in _standard_macros: attribute_name = _standard_macros.get(macroname) elif macroname.startswith('$_CONTACT'): # if this is a custom macro name = macroname[len('$_CONTACT'):-1] attribute_name = "_%s" % name elif macroname.startswith('$CONTACT'): # Lets guess an attribute for this macro # So convert $CONTACTEMAIL$ to email name = macroname[len('$CONTACT'):-1] attribute_name = name.lower() else: return '' return self.get(attribute_name) or '' def _do_relations(self): super(self.__class__, self)._do_relations() groups = AttributeList(self.contactgroups) for i in groups.fields: ObjectRelations.contact_contactgroups[self.contact_name].add(i) ObjectRelations.contactgroup_contacts[i].add(self.contact_name) def add_to_contactgroup(self, contactgroup): return _add_to_contactgroup(self, contactgroup) def remove_from_contactgroup(self, contactgroup): return _remove_from_contactgroup(self, contactgroup) def delete(self, recursive=False, cleanup_related_items=True): """ Delete this contact and optionally remove references in groups and escalations Works like ObjectDefinition.delete() except: Arguments: cleanup_related_items -- If True, remove all references to this contact in contactgroups and escalations recursive -- If True, remove escalations/dependencies that rely on this (and only this) contact """ if recursive is True: # No object is 100% dependent on a contact pass if cleanup_related_items is True and self.contact_name: contactgroups = Contactgroup.objects.filter(members__has_field=self.contact_name) hostSvcAndEscalations = ObjectDefinition.objects.filter(contacts__has_field=self.contact_name) # will find references in Hosts, Services as well as Host/Service-escalations for i in contactgroups: # remove contact from contactgroups i.attribute_removefield('members', self.contact_name) i.save() for i in hostSvcAndEscalations: # remove contact from objects i.attribute_removefield('contacts', self.contact_name) if (i.get_attribute('object_type').endswith("escalation") and recursive is True and i.attribute_is_empty("contacts") and i.attribute_is_empty("contact_groups")): # no contacts or contact_groups defined for this escalation i.delete(recursive=recursive, cleanup_related_items=cleanup_related_items) else: i.save() # Call parent to get delete myself return super(self.__class__, self).delete(recursive=recursive, cleanup_related_items=cleanup_related_items) def rename(self, shortname): """ Renames this object, and triggers a change in related items as well. Args: shortname: New name for this object Returns: None """ old_name = self.contact_name super(Contact, self).rename(shortname) for i in Host.objects.filter(contacts__has_field=old_name): i.attribute_replacefield('contacts', old_name, shortname) i.save() for i in Service.objects.filter(contacts__has_field=old_name): i.attribute_replacefield('contacts', old_name, shortname) i.save() for i in Contactgroup.objects.filter(members__has_field=old_name): i.attribute_replacefield('members', old_name, shortname) i.save() class ServiceDependency(ObjectDefinition): object_type = 'servicedependency' objects = ObjectFetcher('servicedependency') class HostDependency(ObjectDefinition): object_type = 'hostdependency' objects = ObjectFetcher('hostdependency') class HostEscalation(ObjectDefinition): object_type = 'hostescalation' objects = ObjectFetcher('hostescalation') class ServiceEscalation(ObjectDefinition): object_type = 'serviceescalation' objects = ObjectFetcher('serviceescalation') class Contactgroup(ObjectDefinition): object_type = 'contactgroup' objects = ObjectFetcher('contactgroup') def get_effective_contactgroups(self): """ Returns a list of every Contactgroup that is a member of this Contactgroup """ get_object = lambda x: Contactgroup.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.contactgroup_subgroups[self.contactgroup_name]) return map(get_object, list_of_shortnames) def get_effective_contacts(self): """ Returns a list of every Contact that is a member of this Contactgroup """ get_object = lambda x: Contact.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.contactgroup_contacts[self.contactgroup_name]) return map(get_object, list_of_shortnames) def get_effective_hosts(self): """ Return every Host that belongs to this contactgroup """ list_of_shortnames = sorted(ObjectRelations.contactgroup_hosts[self.contactgroup_name]) get_object = lambda x: Host.objects.get_by_id(x, cache_only=True) return map(get_object, list_of_shortnames) def get_effective_services(self): """ Return every Host that belongs to this contactgroup """ # TODO: review this method services = {} for i in Service.objects.all: services[i.get_id()] = i list_of_shortnames = sorted(ObjectRelations.contactgroup_services[self.contactgroup_name]) get_object = lambda x: services[x] return map(get_object, list_of_shortnames) def _do_relations(self): super(self.__class__, self)._do_relations() members = AttributeList(self.members) for i in members.fields: ObjectRelations.contactgroup_contacts[self.contactgroup_name].add(i) ObjectRelations.contact_contactgroups[i].add(self.contactgroup_name) groups = AttributeList(self.contactgroup_members) for i in groups.fields: ObjectRelations.contactgroup_contactgroups[self.contactgroup_name].add(i) def add_contact(self, contact_name): """ Adds one specific contact to this contactgroup. """ contact = Contact.objects.get_by_shortname(contact_name) return _add_to_contactgroup(contact, self) def remove_contact(self, contact_name): """ Remove one specific contact from this contactgroup """ contact = Contact.objects.get_by_shortname(contact_name) return _remove_from_contactgroup(contact, self) def delete(self, recursive=False, cleanup_related_items=True): """ Delete this contactgroup and optionally remove references in hosts/services Works like ObjectDefinition.delete() except: Arguments: cleanup_related_items -- If True, remove all references to this group in hosts,services,etc. recursive -- If True, remove dependant escalations. """ if recursive is True: # No object is 100% dependent on a contactgroup pass if cleanup_related_items is True and self.contactgroup_name: contactgroups = Contactgroup.objects.filter(contactgroup_members__has_field=self.contactgroup_name) contacts = Contact.objects.filter(contactgroups__has_field=self.contactgroup_name) # nagios is inconsistent with the attribute names - notice the missing _ in contactgroups attribute name hostSvcAndEscalations = ObjectDefinition.objects.filter(contact_groups__has_field=self.contactgroup_name) # will find references in Hosts, Services as well as Host/Service-escalations for i in contactgroups: # remove contactgroup from other contactgroups i.attribute_removefield('contactgroup_members', self.contactgroup_name) i.save() for i in contacts: i.attribute_removefield('contactgroups', self.contactgroup_name) i.save() for i in hostSvcAndEscalations: # remove contactgroup from objects i.attribute_removefield('contact_groups', self.contactgroup_name) if (i.get_attribute('object_type').endswith("escalation") and recursive is True and i.attribute_is_empty("contacts") and i.attribute_is_empty("contact_groups")): # no contacts or contact_groups defined for this escalation i.delete(recursive=recursive, cleanup_related_items=cleanup_related_items) else: i.save() # Call parent to get delete myself return super(self.__class__, self).delete(recursive=recursive, cleanup_related_items=cleanup_related_items) def rename(self, shortname): """ Renames this object, and triggers a change in related items as well. Args: shortname: New name for this object Returns: None """ old_name = self.get_shortname() super(Contactgroup, self).rename(shortname) for i in Host.objects.filter(contactgroups__has_field=old_name): i.attribute_replacefield('contactgroups', old_name, shortname) i.save() for i in Service.objects.filter(contactgroups__has_field=old_name): i.attribute_replacefield('contactgroups', old_name, shortname) i.save() for i in Contact.objects.filter(contactgroups__has_field=old_name): i.attribute_replacefield('contactgroups', old_name, shortname) i.save() class Hostgroup(ObjectDefinition): object_type = 'hostgroup' objects = ObjectFetcher('hostgroup') def get_effective_services(self): """ Returns a list of all Service that belong to this hostgroup """ list_of_shortnames = sorted(ObjectRelations.hostgroup_services[self.hostgroup_name]) get_object = lambda x: Service.objects.get_by_id(x, cache_only=True) return map(get_object, list_of_shortnames) def get_effective_hosts(self): """ Returns a list of all Host that belong to this hostgroup """ list_of_shortnames = sorted(ObjectRelations.hostgroup_hosts[self.hostgroup_name]) get_object = lambda x: Host.objects.get_by_shortname(x, cache_only=True) return map(get_object, list_of_shortnames) def get_effective_hostgroups(self): """ Returns a list of every Hostgroup that is a member of this Hostgroup """ get_object = lambda x: Hostgroup.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.hostgroup_subgroups[self.hostgroup_name]) return map(get_object, list_of_shortnames) def _do_relations(self): super(self.__class__, self)._do_relations() members = AttributeList(self.members) for i in members.fields: ObjectRelations.hostgroup_hosts[self.hostgroup_name].add(i) ObjectRelations.host_hostgroups[i].add(self.hostgroup_name) groups = AttributeList(self.hostgroup_members) for i in groups.fields: ObjectRelations.hostgroup_hostgroups[self.hostgroup_name].add(i) def add_host(self, host_name): """ Adds host to this group. Behaves like Hostgroup._add_member_to_group """ host = Host.objects.get_by_shortname(host_name) return _add_object_to_group(host, self) def remove_host(self, host_name): """ Remove host from this group. Behaves like Hostgroup._remove_member_from_group """ host = Host.objects.get_by_shortname(host_name) return _remove_object_from_group(host, self) def delete(self, recursive=False, cleanup_related_items=True): """ Delete this hostgroup and optionally remove references in hosts and services Works like ObjectDefinition.delete() except: Arguments: cleanup_related_items -- If True, remove all references to this group in hosts/services,escalations,etc recursive -- If True, remove services and escalations that bind to this (and only this) hostgroup """ if recursive is True and self.hostgroup_name: for i in Service.objects.filter(hostgroup_name=self.hostgroup_name, host_name__exists=False): # remove only if self.hostgroup_name is the only hostgroup and no host_name is specified i.delete(recursive=recursive) if cleanup_related_items is True and self.hostgroup_name: hostgroups = Hostgroup.objects.filter(hostgroup_members__has_field=self.hostgroup_name) hosts = Host.objects.filter(hostgroups__has_field=self.hostgroup_name) dependenciesAndEscalations = ObjectDefinition.objects.filter( hostgroup_name__has_field=self.hostgroup_name, object_type__isnot='hostgroup') for i in hostgroups: # remove hostgroup from other hostgroups i.attribute_removefield('hostgroup_members', self.hostgroup_name) i.save() for i in hosts: # remove hostgroup from hosts i.attribute_removefield('hostgroups', self.hostgroup_name) i.save() for i in dependenciesAndEscalations: # remove from host/service escalations/dependencies i.attribute_removefield('hostgroup_name', self.hostgroup_name) if ((i.get_attribute('object_type').endswith("escalation") or i.get_attribute('object_type').endswith("dependency")) and recursive is True and i.attribute_is_empty("host_name") and i.attribute_is_empty("hostgroup_name")): i.delete(recursive=recursive, cleanup_related_items=cleanup_related_items) else: i.save() # get these here as we might have deleted some in the block above dependencies = ObjectDefinition.objects.filter(dependent_hostgroup_name__has_field=self.hostgroup_name) for i in dependencies: # remove from host/service escalations/dependencies i.attribute_removefield('dependent_hostgroup_name', self.hostgroup_name) if (i.get_attribute('object_type').endswith("dependency") and recursive is True and i.attribute_is_empty("dependent_host_name") and i.attribute_is_empty("dependent_hostgroup_name")): i.delete(recursive=recursive, cleanup_related_items=cleanup_related_items) else: i.save() # Call parent to get delete myself return super(self.__class__, self).delete(recursive=recursive, cleanup_related_items=cleanup_related_items) def downtime(self, start_time=None, end_time=None, trigger_id=0, duration=7200, author=None, comment='Downtime scheduled by pynag', recursive=False): """ Put every host and service in this hostgroup in a schedule downtime. Arguments: start_time -- When downtime should start. If None, use time.time() (now) end_time -- When scheduled downtime should end. If None use start_time + duration duration -- Alternative to end_time, downtime lasts for duration seconds. Default 7200 seconds. trigger_id -- trigger_id>0 means that this downtime should trigger another downtime with trigger_id. author -- name of the contact scheduling downtime. If None, use current system user comment -- Comment that will be put in with the downtime recursive -- For compatibility with other downtime commands, recursive is always assumed to be true Returns: None because commands sent to nagios have no return values Raises: PynagError if this does not look an active object. """ if recursive is True: pass # Not used, but is here for backwards compatibility if self.register == '0': raise pynag.Utils.PynagError('Cannot schedule a downtime for unregistered object') if not self.hostgroup_name: raise pynag.Utils.PynagError('Cannot schedule a downtime for hostgroup with no hostgroup_name') if start_time is None: start_time = time.time() if duration is None: duration = 7200 duration = int(duration) if end_time is None: end_time = start_time + duration if author is None: author = getpass.getuser() arguments = { 'hostgroup_name': self.hostgroup_name, 'start_time': start_time, 'end_time': end_time, 'fixed': '1', 'trigger_id': trigger_id, 'duration': duration, 'author': author, 'comment': comment, } pynag.Control.Command.schedule_hostgroup_host_downtime(**arguments) pynag.Control.Command.schedule_hostgroup_svc_downtime(**arguments) def rename(self, shortname): """ Rename this hostgroup, and modify hosts if required """ old_name = self.get_shortname() super(Hostgroup, self).rename(shortname) for i in Host.objects.filter(hostgroups__has_field=old_name): if not i.is_defined('hostgroups'): continue i.attribute_replacefield('hostgroups', old_name, shortname) i.save() class Servicegroup(ObjectDefinition): object_type = 'servicegroup' objects = ObjectFetcher('servicegroup') def get_effective_services(self): """ Returns a list of all Service that belong to this Servicegroup """ list_of_shortnames = sorted(ObjectRelations.servicegroup_services[self.servicegroup_name]) get_object = lambda x: Service.objects.get_by_id(x, cache_only=True) return map(get_object, list_of_shortnames) def get_effective_servicegroups(self): """ Returns a list of every Servicegroup that is a member of this Servicegroup """ get_object = lambda x: Servicegroup.objects.get_by_shortname(x, cache_only=True) list_of_shortnames = sorted(ObjectRelations.servicegroup_subgroups[self.servicegroup_name]) return map(get_object, list_of_shortnames) def add_service(self, shortname): """ Adds service to this group. Behaves like _add_object_to_group(object, group)""" service = Service.objects.get_by_shortname(shortname) return _add_object_to_group(service, self) def remove_service(self, shortname): """ remove service from this group. Behaves like _remove_object_from_group(object, group)""" service = Service.objects.get_by_shortname(shortname) return _remove_object_from_group(service, self) def _do_relations(self): super(self.__class__, self)._do_relations() # Members directive for the servicegroup is members = host1,service1,host2,service2,...,hostn,servicen members = AttributeList(self.members).fields while len(members) > 1: host_name = members.pop(0) service_description = members.pop(0) shortname = '%s/%s' % (host_name, service_description) ObjectRelations.servicegroup_members[self.servicegroup_name].add(shortname) # Handle servicegroup_members groups = AttributeList(self.servicegroup_members) for i in groups.fields: ObjectRelations.servicegroup_servicegroups[self.servicegroup_name].add(i) def downtime(self, start_time=None, end_time=None, trigger_id=0, duration=7200, author=None, comment='Downtime scheduled by pynag', recursive=False): """ Put every host and service in this servicegroup in a schedule downtime. Arguments: start_time -- When downtime should start. If None, use time.time() (now) end_time -- When scheduled downtime should end. If None use start_time + duration duration -- Alternative to end_time, downtime lasts for duration seconds. Default 7200 seconds. trigger_id -- trigger_id>0 means that this downtime should trigger another downtime with trigger_id. author -- name of the contact scheduling downtime. If None, use current system user comment -- Comment that will be put in with the downtime recursive -- For compatibility with other downtime commands, recursive is always assumed to be true Returns: None because commands sent to nagios have no return values Raises: PynagError if this does not look an active object. """ if recursive is True: pass # Its here for compatibility but we dont do anything with it. if self.register == '0': raise pynag.Utils.PynagError('Cannot schedule a downtime for unregistered object') if not self.servicegroup_name: raise pynag.Utils.PynagError('Cannot schedule a downtime for servicegroup with no servicegroup_name') if start_time is None: start_time = time.time() if duration is None: duration = 7200 duration = int(duration) if end_time is None: end_time = start_time + duration if author is None: author = getpass.getuser() arguments = { 'servicegroup_name': self.servicegroup_name, 'start_time': start_time, 'end_time': end_time, 'fixed': '1', 'trigger_id': trigger_id, 'duration': duration, 'author': author, 'comment': comment, } pynag.Control.Command.schedule_servicegroup_host_downtime(**arguments) pynag.Control.Command.schedule_servicegroup_svc_downtime(**arguments) class Timeperiod(ObjectDefinition): object_type = 'timeperiod' objects = ObjectFetcher('timeperiod') def _add_object_to_group(my_object, my_group): """ Add one specific object to a specified objectgroup Examples: c = Contact() g = Contactgroup() _add_to_group(c, g ) """ # First of all, we behave a little differently depending on what type of an object we lets define some variables: group_type = my_group.object_type # contactgroup,hostgroup,servicegroup group_name = my_group.get_shortname() # admins object_name = my_object.get_shortname() # root group_field = 'members' # i.e. Contactgroup.members object_field = group_type + 's' # i.e. Host.hostgroups groups = my_object[object_field] or '' # f.e. value of Contact.contactgroups list_of_groups = pynag.Utils.AttributeList(groups) members = my_group[group_field] or '' # f.e. Value of Contactgroup.members list_of_members = pynag.Utils.AttributeList(members) if group_name in list_of_groups: return False # Group says it already has object as a member if object_name in list_of_members: return False # Member says it is already part of group my_object.attribute_appendfield(object_field, group_name) my_object.save() return True def _remove_object_from_group(my_object, my_group): """ Remove one specific object to a specified objectgroup Examples: c = Contact() g = Contactgroup() _remove_object_from_group(c, g ) """ # First of all, we behave a little differently depending on what type of an object we lets define some variables: group_type = my_group.object_type # contactgroup,hostgroup,servicegroup group_name = my_group.get_shortname() # admins object_name = my_object.get_shortname() # root group_field = 'members' # i.e. Contactgroup.members object_field = group_type + 's' # i.e. Host.hostgroups groups = my_object[object_field] or '' # e. value of Contact.contactgroups list_of_groups = pynag.Utils.AttributeList(groups) members = my_group[group_field] or '' # f.e. Value of Contactgroup.members list_of_members = pynag.Utils.AttributeList(members) if group_name in list_of_groups: # Remove group from the object my_object.attribute_removefield(object_field, group_name) my_object.save() if object_name in list_of_members: # Remove object from the group my_group.attribute_removefield(group_field, object_name) my_group.save() def _add_to_contactgroup(my_object, contactgroup): """ add Host or Service to a contactgroup """ if isinstance(contactgroup, basestring): contactgroup = Contactgroup.objects.get_by_shortname(contactgroup) contactgroup_name = contactgroup.contactgroup_name if my_object.object_type == "contact": return _add_object_to_group(my_object, contactgroup) current_contactgroups = AttributeList(my_object.contact_groups) if contactgroup_name not in current_contactgroups.fields: my_object.attribute_appendfield('contact_groups', contactgroup_name) my_object.save() return True else: return False def _remove_from_contactgroup(my_object, contactgroup): """ remove Host or Service from a contactgroup """ if isinstance(contactgroup, basestring): contactgroup = Contactgroup.objects.get_by_shortname(contactgroup) contactgroup_name = contactgroup.contactgroup_name if my_object.object_type == "contact": return _remove_object_from_group(my_object, contactgroup) current_contactgroups = AttributeList(my_object.contact_groups) if contactgroup_name in current_contactgroups.fields: my_object.attribute_removefield('contact_groups', contactgroup_name) my_object.save() return True else: return False string_to_class = {} string_to_class['contact'] = Contact string_to_class['service'] = Service string_to_class['host'] = Host string_to_class['hostgroup'] = Hostgroup string_to_class['contactgroup'] = Contactgroup string_to_class['servicegroup'] = Servicegroup string_to_class['timeperiod'] = Timeperiod string_to_class['hostdependency'] = HostDependency string_to_class['servicedependency'] = ServiceDependency string_to_class['hostescalation'] = HostEscalation string_to_class['serviceescalation'] = ServiceEscalation string_to_class['command'] = Command #string_to_class[None] = ObjectDefinition # Attributelist is put here for backwards compatibility AttributeList = pynag.Utils.AttributeList def _add_property(ClassType, name): """ Create a dynamic property specific ClassType object_definition = ClassType() object_definition.name -> object_definition['name' So in human speak, this reads info from all_attributes and makes sure that Host has Host.host_name Returns: None """ fget = lambda self: self[name] fset = lambda self, value: self.set_attribute(name, value) fdel = lambda self: self.set_attribute(name, None) fdoc = "This is the %s attribute for object definition" setattr(ClassType, name, property(fget, fset, fdel, fdoc)) # Add register, name and use to all objects _add_property(ObjectDefinition, 'register') _add_property(ObjectDefinition, 'name') _add_property(ObjectDefinition, 'use') # For others, create attributes dynamically based on all_attributes.keys() for object_type, attributes in all_attributes.object_definitions.items(): # Lets find common attributes that every object definition should have: if object_type == 'any': continue if object_type not in string_to_class: continue Object = string_to_class[object_type] for attribute in attributes: _add_property(Object, attribute) if __name__ == '__main__': pass pynag-0.9.1+dfsg.orig/pynag/Model/macros.py0000664000175000017500000000243112354645173017551 0ustar clintclint# -*- coding: utf-8 -*- # # pynag - Python Nagios plug-in and configuration environment # Copyright (C) 2010 Pall Sigurdsson # # 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. """ This file contains a dict object that maps Nagios Standard macronames to specific values. i.e. macros['$HOSTADDR$'] should return 'address' """ # TODO: This hash map is incomplete, someone should type everything from the documentation to here: # See: http://nagios.sourceforge.net/docs/3_0/macrolist.html _standard_macros = { '$HOSTADDRESS$': 'address', '$HOSTNAME$': 'host_name', '$SERVICEDESC$': 'service_description', '$CONTACTEMAIL$': 'email', } pynag-0.9.1+dfsg.orig/pynag/Model/all_attributes.py0000664000175000017500000042154512354645173021316 0ustar clintclint# These are gathered from Nagios Object Definition documentation (Version 3.2.3) object_definitions = {} object_definitions["any"] = {} object_definitions["any"]["use"] = {"name": "use", "required": "optional", "value": "use"} object_definitions["any"]["register"] = {"name": "register", "required": "optional", "value": "register"} object_definitions["any"]["name"] = {"name": "name", "required": "optional", "value": "name"} object_definitions["host"] = {} object_definitions["host"]["host_name"] = {"name": "host_name", "required": "required", "value": "host_name"} object_definitions["host"]["alias"] = {"name": "alias", "required": "required", "value": "alias"} object_definitions["host"]["display_name"] = {"name": "display_name", "required": "optional", "value": "display_name"} object_definitions["host"]["address"] = {"name": "address", "required": "required", "value": "address"} object_definitions["host"]["parents"] = {"name": "parents", "required": "optional", "value": "host_names"} object_definitions["host"]["hostgroups"] = {"name": "hostgroups", "required": "optional", "value": "hostgroup_names"} object_definitions["host"]["check_command"] = {"name": "check_command", "required": "optional", "value": "command_name"} object_definitions["host"]["initial_state"] = {"name": "initial_state", "required": "optional", "value": "[o,d,u]"} object_definitions["host"]["max_check_attempts"] = {"name": "max_check_attempts", "required": "required", "value": "#"} object_definitions["host"]["check_interval"] = {"name": "check_interval", "required": "optional", "value": "#"} object_definitions["host"]["retry_interval"] = {"name": "retry_interval", "required": "optional", "value": "#"} object_definitions["host"]["active_checks_enabled"] = {"name": "active_checks_enabled", "required": "optional", "value": "[0/1]"} object_definitions["host"]["passive_checks_enabled"] = {"name": "passive_checks_enabled", "required": "optional", "value": "[0/1]"} object_definitions["host"]["check_period"] = {"name": "check_period", "required": "required", "value": "timeperiod_name"} object_definitions["host"]["obsess_over_host"] = {"name": "obsess_over_host", "required": "optional", "value": "[0/1]"} object_definitions["host"]["check_freshness"] = {"name": "check_freshness", "required": "optional", "value": "[0/1]"} object_definitions["host"]["freshness_threshold"] = {"name": "freshness_threshold", "required": "optional", "value": "#"} object_definitions["host"]["event_handler"] = {"name": "event_handler", "required": "optional", "value": "command_name"} object_definitions["host"]["event_handler_enabled"] = {"name": "event_handler_enabled", "required": "optional", "value": "[0/1]"} object_definitions["host"]["low_flap_threshold"] = {"name": "low_flap_threshold", "required": "optional", "value": "#"} object_definitions["host"]["high_flap_threshold"] = {"name": "high_flap_threshold", "required": "optional", "value": "#"} object_definitions["host"]["flap_detection_enabled"] = {"name": "flap_detection_enabled", "required": "optional", "value": "[0/1]"} object_definitions["host"]["flap_detection_options"] = {"name": "flap_detection_options", "required": "optional", "value": "[o,d,u]"} object_definitions["host"]["process_perf_data"] = {"name": "process_perf_data", "required": "optional", "value": "[0/1]"} object_definitions["host"]["retain_status_information"] = {"name": "retain_status_information", "required": "optional", "value": "[0/1]"} object_definitions["host"]["retain_nonstatus_information"] = {"name": "retain_nonstatus_information", "required": "optional", "value": "[0/1]"} object_definitions["host"]["contacts"] = {"name": "contacts", "required": "required", "value": "contacts"} object_definitions["host"]["contact_groups"] = {"name": "contact_groups", "required": "required", "value": "contact_groups"} object_definitions["host"]["notification_interval"] = {"name": "notification_interval", "required": "required", "value": "#"} object_definitions["host"]["first_notification_delay"] = {"name": "first_notification_delay", "required": "optional", "value": "#"} object_definitions["host"]["notification_period"] = {"name": "notification_period", "required": "required", "value": "timeperiod_name"} object_definitions["host"]["notification_options"] = {"name": "notification_options", "required": "optional", "value": "[d,u,r,f,s]"} object_definitions["host"]["notifications_enabled"] = {"name": "notifications_enabled", "required": "optional", "value": "[0/1]"} object_definitions["host"]["stalking_options"] = {"name": "stalking_options", "required": "optional", "value": "[o,d,u]"} object_definitions["host"]["notes"] = {"name": "notes", "required": "optional", "value": "note_string"} object_definitions["host"]["notes_url"] = {"name": "notes_url", "required": "optional", "value": "url"} object_definitions["host"]["action_url"] = {"name": "action_url", "required": "optional", "value": "url"} object_definitions["host"]["icon_image"] = {"name": "icon_image", "required": "optional", "value": "image_file"} object_definitions["host"]["icon_image_alt"] = {"name": "icon_image_alt", "required": "optional", "value": "alt_string"} object_definitions["host"]["vrml_image"] = {"name": "vrml_image", "required": "optional", "value": "image_file"} object_definitions["host"]["statusmap_image"] = {"name": "statusmap_image", "required": "optional", "value": "image_file"} object_definitions["host"]["2d_coords"] = {"name": "2d_coords", "required": "optional", "value": "x_coord,y_coord"} object_definitions["host"]["3d_coords"] = {"name": "3d_coords", "required": "optional", "value": "x_coord,y_coord,z_coord"} object_definitions["hostgroup"] = {} object_definitions["hostgroup"]["hostgroup_name"] = {"name": "hostgroup_name", "required": "required", "value": "hostgroup_name"} object_definitions["hostgroup"]["alias"] = {"name": "alias", "required": "required", "value": "alias"} object_definitions["hostgroup"]["members"] = {"name": "members", "required": "optional", "value": "hosts"} object_definitions["hostgroup"]["hostgroup_members"] = {"name": "hostgroup_members", "required": "optional", "value": "hostgroups"} object_definitions["hostgroup"]["notes"] = {"name": "notes", "required": "optional", "value": "note_string"} object_definitions["hostgroup"]["notes_url"] = {"name": "notes_url", "required": "optional", "value": "url"} object_definitions["hostgroup"]["action_url"] = {"name": "action_url", "required": "optional", "value": "url"} object_definitions["service"] = {} object_definitions["service"]["host_name"] = {"name": "host_name", "required": "required", "value": "host_name"} object_definitions["service"]["hostgroup_name"] = {"name": "hostgroup_name", "required": "optional", "value": "hostgroup_name"} object_definitions["service"]["service_description"] = {"name": "service_description", "required": "required", "value": "service_description"} object_definitions["service"]["display_name"] = {"name": "display_name", "required": "optional", "value": "display_name"} object_definitions["service"]["servicegroups"] = {"name": "servicegroups", "required": "optional", "value": "servicegroup_names"} object_definitions["service"]["is_volatile"] = {"name": "is_volatile", "required": "optional", "value": "[0/1]"} object_definitions["service"]["check_command"] = {"name": "check_command", "required": "required", "value": "command_name"} object_definitions["service"]["initial_state"] = {"name": "initial_state", "required": "optional", "value": "[o,w,u,c]"} object_definitions["service"]["max_check_attempts"] = {"name": "max_check_attempts", "required": "required", "value": "#"} object_definitions["service"]["check_interval"] = {"name": "check_interval", "required": "required", "value": "#"} object_definitions["service"]["retry_interval"] = {"name": "retry_interval", "required": "required", "value": "#"} object_definitions["service"]["active_checks_enabled"] = {"name": "active_checks_enabled", "required": "optional", "value": "[0/1]"} object_definitions["service"]["passive_checks_enabled"] = {"name": "passive_checks_enabled", "required": "optional", "value": "[0/1]"} object_definitions["service"]["check_period"] = {"name": "check_period", "required": "required", "value": "timeperiod_name"} object_definitions["service"]["obsess_over_service"] = {"name": "obsess_over_service", "required": "optional", "value": "[0/1]"} object_definitions["service"]["check_freshness"] = {"name": "check_freshness", "required": "optional", "value": "[0/1]"} object_definitions["service"]["freshness_threshold"] = {"name": "freshness_threshold", "required": "optional", "value": "#"} object_definitions["service"]["event_handler"] = {"name": "event_handler", "required": "optional", "value": "command_name"} object_definitions["service"]["event_handler_enabled"] = {"name": "event_handler_enabled", "required": "optional", "value": "[0/1]"} object_definitions["service"]["low_flap_threshold"] = {"name": "low_flap_threshold", "required": "optional", "value": "#"} object_definitions["service"]["high_flap_threshold"] = {"name": "high_flap_threshold", "required": "optional", "value": "#"} object_definitions["service"]["flap_detection_enabled"] = {"name": "flap_detection_enabled", "required": "optional", "value": "[0/1]"} object_definitions["service"]["flap_detection_options"] = {"name": "flap_detection_options", "required": "optional", "value": "[o,w,c,u]"} object_definitions["service"]["process_perf_data"] = {"name": "process_perf_data", "required": "optional", "value": "[0/1]"} object_definitions["service"]["retain_status_information"] = {"name": "retain_status_information", "required": "optional", "value": "[0/1]"} object_definitions["service"]["retain_nonstatus_information"] = {"name": "retain_nonstatus_information", "required": "optional", "value": "[0/1]"} object_definitions["service"]["notification_interval"] = {"name": "notification_interval", "required": "required", "value": "#"} object_definitions["service"]["first_notification_delay"] = {"name": "first_notification_delay", "required": "optional", "value": "#"} object_definitions["service"]["notification_period"] = {"name": "notification_period", "required": "required", "value": "timeperiod_name"} object_definitions["service"]["notification_options"] = {"name": "notification_options", "required": "optional", "value": "[w,u,c,r,f,s]"} object_definitions["service"]["notifications_enabled"] = {"name": "notifications_enabled", "required": "optional", "value": "[0/1]"} object_definitions["service"]["contacts"] = {"name": "contacts", "required": "required", "value": "contacts"} object_definitions["service"]["contact_groups"] = {"name": "contact_groups", "required": "required", "value": "contact_groups"} object_definitions["service"]["stalking_options"] = {"name": "stalking_options", "required": "optional", "value": "[o,w,u,c]"} object_definitions["service"]["notes"] = {"name": "notes", "required": "optional", "value": "note_string"} object_definitions["service"]["notes_url"] = {"name": "notes_url", "required": "optional", "value": "url"} object_definitions["service"]["action_url"] = {"name": "action_url", "required": "optional", "value": "url"} object_definitions["service"]["icon_image"] = {"name": "icon_image", "required": "optional", "value": "image_file"} object_definitions["service"]["icon_image_alt"] = {"name": "icon_image_alt", "required": "optional", "value": "alt_string"} object_definitions["servicegroup"] = {} object_definitions["servicegroup"]["servicegroup_name"] = {"name": "servicegroup_name", "required": "required", "value": "servicegroup_name"} object_definitions["servicegroup"]["alias"] = {"name": "alias", "required": "required", "value": "alias"} object_definitions["servicegroup"]["members"] = {"name": "members", "required": "optional", "value": "services"} object_definitions["servicegroup"]["servicegroup_members"] = {"name": "servicegroup_members", "required": "optional", "value": "servicegroups"} object_definitions["servicegroup"]["notes"] = {"name": "notes", "required": "optional", "value": "note_string"} object_definitions["servicegroup"]["notes_url"] = {"name": "notes_url", "required": "optional", "value": "url"} object_definitions["servicegroup"]["action_url"] = {"name": "action_url", "required": "optional", "value": "url"} object_definitions["contact"] = {} object_definitions["contact"]["contact_name"] = {"name": "contact_name", "required": "required", "value": "contact_name"} object_definitions["contact"]["alias"] = {"name": "alias", "required": "optional", "value": "alias"} object_definitions["contact"]["contactgroups"] = {"name": "contactgroups", "required": "optional", "value": "contactgroup_names"} object_definitions["contact"]["host_notifications_enabled"] = {"name": "host_notifications_enabled", "required": "required", "value": "[0/1]"} object_definitions["contact"]["service_notifications_enabled"] = {"name": "service_notifications_enabled", "required": "required", "value": "[0/1]"} object_definitions["contact"]["host_notification_period"] = {"name": "host_notification_period", "required": "required", "value": "timeperiod_name"} object_definitions["contact"]["service_notification_period"] = {"name": "service_notification_period", "required": "required", "value": "timeperiod_name"} object_definitions["contact"]["host_notification_options"] = {"name": "host_notification_options", "required": "required", "value": "[d,u,r,f,s,n]"} object_definitions["contact"]["service_notification_options"] = {"name": "service_notification_options", "required": "required", "value": "[w,u,c,r,f,s,n]"} object_definitions["contact"]["host_notification_commands"] = {"name": "host_notification_commands", "required": "required", "value": "command_name"} object_definitions["contact"]["service_notification_commands"] = {"name": "service_notification_commands", "required": "required", "value": "command_name"} object_definitions["contact"]["email"] = {"name": "email", "required": "optional", "value": "email_address"} object_definitions["contact"]["pager"] = {"name": "pager", "required": "optional", "value": "pager_number or pager_email_gateway"} object_definitions["contact"]["address"] = {"name": "address", "required": "optional", "value": "additional_contact_address"} object_definitions["contact"]["can_submit_commands"] = {"name": "can_submit_commands", "required": "optional", "value": "[0/1]"} object_definitions["contact"]["retain_status_information"] = {"name": "retain_status_information", "required": "optional", "value": "[0/1]"} object_definitions["contact"]["retain_nonstatus_information"] = {"name": "retain_nonstatus_information", "required": "optional", "value": "[0/1]"} object_definitions["contactgroup"] = {} object_definitions["contactgroup"]["contactgroup_name"] = {"name": "contactgroup_name", "required": "required", "value": "contactgroup_name"} object_definitions["contactgroup"]["alias"] = {"name": "alias", "required": "required", "value": "alias"} object_definitions["contactgroup"]["members"] = {"name": "members", "required": "optional", "value": "contacts"} object_definitions["contactgroup"]["contactgroup_members"] = {"name": "contactgroup_members", "required": "optional", "value": "contactgroups"} object_definitions["timeperiod"] = {} object_definitions["timeperiod"]["timeperiod_name"] = {"name": "timeperiod_name", "required": "required", "value": "timeperiod_name"} object_definitions["timeperiod"]["alias"] = {"name": "alias", "required": "required", "value": "alias"} object_definitions["timeperiod"]["[weekday]"] = {"name": "[weekday]", "required": "optional", "value": "timeranges"} object_definitions["timeperiod"]["[exception]"] = {"name": "[exception]", "required": "optional", "value": "timeranges"} object_definitions["timeperiod"]["exclude"] = {"name": "exclude", "required": "optional", "value": "]"} object_definitions["command"] = {} object_definitions["command"]["command_name"] = {"name": "command_name", "required": "required", "value": "command_name"} object_definitions["command"]["command_line"] = {"name": "command_line", "required": "required", "value": "command_line"} object_definitions["servicedependency"] = {} object_definitions["servicedependency"]["dependent_host_name"] = {"name": "dependent_host_name", "required": "required", "value": "host_name"} object_definitions["servicedependency"]["dependent_hostgroup_name"] = {"name": "dependent_hostgroup_name", "required": "optional", "value": "hostgroup_name"} object_definitions["servicedependency"]["dependent_service_description"] = {"name": "dependent_service_description", "required": "required", "value": "service_description"} object_definitions["servicedependency"]["host_name"] = {"name": "host_name", "required": "required", "value": "host_name"} object_definitions["servicedependency"]["hostgroup_name"] = {"name": "hostgroup_name", "required": "optional", "value": "hostgroup_name"} object_definitions["servicedependency"]["service_description"] = {"name": "service_description", "required": "required", "value": "service_description"} object_definitions["servicedependency"]["inherits_parent"] = {"name": "inherits_parent", "required": "optional", "value": "[0/1]"} object_definitions["servicedependency"]["execution_failure_criteria"] = {"name": "execution_failure_criteria", "required": "optional", "value": "[o,w,u,c,p,n]"} object_definitions["servicedependency"]["notification_failure_criteria"] = {"name": "notification_failure_criteria", "required": "optional", "value": "[o,w,u,c,p,n]"} object_definitions["servicedependency"]["dependency_period"] = {"name": "dependency_period", "required": "optional", "value": "timeperiod_name"} object_definitions["serviceescalation"] = {} object_definitions["serviceescalation"]["host_name"] = {"name": "host_name", "required": "required", "value": "host_name"} object_definitions["serviceescalation"]["hostgroup_name"] = {"name": "hostgroup_name", "required": "optional", "value": "hostgroup_name"} object_definitions["serviceescalation"]["service_description"] = {"name": "service_description", "required": "required", "value": "service_description"} object_definitions["serviceescalation"]["contacts"] = {"name": "contacts", "required": "required", "value": "contacts"} object_definitions["serviceescalation"]["contact_groups"] = {"name": "contact_groups", "required": "required", "value": "contactgroup_name"} object_definitions["serviceescalation"]["first_notification"] = {"name": "first_notification", "required": "required", "value": "#"} object_definitions["serviceescalation"]["last_notification"] = {"name": "last_notification", "required": "required", "value": "#"} object_definitions["serviceescalation"]["notification_interval"] = {"name": "notification_interval", "required": "required", "value": "#"} object_definitions["serviceescalation"]["escalation_period"] = {"name": "escalation_period", "required": "optional", "value": "timeperiod_name"} object_definitions["serviceescalation"]["escalation_options"] = {"name": "escalation_options", "required": "optional", "value": "[w,u,c,r]"} object_definitions["hostdependency"] = {} object_definitions["hostdependency"]["dependent_host_name"] = {"name": "dependent_host_name", "required": "required", "value": "host_name"} object_definitions["hostdependency"]["dependent_hostgroup_name"] = {"name": "dependent_hostgroup_name", "required": "optional", "value": "hostgroup_name"} object_definitions["hostdependency"]["host_name"] = {"name": "host_name", "required": "required", "value": "host_name"} object_definitions["hostdependency"]["hostgroup_name"] = {"name": "hostgroup_name", "required": "optional", "value": "hostgroup_name"} object_definitions["hostdependency"]["inherits_parent"] = {"name": "inherits_parent", "required": "optional", "value": "[0/1]"} object_definitions["hostdependency"]["execution_failure_criteria"] = {"name": "execution_failure_criteria", "required": "optional", "value": "[o,d,u,p,n]"} object_definitions["hostdependency"]["notification_failure_criteria"] = {"name": "notification_failure_criteria", "required": "optional", "value": "[o,d,u,p,n]"} object_definitions["hostdependency"]["dependency_period"] = {"name": "dependency_period", "required": "optional", "value": "timeperiod_name"} object_definitions["hostescalation"] = {} object_definitions["hostescalation"]["host_name"] = {"name": "host_name", "required": "required", "value": "host_name"} object_definitions["hostescalation"]["hostgroup_name"] = {"name": "hostgroup_name", "required": "optional", "value": "hostgroup_name"} object_definitions["hostescalation"]["contacts"] = {"name": "contacts", "required": "required", "value": "contacts"} object_definitions["hostescalation"]["contact_groups"] = {"name": "contact_groups", "required": "required", "value": "contactgroup_name"} object_definitions["hostescalation"]["first_notification"] = {"name": "first_notification", "required": "required", "value": "#"} object_definitions["hostescalation"]["last_notification"] = {"name": "last_notification", "required": "required", "value": "#"} object_definitions["hostescalation"]["notification_interval"] = {"name": "notification_interval", "required": "required", "value": "#"} object_definitions["hostescalation"]["escalation_period"] = {"name": "escalation_period", "required": "optional", "value": "timeperiod_name"} object_definitions["hostescalation"]["escalation_options"] = {"name": "escalation_options", "required": "optional", "value": "[d,u,r]"} object_definitions["hostextinfo"] = {} object_definitions["hostextinfo"]["host_name"] = {"name": "host_name", "required": "required", "value": "host_name"} object_definitions["hostextinfo"]["notes"] = {"name": "notes", "required": "optional", "value": "note_string"} object_definitions["hostextinfo"]["notes_url"] = {"name": "notes_url", "required": "optional", "value": "url"} object_definitions["hostextinfo"]["action_url"] = {"name": "action_url", "required": "optional", "value": "url"} object_definitions["hostextinfo"]["icon_image"] = {"name": "icon_image", "required": "optional", "value": "image_file"} object_definitions["hostextinfo"]["icon_image_alt"] = {"name": "icon_image_alt", "required": "optional", "value": "alt_string"} object_definitions["hostextinfo"]["vrml_image"] = {"name": "vrml_image", "required": "optional", "value": "image_file"} object_definitions["hostextinfo"]["statusmap_image"] = {"name": "statusmap_image", "required": "optional", "value": "image_file"} object_definitions["hostextinfo"]["2d_coords"] = {"name": "2d_coords", "required": "optional", "value": "x_coord,y_coord"} object_definitions["hostextinfo"]["3d_coords"] = {"name": "3d_coords", "required": "optional", "value": "x_coord,y_coord,z_coord"} object_definitions["serviceextinfo"] = {} object_definitions["serviceextinfo"]["host_name"] = {"name": "host_name", "required": "required", "value": "host_name"} object_definitions["serviceextinfo"]["service_description"] = {"name": "service_description", "required": "required", "value": "service_description"} object_definitions["serviceextinfo"]["notes"] = {"name": "notes", "required": "optional", "value": "note_string"} object_definitions["serviceextinfo"]["notes_url"] = {"name": "notes_url", "required": "optional", "value": "url"} object_definitions["serviceextinfo"]["action_url"] = {"name": "action_url", "required": "optional", "value": "url"} object_definitions["serviceextinfo"]["icon_image"] = {"name": "icon_image", "required": "optional", "value": "image_file"} object_definitions["serviceextinfo"]["icon_image_alt"] = {"name": "icon_image_alt", "required": "optional", "value": "alt_string"} # Generated via examples/Model/parse-configmain.py main_config = {'accept_passive_host_checks': {'doc': 'This option determines whether or not Nagios will accept passive host checks when it initially (re)starts. If this option is disabled, Nagios will not accept any passive host checks. Note: If you have state retention enabled, Nagios will ignore this setting when it (re)starts and use the last known setting for this option (as stored in the state retention file), unless you disable the use_retained_program_state option. If you want to change this option when state retention is active (and the use_retained_program_state is enabled), you\'ll have to use the appropriate external command or change it via the web interface. Values are as follows: ', 'examples': ['accept_passive_host_checks=1'], 'format': 'accept_passive_host_checks=<0/1>', 'options': ["0 = Don't accept passive host checks", '1 = Accept passive host checks (default)'], 'title': 'Passive Host Check Acceptance Option'}, 'accept_passive_service_checks': {'doc': 'This option determines whether or not Nagios will accept passive service checks when it initially (re)starts. If this option is disabled, Nagios will not accept any passive service checks. Note: If you have state retention enabled, Nagios will ignore this setting when it (re)starts and use the last known setting for this option (as stored in the state retention file), unless you disable the use_retained_program_state option. If you want to change this option when state retention is active (and the use_retained_program_state is enabled), you\'ll have to use the appropriate external command or change it via the web interface. Values are as follows: ', 'examples': ['accept_passive_service_checks=1'], 'format': 'accept_passive_service_checks=<0/1>', 'options': ["0 = Don't accept passive service checks", '1 = Accept passive service checks (default)'], 'title': 'Passive Service Check Acceptance Option'}, 'additional_freshness_latency': {'doc': 'This option determines the number of seconds Nagios will add to any host or services freshness threshold it automatically calculates (e.g. those not specified explicity by the user). More information on freshness checking can be found here. ', 'examples': ['additional_freshness_latency=15'], 'format': 'additional_freshness_latency=<#>', 'options': [], 'title': 'Additional Freshness Threshold Latency Option'}, 'admin_email': {'doc': 'This is the email address for the administrator of the local machine (i.e. the one that Nagios is running on). This value can be used in notification commands by using the $ADMINEMAIL$ macro. ', 'examples': ['admin_email=root@localhost.localdomain'], 'format': 'admin_email=<email_address>', 'options': [], 'title': 'Administrator Email Address'}, 'admin_pager': {'doc': 'This is the pager number (or pager email gateway) for the administrator of the local machine (i.e. the one that Nagios is running on). The pager number/address can be used in notification commands by using the $ADMINPAGER$ macro. ', 'examples': ['admin_pager=pageroot@localhost.localdomain'], 'format': 'admin_pager=<pager_number_or_pager_email_gateway>', 'options': [], 'title': 'Administrator Pager'}, 'auto_reschedule_checks': {'doc': 'This option determines whether or not Nagios will attempt to automatically reschedule active host and service checks to "smooth" them out over time. This can help to balance the load on the monitoring server, as it will attempt to keep the time between consecutive checks consistent, at the expense of executing checks on a more rigid schedule. WARNING: THIS IS AN EXPERIMENTAL FEATURE AND MAY BE REMOVED IN FUTURE VERSIONS. ENABLING THIS OPTION CAN DEGRADE PERFORMANCE - RATHER THAN INCREASE IT - IF USED IMPROPERLY! ', 'examples': ['auto_reschedule_checks=1'], 'format': 'auto_reschedule_checks=<0/1>', 'options': [], 'title': 'Auto-Rescheduling Option'}, 'auto_rescheduling_interval': {'doc': 'This option determines how often (in seconds) Nagios will attempt to automatically reschedule checks. This option only has an effect if the auto_reschedule_checks option is enabled. Default is 30 seconds. WARNING: THIS IS AN EXPERIMENTAL FEATURE AND MAY BE REMOVED IN FUTURE VERSIONS. ENABLING THE AUTO-RESCHEDULING OPTION CAN DEGRADE PERFORMANCE - RATHER THAN INCREASE IT - IF USED IMPROPERLY! ', 'examples': ['auto_rescheduling_interval=30'], 'format': 'auto_rescheduling_interval=<seconds>', 'options': [], 'title': 'Auto-Rescheduling Interval'}, 'auto_rescheduling_window': {'doc': 'This option determines the "window" of time (in seconds) that Nagios will look at when automatically rescheduling checks. Only host and service checks that occur in the next X seconds (determined by this variable) will be rescheduled. This option only has an effect if the auto_reschedule_checks option is enabled. Default is 180 seconds (3 minutes). WARNING: THIS IS AN EXPERIMENTAL FEATURE AND MAY BE REMOVED IN FUTURE VERSIONS. ENABLING THE AUTO-RESCHEDULING OPTION CAN DEGRADE PERFORMANCE - RATHER THAN INCREASE IT - IF USED IMPROPERLY! ', 'examples': ['auto_rescheduling_window=180'], 'format': 'auto_rescheduling_window=<seconds>', 'options': [], 'title': 'Auto-Rescheduling Window'}, 'bare_update_checks': {'doc': 'This option deterines what data Nagios will send to api.nagios.org when it checks for updates. By default, Nagios will send information on the current version of Nagios you have installed, as well as an indicator as to whether this was a new installation or not. Nagios Enterprises uses this data to determine the number of users running specific version of Nagios. Enable this option if you do not wish for this information to be sent. ', 'examples': ['bare_update_checks'], 'format': 'bare_update_checks=<0/1>', 'options': [], 'title': 'Bare Update Checks'}, 'broker_module': {'doc': 'This directive is used to specify an event broker module that should by loaded by Nagios at startup. Use multiple directives if you want to load more than one module. Arguments that should be passed to the module at startup are seperated from the module path by a space. !!! WARNING !!! Do NOT overwrite modules while they are being used by Nagios or Nagios will crash in a fiery display of SEGFAULT glory. This is a bug/limitation either in dlopen(), the kernel, and/or the filesystem. And maybe Nagios... The correct/safe way of updating a module is by using one of these methods: ', 'examples': ['broker_module=/usr/local/nagios/bin/ndomod.o cfg_file=/usr/local/nagios/etc/ndomod.cfg'], 'format': 'broker_module=<modulepath> [moduleargs]', 'options': ['Shutdown Nagios, replace the module file, restart Nagios', 'While Nagios is running... delete the original module file, move the new module file into place, restart Nagios'], 'title': 'Event Broker Modules'}, 'cached_host_check_horizon': {'doc': 'This option determines the maximum amount of time (in seconds) that the state of a previous host check is considered current. Cached host states (from host checks that were performed more recently than the time specified by this value) can improve host check performance immensely. Too high of a value for this option may result in (temporarily) inaccurate host states, while a low value may result in a performance hit for host checks. Use a value of 0 if you want to disable host check caching. More information on cached checks can be found here. ', 'examples': ['cached_host_check_horizon=15'], 'format': 'cached_host_check_horizon=<seconds>', 'options': [], 'title': 'Cached Host Check Horizon'}, 'cached_service_check_horizon': {'doc': 'This option determines the maximum amount of time (in seconds) that the state of a previous service check is considered current. Cached service states (from service checks that were performed more recently than the time specified by this value) can improve service check performance when a lot of service dependencies are used. Too high of a value for this option may result in inaccuracies in the service dependency logic. Use a value of 0 if you want to disable service check caching. More information on cached checks can be found here. ', 'examples': ['cached_service_check_horizon=15'], 'format': 'cached_service_check_horizon=<seconds>', 'options': [], 'title': 'Cached Service Check Horizon'}, 'cfg_dir': {'doc': 'This directive is used to specify a directory which contains object configuration files that Nagios should use for monitoring. All files in the directory with a .cfg extension are processed as object config files. Additionally, Nagios will recursively process all config files in subdirectories of the directory you specify here. You can seperate your configuration files into different directories and specify multiple cfg_dir= statements to have all config files in each directory processed. ', 'examples': ['cfg_dir=/usr/local/nagios/etc/commands', 'cfg_dir=/usr/local/nagios/etc/services', 'cfg_dir=/usr/local/nagios/etc/hosts'], 'format': 'cfg_dir=<directory_name>', 'options': [], 'title': 'Object Configuration Directory'}, 'cfg_file': {'doc': 'This directive is used to specify an object configuration file containing object definitions that Nagios should use for monitoring. Object configuration files contain definitions for hosts, host groups, contacts, contact groups, services, commands, etc. You can seperate your configuration information into several files and specify multiple cfg_file= statements to have each of them processed. ', 'examples': ['cfg_file=/usr/local/nagios/etc/hosts.cfg', 'cfg_file=/usr/local/nagios/etc/services.cfg', 'cfg_file=/usr/local/nagios/etc/commands.cfg'], 'format': 'cfg_file=<file_name>', 'options': [], 'title': 'Object Configuration File'}, 'check_external_commands': {'doc': 'This option determines whether or not Nagios will check the command file for commands that should be executed. This option must be enabled if you plan on using the command CGI to issue commands via the web interface. More information on external commands can be found here. ', 'examples': ['check_external_commands=1'], 'format': 'check_external_commands=<0/1>', 'options': ["0 = Don't check external commands", '1 = Check external commands (default)'], 'title': 'External Command Check Option'}, 'check_for_orphaned_hosts': {'doc': 'This option allows you to enable or disable checks for orphaned hoste checks. Orphaned host checks are checks which have been executed and have been removed from the event queue, but have not had any results reported in a long time. Since no results have come back in for the host, it is not rescheduled in the event queue. This can cause host checks to stop being executed. Normally it is very rare for this to happen - it might happen if an external user or process killed off the process that was being used to execute a host check. If this option is enabled and Nagios finds that results for a particular host check have not come back, it will log an error message and reschedule the host check. If you start seeing host checks that never seem to get rescheduled, enable this option and see if you notice any log messages about orphaned hosts. ', 'examples': ['check_for_orphaned_hosts=1'], 'format': 'check_for_orphaned_hosts=<0/1>', 'options': ["0 = Don't check for orphaned host checks", '1 = Check for orphaned host checks (default)'], 'title': 'Orphaned Host Check Option'}, 'check_for_orphaned_services': {'doc': 'This option allows you to enable or disable checks for orphaned service checks. Orphaned service checks are checks which have been executed and have been removed from the event queue, but have not had any results reported in a long time. Since no results have come back in for the service, it is not rescheduled in the event queue. This can cause service checks to stop being executed. Normally it is very rare for this to happen - it might happen if an external user or process killed off the process that was being used to execute a service check. If this option is enabled and Nagios finds that results for a particular service check have not come back, it will log an error message and reschedule the service check. If you start seeing service checks that never seem to get rescheduled, enable this option and see if you notice any log messages about orphaned services. ', 'examples': ['check_for_orphaned_services=1'], 'format': 'check_for_orphaned_services=<0/1>', 'options': ["0 = Don't check for orphaned service checks", '1 = Check for orphaned service checks (default)'], 'title': 'Orphaned Service Check Option'}, 'check_for_updates': {'doc': 'This option determines whether Nagios will automatically check to see if new updates (releases) are available. It is recommend that you enable this option to ensure that you stay on top of the latest critical patches to Nagios. Nagios is critical to you - make sure you keep it in good shape. Nagios will check once a day for new updates. Data collected by Nagios Enterprises from the update check is processed in accordance with our privacy policy - see http://api.nagios.org for details. ', 'examples': ['check_for_updates=1'], 'format': 'check_for_updates=<0/1>', 'options': [], 'title': 'Update Checks'}, 'check_host_freshness': {'doc': 'This option determines whether or not Nagios will periodically check the "freshness" of host checks. Enabling this option is useful for helping to ensure that passive host checks are received in a timely manner. More information on freshness checking can be found here. ', 'examples': ['check_host_freshness=0'], 'format': 'check_host_freshness=<0/1>', 'options': ["0 = Don't check host freshness", '1 = Check host freshness (default)'], 'title': 'Host Freshness Checking Option'}, 'check_result_path': {'doc': 'This options determines which directory Nagios will use to temporarily store host and service check results before they are processed. This directory should not be used to store any other files, as Nagios will periodically clean this directory of old file (see the max_check_result_file_age option for more information). Note: Make sure that only a single instance of Nagios has access to the check result path. If multiple instances of Nagios have their check result path set to the same directory, you will run into problems with check results being processed (incorrectly) by the wrong instance of Nagios! ', 'examples': ['check_result_path=/var/spool/nagios/checkresults'], 'format': 'check_result_path=<path>', 'options': [], 'title': 'Check Result Path'}, 'check_result_reaper_frequency': {'doc': 'This option allows you to control the frequency in seconds of check result "reaper" events. "Reaper" events process the results from host and service checks that have finished executing. These events consitute the core of the monitoring logic in Nagios. ', 'examples': ['check_result_reaper_frequency=5'], 'format': 'check_result_reaper_frequency=<frequency_in_seconds>', 'options': [], 'title': 'Check Result Reaper Frequency'}, 'check_service_freshness': {'doc': 'This option determines whether or not Nagios will periodically check the "freshness" of service checks. Enabling this option is useful for helping to ensure that passive service checks are received in a timely manner. More information on freshness checking can be found here. ', 'examples': ['check_service_freshness=0'], 'format': 'check_service_freshness=<0/1>', 'options': ["0 = Don't check service freshness", '1 = Check service freshness (default)'], 'title': 'Service Freshness Checking Option'}, 'child_processes_fork_twice': {'doc': 'This option determines whether or not Nagios will fork() child processes twice when it executes host and service checks. By default, Nagios fork()s twice. However, if the use_large_installation_tweaks option is enabled, it will only fork() once. By defining this option in your configuration file, you are able to override things to get the behavior you want. ', 'examples': ['child_processes_fork_twice=0'], 'format': 'child_processes_fork_twice=<0/1>', 'options': ['0 = Fork() just once', '1 = Fork() twice'], 'title': 'Child Processes Fork Twice'}, 'command_check_interval': {'doc': 'If you specify a number with an "s" appended to it (i.e. 30s), this is the number of seconds to wait between external command checks. If you leave off the "s", this is the number of "time units" to wait between external command checks. Unless you\'ve changed the interval_length value (as defined below) from the default value of 60, this number will mean minutes. Note: By setting this value to -1, Nagios will check for external commands as often as possible. Each time Nagios checks for external commands it will read and process all commands present in the command file before continuing on with its other duties. More information on external commands can be found here. ', 'examples': ['command_check_interval=1'], 'format': 'command_check_interval=<xxx>[s]', 'options': [], 'title': 'External Command Check Interval'}, 'command_file': {'doc': 'This is the file that Nagios will check for external commands to process. The command CGI writes commands to this file. The external command file is implemented as a named pipe (FIFO), which is created when Nagios starts and removed when it shuts down. If the file exists when Nagios starts, the Nagios process will terminate with an error message. More information on external commands can be found here. ', 'examples': ['command_file=/usr/local/nagios/var/rw/nagios.cmd'], 'format': 'command_file=<file_name>', 'options': [], 'title': 'External Command File'}, 'date_format': {'doc': 'This option allows you to specify what kind of date/time format Nagios should use in the web interface and date/time macros. Possible options (along with example output) include: ', 'examples': ['date_format=us'], 'format': 'date_format=<option>', 'options': [], 'title': 'Date Format'}, 'debug_file': {'doc': 'This option determines where Nagios should write debugging information. What (if any) information is written is determined by the debug_level and debug_verbosity options. You can have Nagios automaticaly rotate the debug file when it reaches a certain size by using the max_debug_file_size option. ', 'examples': ['debug_file=/usr/local/nagios/var/nagios.debug'], 'format': 'debug_file=<file_name>', 'options': [], 'title': 'Debug File'}, 'debug_level': {'doc': 'This option determines what type of information Nagios should write to the debug_file. This value is a logical OR of the values below. ', 'examples': ['debug_level=24'], 'format': 'debug_level=<#>', 'options': ['-1 = Log everything', '0 = Log nothing (default)', '1 = Function enter/exit information', '2 = Config information', '4 = Process information', '8 = Scheduled event information', '16 = Host/service check information', '32 = Notification information', '64 = Event broker information'], 'title': 'Debug Level'}, 'debug_verbosity': {'doc': 'This option determines how much debugging information Nagios should write to the debug_file. ', 'examples': ['debug_verbosity=1'], 'format': 'debug_verbosity=<#>', 'options': ['0 = Basic information', '1 = More detailed information (default)', '2 = Highly detailed information'], 'title': 'Debug Verbosity'}, 'enable_embedded_perl': {'doc': 'This setting determines whether or not the embedded Perl interpreter is enabled on a program-wide basis. Nagios must be compiled with support for embedded Perl for this option to have an effect. More information on the embedded Perl interpreter can be found here. ', 'examples': ['enable_embedded_perl=1'], 'format': 'enable_embedded_perl=<0/1>', 'options': [], 'title': 'Embedded Perl Interpreter Option'}, 'enable_environment_macros': {'doc': 'This option determines whether or not the Nagios daemon will make all standard macros available as environment variables to your check, notification, event hander, etc. commands. In large Nagios installations this can be problematic because it takes additional memory and (more importantly) CPU to compute the values of all macros and make them available to the environment. ', 'examples': ['enable_environment_macros=0'], 'format': 'enable_environment_macros=<0/1>', 'options': ["0 = Don't make macros available as environment variables", '1 = Make macros available as environment variables (default)'], 'title': 'Environment Macros Option'}, 'enable_event_handlers': {'doc': 'This option determines whether or not Nagios will run event handlers when it initially (re)starts. If this option is disabled, Nagios will not run any host or service event handlers. Note: If you have state retention enabled, Nagios will ignore this setting when it (re)starts and use the last known setting for this option (as stored in the state retention file), unless you disable the use_retained_program_state option. If you want to change this option when state retention is active (and the use_retained_program_state is enabled), you\'ll have to use the appropriate external command or change it via the web interface. Values are as follows: ', 'examples': ['enable_event_handlers=1'], 'format': 'enable_event_handlers=<0/1>', 'options': ['0 = Disable event handlers', '1 = Enable event handlers (default)'], 'title': 'Event Handler Option'}, 'enable_flap_detection': {'doc': 'This option determines whether or not Nagios will try and detect hosts and services that are "flapping". Flapping occurs when a host or service changes between states too frequently, resulting in a barrage of notifications being sent out. When Nagios detects that a host or service is flapping, it will temporarily suppress notifications for that host/service until it stops flapping. Flap detection is very experimental at this point, so use this feature with caution! More information on how flap detection and handling works can be found here. Note: If you have state retention enabled, Nagios will ignore this setting when it (re)starts and use the last known setting for this option (as stored in the state retention file), unless you disable the use_retained_program_state option. If you want to change this option when state retention is active (and the use_retained_program_state is enabled), you\'ll have to use the appropriate external command or change it via the web interface. ', 'examples': ['enable_flap_detection=0'], 'format': 'enable_flap_detection=<0/1>', 'options': ["0 = Don't enable flap detection (default)", '1 = Enable flap detection'], 'title': 'Flap Detection Option'}, 'enable_notifications': {'doc': 'This option determines whether or not Nagios will send out notifications when it initially (re)starts. If this option is disabled, Nagios will not send out notifications for any host or service. Note: If you have state retention enabled, Nagios will ignore this setting when it (re)starts and use the last known setting for this option (as stored in the state retention file), unless you disable the use_retained_program_state option. If you want to change this option when state retention is active (and the use_retained_program_state is enabled), you\'ll have to use the appropriate external command or change it via the web interface. Values are as follows: ', 'examples': ['enable_notifications=1'], 'format': 'enable_notifications=<0/1>', 'options': ['0 = Disable notifications', '1 = Enable notifications (default)'], 'title': 'Notifications Option'}, 'enable_predictive_host_dependency_checks': {'doc': 'This option determines whether or not Nagios will execute predictive checks of hosts that are being depended upon (as defined in host dependencies) for a particular host when it changes state. Predictive checks help ensure that the dependency logic is as accurate as possible. More information on how predictive checks work can be found here. ', 'examples': ['enable_predictive_host_dependency_checks=1'], 'format': 'enable_predictive_host_dependency_checks=<0/1>', 'options': ['0 = Disable predictive checks', '1 = Enable predictive checks (default)'], 'title': 'Predictive Host Dependency Checks Option'}, 'enable_predictive_service_dependency_checks': {'doc': 'This option determines whether or not Nagios will execute predictive checks of services that are being depended upon (as defined in service dependencies) for a particular service when it changes state. Predictive checks help ensure that the dependency logic is as accurate as possible. More information on how predictive checks work can be found here. ', 'examples': ['enable_predictive_service_dependency_checks=1'], 'format': 'enable_predictive_service_dependency_checks=<0/1>', 'options': ['0 = Disable predictive checks', '1 = Enable predictive checks (default)'], 'title': 'Predictive Service Dependency Checks Option'}, 'event_broker_options': {'doc': 'This option controls what (if any) data gets sent to the event broker and, in turn, to any loaded event broker modules. This is an advanced option. When in doubt, either broker nothing (if not using event broker modules) or broker everything (if using event broker modules). Possible values are shown below. ', 'examples': ['event_broker_options=-1'], 'format': 'event_broker_options=<#>', 'options': ['0 = Broker nothing', '-1 = Broker everything', "# = See BROKER_* definitions in source code (include/broker.h) for other values that can be OR'ed together"], 'title': 'Event Broker Options'}, 'event_handler_timeout': {'doc': 'This is the maximum number of seconds that Nagios will allow event handlers to be run. If an event handler exceeds this time limit it will be killed and a warning will be logged. There is often widespread confusion as to what this option really does. It is meant to be used as a last ditch mechanism to kill off commands which are misbehaving and not exiting in a timely manner. It should be set to something high (like 60 seconds or more), so that each event handler command normally finishes executing within this time limit. If an event handler runs longer than this limit, Nagios will kill it off thinking it is a runaway processes. ', 'examples': ['event_handler_timeout=60'], 'format': 'event_handler_timeout=<seconds>', 'options': [], 'title': 'Event Handler Timeout'}, 'execute_host_checks': {'doc': 'This option determines whether or not Nagios will execute on-demand and regularly scheduled host checks when it initially (re)starts. If this option is disabled, Nagios will not actively execute any host checks, although it can still accept passive host checks unless you\'ve disabled them). This option is most often used when configuring backup monitoring servers, as described in the documentation on redundancy, or when setting up a distributed monitoring environment. Note: If you have state retention enabled, Nagios will ignore this setting when it (re)starts and use the last known setting for this option (as stored in the state retention file), unless you disable the use_retained_program_state option. If you want to change this option when state retention is active (and the use_retained_program_state is enabled), you\'ll have to use the appropriate external command or change it via the web interface. Values are as follows: ', 'examples': ['execute_host_checks=1'], 'format': 'execute_host_checks=<0/1>', 'options': ["0 = Don't execute host checks", '1 = Execute host checks (default)'], 'title': 'Host Check Execution Option'}, 'execute_service_checks': {'doc': 'This option determines whether or not Nagios will execute service checks when it initially (re)starts. If this option is disabled, Nagios will not actively execute any service checks and will remain in a sort of "sleep" mode (it can still accept passive checks unless you\'ve disabled them). This option is most often used when configuring backup monitoring servers, as described in the documentation on redundancy, or when setting up a distributed monitoring environment. Note: If you have state retention enabled, Nagios will ignore this setting when it (re)starts and use the last known setting for this option (as stored in the state retention file), unless you disable the use_retained_program_state option. If you want to change this option when state retention is active (and the use_retained_program_state is enabled), you\'ll have to use the appropriate external command or change it via the web interface. Values are as follows: ', 'examples': ['execute_service_checks=1'], 'format': 'execute_service_checks=<0/1>', 'options': ["0 = Don't execute service checks", '1 = Execute service checks (default)'], 'title': 'Service Check Execution Option'}, 'external_command_buffer_slots': {'doc': 'Note: This is an advanced feature. This option determines how many buffer slots Nagios will reserve for caching external commands that have been read from the external command file by a worker thread, but have not yet been processed by the main thread of the Nagios deamon. Each slot can hold one external command, so this option essentially determines how many commands can be buffered. For installations where you process a large number of passive checks (e.g. distributed setups), you may need to increase this number. You should consider using MRTG to graph Nagios\' usage of external command buffers. You can read more on how to configure graphing here. ', 'examples': ['external_command_buffer_slots=512'], 'format': 'external_command_buffer_slots=<#>', 'options': [], 'title': 'External Command Buffer Slots'}, 'free_child_process_memory': {'doc': 'This option determines whether or not Nagios will free memory in child processes when they are fork()ed off from the main process. By default, Nagios frees memory. However, if the use_large_installation_tweaks option is enabled, it will not. By defining this option in your configuration file, you are able to override things to get the behavior you want. ', 'examples': ['free_child_process_memory=0'], 'format': 'free_child_process_memory=<0/1>', 'options': ["0 = Don't free memory", '1 = Free memory'], 'title': 'Child Process Memory Option'}, 'global_host_event_handler': {'doc': 'This option allows you to specify a host event handler command that is to be run for every host state change. The global event handler is executed immediately prior to the event handler that you have optionally specified in each host definition. The command argument is the short name of a command that you define in your object configuration file. The maximum amount of time that this command can run is controlled by the event_handler_timeout option. More information on event handlers can be found here. ', 'examples': ['global_host_event_handler=log-host-event-to-db'], 'format': 'global_host_event_handler=<command>', 'options': [], 'title': 'Global Host Event Handler Option'}, 'global_service_event_handler': {'doc': 'This option allows you to specify a service event handler command that is to be run for every service state change. The global event handler is executed immediately prior to the event handler that you have optionally specified in each service definition. The command argument is the short name of a command that you define in your object configuration file. The maximum amount of time that this command can run is controlled by the event_handler_timeout option. More information on event handlers can be found here. ', 'examples': ['global_service_event_handler=log-service-event-to-db'], 'format': 'global_service_event_handler=<command>', 'options': [], 'title': 'Global Service Event Handler Option'}, 'high_host_flap_threshold': {'doc': 'This option is used to set the high threshold for detection of host flapping. For more information on how flap detection and handling works (and how this option affects things) read this. ', 'examples': ['high_host_flap_threshold=50.0'], 'format': 'high_host_flap_threshold=<percent>', 'options': [], 'title': 'High Host Flap Threshold'}, 'high_service_flap_threshold': {'doc': 'This option is used to set the high threshold for detection of service flapping. For more information on how flap detection and handling works (and how this option affects things) read this. ', 'examples': ['high_service_flap_threshold=50.0'], 'format': 'high_service_flap_threshold=<percent>', 'options': [], 'title': 'High Service Flap Threshold'}, 'host_check_timeout': {'doc': 'This is the maximum number of seconds that Nagios will allow host checks to run. If checks exceed this limit, they are killed and a CRITICAL state is returned and the host will be assumed to be DOWN. A timeout error will also be logged. There is often widespread confusion as to what this option really does. It is meant to be used as a last ditch mechanism to kill off plugins which are misbehaving and not exiting in a timely manner. It should be set to something high (like 60 seconds or more), so that each host check normally finishes executing within this time limit. If a host check runs longer than this limit, Nagios will kill it off thinking it is a runaway processes. ', 'examples': ['host_check_timeout=60'], 'format': 'host_check_timeout=<seconds>', 'options': [], 'title': 'Host Check Timeout'}, 'host_freshness_check_interval': {'doc': 'This setting determines how often (in seconds) Nagios will periodically check the "freshness" of host check results. If you have disabled host freshness checking (with the check_host_freshness option), this option has no effect. More information on freshness checking can be found here. ', 'examples': ['host_freshness_check_interval=60'], 'format': 'host_freshness_check_interval=<seconds>', 'options': [], 'title': 'Host Freshness Check Interval'}, 'host_inter_check_delay_method': {'doc': 'This option allows you to control how host checks that are scheduled to be checked on a regular basis are initially "spread out" in the event queue. Using a "smart" delay calculation (the default) will cause Nagios to calculate an average check interval and spread initial checks of all hosts out over that interval, thereby helping to eliminate CPU load spikes. Using no delay is generally not recommended. Using no delay will cause all host checks to be scheduled for execution at the same time. More information on how to estimate how the inter-check delay affects host check scheduling can be found here.Values are as follows: ', 'examples': ['host_inter_check_delay_method=s'], 'format': 'host_inter_check_delay_method=<n/d/s/x.xx>', 'options': ["n = Don't use any delay - schedule all host checks to run immediately (i.e. at the same time!)", 'd = Use a "dumb" delay of 1 second between host checks', 's = Use a "smart" delay calculation to spread host checks out evenly (default)', 'x.xx = Use a user-supplied inter-check delay of x.xx seconds'], 'title': 'Host Inter-Check Delay Method'}, 'host_perfdata_command': {'doc': 'This option allows you to specify a command to be run after every host check to process host performance data that may be returned from the check. The command argument is the short name of a command definition that you define in your object configuration file. This command is only executed if the process_performance_data option is enabled globally and if the process_perf_data directive in the host definition is enabled. ', 'examples': ['host_perfdata_command=process-host-perfdata'], 'format': 'host_perfdata_command=<command>', 'options': [], 'title': 'Host Performance Data Processing Command'}, 'host_perfdata_file': {'doc': 'This option allows you to specify a file to which host performance data will be written after every host check. Data will be written to the performance file as specified by the host_perfdata_file_template option. Performance data is only written to this file if the process_performance_data option is enabled globally and if the process_perf_data directive in the host definition is enabled. ', 'examples': ['host_perfdata_file=/usr/local/nagios/var/host-perfdata.dat'], 'format': 'host_perfdata_file=<file_name>', 'options': [], 'title': 'Host Performance Data File'}, 'host_perfdata_file_mode': {'doc': 'This option determines how the host performance data file is opened. Unless the file is a named pipe you\'ll probably want to use the default mode of append. ', 'examples': ['host_perfdata_file_mode=a'], 'format': 'host_perfdata_file_mode=<mode>', 'options': ['a = Open file in append mode (default)', 'w = Open file in write mode', 'p = Open in non-blocking read/write mode (useful when writing to pipes)'], 'title': 'Host Performance Data File Mode'}, 'host_perfdata_file_processing_command': {'doc': 'This option allows you to specify the command that should be executed to process the host performance data file. The command argument is the short name of a command definition that you define in your object configuration file. The interval at which this command is executed is determined by the host_perfdata_file_processing_interval directive. ', 'examples': ['host_perfdata_file_processing_command=process-host-perfdata-file'], 'format': 'host_perfdata_file_processing_command=<command>', 'options': [], 'title': 'Host Performance Data File Processing Command'}, 'host_perfdata_file_processing_interval': {'doc': 'This option allows you to specify the interval (in seconds) at which the host performance data file is processed using the host performance data file processing command. A value of 0 indicates that the performance data file should not be processed at regular intervals. ', 'examples': ['host_perfdata_file_processing_interval=0'], 'format': 'host_perfdata_file_processing_interval=<seconds>', 'options': [], 'title': 'Host Performance Data File Processing Interval'}, 'host_perfdata_file_template': {'doc': 'This option determines what (and how) data is written to the host performance data file. The template may contain macros, special characters (\\t for tab, \\r for carriage return, \\n for newline) and plain text. A newline is automatically added after each write to the performance data file. ', 'examples': ['host_perfdata_file_template=[HOSTPERFDATA]\\t$TIMET$\\t$HOSTNAME$\\t$HOSTEXECUTIONTIME$\\t$HOSTOUTPUT$\\t$HOSTPERFDATA$'], 'format': 'host_perfdata_file_template=<template>', 'options': [], 'title': 'Host Performance Data File Template'}, 'illegal_macro_output_chars': {'doc': 'This option allows you to specify illegal characters that should be stripped from macros before being used in notifications, event handlers, and other commands. This DOES NOT affect macros used in service or host check commands. You can choose to not strip out the characters shown in the example above, but I recommend you do not do this. Some of these characters are interpreted by the shell (i.e. the backtick) and can lead to security problems. The following macros are stripped of the characters you specify: $HOSTOUTPUT$, $HOSTPERFDATA$, $HOSTACKAUTHOR$, $HOSTACKCOMMENT$, $SERVICEOUTPUT$, $SERVICEPERFDATA$, $SERVICEACKAUTHOR$, and $SERVICEACKCOMMENT$ ', 'examples': ['illegal_macro_output_chars=`~$^&"|\'<>'], 'format': 'illegal_macro_output_chars=<chars...>', 'options': [], 'title': 'Illegal Macro Output Characters'}, 'illegal_object_name_chars': {'doc': 'This option allows you to specify illegal characters that cannot be used in host names, service descriptions, or names of other object types. Nagios will allow you to use most characters in object definitions, but I recommend not using the characters shown in the example above. Doing may give you problems in the web interface, notification commands, etc. ', 'examples': ['illegal_object_name_chars=`~!$%^&*"|\'<>?,()='], 'format': 'illegal_object_name_chars=<chars...>', 'options': [], 'title': 'Illegal Object Name Characters'}, 'interval_length': {'doc': 'This is the number of seconds per "unit interval" used for timing in the scheduling queue, re-notifications, etc. "Units intervals" are used in the object configuration file to determine how often to run a service check, how often to re-notify a contact, etc. Important: The default value for this is set to 60, which means that a "unit value" of 1 in the object configuration file will mean 60 seconds (1 minute). I have not really tested other values for this variable, so proceed at your own risk if you decide to do so! ', 'examples': ['interval_length=60'], 'format': 'interval_length=<seconds>', 'options': [], 'title': 'Timing Interval Length'}, 'lock_file': {'doc': 'This option specifies the location of the lock file that Nagios should create when it runs as a daemon (when started with the -d command line argument). This file contains the process id (PID) number of the running Nagios process. ', 'examples': ['lock_file=/tmp/nagios.lock'], 'format': 'lock_file=<file_name>', 'options': [], 'title': 'Lock File'}, 'log_archive_path': {'doc': 'This is the directory where Nagios should place log files that have been rotated. This option is ignored if you choose to not use the log rotation functionality. ', 'examples': ['log_archive_path=/usr/local/nagios/var/archives/'], 'format': 'log_archive_path=<path>', 'options': [], 'title': 'Log Archive Path'}, 'log_event_handlers': {'doc': 'This variable determines whether or not service and host event handlers are logged. Event handlers are optional commands that can be run whenever a service or hosts changes state. Logging event handlers is most useful when debugging Nagios or first trying out your event handler scripts. ', 'examples': ['log_event_handlers=1'], 'format': 'log_event_handlers=<0/1>', 'options': ["0 = Don't log event handlers", '1 = Log event handlers'], 'title': 'Event Handler Logging Option'}, 'log_external_commands': {'doc': 'This variable determines whether or not Nagios will log external commands that it receives from the external command file. Note: This option does not control whether or not passive service checks (which are a type of external command) get logged. To enable or disable logging of passive checks, use the log_passive_checks option. ', 'examples': ['log_external_commands=1'], 'format': 'log_external_commands=<0/1>', 'options': ["0 = Don't log external commands", '1 = Log external commands (default)'], 'title': 'External Command Logging Option'}, 'log_file': {'doc': 'This variable specifies where Nagios should create its main log file. This should be the first variable that you define in your configuration file, as Nagios will try to write errors that it finds in the rest of your configuration data to this file. If you have log rotation enabled, this file will automatically be rotated every hour, day, week, or month. ', 'examples': ['log_file=/usr/local/nagios/var/nagios.log'], 'format': 'log_file=<file_name>', 'options': [], 'title': 'Log File'}, 'log_host_retries': {'doc': 'This variable determines whether or not host check retries are logged. Logging host check retries is mostly useful when attempting to debug Nagios or test out host event handlers. ', 'examples': ['log_host_retries=1'], 'format': 'log_host_retries=<0/1>', 'options': ["0 = Don't log host check retries", '1 = Log host check retries'], 'title': 'Host Check Retry Logging Option'}, 'log_initial_states': {'doc': 'This variable determines whether or not Nagios will force all initial host and service states to be logged, even if they result in an OK state. Initial service and host states are normally only logged when there is a problem on the first check. Enabling this option is useful if you are using an application that scans the log file to determine long-term state statistics for services and hosts. ', 'examples': ['log_initial_states=1'], 'format': 'log_initial_states=<0/1>', 'options': ["0 = Don't log initial states (default)", '1 = Log initial states'], 'title': 'Initial States Logging Option'}, 'log_notifications': {'doc': 'This variable determines whether or not notification messages are logged. If you have a lot of contacts or regular service failures your log file will grow relatively quickly. Use this option to keep contact notifications from being logged. ', 'examples': ['log_notifications=1'], 'format': 'log_notifications=<0/1>', 'options': ["0 = Don't log notifications", '1 = Log notifications'], 'title': 'Notification Logging Option'}, 'log_passive_checks': {'doc': 'This variable determines whether or not Nagios will log passive host and service checks that it receives from the external command file. If you are setting up a distributed monitoring environment or plan on handling a large number of passive checks on a regular basis, you may wish to disable this option so your log file doesn\'t get too large. ', 'examples': ['log_passive_checks=1'], 'format': 'log_passive_checks=<0/1>', 'options': ["0 = Don't log passive checks", '1 = Log passive checks (default)'], 'title': 'Passive Check Logging Option'}, 'log_rotation_method': {'doc': 'This is the rotation method that you would like Nagios to use for your log file. Values are as follows: ', 'examples': ['log_rotation_method=d'], 'format': 'log_rotation_method=<n/h/d/w/m>', 'options': ["n = None (don't rotate the log - this is the default)", 'h = Hourly (rotate the log at the top of each hour)', 'd = Daily (rotate the log at midnight each day)', 'w = Weekly (rotate the log at midnight on Saturday)', 'm = Monthly (rotate the log at midnight on the last day of the month)'], 'title': 'Log Rotation Method'}, 'log_service_retries': {'doc': 'This variable determines whether or not service check retries are logged. Service check retries occur when a service check results in a non-OK state, but you have configured Nagios to retry the service more than once before responding to the error. Services in this situation are considered to be in "soft" states. Logging service check retries is mostly useful when attempting to debug Nagios or test out service event handlers. ', 'examples': ['log_service_retries=1'], 'format': 'log_service_retries=<0/1>', 'options': ["0 = Don't log service check retries", '1 = Log service check retries'], 'title': 'Service Check Retry Logging Option'}, 'low_host_flap_threshold': {'doc': 'This option is used to set the low threshold for detection of host flapping. For more information on how flap detection and handling works (and how this option affects things) read this. ', 'examples': ['low_host_flap_threshold=25.0'], 'format': 'low_host_flap_threshold=<percent>', 'options': [], 'title': 'Low Host Flap Threshold'}, 'low_service_flap_threshold': {'doc': 'This option is used to set the low threshold for detection of service flapping. For more information on how flap detection and handling works (and how this option affects things) read this. ', 'examples': ['low_service_flap_threshold=25.0'], 'format': 'low_service_flap_threshold=<percent>', 'options': [], 'title': 'Low Service Flap Threshold'}, 'max_check_result_file_age': {'doc': 'This options determines the maximum age in seconds that Nagios will consider check result files found in the check_result_path directory to be valid. Check result files that are older that this threshold will be deleted by Nagios and the check results they contain will not be processed. By using a value of zero (0) with this option, Nagios will process all check result files - even if they\'re older than your hardware :-). ', 'examples': ['max_check_result_file_age=3600'], 'format': 'max_check_result_file_age=<seconds>', 'options': [], 'title': 'Max Check Result File Age'}, 'max_check_result_reaper_time': {'doc': 'This option allows you to control the maximum amount of time in seconds that host and service check result "reaper" events are allowed to run. "Reaper" events process the results from host and service checks that have finished executing. If there are a lot of results to process, reaper events may take a long time to finish, which might delay timely execution of new host and service checks. This variable allows you to limit the amount of time that an individual reaper event will run before it hands control back over to Nagios for other portions of the monitoring logic. ', 'examples': ['max_check_result_reaper_time=30'], 'format': 'max_check_result_reaper_time=<seconds>', 'options': [], 'title': 'Maximum Check Result Reaper Time'}, 'max_concurrent_checks': {'doc': 'This option allows you to specify the maximum number of service checks that can be run in parallel at any given time. Specifying a value of 1 for this variable essentially prevents any service checks from being run in parallel. Specifying a value of 0 (the default) does not place any restrictions on the number of concurrent checks. You\'ll have to modify this value based on the system resources you have available on the machine that runs Nagios, as it directly affects the maximum load that will be imposed on the system (processor utilization, memory, etc.). More information on how to estimate how many concurrent checks you should allow can be found here. ', 'examples': ['max_concurrent_checks=20'], 'format': 'max_concurrent_checks=<max_checks>', 'options': [], 'title': 'Maximum Concurrent Service Checks'}, 'max_debug_file_size': {'doc': 'This option determines the maximum size (in bytes) of the debug file. If the file grows larger than this size, it will be renamed with a .old extension. If a file already exists with a .old extension it will automatically be deleted. This helps ensure your disk space usage doesn\'t get out of control when debugging Nagios. ', 'examples': ['max_debug_file_size=1000000'], 'format': 'max_debug_file_size=<#>', 'options': [], 'title': 'Maximum Debug File Size'}, 'max_host_check_spread': {'doc': 'This option determines the maximum number of minutes from when Nagios starts that all hosts (that are scheduled to be regularly checked) are checked. This option will automatically adjust the host inter-check delay method (if necessary) to ensure that the initial checks of all hosts occur within the timeframe you specify. In general, this option will not have an affect on host check scheduling if scheduling information is being retained using the use_retained_scheduling_info option. Default value is 30 (minutes). ', 'examples': ['max_host_check_spread=30'], 'format': 'max_host_check_spread=<minutes>', 'options': [], 'title': 'Maximum Host Check Spread'}, 'max_service_check_spread': {'doc': 'This option determines the maximum number of minutes from when Nagios starts that all services (that are scheduled to be regularly checked) are checked. This option will automatically adjust the service inter-check delay method (if necessary) to ensure that the initial checks of all services occur within the timeframe you specify. In general, this option will not have an affect on service check scheduling if scheduling information is being retained using the use_retained_scheduling_info option. Default value is 30 (minutes). ', 'examples': ['max_service_check_spread=30'], 'format': 'max_service_check_spread=<minutes>', 'options': [], 'title': 'Maximum Service Check Spread'}, 'nagios_group': {'doc': 'This is used to set the effective group that the Nagios process should run as. After initial program startup and before starting to monitor anything, Nagios will drop its effective privileges and run as this group. You may specify either a groupname or a GID. ', 'examples': ['nagios_group=nagios'], 'format': 'nagios_group=<groupname/GID>', 'options': [], 'title': 'Nagios Group'}, 'nagios_user': {'doc': 'This is used to set the effective user that the Nagios process should run as. After initial program startup and before starting to monitor anything, Nagios will drop its effective privileges and run as this user. You may specify either a username or a UID. ', 'examples': ['nagios_user=nagios'], 'format': 'nagios_user=<username/UID>', 'options': [], 'title': 'Nagios User'}, 'notification_timeout': {'doc': 'This is the maximum number of seconds that Nagios will allow notification commands to be run. If a notification command exceeds this time limit it will be killed and a warning will be logged. There is often widespread confusion as to what this option really does. It is meant to be used as a last ditch mechanism to kill off commands which are misbehaving and not exiting in a timely manner. It should be set to something high (like 60 seconds or more), so that each notification command finishes executing within this time limit. If a notification command runs longer than this limit, Nagios will kill it off thinking it is a runaway processes. ', 'examples': ['notification_timeout=60'], 'format': 'notification_timeout=<seconds>', 'options': [], 'title': 'Notification Timeout'}, 'object_cache_file': {'doc': 'This directive is used to specify a file in which a cached copy of object definitions should be stored. The cache file is (re)created every time Nagios is (re)started and is used by the CGIs. It is intended to speed up config file caching in the CGIs and allow you to edit the source object config files while Nagios is running without affecting the output displayed in the CGIs. ', 'examples': ['object_cache_file=/usr/local/nagios/var/objects.cache'], 'format': 'object_cache_file=<file_name>', 'options': [], 'title': 'Object Cache File'}, 'obsess_over_hosts': {'doc': 'This value determines whether or not Nagios will "obsess" over host checks results and run the obsessive compulsive host processor command you define. I know - funny name, but it was all I could think of. This option is useful for performing distributed monitoring. If you\'re not doing distributed monitoring, don\'t enable this option. ', 'examples': ['obsess_over_hosts=1'], 'format': 'obsess_over_hosts=<0/1>', 'options': ["0 = Don't obsess over hosts (default)", '1 = Obsess over hosts'], 'title': 'Obsess Over Hosts Option'}, 'obsess_over_services': {'doc': 'This value determines whether or not Nagios will "obsess" over service checks results and run the obsessive compulsive service processor command you define. I know - funny name, but it was all I could think of. This option is useful for performing distributed monitoring. If you\'re not doing distributed monitoring, don\'t enable this option. ', 'examples': ['obsess_over_services=1'], 'format': 'obsess_over_services=<0/1>', 'options': ["0 = Don't obsess over services (default)", '1 = Obsess over services'], 'title': 'Obsess Over Services Option'}, 'ochp_command': {'doc': 'This option allows you to specify a command to be run after every host check, which can be useful in distributed monitoring. This command is executed after any event handler or notification commands. The command argument is the short name of a command definition that you define in your object configuration file. The maximum amount of time that this command can run is controlled by the ochp_timeout option. More information on distributed monitoring can be found here. This command is only executed if the obsess_over_hosts option is enabled globally and if the obsess_over_host directive in the host definition is enabled. ', 'examples': ['ochp_command=obsessive_host_handler'], 'format': 'ochp_command=<command>', 'options': [], 'title': 'Obsessive Compulsive Host Processor Command'}, 'ochp_timeout': {'doc': 'This is the maximum number of seconds that Nagios will allow an obsessive compulsive host processor command to be run. If a command exceeds this time limit it will be killed and a warning will be logged. ', 'examples': ['ochp_timeout=5'], 'format': 'ochp_timeout=<seconds>', 'options': [], 'title': 'Obsessive Compulsive Host Processor Timeout'}, 'ocsp_command': {'doc': 'This option allows you to specify a command to be run after every service check, which can be useful in distributed monitoring. This command is executed after any event handler or notification commands. The command argument is the short name of a command definition that you define in your object configuration file. The maximum amount of time that this command can run is controlled by the ocsp_timeout option. More information on distributed monitoring can be found here. This command is only executed if the obsess_over_services option is enabled globally and if the obsess_over_service directive in the service definition is enabled. ', 'examples': ['ocsp_command=obsessive_service_handler'], 'format': 'ocsp_command=<command>', 'options': [], 'title': 'Obsessive Compulsive Service Processor Command'}, 'ocsp_timeout': {'doc': 'This is the maximum number of seconds that Nagios will allow an obsessive compulsive service processor command to be run. If a command exceeds this time limit it will be killed and a warning will be logged. ', 'examples': ['ocsp_timeout=5'], 'format': 'ocsp_timeout=<seconds>', 'options': [], 'title': 'Obsessive Compulsive Service Processor Timeout'}, 'passive_host_checks_are_soft': {'doc': 'This option determines whether or not Nagios will treat passive host checks as HARD states or SOFT states. By default, a passive host check result will put a host into a HARD state type. You can change this behavior by enabling this option. ', 'examples': ['passive_host_checks_are_soft=1'], 'format': 'passive_host_checks_are_soft=<0/1>', 'options': ['0 = Passive host checks are HARD (default)', '1 = Passive host checks are SOFT'], 'title': 'Passive Host Checks Are SOFT Option'}, 'perfdata_timeout': {'doc': 'This is the maximum number of seconds that Nagios will allow a host performance data processor command or service performance data processor command to be run. If a command exceeds this time limit it will be killed and a warning will be logged. ', 'examples': ['perfdata_timeout=5'], 'format': 'perfdata_timeout=<seconds>', 'options': [], 'title': 'Performance Data Processor Command Timeout'}, 'precached_object_file': {'doc': 'This directive is used to specify a file in which a pre-processed, pre-cached copy of object definitions should be stored. This file can be used to drastically improve startup times in large/complex Nagios installations. Read more information on how to speed up start times here. ', 'examples': ['precached_object_file=/usr/local/nagios/var/objects.precache'], 'format': 'precached_object_file=<file_name>', 'options': [], 'title': 'Precached Object File'}, 'process_performance_data': {'doc': 'This value determines whether or not Nagios will process host and service check performance data. ', 'examples': ['process_performance_data=1'], 'format': 'process_performance_data=<0/1>', 'options': ["0 = Don't process performance data (default)", '1 = Process performance data'], 'title': 'Performance Data Processing Option'}, 'resource_file': {'doc': 'This is used to specify an optional resource file that can contain $USERn$ macro definitions. $USERn$ macros are useful for storing usernames, passwords, and items commonly used in command definitions (like directory paths). The CGIs will not attempt to read resource files, so you can set restrictive permissions (600 or 660) on them to protect sensitive information. You can include multiple resource files by adding multiple resource_file statements to the main config file - Nagios will process them all. See the sample resource.cfg file in the sample-config/ subdirectory of the Nagios distribution for an example of how to define $USERn$ macros. ', 'examples': ['resource_file=/usr/local/nagios/etc/resource.cfg'], 'format': 'resource_file=<file_name>', 'options': [], 'title': 'Resource File'}, 'retain_state_information': {'doc': 'This option determines whether or not Nagios will retain state information for hosts and services between program restarts. If you enable this option, you should supply a value for the state_retention_file variable. When enabled, Nagios will save all state information for hosts and service before it shuts down (or restarts) and will read in previously saved state information when it starts up again. ', 'examples': ['retain_state_information=1'], 'format': 'retain_state_information=<0/1>', 'options': ["0 = Don't retain state information", '1 = Retain state information (default)'], 'title': 'State Retention Option'}, 'retained_contact_host_attribute_mask': {'doc': '', 'examples': [], 'format': '', 'options': [], 'title': ''}, 'retained_contact_service_attribute_mask': {'doc': 'WARNING: This is an advanced feature. You\'ll need to read the Nagios source code to use this option effectively. These options determine which contact attributes are NOT retained across program restarts. There are two masks because there are often separate host and service contact attributes that can be changed. The values for these options are a bitwise AND of values specified by the "MODATTR_" definitions in the include/common.h source code file. By default, all process attributes are retained. ', 'examples': ['retained_contact_host_attribute_mask=0', 'retained_contact_service_attribute_mask=0'], 'format': '', 'options': [], 'title': 'Retained Contact Attribute Masks'}, 'retained_host_attribute_mask': {'doc': '', 'examples': [], 'format': '', 'options': [], 'title': ''}, 'retained_process_host_attribute_mask': {'doc': '', 'examples': [], 'format': '', 'options': [], 'title': ''}, 'retained_process_service_attribute_mask': {'doc': 'WARNING: This is an advanced feature. You\'ll need to read the Nagios source code to use this option effectively. These options determine which process attributes are NOT retained across program restarts. There are two masks because there are often separate host and service process attributes that can be changed. For example, host checks can be disabled at the program level, while service checks are still enabled. The values for these options are a bitwise AND of values specified by the "MODATTR_" definitions in the include/common.h source code file. By default, all process attributes are retained. ', 'examples': ['retained_process_host_attribute_mask=0', 'retained_process_service_attribute_mask=0'], 'format': '', 'options': [], 'title': 'Retained Process Attribute Masks'}, 'retained_service_attribute_mask': {'doc': 'WARNING: This is an advanced feature. You\'ll need to read the Nagios source code to use this option effectively. These options determine which host or service attributes are NOT retained across program restarts. The values for these options are a bitwise AND of values specified by the "MODATTR_" definitions in the include/common.h source code file. By default, all host and service attributes are retained. ', 'examples': ['retained_host_attribute_mask=0', 'retained_service_attribute_mask=0'], 'format': '', 'options': [], 'title': 'Retained Host and Service Attribute Masks'}, 'retention_update_interval': {'doc': 'This setting determines how often (in minutes) that Nagios will automatically save retention data during normal operation. If you set this value to 0, Nagios will not save retention data at regular intervals, but it will still save retention data before shutting down or restarting. If you have disabled state retention (with the retain_state_information option), this option has no effect. ', 'examples': ['retention_update_interval=60'], 'format': 'retention_update_interval=<minutes>', 'options': [], 'title': 'Automatic State Retention Update Interval'}, 'service_check_timeout': {'doc': 'This is the maximum number of seconds that Nagios will allow service checks to run. If checks exceed this limit, they are killed and a CRITICAL state is returned. A timeout error will also be logged. There is often widespread confusion as to what this option really does. It is meant to be used as a last ditch mechanism to kill off plugins which are misbehaving and not exiting in a timely manner. It should be set to something high (like 60 seconds or more), so that each service check normally finishes executing within this time limit. If a service check runs longer than this limit, Nagios will kill it off thinking it is a runaway processes. ', 'examples': ['service_check_timeout=60'], 'format': 'service_check_timeout=<seconds>', 'options': [], 'title': 'Service Check Timeout'}, 'service_freshness_check_interval': {'doc': 'This setting determines how often (in seconds) Nagios will periodically check the "freshness" of service check results. If you have disabled service freshness checking (with the check_service_freshness option), this option has no effect. More information on freshness checking can be found here. ', 'examples': ['service_freshness_check_interval=60'], 'format': 'service_freshness_check_interval=<seconds>', 'options': [], 'title': 'Service Freshness Check Interval'}, 'service_inter_check_delay_method': {'doc': 'This option allows you to control how service checks are initially "spread out" in the event queue. Using a "smart" delay calculation (the default) will cause Nagios to calculate an average check interval and spread initial checks of all services out over that interval, thereby helping to eliminate CPU load spikes. Using no delay is generally not recommended, as it will cause all service checks to be scheduled for execution at the same time. This means that you will generally have large CPU spikes when the services are all executed in parallel. More information on how to estimate how the inter-check delay affects service check scheduling can be found here. Values are as follows: ', 'examples': ['service_inter_check_delay_method=s'], 'format': 'service_inter_check_delay_method=<n/d/s/x.xx>', 'options': ["n = Don't use any delay - schedule all service checks to run immediately (i.e. at the same time!)", 'd = Use a "dumb" delay of 1 second between service checks', 's = Use a "smart" delay calculation to spread service checks out evenly (default)', 'x.xx = Use a user-supplied inter-check delay of x.xx seconds'], 'title': 'Service Inter-Check Delay Method'}, 'service_interleave_factor': {'doc': 'This variable determines how service checks are interleaved. Interleaving allows for a more even distribution of service checks, reduced load on remote hosts, and faster overall detection of host problems. Setting this value to 1 is equivalent to not interleaving the service checks (this is how versions of Nagios previous to 0.0.5 worked). Set this value to s (smart) for automatic calculation of the interleave factor unless you have a specific reason to change it. The best way to understand how interleaving works is to watch the status CGI (detailed view) when Nagios is just starting. You should see that the service check results are spread out as they begin to appear. More information on how interleaving works can be found here.
  • x = A number greater than or equal to 1 that specifies the interleave factor to use. An interleave factor of 1 is equivalent to not interleaving the service checks.
  • s = Use a "smart" interleave factor calculation (default)
', 'examples': ['service_interleave_factor=s'], 'format': 'service_interleave_factor=<s|x>', 'options': ['x = A number greater than or equal to 1 that specifies the interleave factor to use. An interleave factor of 1 is equivalent to not interleaving the service checks.', 's = Use a "smart" interleave factor calculation (default)'], 'title': 'Service Interleave Factor'}, 'service_perfdata_command': {'doc': 'This option allows you to specify a command to be run after every service check to process service performance data that may be returned from the check. The command argument is the short name of a command definition that you define in your object configuration file. This command is only executed if the process_performance_data option is enabled globally and if the process_perf_data directive in the service definition is enabled. ', 'examples': ['service_perfdata_command=process-service-perfdata'], 'format': 'service_perfdata_command=<command>', 'options': [], 'title': 'Service Performance Data Processing Command'}, 'service_perfdata_file': {'doc': 'This option allows you to specify a file to which service performance data will be written after every service check. Data will be written to the performance file as specified by the service_perfdata_file_template option. Performance data is only written to this file if the process_performance_data option is enabled globally and if the process_perf_data directive in the service definition is enabled. ', 'examples': ['service_perfdata_file=/usr/local/nagios/var/service-perfdata.dat'], 'format': 'service_perfdata_file=<file_name>', 'options': [], 'title': 'Service Performance Data File'}, 'service_perfdata_file_mode': {'doc': 'This option determines how the service performance data file is opened. Unless the file is a named pipe you\'ll probably want to use the default mode of append. ', 'examples': ['service_perfdata_file_mode=a'], 'format': 'service_perfdata_file_mode=<mode>', 'options': ['a = Open file in append mode (default)', 'w = Open file in write mode', 'p = Open in non-blocking read/write mode (useful when writing to pipes)'], 'title': 'Service Performance Data File Mode'}, 'service_perfdata_file_processing_command': {'doc': 'This option allows you to specify the command that should be executed to process the service performance data file. The command argument is the short name of a command definition that you define in your object configuration file. The interval at which this command is executed is determined by the service_perfdata_file_processing_interval directive. ', 'examples': ['service_perfdata_file_processing_command=process-service-perfdata-file'], 'format': 'service_perfdata_file_processing_command=<command>', 'options': [], 'title': 'Service Performance Data File Processing Command'}, 'service_perfdata_file_processing_interval': {'doc': 'This option allows you to specify the interval (in seconds) at which the service performance data file is processed using the service performance data file processing command. A value of 0 indicates that the performance data file should not be processed at regular intervals. ', 'examples': ['service_perfdata_file_processing_interval=0'], 'format': 'service_perfdata_file_processing_interval=<seconds>', 'options': [], 'title': 'Service Performance Data File Processing Interval'}, 'service_perfdata_file_template': {'doc': 'This option determines what (and how) data is written to the service performance data file. The template may contain macros, special characters (\\t for tab, \\r for carriage return, \\n for newline) and plain text. A newline is automatically added after each write to the performance data file. ', 'examples': ['service_perfdata_file_template=[SERVICEPERFDATA]\\t$TIMET$\\t$HOSTNAME$\\t$SERVICEDESC$\\t$SERVICEEXECUTIONTIME$\\t$SERVICELATENCY$\\t$SERVICEOUTPUT$\\t$SERVICEPERFDATA$'], 'format': 'service_perfdata_file_template=<template>', 'options': [], 'title': 'Service Performance Data File Template'}, 'sleep_time': {'doc': 'This is the number of seconds that Nagios will sleep before checking to see if the next service or host check in the scheduling queue should be executed. Note that Nagios will only sleep after it "catches up" with queued service checks that have fallen behind. ', 'examples': ['sleep_time=1'], 'format': 'sleep_time=<seconds>', 'options': [], 'title': 'Inter-Check Sleep Time'}, 'soft_state_dependencies': {'doc': 'This option determines whether or not Nagios will use soft state information when checking host and service dependencies. Normally Nagios will only use the latest hard host or service state when checking dependencies. If you want it to use the latest state (regardless of whether its a soft or hard state type), enable this option. ', 'examples': ['soft_state_dependencies=0'], 'format': 'soft_state_dependencies=<0/1>', 'options': ["0 = Don't use soft state dependencies (default)", '1 = Use soft state dependencies'], 'title': 'Soft State Dependencies Option'}, 'state_retention_file': {'doc': 'This is the file that Nagios will use for storing status, downtime, and comment information before it shuts down. When Nagios is restarted it will use the information stored in this file for setting the initial states of services and hosts before it starts monitoring anything. In order to make Nagios retain state information between program restarts, you must enable the retain_state_information option. ', 'examples': ['state_retention_file=/usr/local/nagios/var/retention.dat'], 'format': 'state_retention_file=<file_name>', 'options': [], 'title': 'State Retention File'}, 'status_file': {'doc': '', 'examples': [], 'format': '', 'options': [], 'title': ''}, 'status_log': {'doc': 'This is the file that Nagios uses to store the current status, comment, and downtime information. This file is used by the CGIs so that current monitoring status can be reported via a web interface. The CGIs must have read access to this file in order to function properly. This file is deleted every time Nagios stops and recreated when it starts. ', 'examples': ['status_file=/usr/local/nagios/var/status.dat'], 'format': 'status_file=<file_name>', 'options': [], 'title': 'Status File'}, 'status_update_interval': {'doc': 'This setting determines how often (in seconds) that Nagios will update status data in the status file. The minimum update interval is 1 second. ', 'examples': ['status_update_interval=15'], 'format': 'status_update_interval=<seconds>', 'options': [], 'title': 'Status File Update Interval'}, 'temp_file': {'doc': 'This is a temporary file that Nagios periodically creates to use when updating comment data, status data, etc. The file is deleted when it is no longer needed. ', 'examples': ['temp_file=/usr/local/nagios/var/nagios.tmp'], 'format': 'temp_file=<file_name>', 'options': [], 'title': 'Temp File'}, 'temp_path': {'doc': 'This is a directory that Nagios can use as scratch space for creating temporary files used during the monitoring process. You should run tmpwatch, or a similiar utility, on this directory occassionally to delete files older than 24 hours. ', 'examples': ['temp_path=/tmp'], 'format': 'temp_path=<dir_name>', 'options': [], 'title': 'Temp Path'}, 'translate_passive_host_checks': {'doc': 'This option determines whether or not Nagios will translate DOWN/UNREACHABLE passive host check results to their "correct" state from the viewpoint of the local Nagios instance. This can be very useful in distributed and failover monitoring installations. More information on passive check state translation can be found here. ', 'examples': ['translate_passive_host_checks=1'], 'format': 'translate_passive_host_checks=<0/1>', 'options': ['0 = Disable check translation (default)', '1 = Enable check translation'], 'title': 'Translate Passive Host Checks Option'}, 'use_aggressive_host_checking': {'doc': 'Nagios tries to be smart about how and when it checks the status of hosts. In general, disabling this option will allow Nagios to make some smarter decisions and check hosts a bit faster. Enabling this option will increase the amount of time required to check hosts, but may improve reliability a bit. Unless you have problems with Nagios not recognizing that a host recovered, I would suggest not enabling this option. ', 'examples': ['use_aggressive_host_checking=0'], 'format': 'use_aggressive_host_checking=<0/1>', 'options': ["0 = Don't use aggressive host checking (default)", '1 = Use aggressive host checking'], 'title': 'Aggressive Host Checking Option'}, 'use_agressive_host_checking': {'doc': '', 'examples': [], 'format': '', 'options': [], 'title': ''}, 'use_embedded_perl_implicitly': {'doc': 'This setting determines whether or not the embedded Perl interpreter should be used for Perl plugins/scripts that do not explicitly enable/disable it. Nagios must be compiled with support for embedded Perl for this option to have an effect. More information on the embedded Perl interpreter and the effect of this setting can be found here. ', 'examples': ['use_embedded_perl_implicitly=1'], 'format': 'use_embedded_perl_implicitly=<0/1>', 'options': [], 'title': 'Embedded Perl Implicit Use Option'}, 'use_large_installation_tweaks': {'doc': 'This option determines whether or not the Nagios daemon will take several shortcuts to improve performance. These shortcuts result in the loss of a few features, but larger installations will likely see a lot of benefit from doing so. More information on what optimizations are taken when you enable this option can be found here. ', 'examples': ['use_large_installation_tweaks=0'], 'format': 'use_large_installation_tweaks=<0/1>', 'options': ["0 = Don't use tweaks (default)", '1 = Use tweaks'], 'title': 'Large Installation Tweaks Option'}, 'use_regexp_matching': {'doc': 'This option determines whether or not various directives in your object definitions will be processed as regular expressions. More information on how this works can be found here. ', 'examples': ['use_regexp_matching=0'], 'format': 'use_regexp_matching=<0/1>', 'options': ["0 = Don't use regular expression matching (default)", '1 = Use regular expression matching'], 'title': 'Regular Expression Matching Option'}, 'use_retained_program_state': {'doc': 'This setting determines whether or not Nagios will set various program-wide state variables based on the values saved in the retention file. Some of these program-wide state variables that are normally saved across program restarts if state retention is enabled include the enable_notifications, enable_flap_detection, enable_event_handlers, execute_service_checks, and accept_passive_service_checks options. If you do not have state retention enabled, this option has no effect. ', 'examples': ['use_retained_program_state=1'], 'format': 'use_retained_program_state=<0/1>', 'options': ["0 = Don't use retained program state", '1 = Use retained program state (default)'], 'title': 'Use Retained Program State Option'}, 'use_retained_scheduling_info': {'doc': 'This setting determines whether or not Nagios will retain scheduling info (next check times) for hosts and services when it restarts. If you are adding a large number (or percentage) of hosts and services, I would recommend disabling this option when you first restart Nagios, as it can adversely skew the spread of initial checks. Otherwise you will probably want to leave it enabled. ', 'examples': ['use_retained_scheduling_info=1'], 'format': 'use_retained_scheduling_info=<0/1>', 'options': ["0 = Don't use retained scheduling info", '1 = Use retained scheduling info (default)'], 'title': 'Use Retained Scheduling Info Option'}, 'use_syslog': {'doc': 'This variable determines whether messages are logged to the syslog facility on your local host. Values are as follows: ', 'examples': ['use_syslog=1'], 'format': 'use_syslog=<0/1>', 'options': ["0 = Don't use syslog facility", '1 = Use syslog facility'], 'title': 'Syslog Logging Option'}, 'use_timezone': {'doc': 'This option allows you to override the default timezone that this instance of Nagios runs in. Useful if you have multiple instances of Nagios that need to run from the same server, but have different local times associated with them. If not specified, Nagios will use the system configured timezone. Note Note: If you use this option to specify a custom timezone, you will also need to alter the Apache configuration directives for the CGIs to specify the timezone you want. Example: ', 'examples': ['use_timezone=US/Mountain'], 'format': 'use_timezone=<tz>', 'options': [], 'title': 'Timezone Option'}, 'use_true_regexp_matching': {'doc': 'If you\'ve enabled regular expression matching of various object directives using the use_regexp_matching option, this option will determine when object directives are treated as regular expressions. If this option is disabled (the default), directives will only be treated as regular expressions if they contain *, ?, +, or \\.. If this option is enabled, all appropriate directives will be treated as regular expression - be careful when enabling this! More information on how this works can be found here. ', 'examples': ['use_true_regexp_matching=0'], 'format': 'use_true_regexp_matching=<0/1>', 'options': ["0 = Don't use true regular expression matching (default)", '1 = Use true regular expression matching'], 'title': 'True Regular Expression Matching Option'}} pynag-0.9.1+dfsg.orig/pynag/Control/0000775000175000017500000000000012370025176016264 5ustar clintclintpynag-0.9.1+dfsg.orig/pynag/Control/Command/0000775000175000017500000000000012370025176017642 5ustar clintclintpynag-0.9.1+dfsg.orig/pynag/Control/Command/autogenerated_commands.py0000664000175000017500000025002312354645173024735 0ustar clintclint def add_host_comment( host_name, persistent, author, comment, command_file=None, timestamp=0 ): """ Adds a comment to a particular host. If the "persistent" field is set to zero (0), the comment will be deleted the next time Nagios is restarted. Otherwise, the comment will persist across program restarts until it is deleted manually. """ return send_command("ADD_HOST_COMMENT", command_file, timestamp, host_name, persistent, author, comment) def shutdown_program( command_file=None, timestamp=0 ): """ Shuts down the Nagios process. """ return send_command("SHUTDOWN_PROGRAM", command_file, timestamp, ) def disable_servicegroup_passive_svc_checks( servicegroup_name, command_file=None, timestamp=0 ): """ Disables the acceptance and processing of passive checks for all services in a particular servicegroup. """ return send_command("DISABLE_SERVICEGROUP_PASSIVE_SVC_CHECKS", command_file, timestamp, servicegroup_name) def enable_servicegroup_passive_host_checks( servicegroup_name, command_file=None, timestamp=0 ): """ Enables the acceptance and processing of passive checks for all hosts that have services that are members of a particular service group. """ return send_command("ENABLE_SERVICEGROUP_PASSIVE_HOST_CHECKS", command_file, timestamp, servicegroup_name) def disable_servicegroup_passive_host_checks( servicegroup_name, command_file=None, timestamp=0 ): """ Disables the acceptance and processing of passive checks for all hosts that have services that are members of a particular service group. """ return send_command("DISABLE_SERVICEGROUP_PASSIVE_HOST_CHECKS", command_file, timestamp, servicegroup_name) def change_global_host_event_handler( event_handler_command, command_file=None, timestamp=0 ): """ Changes the global host event handler command to be that specified by the "event_handler_command" option. The "event_handler_command" option specifies the short name of the command that should be used as the new host event handler. The command must have been configured in Nagios before it was last (re)started. """ return send_command("CHANGE_GLOBAL_HOST_EVENT_HANDLER", command_file, timestamp, event_handler_command) def change_global_svc_event_handler( event_handler_command, command_file=None, timestamp=0 ): """ Changes the global service event handler command to be that specified by the "event_handler_command" option. The "event_handler_command" option specifies the short name of the command that should be used as the new service event handler. The command must have been configured in Nagios before it was last (re)started. """ return send_command("CHANGE_GLOBAL_SVC_EVENT_HANDLER", command_file, timestamp, event_handler_command) def change_host_event_handler( host_name, event_handler_command, command_file=None, timestamp=0 ): """ Changes the event handler command for a particular host to be that specified by the "event_handler_command" option. The "event_handler_command" option specifies the short name of the command that should be used as the new host event handler. The command must have been configured in Nagios before it was last (re)started. """ return send_command("CHANGE_HOST_EVENT_HANDLER", command_file, timestamp, host_name, event_handler_command) def change_svc_event_handler( host_name, service_description, event_handler_command, command_file=None, timestamp=0 ): """ Changes the event handler command for a particular service to be that specified by the "event_handler_command" option. The "event_handler_command" option specifies the short name of the command that should be used as the new service event handler. The command must have been configured in Nagios before it was last (re)started. """ return send_command("CHANGE_SVC_EVENT_HANDLER", command_file, timestamp, host_name, service_description, event_handler_command) def change_host_check_command( host_name, check_command, command_file=None, timestamp=0 ): """ Changes the check command for a particular host to be that specified by the "check_command" option. The "check_command" option specifies the short name of the command that should be used as the new host check command. The command must have been configured in Nagios before it was last (re)started. """ return send_command("CHANGE_HOST_CHECK_COMMAND", command_file, timestamp, host_name, check_command) def change_svc_check_command( host_name, service_description, check_command, command_file=None, timestamp=0 ): """ Changes the check command for a particular service to be that specified by the "check_command" option. The "check_command" option specifies the short name of the command that should be used as the new service check command. The command must have been configured in Nagios before it was last (re)started. """ return send_command("CHANGE_SVC_CHECK_COMMAND", command_file, timestamp, host_name, service_description, check_command) def change_normal_host_check_interval( host_name, check_interval, command_file=None, timestamp=0 ): """ Changes the normal (regularly scheduled) check interval for a particular host. """ return send_command("CHANGE_NORMAL_HOST_CHECK_INTERVAL", command_file, timestamp, host_name, check_interval) def enable_svc_notifications( host_name, service_description, command_file=None, timestamp=0 ): """ Enables notifications for a particular service. Notifications will be sent out for the service only if notifications are enabled on a program-wide basis as well. """ return send_command("ENABLE_SVC_NOTIFICATIONS", command_file, timestamp, host_name, service_description) def change_normal_svc_check_interval( host_name, service_description, check_interval, command_file=None, timestamp=0 ): """ Changes the normal (regularly scheduled) check interval for a particular service """ return send_command("CHANGE_NORMAL_SVC_CHECK_INTERVAL", command_file, timestamp, host_name, service_description, check_interval) def change_retry_svc_check_interval( host_name, service_description, check_interval, command_file=None, timestamp=0 ): """ Changes the retry check interval for a particular service. """ return send_command("CHANGE_RETRY_SVC_CHECK_INTERVAL", command_file, timestamp, host_name, service_description, check_interval) def change_max_host_check_attempts( host_name, check_attempts, command_file=None, timestamp=0 ): """ Changes the maximum number of check attempts (retries) for a particular host. """ return send_command("CHANGE_MAX_HOST_CHECK_ATTEMPTS", command_file, timestamp, host_name, check_attempts) def change_max_svc_check_attempts( host_name, service_description, check_attempts, command_file=None, timestamp=0 ): """ Changes the maximum number of check attempts (retries) for a particular service. """ return send_command("CHANGE_MAX_SVC_CHECK_ATTEMPTS", command_file, timestamp, host_name, service_description, check_attempts) def process_service_check_result( host_name, service_description, return_code, plugin_output, command_file=None, timestamp=0 ): """ This is used to submit a passive check result for a particular service. The "return_code" field should be one of the following: 0=OK, 1=WARNING, 2=CRITICAL, 3=UNKNOWN. The "plugin_output" field contains text output from the service check, along with optional performance data. """ return send_command("PROCESS_SERVICE_CHECK_RESULT", command_file, timestamp, host_name, service_description, return_code, plugin_output) def process_host_check_result( host_name, status_code, plugin_output, command_file=None, timestamp=0 ): """ This is used to submit a passive check result for a particular host. The "status_code" indicates the state of the host check and should be one of the following: 0=UP, 1=DOWN, 2=UNREACHABLE. The "plugin_output" argument contains the text returned from the host check, along with optional performance data. """ return send_command("PROCESS_HOST_CHECK_RESULT", command_file, timestamp, host_name, status_code, plugin_output) def remove_host_acknowledgement( host_name, command_file=None, timestamp=0 ): """ This removes the problem acknowledgement for a particular host. Once the acknowledgement has been removed, notifications can once again be sent out for the given host. """ return send_command("REMOVE_HOST_ACKNOWLEDGEMENT", command_file, timestamp, host_name) def remove_svc_acknowledgement( host_name, service_description, command_file=None, timestamp=0 ): """ This removes the problem acknowledgement for a particular service. Once the acknowledgement has been removed, notifications can once again be sent out for the given service. """ return send_command("REMOVE_SVC_ACKNOWLEDGEMENT", command_file, timestamp, host_name, service_description) def schedule_host_downtime( host_name, start_time, end_time, fixed, trigger_id, duration, author, comment, command_file=None, timestamp=0 ): """ Schedules downtime for a specified host. If the "fixed" argument is set to one (1), downtime will start and end at the times specified by the "start" and "end" arguments. Otherwise, downtime will begin between the "start" and "end" times and last for "duration" seconds. The "start" and "end" arguments are specified in time_t format (seconds since the UNIX epoch). The specified host downtime can be triggered by another downtime entry if the "trigger_id" is set to the ID of another scheduled downtime entry. Set the "trigger_id" argument to zero (0) if the downtime for the specified host should not be triggered by another downtime entry. """ return send_command("SCHEDULE_HOST_DOWNTIME", command_file, timestamp, host_name, start_time, end_time, fixed, trigger_id, duration, author, comment) def schedule_svc_downtime( host_name, service_description, start_time, end_time, fixed, trigger_id, duration, author, comment, command_file=None, timestamp=0 ): """ Schedules downtime for a specified service. If the "fixed" argument is set to one (1), downtime will start and end at the times specified by the "start" and "end" arguments. Otherwise, downtime will begin between the "start" and "end" times and last for "duration" seconds. The "start" and "end" arguments are specified in time_t format (seconds since the UNIX epoch). The specified service downtime can be triggered by another downtime entry if the "trigger_id" is set to the ID of another scheduled downtime entry. Set the "trigger_id" argument to zero (0) if the downtime for the specified service should not be triggered by another downtime entry. """ return send_command("SCHEDULE_SVC_DOWNTIME", command_file, timestamp, host_name, service_description, start_time, end_time, fixed, trigger_id, duration, author, comment) def disable_svc_notifications( host_name, service_description, command_file=None, timestamp=0 ): """ Disables notifications for a particular service. """ return send_command("DISABLE_SVC_NOTIFICATIONS", command_file, timestamp, host_name, service_description) def schedule_servicegroup_svc_downtime( servicegroup_name, start_time, end_time, fixed, trigger_id, duration, author, comment, command_file=None, timestamp=0 ): """ Schedules downtime for all services in a specified servicegroup. If the "fixed" argument is set to one (1), downtime will start and end at the times specified by the "start" and "end" arguments. Otherwise, downtime will begin between the "start" and "end" times and last for "duration" seconds. The "start" and "end" arguments are specified in time_t format (seconds since the UNIX epoch). The service downtime entries can be triggered by another downtime entry if the "trigger_id" is set to the ID of another scheduled downtime entry. Set the "trigger_id" argument to zero (0) if the downtime for the services should not be triggered by another downtime entry. """ return send_command("SCHEDULE_SERVICEGROUP_SVC_DOWNTIME", command_file, timestamp, servicegroup_name, start_time, end_time, fixed, trigger_id, duration, author, comment) def schedule_servicegroup_host_downtime( servicegroup_name, start_time, end_time, fixed, trigger_id, duration, author, comment, command_file=None, timestamp=0 ): """ Schedules downtime for all hosts that have services in a specified servicegroup. If the "fixed" argument is set to one (1), downtime will start and end at the times specified by the "start" and "end" arguments. Otherwise, downtime will begin between the "start" and "end" times and last for "duration" seconds. The "start" and "end" arguments are specified in time_t format (seconds since the UNIX epoch). The host downtime entries can be triggered by another downtime entry if the "trigger_id" is set to the ID of another scheduled downtime entry. Set the "trigger_id" argument to zero (0) if the downtime for the hosts should not be triggered by another downtime entry. """ return send_command("SCHEDULE_SERVICEGROUP_HOST_DOWNTIME", command_file, timestamp, servicegroup_name, start_time, end_time, fixed, trigger_id, duration, author, comment) def schedule_host_svc_downtime( host_name, start_time, end_time, fixed, trigger_id, duration, author, comment, command_file=None, timestamp=0 ): """ Schedules downtime for all services associated with a particular host. If the "fixed" argument is set to one (1), downtime will start and end at the times specified by the "start" and "end" arguments. Otherwise, downtime will begin between the "start" and "end" times and last for "duration" seconds. The "start" and "end" arguments are specified in time_t format (seconds since the UNIX epoch). The service downtime entries can be triggered by another downtime entry if the "trigger_id" is set to the ID of another scheduled downtime entry. Set the "trigger_id" argument to zero (0) if the downtime for the services should not be triggered by another downtime entry. """ return send_command("SCHEDULE_HOST_SVC_DOWNTIME", command_file, timestamp, host_name, start_time, end_time, fixed, trigger_id, duration, author, comment) def schedule_hostgroup_host_downtime( hostgroup_name, start_time, end_time, fixed, trigger_id, duration, author, comment, command_file=None, timestamp=0 ): """ Schedules downtime for all hosts in a specified hostgroup. If the "fixed" argument is set to one (1), downtime will start and end at the times specified by the "start" and "end" arguments. Otherwise, downtime will begin between the "start" and "end" times and last for "duration" seconds. The "start" and "end" arguments are specified in time_t format (seconds since the UNIX epoch). The host downtime entries can be triggered by another downtime entry if the "trigger_id" is set to the ID of another scheduled downtime entry. Set the "trigger_id" argument to zero (0) if the downtime for the hosts should not be triggered by another downtime entry. """ return send_command("SCHEDULE_HOSTGROUP_HOST_DOWNTIME", command_file, timestamp, hostgroup_name, start_time, end_time, fixed, trigger_id, duration, author, comment) def schedule_hostgroup_svc_downtime( hostgroup_name, start_time, end_time, fixed, trigger_id, duration, author, comment, command_file=None, timestamp=0 ): """ Schedules downtime for all services associated with hosts in a specified servicegroup. If the "fixed" argument is set to one (1), downtime will start and end at the times specified by the "start" and "end" arguments. Otherwise, downtime will begin between the "start" and "end" times and last for "duration" seconds. The "start" and "end" arguments are specified in time_t format (seconds since the UNIX epoch). The service downtime entries can be triggered by another downtime entry if the "trigger_id" is set to the ID of another scheduled downtime entry. Set the "trigger_id" argument to zero (0) if the downtime for the services should not be triggered by another downtime entry. """ return send_command("SCHEDULE_HOSTGROUP_SVC_DOWNTIME", command_file, timestamp, hostgroup_name, start_time, end_time, fixed, trigger_id, duration, author, comment) def del_host_downtime( downtime_id, command_file=None, timestamp=0 ): """ Deletes the host downtime entry that has an ID number matching the "downtime_id" argument. If the downtime is currently in effect, the host will come out of scheduled downtime (as long as there are no other overlapping active downtime entries). """ return send_command("DEL_HOST_DOWNTIME", command_file, timestamp, downtime_id) def del_svc_downtime( downtime_id, command_file=None, timestamp=0 ): """ Deletes the service downtime entry that has an ID number matching the "downtime_id" argument. If the downtime is currently in effect, the service will come out of scheduled downtime (as long as there are no other overlapping active downtime entries). """ return send_command("DEL_SVC_DOWNTIME", command_file, timestamp, downtime_id) def schedule_host_check( host_name, check_time, command_file=None, timestamp=0 ): """ Schedules the next active check of a particular host at "check_time". The "check_time" argument is specified in time_t format (seconds since the UNIX epoch). Note that the host may not actually be checked at the time you specify. This could occur for a number of reasons: active checks are disabled on a program-wide or service-specific basis, the host is already scheduled to be checked at an earlier time, etc. If you want to force the host check to occur at the time you specify, look at the SCHEDULE_FORCED_HOST_CHECK command. """ return send_command("SCHEDULE_HOST_CHECK", command_file, timestamp, host_name, check_time) def schedule_forced_host_check( host_name, check_time, command_file=None, timestamp=0 ): """ Schedules a forced active check of a particular host at "check_time". The "check_time" argument is specified in time_t format (seconds since the UNIX epoch). Forced checks are performed regardless of what time it is (e.g. timeperiod restrictions are ignored) and whether or not active checks are enabled on a host-specific or program-wide basis. """ return send_command("SCHEDULE_FORCED_HOST_CHECK", command_file, timestamp, host_name, check_time) def schedule_forced_svc_check( host_name, service_description, check_time, command_file=None, timestamp=0 ): """ Schedules a forced active check of a particular service at "check_time". The "check_time" argument is specified in time_t format (seconds since the UNIX epoch). Forced checks are performed regardless of what time it is (e.g. timeperiod restrictions are ignored) and whether or not active checks are enabled on a service-specific or program-wide basis. """ return send_command("SCHEDULE_FORCED_SVC_CHECK", command_file, timestamp, host_name, service_description, check_time) def del_all_host_comments( host_name, command_file=None, timestamp=0 ): """ Deletes all comments assocated with a particular host. """ return send_command("DEL_ALL_HOST_COMMENTS", command_file, timestamp, host_name) def schedule_forced_host_svc_checks( host_name, check_time, command_file=None, timestamp=0 ): """ Schedules a forced active check of all services associated with a particular host at "check_time". The "check_time" argument is specified in time_t format (seconds since the UNIX epoch). Forced checks are performed regardless of what time it is (e.g. timeperiod restrictions are ignored) and whether or not active checks are enabled on a service-specific or program-wide basis. """ return send_command("SCHEDULE_FORCED_HOST_SVC_CHECKS", command_file, timestamp, host_name, check_time) def process_file( file_name, delete, command_file=None, timestamp=0 ): """ Directs Nagios to process all external commands that are found in the file specified by the argument. If the option is non-zero, the file will be deleted once it has been processes. If the option is set to zero, the file is left untouched. """ return send_command("PROCESS_FILE", command_file, timestamp, file_name, delete) def change_host_check_timeperiod( host_name, check_timeperod, command_file=None, timestamp=0 ): """ Changes the check timeperiod for a particular host to what is specified by the "check_timeperiod" option. The "check_timeperiod" option should be the short name of the timeperod that is to be used as the host check timeperiod. The timeperiod must have been configured in Nagios before it was last (re)started. """ return send_command("CHANGE_HOST_CHECK_TIMEPERIOD", command_file, timestamp, host_name, check_timeperod) def send_custom_host_notification( host_name, options, author, comment, command_file=None, timestamp=0 ): """ Allows you to send a custom host notification. Very useful in dire situations, emergencies or to communicate with all admins that are responsible for a particular host. When the host notification is sent out, the $NOTIFICATIONTYPE$ macro will be set to "CUSTOM". The field is a logical OR of the following integer values that affect aspects of the notification that are sent out: 0 = No option (default), 1 = Broadcast (send notification to all normal and all escalated contacts for the host), 2 = Forced (notification is sent out regardless of current time, whether or not notifications are enabled, etc.), 4 = Increment current notification # for the host (this is not done by default for custom notifications). The comment field can be used with the $NOTIFICATIONCOMMENT$ macro in notification commands. """ return send_command("SEND_CUSTOM_HOST_NOTIFICATION", command_file, timestamp, host_name, options, author, comment) def send_custom_svc_notification( host_name, service_description, options, author, comment, command_file=None, timestamp=0 ): """ Allows you to send a custom service notification. Very useful in dire situations, emergencies or to communicate with all admins that are responsible for a particular service. When the service notification is sent out, the $NOTIFICATIONTYPE$ macro will be set to "CUSTOM". The field is a logical OR of the following integer values that affect aspects of the notification that are sent out: 0 = No option (default), 1 = Broadcast (send notification to all normal and all escalated contacts for the service), 2 = Forced (notification is sent out regardless of current time, whether or not notifications are enabled, etc.), 4 = Increment current notification # for the service(this is not done by default for custom notifications). The comment field can be used with the $NOTIFICATIONCOMMENT$ macro in notification commands. """ return send_command("SEND_CUSTOM_SVC_NOTIFICATION", command_file, timestamp, host_name, service_description, options, author, comment) def change_retry_host_check_interval( host_name, service_description, check_interval, command_file=None, timestamp=0 ): """ Changes the retry check interval for a particular host. """ return send_command("CHANGE_RETRY_HOST_CHECK_INTERVAL", command_file, timestamp, host_name, service_description, check_interval) def change_svc_check_timeperiod( host_name, service_description, check_timeperiod, command_file=None, timestamp=0 ): """ Changes the check timeperiod for a particular service to what is specified by the "check_timeperiod" option. The "check_timeperiod" option should be the short name of the timeperod that is to be used as the service check timeperiod. The timeperiod must have been configured in Nagios before it was last (re)started. """ return send_command("CHANGE_SVC_CHECK_TIMEPERIOD", command_file, timestamp, host_name, service_description, check_timeperiod) def change_host_check_timeperiod( host_name, timeperiod, command_file=None, timestamp=0 ): """ Changes the valid check period for the specified host. """ return send_command("CHANGE_HOST_CHECK_TIMEPERIOD", command_file, timestamp, host_name, timeperiod) def change_custom_host_var( host_name, varname, varvalue, command_file=None, timestamp=0 ): """ Changes the value of a custom host variable. """ return send_command("CHANGE_CUSTOM_HOST_VAR", command_file, timestamp, host_name, varname, varvalue) def del_all_svc_comments( host_name, service_description, command_file=None, timestamp=0 ): """ Deletes all comments associated with a particular service. """ return send_command("DEL_ALL_SVC_COMMENTS", command_file, timestamp, host_name, service_description) def change_custom_svc_var( host_name, service_description, varname, varvalue, command_file=None, timestamp=0 ): """ Changes the value of a custom service variable. """ return send_command("CHANGE_CUSTOM_SVC_VAR", command_file, timestamp, host_name, service_description, varname, varvalue) def change_custom_contact_var( contact_name, varname, varvalue, command_file=None, timestamp=0 ): """ Changes the value of a custom contact variable. """ return send_command("CHANGE_CUSTOM_CONTACT_VAR", command_file, timestamp, contact_name, varname, varvalue) def enable_contact_host_notifications( contact_name, command_file=None, timestamp=0 ): """ Enables host notifications for a particular contact. """ return send_command("ENABLE_CONTACT_HOST_NOTIFICATIONS", command_file, timestamp, contact_name) def disable_contact_host_notifications( contact_name, command_file=None, timestamp=0 ): """ Disables host notifications for a particular contact. """ return send_command("DISABLE_CONTACT_HOST_NOTIFICATIONS", command_file, timestamp, contact_name) def enable_contact_svc_notifications( contact_name, command_file=None, timestamp=0 ): """ Disables service notifications for a particular contact. """ return send_command("ENABLE_CONTACT_SVC_NOTIFICATIONS", command_file, timestamp, contact_name) def disable_contact_svc_notifications( contact_name, command_file=None, timestamp=0 ): """ Disables service notifications for a particular contact. """ return send_command("DISABLE_CONTACT_SVC_NOTIFICATIONS", command_file, timestamp, contact_name) def enable_contactgroup_host_notifications( contactgroup_name, command_file=None, timestamp=0 ): """ Enables host notifications for all contacts in a particular contactgroup. """ return send_command("ENABLE_CONTACTGROUP_HOST_NOTIFICATIONS", command_file, timestamp, contactgroup_name) def disable_contactgroup_host_notifications( contactgroup_name, command_file=None, timestamp=0 ): """ Disables host notifications for all contacts in a particular contactgroup. """ return send_command("DISABLE_CONTACTGROUP_HOST_NOTIFICATIONS", command_file, timestamp, contactgroup_name) def enable_contactgroup_svc_notifications( contactgroup_name, command_file=None, timestamp=0 ): """ Enables service notifications for all contacts in a particular contactgroup. """ return send_command("ENABLE_CONTACTGROUP_SVC_NOTIFICATIONS", command_file, timestamp, contactgroup_name) def disable_contactgroup_svc_notifications( contactgroup_name, command_file=None, timestamp=0 ): """ Disables service notifications for all contacts in a particular contactgroup. """ return send_command("DISABLE_CONTACTGROUP_SVC_NOTIFICATIONS", command_file, timestamp, contactgroup_name) def enable_host_notifications( host_name, command_file=None, timestamp=0 ): """ Enables notifications for a particular host. Notifications will be sent out for the host only if notifications are enabled on a program-wide basis as well. """ return send_command("ENABLE_HOST_NOTIFICATIONS", command_file, timestamp, host_name) def disable_svc_flap_detection( host_name, service_description, command_file=None, timestamp=0 ): """ Disables flap detection for the specified service. """ return send_command("DISABLE_SVC_FLAP_DETECTION", command_file, timestamp, host_name, service_description) def change_svc_notification_timeperiod( host_name, service_description, notification_timeperiod, command_file=None, timestamp=0 ): """ Changes the notification timeperiod for a particular service to what is specified by the "notification_timeperiod" option. The "notification_timeperiod" option should be the short name of the timeperiod that is to be used as the service notification timeperiod. The timeperiod must have been configured in Nagios before it was last (re)started. """ return send_command("CHANGE_SVC_NOTIFICATION_TIMEPERIOD", command_file, timestamp, host_name, service_description, notification_timeperiod) def change_contact_svc_notification_timeperiod( contact_name, notification_timeperiod, command_file=None, timestamp=0 ): """ Changes the service notification timeperiod for a particular contact to what is specified by the "notification_timeperiod" option. The "notification_timeperiod" option should be the short name of the timeperiod that is to be used as the contact's service notification timeperiod. The timeperiod must have been configured in Nagios before it was last (re)started. """ return send_command("CHANGE_CONTACT_SVC_NOTIFICATION_TIMEPERIOD", command_file, timestamp, contact_name, notification_timeperiod) def change_contact_host_notification_timeperiod( contact_name, notification_timeperiod, command_file=None, timestamp=0 ): """ Changes the host notification timeperiod for a particular contact to what is specified by the "notification_timeperiod" option. The "notification_timeperiod" option should be the short name of the timeperiod that is to be used as the contact's host notification timeperiod. The timeperiod must have been configured in Nagios before it was last (re)started. """ return send_command("CHANGE_CONTACT_HOST_NOTIFICATION_TIMEPERIOD", command_file, timestamp, contact_name, notification_timeperiod) def change_host_modattr( host_name, value, command_file=None, timestamp=0 ): """ This command changes the modified attributes value for the specified host. Modified attributes values are used by Nagios to determine which object properties should be retained across program restarts. Thus, modifying the value of the attributes can affect data retention. This is an advanced option and should only be used by people who are intimately familiar with the data retention logic in Nagios. """ return send_command("CHANGE_HOST_MODATTR", command_file, timestamp, host_name, value) def change_svc_modattr( host_name, service_description, value, command_file=None, timestamp=0 ): """ This command changes the modified attributes value for the specified service. Modified attributes values are used by Nagios to determine which object properties should be retained across program restarts. Thus, modifying the value of the attributes can affect data retention. This is an advanced option and should only be used by people who are intimately familiar with the data retention logic in Nagios. """ return send_command("CHANGE_SVC_MODATTR", command_file, timestamp, host_name, service_description, value) def change_contact_modattr( contact_name, value, command_file=None, timestamp=0 ): """ This command changes the modified attributes value for the specified contact. Modified attributes values are used by Nagios to determine which object properties should be retained across program restarts. Thus, modifying the value of the attributes can affect data retention. This is an advanced option and should only be used by people who are intimately familiar with the data retention logic in Nagios. """ return send_command("CHANGE_CONTACT_MODATTR", command_file, timestamp, contact_name, value) def change_contact_modhattr( contact_name, value, command_file=None, timestamp=0 ): """ This command changes the modified host attributes value for the specified contact. Modified attributes values are used by Nagios to determine which object properties should be retained across program restarts. Thus, modifying the value of the attributes can affect data retention. This is an advanced option and should only be used by people who are intimately familiar with the data retention logic in Nagios. """ return send_command("CHANGE_CONTACT_MODHATTR", command_file, timestamp, contact_name, value) def change_contact_modsattr( contact_name, value, command_file=None, timestamp=0 ): """ This command changes the modified service attributes value for the specified contact. Modified attributes values are used by Nagios to determine which object properties should be retained across program restarts. Thus, modifying the value of the attributes can affect data retention. This is an advanced option and should only be used by people who are intimately familiar with the data retention logic in Nagios. """ return send_command("CHANGE_CONTACT_MODSATTR", command_file, timestamp, contact_name, value) def disable_host_notifications( host_name, command_file=None, timestamp=0 ): """ Disables notifications for a particular host. """ return send_command("DISABLE_HOST_NOTIFICATIONS", command_file, timestamp, host_name) def enable_all_notifications_beyond_host( host_name, command_file=None, timestamp=0 ): """ Enables notifications for all hosts and services "beyond" (e.g. on all child hosts of) the specified host. The current notification setting for the specified host is not affected. Notifications will only be sent out for these hosts and services if notifications are also enabled on a program-wide basis. """ return send_command("ENABLE_ALL_NOTIFICATIONS_BEYOND_HOST", command_file, timestamp, host_name) def disable_all_notifications_beyond_host( host_name, command_file=None, timestamp=0 ): """ Disables notifications for all hosts and services "beyond" (e.g. on all child hosts of) the specified host. The current notification setting for the specified host is not affected. """ return send_command("DISABLE_ALL_NOTIFICATIONS_BEYOND_HOST", command_file, timestamp, host_name) def enable_host_and_child_notifications( host_name, command_file=None, timestamp=0 ): """ Enables notifications for the specified host, as well as all hosts "beyond" (e.g. on all child hosts of) the specified host. Notifications will only be sent out for these hosts if notifications are also enabled on a program-wide basis. """ return send_command("ENABLE_HOST_AND_CHILD_NOTIFICATIONS", command_file, timestamp, host_name) def add_svc_comment( host_name, service_description, persistent, author, comment, command_file=None, timestamp=0 ): """ Adds a comment to a particular service. If the "persistent" field is set to zero (0), the comment will be deleted the next time Nagios is restarted. Otherwise, the comment will persist across program restarts until it is deleted manually. """ return send_command("ADD_SVC_COMMENT", command_file, timestamp, host_name, service_description, persistent, author, comment) def disable_host_and_child_notifications( host_name, command_file=None, timestamp=0 ): """ Disables notifications for the specified host, as well as all hosts "beyond" (e.g. on all child hosts of) the specified host. """ return send_command("DISABLE_HOST_AND_CHILD_NOTIFICATIONS", command_file, timestamp, host_name) def set_host_notification_number( host_name, notification_number, command_file=None, timestamp=0 ): """ Sets the current notification number for a particular host. A value of 0 indicates that no notification has yet been sent for the current host problem. Useful for forcing an escalation (based on notification number) or replicating notification information in redundant monitoring environments. Notification numbers greater than zero have no noticeable affect on the notification process if the host is currently in an UP state. """ return send_command("SET_HOST_NOTIFICATION_NUMBER", command_file, timestamp, host_name, notification_number) def set_svc_notification_number( host_name, service_description, notification_number, command_file=None, timestamp=0 ): """ Sets the current notification number for a particular service. A value of 0 indicates that no notification has yet been sent for the current service problem. Useful for forcing an escalation (based on notification number) or replicating notification information in redundant monitoring environments. Notification numbers greater than zero have no noticeable affect on the notification process if the service is currently in an OK state. """ return send_command("SET_SVC_NOTIFICATION_NUMBER", command_file, timestamp, host_name, service_description, notification_number) def enable_service_freshness_checks( command_file=None, timestamp=0 ): """ Enables freshness checks of all services on a program-wide basis. Individual services that have freshness checks disabled will not be checked for freshness. """ return send_command("ENABLE_SERVICE_FRESHNESS_CHECKS", command_file, timestamp, ) def enable_host_freshness_checks( command_file=None, timestamp=0 ): """ Enables freshness checks of all hosts on a program-wide basis. Individual hosts that have freshness checks disabled will not be checked for freshness. """ return send_command("ENABLE_HOST_FRESHNESS_CHECKS", command_file, timestamp, ) def disable_service_freshness_checks( command_file=None, timestamp=0 ): """ Disables freshness checks of all services on a program-wide basis. """ return send_command("DISABLE_SERVICE_FRESHNESS_CHECKS", command_file, timestamp, ) def disable_host_freshness_checks( command_file=None, timestamp=0 ): """ Disables freshness checks of all hosts on a program-wide basis. """ return send_command("DISABLE_HOST_FRESHNESS_CHECKS", command_file, timestamp, ) def schedule_and_propagate_triggered_host_downtime( host_name, start_time, end_time, fixed, trigger_id, duration, author, comment, command_file=None, timestamp=0 ): """ Schedules downtime for a specified host and all of its children (hosts). If the "fixed" argument is set to one (1), downtime will start and end at the times specified by the "start" and "end" arguments. Otherwise, downtime will begin between the "start" and "end" times and last for "duration" seconds. The "start" and "end" arguments are specified in time_t format (seconds since the UNIX epoch). Downtime for child hosts are all set to be triggered by the downtime for the specified (parent) host. The specified (parent) host downtime can be triggered by another downtime entry if the "trigger_id" is set to the ID of another scheduled downtime entry. Set the "trigger_id" argument to zero (0) if the downtime for the specified (parent) host should not be triggered by another downtime entry. """ return send_command("SCHEDULE_AND_PROPAGATE_TRIGGERED_HOST_DOWNTIME", command_file, timestamp, host_name, start_time, end_time, fixed, trigger_id, duration, author, comment) def schedule_and_propagate_host_downtime( host_name, start_time, end_time, fixed, trigger_id, duration, author, comment, command_file=None, timestamp=0 ): """ Schedules downtime for a specified host and all of its children (hosts). If the "fixed" argument is set to one (1), downtime will start and end at the times specified by the "start" and "end" arguments. Otherwise, downtime will begin between the "start" and "end" times and last for "duration" seconds. The "start" and "end" arguments are specified in time_t format (seconds since the UNIX epoch). The specified (parent) host downtime can be triggered by another downtime entry if the "trigger_id" is set to the ID of another scheduled downtime entry. Set the "trigger_id" argument to zero (0) if the downtime for the specified (parent) host should not be triggered by another downtime entry. """ return send_command("SCHEDULE_AND_PROPAGATE_HOST_DOWNTIME", command_file, timestamp, host_name, start_time, end_time, fixed, trigger_id, duration, author, comment) def schedule_svc_check( host_name, service_description, check_time, command_file=None, timestamp=0 ): """ Schedules the next active check of a specified service at "check_time". The "check_time" argument is specified in time_t format (seconds since the UNIX epoch). Note that the service may not actually be checked at the time you specify. This could occur for a number of reasons: active checks are disabled on a program-wide or service-specific basis, the service is already scheduled to be checked at an earlier time, etc. If you want to force the service check to occur at the time you specify, look at the SCHEDULE_FORCED_SVC_CHECK command. """ return send_command("SCHEDULE_SVC_CHECK", command_file, timestamp, host_name, service_description, check_time) def del_host_comment( comment_id, command_file=None, timestamp=0 ): """ Deletes a host comment. The id number of the comment that is to be deleted must be specified. """ return send_command("DEL_HOST_COMMENT", command_file, timestamp, comment_id) def schedule_host_svc_checks( host_name, check_time, command_file=None, timestamp=0 ): """ Schedules the next active check of all services on a particular host at "check_time". The "check_time" argument is specified in time_t format (seconds since the UNIX epoch). Note that the services may not actually be checked at the time you specify. This could occur for a number of reasons: active checks are disabled on a program-wide or service-specific basis, the services are already scheduled to be checked at an earlier time, etc. If you want to force the service checks to occur at the time you specify, look at the SCHEDULE_FORCED_HOST_SVC_CHECKS command. """ return send_command("SCHEDULE_HOST_SVC_CHECKS", command_file, timestamp, host_name, check_time) def save_state_information( command_file=None, timestamp=0 ): """ Causes Nagios to save all current monitoring status information to the state retention file. Normally, state retention information is saved before the Nagios process shuts down and (potentially) at regularly scheduled intervals. This command allows you to force Nagios to save this information to the state retention file immediately. This does not affect the current status information in the Nagios process. """ return send_command("SAVE_STATE_INFORMATION", command_file, timestamp, ) def read_state_information( command_file=None, timestamp=0 ): """ Causes Nagios to load all current monitoring status information from the state retention file. Normally, state retention information is loaded when the Nagios process starts up and before it starts monitoring. WARNING: This command will cause Nagios to discard all current monitoring status information and use the information stored in state retention file! Use with care. """ return send_command("READ_STATE_INFORMATION", command_file, timestamp, ) def enable_host_svc_checks( host_name, command_file=None, timestamp=0 ): """ Enables active checks of all services on the specified host. """ return send_command("ENABLE_HOST_SVC_CHECKS", command_file, timestamp, host_name) def disable_host_svc_checks( host_name, command_file=None, timestamp=0 ): """ Enables active checks of all services on the specified host. """ return send_command("DISABLE_HOST_SVC_CHECKS", command_file, timestamp, host_name) def enable_host_svc_notifications( host_name, command_file=None, timestamp=0 ): """ Enables notifications for all services on the specified host. Note that notifications will not be sent out if notifications are disabled on a program-wide basis. """ return send_command("ENABLE_HOST_SVC_NOTIFICATIONS", command_file, timestamp, host_name) def disable_host_svc_notifications( host_name, command_file=None, timestamp=0 ): """ Disables notifications for all services on the specified host. """ return send_command("DISABLE_HOST_SVC_NOTIFICATIONS", command_file, timestamp, host_name) def delay_svc_notification( host_name, service_description, notification_time, command_file=None, timestamp=0 ): """ Delays the next notification for a parciular service until "notification_time". The "notification_time" argument is specified in time_t format (seconds since the UNIX epoch). Note that this will only have an affect if the service stays in the same problem state that it is currently in. If the service changes to another state, a new notification may go out before the time you specify in the "notification_time" argument. """ return send_command("DELAY_SVC_NOTIFICATION", command_file, timestamp, host_name, service_description, notification_time) def delay_host_notification( host_name, notification_time, command_file=None, timestamp=0 ): """ Delays the next notification for a parciular service until "notification_time". The "notification_time" argument is specified in time_t format (seconds since the UNIX epoch). Note that this will only have an affect if the service stays in the same problem state that it is currently in. If the service changes to another state, a new notification may go out before the time you specify in the "notification_time" argument. """ return send_command("DELAY_HOST_NOTIFICATION", command_file, timestamp, host_name, notification_time) def acknowledge_host_problem( host_name, sticky, notify, persistent, author, comment, command_file=None, timestamp=0 ): """ Allows you to acknowledge the current problem for the specified host. By acknowledging the current problem, future notifications (for the same host state) are disabled. If the "sticky" option is set to two (2), the acknowledgement will remain until the host returns to an UP state. Otherwise the acknowledgement will automatically be removed when the host changes state. If the "notify" option is set to one (1), a notification will be sent out to contacts indicating that the current host problem has been acknowledged. If the "persistent" option is set to one (1), the comment associated with the acknowledgement will survive across restarts of the Nagios process. If not, the comment will be deleted the next time Nagios restarts. """ return send_command("ACKNOWLEDGE_HOST_PROBLEM", command_file, timestamp, host_name, sticky, notify, persistent, author, comment) def del_svc_comment( comment_id, command_file=None, timestamp=0 ): """ Deletes a service comment. The id number of the comment that is to be deleted must be specified. """ return send_command("DEL_SVC_COMMENT", command_file, timestamp, comment_id) def acknowledge_svc_problem( host_name, service_description, sticky, notify, persistent, author, comment, command_file=None, timestamp=0 ): """ Allows you to acknowledge the current problem for the specified service. By acknowledging the current problem, future notifications (for the same servicestate) are disabled. If the "sticky" option is set to two (2), the acknowledgement will remain until the service returns to an OK state. Otherwise the acknowledgement will automatically be removed when the service changes state. If the "notify" option is set to one (1), a notification will be sent out to contacts indicating that the current service problem has been acknowledged. If the "persistent" option is set to one (1), the comment associated with the acknowledgement will survive across restarts of the Nagios process. If not, the comment will be deleted the next time Nagios restarts. """ return send_command("ACKNOWLEDGE_SVC_PROBLEM", command_file, timestamp, host_name, service_description, sticky, notify, persistent, author, comment) def start_executing_svc_checks( command_file=None, timestamp=0 ): """ Enables active checks of services on a program-wide basis. """ return send_command("START_EXECUTING_SVC_CHECKS", command_file, timestamp, ) def stop_executing_svc_checks( command_file=None, timestamp=0 ): """ Disables active checks of services on a program-wide basis. """ return send_command("STOP_EXECUTING_SVC_CHECKS", command_file, timestamp, ) def start_accepting_passive_svc_checks( command_file=None, timestamp=0 ): """ Enables passive service checks on a program-wide basis. """ return send_command("START_ACCEPTING_PASSIVE_SVC_CHECKS", command_file, timestamp, ) def stop_accepting_passive_svc_checks( command_file=None, timestamp=0 ): """ Disables passive service checks on a program-wide basis. """ return send_command("STOP_ACCEPTING_PASSIVE_SVC_CHECKS", command_file, timestamp, ) def enable_passive_svc_checks( host_name, service_description, command_file=None, timestamp=0 ): """ Enables passive checks for the specified service. """ return send_command("ENABLE_PASSIVE_SVC_CHECKS", command_file, timestamp, host_name, service_description) def disable_passive_svc_checks( host_name, service_description, command_file=None, timestamp=0 ): """ Disables passive checks for the specified service. """ return send_command("DISABLE_PASSIVE_SVC_CHECKS", command_file, timestamp, host_name, service_description) def enable_event_handlers( command_file=None, timestamp=0 ): """ Enables host and service event handlers on a program-wide basis. """ return send_command("ENABLE_EVENT_HANDLERS", command_file, timestamp, ) def disable_event_handlers( command_file=None, timestamp=0 ): """ Disables host and service event handlers on a program-wide basis. """ return send_command("DISABLE_EVENT_HANDLERS", command_file, timestamp, ) def enable_host_event_handler( host_name, command_file=None, timestamp=0 ): """ Enables the event handler for the specified host. """ return send_command("ENABLE_HOST_EVENT_HANDLER", command_file, timestamp, host_name) def enable_svc_check( host_name, service_description, command_file=None, timestamp=0 ): """ Enables active checks for a particular service. """ return send_command("ENABLE_SVC_CHECK", command_file, timestamp, host_name, service_description) def disable_host_event_handler( host_name, command_file=None, timestamp=0 ): """ Disables the event handler for the specified host. """ return send_command("DISABLE_HOST_EVENT_HANDLER", command_file, timestamp, host_name) def enable_svc_event_handler( host_name, service_description, command_file=None, timestamp=0 ): """ Enables the event handler for the specified service. """ return send_command("ENABLE_SVC_EVENT_HANDLER", command_file, timestamp, host_name, service_description) def disable_svc_event_handler( host_name, service_description, command_file=None, timestamp=0 ): """ Disables the event handler for the specified service. """ return send_command("DISABLE_SVC_EVENT_HANDLER", command_file, timestamp, host_name, service_description) def enable_host_check( host_name, command_file=None, timestamp=0 ): """ Enables (regularly scheduled and on-demand) active checks of the specified host. """ return send_command("ENABLE_HOST_CHECK", command_file, timestamp, host_name) def disable_host_check( host_name, command_file=None, timestamp=0 ): """ Disables (regularly scheduled and on-demand) active checks of the specified host. """ return send_command("DISABLE_HOST_CHECK", command_file, timestamp, host_name) def start_obsessing_over_svc_checks( command_file=None, timestamp=0 ): """ Enables processing of service checks via the OCSP command on a program-wide basis. """ return send_command("START_OBSESSING_OVER_SVC_CHECKS", command_file, timestamp, ) def stop_obsessing_over_svc_checks( command_file=None, timestamp=0 ): """ Disables processing of service checks via the OCSP command on a program-wide basis. """ return send_command("STOP_OBSESSING_OVER_SVC_CHECKS", command_file, timestamp, ) def start_obsessing_over_host_checks( command_file=None, timestamp=0 ): """ Enables processing of host checks via the OCHP command on a program-wide basis. """ return send_command("START_OBSESSING_OVER_HOST_CHECKS", command_file, timestamp, ) def stop_obsessing_over_host_checks( command_file=None, timestamp=0 ): """ Disables processing of host checks via the OCHP command on a program-wide basis. """ return send_command("STOP_OBSESSING_OVER_HOST_CHECKS", command_file, timestamp, ) def start_obsessing_over_host( host_name, command_file=None, timestamp=0 ): """ Enables processing of host checks via the OCHP command for the specified host. """ return send_command("START_OBSESSING_OVER_HOST", command_file, timestamp, host_name) def disable_svc_check( host_name, service_description, command_file=None, timestamp=0 ): """ Disables active checks for a particular service. """ return send_command("DISABLE_SVC_CHECK", command_file, timestamp, host_name, service_description) def stop_obsessing_over_host( host_name, command_file=None, timestamp=0 ): """ Disables processing of host checks via the OCHP command for the specified host. """ return send_command("STOP_OBSESSING_OVER_HOST", command_file, timestamp, host_name) def start_obsessing_over_svc( host_name, service_description, command_file=None, timestamp=0 ): """ Enables processing of service checks via the OCSP command for the specified service. """ return send_command("START_OBSESSING_OVER_SVC", command_file, timestamp, host_name, service_description) def stop_obsessing_over_svc( host_name, service_description, command_file=None, timestamp=0 ): """ Disables processing of service checks via the OCSP command for the specified service. """ return send_command("STOP_OBSESSING_OVER_SVC", command_file, timestamp, host_name, service_description) def enable_failure_prediction( command_file=None, timestamp=0 ): """ Enables failure prediction on a program-wide basis. This feature is not currently implemented in Nagios. """ return send_command("ENABLE_FAILURE_PREDICTION", command_file, timestamp, ) def disable_failure_prediction( command_file=None, timestamp=0 ): """ Disables failure prediction on a program-wide basis. This feature is not currently implemented in Nagios. """ return send_command("DISABLE_FAILURE_PREDICTION", command_file, timestamp, ) def enable_performance_data( command_file=None, timestamp=0 ): """ Enables the processing of host and service performance data on a program-wide basis. """ return send_command("ENABLE_PERFORMANCE_DATA", command_file, timestamp, ) def disable_performance_data( command_file=None, timestamp=0 ): """ Disables the processing of host and service performance data on a program-wide basis. """ return send_command("DISABLE_PERFORMANCE_DATA", command_file, timestamp, ) def start_executing_host_checks( command_file=None, timestamp=0 ): """ Enables active host checks on a program-wide basis. """ return send_command("START_EXECUTING_HOST_CHECKS", command_file, timestamp, ) def stop_executing_host_checks( command_file=None, timestamp=0 ): """ Disables active host checks on a program-wide basis. """ return send_command("STOP_EXECUTING_HOST_CHECKS", command_file, timestamp, ) def start_accepting_passive_host_checks( command_file=None, timestamp=0 ): """ Enables acceptance and processing of passive host checks on a program-wide basis. """ return send_command("START_ACCEPTING_PASSIVE_HOST_CHECKS", command_file, timestamp, ) def disable_notifications( command_file=None, timestamp=0 ): """ Disables host and service notifications on a program-wide basis. """ return send_command("DISABLE_NOTIFICATIONS", command_file, timestamp, ) def stop_accepting_passive_host_checks( command_file=None, timestamp=0 ): """ Disables acceptance and processing of passive host checks on a program-wide basis. """ return send_command("STOP_ACCEPTING_PASSIVE_HOST_CHECKS", command_file, timestamp, ) def enable_passive_host_checks( host_name, command_file=None, timestamp=0 ): """ Enables acceptance and processing of passive host checks for the specified host. """ return send_command("ENABLE_PASSIVE_HOST_CHECKS", command_file, timestamp, host_name) def disable_passive_host_checks( host_name, command_file=None, timestamp=0 ): """ Disables acceptance and processing of passive host checks for the specified host. """ return send_command("DISABLE_PASSIVE_HOST_CHECKS", command_file, timestamp, host_name) def enable_flap_detection( command_file=None, timestamp=0 ): """ Enables host and service flap detection on a program-wide basis. """ return send_command("ENABLE_FLAP_DETECTION", command_file, timestamp, ) def disable_flap_detection( command_file=None, timestamp=0 ): """ Disables host and service flap detection on a program-wide basis. """ return send_command("DISABLE_FLAP_DETECTION", command_file, timestamp, ) def enable_host_flap_detection( host_name, command_file=None, timestamp=0 ): """ Enables flap detection for the specified host. In order for the flap detection algorithms to be run for the host, flap detection must be enabled on a program-wide basis as well. """ return send_command("ENABLE_HOST_FLAP_DETECTION", command_file, timestamp, host_name) def enable_svc_flap_detection( host_name, service_description, command_file=None, timestamp=0 ): """ Enables flap detection for the specified service. In order for the flap detection algorithms to be run for the service, flap detection must be enabled on a program-wide basis as well. """ return send_command("ENABLE_SVC_FLAP_DETECTION", command_file, timestamp, host_name, service_description) def disable_host_flap_detection( host_name, command_file=None, timestamp=0 ): """ Disables flap detection for the specified host. """ return send_command("DISABLE_HOST_FLAP_DETECTION", command_file, timestamp, host_name) def disable_service_flap_detection( host_name, service_description, command_file=None, timestamp=0 ): """ Disables flap detection for the specified service. """ return send_command("DISABLE_SERVICE_FLAP_DETECTION", command_file, timestamp, host_name, service_description) def enable_hostgroup_svc_notifications( hostgroup_name, command_file=None, timestamp=0 ): """ Enables notifications for all services that are associated with hosts in a particular hostgroup. This does not enable notifications for the hosts in the hostgroup - see the ENABLE_HOSTGROUP_HOST_NOTIFICATIONS command for that. In order for notifications to be sent out for these services, notifications must be enabled on a program-wide basis as well. """ return send_command("ENABLE_HOSTGROUP_SVC_NOTIFICATIONS", command_file, timestamp, hostgroup_name) def enable_notifications( command_file=None, timestamp=0 ): """ Enables host and service notifications on a program-wide basis. """ return send_command("ENABLE_NOTIFICATIONS", command_file, timestamp, ) def disable_hostgroup_svc_notifications( hostgroup_name, command_file=None, timestamp=0 ): """ Disables notifications for all services associated with hosts in a particular hostgroup. This does not disable notifications for the hosts in the hostgroup - see the DISABLE_HOSTGROUP_HOST_NOTIFICATIONS command for that. """ return send_command("DISABLE_HOSTGROUP_SVC_NOTIFICATIONS", command_file, timestamp, hostgroup_name) def enable_hostgroup_host_notifications( hostgroup_name, command_file=None, timestamp=0 ): """ Enables notifications for all hosts in a particular hostgroup. This does not enable notifications for the services associated with the hosts in the hostgroup - see the ENABLE_HOSTGROUP_SVC_NOTIFICATIONS command for that. In order for notifications to be sent out for these hosts, notifications must be enabled on a program-wide basis as well. """ return send_command("ENABLE_HOSTGROUP_HOST_NOTIFICATIONS", command_file, timestamp, hostgroup_name) def disable_hostgroup_host_notifications( hostgroup_name, command_file=None, timestamp=0 ): """ Disables notifications for all hosts in a particular hostgroup. This does not disable notifications for the services associated with the hosts in the hostgroup - see the DISABLE_HOSTGROUP_SVC_NOTIFICATIONS command for that. """ return send_command("DISABLE_HOSTGROUP_HOST_NOTIFICATIONS", command_file, timestamp, hostgroup_name) def enable_hostgroup_svc_checks( hostgroup_name, command_file=None, timestamp=0 ): """ Enables active checks for all services associated with hosts in a particular hostgroup. """ return send_command("ENABLE_HOSTGROUP_SVC_CHECKS", command_file, timestamp, hostgroup_name) def disable_hostgroup_svc_checks( hostgroup_name, command_file=None, timestamp=0 ): """ Disables active checks for all services associated with hosts in a particular hostgroup. """ return send_command("DISABLE_HOSTGROUP_SVC_CHECKS", command_file, timestamp, hostgroup_name) def enable_hostgroup_host_checks( hostgroup_name, command_file=None, timestamp=0 ): """ Enables active checks for all hosts in a particular hostgroup. """ return send_command("ENABLE_HOSTGROUP_HOST_CHECKS", command_file, timestamp, hostgroup_name) def disable_hostgroup_host_checks( hostgroup_name, command_file=None, timestamp=0 ): """ Disables active checks for all hosts in a particular hostgroup. """ return send_command("DISABLE_HOSTGROUP_HOST_CHECKS", command_file, timestamp, hostgroup_name) def enable_hostgroup_passive_host_checks( hostgroup_name, command_file=None, timestamp=0 ): """ Enables passive checks for all hosts in a particular hostgroup. """ return send_command("ENABLE_HOSTGROUP_PASSIVE_HOST_CHECKS", command_file, timestamp, hostgroup_name) def disable_hostgroup_passive_host_checks( hostgroup_name, command_file=None, timestamp=0 ): """ Disables passive checks for all hosts in a particular hostgroup. """ return send_command("DISABLE_HOSTGROUP_PASSIVE_HOST_CHECKS", command_file, timestamp, hostgroup_name) def enable_hostgroup_passive_svc_checks( hostgroup_name, command_file=None, timestamp=0 ): """ Enables passive checks for all services associated with hosts in a particular hostgroup. """ return send_command("ENABLE_HOSTGROUP_PASSIVE_SVC_CHECKS", command_file, timestamp, hostgroup_name) def restart_program( command_file=None, timestamp=0 ): """ Restarts the Nagios process. """ return send_command("RESTART_PROGRAM", command_file, timestamp, ) def disable_hostgroup_passive_svc_checks( hostgroup_name, command_file=None, timestamp=0 ): """ Disables passive checks for all services associated with hosts in a particular hostgroup. """ return send_command("DISABLE_HOSTGROUP_PASSIVE_SVC_CHECKS", command_file, timestamp, hostgroup_name) def enable_servicegroup_svc_notifications( servicegroup_name, command_file=None, timestamp=0 ): """ Enables notifications for all services that are members of a particular servicegroup. In order for notifications to be sent out for these services, notifications must also be enabled on a program-wide basis. """ return send_command("ENABLE_SERVICEGROUP_SVC_NOTIFICATIONS", command_file, timestamp, servicegroup_name) def disable_servicegroup_svc_notifications( servicegroup_name, command_file=None, timestamp=0 ): """ Disables notifications for all services that are members of a particular servicegroup. """ return send_command("DISABLE_SERVICEGROUP_SVC_NOTIFICATIONS", command_file, timestamp, servicegroup_name) def enable_servicegroup_host_notifications( servicegroup_name, command_file=None, timestamp=0 ): """ Enables notifications for all hosts that have services that are members of a particular servicegroup. In order for notifications to be sent out for these hosts, notifications must also be enabled on a program-wide basis. """ return send_command("ENABLE_SERVICEGROUP_HOST_NOTIFICATIONS", command_file, timestamp, servicegroup_name) def disable_servicegroup_host_notifications( servicegroup_name, command_file=None, timestamp=0 ): """ Disables notifications for all hosts that have services that are members of a particular servicegroup. """ return send_command("DISABLE_SERVICEGROUP_HOST_NOTIFICATIONS", command_file, timestamp, servicegroup_name) def enable_servicegroup_svc_checks( servicegroup_name, command_file=None, timestamp=0 ): """ Enables active checks for all services in a particular servicegroup. """ return send_command("ENABLE_SERVICEGROUP_SVC_CHECKS", command_file, timestamp, servicegroup_name) def disable_servicegroup_svc_checks( servicegroup_name, command_file=None, timestamp=0 ): """ Disables active checks for all services in a particular servicegroup. """ return send_command("DISABLE_SERVICEGROUP_SVC_CHECKS", command_file, timestamp, servicegroup_name) def enable_servicegroup_host_checks( servicegroup_name, command_file=None, timestamp=0 ): """ Enables active checks for all hosts that have services that are members of a particular hostgroup. """ return send_command("ENABLE_SERVICEGROUP_HOST_CHECKS", command_file, timestamp, servicegroup_name) def disable_servicegroup_host_checks( servicegroup_name, command_file=None, timestamp=0 ): """ Disables active checks for all hosts that have services that are members of a particular hostgroup. """ return send_command("DISABLE_SERVICEGROUP_HOST_CHECKS", command_file, timestamp, servicegroup_name) def enable_servicegroup_passive_svc_checks( servicegroup_name, command_file=None, timestamp=0 ): """ Enables the acceptance and processing of passive checks for all services in a particular servicegroup. """ return send_command("ENABLE_SERVICEGROUP_PASSIVE_SVC_CHECKS", command_file, timestamp, servicegroup_name) pynag-0.9.1+dfsg.orig/pynag/Control/Command/__init__.py0000664000175000017500000000766012354645173021773 0ustar clintclint# -*- coding: utf-8 -*- # # pynag - Python Nagios plug-in and configuration environment # Copyright (C) 2011 Pall Sigurdsson # # 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. """ The Command module is capable of sending commands to Nagios via the configured communication path. """ import time from pynag.Parsers import config, mk_livestatus from pynag.Utils import PynagError import autogenerated_commands as __autogenerated_commands path_to_command_file = None def find_command_file(cfg_file=None): """ Returns path to nagios command_file by looking at what is defined in nagios.cfg Args: cfg_file (str): Path to nagios.cfg configuration file Returns: str. Path to the nagios command file Raises: PynagError """ global path_to_command_file # If we have been called before, the path should be cached in a global variable if path_to_command_file: return path_to_command_file # If we reach here, we have to parse nagios.cfg to find path # to our command file c = config(cfg_file=cfg_file) command_file = c.get_cfg_value('command_file') if not command_file: raise PynagError("command_file not found in your nagios.cfg (%s)" % c.cfg_file) path_to_command_file = command_file return command_file def send_command(command_id, command_file=None, timestamp=0, *args): """ Send one specific command to the command pipe Args: command_id (str): Identifier string of the nagios command Eg: ``ADD_SVC_COMMENT`` command_file (str): Path to nagios command file. timestamp (int): Timestamp in time_t format of the time when the external command was sent to the command file. If 0 of None, it will be set to time.time(). Default 0. args: Command arguments. """ if not timestamp or timestamp == 0: timestamp = time.time() if not command_file: command_file = find_command_file() command_arguments = map(str, args) command_arguments = ";".join(command_arguments) command_string = "[%s] %s;%s" % (timestamp, command_id, command_arguments) try: _write_to_command_file(command_file, command_string) except Exception: _write_to_livestatus(command_string) def _write_to_livestatus(command_string): """ Send a specific command to mk-livestatus See http://nagios.sourceforge.net/docs/nagioscore/3/en/extcommands.html for details Args: command_string (str): String the will be sent as a livestatus command. """ livestatus = mk_livestatus() livestatus.query("COMMAND %s" % command_string) def _write_to_command_file(command_file, command_string=""): """ Send a specific command to nagios command pipe. See http://nagios.sourceforge.net/docs/nagioscore/3/en/extcommands.html for details Args: command_file (str): Path to the Nagios command file. command_string (str): String that will be written to the command file and executed as a Nagios external command. """ f = open(command_file, 'a') f.write(command_string + '\n') f.close() # Everything in autogenerated_commands gets imported directly into this module. filename = __autogenerated_commands.__file__ if filename.endswith('.pyc'): filename = filename.strip('c') execfile(filename) pynag-0.9.1+dfsg.orig/pynag/Control/Command/generate_external_command.py0000664000175000017500000000443612354645173025424 0ustar clintclint#!/usr/bin/python # # This script will autogenerate python functions to communicate with the python command file. # input to the program is the documentation from nagios import BeautifulSoup import sys import textwrap for filename in sys.argv[1:]: html = open(filename).read() soup = BeautifulSoup.BeautifulSoup(html, convertEntities='html') # First get the command format we need: tmp = soup.find('td', {'class': 'MediumBold'}) command_format = tmp.findNext().getText() # command_format should look something like this: # u'ENABLE_SVC_EVENT_HANDLER;<host_name>;<service_description>' # there is a bug in the documentation, where one semicolon is missing, lets adjust: command_format = command_format.replace('><', '>;<') command_format = command_format.replace('service_desription', 'service_description') command_format = command_format.replace('<', '',).replace('>', '') command_format = command_format.split(';') func = command_format[0] # Lets convert function name to lowercase to be polite args = command_format[1:] # Now we have the command format, lets find the description text description = tmp.findParent().findNextSibling().findNextSibling().findNextSibling().findNextSibling().getText() # Let's PEP8 the description wrapper = textwrap.TextWrapper() wrapper.initial_indent = ' ' wrapper.subsequent_indent = ' ' wrapper.width = 68 description = '\n'.join(['\n'.join(wrapper.wrap(block)) for block in description.splitlines()]) strFunction = """ def {function_name_lower}( {arguments} ): \"\"\" {description} \"\"\" return send_command("{function_name}", command_file, timestamp, {function_arguments_linebroken})""" args.extend(['command_file=None', 'timestamp=0']) defSpaces = ' ' * (5 + len(func)) returnSpaces = ' ' * 24 argSplitter = ',\n ' strFunction = strFunction.format( function_name_lower=func.lower(), arguments=argSplitter.join(args), description=description, function_name=func, function_arguments_linebroken=',\n '.join(args[0:-2]) ) strFunction = strFunction.replace('(, ', '(') print strFunction pynag-0.9.1+dfsg.orig/pynag/Control/__init__.py0000664000175000017500000001736712354645173020422 0ustar clintclint# -*- coding: utf-8 -*- # # pynag - Python Nagios plug-in and configuration environment # Copyright (C) 2010 Drew Stinnet # # 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. """ The Control module includes classes to control the Nagios service and the Command submodule wraps Nagios commands. """ import os import pynag.Utils from warnings import warn from pynag.Utils import PynagError, runCommand class daemon(object): """ Control the nagios daemon through python >>> from pynag.Control import daemon >>> >>> d = daemon() # doctest: +SKIP >>> d.restart() # doctest: +SKIP """ SYSV_INIT_SCRIPT = 1 SYSV_INIT_SERVICE = 2 SYSTEMD = 3 systemd_service_path = "/usr/lib/systemd/system" def __init__(self, nagios_bin="/usr/bin/nagios", nagios_cfg="/etc/nagios/nagios.cfg", nagios_init=None, sudo=True, shell=None, service_name="nagios", nagios_config=None ): self.nagios_bin = nagios_bin self.nagios_cfg = nagios_cfg self.nagios_init = nagios_init self.service_name = service_name self.sudo = sudo self.stdout = "" self.stderr = "" self.nagios_config = nagios_config self._deprecate_sudo() self.method = self._guess_method() if shell: warn("shell is deprecated and not necessary anymore", FutureWarning) if nagios_init: warn("nagios_init is deprecated, use service_name instead", FutureWarning) def verify_config(self): """ Run nagios -v config_file to verify that the conf is working :returns: True -- if pynag.Utils.runCommand() returns 0, else None """ cmd = [self.nagios_bin, "-v", self.nagios_cfg] if self.sudo: cmd.insert(0, 'sudo') result, self.stdout, self.stderr = runCommand(cmd, shell=False) if result == 0: return True else: return None def running(self): """ Checks if the daemon is running :returns: Whether or not the daemon is running :rtype: bool """ if self.method == daemon.SYSV_INIT_SCRIPT or \ self.method == daemon.SYSV_INIT_SERVICE: if self.nagios_config is None: self.nagios_config = pynag.Parsers.config() if self.nagios_config._get_pid(): return True elif self.method == daemon.SYSTEMD: result = runCommand(["systemctl", "is-active", self.service_name], shell=False) if result[0] == 0: return True return False def restart(self): """ Restarts Nagios via it's init script. :returns: Return code of the restart command ran by pynag.Utils.runCommand() :rtype: int """ if self.method == daemon.SYSV_INIT_SCRIPT: cmd = [self.nagios_init, "restart"] else: cmd = ["service", self.service_name, "restart"] if self.sudo: cmd.insert(0, 'sudo') result, self.stdout, self.stderr = runCommand(cmd, shell=False) return result def status(self): """ Obtain the status of the Nagios service. :returns: Return code of the status command ran by pynag.Utils.runCommand() :rtype: int """ if self.method == daemon.SYSV_INIT_SCRIPT: cmd = [self.nagios_init, "status"] else: cmd = ["service", self.service_name, "status"] if self.sudo: cmd.insert(0, 'sudo') result, self.stdout, self.stderr = runCommand(cmd, shell=False) return result def start(self): """ Start the Nagios service. :returns: Return code of the start command ran by pynag.Utils.runCommand() :rtype: int """ if self.method == daemon.SYSV_INIT_SCRIPT: cmd = [self.nagios_init, "start"] else: cmd = ["service", self.service_name, "start"] if self.sudo: cmd.insert(0, 'sudo') result, self.stdout, self.stderr = runCommand(cmd, shell=False) return result def stop(self): """ Stop the Nagios service. :returns: Return code of the stop command ran by pynag.Utils.runCommand() :rtype: int """ if self.method == daemon.SYSV_INIT_SCRIPT: cmd = [self.nagios_init, "stop"] else: cmd = ["service", self.service_name, "stop"] if self.sudo: cmd.insert(0, 'sudo') result, self.stdout, self.stderr = runCommand(cmd, shell=False) return result def reload(self): """ Reloads Nagios. :returns: Return code of the reload command ran by pynag.Utils.runCommand() :rtype: int """ if self.method == daemon.SYSV_INIT_SCRIPT: cmd = [self.nagios_init, "reload"] else: cmd = ["service", self.service_name, "reload"] if self.sudo: cmd.insert(0, 'sudo') result, self.stdout, self.stderr = runCommand(cmd, shell=False) return result def _guess_method(self): """ Guesses whether to run via SYSV INIT script og via systemd. Will also modify nagios_init="service nagios" and set service_name=nagios and method to SYSV_INIT_SCRIPT :returns: ``deamon.SYSTEMD`` :rtype: int """ if self.nagios_init and os.path.exists(self.nagios_init): return daemon.SYSV_INIT_SCRIPT elif self.nagios_init and self.nagios_init.split(None, 1)[0].endswith("service"): self.service_name = self.nagios_init.split(None, 1)[1] return daemon.SYSV_INIT_SERVICE elif os.path.exists("%s/%s.service" % (daemon.systemd_service_path, self.service_name)): return daemon.SYSTEMD else: raise PynagError("Unable to detect daemon method, " "could not find init script or " "systemd unit file") def _deprecate_sudo(self): """ Warns with a FutureWarning if sudo is being used in nagios_init or nagios_bin. It will also remove sudo from the command line and set sudo to True """ if self.nagios_init and \ self.nagios_init.split(None, 1)[0].endswith("sudo"): self.sudo = True self.nagios_init = self.nagios_init.split(None, 1)[1] warn("nagios_init command line with sudo is deprecated, please " "use sudo=True for daemon()", FutureWarning) if self.nagios_bin and \ self.nagios_bin.split(None, 1)[0].endswith("sudo"): self.sudo = True self.nagios_bin = self.nagios_bin.split(None, 1)[1] warn("nagios_bin command line with sudo is deprecated, please " "use sudo=True for daemon()", FutureWarning) pynag-0.9.1+dfsg.orig/pynag/__init__.py0000664000175000017500000000171012370025102016741 0ustar clintclint# -*- coding: utf-8 -*- # # pynag - Python Nagios plug-in and configuration environment # Copyright (C) 2010 Drew Stinnet # # 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. import sys import os import re from optparse import OptionParser from pynag import Plugins Plugin = Plugins.simple __version__ = '0.9.1' pynag-0.9.1+dfsg.orig/pynag/Plugins/0000775000175000017500000000000012370025176016265 5ustar clintclintpynag-0.9.1+dfsg.orig/pynag/Plugins/new_threshold_syntax.py0000664000175000017500000001313312353523627023120 0ustar clintclint# -*- coding: utf-8 -*- # # pynag - Python Nagios plug-in and configuration environment # Copyright (C) 2012 Pall Sigurdsson # # 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. """ These are helper functions and implementation of proposed new threshold format for nagios plugins according to: http://nagiosplugins.org/rfc/new_threshold_syntax In short, plugins should implement a --threshold option which takes argument in form of: # metric={metric},ok={range},warn={range},crit={range},unit={unit}prefix={SI prefix} Example: --treshold metric=load1,ok=0..5,warning=5..10,critical=10..inf """ from __future__ import print_function from __future__ import unicode_literals from __future__ import absolute_import import pynag.Plugins from pynag.Utils import PynagError def check_threshold(value, ok=None, warning=None, critical=None): """ Checks value against warning/critical and returns Nagios exit code. Format of range_threshold is according to: http://nagiosplugins.org/rfc/new_threshold_syntax This function returns (in order of appearance): int(0) - If no levels are specified, return OK int(3) - If any invalid input provided, return UNKNOWN int(0) - If an ok level is specified and value is within range, return OK int(2) - If a critical level is specified and value is within range, return CRITICAL int(1) - If a warning level is specified and value is within range, return WARNING int(2) - If an ok level is specified, return CRITICAL int(0) - Otherwise return OK Arguments: value -- value to check ok -- ok range warning -- warning range critical -- critical range # Example Usage: >>> check_threshold(88, warning="90..95", critical="95..100") 0 >>> check_threshold(92, warning="90..95", critical="95..100") 1 >>> check_threshold(96, warning="90..95", critical="95..100") 2 """ try: # 1 - If no levels are specified, return OK if not ok and not warning and not critical: return pynag.Plugins.OK # 2 - If an ok level is specified and value is within range, return OK if ok and check_range(value, ok): return pynag.Plugins.OK # 3 - If a critical level is specified and value is within range, return CRITICAL if critical and check_range(value, critical): return pynag.Plugins.CRITICAL # 4 - If a warning level is specified and value is within range, return WARNING if warning and check_range(value, warning): return pynag.Plugins.WARNING # 5 - If an ok level is specified, return CRITICAL if ok: return pynag.Plugins.CRITICAL # 6 - Otherwise return OK return pynag.Plugins.OK except Exception: # Return unknown if any problem occurs, including invalid input return pynag.Plugins.UNKNOWN def check_range(value, range): """ Returns True if value is within range, else False Arguments: value -- Numerical value to check, can be any number range -- string in the format of "start..end" Examples: >>> check_range(5, "0..10") True >>> check_range(11, "0..10") False """ if not isinstance(range, basestring) or range == '': raise PynagError('range must be a string') # value must be numeric, so we try to convert it to float value = float(value) # If range does not contain ".." then we assume its the older style of # ranges (either a plain number or the start:end syntax) if '..' not in range: return not pynag.Plugins.check_range(value=value, range_threshold=range) # If range starts with ^ we the conditions are inverted if range[0] == '^': return not check_range(value, range[1:]) # Start and end must be defined tmp = range.split('..') if len(tmp) != 2: raise PynagError('Invalid Format for threshold range: "%s"' % range) start, end = tmp if not start in ('inf', '-inf'): start = float(start) if start > value: return False if not end == 'inf': end = float(end) if end < value: return False return True def parse_threshold(threshold): """ takes a threshold string as an input and returns a hash map of options and values Examples: >>> parse_threshold('metric=disk_usage,ok=0..90,warning=90..95,critical=95.100') {'thresholds': [(0, '0..90'), (1, '90..95'), (2, '95.100')], 'metric': 'disk_usage'} """ tmp = threshold.lower().split(',') parsed_thresholds = [] results = {} results['thresholds'] = parsed_thresholds for i in tmp: if i.find('=') < 1: raise PynagError("Invalid input: '%s' is not of the format key=value" % i) key, value = i.split('=', 1) if key in pynag.Plugins.state.keys(): parsed_thresholds.append((pynag.Plugins.state[key], value)) else: results[key] = value return results pynag-0.9.1+dfsg.orig/pynag/Plugins/__init__.py0000664000175000017500000012614412353523627020413 0ustar clintclint# -*- coding: utf-8 -*- # # pynag - Python Nagios plug-in and configuration environment # Copyright (C) 2010 Drew Stinnet # # 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. """ Python Nagios extensions """ from __future__ import print_function from __future__ import unicode_literals from __future__ import absolute_import import sys import os import traceback import signal from platform import node from optparse import OptionParser, OptionGroup from pynag.Utils import PerfData, PynagError, reconsile_threshold, runCommand from pynag.Parsers import ExtraOptsParser import pynag.Utils from . import new_threshold_syntax # Map the return codes OK = 0 WARNING = 1 CRITICAL = 2 UNKNOWN = 3 ok, warning, critical, unknown = 0, 1, 2, 3 state = {} state['ok'] = 0 state['warning'] = 1 state['warn'] = 1 state['w'] = 1 state['critical'] = 2 state['crit'] = 2 state['c'] = 2 state['unknown'] = 3 state['u'] = 3 state['UP'] = 0 state['DOWN'] = 2 state['UNREACHABLE'] = 2 state['OK'] = 0 state['WARNING'] = 1 state['CRITICAL'] = 2 state['UNKNOWN'] = 3 state_text = {} state_text[ok] = 'OK' state_text[warning] = 'Warning' state_text[critical] = "Critical" state_text[unknown] = "Unknown" class simple: """ Nagios plugin helper library based on Nagios::Plugin Sample usage from pynag.Plugins import WARNING, CRITICAL, OK, UNKNOWN, simple as Plugin # Create plugin object np = Plugin() # Add arguments np.add_arg("d", "disk") # Do activate plugin np.activate() ... check stuff, np['disk'] to address variable assigned above... # Add a status message and severity np.add_message( WARNING, "Disk nearing capacity" ) # Get parsed code and messages (code, message) = np.check_messages() # Return information and exit nagios_exit(code, message) """ def __init__(self, shortname=None, version=None, blurb=None, extra=None, url=None, license=None, plugin=None, timeout=15, must_threshold=True): # this is the custom parser self.extra_list_optional = [] self.extra_list_required = [] # Set the option parser stuff here self.parser = OptionParser() # Variables we'll get later self.opts = None self.must_threshold = must_threshold self.data = {} self.data['perfdata'] = [] self.data['messages'] = { OK: [], WARNING: [], CRITICAL: [], UNKNOWN: []} self.data['threshhold'] = None # Error mappings, for easy access self.errors = {"OK": 0, "WARNING": 1, "CRITICAL": 2, "UNKNOWN": 3, } self.status_text = { 0: "OK", 1: "WARNING", 2: "CRITICAL", 3: "UNKNOWN", } # Shortname creation if not shortname: self.data['shortname'] = os.path.basename("%s" % sys.argv[0]) else: self.data['shortname'] = shortname def add_arg(self, spec_abbr, spec, help_text, required=1, action="store"): """ Add an argument to be handled by the option parser. By default, the arg is not required. required = optional parameter action = [store, append, store_true] """ self.parser.add_option("-%s" % spec_abbr, "--%s" % spec, dest="%s" % spec, help=help_text, metavar="%s" % spec.upper(), action=action) if required: self.extra_list_required.append(spec) else: self.extra_list_optional.append(spec) def activate(self): """ Parse out all command line options and get ready to process the plugin. This should be run after argument preps """ timeout = None verbose = 0 self.parser.add_option( "-v", "--verbose", dest="verbose", help="Verbosity Level", metavar="VERBOSE", default=0) self.parser.add_option( "-H", "--host", dest="host", help="Target Host", metavar="HOST") self.parser.add_option( "-t", "--timeout", dest="timeout", help="Connection Timeout", metavar="TIMEOUT") if self.must_threshold is True: self.parser.add_option( "-c", "--critical", dest="critical", help="Critical Threshhold", metavar="CRITICAL") self.parser.add_option( "-w", "--warning", dest="warning", help="Warn Threshhold", metavar="WARNING") (options, args) = self.parser.parse_args() # Set verbosity level if int(options.verbose) in (0, 1, 2, 3): self.data['verbosity'] = int(options.verbose) else: self.data['verbosity'] = verbose # Ensure the hostname is set if options.host: self.data['host'] = options.host # Set timeout if options.timeout: self.data['timeout'] = options.timeout else: self.data['timeout'] = timeout if self.must_threshold is True and not options.critical and not options.warning: self.parser.error( "You must provide a WARNING and/or CRITICAL value") # Set Critical; if the option is available in the plugin if hasattr(options, 'critical'): self.data['critical'] = options.critical else: self.data['critical'] = None # Set Warn; if the option is available in the plugin if hasattr(options, 'warning'): self.data['warning'] = options.warning else: self.data['warning'] = None # Ensurethat the extra items are provided for extra_item in self.extra_list_required: if not options.__dict__[extra_item]: self.parser.error("option '%s' is required" % extra_item) # Put the remaining values into the data dictionary for key, value in options.__dict__.items(): if key in (self.extra_list_required + self.extra_list_optional): self.data[key] = value def add_perfdata(self, label, value, uom=None, warn=None, crit=None, minimum=None, maximum=None): """ Append perfdata string to the end of the message """ # Append perfdata (assume multiple) self.data[ 'perfdata'].append({'label': label, 'value': value, 'uom': uom, 'warn': warn, 'crit': crit, 'min': minimum, 'max': maximum}) def check_perfdata_as_metric(self): for perfdata in self.data['perfdata']: self._add_message_from_range_check( perfdata['value'], perfdata['warn'], perfdata['crit'], perfdata['label'] ) self._check_messages_and_exit() def _add_message_from_range_check(self, value, warning=None, critical=None, label='data'): if not (critical or warning): critical = self.data['critical'] warning = self.data['warning'] if critical and not self._range_checker(value, critical): self.add_message(CRITICAL, "%s %s is outside critical range: %s" % (label, value, critical)) elif warning and not self._range_checker(value, warning): self.add_message( WARNING, "%s %s is outside warning range: %s" % (label, value, warning)) else: self.add_message(OK, "%s %s is inside warning=%s and critical=%s" % (label, value, warning, critical)) def _check_messages_and_exit(self): # Get all messages appended and exit code (code, message) = self.check_messages() # Exit with appropriate exit status and message self.nagios_exit(code, message) def check_range(self, value): """ Check if a value is within a given range. This should replace change_threshold eventually. Exits with appropriate exit code given the range. Taken from: http://nagiosplug.sourceforge.net/developer-guidelines.html Range definition Generate an alert if x... 10 < 0 or > 10, (outside the range of {0 .. 10}) 10: < 10, (outside {10 .. #}) ~:10 > 10, (outside the range of {-# .. 10}) 10:20 < 10 or > 20, (outside the range of {10 .. 20}) @10:20 # 10 and # 20, (inside the range of {10 .. 20}) """ self.hr_range = "" self._add_message_from_range_check(value) self._check_messages_and_exit() def _range_checker(self, value, range_threshold): """ deprecated. Use pynag.Plugins.check_range() """ return check_range(value=value, range_threshold=range_threshold) def send_nsca(self, *args, **kwargs): """ Wrapper around pynag.Utils.send_nsca - here for backwards compatibility """ # Previous versions of this method had a typo where one argument was called # ncsahost instead of nscahost. We will maintain backwards compatibility here. if 'ncsahost' in kwargs and not 'nscahost' in kwargs: kwargs['nscahost'] = kwargs['ncsahost'] del kwargs['ncsahost'] pynag.Utils.send_nsca(*args, **kwargs) return 0 def nagios_exit(self, code_text, message): """ Exit with exit_code, message, and optionally perfdata """ # Change text based codes to int code = self.code_string2int(code_text) # This should be one line (or more in nagios 3) print("%s: %s %s" % (self.status_text[code], message, self.perfdata_string())) sys.exit(code) def perfdata_string(self): # Append perfdata to the message, if perfdata exists if self.data['perfdata']: append = '|' else: append = '' for pd in self.data['perfdata']: append += " '%s'=%s%s;%s;%s;%s;%s" % ( pd['label'], pd['value'], pd['uom'] or '', pd['warn'] or '', pd['crit'] or '', pd['min'] or '', pd['max'] or '') return append def add_message(self, code, message): """ Add a message with code to the object. May be called multiple times. The messages added are checked by check_messages, following. Only CRITICAL, WARNING, OK and UNKNOWN are accepted as valid codes. """ # Change text based codes to int code = self.code_string2int(code) self.data['messages'][code].append(message) def check_messages(self, joinstr=" ", joinallstr=None): """ Check the current set of messages and return an appropriate nagios return code and/or a result message. In scalar context, returns only a return code; in list context returns both a return code and an output message, suitable for passing directly to nagios_exit() joinstr = string A string used to join the relevant array to generate the message string returned in list context i.e. if the 'critical' array is non-empty, check_messages would return: joinstr.join(critical) joinallstr = string By default, only one set of messages are joined and returned in the result message i.e. if the result is CRITICAL, only the 'critical' messages are included in the result; if WARNING, only the 'warning' messages are included; if OK, the 'ok' messages are included (if supplied) i.e. the default is to return an 'errors-only' type message. If joinallstr is supplied, however, it will be used as a string to join the resultant critical, warning, and ok messages together i.e. all messages are joined and returned. """ # Check for messages in unknown, critical, warning, ok to determine # code keys = self.data['messages'].keys() keys.sort(reverse=True) code = UNKNOWN for code in keys: if len(self.data['messages'][code]): break # Create the relevant message for the most severe code if joinallstr is None: message = joinstr.join(self.data['messages'][code]) # Join all strings whether OK, WARN... else: message = "" for c in keys: if len(self.data['messages'][c]): message += joinallstr.join( self.data['messages'][c]) + joinallstr return code, message.rstrip(joinallstr) def code_string2int(self, code_text): """ Changes CRITICAL, WARNING, OK and UNKNOWN code_text to integer representation for use within add_message() and nagios_exit() """ # If code_text is a string, convert to the int try: code = self.errors[str(code_text)] except KeyError: code = code_text return code def __setitem__(self, key, item): self.data[key] = item def __getitem__(self, key): if key in self.data: return self.data[key] else: return None def check_threshold(value, warning=None, critical=None): """ Checks value against warning/critical and returns Nagios exit code. Format of range_threshold is according to: http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT Returns (in order of appearance): UNKNOWN int(3) -- On errors or bad input CRITICAL int(2) -- if value is within critical threshold WARNING int(1) -- If value is within warning threshold OK int(0) -- If value is outside both tresholds Arguments: value -- value to check warning -- warning range critical -- critical range # Example Usage: >>> check_threshold(88, warning="0:90", critical="0:95") 0 >>> check_threshold(92, warning=":90", critical=":95") 1 >>> check_threshold(96, warning=":90", critical=":95") 2 """ if critical and not check_range(value, critical): return CRITICAL elif warning and not check_range(value, warning): return WARNING else: return OK def check_range(value, range_threshold=None): """ Returns True if value is within range_threshold. Format of range_threshold is according to: http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT Arguments: value -- Numerical value to check (i.e. 70 ) range -- Range to compare against (i.e. 0:90 ) Returns: True -- If value is inside the range False -- If value is outside the range (alert if this happens) False -- if invalid value is specified Summary from plugin developer guidelines: --------------------------------------------------------- x Generate an alert if x... --------------------------------------------------------- 10 < 0 or > 10, (outside the range of {0 .. 10}) 10: < 10, (outside {10 .. ∞}) ~:10 > 10, (outside the range of {-∞ .. 10}) 10:20 < 10 or > 20, (outside the range of {10 .. 20}) @10:20 ≥ 10 and ≤ 20, (inside the range of {10 .. 20}) 10 < 0 or > 10, (outside the range of {0 .. 10}) --------------------------------------------------------- # Example runs for doctest, False should mean alert >>> check_range(78, "90") # Example disk is 78% full, threshold is 90 True >>> check_range(5, 10) # Everything between 0 and 10 is True True >>> check_range(0, 10) # Everything between 0 and 10 is True True >>> check_range(10, 10) # Everything between 0 and 10 is True True >>> check_range(11, 10) # Everything between 0 and 10 is True False >>> check_range(-1, 10) # Everything between 0 and 10 is True False >>> check_range(-1, "~:10") # Everything Below 10 True >>> check_range(11, "10:") # Everything above 10 is True True >>> check_range(1, "10:") # Everything above 10 is True False >>> check_range(0, "5:10") # Everything between 5 and 10 is True False >>> check_range(0, "@5:10") # Everything outside 5:10 is True True >>> check_range(None) # Return False if value is not a number False >>> check_range("10000000 PX") # What happens on invalid input False >>> check_range("10000000", "invalid:invalid") # What happens on invalid range Traceback (most recent call last): ... PynagError: Invalid threshold format: invalid:invalid """ # Return false if value is not a number try: float(value) except Exception: return False # if no range_threshold is provided, assume everything is ok if not range_threshold: range_threshold = '~:' range_threshold = str(range_threshold) # If range starts with @, then we do the opposite if range_threshold[0] == '@': return not check_range(value, range_threshold[1:]) if range_threshold.find(':') > -1: (start, end) = (range_threshold.split(':', 1)) # we get here if ":" was not provided in range_threshold else: start = '' end = range_threshold # assume infinity if start is not provided if start == '~': start = None # assume start=0 if start is not provided if start == '': start = 0 # assume infinity if end is not provided if end == '': end = None try: # start is defined and value is lower than start if start is not None and float(value) < float(start): return False if end is not None and float(value) > float(end): return False except ValueError: raise PynagError("Invalid threshold format: %s" % range_threshold) return True class PluginHelper: """ PluginHelper takes away some of the tedious work of writing Nagios plugins. Primary features include: * Keep a collection of your plugin messages (queue for both summary and longoutput) * Keep record of exit status * Keep a collection of your metrics (for both perfdata and thresholds) * Automatic Command-line arguments * Make sure output of your plugin is within Plugin Developer Guidelines Usage: p = PluginHelper() p.status(warning) p.add_summary('Example Plugin with warning status') p.add_metric('cpu load', '90') p.exit() """ _nagios_status = -1 # exit status of the plugin _long_output = None # Long output of the plugin _summary = None # Summary of the plugin _perfdata = None # Performance and Threshold Metrics are stored here show_longoutput = True # If True, print longoutput show_perfdata = True # If True, print perfdata show_summary = True # If True, print Summary show_status_in_summary = True show_legacy = False # Deprecated, doesnt do anything verbose = False # Extra verbosity show_debug = False # Extra debugging # By default, plugins timeout right before nagios kills the plugin timeout = 58 thresholds = None # List of strings in the nagios threshold format options = None # OptionParser() options arguments = None # OptionParser() arguments def __init__(self): self._long_output = [] self._summary = [] self.thresholds = [] # Performance and Threshold Metrics are stored here self._perfdata = PerfData() self.parser = OptionParser() generic_group = OptionGroup(self.parser, "Generic Options") generic_group.add_option( '--timeout', help="Exit plugin with unknown status after x seconds", type='int', metavar='50', dest="timeout", default=self.timeout ) generic_group.add_option( '--threshold', default=[], help="Thresholds in standard nagios threshold format", metavar='range', dest="thresholds", action="append" ) generic_group.add_option( '--th', default=[], help="Same as --threshold", metavar='range', dest="thresholds", action="append" ) generic_group.add_option( '--extra-opts', help="Read options from an ini file. See http://nagiosplugins.org/extra-opts", metavar='@file', dest="extra_opts" ) generic_group.add_option( "-d", "--debug", dest="show_debug", help="Print debug info", metavar="d", action="store_true", default=self.show_debug ) # Display options are options that affect the output of the plugin # But usually not its function display_group = OptionGroup(self.parser, "Display Options") display_group.add_option( "-v", "--verbose", dest="verbose", help="Print more verbose info", metavar="v", action="store_true", default=self.verbose ) display_group.add_option( "--no-perfdata", dest="show_perfdata", help="Dont show any performance data", action="store_false", default=self.show_perfdata ) display_group.add_option( "--no-longoutput", dest="show_longoutput", help="Hide longoutput from the plugin output (i.e. only display first line of the output)", action="store_false", default=self.show_longoutput ) display_group.add_option( "--no-summary", dest="show_summary", help="Hide summary from plugin output", action="store_false", default=self.show_summary ) display_group.add_option( "--get-metrics", dest="get_metrics", help="Print all available metrics and exit (can be combined with --verbose)", action="store_true", default=False ) display_group.add_option( "--legacy", dest="show_legacy", help="Deprecated, do not use", action="store_true", default=self.show_legacy ) self.parser.add_option_group(generic_group) self.parser.add_option_group(display_group) def parse_arguments(self, argument_list=None): """ Parsers commandline arguments, prints error if there is a syntax error. Creates: self.options -- As created by OptionParser.parse() self.arguments -- As created by OptionParser.parse() Arguments: argument_list -- By default use sys.argv[1:], override only if you know what you are doing. Returns: None """ self.options, self.arguments = self.parser.parse_args( args=argument_list) extra_opts = self.options.extra_opts if extra_opts is not None: # --extra-opts was specified if extra_opts == '': # --extra-opts= with no value. section_name = None config_file = None elif '@' in extra_opts: # filename was specified section_name, config_file = extra_opts.split('@', 1) else: # Only section was specified section_name = extra_opts config_file = None values = self.get_default_values(section_name, config_file) self.options, self.arguments = self.parser.parse_args( args=argument_list, values=values) # TODO: Handle it if developer decides to remove some options before # calling parse_arguments() self.thresholds = self.options.thresholds self.show_longoutput = self.options.show_longoutput self.show_perfdata = self.options.show_perfdata self.show_legacy = self.options.show_legacy self.show_debug = self.options.show_debug self.verbose = self.options.verbose #self.show_status_in_summary = self.options.show_status_in_summary self.set_timeout(self.options.timeout) def add_long_output(self, message): """ Appends message to the end of Plugin long_output. Message does not need a \n suffix Examples: >>> p = PluginHelper() >>> p.add_long_output('Status of sensor 1') >>> p.add_long_output('* Temperature: OK') >>> p.add_long_output('* Humidity: OK') >>> p.get_long_output() u'Status of sensor 1\\n* Temperature: OK\\n* Humidity: OK' """ self._long_output.append(message) def add_option(self, *args, **kwargs): """ Same as self.parser.add_option() """ return self.parser.add_option(*args, **kwargs) def get_long_output(self): """ Returns all long_output that has been added via add_long_output """ return '\n'.join(self._long_output) def set_long_output(self, message): """ Overwrite current long_output with message Example: >>> s = PluginHelper() >>> s.add_long_output('first long output') >>> s.set_long_output('Fatal error') >>> s.get_long_output() u'Fatal error' """ self._long_output = [message] def add_summary(self, message): """ Adds message to Plugin Summary """ self._summary.append(message.strip()) def set_summary(self, message): """ Overwrite current summary with message Example: >>> s = PluginHelper() >>> s.add_summary('first summary') >>> s.set_summary('Fatal error') >>> s.get_summary() u'Fatal error' """ self._summary = [message] def get_summary(self): return '. '.join(self._summary) def get_status(self): """ Returns the worst nagios status (integer 0,1,2,3) that has been put with add_status() If status has never been added, returns 3 for UNKNOWN """ # If no status has been set, return unknown if self._nagios_status == -1: return UNKNOWN else: return self._nagios_status def status(self, new_status=None): """ Same as get_status() if new_status=None, otherwise call add_status(new_status) """ if new_status is None: return self.get_status() if new_status not in state_text: new_status = unknown return self.add_status(new_status) def add_status(self, new_status=None): """ Update exit status of the nagios plugin. This function will keep history of the worst status added Examples: >>> p = PluginHelper() >>> p.add_status(0) # ok >>> p.add_status(2) # critical >>> p.add_status(1) # warning >>> p.get_status() # 2 >>> p = PluginHelper() >>> p.add_status('warning') >>> p.add_status('ok') >>> p.get_status() 1 >>> p.add_status('okay') Traceback (most recent call last): ... Exception: Invalid status supplied "okay" """ # If new status was entered as a human readable string (ok,warn,etc) # lets convert it to int: if isinstance(new_status, basestring): if new_status.lower() in state: new_status = state[new_status] else: raise Exception( "Invalid status supplied \"%s\"" % (new_status)) self._nagios_status = max(self._nagios_status, new_status) def add_metric(self, label="", value="", warn="", crit="", min="", max="", uom="", perfdatastring=None): """ Add numerical metric (will be outputted as nagios performanca data) Examples: >>> p = PluginHelper() >>> p.add_metric(label="load1", value="7") >>> p.add_metric(label="load5", value="5") >>> p.add_metric(label="load15",value="2") >>> p.get_perfdata() "'load1'=7;;;; 'load5'=5;;;; 'load15'=2;;;;" >>> p = PluginHelper() >>> p.add_metric(perfdatastring="load1=6;;;;") >>> p.add_metric(perfdatastring="load5=4;;;;") >>> p.add_metric(perfdatastring="load15=1;;;;") >>> p.get_perfdata() "'load1'=6;;;; 'load5'=4;;;; 'load15'=1;;;;" """ if not perfdatastring is None: self._perfdata.add_perfdatametric(perfdatastring=perfdatastring) else: self._perfdata.add_perfdatametric( label=label, value=value, warn=warn, crit=crit, min=min, max=max, uom=uom) def get_default_values(self, section_name=None, config_file=None): """ Returns an optionParser.Values instance of all defaults after parsing extra opts config file The Nagios extra-opts spec we use is the same as described here: http://nagiosplugins.org/extra-opts Arguments """ # Get the program defaults values = self.parser.get_default_values() # Create an ExtraOptsParser instance and get all the values from that # config file extra_opts = ExtraOptsParser( section_name=section_name, config_file=config_file).get_values() for option in self.parser.option_list: name = option.dest if name in extra_opts: if option.action == 'append': setattr(values, name, extra_opts[option.dest]) else: setattr(values, name, extra_opts[option.dest][0]) return values def get_metric(self, label): """ Return one specific metric (PerfdataMetric object) with the specified label. Returns None if not found. Example: >>> p = PluginHelper() >>> p.add_metric(label="load1", value="7") >>> p.add_metric(label="load15",value="2") >>> p.get_metric("load1") 'load1'=7;;;; >>> p.get_metric("unknown") # Returns None """ for i in self._perfdata.metrics: if i.label == label: return i return None def convert_perfdata(self, perfdata): """ Converts new threshold range format to old one. Returns None. Examples: x..y -> x:y inf..y -> :y -inf..y -> :y x..inf -> x: -inf..inf -> : """ for metric in perfdata: metric.warn = reconsile_threshold(metric.warn) metric.crit = reconsile_threshold(metric.crit) return None def get_perfdata(self): """ Get perfdatastring for all valid perfdatametrics collected via add_perfdata Examples: >>> p = PluginHelper() >>> p.add_metric(label="load1", value="7", warn="-inf..10", crit="10..inf") >>> p.add_metric(label="load5", value="5", warn="-inf..7", crit="7..inf") >>> p.add_metric(label="load15",value="2", warn="-inf..5", crit="5..inf") >>> p.get_perfdata() "'load1'=7;10:;~:10;; 'load5'=5;7:;~:7;; 'load15'=2;5:;~:5;;" Example with legacy output (show_legacy should be set with a cmdline option): >>> p.show_legacy = True >>> p.get_perfdata() "'load1'=7;10:;~:10;; 'load5'=5;7:;~:7;; 'load15'=2;5:;~:5;;" """ # Normalize the perfdata to so the thresholds match the current nagios plugin guidelines self.convert_perfdata(self._perfdata.metrics) return str(self._perfdata) def get_plugin_output(self, exit_code=None, summary=None, long_output=None, perfdata=None): """ Get all plugin output as it would be printed to screen with self.exit() Examples of functionality: >>> p = PluginHelper() >>> p.get_plugin_output() u'Unknown -' >>> p = PluginHelper() >>> p.add_summary('Testing') >>> p.add_long_output('Long testing output') >>> p.add_long_output('More output') >>> p.get_plugin_output(exit_code=0) u'OK - Testing\\nLong testing output\\nMore output' >>> p = PluginHelper() >>> p.add_summary('Testing') >>> p.add_status(0) >>> p.get_plugin_output() u'OK - Testing' >>> p = PluginHelper() >>> p.show_status_in_summary = False >>> p.add_summary('Testing') >>> p.add_metric(label="load1", value="7") >>> p.add_metric(label="load5", value="5") >>> p.add_metric(label="load15",value="2") >>> p.get_plugin_output(exit_code=0) u"Testing | 'load1'=7;;;; 'load5'=5;;;; 'load15'=2;;;;" >>> p = PluginHelper() >>> p.show_status_in_summary = False >>> p.add_summary('Testing') >>> p.add_long_output('Long testing output') >>> p.add_long_output('More output') >>> p.add_metric(label="load1", value="7") >>> p.add_metric(label="load5", value="5") >>> p.add_metric(label="load15",value="2") >>> p.get_plugin_output(exit_code=0) u"Testing | 'load1'=7;;;; 'load5'=5;;;; 'load15'=2;;;;\\nLong testing output\\nMore output" """ if summary is None: summary = self.get_summary() if long_output is None: long_output = self.get_long_output() if perfdata is None: perfdata = self.get_perfdata() if exit_code is None: exit_code = self.get_status() return_buffer = "" if self.show_status_in_summary is True: return_buffer += "%s - " % state_text[exit_code] if self.show_summary is True: return_buffer += summary if self.show_perfdata is True and len(perfdata) > 0: return_buffer += " | %s\n" % perfdata if not return_buffer.endswith('\n'): return_buffer += '\n' if self.show_longoutput is True and len(long_output) > 0: return_buffer += long_output return_buffer = return_buffer.strip() return return_buffer def set_timeout(self, seconds=50): """ Configures plugin to timeout after seconds number of seconds """ timeout = lambda x, y: self.exit( unknown, summary="Plugin timeout exceeded after %s seconds." % seconds) signal.signal(signal.SIGALRM, timeout) signal.alarm(seconds) def exit(self, exit_code=None, summary=None, long_output=None, perfdata=None): """ Print all collected output to screen and exit nagios style, no arguments are needed except if you want to override default behavior. Arguments: summary -- Is this text as the plugin summary instead of self.get_summary() long_output -- Use this text as long_output instead of self.get_long_output() perfdata -- Use this text instead of self.get_perfdata() exit_code -- Use this exit code instead of self.status() """ if exit_code is None: exit_code = self.get_status() if self.options and self.options.get_metrics is True: summary = "Available metrics for this plugin:" metrics = [] for i in self._perfdata.metrics: if self.options.verbose is True: metrics.append(str(i)) else: metrics.append(i.label) long_output = '\n'.join(metrics) plugin_output = self.get_plugin_output( exit_code=exit_code, summary=summary, long_output=long_output, perfdata=perfdata) print(plugin_output) sys.exit(exit_code) def check_metric(self, metric_name, thresholds): """ Check one specific metric against a list of thresholds. Updates self.status() and writes to summary or longout as appropriate. Arguments: metric_name -- A string representing the name of the metric (the label part of the performance data) thresholds -- a list in the form of [ (level,range) ] where range is a string in the format of "start..end" Examples: >>> p = PluginHelper() >>> thresholds = [(warning,'2..5'), (critical,'5..inf')] >>> p.get_plugin_output() u'Unknown -' >>> p.add_metric('load15', '3') >>> p.check_metric('load15',thresholds) >>> p.get_plugin_output() u"Warning - Warning on load15 | 'load15'=3;@2:5;~:5;;" >>> p = PluginHelper() >>> thresholds = [(warning,'2..5'), (critical,'5..inf')] >>> p.add_metric('load15', '3') >>> p.verbose = True >>> p.check_metric('load15',thresholds) >>> p.get_plugin_output() u"Warning - Warning on load15 | 'load15'=3;@2:5;~:5;;\\nWarning on load15" Invalid metric: >>> p = PluginHelper() >>> p.add_status(ok) >>> p.add_summary('Everythings fine!') >>> p.get_plugin_output() u'OK - Everythings fine!' >>> thresholds = [(warning,'2..5'), (critical,'5..inf')] >>> p.check_metric('never_added_metric', thresholds) >>> p.get_plugin_output() u'Unknown - Everythings fine!. Metric never_added_metric not found' Invalid threshold: >>> p = PluginHelper() >>> thresholds = [(warning, 'invalid'), (critical,'5..inf')] >>> p.add_metric('load1', '10') >>> p.check_metric('load1', thresholds) Traceback (most recent call last): ... SystemExit: 3 Returns: None """ metric = self.get_metric(label=metric_name) # If threshold was specified but metric not found in our data, set # status unknown if metric is None: self.status(unknown) self.add_summary("Metric %s not found" % (metric_name)) return metric_status = -1 # by default assume nothing default_state = 0 # By default if no treshold matches, we assume OK highest_level = ok # highest threshold range seen # Iterate through all thresholds, and log down warn and crit for # perfdata purposes for level, threshold_range in thresholds: if metric.warn == '' and level == warning: metric.warn = threshold_range elif metric.crit == '' and level == critical: metric.crit = threshold_range if level == ok: default_state = 2 # Iterate all threshold and determine states for level, threshold_range in thresholds: highest_level = max(highest_level, level) # If ok threshold was specified, default state is critical according to spec # If value matches our threshold, we increment the status try: in_range = new_threshold_syntax.check_range( metric.value, threshold_range) except PynagError: self.set_summary( "Could not parse threshold %s=%s for metric %s" % (state_text[ level], threshold_range, metric_name) ) self.set_long_output( "Thresholds should be in the format metric=,ok=0..90,warning=90..95") self.add_long_output("Example: ") self.add_long_output( "--th metric=load,ok=0..1,warning=1..5,critical=5..inf") self.status(unknown) self.exit() if in_range: metric_status = max(metric_status, level) self.debug('%s is within %s range "%s"' % (metric_name, state_text[level], threshold_range)) if level == ok: self.debug( "OK threshold matches, not checking any more thresholds") metric_status = ok break else: self.debug('%s is outside %s range "%s"' % (metric_name, state_text[level], threshold_range)) # If no thresholds matched, set a default return code if metric_status < 0: metric_status = default_state # OK's go to long output, errors go directly to summary self.add_status(metric_status) message = '%s on %s' % (state_text[metric_status], metric_name) # Errors are added to the summary: if metric_status > 0: self.add_summary(message) if self.verbose is True: self.add_long_output(message) def check_all_metrics(self): """ Checks all metrics (add_metric() against any thresholds set in self.options.thresholds or with --threshold from commandline)""" checked_metrics = [] for threshold in self.thresholds: parsed_threshold = new_threshold_syntax.parse_threshold(threshold) metric_name = parsed_threshold['metric'] thresholds = parsed_threshold['thresholds'] self.check_metric(metric_name, thresholds) checked_metrics.append(metric_name) # Lets look at metrics that were not specified on the command-line but might have a default # threshold specified with their metric data for i in self._perfdata.metrics: if i.label in checked_metrics: continue thresholds = [] if i.warn != '': thresholds.append((warning, i.warn)) if i.crit != '': thresholds.append((critical, i.crit)) self.check_metric(i.label, thresholds) def run_function(self, function, *args, **kwargs): """ Executes "function" and exits Nagios style with status "unkown" if there are any exceptions. The stacktrace will be in long_output. Example: >>> p = PluginHelper() >>> p.add_status('ok') >>> p.get_status() 0 >>> p.add_status('okay') Traceback (most recent call last): ... Exception: Invalid status supplied "okay" >>> p.run_function( p.add_status, 'warning' ) >>> p.get_status() 1 >>> p.run_function( p.add_status, 'okay' ) Traceback (most recent call last): ... SystemExit: 3 """ try: function(*args, **kwargs) except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() exit_code = unknown # traceback.print_exc(file=sys.stdout) summary = "Unhandled '%s' exception while running plugin (traceback below)" % exc_type long_output = traceback.format_exc() self.exit(exit_code=exit_code, summary=summary, long_output=long_output, perfdata='') def debug(self, message): # pragma: no cover if self.show_debug is True: self.add_long_output("debug: %s" % message) def __str__(self): """ >>> p = PluginHelper() >>> p.add_status(ok) >>> p.add_summary('Test') >>> print(p) OK - Test """ return self.get_plugin_output() def __repr__(self): return self.get_plugin_output(long_output='', perfdata='') pynag-0.9.1+dfsg.orig/pynag/Parsers/0000775000175000017500000000000012370025176016263 5ustar clintclintpynag-0.9.1+dfsg.orig/pynag/Parsers/__init__.py0000664000175000017500000037466112363024335020413 0ustar clintclint# -*- coding: utf-8 -*- # # pynag - Python Nagios plug-in and configuration environment # Copyright (C) 2010 Drew Stinnet # # 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. """This module contains low-level Parsers for nagios configuration and status objects. Hint: If you are looking to parse some nagios configuration data, you probably want pynag.Model module instead. The highlights of this module are: class Config: For Parsing nagios local nagios configuration files class Livestatus: To connect to MK-Livestatus class StatusDat: To read info from status.dat (not used a lot, migrate to mk-livestatus) class LogFiles: To read nagios log-files class MultiSite: To talk with multiple Livestatus instances """ import os import re import time import sys import socket # for mk_livestatus import stat import pynag.Plugins import pynag.Utils import StringIO import tarfile _sentinel = object() class Config(object): """ Parse and write nagios config files """ # Regex for beginning of object definition # We want everything that matches: # define { __beginning_of_object = re.compile("^\s*define\s+(\w+)\s*\{?(.*)$") def __init__(self, cfg_file=None, strict=False): """ Constructor for :py:class:`pynag.Parsers.config` class Args: cfg_file (str): Full path to nagios.cfg. If None, try to auto-discover location strict (bool): if True, use stricter parsing which is more prone to raising exceptions """ self.cfg_file = cfg_file # Main configuration file self.strict = strict # Use strict parsing or not # If nagios.cfg is not set, lets do some minor autodiscover. if self.cfg_file is None: self.cfg_file = self.guess_cfg_file() self.data = {} self.maincfg_values = [] self._is_dirty = False self.reset() # Initilize misc member variables def guess_nagios_directory(self): """ Returns a path to the nagios configuration directory on your system Use this function for determining the nagios config directory in your code Returns: str. directory containing the nagios.cfg file Raises: :py:class:`pynag.Parsers.ConfigFileNotFound` if cannot guess config file location. """ cfg_file = self.guess_cfg_file() if not cfg_file: raise ConfigFileNotFound("Could not find nagios.cfg") return os.path.dirname(cfg_file) def guess_nagios_binary(self): """ Returns a path to any nagios binary found on your system Use this function if you don't want specify path to the nagios binary in your code and you are confident that it is located in a common location Checked locations are as follows: * /usr/bin/nagios * /usr/sbin/nagios * /usr/local/nagios/bin/nagios * /nagios/bin/nagios * /usr/bin/icinga * /usr/sbin/icinga * /usr/bin/naemon * /usr/sbin/naemon * /usr/local/naemon/bin/naemon.cfg * /usr/bin/shinken * /usr/sbin/shinken Returns: str. Path to the nagios binary None if could not find a binary in any of those locations """ possible_files = ('/usr/bin/nagios', '/usr/sbin/nagios', '/usr/local/nagios/bin/nagios', '/nagios/bin/nagios', '/usr/bin/icinga', '/usr/sbin/icinga', '/usr/bin/naemon', '/usr/sbin/naemon', '/usr/local/naemon/bin/naemon.cfg', '/usr/bin/shinken', '/usr/sbin/shinken') possible_binaries = ('nagios', 'nagios3', 'naemon', 'icinga', 'shinken') for i in possible_binaries: command = ['which', i] code, stdout, stderr = pynag.Utils.runCommand(command=command, shell=False) if code == 0: return stdout.splitlines()[0].strip() return None def guess_cfg_file(self): """ Returns a path to any nagios.cfg found on your system Use this function if you don't want specify path to nagios.cfg in your code and you are confident that it is located in a common location Checked locations are as follows: * /etc/nagios/nagios.cfg * /etc/nagios3/nagios.cfg * /usr/local/nagios/etc/nagios.cfg * /nagios/etc/nagios/nagios.cfg * ./nagios.cfg * ./nagios/nagios.cfg * /etc/icinga/icinga.cfg * /usr/local/icinga/etc/icinga.cfg * ./icinga.cfg * ./icinga/icinga.cfg * /etc/naemon/naemon.cfg * /usr/local/naemon/etc/naemon.cfg * ./naemon.cfg * ./naemon/naemon.cfg * /etc/shinken/shinken.cfg Returns: str. Path to the nagios.cfg or equivalent file None if couldn't find a file in any of these locations. """ possible_files = ('/etc/nagios/nagios.cfg', '/etc/nagios3/nagios.cfg', '/usr/local/nagios/etc/nagios.cfg', '/nagios/etc/nagios/nagios.cfg', './nagios.cfg', './nagios/nagios.cfg', '/etc/icinga/icinga.cfg', '/usr/local/icinga/etc/icinga.cfg', './icinga.cfg', './icinga/icinga.cfg', '/etc/naemon/naemon.cfg', '/usr/local/naemon/etc/naemon.cfg', './naemon.cfg', './naemon/naemon.cfg', '/etc/shinken/shinken.cfg', ) for file_path in possible_files: if self.isfile(file_path): return file_path return None def reset(self): """ Reinitializes the data of a parser instance to its default values. """ self.cfg_files = [] # List of other configuration files self.data = {} # dict of every known object definition self.errors = [] # List of ParserErrors self.item_list = None self.item_cache = None self.maincfg_values = [] # The contents of main nagios.cfg self._resource_values = [] # The contents of any resource_files self.item_apply_cache = {} # This is performance tweak used by _apply_template # This is a pure listof all the key/values in the config files. It # shouldn't be useful until the items in it are parsed through with the proper # 'use' relationships self.pre_object_list = [] self.post_object_list = [] self.object_type_keys = { 'hostgroup': 'hostgroup_name', 'hostextinfo': 'host_name', 'host': 'host_name', 'service': 'name', 'servicegroup': 'servicegroup_name', 'contact': 'contact_name', 'contactgroup': 'contactgroup_name', 'timeperiod': 'timeperiod_name', 'command': 'command_name', #'service':['host_name','description'], } def _has_template(self, target): """ Determine if an item has a template associated with it Args: target (dict): Parsed item as parsed by :py:class:`pynag.Parsers.config` """ return 'use' in target def _get_pid(self): """ Checks the lock_file var in nagios.cfg and returns the pid from the file If the pid file does not exist, returns None. """ try: return self.open(self.get_cfg_value('lock_file'), "r").readline().strip() except Exception: return None def _get_hostgroup(self, hostgroup_name): """ Returns the hostgroup that matches the queried name. Args: hostgroup_name: Name of the hostgroup to be returned (string) Returns: Hostgroup item with hostgroup_name that matches the queried name. """ return self.data['all_hostgroup'].get(hostgroup_name, None) def _get_key(self, object_type, user_key=None): """ Return the correct 'key' for an item. This is mainly a helper method for other methods in this class. It is used to shorten code repetition. Args: object_type: Object type from which to obtain the 'key' (string) user_key: User defined key. Default None. (string) Returns: Correct 'key' for the object type. (string) """ if not user_key and not object_type in self.object_type_keys: raise ParserError("Unknown key for object type: %s\n" % object_type) # Use a default key if not user_key: user_key = self.object_type_keys[object_type] return user_key def _get_item(self, item_name, item_type): """ Return an item from a list Creates a cache of items in self.pre_object_list and returns an element from this cache. Looks for an item with corresponding name and type. Args: item_name: Name of the item to be returned (string) item_type: Type of the item to be returned (string) Returns: Item with matching name and type from :py:attr:`pynag.Parsers.config.item_cache` """ # create local cache for performance optimizations. TODO: Rewrite functions that call this function if not self.item_list: self.item_list = self.pre_object_list self.item_cache = {} for item in self.item_list: if not "name" in item: continue name = item['name'] tmp_item_type = (item['meta']['object_type']) if not tmp_item_type in self.item_cache: self.item_cache[tmp_item_type] = {} self.item_cache[tmp_item_type][name] = item my_cache = self.item_cache.get(item_type, None) if not my_cache: return None return my_cache.get(item_name, None) def _apply_template(self, original_item): """ Apply all attributes of item named parent_name to "original_item". Applies all of the attributes of parents (from the 'use' field) to item. Args: original_item: Item 'use'-ing a parent item. The parent's attributes will be concretely added to this item. Returns: original_item to which have been added all the attributes defined in parent items. """ # TODO: There is space for more performance tweaks here # If item does not inherit from anyone else, lets just return item as is. if 'use' not in original_item: return original_item object_type = original_item['meta']['object_type'] raw_definition = original_item['meta']['raw_definition'] my_cache = self.item_apply_cache.get(object_type, {}) # Performance tweak, if item has been parsed. Lets not do it again if raw_definition in my_cache: return my_cache[raw_definition] parent_names = original_item['use'].split(',') parent_items = [] for parent_name in parent_names: parent_item = self._get_item(parent_name, object_type) if parent_item is None: error_string = "Can not find any %s named %s\n" % (object_type, parent_name) self.errors.append(ParserError(error_string, item=original_item)) continue try: # Parent item probably has use flags on its own. So lets apply to parent first parent_item = self._apply_template(parent_item) except RuntimeError: t, e = sys.exc_info()[:2] self.errors.append(ParserError("Error while parsing item: %s (it might have circular use=)" % str(e), item=original_item)) parent_items.append(parent_item) inherited_attributes = original_item['meta']['inherited_attributes'] template_fields = original_item['meta']['template_fields'] for parent_item in parent_items: for k, v in parent_item.iteritems(): if k in ('use', 'register', 'meta', 'name'): continue if k not in inherited_attributes: inherited_attributes[k] = v if k not in original_item: original_item[k] = v template_fields.append(k) if 'name' in original_item: my_cache[raw_definition] = original_item return original_item def _get_items_in_file(self, filename): """ Return all items in the given file Iterates through all elements in self.data and gatehrs all the items defined in the queried filename. Args: filename: file from which are defined the items that will be returned. Returns: A list containing all the items in self.data that were defined in filename """ return_list = [] for k in self.data.keys(): for item in self[k]: if item['meta']['filename'] == filename: return_list.append(item) return return_list def get_new_item(self, object_type, filename): """ Returns an empty item with all necessary metadata Creates a new item dict and fills it with usual metadata: * object_type : object_type (arg) * filename : filename (arg) * template_fields = [] * needs_commit = None * delete_me = None * defined_attributes = {} * inherited_attributes = {} * raw_definition = "define %s {\\n\\n} % object_type" Args: object_type: type of the object to be created (string) filename: Path to which the item will be saved (string) Returns: A new item with default metadata """ meta = { 'object_type': object_type, 'filename': filename, 'template_fields': [], 'needs_commit': None, 'delete_me': None, 'defined_attributes': {}, 'inherited_attributes': {}, 'raw_definition': "define %s {\n\n}" % object_type, } return {'meta': meta} def _load_file(self, filename): """ Parses filename with self.parse_filename and append results in self._pre_object_list This function is mostly here for backwards compatibility Args: filename: the file to be parsed. This is supposed to a nagios object definition file """ for i in self.parse_file(filename): self.pre_object_list.append(i) def parse_file(self, filename): """ Parses a nagios object configuration file and returns lists of dictionaries. This is more or less a wrapper around :py:meth:`config.parse_string`, so reading documentation there is useful. Args: filename: Path to the file to parse (string) Returns: A list containing elements parsed by :py:meth:`parse_string` """ try: raw_string = self.open(filename, 'rb').read() return self.parse_string(raw_string, filename=filename) except IOError: t, e = sys.exc_info()[:2] parser_error = ParserError(e.strerror) parser_error.filename = e.filename self.errors.append(parser_error) return [] def parse_string(self, string, filename='None'): """ Parses a string, and returns all object definitions in that string Args: string: A string containing one or more object definitions filename (optional): If filename is provided, it will be referenced when raising exceptions Examples: >>> test_string = "define host {\\nhost_name examplehost\\n}\\n" >>> test_string += "define service {\\nhost_name examplehost\\nservice_description example service\\n}\\n" >>> c = config() >>> result = c.parse_string(test_string) >>> for i in result: print i.get('host_name'), i.get('service_description', None) examplehost None examplehost example service Returns: A list of dictionaries, that look like self.data Raises: :py:class:`ParserError` """ append = "" current = None in_definition = {} tmp_buffer = [] result = [] for sequence_no, line in enumerate(string.splitlines(False)): line_num = sequence_no + 1 # If previous line ended with backslash, treat this line as a # continuation of previous line if append: line = append + line append = None # Cleanup and line skips line = line.strip() if line == "": continue if line[0] == "#" or line[0] == ';': continue # If this line ends with a backslash, continue directly to next line if line.endswith('\\'): append = line.strip('\\') continue if line.startswith('}'): # end of object definition if not in_definition: p = ParserError("Unexpected '}' found outside object definition in line %s" % line_num) p.filename = filename p.line_start = line_num raise p in_definition = None current['meta']['line_end'] = line_num # Looks to me like nagios ignores everything after the } so why shouldn't we ? rest = line.split("}", 1)[1] tmp_buffer.append(line) try: current['meta']['raw_definition'] = '\n'.join(tmp_buffer) except Exception: raise ParserError("Encountered Unexpected end of object definition in file '%s'." % filename) result.append(current) # Destroy the Nagios Object current = None continue elif line.startswith('define'): # beginning of object definition if in_definition: msg = "Unexpected 'define' in {filename} on line {line_num}. was expecting '}}'." msg = msg.format(**locals()) self.errors.append(ParserError(msg, item=current)) m = self.__beginning_of_object.search(line) tmp_buffer = [line] object_type = m.groups()[0] if self.strict and object_type not in self.object_type_keys.keys(): raise ParserError( "Don't know any object definition of type '%s'. it is not in a list of known object definitions." % object_type) current = self.get_new_item(object_type, filename) current['meta']['line_start'] = line_num # Start off an object in_definition = True # Looks to me like nagios ignores everything after the {, so why shouldn't we ? rest = m.groups()[1] continue else: # In the middle of an object definition tmp_buffer.append(' ' + line) # save whatever's left in the buffer for the next iteration if not in_definition: append = line continue # this is an attribute inside an object definition if in_definition: #(key, value) = line.split(None, 1) tmp = line.split(None, 1) if len(tmp) > 1: (key, value) = tmp else: key = tmp[0] value = "" # Strip out in-line comments if value.find(";") != -1: value = value.split(";", 1)[0] # Clean info key = key.strip() value = value.strip() # Rename some old values that may be in the configuration # This can probably be removed in the future to increase performance if (current['meta']['object_type'] == 'service') and key == 'description': key = 'service_description' # Special hack for timeperiods as they are not consistent with other objects # We will treat whole line as a key with an empty value if (current['meta']['object_type'] == 'timeperiod') and key not in ('timeperiod_name', 'alias'): key = line value = '' current[key] = value current['meta']['defined_attributes'][key] = value # Something is wrong in the config else: raise ParserError("Error: Unexpected token in file '%s'" % filename) # Something is wrong in the config if in_definition: raise ParserError("Error: Unexpected EOF in file '%s'" % filename) return result def _locate_item(self, item): """ This is a helper function for anyone who wishes to modify objects. It takes "item", locates the file which is configured in, and locates exactly the lines which contain that definition. Returns: (tuple) (everything_before, object_definition, everything_after, filename): * everything_before (list of lines): Every line in filename before object was defined * everything_after (list of lines): Every line in "filename" after object was defined * object_definition (list of lines): Every line used to define our item in "filename" * filename (string): file in which the object was written to Raises: :py:class:`ValueError` if object was not found in "filename" """ if "filename" in item['meta']: filename = item['meta']['filename'] else: raise ValueError("item does not have a filename") # Look for our item, store it as my_item for i in self.parse_file(filename): if self.compareObjects(item, i): my_item = i break else: raise ValueError("We could not find object in %s\n%s" % (filename, item)) # Caller of this method expects to be returned # several lists that describe the lines in our file. # The splitting logic starts here. my_file = self.open(filename) all_lines = my_file.readlines() my_file.close() start = my_item['meta']['line_start'] - 1 end = my_item['meta']['line_end'] everything_before = all_lines[:start] object_definition = all_lines[start:end] everything_after = all_lines[end:] # If there happen to be line continuations in the object we will edit # We will remove them from object_definition object_definition = self._clean_backslashes(object_definition) return everything_before, object_definition, everything_after, filename def _clean_backslashes(self, list_of_strings): """ Returns list_of_strings with all all strings joined that ended with backslashes Args: list_of_strings: List of strings to join Returns: Another list of strings, which lines ending with \ joined together. """ tmp_buffer = '' result = [] for i in list_of_strings: if i.endswith('\\\n'): tmp_buffer += i.strip('\\\n') else: result.append(tmp_buffer + i) tmp_buffer = '' return result def _modify_object(self, item, field_name=None, new_value=None, new_field_name=None, new_item=None, make_comments=False): """ Locates "item" and changes the line which contains field_name. Helper function for object_* functions. Locates "item" and changes the line which contains field_name. If new_value and new_field_name are both None, the attribute is removed. Args: item(dict): The item to be modified field_name(str): The field_name to modify (if any) new_field_name(str): If set, field_name will be renamed new_value(str): If set the value of field_name will be changed new_item(str): If set, whole object will be replaced with this string make_comments: If set, put pynag-branded comments where changes have been made Returns: True on success Raises: :py:class:`ValueError` if object or field_name is not found :py:class:`IOError` is save is unsuccessful. """ if item is None: return if field_name is None and new_item is None: raise ValueError("either field_name or new_item must be set") if '\n' in str(new_value): raise ValueError("Invalid character \\n used as an attribute value.") everything_before, object_definition, everything_after, filename = self._locate_item(item) if new_item is not None: # We have instruction on how to write new object, so we dont need to parse it object_definition = [new_item] else: change = None value = None i = 0 for i in range(len(object_definition)): tmp = object_definition[i].split(None, 1) if len(tmp) == 0: continue # Hack for timeperiods, they dont work like other objects elif item['meta']['object_type'] == 'timeperiod' and field_name not in ('alias', 'timeperiod_name'): tmp = [object_definition[i]] # we can't change timeperiod, so we fake a field rename if new_value is not None: new_field_name = new_value new_value = None value = '' elif len(tmp) == 1: value = '' else: value = tmp[1] k = tmp[0].strip() if k == field_name: # Attribute was found, lets change this line if new_field_name is None and new_value is None: # We take it that we are supposed to remove this attribute change = object_definition.pop(i) break elif new_field_name: # Field name has changed k = new_field_name if new_value is not None: # value has changed value = new_value # Here we do the actual change change = "\t%-30s%s\n" % (k, value) if item['meta']['object_type'] == 'timeperiod' and field_name not in ('alias', 'timeperiod_name'): change = "\t%s\n" % new_field_name object_definition[i] = change break if not change and new_value is not None: # Attribute was not found. Lets add it change = "\t%-30s%s\n" % (field_name, new_value) object_definition.insert(i, change) # Lets put a banner in front of our item if make_comments: comment = '# Edited by PyNag on %s\n' % time.ctime() if len(everything_before) > 0: last_line_before = everything_before[-1] if last_line_before.startswith('# Edited by PyNag on'): everything_before.pop() # remove this line object_definition.insert(0, comment) # Here we overwrite the config-file, hoping not to ruin anything str_buffer = "%s%s%s" % (''.join(everything_before), ''.join(object_definition), ''.join(everything_after)) self.write(filename, str_buffer) return True def open(self, filename, *args, **kwargs): """ Wrapper around global open() Simply calls global open(filename, *args, **kwargs) and passes all arguments as they are received. See global open() function for more details. """ return open(filename, *args, **kwargs) @pynag.Utils.synchronized(pynag.Utils.rlock) def write(self, filename, string): """ Wrapper around open(filename).write() Writes string to filename and closes the file handler. File handler is openned in `'w'` mode. Args: filename: File where *string* will be written. This is the path to the file. (string) string: String to be written to file. (string) Returns: Return code as returned by :py:meth:`os.write` """ fh = self.open(filename, 'w') return_code = fh.write(string) fh.flush() # os.fsync(fh) fh.close() self._is_dirty = True return return_code def item_rewrite(self, item, str_new_item): """ Completely rewrites item with string provided. Args: item: Item that is to be rewritten str_new_item: str representation of the new item .. In the following line, every "\\n" is actually a simple line break This is only a little patch for the generated documentation. Examples:: item_rewrite( item, "define service {\\n name example-service \\n register 0 \\n }\\n" ) Returns: True on success Raises: :py:class:`ValueError` if object is not found :py:class:`IOError` if save fails """ return self._modify_object(item=item, new_item=str_new_item) def item_remove(self, item): """ Delete one specific item from its configuration files Args: item: Item that is to be rewritten str_new_item: string representation of the new item .. In the following line, every "\\n" is actually a simple line break This is only a little patch for the generated documentation. Examples:: item_remove( item, "define service {\\n name example-service \\n register 0 \\n }\\n" ) Returns: True on success Raises: :py:class:`ValueError` if object is not found :py:class:`IOError` if save fails """ return self._modify_object(item=item, new_item="") def item_edit_field(self, item, field_name, new_value): """ Modifies one field of a (currently existing) object. Changes are immediate (i.e. there is no commit) Args: item: Item to be modified. Its field `field_name` will be set to `new_value`. field_name: Name of the field that will be modified. (str) new_value: Value to which will be set the field `field_name`. (str) Example usage:: edit_object( item, field_name="host_name", new_value="examplehost.example.com") # doctest: +SKIP Returns: True on success Raises: :py:class:`ValueError` if object is not found :py:class:`IOError` if save fails """ return self._modify_object(item, field_name=field_name, new_value=new_value) def item_remove_field(self, item, field_name): """ Removes one field of a (currently existing) object. Changes are immediate (i.e. there is no commit) Args: item: Item to remove field from. field_name: Field to remove. (string) Example usage:: item_remove_field( item, field_name="contactgroups" ) Returns: True on success Raises: :py:class:`ValueError` if object is not found :py:class:`IOError` if save fails """ return self._modify_object(item=item, field_name=field_name, new_value=None, new_field_name=None) def item_rename_field(self, item, old_field_name, new_field_name): """ Renames a field of a (currently existing) item. Changes are immediate (i.e. there is no commit). Args: item: Item to modify. old_field_name: Name of the field that will have its name changed. (string) new_field_name: New name given to `old_field_name` (string) Example usage:: item_rename_field(item, old_field_name="normal_check_interval", new_field_name="check_interval") Returns: True on success Raises: :py:class:`ValueError` if object is not found :py:class:`IOError` if save fails """ return self._modify_object(item=item, field_name=old_field_name, new_field_name=new_field_name) def item_add(self, item, filename): """ Adds a new object to a specified config file. Args: item: Item to be created filename: Filename that we are supposed to write the new item to. This is the path to the file. (string) Returns: True on success Raises: :py:class:`IOError` on failed save """ if not 'meta' in item: item['meta'] = {} item['meta']['filename'] = filename # Create directory if it does not already exist dirname = os.path.dirname(filename) if not self.isdir(dirname): os.makedirs(dirname) str_buffer = self.print_conf(item) fh = self.open(filename, 'a') fh.write(str_buffer) fh.close() return True def edit_object(self, item, field_name, new_value): """ Modifies a (currently existing) item. Changes are immediate (i.e. there is no commit) Args: item: Item to modify. field_name: Field that will be updated. new_value: Updated value of field `field_name` Example Usage: edit_object( item, field_name="host_name", new_value="examplehost.example.com") Returns: True on success .. WARNING:: THIS FUNCTION IS DEPRECATED. USE item_edit_field() instead """ return self.item_edit_field(item=item, field_name=field_name, new_value=new_value) def compareObjects(self, item1, item2): """ Compares two items. Returns true if they are equal Compares every key: value pair for both items. If anything is different, the items will not be considered equal. Args: item1, item2: Items to be compared. Returns: True -- Items are equal False -- Items are not equal """ keys1 = item1['meta']['defined_attributes'].keys() keys2 = item2['meta']['defined_attributes'].keys() keys1.sort() keys2.sort() result = True if keys1 != keys2: return False for key in keys1: if key == 'meta': continue key1 = item1[key] key2 = item2[key] # For our purpose, 30 is equal to 30.000 if key == 'check_interval': key1 = int(float(key1)) key2 = int(float(key2)) if str(key1) != str(key2): result = False if result is False: return False return True def edit_service(self, target_host, service_description, field_name, new_value): """ Edit a service's attributes Takes a host, service_description pair to identify the service to modify and sets its field `field_name` to `new_value`. Args: target_host: name of the host to which the service is attached to. (string) service_description: Service description of the service to modify. (string) field_name: Field to modify. (string) new_value: Value to which the `field_name` field will be updated (string) Returns: True on success Raises: :py:class:`ParserError` if the service is not found """ original_object = self.get_service(target_host, service_description) if original_object is None: raise ParserError("Service not found") return self.edit_object(original_object, field_name, new_value) def _get_list(self, item, key): """ Return a comma list from an item Args: item: Item from which to select value. (string) key: Field name of the value to select and return as a list. (string) Example:: _get_list(Foo_object, host_name) define service { service_description Foo host_name larry,curly,moe } returns ['larry','curly','moe'] Returns: A list of the item's values of `key` Raises: :py:class:`ParserError` if item is not a dict """ if not isinstance(item, dict): raise ParserError("%s is not a dictionary\n" % item) # return [] if not key in item: return [] return_list = [] if item[key].find(",") != -1: for name in item[key].split(","): return_list.append(name) else: return_list.append(item[key]) # Alphabetize return_list.sort() return return_list def delete_object(self, object_type, object_name, user_key=None): """ Delete object from configuration files Args: object_type: Type of the object to delete from configuration files. object_name: Name of the object to delete from configuration files. user_key: user_key to pass to :py:meth:`get_object` Returns: True on success. """ item = self.get_object(object_type=object_type, object_name=object_name, user_key=user_key) return self.item_remove(item) def delete_service(self, service_description, host_name): """ Delete service from configuration files Args: service_description: service_description field value of the object to delete from configuration files. host_name: host_name field value of the object to delete from configuration files. Returns: True on success. """ item = self.get_service(host_name, service_description) return self.item_remove(item) def delete_host(self, object_name, user_key=None): """ Delete a host from its configuration files Args: object_name: object_name field value of the object to delete from configuration files. user_key: user_key to pass to :py:meth:`get_object` Returns: True on success. """ return self.delete_object('host', object_name, user_key=user_key) def delete_hostgroup(self, object_name, user_key=None): """ Delete a hostgroup from its configuration files Args: object_name: object_name field value of the object to delete from configuration files. user_key: user_key to pass to :py:meth:`get_object` Returns: True on success. """ return self.delete_object('hostgroup', object_name, user_key=user_key) def get_object(self, object_type, object_name, user_key=None): """ Return a complete object dictionary Args: object_name: object_name field value of the object to delete from configuration files. user_key: User defined key. Default None. (string) Returns: The item found to match all the criterias. None if object is not found """ object_key = self._get_key(object_type, user_key) for item in self.data['all_%s' % object_type]: if item.get(object_key, None) == object_name: return item return None def get_host(self, object_name, user_key=None): """ Return a host object Args: object_name: object_name field value of the object to delete from configuration files. user_key: user_key to pass to :py:meth:`get_object` Returns: The item found to match all the criterias. """ return self.get_object('host', object_name, user_key=user_key) def get_servicegroup(self, object_name, user_key=None): """ Return a Servicegroup object Args: object_name: object_name field value of the object to delete from configuration files. user_key: user_key to pass to :py:meth:`get_object` Returns: The item found to match all the criterias. """ return self.get_object('servicegroup', object_name, user_key=user_key) def get_contact(self, object_name, user_key=None): """ Return a Contact object Args: object_name: object_name field value of the object to delete from configuration files. user_key: user_key to pass to :py:meth:`get_object` Returns: The item found to match all the criterias. """ return self.get_object('contact', object_name, user_key=user_key) def get_contactgroup(self, object_name, user_key=None): """ Return a Contactgroup object Args: object_name: object_name field value of the object to delete from configuration files. user_key: user_key to pass to :py:meth:`get_object` Returns: The item found to match all the criterias. """ return self.get_object('contactgroup', object_name, user_key=user_key) def get_timeperiod(self, object_name, user_key=None): """ Return a Timeperiod object Args: object_name: object_name field value of the object to delete from configuration files. user_key: user_key to pass to :py:meth:`get_object` Returns: The item found to match all the criterias. """ return self.get_object('timeperiod', object_name, user_key=user_key) def get_command(self, object_name, user_key=None): """ Return a Command object Args: object_name: object_name field value of the object to delete from configuration files. user_key: user_key to pass to :py:meth:`get_object` Returns: The item found to match all the criterias. """ return self.get_object('command', object_name, user_key=user_key) def get_hostgroup(self, object_name, user_key=None): """ Return a hostgroup object Args: object_name: object_name field value of the object to delete from configuration files. user_key: user_key to pass to :py:meth:`get_object` Returns: The item found to match all the criterias. """ return self.get_object('hostgroup', object_name, user_key=user_key) def get_servicedependency(self, object_name, user_key=None): """ Return a servicedependency object Args: object_name: object_name field value of the object to delete from configuration files. user_key: user_key to pass to :py:meth:`get_object` Returns: The item found to match all the criterias. """ return self.get_object('servicedependency', object_name, user_key=user_key) def get_hostdependency(self, object_name, user_key=None): """ Return a hostdependency object Args: object_name: object_name field value of the object to delete from configuration files. user_key: user_key to pass to :py:meth:`get_object` Returns: The item found to match all the criterias. """ return self.get_object('hostdependency', object_name, user_key=user_key) def get_service(self, target_host, service_description): """ Return a service object Args: target_host: host_name field of the service to be returned. This is the host to which is attached the service. service_description: service_description field of the service to be returned. Returns: The item found to match all the criterias. """ for item in self.data['all_service']: if item.get('service_description') == service_description and item.get('host_name') == target_host: return item return None def _append_use(self, source_item, name): """ Append attributes to source_item that are inherited via 'use' attribute' Args: source_item: item (dict) to apply the inheritance upon name: obsolete (discovered automatically via source_item['use']. Here for compatibility. Returns: Source Item with appended attributes. Raises: :py:class:`ParserError` on recursion errors """ # Remove the 'use' key if "use" in source_item: del source_item['use'] for possible_item in self.pre_object_list: if "name" in possible_item: # Start appending to the item for k, v in possible_item.iteritems(): try: if k == 'use': source_item = self._append_use(source_item, v) except Exception: raise ParserError("Recursion error on %s %s" % (source_item, v)) # Only add the item if it doesn't already exist if not k in source_item: source_item[k] = v return source_item def _post_parse(self): """ Creates a few optimization tweaks and easy access lists in self.data Creates :py:attr:`config.item_apply_cache` and fills the all_object item lists in self.data. """ self.item_list = None self.item_apply_cache = {} # This is performance tweak used by _apply_template for raw_item in self.pre_object_list: # Performance tweak, make sure hashmap exists for this object_type object_type = raw_item['meta']['object_type'] if not object_type in self.item_apply_cache: self.item_apply_cache[object_type] = {} # Tweak ends if "use" in raw_item: raw_item = self._apply_template(raw_item) self.post_object_list.append(raw_item) # Add the items to the class lists. for list_item in self.post_object_list: type_list_name = "all_%s" % list_item['meta']['object_type'] if not type_list_name in self.data: self.data[type_list_name] = [] self.data[type_list_name].append(list_item) def commit(self): """ Write any changes that have been made to it's appropriate file """ # Loops through ALL items for k in self.data.keys(): for item in self[k]: # If the object needs committing, commit it! if item['meta']['needs_commit']: # Create file contents as an empty string file_contents = "" # find any other items that may share this config file extra_items = self._get_items_in_file(item['meta']['filename']) if len(extra_items) > 0: for commit_item in extra_items: # Ignore files that are already set to be deleted:w if commit_item['meta']['delete_me']: continue # Make sure we aren't adding this thing twice if item != commit_item: file_contents += self.print_conf(commit_item) # This is the actual item that needs commiting if not item['meta']['delete_me']: file_contents += self.print_conf(item) # Write the file filename = item['meta']['filename'] self.write(filename, file_contents) # Recreate the item entry without the commit flag self.data[k].remove(item) item['meta']['needs_commit'] = None self.data[k].append(item) def flag_all_commit(self): """ Flag every item in the configuration to be committed This should probably only be used for debugging purposes """ for object_type in self.data.keys(): for item in self.data[object_type]: item['meta']['needs_commit'] = True def print_conf(self, item): """ Return a string that can be used in a configuration file Args: item: Item to be dumped as a string. Returns: String representation of item. """ output = "" # Header, to go on all files output += "# Configuration file %s\n" % item['meta']['filename'] output += "# Edited by PyNag on %s\n" % time.ctime() # Some hostgroup information if "hostgroup_list" in item['meta']: output += "# Hostgroups: %s\n" % ",".join(item['meta']['hostgroup_list']) # Some hostgroup information if "service_list" in item['meta']: output += "# Services: %s\n" % ",".join(item['meta']['service_list']) # Some hostgroup information if "service_members" in item['meta']: output += "# Service Members: %s\n" % ",".join(item['meta']['service_members']) if len(item['meta']['template_fields']) != 0: output += "# Values from templates:\n" for k in item['meta']['template_fields']: output += "#\t %-30s %-30s\n" % (k, item[k]) output += "\n" output += "define %s {\n" % item['meta']['object_type'] for k, v in item.iteritems(): if v is None: # Skip entries with No value continue if k != 'meta': if k not in item['meta']['template_fields']: output += "\t %-30s %-30s\n" % (k, v) output += "}\n\n" return output def _load_static_file(self, filename=None): """ Load a general config file (like nagios.cfg) that has key=value config file format. Ignore comments Arguments: filename: name of file to parse, if none nagios.cfg will be used Returns: a [ (key,value), (key,value) ] list """ result = [] if not filename: filename = self.cfg_file for line in self.open(filename).readlines(): # Strip out new line characters line = line.strip() # Skip blank lines if line == "": continue # Skip comments if line[0] == "#" or line[0] == ';': continue tmp = line.split("=", 1) if len(tmp) < 2: continue key, value = tmp key = key.strip() value = value.strip() result.append((key, value)) return result def _edit_static_file(self, attribute, new_value, filename=None, old_value=None, append=False): """ Modify a general config file (like nagios.cfg) that has a key=value config file format. Arguments: filename: Name of config file that will be edited (i.e. nagios.cfg) attribute: name of attribute to edit (i.e. check_external_commands) new_value: new value for the said attribute (i.e. "1"). None deletes the line. old_value: Useful if multiple attributes exist (i.e. cfg_dir) and you want to replace a specific one. append: If true, do not overwrite current setting. Instead append this at the end. Use this with settings that are repeated like cfg_file. Examples:: _edit_static_file(filename='/etc/nagios/nagios.cfg', attribute='check_external_commands', new_value='1') _edit_static_file(filename='/etc/nagios/nagios.cfg', attribute='cfg_dir', new_value='/etc/nagios/okconfig', append=True) """ if filename is None: filename = self.cfg_file # For some specific attributes, append should be implied if attribute in ('cfg_file', 'cfg_dir', 'broker_module'): append = True # If/when we make a change, new_line is what will be written new_line = '%s=%s\n' % (attribute, new_value) # new_value=None means line should be removed if new_value is None: new_line = '' write_buffer = self.open(filename).readlines() is_dirty = False # dirty if we make any changes for i, line in enumerate(write_buffer): # Strip out new line characters line = line.strip() # Skip blank lines if line == "": continue # Skip comments if line[0] == "#" or line[0] == ';': continue key, value = line.split("=", 1) key = key.strip() value = value.strip() # If key does not match, we are not interested in this line if key != attribute: continue # If old_value was specified, and it matches, dont have to look any further elif value == old_value: write_buffer[i] = new_line is_dirty = True break # if current value is the same as new_value, no need to make changes elif value == new_value: return False # Special so cfg_dir matches despite double-slashes, etc elif attribute == 'cfg_dir' and new_value and os.path.normpath(value) == os.path.normpath(new_value): return False # We are not appending, and no old value was specified: elif append is False and not old_value: write_buffer[i] = new_line is_dirty = True break if is_dirty is False and new_value is not None: # If we get here, it means we read the whole file, # and we have not yet made any changes, So we assume # We should append to the file write_buffer.append(new_line) is_dirty = True # When we get down here, it is time to write changes to file if is_dirty is True: str_buffer = ''.join(write_buffer) self.write(filename, str_buffer) return True else: return False def needs_reload(self): """ Checks if the Nagios service needs a reload. Returns: True if Nagios service needs reload of cfg files False if reload not needed or Nagios is not running """ if not self.maincfg_values: self.reset() self.parse_maincfg() new_timestamps = self.get_timestamps() object_cache_file = self.get_cfg_value('object_cache_file') if self._get_pid() is None: return False if not object_cache_file: return True if not self.isfile(object_cache_file): return True object_cache_timestamp = new_timestamps.get(object_cache_file, 0) # Reload not needed if no object_cache file if object_cache_file is None: return False for k, v in new_timestamps.items(): if not v or int(v) > object_cache_timestamp: return True return False def needs_reparse(self): """ Checks if the Nagios configuration needs to be reparsed. Returns: True if any Nagios configuration file has changed since last parse() """ # If Parse has never been run: if self.data == {}: return True # If previous save operation has forced a reparse if self._is_dirty is True: return True # If we get here, we check the timestamps of the configs new_timestamps = self.get_timestamps() if len(new_timestamps) != len(self.timestamps): return True for k, v in new_timestamps.items(): if self.timestamps.get(k, None) != v: return True return False @pynag.Utils.synchronized(pynag.Utils.rlock) def parse_maincfg(self): """ Parses your main configuration (nagios.cfg) and stores it as key/value pairs in self.maincfg_values This function is mainly used by config.parse() which also parses your whole configuration set. Raises: py:class:`ConfigFileNotFound` """ # If nagios.cfg is not set, lets do some minor autodiscover. if self.cfg_file is None: raise ConfigFileNotFound('Could not find nagios.cfg') self.maincfg_values = self._load_static_file(self.cfg_file) @pynag.Utils.synchronized(pynag.Utils.rlock) def parse(self): """ Parse all objects in your nagios configuration This functions starts by loading up your nagios.cfg ( parse_maincfg() ) then moving on to your object configuration files (as defined via cfg_file and cfg_dir) and and your resource_file as well. Returns: None Raises: :py:class:`IOError` if unable to read any file due to permission problems """ # reset self.reset() self.parse_maincfg() self.cfg_files = self.get_cfg_files() # When parsing config, we will softly fail if permission denied # comes on resource files. If later someone tries to get them via # get_resource, we will fail hard try: self._resource_values = self.get_resources() except IOError: t, e = sys.exc_info()[:2] self.errors.append(str(e)) self.timestamps = self.get_timestamps() # This loads everything into for cfg_file in self.cfg_files: self._load_file(cfg_file) self._post_parse() self._is_dirty = False def get_resource(self, resource_name): """ Get a single resource value which can be located in any resource.cfg file Arguments: resource_name: Name as it appears in resource file (i.e. $USER1$) Returns: String value of the resource value. Raises: :py:class:`KeyError` if resource is not found :py:class:`ParserError` if resource is not found and you do not have permissions """ resources = self.get_resources() for k, v in resources: if k == resource_name: return v def get_timestamps(self): """ Returns hash map of all nagios related files and their timestamps""" files = {} files[self.cfg_file] = None for k, v in self.maincfg_values: if k in ('resource_file', 'lock_file', 'object_cache_file'): files[v] = None for i in self.get_cfg_files(): files[i] = None # Now lets lets get timestamp of every file for k, v in files.items(): if not self.isfile(k): continue files[k] = self.stat(k).st_mtime return files def isfile(self, *args, **kwargs): """ Wrapper around os.path.isfile """ return os.path.isfile(*args, **kwargs) def isdir(self, *args, **kwargs): """ Wrapper around os.path.isdir """ return os.path.isdir(*args, **kwargs) def islink(self, *args, **kwargs): """ Wrapper around os.path.islink """ return os.path.islink(*args, **kwargs) def readlink(selfself, *args, **kwargs): """ Wrapper around os.readlink """ return os.readlink(*args, **kwargs) def stat(self, *args, **kwargs): """ Wrapper around os.stat """ return os.stat(*args, **kwargs) def remove(self, *args, **kwargs): """ Wrapper around os.remove """ return os.remove(*args, **kwargs) def access(self, *args, **kwargs): """ Wrapper around os.access """ return os.access(*args, **kwargs) def listdir(self, *args, **kwargs): """ Wrapper around os.listdir """ return os.listdir(*args, **kwargs) def exists(self, *args, **kwargs): """ Wrapper around os.path.exists """ return os.path.exists(*args, **kwargs) def get_resources(self): """Returns a list of every private resources from nagios.cfg""" resources = [] for config_object, config_value in self.maincfg_values: if config_object == 'resource_file' and self.isfile(config_value): resources += self._load_static_file(config_value) return resources def extended_parse(self): """ This parse is used after the initial parse() command is run. It is only needed if you want extended meta information about hosts or other objects """ # Do the initial parsing self.parse() # First, cycle through the hosts, and append hostgroup information index = 0 for host in self.data['all_host']: if host.get("register", None) == "0": continue if not "host_name" in host: continue if not "hostgroup_list" in self.data['all_host'][index]['meta']: self.data['all_host'][index]['meta']['hostgroup_list'] = [] # Append any hostgroups that are directly listed in the host definition if "hostgroups" in host: for hostgroup_name in self._get_list(host, 'hostgroups'): if not "hostgroup_list" in self.data['all_host'][index]['meta']: self.data['all_host'][index]['meta']['hostgroup_list'] = [] if hostgroup_name not in self.data['all_host'][index]['meta']['hostgroup_list']: self.data['all_host'][index]['meta']['hostgroup_list'].append(hostgroup_name) # Append any services which reference this host service_list = [] for service in self.data['all_service']: if service.get("register", None) == "0": continue if not "service_description" in service: continue if host['host_name'] in self._get_active_hosts(service): service_list.append(service['service_description']) self.data['all_host'][index]['meta']['service_list'] = service_list # Increment count index += 1 # Loop through all hostgroups, appending them to their respective hosts for hostgroup in self.data['all_hostgroup']: for member in self._get_list(hostgroup, 'members'): index = 0 for host in self.data['all_host']: if not "host_name" in host: continue # Skip members that do not match if host['host_name'] == member: # Create the meta var if it doesn' exist if not "hostgroup_list" in self.data['all_host'][index]['meta']: self.data['all_host'][index]['meta']['hostgroup_list'] = [] if hostgroup['hostgroup_name'] not in self.data['all_host'][index]['meta']['hostgroup_list']: self.data['all_host'][index]['meta']['hostgroup_list'].append(hostgroup['hostgroup_name']) # Increment count index += 1 # Expand service membership index = 0 for service in self.data['all_service']: # Find a list of hosts to negate from the final list self.data['all_service'][index]['meta']['service_members'] = self._get_active_hosts(service) # Increment count index += 1 def _get_active_hosts(self, item): """ Given an object, return a list of active hosts. This will exclude hosts that are negated with a "!" Args: item: Item to obtain active hosts from. Returns: List of all the active hosts for `item` """ # First, generate the negation list negate_hosts = [] # Hostgroups if "hostgroup_name" in item: for hostgroup_name in self._get_list(item, 'hostgroup_name'): if hostgroup_name[0] == "!": hostgroup_obj = self.get_hostgroup(hostgroup_name[1:]) negate_hosts.extend(self._get_list(hostgroup_obj, 'members')) # Host Names if "host_name" in item: for host_name in self._get_list(item, 'host_name'): if host_name[0] == "!": negate_hosts.append(host_name[1:]) # Now get hosts that are actually listed active_hosts = [] # Hostgroups if "hostgroup_name" in item: for hostgroup_name in self._get_list(item, 'hostgroup_name'): if hostgroup_name[0] != "!": active_hosts.extend(self._get_list(self.get_hostgroup(hostgroup_name), 'members')) # Host Names if "host_name" in item: for host_name in self._get_list(item, 'host_name'): if host_name[0] != "!": active_hosts.append(host_name) # Combine the lists return_hosts = [] for active_host in active_hosts: if active_host not in negate_hosts: return_hosts.append(active_host) return return_hosts def get_cfg_dirs(self): """ Parses the main config file for configuration directories Returns: List of all cfg directories used in this configuration Example:: print(get_cfg_dirs()) ['/etc/nagios/hosts','/etc/nagios/objects',...] """ cfg_dirs = [] for config_object, config_value in self.maincfg_values: if config_object == "cfg_dir": cfg_dirs.append(config_value) return cfg_dirs def get_cfg_files(self): """ Return a list of all cfg files used in this configuration Filenames are normalised so that if nagios.cfg specifies relative filenames we will convert it to fully qualified filename before returning. Returns: List of all configurations files used in the configuration. Example: print(get_cfg_files()) ['/etc/nagios/hosts/host1.cfg','/etc/nagios/hosts/host2.cfg',...] """ cfg_files = [] for config_object, config_value in self.maincfg_values: # Add cfg_file objects to cfg file list if config_object == "cfg_file": config_value = self.abspath(config_value) if self.isfile(config_value): cfg_files.append(config_value) # Parse all files in a cfg directory if config_object == "cfg_dir": config_value = self.abspath(config_value) directories = [] raw_file_list = [] directories.append(config_value) # Walk through every subdirectory and add to our list while directories: current_directory = directories.pop(0) # Nagios doesnt care if cfg_dir exists or not, so why should we ? if not self.isdir(current_directory): continue for item in self.listdir(current_directory): # Append full path to file item = "%s" % (os.path.join(current_directory, item.strip())) if self.islink(item): item = os.readlink(item) if self.isdir(item): directories.append(item) if raw_file_list.count(item) < 1: raw_file_list.append(item) for raw_file in raw_file_list: if raw_file.endswith('.cfg'): if self.exists(raw_file) and not self.isdir(raw_file): # Nagios doesnt care if cfg_file exists or not, so we will not throws errors cfg_files.append(raw_file) return cfg_files def abspath(self, path): """ Return the absolute path of a given relative path. The current working directory is assumed to be the dirname of nagios.cfg Args: path: relative path to be transformed into absolute path. (string) Returns: Absolute path of given relative path. Example: >>> c = config(cfg_file="/etc/nagios/nagios.cfg") >>> c.abspath('nagios.cfg') '/etc/nagios/nagios.cfg' >>> c.abspath('/etc/nagios/nagios.cfg') '/etc/nagios/nagios.cfg' """ if not isinstance(path, str): return ValueError("Path must be a string got %s instead" % type(path)) if path.startswith('/'): return path nagiosdir = os.path.dirname(self.cfg_file) normpath = os.path.abspath(os.path.join(nagiosdir, path)) return normpath def get_cfg_value(self, key): """ Returns one specific value from your nagios.cfg file, None if value is not found. Arguments: key: what attribute to fetch from nagios.cfg (example: "command_file" ) Returns: String of the first value found for Example: >>> c = Config() # doctest: +SKIP >>> log_file = c.get_cfg_value('log_file') # doctest: +SKIP # Should return something like "/var/log/nagios/nagios.log" """ if not self.maincfg_values: self.parse_maincfg() for k, v in self.maincfg_values: if k == key: return v return None def get_object_types(self): """ Returns a list of all discovered object types """ return map(lambda x: re.sub("all_", "", x), self.data.keys()) def cleanup(self): """ Remove configuration files that have no configuration items """ for filename in self.cfg_files: if not self.parse_file(filename): # parse_file returns empty list on empty files self.remove(filename) # If nagios.cfg specifies this file directly via cfg_file directive then... for k, v in self.maincfg_values: if k == 'cfg_file' and v == filename: self._edit_static_file(k, old_value=v, new_value=None) def __setitem__(self, key, item): self.data[key] = item def __getitem__(self, key): return self.data[key] class Livestatus(object): """ Wrapper around MK-Livestatus Example usage:: s = Livestatus() for hostgroup s.get_hostgroups(): print(hostgroup['name'], hostgroup['num_hosts']) """ def __init__(self, livestatus_socket_path=None, nagios_cfg_file=None, authuser=None): """ Initilize a new instance of Livestatus Args: livestatus_socket_path: Path to livestatus socket (if none specified, use one specified in nagios.cfg) nagios_cfg_file: Path to your nagios.cfg. If None then try to auto-detect authuser: If specified. Every data pulled is with the access rights of that contact. """ self.nagios_cfg_file = nagios_cfg_file self.error = None if not livestatus_socket_path: c = config(cfg_file=nagios_cfg_file) c.parse_maincfg() self.nagios_cfg_file = c.cfg_file # Look for a broker_module line in the main config and parse its arguments # One of the arguments is path to the file socket created for k, v in c.maincfg_values: if k == 'broker_module' and "livestatus.o" in v: for arg in v.split()[1:]: if arg.startswith('/') or '=' not in arg: livestatus_socket_path = arg break else: # If we get here, then we could not locate a broker_module argument # that looked like a filename msg = "No Livestatus socket defined. Make sure livestatus broker module is loaded." raise ParserError(msg) self.livestatus_socket_path = livestatus_socket_path self.authuser = authuser def test(self, raise_error=True): """ Test if connection to livestatus socket is working Args: raise_error: If set to True, raise exception if test fails,otherwise return False Raises: ParserError if raise_error == True and connection fails Returns: True -- Connection is OK False -- there are problems and raise_error==False """ try: self.query("GET hosts") except Exception: t, e = sys.exc_info()[:2] self.error = e if raise_error: raise ParserError("got '%s' when testing livestatus socket. error was: '%s'" % (type(e), e)) else: return False return True def _get_socket(self): """ Returns a socket.socket() instance to communicate with livestatus Socket might be either unix filesocket or a tcp socket depenging in the content of :py:attr:`livestatus_socket_path` Returns: Socket to livestatus instance (socket.socket) Raises: :py:class:`LivestatusNotConfiguredException` on failed connection. :py:class:`ParserError` If could not parse configured TCP address correctly. """ if not self.livestatus_socket_path: msg = "We could not find path to MK livestatus socket file. Make sure MK livestatus is installed and configured" raise LivestatusNotConfiguredException(msg) try: # If livestatus_socket_path contains a colon, then we assume that it is tcp socket instead of a local filesocket if self.livestatus_socket_path.find(':') > 0: address, tcp_port = self.livestatus_socket_path.split(':', 1) if not tcp_port.isdigit(): msg = 'Could not parse host:port "%s". %s does not look like a valid port is not a valid tcp port.' raise ParserError(msg % (self.livestatus_socket_path, tcp_port)) tcp_port = int(tcp_port) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((address, tcp_port)) else: s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect(self.livestatus_socket_path) return s except IOError: t, e = sys.exc_info()[:2] msg = "%s while connecting to '%s'. Make sure nagios is running and mk_livestatus loaded." raise ParserError(msg % (e, self.livestatus_socket_path)) def query(self, query, *args, **kwargs): """ Performs LQL queries the livestatus socket Queries are corrected and convienient default data are added to the query before sending it to the socket. Args: query: Query to be passed to the livestatus socket (string) args, kwargs: Additionnal parameters that will be sent to :py:meth:`pynag.Utils.grep_to_livestatus`. The result will be appended to the query. Returns: Answer from livestatus. It will be in python format unless specified otherwise. Raises: :py:class:`ParserError` if problems connecting to livestatus. """ # columns parameter is here for backwards compatibility only kwargs.pop('columns', None) # We break query up into a list, of commands, then before sending command to the socket # We will write it one line per item in the array query = query.split('\n') query += pynag.Utils.grep_to_livestatus(*args, **kwargs) # If no response header was specified, we add fixed16 response_header = None if not filter(lambda x: x.startswith('ResponseHeader:'), query): query.append("ResponseHeader: fixed16") response_header = "fixed16" # If no specific outputformat is requested, we will return in python format python_format = False if not filter(lambda x: x.startswith('OutputFormat:'), query): query.append("OutputFormat: python") python_format = True # There is a bug in livestatus where if requesting Stats, then no column headers are sent from livestatus # In later version, the headers are sent, but the output is corrupted. # # We maintain consistency by clinging on to the old bug, and if there are Stats in the output # we will not ask for column headers doing_stats = len(filter(lambda x: x.startswith('Stats:'), query)) > 0 if not filter(lambda x: x.startswith('Stats:'), query) and not filter( lambda x: x.startswith('ColumnHeaders: on'), query): query.append("ColumnHeaders: on") # Check if we need to add authuser to the query if not filter(lambda x: x.startswith('AuthUser:'), query) and self.authuser not in (None, ''): query.append("AuthUser: %s" % self.authuser) # When we reach here, we are done adding options to the query, so we convert to the string that will # be sent to the livestatus socket query = '\n'.join(query) + '\n' self.last_query = query # # Lets create a socket and see if we can write to it # s = self._get_socket() try: s.send(query) except IOError: msg = "Could not write to socket '%s'. Make sure you have the right permissions" raise ParserError(msg % self.livestatus_socket_path) s.shutdown(socket.SHUT_WR) tmp = s.makefile() # Read the response header from livestatus if response_header == "fixed16": response_data = tmp.readline() if len(response_data) == 0: return [] return_code = response_data.split()[0] if not return_code.startswith('2'): error_message = tmp.readline().strip() raise ParserError("Error '%s' from livestatus: %s" % (return_code, error_message)) answer = tmp.read() # We are done with the livestatus socket. lets close it s.close() if answer == '': return [] # If something other than python format was requested, we return the answer as is if python_format is False: return answer # If we reach down here, it means we are supposed to parse the output before returning it try: answer = eval(answer) except Exception: raise ParserError("Error, could not parse response from livestatus.\n%s" % answer) # Workaround for livestatus bug, where column headers are not provided even if we asked for them if doing_stats is True and len(answer) == 1: return answer[0] columns = answer.pop(0) # Lets throw everything into a hashmap before we return result = [] for line in answer: tmp = {} for i, column in enumerate(line): column_name = columns[i] tmp[column_name] = column result.append(tmp) return result def get(self, table, *args, **kwargs): """ Same as self.query('GET %s' % (table,)) Extra arguments will be appended to the query. Args: table: Table from which the data will be retrieved args, kwargs: These will be appendend to the end of the query to perform additionnal instructions. Example:: get('contacts', 'Columns: name alias') Returns: Answer from livestatus in python format. """ return self.query('GET %s' % (table,), *args, **kwargs) def get_host(self, host_name): """ Performs a GET query for a particular host This performs:: '''GET hosts Filter: host_name = %s''' % host_name Args: host_name: name of the host to obtain livestatus data from Returns: Answer from livestatus in python format. """ return self.query('GET hosts', 'Filter: host_name = %s' % host_name)[0] def get_service(self, host_name, service_description): """ Performs a GET query for a particular service This performs:: '''GET services Filter: host_name = %s Filter: service_description = %s''' % (host_name, service_description) Args: host_name: name of the host the target service is attached to. service_description: Description of the service to obtain livestatus data from. Returns: Answer from livestatus in python format. """ return self.query('GET services', 'Filter: host_name = %s' % host_name, 'Filter: description = %s' % service_description)[0] def get_hosts(self, *args, **kwargs): """ Performs a GET query for all hosts This performs:: '''GET hosts %s %s''' % (*args, **kwargs) Args: args, kwargs: These will be appendend to the end of the query to perform additionnal instructions. Returns: Answer from livestatus in python format. """ return self.query('GET hosts', *args, **kwargs) def get_services(self, *args, **kwargs): """ Performs a GET query for all services This performs:: '''GET services %s %s''' % (*args, **kwargs) Args: args, kwargs: These will be appendend to the end of the query to perform additionnal instructions. Returns: Answer from livestatus in python format. """ return self.query('GET services', *args, **kwargs) def get_hostgroups(self, *args, **kwargs): """ Performs a GET query for all hostgroups This performs:: '''GET hostgroups %s %s''' % (*args, **kwargs) Args: args, kwargs: These will be appendend to the end of the query to perform additionnal instructions. Returns: Answer from livestatus in python format. """ return self.query('GET hostgroups', *args, **kwargs) def get_servicegroups(self, *args, **kwargs): """ Performs a GET query for all servicegroups This performs:: '''GET servicegroups %s %s''' % (*args, **kwargs) Args: args, kwargs: These will be appendend to the end of the query to perform additionnal instructions. Returns: Answer from livestatus in python format. """ return self.query('GET servicegroups', *args, **kwargs) def get_contactgroups(self, *args, **kwargs): """ Performs a GET query for all contactgroups This performs:: '''GET contactgroups %s %s''' % (*args, **kwargs) Args: args, kwargs: These will be appendend to the end of the query to perform additionnal instructions. Returns: Answer from livestatus in python format. """ return self.query('GET contactgroups', *args, **kwargs) def get_contacts(self, *args, **kwargs): """ Performs a GET query for all contacts This performs:: '''GET contacts %s %s''' % (*args, **kwargs) Args: args, kwargs: These will be appendend to the end of the query to perform additionnal instructions. Returns: Answer from livestatus in python format. """ return self.query('GET contacts', *args, **kwargs) def get_contact(self, contact_name): """ Performs a GET query for a particular contact This performs:: '''GET contacts Filter: contact_name = %s''' % contact_name Args: contact_name: name of the contact to obtain livestatus data from Returns: Answer from livestatus in python format. """ return self.query('GET contacts', 'Filter: contact_name = %s' % contact_name)[0] def get_servicegroup(self, name): """ Performs a GET query for a particular servicegroup This performs:: '''GET servicegroups Filter: servicegroup_name = %s''' % servicegroup_name Args: servicegroup_name: name of the servicegroup to obtain livestatus data from Returns: Answer from livestatus in python format. """ return self.query('GET servicegroups', 'Filter: name = %s' % name)[0] def get_hostgroup(self, name): """ Performs a GET query for a particular hostgroup This performs:: '''GET hostgroups Filter: hostgroup_name = %s''' % hostgroup_name Args: hostgroup_name: name of the hostgroup to obtain livestatus data from Returns: Answer from livestatus in python format. """ return self.query('GET hostgroups', 'Filter: name = %s' % name)[0] def get_contactgroup(self, name): """ Performs a GET query for a particular contactgroup This performs:: '''GET contactgroups Filter: contactgroup_name = %s''' % contactgroup_name Args: contactgroup_name: name of the contactgroup to obtain livestatus data from Returns: Answer from livestatus in python format. """ return self.query('GET contactgroups', 'Filter: name = %s' % name)[0] class RetentionDat(object): """ Easy way to parse the content of retention.dat After calling parse() contents of retention.dat are kept in self.data Example Usage:: r = retention() r.parse() print r print r.data['info'] """ def __init__(self, filename=None, cfg_file=None): """ Initilize a new instance of retention.dat Args (you only need to provide one of these): filename: path to your retention.dat file cfg_file: path to your nagios.cfg file, path to retention.dat will be looked up in this file """ # If filename is not provided, lets try to discover it from # nagios.cfg if filename is None: c = config(cfg_file=cfg_file) for key, value in c._load_static_file(): if key == "state_retention_file": filename = value self.filename = filename self.data = None def parse(self): """ Parses your status.dat file and stores in a dictionary under self.data Returns: None Raises: :py:class:`ParserError`: if problem arises while reading status.dat :py:class:`ParserError`: if status.dat is not found :py:class:`IOError`: if status.dat cannot be read """ self.data = {} status = {} # Holds all attributes of a single item key = None # if within definition, store everything before = value = None # if within definition, store everything after = if not self.filename: raise ParserError("status.dat file not found") lines = open(self.filename, 'rb').readlines() for sequence_no, line in enumerate(lines): line_num = sequence_no + 1 # Cleanup and line skips line = line.strip() if line == "": pass elif line[0] == "#" or line[0] == ';': pass elif line.find("{") != -1: status = {} status['meta'] = {} status['meta']['type'] = line.split("{")[0].strip() elif line.find("}") != -1: # Status definition has finished, lets add it to # self.data if status['meta']['type'] not in self.data: self.data[status['meta']['type']] = [] self.data[status['meta']['type']].append(status) else: tmp = line.split("=", 1) if len(tmp) == 2: (key, value) = line.split("=", 1) status[key] = value elif key == "long_plugin_output": # special hack for long_output support. We get here if: # * line does not contain { # * line does not contain } # * line does not contain = # * last line parsed started with long_plugin_output= status[key] += "\n" + line else: raise ParserError("Error on %s:%s: Could not parse line: %s" % (self.filename, line_num, line)) def __setitem__(self, key, item): self.data[key] = item def __getitem__(self, key): return self.data[key] def __str__(self): if not self.data: self.parse() str_buffer = "# Generated by pynag" for datatype, datalist in self.data.items(): for item in datalist: str_buffer += "%s {\n" % datatype for attr, value in item.items(): str_buffer += "%s=%s\n" % (attr, value) str_buffer += "}\n" return str_buffer class StatusDat(RetentionDat): """ Easy way to parse status.dat file from nagios After calling parse() contents of status.dat are kept in status.data Example usage:: >>> s = status() >>> s.parse() >>> keys = s.data.keys() >>> 'info' in keys True >>> 'programstatus' in keys True >>> for service in s.data.get('servicestatus',[]): ... host_name=service.get('host_name', None) ... description=service.get('service_description',None) """ def __init__(self, filename=None, cfg_file=None): """ Initilize a new instance of status Args (you only need to provide one of these): filename: path to your status.dat file cfg_file: path to your nagios.cfg file, path to status.dat will be looked up in this file """ # If filename is not provided, lets try to discover it from # nagios.cfg if filename is None: c = config(cfg_file=cfg_file) for key, value in c._load_static_file(): if key == "status_file": filename = value self.filename = filename self.data = None def get_contactstatus(self, contact_name): """ Returns a dictionary derived from status.dat for one particular contact Args: contact_name: `contact_name` field of the contact's status.dat data to parse and return as a dict. Returns: dict derived from status.dat for the contact. Raises: ValueError if object is not found Example: >>> s = status() >>> s.get_contactstatus(contact_name='invalid_contact') ValueError('invalid_contact',) >>> first_contact = s.data['contactstatus'][0]['contact_name'] >>> s.get_contactstatus(first_contact)['contact_name'] == first_contact True """ if self.data is None: self.parse() for i in self.data['contactstatus']: if i.get('contact_name') == contact_name: return i return ValueError(contact_name) def get_hoststatus(self, host_name): """ Returns a dictionary derived from status.dat for one particular contact Args: host_name: `host_name` field of the host's status.dat data to parse and return as a dict. Returns: dict derived from status.dat for the host. Raises: ValueError if object is not found """ if self.data is None: self.parse() for i in self.data['hoststatus']: if i.get('host_name') == host_name: return i raise ValueError(host_name) def get_servicestatus(self, host_name, service_description): """ Returns a dictionary derived from status.dat for one particular service Args: service_name: `service_name` field of the host's status.dat data to parse and return as a dict. Returns: dict derived from status.dat for the service. Raises: ValueError if object is not found """ if self.data is None: self.parse() for i in self.data['servicestatus']: if i.get('host_name') == host_name: if i.get('service_description') == service_description: return i raise ValueError(host_name, service_description) class ObjectCache(Config): """ Loads the configuration as it appears in objects.cache file """ def get_cfg_files(self): for k, v in self.maincfg_values: if k == 'object_cache_file': return [v] class ParserError(Exception): """ ParserError is used for errors that the Parser has when parsing config. Typical usecase when there is a critical error while trying to read configuration. """ filename = None line_start = None message = None def __init__(self, message, item=None): """ Creates an instance of ParserError Args: message: Message to be printed by the error item: Pynag item who caused the error """ self.message = message if item is None: return self.item = item self.filename = item['meta']['filename'] self.line_start = item['meta'].get('line_start') def __str__(self): message = self.message if self.filename and self.line_start: message = '%s in %s, line %s' % (message, self.filename, self.line_start) return repr(message) class ConfigFileNotFound(ParserError): """ This exception is thrown if we cannot locate any nagios.cfg-style config file. """ pass class LivestatusNotConfiguredException(ParserError): """ This exception is raised if we tried to autodiscover path to livestatus and failed """ class LogFiles(object): """ Parses Logfiles defined in nagios.cfg and allows easy access to its content Content is stored in python-friendly arrays of dicts. Output should be more or less compatible with mk_livestatus log output """ def __init__(self, maincfg=None): self.config = config(maincfg) self.log_file = self.config.get_cfg_value('log_file') self.log_archive_path = self.config.get_cfg_value('log_archive_path') def get_log_entries(self, start_time=None, end_time=None, strict=True, search=None, **kwargs): """ Get Parsed log entries for given timeperiod. Args: start_time: unix timestamp. if None, return all entries from today end_time: If specified, only fetch log entries older than this (unix timestamp) strict: If True, only return entries between start_time and end_time, if False, then return entries that belong to same log files as given timeset search: If provided, only return log entries that contain this string (case insensitive) kwargs: All extra arguments are provided as filter on the log entries. f.e. host_name="localhost" Returns: List of dicts """ now = time.time() if end_time is None: end_time = now if start_time is None: if 'filename' in kwargs: start_time = 1 else: seconds_in_a_day = 60 * 60 * 24 seconds_today = end_time % seconds_in_a_day # midnight of today start_time = end_time - seconds_today start_time = int(start_time) end_time = int(end_time) logfiles = self.get_logfiles() if 'filename' in kwargs: logfiles = filter(lambda x: x == kwargs.get('filename'), logfiles) # If start time was provided, skip all files that we last modified # before start_time if start_time: logfiles = filter(lambda x: start_time <= os.stat(x).st_mtime, logfiles) # Log entries are returned in ascending order, which is the opposite of # what get_logfiles returns. logfiles.reverse() result = [] for log_file in logfiles: entries = self._parse_log_file(filename=log_file) if len(entries) == 0: continue first_entry = entries[0] last_entry = entries[-1] if first_entry['time'] > end_time: continue # If strict, filter entries to only include the ones in the timespan if strict is True: entries = [x for x in entries if x['time'] >= start_time and x['time'] <= end_time] # If search string provided, filter the string if search is not None: entries = [x for x in entries if x['message'].lower().find(search.lower()) > -1] for k, v in kwargs.items(): entries = [x for x in entries if x.get(k) == v] result += entries if start_time is None or int(start_time) >= int(first_entry.get('time')): continue # Now, logfiles should in MOST cases come sorted for us. # However we rely on modification time of files and if it is off, # We want to make sure log entries are coming in the correct order. # The following sort should not impact performance in the typical use case. result.sort(key=lambda x: x.get('time')) return result def get_logfiles(self): """ Returns a list with the fullpath to every log file used by nagios. Lists are sorted by modification times. Newest logfile is at the front of the list so usually nagios.log comes first, followed by archivelogs Returns: List of strings """ logfiles = [] for filename in os.listdir(self.log_archive_path): full_path = "%s/%s" % (self.log_archive_path, filename) logfiles.append(full_path) logfiles.append(self.log_file) # Sort the logfiles by modification time, newest file at the front compare_mtime = lambda a, b: os.stat(a).st_mtime < os.stat(b).st_mtime logfiles.sort(key=lambda x: int(os.stat(x).st_mtime)) # Newest logfiles go to the front of the list logfiles.reverse() return logfiles def get_flap_alerts(self, **kwargs): """ Same as :py:meth:`get_log_entries`, except return timeperiod transitions. Takes same parameters. """ return self.get_log_entries(class_name="timeperiod transition", **kwargs) def get_notifications(self, **kwargs): """ Same as :py:meth:`get_log_entries`, except return only notifications. Takes same parameters. """ return self.get_log_entries(class_name="notification", **kwargs) def get_state_history(self, start_time=None, end_time=None, host_name=None, strict=True, service_description=None): """ Returns a list of dicts, with the state history of hosts and services. Args: start_time: unix timestamp. if None, return all entries from today end_time: If specified, only fetch log entries older than this (unix timestamp) host_name: If provided, only return log entries that contain this string (case insensitive) service_description: If provided, only return log entries that contain this string (case insensitive) Returns: List of dicts with state history of hosts and services """ log_entries = self.get_log_entries(start_time=start_time, end_time=end_time, strict=strict, class_name='alerts') result = [] last_state = {} now = time.time() for line in log_entries: if 'state' not in line: continue line['duration'] = now - int(line.get('time')) if host_name is not None and host_name != line.get('host_name'): continue if service_description is not None and service_description != line.get('service_description'): continue if start_time is None: start_time = int(line.get('time')) short_name = "%s/%s" % (line['host_name'], line['service_description']) if short_name in last_state: last = last_state[short_name] last['end_time'] = line['time'] last['duration'] = last['end_time'] - last['time'] line['previous_state'] = last['state'] last_state[short_name] = line if strict is True: if start_time is not None and int(start_time) > int(line.get('time')): continue if end_time is not None and int(end_time) < int(line.get('time')): continue result.append(line) return result def _parse_log_file(self, filename=None): """ Parses one particular nagios logfile into arrays of dicts. Args: filename: Log file to be parsed. If is None, then log_file from nagios.cfg is used. Returns: A list of dicts containing all data from the log file """ if filename is None: filename = self.log_file result = [] for line in open(filename).readlines(): parsed_entry = self._parse_log_line(line) if parsed_entry != {}: parsed_entry['filename'] = filename result.append(parsed_entry) return result def _parse_log_line(self, line): """ Parse one particular line in nagios logfile and return a dict. Args: line: Line of the log file to be parsed. Returns: dict containing the information from the log file line. """ host = None service_description = None state = None check_attempt = None plugin_output = None contact = None m = re.search('^\[(.*?)\] (.*?): (.*)', line) if m is None: return {} line = line.strip() timestamp, logtype, options = m.groups() result = {} try: timestamp = int(timestamp) except ValueError: timestamp = 0 result['time'] = int(timestamp) result['type'] = logtype result['options'] = options result['message'] = line result['class'] = 0 # unknown result['class_name'] = 'unclassified' if logtype in ('CURRENT HOST STATE', 'CURRENT SERVICE STATE', 'SERVICE ALERT', 'HOST ALERT'): result['class'] = 1 result['class_name'] = 'alerts' if logtype.find('HOST') > -1: # This matches host current state: m = re.search('(.*?);(.*?);(.*);(.*?);(.*)', options) if m is None: return result host, state, hard, check_attempt, plugin_output = m.groups() service_description = None if logtype.find('SERVICE') > -1: m = re.search('(.*?);(.*?);(.*?);(.*?);(.*?);(.*)', options) if m is None: return result host, service_description, state, hard, check_attempt, plugin_output = m.groups() result['host_name'] = host result['service_description'] = service_description result['state'] = int(pynag.Plugins.state[state]) result['check_attempt'] = check_attempt result['plugin_output'] = plugin_output result['text'] = plugin_output elif "NOTIFICATION" in logtype: result['class'] = 3 result['class_name'] = 'notification' if logtype == 'SERVICE NOTIFICATION': m = re.search('(.*?);(.*?);(.*?);(.*?);(.*?);(.*)', options) if m is None: return result contact, host, service_description, state, command, plugin_output = m.groups() elif logtype == 'HOST NOTIFICATION': m = re.search('(.*?);(.*?);(.*?);(.*?);(.*)', options) if m is None: return result contact, host, state, command, plugin_output = m.groups() service_description = None result['contact_name'] = contact result['host_name'] = host result['service_description'] = service_description try: result['state'] = int(pynag.Plugins.state[state]) except Exception: result['state'] = -1 result['plugin_output'] = plugin_output result['text'] = plugin_output elif logtype == "EXTERNAL COMMAND": result['class'] = 5 result['class_name'] = 'command' m = re.search('(.*?);(.*)', options) if m is None: return result command_name, text = m.groups() result['command_name'] = command_name result['text'] = text elif logtype in ('PASSIVE SERVICE CHECK', 'PASSIVE HOST CHECK'): result['class'] = 4 result['class_name'] = 'passive' if logtype.find('HOST') > -1: # This matches host current state: m = re.search('(.*?);(.*?);(.*)', options) if m is None: return result host, state, plugin_output = m.groups() service_description = None if logtype.find('SERVICE') > -1: m = re.search('(.*?);(.*?);(.*?);(.*)', options) if m is None: return result host, service_description, state, plugin_output = m.groups() result['host_name'] = host result['service_description'] = service_description result['state'] = state result['plugin_output'] = plugin_output result['text'] = plugin_output elif logtype in ('SERVICE FLAPPING ALERT', 'HOST FLAPPING ALERT'): result['class_name'] = 'flapping' elif logtype == 'TIMEPERIOD TRANSITION': result['class_name'] = 'timeperiod_transition' elif logtype == 'Warning': result['class_name'] = 'warning' result['state'] = "1" result['text'] = options if 'text' not in result: result['text'] = result['options'] result['log_class'] = result['class'] # since class is a python keyword return result class ExtraOptsParser(object): """ Get Nagios Extra-Opts from a config file as specified by http://nagiosplugins.org/extra-opts We could ALMOST use pythons ConfParser but nagios plugin team thought it would be a good idea to support multiple values per key, so a dict datatype no longer works. Its a shame because we have to make our own "ini" parser as a result Usage:: # cat /etc/nagios/plugins.ini [main] host_name = localhost [other section] host_name = example.com # EOF e = ExtraOptsParser(section_name='main', config_file='/etc/nagios/plugins.ini') e.get('host_name') # returns "localhost" e.get_values() # Returns a dict of all the extra opts e.getlist('host_name') # returns all values of host_name (if more than one were specified) in a list """ standard_locations = [ "/etc/nagios/plugins.ini", "/usr/local/nagios/etc/plugins.ini", "/usr/local/etc/nagios/plugins.ini", "/etc/opt/nagios/plugins.ini", "/etc/nagios-plugins.ini", "/usr/local/etc/nagios-plugins.ini", "/etc/opt/nagios-plugins.ini", ] def __init__(self, section_name=None, config_file=None): if not section_name: section_name = self.get_default_section_name() if not config_file: config_file = self.get_default_config_file() self.section_name = section_name self.config_file = config_file self._all_options = self.parse_file(filename=config_file) or {} def get_values(self): """ Returns a dict with all extra-options with the granted section_name and config_file Results are in the form of:: { 'key': ["possible","values"] } """ return self._all_options.get(self.section_name, {}) def get_default_section_name(self): """ According to extra-opts standard, the default should be filename of check script being run """ return os.path.basename(sys.argv[0]) def get_default_config_file(self): """ Return path to first readable extra-opt config-file found According to the nagiosplugins extra-opts spec the search method is as follows: 1. Search for nagios.ini or nagios-plugins.ini in : splitted variable NAGIOS_CONFIG_PATH 2. Search in a predefined list of files 3. Return None if no config file is found The method works as follows: To quote the spec on NAGIOS_CONFIG_PATH: *"To use a custom location, set a NAGIOS_CONFIG_PATH environment variable to the set of directories that should be checked (this is a colon-separated list just like PATH). The first plugins.ini or nagios-plugins.ini file found in these directories will be used."* """ search_path = [] nagios_config_path = os.environ.get('NAGIOS_CONFIG_PATH', '') for path in nagios_config_path.split(':'): search_path.append(os.path.join(path, 'plugins.ini')) search_path.append(os.path.join(path, 'nagios-plugins.ini')) search_path += self.standard_locations self.search_path = search_path for path in search_path: if os.path.isfile(path): return path return None def get(self, option_name, default=_sentinel): """ Return the value of one specific option Args: option_name: The value set to this option will be returned Returns: The value of `option_name` Raises: :py:class:`ValueError` when `option_name` cannot be found in options """ result = self.getlist(option_name, default) # If option was not found, raise error if result == _sentinel: raise ValueError("Option named %s was not found" % (option_name)) elif result == default: return result elif not result: # empty list return result else: return result[0] def getlist(self, option_name, default=_sentinel): """ Return a list of all values for option_name Args: option_name: All the values set to this option will be returned Returns: List containing all the options set to `option_name` Raises: :py:class:`ValueError` when `option_name` cannot be found in options """ result = self.get_values().get(option_name, default) if result == _sentinel: raise ValueError("Option named %s was not found" % (option_name)) return result def parse_file(self, filename): """ Parses an ini-file and returns a dict of the ini values. The datatype returned is a list of sections where each section is a dict of values. Args: filename: Full path to the ini-file to be parsed. Example the following the file:: [main] name = this is a name key = value key = value2 Would return:: [ {'main': { 'name': ['this is a name'], 'key': [value, value2] } }, ] """ if filename is None: return {} f = open(filename) try: data = f.read() return self.parse_string(data) finally: f.close() def parse_string(self, string): """ Parses a string that is supposed to be ini-style format. See :py:meth:`parse_file` for more info Args: string: String to be parsed. Should be in ini-file format. Returns: Dictionnary containing all the sections of the ini-file and their respective data. Raises: :py:class:`ParserError` when line does not follow the ini format. """ sections = {} # When parsing inside a section, the name of it stored here. section_name = None current_section = pynag.Utils.defaultdict(dict) for line_no, line, in enumerate(string.splitlines()): line = line.strip() # skip empty lines if not line or line[0] in ('#', ';'): continue # Check if this is a new section if line.startswith('[') and line.endswith(']'): section_name = line.strip('[').strip(']').strip() current_section = pynag.Utils.defaultdict(list) sections[section_name] = current_section continue # All entries should have key=value format if not '=' in line: error = "Line %s should be in the form of key=value format (got '%s' instead)" % (line_no, line) raise ParserError(error) # If we reach here, we parse current line into key and a value section key, value = line.split('=', 1) key = key.strip() value = value.strip() sections[section_name][key].append(value) return sections class SshConfig(Config): """ Parse object configuration files from remote host via ssh Uses python-paramiko for ssh connections. """ def __init__(self, host, username, password=None, cfg_file=None): """ Creates a SshConfig instance Args: host: Host to connect to username: User to connect with password: Password for `username` cfg_file: Nagios main cfg file """ import paramiko self.ssh = paramiko.SSHClient() self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) self.ssh.connect(host, username=username, password=password) self.ftp = self.ssh.open_sftp() import cStringIO c = cStringIO.StringIO() self.tar = tarfile.open(mode='w', fileobj=c) self.cached_stats = {} super(SshConfig, self).__init__(cfg_file=cfg_file) def open(self, filename, *args, **kwargs): """ Behaves like file.open only, via ssh connection """ return self.tar.extractfile(filename) tarinfo = self._get_file(filename) string = tarinfo.tobuf() print string return StringIO.StringIO(string) return self.tar.extractfile(tarinfo) def add_to_tar(self, path): """ """ print "Taring ", path command = "find '{path}' -type f | tar -c -T - --to-stdout --absolute-names" command = command.format(path=path) print command stdin, stdout, stderr = self.ssh.exec_command(command, bufsize=50000) tar = tarfile.open(fileobj=stdout, mode='r|') if not self.tar: self.tar = tar # return else: for i in tar: self.tar.addfile(i) def is_cached(self, filename): if not self.tar: return False return filename in self.tar.getnames() def _get_file(self, filename): """ Download filename and return the TarInfo object """ if filename not in self.tar.getnames(): self.add_to_tar(filename) return self.tar.getmember(filename) def get_cfg_files(self): cfg_files = [] for config_object, config_value in self.maincfg_values: # Add cfg_file objects to cfg file list if config_object == "cfg_file": config_value = self.abspath(config_value) if self.isfile(config_value): cfg_files.append(config_value) elif config_object == "cfg_dir": absolut_path = self.abspath(config_value) command = "find '%s' -type f -iname \*cfg" % (absolut_path) stdin, stdout, stderr = self.ssh.exec_command(command) raw_filelist = stdout.read().splitlines() cfg_files += raw_filelist else: continue if not self.is_cached(config_value): self.add_to_tar(config_value) return cfg_files def isfile(self, path): """ Behaves like os.path.isfile only, via ssh connection """ try: copy = self._get_file(path) return copy.isfile() except IOError: return False def isdir(self, path): """ Behaves like os.path.isdir only, via ssh connection """ try: file_stat = self.stat(path) return stat.S_ISDIR(file_stat.st_mode) except IOError: return False def islink(self, path): """ Behaves like os.path.islink only, via ssh connection """ try: file_stat = self.stat(path) return stat.S_ISLNK(file_stat.st_mode) except IOError: return False def readlink(self, path): """ Behaves like os.readlink only, via ssh connection """ return self.ftp.readlink(path) def stat(self, *args, **kwargs): """ Wrapper around os.stat only, via ssh connection """ path = args[0] if not self.is_cached(path): self.add_to_tar(path) if path not in self.tar.getnames(): raise IOError("No such file or directory %s" % path) member = self.tar.getmember(path) member.st_mode = member.mode member.st_mtime = member.mtime return member def access(self, *args, **kwargs): """ Wrapper around os.access only, via ssh connection """ return os.access(*args, **kwargs) def exists(self, path): """ Wrapper around os.path.exists only, via ssh connection """ try: self.ftp.stat(path) return True except IOError: return False def listdir(self, *args, **kwargs): """ Wrapper around os.listdir but via ssh connection """ stats = self.ftp.listdir_attr(*args, **kwargs) for i in stats: self.cached_stats[args[0] + "/" + i.filename] = i files = map(lambda x: x.filename, stats) return files class MultiSite(Livestatus): """ Wrapps around multiple Livesatus instances and aggregates the results of queries. Example: >>> m = MultiSite() >>> m.add_backend(path='/var/spool/nagios/livestatus.socket', name='local') >>> m.add_backend(path='127.0.0.1:5992', name='remote') """ def __init__(self, *args, **kwargs): super(MultiSite, self).__init__(*args, **kwargs) self.backends = {} def add_backend(self, path, name): """ Add a new livestatus backend to this instance. Arguments: path (str): Path to file socket or remote address name (str): Friendly shortname for this backend """ backend = Livestatus( livestatus_socket_path=path, nagios_cfg_file=self.nagios_cfg_file, authuser=self.authuser ) self.backends[name] = backend def get_backends(self): """ Returns a list of mk_livestatus instances Returns: list. List of mk_livestatus instances """ return self.backends def get_backend(self, backend_name): """ Return one specific backend that has previously been added """ if not backend_name: return self.backends.values()[0] try: return self.backends[backend_name] except KeyError: raise ParserError("No backend found with name='%s'" % backend_name) def query(self, query, *args, **kwargs): """ Behaves like mk_livestatus.query() except results are aggregated from multiple backends Arguments: backend (str): If specified, fetch only data from this backend (see add_backend()) *args: Passed directly to mk_livestatus.query() **kwargs: Passed directly to mk_livestatus.query() """ result = [] backend = kwargs.pop('backend', None) # Special hack, if 'Stats' argument was provided to livestatus # We have to maintain compatibility with old versions of livestatus # and return single list with all results instead of a list of dicts doing_stats = any(map(lambda x: x.startswith('Stats:'), args + (query,))) # Iterate though all backends and run the query # TODO: Make this multithreaded for name, backend_instance in self.backends.items(): # Skip if a specific backend was requested and this is not it if backend and backend != name: continue query_result = backend_instance.query(query, *args, **kwargs) if doing_stats: result = self._merge_statistics(result, query_result) else: for row in query_result: row['backend'] = name result.append(row) return result def _merge_statistics(self, list1, list2): """ Merges multiple livestatus results into one result Arguments: list1 (list): List of integers list2 (list): List of integers Returns: list. Aggregated results of list1 + list2 Example: >>> result1 = [1,1,1,1] >>> result2 = [2,2,2,2] >>> MultiSite()._merge_statistics(result1, result2) [3, 3, 3, 3] """ if not list1: return list2 if not list2: return list1 number_of_columns = len(list1) result = [0] * number_of_columns for row in (list1, list2): for i, column in enumerate(row): result[i] += column return result def get_host(self, host_name, backend=None): """ Same as Livestatus.get_host() """ backend = self.get_backend(backend) return backend.get_host(host_name) def get_service(self, host_name, service_description, backend=None): """ Same as Livestatus.get_service() """ backend = self.get_backend(backend) return backend.get_service(host_name, service_description) def get_contact(self, contact_name, backend=None): """ Same as Livestatus.get_contact() """ backend = self.get_backend(backend) return backend.get_contact(contact_name) def get_contactgroup(self, contactgroup_name, backend=None): """ Same as Livestatus.get_contact() """ backend = self.get_backend(backend) return backend.get_contactgroup(contactgroup_name) def get_servicegroup(self, servicegroup_name, backend=None): """ Same as Livestatus.get_servicegroup() """ backend = self.get_backend(backend) return backend.get_servicegroup(servicegroup_name) def get_hostgroup(self, hostgroup_name, backend=None): """ Same as Livestatus.get_hostgroup() """ backend = self.get_backend(backend) return backend.get_hostgroup(hostgroup_name) class config(Config): """ This class is here only for backwards compatibility. Use Config instead. """ class mk_livestatus(Livestatus): """ This class is here only for backwards compatibility. Use Livestatus instead. """ class object_cache(ObjectCache): """ This class is here only for backwards compatibility. Use ObjectCache instead. """ class status(StatusDat): """ This class is here only for backwards compatibility. Use StatusDat instead. """ class retention(RetentionDat): """ This class is here only for backwards compatibility. Use RetentionDat instead. """ if __name__ == '__main__': import time start = time.time() ssh = SshConfig(host='status.adagios.org', username='palli') ssh.ssh.get_transport().window_size = 3 * 1024 * 1024 ssh.ssh.get_transport().use_compression() # ssh.add_to_tar('/etc/nagios') # sys.exit() # ssh.ssh.exec_command("/bin/ls") print "before reset" ssh.parse() end = time.time() print "duration=", end - start bland = ssh.tar.getmember('/etc/nagios/okconfig/hosts/web-servers/bland.is-http.cfg') print bland.tobuf() sys.exit(0) print "ssh up" ssh_conn = FastTransport(('status.adagios.org', 22)) ssh_conn.connect(username='palli') ftp = paramiko.SFTPClient.from_transport(ssh_conn) print "connected" \ "" ssh.ssh = ssh_conn ssh.ftp = ftp print "starting parse" print "done parsing" pynag-0.9.1+dfsg.orig/debian.upstream/0000775000175000017500000000000012370025176016607 5ustar clintclintpynag-0.9.1+dfsg.orig/debian.upstream/rules0000775000175000017500000000030212310325103017645 0ustar clintclint#!/usr/bin/make -f %: dh $@ --with python2 --buildsystem=python_distutils override_dh_auto_install: python setup.py install --root=debian/pynag --install-layout=deb override_dh_auto_build: pynag-0.9.1+dfsg.orig/debian.upstream/compat0000664000175000017500000000000212310325103017770 0ustar clintclint7 pynag-0.9.1+dfsg.orig/debian.upstream/copyright0000664000175000017500000000203212310325103020522 0ustar clintclintFormat: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: pynag Upstream-Contact: Pall Sigurdsson: Source: https://github.com/pynag/pynag/ Files: * Copyright: 2008-2010 Drew Stinnett 2010 Pall Sigurdsson License: GPL-2 This package 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 package 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, see . On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". pynag-0.9.1+dfsg.orig/debian.upstream/changelog0000664000175000017500000000643512370025102020456 0ustar clintclintpynag (0.9.1-1) unstable; urgency=low * New upstream version -- Tomas Edwardsson Tue, 05 Aug 2014 00:50:10 +0000 pynag (0.9.0-1) unstable; urgency=low * New upstream version -- Tomas Edwardsson Wed, 23 Jul 2014 11:40:22 +0000 pynag (0.8.9-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Tue, 15 Apr 2014 16:56:00 +0000 pynag (0.8.8-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Tue, 15 Apr 2014 16:38:41 +0000 pynag (0.8.7-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Mon, 14 Apr 2014 20:55:50 +0000 pynag (0.8.6-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Mon, 14 Apr 2014 20:27:13 +0000 pynag (0.8.5-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Tue, 08 Apr 2014 14:17:12 +0000 pynag (0.8.4-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Tue, 08 Apr 2014 10:58:55 +0000 pynag (0.8.3-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Mon, 24 Mar 2014 16:10:12 +0000 pynag (0.8.2-1) unstable; urgency=low * New upstream version -- Tomas Edwardsson Fri, 21 Mar 2014 10:39:37 +0000 pynag (0.8.1-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Wed, 19 Feb 2014 10:39:37 +0000 pynag (0.7.0-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Wed, 14 Oct 2013 10:39:37 +0000 pynag (0.6.1-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Wed, 30 Aug 2013 10:39:37 +0000 pynag (0.6.0-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Wed, 28 Aug 2013 10:39:37 +0000 pynag (0.5.0-1) unstable; urgency=low * New upstream version -- Tomas Edwardsson Fri, 28 Jun 2013 12:19:09 +0000 pynag (0.4.9-1) unstable; urgency=low * New upstream version -- Tomas Edwardsson Tue, 30 Apr 2013 15:01:21 +0000 pynag (0.4.8-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Wed, 12 Dec 2012 11:05:37 +0000 pynag (0.4.7-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Fri, 26 Oct 2012 08:49:37 +0000 pynag (0.4.6-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Sun, 16 Sep 2012 08:49:37 +0000 pynag (0.4.3-1) unstable; urgency=low * New upstream version -- Pall Sigurdsson Fri, 23 Jul 2012 08:49:37 +0000 pynag (0.4.2-2) unstable; urgency=low * Fixed duplicate copyright field in debian/copyright * Added missing debian watch file * Improved package long description * homepage field added to debian/control * Removed X-Python-Version from debian/control * Python build-dependency bumped * Removed debian/links -- Pall Sigurdsson Fri, 11 Jul 2012 00:49:37 +0000 pynag (0.4.2-1) unstable; urgency=low * Initial packaging. Closes: 680954 -- Pall Sigurdsson Fri, 29 Jun 2012 16:54:37 +0000 pynag-0.9.1+dfsg.orig/debian.upstream/watch0000664000175000017500000000013012310325103017615 0ustar clintclintversion=3 http://code.google.com/p/pynag/downloads/list \ .*pynag-([.\d]+).*\.tar\.gz.* pynag-0.9.1+dfsg.orig/debian.upstream/source/0000775000175000017500000000000012370025176020107 5ustar clintclintpynag-0.9.1+dfsg.orig/debian.upstream/source/format0000664000175000017500000000001412310325103021300 0ustar clintclint3.0 (quilt) pynag-0.9.1+dfsg.orig/debian.upstream/control0000664000175000017500000000074512310325103020203 0ustar clintclintSource: pynag Maintainer: Pall Sigurdsson Section: python Priority: optional Build-Depends: python (>= 2.6.6-3~), debhelper (>= 7.4.3) Standards-Version: 3.9.3 Homepage: http://pynag.org/ X-Python-Version: >= 2.4 Package: pynag Architecture: all Depends: ${misc:Depends}, ${python:Depends} Description: Python modules and utilities for pragmatically handling Nagios configuration file maintenance, status information, log file parsing and plug-in development. pynag-0.9.1+dfsg.orig/README.md0000664000175000017500000001126312363713622015012 0ustar clintclint[![Build Status](https://travis-ci.org/pynag/pynag.png?branch=master)](https://travis-ci.org/pynag/pynag) [![Coverage Status](https://coveralls.io/repos/pynag/pynag/badge.png?branch=master)](https://coveralls.io/r/pynag/pynag?branch=master) [![PyPI version](https://badge.fury.io/py/pynag.png)](http://badge.fury.io/py/pynag) About ===== Pynag a tool and a library for managing nagios configuration and provides a framework to write plugins. Pynag command-line utility offers the following features: - list python object definitions (e.g. list all hosts/services) - create new object definitions from command line - copy object definitions - remove object definitions - edit nagios.cfg from command-line Pynag also has the following modules: - Model - Easy way to work with configuration as python objects - Plugins - convenience classes for writing python plugins - Parsers - Various parsers for nagios configuration files - Control - Control of Nagios daemon Install ======= Using fedora/redhat: yum install pynag Using debian/ubuntu: apt-get install python-pynag pynag Using pip: pip install pynag Install latest git repository from source: git clone https://github.com/pynag/pynag.git cd pynag python setup.py build python setup.py install Getting started =============== List all services: import pynag.Model all_services pynag.Model.Service.objects.all for i in all_services: print i.host_name, i.service_description Change an address of a host: import pynag.Model myhost = pynag.Model.Host.objects.get_by_shortname('myhost.example.com') myhost.address = '127.0.0.1' myhost.save() # See what the host definition looks like after change: print myhost Create a new ssh service check for every host in the unix hostgroup: import pynag.Model hosts = pynag.Model.Host.objects.filter(hostgroup="unixservers") for host in hosts: new_service = pynag.Model.Service() new_service.host_name = host.host_name new_service.service_description = "SSH Connectivity" new_service.check_command = "check_ssh" # optionally control where new object is saved: new_service.set_filename( host.get_filename() ) new_service.save() Further Documentation ===================== We blatantly admit that documentation is scarce in pynag. Most of the documentation is in-line in pydoc strings in the respective python modules. Any help with improving the documentation is much appreciated. For more documentation see * http://docs.pynag.org/ * The pynag/examples directory * Our github wiki: https://github.com/pynag/pynag/wiki Pynag Command Line Tool ======================= Pynag also comes with a command-line tool that gives good examples what is possible with the library. Some example commands: list all hosts and their ip address: pynag list host_name address where object_type=host Change contactgroup for all services for a particular host: pynag update set contactgroups=admins where host_name="myhost" and object_type=service Copy a host, give it a new hostname and ip address: pynag copy set host_name=newhost address=newaddress where object_type=host and host_name=oldhost # Same for all its services: pynag copy set host_name=newhost where object_type=service and host_name=oldhost Known Issues ============ Model module's get_effective_* functions are not complete if your configuration is using regular expressions. For example, pynag.Model.Service.get_effective_hosts will fail on the following service definition: define service { service_description check http check_command check_http host_name www* } Same applies for exemptions like this one: define service { service_description check http check_command check_http hostgroup_name webservers host_name !dmzhost1,dmzhost2 } Who uses pynag ============== There are a few open source projects out there that use pynag. The ones we know of are: * Adagios: Impressive web configuration and status interface * Okconfig: Monitoring pack generator for Nagios * RESTlos: generic RESTful api for nagios-like monitoring systems Pynag is also used by lots of plugins around the world including: * check_eva.py * check_hpacucly.py * check_ipa/check_ipa_replication Know of more projects using pynag ? Contact us and we'll add them here. Contact us ========== If you need any help, want to contribute or just want to talk about pynag you can find us on one of the following: * Bug reports, feature requests: https://github.com/pynag/pynag/issues * Mailing list: https://groups.google.com/forum/?fromgroups#!forum/pynag-discuss * Irc chat: #pynag on freenode pynag-0.9.1+dfsg.orig/man/0000775000175000017500000000000012370025176014301 5ustar clintclintpynag-0.9.1+dfsg.orig/man/pynag.10000664000175000017500000001547212370025113015501 0ustar clintclint.\" Man page generated from reStructuredText. . .TH "PYNAG" "1" "August 05, 2014" "0.9.1" "pynag" .SH NAME pynag \- command line front for manipulating nagios configuration . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp pynag [options] [arguments] .SH DESCRIPTION .sp pynag is a command\-line utility that can be used to view or change current nagios configuration. .SH SUB-COMMANDS .sp \fIlist\fP .INDENT 0.0 .INDENT 3.5 print to screen nagios configuration objects as specified by a WHERE clause .INDENT 0.0 .INDENT 3.5 .nf pynag list [attribute1] [attribute2] [WHERE ...] .fi .sp .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp \fIupdate\fP .INDENT 0.0 .INDENT 3.5 modify specific attributes of nagios objects as specified by a WHERE and SET clause .INDENT 0.0 .INDENT 3.5 .nf pynag update set attr1=value WHERE attr=value and attr=value .fi .sp .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp \fIdelete\fP .INDENT 0.0 .INDENT 3.5 Delete objects from nagios configuration as specified by a WHERE clause .INDENT 0.0 .INDENT 3.5 .nf pynag delete delete .fi .sp .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp \fIadd\fP .INDENT 0.0 .INDENT 3.5 Add a new object definition .INDENT 0.0 .INDENT 3.5 .nf pynag add [attr2=value2] .fi .sp .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp \fIcopy\fP .INDENT 0.0 .INDENT 3.5 Copy objects, specifiying which attributes to change .INDENT 0.0 .INDENT 3.5 .nf pynag copy .fi .sp .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp \fIexecute\fP .INDENT 0.0 .INDENT 3.5 Executes the currently configured check command for a host or a service .INDENT 0.0 .INDENT 3.5 .nf pynag execute [service_description] .fi .sp .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp \fIconfig\fP .INDENT 0.0 .INDENT 3.5 modify values in main nagios configuration file (nagios.cfg) .INDENT 0.0 .INDENT 3.5 .nf pynag config [\-\-set ] [\-\-old_value=attribute] pynag config [\-\-append ] [\-\-old_value=attribute] pynag config [\-\-remove ] [\-\-old_value=attribute] pynag config [\-\-get ] .fi .sp .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SH WHERE STATEMENTS .sp Some Subcommands use WHERE statements to filter which objects to work with. Where has certain similarity with SQL syntax. .INDENT 0.0 .TP .B Syntax: .nf WHERE [AND attr=value] [OR attr=value] .in +2 [another where statement] .in -2 .fi .sp .sp where "attr" is any nagios attribute (i.e. host_name or service_description). .TP .B Example: .nf pynag list WHERE host_name=localhost and object_type=service pynag list WHERE object_type=host or object_type=service .fi .sp .UNINDENT .sp Any search attributes have the same syntax as the pynag filter. For example these work just fine: .INDENT 0.0 .INDENT 3.5 .nf pynag list WHERE host_name__contains=production pynag list WHERE host_name__startswith=prod pynag list WHERE host_name__notcontains=test pynag list host_name address WHERE address__exists=True pynag list host_name WHERE register__isnot=0 .fi .sp .UNINDENT .UNINDENT .sp The pynag filter supports few parameters that are not just attributes. .sp Example: .INDENT 0.0 .IP \(bu 2 filename \-\- The filename which the object belongs .IP \(bu 2 id \-\- pynag unique identifier for the object .IP \(bu 2 effective_command_line \-\- command which nagios will execute .UNINDENT .sp Of course these can be combined with the pynag filter syntax: .INDENT 0.0 .INDENT 3.5 .nf pynag list where filename__startswith=/etc/nagios/conf.d/ pynag list host_name service_description effective_command_line .fi .sp .UNINDENT .UNINDENT .sp For detailed description of the filter see pydoc for pynag.Model.ObjectDefintion.filter() .SH SET STATEMENTS .sp Subcommands that use SET statements (like update or copy) use them a list of attributes change for a specific object. .INDENT 0.0 .TP .B Syntax: .nf SET [attr2=value2] [...] .fi .sp .TP .B Example: .nf pynag update SET address=127.0.0.1 WHERE host_name=localhost and object_type=host .fi .sp .UNINDENT .SH EXAMPLES .SS List all services that have "myhost" as a host_name .nf pynag list host_name service_description WHERE host_name=myhost and object_type=service .fi .sp .SS Set check_period to 24x7 on all services that belong to host "myhost" .nf pynag update set check_period=24x7 WHERE host_name=myhost .fi .sp .SS list examples .nf pynag list host_name address WHERE object_type=host pynag list host_name service_description WHERE host_name=examplehost and object_type=service .fi .sp .SS update examples .nf pynag update SET host_name=newhostname WHERE host_name=oldhostname pynag update SET address=127.0.0.1 WHERE host_name=\(aqexamplehost.example.com\(aq and object_type=host .fi .sp .SS copy examples .nf pynag copy SET host_name=newhostname WHERE host_name=oldhostname pynag copy SET address=127.0.0.1 WHERE host_name=\(aqexamplehost.example.com\(aq and object_type=host .fi .sp .SS add examples .nf pynag add host host_name=examplehost use=generic\-host address=127.0.0.1 pynag add service service_description="Test Service" use="check_nrpe" host_name="localhost" .fi .sp .SS delete examples .nf pynag delete where object_type=service and host_name=\(aqmydeprecated_host\(aq pynag delete where filename__startswith=\(aq/etc/nagios/myoldhosts\(aq .fi .sp .SS execute examples .nf pynag execute localhost pynag execute localhost "Disk Space .fi .sp .SH ADDITIONAL RESOURCES .sp See \fI\%http://github.com/pynag/pynag.git\fP for more information. .SH AUTHOR Pall Sigurdsson and Tomas Edwardsson .SH COPYRIGHT 2014, Pall Sigurdsson and Tomas Edwardsson .\" Generated by docutils manpage writer. . pynag-0.9.1+dfsg.orig/man/pynag.1.gz0000664000175000017500000000364612370025113016120 0ustar clintclintK*SYmo6_q06 j&6 ^58\lEdD\VJ;ReGvؾEH{xwׂ?_pXK_2ArW<й?i>?~ȕCJNدFU/Zs3p3xm8"p)0O%Ldyk,Td.9%҄O$H8-' C#Rr!%"6cIW9ng6۹rⴝ'aI؝8q}{9; ö0A&`OϼmW4-(7 #8^}ugK,ųk3hzH\şxNA*EH`Ƀ/UJͪK,H`>).Dp8ď1i1 Ϳ!dǸVa~Cȸsq8C,/\^kS9Hby>cXFЫ&LTYߨE8}5-\r`awe8igbҘM9Ӆ\L(LQ%s%%&ˀKMaU"/pjhmr \3tji8[d*Z]@GϦ` Y"sɢ mݵiNUIp$gPտ8@o.|$GOM^n+RӢw&~Zmlu:h݇i@"5OO]l17j{FgE R͟͞+v>}k[;= z r-45Y9ȋPͩc/Jÿn HljZ=~B^ԚM؁2#-eB"3oUD1bS3w6}BI{) I]cz*nuyl0CR/f)5};4u +^LZg :[7C1Z{?ƵWÑsGϥ;j%@815G Ca |^VUrʼ"{s:pynag-0.9.1+dfsg.orig/setup.py0000775000175000017500000000467112353523627015260 0ustar clintclint#!/usr/bin/python ## setup.py ### from distutils.core import setup, Command from distutils.command.build_py import build_py as _build_py from pynag import __version__ from subprocess import call, PIPE, Popen import sys NAME = "pynag" SHORT_DESC = "Python modules for Nagios plugins and configuration" LONG_DESC = """ Python modules and utilities for pragmatically handling Nagios configuration file maintenance, status information, log file parsing and plug-in development. """ class BuildMan(Command): """Builds the man page using sphinx""" user_options = [] def run(self): cmd = "sphinx-build -b man docs man" sphinx_proc = Popen(cmd.split(), stdout=PIPE, stderr=PIPE) stdout, stderr = sphinx_proc.communicate() return_code = sphinx_proc.wait() if return_code: print "Warning: Build of manpage failed \"%s\":\n%s\n%s" % ( cmd, stdout, stderr) def initialize_options(self): pass def finalize_options(self): pass class PynagTest(Command): """Runs the build-test.py testing suite""" user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): errno = call([sys.executable, 'tests/build-test.py']) raise SystemExit(errno) if __name__ == "__main__": manpath = "share/man/man1" etcpath = "/etc/%s" % NAME etcmodpath = "/etc/%s/modules" % NAME initpath = "/etc/init.d/" logpath = "/var/log/%s/" % NAME varpath = "/var/lib/%s/" % NAME rotpath = "/etc/logrotate.d" setup( name='%s' % NAME, version=__version__, author='Drew Stinnett', description=SHORT_DESC, long_description=LONG_DESC, author_email='drew@drewlink.com', url='http://pynag.org/', license='GPLv2', scripts=['scripts/pynag'], packages=[ 'pynag', 'pynag.Model', 'pynag.Model.EventHandlers', 'pynag.Plugins', 'pynag.Parsers', 'pynag.Control', 'pynag.Utils', 'pynag.Control', 'pynag.Control.Command', ], data_files=[(manpath, ['man/pynag.1.gz',]),], cmdclass={ 'test': PynagTest, 'build_man': BuildMan, }, requires=['unittest2'], ) pynag-0.9.1+dfsg.orig/scripts/0000775000175000017500000000000012370025176015215 5ustar clintclintpynag-0.9.1+dfsg.orig/scripts/pynag0000775000175000017500000010420012353523627016263 0ustar clintclint#!/usr/bin/env python # -*- coding: utf-8 -*- # # pynag - Python Nagios plug-in and configuration environment # Copyright (C) 2010 Pall Sigurdsson # # 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. import sys import traceback from optparse import OptionParser,OptionGroup import pprint import pynag.Model import pynag.Parsers import pynag.Utils from pynag.Model import cfg_file from pynag import __version__ debug = False usage = """usage: %prog [options] [arguments] Available subcommands: list List hosts, services, etc. update Update hosts, services, etc. add Create a new host, service, etc. copy Copy a current host, service, etc. delete Delete a host, service, etc. execute Execute a check command for host or service config View or edit values in your nagios.cfg acknowledge Acknowledge host/service problems downtime Schedule downtime for objects livestatus Make queries to MK Livestatus version Output version information and exit See "man pynag" for examples and clarification of "where" syntax. For help on specific sub-command type: %prog --help (or --examples) """ parser = OptionParser(usage=usage) display_options = OptionGroup(parser, 'Display Options') parser.add_option('--cfg_file', dest="cfg_file", metavar='filename', default=None, help="Path to your nagios.cfg file") display_options.add_option('--examples', dest="examples", default=False, action="store_true", help="Show example usage") display_options.add_option('--debug', dest="debug", default=False, action="store_true", help="Print debug information to screen") display_options.add_option("--verbose", dest="verbose", action="store_true", default=False,help="Be extra verbose in output") display_options.add_option("--quiet", dest="quiet", action="store_true", default=False,help="Be less verbose in output") display_options.add_option("--width", dest="width", default=20, help="Column width in output (default 20)") display_options.add_option("--seperator", dest="seperator", metavar='sep', default=None,help="Seperator between columns in output. If none is specified, then fixed with columns are used instead of seperators.") parser.add_option_group(display_options) def parse_arguments(): """ Parse command line arguments and run command specified commands """ if len(sys.argv) < 2: parser.print_usage() elif sys.argv[1] == 'list': list_objects() elif sys.argv[1] == 'update': update_objects() elif sys.argv[1] == 'add': add_object() elif sys.argv[1] == 'copy': copy_object() elif sys.argv[1] == 'execute': execute_check_command() elif sys.argv[1] == 'delete': delete_object() elif sys.argv[1] == 'acknowledge': acknowledge() elif sys.argv[1] == 'downtime': downtime() elif sys.argv[1] == 'livestatus': livestatus() elif sys.argv[1] in ('config','config-set', 'config-append'): # TODO: Deprecate config-set and config-append in 0.5.x series main_config() elif sys.argv[1] == "help": parser.print_help() elif sys.argv[1] == "--help": parser.print_help() elif sys.argv[1] in ('--examples','examples'): print_examples() elif sys.argv[1] in ('version','--version'): print __version__ else: parser.error("Unknown sub-command '%s'" % sys.argv[1]) def execute_check_command(): """ Execute a host or service check_command """ parser.usage = '''%prog execute [service_description]''' (opts,args) = parser.parse_args(sys.argv[2:]) pynag.Model.cfg_file = opts.cfg_file try: if len(args) == 0: parser.error("You need to provide at least one argument to execute") host_name = args[0] host = pynag.Model.Host.objects.get_by_shortname(host_name) if len(args) == 1: obj = host else: host_name = args[0] service_description = " ".join(args[1:]) for i in host.get_effective_services(): if i.service_description == service_description: obj = i break else: raise pynag.Utils.PynagError("Host %s does not seem to have any service named '%s'" % (host_name,service_description)) if opts.quiet == False: print "# %s" %(obj.get_effective_command_line(host_name=host_name)) exit_code,stdout,stderr = obj.run_check_command(host_name=host_name) print stdout, if stderr != '': print "" print "----- errors -------" print stderr, print "--------------------" print "" if opts.quiet == False: print "# %s check exited with exit code %s" %(obj.object_type, exit_code) except KeyError, e: parser.error("Could not find host '%s'" % e) def search_objects(search_statements): """ takes a list of search statements, returns list of ObjectDefinition that matches the filter. Arguments: search_statements -- e.g. ['host_name=host1','and','service_description="Ping"'] Returns: [ObjectDefinition] -- e.g. [Object1,Object2] """ conditions = {} objects = [] for statement in search_statements: tmp = statement.split('=',1) if statement.lower() == 'and': continue if statement.lower() == 'or': objects += pynag.Model.ObjectDefinition.objects.filter(**conditions) conditions = {} continue if len(tmp) != 2: raise pynag.Utils.PynagError('Invalid statement: "%s"' %statement) key,value = tmp conditions[key] = value objects += pynag.Model.ObjectDefinition.objects.filter(**conditions) return sorted( set(objects) ) def search_object_list(object_list,search_statements): """ Same as search_objects() but searches object_list instead of pynag.Model.ObjectDefinition.objects.all. """ conditions = {} for statement in search_statements: tmp = statement.split('=',1) if statement.lower() == 'and': continue if len(tmp) != 2: raise pynag.Utils.PynagError('Invalid statement: "%s"' %statement) key,value = tmp conditions[key] = value return pynag.Utils.grep(object_list,**conditions) def main_config(): """ Get or change a value in main configuration file """ parser.usage = '''%prog config <--set|--append|--remove|--get> [OPTIONS]''' parser.add_option("--filename", dest="filename", default=None, help="Path to Nagios configuration file (deprecated, use --cfg_file)") parser.add_option("--append", dest="append", default=None, help="Append key=value pair to cfg file (example: cfg_file=/etc/nagios/myconfig.cfg)") parser.add_option("--old_value", dest="old_value", default=None, help="if specified, change only attributes that have this value set (by default change first occurance of key)") parser.add_option("--set", dest="set", default=None, help="Set key=value in cfg file (example: process_performance_data=1)") parser.add_option("--remove", dest="remove", default=None, help="Remove attribute in cfg file (example: --remove cfg_file). Use with --old_value in case of multiple attributes with same name") parser.add_option("--get", dest="get", default=None, help="Print one specific value from your file") (opts,args) = parser.parse_args(sys.argv[1:]) pynag.Model.cfg_file = opts.cfg_file if opts.examples == True: return print_examples() # For backwards compatibility, let --filename act as --cfg_file if --cfg_file is not set # If neither option is specified, cfg_file will be set to None which means auto-detect # TODO: Deprecate --filename option in 0.5.x series if not pynag.Model.cfg_file: pynag.Model.cfg_file = opts.filename if not opts.append and not opts.remove and not opts.set and not opts.get: parser.error("Please specify at least one of --set --remove --get --append") c = pynag.Parsers.config(cfg_file=pynag.Model.cfg_file) c.parse() # For backwards compatibility, lets accept config-set and config-append instead command = args.pop(0) if command == 'config-append': append = True elif command == 'config-set': append = False if command in ('config-append','config-set'): attributes = {} while len(args) > 0: arg = args.pop(0) tmp = arg.split('=',1) if len(tmp) != 2: raise pynag.Utils.PynagError("attributes should be in the form of attribute=new_value for copy (got %s)" % arg ) attr,value = tmp attributes[ attr ] = value for k,v in attributes.items(): result = c._edit_static_file(attribute=k, new_value=v, old_value=opts.old_value, append=append) if result: if opts.quiet == False: print "%s: set %s=%s" % (c.cfg_file, k, result) else: if opts.quiet == False: print "%s: No changed were needed" % c.cfg_file return # Handle a --set operation if opts.set is not None: tmp = opts.set.split('=',1) if len(tmp) != 2: raise pynag.Utils.PynagError("attributes should be in the form of attribute=new_value (got %s)" % tmp) k,v = tmp result = c._edit_static_file(filename=c.cfg_file, attribute=k, new_value=v, old_value=opts.old_value, append=False) if result: if opts.quiet == False: print "%s: set %s=%s" % (c.cfg_file, k, v) else: if opts.quiet == False: print "%s: No changed were needed" % c.cfg_file # Handle a --append operation if opts.append is not None: tmp = opts.append.split('=',1) if len(tmp) != 2: raise pynag.Utils.PynagError("attributes should be in the form of attribute=new_value (got %s)" % tmp) k,v = tmp result = c._edit_static_file(filename=c.cfg_file, attribute=k, new_value=v, old_value=opts.old_value, append=True) if result: if opts.quiet == False: print "%s: set %s=%s" % (c.cfg_file, k, v) else: if opts.quiet == False: print "%s: No changed were needed" % c.cfg_file # Handle a remove operation if opts.remove is not None: result = c._edit_static_file(filename=c.cfg_file, attribute=opts.remove, new_value=None, old_value=opts.old_value, ) if result: if opts.quiet == False: print "%s: %s was removed" % (c.cfg_file, opts.remove) else: if opts.quiet == False: print "%s: No changed were needed" % c.cfg_file # Handle --get operation if opts.get is not None: found_some_attributes = False for k,v in c.maincfg_values: k = k.strip() v = v.strip() if k == opts.get: found_some_attributes=True print v if not found_some_attributes and opts.quiet == False: print "no '%s=*' found in '%s'" % (opts.get, c.cfg_file) def list_objects(): """ List nagios objects given a specified search filter """ parser.usage = ''' %prog list [attribute1] [attribute2] [WHERE <...>] ''' parser.add_option("--print", dest="print_definition", action='store_true', default=False, help="Print actual definition instead of attributes") (opts,args) = parser.parse_args(sys.argv[2:]) pynag.Model.cfg_file = opts.cfg_file if opts.examples == True: return print_examples() attributes,where_statement=split_where_and_set(args) objects = search_objects(search_statements=where_statement) if objects is None: objects = pynag.Model.ObjectDefinition.objects.all if opts.print_definition: for i in objects: print i else: pretty_print(objects=objects,attributes=attributes) def delete_object(): """ Delete one specific nagios object """ parser.usage = '''%prog delete ''' parser.add_option("--force", dest="force", action="store_true", default=False,help="Don't prompt for confirmation") parser.add_option("--recursive", dest="recursive", action="store_true", default=False,help="Recursively apply to related child objects") (opts,args) = parser.parse_args(sys.argv[2:]) pynag.Model.cfg_file = opts.cfg_file if opts.examples == True: return print_examples() attributes,where_statement=split_where_and_set(args) objects = search_objects(search_statements=where_statement) if len(where_statement) == 0: parser.error("Refusing to update every object. Please trim down the selection with a WHERE statement") if objects is None: parser.error('Refusing to delete every object. Please trim down the selection with a WHERE statement') if len(objects) > 0 and not opts.force: pretty_print(objects) answer = raw_input('Delete these %s objects ? (y/N) ' % (len(objects))) if answer.lower() not in ('y', 'yes'): return for i in objects: if opts.verbose: print i try: i.delete(recursive=opts.recursive) if opts.quiet == False: print i.get_shortname(), "deleted." except ValueError: print i.get_shortname(), "not found." if opts.quiet == False: print len(objects), "objects deleted" def add_object(): parser.usage = ''' %prog add [attr2=value2]''' parser.add_option("--filename", dest="filename", help="Write to this specific file") (opts,args) = parser.parse_args(sys.argv[2:]) pynag.Model.cfg_file = opts.cfg_file if len(args) == 0: parser.error("You need to specify object_type and attributes for add") attributes = {} object_type = args.pop(0) while len(args) > 0: arg = args.pop(0) tmp = arg.split('=',1) if len(tmp) != 2: raise pynag.Utils.PynagError("attributes should be in the form of attribute=new_value for update (got %s)" % arg ) attr,value = tmp attributes[ attr ] = value if attributes == []: parser.error('Refusing to write an empty %s definition. Please specify some attributes' % object_type) obj = pynag.Model.string_to_class[object_type](filename=opts.filename) for k,v in attributes.items(): obj[k] = v obj.save() print "Object saved to", obj.get_filename() if opts.verbose: print "# This is what actual statement looks like" print obj def acknowledge(): parser.usage = ''' %prog acknowledge < HOST [SERVICE] | WHERE ... > ''' parser.add_option("--comment", dest="comment", default="Acknowledged with pynag",help="Comment to put on the acknowledgement") parser.add_option("--author", dest="author", default="pynag",help="Author to put on the acknowledgement") parser.add_option("--timestamp", dest="ts", default="0",help="Timestamp of the acknowledgement (default: now)") (opts,args) = parser.parse_args(sys.argv[2:]) pynag.Model.cfg_file = opts.cfg_file objects =[] if opts.examples == True: return print_examples() if len(args) == 0: parser.error('No hosts or services specified.') elif 'where' in str(args).lower(): attributes,where_statement=split_where_and_set(args) objects = search_objects(search_statements=where_statement) elif len(args) == 1: objects = pynag.Model.ObjectDefinition.objects.filter(host_name=args[0]) elif len(args) == 2: objects = pynag.Model.ObjectDefinition.objects.filter(host_name=args[0],service_description=args[1]) else: parser.error('Invalid number of attributes specified') for i in objects: if i.object_type not in ('host','service') or i.register == '0': objects.remove(i) # Here we actually ack the objects if opts.quiet == False: pretty_print(objects) answer = raw_input('Acknowledge these %s objects ? (y/N) ' % (len(objects))) if answer.lower() not in ('y', 'yes'): return for i in objects: if opts.quiet is False: print "Acknowledge %s: %s" % (i.object_type, i.get_shortname()) i.acknowledge(comment=opts.comment,author=opts.author,timestamp=opts.ts) def downtime(): parser.usage = ''' %prog downtime < --list | --remove | HOST [SERVICE] | WHERE ... > ''' parser.add_option("--comment", dest="comment", default="Downtime Scheduled from pynag",help="Comment to put on the downtime") parser.add_option("--author", dest="author", default="pynag",help="Author to put on the downtime") parser.add_option("--start_time", dest="st", default=None,help="When downtime should start (default: now)") parser.add_option("--end_time", dest="et", default=None,help="When downtime should end (default: now+2 hours)") parser.add_option("--duration", dest="dur", default=7200,help="Alternative to end_time, how long should downtime last (in seconds)") parser.add_option("--print", dest="print_definition", action='store_true', default=False, help="When using --list, print full hash map instead of attribute table") parser.add_option("--list", dest="list", default=False, action="store_true",help="list downtimes that match search filters") parser.add_option("--remove", dest="remove", default=False, action="store_true",help="Remove downtimes that match search filters") parser.add_option("--recursive", dest="recursive", default=False, action="store_true",help="schedule downtime for hosts and all its services") (opts,args) = parser.parse_args(sys.argv[2:]) pynag.Model.cfg_file = opts.cfg_file objects =[] if opts.examples == True: return print_examples() elif opts.list == True or opts.remove == True: # Where are not scheduling anything just listing attributes,where_statement=split_where_and_set(args) if attributes == []: attributes = ['id','host_name','service_description','comment'] downtimes = _get_all_downtimes() downtimes = search_object_list(downtimes, where_statement) if opts.print_definition == True: pp = pprint.PrettyPrinter(indent=4) pp.pprint(downtimes) else: pretty_print(downtimes, attributes=attributes) if opts.remove == True: answer = raw_input('Remove these %s downtimes? (y/N) ' % (len(downtimes))) if answer.lower() not in ('y', 'yes'): return for i in downtimes: if opts.quiet == False: print "Removing downtime %s: " % (i.get('id')) if i.get('service_description', None) in (None, ''): pynag.Control.Command.del_host_downtime(i.get('id')) else: pynag.Control.Command.del_svc_downtime(i.get('id')) return elif len(args) == 0: parser.error('No hosts or services specified.') elif 'where' in str(args).lower(): attributes,where_statement=split_where_and_set(args) objects = search_objects(search_statements=where_statement) elif len(args) == 1: objects = pynag.Model.ObjectDefinition.objects.filter(host_name=args[0]) elif len(args) == 2: objects = pynag.Model.ObjectDefinition.objects.filter(host_name=args[0],service_description=args[1]) else: parser.error('Invalid number of attributes specified') # If we reach all the way down here, we are scheduling downtimes # Filter everything from objects that does not have a downtime() method or is not registered filter_function = lambda x: x.register != '0' and 'downtime' in dir(x) objects = filter(filter_function, objects) # Here we actually talk to the the objects if opts.quiet == False: pretty_print(objects) answer = raw_input('Schedule downtime for these %s objects ? (y/N) ' % (len(objects))) if answer.lower() not in ('y', 'yes'): return for i in objects: if opts.quiet == False: print "Downtime %s: %s" % (i.object_type,i.get_shortname()) i.downtime(comment=opts.comment,author=opts.author,start_time=opts.st, end_time=opts.et, duration=opts.dur, recursive=opts.recursive) def _get_all_downtimes(): """ Returns a list of dict, describing all current downtimes """ # Try first via livestatus, if that does not work, parse status.dat try: l = pynag.Parsers.mk_livestatus(nagios_cfg_file=pynag.Model.cfg_file) return l.query('GET downtimes') except Exception: s = pynag.Parsers.status(cfg_file=pynag.Model.cfg_file) s.parse() result = [] result += s.data.get('servicedowntime', []) result += s.data.get('hostdowntime', []) for i in result: i['id'] = i.get('comment_id', None) return result def copy_object(): parser.usage = ''' %prog copy [--filename /path/to/file] ''' parser.add_option("--filename", dest="filename", help="Write to this specific file") parser.add_option("--recursive", dest="recursive", action="store_true", default=False,help="Recursively apply to related child objects") (opts,args) = parser.parse_args(sys.argv[2:]) pynag.Model.cfg_file = opts.cfg_file set = {} # dict of which attributes to change attributes,where_statement=split_where_and_set(args) if len(where_statement) == 0: parser.error("Refusing to copy every object. Please trim down the selection with a WHERE statement") if len(attributes) == 0 and opts.filename is None: parser.error("No changes specified. Please specify what to update with a SET statement") objects = search_objects(search_statements=where_statement) # If user specified an attribute not in the form of key=value, lets prompt for the value for i in attributes: tmp = i.split('=', 1) if len(tmp) == 2: if tmp[1] == 'None': tmp[1] = None set[tmp[0]] = tmp[1] else: set[tmp[0]] = raw_input('Enter a value for %s: ' % tmp[0]) # Here we actually copy the object pretty_print(objects) answer = raw_input('Update these %s objects ? (y/N) ' % (len(objects))) if answer.lower() not in ('y', 'yes'): return number_of_copies = 0 for obj in objects: copied_objects = obj.copy(filename=opts.filename,recursive=opts.recursive,**set) # obj.copy() in most cases should return a list. If not, lets convert it if not isinstance(copied_objects, list): copied_objects = [copied_objects] number_of_copies += len(copied_objects) for new_obj in copied_objects: if opts.quiet == False: print "Object saved to", new_obj.get_filename() if opts.verbose: print "# This is what actual statement looks like" print new_obj if opts.quiet == False: print "%s objects copied." % (number_of_copies) def livestatus(): """ Make a custom query to MK Livestatus """ parser.usage = '''%prog livestatus '' [query2] [query3] [...] ''' parser.add_option("--get", dest="get", metavar="table", default=None, help="GET a specific livestatus table (i.e. services, hosts, log)") parser.add_option("--filter", dest="filters", action="append", default=[], help="Add a Filter: to your query") parser.add_option("--columns", dest="columns", action="append", default=[], help="Space seperated list of columns to display") parser.add_option("--limit", dest="limit", default=None, help="Limit results to LIMIT rows") parser.add_option("--list-columns", dest="list_columns", metavar="table", default=None, help="List available columns in table you specify (i.e. services, hosts)") parser.add_option("--socket", dest="socket", default=None, help="Path to livestatus (by default detect it from nagios.cfg)") (opts,args) = parser.parse_args(sys.argv[2:]) pynag.Model.cfg_file = opts.cfg_file if opts.examples == True: return print_examples() # We will be working with args, here as the main source of queries. # Any options added should be appended to args if opts.get is not None: args.insert(0, "GET %s" % opts.get ) if opts.limit is not None: args.append( 'Limit: %s' % opts.limit ) for i in opts.filters: args.append( 'Filter: %s' % i ) all_columns = [] for i in opts.columns: all_columns += i.split() if all_columns != []: args.append('Columns: %s' % ' '.join(all_columns)) if opts.list_columns is not None: all_columns = ["name","type","description"] args = ["GET columns","Columns: name type description","Filter: table = %s" % opts.list_columns] if len(args) == 0: parser.error('No arguments provided. Take a look at --examples or --help') # Here we run the actual query if opts.verbose == True: print "--- this query will be sent ---" for i in args: print i print "--- end of query --------------" livestatus = pynag.Parsers.mk_livestatus(nagios_cfg_file=opts.cfg_file, livestatus_socket_path=opts.socket) result = livestatus.query(*args) # We are done with the query, lets output the results if all_columns == []: all_columns = ["name"] pretty_print(result, attributes=all_columns) def update_objects(): parser.usage = ''' %prog update [SET attr1=value1 [attr2=value2]] [WHERE ...]''' parser.add_option("--force", dest="force", action="store_true", default=False,help="Don't prompt for confirmation") (opts,args) = parser.parse_args(sys.argv[2:]) pynag.Model.cfg_file = opts.cfg_file set = {} attributes,where_statement=split_where_and_set(args) if len(where_statement) == 0: parser.error("Refusing to update every object. Please trim down the selection with a WHERE statement") if len(attributes) == 0: parser.error("No changes specified. Please specify what to update with a SET statement") objects = search_objects(search_statements=where_statement) # If user specified an attribute not in the form of key=value, lets prompt for the value for i in attributes: tmp = i.split('=', 1) if len(tmp) == 2: if tmp[1] == 'None': tmp[1] = None set[tmp[0]] = tmp[1] else: set[tmp[0]] = raw_input('Enter a value for %s: ' % tmp[0]) if len(objects) > 0 and not opts.force: pretty_print(objects) answer = raw_input('Update these %s objects ? (y/N) ' % (len(objects))) if answer.lower() not in ('y', 'yes'): return update_many(objects=objects, **set) if opts.verbose: for i in objects: print i if opts.quiet == False: print "Updated %s objects" % (len(objects)) def pretty_print(objects, attributes=None): """ Takes in a list of objects and prints them in a pretty table format """ # Set default attributes if none are specified if not attributes: attributes = ['object_type','shortname', 'filename'] # Set minimum width of columns. High values here effective turn output into fixed with min_width = "%%-%ss" % parser.values.width # by default should turn this string into "%-20s" # If no column seperator was specified, use emptystring if parser.values.seperator is None: parser.values.seperator = ' ' # Print header if parser.values.quiet == False: # Apply fixed with to columns transformed_attributes = map(lambda x: min_width % x, attributes) row = parser.values.seperator.join(transformed_attributes) print row print "-"*80 # Print one row for each object for i in objects: # For every attribute we want to print, get the value for this row columns = map(lambda x: i.get(x, "null"), attributes) # Pad every column with spaces if width was specified transformed_columns = map(lambda x: min_width % x, columns) # Print the row print parser.values.seperator.join(transformed_columns) # Print Footer if parser.values.quiet == False: message = "%s objects matches search condition" % ( len(objects) ) pre = "-"*10 post = "-"*(80-10-len(message)) print pre + message + post def update_many(objects, **kwargs): """ Helper function to update many objects at once Arguments: objects -- List of pynag.Model.Objectdefinition kwargs -- Arbitary list of arguments Examples: update_many(Service.objects.all, host_name="new_host_name", register="0") """ for i in objects: for attr,value in kwargs.items(): i[attr] = value i.save() print "%s (%s): changed %s to %s" % (i.get_filename(), i.get_shortname(),attr, value) def split_where_and_set(argument_list): """ Takes a list of commandline arguments, and splits a "where" condition from other arguments. Returns: ([attributes],[where_statement]) ex. attributes: ['host_name,'service_description'] ex. where_statement: ['host_name=localhost','and','service_description=Ping'] """ attributes = [] where_statement = [] currently_parsing = "set statement" for i in argument_list: if i.lower() == 'where': currently_parsing="where statement" elif i.lower() == 'set' or i.lower() == 'attributes': currently_parsing="set statement" elif i.lower() == 'unset': currently_parsing = "unset statement" elif currently_parsing == 'where statement': where_statement.append(i) elif currently_parsing == "set statement": attributes.append(i) elif currently_parsing == "unset statement": i = i + "=None" attributes.append(i) else: continue return attributes,where_statement def parse_configfile(): """ Parse configuration file """ examples = {} examples['list'] = ''' # %prog list host_name address WHERE object_type=host" # %prog list host_name service_description WHERE host_name=examplehost and object_type=service" ''' examples['update'] = ''' # %prog update SET host_name=newhostname WHERE host_name=oldhostname" # %prog update SET address=127.0.0.1 WHERE host_name='examplehost.example.com' and object_type=host" # %prog update UNSET contacts WHERE host_name='examplehost.example.com' and object_type=host" ''' examples['copy'] = ''' # %prog copy SET host_name=newhostname WHERE host_name=oldhostname" # %prog copy SET address=127.0.0.1 WHERE host_name='examplehost.example.com' and object_type=host" # %prog copy SET name=template-host register=0 UNSET address host_name WHERE host_name='examplehost.example.com' and object_type=host" ''' examples['add'] = ''' # %prog add host host_name=examplehost use=generic-host address=127.0.0.1" # %prog add service service_description="Test Service" use="check_nrpe" host_name="localhost" ''' examples['delete'] = ''' # %prog delete where object_type=service and host_name='mydeprecated_host' # %prog delete where filename__startswith='/etc/nagios/myoldhosts' ''' examples['acknowledge'] = ''' # %prog acknowledge localhost "Disk Usage" --comment "Freeing up some space" --author "admin" ' # %prog acknowledge where service_description__contains=vmware --comment "Bulk ack on all vmware checks" ' ''' examples['downtime'] = ''' # %prog downtime localhost --comment "Downtime for localhost and all its services" --recursive' # %prog downtime where service_description__contains=vmware --comment "Downtime on all vmware checks" ' ''' examples['config'] = ''' # %prog config --set process_perfdata=1 # %prog config --append cfg_dir=/etc/nagios/conf.d # %prog config --remove cfg_dir --old_value=/etc/nagios/conf.d # %prog config --get object_cache_file ''' examples['execute'] = ''' # %prog execute localhost # %prog execute localhost "Disk Space" ''' examples['livestatus'] = ''' # %prog livestatus --get hosts --columns "state address name" # %prog livestatus --get services --columns "state host_name description" --filter "host_name = localhost" --limit 10 # %prog livestatus --list-columns hosts ''' def print_examples(): """ Print example usage and exit. If subcommand has been defined on the command line. Only print examples """ subcommand = sys.argv[1] # Print all examples if no subcommand is specified if subcommand == '--examples': for k,v in examples.items(): print "%s Examples:" % k print v.replace('%prog', sys.argv[0] ) print "" sys.exit() if subcommand not in examples.keys(): print "No examples found for subcommand '%s'" % subcommand sys.exit(1) print "Examples for the '%s' subcommand:" % sys.argv[1] print examples[sys.argv[1]].replace('%prog', sys.argv[0] ) sys.exit() if __name__ == '__main__': # since optparse runs inside various places, a quick hack to enable --debug globally # TODO: Put all optparsing inside to a single function. if "--debug" in sys.argv: debug = True try: parse_arguments() except Exception, e: print "Error occured, use --debug to see full traceback:" print "" print "%s: %s" % (e.__class__.__name__, e) if debug: traceback.print_exc(file=sys.stdout) sys.exit(1) pynag-0.9.1+dfsg.orig/examples/0000775000175000017500000000000012370025176015344 5ustar clintclintpynag-0.9.1+dfsg.orig/examples/Model/0000775000175000017500000000000012370025176016404 5ustar clintclintpynag-0.9.1+dfsg.orig/examples/Model/list_services_for_one_host.py0000664000175000017500000000100412353523624024375 0ustar clintclint#!/usr/bin/python import sys sys.path.insert(1, '/opt/pynag') from pynag import Model # If your nagios config is in an unusal place, uncomment this: # Model.cfg_file = '/etc/nagios/nagios.cfg' # Lets find all services that belong to the host "localhost" host_name = "localhost" services = Model.Service.objects.filter(host_name='localhost') print "%-30s %-30s" % ("Hostname", "Service_description") for service in services: print "%-30s %-30s" % ( service['host_name'], service['service_description'] ) pynag-0.9.1+dfsg.orig/examples/Model/list_all_services.py0000664000175000017500000000062312353523624022467 0ustar clintclint#!/usr/bin/python import sys sys.path.insert(1, '/opt/pynag') from pynag import Model # If your nagios config is in an unusal place, uncomment this: # Model.cfg_file = '/etc/nagios/nagios.cfg' services = Model.Service.objects.all print "%-30s %-30s" % ("Hostname", "Service_description") for service in services: print "%-30s %-30s" % ( service['host_name'], service['service_description'] ) pynag-0.9.1+dfsg.orig/examples/Model/list_all_hostgroups.py0000664000175000017500000000046312353523624023063 0ustar clintclint#!/usr/bin/python import sys sys.path.insert(1, '/opt/pynag') from pynag import Model # If your nagios config is in an unusal place, uncomment this: # Model.cfg_file = '/etc/nagios/nagios.cfg' hostgroups = Model.Hostgroup.objects.all for hostgroup in hostgroups: print hostgroup['hostgroup_name'] pynag-0.9.1+dfsg.orig/examples/Model/working_with_fields.py0000664000175000017500000000315012353523624023020 0ustar clintclint#!/usr/bin/python # # This example shows how to work with attributes in nagios that # Are in a comma-seperated form. Like this: # # define service { # contact_groups +admins,webmasters,hostmasters # host_name host.example.com # service_description Test Service # } # import pynag.Model # First create a test object my_service = pynag.Model.Service() my_service['host_name'] = 'examplehost' my_service['service_description'] = 'Test Service' my_service['contact_groups'] = '+admins,webmasters,hostmasters' print "*** Created a demo servicecheck that looks like this:" print my_service print "\n--- Removing with attribute_removefield()" my_service.attribute_removefield('contact_groups', 'hostmasters') print "my_service.contact_groups = ", my_service.contact_groups print "\n--- Add a new contact_group with attribute_appendfield()" my_service.attribute_appendfield('contact_groups', "mycontactgroup") print "my_service.contact_groups = ", my_service.contact_groups print "\n-- Replacing a contact_group midfield with attribute_replacefield()" my_service.attribute_replacefield('contact_groups', "webmasters", "hostmaster") print "my_service.contact_groups = ", my_service.contact_groups # A more advanced example. Find all services that inherit from "generic-service" and # Replace it with "my-specific-service": print "\n--- More advanced example, editing multiple objects at once..." my_services = pynag.Model.Service.objects.filter(use__hasfield='generic-service') for service in my_services: service.attribute_replacefield('use','generic-service','my-specific-service') # service.save() pynag-0.9.1+dfsg.orig/examples/Model/edit_service.py0000664000175000017500000000155112353523624021427 0ustar clintclint#!/usr/bin/python # # Example script on how to modify a single Nagios service from pynag import Model import sys # Parse commandline arguments if len(sys.argv) != 5: print ''' Usage: %s Example: %s localhost Ping check_command 'check_ping' ''' % (sys.argv[0], sys.argv[0]) sys.exit(1) host_name = sys.argv[1] service_description = sys.argv[2] field_name = sys.argv[3] new_value = sys.argv[4] # Get a list of all services that fit our search pattern search_results = Model.Service.objects.filter(host_name=host_name, service_description=service_description) if len(search_results) == 0: print "no service found for host_name=%s and service_description=%s" % ( host_name, service_description ) my_service = search_results[0] my_service.set_attribute(field_name,new_value) my_service.save() pynag-0.9.1+dfsg.orig/examples/Model/remove_empty_configfiles.py0000664000175000017500000000127512353523624024050 0ustar clintclint#!/usr/bin/python # # This script looks for files in your configuration that have no objects in them. # # import os import pynag.Model # Load pynag cache all_objects = pynag.Model.ObjectDefinition.objects.all for i in pynag.Model.config.get_cfg_files(): objects = pynag.Model.ObjectDefinition.objects.filter(filename=i) if len(objects) == 0: # No objects found in that file # Objects defined via cfg_file= should not be removed because nagios will not reload # after you remove the file for k,v, in pynag.Model.config.maincfg_values: if k == 'cfg-file' and v == i: continue print "Empty config file: %s" % i # os.remove(i) pynag-0.9.1+dfsg.orig/examples/Model/restructure_config_files.py0000664000175000017500000000316012353523624024056 0ustar clintclint#!/usr/bin/env python # # This pynag script will parse all your nagios configuration # And write a copy of every single object to /tmp/nagios/conf.d # # This can be very handy if your configuration files are a mess # or if you are thinking about splitting a big file of services # into one file per host # # The script will only write the copy to /tmp so you will # have to manually remove old objects before you copy this # into your /etc/nagios/conf.d or wherever you want to keep # your objects import pynag.Model from pynag.Model import ObjectDefinition import os import os.path # cfg_file is where our main nagios config file is pynag.Model.cfg_file = '/etc/nagios3/nagios.cfg' # pynag_directory is where the new objects will be saved pynag.Model.pynag_directory = '/tmp/nagios/conf.d' all_objects = ObjectDefinition.objects.all # Use this instead if you only want to clean up a single directory # all_objects = ObjectDefinition.objects.filter(filename__contains='/etc/nagios/all_the_services.cfg') for i in all_objects: print "Saving", i.object_type, i.get_description(), "...", # Set a new filename for our object, None means # That pynag decides where it goes new_filename = i.get_suggested_filename() print new_filename continue # Alternative: # if i.object.type == 'host' and i.host_name is not None: # new_filename = '/tmp/nagios/conf.d/hosts/%s" % i.host_name dirname = os.path.dirname(new_filename) if not os.path.exists(dirname): os.makedirs(dirname) with open(new_filename, 'a') as f: data = "\n" + str(i) f.write(data) print new_filename pynag-0.9.1+dfsg.orig/examples/Model/parse-configmain.py0000664000175000017500000000565712310325103022200 0ustar clintclint#!/usr/bin/python # This script parses the configmain.html file from the Nagios project and tries # to extract information regarding options. import re from pprint import pprint f = open('configmain.html') k = None p = False doc = "" format = "" examples = [] options = [] title = "" info = {} for l in f.readlines(): # no newline l = l[:-1] # Empty line if not len(l): continue # no config key and we are not at the start of a config key section if k is None and l.startswith('(?P.+?)', l) if m: format = m.group('format') continue # This config key has examples m = re.match(r'.*(?P.+?)', l) if m: examples.append(m.group('example')) continue # More descriptive title for the config key m = re.match(r'.*(?P.+?)</strong>', l) if m: title = m.group('title') continue # Here starts the main doc string if l == "<p>": p = True continue # Here ends the doc string if l == "</p>": p = False continue # Description of the options if l[:4] == '<li>': options.append(l[4:]) # We are in the main doc section if p and k: doc += "%s " % (l) # Save the last config key info[k] = { 'title': title, 'doc': doc, 'format': format, 'examples': examples, 'options': options } for k in info: if info[k]['format'].endswith('=<0/1>'): info[k]['type']='boolean' elif info[k]['format'].endswith('=<seconds>'): info[k]['type']='seconds' elif info[k]['format'].endswith('=<percent>'): info[k]['type']='percent' elif info[k]['format'].endswith('=<file_name>'): info[k]['type']='file_name' elif info[k]['format'].endswith('=<minutes>'): info[k]['type']='minutes' elif info[k]['format'].endswith('=<#>'): info[k]['type']='integer' elif info[k]['format'].endswith('=<command>'): info[k]['type']='command' else: info[k]['type']='unclassified' # PrettyPrint pprint(info) # vim: smartindent tabstop=4 shiftwidth=4 expandtab ���������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Plugins/�������������������������������������������������������������0000775�0001750�0001750�00000000000�12370025176�016765� 5����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Plugins/check_stuff.py�����������������������������������������������0000664�0001750�0001750�00000000616�12310325103�021611� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# check_stuff.py Takes any arguments from the command_line and treats them as performance metrics. from pynag.Plugins import PluginHelper my_plugin = PluginHelper() my_plugin.parse_arguments() # Any perfdatastring added as argument will be treated as a performance metric for i in my_plugin.arguments: my_plugin.add_metric(perfdatastring=i) my_plugin.check_all_metrics() my_plugin.exit() ������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Plugins/check_load.py������������������������������������������������0000664�0001750�0001750�00000003700�12353523627�021420� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# check_load.py - Check load average. Thresholds can be specified from the commandline # Import PluginHelper and some utility constants from the Plugins module from pynag.Plugins import PluginHelper,ok,warning,critical,unknown # Create an instance of PluginHelper() helper = PluginHelper() # Optionally, let helper handle command-line arguments for us for example --threshold # Note: If your plugin needs any commandline arguments on its own (like --hostname) you should add them # before this step with helper.parser.add_option() helper.parse_arguments() # Here starts our plugin specific logic. Lets try to read /proc/loadavg # And if it fails, we exit immediately with UNKNOWN status try: content = open('/proc/loadavg').read() except Exception, e: helper.exit(summary="Could not read /proc/loadavg", long_output=str(e), exit_code=unknown, perfdata='') # We have read the contents of loadavg file. Lets put it in the summary of our plugin output: helper.add_summary("Load: %s" % content) # Read metrics from /proc/loadavg and add them as performance metrics load1,load5,load15,processes,last_proc_id = content.split() running,total = processes.split('/') # If we so desire we can set default thresholds by adding warn attribute here # However we decide that there are no thresholds by default and they have to be # applied on runtime with the --threshold option helper.add_metric(label='load1',value=load1) helper.add_metric(label='load5',value=load5) helper.add_metric(label='load15',value=load15) helper.add_metric(label='running_processes',value=running) helper.add_metric(label='total_processes',value=total) # By default assume everything is ok. Any thresholds specified with --threshold can overwrite this status: helper.status(ok) # Here all metrics will be checked against thresholds that are either # built-in or added via --threshold from the command-line helper.check_all_metrics() # Print out plugin information and exit nagios-style helper.exit() ����������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Plugins/check_dns.py�������������������������������������������������0000664�0001750�0001750�00000004534�12310325103�021251� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# check_dns.py -- Returns OK if a hostname resolves to any ip address # check_dns plugin will need some system libraries for DNS lookup from _socket import gaierror import socket import time # Import PluginHelper and some utility constants from the Plugins module from pynag.Plugins import PluginHelper,ok,warning,critical,unknown # Create an instance of PluginHelper() my_plugin = PluginHelper() # Our Plugin will need -H and -a attributes and we will use PluginHelpers wrapper around optparse for this: my_plugin.parser.add_option('-H', help="Hostname or ip address", dest="hostname") my_plugin.parser.add_option('-a', help="Expected Address", dest="address") # When parse_arguments is called some default options like --threshold and --no-longoutput are automatically added my_plugin.parse_arguments() # # Here starts Plugin-specific logic # # Get the hostname and expected address that was provided on the command-line # address will be optional, but we will throw and error if hostname is not provided hostname = my_plugin.options.hostname address = my_plugin.options.address if hostname is None: my_plugin.parser.error('-H argument is required') # Here comes the specific check logic try: start_time = time.time() result = socket.gethostbyname( hostname ) # result will contain the ip address resolved end_time = time.time() # If no address was specified with -a, then we return # OK if hostname resolved to anything at all if address is None or address == result: my_plugin.status(ok) my_plugin.add_summary("%s resolves to %s" % (hostname, result)) else: my_plugin.status(critical) my_plugin.add_summary("%s resolves to %s but should resolve to %s" % (hostname,result,address)) # Add run_time metric, so we can also alert if lookup takes to long run_time = end_time - start_time my_plugin.add_metric('run_time', run_time) except gaierror: # If any exceptions happened in the code above, lets return a critical status my_plugin.status(critical) my_plugin.add_summary('Could not resolve host "%s"' % hostname ) # when check_all_metrics() is run, any metrics we have added with add_metric() will be processed against # Thresholds (like --threshold). This part will allow our plugin users to alert on lookup_time my_plugin.check_all_metrics() # Print status output and exit my_plugin.exit() ��������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Plugins/check_cpu.py�������������������������������������������������0000775�0001750�0001750�00000001337�12310325103�021255� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import os,sys ## Import plugin from nagios Module from pynag.Plugins import simple as Plugin ## Create the plugin option np = Plugin() ## Add a command line argument np.add_arg("l","load-file", "Enter a load average file", required=None) ## This starts the actual plugin activation np.activate() ## Use a custom load average file, if specified to if np['load-file']: load_file = np['load-file'] else: load_file = "/proc/loadavg" if not os.path.isfile(load_file): np.nagios_exit("UNKNOWN", "Missing Load average file %s" % load_file) ## Get the check value current_load = open(load_file).readline().split()[0] ## Add the perdata np.add_perfdata("1min", current_load) np.check_range(current_load) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/�������������������������������������������������������������0000775�0001750�0001750�00000000000�12370025176�016763� 5����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/suggest_optimizations.py�������������������������������������0000775�0001750�0001750�00000001751�12353523624�024020� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config def is_ip(ip_address): import socket try: socket.inet_aton(ip_address) return True # We got through that call without an error, so it is valid except socket.error: return False # There was an error, so it is invalid ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.parse() print "Checking hosts using dns names instead of ip addresses in the 'address' field" for host in nc['all_host']: if host.has_key('address'): if not is_ip(host['address']): print "%s has a name instead of ip in the address field (%s)" % (host['alias'], host['address']) print "Checking for weird service definitions" for service in nc['all_service']: if service.has_key('register') and service['register'] == 0: continue if not service.has_key('host_name'): print nc.print_conf( service ) �����������������������pynag-0.9.1+dfsg.orig/examples/Parsers/get_service.py�����������������������������������������������0000775�0001750�0001750�00000001153�12353523624�021641� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 3: sys.stderr.write("Usage: %s 'Service Description' 'Host Name'\n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config service_description = sys.argv[1] target_host = sys.argv[2] ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') #nc.parse() nc.parse() service = nc.get_service(target_host, service_description) if not service: sys.stderr.write("Service not found: %s %s\n" % (service_description, target_host)) sys.exit(2) print nc.print_conf(service) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/find_orphans.py����������������������������������������������0000775�0001750�0001750�00000001617�12353523624�022021� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.parse() ## These are top level items. We don't care if they dont' have a parent. ## Things like datacenters should be displayed here top_level_items = ['main data center'] orphan_hosts = [] print "The following hosts do not have parent items:" for host in nc['all_host']: use_attr = '' for attribute in ['host_name', 'name', 'alias']: if host.has_key(attribute): use_attr = attribute if not host.has_key('parents') or not host['parents']: if host[use_attr] not in top_level_items: orphan_hosts.append(host) print "%-12s %-32s (%s)" % (use_attr, host[use_attr], host['meta']['filename']) if not len(orphan_hosts): print "No ophaned hosts found" �����������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/optimize_config.py�������������������������������������������0000775�0001750�0001750�00000000770�12310325103�022514� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config def is_ip(ip_address): import socket try: socket.inet_aton(ip_address) return True # We got through that call without an error, so it is valid except socket.error: return False # There was an error, so it is invalid ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.extended_parse() nc.flag_all_commit() nc.commit() ��������pynag-0.9.1+dfsg.orig/examples/Parsers/get_hostgroup.py���������������������������������������������0000775�0001750�0001750�00000001001�12353523624�022223� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 2: sys.stderr.write("Usage: %s 'Hostgroup'\n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config target_host = sys.argv[1] ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.parse() hostgroup = nc.get_hostgroup(target_host) if not hostgroup: sys.stderr.write("Hostgroup not found: %s\n" % hostgroup) sys.exit(2) print nc.print_conf(hostgroup) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/get_timeperiod.py��������������������������������������������0000775�0001750�0001750�00000000756�12353523624�022352� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 2: sys.stderr.write("Usage: %s 'Timeperiod Name'\n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config target_item = sys.argv[1] ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.parse() item = nc.get_timeperiod(target_item) if not item: sys.stderr.write("Item not found: %s\n" % item) sys.exit(2) print nc.print_conf(item) ������������������pynag-0.9.1+dfsg.orig/examples/Parsers/list_hosts_groups.py�����������������������������������������0000775�0001750�0001750�00000001235�12353523624�023135� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 2: sys.stderr.write("Usage: %s 'Host Alias'\n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config target_host = sys.argv[1] ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') #nc.parse() nc.extended_parse() ## Find services that this host belongs to for hostgroup in nc.get_host(target_host)['meta']['hostgroup_list']: ## Check to see if this is the only host in this service #return_item = nc.get_service(target_host, service_description) print hostgroup # print return_item['service_description'] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/get_contactgroup.py������������������������������������������0000775�0001750�0001750�00000000762�12353523624�022716� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 2: sys.stderr.write("Usage: %s 'Contactgroup Name'\n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config target_item = sys.argv[1] ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.parse() item = nc.get_contactgroup(target_item) if not item: sys.stderr.write("Item not found: %s\n" % item) sys.exit(2) print nc.print_conf(item) ��������������pynag-0.9.1+dfsg.orig/examples/Parsers/get_servicegroup.py������������������������������������������0000775�0001750�0001750�00000001002�12353523624�022707� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 2: sys.stderr.write("Usage: %s 'Servicegroup Name'\n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config target_servicegroup = sys.argv[1] ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.parse() item = nc.get_servicegroup(target_servicegroup) if not item: sys.stderr.write("Item not found: %s\n" % item) sys.exit(2) print nc.print_conf(item) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/get_host.py��������������������������������������������������0000775�0001750�0001750�00000000767�12353523624�021170� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 2: sys.stderr.write("Usage: %s 'Host Name'\n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config target_host = sys.argv[1] ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') #nc.parse() nc.extended_parse() host = nc.get_host(target_host) if not host: sys.stderr.write("Host not found: %s\n" % host) sys.exit(2) print nc.print_conf(host) ���������pynag-0.9.1+dfsg.orig/examples/Parsers/remove_host.py�����������������������������������������������0000775�0001750�0001750�00000005473�12353523624�021705� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 2: sys.stderr.write("Usage: %s 'Host Alias'\n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config target_host = sys.argv[1] ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.extended_parse() nc.cleanup() ## Find services that this host belongs to if not nc.get_host(target_host): sys.stderr.write("%s does not exist\n" % target_host) sys.exit(2) for service_description in nc.get_host(target_host)['meta']['service_list']: service = nc.get_service(target_host, service_description) ## Check to see if this is the only host in this service host_list = [] if service.has_key('host_name'): for host in nc._get_list(service, 'host_name'): if host[0] != "!": host_list.append(host) else: continue ## Ignore if this host isn't listed if len(host_list) == 0: continue if len(host_list) > 1: print "Removing %s from %s" % (target_host, service['service_description']) new_item = nc.get_service(service['service_description'], target_host) host_list.remove(target_host) host_string = ",".join(host_list) print "New Value: %s" % host_string nc.edit_service(target_host, service['service_description'], 'host_name',host_string) elif (len(host_list) == 1) and not service.has_key('hostgroup_name'): print "Deleting %s" % service['service_description'] nc.delete_service(service['service_description'], target_host) elif (len(host_list) == 1) and (host_list[0] is target_host): print "Deleting %s" % service['service_description'] nc.delete_service(service['service_description'], target_host) else: print "Unknown Action" sys.exit(2) nc.commit() ## Delete from groups host_obj = nc.get_host(target_host) for hostgroup in host_obj['meta']['hostgroup_list']: print "Removing %s from hostgroup %s" % (target_host, hostgroup) hostgroup_obj = nc.get_hostgroup(hostgroup) ## Get the list #hostgroup_obj['members'] = nc._get_list(hostgroup_obj, 'members').remove(target_host) ## Remove the original objct member_list = nc._get_list(hostgroup_obj, 'members') member_list.remove(target_host) nc['all_hostgroup'].remove(hostgroup_obj) hostgroup_obj['meta']['needs_commit'] = True member_string = ",".join(member_list) hostgroup_obj['members'] = ",".join(member_list) nc['all_hostgroup'].append(hostgroup_obj) nc.commit() ## Delete a host result = nc.delete_object('host',target_host) if result: print "Deleted host" ## Delete hostextinfo result = nc.delete_object('hostextinfo',target_host) if result: print "Deleted hostextinfo" nc.commit() nc.cleanup() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/list_services.py���������������������������������������������0000775�0001750�0001750�00000001227�12353523624�022222� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 2: sys.stderr.write("Usage: %s 'Host Alias'\n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config target_host = sys.argv[1] ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') #nc.parse() nc.extended_parse() ## Find services that this host belongs to for service in nc.get_host(target_host)['meta']['service_list']: ## Check to see if this is the only host in this service #return_item = nc.get_service(target_host, service_description) print service # print return_item['service_description'] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/parse_files.py�����������������������������������������������0000775�0001750�0001750�00000000433�12353523624�021636� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.parse() #nc._post_parse() for host in nc['all_host']: print nc.print_conf(host) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/remove_host_from_group.py������������������������������������0000775�0001750�0001750�00000002716�12353523624�024141� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 3: sys.stderr.write("Usage: %s 'Host Name' 'Hostgroup Name' \n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.parse() target_host = sys.argv[1] target_group = sys.argv[2] ## Get the host object host_obj = nc.get_host(target_host) if not host_obj: sys.stderr.write("host_name '%s' does not exist\n" % target_host) sys.exit(2) ## Find the hostgroup from our global dictionaries group_obj = nc.get_hostgroup(target_group) if not group_obj: sys.stderr.write("%s does not exist\n" % target_group) sys.exit(2) ## Get a list of the host_name's in this group existing_list = group_obj['members'].split(",") if target_host not in existing_list: sys.stderr.write("%s is not in the group\n" % target_host) sys.exit(2) else: existing_list.remove(target_host) print "Removing %s from %s" % (target_host, target_group) ## Alphabetize the list, for easier readability (and to make it pretty) existing_list.sort() ## Remove old group nc['all_hostgroup'].remove(group_obj) ## Save the new member list group_obj['members'] = ",".join(existing_list) ## Mark the commit flag for the group group_obj['meta']['needs_commit'] = True ## Add the group back in with new members nc['all_hostgroup'].append(group_obj) ## Commit the changes to file nc.commit() ��������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/get_service_info.py������������������������������������������0000775�0001750�0001750�00000000641�12353523624�022655� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 3: sys.stderr.write("Usage: %s 'Host Name' 'Service Description'\n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.parse() service = nc.get_service(sys.argv[1],sys.argv[2]) print nc.print_conf(service) �����������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/get_contact.py�����������������������������������������������0000775�0001750�0001750�00000000750�12353523624�021636� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 2: sys.stderr.write("Usage: %s 'Contact Name'\n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config target_item = sys.argv[1] ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.parse() item = nc.get_contact(target_item) if not item: sys.stderr.write("Item not found: %s\n" % item) sys.exit(2) print nc.print_conf(item) ������������������������pynag-0.9.1+dfsg.orig/examples/Parsers/get_command.py�����������������������������������������������0000775�0001750�0001750�00000000750�12353523624�021621� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import sys if len(sys.argv) != 2: sys.stderr.write("Usage: %s 'Command Name'\n" % (sys.argv[0])) sys.exit(2) ## This is for the custom nagios module sys.path.insert(1, '../') from pynag.Parsers import config target_item = sys.argv[1] ## Create the plugin option nc = config('/etc/nagios/nagios.cfg') nc.parse() item = nc.get_command(target_item) if not item: sys.stderr.write("Item not found: %s\n" % item) sys.exit(2) print nc.print_conf(item) ������������������������pynag-0.9.1+dfsg.orig/examples/README���������������������������������������������������������������0000664�0001750�0001750�00000000565�12310325103�016215� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This Directory Contains Examples for how to use the pynag library to manipulate Nagios Configurations. These examples are split into the following directories: * Model: Examples on high-level config manipulation with the Model module * Parsers: Examples on low-level configuration with the Parsers.config module * Plugins Creating nagios plugins with the Plugin module �������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/�������������������������������������������������������������������������0000775�0001750�0001750�00000000000�12370025176�014456� 5����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/pynag-command.rst��������������������������������������������������������0000664�0001750�0001750�00000011112�12320621003�017720� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ==== NAME ==== SYNOPSIS -------- pynag <sub-command> [options] [arguments] DESCRIPTION ----------- pynag is a command-line utility that can be used to view or change current nagios configuration. sub-commands ------------ *list* print to screen nagios configuration objects as specified by a WHERE clause | pynag list [attribute1] [attribute2] [WHERE ...] *update* modify specific attributes of nagios objects as specified by a WHERE and SET clause | pynag update set attr1=value WHERE attr=value and attr=value *delete* Delete objects from nagios configuration as specified by a WHERE clause | pynag delete delete <WHERE ...> *add* Add a new object definition | pynag add <object_type> <attr1=value1> [attr2=value2] *copy* Copy objects, specifiying which attributes to change | pynag copy <WHERE ...> <SET attr1=value1 [attr2=value2] ...> *execute* Executes the currently configured check command for a host or a service | pynag execute <host_name> [service_description] *config* modify values in main nagios configuration file (nagios.cfg) | pynag config [--set <attribute=value>] [--old_value=attribute] | pynag config [--append <attribute=value>] [--old_value=attribute] | pynag config [--remove <attribute>] [--old_value=attribute] | pynag config [--get <attribute>] WHERE statements ---------------- Some Subcommands use WHERE statements to filter which objects to work with. Where has certain similarity with SQL syntax. Syntax: | WHERE <attr=value> [AND attr=value] [OR attr=value] \ | [another where statement] where "attr" is any nagios attribute (i.e. host_name or service_description). Example: | pynag list WHERE host_name=localhost and object_type=service | pynag list WHERE object_type=host or object_type=service Any search attributes have the same syntax as the pynag filter. For example these work just fine: | pynag list WHERE host_name__contains=production | pynag list WHERE host_name__startswith=prod | pynag list WHERE host_name__notcontains=test | pynag list host_name address WHERE address__exists=True | pynag list host_name WHERE register__isnot=0 The pynag filter supports few parameters that are not just attributes. Example: * filename -- The filename which the object belongs * id -- pynag unique identifier for the object * effective_command_line -- command which nagios will execute Of course these can be combined with the pynag filter syntax: | pynag list where filename__startswith=/etc/nagios/conf.d/ | pynag list host_name service_description effective_command_line For detailed description of the filter see pydoc for pynag.Model.ObjectDefintion.filter() SET statements -------------- Subcommands that use SET statements (like update or copy) use them a list of attributes change for a specific object. Syntax: | SET <attr1=value1> [attr2=value2] [...] Example: | pynag update SET address=127.0.0.1 WHERE host_name=localhost and object_type=host EXAMPLES -------- List all services that have "myhost" as a host_name ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | pynag list host_name service_description WHERE host_name=myhost and object_type=service Set check_period to 24x7 on all services that belong to host "myhost" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | pynag update set check_period=24x7 WHERE host_name=myhost list examples ^^^^^^^^^^^^^ | pynag list host_name address WHERE object_type=host | pynag list host_name service_description WHERE host_name=examplehost and object_type=service update examples ^^^^^^^^^^^^^^^ | pynag update SET host_name=newhostname WHERE host_name=oldhostname | pynag update SET address=127.0.0.1 WHERE host_name='examplehost.example.com' and object_type=host copy examples ^^^^^^^^^^^^^ | pynag copy SET host_name=newhostname WHERE host_name=oldhostname | pynag copy SET address=127.0.0.1 WHERE host_name='examplehost.example.com' and object_type=host add examples ^^^^^^^^^^^^ | pynag add host host_name=examplehost use=generic-host address=127.0.0.1 | pynag add service service_description="Test Service" use="check_nrpe" host_name="localhost" delete examples ^^^^^^^^^^^^^^^ | pynag delete where object_type=service and host_name='mydeprecated_host' | pynag delete where filename__startswith='/etc/nagios/myoldhosts' execute examples ^^^^^^^^^^^^^^^^ | pynag execute localhost | pynag execute localhost "Disk Space Additional Resources -------------------- See http://github.com/pynag/pynag.git for more information. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/Makefile�����������������������������������������������������������������0000664�0001750�0001750�00000012670�12311602524�016115� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make <target>' where <target> is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pynag.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pynag.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/pynag" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pynag" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." ������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/make.bat�����������������������������������������������������������������0000664�0001750�0001750�00000011746�12311602524�016065� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^<target^>` where ^<target^> is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pynag.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pynag.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end ��������������������������pynag-0.9.1+dfsg.orig/docs/pynag.rst����������������������������������������������������������������0000664�0001750�0001750�00000000562�12363713622�016333� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������The pynag module ================ :mod:`pynag` Package -------------------- .. automodule:: pynag.__init__ :members: :undoc-members: :show-inheritance: Subpackages ----------- .. toctree:: pynag.Control pynag.Model pynag.Parsers pynag.Plugins pynag.Utils The pynag command line ====================== .. toctree:: pynag-command ����������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/pynag.Utils.rst����������������������������������������������������������0000664�0001750�0001750�00000000516�12363713622�017431� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Utils Package ============= :mod:`Utils` Package -------------------- .. automodule:: pynag.Utils :members: :undoc-members: :show-inheritance: .. autoclass:: pynag.Utils.CheckResult :members: :undoc-members: :show-inheritance: The importer ____________ .. automodule:: pynag.Utils.importer :members: ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/pynag.Control.Command.rst������������������������������������������������0000664�0001750�0001750�00000000261�12363713622�021323� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Command Package =============== :mod:`Command` Package ---------------------- .. automodule:: pynag.Control.Command :members: :undoc-members: :show-inheritance: �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/pynag.Model.EventHandlers.rst��������������������������������������������0000664�0001750�0001750�00000000314�12311602524�022115� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������EventHandlers Package ===================== :mod:`EventHandlers` Package ---------------------------- .. automodule:: pynag.Model.EventHandlers :members: :undoc-members: :show-inheritance: ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/pynag.Parsers.rst��������������������������������������������������������0000664�0001750�0001750�00000000250�12311602524�017732� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Parsers Package =============== :mod:`Parsers` Package ---------------------- .. automodule:: pynag.Parsers :members: :undoc-members: :show-inheritance: ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/pynag.Plugins.rst��������������������������������������������������������0000664�0001750�0001750�00000000534�12311602524�017741� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plugins Package =============== :mod:`Plugins` Package ---------------------- .. automodule:: pynag.Plugins :members: :undoc-members: :show-inheritance: :mod:`new_threshold_syntax` Module ---------------------------------- .. automodule:: pynag.Plugins.new_threshold_syntax :members: :undoc-members: :show-inheritance: ��������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/pynag.Control.rst��������������������������������������������������������0000664�0001750�0001750�00000000352�12311602524�017736� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Control Package =============== :mod:`Control` Package ---------------------- .. automodule:: pynag.Control :members: :undoc-members: :show-inheritance: Subpackages ----------- .. toctree:: pynag.Control.Command ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/modules.rst��������������������������������������������������������������0000664�0001750�0001750�00000000064�12311602524�016651� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag ===== .. toctree:: :maxdepth: 4 pynag ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/introduction.rst���������������������������������������������������������0000664�0001750�0001750�00000000441�12311602524�017721� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������************ Introduction ************ .. sectionauthor:: Tomas Edwardsson <tommi at tommi.org> About pynag =========== Pynag is a all around python interface to Nagios and bretheren (Icinga, Naemon and Shinken) as well as providing a command line interface to them for managing them. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/conf.py������������������������������������������������������������������0000664�0001750�0001750�00000017540�12353523624�015766� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # # pynag documentation build configuration file, created by # sphinx-quickstart on Sun Mar 16 17:21:51 2014. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os sys.path.insert(0,os.path.abspath('.')) sys.path.insert(0,os.path.abspath('..')) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) from pynag import __version__ # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'pynag' copyright = u'2014, Pall Sigurdsson and Tomas Edwardsson' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = __version__ # The full version, including alpha/beta/rc tags. release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # "<project> v<release> documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a <link> tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'pynagdoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'pynag.tex', u'pynag Documentation', u'Pall Sigurdsson and Tomas Edwardsson', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('pynag-command', 'pynag', u'command line front for manipulating nagios configuration', [u'Pall Sigurdsson and Tomas Edwardsson'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'pynag', u'pynag Documentation', u'Pall Sigurdsson and Tomas Edwardsson', 'pynag', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' ����������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/pynag.Model.rst����������������������������������������������������������0000664�0001750�0001750�00000001014�12311602524�017352� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Model Package ============= :mod:`Model` Package -------------------- .. automodule:: pynag.Model :members: :undoc-members: :show-inheritance: :mod:`all_attributes` Module ---------------------------- .. automodule:: pynag.Model.all_attributes :members: :undoc-members: :show-inheritance: :mod:`macros` Module -------------------- .. automodule:: pynag.Model.macros :members: :undoc-members: :show-inheritance: Subpackages ----------- .. toctree:: pynag.Model.EventHandlers ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/�����������������������������������������������������������������0000775�0001750�0001750�00000000000�12426060361�016101� 5����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/file.png���������������������������������������������������������0000664�0001750�0001750�00000000610�12311602524�017517� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������a���bKGD������ pHYs�� �� ����tIME  )T��IDAT8˭J@Ir('[ "&xYZ X0!i|_@tD] #xjv YNaEi(əy@D&`6PZk$)5%"z.NA#Aba`Vs_3c,�2mj �[klvy|!Iմy;v "߮a?A7`c^nk?Bg}TЙD# "RD1yER*6MJ3K_Ut8F~����IENDB`������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/doctools.js������������������������������������������������������0000664�0001750�0001750�00000015270�12311602524�020266� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * doctools.js * ~~~~~~~~~~~ * * Sphinx JavaScript utilities for all documentation. * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ /** * select a different prefix for underscore */ $u = _.noConflict(); /** * make the code below compatible with browsers without * an installed firebug like debugger if (!window.console || !console.firebug) { var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; window.console = {}; for (var i = 0; i < names.length; ++i) window.console[names[i]] = function() {}; } */ /** * small helper function to urldecode strings */ jQuery.urldecode = function(x) { return decodeURIComponent(x).replace(/\+/g, ' '); } /** * small helper function to urlencode strings */ jQuery.urlencode = encodeURIComponent; /** * This function returns the parsed url parameters of the * current request. Multiple values per key are supported, * it will always return arrays of strings for the value parts. */ jQuery.getQueryParameters = function(s) { if (typeof s == 'undefined') s = document.location.search; var parts = s.substr(s.indexOf('?') + 1).split('&'); var result = {}; for (var i = 0; i < parts.length; i++) { var tmp = parts[i].split('=', 2); var key = jQuery.urldecode(tmp[0]); var value = jQuery.urldecode(tmp[1]); if (key in result) result[key].push(value); else result[key] = [value]; } return result; }; /** * small function to check if an array contains * a given item. */ jQuery.contains = function(arr, item) { for (var i = 0; i < arr.length; i++) { if (arr[i] == item) return true; } return false; }; /** * highlight a given string on a jquery object by wrapping it in * span elements with the given class name. */ jQuery.fn.highlightText = function(text, className) { function highlight(node) { if (node.nodeType == 3) { var val = node.nodeValue; var pos = val.toLowerCase().indexOf(text); if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { var span = document.createElement("span"); span.className = className; span.appendChild(document.createTextNode(val.substr(pos, text.length))); node.parentNode.insertBefore(span, node.parentNode.insertBefore( document.createTextNode(val.substr(pos + text.length)), node.nextSibling)); node.nodeValue = val.substr(0, pos); } } else if (!jQuery(node).is("button, select, textarea")) { jQuery.each(node.childNodes, function() { highlight(this); }); } } return this.each(function() { highlight(this); }); }; /** * Small JavaScript module for the documentation. */ var Documentation = { init : function() { this.fixFirefoxAnchorBug(); this.highlightSearchWords(); this.initIndexTable(); }, /** * i18n support */ TRANSLATIONS : {}, PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, LOCALE : 'unknown', // gettext and ngettext don't access this so that the functions // can safely bound to a different name (_ = Documentation.gettext) gettext : function(string) { var translated = Documentation.TRANSLATIONS[string]; if (typeof translated == 'undefined') return string; return (typeof translated == 'string') ? translated : translated[0]; }, ngettext : function(singular, plural, n) { var translated = Documentation.TRANSLATIONS[singular]; if (typeof translated == 'undefined') return (n == 1) ? singular : plural; return translated[Documentation.PLURALEXPR(n)]; }, addTranslations : function(catalog) { for (var key in catalog.messages) this.TRANSLATIONS[key] = catalog.messages[key]; this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); this.LOCALE = catalog.locale; }, /** * add context elements like header anchor links */ addContextElements : function() { $('div[id] > :header:first').each(function() { $('<a class="headerlink">\u00B6</a>'). attr('href', '#' + this.id). attr('title', _('Permalink to this headline')). appendTo(this); }); $('dt[id]').each(function() { $('<a class="headerlink">\u00B6</a>'). attr('href', '#' + this.id). attr('title', _('Permalink to this definition')). appendTo(this); }); }, /** * workaround a firefox stupidity */ fixFirefoxAnchorBug : function() { if (document.location.hash && $.browser.mozilla) window.setTimeout(function() { document.location.href += ''; }, 10); }, /** * highlight the search words provided in the url in the text */ highlightSearchWords : function() { var params = $.getQueryParameters(); var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; if (terms.length) { var body = $('div.body'); window.setTimeout(function() { $.each(terms, function() { body.highlightText(this.toLowerCase(), 'highlighted'); }); }, 10); $('<p class="highlight-link"><a href="javascript:Documentation.' + 'hideSearchWords()">' + _('Hide Search Matches') + '</a></p>') .appendTo($('#searchbox')); } }, /** * init the domain index toggle buttons */ initIndexTable : function() { var togglers = $('img.toggler').click(function() { var src = $(this).attr('src'); var idnum = $(this).attr('id').substr(7); $('tr.cg-' + idnum).toggle(); if (src.substr(-9) == 'minus.png') $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); else $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); }).css('display', ''); if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { togglers.click(); } }, /** * helper function to hide the search marks again */ hideSearchWords : function() { $('#searchbox .highlight-link').fadeOut(300); $('span.highlighted').removeClass('highlighted'); }, /** * make the url absolute */ makeURL : function(relativeURL) { return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; }, /** * get the current relative url */ getCurrentURL : function() { var path = document.location.pathname; var parts = path.split(/\//); $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { if (this == '..') parts.pop(); }); var url = parts.join('/'); return path.substring(url.lastIndexOf('/') + 1, path.length - 1); } }; // quick alias for translations _ = Documentation.gettext; $(document).ready(function() { Documentation.init(); }); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/comment-bright.png�����������������������������������������������0000664�0001750�0001750�00000006654�12311602524�021535� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������a�� OiCCPPhotoshop ICC profile��xڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $p�d!s#�~<<+"�x �M0B\t8K�@zB�@F&S��`cb�P-�`'�{�[!� eD�h;�VE�X0�fK9�-�0IWfH�� � �0Q)�{�`##x��FW<+*��x<$9E[-qWW.(I+6aa@.y24��x6_-"bbϫp@��t~,/;m%h^ uf@�Wp~<<EJB[aW}g_Wl~<$2]GLϒ bG "IbX*QqD2"B)%d,>5�j>{-]cK'Xt��o(hw?G%�fIq��^D$.Tʳ?��D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;�2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ<FFi\$mmƣ&&!&KMMRM);L;L֙͢5=12כ߷`ZxZ,eIZYnZ9YXUZ]F%ֻNNgðɶۮm}agbgŮ}}= Z~sr:V:ޚΜ?}/gX3)iSGggs󈋉K.>.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz�%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9<qy +V<*mOW~&zMk^ʂk U }]OX/Yߵa>(xoʿܔĹdff-[n ڴ VE/(ۻC<e;?TTTT6ݵan{4[>ɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG4<YyJTiӓgό}~.`ۢ{cjotE;;\tWW:_mt<Oǻ\kz{f7y՞9=ݽzo~r'˻w'O_@AC݇?[jwGCˆ 8>99?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-���bKGD������ pHYs�� �� ����tIME 6 B\<��IDAT8˅Kh]es1mA`jh[-E(FEaA!bIȐ*BX"؁4)NURZ!Mhjssm؋^-\gg ]o|Ҭ[346>zd �]#8Oݺt{5uIXN!I=@Vf=v1}e>;fvnvxaHrʪJF`D¹WZ]S%S)WAb |0K=So7D~\~q-˟\aMZ,S'*} F`Nnz674U<hRR躃FM`f"ҹ%*Rivq;"0'hPϛ VPRa}d+*Q "u,S4LqjrɢBCU$1 "q䈋+Ł'^fu+8D8C MPj M8DXtm c|o5|f(EAcD! |7ny@_(_~2Yby y Ek\Μ2v y؍ݰgJŻܽT&Y5<Sx꽇O{oOm;5A ^zGߪx߷8߲.G �l:?�)T%!G����IENDB`������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/up.png�����������������������������������������������������������0000664�0001750�0001750�00000000553�12311602524�017232� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������a���sRGB����bKGD������C��� pHYs�� �� B(x���tIME!.<̓E���IDAT8͓NABP\EG{%<|x�c  cr6@t;b$;3&)h1!﫳Hz�z@=)p� 3۵e2/ߴ ( %^ND^ }3H1DoǪISFұ?, G`{v^X[b]&HC3{:sO& ?,[eL#����IENDB`�����������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/basic.css��������������������������������������������������������0000664�0001750�0001750�00000020417�12311602524�017674� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * basic.css * ~~~~~~~~~ * * Sphinx stylesheet -- basic theme. * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ /* -- main layout ----------------------------------------------------------- */ div.clearer { clear: both; } /* -- relbar ---------------------------------------------------------------- */ div.related { width: 100%; font-size: 90%; } div.related h3 { display: none; } div.related ul { margin: 0; padding: 0 0 0 10px; list-style: none; } div.related li { display: inline; } div.related li.right { float: right; margin-right: 5px; } /* -- sidebar --------------------------------------------------------------- */ div.sphinxsidebarwrapper { padding: 10px 5px 0 10px; } div.sphinxsidebar { float: left; width: 230px; margin-left: -100%; font-size: 90%; } div.sphinxsidebar ul { list-style: none; } div.sphinxsidebar ul ul, div.sphinxsidebar ul.want-points { margin-left: 20px; list-style: square; } div.sphinxsidebar ul ul { margin-top: 0; margin-bottom: 0; } div.sphinxsidebar form { margin-top: 10px; } div.sphinxsidebar input { border: 1px solid #98dbcc; font-family: sans-serif; font-size: 1em; } div.sphinxsidebar #searchbox input[type="text"] { width: 170px; } div.sphinxsidebar #searchbox input[type="submit"] { width: 30px; } img { border: 0; } /* -- search page ----------------------------------------------------------- */ ul.search { margin: 10px 0 0 20px; padding: 0; } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px; } ul.search li a { font-weight: bold; } ul.search li div.context { color: #888; margin: 2px 0 0 30px; text-align: left; } ul.keywordmatches li.goodmatch a { font-weight: bold; } /* -- index page ------------------------------------------------------------ */ table.contentstable { width: 90%; } table.contentstable p.biglink { line-height: 150%; } a.biglink { font-size: 1.3em; } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90%; } /* -- general index --------------------------------------------------------- */ table.indextable { width: 100%; } table.indextable td { text-align: left; vertical-align: top; } table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0; } table.indextable tr.pcap { height: 10px; } table.indextable tr.cap { margin-top: 10px; background-color: #f2f2f2; } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer; } div.modindex-jumpbox { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 1em 0 1em 0; padding: 0.4em; } div.genindex-jumpbox { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 1em 0 1em 0; padding: 0.4em; } /* -- general body styles --------------------------------------------------- */ a.headerlink { visibility: hidden; } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible; } div.body p.caption { text-align: inherit; } div.body td { text-align: left; } .field-list ul { padding-left: 1em; } .first { margin-top: 0 !important; } p.rubric { margin-top: 30px; font-weight: bold; } img.align-left, .figure.align-left, object.align-left { clear: left; float: left; margin-right: 1em; } img.align-right, .figure.align-right, object.align-right { clear: right; float: right; margin-left: 1em; } img.align-center, .figure.align-center, object.align-center { display: block; margin-left: auto; margin-right: auto; } .align-left { text-align: left; } .align-center { text-align: center; } .align-right { text-align: right; } /* -- sidebars -------------------------------------------------------------- */ div.sidebar { margin: 0 0 0.5em 1em; border: 1px solid #ddb; padding: 7px 7px 0 7px; background-color: #ffe; width: 40%; float: right; } p.sidebar-title { font-weight: bold; } /* -- topics ---------------------------------------------------------------- */ div.topic { border: 1px solid #ccc; padding: 7px 7px 0 7px; margin: 10px 0 10px 0; } p.topic-title { font-size: 1.1em; font-weight: bold; margin-top: 10px; } /* -- admonitions ----------------------------------------------------------- */ div.admonition { margin-top: 10px; margin-bottom: 10px; padding: 7px; } div.admonition dt { font-weight: bold; } div.admonition dl { margin-bottom: 0; } p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; } div.body p.centered { text-align: center; margin-top: 25px; } /* -- tables ---------------------------------------------------------------- */ table.docutils { border: 0; border-collapse: collapse; } table.docutils td, table.docutils th { padding: 1px 8px 1px 5px; border-top: 0; border-left: 0; border-right: 0; border-bottom: 1px solid #aaa; } table.field-list td, table.field-list th { border: 0 !important; } table.footnote td, table.footnote th { border: 0 !important; } th { text-align: left; padding-right: 5px; } table.citation { border-left: solid 1px gray; margin-left: 1px; } table.citation td { border-bottom: none; } /* -- other body styles ----------------------------------------------------- */ ol.arabic { list-style: decimal; } ol.loweralpha { list-style: lower-alpha; } ol.upperalpha { list-style: upper-alpha; } ol.lowerroman { list-style: lower-roman; } ol.upperroman { list-style: upper-roman; } dl { margin-bottom: 15px; } dd p { margin-top: 0px; } dd ul, dd table { margin-bottom: 10px; } dd { margin-top: 3px; margin-bottom: 10px; margin-left: 30px; } dt:target, .highlighted { background-color: #fbe54e; } dl.glossary dt { font-weight: bold; font-size: 1.1em; } .field-list ul { margin: 0; padding-left: 1em; } .field-list p { margin: 0; } .refcount { color: #060; } .optional { font-size: 1.3em; } .versionmodified { font-style: italic; } .system-message { background-color: #fda; padding: 5px; border: 3px solid red; } .footnote:target { background-color: #ffa; } .line-block { display: block; margin-top: 1em; margin-bottom: 1em; } .line-block .line-block { margin-top: 0; margin-bottom: 0; margin-left: 1.5em; } .guilabel, .menuselection { font-family: sans-serif; } .accelerator { text-decoration: underline; } .classifier { font-style: oblique; } abbr, acronym { border-bottom: dotted 1px; cursor: help; } /* -- code displays --------------------------------------------------------- */ pre { overflow: auto; overflow-y: hidden; /* fixes display issues on Chrome browsers */ } td.linenos pre { padding: 5px 0px; border: 0; background-color: transparent; color: #aaa; } table.highlighttable { margin-left: 0.5em; } table.highlighttable td { padding: 0 0.5em 0 0.5em; } tt.descname { background-color: transparent; font-weight: bold; font-size: 1.2em; } tt.descclassname { background-color: transparent; } tt.xref, a tt { background-color: transparent; font-weight: bold; } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { background-color: transparent; } .viewcode-link { float: right; } .viewcode-back { float: right; font-family: sans-serif; } div.viewcode-block:target { margin: -1px -10px; padding: 0 10px; } /* -- math display ---------------------------------------------------------- */ img.math { vertical-align: middle; } div.body div.math p { text-align: center; } span.eqno { float: right; } /* -- printout stylesheet --------------------------------------------------- */ @media print { div.document, div.documentwrapper, div.bodywrapper { margin: 0 !important; width: 100%; } div.sphinxsidebar, div.related, div.footer, #top-link { display: none; } }�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/searchtools.js���������������������������������������������������0000664�0001750�0001750�00000037253�12311602524�020773� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * searchtools.js_t * ~~~~~~~~~~~~~~~~ * * Sphinx JavaScript utilties for the full-text search. * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ /** * helper function to return a node containing the * search summary for a given text. keywords is a list * of stemmed words, hlwords is the list of normal, unstemmed * words. the first one is used to find the occurance, the * latter for highlighting it. */ jQuery.makeSearchSummary = function(text, keywords, hlwords) { var textLower = text.toLowerCase(); var start = 0; $.each(keywords, function() { var i = textLower.indexOf(this.toLowerCase()); if (i > -1) start = i; }); start = Math.max(start - 120, 0); var excerpt = ((start > 0) ? '...' : '') + $.trim(text.substr(start, 240)) + ((start + 240 - text.length) ? '...' : ''); var rv = $('<div class="context"></div>').text(excerpt); $.each(hlwords, function() { rv = rv.highlightText(this, 'highlighted'); }); return rv; } /** * Porter Stemmer */ var Stemmer = function() { var step2list = { ational: 'ate', tional: 'tion', enci: 'ence', anci: 'ance', izer: 'ize', bli: 'ble', alli: 'al', entli: 'ent', eli: 'e', ousli: 'ous', ization: 'ize', ation: 'ate', ator: 'ate', alism: 'al', iveness: 'ive', fulness: 'ful', ousness: 'ous', aliti: 'al', iviti: 'ive', biliti: 'ble', logi: 'log' }; var step3list = { icate: 'ic', ative: '', alize: 'al', iciti: 'ic', ical: 'ic', ful: '', ness: '' }; var c = "[^aeiou]"; // consonant var v = "[aeiouy]"; // vowel var C = c + "[^aeiouy]*"; // consonant sequence var V = v + "[aeiou]*"; // vowel sequence var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 var s_v = "^(" + C + ")?" + v; // vowel in stem this.stemWord = function (w) { var stem; var suffix; var firstch; var origword = w; if (w.length < 3) return w; var re; var re2; var re3; var re4; firstch = w.substr(0,1); if (firstch == "y") w = firstch.toUpperCase() + w.substr(1); // Step 1a re = /^(.+?)(ss|i)es$/; re2 = /^(.+?)([^s])s$/; if (re.test(w)) w = w.replace(re,"$1$2"); else if (re2.test(w)) w = w.replace(re2,"$1$2"); // Step 1b re = /^(.+?)eed$/; re2 = /^(.+?)(ed|ing)$/; if (re.test(w)) { var fp = re.exec(w); re = new RegExp(mgr0); if (re.test(fp[1])) { re = /.$/; w = w.replace(re,""); } } else if (re2.test(w)) { var fp = re2.exec(w); stem = fp[1]; re2 = new RegExp(s_v); if (re2.test(stem)) { w = stem; re2 = /(at|bl|iz)$/; re3 = new RegExp("([^aeiouylsz])\\1$"); re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); if (re2.test(w)) w = w + "e"; else if (re3.test(w)) { re = /.$/; w = w.replace(re,""); } else if (re4.test(w)) w = w + "e"; } } // Step 1c re = /^(.+?)y$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(s_v); if (re.test(stem)) w = stem + "i"; } // Step 2 re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; suffix = fp[2]; re = new RegExp(mgr0); if (re.test(stem)) w = stem + step2list[suffix]; } // Step 3 re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; suffix = fp[2]; re = new RegExp(mgr0); if (re.test(stem)) w = stem + step3list[suffix]; } // Step 4 re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; re2 = /^(.+?)(s|t)(ion)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(mgr1); if (re.test(stem)) w = stem; } else if (re2.test(w)) { var fp = re2.exec(w); stem = fp[1] + fp[2]; re2 = new RegExp(mgr1); if (re2.test(stem)) w = stem; } // Step 5 re = /^(.+?)e$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(mgr1); re2 = new RegExp(meq1); re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) w = stem; } re = /ll$/; re2 = new RegExp(mgr1); if (re.test(w) && re2.test(w)) { re = /.$/; w = w.replace(re,""); } // and turn initial Y back to y if (firstch == "y") w = firstch.toLowerCase() + w.substr(1); return w; } } /** * Search Module */ var Search = { _index : null, _queued_query : null, _pulse_status : -1, init : function() { var params = $.getQueryParameters(); if (params.q) { var query = params.q[0]; $('input[name="q"]')[0].value = query; this.performSearch(query); } }, loadIndex : function(url) { $.ajax({type: "GET", url: url, data: null, success: null, dataType: "script", cache: true}); }, setIndex : function(index) { var q; this._index = index; if ((q = this._queued_query) !== null) { this._queued_query = null; Search.query(q); } }, hasIndex : function() { return this._index !== null; }, deferQuery : function(query) { this._queued_query = query; }, stopPulse : function() { this._pulse_status = 0; }, startPulse : function() { if (this._pulse_status >= 0) return; function pulse() { Search._pulse_status = (Search._pulse_status + 1) % 4; var dotString = ''; for (var i = 0; i < Search._pulse_status; i++) dotString += '.'; Search.dots.text(dotString); if (Search._pulse_status > -1) window.setTimeout(pulse, 500); }; pulse(); }, /** * perform a search for something */ performSearch : function(query) { // create the required interface elements this.out = $('#search-results'); this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out); this.dots = $('<span></span>').appendTo(this.title); this.status = $('<p style="display: none"></p>').appendTo(this.out); this.output = $('<ul class="search"/>').appendTo(this.out); $('#search-progress').text(_('Preparing search...')); this.startPulse(); // index already loaded, the browser was quick! if (this.hasIndex()) this.query(query); else this.deferQuery(query); }, query : function(query) { var stopwords = ["and","then","into","it","as","are","in","if","for","no","there","their","was","is","be","to","that","but","they","not","such","with","by","a","on","these","of","will","this","near","the","or","at"]; // Stem the searchterms and add them to the correct list var stemmer = new Stemmer(); var searchterms = []; var excluded = []; var hlterms = []; var tmp = query.split(/\s+/); var objectterms = []; for (var i = 0; i < tmp.length; i++) { if (tmp[i] != "") { objectterms.push(tmp[i].toLowerCase()); } if ($u.indexOf(stopwords, tmp[i]) != -1 || tmp[i].match(/^\d+$/) || tmp[i] == "") { // skip this "word" continue; } // stem the word var word = stemmer.stemWord(tmp[i]).toLowerCase(); // select the correct list if (word[0] == '-') { var toAppend = excluded; word = word.substr(1); } else { var toAppend = searchterms; hlterms.push(tmp[i].toLowerCase()); } // only add if not already in the list if (!$.contains(toAppend, word)) toAppend.push(word); }; var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" ")); // console.debug('SEARCH: searching for:'); // console.info('required: ', searchterms); // console.info('excluded: ', excluded); // prepare search var filenames = this._index.filenames; var titles = this._index.titles; var terms = this._index.terms; var fileMap = {}; var files = null; // different result priorities var importantResults = []; var objectResults = []; var regularResults = []; var unimportantResults = []; $('#search-progress').empty(); // lookup as object for (var i = 0; i < objectterms.length; i++) { var others = [].concat(objectterms.slice(0,i), objectterms.slice(i+1, objectterms.length)) var results = this.performObjectSearch(objectterms[i], others); // Assume first word is most likely to be the object, // other words more likely to be in description. // Therefore put matches for earlier words first. // (Results are eventually used in reverse order). objectResults = results[0].concat(objectResults); importantResults = results[1].concat(importantResults); unimportantResults = results[2].concat(unimportantResults); } // perform the search on the required terms for (var i = 0; i < searchterms.length; i++) { var word = searchterms[i]; // no match but word was a required one if ((files = terms[word]) == null) break; if (files.length == undefined) { files = [files]; } // create the mapping for (var j = 0; j < files.length; j++) { var file = files[j]; if (file in fileMap) fileMap[file].push(word); else fileMap[file] = [word]; } } // now check if the files don't contain excluded terms for (var file in fileMap) { var valid = true; // check if all requirements are matched if (fileMap[file].length != searchterms.length) continue; // ensure that none of the excluded terms is in the // search result. for (var i = 0; i < excluded.length; i++) { if (terms[excluded[i]] == file || $.contains(terms[excluded[i]] || [], file)) { valid = false; break; } } // if we have still a valid result we can add it // to the result list if (valid) regularResults.push([filenames[file], titles[file], '', null]); } // delete unused variables in order to not waste // memory until list is retrieved completely delete filenames, titles, terms; // now sort the regular results descending by title regularResults.sort(function(a, b) { var left = a[1].toLowerCase(); var right = b[1].toLowerCase(); return (left > right) ? -1 : ((left < right) ? 1 : 0); }); // combine all results var results = unimportantResults.concat(regularResults) .concat(objectResults).concat(importantResults); // print the results var resultCount = results.length; function displayNextItem() { // results left, load the summary and display it if (results.length) { var item = results.pop(); var listItem = $('<li style="display:none"></li>'); if (DOCUMENTATION_OPTIONS.FILE_SUFFIX == '') { // dirhtml builder var dirname = item[0] + '/'; if (dirname.match(/\/index\/$/)) { dirname = dirname.substring(0, dirname.length-6); } else if (dirname == 'index/') { dirname = ''; } listItem.append($('<a/>').attr('href', DOCUMENTATION_OPTIONS.URL_ROOT + dirname + highlightstring + item[2]).html(item[1])); } else { // normal html builders listItem.append($('<a/>').attr('href', item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX + highlightstring + item[2]).html(item[1])); } if (item[3]) { listItem.append($('<span> (' + item[3] + ')</span>')); Search.output.append(listItem); listItem.slideDown(5, function() { displayNextItem(); }); } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) { $.get(DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[0] + '.txt', function(data) { if (data != '') { listItem.append($.makeSearchSummary(data, searchterms, hlterms)); Search.output.append(listItem); } listItem.slideDown(5, function() { displayNextItem(); }); }, "text"); } else { // no source available, just display title Search.output.append(listItem); listItem.slideDown(5, function() { displayNextItem(); }); } } // search finished, update title and status message else { Search.stopPulse(); Search.title.text(_('Search Results')); if (!resultCount) Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.')); else Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount)); Search.status.fadeIn(500); } } displayNextItem(); }, performObjectSearch : function(object, otherterms) { var filenames = this._index.filenames; var objects = this._index.objects; var objnames = this._index.objnames; var titles = this._index.titles; var importantResults = []; var objectResults = []; var unimportantResults = []; for (var prefix in objects) { for (var name in objects[prefix]) { var fullname = (prefix ? prefix + '.' : '') + name; if (fullname.toLowerCase().indexOf(object) > -1) { var match = objects[prefix][name]; var objname = objnames[match[1]][2]; var title = titles[match[0]]; // If more than one term searched for, we require other words to be // found in the name/title/description if (otherterms.length > 0) { var haystack = (prefix + ' ' + name + ' ' + objname + ' ' + title).toLowerCase(); var allfound = true; for (var i = 0; i < otherterms.length; i++) { if (haystack.indexOf(otherterms[i]) == -1) { allfound = false; break; } } if (!allfound) { continue; } } var descr = objname + _(', in ') + title; anchor = match[3]; if (anchor == '') anchor = fullname; else if (anchor == '-') anchor = objnames[match[1]][1] + '-' + fullname; result = [filenames[match[0]], fullname, '#'+anchor, descr]; switch (match[2]) { case 1: objectResults.push(result); break; case 0: importantResults.push(result); break; case 2: unimportantResults.push(result); break; } } } } // sort results descending objectResults.sort(function(a, b) { return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0); }); importantResults.sort(function(a, b) { return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0); }); unimportantResults.sort(function(a, b) { return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0); }); return [importantResults, objectResults, unimportantResults] } } $(document).ready(function() { Search.init(); });�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/ajax-loader.gif��������������������������������������������������0000664�0001750�0001750�00000001241�12311602524�020751� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������GIF89a����U|NU|l!Created with ajaxload.info�!� ���! NETSCAPE2.0���,�������30Ikc:Nf E1º.`q-[9ݦ9 JkH��!� ��,�������4N!  DqBQT`1 `LE[|ua C%$*�!� ��,�������62#+AȐ̔V/cNIBap ̳ƨ+Y2d�!� ��,�������3b%+2V_ ! 1DaFbR]=08,Ȥr9L��!� ��,�������2r'+JdL &v`\bThYB)@<&,ȤR��!� ��,�������3 9tڞ0!.BW1  sa50 m)J��!� ��,�������2 ٜU]qp`a4AF0` @1Α��!� ��,�������20IeBԜ) q10ʰPaVڥ ub[��;������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/down-pressed.png�������������������������������������������������0000664�0001750�0001750�00000000560�12311602524�021216� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������a���sRGB����bKGD������C��� pHYs�� �� B(x���tIME �-vF#���IDAT8!OAJ, ++@I vbÿ@W7F HN#48646TMvv޼7Dsax1U q;< E-f)j%po4xF78G>)- EYm4%7YTk-Qa"NWAo-yeq,) Ypt\hqmszG]Nar߶s^l vh\2%0EeRv����IENDB`������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/minus.png��������������������������������������������������������0000664�0001750�0001750�00000000307�12311602524�017736� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��� ��� ���&q��� pHYs�� �� ����tIME <8���tEXtComment�̖���RIDATc<s ^U߿cEab `PrGE8~8LLLn;|ea``w3�=U[.����IENDB`�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/sidebar.js�������������������������������������������������������0000664�0001750�0001750�00000011204�12311602524�020042� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * sidebar.js * ~~~~~~~~~~ * * This script makes the Sphinx sidebar collapsible. * * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds * in .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton * used to collapse and expand the sidebar. * * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden * and the width of the sidebar and the margin-left of the document * are decreased. When the sidebar is expanded the opposite happens. * This script saves a per-browser/per-session cookie used to * remember the position of the sidebar among the pages. * Once the browser is closed the cookie is deleted and the position * reset to the default (expanded). * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ $(function() { // global elements used by the functions. // the 'sidebarbutton' element is defined as global after its // creation, in the add_sidebar_button function var bodywrapper = $('.bodywrapper'); var sidebar = $('.sphinxsidebar'); var sidebarwrapper = $('.sphinxsidebarwrapper'); // for some reason, the document has no sidebar; do not run into errors if (!sidebar.length) return; // original margin-left of the bodywrapper and width of the sidebar // with the sidebar expanded var bw_margin_expanded = bodywrapper.css('margin-left'); var ssb_width_expanded = sidebar.width(); // margin-left of the bodywrapper and width of the sidebar // with the sidebar collapsed var bw_margin_collapsed = '.8em'; var ssb_width_collapsed = '.8em'; // colors used by the current theme var dark_color = $('.related').css('background-color'); var light_color = $('.document').css('background-color'); function sidebar_is_collapsed() { return sidebarwrapper.is(':not(:visible)'); } function toggle_sidebar() { if (sidebar_is_collapsed()) expand_sidebar(); else collapse_sidebar(); } function collapse_sidebar() { sidebarwrapper.hide(); sidebar.css('width', ssb_width_collapsed); bodywrapper.css('margin-left', bw_margin_collapsed); sidebarbutton.css({ 'margin-left': '0', 'height': bodywrapper.height() }); sidebarbutton.find('span').text('»'); sidebarbutton.attr('title', _('Expand sidebar')); document.cookie = 'sidebar=collapsed'; } function expand_sidebar() { bodywrapper.css('margin-left', bw_margin_expanded); sidebar.css('width', ssb_width_expanded); sidebarwrapper.show(); sidebarbutton.css({ 'margin-left': ssb_width_expanded-12, 'height': bodywrapper.height() }); sidebarbutton.find('span').text('«'); sidebarbutton.attr('title', _('Collapse sidebar')); document.cookie = 'sidebar=expanded'; } function add_sidebar_button() { sidebarwrapper.css({ 'float': 'left', 'margin-right': '0', 'width': ssb_width_expanded - 28 }); // create the button sidebar.append( '<div id="sidebarbutton"><span>«</span></div>' ); var sidebarbutton = $('#sidebarbutton'); light_color = sidebarbutton.css('background-color'); // find the height of the viewport to center the '<<' in the page var viewport_height; if (window.innerHeight) viewport_height = window.innerHeight; else viewport_height = $(window).height(); sidebarbutton.find('span').css({ 'display': 'block', 'margin-top': (viewport_height - sidebar.position().top - 20) / 2 }); sidebarbutton.click(toggle_sidebar); sidebarbutton.attr('title', _('Collapse sidebar')); sidebarbutton.css({ 'color': '#FFFFFF', 'border-left': '1px solid ' + dark_color, 'font-size': '1.2em', 'cursor': 'pointer', 'height': bodywrapper.height(), 'padding-top': '1px', 'margin-left': ssb_width_expanded - 12 }); sidebarbutton.hover( function () { $(this).css('background-color', dark_color); }, function () { $(this).css('background-color', light_color); } ); } function set_position_from_cookie() { if (!document.cookie) return; var items = document.cookie.split(';'); for(var k=0; k<items.length; k++) { var key_val = items[k].split('='); var key = key_val[0]; if (key == 'sidebar') { var value = key_val[1]; if ((value == 'collapsed') && (!sidebar_is_collapsed())) collapse_sidebar(); else if ((value == 'expanded') && (sidebar_is_collapsed())) expand_sidebar(); } } } add_sidebar_button(); var sidebarbutton = $('#sidebarbutton'); set_position_from_cookie(); }); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/default.css������������������������������������������������������0000664�0001750�0001750�00000007710�12311602524�020240� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * default.css_t * ~~~~~~~~~~~~~ * * Sphinx stylesheet -- default theme. * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @import url("basic.css"); /* -- page layout ----------------------------------------------------------- */ body { font-family: sans-serif; font-size: 100%; background-color: #11303d; color: #000; margin: 0; padding: 0; } div.document { background-color: #1c4e63; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 230px; } div.body { background-color: #ffffff; color: #000000; padding: 0 20px 30px 20px; } div.footer { color: #ffffff; width: 100%; padding: 9px 0 9px 0; text-align: center; font-size: 75%; } div.footer a { color: #ffffff; text-decoration: underline; } div.related { background-color: #133f52; line-height: 30px; color: #ffffff; } div.related a { color: #ffffff; } div.sphinxsidebar { } div.sphinxsidebar h3 { font-family: 'Trebuchet MS', sans-serif; color: #ffffff; font-size: 1.4em; font-weight: normal; margin: 0; padding: 0; } div.sphinxsidebar h3 a { color: #ffffff; } div.sphinxsidebar h4 { font-family: 'Trebuchet MS', sans-serif; color: #ffffff; font-size: 1.3em; font-weight: normal; margin: 5px 0 0 0; padding: 0; } div.sphinxsidebar p { color: #ffffff; } div.sphinxsidebar p.topless { margin: 5px 10px 10px 10px; } div.sphinxsidebar ul { margin: 10px; padding: 0; color: #ffffff; } div.sphinxsidebar a { color: #98dbcc; } div.sphinxsidebar input { border: 1px solid #98dbcc; font-family: sans-serif; font-size: 1em; } /* -- hyperlink styles ------------------------------------------------------ */ a { color: #355f7c; text-decoration: none; } a:visited { color: #355f7c; text-decoration: none; } a:hover { text-decoration: underline; } /* -- body styles ----------------------------------------------------------- */ div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: 'Trebuchet MS', sans-serif; background-color: #f2f2f2; font-weight: normal; color: #20435c; border-bottom: 1px solid #ccc; margin: 20px -20px 10px -20px; padding: 3px 0 3px 10px; } div.body h1 { margin-top: 0; font-size: 200%; } div.body h2 { font-size: 160%; } div.body h3 { font-size: 140%; } div.body h4 { font-size: 120%; } div.body h5 { font-size: 110%; } div.body h6 { font-size: 100%; } a.headerlink { color: #c60f0f; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; } a.headerlink:hover { background-color: #c60f0f; color: white; } div.body p, div.body dd, div.body li { text-align: justify; line-height: 130%; } div.admonition p.admonition-title + p { display: inline; } div.admonition p { margin-bottom: 5px; } div.admonition pre { margin-bottom: 5px; } div.admonition ul, div.admonition ol { margin-bottom: 5px; } div.note { background-color: #eee; border: 1px solid #ccc; } div.seealso { background-color: #ffc; border: 1px solid #ff6; } div.topic { background-color: #eee; } div.warning { background-color: #ffe4e4; border: 1px solid #f66; } p.admonition-title { display: inline; } p.admonition-title:after { content: ":"; } pre { padding: 5px; background-color: #eeffcc; color: #333333; line-height: 120%; border: 1px solid #ac9; border-left: none; border-right: none; } tt { background-color: #ecf0f3; padding: 0 1px 0 1px; font-size: 0.95em; } th { background-color: #ede; } .warning tt { background: #efc2c2; } .note tt { background: #d6d6d6; } .viewcode-back { font-family: sans-serif; } div.viewcode-block:target { background-color: #f4debf; border-top: 1px solid #ac9; border-bottom: 1px solid #ac9; }��������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/websupport.js����������������������������������������������������0000664�0001750�0001750�00000061236�12311602524�020655� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * websupport.js * ~~~~~~~~~~~~~ * * sphinx.websupport utilties for all documentation. * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ (function($) { $.fn.autogrow = function() { return this.each(function() { var textarea = this; $.fn.autogrow.resize(textarea); $(textarea) .focus(function() { textarea.interval = setInterval(function() { $.fn.autogrow.resize(textarea); }, 500); }) .blur(function() { clearInterval(textarea.interval); }); }); }; $.fn.autogrow.resize = function(textarea) { var lineHeight = parseInt($(textarea).css('line-height'), 10); var lines = textarea.value.split('\n'); var columns = textarea.cols; var lineCount = 0; $.each(lines, function() { lineCount += Math.ceil(this.length / columns) || 1; }); var height = lineHeight * (lineCount + 1); $(textarea).css('height', height); }; })(jQuery); (function($) { var comp, by; function init() { initEvents(); initComparator(); } function initEvents() { $('a.comment-close').live("click", function(event) { event.preventDefault(); hide($(this).attr('id').substring(2)); }); $('a.vote').live("click", function(event) { event.preventDefault(); handleVote($(this)); }); $('a.reply').live("click", function(event) { event.preventDefault(); openReply($(this).attr('id').substring(2)); }); $('a.close-reply').live("click", function(event) { event.preventDefault(); closeReply($(this).attr('id').substring(2)); }); $('a.sort-option').live("click", function(event) { event.preventDefault(); handleReSort($(this)); }); $('a.show-proposal').live("click", function(event) { event.preventDefault(); showProposal($(this).attr('id').substring(2)); }); $('a.hide-proposal').live("click", function(event) { event.preventDefault(); hideProposal($(this).attr('id').substring(2)); }); $('a.show-propose-change').live("click", function(event) { event.preventDefault(); showProposeChange($(this).attr('id').substring(2)); }); $('a.hide-propose-change').live("click", function(event) { event.preventDefault(); hideProposeChange($(this).attr('id').substring(2)); }); $('a.accept-comment').live("click", function(event) { event.preventDefault(); acceptComment($(this).attr('id').substring(2)); }); $('a.delete-comment').live("click", function(event) { event.preventDefault(); deleteComment($(this).attr('id').substring(2)); }); $('a.comment-markup').live("click", function(event) { event.preventDefault(); toggleCommentMarkupBox($(this).attr('id').substring(2)); }); } /** * Set comp, which is a comparator function used for sorting and * inserting comments into the list. */ function setComparator() { // If the first three letters are "asc", sort in ascending order // and remove the prefix. if (by.substring(0,3) == 'asc') { var i = by.substring(3); comp = function(a, b) { return a[i] - b[i]; }; } else { // Otherwise sort in descending order. comp = function(a, b) { return b[by] - a[by]; }; } // Reset link styles and format the selected sort option. $('a.sel').attr('href', '#').removeClass('sel'); $('a.by' + by).removeAttr('href').addClass('sel'); } /** * Create a comp function. If the user has preferences stored in * the sortBy cookie, use those, otherwise use the default. */ function initComparator() { by = 'rating'; // Default to sort by rating. // If the sortBy cookie is set, use that instead. if (document.cookie.length > 0) { var start = document.cookie.indexOf('sortBy='); if (start != -1) { start = start + 7; var end = document.cookie.indexOf(";", start); if (end == -1) { end = document.cookie.length; by = unescape(document.cookie.substring(start, end)); } } } setComparator(); } /** * Show a comment div. */ function show(id) { $('#ao' + id).hide(); $('#ah' + id).show(); var context = $.extend({id: id}, opts); var popup = $(renderTemplate(popupTemplate, context)).hide(); popup.find('textarea[name="proposal"]').hide(); popup.find('a.by' + by).addClass('sel'); var form = popup.find('#cf' + id); form.submit(function(event) { event.preventDefault(); addComment(form); }); $('#s' + id).after(popup); popup.slideDown('fast', function() { getComments(id); }); } /** * Hide a comment div. */ function hide(id) { $('#ah' + id).hide(); $('#ao' + id).show(); var div = $('#sc' + id); div.slideUp('fast', function() { div.remove(); }); } /** * Perform an ajax request to get comments for a node * and insert the comments into the comments tree. */ function getComments(id) { $.ajax({ type: 'GET', url: opts.getCommentsURL, data: {node: id}, success: function(data, textStatus, request) { var ul = $('#cl' + id); var speed = 100; $('#cf' + id) .find('textarea[name="proposal"]') .data('source', data.source); if (data.comments.length === 0) { ul.html('<li>No comments yet.</li>'); ul.data('empty', true); } else { // If there are comments, sort them and put them in the list. var comments = sortComments(data.comments); speed = data.comments.length * 100; appendComments(comments, ul); ul.data('empty', false); } $('#cn' + id).slideUp(speed + 200); ul.slideDown(speed); }, error: function(request, textStatus, error) { showError('Oops, there was a problem retrieving the comments.'); }, dataType: 'json' }); } /** * Add a comment via ajax and insert the comment into the comment tree. */ function addComment(form) { var node_id = form.find('input[name="node"]').val(); var parent_id = form.find('input[name="parent"]').val(); var text = form.find('textarea[name="comment"]').val(); var proposal = form.find('textarea[name="proposal"]').val(); if (text == '') { showError('Please enter a comment.'); return; } // Disable the form that is being submitted. form.find('textarea,input').attr('disabled', 'disabled'); // Send the comment to the server. $.ajax({ type: "POST", url: opts.addCommentURL, dataType: 'json', data: { node: node_id, parent: parent_id, text: text, proposal: proposal }, success: function(data, textStatus, error) { // Reset the form. if (node_id) { hideProposeChange(node_id); } form.find('textarea') .val('') .add(form.find('input')) .removeAttr('disabled'); var ul = $('#cl' + (node_id || parent_id)); if (ul.data('empty')) { $(ul).empty(); ul.data('empty', false); } insertComment(data.comment); var ao = $('#ao' + node_id); ao.find('img').attr({'src': opts.commentBrightImage}); if (node_id) { // if this was a "root" comment, remove the commenting box // (the user can get it back by reopening the comment popup) $('#ca' + node_id).slideUp(); } }, error: function(request, textStatus, error) { form.find('textarea,input').removeAttr('disabled'); showError('Oops, there was a problem adding the comment.'); } }); } /** * Recursively append comments to the main comment list and children * lists, creating the comment tree. */ function appendComments(comments, ul) { $.each(comments, function() { var div = createCommentDiv(this); ul.append($(document.createElement('li')).html(div)); appendComments(this.children, div.find('ul.comment-children')); // To avoid stagnating data, don't store the comments children in data. this.children = null; div.data('comment', this); }); } /** * After adding a new comment, it must be inserted in the correct * location in the comment tree. */ function insertComment(comment) { var div = createCommentDiv(comment); // To avoid stagnating data, don't store the comments children in data. comment.children = null; div.data('comment', comment); var ul = $('#cl' + (comment.node || comment.parent)); var siblings = getChildren(ul); var li = $(document.createElement('li')); li.hide(); // Determine where in the parents children list to insert this comment. for(i=0; i < siblings.length; i++) { if (comp(comment, siblings[i]) <= 0) { $('#cd' + siblings[i].id) .parent() .before(li.html(div)); li.slideDown('fast'); return; } } // If we get here, this comment rates lower than all the others, // or it is the only comment in the list. ul.append(li.html(div)); li.slideDown('fast'); } function acceptComment(id) { $.ajax({ type: 'POST', url: opts.acceptCommentURL, data: {id: id}, success: function(data, textStatus, request) { $('#cm' + id).fadeOut('fast'); $('#cd' + id).removeClass('moderate'); }, error: function(request, textStatus, error) { showError('Oops, there was a problem accepting the comment.'); } }); } function deleteComment(id) { $.ajax({ type: 'POST', url: opts.deleteCommentURL, data: {id: id}, success: function(data, textStatus, request) { var div = $('#cd' + id); if (data == 'delete') { // Moderator mode: remove the comment and all children immediately div.slideUp('fast', function() { div.remove(); }); return; } // User mode: only mark the comment as deleted div .find('span.user-id:first') .text('[deleted]').end() .find('div.comment-text:first') .text('[deleted]').end() .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id + ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id) .remove(); var comment = div.data('comment'); comment.username = '[deleted]'; comment.text = '[deleted]'; div.data('comment', comment); }, error: function(request, textStatus, error) { showError('Oops, there was a problem deleting the comment.'); } }); } function showProposal(id) { $('#sp' + id).hide(); $('#hp' + id).show(); $('#pr' + id).slideDown('fast'); } function hideProposal(id) { $('#hp' + id).hide(); $('#sp' + id).show(); $('#pr' + id).slideUp('fast'); } function showProposeChange(id) { $('#pc' + id).hide(); $('#hc' + id).show(); var textarea = $('#pt' + id); textarea.val(textarea.data('source')); $.fn.autogrow.resize(textarea[0]); textarea.slideDown('fast'); } function hideProposeChange(id) { $('#hc' + id).hide(); $('#pc' + id).show(); var textarea = $('#pt' + id); textarea.val('').removeAttr('disabled'); textarea.slideUp('fast'); } function toggleCommentMarkupBox(id) { $('#mb' + id).toggle(); } /** Handle when the user clicks on a sort by link. */ function handleReSort(link) { var classes = link.attr('class').split(/\s+/); for (var i=0; i<classes.length; i++) { if (classes[i] != 'sort-option') { by = classes[i].substring(2); } } setComparator(); // Save/update the sortBy cookie. var expiration = new Date(); expiration.setDate(expiration.getDate() + 365); document.cookie= 'sortBy=' + escape(by) + ';expires=' + expiration.toUTCString(); $('ul.comment-ul').each(function(index, ul) { var comments = getChildren($(ul), true); comments = sortComments(comments); appendComments(comments, $(ul).empty()); }); } /** * Function to process a vote when a user clicks an arrow. */ function handleVote(link) { if (!opts.voting) { showError("You'll need to login to vote."); return; } var id = link.attr('id'); if (!id) { // Didn't click on one of the voting arrows. return; } // If it is an unvote, the new vote value is 0, // Otherwise it's 1 for an upvote, or -1 for a downvote. var value = 0; if (id.charAt(1) != 'u') { value = id.charAt(0) == 'u' ? 1 : -1; } // The data to be sent to the server. var d = { comment_id: id.substring(2), value: value }; // Swap the vote and unvote links. link.hide(); $('#' + id.charAt(0) + (id.charAt(1) == 'u' ? 'v' : 'u') + d.comment_id) .show(); // The div the comment is displayed in. var div = $('div#cd' + d.comment_id); var data = div.data('comment'); // If this is not an unvote, and the other vote arrow has // already been pressed, unpress it. if ((d.value !== 0) && (data.vote === d.value * -1)) { $('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide(); $('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show(); } // Update the comments rating in the local data. data.rating += (data.vote === 0) ? d.value : (d.value - data.vote); data.vote = d.value; div.data('comment', data); // Change the rating text. div.find('.rating:first') .text(data.rating + ' point' + (data.rating == 1 ? '' : 's')); // Send the vote information to the server. $.ajax({ type: "POST", url: opts.processVoteURL, data: d, error: function(request, textStatus, error) { showError('Oops, there was a problem casting that vote.'); } }); } /** * Open a reply form used to reply to an existing comment. */ function openReply(id) { // Swap out the reply link for the hide link $('#rl' + id).hide(); $('#cr' + id).show(); // Add the reply li to the children ul. var div = $(renderTemplate(replyTemplate, {id: id})).hide(); $('#cl' + id) .prepend(div) // Setup the submit handler for the reply form. .find('#rf' + id) .submit(function(event) { event.preventDefault(); addComment($('#rf' + id)); closeReply(id); }) .find('input[type=button]') .click(function() { closeReply(id); }); div.slideDown('fast', function() { $('#rf' + id).find('textarea').focus(); }); } /** * Close the reply form opened with openReply. */ function closeReply(id) { // Remove the reply div from the DOM. $('#rd' + id).slideUp('fast', function() { $(this).remove(); }); // Swap out the hide link for the reply link $('#cr' + id).hide(); $('#rl' + id).show(); } /** * Recursively sort a tree of comments using the comp comparator. */ function sortComments(comments) { comments.sort(comp); $.each(comments, function() { this.children = sortComments(this.children); }); return comments; } /** * Get the children comments from a ul. If recursive is true, * recursively include childrens' children. */ function getChildren(ul, recursive) { var children = []; ul.children().children("[id^='cd']") .each(function() { var comment = $(this).data('comment'); if (recursive) comment.children = getChildren($(this).find('#cl' + comment.id), true); children.push(comment); }); return children; } /** Create a div to display a comment in. */ function createCommentDiv(comment) { if (!comment.displayed && !opts.moderator) { return $('<div class="moderate">Thank you! Your comment will show up ' + 'once it is has been approved by a moderator.</div>'); } // Prettify the comment rating. comment.pretty_rating = comment.rating + ' point' + (comment.rating == 1 ? '' : 's'); // Make a class (for displaying not yet moderated comments differently) comment.css_class = comment.displayed ? '' : ' moderate'; // Create a div for this comment. var context = $.extend({}, opts, comment); var div = $(renderTemplate(commentTemplate, context)); // If the user has voted on this comment, highlight the correct arrow. if (comment.vote) { var direction = (comment.vote == 1) ? 'u' : 'd'; div.find('#' + direction + 'v' + comment.id).hide(); div.find('#' + direction + 'u' + comment.id).show(); } if (opts.moderator || comment.text != '[deleted]') { div.find('a.reply').show(); if (comment.proposal_diff) div.find('#sp' + comment.id).show(); if (opts.moderator && !comment.displayed) div.find('#cm' + comment.id).show(); if (opts.moderator || (opts.username == comment.username)) div.find('#dc' + comment.id).show(); } return div; } /** * A simple template renderer. Placeholders such as <%id%> are replaced * by context['id'] with items being escaped. Placeholders such as <#id#> * are not escaped. */ function renderTemplate(template, context) { var esc = $(document.createElement('div')); function handle(ph, escape) { var cur = context; $.each(ph.split('.'), function() { cur = cur[this]; }); return escape ? esc.text(cur || "").html() : cur; } return template.replace(/<([%#])([\w\.]*)\1>/g, function() { return handle(arguments[2], arguments[1] == '%' ? true : false); }); } /** Flash an error message briefly. */ function showError(message) { $(document.createElement('div')).attr({'class': 'popup-error'}) .append($(document.createElement('div')) .attr({'class': 'error-message'}).text(message)) .appendTo('body') .fadeIn("slow") .delay(2000) .fadeOut("slow"); } /** Add a link the user uses to open the comments popup. */ $.fn.comment = function() { return this.each(function() { var id = $(this).attr('id').substring(1); var count = COMMENT_METADATA[id]; var title = count + ' comment' + (count == 1 ? '' : 's'); var image = count > 0 ? opts.commentBrightImage : opts.commentImage; var addcls = count == 0 ? ' nocomment' : ''; $(this) .append( $(document.createElement('a')).attr({ href: '#', 'class': 'sphinx-comment-open' + addcls, id: 'ao' + id }) .append($(document.createElement('img')).attr({ src: image, alt: 'comment', title: title })) .click(function(event) { event.preventDefault(); show($(this).attr('id').substring(2)); }) ) .append( $(document.createElement('a')).attr({ href: '#', 'class': 'sphinx-comment-close hidden', id: 'ah' + id }) .append($(document.createElement('img')).attr({ src: opts.closeCommentImage, alt: 'close', title: 'close' })) .click(function(event) { event.preventDefault(); hide($(this).attr('id').substring(2)); }) ); }); }; var opts = { processVoteURL: '/_process_vote', addCommentURL: '/_add_comment', getCommentsURL: '/_get_comments', acceptCommentURL: '/_accept_comment', deleteCommentURL: '/_delete_comment', commentImage: '/static/_static/comment.png', closeCommentImage: '/static/_static/comment-close.png', loadingImage: '/static/_static/ajax-loader.gif', commentBrightImage: '/static/_static/comment-bright.png', upArrow: '/static/_static/up.png', downArrow: '/static/_static/down.png', upArrowPressed: '/static/_static/up-pressed.png', downArrowPressed: '/static/_static/down-pressed.png', voting: false, moderator: false }; if (typeof COMMENT_OPTIONS != "undefined") { opts = jQuery.extend(opts, COMMENT_OPTIONS); } var popupTemplate = '\ <div class="sphinx-comments" id="sc<%id%>">\ <p class="sort-options">\ Sort by:\ <a href="#" class="sort-option byrating">best rated</a>\ <a href="#" class="sort-option byascage">newest</a>\ <a href="#" class="sort-option byage">oldest</a>\ </p>\ <div class="comment-header">Comments</div>\ <div class="comment-loading" id="cn<%id%>">\ loading comments... <img src="<%loadingImage%>" alt="" /></div>\ <ul id="cl<%id%>" class="comment-ul"></ul>\ <div id="ca<%id%>">\ <p class="add-a-comment">Add a comment\ (<a href="#" class="comment-markup" id="ab<%id%>">markup</a>):</p>\ <div class="comment-markup-box" id="mb<%id%>">\ reStructured text markup: <i>*emph*</i>, <b>**strong**</b>, \ <tt>``code``</tt>, \ code blocks: <tt>::</tt> and an indented block after blank line</div>\ <form method="post" id="cf<%id%>" class="comment-form" action="">\ <textarea name="comment" cols="80"></textarea>\ <p class="propose-button">\ <a href="#" id="pc<%id%>" class="show-propose-change">\ Propose a change ▹\ </a>\ <a href="#" id="hc<%id%>" class="hide-propose-change">\ Propose a change ▿\ </a>\ </p>\ <textarea name="proposal" id="pt<%id%>" cols="80"\ spellcheck="false"></textarea>\ <input type="submit" value="Add comment" />\ <input type="hidden" name="node" value="<%id%>" />\ <input type="hidden" name="parent" value="" />\ </form>\ </div>\ </div>'; var commentTemplate = '\ <div id="cd<%id%>" class="sphinx-comment<%css_class%>">\ <div class="vote">\ <div class="arrow">\ <a href="#" id="uv<%id%>" class="vote" title="vote up">\ <img src="<%upArrow%>" />\ </a>\ <a href="#" id="uu<%id%>" class="un vote" title="vote up">\ <img src="<%upArrowPressed%>" />\ </a>\ </div>\ <div class="arrow">\ <a href="#" id="dv<%id%>" class="vote" title="vote down">\ <img src="<%downArrow%>" id="da<%id%>" />\ </a>\ <a href="#" id="du<%id%>" class="un vote" title="vote down">\ <img src="<%downArrowPressed%>" />\ </a>\ </div>\ </div>\ <div class="comment-content">\ <p class="tagline comment">\ <span class="user-id"><%username%></span>\ <span class="rating"><%pretty_rating%></span>\ <span class="delta"><%time.delta%></span>\ </p>\ <div class="comment-text comment"><#text#></div>\ <p class="comment-opts comment">\ <a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a>\ <a href="#" class="close-reply" id="cr<%id%>">reply ▿</a>\ <a href="#" id="sp<%id%>" class="show-proposal">proposal ▹</a>\ <a href="#" id="hp<%id%>" class="hide-proposal">proposal ▿</a>\ <a href="#" id="dc<%id%>" class="delete-comment hidden">delete</a>\ <span id="cm<%id%>" class="moderation hidden">\ <a href="#" id="ac<%id%>" class="accept-comment">accept</a>\ </span>\ </p>\ <pre class="proposal" id="pr<%id%>">\ <#proposal_diff#>\ </pre>\ <ul class="comment-children" id="cl<%id%>"></ul>\ </div>\ <div class="clearleft"></div>\ </div>\ </div>'; var replyTemplate = '\ <li>\ <div class="reply-div" id="rd<%id%>">\ <form id="rf<%id%>">\ <textarea name="comment" cols="80"></textarea>\ <input type="submit" value="Add reply" />\ <input type="button" value="Cancel" />\ <input type="hidden" name="parent" value="<%id%>" />\ <input type="hidden" name="node" value="" />\ </form>\ </div>\ </li>'; $(document).ready(function() { init(); }); })(jQuery); $(document).ready(function() { // add comment anchors for all paragraphs that are commentable $('.sphinx-has-comment').comment(); // highlight search words in search results $("div.context").each(function() { var params = $.getQueryParameters(); var terms = (params.q) ? params.q[0].split(/\s+/) : []; var result = $(this); $.each(terms, function() { result.highlightText(this.toLowerCase(), 'highlighted'); }); }); // directly open comment window if requested var anchor = document.location.hash; if (anchor.substring(0, 9) == '#comment-') { $('#ao' + anchor.substring(9)).click(); document.location.hash = '#s' + anchor.substring(9); } }); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/up-pressed.png���������������������������������������������������0000664�0001750�0001750�00000000564�12311602524�020677� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������a���sRGB����bKGD������C��� pHYs�� �� B(x���tIME �,Ze���IDAT8͓jA*WKk-,By@- و/`cXYh!6jf GrOlXvvfk2!p!GOOԲ &zf 6|M~%`]* ΛM]K ZĆ1E�r%ȶcm1`<Ezhl^)Al�_ԩ`U 8` W+Ky,7Dlx7+-=AOzw����IENDB`��������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/down.png���������������������������������������������������������0000664�0001750�0001750�00000000553�12311602524�017555� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������a���sRGB����bKGD������C��� pHYs�� �� B(x���tIME"U{���IDAT8ҡNCAJ, ++@4>�/U^,~T&3M^^^PM6ٹs*RJa)eG*�W<"F Fg78G>q OIp:sAj5GنyD^+yU:p_%G@D|aOs(yM,"msx:.b@D|`Vٟ۲иeKſ/G!����IENDB`�����������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/comment-close.png������������������������������������������������0000664�0001750�0001750�00000006772�12311602524�021364� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������a�� OiCCPPhotoshop ICC profile��xڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $p�d!s#�~<<+"�x �M0B\t8K�@zB�@F&S��`cb�P-�`'�{�[!� eD�h;�VE�X0�fK9�-�0IWfH�� � �0Q)�{�`##x��FW<+*��x<$9E[-qWW.(I+6aa@.y24��x6_-"bbϫp@��t~,/;m%h^ uf@�Wp~<<EJB[aW}g_Wl~<$2]GLϒ bG "IbX*QqD2"B)%d,>5�j>{-]cK'Xt��o(hw?G%�fIq��^D$.Tʳ?��D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;�2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ<FFi\$mmƣ&&!&KMMRM);L;L֙͢5=12כ߷`ZxZ,eIZYnZ9YXUZ]F%ֻNNgðɶۮm}agbgŮ}}= Z~sr:V:ޚΜ?}/gX3)iSGggs󈋉K.>.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz�%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9<qy +V<*mOW~&zMk^ʂk U }]OX/Yߵa>(xoʿܔĹdff-[n ڴ VE/(ۻC<e;?TTTT6ݵan{4[>ɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG4<YyJTiӓgό}~.`ۢ{cjotE;;\tWW:_mt<Oǻ\kz{f7y՞9=ݽzo~r'˻w'O_@AC݇?[jwGCˆ 8>99?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-���bKGD������ pHYs�� �� ����tIME!��,IDAT8e_Hu?}s3y˕U2MvQ֊FE.łĊbE$DDZF5b@Q":2{n.s<_ y?mwV@tR`}Z _# _=_@ w^R%6gC-έ(K>| ${}<u|kA:!Oq`- oL66\5.*вj$(j(-qDzrpOԴ|b`$6DQK4T"dLk`e읪7LNe%40JAh#6R'K]4;ۯo((pjfX0q"`|HUE )_"q<׀wPGpW}KshdYL 1|#1dWvsMu׃X9P+|&p#\oFH ~ &sΆ6ʊzcu{~|xʡ~8Jzv"Mև|Zk9Q~´\Wq$4RzP0G( DGgqw=0{˦u]'B/PD b4i$ q�dž�3?)'fqW4XM^d2F#|=RykPYk:?mSc 痭fƖK/if}NL5]Vy{M^wBaE!0c;^R]qIU/)(<0+dgq__E:_WxV!Аj~7,dS����IENDB`������pynag-0.9.1+dfsg.orig/docs/_static/comment.png������������������������������������������������������0000664�0001750�0001750�00000006565�12311602524�020261� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������a�� OiCCPPhotoshop ICC profile��xڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $p�d!s#�~<<+"�x �M0B\t8K�@zB�@F&S��`cb�P-�`'�{�[!� eD�h;�VE�X0�fK9�-�0IWfH�� � �0Q)�{�`##x��FW<+*��x<$9E[-qWW.(I+6aa@.y24��x6_-"bbϫp@��t~,/;m%h^ uf@�Wp~<<EJB[aW}g_Wl~<$2]GLϒ bG "IbX*QqD2"B)%d,>5�j>{-]cK'Xt��o(hw?G%�fIq��^D$.Tʳ?��D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;�2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ<FFi\$mmƣ&&!&KMMRM);L;L֙͢5=12כ߷`ZxZ,eIZYnZ9YXUZ]F%ֻNNgðɶۮm}agbgŮ}}= Z~sr:V:ޚΜ?}/gX3)iSGggs󈋉K.>.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz�%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9<qy +V<*mOW~&zMk^ʂk U }]OX/Yߵa>(xoʿܔĹdff-[n ڴ VE/(ۻC<e;?TTTT6ݵan{4[>ɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG4<YyJTiӓgό}~.`ۢ{cjotE;;\tWW:_mt<Oǻ\kz{f7y՞9=ݽzo~r'˻w'O_@AC݇?[jwGCˆ 8>99?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-���bKGD������ pHYs�� �� ����tIME �1;V��IDAT8ukU?sg4h`G1 RQܸp%Bn"bЍXJ .4V iZ##T;m!4bP~7r>ιbwc;m;oӍAΆ ζZ^/|s{;yR=9(r�tVoG1w#_ө{*E&!(LVuoᲵ‘D� PG4 :&~*ݳreu: S-,U^E&JY[P!RB ŖޞʖR@_ȐdBfNvHf"2T]R j'B1ddAak/DIJD D2H&L`&L $Ex,6|~_\P $MH`I=@Z||ttvgcЕWTZ'3rje"ܵx9W> mb|byfFRx{w%DZC$wdցHmWnta(M<~;9]C/_;Տ#}o`zSڷ_>:;x컓?yݩ|}~wam-/7=0S5R�P"*֯ ����IENDB`�������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/plus.png���������������������������������������������������������0000664�0001750�0001750�00000000307�12311602524�017566� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��� ��� ���&q��� pHYs�� �� ����tIME 1l9���tEXtComment�̖���RIDATc<s ^U߿cBDab `4>z(BpipPc� � |����IENDB`�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/_static/pygments.css�����������������������������������������������������0000664�0001750�0001750�00000007534�12311602524�020466� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.highlight .hll { background-color: #ffffcc } .highlight { background: #eeffcc; } .highlight .c { color: #408090; font-style: italic } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { color: #007020; font-weight: bold } /* Keyword */ .highlight .o { color: #666666 } /* Operator */ .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #007020 } /* Comment.Preproc */ .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #333333 } /* Generic.Output */ .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0044DD } /* Generic.Traceback */ .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #007020 } /* Keyword.Pseudo */ .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #902000 } /* Keyword.Type */ .highlight .m { color: #208050 } /* Literal.Number */ .highlight .s { color: #4070a0 } /* Literal.String */ .highlight .na { color: #4070a0 } /* Name.Attribute */ .highlight .nb { color: #007020 } /* Name.Builtin */ .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ .highlight .no { color: #60add5 } /* Name.Constant */ .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #007020 } /* Name.Exception */ .highlight .nf { color: #06287e } /* Name.Function */ .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #bb60d5 } /* Name.Variable */ .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #208050 } /* Literal.Number.Float */ .highlight .mh { color: #208050 } /* Literal.Number.Hex */ .highlight .mi { color: #208050 } /* Literal.Number.Integer */ .highlight .mo { color: #208050 } /* Literal.Number.Oct */ .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ .highlight .sc { color: #4070a0 } /* Literal.String.Char */ .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ .highlight .sx { color: #c65d09 } /* Literal.String.Other */ .highlight .sr { color: #235388 } /* Literal.String.Regex */ .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ .highlight .ss { color: #517918 } /* Literal.String.Symbol */ .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */��������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/docs/index.rst����������������������������������������������������������������0000664�0001750�0001750�00000001024�12311602524�016305� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.. pynag documentation master file, created by sphinx-quickstart on Sun Mar 16 17:21:51 2014. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pynag's documentation! ================================= :Release: |version| :Date: |today| This document is under a `Creative Commons Attribution - Non-Commercial - Share Alike 2.5 <http://creativecommons.org/licenses/by-nc-sa/2.5/>`_ license. .. toctree:: :maxdepth: 3 introduction pynag ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/CHANGES�����������������������������������������������������������������������0000664�0001750�0001750�00000035233�12370025075�014525� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Changes in 0.9.1 (August 5 2014) Bug Fixes: - Fixes file_time bug in Utils.CheckResult Changes in 0.9.0 (July 23 2014) New Features: - new method: Model.*.rename() - new class: Utils.CheckResult, add checkresults http://docs.pynag.org/en/latest/pynag.Utils.html#pynag.Utils.CheckResult - new importer module, currently supports csv files http://docs.pynag.org/en/latest/pynag.Utils.html#module-pynag.Utils.importer - better docstrings, reflected on http://docs.pynag.org Bug Fixes: - Bug fixes in Model.*.get_current_status() - Fix Parsers.Livestatus.test() traceback - Fix Parsers line continuation - Fix contactgroup/service and contactgroup/host relationships Changes in 0.8.9 (Apr 15 2014) Bug Fixes: - Deploy gzipped version of manpage with setup.py Changes in 0.8.8 (Apr 15 2014) Bug Fixes: - MANIFEST fixes in the build process New Features: - Utils.misc.FakeNagiosEnvironment() - Parsers.MultiSite now has backend aware get_host, get_contact, etc - Incremental updates to unit tests Changes in 0.8.7 (Apr 14 2014) Bug Fixes: - Ship a gzipped version of manpage with release Changes in 0.8.6 (Apr 14 2014) Bug Fixes: - manpage is now delivered with the tarball (not generated on build itme) - Docstring updates New Features: - Support for systemd init scripts - New Module Parsers.MultiSite - All classes in Parsers are now new-style Changes in 0.8.5 (Apr 8 2014) Bug Fixes: - Fix broken build due to manpage requirements Changes in 0.8.4 (Apr 8 2014) New Features: - get_effective_* is now faster - New decorator Utils.cache_only() - New method Service.merge_with_host() Bug Fixes: - Fix hostgroup/service relationships Changes in 0.8.3 (Mar 24 2014) New Features: - Utils.runCommand() supports new env parameter Bug Fixes: - Parsers.LogFiles() now orders archivelogs by last modified time Changes in 0.8.2 (Mar 21 2014) New Features: - Utils.AttributeList() - now aware of "null" values - Notable performance tweaks in parsing - Experimental Config Parser over ssh - New EventHandler: NagiosReloadHandler - Better shinken compatiblity - New method: guess_nagios_binary() - Major improvements in grep_to_livestatus Bug Fixes: - Code is more python3 compatible - Performance tweaks in external commands - Fixed Macro resolving for hosts - Bugfixes in "Pynag livestatus" subcommand Changes in 0.8.1 (Feb 19 2014) New Features: - Support setting of custom host variables - Allow Model.*.acknowledge() with a timestamp Bug Fixes: - Better support for weird mk-livestatus configs - Fix unintilized variables in pynag.Parsers.config Changes in 0.8.0 (Jan 15 2014) New Features: - Auto guess config locations for icinga and naemon - Performance improvements in Model - send_nsca moved to pynag.Utils - Model.filter() now uses Utils.grep() internally - Utils.runCommand() now supports shell=False Bug Fixes: - fix host.delete() not deleting services - Fixes for __in __notin and __contains in Utils.grep() - Bug fixes in send_nsca Changes in 0.7.0 (Oct 14 2013) New Features: - All plugins now have a --timeout=58 seconds - New option to plugins: --extra-opts - More thread safety in Model - New method: ObjectDefinition.move() to move definition to a new file - Plugins new threshold syntax can now fail back on the old syntax Bug Fixes: - python 2.4 compatibility in unittests - Removed deprecated overload of Contact.get_description() Changes in 0.6.1 (Aug 30 2013) Bug Fixes: - Disable unittesting on rhel5 (stops build because of unittest2) Changes in 0.6.0 (Aug 28 2013) New Features: - Support line continuations with \ at end of line in config files - Fix traceback in ParserError initilization - Better error messages when invalid livestatus syntax has been used - Utils.grep() new keywords __notstartswith and __notendswith - Model.ObjectDefinition now supports "in" operator - Multiple performance tweaks for parsing - Plugins.PluginHelper.add_option() wrapper created - Performance tweaks in Utils.grep() - Thread-safety in Parsing/Saving methods in Model and Parser - "in" operator implemented for Object Definitions - delete() methods now more clever for {host,service,contact}s and groups - Incremental performance updates on parsing - New method: ObjectDefinition.attribute_is_empty() - Massive refactor/rewrites in parsing logic Bug Fixes: - Fix traceback on search when searching for shortname in invalid configs - pynag.Utils.GitRepo better handles file renames now - pynag.Plugins.PluginHelper - no more crashing if parse_arguments was not called - Fix parsing when multiple objects have conflicting names - Support relative paths for cfg_dir and cfg_file Changes in 0.5.0 (Jun 26 2013) New Features: - pynag cli - new action "unset" to remove attributes when updating - PluginHelper - New option --pnp-compatible-perfdata - TCP Support for mk-livestatus - ObjectDefinitions now have new method: set_macro() - Vast improvements to unit testing - Fix unhandled tracebacks if invalid templates are defined - Utils.GitRepo has new method: show() - Vast performance improvement in parsing - ObjectDefinition now iterable - New Utility Class pynag.Utils.PluginOutput - ObjectDefinition: magic fields like shortname are now part of keys() - mk_livestatus new utility methods: get_servicegroups,hostgroups,etc Noteworthy bug fixes: - tracis-ci and coveralls.io support - Macroresolving when ARG1 contains other macros - get_suggested_filename() no longer returns filenames with invalid characters - check_range(): Always return True on invalid range_format - check_range(): raise exception on invalid input thresholds Changes in 0.4.9 (Apr 30 2013) New Features: - pynag cli now has livestatus subcommand - pynag cli multiple changes to make it more friendly to bash scripts - --quiet option - --seperator option - pynag cli has acknowledge and downtime subcommands - pynag.Model.*.delete() now are more context aware and remove references from other objects - More features in pynag.Utils.GitRepo - Parser for retention.dat created - pynag.utils.grep_to_livestatus() to convert from pynag style filters to livestatus style filters - add_to_contactgroup() created for pynag.Model.Services, Hosts, and Contacts - New functions pynag.Utils.grep(). Model filter() will use this in the future. - pynag.Model - object definitions now implement sort() - pynag.Parsers.Logfiles added to parse nagios log files Bugfixes: - Multiple doctest and unittest fixes and improvements to code quality - send_nsca - revert back to tabs instead of spaces - Fix unhandled traceback with pynag copy - Fix bug where directories with ending .cfg were parsed as files - PluginHelper fixed in accordance to http://nagiosplugins.org/rfc/new_threshold_syntax Changes in 0.4.8 (Dec 10 2012) New Features: - MK Livestatus integration in pynag.Parsers.mk_livestatus - Testing framework for plugins in scripts/plugintest - Control.Command now uses livestatus if available. - pynag.Plugins.PluginHelper() prototype - Support for Nagios plugins new threshold format Bugfixes: - New Service() objects with no host_name are not saved in None/service_description anymore. - GitEventHandler could not list files with spaces - pynag.Plugins.simple.check_range was very broken, fixed and added testing framework for pynag.Plugins - Fix for correctly returning exit codes in Control.daemon() - Static file parser now skips line not in format of key=value - pynag.Parsers.status.get_hoststatus() and pynag.Parsers.status.get_servicestatus() should raise a ValueError rather than return it. - timeperiod_name and alias were incorrectly saved - Unhandled exception in contact.get_effective_services() Changes in 0.4.7 (Oct 26 2012) New Features: - pynag cli: new parameter --debug - pynag cli: new parameter --version - Parsers.config.get_cfg_value() implemented - Parsers.config.get_pid() implemented - New Module: pynag.Control.Command for interactions with command_pipe - Parsers.config.get_pid() implemented Bugfixes: - Discrepancy in CLI where filters between list and delete - CLI now prints nice error message instead of stacktrace on errors - ObjectFetcher.filter(): parameters ending in __isnot were always treated as an OR condition. - "pynag execute" now works with services that define hostgroups instead of host_name - Fixed macro resolving issues for services with hostgroups defined instead of host_name - Fixed Default initilization of ObjectRelations.hostgroup_subgroups - Bugfix: Nagios needs_reload() always returns false - Bugfix: pynag.Parsers.config.needs_reload() always returns false - Bugfix: Inverted conditions on pynag.Plugins.simple.check_range() - Bugfix: Unhandled exception in pynag.Plugins.simple where must_thresholds=False Changes in 0.4.6 (Sep 16 2012) New Features: - CLI Improvements - manpage for pynag CLI updated - New module Utils created - Experimental new get_status() for ObjectDefinition - Reworked GitEventHandler - new function, Control.daemon.status() - Experimental Perfdata analysis support Bugfixes: - Various bugfixes for get_effective_*groups() - Various bugfixes in Parsers.config._edit_static_file() - needs_reload() now compares timestamps with object_cache_file instead of lockfile - Removed unneeded error handling from Control.daemon class - Model filter, bugfix in __isnot= parameter Changes in 0.4.5 (Aug 21 2012) Bugfixes: - popen2 import moved to send_nsca() to hide deprecationwarning - pynag.Model no longer depends on defaultdict to work (for python 2.4) - Removed dependency on defaultdict (does not exist on python 2.4) - When permission problems occur for reading status.dat, exception is now thrown in parse() rather than init() - Version number is now correct - debian subdir moved to debian.upstream for packaging Changes in 0.4.4 (Aug 20 2012) New Features: - Host.copy(recursive=True) now recursively copies services - README moved to markdown format (palli@opensource.is) - Parsers.status class reworked. Now support get_servicestatus() - filter() now supports __exists suffix for as a search condition - Major rework og object relations and object cache (palli@opensource.is) Bugfixes: - get_all_macros returns empty hash map on non-existant check_command - New get_effective_* functions i.e. Host.get_effective_contacts() - ObjectDefinition.get_id() changed from md5sum to built-in __hash__() - Fixes to get_effective_command_line() where macro within another macro was not properly solved. (palli@opensource.is) - GitEventHandler changed to use subprocess module instead of GitPython - Support for nagios's ambigious Timeperiod format. - check_thresholds() added (Issue 22) (palli@opensource.is) - check_range() conditions now inverted (Fixes issue 22) - /usr/local/nagios/etc/ added to paths where nagios.cfg might be found (Thanks Abhinav Upadhyay) - pycharm code inspection cleanup (palli@opensource.is) Changes in 0.4.3 (Jul 13 2012) Bugfixes: - Model: Objectdefinition.delete() cascade parameter renamed to recursive. Recursive implemented for host. - Various fixes where nagios.cfg is not located in /etc/nagios/ - Improvements to build process - Parsers: Exception handling loosened to not catch kill signals - Model: calling filter() with __has_field now works on items seperated by ", " Changes in 0.4.2 New features: - new command-line tool pynag introduced (deprecated pynag-maincfg, etc). pynag utility offers an easy way to create/update/delete configuration objects from the command line. (palli@opensource.is) - Model: ObjectDefinition.attributes are now defined according to documentation, no longer dynamically created in order to ensure consistency (palli@opensource.is) - Model: run_check_command() available for hosts and services (palli@opensource.is) - Parsers: Working with comma seperated attribute values now easier via new AttributeList class (palli@opensource.is) - Model: copy() feature added to ObjectDefinitions. (palli@opensource.is) Bugs fixed: - Model, ObjectDefinition.copy() now returns a copy of the newly created object - Parsers, Appending cfg_dir to nagios maincfg and pathnames dont match exactly what is in config before (for example double slashes would cause a duplicate) - Parsers: Cache is no longer reloaded as often (improves performance) (palli@opensource.is) - Parsers: Properly raise ParserError if unexpected '}' is encountered while Parsing (palli@opensource.is) - Parsers: ParserError class now has cleaner more detailed output (palli@opensource.is) - Model: get_all_macros() now returns custom macros (palli@opensource.is) - Fixed key errors where data is not present (tommi@tommi.org) - Parsers: improvements to reload_cache() to fix memory leak in save() - Code cleanup ala Eclipse python analyser. Unused variables etc removed. Changes in 0.4.1 Plugins Fix to check_range which now support multiple status lines Modifications for python-2.3 support Scripts Added pynag-maincfg which allows for editing nagios.cfg Added pynag-addservice for easy addition of services Model Ability to set filename for new objects save() now works on when changing 'use' Modifications for python-2.3 support Parsers typo fixed in edit_service Memory leak fix Added servicedependency and hostdependency support EventHandlers Added GitEventHandler which automaticly commits to git Changes in 0.4.0 Features added: - Model, New module that turns configuration into python objects - Parsers, major refactoring Changes in 0.3.1 Bugs Fixed: - Plugin, Fixed joinallstr, wasn't being added between severity levels. Features added: - Plugin, Add must_threshold variable for programs that do not need classic -w or -c - Plugin, allow add_arg to be specified multiple times if multiple=True specified - Plugin, removed space character between severity and colon, "OK:" instead of "OK :" Changes in 0.3 Bugs Fixed: Features added: - Plugin add_messages() and check_messages() - new build system for easier RPM creation Misc Changes: New Contributors: Tomas Ewdardsson, Pall Sigurdsson ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/AUTHORS�����������������������������������������������������������������������0000664�0001750�0001750�00000003052�12310325103�014561� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Core maintainers: Pall Sigurdsson <pall.sigurdsson@gmail.com> Pall Valmundsson <pall.valmundsson@gmail.com> Tomas Edwardsson <tommi@tommi.org> Original author: Drew Stinnett <drew@drewlink.com> Contributors by year, sorted by number of patches. Contributors in 2013: 192 Pall Sigurdsson <palli@opensource.is> 88 Pall Valmundsson <pall.valmundsson@gmail.com> 26 Pall Sigurdsson <palli-github@minor.is> 25 Tomas Edwardsson <tommi@tommi.org> 14 gradecke <gerdradecke@gmx.de> 3 refik <refik.rfk@gmail.com> 2 palli <palli-github@minor.is> 2 Gerd Radecke <gerdradecke@gmx.de> 1 Olivier Bornet <Olivier.Bornet@idiap.ch> 1 Michael Sverdlik <mhsver@gmail.com> 1 Daniel Kimsey <dekimsey@ufl.edu> 1 bene <b.von.st.vieth@fz-juelich.de> Contributors in 2012: 216 Pall Sigurdsson <palli@opensource.is> 80 Tomas Edwardsson <tommi@tommi.org> 33 Abhinav Upadhyay <er.abhinav.upadhyay@gmail.com> 4 Pall Valmundsson <pall.valmundsson@gmail.com> 4 palli <palli-github@minor.is> 4 abhinav <er.abhinav.upadhyay@gmail.com> 2 Tomas Edwardsson <tomas.edwardsson@gmail.com> 1 palli-lsh <palli@lsh.is> 1 Christian Eichelmann <ceichelmann@gmx.de> 1 Abhinav Upadhyay <abhinav@abhinav.(none)> Contributors in 2011: 81 Pall Sigurdsson <pall.sigurdsson@gmail.com> 29 Tomas Edwardsson <tomas.edwardsson@gmail.com> 1 Drew Stinnet <drew@drewlink.com> Contributors in 2008: 49 Drew Stinnet <drew@drewlink.com> 1 (no author) <(no author)> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pynag-0.9.1+dfsg.orig/PKG-INFO����������������������������������������������������������������������0000664�0001750�0001750�00000000672�12370025176�014630� 0����������������������������������������������������������������������������������������������������ustar �clint���������������������������clint������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Metadata-Version: 1.1 Name: pynag Version: 0.9.1 Summary: Python modules for Nagios plugins and configuration Home-page: http://pynag.org/ Author: Drew Stinnett Author-email: drew@drewlink.com License: GPLv2 Description: Python modules and utilities for pragmatically handling Nagios configuration file maintenance, status information, log file parsing and plug-in development. Platform: UNKNOWN Requires: unittest2 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������