1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22class CMapImporter extends CImporter { 23 24 /** 25 * Import maps. 26 * 27 * @param array $maps 28 */ 29 public function import(array $maps) { 30 $maps = zbx_toHash($maps, 'name'); 31 32 $maps = $this->resolveMapElementReferences($maps); 33 34 /* 35 * Get all importable maps with removed elements and links. First import maps and then update maps with 36 * elements and links from import file. This way we make sure we are able to resolve any references 37 * between maps and links that are imported. 38 */ 39 $mapsWithoutElements = $this->getMapsWithoutElements($maps); 40 41 $mapsToProcess = ['createMissing' => [], 'updateExisting' => []]; 42 43 foreach ($mapsWithoutElements as $mapName => $mapWithoutElements) { 44 $mapId = $this->referencer->resolveMap($mapWithoutElements['name']); 45 if ($mapId) { 46 // Update sysmapid in source map too. 47 $mapWithoutElements['sysmapid'] = $mapId; 48 $maps[$mapName]['sysmapid'] = $mapId; 49 50 $mapsToProcess['updateExisting'][] = $mapWithoutElements; 51 } 52 else { 53 $mapsToProcess['createMissing'][] = $mapWithoutElements; 54 } 55 } 56 57 if ($this->options['maps']['createMissing'] && $mapsToProcess['createMissing']) { 58 $newMapIds = API::Map()->create($mapsToProcess['createMissing']); 59 foreach ($mapsToProcess['createMissing'] as $num => $map) { 60 $mapId = $newMapIds['sysmapids'][$num]; 61 $this->referencer->addMapRef($map['name'], $mapId); 62 63 $maps[$map['name']]['sysmapid'] = $mapId; 64 } 65 } 66 67 if ($this->options['maps']['updateExisting'] && $mapsToProcess['updateExisting']) { 68 API::Map()->update($mapsToProcess['updateExisting']); 69 } 70 71 // Form an array of maps that need to be updated with elements and links, respecting the create/update options. 72 $mapsToUpdate = []; 73 foreach ($mapsToProcess as $mapActionKey => $mapArray) { 74 if ($this->options['maps'][$mapActionKey] && $mapsToProcess[$mapActionKey]) { 75 foreach ($mapArray as $mapItem) { 76 $map = [ 77 'sysmapid' => $maps[$mapItem['name']]['sysmapid'], 78 'name' => $mapItem['name'], 79 'shapes' => $maps[$mapItem['name']]['shapes'], 80 'lines' => $maps[$mapItem['name']]['lines'], 81 'selements' => $maps[$mapItem['name']]['selements'], 82 'links' => $maps[$mapItem['name']]['links'] 83 ]; 84 $map = $this->resolveMapReferences($map); 85 86 // Remove the map name so API does not make an update query to the database. 87 unset($map['name']); 88 $mapsToUpdate[] = $map; 89 } 90 } 91 } 92 93 if ($mapsToUpdate) { 94 API::Map()->update($mapsToUpdate); 95 } 96 } 97 98 /** 99 * Return maps without their elements. 100 * 101 * @param array $maps 102 * 103 * @return array 104 */ 105 protected function getMapsWithoutElements(array $maps) { 106 foreach ($maps as &$map) { 107 if (array_key_exists('selements', $map)) { 108 unset($map['selements']); 109 } 110 if (array_key_exists('links', $map)) { 111 unset($map['links']); 112 } 113 } 114 unset($map); 115 116 return $maps; 117 } 118 119 /** 120 * Change all references in map to database ids. 121 * 122 * @throws Exception 123 * 124 * @param array $map 125 * 126 * @return array 127 */ 128 protected function resolveMapReferences(array $map) { 129 if (isset($map['selements'])) { 130 foreach ($map['selements'] as &$selement) { 131 switch ($selement['elementtype']) { 132 case SYSMAP_ELEMENT_TYPE_MAP: 133 $selement['elements'][0]['sysmapid'] = $this->referencer->resolveMap($selement['elements'][0]['name']); 134 if (!$selement['elements'][0]['sysmapid']) { 135 throw new Exception(_s('Cannot find map "%1$s" used in map "%2$s".', 136 $selement['elements'][0]['name'], $map['name'])); 137 } 138 139 unset($selement['elements'][0]['name']); 140 break; 141 142 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 143 $selement['elements'][0]['groupid'] = $this->referencer->resolveGroup($selement['elements'][0]['name']); 144 if (!$selement['elements'][0]['groupid']) { 145 throw new Exception(_s('Cannot find group "%1$s" used in map "%2$s".', 146 $selement['elements'][0]['name'], $map['name'])); 147 } 148 149 unset($selement['elements'][0]['name']); 150 break; 151 152 case SYSMAP_ELEMENT_TYPE_HOST: 153 $selement['elements'][0]['hostid'] = $this->referencer->resolveHost($selement['elements'][0]['host']); 154 if (!$selement['elements'][0]['hostid']) { 155 throw new Exception(_s('Cannot find host "%1$s" used in map "%2$s".', 156 $selement['elements'][0]['host'], $map['name'])); 157 } 158 159 unset($selement['elements'][0]['host']); 160 break; 161 162 case SYSMAP_ELEMENT_TYPE_TRIGGER: 163 foreach ($selement['elements'] as &$element) { 164 $element['triggerid'] = $this->referencer->resolveTrigger($element['description'], 165 $element['expression'], $element['recovery_expression'] 166 ); 167 168 if (!$element['triggerid']) { 169 throw new Exception(_s( 170 'Cannot find trigger "%1$s" used in map "%2$s".', 171 $element['description'], 172 $map['name'] 173 )); 174 } 175 176 unset($element['description'], $element['expression'], $element['recovery_expression']); 177 } 178 unset($element); 179 break; 180 181 case SYSMAP_ELEMENT_TYPE_IMAGE: 182 unset($selement['elements']); 183 break; 184 } 185 186 $icons = [ 187 'icon_off' => 'iconid_off', 188 'icon_on' => 'iconid_on', 189 'icon_disabled' => 'iconid_disabled', 190 'icon_maintenance' => 'iconid_maintenance' 191 ]; 192 foreach ($icons as $element => $field) { 193 if (!empty($selement[$element])) { 194 $image = getImageByIdent($selement[$element]); 195 if (!$image) { 196 throw new Exception(_s('Cannot find icon "%1$s" used in map "%2$s".', 197 $selement[$element]['name'], $map['name'])); 198 } 199 $selement[$field] = $image['imageid']; 200 } 201 } 202 } 203 unset($selement); 204 } 205 206 if (isset($map['links'])) { 207 foreach ($map['links'] as &$link) { 208 if (!$link['linktriggers']) { 209 unset($link['linktriggers']); 210 continue; 211 } 212 213 foreach ($link['linktriggers'] as &$linkTrigger) { 214 $trigger = $linkTrigger['trigger']; 215 $triggerId = $this->referencer->resolveTrigger($trigger['description'], $trigger['expression'], 216 $trigger['recovery_expression'] 217 ); 218 219 if (!$triggerId) { 220 throw new Exception(_s( 221 'Cannot find trigger "%1$s" used in map "%2$s".', 222 $trigger['description'], 223 $map['name'] 224 )); 225 } 226 227 $linkTrigger['triggerid'] = $triggerId; 228 } 229 unset($linkTrigger); 230 } 231 unset($link); 232 } 233 234 return $map; 235 } 236 237 /** 238 * Resolves the iconmap and background images for the maps. 239 * 240 * @throws Exception if icon map or background image is not found. 241 * 242 * @param array $maps 243 * 244 * @return array 245 */ 246 protected function resolveMapElementReferences(array $maps) { 247 foreach ($maps as &$map) { 248 if (isset($map['iconmap']) && $map['iconmap']) { 249 $map['iconmapid'] = $this->referencer->resolveIconMap($map['iconmap']['name']); 250 251 if (!$map['iconmapid']) { 252 throw new Exception(_s('Cannot find icon map "%1$s" used in map "%2$s".', 253 $map['iconmap']['name'], $map['name'] 254 )); 255 } 256 } 257 258 if (isset($map['background']) && $map['background']) { 259 $image = getImageByIdent($map['background']); 260 261 if (!$image) { 262 throw new Exception(_s('Cannot find background image "%1$s" used in map "%2$s".', 263 $map['background']['name'], $map['name'] 264 )); 265 } 266 $map['backgroundid'] = $image['imageid']; 267 } 268 } 269 unset($map); 270 271 return $maps; 272 } 273} 274