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 * A class to display discovery table as a screen element.
24 */
25class CScreenDiscovery extends CScreenBase {
26
27	/**
28	 * Data
29	 *
30	 * @var array
31	 */
32	protected $data = [];
33
34	/**
35	 * Init screen data.
36	 *
37	 * @param array		$options
38	 * @param array		$options['data']
39	 */
40	public function __construct(array $options = []) {
41		parent::__construct($options);
42
43		$this->data = $options['data'];
44	}
45
46	/**
47	 * Process screen.
48	 *
49	 * @return CDiv (screen inside container)
50	 */
51	public function get() {
52		$this->dataId = 'discovery';
53
54		$sort_field = $this->data['sort'];
55		$sort_order = $this->data['sortorder'];
56		$druleid = $this->data['druleid'];
57
58		// discovery rules
59		$options = [
60			'output' => ['druleid', 'name'],
61			'selectDHosts' => ['dhostid', 'status', 'lastup', 'lastdown'],
62			'filter' => ['status' => DRULE_STATUS_ACTIVE]
63		];
64
65		if ($druleid > 0) {
66			$options['druleids'] = $druleid; // set selected discovery rule id
67		}
68
69		$drules = API::DRule()->get($options);
70		if ($drules) {
71			order_result($drules, 'name');
72		}
73
74		// discovery services
75		$options = [
76			'selectHosts' => ['hostid', 'name', 'status'],
77			'output' => ['dserviceid', 'type', 'key_', 'port', 'status', 'lastup', 'lastdown', 'ip', 'dns'],
78			'sortfield' => $sort_field,
79			'sortorder' => $sort_order,
80			'limitSelects' => 1
81		];
82		if ($druleid > 0) {
83			$options['druleids'] = $druleid;
84		}
85		else {
86			$options['druleids'] = zbx_objectValues($drules, 'druleid');
87		}
88		$dservices = API::DService()->get($options);
89
90		// user macros
91		$macros = API::UserMacro()->get([
92			'output' => ['macro', 'value'],
93			'globalmacro' => true
94		]);
95		$macros = zbx_toHash($macros, 'macro');
96
97		// services
98		$services = [];
99		foreach ($dservices as $dservice) {
100			$key_ = $dservice['key_'];
101			if ($key_ !== '') {
102				if (array_key_exists($key_, $macros)) {
103					$key_ = $macros[$key_]['value'];
104				}
105				$key_ = ': '.$key_;
106			}
107			$service_name = discovery_check_type2str($dservice['type']).
108				discovery_port2str($dservice['type'], $dservice['port']).$key_;
109			$services[$service_name] = 1;
110		}
111		ksort($services);
112
113		// discovery services to hash
114		$dservices = zbx_toHash($dservices, 'dserviceid');
115
116		// discovery hosts
117		$dhosts = API::DHost()->get([
118			'druleids' => zbx_objectValues($drules, 'druleid'),
119			'selectDServices' => ['dserviceid', 'ip', 'dns', 'type', 'status', 'key_'],
120			'output' => ['dhostid', 'lastdown', 'lastup', 'druleid']
121		]);
122		$dhosts = zbx_toHash($dhosts, 'dhostid');
123
124		$header = [
125			make_sorting_header(_('Discovered device'), 'ip', $sort_field, $sort_order,
126				'zabbix.php?action=discovery.view'
127			),
128			_('Monitored host'),
129			_('Uptime').'/'._('Downtime')
130		];
131
132		foreach ($services as $name => $foo) {
133			$header[] = (new CColHeader($name))->addClass('vertical_rotation');
134		}
135
136		// create table
137		$table = (new CTableInfo())
138			->makeVerticalRotation()
139			->setHeader($header);
140
141		foreach ($drules as $drule) {
142			$discovery_info = [];
143
144			foreach ($drule['dhosts'] as $dhost) {
145				if ($dhost['status'] == DHOST_STATUS_DISABLED) {
146					$hclass = 'disabled';
147					$htime = $dhost['lastdown'];
148				}
149				else {
150					$hclass = 'enabled';
151					$htime = $dhost['lastup'];
152				}
153
154				// $primary_ip stores the primary host ip of the dhost
155				$primary_ip = '';
156
157				foreach ($dhosts[$dhost['dhostid']]['dservices'] as $dservice) {
158					$dservice = $dservices[$dservice['dserviceid']];
159
160					$hostName = '';
161
162					$host = reset($dservices[$dservice['dserviceid']]['hosts']);
163					if (!is_null($host)) {
164						$hostName = $host['name'];
165					}
166
167					if ($primary_ip !== '') {
168						if ($primary_ip === $dservice['ip']) {
169							$htype = 'primary';
170						}
171						else {
172							$htype = 'slave';
173						}
174					}
175					else {
176						$primary_ip = $dservice['ip'];
177						$htype = 'primary';
178					}
179
180					if (!array_key_exists($dservice['ip'], $discovery_info)) {
181						$discovery_info[$dservice['ip']] = [
182							'ip' => $dservice['ip'],
183							'dns' => $dservice['dns'],
184							'type' => $htype,
185							'class' => $hclass,
186							'host' => $hostName,
187							'time' => $htime,
188						];
189					}
190
191					if ($dservice['status'] == DSVC_STATUS_DISABLED) {
192						$class = ZBX_STYLE_INACTIVE_BG;
193						$time = 'lastdown';
194					}
195					else {
196						$class = ZBX_STYLE_ACTIVE_BG;
197						$time = 'lastup';
198					}
199
200					$key_ = $dservice['key_'];
201					if ($key_ !== '') {
202						if (array_key_exists($key_, $macros)) {
203							$key_ = $macros[$key_]['value'];
204						}
205						$key_ = NAME_DELIMITER.$key_;
206					}
207
208					$service_name = discovery_check_type2str($dservice['type']).
209						discovery_port2str($dservice['type'], $dservice['port']).$key_;
210
211					$discovery_info[$dservice['ip']]['services'][$service_name] = [
212						'class' => $class,
213						'time' => $dservice[$time]
214					];
215				}
216			}
217
218			if ($druleid == 0 && $discovery_info) {
219				$col = new CCol(
220					[bold($drule['name']), SPACE.'('._n('%d device', '%d devices', count($discovery_info)).')']
221				);
222				$col->setColSpan(count($services) + 3);
223
224				$table->addRow($col);
225			}
226			order_result($discovery_info, $sort_field, $sort_order);
227
228			foreach ($discovery_info as $ip => $h_data) {
229				$dns = $h_data['dns'] == '' ? '' : ' ('.$h_data['dns'].')';
230				$row = [
231					$h_data['type'] == 'primary'
232						? (new CSpan($ip.$dns))->addClass($h_data['class'])
233						: new CSpan(SPACE.SPACE.$ip.$dns),
234					new CSpan(array_key_exists('host', $h_data) ? $h_data['host'] : ''),
235					(new CSpan((($h_data['time'] == 0 || $h_data['type'] === 'slave')
236						? ''
237						: convert_units(['value' => time() - $h_data['time'], 'units' => 'uptime'])))
238					)
239						->addClass($h_data['class'])
240				];
241
242				foreach ($services as $name => $foo) {
243					$class = null;
244					$time = SPACE;
245					$hint = (new CDiv(SPACE))->addClass($class);
246
247					$hint_table = null;
248					if (array_key_exists($name, $h_data['services'])) {
249						$class = $h_data['services'][$name]['class'];
250						$time = $h_data['services'][$name]['time'];
251
252						$hint_table = (new CTableInfo())->setAttribute('style', 'width: auto;');
253
254						if ($class == ZBX_STYLE_ACTIVE_BG) {
255							$hint_table->setHeader(_('Uptime'));
256						}
257						else {
258							$hint_table->setHeader(_('Downtime'));
259						}
260
261						$hint_table->addRow(
262							(new CCol(zbx_date2age($h_data['services'][$name]['time'])))->addClass($class)
263						);
264					}
265					$column = (new CCol($hint))->addClass($class);
266					if (!is_null($hint_table)) {
267						$column->setHint($hint_table);
268					}
269					$row[] = $column;
270				}
271				$table->addRow($row);
272			}
273		}
274
275		return $this->getOutput($table, true, $this->data);
276	}
277}
278