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
22require_once dirname(__FILE__).'/include/config.inc.php';
23require_once dirname(__FILE__).'/include/services.inc.php';
24require_once dirname(__FILE__).'/include/triggers.inc.php';
25require_once dirname(__FILE__).'/include/html.inc.php';
26
27$page['title'] = _('Configuration of services');
28$page['file'] = 'services.php';
29$page['scripts'] = ['class.calendar.js'];
30
31require_once dirname(__FILE__).'/include/page_header.php';
32
33//	VAR		TYPE	OPTIONAL FLAGS	VALIDATION	EXCEPTION
34$fields = [
35	'serviceid' =>				[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,		null],
36	'name' => 					[T_ZBX_STR, O_OPT, null,	NOT_EMPTY, 'isset({add}) || isset({update})', _('Name')],
37	'algorithm' =>				[T_ZBX_INT, O_OPT, null,	IN('0,1,2'),'isset({add}) || isset({update})'],
38	'showsla' =>				[T_ZBX_INT, O_OPT, null,	IN([SERVICE_SHOW_SLA_OFF, SERVICE_SHOW_SLA_ON]),	null],
39	'goodsla' => 				[T_ZBX_DBL, O_OPT, null,	BETWEEN(0, 100), null,
40									_('Calculate SLA, acceptable SLA (in %)')
41								],
42	'sortorder' => 				[T_ZBX_INT, O_OPT, null,	BETWEEN(0, 999), null, _('Sort order (0->999)')],
43	'times' =>					[T_ZBX_STR, O_OPT, null,	null,		null],
44	'triggerid' =>				[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,		null],
45	'trigger' =>				[T_ZBX_STR, O_OPT, null,	null,		null],
46	'new_service_time' =>		[T_ZBX_STR, O_OPT, null,	null,		null],
47	'new_service_time_from' =>	[T_ZBX_ABS_TIME, O_OPT, null, 	NOT_EMPTY,	null, _('From')],
48	'new_service_time_till' =>	[T_ZBX_ABS_TIME, O_OPT, null, 	NOT_EMPTY,	null, _('Till')],
49	'children' =>				[T_ZBX_STR, O_OPT, P_SYS,	null,		null],
50	'parentid' =>				[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,		null],
51	'parentname' =>				[T_ZBX_STR, O_OPT, null,	null,		null],
52	// actions
53	'add' =>					[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
54	'update' =>					[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
55	'add_service_time' =>		[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
56	'delete' =>					[T_ZBX_STR, O_OPT, P_SYS|P_ACT,	null,		null],
57	// others
58	'form' =>					[T_ZBX_STR, O_OPT, P_SYS,	null,		null],
59	'form_refresh' =>			[T_ZBX_INT, O_OPT, null,	null,		null]
60];
61check_fields($fields);
62
63/*
64 * Permissions
65 */
66if (!empty($_REQUEST['serviceid'])) {
67	$options = [
68		'output' => API_OUTPUT_EXTEND,
69		'serviceids' => $_REQUEST['serviceid']
70	];
71
72	if (isset($_REQUEST['delete'])) {
73		$options['output'] = ['serviceid', 'name'];
74	}
75	else {
76		$options['selectParent'] = ['serviceid', 'name'];
77		$options['selectDependencies'] = API_OUTPUT_EXTEND;
78		$options['selectTimes'] = API_OUTPUT_EXTEND;
79	}
80
81	$service = API::Service()->get($options);
82	$service = reset($service);
83	if (!$service && hasRequest('delete')) {
84		show_error_message(_('No permissions to referred object or it does not exist!'));
85	}
86	elseif (!$service) {
87		access_deny();
88	}
89}
90
91/*
92 * Actions
93 */
94
95// delete
96if (hasRequest('delete') && $service) {
97	DBstart();
98
99	$result = API::Service()->delete([$service['serviceid']]);
100
101	if ($result) {
102		add_audit(AUDIT_ACTION_DELETE, AUDIT_RESOURCE_IT_SERVICE, 'Name ['.$service['name'].'] id ['.$service['serviceid'].']');
103		unset($_REQUEST['form']);
104	}
105	unset($service);
106
107	$result = DBend($result);
108	show_messages($result, _('Service deleted'), _('Cannot delete service'));
109}
110
111$service_times = [];
112
113if (hasRequest('add') || hasRequest('update')) {
114	DBstart();
115
116	$children = getRequest('children', []);
117	$dependencies = [];
118
119	foreach ($children as $child) {
120		$dependencies[] = [
121			'dependsOnServiceid' => $child['serviceid'],
122			'soft' => (isset($child['soft'])) ? $child['soft'] : 0
123		];
124	}
125
126	$request = [
127		'name' => getRequest('name'),
128		'triggerid' => getRequest('triggerid'),
129		'algorithm' => getRequest('algorithm'),
130		'showsla' => getRequest('showsla', SERVICE_SHOW_SLA_OFF),
131		'sortorder' => getRequest('sortorder'),
132		'times' => getRequest('times', []),
133		'parentid' => getRequest('parentid'),
134		'dependencies' => $dependencies
135	];
136
137	if (hasRequest('goodsla')) {
138		$request['goodsla'] = getRequest('goodsla');
139	}
140
141	if (isset($service['serviceid'])) {
142		$request['serviceid'] = $service['serviceid'];
143
144		$result = API::Service()->update($request);
145
146		$messageSuccess = _('Service updated');
147		$messageFailed = _('Cannot update service');
148		$auditAction = AUDIT_ACTION_UPDATE;
149	}
150	else {
151		$result = API::Service()->create($request);
152
153		$messageSuccess = _('Service created');
154		$messageFailed = _('Cannot add service');
155		$auditAction = AUDIT_ACTION_ADD;
156	}
157
158	if ($result) {
159		$serviceid = (isset($service['serviceid'])) ? $service['serviceid'] : reset($result['serviceids']);
160		add_audit($auditAction, AUDIT_RESOURCE_IT_SERVICE, 'Name ['.$_REQUEST['name'].'] id ['.$serviceid.']');
161		unset($_REQUEST['form']);
162	}
163
164	$result = DBend($result);
165	show_messages($result, $messageSuccess, $messageFailed);
166}
167// Validate and get service times.
168elseif (hasRequest('add_service_time') && hasRequest('new_service_time')) {
169	$new_service_time = getRequest('new_service_time');
170	$_REQUEST['times'] = getRequest('times', []);
171	$result = true;
172
173	if ($new_service_time['type'] == SERVICE_TIME_TYPE_ONETIME_DOWNTIME) {
174		$absolute_time_parser = new CAbsoluteTimeParser();
175
176		$absolute_time_parser->parse(getRequest('new_service_time_from'));
177		$new_service_time_from = $absolute_time_parser->getDateTime(true);
178
179		if (!validateDateInterval($new_service_time_from->format('Y'), $new_service_time_from->format('m'),
180				$new_service_time_from->format('d'))) {
181			$result = false;
182			error(_s('"%s" must be between 1970.01.01 and 2038.01.18.', _('From')));
183		}
184
185		$absolute_time_parser->parse(getRequest('new_service_time_till'));
186		$new_service_time_till = $absolute_time_parser->getDateTime(true);
187
188		if (!validateDateInterval($new_service_time_till->format('Y'), $new_service_time_till->format('m'),
189				$new_service_time_till->format('d'))) {
190			$result = false;
191			error(_s('"%s" must be between 1970.01.01 and 2038.01.18.', _('Till')));
192		}
193
194		if ($result) {
195			$new_service_time['ts_from'] = $new_service_time_from->getTimestamp();
196			$new_service_time['ts_to'] = $new_service_time_till->getTimestamp();
197		}
198	}
199	else {
200		$new_service_time['ts_from'] = dowHrMinToSec($new_service_time['from_week'], $new_service_time['from_hour'],
201			$new_service_time['from_minute']
202		);
203		$new_service_time['ts_to'] = dowHrMinToSec($new_service_time['to_week'], $new_service_time['to_hour'],
204			$new_service_time['to_minute']
205		);
206	}
207
208	if ($result) {
209		try {
210			checkServiceTime($new_service_time);
211
212			// If this time is not already there, adding it for inserting.
213			unset($new_service_time['from_week']);
214			unset($new_service_time['to_week']);
215			unset($new_service_time['from_hour']);
216			unset($new_service_time['to_hour']);
217			unset($new_service_time['from_minute']);
218			unset($new_service_time['to_minute']);
219
220			if (!str_in_array($new_service_time, $_REQUEST['times'])) {
221				$_REQUEST['times'][] = $new_service_time;
222			}
223		}
224		catch (APIException $e) {
225			error($e->getMessage());
226		}
227	}
228
229	show_messages();
230}
231else {
232	unset($_REQUEST['new_service_time']['from_week']);
233	unset($_REQUEST['new_service_time']['to_week']);
234	unset($_REQUEST['new_service_time']['from_hour']);
235	unset($_REQUEST['new_service_time']['to_hour']);
236	unset($_REQUEST['new_service_time']['from_minute']);
237	unset($_REQUEST['new_service_time']['to_minute']);
238}
239
240if (hasRequest('form')) {
241	$data = [
242		'form' => getRequest('form'),
243		'form_refresh' => getRequest('form_refresh', 0),
244		'service' => (getRequest('serviceid', 0) != 0) ? $service : null,
245		'times' => getRequest('times', []),
246		'new_service_time' => getRequest('new_service_time', ['type' => SERVICE_TIME_TYPE_UPTIME])
247	];
248
249	// Populate the form from the object from the database.
250	if (getRequest('serviceid', 0) != 0 && !hasRequest('form_refresh')) {
251		$data['name'] = $data['service']['name'];
252		$data['algorithm'] = $data['service']['algorithm'];
253		$data['showsla'] = $data['service']['showsla'];
254		$data['goodsla'] = $data['service']['goodsla'];
255		$data['sortorder'] = $data['service']['sortorder'];
256		$data['triggerid'] = isset($data['service']['triggerid']) ? $data['service']['triggerid'] : 0;
257		$data['times'] = $service['times'];
258
259		// parent
260		if ($parent = $service['parent']) {
261			$data['parentid'] = $parent['serviceid'];
262			$data['parentname'] = $parent['name'];
263		}
264		else {
265			$data['parentid'] = 0;
266			$data['parentname'] = 'root';
267		}
268
269		// get children
270		$data['children'] = [];
271		if ($service['dependencies']) {
272			$child_services = API::Service()->get([
273				'serviceids' => zbx_objectValues($service['dependencies'], 'servicedownid'),
274				'selectTrigger' => ['description'],
275				'output' => ['name', 'triggerid'],
276				'preservekeys' => true
277			]);
278
279			foreach ($service['dependencies'] as $dependency) {
280				$child_service = $child_services[$dependency['servicedownid']];
281				$data['children'][] = [
282					'name' => $child_service['name'],
283					'triggerid' => $child_service['triggerid'],
284					'trigger' => ($child_service['triggerid'] == 0) ? '' : $child_service['trigger']['description'],
285					'serviceid' => $dependency['servicedownid'],
286					'soft' => $dependency['soft']
287				];
288			}
289
290			CArrayHelper::sort($data['children'], ['name', 'serviceid']);
291		}
292	}
293	// Populate the form from a submitted request.
294	else {
295		$new_service_time = getRequest('new_service_time');
296
297		if ($new_service_time && $new_service_time['type'] == SERVICE_TIME_TYPE_ONETIME_DOWNTIME) {
298			$data['new_service_time_from'] = hasRequest('new_service_time_from')
299				? getRequest('new_service_time_from')
300				: (new DateTime('today'))->format(ZBX_DATE_TIME);
301
302			$data['new_service_time_till'] = hasRequest('new_service_time_till')
303				? getRequest('new_service_time_till')
304				: (new DateTime('tomorrow'))->format(ZBX_DATE_TIME);
305		}
306
307		$data['name'] = getRequest('name', '');
308		$data['algorithm'] = getRequest('algorithm', SERVICE_ALGORITHM_MAX);
309		$data['showsla'] = getRequest('showsla', SERVICE_SHOW_SLA_OFF);
310		$data['goodsla'] = getRequest('goodsla', SERVICE_SLA);
311		$data['sortorder'] = getRequest('sortorder', 0);
312		$data['triggerid'] = getRequest('triggerid', 0);
313		$data['parentid'] = getRequest('parentid', 0);
314		$data['parentname'] = getRequest('parentname', '');
315		$data['children'] = getRequest('children', []);
316	}
317
318	// get trigger
319	if ($data['triggerid'] > 0) {
320		$trigger = API::Trigger()->get([
321			'triggerids' => $data['triggerid'],
322			'output' => ['description'],
323			'selectHosts' => ['name'],
324			'expandDescription' => true
325		]);
326		$trigger = reset($trigger);
327		$host = reset($trigger['hosts']);
328		$data['trigger'] = $host['name'].NAME_DELIMITER.$trigger['description'];
329	}
330	else {
331		$data['trigger'] = '';
332	}
333
334	// render view
335	$servicesView = new CView('configuration.services.edit', $data);
336	$servicesView->render();
337	$servicesView->show();
338}
339else {
340	// services
341	$services = API::Service()->get([
342		'output' => ['name', 'serviceid', 'algorithm', 'sortorder'],
343		'selectParent' => ['serviceid'],
344		'selectDependencies' => ['servicedownid', 'soft', 'linkid'],
345		'selectTrigger' => ['description'],
346		'preservekeys' => true
347	]);
348
349	sortServices($services);
350
351	$treeData = [];
352	createServiceConfigurationTree($services, $treeData);
353	$tree = new CServiceTree('service_conf_tree', $treeData, [
354		'caption' => _('Service'),
355		'action' => _('Action'),
356		'algorithm' => _('Status calculation'),
357		'description' => _('Trigger')
358	]);
359	if (empty($tree)) {
360		error(_('Cannot format tree.'));
361	}
362
363	$data = ['tree' => $tree];
364
365	// render view
366	$servicesView = new CView('configuration.services.list', $data);
367	$servicesView->render();
368	$servicesView->show();
369}
370
371require_once dirname(__FILE__).'/include/page_footer.php';
372