├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples ├── example_inventory.csv ├── example_macros.csv ├── example_proxy.csv ├── example_separator_comma.csv ├── example_separator_tab.csv ├── example_snmp.csv └── example_tag.csv ├── images └── csv-host-importer.png └── modules └── csv-host-importer ├── Module.php ├── actions ├── CSVHostImport.php ├── CSVHostImportAction.php └── CSVHostImportExample.php ├── manifest.json ├── manifest.v1.json └── views ├── layout.ichi.example.php └── module.ichi.import.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Version 6.3.0 4 | 5 | * Support for Zabbix 6.4 and 7.0. 6 | * Added support for host tags and macros (`HOST_TAGS`, `HOST_MACROS`). 7 | * Added support for inventory fields (`INV_*`). 8 | * Added support for SNMP community string (`SNMP_COMMUNITY`). 9 | * Added support for additional CSV separators: comma and tabulator 10 | * Example CSV now automatically uses the template name of the current Zabbix version. 11 | * Fixed visible host names being required. 12 | * Fixed incorrect check for missing host groups. 13 | 14 | ## Version 6.2.1 15 | 16 | * Fixed JMX port not being set correctly on the host 17 | 18 | ## Version 6.2.0 19 | 20 | * Added support for interface ports (`AGENT_PORT`, `SNMP_PORT`) and JMX interfaces (`JMX_IP`, `JMX_DNS`, `JMX_PORT`). 21 | * Added support for host tags (`HOST_TAGS`). 22 | * Added support for proxy (`PROXY`). 23 | 24 | ## Version 6.1.0 25 | 26 | * Support for Zabbix 6.4 (for Zabbix 6.0 and 6.2, use previous version) 27 | * Moved menu entry to `Administration` 28 | 29 | ## Version 6.0.4 30 | 31 | * Support for Zabbix 6.2 32 | 33 | ## Version 6.0.2 34 | 35 | * Fixed "Missing host list in session" error when reading larger CSV files 36 | * Fixed incorrect host group and template assignment when the respective fields are empty 37 | * Optional CSV columns now can be omitted entirely from the CSV file instead of leaving them empty 38 | 39 | ## Version 6.0.1 40 | 41 | * Support for Zabbix 6.0 42 | 43 | ## Version 5.4.2 44 | 45 | * Support for Zabbix 5.0, 5.2 and 5.4 46 | * Non-functional menu entry for non-superadmins is now hidden 47 | 48 | ## Version 5.0.2 49 | 50 | * Added support for templates 51 | 52 | ## Version 5.0.1 53 | 54 | * Improved validation of CSV input 55 | 56 | ## Version 5.0.0 57 | 58 | * Initial public release for Zabbix 5.4 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IntelliTrend Zabbix CSV Host Importer 2 | 3 | This is a Zabbix frontend module that provides a simplified host import via CSV files. 4 | 5 | ![csv-host-importer](./images/csv-host-importer.png) 6 | 7 | ## License 8 | 9 | This software is licensed under the GNU Lesser General Public License v3.0. 10 | 11 | ## Download 12 | 13 | You can find the latest versions for the respective Zabbix releases on the [Github releases page](https://github.com/intellitrend/zabbix-csv-host-import-module/releases). 14 | 15 | ## Requirements 16 | 17 | - Zabbix 6.0, 6.2, 6.4 or 7.0 18 | - File write access to the Zabbix frontend server 19 | - Super admin permissions for the Zabbix users that want to use the frontend module 20 | 21 | ## Installation 22 | 23 | For Debian and Ubuntu server, the Zabbix Frontend modules are usually placed in ``/usr/share/zabbix/modules/`` (up to Zabbix 7.0) or ``/usr/share/zabbix/ui/modules/`` (Zabbix 7.2 and newer). 24 | 25 | Copy the folder `modules/csv-host-importer` to `/usr/share/zabbix/modules/csv-host-importer` or `/usr/share/zabbix/ui/modules/csv-host-importer` on the Zabbix frontend web server, depending on the Zabbix version. 26 | 27 | **Note:** If you're using Zabbix 6.2 or 6.0, you'll need to remove `manifest.json` and rename `manifest.v1.json` to `manifest.json`. 28 | 29 | Then go to `Administration`, `General`, `Modules`, click `Scan directory` and enable the new module in the list. 30 | 31 | ## Usage 32 | 33 | Once the frontend module is activated, a new menu entry `Host CSV Importer` should appear under `Configuration` (6.0, 6.2) or `Data collection` (6.4+). 34 | 35 | Here's an example of two hosts: the first one with Zabbix agent and another with an SNMPv2 agent: 36 | ``` 37 | NAME;VISIBLE_NAME;HOST_GROUPS;TEMPLATES;AGENT_IP;AGENT_DNS;SNMP_IP;SNMP_DNS;SNMP_VERSION;DESCRIPTION 38 | example1;Example Host Agent;First host group, second host group;Linux by Zabbix agent;127.0.0.1;localhost;;;;Example Zabbix Agent host 39 | example2;Example Host SNMP;Third host group;Generic by SNMP;;;127.0.0.1;localhost;2;Example SNMPv2 host 40 | ``` 41 | 42 | You can find more specific examples in the [examples directory](./examples/). 43 | 44 | The following CSV columns are supported: 45 | 46 | | Name | Purpose | Default | 47 | | --------------------- | ------------------------------------------------------------ | ----------------- | 48 | | NAME | Host name. (required) | | 49 | | VISIBLE_NAME | Host visible name. | | 50 | | HOST_GROUPS | List of host group names, separated by a '\|'. Missing host groups are created automatically. (required) | | 51 | | HOST_TAGS | List of host tags, separated by a '\|'. The tag format can be either be ``tag name`` (empty value) or ``tag name=tag value``. | | 52 | | HOST_MACROS | List of host macros, separated by a '\|'. The macro format can be either be ``{$MACRONAME}`` (empty value) or ``{$MACRONAME}=macro value``. | | 53 | | PROXY | Name of the proxy that should monitor the host. | | 54 | | PROXY_GROUP | Name of the proxy group assigned to the host. (Zabbix 7+) | | 55 | | TEMPLATES | List of template names to assign to the host, separated by a '\|'. Templates must exist with the specified name. | | 56 | | AGENT_IP | Interface: Zabbix Agent IP address. | | 57 | | AGENT_DNS | Interface: Zabbix Agent DNS name. | | 58 | | AGENT_PORT | Interface: Zabbix Agent port. | 10050 | 59 | | SNMP_IP | Interface: SNMP IP address. | | 60 | | SNMP_DNS | Interface: SNMP DNS name. | | 61 | | SNMP_PORT | Interface: SNMP port. | 161 | 62 | | SNMP_VERSION | Interface: SNMP version number (1, 2 or 3). | | 63 | | SNMP_COMMUNITY | Interface: SNMP community string. | {$SNMP_COMMUNITY} | 64 | | SNMP_MAXREP | Interface: SNMP max repitition count. | 10 | 65 | | SNMP_V3SECNAME | Interface: SNMPv3 security name. | | 66 | | SNMP_V3SECLEVEL | Interface: SNMPv3 security level. | | 67 | | SNMP_V3AUTHPASSPHRASE | Interface: SNMPv3 auth passphrase. | | 68 | | SNMP_V3PRIVPASSPHRASE | Interface: SNMPv3 priv passphrase. | | 69 | | SNMP_V3AUTHPROTOCOL | Interface: SNMPv3 auth protocol. | | 70 | | SNMP_V3PRIVPROTOCOL | Interface: SNMPv3 priv protocol. | | 71 | | SNMP_V3CONTEXTNAME | Interface: SNMPv3 context name. | | 72 | | JMX_IP | Interface: JMX IP address. | | 73 | | JMX_DNS | Interface: JMX DNS name. | | 74 | | JMX_PORT | Interface: JMX port. | 12345 | 75 | | DESCRIPTION | Host description field. | | 76 | | INV_ALIAS | Inventory: Alias | | 77 | | INV_ASSET_TAG | Inventory: Asset tag | | 78 | | INV_CHASSIS | Inventory: Chassis | | 79 | | INV_CONTACT | Inventory: Contact person | | 80 | | INV_CONTRACT_NUMBER | Inventory: Contract number | | 81 | | INV_DATE_HW_DECOMM | Inventory: HW decommissioning date | | 82 | | INV_DATE_HW_EXPIRY | Inventory: HW maintenance expiry date | | 83 | | INV_DATE_HW_INSTALL | Inventory: HW installation date | | 84 | | INV_DATE_HW_PURCHASE | Inventory: HW purchase date | | 85 | | INV_DEPLOYMENT_STATUS | Inventory: Deployment status | | 86 | | INV_HARDWARE | Inventory: Hardware | | 87 | | INV_HARDWARE_FULL | Inventory: Detailed hardware | | 88 | | INV_HOST_NETMASK | Inventory: Host subnet mask | | 89 | | INV_HOST_NETWORKS | Inventory: Host networks | | 90 | | INV_HOST_ROUTER | Inventory: Host router | | 91 | | INV_HW_ARCH | Inventory: HW architecture | | 92 | | INV_INSTALLER_NAME | Inventory: Installer name | | 93 | | INV_LOCATION | Inventory: Location | | 94 | | INV_LOCATION_LAT | Inventory: Location latitude | | 95 | | INV_LOCATION_LON | Inventory: Location longitude | | 96 | | INV_MACADDRESS_A | Inventory: MAC address A | | 97 | | INV_MACADDRESS_B | Inventory: MAC address B | | 98 | | INV_MODEL | Inventory: Model | | 99 | | INV_NAME | Inventory: Name | | 100 | | INV_NOTES | Inventory: Notes | | 101 | | INV_OOB_IP | Inventory: OOB IP address | | 102 | | INV_OOB_NETMASK | Inventory: OOB host subnet mask | | 103 | | INV_OOB_ROUTER | Inventory: OOB router | | 104 | | INV_OS | Inventory: OS name | | 105 | | INV_OS_FULL | Inventory: Detailed OS name | | 106 | | INV_OS_SHORT | Inventory: Short OS name | | 107 | | INV_POC_1_CELL | Inventory: Primary POC mobile number | | 108 | | INV_POC_1_EMAIL | Inventory: Primary email | | 109 | | INV_POC_1_NAME | Inventory: Primary POC name | | 110 | | INV_POC_1_NOTES | Inventory: Primary POC notes | | 111 | | INV_POC_1_PHONE_A | Inventory: Primary POC phone A | | 112 | | INV_POC_1_PHONE_B | Inventory: Primary POC phone B | | 113 | | INV_POC_1_SCREEN | Inventory: Primary POC screen name | | 114 | | INV_POC_2_CELL | Inventory: Secondary POC mobile number | | 115 | | INV_POC_2_EMAIL | Inventory: Secondary POC email | | 116 | | INV_POC_2_NAME | Inventory: Secondary POC name | | 117 | | INV_POC_2_NOTES | Inventory: Secondary POC notes | | 118 | | INV_POC_2_PHONE_A | Inventory: Secondary POC phone A | | 119 | | INV_POC_2_PHONE_B | Inventory: Secondary POC phone B | | 120 | | INV_POC_2_SCREEN | Inventory: Secondary POC screen name | | 121 | | INV_SERIALNO_A | Inventory: Serial number A | | 122 | | INV_SERIALNO_B | Inventory: Serial number B | | 123 | | INV_SITE_ADDRESS_A | Inventory: Site address A | | 124 | | INV_SITE_ADDRESS_B | Inventory: Site address B | | 125 | | INV_SITE_ADDRESS_C | Inventory: Site address C | | 126 | | INV_SITE_CITY | Inventory: Site city | | 127 | | INV_SITE_COUNTRY | Inventory: Site country | | 128 | | INV_SITE_NOTES | Inventory: Site notes | | 129 | | INV_SITE_RACK | Inventory: Site rack location | | 130 | | INV_SITE_STATE | Inventory: Site state | | 131 | | INV_SITE_ZIP | Inventory: Site ZIP/postal code | | 132 | | INV_SOFTWARE | Inventory: Software | | 133 | | INV_SOFTWARE_APP_A | Inventory: Software application A | | 134 | | INV_SOFTWARE_APP_B | Inventory: Software application B | | 135 | | INV_SOFTWARE_APP_C | Inventory: Software application C | | 136 | | INV_SOFTWARE_APP_D | Inventory: Software application D | | 137 | | INV_SOFTWARE_APP_E | Inventory: Software application E | | 138 | | INV_SOFTWARE_FULL | Inventory: Software details | | 139 | | INV_TAG | Inventory: Tag | | 140 | | INV_TYPE | Inventory: Type | | 141 | | INV_TYPE_FULL | Inventory: Type details | | 142 | | INV_URL_A | Inventory: URL A | | 143 | | INV_URL_B | Inventory: URL B | | 144 | | INV_URL_C | Inventory: URL C | | 145 | | INV_VENDOR | Inventory: Vendor | | 146 | 147 | Additional hints: 148 | 149 | * The columns are case-insensitive, so ``NAME``, ``name`` and ``NaMe`` are all valid. 150 | * The columns must be in the first line of the CSV file. 151 | * The separator character must be the same as in the import dialog and can either be a semicolon, comma or a tabulator. 152 | 153 | The CSV file can then be imported in the same menu entry. You get a chance to preview the host list before the actual import. 154 | 155 | -------------------------------------------------------------------------------- /examples/example_inventory.csv: -------------------------------------------------------------------------------- 1 | NAME;HOST_GROUPS;INV_ALIAS;INV_ASSET_TAG;INV_CHASSIS;INV_CONTACT;INV_CONTRACT_NUMBER;INV_DATE_HW_DECOMM;INV_DATE_HW_EXPIRY;INV_DATE_HW_INSTALL;INV_DATE_HW_PURCHASE;INV_DEPLOYMENT_STATUS;INV_HARDWARE;INV_HARDWARE_FULL;INV_HOST_NETMASK;INV_HOST_NETWORKS;INV_HOST_ROUTER;INV_HW_ARCH;INV_INSTALLER_NAME;INV_LOCATION;INV_LOCATION_LAT;INV_LOCATION_LON;INV_MACADDRESS_A;INV_MACADDRESS_B;INV_MODEL;INV_NAME;INV_NOTES;INV_OOB_IP;INV_OOB_NETMASK;INV_OOB_ROUTER;INV_OS;INV_OS_FULL;INV_OS_SHORT;INV_POC_1_CELL;INV_POC_1_EMAIL;INV_POC_1_NAME;INV_POC_1_NOTES;INV_POC_1_PHONE_A;INV_POC_1_PHONE_B;INV_POC_1_SCREEN;INV_POC_2_CELL;INV_POC_2_EMAIL;INV_POC_2_NAME;INV_POC_2_NOTES;INV_POC_2_PHONE_A;INV_POC_2_PHONE_B;INV_POC_2_SCREEN;INV_SERIALNO_A;INV_SERIALNO_B;INV_SITE_ADDRESS_A;INV_SITE_ADDRESS_B;INV_SITE_ADDRESS_C;INV_SITE_CITY;INV_SITE_COUNTRY;INV_SITE_NOTES;INV_SITE_RACK;INV_SITE_STATE;INV_SITE_ZIP;INV_SOFTWARE;INV_SOFTWARE_APP_A;INV_SOFTWARE_APP_B;INV_SOFTWARE_APP_C;INV_SOFTWARE_APP_D;INV_SOFTWARE_APP_E;INV_SOFTWARE_FULL;INV_TAG;INV_TYPE;INV_TYPE_FULL;INV_URL_A;INV_URL_B;INV_URL_C;INV_VENDOR 2 | example_inventory;First host group;Inventory Alias;Inventory Asset tag;Inventory Chassis;Inventory Contact person;Inventory Contract number;Inventory HW decommissioning date;Inventory HW maintenance expiry date;Inventory HW installation date;Inventory HW purchase date;Inventory Deployment status;Inventory Hardware;Inventory Detailed hardware;Inventory Host subnet mask;Inventory Host networks;Inventory Host router;Inventory HW architecture;Inventory Installer name;Inventory Location;Location lat;Location long;Inventory MAC address A;Inventory MAC address B;Inventory Model;Inventory Name;Inventory Notes;Inventory OOB IP address;Inventory OOB host subnet mask;Inventory OOB router;Inventory OS name;Inventory Detailed OS name;Inventory Short OS name;Inventory Primary POC mobile number;Inventory Primary email;Inventory Primary POC name;Inventory Primary POC notes;Inventory Primary POC phone A;Inventory Primary POC phone B;Inventory Primary POC screen name;Inventory Secondary POC mobile number;Inventory Secondary POC email;Inventory Secondary POC name;Inventory Secondary POC notes;Inventory Secondary POC phone A;Inventory Secondary POC phone B;Inventory Secondary POC screen name;Inventory Serial number A;Inventory Serial number B;Inventory Site address A;Inventory Site address B;Inventory Site address C;Inventory Site city;Inventory Site country;Inventory Site notes;Inventory Site rack location;Inventory Site state;Inventory Site ZIP/postal code;Inventory Software;Inventory Software application A;Inventory Software application B;Inventory Software application C;Inventory Software application D;Inventory Software application E;Inventory Software details;Inventory Tag;Inventory Type;Inventory Type details;Inventory URL A;Inventory URL B;Inventory URL C;Inventory Vendor -------------------------------------------------------------------------------- /examples/example_macros.csv: -------------------------------------------------------------------------------- 1 | NAME;HOST_GROUPS;HOST_MACROS 2 | example_macros;First host group;{$EMPTY_MACRO}|{$FIRST_MACRO}=with value|{$SECOND_MACRO}=Equals=allowed 3 | -------------------------------------------------------------------------------- /examples/example_proxy.csv: -------------------------------------------------------------------------------- 1 | NAME;HOST_GROUPS;PROXY 2 | example_proxy;First host group;Test Proxy -------------------------------------------------------------------------------- /examples/example_separator_comma.csv: -------------------------------------------------------------------------------- 1 | NAME,VISIBLE_NAME,HOST_GROUPS,HOST_TAGS,TEMPLATES,AGENT_IP,AGENT_DNS,SNMP_IP,SNMP_DNS,SNMP_VERSION,DESCRIPTION 2 | example_separator_comma,Example Host Comma,First host group|Second host group,,Linux by Zabbix agent,127.0.0.1,localhost,,,,Example Zabbix Agent host -------------------------------------------------------------------------------- /examples/example_separator_tab.csv: -------------------------------------------------------------------------------- 1 | NAME VISIBLE_NAME HOST_GROUPS HOST_TAGS TEMPLATES AGENT_IP AGENT_DNS SNMP_IP SNMP_DNS SNMP_VERSION DESCRIPTION 2 | example_separator_tab Example Host Tab First host group|Second host group Linux by Zabbix agent 127.0.0.1 localhost Example Zabbix Agent host -------------------------------------------------------------------------------- /examples/example_snmp.csv: -------------------------------------------------------------------------------- 1 | NAME; VISIBLE_NAME; HOST_GROUPS; SNMP_IP; SNMP_DNS; SNMP_VERSION; SNMP_COMMUNITY 2 | example_snmp; Test SNMP; First host group; 127.0.0.1; localhost; 2; TestCommunityString -------------------------------------------------------------------------------- /examples/example_tag.csv: -------------------------------------------------------------------------------- 1 | NAME;HOST_GROUPS;HOST_TAGS 2 | example_tags;First host group;First tag|Second tag=with value|Third tag=Equals=allowed|Empty tag= 3 | -------------------------------------------------------------------------------- /images/csv-host-importer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intellitrend/zabbix-csv-host-import-module/d18cf9340f72c9119288af7a1671783972c7b2df/images/csv-host-importer.png -------------------------------------------------------------------------------- /modules/csv-host-importer/Module.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright IntelliTrend GmbH, https://www.intellitrend.de 8 | * @license GNU Lesser General Public License v3.0 9 | * 10 | * You can redistribute this library and/or modify it under the terms of 11 | * the GNU LGPL as published by the Free Software Foundation, 12 | * either version 3 of the License, or any later version. 13 | * However you must not change author and copyright information. 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace Modules\ICHI; 19 | 20 | use APP; 21 | use CController as CAction; 22 | use CWebUser; 23 | use CMenuItem; 24 | 25 | // alias for Zabbix 6.0 26 | if (!class_exists('Zabbix\Core\CModule') && class_exists('Core\CModule')) { 27 | class_alias('Core\CModule', 'Zabbix\Core\CModule'); 28 | } 29 | 30 | use Zabbix\Core\CModule; 31 | 32 | /** 33 | * Please see Core\CModule class for additional reference. 34 | */ 35 | class Module extends CModule { 36 | 37 | /** 38 | * Initialize module. 39 | */ 40 | public function init(): void { 41 | // Only super admins should see the menu entry 42 | if (CWebUser::getType() != USER_TYPE_SUPER_ADMIN) { 43 | return; 44 | } 45 | 46 | if (substr(ZABBIX_VERSION, 0, 3) == "6.0") { 47 | $menu = _('Configuration'); 48 | } else { 49 | $menu = _('Data collection'); 50 | } 51 | 52 | // Initialize main menu (CMenu class instance). 53 | APP::Component()->get('menu.main')->findOrAdd($menu)->getSubmenu()->add((new CMenuItem(_('Host CSV Importer')))->setAction('ichi.import')); 54 | } 55 | 56 | /** 57 | * Event handler, triggered before executing the action. 58 | * 59 | * @param CAction $action Action instance responsible for current request. 60 | */ 61 | public function onBeforeAction(CAction $action): void { 62 | } 63 | 64 | /** 65 | * Event handler, triggered on application exit. 66 | * 67 | * @param CAction $action Action instance responsible for current request. 68 | */ 69 | public function onTerminate(CAction $action): void { 70 | } 71 | } -------------------------------------------------------------------------------- /modules/csv-host-importer/actions/CSVHostImport.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright IntelliTrend GmbH, https://www.intellitrend.de 8 | * @license GNU Lesser General Public License v3.0 9 | * 10 | * You can redistribute this library and/or modify it under the terms of 11 | * the GNU LGPL as published by the Free Software Foundation, 12 | * either version 3 of the License, or any later version. 13 | * However you must not change author and copyright information. 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace Modules\ICHI\Actions; 19 | 20 | use CControllerResponseData; 21 | use CRoleHelper; 22 | use API; 23 | use CWebUser; 24 | 25 | function startsWith($haystack, $needle ) { 26 | return substr($haystack, 0, strlen($needle)) === $needle; 27 | } 28 | 29 | /** 30 | * Host CSV importer module action. 31 | */ 32 | class CSVHostImport extends CSVHostImportAction { 33 | 34 | // maximum length of a single CSV line 35 | const CSV_MAX_LINE_LEN = 8192; 36 | 37 | // separator used for fields that can contain multiple elements 38 | const ELEMENT_SEPARATOR = '|'; 39 | // separator used for elements that can have a value (tags) 40 | const VALUE_SEPARATOR = '='; 41 | 42 | // user-friendly messages for upload error codes 43 | const UPLOAD_ERRORS = [ 44 | 0 => 'There is no error, the file uploaded with success', 45 | 1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini', 46 | 2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form', 47 | 3 => 'The uploaded file was only partially uploaded', 48 | 4 => 'No file was uploaded', 49 | 6 => 'Missing a temporary folder', 50 | 7 => 'Failed to write file to disk.', 51 | 8 => 'A PHP extension stopped the file upload.', 52 | ]; 53 | 54 | private $csvColumns; 55 | private $hostlist = []; 56 | private $hostcols = []; 57 | private $step = 0; 58 | private $separator; 59 | 60 | /** 61 | * Initialize action. Method called by Zabbix core. 62 | * 63 | * @return void 64 | */ 65 | public function init(): void { 66 | // define CSV columns 67 | $this->csvColumns = [ 68 | // Technical name Friendly name Default Required 69 | ['NAME', 'Name', '', true], 70 | ['VISIBLE_NAME', 'Visible name', '', false], 71 | ['HOST_GROUPS', 'Host groups', '', true], 72 | ['HOST_TAGS', 'Host tags', '', false], 73 | ['HOST_MACROS', 'Host macros', '', false], 74 | ['PROXY', 'Proxy', '', false], 75 | ['PROXY_GROUP', 'Proxy Group', '', false], 76 | ['STATUS', 'Host enabled status' , '0', false], 77 | ['TEMPLATES', 'Templates', '', false], 78 | ['AGENT_IP', 'Agent IP', '', false], 79 | ['AGENT_DNS', 'Agent DNS', '', false], 80 | ['AGENT_PORT', 'Agent port', '10050', false], 81 | ['SNMP_IP', 'SNMP IP', '', false], 82 | ['SNMP_DNS', 'SNMP DNS', '', false], 83 | ['SNMP_PORT', 'SNMP port', '161', false], 84 | ['SNMP_VERSION', 'SNMP version', '', false], 85 | ['SNMP_BULK', 'SNMP bulk mode', '1', false], 86 | ['SNMP_COMMUNITY', 'SNMP community', '{$SNMP_COMMUNITY}', false], 87 | ['SNMP_MAXREP', 'SNMP max repetitions', '10', false], 88 | ['SNMP_V3SECNAME', 'SNMPv3 security name', '', false], 89 | ['SNMP_V3SECLEVEL', 'SNMPv3 security level', '', false], 90 | ['SNMP_V3AUTHPASSPHRASE', 'SNMPv3 authentication passphrase', '', false], 91 | ['SNMP_V3PRIVPASSPHRASE', 'SNMPv3 privacy passphrase', '', false], 92 | ['SNMP_V3AUTHPROTOCOL', 'SNMPv3 authentication protocol', '', false], 93 | ['SNMP_V3PRIVPROTOCOL', 'SNMPv3 privacy protocol', '', false], 94 | ['SNMP_V3CONTEXTNAME', 'SNMPv3 context name', '', false], 95 | ['DESCRIPTION', 'Description', '', false], 96 | ['JMX_IP', 'JMX IP', '', false], 97 | ['JMX_DNS', 'JMX DNS', '', false], 98 | ['JMX_PORT', 'JMX port', '12345', false], 99 | ['INV_ALIAS', 'Alias', '', false], 100 | ['INV_ASSET_TAG', 'Asset tag', '', false], 101 | ['INV_CHASSIS', 'Chassis', '', false], 102 | ['INV_CONTACT', 'Contact person', '', false], 103 | ['INV_CONTRACT_NUMBER', 'Contract number', '', false], 104 | ['INV_DATE_HW_DECOMM', 'HW decommissioning date', '', false], 105 | ['INV_DATE_HW_EXPIRY', 'HW maintenance expiry date', '', false], 106 | ['INV_DATE_HW_INSTALL', 'HW installation date', '', false], 107 | ['INV_DATE_HW_PURCHASE', 'HW purchase date', '', false], 108 | ['INV_DEPLOYMENT_STATUS', 'Deployment status', '', false], 109 | ['INV_HARDWARE', 'Hardware', '', false], 110 | ['INV_HARDWARE_FULL', 'Detailed hardware', '', false], 111 | ['INV_HOST_NETMASK', 'Host subnet mask', '', false], 112 | ['INV_HOST_NETWORKS', 'Host networks', '', false], 113 | ['INV_HOST_ROUTER', 'Host router', '', false], 114 | ['INV_HW_ARCH', 'HW architecture', '', false], 115 | ['INV_INSTALLER_NAME', 'Installer name', '', false], 116 | ['INV_LOCATION', 'Location', '', false], 117 | ['INV_LOCATION_LAT', 'Location latitude', '', false], 118 | ['INV_LOCATION_LON', 'Location longitude', '', false], 119 | ['INV_MACADDRESS_A', 'MAC address A', '', false], 120 | ['INV_MACADDRESS_B', 'MAC address B', '', false], 121 | ['INV_MODEL', 'Model', '', false], 122 | ['INV_NAME', 'Name', '', false], 123 | ['INV_NOTES', 'Notes', '', false], 124 | ['INV_OOB_IP', 'OOB IP address', '', false], 125 | ['INV_OOB_NETMASK', 'OOB host subnet mask', '', false], 126 | ['INV_OOB_ROUTER', 'OOB router', '', false], 127 | ['INV_OS', 'OS name', '', false], 128 | ['INV_OS_FULL', 'Detailed OS name', '', false], 129 | ['INV_OS_SHORT', 'Short OS name', '', false], 130 | ['INV_POC_1_CELL', 'Primary POC mobile number', '', false], 131 | ['INV_POC_1_EMAIL', 'Primary email', '', false], 132 | ['INV_POC_1_NAME', 'Primary POC name', '', false], 133 | ['INV_POC_1_NOTES', 'Primary POC notes', '', false], 134 | ['INV_POC_1_PHONE_A', 'Primary POC phone A', '', false], 135 | ['INV_POC_1_PHONE_B', 'Primary POC phone B', '', false], 136 | ['INV_POC_1_SCREEN', 'Primary POC screen name', '', false], 137 | ['INV_POC_2_CELL', 'Secondary POC mobile number', '', false], 138 | ['INV_POC_2_EMAIL', 'Secondary POC email', '', false], 139 | ['INV_POC_2_NAME', 'Secondary POC name', '', false], 140 | ['INV_POC_2_NOTES', 'Secondary POC notes', '', false], 141 | ['INV_POC_2_PHONE_A', 'Secondary POC phone A', '', false], 142 | ['INV_POC_2_PHONE_B', 'Secondary POC phone B', '', false], 143 | ['INV_POC_2_SCREEN', 'Secondary POC screen name', '', false], 144 | ['INV_SERIALNO_A', 'Serial number A', '', false], 145 | ['INV_SERIALNO_B', 'Serial number B', '', false], 146 | ['INV_SITE_ADDRESS_A', 'Site address A', '', false], 147 | ['INV_SITE_ADDRESS_B', 'Site address B', '', false], 148 | ['INV_SITE_ADDRESS_C', 'Site address C', '', false], 149 | ['INV_SITE_CITY', 'Site city', '', false], 150 | ['INV_SITE_COUNTRY', 'Site country', '', false], 151 | ['INV_SITE_NOTES', 'Site notes', '', false], 152 | ['INV_SITE_RACK', 'Site rack location', '', false], 153 | ['INV_SITE_STATE', 'Site state', '', false], 154 | ['INV_SITE_ZIP', 'Site ZIP/postal code', '', false], 155 | ['INV_SOFTWARE', 'Software', '', false], 156 | ['INV_SOFTWARE_APP_A', 'Software application A', '', false], 157 | ['INV_SOFTWARE_APP_B', 'Software application B', '', false], 158 | ['INV_SOFTWARE_APP_C', 'Software application C', '', false], 159 | ['INV_SOFTWARE_APP_D', 'Software application D', '', false], 160 | ['INV_SOFTWARE_APP_E', 'Software application E', '', false], 161 | ['INV_SOFTWARE_FULL', 'Software details', '', false], 162 | ['INV_TAG', 'Tag', '', false], 163 | ['INV_TYPE', 'Type', '', false], 164 | ['INV_TYPE_FULL', 'Type details', '', false], 165 | ['INV_URL_A', 'URL A', '', false], 166 | ['INV_URL_B', 'URL B', '', false], 167 | ['INV_URL_C', 'URL C', '', false], 168 | ['INV_VENDOR', 'Vendor', '', false], 169 | ]; 170 | 171 | /** 172 | * Disable SID (Sessoin ID) validation. Session ID validation should only be used for actions which involde data 173 | * modification, such as update or delete actions. In such case Session ID must be presented in the URL, so that 174 | * the URL would expire as soon as the session expired. 175 | */ 176 | if (method_exists($this, 'disableSIDvalidation')) { 177 | $this->disableSIDvalidation(); 178 | } else { 179 | $this->disableCsrfValidation(); 180 | } 181 | } 182 | 183 | /** 184 | * Check and sanitize user input parameters. Method called by Zabbix core. Execution stops if false is returned. 185 | * 186 | * @return bool true on success, false on error. 187 | */ 188 | protected function checkInput(): bool { 189 | $fields = [ 190 | 'step' => 'in 0,1,2', 191 | 'separator' => 'in 0,1,2', 192 | 'cancel' => 'string', 193 | ]; 194 | 195 | $ret = $this->validateInput($fields); 196 | 197 | return $ret; 198 | } 199 | 200 | /** 201 | * Check if the user has permission to execute this action. Method called by Zabbix core. 202 | * Execution stops if false is returned. 203 | * 204 | * @return bool 205 | */ 206 | protected function checkPermissions(): bool { 207 | return $this->checkAccess(CRoleHelper::UI_ADMINISTRATION_GENERAL); 208 | } 209 | 210 | private function csvUpload($path): bool { 211 | // can't continue here if there was no upload 212 | if (!isset($_FILES['csv_file'])) { 213 | error(_('Missing file upload.')); 214 | return false; 215 | } 216 | 217 | // check if there was a problem with the upload 218 | $csv_file = $_FILES['csv_file']; 219 | if ($csv_file['error'] != UPLOAD_ERR_OK) { 220 | error(_(self::UPLOAD_ERRORS[$csv_file['error']])); 221 | return false; 222 | } 223 | 224 | move_uploaded_file($csv_file['tmp_name'], $path); 225 | return true; 226 | } 227 | 228 | private function csvParse($path): bool { 229 | try { 230 | $this->hostlist = []; 231 | $this->hostcols = []; 232 | 233 | if (($fp = fopen($path, 'r')) !== FALSE) { 234 | // check for BOM 235 | if (fgets($fp, 4) !== "\xef\xbb\xbf") { 236 | // BOM not found - rewind pointer to start of file 237 | rewind($fp); 238 | } 239 | 240 | // get first CSV line, which is the header 241 | $header = fgetcsv($fp, self::CSV_MAX_LINE_LEN, $this->csvSeparators[$this->separator]); 242 | if ($header === FALSE) { 243 | error(_('Empty CSV file.')); 244 | return false; 245 | } 246 | 247 | // trim and upper-case all values in the header row 248 | $header_count = count($header); 249 | for ($i = 0; $i < $header_count; $i++) { 250 | $header[$i] = trim($header[$i]); 251 | foreach ($this->csvColumns as $csvColumn) { 252 | // check for technical name or friendly name 253 | if (strcasecmp($header[$i], $csvColumn[0]) === 0 || strcasecmp($header[$i], $csvColumn[1]) === 0) { 254 | // save column index 255 | $header[$i] = $csvColumn[0]; 256 | // add to defined column list 257 | $this->hostcols[$csvColumn[0]] = $csvColumn[1]; 258 | break; 259 | } 260 | } 261 | } 262 | 263 | // check if all required columns are defined (surplus columns are silently ignored) 264 | foreach ($this->csvColumns as $csvColumn) { 265 | if ($csvColumn[3] && array_search($csvColumn[0], $header) === false) { 266 | error(_s('Missing required column "%1$s" / "%2$s" in CSV file.', $csvColumn[0], $csvColumn[1])); 267 | return false; 268 | } 269 | } 270 | 271 | // get all other records till the end of the file 272 | $linenum = 1; // header was already read, so start at 1 273 | while (($line = fgetcsv($fp, self::CSV_MAX_LINE_LEN, $this->csvSeparators[$this->separator])) !== FALSE) { 274 | $linenum++; 275 | $column_count = count($line); 276 | if ($column_count < $header_count) { 277 | error(_s('Missing column "%1$s" in line %2$d"', $header[$column_count], $linenum)); 278 | continue; 279 | } 280 | 281 | $host = []; 282 | foreach ($line as $index => $value) { 283 | if ($index >= $header_count) { 284 | // ignore surplus columns 285 | break; 286 | } 287 | $host[$header[$index]] = trim($value); 288 | } 289 | 290 | // make sure all columns are defined 291 | foreach ($this->csvColumns as $csvColumn) { 292 | // required coumns not only must exist but also be non-empty 293 | if ($csvColumn[3] && (!array_key_exists($csvColumn[0], $host) || $host[$csvColumn[0]] === '')) { 294 | error(_s('Empty required column "%1$s" in CSV file line %2$d.', $csvColumn[0], $linenum)); 295 | return false; 296 | } 297 | 298 | // set default value if column is defined and empty 299 | if (!array_key_exists($csvColumn[0], $host)) { 300 | $host[$csvColumn[0]] = $csvColumn[2]; 301 | } 302 | } 303 | 304 | $this->hostlist[] = $host; 305 | } 306 | fclose($fp); 307 | } 308 | } catch (Exception $e) { 309 | // catch potential parsing exceptions and display them in the view 310 | error($e->getMessage()); 311 | return false; 312 | } 313 | 314 | return true; 315 | } 316 | 317 | private function importHost($host): int { 318 | // Get the current API version 319 | $api_version = API::APIInfo()->version([]); 320 | 321 | $zbxhost = [ 322 | 'host' => $host['NAME'] 323 | ]; 324 | 325 | if ($host['VISIBLE_NAME'] !== '') { 326 | $zbxhost['name'] = $host['VISIBLE_NAME']; 327 | } 328 | 329 | if ($host['DESCRIPTION'] !== '') { 330 | $zbxhost['description'] = $host['DESCRIPTION']; 331 | } 332 | 333 | if ($host['HOST_GROUPS'] !== '') { 334 | $hostgroups = explode(self::ELEMENT_SEPARATOR, $host['HOST_GROUPS']); 335 | $zbxhostgroups = []; 336 | 337 | foreach ($hostgroups as $hostgroup) { 338 | $hostgroup = trim($hostgroup); 339 | if ($hostgroup === '') { 340 | continue; 341 | } 342 | 343 | $hostgroup = trim($hostgroup); 344 | $zbxhostgroup = API::HostGroup()->get([ 345 | 'output' => ['groupid'], 346 | 'filter' => ['name' => $hostgroup], 347 | 'limit' => 1 348 | ]); 349 | 350 | if (!$zbxhostgroup) { 351 | $result = API::HostGroup()->create(['name' => $hostgroup]); 352 | $zbxhostgroup = [['groupid' => $result['groupids'][0]]]; 353 | } 354 | 355 | $zbxhostgroups[] = $zbxhostgroup[0]; 356 | } 357 | 358 | $zbxhost['groups'] = $zbxhostgroups; 359 | } 360 | 361 | if ($host['HOST_TAGS'] !== '') { 362 | $hosttags = explode(self::ELEMENT_SEPARATOR, $host['HOST_TAGS']); 363 | $zbxhost['tags'] = []; 364 | 365 | foreach ($hosttags as $hosttag) { 366 | if ($hosttag === '') { 367 | continue; 368 | } 369 | 370 | if (str_contains($hosttag, self::VALUE_SEPARATOR)) { 371 | $tmp = explode(self::VALUE_SEPARATOR, $hosttag, 2); 372 | $zbxhost['tags'][] = [ 373 | 'tag' => $tmp[0], 374 | 'value' => $tmp[1], 375 | ]; 376 | } else { 377 | $zbxhost['tags'][] = [ 378 | 'tag' => $hosttag, 379 | ]; 380 | } 381 | } 382 | } 383 | 384 | if ($host['HOST_MACROS'] !== '') { 385 | $hostmacros = explode(self::ELEMENT_SEPARATOR, $host['HOST_MACROS']); 386 | $zbxhost['macros'] = []; 387 | 388 | foreach ($hostmacros as $hostmacro) { 389 | if ($hostmacro === '') { 390 | continue; 391 | } 392 | 393 | if (str_contains($hostmacro, self::VALUE_SEPARATOR)) { 394 | $tmp = explode(self::VALUE_SEPARATOR, $hostmacro, 2); 395 | $zbxhost['macros'][] = [ 396 | 'macro' => $tmp[0], 397 | 'value' => $tmp[1], 398 | ]; 399 | } else { 400 | $zbxhost['macros'][] = [ 401 | 'macro' => $hostmacro, 402 | 'value' => '', 403 | ]; 404 | } 405 | } 406 | } 407 | 408 | $zbxhost['inventory'] = []; 409 | foreach ($host as $key => $value) { 410 | if (startsWith($key, 'INV_') && $value !== '') { 411 | $invKey = strtolower(substr($key, 4)); 412 | $zbxhost['inventory'][$invKey] = $value; 413 | } 414 | } 415 | 416 | if ($host['PROXY'] !== '') { 417 | if ($api_version >= '7.0') { 418 | $zbxproxy = API::Proxy()->get([ 419 | 'output' => ['proxyid'], 420 | 'filter' => ['name' => $host['PROXY']], 421 | 'limit' => 1 422 | ]); 423 | 424 | if ($zbxproxy) { 425 | $zbxhost['proxyid'] = $zbxproxy[0]['proxyid']; 426 | $zbxhost['monitored_by'] = 1; // PROXY 427 | } else { 428 | error(_s('Proxy "%1$s" on host "%2$s" not found.', $host['PROXY'], $host['NAME'])); 429 | return -1; 430 | } 431 | } else { 432 | $zbxproxy = API::Proxy()->get([ 433 | 'output' => ['proxyid'], 434 | 'filter' => ['host' => $host['PROXY']], 435 | 'limit' => 1 436 | ]); 437 | 438 | if ($zbxproxy) { 439 | $zbxhost['proxy_hostid'] = $zbxproxy[0]['proxyid']; 440 | } else { 441 | error(_s('Proxy "%1$s" on host "%2$s" not found.', $host['PROXY'], $host['NAME'])); 442 | return -1; 443 | } 444 | } 445 | } 446 | 447 | if ($host['PROXY_GROUP'] !== '') { 448 | if ($api_version >= '7.0') { 449 | $zbxproxygroup = API::ProxyGroup()->get([ 450 | 'output' => ['proxy_groupid'], 451 | 'filter' => ['name' => $host['PROXY_GROUP']], 452 | 'limit' => 1 453 | ]); 454 | if ($zbxproxygroup) { 455 | $zbxhost['proxy_groupid'] = $zbxproxygroup[0]['proxy_groupid']; 456 | $zbxhost['monitored_by'] = 2; // PROXY_GROUP 457 | } else { 458 | error(_s('Proxy Group "%1$s" on host "%2$s" not found.', $host['PROXY_GROUP'], $host['NAME'])); 459 | return -1; 460 | } 461 | } else { 462 | error(_s('Proxy Groups supported on API v7 and above. Cant add proxy group "%1$s" on host "%2$s" ', $host['PROXY_GROUP'], $host['NAME'])); 463 | return -1; 464 | } 465 | } 466 | 467 | if ($host['STATUS'] !== '') { 468 | $zbxhost['status'] = $host['STATUS'] !== '' ? intval($host['STATUS']) : 0; 469 | } 470 | 471 | if ($host['TEMPLATES'] !== '') { 472 | $templates = explode(self::ELEMENT_SEPARATOR, $host['TEMPLATES']); 473 | $zbxtemplates = []; 474 | 475 | foreach ($templates as $template) { 476 | $template = trim($template); 477 | if ($template === '') { 478 | continue; 479 | } 480 | 481 | $zbxtemplate = API::Template()->get([ 482 | 'output' => ['templateid'], 483 | 'filter' => ['name' => $template], 484 | 'limit' => 1 485 | ]); 486 | 487 | if ($zbxtemplate) { 488 | $zbxtemplates[] = $zbxtemplate[0]; 489 | } else { 490 | error(_s('Template "%1$s" on host "%2$s" not found.', $template, $host['NAME'])); 491 | return -1; 492 | } 493 | } 494 | 495 | $zbxhost['templates'] = $zbxtemplates; 496 | } 497 | 498 | $zbxinterfaces = []; 499 | 500 | if ($host['AGENT_IP'] !== '' || $host['AGENT_DNS'] !== '') { 501 | $zbxinterfaces[] = [ 502 | 'type' => 1, 503 | 'dns' => $host['AGENT_DNS'], 504 | 'ip' => $host['AGENT_IP'], 505 | 'main' => 1, 506 | 'useip' => $host['AGENT_IP'] !== '' ? 1 : 0, 507 | 'port' => $host['AGENT_PORT'] !== '' ? intval($host['AGENT_PORT']) : 10050, 508 | ]; 509 | } 510 | 511 | if (($host['SNMP_IP'] !== '' || $host['SNMP_DNS'] !== '') && $host['SNMP_VERSION'] !== '3') { 512 | $zbxinterfaces[] = [ 513 | 'type' => 2, 514 | 'dns' => $host['SNMP_DNS'], 515 | 'ip' => $host['SNMP_IP'], 516 | 'main' => 1, 517 | 'useip' => $host['SNMP_IP'] !== '' ? 1 : 0, 518 | 'port' => $host['SNMP_PORT'] !== '' ? intval($host['SNMP_PORT']) : 161, 519 | 'details' => [ 520 | 'version' => $host['SNMP_VERSION'] !== '' ? intval($host['SNMP_VERSION']) : 1, 521 | 'bulk' => $host['SNMP_BULK'] !== '' ? intval($host['SNMP_BULK']) : 1, 522 | 'community' => $host['SNMP_COMMUNITY'], 523 | 'max_repetitions' => $host['SNMP_MAXREP'] !== '' ? intval($host['SNMP_MAXREP']) : 10 524 | ] 525 | ]; 526 | } 527 | 528 | if (($host['SNMP_IP'] !== '' || $host['SNMP_DNS'] !== '') && ($host['SNMP_VERSION'] == '3')) { 529 | $zbxinterfaces[] = [ 530 | 'type' => 2, 531 | 'dns' => $host['SNMP_DNS'], 532 | 'ip' => $host['SNMP_IP'], 533 | 'main' => 1, 534 | 'useip' => $host['SNMP_IP'] !== '' ? 1 : 0, 535 | 'port' => $host['SNMP_PORT'] !== '' ? intval($host['SNMP_PORT']) : 161, 536 | 'details' => [ 537 | 'version' => intval($host['SNMP_VERSION']), 538 | 'bulk' => $host['SNMP_BULK'] !== '' ? intval($host['SNMP_BULK']) : 1, 539 | 'max_repetitions' => $host['SNMP_MAXREP'] !== '' ? intval($host['SNMP_MAXREP']) : 10, 540 | 'securityname' => $host['SNMP_V3SECNAME'], 541 | 'securitylevel' => intval($host['SNMP_V3SECLEVEL']), 542 | 'authpassphrase' => $host['SNMP_V3AUTHPASSPHRASE'], 543 | 'privpassphrase' => $host['SNMP_V3PRIVPASSPHRASE'], 544 | 'authprotocol' => intval($host['SNMP_V3AUTHPROTOCOL']), 545 | 'privprotocol' => intval($host['SNMP_V3PRIVPROTOCOL']), 546 | 'contextname' => $host['SNMP_V3CONTEXTNAME'] 547 | ] 548 | ]; 549 | } 550 | 551 | if ($host['JMX_IP'] !== '' || $host['JMX_DNS'] !== '') { 552 | $zbxinterfaces[] = [ 553 | 'type' => 4, 554 | 'dns' => $host['JMX_DNS'], 555 | 'ip' => $host['JMX_IP'], 556 | 'main' => 1, 557 | 'useip' => $host['JMX_IP'] !== '' ? 1 : 0, 558 | 'port' => $host['JMX_PORT'] !== '' ? intval($host['JMX_PORT']) : 12345, 559 | ]; 560 | } 561 | 562 | if ($zbxinterfaces) { 563 | $zbxhost['interfaces'] = $zbxinterfaces; 564 | } 565 | 566 | $result = API::Host()->create($zbxhost); 567 | if ($result && $result['hostids']) { 568 | return intval($result['hostids'][0]); 569 | } 570 | 571 | return -1; 572 | } 573 | 574 | private function importHosts() { 575 | foreach ($this->hostlist as &$host) { 576 | $host['HOSTID'] = $this->importHost($host); 577 | } 578 | unset($host); 579 | } 580 | 581 | /** 582 | * Prepare the response object for the view. Method called by Zabbix core. 583 | * 584 | * @return void 585 | */ 586 | protected function doAction() { 587 | $tmpPath = sprintf("%s/ichi.hostlist.%d.csv", sys_get_temp_dir(), CWebUser::$data['userid']); 588 | 589 | if ($this->hasInput('separator')) { 590 | $this->separator = $this->getInput('separator'); 591 | } else { 592 | $this->separator = 0; 593 | } 594 | 595 | if ($this->hasInput('step')) { 596 | $this->step = intval($this->getInput('step')) & 3; 597 | } else { 598 | $this->step = 0; 599 | } 600 | 601 | // reset step if cancelled by user 602 | if ($this->hasInput('cancel')) { 603 | $this->step = 0; 604 | } 605 | 606 | switch ($this->step) { 607 | case 0: 608 | // upload 609 | if (file_exists($tmpPath)) { 610 | unlink($tmpPath); 611 | } 612 | break; 613 | case 1: 614 | // preview 615 | if (!$this->csvUpload($tmpPath) || !$this->csvParse($tmpPath)) { 616 | // upload or parser error, go back to upload step 617 | $this->step = 0; 618 | } 619 | break; 620 | case 2: 621 | // import 622 | if (!file_exists($tmpPath)) { 623 | error(_('Missing temporary host file.')); 624 | break; 625 | } 626 | if (!$this->csvParse($tmpPath)) { 627 | error(_('Unexpected parsing error.')); 628 | break; 629 | } 630 | $this->importHosts(); 631 | unlink($tmpPath); 632 | break; 633 | } 634 | 635 | $response = new CControllerResponseData([ 636 | 'hostlist' => $this->hostlist, 637 | 'hostcols' => $this->hostcols, 638 | 'step' => $this->step, 639 | 'separator' => $this->separator, 640 | ]); 641 | $response->setTitle(_('Host CSV Importer')); 642 | $this->setResponse($response); 643 | } 644 | } 645 | ?> -------------------------------------------------------------------------------- /modules/csv-host-importer/actions/CSVHostImportAction.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright IntelliTrend GmbH, https://www.intellitrend.de 8 | * @license GNU Lesser General Public License v3.0 9 | * 10 | * You can redistribute this library and/or modify it under the terms of 11 | * the GNU LGPL as published by the Free Software Foundation, 12 | * either version 3 of the License, or any later version. 13 | * However you must not change author and copyright information. 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace Modules\ICHI\Actions; 19 | 20 | use CController as CAction; 21 | 22 | abstract class CSVHostImportAction extends CAction { 23 | // supported CSV separator characters 24 | protected $csvSeparators = [';', ',', "\t"]; 25 | } 26 | 27 | ?> 28 | -------------------------------------------------------------------------------- /modules/csv-host-importer/actions/CSVHostImportExample.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright IntelliTrend GmbH, https://www.intellitrend.de 8 | * @license GNU Lesser General Public License v3.0 9 | * 10 | * You can redistribute this library and/or modify it under the terms of 11 | * the GNU LGPL as published by the Free Software Foundation, 12 | * either version 3 of the License, or any later version. 13 | * However you must not change author and copyright information. 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace Modules\ICHI\Actions; 19 | 20 | use CControllerResponseData; 21 | use CRoleHelper; 22 | 23 | /** 24 | * Host CSV importer example page action. 25 | */ 26 | class CSVHostImportExample extends CSVHostImportAction { 27 | 28 | /** 29 | * Initialize action. Method called by Zabbix core. 30 | * 31 | * @return void 32 | */ 33 | public function init(): void { 34 | /** 35 | * Disable SID (Sessoin ID) validation. Session ID validation should only be used for actions which involde data 36 | * modification, such as update or delete actions. In such case Session ID must be presented in the URL, so that 37 | * the URL would expire as soon as the session expired. 38 | */ 39 | if (method_exists($this, 'disableSIDvalidation')) { 40 | $this->disableSIDvalidation(); 41 | } else { 42 | $this->disableCsrfValidation(); 43 | } 44 | } 45 | 46 | /** 47 | * Check and sanitize user input parameters. Method called by Zabbix core. Execution stops if false is returned. 48 | * 49 | * @return bool true on success, false on error. 50 | */ 51 | protected function checkInput(): bool { 52 | $fields = [ 53 | 'separator' => 'in 0,1,2', 54 | ]; 55 | 56 | $ret = $this->validateInput($fields); 57 | 58 | return $ret; 59 | } 60 | 61 | /** 62 | * Check if the user has permission to execute this action. Method called by Zabbix core. 63 | * Execution stops if false is returned. 64 | * 65 | * @return bool 66 | */ 67 | protected function checkPermissions(): bool { 68 | return $this->checkAccess(CRoleHelper::UI_ADMINISTRATION_GENERAL); 69 | } 70 | 71 | /** 72 | * Prepare the response object for the view. Method called by Zabbix core. 73 | * 74 | * @return void 75 | */ 76 | protected function doAction() { 77 | if ($this->hasInput('separator')) { 78 | $separator = $this->getInput('separator'); 79 | } else { 80 | $separator = 0; 81 | } 82 | 83 | $response = new CControllerResponseData([ 84 | 'separator' => $this->csvSeparators[$separator], 85 | ]); 86 | $this->setResponse($response); 87 | } 88 | } -------------------------------------------------------------------------------- /modules/csv-host-importer/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "id": "intellitrend_csv_host_importer", 4 | "type": "module", 5 | "name": "CSV Host Importer", 6 | "version": "6.3.0", 7 | "namespace": "ICHI", 8 | "author": "IntelliTrend GmbH", 9 | "url": "https://www.intellitrend.de/", 10 | "description": "Import hosts via CSV files.", 11 | "actions": { 12 | "ichi.import": { 13 | "class": "CSVHostImport", 14 | "view": "module.ichi.import" 15 | }, 16 | "ichi.example": { 17 | "class": "CSVHostImportExample", 18 | "layout": "layout.ichi.example" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /modules/csv-host-importer/manifest.v1.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 1, 3 | "id": "intellitrend_csv_host_importer", 4 | "name": "CSV Host Importer", 5 | "version": "6.3.0", 6 | "namespace": "ICHI", 7 | "author": "IntelliTrend GmbH", 8 | "url": "https://www.intellitrend.de/", 9 | "description": "Import hosts via CSV files.", 10 | "actions": { 11 | "ichi.import": { 12 | "class": "CSVHostImport", 13 | "view": "module.ichi.import" 14 | }, 15 | "ichi.example": { 16 | "class": "CSVHostImportExample", 17 | "layout": "layout.ichi.example" 18 | } 19 | }, 20 | "config": { 21 | } 22 | } -------------------------------------------------------------------------------- /modules/csv-host-importer/views/layout.ichi.example.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright IntelliTrend GmbH, https://www.intellitrend.de 8 | * @license GNU Lesser General Public License v3.0 9 | * 10 | * You can redistribute this library and/or modify it under the terms of 11 | * the GNU LGPL as published by the Free Software Foundation, 12 | * either version 3 of the License, or any later version. 13 | * However you must not change author and copyright information. 14 | */ 15 | 16 | header('Content-Type: text/csv'); 17 | header('Content-Disposition: attachment; filename="hosts_example.csv"'); 18 | 19 | if (substr(ZABBIX_VERSION, 0, 3) == '6.0') { 20 | $template = 'Generic SNMP'; 21 | } else { 22 | $template = 'Generic by SNMP'; 23 | } 24 | 25 | $separator = $data['separator']; 26 | 27 | $out = fopen('php://output', 'w'); 28 | fputcsv($out, ['NAME', 'VISIBLE_NAME', 'HOST_GROUPS', 'HOST_TAGS', 'TEMPLATES', 'AGENT_IP', 'AGENT_DNS', 'SNMP_IP', 'SNMP_DNS', 'SNMP_VERSION', 'DESCRIPTION'], $separator); 29 | fputcsv($out, ['example1', 'Example Host Agent', 'First host group|Second host group', '', 'Linux by Zabbix agent', '127.0.0.1', 'localhost', '', '', '', 'Example Zabbix Agent host'], $separator); 30 | fputcsv($out, ['example2', 'Example Host SNMP', 'Third host group', 'First tag|Second tag=with value', $template, '', '', '127.0.0.1', 'localhost', '2', 'Example SNMPv2 host'], $separator); 31 | ?> 32 | -------------------------------------------------------------------------------- /modules/csv-host-importer/views/module.ichi.import.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright IntelliTrend GmbH, https://www.intellitrend.de 8 | * @license GNU Lesser General Public License v3.0 9 | * 10 | * You can redistribute this library and/or modify it under the terms of 11 | * the GNU LGPL as published by the Free Software Foundation, 12 | * either version 3 of the License, or any later version. 13 | * However you must not change author and copyright information. 14 | */ 15 | 16 | // alias for Zabbix 6.0 17 | if (!class_exists('CHtmlPage') && class_exists('CWidget')) { 18 | class_alias('CWidget', 'CHtmlPage'); 19 | } 20 | 21 | $widget = (new CHtmlPage())->setTitle(_('CSV Host Importer')); 22 | $form_list = (new CFormList('hostListFormList')); 23 | $form = (new CForm('post', (new CUrl('zabbix.php')) 24 | ->setArgument('action', 'ichi.import') 25 | ->getUrl(), 'multipart/form-data') 26 | ); 27 | 28 | $button_name = ''; // label for the button of the next step 29 | $other_buttons = []; // optional extra buttons 30 | $step = $data['step']; // current step 31 | $separator = $data['separator']; 32 | 33 | switch ($step) { 34 | case 0: 35 | // upload 36 | $form_list->addRow( 37 | (new CDiv(_('This form allows you to import hosts using CSV files. Click "Example" to download an example CSV file.'))) 38 | ->addClass('table-forms-separator') 39 | ); 40 | $form_list->addRow( 41 | (new CLabel(_('CSV File'), 'csv_file'))->setAsteriskMark(), 42 | (new CFile('csv_file')) 43 | ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH) 44 | ->setAriaRequired() 45 | ); 46 | $form_list->addRow( 47 | (new CLabel(_('CSV Separator'), 'separator')), 48 | (new CSelect('separator')) 49 | ->setFocusableElementId('separator') 50 | ->setValue($separator) 51 | ->addOptions(CSelect::createOptionsFromArray([0 => _('Semicolon'), 1 => _('Comma'), 2 => _('Tabulator')])) 52 | ); 53 | 54 | $button_name = 'Preview'; 55 | 56 | $example_url = (new CUrl('zabbix.php'))->setArgument('action', 'ichi.example'); 57 | $other_buttons[] = (new CSimpleButton(_('Example')))->onClick("document.location = " . json_encode($example_url->getUrl()) . " + '&separator=' + jQuery('input[name=separator]').val()"); 58 | 59 | $step++; 60 | break; 61 | case 1: 62 | // preview 63 | $form_list->addRow( 64 | (new CDiv(_('Please review your host import. If you\'re satisfied with the result, click "Import" to create to the hosts as listed here.'))) 65 | ->addClass('table-forms-separator') 66 | ); 67 | $hostlist = $data['hostlist']; 68 | $hostcols = $data['hostcols']; 69 | 70 | $table = (new CTable())->setId('hostlist-table'); 71 | if (defined('ZBX_STYLE_VALUEMAP_LIST_TABLE')) { 72 | $table->addClass(ZBX_STYLE_VALUEMAP_LIST_TABLE); 73 | } 74 | 75 | $cols = []; 76 | 77 | foreach ($hostcols as $raw => $friendly) { 78 | $cols[] = (new CTableColumn(_($friendly))) 79 | ->addStyle('min-width: 10em;') 80 | ->addClass('table-col-handle'); 81 | } 82 | 83 | $table->setColumns($cols); 84 | 85 | foreach ($hostlist as $row) { 86 | $cols = []; 87 | foreach ($hostcols as $raw => $friendly) { 88 | $cols[] = new CCol($row[$raw] ?? ''); 89 | } 90 | 91 | $table->addRow($cols, 'form_row'); 92 | } 93 | 94 | $form_list->addRow($table); 95 | $form_list->addRow( 96 | (new CInput('hidden')) 97 | ->setAttribute('value', $separator) 98 | ->setName('separator') 99 | ); 100 | 101 | $button_name = 'Import'; 102 | $other_buttons[] = new CSubmit("cancel", _("Cancel")); 103 | $step++; 104 | break; 105 | case 2: 106 | // import 107 | $hostlist = $data['hostlist']; 108 | 109 | $table = (new CTable())->setId('hostlist-table'); 110 | if (defined('ZBX_STYLE_VALUEMAP_LIST_TABLE')) { 111 | $table->addClass(ZBX_STYLE_VALUEMAP_LIST_TABLE); 112 | } 113 | 114 | $table->setColumns([ 115 | (new CTableColumn(_('Name'))) 116 | ->addStyle('min-width: 10em;') 117 | ->addClass('table-col-handle'), 118 | (new CTableColumn(_('Visible Name'))) 119 | ->addStyle('min-width: 10em;') 120 | ->addClass('table-col-handle'), 121 | (new CTableColumn(_('Status'))) 122 | ->addStyle('min-width: 10em;') 123 | ->addClass('table-col-handle'), 124 | ]); 125 | 126 | foreach ($hostlist as $row) { 127 | $hostid = $row['HOSTID']; 128 | 129 | $cols = []; 130 | $cols[] = new CCol($row['NAME']); 131 | $cols[] = new CCol($row['VISIBLE_NAME'] ?? ''); 132 | 133 | if ($hostid != -1) { 134 | $cols[] = new CCol( 135 | new CLink('Created', (new CUrl('zabbix.php')) 136 | ->setArgument('action', 'host.edit') 137 | ->setArgument('hostid', $hostid) 138 | ) 139 | ); 140 | } else { 141 | $cols[] = (new CCol('Error'))->addClass(ZBX_STYLE_RED); 142 | } 143 | 144 | $table->addRow($cols, 'form_row'); 145 | } 146 | 147 | $form_list->addRow($table); 148 | 149 | $button_name = 'Back'; 150 | $step = 0; 151 | break; 152 | } 153 | 154 | $tab_view = (new CTabView())->addTab('hostListTab', _('Background'), $form_list); 155 | 156 | if ($button_name !== '') { 157 | $tab_view->setFooter(makeFormFooter( 158 | new CSubmit(null, _($button_name)), 159 | $other_buttons 160 | )); 161 | } 162 | 163 | $form->addVar('step', $step); 164 | $form->addItem($tab_view); 165 | 166 | $widget->addItem($form); 167 | $widget->show(); 168 | ?> 169 | --------------------------------------------------------------------------------