pax_global_header00006660000000000000000000000064131140062400014501gustar00rootroot0000000000000052 comment=05fdb2e97acb9b2f17e458b43174d458858e9202 zabbix-cli-1.7.0/000077500000000000000000000000001311400624000135325ustar00rootroot00000000000000zabbix-cli-1.7.0/LICENSE000066400000000000000000001044621311400624000145460ustar00rootroot00000000000000GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {one line to give the program's name and a brief idea of what it does.} Copyright (C) {year} {name of author} This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: {project} Copyright (C) {year} {fullname} This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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. But first, please read . zabbix-cli-1.7.0/README.md000066400000000000000000000011521311400624000150100ustar00rootroot00000000000000zabbix-cli ========== Command-line interface for the Zabbix monitoring system Zabbix-cli is a terminal client for managing some Zabbix administration tasks via the zabbix-API. The zabbix-cli code is distributed under the GNU General Public License 3 and it is written in Python. It has been developed and tested by members of the Department for IT Infrastructure at [the Center for Information Technology](https://www.usit.uio.no) at [the University of Oslo, Norway](https://www.uio.no/). The project home page is on [GitHub](https://github.com/usit-gd/zabbix-cli). Please report any issues or improvements there. zabbix-cli-1.7.0/bin/000077500000000000000000000000001311400624000143025ustar00rootroot00000000000000zabbix-cli-1.7.0/bin/zabbix-cli000077500000000000000000000273541311400624000162670ustar00rootroot00000000000000#!/usr/bin/env python # # Authors: # rafael@postgresql.org.es / http://www.postgresql.org.es/ # # Copyright (c) 2014-2015 USIT-University of Oslo # # This file is part of Zabbix-cli # https://github.com/rafaelma/zabbix-cli # # Zabbix-CLI 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. # # Zabbix-CLI 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 Zabbix-CLI. If not, see . import sys import os import getpass import argparse from zabbix_cli.config import * from zabbix_cli.logs import * from zabbix_cli.cli import * if __name__ == '__main__': try: # # Processing command line parameters # output_format = '' config_file = '' zabbix_command = '' input_file = '' parser = argparse.ArgumentParser(prog=sys.argv[0],description='zabbix-cli - Zabbix client') parser.add_argument('--output','-o', metavar='[csv|json|table]', choices=['csv','json','table'], required=False, dest='output_format') parser.add_argument('--config','-c', metavar='', required=False, dest='config_file') parser.add_argument('--command','-C', metavar='',required=False,dest='zabbix_command') parser.add_argument('--file','-f', metavar='',required=False,dest='input_file') args = parser.parse_args() if args.output_format: output_format = args.output_format if args.config_file: config_file = args.config_file if args.zabbix_command: zabbix_command = args.zabbix_command if args.input_file: input_file = args.input_file conf = configuration(config_file) # # If logging is activated, start logging to the file defined # with log_file in the config file. # if conf.logging == 'ON': logs = log("zabbix-cli",config_file) else: logs = None if conf.logging == 'ON': logs.logger.debug('**** Zabbix-CLI startet. ****') # # Non-interactive authentication procedure # # If the file .zabbix_cli_auth exists at $HOME, use the # information in this file to authenticate into Zabbix API # # Format: # :: # # Use .zabbix-cli_auth_token if it exists and .zabbix_cli_auth # does not exist. # # Format: # :: # auth_token = '' username = '' password = '' zabbix_auth_file = '' zabbix_auth_token_file = '' if os.getenv('HOME') is not None: zabbix_auth_file = os.getenv('HOME') + '/.zabbix-cli_auth' zabbix_auth_token_file = os.getenv('HOME') + '/.zabbix-cli_auth_token' else: print '\n[ERROR]: The $HOME environment variable is not defined. Zabbix-CLI cannot read ~/.zabbix-cli_auth or ~/.zabbix-cli_auth_token' if conf.logging == 'ON': logs.logger.error('The $HOME environment variable is not defined. Zabbix-CLI cannot read ~/.zabbix-cli_auth or ~/.zabbix-cli_auth_token') sys.exit(1) env_username = os.getenv('ZABBIX_USERNAME') env_password = os.getenv('ZABBIX_PASSWORD') if env_username is not None and env_password is not None: username = env_username password = env_password if conf.logging == 'ON': logs.logger.info('Environment variables ZABBIX_USERNAME and ZABBIX_PASSWORD exist. Using these variables to get authentication information') elif os.path.isfile(zabbix_auth_file): try: os.chmod(zabbix_auth_file,0400) with open(zabbix_auth_file,'r') as file: for line in file: (username, password) = line.split('::') password = password.replace('\n','') if conf.logging == 'ON': logs.logger.info('File %s exists. Using this file to get authentication information',zabbix_auth_file) except Exception as e: print '\n[ERROR]: %s\n',e if conf.logging == 'ON': logs.logger.error('Problems using file %s - %s',zabbix_auth_file,e) elif os.path.isfile(zabbix_auth_token_file): try: os.chmod(zabbix_auth_token_file,0600) with open(zabbix_auth_token_file,'r') as file: for line in file: (username, auth_token) = line.split('::') if conf.logging == 'ON': logs.logger.info('File %s exists. Using this file to get authentication token information',zabbix_auth_token_file) except Exception as e: print '\n[ERROR]: %s\n',e if conf.logging == 'ON': logs.logger.error('Problems using file %s - %s',zabbix_auth_token_file,e) # # Interactive authentication procedure # else: default_user = getpass.getuser() print '-------------------------' print 'Zabbix-CLI authentication' print '-------------------------' try: username = raw_input('# Username[' + default_user +']: ') password = getpass.getpass('# Password: ') except Exception as e: print '\n[Aborted]' sys.exit(0) if username == '': username = default_user # # Check that username and password have some values if the # API-auth-token is empty ($HOME/.zabbix-cli_auth_token does # not exist) # if auth_token == '': if username == '' or password == '': print '\n[ERROR]: Username or password is empty\n' if conf.logging == 'ON': logs.logger.error('Username or password is empty') sys.exit(1) if conf.logging == 'ON': # # Activate the username information in the log # name as soon as we have this information # logs.formatter = logging.Formatter("%(asctime)s [%(name)s][" + username + "][%(process)d][%(levelname)s]: %(message)s") logs.fh.setFormatter(logs.formatter) logs.logger.addHandler(logs.fh) # # Zabbix-CLI in interactive modus # if zabbix_command == '' and input_file == '': if conf.logging == 'ON': logs.logger.debug('Zabbix-CLI running in interactive modus') os.system('clear') cli = zabbixcli(logs,conf,username,password,auth_token) cli.cmdloop() # # Zabbix-CLI in bulk execution modus. # # This mode is activated when we run zabbix-cli with the # parameter -f to define a file with zabbix-cli commands. # elif zabbix_command == '' and input_file != '': cli = zabbixcli(logs,conf,username,password,auth_token) # Normalized absolutized version of the pathname if # files does not include an absolute path if os.path.isabs(input_file) == False: input_file = os.path.abspath(input_file) if os.path.exists(input_file): if conf.logging == 'ON': logs.logger.info('File [%s] exists. Bulk execution of commands defined in this file.',input_file) print '[OK] File [' + input_file + '] exists. Bulk execution of commands defined in this file started.' # # Register that this is a bulk execution via -f # parameter. This will activate some performance # improvements to boost bulk execution. # cli.bulk_execution = True # # Processing zabbix commands in file. # # Empty lines or comment lines (started with #) will # not be considered. try: with open(input_file,'r') as file: for input_line in file: if input_line.find('#',0) == -1 and input_line.strip() != '': zabbix_cli_command = input_line.strip() cli.onecmd(zabbix_cli_command) if conf.logging == 'ON': logs.logger.info('Zabbix-cli command [%s] executed via input file',zabbix_cli_command) except Exception as e: if conf.logging == 'ON': logs.logger.error('Problems using input file [%s] - %s',input_file,e) print '[ERROR] Problems using input file [' + input_file + '] - ' + str(e) sys.exit(1) else: if conf.logging == 'ON': logs.logger.info('Input file [%s] does not exist. Bulk execution of commands aborted.',input_file) print '[ERROR] Input file [' + input_file + '] does not exist. Bulk execution of commands aborted' # # Zabbix-CLI in non-interactive modus(command line) # elif zabbix_command != '': if conf.logging == 'ON': logs.logger.debug('Zabbix-CLI running in non-interactive modus') # CSV format output if output_format == 'csv': cli = zabbixcli(logs,conf,username,password,auth_token) cli.output_format = 'csv' cli.non_interactive = True cli.onecmd(zabbix_command) # JSON format output elif output_format == 'json': cli = zabbixcli(logs,conf,username,password,auth_token) cli.output_format = 'json' cli.non_interactive = True cli.onecmd(zabbix_command) # Table format output else: cli = zabbixcli(logs,conf,username,password,auth_token) cli.output_format = 'table' cli.non_interactive = True cli.onecmd(zabbix_command) else: raise NotImplementedError if conf.logging == 'ON': logs.logger.debug('**** Zabbix-CLI stopped. ****') except KeyboardInterrupt: print print "\nDone, thank you for using Zabbix-CLI" if conf.logging == 'ON': logs.logger.debug('**** Zabbix-CLI stopped. ****') sys.exit(0) except Exception as e: print '\n[ERROR]: %s\n',e zabbix-cli-1.7.0/bin/zabbix-cli-bulk-execution000077500000000000000000000112041311400624000212060ustar00rootroot00000000000000#!/usr/bin/env python # # Authors: # rafael@postgresql.org.es / http://www.postgresql.org.es/ # # Copyright (c) 2014-2015 USIT-University of Oslo # # This file is part of Zabbix-cli # https://github.com/rafaelma/zabbix-cli # # Zabbix-CLI 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. # # Zabbix-CLI 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 Zabbix-CLI. If not, see . import sys import os import argparse import subprocess from zabbix_cli.config import * from zabbix_cli.logs import * if __name__ == '__main__': try: # # Process command line parameters # config_file = '' input_file = '' parser = argparse.ArgumentParser(prog=sys.argv[0]) parser.add_argument('--input-file', '-f', metavar='[Filename]', required=True, help='Input file', dest='input_file') parser.add_argument('--config','-c', metavar='', required=False, dest='config_file') args = parser.parse_args() input_file = args.input_file if args.config_file: config_file = args.config_file conf = configuration(config_file) # # If logging is activated, start logging to the file defined # with log_file in the config file. # if conf.logging == 'ON': logs = log("zabbix-cli-bulk-execution",config_file) else: logs = None if conf.logging == 'ON': logs.logger.debug('**** Zabbix-cli-bulk-execution startet. ****') # Normalized absolutized version of the pathname if # files does not include an absolute path if os.path.isabs(input_file) == False: input_file = os.path.abspath(input_file) if os.path.exists(input_file): if conf.logging == 'ON': logs.logger.info('File [%s] exists. Bulk execution of commands defined in this file.',input_file) print '[OK] File [' + input_file + '] exists. Bulk execution of commands defined in this file started.' # # Processing zabbix commands in file # try: with open(input_file,'r') as file: for line in file: zabbix_cli_command = line.strip() command = 'zabbix-cli -o json -C "' + zabbix_cli_command + '"' DEVNULL = open(os.devnull, 'w') proc = subprocess.Popen([command],stdout=DEVNULL,stderr=DEVNULL,shell=True) proc.wait() if proc.returncode == 0: if conf.logging == 'ON': logs.logger.info('Zabbix-cli command [%s] executed',command) print '[OK] Zabbix-cli command [' + command + '] executed' else: if conf.logging == 'ON': logs.logger.error('Zabbix-cli command [%s] could not be executed',command) print '[ERROR] Zabbix-cli command [' + command + '] could not bed executed' except Exception as e: if conf.logging == 'ON': logs.logger.error('Problems using file [%s] - %s',input_file,e) print '[ERROR] Problems using file [' + input_file + '] - ' + str(e) sys.exit(1) else: if conf.logging == 'ON': logs.logger.info('File [%s] does not exist. Bulk execution of commands aborted.',input_file) print '[ERROR] File [' + input_file + '] does not exist. Bulk execution of commands aborted' if conf.logging == 'ON': logs.logger.debug('**** Zabbix-cli-bulk-execution finished. ****') except Exception as e: print '\n[ERROR]: %s\n',e if conf.logging == 'ON': logs.logger.error('Problems running zabbix-cli-bulk-execution - %s',e) print 'Problems running zabbix-cli-bulk-execution - ' + str(e) sys.exit(1) zabbix-cli-1.7.0/bin/zabbix-cli-init000077500000000000000000000064511311400624000172230ustar00rootroot00000000000000#!/usr/bin/env python # # Authors: # Mustafa Ocak # muo@uio.no # # Copyright (c) 2015 USIT-University of Oslo # # This script initialize zabbix-cli environment. it will copy # /etc/zabbix-cli/zabbix-cli.conf to $HOME/.zabbix-cli/ and change log # configuration in zabbix-cli.conf so that zabbix-cli logs to # $HOME/.zabbix-cli/ # ########################################################################################### from os import getenv,path,remove,close,makedirs from tempfile import mkstemp from shutil import move, copy2 import sys import os import argparse def replace(file_path, pattern, subst): # # changing the line having log configuration option # replacing # log_file=/var/log/zabbix-cli/zabbix-cli.log # to log_file=~/.zabbix-cli/zabbix-cli.log try: #Create temp file fh, abs_path = mkstemp() with open(abs_path,'w') as new_file: with open(file_path) as old_file: for line in old_file: new_file.write(line.replace(pattern, subst)) close(fh) #Remove original file remove(file_path) #Move new file move(abs_path, file_path) except Exception as e: print '\n[ERROR]: %s\n',e if __name__ == "__main__": if os.getenv('HOME') is not None: zabbixconfdir = getenv('HOME')+"/.zabbix-cli/" else: print '\n[ERROR]: The $HOME environment variable is not defined. zabbix-cli-init cannot generate the configuration file in your home directory.' sys.exit(1) defconf="/usr/share/zabbix-cli/zabbix-cli.conf" filename="zabbix-cli.conf" file_path=path.join(zabbixconfdir,filename) try: parser = argparse.ArgumentParser(prog=sys.argv[0],description='zabbix-cli-init - Zabbix-cli initialization program') parser.add_argument('--zabbix-url','-z', metavar='', required=True, dest='zabbix_api_url') args = parser.parse_args() if args.zabbix_api_url: zabbix_api_url = args.zabbix_api_url except Exception as e: print e sys.exit(1) # # creating ~/.zabbix-cli folder if not exists # if not path.exists(zabbixconfdir): try: makedirs(zabbixconfdir) except Exception as e: print '\n[ERROR]: %s\n',e # # /etc/zabbix-cli.conf will be created under installation of # zabbix-cli package. copying /etc/zabbix-cli.conf file to # ~/.zabbix-cli/zabbix-cli.conf # if path.isfile(defconf): try: copy2(defconf,zabbixconfdir) except Exception as e: print '\n[ERROR]: %s\n',e else: print "Cannot find /etc/zabbix-cli/zabbix-cli.conf file. ERROR with zabbix-cli installation." sys.exit(1) # # changing the line having log configuration option # replacing log file option # log_file=/var/log/zabbix-cli/zabbix-cli.log # to log_file=~/.zabbix-cli/zabbix-cli.log # replace(file_path,'log_file=/var/log/zabbix-cli/zabbix-cli.log','log_file=' + getenv('HOME') + '/.zabbix-cli/zabbix-cli.log') # # Changing the line having the zabbix API url example with real # URL. # replace(file_path,';zabbix_api_url=https://zabbix.example.net/zabbix','zabbix_api_url=' + zabbix_api_url) zabbix-cli-1.7.0/debian/000077500000000000000000000000001311400624000147545ustar00rootroot00000000000000zabbix-cli-1.7.0/debian/changelog000066400000000000000000000004701311400624000166270ustar00rootroot00000000000000zabbix-cli (1.7.0-1) unstable; urgency=low * New release 1.7.0 -- Petter Reinholdtsen Thu, 01 Jun 2017 12:02:45 +0200 zabbix-cli (1.6.1-1) unstable; urgency=low * Initial upload to Debian (Closes: #842337): -- Petter Reinholdtsen Mon, 17 Oct 2016 11:41:24 +0000 zabbix-cli-1.7.0/debian/compat000066400000000000000000000000021311400624000161520ustar00rootroot000000000000009 zabbix-cli-1.7.0/debian/control000066400000000000000000000013521311400624000163600ustar00rootroot00000000000000Source: zabbix-cli Section: misc Priority: optional Maintainer: Rafael Martinez Guerrero Uploaders: Petter Reinholdtsen Build-Depends: debhelper (>= 9~) , python | python-all | python-dev | python-all-dev , python-setuptools Standards-Version: 3.9.8 Homepage: https://github.com/usit-gd/zabbix-cli Vcs-Browser: https://github.com/usit-gd/zabbix-cli Vcs-Git: https://github.com/usit-gd/zabbix-cli X-Python-Version: >= 2.6 Package: zabbix-cli Architecture: all Depends: ${misc:Depends} , ${python:Depends} , python-requests , python-ipaddr Description: Command-line interface for Zabbix monitoring system Zabbix-cli is a terminal client for managing some Zabbix administration tasks via the zabbix-API. zabbix-cli-1.7.0/debian/copyright000066400000000000000000000066101311400624000167120ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0 Files: * Copyright: 2014-2016 The University of Oslo License: GPL-3.0+ Files: zabbix_cli/prettytable.py Copyright: 2009-2013, Luke Maurits License: BSD-3-Clause Files: zabbix_cli/pyzabbix.py Copyright: 2016 Luke Cyca License: LGPL-2.1 License: GPL-3.0+ 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 3 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 3 can be found in "/usr/share/common-licenses/GPL-3". License: BSD-3-Clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. License: LGPL-2.1 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. . This library 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 Lesser General Public License for more details. . You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the complete text of the GNU General Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". zabbix-cli-1.7.0/debian/rules000077500000000000000000000000541311400624000160330ustar00rootroot00000000000000#!/usr/bin/make -f %: dh $@ --with python2 zabbix-cli-1.7.0/debian/watch000066400000000000000000000003551311400624000160100ustar00rootroot00000000000000version=3 opts="repacksuffix=+dfsg,dversionmangle=s/\+dfsg//g,filenamemangle=s/(?:.*?)?v?(\d[\d.]*)\.tar\.gz/zabbix-cli-$1.tar.gz/" \ https://github.com/usit-gd/zabbix-cli/releases .*/archive/v?(\d[\d.-]+)\.(?:tar(?:\.gz|\.bz2)?|tgz) zabbix-cli-1.7.0/docs/000077500000000000000000000000001311400624000144625ustar00rootroot00000000000000zabbix-cli-1.7.0/docs/Makefile000066400000000000000000000007721311400624000161300ustar00rootroot00000000000000# # Makefile # VERSION="1.7.0" all: html man pdf html: rst2html -gdt --stylesheet-path=style.css manual.rst > zabbix-cli-manual-$(VERSION).html man: rst2man manual.rst > zabbix-cli-manual-$(VERSION).man pdf: cat manual.rst | sed s/":scale: 50%"/":scale: 100%"/g > manual.tmp rst2pdf --output zabbix-cli-manual-$(VERSION).pdf manual.tmp rm -f manual.tmp clean: rm -f zabbix-cli-manual-$(VERSION)*.html rm -f zabbix-cli-manual-$(VERSION)*.man rm -f zabbix-cli-manual-$(VERSION)*.pdf rm -f *~ zabbix-cli-1.7.0/docs/check_commands.sh000077500000000000000000000232471311400624000177670ustar00rootroot00000000000000#!/bin/sh ZABBIX_CLI=/usr/bin/zabbix-cli #${ZABBIX_CLI} --use-json-format remove_hostgroup \"AAA-00\" #${ZABBIX_CLI} --use-json-format remove_hostgroup \"AAA-001\" # return: error ${ZABBIX_CLI} --use-json-format remove_host \"aaa.000001\" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 1: OK\n" else echo -e "TEST 1: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format create_hostgroup \"\" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 2: OK\n" else echo -e "TEST 2: ERROR\n" fi # return:ok ${ZABBIX_CLI} --use-json-format create_hostgroup \"AAA-001\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 3: OK\n" else echo -e "TEST 3: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_hostgroup \"AAA-002\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 4: OK\n" else echo -e "TEST 4: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_host \"aaa-001.uio.no\" \"\" \"\" \"\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 5: OK\n" else echo -e "TEST 5: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format remove_host \"aaa-001.uio.no\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 6: OK\n" else echo -e "TEST 6: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_host \"aaa-001.uio.no\" \"AAA-001\" \"\" \"\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 7: OK\n" else echo -e "TEST 7: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format remove_host \"aaa-001.uio.no\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 8: OK\n" else echo -e "TEST 8: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format create_host \"aaa-001.uio.no\" \"AAA-0\" \"\" \"\" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 9: OK\n" else echo -e "TEST 9: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format remove_host \"aaa-001.uio.no\" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 10: OK\n" else echo -e "TEST 10: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_host \"aaa-001.uio.no\" \"AAA-001\" \"*.uio.no\" \"\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 11: OK\n" else echo -e "TEST 11: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format remove_host \"aaa-001.uio.no\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 12: OK\n" else echo -e "TEST 12: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format create_host \"aaa-001.uio.no\" \"AAA-001\" \"*.xxxx\" \"\" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 13: OK\n" else echo -e "TEST 13: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format remove_host \"aaa-001.uio.no\" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 14: OK\n" else echo -e "TEST 14: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_host \"aaa-001.uio.no\" \"AAA-001\" \"*.uio.no\" \"0\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 15: OK\n" else echo -e "TEST 15: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format remove_host \"aaa-001.uio.no\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 16: OK\n" else echo -e "TEST 16: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_host \"aaa-001.uio.no\" \"AAA-001\" \"*.uio.no\" \"1\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 17: OK\n" else echo -e "TEST 17: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format remove_host \"aaa-001.uio.no\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 18: OK\n" else echo -e "TEST 18: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_host \"aaa-001.uio.no\" \"AAA-001\" \"*.uio.no\" \"10\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 19: OK\n" else echo -e "TEST 19: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format remove_host \"aaa-001.uio.no\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 20: OK\n" else echo -e "TEST 20: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_host \"aaa-001.uio.no\" \"AAA-001\" \"\" \"\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 21: OK\n" else echo -e "TEST 21: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_host \"aaa-002.uio.no\" \"AAA-001\" \"\" \"\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 22: OK\n" else echo -e "TEST 22: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format add_host_to_hostgroup \" aaa-001.uio.no , aaa-002.uio.no \" \" AAA-001, AAA-002 \" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 23: OK\n" else echo -e "TEST 23: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format add_host_to_hostgroup \" aaa-001.uio.no , aaa-002.uio.no \" \" AAA-001, AAA-002, AAA-0000000 \" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 24: OK\n" else echo -e "TEST 24: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format add_host_to_hostgroup \" aaa-001.uio.no , aaa-002222222.uio.no \" \"AAA-001, AAA-002 \" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 25: OK\n" else echo -e "TEST 25: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format link_template_to_host \" Template AAA-0 \" \" aaa-001.uio.no , aaa-002.uio.no \" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 26: OK\n" else echo -e "TEST 26: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format link_template_to_host \" Template AAA-001 \" \" aaa-0011111.uio.no , aaa-002.uio.no \" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 27: OK\n" else echo -e "TEST 27: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format link_template_to_host \" Template AAA-001 , Template AAA-002 \" \" aaa-001.uio.no , aaa-002.uio.no \" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 28: OK\n" else echo -e "TEST 28: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format unlink_template_from_host \" Template AAA-0 \" \" aaa-001.uio.no , aaa-002.uio.no \" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 29: OK\n" else echo -e "TEST 29: ERROR\n" fi # return error ${ZABBIX_CLI} --use-json-format unlink_template_from_host \" Template AAA-001 \" \" aaa-0011111.uio.no , aaa-002.uio.no \" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 30: OK\n" else echo -e "TEST 30: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format unlink_template_from_host \" Template AAA-001 , Template AAA-002\" \" aaa-001.uio.no , aaa-002.uio.no \" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 31: OK\n" else echo -e "TEST 31: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format remove_host_from_hostgroup \" aaa-001.uio.no , aaa-002222222.uio.no \" \"AAA-001, AAA-002 \" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 32: OK\n" else echo -e "TEST 32: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format remove_host_from_hostgroup \" aaa-001.uio.no , aaa-002.uio.no \" \" AAA-001, AAA-002, AAA-0000000 \" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 33: OK\n" else echo -e "TEST 33: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format remove_host_from_hostgroup \" aaa-001.uio.no , aaa-002.uio.no \" \" AAA-001, AAA-002 \" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 34: OK\n" else echo -e "TEST 34: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format remove_host \"aaa-001.uio.no\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 35: OK\n" else echo -e "TEST 35: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format remove_host \"aaa-002.uio.no\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 36: OK\n" else echo -e "TEST 36: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format create_usergroup \"\" \"\" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 37: OK\n" else echo -e "TEST 37: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_usergroup \"AAA-usergroup\" \"\" \"\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 38: OK\n" else echo -e "TEST 38: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_usergroup \"BBB-usergroup\" \"0\" \"0\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 39: OK\n" else echo -e "TEST 39: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_user \"AAA-user\" \"AAA\" \"user\" \"\" \"\" \"\" \"\" \"13\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 40: OK\n" else echo -e "TEST 40: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_user \"BBB-user\" \"BBB\" \"user\" \"\" \"\" \"\" \"\" \"13\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 41: OK\n" else echo -e "TEST 41: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format create_user \"AAA-user\" \"AAA\" \"user\" \"\" \"\" \"\" \"\" \"13\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 42: OK\n" else echo -e "TEST 42: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format add_user_to_usergroup \"\" \"AAA-usergroup, BBB-usergroup\" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 43: OK\n" else echo -e "TEST 43: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format add_user_to_usergroup \"AAA-user\" \"\" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 44: OK\n" else echo -e "TEST 44: ERROR\n" fi # return: error ${ZABBIX_CLI} --use-json-format add_user_to_usergroup \"AAA-user\" \"asdsad\" RETVAL=$? if [ $RETVAL -eq 1 ]; then echo -e "TEST 45: OK\n" else echo -e "TEST 45: ERROR\n" fi # return: ok ${ZABBIX_CLI} --use-json-format add_user_to_usergroup \"AAA-user, BBB-user \" \"AAA-usergroup, BBB-usergroup\" RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -e "TEST 46: OK\n" else echo -e "TEST 46: ERROR\n" fi zabbix-cli-1.7.0/docs/manual.rst000066400000000000000000001341041311400624000164740ustar00rootroot00000000000000===================================== Zabbix-CLI ===================================== | | Version-1.7.0 | | Rafael Martinez Guerrero (University of Oslo) | E-mail: rafael@postgresql.org.es | | Source: https://github.com/usit-gd/zabbix-cli | .. contents:: Introduction ============ Zabbix-cli is a terminal client for managing some Zabbix administration tasks via the zabbix-API. The zabbix-cli code is distributed under the GNU General Public License 3 and it is written in Python. It has been developed and tested by members of the Department for IT Infrastructure at the Center for Information Technology at the University of Oslo, Norway. Main features ============= * Terminal client * Two execution modes available: Zabbix-CLI shell and commandline. * 54 zabbix-CLI commands available. * Multilevel configuration system. * Possibility to define Bulk updates. Several performance improvements are used when running in bulk modus. * Authentication-token, authentication-file and environment variables support for autologin. * Support for plain, CSV and JSON output. * Online help * Written in Python. Installation ============ System requirements ------------------- * Linux/Unix * Python 2.6 or 2.7 * Python modules: request ipaddr Before you install Zabbix-CLI you have to install the software needed by this tool In systems using ``yum``, e.g. Centos, RHEL, Fedora:: yum install python-requests python-ipaddr In system using ``apt-get``, e.g. Debian, Ubuntu:: apt-get install python-requests python-ipaddr If you are going to install from source, you need to install also these packages: ``python-dev(el), python-setuptools, git, make, python-docutils`` In systems using ``yum``:: yum install python-devel python-setuptools git make python-docutils In system using ``apt-get``:: apt-get install python-dev python-setuptools git make python-docutils Installing from source ---------------------- The easiest way to install zabbix-cli from source is to download the latest stable release from GitHub https://github.com/usit-gd/zabbix-cli/releases in tar.gz or zip format. You can also clone the official GitHub GIT repository and get the latest code from the master branch. :: [root@server]# cd [root@server]# git clone https://github.com/usit-gd/zabbix-cli.git [root@server]# cd zabbix-cli [root@server]# ./setup.py install ..... **NOTE**: The code in the master branch can be unstable and with bugs between releases. Use it at your own risk. For stable code to be used in production use the source code distributed via the release section: https://github.com/usit-gd/zabbix-cli/releases Installing via RPM packages --------------------------- The University of Oslo has made available an official repository that can be used to install RPM packages via yum. First, you have to create this file ``/etc/yum.repos.d/zabbix-cli.repo``: :: TODO [zabbix-cli] name=Zabbix-CLI Official Repository - $basearch baseurl=http://xxxxxxxxxxx/rhel/6/$basearch/ enabled=1 gpgcheck=1 gpgkey=xxxxxxxxxx And run these commands to install the client: :: # yum update # yum install zabbix-cli Installing via Deb packages ---------------------------- Zabbix-CLI has been accepted into the official Debian package repository (unstable). It is available for Debian and Ubuntu systems. Check https://packages.qa.debian.org/z/zabbix-cli.html for details. :: TODO # echo "" > /etc # apt-get update # apt-get install zabbix-cli Configuration ============= Configuration file ------------------ Zabbix-CLI needs a configuration file to work. Until version 1.5.4 we supported a **singlelevel configuration system** with three possible locations for our configuration file: #. Config file defined with ``--config`` or ``-c`` parameter when starting ``zabbix-cli`` #. ``$HOME/.zabbix-cli/zabbix-cli.conf`` #. ``/etc/zabbix-cli/zabbix-cli.conf`` With the **singlelevel configuration system**, Zabbix-cli checked for a configuration file in these locations and in this order and used the first one that existed. This means that you could always override: 3) with 2) or 1), and 2) with 1). From version 1.6.0, Zabbix-cli has started to use a **multilevel configuration system.** This means thet we do not override entire configuration files but we merge all the defined configuration files in our system and use the parameter values defined in the configuration file with higher priority if a parameter is defined in more than one file. The ordered list with the files with higher on top: #. ``/usr/share/zabbix-cli/zabbix-cli.fixed.conf`` #. ``/etc/zabbix-cli/zabbix-cli.fixed.conf`` #. Configuration file defined with the parameter ``-c`` / ``--config`` when executing zabbix-cli #. ``$HOME/.zabbix-cli/zabbix-cli.conf`` #. ``/etc/zabbix-cli/zabbix-cli.conf`` #. ``/usr/share/zabbix-cli/zabbix-cli.conf`` With this implementation: * Local configuration will be kept during upgrades. * The local configuration is separate from the package defaults. * Several actors will be allow to have their own files. * It is possible to provide package, host and user defaults, as well as locking down features on a host, package level. * Always well known where the admin made his changes A default configuration file can be found in ``/usr/share/zabbix-cli/zabbix-cli.conf`` or ``etc/zabbix-cli.conf`` in the source code. The easiest way to configurate your client will be running this command to create your own ``$HOME/.zabbix-cli/zabbix-cli.conf`` file.:: # zabbix-cli-init The parameter ``zabbix_api_url`` must be defined in the configuration file. Without this parameter, ``zabbix-cli`` will not know where to connect. This parameter will be defined automatically if you have run the command ``zabbix-cli-init``. Remember to activate logging with ``logging=ON`` if you want to activate logging. The user running ``zabbix-cli`` must have read/write access to the log file defined with ``log_file``. This parameter will be defined automatically with an OFF value if you have run the command ``zabbix-cli-init``. From version 1.6.0 we have a new zabbix-cli command that can be used to see all the active configuration files in your system and the configuration parameters that zabbix-cli is using:: [zabbix-cli rafael@zabbix-ID]$ show_zabbixcli_config +----------------------------------------------+ | Active configuration files | +----------------------------------------------+ | */usr/share/zabbix-cli/zabbix-cli.fixed.conf | | */etc/zabbix-cli/zabbix-cli.fixed.conf | | */root/.zabbix-cli/zabbix-cli.conf | | */etc/zabbix-cli/zabbix-cli.conf | | */usr/share/zabbix-cli/zabbix-cli.conf | +----------------------------------------------+ +--------------------------------------+---------------------------------------+ | Configuration parameter | Value | +--------------------------------------+---------------------------------------+ | zabbix_api_url | https://zabbix.example.org | | system_id | zabbix-ID | | default_hostgroup | All-hosts | | default_admin_usergroup | Zabbix-admin | | default_create_user_usergroup | All-users | | default_notification_users_usergroup | All-notification-users | | default_directory_exports | /home/user/zabbix_exports | | default_export_format | XML | | include_timestamp_export_filename | ON | | use_colors | ON | | use_auth_token_file | ON | | logging | ON | | log_level | INFO | | log_file | /home/user/.zabbix-cli/zabbix-cli.log | +--------------------------------------+---------------------------------------+ Environment Authentication -------------------------- You can define the ``ZABBIX_USERNAME`` and ``ZABBIX_PASSWORD`` environment variables to pass authentication credentials to ``zabbix-cli``. For example: :: export ZABBIX_USERNAME=zbxuser read -srp "Zabbix Password: " ZABBIX_PASSWORD; export ZABBIX_PASSWORD; zabbix-cli **NOTE**: It is important to remember that this method will save the password in clear text in a environment variable. This value will be available to other processes running in the same session. Authentication file ------------------- You can define the file ``$HOME/.zabbix-cli_auth`` if you want to avoid to write your username and password everytime you use ``zabbix-cli``. This can be useful if you are running ``zabbix-cli`` in non-interactive modus from scripts or automated jobs. The format of this file is a line with this information:: USERNAME::PASSWORD **NOTE:** The password will be saved in clear text so be carefull with the information saved here and restrict access to this file only to your user. ``chmod 400 ~/.zabbix-cli_auth`` will be defined by ``zabbix-cli`` on this file the first time it uses it. Authentication token file ------------------------- The file ``$HOME/.zabbix-cli_auth_token`` will be created with information about the API-auth-token from the last login if the parameter ``use_auth_token_file=ON`` is defined in the configuration file. The information in this file will be used, if we can, to avoid having to write the username and password everytime you use ``zabbix-cli``. This can be useful if you are running ``zabbix-cli`` in non-interactive modus from scripts or automated jobs. This authentication method will work as long as the API-auth-token saved is active in Zabbix. The ``Auto-logout`` attribute of the user will define how long the API-auth-token will be active. If the API-auth-token is not valid, ``zabbix-cli`` will delete the file ``$HOME/.zabbix-cli_auth_token`` and you will have to login again with a valid username and password. Zabbix-CLI shell ================ The Zabbix-CLI interactive shell can be started by running the program ``/usr/bin/zabbix-cli`` :: [user@host]# zabbix-cli ############################################################# Welcome to the Zabbix command-line interface (v.1.7.0) ############################################################# Type help or \? to list commands. [zabbix-cli rafael@zabbix-ID]$ help Documented commands (type help ): ======================================== EOF shell acknowledge_event show_alarms acknowledge_trigger_last_event show_global_macros add_host_to_hostgroup show_history add_user_to_usergroup show_host add_usergroup_permissions show_host_inventory clear show_host_usermacros create_host show_hostgroup create_host_interface show_hostgroups create_hostgroup show_hosts create_maintenance_definition show_items create_notification_user show_maintenance_definitions create_user show_maintenance_periods create_usergroup show_template define_global_macro show_templates define_host_monitoring_status show_trigger_events define_host_usermacro show_triggers export_configuration show_usergroup import_configuration show_usergroups link_template_to_host show_usermacro_host_list load_balance_proxy_hosts show_usermacro_template_list move_proxy_hosts show_users quit show_zabbixcli_config remove_host unlink_template_from_host remove_host_from_hostgroup update_host_inventory remove_maintenance_definition update_host_proxy remove_user update_usergroup_permissions remove_user_from_usergroup Miscellaneous help topics: ========================== shortcuts support Undocumented commands: ====================== help **NOTE:** It is possible to use Zabbix-CLI in a non-interactive modus by running ``/usr/bin/zabbix-cli`` with the parameter ``--command `` or ``-C `` in the OS shell. This can be used to run ``zabbix-cli`` commands from shell scripts or other programs .e.g. :: [user@host]# zabbix-cli -C "show_usergroups" +---------+---------------------------+--------------------+-------------+ | GroupID | Name | GUI access | Status | +---------+---------------------------+--------------------+-------------+ | 13 | DBA | System default (0) | Enable (0) | | 9 | Disabled | System default (0) | Disable (1) | | 11 | Enabled debug mode | System default (0) | Enable (0) | | 8 | Guests | Disable (2) | Disable (1) | | 12 | No access to the frontend | Disable (2) | Enable (0) | | 49 | testgroup | System default (0) | Enable (0) | | 15 | Test users | System default (0) | Enable (0) | | 16 | Test users intern | Internal (1) | Enable (0) | | 7 | Zabbix administrators | Internal (1) | Enable (0) | | 14 | Zabbix core | System default (0) | Enable (0) | +---------+---------------------------+--------------------+-------------+ From version 1.5.4 it is possible to use the parameter ``--file `` or ``-f `` to define a file with multiple ``zabbix-cli`` commands. Some performance improvements get activated when executing ``zabbix-cli`` in this way. The perfomance gain when running multiple commands via an input file can be as high as 70% when creating new hosts in Zabbix. :: [user@host]# cat zabbix_input_file.txt # This a comment. # Creating hosts. create_host test000001.example.net All-manual-hosts .+ 1 create_host test000002.example.net All-manual-hosts .+ 1 create_host test000003.example.net All-manual-hosts .+ 1 # Deleting hosts remove_host test000001.example.net remove_host test000002.example.net remove_host test000003.example.net [user@host]# zabbix-cli -f zabbix_input_file.txt [OK] File [/home/user/zabbix_input_file.txt] exists. Bulk execution of commands defined in this file started. [Done]: Host (test000001.example.net) with ID: 14213 created [Done]: Host (test000002.example.net) with ID: 14214 created [Done]: Host (test000003.example.net) with ID: 14215 created [Done]: Hosts (test000001.example.net) with IDs: 14213 removed [Done]: Hosts (test000002.example.net) with IDs: 14214 removed [Done]: Hosts (test000003.example.net) with IDs: 14215 removed One can also use the parameters ``--output csv`` or ``--output json`` when running ``zabbix-cli`` in non-interactive modus to generate an output in CSV or JSON format. :: [user@host ~]# zabbix-cli --output csv show_usergroups "13","DBA","System default (0)","Enable (0)" "9","Disabled","System default (0)","Disable (1)" "11","Enabled debug mode","System default (0)","Enable (0)" "8","Guests","Disable (2)","Disable (1)" "12","No access to the frontend","Disable (2)","Enable (0)" "49","testgroup","System default (0)","Enable (0)" "15","Test users","System default (0)","Enable (0)" "16","Test users intern","Internal (1)","Enable (0)" "7","Zabbix administrators","Internal (1)","Enable (0)" "14","Zabbix core","System default (0)","Enable (0)" Remember that you have to use ``""`` and escape some characters if running commands in non-interactive modus with parameters that have spaces or special characters for the shell.e.g. :: [user@host ~]# zabbix-cli -C "show_host * \"'available':'2','maintenance_status':'1'\" " +--------+----------------------+-------------------------+-----------------------------------+--------------------+-----------------+-----------------+---------------+ | HostID | Name | Hostgroups | Templates | Applications | Zabbix agent | Maintenance | Status | +--------+----------------------+-------------------------+-----------------------------------+--------------------+-----------------+-----------------+---------------+ | 10110 | test01.uio.no | [8] Database servers | [10102] Template App SSH Service | CPU | Unavailable (2) | In progress (1) | Monitored (0) | | | | | [10104] Template ICMP Ping | Filesystems | | | | | | | | [10001] Template OS Linux | General | | | | | | | | | ICMP | | | | | | | | | Memory | | | | | | | | | Network interfaces | | | | | | | | | OS | | | | | | | | | Performance | | | | | | | | | Processes | | | | | | | | | SSH service | | | | | | | | | Security | | | | | | | | | Zabbix agent | | | | +--------+----------------------+-------------------------+-----------------------------------+--------------------+-----------------+-----------------+---------------+ | 10484 | test02.uio.no | [12] Web servers | [10094] Template App HTTP Service | HTTP service | Unavailable (2) | In progress (1) | Monitored (0) | | | | [13] PostgreSQL servers | [10073] Template App MySQL | ICMP | | | | | | | [17] MySQL servers | [10102] Template App SSH Service | MySQL | | | | | | | [21] ssh servers | [10104] Template ICMP Ping | SSH service | | | | | | | [5] Discovered hosts | | | | | | | | | [8] Database servers | | | | | | +--------+----------------------+-------------------------+-----------------------------------+--------------------+-----------------+-----------------+---------------+ | 10427 | test03.uio.no | [12] Web servers | [10094] Template App HTTP Service | HTTP service | Unavailable (2) | In progress (1) | Monitored (0) | | | | [17] MySQL servers | [10073] Template App MySQL | ICMP | | | | | | | [21] ssh servers | [10102] Template App SSH Service | MySQL | | | | | | | [5] Discovered hosts | [10104] Template ICMP Ping | SSH service | | | | | | | [8] Database servers | | | | | | +--------+----------------------+-------------------------+-----------------------------------+--------------------+-----------------+-----------------+---------------+ acknowledge_event ----------------- This command acknowledges an event :: acknowledge_events [eventIDs] [message] Parameters: * **[eventIDs]:** IDs of the events to acknowledge. One can define several values in a comma separated list. * **[message]:** Text of the acknowledgement message. acknowledge_trigger_last_event ------------------------------ This command acknowledges the last event of a trigger. :: acknowledge_trigger_last_event [triggerIDs] [message] Parameters: * **[triggerIDs]:** IDs of the triggers to acknowledge. One can define several values in a comma separated list. * **[message]:** Text of the acknowledgement message. add_host_to_hostgroup --------------------- This command adds one/several hosts to one/several hostgroups :: add_host_to_hostgroup [hostnames] [hostgroups] Parameters: * **[hostnames]:** Hostname or zabbix-hostID. One can define several values in a comma separated list. * **[hostgroups]:** Hostgroup name or zabbix-hostgroupID. One can define several values in a comma separated list. add_user_to_usergroup --------------------- This command adds one/several users to one/several usergroups :: add_user_to_hostgroup [usernames] [usergroups] Parameters: * **[usernames]:** Username or zabbix-userID. One can define several values in a comma separated list. * **[usergroups]:** Usergroup name or zabbix-usergroupID. One can define several values in a comma separated list. add_usergroup_permissions ------------------------- This command adds a permission for an usergroup on a hostgroup. If the usergroup already have permissions on the hostgroup, nothing will be changed. :: define_usergroup_permissions [usergroup] [hostgroups] [permission code] Parameters: * **usergroup:** Usergroup that will get a permission on a hostgroup * **hostgroups:** Hostgroup names where the permission will apply. One can define several values in a comma separated list. * **permission:** - **deny**: Deny [usergroup] all access to [hostgroups] - **ro**: Give [usergroup] read access to [hostgroups] - **rw**: Give [usergroup] read and write access to [hostgroups] clear ----- This command clears the screen and shows the welcome banner :: clear create_host ----------- This command creates a host. :: create_host [hostname|IP] [hostgroups] [proxy] [status] Parameters: * **[Hostname|IP]:** Hostname or IPaddress * **[hostgroups]:** Hostgroup name or zabbix-hostgroupID to add the host to. One can define several values in a comma separated list. Remember that the host will get added per default to all hostgroups defined with the parameter ``default_hostgroup`` in the zabbix-cli configuration file. This command will fail if both ``default_hostgroup`` and [hostgroups] are empty. * **[proxy]:** Proxy server used to monitor this host. One can use regular expressions to define a group of proxy servers from where the system will choose a random proxy. If this parameter is not defined, the system will assign a random proxy from the list of all available proxies. If the system does not have proxy servers defined, the new host will be monitor by the Zabbix-server. e.g. Some regular expressions that can be used: - *proxy-(prod|test)+d\.example\.org* e.g. proxy-prod1.example.org and proxy-test8.example.org will match this expression. - *.+* All proxies will match this expression. * **[status]:** Status of the host. If this parameter is not defined, the system will use the default. - 0 - (default) monitored host - 1 - unmonitored host All host created with this function will get assigned a default interface of type 'Agent' using the port 10050. create_host_interface --------------------- This command creates a hostinterface :: create_host_interface [hostname] [interface connection] [interface type] [interface port] [interface IP] [interface DNS] [default interface] Parameters: * **[hostname]**: Hostname * **[interface connection]**: Type of connection. Possible values: - 0 - Connect using host DNS name (Default) or interface DNS if provided - 1 - Connect using host IP address * **[interface type]**: Type of interface. Possible values: - 1 - Zabbix agent - 2 - SNMP (Default) - 3 - IPMI - 4 - JMX * **[interface port]**: Interface port (Default: 161) * **[interface IP]**: IP address if interface connection is 1 * **[interface DNS]**: DNS if interface connection is 0: (hostname by default) * **[default interface]**: Define this interface som default. Possible values: - 0 - Not default interface - 1 - Default interface (Default) The default value for a parameter is shown between brackets []. If the user does not define any value or a wrong value, the default value will be used. This command can be run with or without parameters. e.g.: create_hostgroup ---------------- This command creates a hostgroup :: create_hostgroup [group name] Parameters: * **[group name]:** Name of the hostgroup create_maintenance_definition ----------------------------- This command creates a 'one time only' maintenance definition for a defined period of time. Use the zabbix dashboard for more advance definitions. :: create_maintenance_definition [name] [description] [host/hostgroup] [time period] [maintenance type] Parameters: * **[name]**: Maintenance definition name. * **[description]**: Maintenance definition description * **[host/hostgroup]**: Host/s and/or hostgroup/s the that will undergo maintenance. One can define more than one value in a comma separated list and mix host and hostgroup values. * **[time period]** Time period when the maintenance must come into effect. One can define an interval between to timestamps in ISO format or a time period in minutes, hours or days from the moment the definition is created. e.g. From 22:00 until 23:00 on 2016-11-21 -> '2016-11-21T22:00 to 2016-11-21T23:00' 2 hours from the moment we create the maintenance -> '2 hours' * **[maintenance type]** Maintenance type. Type values: - 0 - (default) With data collection - 1 - Without data collection create_notification_user ------------------------ This command creates a notification user. These users are used to send notifications when a zabbix event happens. Sometimes we need to send a notification to a place not owned by any user in particular, e.g. an email list or jabber channel but Zabbix has not the possibility of defining media for a usergroup. This is the reason we use *notification users*. They are users nobody owns, but that can be used by other users to send notifications to the media defined in the notification user profile. All notification users will have an 'Alias' value that starts with *notification-user-* Check the parameter **default_notification_users_usergroup** in your zabbix-cli configuration file. The usergroup defined here has to exists if you want this command to work. :: create_notification_user [sendto] [mediatype] [remarks] Parameters: * **[sendto]**: E-mail address, SMS number, jabber address, ... * **[mediatype]**: One of the media types names defined in your Zabbix installation, e.g. Email, SMS, jabber, ... * **[remarks]**: Comments about this user. e.g. Operations email. Max lenght is 20 characters. create_user ----------- This command creates a user. :: create_user [alias] [name] [surname] [passwd] [type] [autologin] [autologout] [groups] Parameters: * **[alias]:** User alias (account name) * **[name]:** Name of the user * **[surname]:** Surname of the user * **[passwd]:** Password * **[type]:** Type of the user. Possible values: - 1 - (default) Zabbix user; - 2 - Zabbix admin; - 3 - Zabbix super admin. * **[autologin]:** Whether to enable auto-login. Possible values: - 0 - (default) auto-login disabled; - 1 - auto-login enabled. * **[autologout]:** User session life time in seconds. If set to 0, the session will never expire. Default: 86400 * **[groups]:** User groups to add the user to. Remember that the user will get added per default to all usergroups defined with the parameter ``default_usergroup`` in the zabbix-cli configuration file. This command will fail if both ``default_usergroup`` and [groups] are empty. create_usergroup ---------------- This command creates an usergroup :: create_usergroup [group name] [GUI access] [Status] Parameters: * **[group name]:** Name of the usergroup * **[GUI access]:** Frontend authentication method of the users in the group. Possible values: - 0 - (default) use the system default authentication method; - 1 - use internal authentication; - 2 - disable access to the frontend. * **[status]:** Whether the user group is enabled or disabled. Possible values are: - 0 - (default) enabled; - 1 - disabled. define_global_macro ------------------- This command defines a global macro :: define_global_macro [macro name] [macro value] Parameters: * **macro name:** Name of the zabbix macro. The system will format this value to use the macro format definition needed by Zabbix. e.g. site_url will be converted to ${SITE_URL} * **macro value:** Default value of the macro define_host_usermacro --------------------- This command defines a host usermacro. :: defines_host_usermacro [hostname] [macro name] [macro value] Parameters: * **hostname:** Hostname that will get the macro locally defined. * **macro name:** Name of the zabbix macro. The system will format this value to use the macro format definition needed by Zabbix. e.g. site_url will be converted to ${SITE_URL} * **macro value:** Default value of the macro define_host_monitoring_status ----------------------------- This command defines the monitoring status of a host. A monitor status of 'Not monitored (off)' will stop all monitoring of the host and a 'Monitored (on)' value will start monitoring. :: defines_host_monitoring_status [hostname] [on/off] Parameteres: * **hostname:** Hostname that will get the monitoring status updated. export_configuration -------------------- This command exports the configuration of different Zabbix components to a JSON or XML file. This files can be used to import or restore these objects in a Zabbix system. Several parameters in the zabbix-cli.conf configuration file can be used to control some export options. :: export_configuration [export_directory] [object type] [object name] Parameters: * **[export directory]:** Directory where the export files will be saved. * **[object type]:** Possible values: ``groups``, ``hosts``, ``images``, ``maps``, ``screens``, ``templates`` One can use the special value ``#all#`` to export all object type groups. * **[object name]:** Object name or Zabbix-ID. One can define several values in a comma separated list. One can use the special value #all# to export all objects in a object type group. This parameter will be defined automatically as #all# if [object type] == #all# import_configuration -------------------- This command imports the configuration of a Zabbix component. We use the options ``createMissing=True`` and ``updateExisting=True`` when importing data. This means that new objects will be created if they do not exists and that existing objects will be updated if they exist. :: import_configuration [import file] [dry run] Parameters: * **[import file]:** File with the JSON or XML code to import. This command will use the file extension (.json or .xml) to find out the import format. This command finds all the pathnames matching a specified pattern according to the rules used by the Unix shell. Tilde expansion ``~``, ``*``, ``?``, and character ranges expressed with ``[]`` will be correctly matched. For a literal match, wrap the meta-characters in brackets. For example, '[?]' matches the character '?'. * **[dry run]:** If this parameter is used, the command will only show the files that would be imported without running the import process. - 0 - Dry run deactivated - 1 (default) - Dry run activated link_template_to_host --------------------- This command links one/several templates to one/several hosts :: link_template_to_host [templates] [hostnames] Parameters: * **[templates]:** Template or zabbix-templateID. One can define several values in a comma separated list. * **[hostnames]:** Hostname or zabbix-hostID. One can define several values in a comma separated list. load_balance_proxy_hosts ------------------------ This command will spread hosts evenly along a serie of proxies. :: load_balance_proxy_hosts [proxy list] Parameters: * **proxy list:** Comma delimited list with the proxies that will share the monitoring task for a group of hosts. The group of hosts is obtained from the hosts assigned to the proxies in [proxy list] e.g. If proxy-1 is monitoring 1500 hosts and proxy-2 is monitoring 500 hosts, we can run this command to redistribute the 2000 hosts between the two proxies. Every proxy will get assigned automatically ca 1000 hosts from the list of 2000 host :: load_balance_proxy_host proxy-1,proxy-2 move_proxy_hosts ----------------- This command moves all hosts monitored by a proxy (src) to another proxy (dst). :: move_proxy_hosts [proxy_src] [proxy_dst] Parameters: * **proxy_src:** Source proxy server. * **proxy_dst:** Destination proxy server. quit ---- This command quits/terminates the zabbix-CLI shell. :: quit A shortcut to this command is ``\q``. remove_host ----------- This command removes a hosts :: remove_host [hostname] Parameters: * **[hostname]:** Hostname or zabbix-hostID. remove_host_from_hostgroup -------------------------- This command removes one/several hosts from one/several hostgroups :: remove_host_from_hostgroup [hostnames] [hostgroups] Parameters: * **[hostnames]:** Hostname or zabbix-hostID. One can define several values in a comma separated list. * **[hostgroups]:** Hostgroup name or zabbix-hostgroupID. One can define several values in a comma separated list. remove_maintenance_definition ----------------------------- This command removes one or several maintenance definitions :: remove_maintenance_definitions [definitionID] Parameters: * **[definitionID]**: Definition ID. One can define more than one value in a comma separated list. remove_user ------------ This command removes an user. :: remove_user [username] Parameters: * **username:** Username to remove. remove_user_from_usergroup -------------------------- This command removes an user from one/several usergroups :: remove_user_to_usergroup [username] [usergroups] Parameters: * **username:** Username to remove * **usergroups:** Usergroup names from where the username will be removed. One can define several values in a comma separated list. shell ----- This command runs a command in the operative system. :: shell [command] Parameters: * **[command]:** Any command that can be run in the operative system. It exists a shortcut ``[!]`` for this command that can be used insteed of ``shell``. This command can be run only with parameters. e.g.: :: [pgbackman]$ ! ls -l total 88 -rw-rw-r--. 1 vagrant vagrant 135 May 30 10:04 AUTHORS drwxrwxr-x. 2 vagrant vagrant 4096 May 30 10:03 bin drwxrwxr-x. 4 vagrant vagrant 4096 May 30 10:03 docs drwxrwxr-x. 2 vagrant vagrant 4096 May 30 10:03 etc -rw-rw-r--. 1 vagrant vagrant 0 May 30 10:04 INSTALL -rw-rw-r--. 1 vagrant vagrant 35121 May 30 10:04 LICENSE drwxrwxr-x. 4 vagrant vagrant 4096 May 30 10:03 vagrant show_alarms ----------- This command shows all active alarms with the last event unacknowledged. :: show_alarms [description] [filters] [hostgroups] [Last event unacknowledged] Parameters: * **description:** Type of alarm description to search for. Leave this parameter empty to search for all descriptions. One can also use wildcards. * **filters:** One can filter the result by host and priority. No wildcards can be used. Priority values: - 0 - (default) not classified; - 1 - information; - 2 - warning; - 3 - average; - 4 - high; - 5 - disaster. * **hostgroups:** One can filter the result to get alarms from a particular hostgroup or group og hostgroups. One can define several values in a comma separated list. * **Last event unacknowledged:** One can filter the result after the acknowledged value of the last event of an alarm. Values: - true - (default) Show only active alarms with last event unacknowledged. - false - Show all active alarms, also those with the last event acknowledged. e.g.: Get all alarms with priority 'High' that contain the word 'disk' in the description from all hostgroups in the system and the last event unacknowledged:: show_alarms *disk* "'priority':'4'" * true show_global_macros ------------------ This command shows all global macros :: show_global_macros show_history ------------ Show the list of commands that have been entered during the zabbix-cli shell session. :: show_history A shortcut to this command is ``\s``. One can also use the *Emacs Line-Edit Mode Command History Searching* to get previous commands containing a string. Hit ``[CTRL]+[r]`` in the zabbix-CLI shell followed by the search string you are trying to find in the history. show_host --------- This command shows hosts information :: show_host [HostID / Hostname] [Filter] Parameters: * **HostID / Hostname:** One can search by HostID or by Hostname. One can use wildcards if we search by Hostname * **Filter:** - Zabbix agent: 'available': (0=Unknown, 1=Available, 2=Unavailable) - Maintenance: 'maintenance_status': (0:No maintenance, 1:In progress) - Status: 'status': (0:Monitored,1: Not monitored) e.g.: Show all hosts with Zabbix agent: Available AND Status: Monitored: :: show_host * "'available':'1','status':'0'" show_host_inventory -------------------- This command shows hosts inventory :: show_host_inventory [Hostname] Parameters: * **Hostname:** Hostname. This command will return all inventory information in json format when running zabbix-cli in non-interactive modus. If zabbix-cli is running in interactive modus, only a few attributes will be shown (hostname, vendor,chassis,gateway,contact address) show_host_usermacros -------------------- This command shows all usermacros for a host :: show_host_usermacros [hostname] Parameters: * **Hostname:** Hostname. show_hostgroup -------------- This command show hostgroups information :: show_hostgroup [hostgroup] Parameters: * **hostgroup:** Hostgroup name. One can use wildcards. show_hostgroups --------------- This command shows all hostgroups defined in the system. :: show_hostgroups show_hosts ---------- This command shows all hosts defined in the system. :: show_hosts show_items ---------- This command shows items that belong to a template. :: show_items [template] Parameters: * **[templates]:** Template or zabbix-templateID. show_maintenance_definitions ---------------------------- This command shows maintenance definitions global information. The logical operator AND will be used if one defines more than one parameter. :: show_maintenance_definitions [definitionID] [hostgroup] [host] Parameters: * **[definitionID]**: Definition ID. One can define more than one value. * **[hostgroup]**: Hostgroup name. One can define more than one value. * **[host]**: Hostname. One can define more than one value. show_maintenance_periods ------------------------ This command shows maintenance periods global information. :: show_maintenance_periods [definitionID] Parameters: * **[definitionID]**: Definition ID. One can define more than one value. show_template ------------- This command show templates information :: show_template [Template name] Parameters: * **Template name:** One can search by template name. We can use wildcards. show_templates -------------- This command shows all templates defined in the system. :: show_templates show_trigger_events ------------------- This command shows the events generated by a trigger. :: show_trigger_events [triggerID] [count] * **[triggerID]:** ID of the trigger we want tho show. * **[count]:** Number of events to show (Default: 1) show_triggers ------------- This command shows triggers that belong to a template. :: show_triggers [template] Parameters: * **[templates]:** Template or zabbix-templateID. show_usergroup -------------- This command shows user group information. :: show_usergroup [usergroup] Parameters: * **usergroup:** User group name. One can use wildcards. show_usergroups --------------- This command shows user groups information. :: show_usergroups show_usermacro_host_list ------------------------ This command shows all host with a defined usermacro :: show_usermacro_host_list [usermacro] Parameters: * **usermacro:** Name of the zabbix usermacro. The system will format this value to use the macro format definition needed by Zabbix. e.g. site_url will be converted to ${SITE_URL} show_usermacro_template_list ---------------------------- This command shows all templates with a defined macro :: show_usermacro_template_list [macro name] Parameters: * **usermacro:** Name of the zabbix usermacro. The system will format this value to use the macro format definition needed by Zabbix. e.g. site_url will be converted to ${SITE_URL} show_users ---------- This command shows users information. :: show_users show_zabbixcli_config --------------------- This command shows information about the configuration used by this zabbix-cli instance. :: show_zabbixcli_config ulink_template_from_host ------------------------ This command unlinks and clear one/several templates from one/several hosts :: unlink_template_from_host [templates] [hostnames] Parameters: * **[templates]:** Template or zabbix-templateID. One can define several values in a comma separated list. * **[hostnames]:** Hostname or zabbix-hostID. One can define several values in a comma separated list. update_host_inventory --------------------- This command updates one hosts' inventory :: update_host_inventory [hostname] [inventory_key] [inventory value] Inventory key is not the same as seen in web-gui. To look at possible keys and their current values, use "zabbix-cli --use-json-format show_host_inventory " update_host_proxy ----------------- This command defines the proxy used to monitor a host :: update_host_proxy [hostname] [proxy] Parameters: * **hostname:** Hostname to update * **proxy:** Zabbix proxy that will monitor [hostname] update_usergroup_permissions ---------------------------- This command updates the permissions for an usergroup on a hostgroup. :: define_usergroup_permissions [usergroup] [hostgroups] [permission code] Parameters: * **[usergroup]**: Usergroup that will get a permission on a hostgroup * **[hostgroups]**: Hostgroup names where the permission will apply. One can define several values in a comma separated list. * **[permission]**: - deny: Deny [usergroup] all access to [hostgroups] - ro: Give [usergroup] read access to [hostgroups] - rw: Give [usergroup] read and write access to [hostgroups] Authors ======= In alphabetical order: | | Rafael Martinez Guerrero | E-mail: rafael@postgresql.org.es / rafael@usit.uio.no | PostgreSQL-es / University Center for Information Technology (USIT), University of Oslo, Norway | License and Contributions ========================= Zabbix-CLI is the property of USIT-University of Oslo, and its code is distributed under GNU General Public License 3. | Copyright © 2014-2016 USIT-University of Oslo. zabbix-cli-1.7.0/docs/style.css000066400000000000000000000474761311400624000163560ustar00rootroot00000000000000@import url(html4css1.css); /** * :Author: Chad Skeeters * :Contact: cskeeters@nciinc.com * Stylesheet for use with Docutils/rst2html. * Example: rst2html --stylesheet=rst2html.css README.rst doc/html/README.html */ html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } a:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } a:hover, a:active { outline: 0; } sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } img { width: auto\9; height: auto; max-width: 100%; vertical-align: middle; border: 0; -ms-interpolation-mode: bicubic; } @media print { * { color: #000 !important; text-shadow: none !important; background: transparent !important; box-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } @ page { margin: 0.5cm; } h1 { page-break-before: always; } h1.title { page-break-before: avoid; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } } body { margin: 40px; margin-right: auto; margin-left: auto; width: 880px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; color: #333333; background-color: #ffffff; } a { color: #0088cc; text-decoration: none; } a:hover, a:focus { color: #005580; text-decoration: underline; } .img-rounded { -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; } .img-polaroid { padding: 4px; background-color: #fff; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.2); -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } p { margin: 0 0 10px; } small { font-size: 85%; } strong { font-weight: bold; } em { font-style: italic; } cite { font-style: normal; } h1, h2, h3, h4, h5, h6 { font-family: inherit; font-weight: bold; line-height: 20px; color: inherit; text-rendering: optimizelegibility; } h1 { font-size: 2em; padding-bottom: .2em; border-bottom: 1px solid grey; } h1.title { padding-bottom: 1em; border-bottom: 0px; } h2 { font-size: 1.5em; } h3 { font-size: 1.3em; font-family: Georgia, serif; font-style: italic; /*font-weight:normal;*/; } h4 { font-size: 1.3em; } h5 { font-size: 1.2em; } h6 { font-size: 1.1em; } ul, ol { padding: 0; margin: 0 0 10px 25px; } ul ul, ul ol, ol ol, ol ul { margin-bottom: 0; } li { line-height: 20px; } dl { margin-bottom: 20px; } dt, dd { line-height: 20px; } dt { font-weight: bold; } dd { margin-left: 10px; } hr { margin: 20px 0; border: 0; border-top: 1px solid #eeeeee; border-bottom: 1px solid #ffffff; } abbr[title], abbr[data-original-title] { cursor: help; border-bottom: 1px dotted #999999; } abbr.initialism { font-size: 90%; text-transform: uppercase; } blockquote { padding: 0 0 0 15px; margin: 0 0 20px; border-left: 5px solid #eeeeee; } blockquote p { margin-bottom: 0; font-size: 17.5px; font-weight: 300; line-height: 1.25; } q:before, q:after, blockquote:before, blockquote:after { content: ""; } address { display: block; margin-bottom: 20px; font-style: normal; line-height: 20px; } code, pre { padding: 0 3px 2px; font-family: Monaco, Menlo, Consolas, "Courier New", monospace; font-size: 12px; color: #333333; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } code { padding: 2px 4px; color: #d14; white-space: nowrap; background-color: #f7f7f9; border: 1px solid #e1e1e8; } pre { display: block; padding: 9.5px; margin: 0 0 10px; font-size: 13px; line-height: 20px; #word-break: break-all; #word-wrap: break-word; #white-space: pre; #white-space: pre-wrap; background-color: #f5f5f5; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.15); -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; overflow: auto; } pre.prettyprint { margin-bottom: 20px; } pre code { padding: 0; color: inherit; white-space: pre; white-space: pre-wrap; background-color: transparent; border: 0; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } table { max-width: 100%; background-color: transparent; border-collapse: collapse; border-spacing: 0; } .table { width: 100%; margin-bottom: 20px; } .table th, .table td { padding: 8px; line-height: 20px; text-align: left; vertical-align: top; border-top: 1px solid #dddddd; } .table th { font-weight: bold; } .table thead th { vertical-align: bottom; } .table caption + thead tr:first-child th, .table caption + thead tr:first-child td, .table colgroup + thead tr:first-child th, .table colgroup + thead tr:first-child td, .table thead:first-child tr:first-child th, .table thead:first-child tr:first-child td { border-top: 0; } .table tbody + tbody { border-top: 2px solid #dddddd; } .table .table { background-color: #ffffff; } .table-condensed th, .table-condensed td { padding: 4px 5px; } .table-bordered { border: 1px solid #dddddd; border-collapse: separate; *border-collapse: collapse; border-left: 0; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .table-bordered th, .table-bordered td { border-left: 1px solid #dddddd; } .table-bordered caption + thead tr:first-child th, .table-bordered caption + tbody tr:first-child th, .table-bordered caption + tbody tr:first-child td, .table-bordered colgroup + thead tr:first-child th, .table-bordered colgroup + tbody tr:first-child th, .table-bordered colgroup + tbody tr:first-child td, .table-bordered thead:first-child tr:first-child th, .table-bordered tbody:first-child tr:first-child th, .table-bordered tbody:first-child tr:first-child td { border-top: 0; } .table-bordered thead:first-child tr:first-child > th:first-child, .table-bordered tbody:first-child tr:first-child > td:first-child, .table-bordered tbody:first-child tr:first-child > th:first-child { -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topleft: 4px; } .table-bordered thead:first-child tr:first-child > th:last-child, .table-bordered tbody:first-child tr:first-child > td:last-child, .table-bordered tbody:first-child tr:first-child > th:last-child { -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-topright: 4px; } .table-bordered thead:last-child tr:last-child > th:first-child, .table-bordered tbody:last-child tr:last-child > td:first-child, .table-bordered tbody:last-child tr:last-child > th:first-child, .table-bordered tfoot:last-child tr:last-child > td:first-child, .table-bordered tfoot:last-child tr:last-child > th:first-child { -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomleft: 4px; } .table-bordered thead:last-child tr:last-child > th:last-child, .table-bordered tbody:last-child tr:last-child > td:last-child, .table-bordered tbody:last-child tr:last-child > th:last-child, .table-bordered tfoot:last-child tr:last-child > td:last-child, .table-bordered tfoot:last-child tr:last-child > th:last-child { -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; -moz-border-radius-bottomright: 4px; } .table-bordered tfoot + tbody:last-child tr:last-child td:first-child { -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; -moz-border-radius-bottomleft: 0; } .table-bordered tfoot + tbody:last-child tr:last-child td:last-child { -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; -moz-border-radius-bottomright: 0; } .table-bordered caption + thead tr:first-child th:first-child, .table-bordered caption + tbody tr:first-child td:first-child, .table-bordered colgroup + thead tr:first-child th:first-child, .table-bordered colgroup + tbody tr:first-child td:first-child { -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topleft: 4px; } .table-bordered caption + thead tr:first-child th:last-child, .table-bordered caption + tbody tr:first-child td:last-child, .table-bordered colgroup + thead tr:first-child th:last-child, .table-bordered colgroup + tbody tr:first-child td:last-child { -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-topright: 4px; } .table-striped tbody > tr:nth-child(odd) > td, .table-striped tbody > tr:nth-child(odd) > th { background-color: #f9f9f9; } .table-hover tbody tr:hover > td, .table-hover tbody tr:hover > th { background-color: #f5f5f5; } table td[class*="span"], table th[class*="span"], .row-fluid table td[class*="span"], .row-fluid table th[class*="span"] { display: table-cell; float: none; margin-left: 0; } .hero-unit { padding: 60px; margin-bottom: 30px; font-size: 18px; font-weight: 200; line-height: 30px; color: inherit; background-color: #eeeeee; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; } .hero-unit h1 { margin-bottom: 0; font-size: 60px; line-height: 1; letter-spacing: -1px; color: inherit; } .hero-unit li { line-height: 30px; } /* rst2html default used to remove borders from tables and images */ .borderless, table.borderless td, table.borderless th { border: 0; } table.borderless td, table.borderless th { /* Override padding for "table.docutils td" with "! important". The right padding separates the table cells. */ padding: 0 0.5em 0 0 ! important; } .first { /* Override more specific margin styles with "! important". */ margin-top: 0 ! important; } .last, .with-subtitle { margin-bottom: 0 ! important; } .hidden { display: none; } a.toc-backref { text-decoration: none; color: black; } blockquote.epigraph { margin: 2em 5em; } dl.docutils dd { margin-bottom: 0.5em; } object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { overflow: hidden; } /* Uncomment (and remove this text!) to get bold-faced definition list terms dl.docutils dt { font-weight: bold } */ div.abstract { margin: 2em 5em; } div.abstract p.topic-title { font-weight: bold; text-align: center; } div.admonition, div.attention, div.caution, div.danger, div.error, div.hint, div.important, div.note, div.tip, div.warning { margin: 2em; border: medium outset; padding: 1em; } div.note, div.warning { margin: 1.5em 0px; border: none; } div.note p.admonition-title, div.warning p.admonition-title { display: none; } /* Clearfix * http://css-tricks.com/snippets/css/clear-fix/ */ div.note:after, div.warning:after { content: ""; display: table; clear: both; } div.note p:before, div.warning p:before { display: block; float: left; font-size: 4em; line-height: 1em; margin-right: 20px; margin-left: 0em; margin-top: -10px; content: '\0270D'; /*handwriting*/; } div.warning p:before { content: '\026A0'; /*warning*/; } div.admonition p.admonition-title, div.hint p.admonition-title, div.important p.admonition-title, div.note p.admonition-title, div.tip p.admonition-title { font-weight: bold; font-family: sans-serif; } div.attention p.admonition-title, div.caution p.admonition-title, div.danger p.admonition-title, div.error p.admonition-title, div.warning p.admonition-title, .code .error { color: red; font-weight: bold; font-family: sans-serif; } /* Uncomment (and remove this text!) to get reduced vertical space in compound paragraphs. div.compound .compound-first, div.compound .compound-middle { margin-bottom: 0.5em } div.compound .compound-last, div.compound .compound-middle { margin-top: 0.5em } */ div.dedication { margin: 2em 5em; text-align: center; font-style: italic; } div.dedication p.topic-title { font-weight: bold; font-style: normal; } div.figure { margin-left: 2em; margin-right: 2em; } div.footer, div.header { clear: both; font-size: smaller; } div.line-block { display: block; margin-top: 1em; margin-bottom: 1em; } div.line-block div.line-block { margin-top: 0; margin-bottom: 0; margin-left: 1.5em; } div.sidebar { margin: 0 0 0.5em 1em; border: medium outset; padding: 1em; background-color: #ffffee; width: 40%; float: right; clear: right; } div.sidebar p.rubric { font-family: sans-serif; font-size: medium; } div.system-messages { margin: 5em; } div.system-messages h1 { color: red; } div.system-message { border: medium outset; padding: 1em; } div.system-message p.system-message-title { color: red; font-weight: bold; } div.topic { margin: 2em; } h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { margin-top: 0.4em; } h1.title { text-align: center; } h2.subtitle { text-align: center; } hr.docutils { width: 75%; } 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 { clear: both; text-align: center; } .align-right { text-align: right; } /* reset inner alignment in figures */ div.align-right { text-align: inherit; } /* div.align-center * { */ /* text-align: left } */ ol.simple, ul.simple { margin-bottom: 1em; } 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; } p.attribution { text-align: right; margin-left: 50%; } p.caption { font-style: italic; } p.credits { font-style: italic; font-size: smaller; } p.label { white-space: nowrap; } p.rubric { font-weight: bold; font-size: larger; color: maroon; text-align: center; } p.sidebar-title { font-family: sans-serif; font-weight: bold; font-size: larger; } p.sidebar-subtitle { font-family: sans-serif; font-weight: bold; } p.topic-title { font-weight: bold; } pre.address { margin-bottom: 0; margin-top: 0; font: inherit; } pre.literal-block, pre.doctest-block, pre.math, pre.code { margin-left: 2em; margin-right: 2em; } pre.code .ln { color: grey; } /* line numbers */ pre.code, code { background-color: #eeeeee; } pre.code .comment, code .comment { color: #5C6576; } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold; } pre.code .literal.string, code .literal.string { color: #0C5404; } pre.code .name.builtin, code .name.builtin { color: #352B84; } pre.code .deleted, code .deleted { background-color: #DEB0A1; } pre.code .inserted, code .inserted { background-color: #A3D289; } span.classifier { font-family: sans-serif; font-style: oblique; } span.classifier-delimiter { font-family: sans-serif; font-weight: bold; } span.interpreted { font-family: sans-serif; } span.option { white-space: nowrap; } span.pre { white-space: pre; } span.problematic { color: red; } span.section-subtitle { /* font-size relative to parent (h1..h6 element) */ font-size: 80%; } table.citation { border-left: solid 1px gray; margin-left: 1px; } table.docinfo { margin: 2em 4em; } table.docutils { margin-top: 0.5em; margin-bottom: 0.5em; } table.footnote { border-left: solid 1px black; margin-left: 1px; } table.docutils td, table.docutils th, table.docinfo td, table.docinfo th { padding-left: 0.5em; padding-right: 0.5em; vertical-align: top; } table.docutils th.field-name, table.docinfo th.docinfo-name { font-weight: bold; text-align: left; white-space: nowrap; padding-left: 0; } h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { font-size: 100%; } ul.auto-toc { list-style-type: none; } .code .pygments-hll { background-color: #ffffcc; } .code .pygments-c { color: #60a0b0; font-style: italic; } /* Comment */ .code .pygments-err { border: 1px solid #FF0000; } /* Error */ .code .pygments-k { color: #007020; font-weight: bold; } /* Keyword */ .code .pygments-o { color: #666666; } /* Operator */ .code .pygments-cm { color: #60a0b0; font-style: italic; } /* Comment.Multiline */ .code .pygments-cp { color: #007020; } /* Comment.Preproc */ .code .pygments-c1 { color: #60a0b0; font-style: italic; } /* Comment.Single */ .code .pygments-cs { color: #60a0b0; background-color: #fff0f0; } /* Comment.Special */ .code .pygments-gd { color: #A00000; } /* Generic.Deleted */ .code .pygments-ge { font-style: italic; } /* Generic.Emph */ .code .pygments-gr { color: #FF0000; } /* Generic.Error */ .code .pygments-gh { color: #000080; font-weight: bold; } /* Generic.Heading */ .code .pygments-gi { color: #00A000; } /* Generic.Inserted */ .code .pygments-go { color: #888888; } /* Generic.Output */ .code .pygments-gp { color: #c65d09; font-weight: bold; } /* Generic.Prompt */ .code .pygments-gs { font-weight: bold; } /* Generic.Strong */ .code .pygments-gu { color: #800080; font-weight: bold; } /* Generic.Subheading */ .code .pygments-gt { color: #0044DD; } /* Generic.Traceback */ .code .pygments-kc { color: #007020; font-weight: bold; } /* Keyword.Constant */ .code .pygments-kd { color: #007020; font-weight: bold; } /* Keyword.Declaration */ .code .pygments-kn { color: #007020; font-weight: bold; } /* Keyword.Namespace */ .code .pygments-kp { color: #007020; } /* Keyword.Pseudo */ .code .pygments-kr { color: #007020; font-weight: bold; } /* Keyword.Reserved */ .code .pygments-kt { color: #902000; } /* Keyword.Type */ .code .pygments-m { color: #40a070; } /* Literal.Number */ .code .pygments-s { color: #4070a0; } /* Literal.String */ .code .pygments-na { color: #4070a0; } /* Name.Attribute */ .code .pygments-nb { color: #007020; } /* Name.Builtin */ .code .pygments-nc { color: #0e84b5; font-weight: bold; } /* Name.Class */ .code .pygments-no { color: #60add5; } /* Name.Constant */ .code .pygments-nd { color: #555555; font-weight: bold; } /* Name.Decorator */ .code .pygments-ni { color: #d55537; font-weight: bold; } /* Name.Entity */ .code .pygments-ne { color: #007020; } /* Name.Exception */ .code .pygments-nf { color: #06287e; } /* Name.Function */ .code .pygments-nl { color: #002070; font-weight: bold; } /* Name.Label */ .code .pygments-nn { color: #0e84b5; font-weight: bold; } /* Name.Namespace */ .code .pygments-nt { color: #062873; font-weight: bold; } /* Name.Tag */ .code .pygments-nv { color: #bb60d5; } /* Name.Variable */ .code .pygments-ow { color: #007020; font-weight: bold; } /* Operator.Word */ .code .pygments-w { color: #bbbbbb; } /* Text.Whitespace */ .code .pygments-mf { color: #40a070; } /* Literal.Number.Float */ .code .pygments-mh { color: #40a070; } /* Literal.Number.Hex */ .code .pygments-mi { color: #40a070; } /* Literal.Number.Integer */ .code .pygments-mo { color: #40a070; } /* Literal.Number.Oct */ .code .pygments-sb { color: #4070a0; } /* Literal.String.Backtick */ .code .pygments-sc { color: #4070a0; } /* Literal.String.Char */ .code .pygments-sd { color: #4070a0; font-style: italic; } /* Literal.String.Doc */ .code .pygments-s2 { color: #4070a0; } /* Literal.String.Double */ .code .pygments-se { color: #4070a0; font-weight: bold; } /* Literal.String.Escape */ .code .pygments-sh { color: #4070a0; } /* Literal.String.Heredoc */ .code .pygments-si { color: #70a0d0; font-style: italic; } /* Literal.String.Interpol */ .code .pygments-sx { color: #c65d09; } /* Literal.String.Other */ .code .pygments-sr { color: #235388; } /* Literal.String.Regex */ .code .pygments-s1 { color: #4070a0; } /* Literal.String.Single */ .code .pygments-ss { color: #517918; } /* Literal.String.Symbol */ .code .pygments-bp { color: #007020; } /* Name.Builtin.Pseudo */ .code .pygments-vc { color: #bb60d5; } /* Name.Variable.Class */ .code .pygments-vg { color: #bb60d5; } /* Name.Variable.Global */ .code .pygments-vi { color: #bb60d5; } /* Name.Variable.Instance */ .code .pygments-il { color: #40a070; } /* Literal.Number.Integer.Long */ zabbix-cli-1.7.0/etc/000077500000000000000000000000001311400624000143055ustar00rootroot00000000000000zabbix-cli-1.7.0/etc/zabbix-cli.conf000066400000000000000000000076641311400624000172150ustar00rootroot00000000000000; ; Authors: ; rafael@postgresql.org.es / http://www.postgresql.org.es/ ; ; Copyright (c) 2014-2016 USIT-University of Oslo ; ; This file is part of Zabbix-CLI ; https://github.com/rafaelma/zabbix-cli ; ; Zabbix-cli 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. ; ; Zabbix-cli 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 Zabbix-CLI. If not, see . ; ; Zabbix-cli uses a multilevel configuration approach to implement a ; flexible configuration system. ; ; Zabbix-cli will check these configuration files if they exist and ; will merge the information to get the configuration to use. ; ; 1. /usr/share/zabbix-cli/zabbix-cli.fixed.conf ; 2. /etc/zabbix-cli/zabbix-cli.fixed.conf ; 3. Configuration file defined with the parameter -c / --config when executing zabbix-cli ; 4. $HOME/.zabbix-cli/zabbix-cli.conf ; 5. /etc/zabbix-cli/zabbix-cli.conf ; 6. /usr/share/zabbix-cli/zabbix-cli.conf ; ; Check the documentation for full details on how to configurate the ; system. ; ; ###################### ; Zabbix API section ; ###################### [zabbix_api] ; Zabbix API URL without /api_jsonrpc.php ; NB: http does not work, and does so in unexpected ways (with json parse error) ;zabbix_api_url=https://zabbix.example.net/zabbix ; ############################ ; zabbix_config section ; ############################ [zabbix_config] ; system ID. This ID will be use in the zabbix-cli prompt ; to identify the system we are connected to. ; Default: zabbix-ID ;system_id=Test ; Default hostgroup. We need a default hostgroup when ; creating a host. ; Default: All-hosts default_hostgroup=All-hosts ; Default admin usergroup/s(root). We need a default Admin usergroup when ; creating a hostgroup to get the right privileges in place. ; Default: Zabbix-root default_admin_usergroup=Zabbix-root ; Default usergroup. We need a default usergroup/s when ; creating a user to get the default access in place. ; Default: All-users default_create_user_usergroup=All-users ; Default notification usergroup. We need a default notification usergroup when ; creating a notification user to get the default access in place. ; Default: All-notification-users default_notification_users_usergroup=All-notification-users ; Default directory to save exported configuration files. ; Default: $HOME/zabbix_exports ; default_directory_exports= ; Default export format. ; default_export_format: JSON, XML ; Default: XML ; default_export_format=XML ; ; We deactivate this parameter until ; https://support.zabbix.com/browse/ZBX-10607 gets fixed. ; We use XML as the export format. ; Include timestamp in export filenames ; include_timestamp_export_filename: ON, OFF ; Default: ON include_timestamp_export_filename=ON ; Use colors when showing alarm information ; use_colors: ON, OFF ; Default: ON use_colors=ON ; Generate a file $HOME/.zabbix-cli_auth_token with the API-token ; delivered by the API after the last authentication of a user. If ; this file exists and the token is still active, the user would not ; have to write the username/password to login ; use_auth_token_file: ON,OFF ; Default: OFF use_auth_token_file=OFF ; ###################### ; Logging section ; ###################### [logging] ; The user running zabbix-cli has to have RW access to log_file if ; logging is active ; Logging: ON, OFF ; Default: OFF logging=OFF ; Log level: DEBUG, INFO, WARN, ERROR, CRITICAL ; Default: ERROR log_level=INFO ; Log file used by zabbix-cli ; Default: /var/log/zabbix-cli/zabbix-cli.log ;log_file=/var/log/zabbix-cli/zabbix-cli.log zabbix-cli-1.7.0/rpm/000077500000000000000000000000001311400624000143305ustar00rootroot00000000000000zabbix-cli-1.7.0/rpm/zabbix-cli.spec000066400000000000000000000045131311400624000172330ustar00rootroot00000000000000%if 0%{?rhel} && 0%{?rhel} == 6 %{!?__python2: %global __python2 /usr/bin/python2} %{!?python2_sitelib: %global python2_sitelib %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} %{!?python2_sitearch: %global python2_sitearch %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")} %endif %{!?pybasever: %global pybasever %(%{__python2} -c "import sys;print(sys.version[0:3])")} Name: zabbix-cli Version: 1.7.0 Release: 1%{?dist} Summary: Command-line interface for Zabbix Group: System Environment/Base License: GPLv3+ URL: https://github.com/usit-gd/zabbix-cli Source0: https://github.com/usit-gd/zabbix-cli/archive/%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root Requires: python-argparse, python-requests, python-ipaddr BuildRequires: python2-devel, python-setuptools BuildArch: noarch %description Command-line interface for Zabbix monitoring system. %prep %setup -q %build %{__python2} setup.py build %install %{__python2} setup.py install -O1 --skip-build --root %{buildroot} mkdir -p %{buildroot}%{_defaultdocdir}/zabbix-cli-%{version} %files %defattr(-, root, root, 0755) %{python2_sitelib}/zabbix_cli-%{version}-py%{pybasever}.egg-info/ %{python2_sitelib}/zabbix_cli/ %{_bindir}/zabbix-cli* %dir %{_datadir}/zabbix-cli/ %dir %{_defaultdocdir}/zabbix-cli-%{version} %{_datadir}/zabbix-cli/zabbix-cli.conf %doc LICENSE docs/manual.rst %changelog * Fri May 19 2017 Rafael Martinez Guerrero - 1.7.0-1 - New version 1.7.0 * Mon Dec 12 2016 Rafael Martinez Guerrero - 1.6.1-1 - New version 1.6.1 * Wed Nov 30 2016 Rafael Martinez Guerrero - 1.6.0-1 - New version 1.6.0 * Sat Oct 29 2016 Volker Froehlich - 1.5.4-5 - Match the actual license claimed in the code - Remove -n from the setup macro, because it was the default - Own the config directory - Don't require setuptools - Replace define with global - Remove python2 from Requires, as it is probably installed anyway and is a dependency of the required modules anyway - Add license file * Fri Oct 14 2016 Rafael Martinez Guerrero - 1.5.4-4 - Merge internal UiO spec file with Fabians file * Thu Sep 22 2016 - Fabian Arrotin - 1.5.4 - initial spec zabbix-cli-1.7.0/setup.py000077500000000000000000000046521311400624000152560ustar00rootroot00000000000000#!/usr/bin/env python # # Authors: # rafael@postgresql.org.es / http://www.postgresql.org.es/ # # Copyright (c) 2014-2016 USIT-University of Oslo # # This file is part of Zabbix-Cli # https://github.com/rafaelma/zabbix-cli # # Zabbix-Cli 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. # # Zabbix-Cli 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 Zabbix-Cli. If not, see . import subprocess import platform import shutil import sys import os import pwd import grp from setuptools import setup ''' setup.py installation file ''' try: zabbix_cli = {} with open('zabbix_cli/version.py', 'r') as version_file: exec (version_file.read(), zabbix_cli) if sys.version_info < (2, 6): raise SystemExit('ERROR: zabbix-cli needs at least python 2.6 to work') else: install_requires = ['argparse','requests','ipaddr'] # # Setup # setup(name='zabbix_cli', version=zabbix_cli['__version__'], description='ZABBIX-CLI - Zabbix terminal client', author='Rafael Martinez Guerrero', author_email='rafael@postgresql.org.es', url='https://github.com/usit-gd/zabbix-cli', packages=['zabbix_cli',], scripts=['bin/zabbix-cli','bin/zabbix-cli-bulk-execution','bin/zabbix-cli-init'], data_files=[('/usr/share/zabbix-cli', ['etc/zabbix-cli.conf'])], install_requires=install_requires, platforms=['Linux'], classifiers=[ 'Environment :: Console', 'Development Status :: 5 - Production/Stable', 'Topic :: System :: Monitoring', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', 'Programming Language :: Python', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', ], ) except Exception as e: print e zabbix-cli-1.7.0/zabbix_cli/000077500000000000000000000000001311400624000156405ustar00rootroot00000000000000zabbix-cli-1.7.0/zabbix_cli/__init__.py000066400000000000000000000000001311400624000177370ustar00rootroot00000000000000zabbix-cli-1.7.0/zabbix_cli/cli.py000066400000000000000000010165701311400624000167730ustar00rootroot00000000000000#!/usr/bin/env python # # Authors: # rafael@postgresql.org.es / http://www.postgresql.org.es/ # # Copyright (c) 2014-2015 USIT-University of Oslo # # This file is part of Zabbix-CLI # https://github.com/rafaelma/zabbix-cli # # Zabbix-CLI 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. # # Zabbix-CLI 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 Zabbix-CLI. If not, see . # import cmd import sys import os import time import signal import shlex import datetime import subprocess import ast import random import hashlib import textwrap import json import xml.dom.minidom import glob import re import ipaddr from zabbix_cli.config import * from zabbix_cli.logs import * from zabbix_cli.prettytable import * import zabbix_cli.version from zabbix_cli.pyzabbix import ZabbixAPI, ZabbixAPIException # ############################################ # class zabbix_cli # ############################################ class zabbixcli(cmd.Cmd): ''' This class implements the Zabbix shell. It is based on the python module cmd ''' # ############################### # Constructor # ############################### def __init__(self,logs,conf,username='',password='',auth_token=''): cmd.Cmd.__init__(self) try: # Zabbix-cli version self.version = self.get_version() # Zabbix-cli welcome intro self.intro = '\n#############################################################\n' + \ 'Welcome to the Zabbix command-line interface (v.' + self.version + ')\n' + \ '#############################################################\n' + \ 'Type help or \? to list commands.\n' # Pointer to Configuration class self.conf = conf # Pointer to logging class self.logs = logs # zabbix-API Username self.api_username = username # zabbix-API password self.api_password = password # zabbix-API auth-token self.api_auth_token = auth_token # Default output format (table|json|csv) self.output_format = 'table' # Use of colors (on|off) self.use_colors = self.conf.use_colors # Use of auth-token file (on|off) self.use_auth_token_file = self.conf.use_auth_token_file # Bulk execution of commands (True|False) self.bulk_execution = False # Non-interactive execution (True|False) self.non_interactive = False # SystemID show in prompt text self.system_id = self.conf.system_id # Prompt text self.prompt = '[zabbix-cli ' + self.api_username + '@' + self.system_id + ']$ ' if self.conf.logging == 'ON': self.logs.logger.debug('Zabbix API url: %s',self.conf.zabbix_api_url) # # Connecting to the Zabbix JSON-API # zabbix_auth_token_file = os.getenv('HOME') + '/.zabbix-cli_auth_token' self.zapi = ZabbixAPI(self.conf.zabbix_api_url) self.zapi.session.verify = True self.api_auth_token = self.zapi.login(self.api_username,self.api_password,self.api_auth_token) if self.conf.logging == 'ON': self.logs.logger.debug('Connected to Zabbix JSON-API') # # The file $HOME/.zabbix-cli_auth_token is created if it does not exists. # # Format: # USERNAME::API-auth-token returned after the las login. # if self.use_auth_token_file == 'ON' and os.path.isfile(zabbix_auth_token_file) == False: with open(zabbix_auth_token_file,'w') as auth_token_file: auth_token_file.write(self.api_username + '::' + self.api_auth_token) auth_token_file.flush() if self.conf.logging == 'ON': self.logs.logger.info('API-auth-token file created.') # # Populate the dictionary used as a cache with hostid and # hostname data # self.hostid_cache = self.populate_hostid_cache() # # Populate the dictionary used as a cache with proxyid and # proxy name data # self.proxyid_cache = self.populate_proxyid_cache() # # Populate the dictionary used as a cache with hostgroupname and # hostgroupid # self.hostgroupname_cache = self.populate_hostgroupname_cache() except Exception as e: print '\n[ERROR]: ',e print if self.conf.logging == 'ON': self.logs.logger.error('Problems logging to %s',self.conf.zabbix_api_url) zabbix_auth_token_file = os.getenv('HOME') + '/.zabbix-cli_auth_token' if os.path.isfile(zabbix_auth_token_file): try: os.remove(zabbix_auth_token_file) if self.conf.logging == 'ON': self.logs.logger.info('API-auth-token has probably expired. File %s deleted.',zabbix_auth_token_file) except Exception as e: print '\n[ERROR]: ',e if self.conf.logging == 'ON': self.logs.logger.error('Problems deleting file %s - %s',zabbix_auth_token_file,e) sys.exit(1) sys.exit(1) # ############################################ # Method show_maintenance_definitions # ############################################ def do_show_maintenance_definitions(self,args): ''' DESCRIPTION: This command shows maintenance definitions global information. The logical operator AND will be used if one defines more than one parameter. COMMAND: show_maintenance_definitions [definitionID] [hostgroup] [host] [definitionID] -------------- Definition ID. One can define more than one value. [hostgroup]: ------------ Hostgroup name. One can define more than one value. [host]: ------- Hostname. One can define more than one value. ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' maintenances = raw_input('# MaintenanceID [*]: ').strip() hostgroups = raw_input('# Hostgroups [*]: ').strip() hostnames = raw_input('# Hosts [*]: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command with parameters # elif len(arg_list) == 3: maintenances = arg_list[0].strip() hostgroups = arg_list[1].strip() hostnames = arg_list[2].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Generate maintenances, hosts and hostgroups IDs # try: maintenance_list = [] hostnames_list = [] hostgroups_list = [] maintenance_ids = '' hostgroup_ids = '' host_ids = '' search_data = ',' if maintenances == '*': maintenances = '' if hostgroups == '*': hostgroups = '' if hostnames == '*': hostnames = '' if maintenances != '': for maintenance in maintenances.split(','): maintenance_list.append(str(maintenance).strip()) maintenance_ids = "','".join(maintenance_list) search_data += '\'maintenanceids\':[\'' + maintenance_ids + '\'],' if hostgroups != '': for hostgroup in hostgroups.split(','): if hostgroup.isdigit(): hostgroups_list.append(str(hostgroup).strip()) else: hostgroups_list.append(str(self.get_hostgroup_id(hostgroup.strip()))) hostgroup_ids = "','".join(hostgroups_list) search_data += '\'groupids\':[\'' + hostgroup_ids + '\'],' if hostnames != '': for hostname in hostnames.split(','): if hostname.isdigit(): hostnames_list.append(str(hostname).strip()) else: hostnames_list.append(str(self.get_host_id(hostname.strip()))) host_ids = "','".join(hostnames_list) search_data += '\'hostids\':[\'' + host_ids + '\'],' except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting maintenance definitions information - %s',e) self.generate_feedback('Error','Problems getting maintenance definitions information') return False # # Get result from Zabbix API # try: query=ast.literal_eval("{'output':'extend'" + search_data + "'selectGroups':['name'],'selectHosts':['name'],'sortfield':'maintenanceid','sortorder':'ASC','searchByAny':'True'}") result = self.zapi.maintenance.get(**query) if self.conf.logging == 'ON': self.logs.logger.info('Command show_maintenance_definitions executed') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting maintenance definitions information - %s',e) self.generate_feedback('Error','Problems getting maintenance definitions information') return False # # Get the columns we want to show from result # for maintenance in result: if (int(time.time()) - int(maintenance['active_till'])) > 0: state = 'Expired' else: state = 'Active' if self.output_format == 'json': result_columns [result_columns_key] = {'maintenanceid':maintenance['maintenanceid'], 'name':maintenance['name'], 'maintenance_type':self.get_maintenance_type(int(maintenance['maintenance_type'])), 'state':state, 'active_till':maintenance['active_till'], 'hosts':maintenance['hosts'], 'groups':maintenance['groups'], 'description':maintenance['description']} else: host_list = [] maintenance['hosts'].sort() for host in maintenance['hosts']: host_list.append(host['name']) group_list = [] maintenance['groups'].sort() for group in maintenance['groups']: group_list.append(group['name']) result_columns [result_columns_key] = {'1':maintenance['maintenanceid'], '2':'\n'.join(textwrap.wrap(maintenance['name'],30)), '3':self.get_maintenance_type(int(maintenance['maintenance_type'])), '4':state, '5':datetime.datetime.utcfromtimestamp(float(maintenance['active_till'])).strftime('%Y-%m-%dT%H:%M:%SZ'), '6':'\n'.join(host_list), '7':'\n'.join(group_list), '8':'\n'.join(textwrap.wrap(maintenance['description'],40))} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['ID','Name','Type','State','To','Hostnames','Hostgroups','Description'], ['Name','Description','Hostnames','Hostgroups'], ['ID'], ALL) # ############################################ # Method show_maintenance_periods # ############################################ def do_show_maintenance_periods(self,args): ''' DESCRIPTION: This command shows maintenance periods global information. COMMAND: show_maintenance_periods [definitionID] [definitionID] -------------- Definition ID. One can define more than one value. ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' maintenances = raw_input('# MaintenanceID [*]: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command with parameters # elif len(arg_list) == 1: maintenances = arg_list[0].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Generate maintenances, hosts and hostgroups IDs # try: search_data = ',' maintenance_list = [] maintenance_ids = '' if maintenances == '*': maintenances = '' if maintenances != '': for maintenance in maintenances.split(','): maintenance_list.append(str(maintenance).strip()) maintenance_ids = "','".join(maintenance_list) search_data += '\'maintenanceids\':[\'' + maintenance_ids + '\'],' except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting maintenance periods information - %s',e) self.generate_feedback('Error','Problems getting maintenance periods information') return False # # Get result from Zabbix API # try: query=ast.literal_eval("{'output':'extend'" + search_data + "'selectGroups':['name'],'selectHosts':['name'],'selectTimeperiods':['timeperiodid','day','dayofweek','every','month','period','start_date','start_time','timeperiod_type'],'sortfield':'maintenanceid','sortorder':'ASC','searchByAny':'True'}") result = self.zapi.maintenance.get(**query) if self.conf.logging == 'ON': self.logs.logger.info('Command show_maintenance_periods executed') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting maintenance periods information - %s',e) self.generate_feedback('Error','Problems getting maintenance periods information') return False # # Get the columns we want to show from result # for maintenance in result: if self.output_format == 'json': result_columns [result_columns_key] = {'maintenanceid':maintenance['maintenanceid'], 'name':maintenance['name'], 'timeperiods':maintenance['timeperiods'], 'hosts':maintenance['hosts'], 'groups':maintenance['groups']} result_columns_key = result_columns_key + 1 else: host_list = [] maintenance['hosts'].sort() for host in maintenance['hosts']: host_list.append(host['name']) group_list = [] maintenance['groups'].sort() for group in maintenance['groups']: group_list.append(group['name']) for period in maintenance['timeperiods']: result_columns [result_columns_key] = {1:maintenance['maintenanceid'], 2:'\n'.join(textwrap.wrap(maintenance['name'],30)), 3:period['timeperiodid'], 4:period['day'], 5:str(bin(int(period['dayofweek'])))[2:], 6:period['every'], 7:str(bin(int(period['month'])))[2:], 8:datetime.datetime.utcfromtimestamp(float(period['start_date'])).strftime('%Y-%m-%dT%H:%M:%SZ'), 9:str(datetime.timedelta(seconds=long(period['start_time']))), 10:str(datetime.timedelta(seconds=long(period['period']))), 11:self.get_maintenance_period_type(int(period['timeperiod_type'])), 12:'\n'.join(host_list), 13:'\n'.join(group_list)} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['DefID','DefName','PerID','Days','Dayweek','Every','Month','Start_date','Start_time','Period','PerType','Hostnames','Hostgroups'], ['DefName','Hostnames','Hostgroups'], ['DefID'], ALL) # ############################################ # Method show_zabbixcli_config # ############################################ def do_show_zabbixcli_config(self,args): ''' DESCRIPTION: This command shows information about the configuration used by this zabbix-cli instance. COMMAND: show_zabbixcli_config ''' result_columns = {} result_columns_key = 0 # Generate information of the configuration files that are # active and exist in this host. # # This is the priority list of configuration files that can # exist in the system. Files close to the top of the list will # have priority to define configuration parameters in the # system. # # 1. /usr/share/zabbix-cli/zabbix-cli.fixed.conf # 2. /etc/zabbix-cli/zabbix-cli.fixed.conf # 3. Configuration file defined with the parameter -c / --config when executing zabbix-cli # 4. $HOME/.zabbix-cli/zabbix-cli.conf # 5. /etc/zabbix-cli/zabbix-cli.conf # 6. /usr/share/zabbix-cli/zabbix-cli.conf # config_file_list = ['/usr/share/zabbix-cli/zabbix-cli.fixed.conf', '/etc/zabbix-cli/zabbix-cli.fixed.conf', self.conf.config_file_from_parameter, os.getenv('HOME') + '/.zabbix-cli/zabbix-cli.conf', '/etc/zabbix-cli/zabbix-cli.conf', '/usr/share/zabbix-cli/zabbix-cli.conf'] for file in config_file_list: if os.path.isfile(file): result_columns [result_columns_key] = {1:'*' + file} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['Active configuration files'], ['Active configuration files'], [''], FRAME) # # Generate information with all the configuration parameters # result_columns = {} result_columns [0] = {1:'zabbix_api_url',2:self.conf.zabbix_api_url} result_columns [1] = {1:'system_id',2:self.conf.system_id} result_columns [2] = {1:'default_hostgroup',2:self.conf.default_hostgroup} result_columns [3] = {1:'default_admin_usergroup',2:self.conf.default_admin_usergroup} result_columns [4] = {1:'default_create_user_usergroup',2:self.conf.default_create_user_usergroup} result_columns [5] = {1:'default_notification_users_usergroup',2:self.conf.default_notification_users_usergroup} result_columns [6] = {1:'default_directory_exports',2:self.conf.default_directory_exports} result_columns [7] = {1:'default_export_format',2:self.conf.default_export_format} result_columns [8] = {1:'include_timestamp_export_filename',2:self.conf.include_timestamp_export_filename} result_columns [9] = {1:'use_colors',2:self.conf.use_colors} result_columns [10] = {1:'use_auth_token_file',2:self.conf.use_auth_token_file} result_columns [11] = {1:'logging',2:self.conf.logging} result_columns [12] = {1:'log_level',2:self.conf.log_level} result_columns [13] = {1:'log_file',2:self.conf.log_file} # # Generate output # self.generate_output(result_columns, ['Configuration parameter','Value'], ['Value'], ['Configuration parameter'], FRAME) # ############################################ # Method show_hostgroups # ############################################ def do_show_hostgroups(self,args): ''' DESCRIPTION: This command shows all hostgroups defined in the system. COMMAND: show_hostgroups ''' cmd.Cmd.onecmd(self,'show_hostgroup "*"') # ############################################ # Method show_hostgroup # ############################################ def do_show_hostgroup(self,args): ''' DESCRIPTION: This command shows hostgroup information COMMAND: show_hostgroup [hostgroup] [hostgroup]: ---------------- Hostgroup name. One can use wildcards. ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' hostgroup = raw_input('# Hostgroup: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command with parameters # elif len(arg_list) == 1: hostgroup = arg_list[0].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if hostgroup == '': self.generate_feedback('Error','Template value is empty') return False # # Get result from Zabbix API # try: result = self.zapi.hostgroup.get(output='extend', search={'name':hostgroup}, searchWildcardsEnabled=True, selectHosts=['host'], sortfield='name', sortorder='ASC') if self.conf.logging == 'ON': self.logs.logger.info('Command show_hostgroups executed') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting hostgroups information - %s',e) self.generate_feedback('Error','Problems getting hostgroups information') return False # # Get the columns we want to show from result # for group in result: if self.output_format == 'json': group['hosts'].sort() result_columns [result_columns_key] = {'groupid':group['groupid'], 'name':group['name'], 'flags':self.get_hostgroup_flag(int(group['flags'])), 'type':self.get_hostgroup_type(int(group['internal'])), 'hosts':group['hosts']} else: host_list = [] group['hosts'].sort() for host in group['hosts']: host_list.append(host['host']) result_columns [result_columns_key] = {'1':group['groupid'], '2':group['name'], '3':self.get_hostgroup_flag(int(group['flags'])), '4':self.get_hostgroup_type(int(group['internal'])), '5':'\n'.join(textwrap.wrap(', '.join(host_list),60))} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['GroupID','Name','Flag','Type','Hosts'], ['Name','Hosts'], ['GroupID'], ALL) # ############################################ # Method show_hosts # ############################################ def do_show_hosts(self,args): ''' DESCRIPTION: This command shows all hosts defined in the system. COMMAND: show_hosts ''' cmd.Cmd.onecmd(self,'show_host "*"') # ############################################ # Method show_host # ############################################ def do_show_host(self,args): ''' DESCRIPTION: This command shows hosts information COMMAND: show_host [HostID / Hostname] [Filter] [HostID / Hostname]: ------------------- One can search by HostID or by Hostname. One can use wildcards if we search by Hostname [Filter]: -------- * Zabbix agent: 'available': 0=Unknown 1=Available 2=Unavailable * Maintenance: 'maintenance_status': 0:No maintenance 1:In progress * Status: 'status': 0:Monitored 1: Not monitored e.g.: Show all hosts with Zabbix agent: Available AND Status: Monitored: show_host * "'available':'1','status':'0'" ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' host = raw_input('# Host: ').strip() filter = raw_input('# Filter: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 1: host = arg_list[0].strip() filter = '' # # Command with filters attributes # elif len(arg_list) == 2: host = arg_list[0].strip() filter = arg_list[1].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Check if we are searching by hostname or hostID # if host.isdigit(): search_host = '\'hostids\':\'' + host + '\'' else: search_host = '\'search\':{\'host\':\'' + host + '\'}' # # Generate query # try: query=ast.literal_eval("{'output':'extend'," + search_host + ",'selectParentTemplates':['templateid','name'],'selectGroups':['groupid','name'],'selectApplications':['name'],'sortfield':'host','sortorder':'ASC','searchWildcardsEnabled':'True','filter':{" + filter + "}}") except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems generating show_host query - %s',e) self.generate_feedback('Error','Problems generating show_host query') return False # # Get result from Zabbix API # try: result = self.zapi.host.get(**query) if self.conf.logging == 'ON': self.logs.logger.info('Command show_host executed.') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting host information - %s',e) self.generate_feedback('Error','Problems getting host information') return False # # Get the columns we want to show from result # for host in result: if self.output_format == 'json': result_columns [result_columns_key] = {'hostid':host['hostid'], 'host':host['host'], 'groups':host['groups'], 'templates':host['parentTemplates'], 'zabbix_agent':self.get_zabbix_agent_status(int(host['available'])), 'maintenance_status':self.get_maintenance_status(int(host['maintenance_status'])), 'status':self.get_monitoring_status(int(host['status']))} else: hostgroup_list = [] template_list = [] host['groups'].sort() host['parentTemplates'].sort() for hostgroup in host['groups']: hostgroup_list.append(hostgroup['name']) for template in host['parentTemplates']: template_list.append(template['name']) result_columns [result_columns_key] = {'1':host['hostid'], '2':host['host'], '3':'\n'.join(hostgroup_list), '4':'\n'.join(template_list), '5':self.get_zabbix_agent_status(int(host['available'])), '6':self.get_maintenance_status(int(host['maintenance_status'])), '7':self.get_monitoring_status(int(host['status']))} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['HostID','Name','Hostgroups','Templates','Zabbix agent','Maintenance','Status'], ['Name','Hostgroups','Templates'], ['HostID'], ALL) # ############################################ # Method update_host_inventory # ############################################ def do_update_host_inventory(self,args): ''' DESCRIPTION: This command updates one hosts' inventory COMMAND: update_host_inventory [hostname] [inventory_key] [inventory value] Inventory key is not the same as seen in web-gui. To look at possible keys and their current values, use "zabbix-cli --use-json-format show_host_inventory " ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' host = raw_input('# Host: ') inventory_key = raw_input('# Inventory key: ') inventory_value = raw_input('# Inventory value: ') print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without inventory_key and inventory value attributes # elif len(arg_list) == 1: host = arg_list[0] inventory_key = raw_input('# Inventory key: ') inventory_value = raw_input('# Inventory value: ') # # Command cithout inventory value attribute # elif len(arg_list) == 2: host = arg_list[0] inventory_key = arg_list[1] inventory_value = raw_input('# Inventory value: ') elif len(arg_list) == 3: host = arg_list[0] inventory_key = arg_list[1] inventory_value = arg_list[2] # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False try: host_id = str(self.get_host_id(host)) except Exception as e: print '\n[ERROR]: ',e,'\n' return False # # Generate query # if host_id == '0': self.generate_feedback('Error','Host id for "' + host + '" not found') return False update_id = "'hostid': '" + host_id +"'" update_value = "'inventory': {'" + inventory_key + "':'" + inventory_value +"'}" try: query = ast.literal_eval("{" + update_id + "," + update_value + "}") except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems generating query - %s',e) self.generate_feedback('Error','Problems generating query') return False # # Get result from Zabbix API # try: result = self.zapi.host.update(**query) if self.conf.logging == 'ON': self.logs.logger.info('Command update_host_inventory executed [%s] [%s] [%s].',host,inventory_key,inventory_value) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems updating host inventory information - %s',e) self.generate_feedback('Error','Problems updating host inventory information') return False # ############################################ # Method show_host_inventory # ############################################ def do_show_host_inventory(self,args): ''' DESCRIPTION: This command shows hosts inventory COMMAND: show_host_inventory [Hostname] [Hostname]: ---------- Hostname. ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' host = raw_input('# Host: ').strip() filter = raw_input('# Filter: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # # elif len(arg_list) == 1: host = arg_list[0].strip() filter = '' # # Command with filters attributes # elif len(arg_list) == 2: host = arg_list[0].strip() filter = arg_list[1].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Check if we are searching by hostname or hostID # if host.isdigit(): search_host = '\'hostids\':\'' + host + '\'' else: search_host = '\'search\':{\'host\':\'' + host + '\'}' # # Generate query # try: query=ast.literal_eval("{'output':'extend'," + search_host + ",'selectInventory':'extend','sortfield':'host','sortorder':'ASC','searchWildcardsEnabled':'True','filter':{" + filter + "}}") except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems generating query - %s',e) self.generate_feedback('Error','Problems generating query') return False # # Get result from Zabbix API # try: result = self.zapi.host.get(**query) if self.conf.logging == 'ON': self.logs.logger.info('Command show_host_inventory [%s] executed.',host) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting host inventory information - %s',e) self.generate_feedback('Error','Problems getting host inventory information') return False # # Get the columns we want to show from result if the host has # some inventory data # for host in result: if host['inventory'] != []: if self.output_format == 'json': result_columns [result_columns_key] = dict({"host":host['host']}.items() + host['inventory'].items()) else: result_columns [result_columns_key] = {'1':host['host'], '2':host['inventory']['vendor'], '3':host['inventory']['chassis'], '4':host['inventory']['host_router'], '5':host['inventory']['poc_1_email']} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['Hostname','Vendor','Chassis','Networks','Contact'], ['Hostname','Chassis','Contact'], [], FRAME) # ############################################ # Method show_usergroups # ############################################ def do_show_usergroups(self,args): ''' DESCRIPTION: This command shows all usergroups defined in the system. COMMAND: show_usergroups ''' cmd.Cmd.onecmd(self,'show_usergroup "*"') # ############################################ # Method show_usergroup # ############################################ def do_show_usergroup(self,args): ''' DESCRIPTION: This command shows user group information. COMMAND: show_usergroup [usergroup] [usergroup] ----------- User group name. One can use wildcards. ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' usergroup = raw_input('# Usergroup: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command with parameters # elif len(arg_list) == 1: usergroup = arg_list[0].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if usergroup == '': self.generate_feedback('Error','Usergroup value is empty') return False # # Get result from Zabbix API # try: result = self.zapi.usergroup.get(output='extend', search={'name':usergroup}, searchWildcardsEnabled=True, sortfield='name', sortorder='ASC', selectUsers=['alias']) if self.conf.logging == 'ON': self.logs.logger.info('Command show_usergroup executed') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting usergroup information - %s',e) self.generate_feedback('Error','Problems getting usergroup information') return False # # Get the columns we want to show from result # for group in result: if self.output_format == 'json': group['users'].sort() result_columns [result_columns_key] ={'usrgrpid':group['usrgrpid'], 'name':group['name'], 'gui_access':self.get_gui_access(int(group['gui_access'])), 'user_status':self.get_usergroup_status(int(group['users_status'])), 'users':group['users']} else: users = [] group['users'].sort() for user in group['users']: users.append(user['alias']) result_columns [result_columns_key] ={'1':group['usrgrpid'], '2':group['name'], '3':self.get_gui_access(int(group['gui_access'])), '4':self.get_usergroup_status(int(group['users_status'])), '5':'\n'.join(textwrap.wrap(', '.join(users),60))} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['GroupID','Name','GUI access','Status','Users'], ['Name','Users'], ['GroupID'], FRAME) # ############################################ # Method show_users # ############################################ def do_show_users(self,args): ''' DESCRIPTION: This command shows users information. COMMAND: show_users ''' result_columns = {} result_columns_key = 0 # # Get result from Zabbix API # try: result = self.zapi.user.get(output='extend', getAccess=True, selectUsrgrps=['name'], sortfield='alias', sortorder='ASC') if self.conf.logging == 'ON': self.logs.logger.info('Command show_users executed') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting users information - %s',e) self.generate_feedback('Error','Problems getting users information') return False # # Get the columns we want to show from result # for user in result: if self.output_format == 'json': result_columns [result_columns_key] ={'userid':user['userid'], 'alias':user['alias'], 'name':user['name'] + ' ' + user['surname'], 'autologin':self.get_autologin_type(int(user['autologin'])), 'autologout':user['autologout'], 'type':self.get_user_type(int(user['type'])), 'usrgrps':user['usrgrps']} else: usrgrps = [] for group in user['usrgrps']: usrgrps.append(group['name']) result_columns [result_columns_key] ={'1':user['userid'], '2':user['alias'], '3':user['name'] + ' ' + user['surname'], '4':self.get_autologin_type(int(user['autologin'])), '5':user['autologout'], '6':self.get_user_type(int(user['type'])), '7':'\n'.join(textwrap.wrap(', '.join(usrgrps),60))} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['UserID','Alias','Name','Autologin','Autologout','Type','Usrgrps'], ['Name','Type','Usrgrps'], ['UserID'], FRAME) # ############################################ # Method show_alarms # ############################################ def do_show_alarms(self,args): ''' DESCRIPTION: This command shows all active alarms with the last event unacknowledged. COMMAND: show_alarms [description] [filters] [hostgroups] [Last event unacknowledged] [description] ------------- Type of alarm description to search for. Leave this parameter empty to search for all descriptions. One can also use wildcards. [filters] --------- One can filter the result by host and priority. No wildcards can be used. Priority values: 0 - (default) not classified; 1 - information; 2 - warning; 3 - average; 4 - high; 5 - disaster. [hostgroups] ----------- One can filter the result to get alarms from a particular hostgroup or group og hostgroups. One can define several values in a comma separated list. [Last event unacknowledged] --------------------------- One can filter the result after the acknowledged value of the last event of an alarm. Values: true - (default) Show only active alarms with last event unacknowledged. false - Show all active alarms, also those with the last event acknowledged. e.g.: Get all alarms with priority 'High' that contain the word 'disk' in the description for the host 'host.example.org' and the last event unacknowledged show_alarms *disk* "'host':'host.example.org','priority':'4'" * true ''' result_columns = {} result_columns_key = 0 filters = '' hostgroup_list = [] try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' description = raw_input('# Description []: ').strip() filters = raw_input('# Filter []: ').strip() hostgroups = raw_input('# Hostgroups []: ').strip() ack_filter = raw_input('# Last event unacknowledged [true]: ').strip().lower() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 4: description = arg_list[0].strip() filters = arg_list[1].strip() hostgroups = arg_list[2].strip() ack_filter = arg_list[3].strip().lower() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if ack_filter in ['','true']: ack_filter = ",'withLastEventUnacknowledged':'True'" elif ack_filter in ['*','false']: ack_filter = "" else: ack_filter = ",'withLastEventUnacknowledged':'True'" if filters == '*': filters = '' if filters != '': filters = ',' + filters if hostgroups == '' or hostgroups == '*': groupids = '' else: # # Generate a list with all hostgroupsIDs from the defined # hostgroups # for hostgroup in hostgroups.split(','): if hostgroup.isdigit() == True: hostgroup_list.append(hostgroup) else: try: hostgroup_list.append(self.get_hostgroup_id(hostgroup.strip())) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting the hostgroupID for %s - %s',hostgroup,e) self.generate_feedback('Error','Problems getting the hostgroupID for [' + hostgroup + ']') return False groupids = "'groupids':['" + "','".join(hostgroup_list) + "']" # # Generate query # try: query=ast.literal_eval("{'selectHosts':'host'" + ack_filter + ",'search':{'description':'" + description + "'},'skipDependent':1,'monitored':1,'active':1,'output':'extend','expandDescription':1,'sortfield':'lastchange','sortorder':'DESC','searchWildcardsEnabled':'True','filter':{'value':'1'" + filters + "}," + groupids + "}") except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems generating show_alarms query - %s',e) self.generate_feedback('Error','Problems generating show_alarms query') return False # # Get result from Zabbix API # try: result = self.zapi.trigger.get(**query) if self.conf.logging == 'ON': self.logs.logger.info('Command show_alarms executed') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting alarm information - %s',e) self.generate_feedback('Error','Problems getting alarm information') return False # # Get the columns we want to show from result # for trigger in result: lastchange = datetime.datetime.fromtimestamp(int(trigger['lastchange'])) age = datetime.datetime.now() - lastchange if self.output_format == 'json': result_columns [result_columns_key] = {'triggerid':trigger['triggerid'], 'hostname':self.get_host_name(trigger['hosts'][0]['hostid']), 'description':trigger['description'], 'severity':self.get_trigger_severity(int(trigger['priority'])), 'lastchange':str(lastchange), 'age':str(age)} else: if self.use_colors == 'ON': if int(trigger['priority']) == 1: ansi_code = "\033[38;5;158m" ansi_end = "\033[0m" elif int(trigger['priority']) == 2: ansi_code = "\033[38;5;190m" ansi_end = "\033[0m" elif int(trigger['priority']) == 3: ansi_code = "\033[38;5;208m" ansi_end = "\033[0m" elif int(trigger['priority']) == 4: ansi_code = "\033[38;5;160m" ansi_end = "\033[0m" elif int(trigger['priority']) == 5: ansi_code = "\033[38;5;196m" ansi_end = "\033[0m" else: ansi_code = '' ansi_end = '' else: ansi_code = '' ansi_end = '' result_columns [result_columns_key] = {'1':trigger['triggerid'], '2':self.get_host_name(trigger['hosts'][0]['hostid']), '3':'\n '.join(textwrap.wrap("* " + trigger['description'],62)), '4':ansi_code + self.get_trigger_severity(int(trigger['priority'])).upper() + ansi_end, '5':str(lastchange), '6':str(age)} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['TriggerID','Host','Description','Severity','Last change', 'Age'], ['Host','Description','Last change','Age'], ['TriggerID'], FRAME) # ############################################ # Method do_add_host_to_hostgroup # ############################################ def do_add_host_to_hostgroup(self,args): ''' DESCRIPTION: This command adds one/several hosts to one/several hostgroups COMMAND: add_host_to_hostgroup [hostnames] [hostgroups] [hostnames] ----------- Hostnames or IDs. One can define several values in a comma separated list. [hostgroups] ------------ Hostgroup names or IDs. One can define several values in a comma separated list. ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' hostnames = raw_input('# Hostnames: ').strip() hostgroups = raw_input('# Hostgroups: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 2: hostnames = arg_list[0].strip() hostgroups = arg_list[1].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if hostgroups == '': self.generate_feedback('Error','Hostgroups information is empty') return False if hostnames == '': self.generate_feedback('Error','Hostnames information is empty') return False try: # # Generate hosts and hostgroups IDs # hostgroups_list = [] hostnames_list = [] hostgroup_ids = '' host_ids = '' for hostgroup in hostgroups.split(','): if hostgroup.isdigit(): hostgroups_list.append('{"groupid":"' + str(hostgroup).strip() + '"}') else: hostgroups_list.append('{"groupid":"' + str(self.get_hostgroup_id(hostgroup.strip())) + '"}') hostgroup_ids = ','.join(hostgroups_list) for hostname in hostnames.split(','): if hostname.isdigit(): hostnames_list.append('{"hostid":"' + str(hostname).strip() + '"}') else: hostnames_list.append('{"hostid":"' + str(self.get_host_id(hostname.strip())) + '"}') host_ids = ','.join(hostnames_list) # # Generate zabbix query # query=ast.literal_eval("{\"groups\":[" + hostgroup_ids + "],\"hosts\":[" + host_ids + "]}") # # Add hosts to hostgroups # result = self.zapi.hostgroup.massadd(**query) self.generate_feedback('Done','Hosts ' + hostnames + ' (' + host_ids + ') added to these groups: ' + hostgroups + ' (' + hostgroup_ids + ')') if self.conf.logging == 'ON': self.logs.logger.info('Hosts: %s (%s) added to these groups: %s (%s)',hostnames,host_ids,hostgroups,hostgroup_ids) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems adding hosts %s (%s) to groups %s (%s) - %s',hostnames,host_ids,hostgroups,hostgroup_ids,e) self.generate_feedback('Error','Problems adding hosts ' + hostnames + ' (' + host_ids + ') to groups ' + hostgroups + ' (' + hostgroup_ids + ')') return False # ############################################ # Method do_remove_host_from_hostgroup # ############################################ def do_remove_host_from_hostgroup(self,args): ''' DESCRIPTION: This command removes one/several hosts from one/several hostgroups COMMAND: remove_host_from_hostgroup [hostnames] [hostgroups] [hostnames] ----------- Hostnames or IDs. One can define several values in a comma separated list. [hostgroups] ------------ Hostgroup names or IDs. One can define several values in a comma separated list. ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' hostnames = raw_input('# Hostnames: ').strip() hostgroups = raw_input('# Hostgroups: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command with parameters # elif len(arg_list) == 2: hostnames = arg_list[0].strip() hostgroups = arg_list[1].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('ERROR',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if hostgroups == '': self.generate_feedback('Error','Hostgroups information is empty') return False if hostnames == '': self.generate_feedback('Error','Hostnames information is empty') return False try: # # Generate hosts and hostgroups IDs # hostgroups_list = [] hostnames_list = [] hostgroup_ids = '' host_ids = '' for hostgroup in hostgroups.split(','): if hostgroup.isdigit(): hostgroups_list.append(str(hostgroup).strip()) else: hostgroups_list.append(str(self.get_hostgroup_id(hostgroup.strip()))) hostgroup_ids = ','.join(hostgroups_list) for hostname in hostnames.split(','): if hostname.isdigit(): hostnames_list.append(str(hostname).strip()) else: hostnames_list.append(str(self.get_host_id(hostname.strip()))) host_ids = ','.join(hostnames_list) # # Generate zabbix query # query=ast.literal_eval("{\"groupids\":[" + hostgroup_ids + "],\"hostids\":[" + host_ids + "]}") # # Remove hosts from hostgroups # result = self.zapi.hostgroup.massremove(**query) if self.conf.logging == 'ON': self.logs.logger.info('Hosts: %s (%s) removed from these groups: %s (%s)',hostnames,host_ids,hostgroups,hostgroup_ids) self.generate_feedback('Done','Hosts ' + hostnames + ' (' + host_ids + ') removed from these groups: ' + hostgroups + ' (' + hostgroup_ids + ')') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems removing hosts %s (%s) from groups %s (%s) - %s',hostnames,host_ids,hostgroups,hostgroup_ids,e) self.generate_feedback('Error','Problems removing hosts ' + hostnames + ' (' + host_ids + ') from groups (' + hostgroups + ' (' + hostgroup_ids + ')' ) return False # ############################################ # Method do_add_user_to_usergroup # ############################################ def do_add_user_to_usergroup(self,args): ''' DESCRIPTION: This command adds one/several users to one/several usergroups COMMAND: add_user_to_usergroup [usernames] [usergroups] [usernames] ----------- Usernames or IDs. One can define several values in a comma separated list. [usergroups] ------------ Usergroup names or IDs. One can define several values in a comma separated list. ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' usernames = raw_input('# Usernames: ').strip() usergroups = raw_input('# Usergroups: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 2: usernames = arg_list[0].strip() usergroups = arg_list[1].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if usergroups == '': self.generate_feedback('Error','Usergroups value is empty') return False if usernames == '': self.generate_feedback('Error','Usernames value is empty') return False try: # # Generate users and usergroups IDs # usergroups_list = [] usernames_list = [] for usergroup in usergroups.split(','): if usergroup.isdigit(): usergroups_list.append(str(usergroup).strip()) else: usergroups_list.append(str(self.get_usergroup_id(usergroup.strip()))) for username in usernames.split(','): if username.isdigit(): usernames_list.append(str(username).strip()) else: usernames_list.append(str(self.get_user_id(username.strip()))) # # Add users to usergroups # result = self.zapi.usergroup.massadd(usrgrpids=usergroups_list,userids=usernames_list) self.generate_feedback('Done','Users ' + usernames + ' added to these usergroups: ' + usergroups) if self.conf.logging == 'ON': self.logs.logger.info('Users: %s added to these usergroups: %s',usernames,usergroups) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems adding users %s to usergroups %s - %s',usernames,usergroups,e) self.generate_feedback('Error','Problems adding users ' + usernames + ' to usergroups ' + usergroups) return False # ############################################ # Method do_add_user_to_usergroup # ############################################ def do_remove_user_from_usergroup(self,args): ''' DESCRIPTION: This command removes an user from one/several usergroups COMMAND: remove_user_to_usergroup [username] [usergroups] [username] ----------- Username to remove [usergroups] ------------ Usergroup names from where the username will be removed. One can define several values in a comma separated list. ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' username = raw_input('# Username: ').strip() usergroups = raw_input('# Usergroups: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 2: username = arg_list[0].strip() usergroups = arg_list[1].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if usergroups == '': self.generate_feedback('Error','Usergroups value is empty') return False if username == '': self.generate_feedback('Error','Username value is empty') return False user_to_remove = [] user_to_remove.append(username) try: for usergroup in usergroups.split(','): usergroup = usergroup.strip() usernames_list_orig = [] usernames_list_final = [] usernameids_list_final = [] # # Get list with users to keep in this usergroup # result = self.zapi.usergroup.get(output='extend', search={'name':usergroup}, searchWildcardsEnabled=True, sortfield='name', sortorder='ASC', selectUsers=['alias']) for users in result: for alias in users['users']: usernames_list_orig.append(alias['alias']) usernames_list_final = list(set(usernames_list_orig) - set(user_to_remove)) # # Update usergroup with the new users list # usergroupid = self.get_usergroup_id(usergroup) for user in usernames_list_final: usernameids_list_final.append(self.get_user_id(user)) result = self.zapi.usergroup.update(usrgrpid=usergroupid,userids=usernameids_list_final) self.generate_feedback('Done','User ' + username + ' removed from this usergroup: ' + usergroup) if self.conf.logging == 'ON': self.logs.logger.info('User: %s removed from this usergroup: %s',username,usergroup) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems removing user %s from usergroups %s - %s',username,usergroups,e) self.generate_feedback('Error','Problems removing user ' + username + ' from usergroups ' + usergroups) return False # ############################################ # Method do_link_template_to_host # ############################################ def do_link_template_to_host(self,args): ''' DESCRIPTION: This command links one/several templates to one/several hosts COMMAND: link_template_to_host [templates] [hostnames] [templates] ------------ Template names or IDs. One can define several values in a comma separated list. [hostnames] ----------- Hostnames or IDs. One can define several values in a comma separated list. ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' templates = raw_input('# Templates: ').strip() hostnames = raw_input('# Hostnames: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 2: templates = arg_list[0].strip() hostnames = arg_list[1].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('ERROR',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if templates == '': self.generate_feedback('Error','Templates information is empty') return False if hostnames == '': self.generate_feedback('Error','Hostnames information is empty') return False try: # # Generate templates and hosts IDs # templates_list = [] hostnames_list = [] template_ids = '' host_ids = '' for template in templates.split(','): if template.isdigit(): templates_list.append('{"templateid":"' + str(template).strip() + '"}') else: templates_list.append('{"templateid":"' + str(self.get_template_id(template.strip())) + '"}') template_ids = ','.join(templates_list) for hostname in hostnames.split(','): if hostname.isdigit(): hostnames_list.append('{"hostid":"' + str(hostname).strip() + '"}') else: hostnames_list.append('{"hostid":"' + str(self.get_host_id(hostname.strip())) + '"}') host_ids = ','.join(hostnames_list) # # Generate zabbix query # query=ast.literal_eval("{\"templates\":[" + template_ids + "],\"hosts\":[" + host_ids + "]}") # # Link templates to hosts # result = self.zapi.template.massadd(**query) if self.conf.logging == 'ON': self.logs.logger.info('Templates: %s (%s) linked to these hosts: %s (%s)',templates,template_ids,hostnames,host_ids) self.generate_feedback('Done','Templates ' + templates + ' (' + template_ids + ') linked to these hosts: ' + hostnames + ' (' + host_ids + ')') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems linking templates %s (%s) to hosts %s (%s) - %s',templates,template_ids,hostnames,host_ids,e) self.generate_feedback('Error','Problems linking templates ' + templates + ' (' + template_ids + ') to hosts ' + hostnames + ' (' + host_ids + ')') return False # ############################################ # Method do_unlink_template_from_host # ############################################ def do_unlink_template_from_host(self,args): ''' DESCRIPTION: This command unlink one/several templates from one/several hosts COMMAND: unlink_template_from_host [templates] [hostnames] [templates] ------------ Templates names or IDs. One can define several values in a comma separated list. [hostnames] ----------- Hostnames or IDs. One can define several values in a comma separated list. ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' templates = raw_input('# Templates: ').strip() hostnames = raw_input('# Hostnames: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 2: templates = arg_list[0].strip() hostnames = arg_list[1].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if templates == '': self.generate_feedback('Error','Templates information is empty') return False if hostnames == '': self.generate_feedback('Error','Hostnames information is empty') return False try: # # Generate templates and hosts IDs # templates_list = [] hostnames_list = [] template_ids = '' host_ids = '' for template in templates.split(','): if template.isdigit(): templates_list.append(str(template).strip()) else: templates_list.append(str(self.get_template_id(template.strip()))) template_ids = ','.join(templates_list) for hostname in hostnames.split(','): if hostname.isdigit(): hostnames_list.append(str(hostname).strip()) else: hostnames_list.append(str(self.get_host_id(hostname.strip()))) host_ids = ','.join(hostnames_list) # # Generate zabbix query # query=ast.literal_eval("{\"hostids\":[" + host_ids + "],\"templateids_clear\":[" + template_ids + "]}") # # Unlink templates from hosts # result = self.zapi.host.massremove(**query) if self.conf.logging == 'ON': self.logs.logger.info('Templates: %s (%s) unlinked and cleared from these hosts: %s (%s)',templates,template_ids,hostnames,host_ids) self.generate_feedback('Done','Templates ' + templates + ' (' + template_ids + ') unlinked and cleared from these hosts: ' + hostnames + ' (' + host_ids + ')') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems unlinking and clearing templates %s (%s) from hosts %s (%s) - %s',templates,template_ids,hostnames,host_ids,e) self.generate_feedback('Error','Problems unlinking and clearing templates ' + templates + ' (' + template_ids + ') from hosts ' + hostnames + ' (' + host_ids + ')') return False # ############################################ # Method do_create_usergroup # ############################################ def do_create_usergroup(self,args): ''' DESCRIPTION: This command creates an usergroup. COMMAND: create_usergroup [group name] [GUI access] [Status] [group name] ------------ Usergroup name [GUI access] ------------ 0:'System default' [*] 1:'Internal' 2:'Disable' [Status] -------- 0:'Enable' [*] 1:'Disable' ''' # Default 0: System default gui_access_default = '0' # Default 0: Enable users_status_default = '0' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' groupname = raw_input('# Name: ').strip() gui_access = raw_input('# GUI access ['+ gui_access_default + ']: ').strip() users_status = raw_input('# Status ['+ users_status_default + ']: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 3: groupname = arg_list[0].strip() gui_access = arg_list[1].strip() users_status = arg_list[2].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if gui_access == '' or gui_access not in ('0','1','2'): gui_access = gui_access_default if users_status == '' or users_status not in ('0','1'): users_status = users_status_default # # Check if usergroup exists # try: result = self.usergroup_exists(groupname) if self.conf.logging == 'ON': self.logs.logger.debug('Cheking if usergroup (%s) exists',groupname) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems checking if usergroup (%s) exists - %s',groupname,e) self.generate_feedback('Error','Problems checking if usergroup (' + groupname + ') exists') return False # # Create usergroup if it does not exist # try: if result == True: if self.conf.logging == 'ON': self.logs.logger.debug('Usergroup (%s) already exists',groupname) self.generate_feedback('Warning','This usergroup (' + groupname + ') already exists.') return False elif result == False: result = self.zapi.usergroup.create(name=groupname, gui_access=gui_access, users_status=users_status) if self.conf.logging == 'ON': self.logs.logger.info('Usergroup (%s) with ID: %s created',groupname,str(result['usrgrpids'][0])) self.generate_feedback('Done','Usergroup (' + groupname + ') with ID: ' + str(result['usrgrpids'][0]) + ' created.') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems creating Usergroup (%s) - %s',groupname,e) self.generate_feedback('Error','Problems creating usergroup (' + groupname + ')') return False # ############################################ # Method do_create_host # ############################################ def do_create_host(self,args): ''' DESCRIPTION: This command creates a host. COMMAND: create_host [hostname|IP] [hostgroups] [proxy] [status] [hostname|IP] ------------- Hostname or IP [hostgroups] ------------ Hostgroup names or IDs. One can define several values in a comma separated list. Remember that the host will get added to all hostgroups defined with the parameter 'default_hostgroup' in the zabbix-cli configuration file 'zabbix-cli.conf' This command will fail if both 'default_hostgroup' and [hostgroups] are empty. [proxy] ------- Proxy server used to monitor this host. One can use regular expressions to define a group of proxy servers from where the system will choose a random proxy. If this parameter is not defined, the system will assign a random proxy from the list of all available proxies. If the system does not have proxy servers defined, the new host will be monitor by the Zabbix-server. e.g. Some regular expressions that can be used: * proxy-(prod|test)+d\.example\.org e.g. proxy-prod1.example.org and proxy-test8.example.org will match this expression. * .+ All proxies will match this expression. [Status] -------- 0:'Monitored' [*] 1:'Unmonitored' All host created with this command will get assigned a default interface of type 'Agent' using the port 10050. ''' # Default hostgroups. # Use these values only if they exist. hostgroup_default = self.conf.default_hostgroup.strip() for hostgroup in self.conf.default_hostgroup.split(','): if self.hostgroup_exists(hostgroup.strip()) == False: hostgroup_default = '' break # Proxy server to use to monitor this host proxy_default = '.+' # Default 0: Enable host_status_default = '0' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' host = raw_input('# Hostname|IP: ').strip() hostgroups = raw_input('# Hostgroups[' + hostgroup_default + ']: ').strip() proxy = raw_input('# Proxy ['+ proxy_default + ']: ').strip() host_status = raw_input('# Status ['+ host_status_default + ']: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 4: host = arg_list[0].strip() hostgroups = arg_list[1].strip() proxy = arg_list[2].strip() host_status = arg_list[3].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if host == '': self.generate_feedback('Error','Hostname|IP value is empty') return False if proxy == '': proxy = proxy_default if host_status == '' or host_status not in ('0','1'): host_status = host_status_default # Generate interface definition. Per default all hosts get a # Zabbix agent and a SNMP interface defined try: # Check if we are using a hostname or an IP ipaddr.IPAddress(host) useip = '"useip":1,' interface_ip = '"ip":"' + host + '",' interface_dns = '"dns":"",' except ValueError: useip = '"useip":0,' interface_ip = '"ip":"",' interface_dns = '"dns":"' + host + '",' interfaces_def = '"interfaces":[' + \ '{"type":1,' + \ '"main":1,' + \ useip + \ interface_ip + \ interface_dns + \ '"port":"10050"},' + \ '{"type":2,' + \ '"main":1,' + \ useip + \ interface_ip + \ interface_dns + \ '"port":"161"}]' # # Generate hostgroups and proxy IDs # try: hostgroups_list = [] hostgroup_ids = '' for hostgroup in hostgroup_default.split(','): if hostgroup != '': hostgroups_list.append('{"groupid":"' + str(self.get_hostgroup_id(hostgroup.strip())) + '"}') for hostgroup in hostgroups.split(','): if hostgroup != '': if hostgroup.isdigit(): hostgroups_list.append('{"groupid":"' + str(hostgroup).strip() + '"}') else: hostgroups_list.append('{"groupid":"' + str(self.get_hostgroup_id(hostgroup.strip())) + '"}') hostgroup_ids = ','.join(set(hostgroups_list)) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('%s',e) self.generate_feedback('Error',e) return False try: proxy_id = str(self.get_random_proxyid(proxy.strip())) proxy_hostid = "\"proxy_hostid\":\"" + proxy_id + "\"," except Exception as e: if self.conf.logging == 'ON': self.logs.logger.debug('Host [%s] - %s',host,e) proxy_hostid = "" # # Checking if host exists # try: result = self.host_exists(host.strip()) if self.conf.logging == 'ON': self.logs.logger.debug('Cheking if host (%s) exists',host) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems checking if host (%s) exists - %s',host,e) self.generate_feedback('Error','Problems checking if host (' + host + ') exists') return False try: if result == True: if self.conf.logging == 'ON': self.logs.logger.debug('Host (%s) already exists',host) self.generate_feedback('Warning','This host (' + host + ') already exists.') return False elif result == False: # # Create host via Zabbix-API # query=ast.literal_eval("{\"host\":\"" + host + "\"," + "\"groups\":[" + hostgroup_ids + "]," + proxy_hostid + "\"status\":" + host_status + "," + interfaces_def + ",\"inventory_mode\":1,\"inventory\":{\"name\":\"" + host +"\"}}") result = self.zapi.host.create(**query) if self.conf.logging == 'ON': self.logs.logger.info('Host (%s) with ID: %s created',host,str(result['hostids'][0])) self.generate_feedback('Done','Host (' + host + ') with ID: ' + str(result['hostids'][0]) + ' created') # # Update the hostid cache with the created host. # self.hostid_cache[result['hostids'][0]] = host except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems creating host (%s) - %s',host,e) self.generate_feedback('Error','Problems creating host (' + host + ')') return False # ############################################ # Method do_remove_host # ############################################ def do_remove_host(self,args): ''' DESCRIPTION: This command removes a host. COMMAND: remove_host [hostname] [hostname] ---------- Hostname ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' hostname = raw_input('# Hostname: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 1: hostname = arg_list[0].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if hostname == '': self.generate_feedback('Error','Hostname value is empty') return False try: # # Generate hostnames IDs # if hostname.isdigit() == False: hostid = str(self.get_host_id(hostname)) else: hostid = str(hostname) # # Delete host via zabbix-API # result = self.zapi.host.delete(hostid) if self.conf.logging == 'ON': self.logs.logger.info('Hosts (%s) with IDs: %s removed',hostname,str(result['hostids'][0])) self.generate_feedback('Done','Hosts (' + hostname + ') with IDs: ' + str(result['hostids'][0]) + ' removed') # # Delete the deleted host from the hostid cache if it # exists. If a host is created via the zabbix frontend # after a zabbix-cli session has been started, the host # will not exist in the zabbix-cli cache of this session. # if hostid in self.hostid_cache.values(): del self.hostid_cache[hostid] except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems removing hosts (%s) - %s',hostname,e) self.generate_feedback('Error','Problems removing hosts (' + hostname + ')') return False # ############################################ # Method do_remove_maintenance_definition # ############################################ def do_remove_maintenance_definition(self,args): ''' DESCRIPTION: This command removes one or several maintenance definitions COMMAND: remove_maintenance_definitions [definitionID] [definitionID] -------------- Definition ID. One can define more than one value in a comma separated list. ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' maintenanceid = raw_input('# maintenanceID: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 1: maintenanceid = arg_list[0].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if maintenanceid == '': self.generate_feedback('Error','MaintenceID value is empty') return False try: # # Generate maintenanceIDs list # maintenances = [int(i) for i in maintenanceid.replace(' ','').split(',')] # # Delete maintenances via zabbix-API # for maintenance in maintenances: result = self.zapi.maintenance.delete(maintenance) if self.conf.logging == 'ON': self.logs.logger.info('Maintenances defintions with IDs: [%s] removed',maintenanceid.replace(' ','')) self.generate_feedback('Done','Maintenance definitions with IDs: [' + maintenanceid.replace(' ','') + '] removed') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems removing maintenance IDs: [%s] - %s',maintenanceid.replace(' ',''),e) self.generate_feedback('Error','Problems removing maintenance IDs (' + maintenanceid.replace(' ','') + ')') return False # ############################################ # Method do_create_maintenance_definition # ############################################ def do_create_maintenance_definition(self,args): ''' DESCRIPTION: This command creates a 'one time only' maintenance definition for a defined period of time. Use the zabbix dashboard for more advance definitions. COMMAND: create_maintenance_definition [name] [description] [host/hostgroup] [time period] [maintenance type] [name] ------ Maintenance definition name. [description] ------------- Maintenance definition description [host/hostgroup] ---------------- Host/s and/or hostgroup/s the that will undergo maintenance. One can define more than one value in a comma separated list and mix host and hostgroup values. [time period] ------------- Time period when the maintenance must come into effect. One can define an interval between to timestamps in ISO format or a time period in minutes, hours or days from the moment the definition is created. e.g. From 22:00 until 23:00 on 2016-11-21 -> '2016-11-21T22:00 to 2016-11-21T23:00' 2 hours from the moment we create the maintenance -> '2 hours' [maintenance type] ------------------ Maintenance type. Type values: 0 - (default) With data collection 1 - Without data collection ''' host_ids = [] hostgroup_ids = [] # Default values x = hashlib.md5() x.update(str(random.randint(1, 1000000))) tag_default = x.hexdigest()[1:10].upper() maintenance_name_default = 'zabbixCLI-' + tag_default time_period_default = '1 hour' maintenance_type_default = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' maintenance_name = raw_input('# Maintenance name [' + maintenance_name_default + ']: ').strip() maintenance_description = raw_input('# Maintenance description []: ').strip() host_hostgroup = raw_input('# Host/Hostgroup []: ').strip() time_period = raw_input('# Time period [' + time_period_default + ']: ').strip() maintenance_type_ = raw_input('# Maintenance type [' + str(maintenance_type_default) + ']: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 5: maintenance_name = arg_list[0].strip() maintenance_description = arg_list[1].strip() host_hostgroup = arg_list[2].strip() time_period = arg_list[3].strip() maintenance_type_ = arg_list[4].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False try: # # Sanity check # if maintenance_name == '': maintenance_name = maintenance_name_default if host_hostgroup == '': self.generate_feedback('Error','Maintenance host/hostgroup value is empty') return False if time_period == '': time_period = time_period_default.upper() else: time_period = time_period.upper() if maintenance_type_ == '' or maintenance_type_ not in ('0','1'): maintenance_type_ = maintenance_type_default # # Generate lists with hostID anf hostgroupID information. # for value in host_hostgroup.replace(' ','').split(','): if self.host_exists(value): host_ids.append(self.get_host_id(value)) elif self.hostgroup_exists(value): hostgroup_ids.append(self.get_hostgroup_id(value)) # # Generate since, till and period (in sec) when # maintenance period is defined with # TO # # ISO timestamp = %Y-%m-%dT%H:%M # if 'TO' in time_period: from_,to_ = time_period.split('TO') since_tmp = datetime.datetime.strptime(from_.strip(), "%Y-%m-%dT%H:%M") till_tmp = datetime.datetime.strptime(to_.strip(), "%Y-%m-%dT%H:%M") diff = (till_tmp-since_tmp) sec = (diff.seconds + diff.days * 24 * 3600) # Convert to timestamp since = time.mktime(since_tmp.timetuple()) till = time.mktime(till_tmp.timetuple()) # # Generate since, till and period (in sec) when # maintenance period id defined with a time period in # minutes, hours or days # # time period -> 'x minutes', 'y hours', 'z days' # else: if 'SECOND' in time_period: sec = int(time_period.replace(' ','').replace('SECONDS','').replace('SECOND','')) elif 'MINUTE' in time_period: sec = int(time_period.replace(' ','').replace('MINUTES','').replace('MINUTE','')) * 60 elif 'HOUR' in time_period: sec = int(time_period.replace(' ','').replace('HOURS','').replace('HOUR','')) * 60 * 60 elif 'DAY' in time_period: sec = int(time_period.replace(' ','').replace('DAYS','').replace('DAY','')) * 60 * 60 * 24 since_tmp = datetime.datetime.now() till_tmp = since_tmp + datetime.timedelta(seconds=sec) # Convert to timestamp since = time.mktime(since_tmp.timetuple()) till = time.mktime(till_tmp.timetuple()) # # Create maintenance period # result = self.zapi.maintenance.create(name=maintenance_name, maintenance_type=maintenance_type_, active_since=since, active_till=till, description=maintenance_description, hostids=host_ids, groupids=hostgroup_ids, timeperiods=[ { 'start_date':since, 'period':sec, 'timeperiod_type':0 } ]) if self.conf.logging == 'ON': self.logs.logger.info('Maintenances definition with name [%s] created',maintenance_name) self.generate_feedback('Done','Maintenance definition with name [' + maintenance_name + '] created') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems creating maintenance definition: [%s] - %s',maintenance_name,e) self.generate_feedback('Error','Problems creating maintenance definition (' + maintenance_name + ')') return False # ############################################ # Method do_create_host_interface # ############################################ def do_create_host_interface(self,args): ''' DESCRIPTION: This command creates a hostinterface. COMMAND: create_host_interface [hostname] [interface connection] [interface type] [interface port] [interface IP] [interface DNS] [default interface] [hostname] ---------- Hostname [interface connection] ---------------------- 0: Connect using host DNS name or interface DNS if provided [*] 1: Connect using host IP address [interface type] ---------------- 1: Zabbix agent 2: SNMP [*] 3: IPMI 4: JMX [interface port] ---------------- Interface port [161] [interface IP] -------------- IP address if interface connection is 1: [interface DNS] -------------- DNS if interface connection is 0: (hostname by default) [default interface] ------------------- 0: Not default interface 1: Default interface [*] ''' # # Default host interface information # # We use DNS not IP interface_ip_default = '' # This interface is the 1:default one interface_main_default = '1' # Port used by the interface interface_port_default = '161' # Interface type. 2:SNMP interface_type_default = '2' # Interface connection. 0:DNS interface_useip_default = '0' # The default DNS will be set to hostname when parsed interface_dns_default = '' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' hostname = raw_input('# Hostname: ').strip() interface_useip = raw_input('# Interface connection[' + interface_useip_default + ']: ').strip() interface_type = raw_input('# Interface type[' + interface_type_default + ']: ').strip() interface_port = raw_input('# Interface port[' + interface_port_default + ']: ').strip() interface_ip = raw_input('# Interface IP[' + interface_ip_default + ']: ').strip() interface_dns = raw_input('# Interface DNS[' + interface_dns_default + ']: ').strip() interface_main = raw_input('# Default interface[' + interface_main_default + ']: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 7: hostname = arg_list[0].strip() interface_useip = arg_list[1].strip() interface_type = arg_list[2].strip() interface_port = arg_list[3].strip() interface_ip = arg_list[4].strip() interface_dns = arg_list[5].strip() interface_main = arg_list[6].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if hostname == '': self.generate_feedback('Error','Hostname value is empty') return False interface_dns_default = hostname if interface_useip == '' or interface_useip not in ('0','1'): interface_useip = interface_useip_default if interface_type == '' or interface_type not in ('1','2','3','4'): interface_type = interface_type_default if interface_port == '' : interface_port = interface_port_default if interface_dns == '': interface_dns = interface_dns_default if interface_useip == '1' and interface_ip == '': self.generate_feedback('Error','Host IP value is empty and connection type is 1:IP') return False if interface_main == '' or interface_main not in ('0','1'): interface_main = interface_main_default # Generate interface definition if interface_useip == '0': interfaces_def = '"type":' + interface_type + \ ',"main":' + interface_main + \ ',"useip":' + interface_useip + \ ',"ip":"' + \ '","dns":"' + interface_dns + \ '","port":"' + interface_port + '"' elif interface_useip == '1': interfaces_def = '"type":' + interface_type + \ ',"main":' + interface_main + \ ',"useip":' + interface_useip + \ ',"ip":"' + interface_ip + \ '","dns":"' + \ '","port":"' + interface_port + '"' # # Checking if hostname exists # try: host_exists = self.host_exists(hostname) if self.conf.logging == 'ON': self.logs.logger.debug('Cheking if host (%s) exists',hostname) if host_exists == False: if self.conf.logging == 'ON': self.logs.logger.error('Host (%s) does not exists. Host Interface can not be created',hostname) self.generate_feedback('Error','Host (' + hostname + ') does not exists. Host Interface can not be created') return False elif host_exists == True: hostid = str(self.get_host_id(hostname)) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems checking if host (%s) exists - %s',hostname,e) self.generate_feedback('Error','Problems checking if host (' + hostname + ') exists') return False # # Create host interface if it does not exist # try: query=ast.literal_eval("{\"hostid\":\"" + hostid + "\"," + interfaces_def + "}") result = self.zapi.hostinterface.create(**query) if self.conf.logging == 'ON': self.logs.logger.info('Host interface with ID: %s created on %s',str(result['interfaceids'][0]),hostname) self.generate_feedback('Done','Host interface with ID: ' + str(result['interfaceids'][0]) + ' created on ' + hostname) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems creating host interface on %s- %s',hostname,e) self.generate_feedback('Error','Problems creating host interface on ' + hostname + '') return False # ############################################ # Method do_create_user # ############################################ def do_create_user(self,args): '''DESCRIPTION: This command creates an user. COMMAND: create_user [alias] [name] [surname] [passwd] [type] [autologin] [autologout] [groups] [alias] ------- User alias (account name) [name] ------ Name [surname] --------- Surname [passwd] -------- Password. The system will generate an automatic password if this value is not defined. [type] ------ 1:'User' [*] 2:'Admin' 3:'Super admin' [autologin] ----------- 0:'Disable' [*] 1:'Enable' [autologout] ------------ In seconds [86400] [groups] -------- Usergroup names where this user will be registered. One can define several values in a comma separated list. ''' # Default: md5 value of a random int >1 and <1000000 x = hashlib.md5() x.update(str(random.randint(1,1000000))) passwd_default = x.hexdigest() # Default: 1: Zabbix user type_default = '1' # Default: 0: Disable autologin_default = '0' # Default: 1 day: 86400s autologout_default = '86400' # Default usergroups usergroup_default = self.conf.default_create_user_usergroup.strip() try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' alias = raw_input('# Alias []: ').strip() name = raw_input('# Name []: ').strip() surname = raw_input('# Surname []: ').strip() passwd = raw_input('# Password []: ').strip() type = raw_input('# User type [' + type_default + ']: ').strip() autologin = raw_input('# Autologin [' + autologin_default + ']: ').strip() autologout = raw_input('# Autologout [' + autologout_default + ']: ').strip() usrgrps = raw_input('# Usergroups []: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command with parameters # elif len(arg_list) == 8: alias = arg_list[0].strip() name = arg_list[1].strip() surname = arg_list[2].strip() passwd = arg_list[3].strip() type = arg_list[4].strip() autologin = arg_list[5].strip() autologout = arg_list[6].strip() usrgrps = arg_list[7].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if alias == '': self.generate_feedback('Error','User Alias is empty') return False if passwd == '': passwd = passwd_default if type == '' or type not in ('1','2','3'): type = type_default if autologin == '': autologin = autologin_default if autologout == '': autologout = autologout_default usergroup_list = [] try: for usrgrp in usergroup_default.split(','): if usrgrp != '': usergroup_list.append(str(self.get_usergroup_id(usrgrp.strip()))) for usrgrp in usrgrps.split(','): if usrgrp != '': usergroup_list.append(str(self.get_usergroup_id(usrgrp.strip()))) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting usergroupID - %s',e) self.generate_feedback('Error','Problems getting usergroupID - ' + str(e)) return False # # Check if user exists # try: result = self.zapi.user.get(search={'alias':alias},output='extend',searchWildcardsEnabled=True) if self.conf.logging == 'ON': self.logs.logger.debug('Checking if user (%s) exists',alias) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems checking if user (%s) exists - %s',alias,e) self.generate_feedback('Error','Problems checking if user (' + alias + ') exists') return False # # Create user # try: if result != []: if self.conf.logging == 'ON': self.logs.logger.debug('This user (%s) already exists',alias) self.generate_feedback('Warning','This user (' + alias + ') already exists.') return False else: result = self.zapi.user.create(alias=alias, name=name, surname=surname, passwd=passwd, type=type, autologin=autologin, autologout=autologout, usrgrps=usergroup_list) if self.conf.logging == 'ON': self.logs.logger.info('User (%s) with ID: %s created',alias,str(result['userids'][0])) self.generate_feedback('Done','User (' + alias + ') with ID: ' + str(result['userids'][0]) + ' created.') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems creating user (%s) - %s',alias,e) self.generate_feedback('Error','Problems creating user (' + alias + ')') return False # ############################################ # Method do_create_notification_user # ############################################ def do_create_notification_user(self,args): '''DESCRIPTION: This command creates a notification user. These users can be used to send notifications when a zabbix event happens. Sometimes we need to send a notification to a place not owned by any user in particular, e.g. an email list or jabber channel but Zabbix has not the possibility of defining media for a usergroup. This is the reason we use *notification users*. They are users nobody owns, but that can be used by other users to send notifications to the media defined in the notification user profile. Check the parameter **default_notification_users_usergroup** in your zabbix-cli configuration file. The usergroup defined here has to exists if you want this command to work. COMMAND: create_notification_user [sendto] [mediatype] [remarks] [sendto] -------- E-mail address, SMS number, jabber address, ... [mediatype] ----------- One of the media types names defined in your Zabbix installation, e.g. Email, SMS, jabber, ... [remarks] --------- Comments about this user. e.g. Operations email. Max lenght is 20 characters. ''' # Default: md5 value of a random int >1 and <1000000 x = hashlib.md5() x.update(str(random.randint(1,1000000))) passwd_default = x.hexdigest() # Default: 1: Zabbix user type_default = '1' # Default: 0: Disable autologin_default = '0' # Default: 1 day: 86400s autologout_default = '3600' # Default usergroups usergroup_default = self.conf.default_create_user_usergroup.strip() notifications_usergroup_default = self.conf.default_notification_users_usergroup.strip() try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' sendto = raw_input('# SendTo []: ').strip() mediatype = raw_input('# Media type []: ').strip() remarks = raw_input('# Remarks []: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command with parameters # elif len(arg_list) == 3: sendto = arg_list[0].strip() mediatype = arg_list[1].strip() remarks = arg_list[2].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if sendto == '': self.generate_feedback('Error','SendTo is empty') return False if mediatype == '': self.generate_feedback('Error','Media type is empty') return False if remarks.strip() == '': alias = 'notification-user-' + sendto.replace('.','-') else: alias = 'notification-user-' + remarks.strip()[:20].replace(' ','_') + '-' + sendto.replace('.','-') passwd = passwd_default type = type_default autologin = autologin_default autologout = autologout_default usergroup_list = [] try: for usrgrp in usergroup_default.split(','): if usrgrp != '': usergroup_list.append(str(self.get_usergroup_id(usrgrp.strip()))) for usrgrp in notifications_usergroup_default.split(','): if usrgrp != '': usergroup_list.append(str(self.get_usergroup_id(usrgrp.strip()))) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting usergroupID - %s',e) self.generate_feedback('Error','Problems getting usergroupID - ' + str(e)) return False # # Check if user exists # try: result1 = self.zapi.user.get(search={'alias':alias},output='extend',searchWildcardsEnabled=True) if self.conf.logging == 'ON': self.logs.logger.debug('Checking if user (%s) exists',alias) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems checking if user (%s) exists - %s',alias,e) self.generate_feedback('Error','Problems checking if user (' + alias + ') exists') return False # # Check media type exists # try: result2 = self.zapi.mediatype.get(search={'description':mediatype},output='extend',searchWildcardsEnabled=True) if self.conf.logging == 'ON': self.logs.logger.debug('Checking if media type (%s) exists',mediatype) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems checking if media type (%s) exists - %s',mediatype,e) self.generate_feedback('Error','Problems checking if media type (' + mediatype + ') exists') return False # # Create user # try: if result1 != []: if self.conf.logging == 'ON': self.logs.logger.debug('This user (%s) already exists',alias) self.generate_feedback('Warning','This user (' + alias + ') already exists.') return False elif result2 == []: if self.conf.logging == 'ON': self.logs.logger.debug('This media type (%s) does not exist',mediatype) self.generate_feedback('Warning','This media type (' + mediatype + ') does not exist.') return False else: result = self.zapi.user.create(alias=alias, passwd=passwd, type=type, autologin=autologin, autologout=autologout, usrgrps=usergroup_list, user_medias=[ { 'mediatypeid':result2[0]['mediatypeid'], 'sendto':sendto, 'active':0, 'severity':63, 'period':'1-7,00:00-24:00' } ] ) if self.conf.logging == 'ON': self.logs.logger.info('User (%s) with ID: %s created',alias,str(result['userids'][0])) self.generate_feedback('Done','User (' + alias + ') with ID: ' + str(result['userids'][0]) + ' created.') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems creating user (%s) - %s',alias,e) self.generate_feedback('Error','Problems creating user (' + alias + ')') return False # ############################################ # Method do_remove_user # ############################################ def do_remove_user(self,args): ''' DESCRIPTION: This command removes an user. COMMAND: remove_user [username] [username] ---------- Username to remove ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' username = raw_input('# Username: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command without filters attributes # elif len(arg_list) == 1: username = arg_list[0].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if username == '': self.generate_feedback('Error','Username value is empty') return False try: if username.isdigit() == False: userid = str(self.get_user_id(username)) else: userid = str(username) result = self.zapi.user.delete(userid) if self.conf.logging == 'ON': self.logs.logger.info('User (%s) with IDs: %s removed',username,str(result['userids'][0])) self.generate_feedback('Done','User (' + username + ') with IDs: ' + str(result['userids'][0]) + ' removed') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems removing username (%s) - %s',username,e) self.generate_feedback('Error','Problems removing username (' + username + ')') return False # ############################################ # Method do_create_hostgroup # ############################################ def do_create_hostgroup(self, args): ''' DESCRIPTION: This command creates a hostgroup COMMAND: create_hostgroup [hostgroup] ''' # Default values admin_usergroup_default = self.conf.default_admin_usergroup all_usergroup_default = self.conf.default_create_user_usergroup.strip() try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' hostgroup = raw_input('# Name: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 1: hostgroup = arg_list[0].strip() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if hostgroup == '': self.generate_feedback('Error','Hostgroup value is empty') return False # # Checking if hostgroup exists # try: result = self.hostgroup_exists(hostgroup.strip()) if self.conf.logging == 'ON': self.logs.logger.debug('Checking if hostgroup (%s) exists',hostgroup) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems checking if hostgroup (%s) exists - %s',hostgroup,e) self.generate_feedback('Error','Problems checking if hostgroup (' + hostgroup + ') exists') return False try: # # Create hostgroup if it does not exist # if result == False: data = self.zapi.hostgroup.create(name=hostgroup) hostgroupid = data['groupids'][0] if self.conf.logging == 'ON': self.logs.logger.info('Hostgroup (%s) with ID: %s created',hostgroup,hostgroupid) # # Give RW access to the new hostgroup to the default admin usergroup # defined in zabbix-cli.conf # # Give RO access to the new hostgroup to the default all usergroup # defined in zabbix-cli.conf # try: for group in admin_usergroup_default.strip().split(','): usrgrpid = self.get_usergroup_id(group) result = self.zapi.usergroup.massadd(usrgrpids=[usrgrpid],rights={'id':hostgroupid,'permission':3}) if self.conf.logging == 'ON': self.logs.logger.info('Admin usergroup (%s) has got RW permissions on hostgroup (%s) ',group,hostgroup) for group in all_usergroup_default.strip().split(','): usrgrpid = self.get_usergroup_id(group) result = self.zapi.usergroup.massadd(usrgrpids=[usrgrpid],rights={'id':hostgroupid,'permission':2}) if self.conf.logging == 'ON': self.logs.logger.info('All users usergroup (%s) has got RO permissions on hostgroup (%s) ',group,hostgroup) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems giving the admin usergroup %s RW access to %s - %s',admin_usergroup_default,hostgroup,e) self.generate_feedback('Error','Problems giving the admin usergroup ' + admin_usergroup_default +' RW access to ' + hostgroup) return False self.generate_feedback('Done','Hostgroup (' + hostgroup + ') with ID: ' + hostgroupid + ' created.') else: if self.conf.logging == 'ON': self.logs.logger.debug('This hostgroup (%s) already exists',hostgroup) self.generate_feedback('Warning','This hostgroup (' + hostgroup + ') already exists.') return False except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems creating hostgroup (%s) - %s',hostgroup,e) self.generate_feedback('Error','Problems creating hostgroup (' + hostgroup + ')') return False # ############################################ # Method do_add_usergroup_permissions # ############################################ def do_add_usergroup_permissions(self, args): ''' DESCRIPTION: This command adds a permission for an usergroup on a hostgroup. If the usergroup already have permissions on the hostgroup, nothing will be changed. COMMAND: define_usergroup_permissions [usergroup] [hostgroups] [permission code] [usergroup] ----------- Usergroup that will get a permission on a hostgroup [hostgroups] ------------ Hostgroup names where the permission will apply. One can define several values in a comma separated list. [permission] ------------ * deny: Deny [usergroup] all access to [hostgroups] * ro: Give [usergroup] read access to [hostgroups] * rw: Give [usergroup] read and write access to [hostgroups] ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' usergroup = raw_input('# Usergroup: ').strip() hostgroups = raw_input('# Hostgroup: ').strip() permission = raw_input('# Permission: ').strip().lower() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 3: usergroup = arg_list[0].strip() hostgroups = arg_list[1].strip() permission = arg_list[2].strip().lower() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if usergroup == '': self.generate_feedback('Error','Usergroup value is empty') return False if hostgroups == '': self.generate_feedback('Error','Hostgroups value is empty') return False if permission not in ('deny','ro','rw'): self.generate_feedback('Error','Permission value is not valid') return False # # Define access permissions to the hostgroups # try: usrgrpid = self.get_usergroup_id(usergroup) permission_code = self.get_permission_code(permission) for group in hostgroups.split(','): hostgroupid = self.get_hostgroup_id(group) result = self.zapi.usergroup.massadd(usrgrpids=[usrgrpid],rights={'id':hostgroupid,'permission':permission_code}) if self.conf.logging == 'ON': self.logs.logger.info('Usergroup [%s] has got [%s] permission on hostgroup [%s] ',usergroup,permission,group) self.generate_feedback('Done','Usergroup [' + usergroup + '] has got [' + permission + '] permission on hostgroup [' + group + ']') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems giving the usergroup [%s] [%s] access to the hostgroup [%s] - %s',usergroup,permission,hostgroups,e) self.generate_feedback('Error','Problems giving the usergroup [' + usergroup + '] [' + permission + '] access to the hostgroup [' + hostgroups + ']') return False # ############################################ # Method do_update_usergroup_permissions # ############################################ def do_update_usergroup_permissions(self, args): ''' DESCRIPTION: This command updates the permissions for an usergroup on a hostgroup. COMMAND: define_usergroup_permissions [usergroup] [hostgroups] [permission code] [usergroup] ----------- Usergroup that will get a permission on a hostgroup [hostgroups] ------------ Hostgroup names where the permission will apply. One can define several values in a comma separated list. [permission] ------------ * deny: Deny [usergroup] all access to [hostgroups] * ro: Give [usergroup] read access to [hostgroups] * rw: Give [usergroup] read and write access to [hostgroups] ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' usergroup = raw_input('# Usergroup: ').strip() hostgroups = raw_input('# Hostgroup: ').strip() permission = raw_input('# Permission: ').strip().lower() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 3: usergroup = arg_list[0].strip() hostgroups = arg_list[1].strip() permission = arg_list[2].strip().lower() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if usergroup == '': self.generate_feedback('Error','Usergroup value is empty') return False if hostgroups == '': self.generate_feedback('Error','Hostgroups value is empty') return False if permission not in ('deny','ro','rw'): self.generate_feedback('Error','Permission value is not valid') return False # # Define access permissions to the hostgroups # try: usrgrpid = self.get_usergroup_id(usergroup) permission_code = self.get_permission_code(permission) for group in hostgroups.split(','): hostgroupid = self.get_hostgroup_id(group) result = self.zapi.usergroup.massupdate(usrgrpids=[usrgrpid],rights={'id':hostgroupid,'permission':permission_code}) if self.conf.logging == 'ON': self.logs.logger.info('Usergroup [%s] has got [%s] permission on hostgroup [%s] ',usergroup,permission,group) self.generate_feedback('Done','Usergroup [' + usergroup + '] has got [' + permission + '] permission on hostgroup [' + group + ']') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems giving the usergroup [%s] [%s] access to the hostgroup [%s] - %s',usergroup,permission,hostgroups,e) self.generate_feedback('Error','Problems giving the usergroup [' + usergroup + '] [' + permission + '] access to the hostgroup [' + hostgroups + ']') return False # ############################################ # Method do_define_global_macro # ############################################ def do_define_global_macro(self, args): ''' DESCRIPTION: This command defines a global Zabbix macro COMMAND: define_global_macro [macro name] [macro value] [macro name] ------------ Name of the zabbix macro. The system will format this value to use the macro format definition needed by Zabbix. e.g. site_url will be converted to ${SITE_URL} [macro value] ------------- Default value of the macro ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' global_macro_name = raw_input('# Global macro name: ').strip() global_macro_value = raw_input('# Global macro value: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 2: global_macro_name = arg_list[0].strip() global_macro_value = arg_list[1].strip() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if global_macro_name == '': self.generate_feedback('Error','Global macro name is empty') return False else: global_macro_name = '{$' + global_macro_name.upper() + '}' if global_macro_value == '': self.generate_feedback('Error','Global macro value is empty') return False # # Checking if global macro exists # try: result = self.zapi.usermacro.get(search={'macro':global_macro_name}, globalmacro=True, output='extend') if self.conf.logging == 'ON': self.logs.logger.debug('Cheking if global macro (%s) exists',global_macro_name) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems checking if global macro (%s) exists - %s',global_macro_name,e) self.generate_feedback('Error','Problems checking if global macro (' + global_macro_name + ') exists') return False try: if result == []: # # Create global macro if it does not exist # data = self.zapi.usermacro.createglobal(macro=global_macro_name,value=global_macro_value) globalmacroid = data['globalmacroids'][0] if self.conf.logging == 'ON': self.logs.logger.info('Global macro (%s) with ID: %s created',global_macro_name,globalmacroid) self.generate_feedback('Done','Global macro (' + global_macro_name + ') with ID: ' + globalmacroid + ' created.') else: # # Update global macro if it does exist # data = self.zapi.usermacro.updateglobal(globalmacroid=result[0]['globalmacroid'], value=global_macro_value) if self.conf.logging == 'ON': self.logs.logger.info('Global macro (%s) already exists. Value (%s) updated to (%s)',global_macro_name,result[0]['value'],global_macro_value) self.generate_feedback('Done','Global macro (' + global_macro_name + ') already exists. Value (' + result[0]['value'] + ') updated to (' + global_macro_value + ')') return False except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems defining global macro (%s) - %s',global_macro_name,e) self.generate_feedback('Error','Problems defining global macro (' + global_macro_name + ')') return False # ############################################ # Method do_define_host_usermacro # ############################################ def do_define_host_usermacro(self, args): ''' DESCRIPTION: This command defines a host usermacro COMMAND: defines_host_usermacro [hostname] [macro name] [macro value] [hostname] ---------- Hostname that will get the usermacro locally defined. [macro name] ------------ Name of the zabbix macro. The system will format this value to use the macro format definition needed by Zabbix. e.g. site_url will be converted to ${SITE_URL} [macro value] ------------- Default value of the macro ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' hostname = raw_input('# Hostname: ').strip() host_macro_name = raw_input('# Macro name: ').strip() host_macro_value = raw_input('# Macro value: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 3: hostname = arg_list[0].strip() host_macro_name = arg_list[1].strip() host_macro_value = arg_list[2].strip() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if host_macro_name == '': self.generate_feedback('Error','Host macro name is empty') return False else: host_macro_name = '{$' + host_macro_name.upper() + '}' if host_macro_value == '': self.generate_feedback('Error','Host macro value is empty') return False if hostname == '': self.generate_feedback('Error','Hostname is empty') return False if hostname.isdigit() == True: hostid = hostname else: try: hostid = self.get_host_id(hostname.strip()) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.info('Hostname %s does not exist',hostname) self.generate_feedback('Error','Hostname ' + hostname + ' does not exist') return False # # Checking if host macro exists # try: result = self.zapi.usermacro.get(search={'macro':host_macro_name}, hostids=hostid, output='extend') if self.conf.logging == 'ON': self.logs.logger.debug('Cheking if host macro (%s:%s) exists',hostname,host_macro_name) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems checking if host macro (%s:%s) exists - %s',hostname,host_macro_name,e) self.generate_feedback('Error','Problems checking if host macro (' + hostname + ':' + host_macro_name + ') exists') return False try: if result == []: # # Create host macro if it does not exist # data = self.zapi.usermacro.create(hostid=hostid, macro=host_macro_name, value=host_macro_value) hostmacroid = data['hostmacroids'][0] if self.conf.logging == 'ON': self.logs.logger.info('Host macro (%s:%s) with ID: %s created',hostname,host_macro_name,hostmacroid) self.generate_feedback('Done','Host macro (' + hostname + ':' + host_macro_name + ') with ID: ' + hostmacroid + ' created.') else: # # Update host macro if it does exist # data = self.zapi.usermacro.update(hostmacroid=result[0]['hostmacroid'], value=host_macro_value) if self.conf.logging == 'ON': self.logs.logger.info('Host macro (%s:%s) already exists. Value (%s) updated to (%s)',hostname,host_macro_name,result[0]['value'],host_macro_value) self.generate_feedback('Done','Host macro (' + hostname + ':' + host_macro_name + ') already exists. Value (' + result[0]['value'] + ') updated to (' + host_macro_value+ ')') return False except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems defining host macro (%s:%s) - %s',hostname,host_macro_name,e) self.generate_feedback('Error','Problems defining host macro (' + hostname + ':' + global_macro_name + ')') return False # ############################################ # Method do_define_host_monitoring_status # ############################################ def do_define_host_monitoring_status(self, args): ''' DESCRIPTION: This command defines the monitoring status of a host COMMAND: defines_host_monitoring_status [hostname] [on/off] [hostname] ---------- Hostname that will get the monitoring status updated. ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' hostname = raw_input('# Hostname: ').strip() monitoring_status = raw_input('# Monitoring status[ON|OFF]: ').strip().lower() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 2: hostname = arg_list[0].strip().lower() monitoring_status = arg_list[1].strip().lower() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if monitoring_status == '' or monitoring_status not in ('on','off'): self.generate_feedback('Error','Monitoring status value is not valid') return False else: if monitoring_status == 'on': monitoring_status = 0 elif monitoring_status == 'off': monitoring_status = 1 if hostname == '': self.generate_feedback('Error','Hostname is empty') return False # # Checking if host exists # try: result = self.host_exists(hostname) if self.conf.logging == 'ON': self.logs.logger.debug('Cheking if host (%s) exists',hostname,) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems checking if host (%s) exists - %s',hostname,e) self.generate_feedback('Error','Problems checking if host (' + hostname + ') exists') return False try: if result == True: # # Update host monitoring status # hostid = self.get_host_id(hostname.strip()) data = self.zapi.host.update(hostid=hostid, status=monitoring_status) if self.conf.logging == 'ON': self.logs.logger.info('Monitoring status for hostname (%s) changed to (%s)',hostname,monitoring_status) self.generate_feedback('Done','Monitoring status for hostname (' + hostname + ') changed to (' + str(monitoring_status) + ')') else: if self.conf.logging == 'ON': self.logs.logger.debug('Hostname (%s) does not exist',hostname) self.generate_feedback('Done','Hostname (' +hostname + ') does not exist') return False except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems updating monitoring status for hostname (%s) - %s',hostname,e) self.generate_feedback('Error','Problems updating monitoring status for hostname (' + hostname + ')') return False # ############################################ # Method do_update_host_proxy # ############################################ def do_update_host_proxy(self, args): ''' DESCRIPTION: This command defines the proxy used to monitor a host COMMAND: update_host_proxy [hostname] [proxy] [hostname] ---------- Hostname to update [proxy] ------- Zabbix proxy server that will monitor [hostname] ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' hostname = raw_input('# Hostname: ').strip() proxy = raw_input('# Proxy: ').strip().lower() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 2: hostname = arg_list[0].strip() proxy = arg_list[1].strip() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if hostname == '': self.generate_feedback('Error','Hostname is empty') return False if hostname.isdigit() == True: hostid = hostname else: try: hostid = self.get_host_id(hostname.strip()) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Hostname %s does not exist',hostname) self.generate_feedback('Error','Hostname ' + hostname + ' does not exist') return False try: proxy_id = self.get_proxy_id(proxy) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Proxy %s does not exist',proxy) self.generate_feedback('Error','Proxy ' + proxy + ' does not exist') return False # # Checking if host exists # try: result = self.host_exists(hostname) if self.conf.logging == 'ON': self.logs.logger.debug('Cheking if host (%s) exists',hostname,) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems checking if host (%s) exists - %s',hostname,e) self.generate_feedback('Error','Problems checking if host (' + hostname + ') exists') return False try: if result == True: # # Update proxy used to monitor the host # data = self.zapi.host.update(hostid=hostid, proxy_hostid=proxy_id) if self.conf.logging == 'ON': self.logs.logger.info('Proxy for hostname (%s) changed to (%s)',hostname,proxy) self.generate_feedback('Done','Proxy for hostname (' + hostname + ') changed to (' + str(proxy) + ')') else: if self.conf.logging == 'ON': self.logs.logger.debug('Hostname (%s) does not exist',hostname) self.generate_feedback('Done','Hostname (' +hostname + ') does not exist') return False except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems updating proxy for hostname (%s) - %s',hostname,e) self.generate_feedback('Error','Problems updating proxy for hostname (' + hostname + ')') return False # ############################################ # Method do_acknowledge_event # ############################################ def do_acknowledge_event(self, args): ''' DESCRIPTION: This command acknowledges an event COMMAND: acknowledge_events [eventIDs] [message] [eventIDs] ---------- IDs of the events to acknowledge. One can define several values in a comma separated list. [message] --------- Text of the acknowledgement message. ''' ack_message_default = '[Zabbix-CLI] Acknowledged via acknowledge_events' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' event_ids = raw_input('# EventIDs: ').strip() ack_message = raw_input('# Message[' + ack_message_default + ']:').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 2: event_ids = arg_list[0].strip() ack_message = arg_list[1].strip() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if ack_message == '': ack_message = ack_message_default event_ids = event_ids.replace(' ','').split(',') try: data = self.zapi.event.acknowledge(eventids=event_ids, message=ack_message) if self.conf.logging == 'ON': self.logs.logger.info('Acknowledge message [%s] for eventID [%s] registered',ack_message,event_ids) self.generate_feedback('Done','Acknowledge message [' + ack_message + '] for eventID [' + ','.join(event_ids) + '] registered') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems registering the acknowledge message [%s] for eventID [%s] - %s',ack_message,event_ids,e) self.generate_feedback('Error','Problems registering the acknowledge message [' + ack_message + '] for eventID [' + ','.join(event_ids) + ']') return False # ############################################ # Method do_acknowledge_trigger_last_event # ############################################ def do_acknowledge_trigger_last_event(self, args): ''' DESCRIPTION: This command acknowledges the last event of a trigger. COMMAND: acknowledge_trigger_last_event [triggerIDs] [message] [triggerIDs] ------------ IDs of the triggers to acknowledge. One can define several values in a comma separated list. [message] --------- Text of the acknowledgement message. ''' event_ids = [] ack_message_default = '[Zabbix-CLI] Acknowledged via acknowledge_trigger_last_event' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' trigger_ids = raw_input('# TriggerIDs: ').strip() ack_message = raw_input('# Message[' + ack_message_default + ']:').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 2: trigger_ids = arg_list[0].strip() ack_message = arg_list[1].strip() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if ack_message == '': ack_message = ack_message_default trigger_ids = trigger_ids.replace(' ','').split(',') try: for trigger_id in trigger_ids: data = self.zapi.event.get(objectids=trigger_id,sortfield=['clock'],sortorder='DESC',limit=1) event_ids.append(data[0]['eventid']) result = self.zapi.event.acknowledge(eventids=event_ids, message=ack_message) if self.conf.logging == 'ON': self.logs.logger.info('Acknowledge message [%s] for last eventIDs [%s] on triggerIDs [%s] registered',ack_message,','.join(event_ids),','.join(trigger_ids)) self.generate_feedback('Done','Acknowledge message [' + ack_message + '] for last eventIDs [' + ','.join(event_ids) + '] on triggerIDs [' + ','.join(trigger_ids) + '] registered') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems registering acknowledge message [%s] for last eventIDs [%s] on triggerIDs [%s] - %s', ack_message, ','.join(event_ids), ','.join(trigger_ids), e) self.generate_feedback('Error','Problems registering acknowledge message [' + ack_message + '] for last eventIDs [' + ','.join(event_ids) + '] on triggerIDs [' + ','.join(trigger_ids) + ']') return False # ############################################ # Method do_show_trigger_events # ############################################ def do_show_trigger_events(self, args): ''' DESCRIPTION: This command shows the events generated by a trigger. COMMAND: show_trigger_events [triggerID] [count] [triggerID] ------------ ID of the trigger we want tho show. [count] --------- Number of events to show (Default: 1) ''' result_columns = {} result_columns_key = 0 event_ids = [] events_count_default = 1 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' trigger_id = raw_input('# TriggerIDs: ').strip() events_count = raw_input('# Events count[' + str(events_count_default) + ']: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 2: trigger_id = arg_list[0].strip() events_count = arg_list[1].strip() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if events_count == '': events_count = events_count_default try: result = self.zapi.event.get(objectids=trigger_id, sortfield=['clock'], sortorder='DESC', limit=events_count, output='extend') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting events for triggerID [%s] - %s',str(trigger_id),e) self.generate_feedback('Error','Problems getting events for triggerID [' + str(trigger_id) + ']') return False # # Get the columns we want to show from result # for event in result: clock = datetime.datetime.fromtimestamp(int(event['clock'])) age = datetime.datetime.now() - clock if self.output_format == 'json': result_columns [result_columns_key] = {'eventid':event['eventid'], 'triggerid':event['objectid'], 'clock':str(clock), 'age':str(age), 'acknowledged':self.get_ack_status(int(event['acknowledged'])), 'value':self.get_event_status(int(event['value']))} else: result_columns [result_columns_key] = {'1':event['eventid'], '2':event['objectid'], '3':str(clock), '4':str(age), '5':self.get_ack_status(int(event['acknowledged'])), '6':self.get_event_status(int(event['value']))} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['EventID','TriggerID','Last change','Age','Acknowledged','Status'], ['Last change','Age'], ['EventID','TriggerID'], FRAME) # ############################################ # Method show_templates # ############################################ def do_show_templates(self,args): ''' DESCRIPTION: This command shows all templates defined in the system. COMMAND: show_templates ''' cmd.Cmd.onecmd(self,'show_template "*"') # ######################################## # do_show_template # ######################################## def do_show_template(self, args): ''' DESCRITION This command show templates information COMMAND: show_template [Template name] [Template name]: ---------------- One can search by template name. We can use wildcards. ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' template = raw_input('# Template: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command with parameters # elif len(arg_list) == 1: template = arg_list[0].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if template == '': self.generate_feedback('Error','Template value is empty') return False # # Get template # try: result = self.zapi.template.get(output='extend', search={'host':template}, searchWildcardsEnabled=True, sortfield='host', selectHosts=['host'], sortorder='ASC') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting the template list - %s',e) self.generate_feedback('Error','Problems getting the template list') return False # # Get the columns we want to show from result # for template in result: if self.output_format == 'json': result_columns [result_columns_key] ={'templateid':template['templateid'], 'name':template['host'], 'hosts':template['hosts']} else: host_list = [] template['hosts'].sort() for host in template['hosts']: host_list.append(host['host']) result_columns [result_columns_key] ={'1':template['templateid'], '2':template['host'], '3':'\n'.join(host_list)} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['TemplateID','Name','Hosts'], ['Name','Hosts'], ['TemplateID'], ALL) # ######################################## # do_show_global_macros # ######################################## def do_show_global_macros(self, args): ''' DESCRITION: This command shows all global macros COMMAND: show global_macros ''' result_columns = {} result_columns_key = 0 try: result = self.zapi.usermacro.get(output='extend', globalmacro=True, sortfield='macro', sortorder='ASC') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting globalmacros list - %s',e) self.generate_feedback('Error','Problems getting globalmacros list') return False # # Get the columns we want to show from result # for global_macro in result: if self.output_format == 'json': result_columns [result_columns_key] ={'globalmacroid':global_macro['globalmacroid'], 'name':global_macro['macro'], 'value':global_macro['value']} else: result_columns [result_columns_key] ={'1':global_macro['globalmacroid'], '2':global_macro['macro'], '3':global_macro['value']} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['MacroID','Name','Value'], ['Name','Value'], ['MacroID'], FRAME) # ######################################## # do_show_host_usermacros # ######################################## def do_show_host_usermacros(self, args): ''' DESCRITION: This command shows all usermacros for a host COMMAND: show_host_usermacros [hostname] [hostname] ---------- Hostname ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' hostname = raw_input('# Hostname: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 1: hostname = arg_list[0].strip() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if hostname == '': self.generate_feedback('Error','Hostname is empty') return False if hostname.isdigit() == True: hostid = hostname else: try: hostid = self.get_host_id(hostname.strip()) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.info('Hostname %s does not exist',hostname) self.generate_feedback('Error','Hostname ' + hostname + ' does not exist') return False # # Get host macros # try: result = self.zapi.usermacro.get(output='extend', hostids=hostid, sortfield='macro', sortorder='ASC') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting globalmacros list - %s',e) self.generate_feedback('Error','Problems getting globalmacros list') return False # # Get the columns we want to show from result # for host_macro in result: if self.output_format == 'json': result_columns [result_columns_key] ={'hostmacroid':host_macro['hostmacroid'], 'name':host_macro['macro'], 'value':host_macro['value']} else: result_columns [result_columns_key] ={'1':host_macro['hostmacroid'], '2':host_macro['macro'], '3':host_macro['value']} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['MacroID','Name','Value'], ['Name','Value'], ['MacroID'], FRAME) # ######################################## # do_show_usermacro_host_list # ######################################## def do_show_usermacro_host_list(self, args): ''' DESCRITION: This command shows all host with a defined usermacro COMMAND: show_usermacro_host_list [usermacro] [usermacro] ----------- Usermacro name. The system will format this value to use the macro format definition needed by Zabbix. e.g. site_url will be converted to ${SITE_URL} ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' host_macro_name= raw_input('# Host macro name: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 1: host_macro_name = arg_list[0].strip() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if host_macro_name == '': self.generate_feedback('Error','Host macro name is empty') return False else: host_macro_name = '{$' + host_macro_name.upper() + '}' # # Get macro hostlist # try: result = self.zapi.usermacro.get(output='extend', selectHosts=['host'], search={'macro':host_macro_name}, searchWildcardsEnabled=True, sortfield='macro') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting host list for macro %s - %s',host_macro_name,e) self.generate_feedback('Error','Problems getting host list for macro ' + host_macro_name) return False # # Get the columns we want to show from result # for macro in result: if len(macro['hosts']) > 0: if self.output_format == 'json': result_columns [result_columns_key] ={'macro':macro['macro'], 'value':macro['value'], 'hostid':macro['hosts'][0]['hostid'], 'host':macro['hosts'][0]['host']} else: result_columns [result_columns_key] ={'1':macro['macro'], '2':macro['value'], '3':macro['hosts'][0]['hostid'], '4':macro['hosts'][0]['host']} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['Macro','Value','HostID','Host'], ['Macro','Value','Host'], ['HostID'], FRAME) # ######################################## # do_show_usermacro_template_list # ######################################## def do_show_usermacro_template_list(self, args): ''' DESCRITION: This command shows all templates with a defined macro COMMAND: show_usermacro_template_list [usermacro] [usermacro] ----------- Usermacro name. The system will format this value to use the macro format definition needed by Zabbix. e.g. site_url will be converted to ${SITE_URL} ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' template_macro_name= raw_input('# Host macro name: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 1: template_macro_name = arg_list[0].strip() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if template_macro_name == '': self.generate_feedback('Error','Host macro name is empty') return False else: template_macro_name = '{$' + template_macro_name.upper() + '}' # # Get macro hostlist # try: result = self.zapi.usermacro.get(output='extend', selectTemplates=['host'], search={'macro':template_macro_name}, searchWildcardsEnabled=True, sortfield='macro') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting template list for macro %s - %s',template_macro_name,e) self.generate_feedback('Error','Problems getting template list for macro ' + template_macro_name) return False # # Get the columns we want to show from result # for macro in result: if len(macro['templates']) > 0: if self.output_format == 'json': result_columns [result_columns_key] ={'macro':macro['macro'], 'value':macro['value'], 'templateid':macro['templates'][0]['templateid'], 'template':macro['templates'][0]['host']} else: result_columns [result_columns_key] ={'1':macro['macro'], '2':macro['value'], '3':macro['templates'][0]['templateid'], '4':macro['templates'][0]['host']} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['Macro','Value','TemplateID','Template'], ['Macro','Value','Template'], ['TemplateID'], FRAME) # ####################################### # do_show_items # ####################################### def do_show_items(self, args): ''' DESCRIPTION: This command shows items that belong to a template COMMAND: show_items [template] [template] ---------- Template name or zabbix-templateID ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' template = raw_input('# Template: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command with parameters # elif len(arg_list) == 1: template = arg_list[0].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if template == '': self.generate_feedback('Error','Template value is empty') return False # # Getting template ID # if template.isdigit() == False: try: templateid = self.get_template_id(template) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('%s',e) self.generate_feedback('Error',e) return False else: templateid = template # # Getting items # try: result = self.zapi.item.get(output='extend', templateids=templateid, sortfield='name', sortorder='ASC') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting items list for template (%s) - %s',template,e) self.generate_feedback('Error','Problems getting items list for template (' + template + ')') return False # # Get the columns we want to show from result # for item in result: if self.output_format == 'json': result_columns [result_columns_key] ={'itemid':item['itemid'], 'name':item['name'], 'key':item['key_'], 'type':self.get_item_type(int(item['type'])), 'interval':item['delay'], 'history':item['history'], 'description':'\n'.join(textwrap.wrap(item['description'],60))} else: result_columns [result_columns_key] ={'1':item['itemid'], '2':item['name'], '3':item['key_'], '4':self.get_item_type(int(item['type'])), '5':item['delay'], '6':item['history'], '7':'\n'.join(textwrap.wrap(item['description'],60))} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['ItemID','Name','Key','Type','Interval','History','Description'], ['Name','Name','Key','Description'], ['ItemID'], FRAME) # ########################################## # do_show_triggers # ########################################## def do_show_triggers(self, args): ''' DESCRIPTION: This command shows triggers that belong to a template COMMAND: show_triggers [template] [template] ---------- Template name or zabbix-templateID ''' result_columns = {} result_columns_key = 0 try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' template = raw_input('# Template: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command with parameters # elif len(arg_list) == 1: template = arg_list[0].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if template == '': self.generate_feedback('Error','Template value is empty') return False # # Getting template ID # if template.isdigit() == False: try: templateid = self.get_template_id(template) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting templateID - %s',e) self.generate_feedback('Error','Problems getting templateID') return False else: templateid = template # # Getting triggers # try: result = self.zapi.trigger.get(output='triggerid', templateids=templateid, sortfield='triggerid', sortorder='ASC') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting trigger list for template (%s) - %s',template,e) self.generate_feedback('Error','Problems getting trigger list for template (' + template + ')') return False # # Get the columns we want to show from result # for trigger in result: trigger_data = self.zapi.trigger.get(output='extend', expandExpression=1, triggerids=trigger['triggerid']) for data in trigger_data: if self.output_format == 'json': result_columns [result_columns_key] = {'triggerid':data['triggerid'], 'expression':data['expression'], 'description':data['description'], 'priority':self.get_trigger_severity(int(data['priority'])), 'status':self.get_trigger_status(int(data['status']))} else: result_columns [result_columns_key] = {'1':data['triggerid'], '2':data['expression'], '3':data['description'], '4':self.get_trigger_severity(int(data['priority'])), '5':self.get_trigger_status(int(data['status']))} result_columns_key = result_columns_key + 1 # # Generate output # self.generate_output(result_columns, ['TriggerID','Expression','Description','Priority','Status'], ['Expression','Description'], ['TriggerID'], FRAME) # ############################################ # Method export_configuration # ############################################ def do_export_configuration(self,args): ''' DESCRIPTION: This command exports the configuration of different Zabbix components to a JSON or XML file. Several parameters in the zabbix-cli.conf configuration file can be used to control some export options. COMMAND: export_configuration [export_directory] [object type] [object name] [export directory] ------------------ Directory where the export files will be saved. [object type] ------------------ Possible values: groups, hosts, images, maps, screens, templates One can use the special value #all# to export all object type groups. [object name] ------------- Object name or Zabbix-ID. One can define several values in a comma separated list. One can use the special value #ALL# to export all objects in a object type group. This parameter will be defined automatically as #all# if [object type] == #all# ''' # # Default values # # Object type object_type_list = ['groups', 'hosts', 'images', 'maps', 'screens', 'templates'] object_type_to_export = [] # Default object type default_object_type = '#all#' # Default object name default_object_name = '#all#' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' directory_exports = raw_input('# Directory [' + self.conf.default_directory_exports + ']: ').strip() object_type = raw_input('# Object type [' + default_object_type + ']: ').strip().lower() object_name = raw_input('# Object name [' + default_object_name + ']: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command with parameters # elif len(arg_list) == 3: directory_exports = arg_list[0].strip() object_type = arg_list[1].strip().lower() object_name = arg_list[2].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # if directory_exports == '': directory_exports = self.conf.default_directory_exports for obj_type in object_type_list: if os.path.exists(directory_exports + '/' + obj_type) == False: try: os.makedirs(directory_exports + '/' + obj_type,0700) if self.conf.logging == 'ON': self.logs.logger.info('Export directory created: %s',directory_exports + '/' + obj_type) except OSError as e: if self.conf.logging == 'ON': self.logs.logger.error('OS error when creating export directory %s - %s',directory_exports + '/' + obj_type,e) self.generate_feedback('Error','OS error when creating export directory ' + directory_exports + '/' + obj_type) return False if object_type == '': object_type = default_object_type if object_type not in object_type_list + ['#all#']: self.generate_feedback('Error','Object type is not a valid value') return False if object_type.lower() == '#all#': object_type_to_export = object_type_list else: object_type_to_export.append(object_type) if object_name == '' or object_type.lower() == '#all#': object_name = default_object_name # # Generate export files for all defined object types # for obj_type in object_type_to_export: object_name_list = {} # # Generate object IDs list to export if the special value #all# # has been defined. # if object_name.lower() == '#all#': try: if obj_type == 'groups': data = self.zapi.hostgroup.get(output="extend") for object in data: object_name_list[object['groupid']] = object['name'] elif obj_type == 'hosts': data = self.zapi.host.get(output="extend") for object in data: object_name_list[object['hostid']] = object['host'] elif obj_type == 'images': data = self.zapi.image.get(output="extend") for object in data: object_name_list[object['imageid']] = object['name'] elif obj_type == 'maps': data = self.zapi.map.get(output="extend") for object in data: object_name_list[object['sysmapid']] = object['name'] elif obj_type == 'screens': data = self.zapi.screen.get(output="extend") for object in data: object_name_list[object['screenid']] = object['name'] elif obj_type == 'templates': data = self.zapi.template.get(output="extend") for object in data: object_name_list[object['templateid']] = object['host'] except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting all [%s] objects - %s',obj_type,e) self.generate_feedback('Error','Problems getting all [' + obj_type + '] objects') return False # # Generate object IDs list to export for all defined # object names. # else: for name in object_name.split(','): if name.strip().isdigit() == True and name.strip() != '': object_name_list[str(name).strip()] = str(name).strip() elif name.strip().isdigit() == False and name.strip() != '': try: if obj_type == 'groups': id = str(self.get_hostgroup_id(name.strip())) elif obj_type == 'hosts': id = str(self.get_host_id(name.strip())) elif obj_type == 'images': id = str(self.get_image_id(name.strip())) elif obj_type == 'maps': id = str(self.get_map_id(name.strip())) elif obj_type == 'screens': id = str(self.get_screen_id(name.strip())) elif obj_type == 'templates': id = str(self.get_template_id(name.strip())) object_name_list[id] = name.strip() except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting ID for object type [%s] and object name [%s] - %s',obj_type,name,e) self.generate_feedback('Error','Problems getting ID for object type [' + obj_type + '] and object name [' + name + ']') return False # # Generate export files for all defined object names # for obj_name_key in object_name_list.keys(): try: data = self.zapi.configuration.export(format=self.conf.default_export_format.lower(), options={obj_type:[obj_name_key]}) # # Formating and indenting the export data # if self.conf.default_export_format.upper() == 'JSON': output= json.dumps(json.JSONDecoder().decode(data),sort_keys=True,indent=2) else: ''' We have problems importing xml files that have been formatting with toprettyxml. This has to be investigated. xml_code = xml.dom.minidom.parseString(data) output= xml_code.toprettyxml(indent=' ') ''' output = data # # Writing to the export file # filename = self.generate_export_filename(directory_exports,obj_type,obj_name_key,object_name_list[obj_name_key]) with open(filename,'w') as export_filename: export_filename.write(output.encode("utf8")) if self.conf.logging == 'ON': self.logs.logger.info('Export file/s for object type [%s] and object name [%s] generated',obj_type,object_name_list[obj_name_key]) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems generating export file for object type [%s] and object name [%s] - %s',obj_type,object_name_list[obj_name_key],e) self.generate_feedback('Error','Problems generating export file for object type [' + obj_type+ '] and object name [' + object_name_list[obj_name_key] + ']') return False if self.conf.logging == 'ON': self.logs.logger.info('Export file/s for object type [%s] and object name [%s] generated',object_type,object_name) self.generate_feedback('Done','Export file/s for object type [' + object_type+ '] and object name [' + object_name + '] generated') # ############################################ # Method import_configuration # ############################################ def do_import_configuration(self,args): '''DESCRIPTION: This command imports the configuration of a Zabbix component. We use the options createMissing=True and updateExisting=True when importing data. This means that new objects will be created if they do not exists and that existing objects will be updated if they exist. COMMAND: import_configuration [import file] [dry run] [import file] ------------- File with the JSON or XML code to import. This command will use the file extension (.json or .xml) to find out the import format. This command finds all the pathnames matching a specified pattern according to the rules used by the Unix shell. Tilde expansion, *, ?, and character ranges expressed with [] will be correctly matched. For a literal match, wrap the meta-characters in brackets. For example, '[?]' matches the character '?'. [dry run] --------- If this parameter is used, the command will only show the files that would be imported without running the import process. 0: Dry run deactivated 1: Dry run activated [*] ''' # # Default values # total_files_imported = 0 total_files_not_imported = 0 dry_run_default = '1' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False # # Command without parameters # if len(arg_list) == 0: try: print '--------------------------------------------------------' files = raw_input('# Import file []: ').strip() dry_run = raw_input('# Dry run [' + dry_run_default + ']: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False # # Command with parameters # elif len(arg_list) == 1: files = arg_list[0].strip() dry_run = dry_run_default elif len(arg_list) == 2: files = arg_list[0].strip() dry_run = arg_list[1].strip() # # Command with the wrong number of parameters # else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # files_orig = files if files == '': self.generate_feedback('Error','Files value is empty') return False if dry_run == '' or dry_run not in ('0','1'): dry_run = dry_run_default # # Expand users HOME when using ~ or ~user # files = os.path.expanduser(files) # Normalized absolutized version of the pathname if # files does not include an absolute path if os.path.isabs(files) == False: files = os.path.abspath(files) # # Finds all the pathnames matching a specified pattern # according to the rules used by the Unix shell. No tilde # expansion is done, but *, ?, and character ranges expressed # with [] will be correctly matched. For a literal match, wrap # the meta-characters in brackets. For example, '[?]' matches # the character '?'. # expanded_files = glob.glob(files) if expanded_files == []: if self.conf.logging == 'ON': self.logs.logger.error('Files %s do not exists',files) if dry_run == '1': # # Dry run. Show files that would be imported # without running the import process. # print print '# -----------------------------------------------' print '# Dry run: ON' print '# These files would be imported with dry run: OFF' print '# -----------------------------------------------' print for file in expanded_files: if os.path.exists(file): if os.path.isfile(file): file_ext = os.path.splitext(file)[1] if file_ext.lower() == '.json': format = 'json' elif file_ext.lower() == '.xml': format = 'xml' else: total_files_not_imported = total_files_not_imported +1 if self.conf.logging == 'ON': self.logs.logger.error('The file %s is not a JSON or XML file',file) # Get the next file if this one is not a JSON or XML file continue if dry_run == '1': # # Dry run. Show files that would be imported # without running the import process. # print '# File: ' + file elif dry_run == '0': # # Import the file # try: with open(file,'r') as import_filename: import_data = import_filename.read() data = self.zapi.confimport(format=format, source=import_data, rules={ 'applications':{'createMissing':'true','updateExisting':'true'}, 'discoveryRules':{'createMissing':'true','updateExisting':'true'}, 'graphs':{'createMissing':'true','updateExisting':'true'}, 'groups':{'createMissing':'true'}, 'hosts':{'createMissing':'true','updateExisting':'true'}, 'images':{'createMissing':'true','updateExisting':'true'}, 'items':{'createMissing':'true','updateExisting':'true'}, 'maps':{'createMissing':'true','updateExisting':'true'}, 'screens':{'createMissing':'true','updateExisting':'true'}, 'templateLinkage':{'createMissing':'true'}, 'templates':{'createMissing':'true','updateExisting':'true'}, 'templateScreens':{'createMissing':'true','updateExisting':'true'}, 'triggers':{'createMissing':'true','updateExisting':'true'} }) if data == True: total_files_imported = total_files_imported + 1 if self.conf.logging == 'ON': self.logs.logger.info('The file %s has been imported into Zabbix',file) elif data == False: total_files_not_imported = total_files_not_imported +1 if self.conf.logging == 'ON': self.logs.logger.info('The file %s could not been imported into Zabbix',file) except Exception as e: total_files_not_imported = total_files_not_imported + 1 if self.conf.logging == 'ON': self.logs.logger.error('The file %s could not be imported into Zabbix - %s',file,e) else: if self.conf.logging == 'ON': self.logs.logger.error('The file %s does not exists',file) self.generate_feedback('done','Total files Imported ['+ str(total_files_imported) +'] / Not imported [' + str(total_files_not_imported) +']') # ############################################ # Method move_proxy_hosts # ############################################ def do_move_proxy_hosts(self,args): ''' DESCRIPTION: This command moves all hosts monitored by a proxy (src) to another proxy (dst). COMMAND: move_proxy_hosts [proxy_src] [proxy_dst] [proxy_src] ----------- Source proxy server. [proxy_dst] ----------- Destination proxy server. ''' try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' proxy_src = raw_input('# SRC Proxy: ').strip() proxy_dst = raw_input('# DST Proxy: ').strip().lower() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 2: proxy_src = arg_list[0].strip() proxy_dst = arg_list[1].strip() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # try: proxy_src_id = self.get_proxy_id(proxy_src) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('SRC Proxy %s does not exist',proxy_src) self.generate_feedback('Error','SRC Proxy ' + proxy_src + ' does not exist') return False try: proxy_dst_id = self.get_proxy_id(proxy_dst) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('DST Proxy %s does not exist',proxy_dst) self.generate_feedback('Error','DST Proxy ' + proxy_dst + ' does not exist') return False try: result = self.zapi.proxy.get(output='extend', proxyids=proxy_src_id, selectHosts=['hostid','name']) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting host list from SRC proxy %s - %s',proxy_src,e) self.generate_feedback('Error','Problems getting host list from SRC proxy %s' + proxy_src) return False try: hostid_list_tmp =[] hostid_list = [] for host in result[0]['hosts']: hostid_list_tmp.append('{"hostid":"' + str(host['hostid']) + '"}') hostid_list = ','.join(hostid_list_tmp) query=ast.literal_eval("{\"hosts\":[" + hostid_list + "],\"proxy_hostid\":\"" + proxy_dst_id + "\"}") result = self.zapi.host.massupdate(**query) if self.conf.logging == 'ON': self.logs.logger.info('Hosts from SRC Proxy %s have been moved to DST proxy %s',proxy_src,proxy_dst) self.generate_feedback('Error','Hosts from SRC Proxy ' + proxy_src + ' have been moved to DST proxy ' + proxy_dst) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems moving hosts from SRC Proxy %s to DST proxy %s - %s',proxy_src,proxy_dst,e) self.generate_feedback('Error','Problems moving host from SRC Proxy ' + proxy_src + ' to DST proxy ' + proxy_dst) return False # ############################################ # Method load_balance_proxy_hosts # ############################################ def do_load_balance_proxy_hosts(self,args): ''' DESCRIPTION: This command will spread hosts evenly along a serie of proxies. COMMAND: load_balance_proxy_hosts [proxy list] [proxy list]: Comma delimited list with the proxies that will share the monitoring task for a group of hosts. The group of hosts is obtained from the hosts assigned to the proxies in [proxy list] ''' proxy_list = [] proxyid_list = [] all_hosts = [] host_proxy_relation = {} try: arg_list = shlex.split(args) except ValueError as e: print '\n[ERROR]: ',e,'\n' return False if len(arg_list) == 0: try: print '--------------------------------------------------------' proxies = raw_input('# Proxies: ').strip() print '--------------------------------------------------------' except Exception as e: print '\n--------------------------------------------------------' print '\n[Aborted] Command interrupted by the user.\n' return False elif len(arg_list) == 1: proxies = arg_list[0].strip() else: self.generate_feedback('Error',' Wrong number of parameters used.\n Type help or \? to list commands') return False # # Sanity check # proxy_list = proxies.split(',') for proxy in proxy_list: try: proxyid = self.get_proxy_id(proxy.strip()) proxyid_list.append(proxyid) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Proxy [%s] does not exist - %s',proxy.strip(),e) self.generate_feedback('Error','Proxy [' + proxy.strip() + '] does not exist') return False # # Getting all host monitored by the proxies defined in # proxyid_list. These are the host that will get spreaded # evenly along the defined proxies. # try: for proxyid in proxyid_list: result = self.zapi.proxy.get(output='extend', proxyids=proxyid, selectHosts=['hostid']) for host in result[0]['hosts']: all_hosts.append(host['hostid']) except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems getting affected hosts - %s',e) self.generate_feedback('Error','Problems getting affected hosts') return False # # Create a dicctionary with hostid:proxyid entries. The # proxyid value will be chosen randomly from the list of # defined proxies. # for hostid in all_hosts: host_proxy_relation[hostid] = proxyid_list[random.randint(0,len(proxyid_list) - 1)] try: for proxyid in proxyid_list: hostid_list_tmp = [] hostid_list = [] for hostid,proxyid2 in host_proxy_relation.iteritems(): if proxyid2 == proxyid: hostid_list_tmp.append('{"hostid":"' + str(hostid) + '"}') hostid_list = ','.join(hostid_list_tmp) query=ast.literal_eval("{\"hosts\":[" + hostid_list + "],\"proxy_hostid\":\"" + proxyid + "\"}") result = self.zapi.host.massupdate(**query) if self.conf.logging == 'ON': self.logs.logger.info('Balanced configuration of hosts along defined proxies done') self.generate_feedback('Done','Balanced configuration of hosts along defined proxies done') except Exception as e: if self.conf.logging == 'ON': self.logs.logger.error('Problems assigning new proxy values for the affected hosts - %s',e) self.generate_feedback('Error','Problems assigning new proxy values for the affected hosts') return False # ############################################ # Method generate_export_filename # ############################################ def generate_export_filename(self,directory_exports,obj_type, obj_id, obj_name): ''' Generate filename to export the configuration ''' if self.conf.default_export_format.upper() == 'JSON': file_ext = 'json' elif self.conf.default_export_format.upper() == 'XML': file_ext = 'xml' else: file_ext = 'json' if self.conf.include_timestamp_export_filename.upper() == 'ON': timestamp = '_' + datetime.datetime.now().strftime('%Y-%m-%dT%H%M%S%Z') elif self.conf.include_timestamp_export_filename.upper() == 'OFF': timestamp = '' else: timestamp = '_' + datetime.datetime.now().strftime('%Y-%m-%dT%H%M%S%Z') filename = directory_exports + '/' + obj_type + '/zabbix_export_' + obj_type + '_' + obj_name.replace(' ','_').replace('/','_') + '_' + obj_id + timestamp + '.' + file_ext return filename # ############################################ # Method get_ack_status # ############################################ def get_ack_status(self,code): ''' Get ack status from code ''' ack_status = {0:'No',1:'Yes'} if code in ack_status: return ack_status[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_event_status # ############################################ def get_event_status(self,code): ''' Get event status from code ''' event_status = {0:'OK',1:'Problem'} if code in event_status: return event_status[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_trigger_severity # ############################################ def get_trigger_severity(self,code): ''' Get trigger severity from code ''' trigger_severity = {0:'Not classified',1:'Information',2:'Warning',3:'Average',4:'High',5:'Disaster'} if code in trigger_severity: return trigger_severity[code] else: return 'Unknown' # ############################################ # Method get_trigger_status # ############################################ def get_trigger_status(self,code): ''' Get trigger status from code ''' trigger_status = {0:'Enable',1:'Disable'} if code in trigger_status: return trigger_status[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_maintenance_status # ############################################ def get_maintenance_status(self,code): ''' Get maintenance status from code ''' maintenance_status = {0:'No maintenance',1:'In progress'} if code in maintenance_status: return maintenance_status[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_monitoring_status # ############################################ def get_monitoring_status(self,code): ''' Get monitoring status from code ''' monitoring_status = {0:'Monitored',1:'Not monitored'} if code in monitoring_status: return monitoring_status[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_monitoring_status # ############################################ def get_zabbix_agent_status(self,code): ''' Get zabbix agent status from code ''' zabbix_agent_status = {1:'Available',2:'Unavailable'} if code in zabbix_agent_status: return zabbix_agent_status[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_gui_access # ############################################ def get_gui_access(self,code): ''' Get GUI access from code ''' gui_access = {0:'System default',1:'Internal',2:'Disable'} if code in gui_access: return gui_access[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_usergroup_status # ############################################ def get_usergroup_status(self,code): ''' Get usergroup status from code ''' usergroup_status = {0:'Enable',1:'Disable'} if code in usergroup_status: return usergroup_status[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_hostgroup_flag # ############################################ def get_hostgroup_flag(self,code): ''' Get hostgroup flag from code ''' hostgroup_flag = {0:'Plain',4:'Discover'} if code in hostgroup_flag: return hostgroup_flag[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_hostgroup_type # ############################################ def get_hostgroup_type(self,code): ''' Get hostgroup type from code ''' hostgroup_type = {0:'Not internal',1:'Internal'} if code in hostgroup_type: return hostgroup_type[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_user_type # ############################################ def get_user_type(self,code): ''' Get user type from code ''' user_type = {1:'User',2:'Admin',3:'Super admin'} if code in user_type: return user_type[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_maintenance_type # ############################################ def get_maintenance_type(self,code): ''' Get maintenance type from code ''' maintenance_type = {0:'With DC',1:'Without DC'} if code in maintenance_type: return maintenance_type[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_maintenance_period_type # ############################################ def get_maintenance_period_type(self,code): ''' Get maintenance period type from code ''' maintenance_period_type = {0:'One time',2:'Daily',3:'Weekly',4:'Monthly'} if code in maintenance_period_type: return maintenance_period_type[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_autologin_type # ############################################ def get_autologin_type(self,code): ''' Get autologin type from code ''' autologin_type = {0:'Disable',1:'Enable'} if code in autologin_type: return autologin_type[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method get_autologin_type # ############################################ def get_permission_code(self,permission): ''' Get permission code ''' permission_code = {'deny':0,'ro':2,'rw':3} if permission in permission_code: return permission_code[permission] else: return 0 # ############################################ # Method get_item_type # ############################################ def get_item_type(self,code): ''' Get item type from code ''' item_type = {0:'Zabbix agent', 1:'SNMPv1 agent', 2:'Zabbix trapper', 3:'simple check', 4:'SNMPv2 agent', 5:'Zabbix internal', 6:'SNMPv3 agent', 7:'Zabbix agent (active)', 8:'Zabbix aggregate', 9:'web item', 10:'external check', 11:'database monitor', 12:'IPMI agent', 13:'SSH agent', 14:'TELNET agent', 15:'calculated', 16:'JMX agent', 17:'SNMP trap'} if code in item_type: return item_type[code] + " (" + str(code) +")" else: return 'Unknown' + " (" + str(code) +")" # ############################################ # Method generate_output # ############################################ def generate_output(self,result,colnames,left_col,right_col,hrules): ''' Generate the result output ''' try: if self.output_format == 'table': x = PrettyTable(colnames) x.header = True x.padding_width = 1 # FRAME, ALL, NONE x.hrules = hrules for column in left_col: x.align[column] = "l" for column in right_col: x.align[column] = "r" for records in result: columns = [] for column in sorted(result[records].keys()): columns.append(result[records][column]) x.add_row(columns) print x.get_string() print elif self.output_format == 'csv': print ",".join(colnames) for records in result: columns = [] for column in sorted(result[records]): columns.append(result[records][column]) print '"' + '","'.join(columns) + '"' elif self.output_format == 'json': print json.dumps(result,sort_keys=True,indent=2) except Exception as e: print '\n[Error] Problems generating the output ',e if self.conf.logging == 'ON': self.logs.logger.error('Problems generating the output') # ############################################ # Method generate_feedback # ############################################ def generate_feedback(self,return_code,message): ''' Generate feedback messages ''' if self.output_format == 'table': print '\n[' + return_code.title() + ']: ' + str(message) + '\n' print if self.non_interactive == True or self.bulk_execution == True: if return_code.lower() == 'done': sys.exit(0) elif return_code.lower() == 'error': sys.exit(1) elif self.output_format == 'csv': print '"' + return_code.lower() + '","' + str(message) + '"\n' if self.non_interactive == True or self.bulk_execution == True: if return_code.lower() == 'done': sys.exit(0) elif return_code.lower() == 'error': sys.exit(1) elif self.output_format == 'json': output = {"return_code":return_code.lower(),"message":str(message)} print json.dumps(output,sort_keys=True,indent=2) if self.non_interactive == True or self.bulk_execution == True: if return_code.lower() == 'done': sys.exit(0) elif return_code.lower() == 'error': sys.exit(1) # ############################################ # Method do_clear # ############################################ def do_clear(self,args): ''' DESCRIPTION: Clears the screen and shows the welcome banner. COMMAND: clear ''' os.system('clear') print self.intro # ############################################ # Method default # ############################################ def default(self,line): self.generate_feedback('Error',' Unknown command: %s.\n Type help or \? to list commands' % line) # ############################################ # Method emptyline # ############################################ def emptyline(self): pass # ############################################ # Method precmd # ############################################ def precmd(self, line_in): if line_in != '': split_line = line_in.split() if split_line[0] not in ['EOF','shell','SHELL','\!']: split_line[0] = split_line[0].lower() line_out = ' '.join(split_line) else: line_out = line_in if split_line[0] == '\h': line_out = 'help' elif split_line[0] == '\?': line_out = 'help' elif split_line[0] == '\!': line_out = line_out.replace('\!','shell') elif line_out == '\s': line_out = 'show_history' elif line_out == '\q': line_out = 'quit' self._hist += [ line_out.strip() ] else: line_out = '' return cmd.Cmd.precmd(self, line_out) # ############################################ # Method do_shell # ############################################ def do_shell(self, line): ''' DESCRIPTION: This command runs a command in the operative system COMMAND: shell [command] [command]: ---------- Any command that can be run in the operative system. ''' try: proc = subprocess.Popen([line],stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True) output, errors = proc.communicate() print output,errors print except Exception as e: self.generate_feedback('Error','Problems running %s' % line) # ############################################ # Method do_quit # ############################################ def do_quit(self, args): ''' DESCRIPTION: Quits/terminate the Zabbix-CLI shell. COMMAND: quit ''' print '\nDone, thank you for using Zabbix-CLI' return True # ############################################ # Method do_EOF # ############################################ def do_EOF(self, line): ''' DESCRIPTION: Quit/terminate the Zabbix-CLI shell. COMMAND: EOF ''' print print '\nDone, thank you for using Zabbix-CLI' return True # ############################################ # Method do_hist # ############################################ def do_show_history(self, args): ''' DESCRIPTION: This command shows the list of commands that have been entered during the Zabbix-CLI shell session. COMMAND: show_history ''' cnt = 0 print for line in self._hist: print '[' + str(cnt) + ']: ' + line cnt = cnt +1 print # ######################################################## # Method hostgroup_exists # ######################################################## def hostgroup_exists(self, hostgroup): ''' DESCRIPTION: Find out if hostgroup exists ''' try: data = self.zapi.hostgroup.get(filter={'name':hostgroup}) if data != []: return True else: return False except Exception as e: raise e # ######################################################## # Method get_hostgroup_id # ######################################################## def get_hostgroup_id(self, hostgroup): ''' DESCRIPTION: Get the hostgroup_id for a hostgroup ''' try: if self.bulk_execution == True: if hostgroup in self.hostgroupname_cache: hostgroupid = self.hostgroupname_cache[hostgroup] else: raise Exception('Could not find hostgroupID for: ' + hostgroup) else: data = self.zapi.hostgroup.get(filter={'name':hostgroup}) if data != []: hostgroupid = data[0]['groupid'] else: raise Exception('Could not find hostgroupID for: ' + hostgroup) except Exception as e: raise e return str(hostgroupid) # ################################################# # Method host_exists # ################################################# def host_exists(self, host): ''' DESCRIPTION: Find out if a hostname exists in zabbix ''' try: if self.bulk_execution == True: if host in self.hostid_cache.values(): return True else: return False else: data = self.zapi.host.get(filter={"host":host}) if data != []: return True else: return False except Exception as e: raise e # ################################################# # Method get_host_id # ################################################# def get_host_id(self, host): ''' DESCRIPTION: Get the hostid for a host ''' try: data = self.zapi.host.get(filter={"host":host}) if data != []: hostid = data[0]['hostid'] else: raise Exception('Could not find hostID for:' + host) except Exception as e: raise e return str(hostid) # ################################################# # Method get_host_name # ################################################# def get_host_name(self, hostid): ''' DESCRIPTION: Get the host name for a hostID ''' try: # # Return the value if it exists from the dictionary # hostid_cache. # if hostid in self.hostid_cache: host_name = self.hostid_cache[hostid] else: data = self.zapi.host.get(output=['host'], hostids=hostid) if data != []: host_name = data[0]['host'] self.hostid_cache[hostid] = host_name else: raise Exception('Could not find hostname for ID:' + hostid) except Exception as e: raise e return str(host_name) # ################################################# # Method get_template_name # ################################################# def get_template_name(self, templateid): ''' DESCRIPTION: Get the template name for a templateID ''' try: data = self.zapi.template.get(output='extend', templateids=templateid) if data != []: template_name = data[0]['name'] else: raise Exception('Could not find template for ID:' + templateid) except Exception as e: raise e return str(template_name) # ################################################# # Method get_image_id # ################################################# def get_image_id(self, image): ''' DESCRIPTION: Get the imageid for a image ''' print image try: data = self.zapi.image.get(filter={"name":image}) print data if data != []: imageid = data[0]['imageid'] else: raise Exception('Could not find imageID for:' + image) except Exception as e: raise e return str(imageid) # ################################################# # Method get_map_id # ################################################# def get_map_id(self, map): ''' DESCRIPTION: Get the mapid for a map ''' try: data = self.zapi.map.getobjects(name=map) if data != []: mapid = data[0]['sysmapid'] else: raise Exception('Could not find mapID for:' + map) except Exception as e: raise e return str(mapid) # ################################################# # Method get_screen_id # ################################################# def get_screen_id(self, screen): ''' DESCRIPTION: Get the screenid for a screen ''' try: data = self.zapi.screen.get(filter={"name":screen}) if data != []: screenid = data[0]['screenid'] else: raise Exception('Could not find screenID for:' + screen) except Exception as e: raise e return str(screenid) # ############################################### # Method get_template_id # ############################################### def get_template_id(self, template): ''' DESCRIPTION: Get the templateid for a template ''' try: data = self.zapi.template.get(filter={"host":template}) if data != []: templateid = data[0]['templateid'] else: raise Exception('Could not find TemplateID for:' + template) except Exception as e: raise e return str(templateid) # ########################################## # Method usergroup_exists # ########################################## def usergroup_exists(self, usergroup): ''' DESCRIPTION: Find out if usergroups exists ''' try: data = self.zapi.usergroup.get(output=['usrgrpid'], filter={"name":usergroup}) if data != []: return True else: return False except Exception as e: raise e # ########################################## # Method get_usergroup_id # ########################################## def get_usergroup_id(self, usergroup): ''' DESCRIPTION: Get the usergroupid for a usergroup ''' try: data = self.zapi.usergroup.get(output=['usrgrpid'], filter={"name":usergroup}) if data != []: usergroupid = data[0]['usrgrpid'] else: raise Exception('Could not find usergroupID for: ' + usergroup) except Exception as e: raise e return str(usergroupid) # ########################################## # Method get_user_id # ########################################## def get_user_id(self, user): ''' DESCRIPTION: Get the userid for a user ''' try: data = self.zapi.user.get(filter={'alias':user}) if data != []: userid = data[0]['userid'] else: raise Exception('Could not find userID for: ' + user) except Exception as e: raise e return str(userid) # ########################################## # Method get_proxy_id # ########################################## def get_proxy_id(self, proxy): ''' DESCRIPTION: Get the proxyid for a proxy server ''' try: if proxy != '': data = self.zapi.proxy.get(filter={"host":proxy}) if data != []: proxyid = data[0]['proxyid'] else: raise Exception('Could not find proxyID for:' + proxy) else: raise Exception('Cannot get the proxyID of an empty proxy value') except Exception as e: raise e return str(proxyid) # ########################################## # Method get_random_proxy # ########################################## def get_random_proxyid(self,proxy_pattern): ''' Return a random proxyID from the list of proxies that match the regular expression sent as a parameter to this funtion ''' proxy_list = [] match_pattern = re.compile(proxy_pattern) try: if self.bulk_execution == True: for proxyid,proxy_name in self.proxyid_cache.iteritems(): if match_pattern.match(proxy_name): proxy_list.append(proxyid) else: data = self.zapi.proxy.get(output=['proxyid','host']) for proxy in data: if match_pattern.match(proxy['host']): proxy_list.append(proxy['proxyid']) except Exception as e: raise e proxy_list_len = len(proxy_list) if proxy_list_len > 0: get_random_index = random.randint(0,proxy_list_len - 1) return proxy_list[get_random_index] else: raise Exception('The proxy list is empty. Using the zabbix server to monitor this host.') # ################################################# # Method populate_hostid_cache # ################################################# def populate_hostid_cache(self): ''' DESCRIPTION: Populate hostid cache ''' # This method initializes a dictionary when we start # zabbix-cli with all hostid:hostname from hosts that are # defined in zabbix. We use this as a cache to get hostname # information for a hostid. # # This cache is necessary e.g. by the show_alarms to avoid an # API call per active trigger to get the hostname of the host # with the associated trigger because this is a very expensive # operation on big systems with many active triggers. # # This has to be done this way now because with zabbix 3.0 the # API has changed and it is not possible to get the hostname # value via trigger.get(). expandData parameter got # deprecated in 2.4 and removed in 3.0. # try: temp_dict = {} data = self.zapi.host.get(output=['hostid','host']) for host in data: temp_dict[host['hostid']] = host['host'] return temp_dict except Exception as e: raise e # ################################################# # Method populate_hostgroupname_cache # ################################################# def populate_hostgroupname_cache(self): ''' DESCRIPTION: Populate hostgroupname cache ''' # This method initializes a dictionary with all hostgroups in # the system. # # This will help the performance of creating a host via bulk # executions because we avoid an extra call to the zabbix-API. # try: temp_dict = {} data = self.zapi.hostgroup.get(output=['groupid','name']) for hostgroup in data: temp_dict[hostgroup['name']] = hostgroup['groupid'] return temp_dict except Exception as e: raise e # ################################################# # Method populate_proxyid_cache # ################################################# def populate_proxyid_cache(self): ''' DESCRIPTION: Populate proxyid cache ''' # This method initializes a dictionary with all active proxies # in the system. # # This will help the performance of creating a host via bulk # executions because we avoid an extra call to the zabbix-API. # try: temp_dict = {} data = self.zapi.proxy.get(output=['proxyid','host']) for proxy in data: temp_dict[proxy['proxyid']] = proxy['host'] return temp_dict except Exception as e: raise e # ############################################ # Method preloop # ############################################ def preloop(self): ''' Initialization before prompting user for commands. ''' cmd.Cmd.preloop(self) ## sets up command completion self._hist = [] ## No history yet self._locals = {} ## Initialize execution namespace for user self._globals = {} # ############################################ # Method help_shortcuts # ############################################ def help_shortcuts(self): ''' Help information about shortcuts in Zabbix-CLI ''' print ''' Shortcuts in Zabbix-CLI: \s - display history \q - quit Zabbix-CLI shell \! [COMMAND] - Execute a command in shell ! [COMMAND] - Execute a command in shell ''' # ############################################ # Method help_shortcuts # ############################################ def help_support(self): ''' Help information about Zabbix-CLI support ''' print ''' The latest information and versions of Zabbix-CLI can be obtained from: https://github.com/usit-gd/zabbix-cli The Zabbix-CLI documentation is available from: https://github.com/usit-gd/zabbix-cli/blob/master/docs/manual.rst Zabbix documentation: http://www.zabbix.com/documentation.php ''' # ############################################ # Method handler # ############################################ def signal_handler_sigint(self,signum, frame): cmd.Cmd.onecmd(self,'quit') sys.exit(0) # ############################################ # Method get_version # ############################################ def get_version(self): ''' Get Zabbix-CLI version ''' try: return zabbix_cli.version.__version__ except Exception as e: return 'Unknown' if __name__ == '__main__': signal.signal(signal.SIGINT, zabbix_cli().signal_handler_sigint) signal.signal(signal.SIGTERM,zabbix_cli().signal_handler_sigint) zabbix_cli().cmdloop() zabbix-cli-1.7.0/zabbix_cli/config.py000066400000000000000000000150511311400624000174610ustar00rootroot00000000000000#!/usr/bin/env python # # Authors: # rafael@postgresql.org.es / http://www.postgresql.org.es/ # # Copyright (c) 2014-2016 USIT-University of Oslo # # This file is part of Zabbix-CLI # https://github.com/rafaelma/zabbix-cli # # Zabbix-CLI 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. # # Zabbix-CLI 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 Zabbix-CLI. If not, see . import socket import os import ConfigParser import sys class configuration(): # ############################################ # Constructor # ############################################ def __init__(self,config_file_from_parameter): """ The Constructor.""" self.config_file_from_parameter = config_file_from_parameter self.config_file_list = [] # Zabbix API section self.zabbix_api_url = '' # Zabbix_config section self.system_id = 'zabbix-ID' self.default_hostgroup = 'All-hosts' self.default_admin_usergroup = 'Zabbix-root' self.default_create_user_usergroup = 'All-users' self.default_notification_users_usergroup = 'All-notification-users' self.default_directory_exports = os.getenv('HOME') + '/zabbix_exports' self.default_export_format = 'XML' self.include_timestamp_export_filename = 'ON' self.use_colors = 'ON' self.use_auth_token_file = 'OFF' # Logging section self.logging = 'OFF' self.log_level = 'ERROR' self.log_file = '/var/log/zabbix-cli/zabbix-cli.log' self.set_configuration_file() self.set_configuration_parameters() # ############################################ # Method # ############################################ def set_configuration_file(self): """Set the zabbix-cli configuration file""" # This list defines the priority list of configuration files # that can exist in the system. Files close to the top of the # list will have priority to define configuration parameters # in the system. # # 1. /usr/share/zabbix-cli/zabbix-cli.fixed.conf # 2. /etc/zabbix-cli/zabbix-cli.fixed.conf # 3. Configuration file defined with the parameter -c / --config when executing zabbix-cli # 4. $HOME/.zabbix-cli/zabbix-cli.conf # 5. /etc/zabbix-cli/zabbix-cli.conf # 6. /usr/share/zabbix-cli/zabbix-cli.conf # config_file_priority_list = ['/usr/share/zabbix-cli/zabbix-cli.conf', '/etc/zabbix-cli/zabbix-cli.conf', os.getenv('HOME') + '/.zabbix-cli/zabbix-cli.conf'] + [self.config_file_from_parameter] + ['/etc/zabbix-cli/zabbix-cli.fixed.conf','/usr/share/zabbix-cli/zabbix-cli.fixed.conf'] # We check if the configuration files defined in # config_file_priority_list exist before we start reading # them. for file in config_file_priority_list: if os.path.isfile(file): self.config_file_list.append(file) if not self.config_file_list: print '\n[ERROR]: No config file found. Exiting.\n' sys.exit(1) # ############################################ # Method # ############################################ def set_configuration_parameters(self): """Set configuration parameters""" for config_file in self.config_file_list: config = ConfigParser.RawConfigParser() config.read(config_file) # # Zabbix APIsection # if config.has_option('zabbix_api','zabbix_api_url'): self.zabbix_api_url = config.get('zabbix_api','zabbix_api_url') # # Zabbix configuration # if config.has_option('zabbix_config','system_id'): self.system_id = config.get('zabbix_config','system_id') if config.has_option('zabbix_config','default_hostgroup'): self.default_hostgroup = config.get('zabbix_config','default_hostgroup') if config.has_option('zabbix_config','default_admin_usergroup'): self.default_admin_usergroup = config.get('zabbix_config','default_admin_usergroup') if config.has_option('zabbix_config','default_create_user_usergroup'): self.default_create_user_usergroup = config.get('zabbix_config','default_create_user_usergroup') if config.has_option('zabbix_config','default_notification_users_usergroup'): self.default_notification_users_usergroup = config.get('zabbix_config','default_notification_users_usergroup') if config.has_option('zabbix_config','default_directory_exports'): self.default_directory_exports = config.get('zabbix_config','default_directory_exports') # # We deactivate this until https://support.zabbix.com/browse/ZBX-10607 gets fixed. # We use XML as the export format. # # if config.has_option('zabbix_config','default_export_format'): # self.default_export_format = config.get('zabbix_config','default_export_format') # if config.has_option('zabbix_config','include_timestamp_export_filename'): self.include_timestamp_export_filename = config.get('zabbix_config','include_timestamp_export_filename') if config.has_option('zabbix_config','use_colors'): self.use_colors = config.get('zabbix_config','use_colors') if config.has_option('zabbix_config','use_auth_token_file'): self.use_auth_token_file = config.get('zabbix_config','use_auth_token_file') # # Logging section # if config.has_option('logging','logging'): self.logging = config.get('logging','logging') if config.has_option('logging','log_level'): self.log_level = config.get('logging','log_level') if config.has_option('logging','log_file'): self.log_file = config.get('logging','log_file') zabbix-cli-1.7.0/zabbix_cli/logs.py000066400000000000000000000036301311400624000171600ustar00rootroot00000000000000#!/usr/bin/env python # # Authors: # rafael@postgresql.org.es / http://www.postgresql.org.es/ # # Copyright (c) 2014-2015 USIT-University of Oslo # # This file is part of Zabbix-CLI # https://github.com/rafaelma/zabbix-cli # # Zabbix-CLI 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. # # Zabbix-CLI 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 Zabbix-CLI. If not, see . import os import sys import logging from zabbix_cli.config import * class log(logging.Logger): # ############################################ # Constructor # ############################################ def __init__(self, logger_name,config_file): """ The Constructor.""" self.logger_name = logger_name self.conf = configuration(config_file) self.logger = logging.getLogger(logger_name) level = logging.getLevelName(self.conf.log_level.upper()) self.logger.setLevel(level) try: self.fh = logging.FileHandler(self.conf.log_file) self.fh.setLevel(level) self.formatter = logging.Formatter("%(asctime)s [%(name)s][None][%(process)d][%(levelname)s]: %(message)s") self.fh.setFormatter(self.formatter) self.logger.addHandler(self.fh) except Exception as e: print "ERROR: Problems with the log configuration needed by Zabbix-CLI: %s" % e sys.exit(1) zabbix-cli-1.7.0/zabbix_cli/prettytable.py000066400000000000000000001516741311400624000205670ustar00rootroot00000000000000#!/usr/bin/env python # # Copyright (c) 2009-2013, Luke Maurits # All rights reserved. # With contributions from: # * Chris Clark # * Klein Stephane # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. __version__ = "0.7.2" import copy import csv import random import re import sys import textwrap import itertools import unicodedata py3k = sys.version_info[0] >= 3 if py3k: unicode = str basestring = str itermap = map iterzip = zip uni_chr = chr from html.parser import HTMLParser else: itermap = itertools.imap iterzip = itertools.izip uni_chr = unichr from HTMLParser import HTMLParser if py3k and sys.version_info[1] >= 2: from html import escape else: from cgi import escape # hrule styles FRAME = 0 ALL = 1 NONE = 2 HEADER = 3 # Table styles DEFAULT = 10 MSWORD_FRIENDLY = 11 PLAIN_COLUMNS = 12 RANDOM = 20 _re = re.compile("\033\[[0-9;]*m") def _get_size(text): lines = text.split("\n") height = len(lines) width = max([_str_block_width(line) for line in lines]) return (width, height) class PrettyTable(object): def __init__(self, field_names=None, **kwargs): """Return a new PrettyTable instance Arguments: encoding - Unicode encoding scheme used to decode any encoded input field_names - list or tuple of field names fields - list or tuple of field names to include in displays start - index of first data row to include in output end - index of last data row to include in output PLUS ONE (list slice style) header - print a header showing field names (True or False) header_style - stylisation to apply to field names in header ("cap", "title", "upper", "lower" or None) border - print a border around the table (True or False) hrules - controls printing of horizontal rules after rows. Allowed values: FRAME, HEADER, ALL, NONE vrules - controls printing of vertical rules between columns. Allowed values: FRAME, ALL, NONE int_format - controls formatting of integer data float_format - controls formatting of floating point data padding_width - number of spaces on either side of column data (only used if left and right paddings are None) left_padding_width - number of spaces on left hand side of column data right_padding_width - number of spaces on right hand side of column data vertical_char - single character string used to draw vertical lines horizontal_char - single character string used to draw horizontal lines junction_char - single character string used to draw line junctions sortby - name of field to sort rows by sort_key - sorting key function, applied to data points before sorting valign - default valign for each row (None, "t", "m" or "b") reversesort - True or False to sort in descending or ascending order""" self.encoding = kwargs.get("encoding", "UTF-8") # Data self._field_names = [] self._align = {} self._valign = {} self._max_width = {} self._rows = [] if field_names: self.field_names = field_names else: self._widths = [] # Options self._options = "start end fields header border sortby reversesort sort_key attributes format hrules vrules".split() self._options.extend("int_format float_format padding_width left_padding_width right_padding_width".split()) self._options.extend("vertical_char horizontal_char junction_char header_style valign xhtml print_empty".split()) for option in self._options: if option in kwargs: self._validate_option(option, kwargs[option]) else: kwargs[option] = None self._start = kwargs["start"] or 0 self._end = kwargs["end"] or None self._fields = kwargs["fields"] or None if kwargs["header"] in (True, False): self._header = kwargs["header"] else: self._header = True self._header_style = kwargs["header_style"] or None if kwargs["border"] in (True, False): self._border = kwargs["border"] else: self._border = True self._hrules = kwargs["hrules"] or FRAME self._vrules = kwargs["vrules"] or ALL self._sortby = kwargs["sortby"] or None if kwargs["reversesort"] in (True, False): self._reversesort = kwargs["reversesort"] else: self._reversesort = False self._sort_key = kwargs["sort_key"] or (lambda x: x) self._int_format = kwargs["int_format"] or {} self._float_format = kwargs["float_format"] or {} self._padding_width = kwargs["padding_width"] or 1 self._left_padding_width = kwargs["left_padding_width"] or None self._right_padding_width = kwargs["right_padding_width"] or None self._vertical_char = kwargs["vertical_char"] or self._unicode("|") self._horizontal_char = kwargs["horizontal_char"] or self._unicode("-") self._junction_char = kwargs["junction_char"] or self._unicode("+") if kwargs["print_empty"] in (True, False): self._print_empty = kwargs["print_empty"] else: self._print_empty = True self._format = kwargs["format"] or False self._xhtml = kwargs["xhtml"] or False self._attributes = kwargs["attributes"] or {} def _unicode(self, value): if not isinstance(value, basestring): value = str(value) if not isinstance(value, unicode): value = unicode(value, self.encoding, "strict") return value def _justify(self, text, width, align): excess = width - _str_block_width(text) if align == "l": return text + excess * " " elif align == "r": return excess * " " + text else: if excess % 2: # Uneven padding # Put more space on right if text is of odd length... if _str_block_width(text) % 2: return (excess//2)*" " + text + (excess//2 + 1)*" " # and more space on left if text is of even length else: return (excess//2 + 1)*" " + text + (excess//2)*" " # Why distribute extra space this way? To match the behaviour of # the inbuilt str.center() method. else: # Equal padding on either side return (excess//2)*" " + text + (excess//2)*" " def __getattr__(self, name): if name == "rowcount": return len(self._rows) elif name == "colcount": if self._field_names: return len(self._field_names) elif self._rows: return len(self._rows[0]) else: return 0 else: raise AttributeError(name) def __getitem__(self, index): new = PrettyTable() new.field_names = self.field_names for attr in self._options: setattr(new, "_"+attr, getattr(self, "_"+attr)) setattr(new, "_align", getattr(self, "_align")) if isinstance(index, slice): for row in self._rows[index]: new.add_row(row) elif isinstance(index, int): new.add_row(self._rows[index]) else: raise Exception("Index %s is invalid, must be an integer or slice" % str(index)) return new if py3k: def __str__(self): return self.__unicode__() else: def __str__(self): return self.__unicode__().encode(self.encoding) def __unicode__(self): return self.get_string() ############################## # ATTRIBUTE VALIDATORS # ############################## # The method _validate_option is all that should be used elsewhere in the code base to validate options. # It will call the appropriate validation method for that option. The individual validation methods should # never need to be called directly (although nothing bad will happen if they *are*). # Validation happens in TWO places. # Firstly, in the property setters defined in the ATTRIBUTE MANAGMENT section. # Secondly, in the _get_options method, where keyword arguments are mixed with persistent settings def _validate_option(self, option, val): if option in ("field_names"): self._validate_field_names(val) elif option in ("start", "end", "max_width", "padding_width", "left_padding_width", "right_padding_width", "format"): self._validate_nonnegative_int(option, val) elif option in ("sortby"): self._validate_field_name(option, val) elif option in ("sort_key"): self._validate_function(option, val) elif option in ("hrules"): self._validate_hrules(option, val) elif option in ("vrules"): self._validate_vrules(option, val) elif option in ("fields"): self._validate_all_field_names(option, val) elif option in ("header", "border", "reversesort", "xhtml", "print_empty"): self._validate_true_or_false(option, val) elif option in ("header_style"): self._validate_header_style(val) elif option in ("int_format"): self._validate_int_format(option, val) elif option in ("float_format"): self._validate_float_format(option, val) elif option in ("vertical_char", "horizontal_char", "junction_char"): self._validate_single_char(option, val) elif option in ("attributes"): self._validate_attributes(option, val) else: raise Exception("Unrecognised option: %s!" % option) def _validate_field_names(self, val): # Check for appropriate length if self._field_names: try: assert len(val) == len(self._field_names) except AssertionError: raise Exception("Field name list has incorrect number of values, (actual) %d!=%d (expected)" % (len(val), len(self._field_names))) if self._rows: try: assert len(val) == len(self._rows[0]) except AssertionError: raise Exception("Field name list has incorrect number of values, (actual) %d!=%d (expected)" % (len(val), len(self._rows[0]))) # Check for uniqueness try: assert len(val) == len(set(val)) except AssertionError: raise Exception("Field names must be unique!") def _validate_header_style(self, val): try: assert val in ("cap", "title", "upper", "lower", None) except AssertionError: raise Exception("Invalid header style, use cap, title, upper, lower or None!") def _validate_align(self, val): try: assert val in ["l","c","r"] except AssertionError: raise Exception("Alignment %s is invalid, use l, c or r!" % val) def _validate_valign(self, val): try: assert val in ["t","m","b",None] except AssertionError: raise Exception("Alignment %s is invalid, use t, m, b or None!" % val) def _validate_nonnegative_int(self, name, val): try: assert int(val) >= 0 except AssertionError: raise Exception("Invalid value for %s: %s!" % (name, self._unicode(val))) def _validate_true_or_false(self, name, val): try: assert val in (True, False) except AssertionError: raise Exception("Invalid value for %s! Must be True or False." % name) def _validate_int_format(self, name, val): if val == "": return try: assert type(val) in (str, unicode) assert val.isdigit() except AssertionError: raise Exception("Invalid value for %s! Must be an integer format string." % name) def _validate_float_format(self, name, val): if val == "": return try: assert type(val) in (str, unicode) assert "." in val bits = val.split(".") assert len(bits) <= 2 assert bits[0] == "" or bits[0].isdigit() assert bits[1] == "" or bits[1].isdigit() except AssertionError: raise Exception("Invalid value for %s! Must be a float format string." % name) def _validate_function(self, name, val): try: assert hasattr(val, "__call__") except AssertionError: raise Exception("Invalid value for %s! Must be a function." % name) def _validate_hrules(self, name, val): try: assert val in (ALL, FRAME, HEADER, NONE) except AssertionError: raise Exception("Invalid value for %s! Must be ALL, FRAME, HEADER or NONE." % name) def _validate_vrules(self, name, val): try: assert val in (ALL, FRAME, NONE) except AssertionError: raise Exception("Invalid value for %s! Must be ALL, FRAME, or NONE." % name) def _validate_field_name(self, name, val): try: assert (val in self._field_names) or (val is None) except AssertionError: raise Exception("Invalid field name: %s!" % val) def _validate_all_field_names(self, name, val): try: for x in val: self._validate_field_name(name, x) except AssertionError: raise Exception("fields must be a sequence of field names!") def _validate_single_char(self, name, val): try: assert _str_block_width(val) == 1 except AssertionError: raise Exception("Invalid value for %s! Must be a string of length 1." % name) def _validate_attributes(self, name, val): try: assert isinstance(val, dict) except AssertionError: raise Exception("attributes must be a dictionary of name/value pairs!") ############################## # ATTRIBUTE MANAGEMENT # ############################## def _get_field_names(self): return self._field_names """The names of the fields Arguments: fields - list or tuple of field names""" def _set_field_names(self, val): val = [self._unicode(x) for x in val] self._validate_option("field_names", val) if self._field_names: old_names = self._field_names[:] self._field_names = val if self._align and old_names: for old_name, new_name in zip(old_names, val): self._align[new_name] = self._align[old_name] for old_name in old_names: if old_name not in self._align: self._align.pop(old_name) else: for field in self._field_names: self._align[field] = "c" if self._valign and old_names: for old_name, new_name in zip(old_names, val): self._valign[new_name] = self._valign[old_name] for old_name in old_names: if old_name not in self._valign: self._valign.pop(old_name) else: for field in self._field_names: self._valign[field] = "t" field_names = property(_get_field_names, _set_field_names) def _get_align(self): return self._align def _set_align(self, val): self._validate_align(val) for field in self._field_names: self._align[field] = val align = property(_get_align, _set_align) def _get_valign(self): return self._valign def _set_valign(self, val): self._validate_valign(val) for field in self._field_names: self._valign[field] = val valign = property(_get_valign, _set_valign) def _get_max_width(self): return self._max_width def _set_max_width(self, val): self._validate_option("max_width", val) for field in self._field_names: self._max_width[field] = val max_width = property(_get_max_width, _set_max_width) def _get_fields(self): """List or tuple of field names to include in displays Arguments: fields - list or tuple of field names to include in displays""" return self._fields def _set_fields(self, val): self._validate_option("fields", val) self._fields = val fields = property(_get_fields, _set_fields) def _get_start(self): """Start index of the range of rows to print Arguments: start - index of first data row to include in output""" return self._start def _set_start(self, val): self._validate_option("start", val) self._start = val start = property(_get_start, _set_start) def _get_end(self): """End index of the range of rows to print Arguments: end - index of last data row to include in output PLUS ONE (list slice style)""" return self._end def _set_end(self, val): self._validate_option("end", val) self._end = val end = property(_get_end, _set_end) def _get_sortby(self): """Name of field by which to sort rows Arguments: sortby - field name to sort by""" return self._sortby def _set_sortby(self, val): self._validate_option("sortby", val) self._sortby = val sortby = property(_get_sortby, _set_sortby) def _get_reversesort(self): """Controls direction of sorting (ascending vs descending) Arguments: reveresort - set to True to sort by descending order, or False to sort by ascending order""" return self._reversesort def _set_reversesort(self, val): self._validate_option("reversesort", val) self._reversesort = val reversesort = property(_get_reversesort, _set_reversesort) def _get_sort_key(self): """Sorting key function, applied to data points before sorting Arguments: sort_key - a function which takes one argument and returns something to be sorted""" return self._sort_key def _set_sort_key(self, val): self._validate_option("sort_key", val) self._sort_key = val sort_key = property(_get_sort_key, _set_sort_key) def _get_header(self): """Controls printing of table header with field names Arguments: header - print a header showing field names (True or False)""" return self._header def _set_header(self, val): self._validate_option("header", val) self._header = val header = property(_get_header, _set_header) def _get_header_style(self): """Controls stylisation applied to field names in header Arguments: header_style - stylisation to apply to field names in header ("cap", "title", "upper", "lower" or None)""" return self._header_style def _set_header_style(self, val): self._validate_header_style(val) self._header_style = val header_style = property(_get_header_style, _set_header_style) def _get_border(self): """Controls printing of border around table Arguments: border - print a border around the table (True or False)""" return self._border def _set_border(self, val): self._validate_option("border", val) self._border = val border = property(_get_border, _set_border) def _get_hrules(self): """Controls printing of horizontal rules after rows Arguments: hrules - horizontal rules style. Allowed values: FRAME, ALL, HEADER, NONE""" return self._hrules def _set_hrules(self, val): self._validate_option("hrules", val) self._hrules = val hrules = property(_get_hrules, _set_hrules) def _get_vrules(self): """Controls printing of vertical rules between columns Arguments: vrules - vertical rules style. Allowed values: FRAME, ALL, NONE""" return self._vrules def _set_vrules(self, val): self._validate_option("vrules", val) self._vrules = val vrules = property(_get_vrules, _set_vrules) def _get_int_format(self): """Controls formatting of integer data Arguments: int_format - integer format string""" return self._int_format def _set_int_format(self, val): # self._validate_option("int_format", val) for field in self._field_names: self._int_format[field] = val int_format = property(_get_int_format, _set_int_format) def _get_float_format(self): """Controls formatting of floating point data Arguments: float_format - floating point format string""" return self._float_format def _set_float_format(self, val): # self._validate_option("float_format", val) for field in self._field_names: self._float_format[field] = val float_format = property(_get_float_format, _set_float_format) def _get_padding_width(self): """The number of empty spaces between a column's edge and its content Arguments: padding_width - number of spaces, must be a positive integer""" return self._padding_width def _set_padding_width(self, val): self._validate_option("padding_width", val) self._padding_width = val padding_width = property(_get_padding_width, _set_padding_width) def _get_left_padding_width(self): """The number of empty spaces between a column's left edge and its content Arguments: left_padding - number of spaces, must be a positive integer""" return self._left_padding_width def _set_left_padding_width(self, val): self._validate_option("left_padding_width", val) self._left_padding_width = val left_padding_width = property(_get_left_padding_width, _set_left_padding_width) def _get_right_padding_width(self): """The number of empty spaces between a column's right edge and its content Arguments: right_padding - number of spaces, must be a positive integer""" return self._right_padding_width def _set_right_padding_width(self, val): self._validate_option("right_padding_width", val) self._right_padding_width = val right_padding_width = property(_get_right_padding_width, _set_right_padding_width) def _get_vertical_char(self): """The charcter used when printing table borders to draw vertical lines Arguments: vertical_char - single character string used to draw vertical lines""" return self._vertical_char def _set_vertical_char(self, val): val = self._unicode(val) self._validate_option("vertical_char", val) self._vertical_char = val vertical_char = property(_get_vertical_char, _set_vertical_char) def _get_horizontal_char(self): """The charcter used when printing table borders to draw horizontal lines Arguments: horizontal_char - single character string used to draw horizontal lines""" return self._horizontal_char def _set_horizontal_char(self, val): val = self._unicode(val) self._validate_option("horizontal_char", val) self._horizontal_char = val horizontal_char = property(_get_horizontal_char, _set_horizontal_char) def _get_junction_char(self): """The charcter used when printing table borders to draw line junctions Arguments: junction_char - single character string used to draw line junctions""" return self._junction_char def _set_junction_char(self, val): val = self._unicode(val) self._validate_option("vertical_char", val) self._junction_char = val junction_char = property(_get_junction_char, _set_junction_char) def _get_format(self): """Controls whether or not HTML tables are formatted to match styling options Arguments: format - True or False""" return self._format def _set_format(self, val): self._validate_option("format", val) self._format = val format = property(_get_format, _set_format) def _get_print_empty(self): """Controls whether or not empty tables produce a header and frame or just an empty string Arguments: print_empty - True or False""" return self._print_empty def _set_print_empty(self, val): self._validate_option("print_empty", val) self._print_empty = val print_empty = property(_get_print_empty, _set_print_empty) def _get_attributes(self): """A dictionary of HTML attribute name/value pairs to be included in the tag when printing HTML Arguments: attributes - dictionary of attributes""" return self._attributes def _set_attributes(self, val): self._validate_option("attributes", val) self._attributes = val attributes = property(_get_attributes, _set_attributes) ############################## # OPTION MIXER # ############################## def _get_options(self, kwargs): options = {} for option in self._options: if option in kwargs: self._validate_option(option, kwargs[option]) options[option] = kwargs[option] else: options[option] = getattr(self, "_"+option) return options ############################## # PRESET STYLE LOGIC # ############################## def set_style(self, style): if style == DEFAULT: self._set_default_style() elif style == MSWORD_FRIENDLY: self._set_msword_style() elif style == PLAIN_COLUMNS: self._set_columns_style() elif style == RANDOM: self._set_random_style() else: raise Exception("Invalid pre-set style!") def _set_default_style(self): self.header = True self.border = True self._hrules = FRAME self._vrules = ALL self.padding_width = 1 self.left_padding_width = 1 self.right_padding_width = 1 self.vertical_char = "|" self.horizontal_char = "-" self.junction_char = "+" def _set_msword_style(self): self.header = True self.border = True self._hrules = NONE self.padding_width = 1 self.left_padding_width = 1 self.right_padding_width = 1 self.vertical_char = "|" def _set_columns_style(self): self.header = True self.border = False self.padding_width = 1 self.left_padding_width = 0 self.right_padding_width = 8 def _set_random_style(self): # Just for fun! self.header = random.choice((True, False)) self.border = random.choice((True, False)) self._hrules = random.choice((ALL, FRAME, HEADER, NONE)) self._vrules = random.choice((ALL, FRAME, NONE)) self.left_padding_width = random.randint(0,5) self.right_padding_width = random.randint(0,5) self.vertical_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?") self.horizontal_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?") self.junction_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?") ############################## # DATA INPUT METHODS # ############################## def add_row(self, row): """Add a row to the table Arguments: row - row of data, should be a list with as many elements as the table has fields""" if self._field_names and len(row) != len(self._field_names): raise Exception("Row has incorrect number of values, (actual) %d!=%d (expected)" %(len(row),len(self._field_names))) if not self._field_names: self.field_names = [("Field %d" % (n+1)) for n in range(0,len(row))] self._rows.append(list(row)) def del_row(self, row_index): """Delete a row to the table Arguments: row_index - The index of the row you want to delete. Indexing starts at 0.""" if row_index > len(self._rows)-1: raise Exception("Cant delete row at index %d, table only has %d rows!" % (row_index, len(self._rows))) del self._rows[row_index] def add_column(self, fieldname, column, align="c", valign="t"): """Add a column to the table. Arguments: fieldname - name of the field to contain the new column of data column - column of data, should be a list with as many elements as the table has rows align - desired alignment for this column - "l" for left, "c" for centre and "r" for right valign - desired vertical alignment for new columns - "t" for top, "m" for middle and "b" for bottom""" if len(self._rows) in (0, len(column)): self._validate_align(align) self._validate_valign(valign) self._field_names.append(fieldname) self._align[fieldname] = align self._valign[fieldname] = valign for i in range(0, len(column)): if len(self._rows) < i+1: self._rows.append([]) self._rows[i].append(column[i]) else: raise Exception("Column length %d does not match number of rows %d!" % (len(column), len(self._rows))) def clear_rows(self): """Delete all rows from the table but keep the current field names""" self._rows = [] def clear(self): """Delete all rows and field names from the table, maintaining nothing but styling options""" self._rows = [] self._field_names = [] self._widths = [] ############################## # MISC PUBLIC METHODS # ############################## def copy(self): return copy.deepcopy(self) ############################## # MISC PRIVATE METHODS # ############################## def _format_value(self, field, value): if isinstance(value, int) and field in self._int_format: value = self._unicode(("%%%sd" % self._int_format[field]) % value) elif isinstance(value, float) and field in self._float_format: value = self._unicode(("%%%sf" % self._float_format[field]) % value) return self._unicode(value) def _compute_widths(self, rows, options): if options["header"]: widths = [_get_size(field)[0] for field in self._field_names] else: widths = len(self.field_names) * [0] for row in rows: for index, value in enumerate(row): fieldname = self.field_names[index] if fieldname in self.max_width: widths[index] = max(widths[index], min(_get_size(value)[0], self.max_width[fieldname])) else: widths[index] = max(widths[index], _get_size(value)[0]) self._widths = widths def _get_padding_widths(self, options): if options["left_padding_width"] is not None: lpad = options["left_padding_width"] else: lpad = options["padding_width"] if options["right_padding_width"] is not None: rpad = options["right_padding_width"] else: rpad = options["padding_width"] return lpad, rpad def _get_rows(self, options): """Return only those data rows that should be printed, based on slicing and sorting. Arguments: options - dictionary of option settings.""" # Make a copy of only those rows in the slice range rows = copy.deepcopy(self._rows[options["start"]:options["end"]]) # Sort if necessary if options["sortby"]: sortindex = self._field_names.index(options["sortby"]) # Decorate rows = [[row[sortindex]]+row for row in rows] # Sort rows.sort(reverse=options["reversesort"], key=options["sort_key"]) # Undecorate rows = [row[1:] for row in rows] return rows def _format_row(self, row, options): return [self._format_value(field, value) for (field, value) in zip(self._field_names, row)] def _format_rows(self, rows, options): return [self._format_row(row, options) for row in rows] ############################## # PLAIN TEXT STRING METHODS # ############################## def get_string(self, **kwargs): """Return string representation of table in current state. Arguments: start - index of first data row to include in output end - index of last data row to include in output PLUS ONE (list slice style) fields - names of fields (columns) to include header - print a header showing field names (True or False) border - print a border around the table (True or False) hrules - controls printing of horizontal rules after rows. Allowed values: ALL, FRAME, HEADER, NONE vrules - controls printing of vertical rules between columns. Allowed values: FRAME, ALL, NONE int_format - controls formatting of integer data float_format - controls formatting of floating point data padding_width - number of spaces on either side of column data (only used if left and right paddings are None) left_padding_width - number of spaces on left hand side of column data right_padding_width - number of spaces on right hand side of column data vertical_char - single character string used to draw vertical lines horizontal_char - single character string used to draw horizontal lines junction_char - single character string used to draw line junctions sortby - name of field to sort rows by sort_key - sorting key function, applied to data points before sorting reversesort - True or False to sort in descending or ascending order print empty - if True, stringify just the header for an empty table, if False return an empty string """ options = self._get_options(kwargs) lines = [] # Don't think too hard about an empty table # Is this the desired behaviour? Maybe we should still print the header? if self.rowcount == 0 and (not options["print_empty"] or not options["border"]): return "" # Get the rows we need to print, taking into account slicing, sorting, etc. rows = self._get_rows(options) # Turn all data in all rows into Unicode, formatted as desired formatted_rows = self._format_rows(rows, options) # Compute column widths self._compute_widths(formatted_rows, options) # Add header or top of border self._hrule = self._stringify_hrule(options) if options["header"]: lines.append(self._stringify_header(options)) elif options["border"] and options["hrules"] in (ALL, FRAME): lines.append(self._hrule) # Add rows for row in formatted_rows: lines.append(self._stringify_row(row, options)) # Add bottom of border if options["border"] and options["hrules"] == FRAME: lines.append(self._hrule) return self._unicode("\n").join(lines) def _stringify_hrule(self, options): if not options["border"]: return "" lpad, rpad = self._get_padding_widths(options) if options['vrules'] in (ALL, FRAME): bits = [options["junction_char"]] else: bits = [options["horizontal_char"]] # For tables with no data or fieldnames if not self._field_names: bits.append(options["junction_char"]) return "".join(bits) for field, width in zip(self._field_names, self._widths): if options["fields"] and field not in options["fields"]: continue bits.append((width+lpad+rpad)*options["horizontal_char"]) if options['vrules'] == ALL: bits.append(options["junction_char"]) else: bits.append(options["horizontal_char"]) if options["vrules"] == FRAME: bits.pop() bits.append(options["junction_char"]) return "".join(bits) def _stringify_header(self, options): bits = [] lpad, rpad = self._get_padding_widths(options) if options["border"]: if options["hrules"] in (ALL, FRAME): bits.append(self._hrule) bits.append("\n") if options["vrules"] in (ALL, FRAME): bits.append(options["vertical_char"]) else: bits.append(" ") # For tables with no data or field names if not self._field_names: if options["vrules"] in (ALL, FRAME): bits.append(options["vertical_char"]) else: bits.append(" ") for field, width, in zip(self._field_names, self._widths): if options["fields"] and field not in options["fields"]: continue if self._header_style == "cap": fieldname = field.capitalize() elif self._header_style == "title": fieldname = field.title() elif self._header_style == "upper": fieldname = field.upper() elif self._header_style == "lower": fieldname = field.lower() else: fieldname = field bits.append(" " * lpad + self._justify(fieldname, width, self._align[field]) + " " * rpad) if options["border"]: if options["vrules"] == ALL: bits.append(options["vertical_char"]) else: bits.append(" ") # If vrules is FRAME, then we just appended a space at the end # of the last field, when we really want a vertical character if options["border"] and options["vrules"] == FRAME: bits.pop() bits.append(options["vertical_char"]) if options["border"] and options["hrules"] != NONE: bits.append("\n") bits.append(self._hrule) return "".join(bits) def _stringify_row(self, row, options): for index, field, value, width, in zip(range(0,len(row)), self._field_names, row, self._widths): # Enforce max widths lines = value.split("\n") new_lines = [] for line in lines: if _str_block_width(line) > width: line = textwrap.fill(line, width) new_lines.append(line) lines = new_lines value = "\n".join(lines) row[index] = value row_height = 0 for c in row: h = _get_size(c)[1] if h > row_height: row_height = h bits = [] lpad, rpad = self._get_padding_widths(options) for y in range(0, row_height): bits.append([]) if options["border"]: if options["vrules"] in (ALL, FRAME): bits[y].append(self.vertical_char) else: bits[y].append(" ") for field, value, width, in zip(self._field_names, row, self._widths): valign = self._valign[field] lines = value.split("\n") dHeight = row_height - len(lines) if dHeight: if valign == "m": lines = [""] * int(dHeight / 2) + lines + [""] * (dHeight - int(dHeight / 2)) elif valign == "b": lines = [""] * dHeight + lines else: lines = lines + [""] * dHeight y = 0 for l in lines: if options["fields"] and field not in options["fields"]: continue bits[y].append(" " * lpad + self._justify(l, width, self._align[field]) + " " * rpad) if options["border"]: if options["vrules"] == ALL: bits[y].append(self.vertical_char) else: bits[y].append(" ") y += 1 # If vrules is FRAME, then we just appended a space at the end # of the last field, when we really want a vertical character for y in range(0, row_height): if options["border"] and options["vrules"] == FRAME: bits[y].pop() bits[y].append(options["vertical_char"]) if options["border"] and options["hrules"]== ALL: bits[row_height-1].append("\n") bits[row_height-1].append(self._hrule) for y in range(0, row_height): bits[y] = "".join(bits[y]) return "\n".join(bits) ############################## # HTML STRING METHODS # ############################## def get_html_string(self, **kwargs): """Return string representation of HTML formatted version of table in current state. Arguments: start - index of first data row to include in output end - index of last data row to include in output PLUS ONE (list slice style) fields - names of fields (columns) to include header - print a header showing field names (True or False) border - print a border around the table (True or False) hrules - controls printing of horizontal rules after rows. Allowed values: ALL, FRAME, HEADER, NONE vrules - controls printing of vertical rules between columns. Allowed values: FRAME, ALL, NONE int_format - controls formatting of integer data float_format - controls formatting of floating point data padding_width - number of spaces on either side of column data (only used if left and right paddings are None) left_padding_width - number of spaces on left hand side of column data right_padding_width - number of spaces on right hand side of column data sortby - name of field to sort rows by sort_key - sorting key function, applied to data points before sorting attributes - dictionary of name/value pairs to include as HTML attributes in the
tag xhtml - print
tags if True,
tags if false""" options = self._get_options(kwargs) if options["format"]: string = self._get_formatted_html_string(options) else: string = self._get_simple_html_string(options) return string def _get_simple_html_string(self, options): lines = [] if options["xhtml"]: linebreak = "
" else: linebreak = "
" open_tag = [] open_tag.append("") lines.append("".join(open_tag)) # Headers if options["header"]: lines.append(" ") for field in self._field_names: if options["fields"] and field not in options["fields"]: continue lines.append(" " % escape(field).replace("\n", linebreak)) lines.append(" ") # Data rows = self._get_rows(options) formatted_rows = self._format_rows(rows, options) for row in formatted_rows: lines.append(" ") for field, datum in zip(self._field_names, row): if options["fields"] and field not in options["fields"]: continue lines.append(" " % escape(datum).replace("\n", linebreak)) lines.append(" ") lines.append("
%s
%s
") return self._unicode("\n").join(lines) def _get_formatted_html_string(self, options): lines = [] lpad, rpad = self._get_padding_widths(options) if options["xhtml"]: linebreak = "
" else: linebreak = "
" open_tag = [] open_tag.append("") lines.append("".join(open_tag)) # Headers if options["header"]: lines.append(" ") for field in self._field_names: if options["fields"] and field not in options["fields"]: continue lines.append(" %s" % (lpad, rpad, escape(field).replace("\n", linebreak))) lines.append(" ") # Data rows = self._get_rows(options) formatted_rows = self._format_rows(rows, options) aligns = [] valigns = [] for field in self._field_names: aligns.append({ "l" : "left", "r" : "right", "c" : "center" }[self._align[field]]) valigns.append({"t" : "top", "m" : "middle", "b" : "bottom"}[self._valign[field]]) for row in formatted_rows: lines.append(" ") for field, datum, align, valign in zip(self._field_names, row, aligns, valigns): if options["fields"] and field not in options["fields"]: continue lines.append(" %s" % (lpad, rpad, align, valign, escape(datum).replace("\n", linebreak))) lines.append(" ") lines.append("") return self._unicode("\n").join(lines) ############################## # UNICODE WIDTH FUNCTIONS # ############################## def _char_block_width(char): # Basic Latin, which is probably the most common case #if char in xrange(0x0021, 0x007e): #if char >= 0x0021 and char <= 0x007e: if 0x0021 <= char <= 0x007e: return 1 # Chinese, Japanese, Korean (common) if 0x4e00 <= char <= 0x9fff: return 2 # Hangul if 0xac00 <= char <= 0xd7af: return 2 # Combining? if unicodedata.combining(uni_chr(char)): return 0 # Hiragana and Katakana if 0x3040 <= char <= 0x309f or 0x30a0 <= char <= 0x30ff: return 2 # Full-width Latin characters if 0xff01 <= char <= 0xff60: return 2 # CJK punctuation if 0x3000 <= char <= 0x303e: return 2 # Backspace and delete if char in (0x0008, 0x007f): return -1 # Other control characters elif char in (0x0000, 0x001f): return 0 # Take a guess return 1 def _str_block_width(val): return sum(itermap(_char_block_width, itermap(ord, _re.sub("", val)))) ############################## # TABLE FACTORIES # ############################## def from_csv(fp, field_names = None, **kwargs): dialect = csv.Sniffer().sniff(fp.read(1024)) fp.seek(0) reader = csv.reader(fp, dialect) table = PrettyTable(**kwargs) if field_names: table.field_names = field_names else: if py3k: table.field_names = [x.strip() for x in next(reader)] else: table.field_names = [x.strip() for x in reader.next()] for row in reader: table.add_row([x.strip() for x in row]) return table def from_db_cursor(cursor, **kwargs): if cursor.description: table = PrettyTable(**kwargs) table.field_names = [col[0] for col in cursor.description] for row in cursor.fetchall(): table.add_row(row) return table class TableHandler(HTMLParser): def __init__(self, **kwargs): HTMLParser.__init__(self) self.kwargs = kwargs self.tables = [] self.last_row = [] self.rows = [] self.max_row_width = 0 self.active = None self.last_content = "" self.is_last_row_header = False def handle_starttag(self,tag, attrs): self.active = tag if tag == "th": self.is_last_row_header = True def handle_endtag(self,tag): if tag in ["th", "td"]: stripped_content = self.last_content.strip() self.last_row.append(stripped_content) if tag == "tr": self.rows.append( (self.last_row, self.is_last_row_header)) self.max_row_width = max(self.max_row_width, len(self.last_row)) self.last_row = [] self.is_last_row_header = False if tag == "table": table = self.generate_table(self.rows) self.tables.append(table) self.rows = [] self.last_content = " " self.active = None def handle_data(self, data): self.last_content += data def generate_table(self, rows): """ Generates from a list of rows a PrettyTable object. """ table = PrettyTable(**self.kwargs) for row in self.rows: if len(row[0]) < self.max_row_width: appends = self.max_row_width - len(row[0]) for i in range(1,appends): row[0].append("-") if row[1] == True: self.make_fields_unique(row[0]) table.field_names = row[0] else: table.add_row(row[0]) return table def make_fields_unique(self, fields): """ iterates over the row and make each field unique """ for i in range(0, len(fields)): for j in range(i+1, len(fields)): if fields[i] == fields[j]: fields[j] += "'" def from_html(html_code, **kwargs): """ Generates a list of PrettyTables from a string of HTML code. Each in the HTML becomes one PrettyTable object. """ parser = TableHandler(**kwargs) parser.feed(html_code) return parser.tables def from_html_one(html_code, **kwargs): """ Generates a PrettyTables from a string of HTML code which contains only a single
""" tables = from_html(html_code, **kwargs) try: assert len(tables) == 1 except AssertionError: raise Exception("More than one
in provided HTML code! Use from_html instead.") return tables[0] ############################## # MAIN (TEST FUNCTION) # ############################## def main(): x = PrettyTable(["City name", "Area", "Population", "Annual Rainfall"]) x.sortby = "Population" x.reversesort = True x.int_format["Area"] = "04d" x.float_format = "6.1f" x.align["City name"] = "l" # Left align city names x.add_row(["Adelaide", 1295, 1158259, 600.5]) x.add_row(["Brisbane", 5905, 1857594, 1146.4]) x.add_row(["Darwin", 112, 120900, 1714.7]) x.add_row(["Hobart", 1357, 205556, 619.5]) x.add_row(["Sydney", 2058, 4336374, 1214.8]) x.add_row(["Melbourne", 1566, 3806092, 646.9]) x.add_row(["Perth", 5386, 1554769, 869.4]) print(x) if __name__ == "__main__": main() zabbix-cli-1.7.0/zabbix_cli/pyzabbix.py000066400000000000000000000161541311400624000200510ustar00rootroot00000000000000# # Most of the code in this file is from the project PyZabbix: # https://github.com/lukecyca/pyzabbix # # We have modified the login method to be able to send an auth-token so # we do not have to login again as long as the auth-token used is still # active. # # We have also modified the output when an error happens to not show # the username + password information. # from __future__ import unicode_literals import logging import requests import json class _NullHandler(logging.Handler): def emit(self, record): pass logger = logging.getLogger(__name__) logger.addHandler(_NullHandler()) class ZabbixAPIException(Exception): """ generic zabbix api exception code list: -32602 - Invalid params (eg already exists) -32500 - no permissions """ pass class ZabbixAPI(object): def __init__(self, server='http://localhost/zabbix', session=None, use_authenticate=False, timeout=None): """ Parameters: server: Base URI for zabbix web interface (omitting /api_jsonrpc.php) session: optional pre-configured requests.Session instance use_authenticate: Use old (Zabbix 1.8) style authentication timeout: optional connect and read timeout in seconds, default: None (if you're using Requests >= 2.4 you can set it as tuple: "(connect, read)" which is used to set individual connect and read timeouts.) """ if session: self.session = session else: self.session = requests.Session() # Default headers for all requests self.session.headers.update({ 'Content-Type': 'application/json-rpc', 'User-Agent': 'python/pyzabbix', 'Cache-Control': 'no-cache' }) self.use_authenticate = use_authenticate self.auth = '' self.id = 0 self.timeout = timeout self.url = server + '/api_jsonrpc.php' logger.info("JSON-RPC Server Endpoint: %s", self.url) def login(self, user='', password='', auth_token=''): """ Convenience method for calling user.authenticate and storing the resulting auth token for further commands. If use_authenticate is set, it uses the older (Zabbix 1.8) authentication command """ # If the file $HOME/.zabbix-cli_auth_token exists from an # older session, the system will try to reuse the # API-auth-token saved in this file. # # If the file $HOME/.zabbix-cli_auth_token does not exist, we # will login with the username and password. # if auth_token == '': self.auth = '' if self.use_authenticate: self.auth = self.user.authenticate(user=user, password=password) else: self.auth = self.user.login(user=user, password=password) else: self.auth = auth_token self.api_version() return self.auth def confimport(self, format='', source='', rules=''): """Alias for configuration.import because it clashes with Python's import reserved keyword""" return self.do_request( method="configuration.import", params={"format": format, "source": source, "rules": rules} )['result'] def api_version(self): return self.apiinfo.version() def do_request(self, method, params=None): request_json = { 'jsonrpc': '2.0', 'method': method, 'params': params or {}, 'id': self.id, } # We don't have to pass the auth token if asking for the apiinfo.version if self.auth and method != 'apiinfo.version': request_json['auth'] = self.auth logger.debug("Sending: %s", json.dumps(request_json, indent=4, separators=(',', ': '))) response = self.session.post( self.url, data=json.dumps(request_json), timeout=self.timeout ) logger.debug("Response Code: %s", str(response.status_code)) # NOTE: Getting a 412 response code means the headers are not in the # list of allowed headers. response.raise_for_status() if not len(response.text): raise ZabbixAPIException("Received empty response") try: response_json = json.loads(response.text) except ValueError: raise ZabbixAPIException( "Unable to parse json: %s" % response.text ) logger.debug("Response Body: %s", json.dumps(response_json, indent=4, separators=(',', ': '))) self.id += 1 if 'error' in response_json: # some exception if 'data' not in response_json['error']: # some errors don't contain 'data': workaround for ZBX-9340 response_json['error']['data'] = "No data" # # We do not want to get the password value in the error # message if the user uses a not valid username or # password. # if response_json['error']['data'] == 'Login name or password is incorrect.': msg = "Error {code}: {message}: {data}".format( code=response_json['error']['code'], message=response_json['error']['message'], data=response_json['error']['data']) elif response_json['error']['data'] == 'Not authorized': msg = "Error {code}: {data}: {message}".format( code=response_json['error']['code'], data=response_json['error']['data'], message=response_json['error']['message'] + '\n\n* Your API-auth-token has probably expired.\n' + '* Try to login again with your username and password') else: msg = "Error {code}: {message}: {data} while sending {json}".format( code=response_json['error']['code'], message=response_json['error']['message'], data=response_json['error']['data'], json=str(request_json)) raise ZabbixAPIException(msg, response_json['error']['code']) return response_json def __getattr__(self, attr): """Dynamically create an object class (ie: host)""" return ZabbixAPIObjectClass(attr, self) class ZabbixAPIObjectClass(object): def __init__(self, name, parent): self.name = name self.parent = parent def __getattr__(self, attr): """Dynamically create a method (ie: get)""" def fn(*args, **kwargs): if args and kwargs: raise TypeError("Found both args and kwargs") return self.parent.do_request( '{0}.{1}'.format(self.name, attr), args or kwargs )['result'] return fn zabbix-cli-1.7.0/zabbix_cli/version.py000066400000000000000000000000541311400624000176760ustar00rootroot00000000000000#!/usr/bin/env python __version__ = '1.7.0'