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
22/**
23 * Import formatter
24 */
25class CImportDataAdapter {
26
27	/**
28	 * @var array configuration import data
29	 */
30	protected $data;
31
32	/**
33	 * Current import version.
34	 *
35	 * @var string
36	 */
37	protected $currentVersion;
38
39	/**
40	 * Set the data and initialize the adapter.
41	 *
42	 * @param array $data   import data
43	 */
44	public function load(array $data) {
45		$this->data = $data['zabbix_export'];
46	}
47
48	/**
49	 * Get groups from the imported data.
50	 *
51	 * @return array
52	 */
53	public function getGroups() {
54		return array_key_exists('groups', $this->data) ? $this->data['groups'] : [];
55	}
56
57	/**
58	 * Get templates from the imported data.
59	 *
60	 * @return array
61	 */
62	public function getTemplates() {
63		$templates = [];
64
65		if (array_key_exists('templates', $this->data)) {
66			foreach ($this->data['templates'] as $template) {
67				$template = CArrayHelper::renameKeys($template, ['template' => 'host']);
68
69				$templates[] = CArrayHelper::getByKeys($template, [
70					'groups', 'macros', 'templates', 'host', 'status', 'name', 'description', 'tags'
71				]);
72			}
73		}
74
75		return $templates;
76	}
77
78	/**
79	 * Get hosts from the imported data.
80	 *
81	 * @return array
82	 */
83	public function getHosts() {
84		$hosts = [];
85
86		if (array_key_exists('hosts', $this->data)) {
87			foreach ($this->data['hosts'] as $host) {
88				$host = CArrayHelper::renameKeys($host, ['proxyid' => 'proxy_hostid']);
89
90				if (array_key_exists('interfaces', $host)) {
91					foreach ($host['interfaces'] as $inum => $interface) {
92						$host['interfaces'][$inum] = CArrayHelper::renameKeys($interface, ['default' => 'main']);
93					}
94				}
95
96				$hosts[] = CArrayHelper::getByKeys($host, [
97					'inventory', 'proxy', 'groups', 'templates', 'macros', 'interfaces', 'host', 'status',
98					'description', 'ipmi_authtype', 'ipmi_privilege', 'ipmi_username', 'ipmi_password', 'name',
99					'inventory_mode', 'tls_connect', 'tls_accept', 'tls_issuer', 'tls_subject', 'tls_psk_identity',
100					'tls_psk', 'tags'
101				]);
102			}
103		}
104
105		return $hosts;
106	}
107
108	/**
109	 * Get applications from the imported data.
110	 *
111	 * @return array
112	 */
113	public function getApplications() {
114		$applications = [];
115
116		if (array_key_exists('hosts', $this->data)) {
117			foreach ($this->data['hosts'] as $host) {
118				if (array_key_exists('applications', $host)) {
119					foreach ($host['applications'] as $application) {
120						$applications[$host['host']][$application['name']] = $application;
121					}
122				}
123			}
124		}
125
126		if (array_key_exists('templates', $this->data)) {
127			foreach ($this->data['templates'] as $template) {
128				if (array_key_exists('applications', $template)) {
129					foreach ($template['applications'] as $application) {
130						$applications[$template['template']][$application['name']] = $application;
131					}
132				}
133			}
134		}
135
136		return $applications;
137	}
138
139	/**
140	 * Get value maps from the imported data.
141	 *
142	 * @return array
143	 */
144	public function getValueMaps() {
145		return array_key_exists('value_maps', $this->data) ? $this->data['value_maps'] : [];
146	}
147
148	/**
149	 * Get items from the imported data.
150	 *
151	 * @return array
152	 */
153	public function getItems() {
154		$items = [];
155
156		if (array_key_exists('hosts', $this->data)) {
157			foreach ($this->data['hosts'] as $host) {
158				if (array_key_exists('items', $host)) {
159					foreach ($host['items'] as $item) {
160						$items[$host['host']][$item['key']] = $this->renameItemFields($item);
161					}
162				}
163			}
164		}
165
166		if (array_key_exists('templates', $this->data)) {
167			foreach ($this->data['templates'] as $template) {
168				if (array_key_exists('items', $template)) {
169					foreach ($template['items'] as $item) {
170						$items[$template['template']][$item['key']] = $this->renameItemFields($item);
171					}
172				}
173			}
174		}
175
176		return $items;
177	}
178
179	/**
180	 * Get discovery rules from the imported data.
181	 *
182	 * @return array
183	 */
184	public function getDiscoveryRules() {
185		$discovery_rules = [];
186
187		if (array_key_exists('hosts', $this->data)) {
188			foreach ($this->data['hosts'] as $host) {
189				if (array_key_exists('discovery_rules', $host)) {
190					foreach ($host['discovery_rules'] as $discovery_rule) {
191						$discovery_rules[$host['host']][$discovery_rule['key']] =
192							$this->formatDiscoveryRule($discovery_rule, $host['host']);
193					}
194				}
195			}
196		}
197
198		if (array_key_exists('templates', $this->data)) {
199			foreach ($this->data['templates'] as $template) {
200				if (array_key_exists('discovery_rules', $template)) {
201					foreach ($template['discovery_rules'] as $discovery_rule) {
202						$discovery_rules[$template['template']][$discovery_rule['key']] =
203							$this->formatDiscoveryRule($discovery_rule, $template['template']);
204					}
205				}
206			}
207		}
208
209		return $discovery_rules;
210	}
211
212	/**
213	 * Get web scenarios from the imported data.
214	 *
215	 * @return array
216	 */
217	public function getHttpTests() {
218		$httptests = [];
219
220		if (array_key_exists('hosts', $this->data)) {
221			foreach ($this->data['hosts'] as $host) {
222				if (array_key_exists('httptests', $host)) {
223					foreach ($host['httptests'] as $httptest) {
224						$httptests[$host['host']][$httptest['name']] = $this->formatHttpTest($httptest);
225					}
226				}
227			}
228		}
229
230		if (array_key_exists('templates', $this->data)) {
231			foreach ($this->data['templates'] as $template) {
232				if (array_key_exists('httptests', $template)) {
233					foreach ($template['httptests'] as $httptest) {
234						$httptests[$template['template']][$httptest['name']] = $this->formatHttpTest($httptest);
235					}
236				}
237			}
238		}
239
240		return $httptests;
241	}
242
243	/**
244	 * Get web scenario steps from the imported data.
245	 *
246	 * @return array
247	 */
248	public function getHttpSteps() {
249		$httpsteps = [];
250
251		if (array_key_exists('hosts', $this->data)) {
252			foreach ($this->data['hosts'] as $host) {
253				if (array_key_exists('httptests', $host)) {
254					foreach ($host['httptests'] as $httptest) {
255						foreach ($httptest['steps'] as $httpstep) {
256							$httpsteps[$host['host']][$httptest['name']][$httpstep['name']] = $httpstep;
257						}
258					}
259				}
260			}
261		}
262
263		if (array_key_exists('templates', $this->data)) {
264			foreach ($this->data['templates'] as $template) {
265				if (array_key_exists('httptests', $template)) {
266					foreach ($template['httptests'] as $httptest) {
267						foreach ($httptest['steps'] as $httpstep) {
268							$httpsteps[$template['template']][$httptest['name']][$httpstep['name']] = $httpstep;
269						}
270					}
271				}
272			}
273		}
274
275		return $httpsteps;
276	}
277
278	/**
279	 * Get graphs from the imported data.
280	 *
281	 * @return array
282	 */
283	public function getGraphs() {
284		$graphs = [];
285
286		if (array_key_exists('graphs', $this->data)) {
287			foreach ($this->data['graphs'] as $graph) {
288				$graphs[] = $this->renameGraphFields($graph);
289			}
290		}
291
292		return $graphs;
293	}
294
295	/**
296	 * Get simple triggers from the imported data.
297	 *
298	 * @return array
299	 */
300	protected function getSimpleTriggers() {
301		$simple_triggers = [];
302		$expression_options = ['lldmacros' => false, 'allow_func_only' => true];
303
304		if (array_key_exists('hosts', $this->data)) {
305			foreach ($this->data['hosts'] as $host) {
306				if (array_key_exists('items', $host)) {
307					foreach ($host['items'] as $item) {
308						if (array_key_exists('triggers', $item)) {
309							foreach ($item['triggers'] as $simple_trigger) {
310								$simple_trigger = $this->enrichSimpleTriggerExpression($host['host'], $item['key'],
311									$simple_trigger, $expression_options
312								);
313								$simple_triggers[] = $this->renameTriggerFields($simple_trigger);
314							}
315							unset($item['triggers']);
316						}
317					}
318				}
319			}
320		}
321
322		if (array_key_exists('templates', $this->data)) {
323			foreach ($this->data['templates'] as $template) {
324				if (array_key_exists('items', $template)) {
325					foreach ($template['items'] as $item) {
326						if (array_key_exists('triggers', $item)) {
327							foreach ($item['triggers'] as $simple_trigger) {
328								$simple_trigger = $this->enrichSimpleTriggerExpression($template['template'],
329									$item['key'], $simple_trigger, $expression_options
330								);
331								$simple_triggers[] = $this->renameTriggerFields($simple_trigger);
332							}
333							unset($item['triggers']);
334						}
335					}
336				}
337			}
338		}
339
340		return $simple_triggers;
341	}
342
343	/**
344	 * Get triggers from the imported data.
345	 *
346	 * @return array
347	 */
348	public function getTriggers() {
349		$triggers = [];
350
351		if (array_key_exists('triggers', $this->data)) {
352			foreach ($this->data['triggers'] as $trigger) {
353				$triggers[] = $this->renameTriggerFields($trigger);
354			}
355		}
356
357		return array_merge($triggers, $this->getSimpleTriggers());
358	}
359
360	/**
361	 * Get images from the imported data.
362	 *
363	 * @return array
364	 */
365	public function getImages() {
366		$images = [];
367
368		if (array_key_exists('images', $this->data)) {
369			foreach ($this->data['images'] as $image) {
370				$images[] = CArrayHelper::renameKeys($image, ['encodedImage' => 'image']);
371			}
372		}
373
374		return $images;
375	}
376
377	/**
378	 * Get maps from the imported data.
379	 *
380	 * @return array
381	 */
382	public function getMaps() {
383		return array_key_exists('maps', $this->data) ? $this->data['maps'] : [];
384	}
385
386	/**
387	 * Get screens from the imported data.
388	 *
389	 * @return array
390	 */
391	public function getScreens() {
392		$screens = [];
393
394		if (array_key_exists('screens', $this->data)) {
395			foreach ($this->data['screens'] as $screen) {
396				$screens[] = CArrayHelper::renameKeys($screen, ['screen_items' => 'screenitems']);
397			}
398		}
399
400		return $screens;
401	}
402
403	/**
404	 * Get template screens from the imported data.
405	 *
406	 * @return array
407	 */
408	public function getTemplateScreens() {
409		$screens = [];
410
411		if (array_key_exists('templates', $this->data)) {
412			foreach ($this->data['templates'] as $template) {
413				if (array_key_exists('screens', $template)) {
414					foreach ($template['screens'] as $screen) {
415						$screens[$template['template']][$screen['name']] =
416							CArrayHelper::renameKeys($screen, ['screen_items' => 'screenitems']);
417					}
418				}
419			}
420		}
421
422		return $screens;
423	}
424
425	/**
426	 * Get media types from the imported data.
427	 *
428	 * @return array
429	 */
430	public function getMediaTypes() {
431		$media_types = [];
432
433		if (array_key_exists('media_types', $this->data)) {
434			$keys = [
435				'password' => 'passwd',
436				'script_name' => 'exec_path',
437				'max_sessions' => 'maxsessions',
438				'attempts' => 'maxattempts'
439			];
440
441			$message_template_keys = [
442				'event_source' => 'eventsource',
443				'operation_mode' => 'recovery'
444			];
445
446			foreach ($this->data['media_types'] as $media_type) {
447				if (array_key_exists('message_templates', $media_type)) {
448					foreach ($media_type['message_templates'] as &$message_template) {
449						$message_template = CArrayHelper::renameKeys($message_template, $message_template_keys);
450					}
451					unset($message_template);
452				}
453
454				if ($media_type['type'] == MEDIA_TYPE_EXEC && array_key_exists('parameters', $media_type)) {
455					$media_type['exec_params'] = $media_type['parameters']
456						? implode("\n", $media_type['parameters'])."\n"
457						: '';
458					unset($media_type['parameters']);
459				}
460
461				$media_types[] = CArrayHelper::renameKeys($media_type, $keys);
462			}
463		}
464
465		return $media_types;
466	}
467
468	/**
469	 * Enriches trigger expression and trigger recovery expression with host:item pair.
470	 *
471	 * @param string $host
472	 * @param string $item_key
473	 * @param array  $simple_trigger
474	 * @param string $simple_trigger['expression]
475	 * @param int    $simple_trigger['recovery_mode]
476	 * @param string $simple_trigger['recovery_expression]
477	 * @param array  $options
478	 * @param bool   $options['lldmacros']                  (optional)
479	 * @param bool   $options['allow_func_only']            (optional)
480	 *
481	 * @return array
482	 */
483	protected function enrichSimpleTriggerExpression($host, $item_key, array $simple_trigger, array $options) {
484		$expression_data = new CTriggerExpression($options);
485		$prefix = $host.':'.$item_key.'.';
486
487		if ($expression_data->parse($simple_trigger['expression'])) {
488			foreach (array_reverse($expression_data->expressions) as $expression) {
489				if ($expression['host'] === '' && $expression['item'] === '') {
490					$simple_trigger['expression'] = substr_replace($simple_trigger['expression'], $prefix,
491						$expression['pos'] + 1, 0
492					);
493				}
494			}
495		}
496
497		if ($simple_trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION
498				&& $expression_data->parse($simple_trigger['recovery_expression'])) {
499			foreach (array_reverse($expression_data->expressions) as $expression) {
500				if ($expression['host'] === '' && $expression['item'] === '') {
501					$simple_trigger['recovery_expression'] = substr_replace($simple_trigger['recovery_expression'],
502						$prefix, $expression['pos'] + 1, 0
503					);
504				}
505			}
506		}
507
508		return $simple_trigger;
509	}
510
511	/**
512	 * Format discovery rule.
513	 *
514	 * @param array  $discovery_rule
515	 * @param string $host
516	 *
517	 * @return array
518	 */
519	protected function formatDiscoveryRule(array $discovery_rule, $host) {
520		$discovery_rule = $this->renameItemFields($discovery_rule);
521		$discovery_rule = $this->formatDiscoveryRuleOverrideFields($discovery_rule);
522
523		foreach ($discovery_rule['item_prototypes'] as &$item_prototype) {
524			if (array_key_exists('trigger_prototypes', $item_prototype)) {
525				foreach ($item_prototype['trigger_prototypes'] as $trigger_prototype) {
526					$discovery_rule['trigger_prototypes'][] =  $this->enrichSimpleTriggerExpression($host,
527						$item_prototype['key'], $trigger_prototype, ['allow_func_only' => true]
528					);
529				}
530				unset($item_prototype['trigger_prototypes']);
531			}
532
533			$item_prototype = $this->renameItemFields($item_prototype);
534		}
535		unset($item_prototype);
536
537		foreach ($discovery_rule['trigger_prototypes'] as &$trigger_prototype) {
538			$trigger_prototype = $this->renameTriggerFields($trigger_prototype);
539		}
540		unset($trigger_prototype);
541
542		foreach ($discovery_rule['graph_prototypes'] as &$graph_prototype) {
543			$graph_prototype = $this->renameGraphFields($graph_prototype);
544		}
545		unset($graph_prototype);
546
547		return $discovery_rule;
548	}
549
550	/**
551	 * Format low-level disovery rule overrides.
552	 *
553	 * @param array $discovery_rule  Data of single low-level discovery rule.
554	 *
555	 * @return array
556	 */
557	protected function formatDiscoveryRuleOverrideFields(array $discovery_rule) {
558		if ($discovery_rule['overrides']) {
559			foreach ($discovery_rule['overrides'] as &$override) {
560				if (!$override['filter']) {
561					unset($override['filter']);
562				}
563
564				foreach ($override['operations'] as &$operation) {
565					if (array_key_exists('discover', $operation) && $operation['discover'] !== '') {
566						$operation['opdiscover']['discover'] = $operation['discover'];
567					}
568
569					switch ($operation['operationobject']) {
570						case OPERATION_OBJECT_ITEM_PROTOTYPE:
571							if (array_key_exists('status', $operation) && $operation['status'] !== '') {
572								$operation['opstatus']['status'] = $operation['status'];
573							}
574							if (array_key_exists('delay', $operation) && $operation['delay'] !== '') {
575								$operation['opperiod']['delay'] = $operation['delay'];
576							}
577							if (array_key_exists('history', $operation) && $operation['history'] !== '') {
578								$operation['ophistory']['history'] = $operation['history'];
579							}
580							if (array_key_exists('trends', $operation) && $operation['trends'] !== '') {
581								$operation['optrends']['trends'] = $operation['trends'];
582							}
583							break;
584
585						case OPERATION_OBJECT_TRIGGER_PROTOTYPE:
586							if (array_key_exists('status', $operation) && $operation['status'] !== '') {
587								$operation['opstatus']['status'] = $operation['status'];
588							}
589							if (array_key_exists('severity', $operation) && $operation['severity'] !== '') {
590								$operation['opseverity']['severity'] = $operation['severity'];
591							}
592							if (array_key_exists('tags', $operation) && $operation['tags']) {
593								$operation['optag'] = [];
594								foreach ($operation['tags'] as $tag) {
595									$operation['optag'][] = $tag;
596								}
597							}
598							break;
599
600						case OPERATION_OBJECT_HOST_PROTOTYPE:
601							if (array_key_exists('status', $operation) && $operation['status'] !== '') {
602								$operation['opstatus']['status'] = $operation['status'];
603							}
604							if (array_key_exists('templates', $operation) && $operation['templates']) {
605								$operation['optemplate'] = [];
606								foreach ($operation['templates'] as $template) {
607									$operation['optemplate'][] = $template;
608								}
609							}
610							if (array_key_exists('inventory_mode', $operation) && $operation['inventory_mode'] !== '') {
611								$operation['opinventory']['inventory_mode'] = $operation['inventory_mode'];
612							}
613							break;
614					}
615
616					unset($operation['status'], $operation['discover'], $operation['delay'], $operation['history'],
617						$operation['trends'], $operation['severity'], $operation['tags'], $operation['templates'],
618						$operation['inventory_mode']
619					);
620				}
621				unset($operation);
622			}
623			unset($override);
624		}
625
626		return $discovery_rule;
627	}
628
629	/**
630	 * Rename items, discovery rules, item prototypes fields.
631	 *
632	 * @param array $item
633	 *
634	 * @return array
635	 */
636	protected function renameItemFields(array $item) {
637		return CArrayHelper::renameKeys($item, ['key' => 'key_', 'allowed_hosts' => 'trapper_hosts']);
638	}
639
640	/**
641	 * Format web scenario.
642	 *
643	 * @param array $httptest
644	 *
645	 * @return array
646	 */
647	protected function formatHttpTest(array $httptest) {
648		$httptest = $this->renameHttpTestFields($httptest);
649
650		$no = 0;
651		foreach ($httptest['steps'] as &$step) {
652			$step['no'] = ++$no;
653		}
654		unset($step);
655
656		return $httptest;
657	}
658
659	/**
660	 * Rename web scenarios fields.
661	 *
662	 * @param array $httptest
663	 *
664	 * @return array
665	 */
666	protected function renameHttpTestFields(array $httptest) {
667		return CArrayHelper::renameKeys($httptest, ['attempts' => 'retries']);
668	}
669
670	/**
671	 * Rename triggers, trigger prototypes fields.
672	 *
673	 * @param array $trigger
674	 *
675	 * @return array
676	 */
677	protected function renameTriggerFields(array $trigger) {
678		$trigger = CArrayHelper::renameKeys($trigger, ['description' => 'comments']);
679
680		return CArrayHelper::renameKeys($trigger, ['name' => 'description', 'severity' => 'priority']);
681	}
682
683	/**
684	 * Rename graphs, graph prototypes fields.
685	 *
686	 * @param array $graph
687	 *
688	 * @return array
689	 */
690	protected function renameGraphFields(array $graph) {
691		return CArrayHelper::renameKeys($graph, [
692			'type' => 'graphtype',
693			'ymin_type_1' => 'ymin_type',
694			'ymax_type_1' => 'ymax_type',
695			'graph_items' => 'gitems'
696		]);
697	}
698}
699