You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
548 lines
18 KiB
548 lines
18 KiB
<?php declare(strict_types = 0);
|
|
/*
|
|
** Zabbix
|
|
** Copyright (C) 2001-2023 Zabbix SIA
|
|
**
|
|
** This program is free software; you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation; either version 2 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software
|
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
**/
|
|
|
|
|
|
/**
|
|
* Converter for converting import data from 4.4 to 5.0.
|
|
*/
|
|
class C44ImportConverter extends CConverter {
|
|
|
|
|
|
/**
|
|
* Update types.
|
|
*/
|
|
const TYPE_ITEM = 1;
|
|
const TYPE_DISCOVERY_RULE = 2;
|
|
const TYPE_ITEM_PROTOTYPE = 3;
|
|
|
|
/**
|
|
* Convert import data from 4.4 to 5.0 version.
|
|
*
|
|
* @param array $data
|
|
*
|
|
* @return array
|
|
*/
|
|
public function convert($data): array {
|
|
$data['zabbix_export']['version'] = '5.0';
|
|
|
|
if (array_key_exists('hosts', $data['zabbix_export'])) {
|
|
$data['zabbix_export']['hosts'] = $this->convertSnmpFieldsToInterfaces($data['zabbix_export']['hosts']);
|
|
}
|
|
|
|
$data['zabbix_export'] = $this->sanitizeSnmpFields($data['zabbix_export']);
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Set SNMP_AGENT type and unset all SNMP fields from items, discovery rules and item prototypes
|
|
* in hosts and templates.
|
|
*
|
|
* @param array $data
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function sanitizeSnmpFields(array $data): array {
|
|
$fields = ['snmp_community', 'snmpv3_contextname', 'snmpv3_securityname', 'snmpv3_securitylevel',
|
|
'snmpv3_authprotocol', 'snmpv3_authpassphrase', 'snmpv3_privprotocol', 'snmpv3_privpassphrase', 'port'
|
|
];
|
|
$types = [CXmlConstantName::SNMPV1, CXmlConstantName::SNMPV2, CXmlConstantName::SNMPV3];
|
|
|
|
foreach (['hosts', 'templates'] as $tag) {
|
|
if (array_key_exists($tag, $data)) {
|
|
foreach ($data[$tag] as &$value) {
|
|
if (array_key_exists('items', $value)) {
|
|
foreach ($value['items'] as &$item) {
|
|
foreach ($fields as $field) {
|
|
unset($item[$field]);
|
|
}
|
|
|
|
if (array_key_exists('type', $item) && in_array($item['type'], $types)) {
|
|
$item['type'] = CXmlConstantName::SNMP_AGENT;
|
|
}
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
if (array_key_exists('discovery_rules', $value)) {
|
|
foreach ($value['discovery_rules'] as &$drule) {
|
|
foreach ($fields as $field) {
|
|
unset($drule[$field]);
|
|
}
|
|
|
|
if (array_key_exists('type', $drule) && in_array($drule['type'], $types)) {
|
|
$drule['type'] = CXmlConstantName::SNMP_AGENT;
|
|
}
|
|
|
|
if (array_key_exists('item_prototypes', $drule)) {
|
|
foreach ($drule['item_prototypes'] as &$prototype) {
|
|
foreach ($fields as $field) {
|
|
unset($prototype[$field]);
|
|
}
|
|
|
|
if (array_key_exists('type', $prototype) && in_array($prototype['type'], $types)) {
|
|
$prototype['type'] = CXmlConstantName::SNMP_AGENT;
|
|
}
|
|
}
|
|
unset($prototype);
|
|
}
|
|
}
|
|
unset($drule);
|
|
}
|
|
}
|
|
unset($value);
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Get interface fields with default values.
|
|
* Because in XML can import interface that contain only interface_ref field.
|
|
*
|
|
* @param array $interface
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function getDefaultInterfaceArray(array $interface): array {
|
|
return $interface + [
|
|
'default' => CXmlConstantName::YES,
|
|
'type' => CXmlConstantName::SNMP,
|
|
'useip' => CXmlConstantName::YES,
|
|
'ip' => '127.0.0.1',
|
|
'dns' => '',
|
|
'port' => '10050'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Create helper array for interfaces.
|
|
*
|
|
* @param array $data
|
|
* @param int $type array type; 1 - item, 2 - discovery rule, 3 - item prototype
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function createHelperArray(array $data, int $type): array {
|
|
return [
|
|
'from' => $type,
|
|
'id' => $data['key'],
|
|
'ref' => $data['interface_ref'],
|
|
'port' => array_key_exists('port', $data) ? $data['port'] : '',
|
|
'type' => $data['type'],
|
|
'community' => ($data['type'] === CXmlConstantName::SNMPV1 || $data['type'] === CXmlConstantName::SNMPV2)
|
|
? (array_key_exists('snmp_community', $data) ? $data['snmp_community'] : '')
|
|
: '',
|
|
'contextname' => ($data['type'] === CXmlConstantName::SNMPV3)
|
|
? (array_key_exists('snmpv3_contextname', $data) ? $data['snmpv3_contextname'] : '')
|
|
: '',
|
|
'securityname' => ($data['type'] === CXmlConstantName::SNMPV3)
|
|
? (array_key_exists('snmpv3_securityname', $data) ? $data['snmpv3_securityname'] : '')
|
|
: '',
|
|
'securitylevel' => ($data['type'] === CXmlConstantName::SNMPV3)
|
|
? (array_key_exists('snmpv3_securitylevel', $data)
|
|
? $data['snmpv3_securitylevel']
|
|
: CXmlConstantName::NOAUTHNOPRIV)
|
|
: '',
|
|
'authprotocol' => ($data['type'] === CXmlConstantName::SNMPV3)
|
|
? (array_key_exists('snmpv3_authprotocol', $data)
|
|
? $data['snmpv3_authprotocol']
|
|
: CXmlConstantName::MD5)
|
|
: '',
|
|
'authpassphrase' => ($data['type'] === CXmlConstantName::SNMPV3)
|
|
? (array_key_exists('snmpv3_authpassphrase', $data) ? $data['snmpv3_authpassphrase'] : '')
|
|
: '',
|
|
'privprotocol' => ($data['type'] === CXmlConstantName::SNMPV3)
|
|
? (array_key_exists('snmpv3_privprotocol', $data)
|
|
? $data['snmpv3_privprotocol']
|
|
: CXmlConstantName::DES)
|
|
: '',
|
|
'privpassphrase' => ($data['type'] === CXmlConstantName::SNMPV3)
|
|
? (array_key_exists('snmpv3_privpassphrase', $data) ? $data['snmpv3_privpassphrase'] : '')
|
|
: ''
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Extract SNMP fields from items, discovery rules and item prototypes.
|
|
*
|
|
* @param array $host
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function extractSnmpFields(array $host): array {
|
|
// SNMP types.
|
|
$types = [CXmlConstantName::SNMPV1, CXmlConstantName::SNMPV2, CXmlConstantName::SNMPV3,
|
|
CXmlConstantName::SNMP_TRAP
|
|
];
|
|
$interfaces = [];
|
|
|
|
if (array_key_exists('items', $host)) {
|
|
// Getting all SNMP items and their interfaces.
|
|
foreach ($host['items'] as $item) {
|
|
if (array_key_exists('type', $item) && in_array($item['type'], $types)) {
|
|
$interfaceid = str_replace('if', '', $item['interface_ref']);
|
|
|
|
$interfaces[$interfaceid][] = $this->createHelperArray($item, self::TYPE_ITEM);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (array_key_exists('discovery_rules', $host)) {
|
|
// Getting all SNMP discovery rules and their interfaces.
|
|
foreach ($host['discovery_rules'] as $drule) {
|
|
if (array_key_exists('type', $drule) && in_array($drule['type'], $types)) {
|
|
$interfaceid = str_replace('if', '', $drule['interface_ref']);
|
|
|
|
$interfaces[$interfaceid][] = $this->createHelperArray($drule, self::TYPE_DISCOVERY_RULE);
|
|
}
|
|
|
|
if (array_key_exists('item_prototypes', $drule)) {
|
|
// Getting all SNMP item prototypes and their interfaces.
|
|
foreach ($drule['item_prototypes'] as $prototype) {
|
|
if (array_key_exists('type', $prototype) && in_array($prototype['type'], $types)) {
|
|
$interfaceid = str_replace('if', '', $prototype['interface_ref']);
|
|
|
|
$interfaces[$interfaceid][] = $this->createHelperArray($prototype,
|
|
self::TYPE_ITEM_PROTOTYPE
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $interfaces;
|
|
}
|
|
|
|
/**
|
|
* Get max interfaceid from host interfaces.
|
|
*
|
|
* @param array $interfaces
|
|
*
|
|
* @return int
|
|
*/
|
|
protected function getHostMaxInterfaceId(array $interfaces): int {
|
|
$max_id = 1;
|
|
|
|
foreach ($interfaces as $interface) {
|
|
$interfaceid = (int) str_replace('if', '', $interface['interface_ref']);
|
|
|
|
if ($interfaceid > $max_id) {
|
|
$max_id = $interfaceid;
|
|
}
|
|
}
|
|
|
|
return $max_id;
|
|
}
|
|
|
|
/**
|
|
* Create new interface array.
|
|
*
|
|
* @param array $item
|
|
* @param int $maxid
|
|
* @param array $parent_interface
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function createNewInterface(array $item, int $maxid, array $parent_interface): array {
|
|
$interface = ['interface_ref' => 'if'.$maxid] + $parent_interface;
|
|
|
|
if ($item['type'] == CXmlConstantName::SNMP_TRAP) {
|
|
$interface['details']['version'] = CXmlConstantName::SNMPV1;
|
|
$interface['details']['community'] = 'public';
|
|
}
|
|
else {
|
|
$interface['details']['version'] = $item['type'];
|
|
}
|
|
|
|
// Set item port if have.
|
|
if ($item['port'] !== '') {
|
|
$interface['port'] = $item['port'];
|
|
}
|
|
|
|
if ($item['type'] === CXmlConstantName::SNMPV1 || $item['type'] === CXmlConstantName::SNMPV2) {
|
|
$interface['details']['community'] = $item['community'];
|
|
}
|
|
|
|
if ($item['type'] === CXmlConstantName::SNMPV3) {
|
|
$interface['details']['contextname'] = $item['contextname'];
|
|
$interface['details']['securityname'] = $item['securityname'];
|
|
$interface['details']['securitylevel'] = $item['securitylevel'];
|
|
$interface['details']['authprotocol'] = $item['authprotocol'];
|
|
$interface['details']['authpassphrase'] = $item['authpassphrase'];
|
|
$interface['details']['privprotocol'] = $item['privprotocol'];
|
|
$interface['details']['privpassphrase'] = $item['privpassphrase'];
|
|
}
|
|
|
|
return $interface;
|
|
}
|
|
|
|
/**
|
|
* Update interface_ref in items, discovery rules and item prototypes.
|
|
*
|
|
* @param array $host
|
|
* @param array $updates itemid => new interface_ref
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function updateInterfaceRef(array $host, array $updates): array {
|
|
if (array_key_exists('items', $host) && array_key_exists(self::TYPE_ITEM, $updates)) {
|
|
foreach ($host['items'] as &$item) {
|
|
if (array_key_exists($item['key'], $updates[self::TYPE_ITEM])) {
|
|
$item['interface_ref'] = $updates[self::TYPE_ITEM][$item['key']];
|
|
}
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
if (array_key_exists('discovery_rules', $host)) {
|
|
foreach ($host['discovery_rules'] as &$drule) {
|
|
if (array_key_exists(self::TYPE_DISCOVERY_RULE, $updates)) {
|
|
if (array_key_exists($drule['key'], $updates[self::TYPE_DISCOVERY_RULE])) {
|
|
$drule['interface_ref'] = $updates[self::TYPE_DISCOVERY_RULE][$drule['key']];
|
|
}
|
|
}
|
|
|
|
if (array_key_exists('item_prototypes', $drule)
|
|
&& array_key_exists(self::TYPE_ITEM_PROTOTYPE, $updates)) {
|
|
foreach ($drule['item_prototypes'] as &$prototype) {
|
|
if (array_key_exists($prototype['key'], $updates[self::TYPE_ITEM_PROTOTYPE])) {
|
|
$prototype['interface_ref'] = $updates[self::TYPE_ITEM_PROTOTYPE][$prototype['key']];
|
|
}
|
|
}
|
|
unset($prototype);
|
|
}
|
|
}
|
|
unset($drule);
|
|
}
|
|
|
|
return $host;
|
|
}
|
|
|
|
/**
|
|
* Convert SNMP fields from host items, discovery rules and item prototypes to interfaces.
|
|
*
|
|
* @param array $hosts
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function convertSnmpFieldsToInterfaces(array $hosts): array {
|
|
foreach ($hosts as &$host) {
|
|
// Store new interfaces.
|
|
$new_interfaces = [];
|
|
|
|
// Store id where we need update interface_ref.
|
|
$updates = [];
|
|
|
|
// Store values related to interface.
|
|
$interfaces = $this->extractSnmpFields($host);
|
|
|
|
// Getting all interfaces.
|
|
if (array_key_exists('interfaces', $host)) {
|
|
$max_interfaceid = $this->getHostMaxInterfaceId($host['interfaces']);
|
|
|
|
foreach ($host['interfaces'] as $key => &$interface) {
|
|
$interfaceid = str_replace('if', '', $interface['interface_ref']);
|
|
|
|
// Working only with SNMP interfaces.
|
|
if (array_key_exists('type', $interface) && $interface['type'] === CXmlConstantName::SNMP) {
|
|
// Save bulk value.
|
|
$interface['details'] = [
|
|
'bulk' => array_key_exists('bulk', $interface) ? $interface['bulk'] : CXmlConstantName::YES
|
|
];
|
|
|
|
unset($interface['bulk']);
|
|
|
|
$parent_interface = $this->getDefaultInterfaceArray($interface);
|
|
|
|
// Check if interface used in items.
|
|
if (array_key_exists($interfaceid, $interfaces)) {
|
|
// Clone interface and map it with parent interface.
|
|
$new_interfaces[$interfaceid][] = [
|
|
'interface_ref' => 'if'.(++$max_interfaceid),
|
|
'new' => true
|
|
] + $parent_interface;
|
|
|
|
// Walk through all items for this interface.
|
|
foreach ($interfaces[$interfaceid] as $item) {
|
|
// Set SNMP version from first item.
|
|
foreach ($new_interfaces[$interfaceid] as &$iface) {
|
|
if (array_key_exists('new', $iface)) {
|
|
if ($item['type'] === CXmlConstantName::SNMP_TRAP) {
|
|
// Use default SNMP V1 interface for SNMP traps.
|
|
$iface['details']['version'] = CXmlConstantName::SNMPV1;
|
|
$iface['details']['community'] = 'public';
|
|
}
|
|
elseif ($item['type'] === CXmlConstantName::SNMPV1
|
|
|| $item['type'] === CXmlConstantName::SNMPV2) {
|
|
$iface['details']['version'] = $item['type'];
|
|
$iface['details']['community'] = $item['community'];
|
|
}
|
|
elseif ($item['type'] === CXmlConstantName::SNMPV3) {
|
|
$iface['details']['version'] = CXmlConstantName::SNMPV3;
|
|
$iface['details']['contextname'] = $item['contextname'];
|
|
$iface['details']['securityname'] = $item['securityname'];
|
|
$iface['details']['securitylevel'] = $item['securitylevel'];
|
|
$iface['details']['authprotocol'] = $item['authprotocol'];
|
|
$iface['details']['authpassphrase'] = $item['authpassphrase'];
|
|
$iface['details']['privprotocol'] = $item['privprotocol'];
|
|
$iface['details']['privpassphrase'] = $item['privpassphrase'];
|
|
}
|
|
|
|
// Item port not set here because we will find it in next steps.
|
|
|
|
unset($iface['new']);
|
|
break;
|
|
}
|
|
}
|
|
unset($iface);
|
|
|
|
// Find interfaces having same SNMP version.
|
|
$same_ver_interfaces = array_filter($new_interfaces[$interfaceid],
|
|
function (array $iface) use (&$item): bool {
|
|
// Use default SNMP V1 interface for SNMP traps.
|
|
if ($item['type'] === CXmlConstantName::SNMP_TRAP) {
|
|
$item['type'] = CXmlConstantName::SNMPV1;
|
|
$item['community'] = 'public';
|
|
}
|
|
|
|
return ($iface['details']['version'] === $item['type']);
|
|
}
|
|
);
|
|
|
|
if ($same_ver_interfaces) {
|
|
$same_interfaces = array_filter($same_ver_interfaces,
|
|
function (array $iface) use ($item, $parent_interface): bool {
|
|
// If item port differs from interface ports it is 100% new interface.
|
|
if ($item['port'] === '') {
|
|
// Item port not set and interface port not equal parent port.
|
|
if ($iface['port'] !== $parent_interface['port']) {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
// If item port not equal interface ports it is 100% new interface.
|
|
if ($iface['port'] !== $item['port']) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// If interface community string is equal with item it is our interface.
|
|
if ($item['type'] === CXmlConstantName::SNMPV1
|
|
|| $item['type'] === CXmlConstantName::SNMPV2) {
|
|
return ($iface['details']['community'] === $item['community']);
|
|
}
|
|
|
|
// Compare all item specific SNMPV3 fields with interface properties.
|
|
if ($item['type'] === CXmlConstantName::SNMPV3) {
|
|
return ($iface['details']['contextname'] === $item['contextname']
|
|
&& $iface['details']['securityname'] === $item['securityname']
|
|
&& $iface['details']['securitylevel'] === $item['securitylevel']
|
|
&& $iface['details']['authprotocol'] === $item['authprotocol']
|
|
&& $iface['details']['authpassphrase'] === $item['authpassphrase']
|
|
&& $iface['details']['privprotocol'] === $item['privprotocol']
|
|
&& $iface['details']['privpassphrase'] === $item['privpassphrase']);
|
|
}
|
|
}
|
|
);
|
|
|
|
if ($same_interfaces) {
|
|
$iface = current($same_interfaces);
|
|
$updates[$item['from']][$item['id']] = $iface['interface_ref'];
|
|
}
|
|
else {
|
|
// Create new interface if not found.
|
|
$iface = $this->createNewInterface($item, ++$max_interfaceid,
|
|
$parent_interface
|
|
);
|
|
|
|
$new_interfaces[$interfaceid][] = $iface;
|
|
|
|
$updates[$item['from']][$item['id']] = $iface['interface_ref'];
|
|
}
|
|
}
|
|
else {
|
|
// Create new interface if not found with same version.
|
|
$iface = $this->createNewInterface($item, ++$max_interfaceid, $parent_interface);
|
|
|
|
$new_interfaces[$interfaceid][] = $iface;
|
|
|
|
$updates[$item['from']][$item['id']] = $iface['interface_ref'];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Interface not used in items. Create with default values.
|
|
$new_interfaces[$interfaceid][] = ['details' => $parent_interface['details']
|
|
+ ['version' => CXmlConstantName::SNMPV2, 'community' => '{$SNMP_COMMUNITY}']
|
|
] + $parent_interface;
|
|
}
|
|
|
|
// Delete original interface because we created new.
|
|
unset($host['interfaces'][$key]);
|
|
continue;
|
|
}
|
|
|
|
// Unset bulk field from interfaces.
|
|
unset($interface['bulk']);
|
|
}
|
|
unset($interface);
|
|
}
|
|
|
|
$host = $this->updateInterfaceRef($host, $updates);
|
|
|
|
// Add all new interfaces to host interfaces.
|
|
foreach ($new_interfaces as $values) {
|
|
foreach ($values as $value) {
|
|
$host['interfaces'][] = $value;
|
|
}
|
|
}
|
|
|
|
// Set proper default field for interfaces.
|
|
if (array_key_exists('interfaces', $host)) {
|
|
$main = false;
|
|
foreach ($host['interfaces'] as &$interface) {
|
|
if (array_key_exists('type', $interface) && $interface['type'] === CXmlConstantName::SNMP) {
|
|
if ($main) {
|
|
$interface['default'] = CXmlConstantName::NO;
|
|
continue;
|
|
}
|
|
|
|
if (!array_key_exists('default', $interface)) {
|
|
$main = true;
|
|
}
|
|
else {
|
|
if ($interface['default'] === CXmlConstantName::YES) {
|
|
$main = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
unset($interface);
|
|
}
|
|
}
|
|
unset($host);
|
|
|
|
return $hosts;
|
|
}
|
|
}
|