PageRenderTime 53ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/zabbix-2.0.1/frontends/php/api/classes/CService.php

#
PHP | 1563 lines | 906 code | 176 blank | 481 comment | 152 complexity | f338431534c44911b5c177a2c1017d90 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0
  1. <?php
  2. /*
  3. ** Zabbix
  4. ** Copyright (C) 2000-2012 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. * Class containing methods for operations with IT Services
  22. * @package API
  23. */
  24. class CService extends CZBXAPI {
  25. protected $tableName = 'services';
  26. protected $tableAlias = 's';
  27. protected $sortColumns = array('sortorder', 'name');
  28. public function __construct() {
  29. parent::__construct();
  30. $this->getOptions = array_merge($this->getOptions, array(
  31. 'parentids' => null,
  32. 'childids' => null,
  33. 'countOutput' => null,
  34. 'selectParent' => null,
  35. 'selectDependencies' => null,
  36. 'selectParentDependencies' => null,
  37. 'selectTimes' => null,
  38. 'selectAlarms' => null,
  39. 'selectTrigger' => null,
  40. 'sortfield' => '',
  41. 'sortorder' => ''
  42. ));
  43. }
  44. /**
  45. * Get services.
  46. *
  47. * Allowed options:
  48. * - parentids - fetch the services that are hardlinked to the given parent services;
  49. * - childids - fetch the services that are hardlinked to the given child services;
  50. * - countOutput - return the number of the results as an integer;
  51. * - selectParent - include the parent service in the result;
  52. * - selectDependencies - include service child dependencies in the result;
  53. * - selectParentDependencies - include service parent dependencies in the result;
  54. * - selectTimes - include service times in the result;
  55. * - selectAlarms - include alarms generated by the service;
  56. * - selectTrigger - include the linked trigger;
  57. * - sortfield - name of columns to sort by;
  58. * - sortorder - sort order.
  59. *
  60. * @param array $options
  61. *
  62. * @return array
  63. */
  64. public function get(array $options) {
  65. $options = zbx_array_merge($this->getOptions, $options);
  66. // if the selectTrigger option is used, make sure we select the triggerid to be able to fetch the related triggers
  67. if ($options['selectTrigger'] !== null) {
  68. $options['output'] = $this->extendOutputOption($this->tableName(), 'triggerid', $options['output']);
  69. }
  70. // build and execute query
  71. $sql = $this->createSelectQuery($this->tableName(), $options);
  72. $res = DBselect($sql, $options['limit']);
  73. // fetch results
  74. $result = array();
  75. while ($row = DBfetch($res)) {
  76. // a count query, return a single result
  77. if ($options['countOutput'] !== null) {
  78. $result = $row['rowscount'];
  79. }
  80. // a normal select query
  81. else {
  82. $result[$row[$this->pk()]] = $this->unsetExtraFields($this->tableName(), $row, $options['output']);
  83. }
  84. }
  85. if ($options['countOutput'] !== null) {
  86. return $result;
  87. }
  88. if ($result) {
  89. $result = $this->addRelatedObjects($options, $result);
  90. }
  91. if ($options['preservekeys'] === null) {
  92. $result = zbx_cleanHashes($result);
  93. }
  94. return $result;
  95. }
  96. /**
  97. * Validates the input parameters for the create() method.
  98. *
  99. * @throws APIException if the input is invalid
  100. *
  101. * @param array $services
  102. *
  103. * @return void
  104. */
  105. protected function validateCreate(array $services) {
  106. foreach ($services as $service) {
  107. $this->checkName($service);
  108. $this->checkAlgorithm($service);
  109. $this->checkShowSla($service);
  110. $this->checkGoodSla($service);
  111. $this->checkSortOrder($service);
  112. $this->checkTriggerId($service);
  113. $this->checkStatus($service);
  114. $this->checkParentId($service);
  115. $error = _s('Wrong fields for service "%1$s".', $service['name']);
  116. $this->checkUnsupportedFields($this->tableName(), $service, $error, array(
  117. 'parentid', 'dependencies', 'times'
  118. ));
  119. }
  120. $this->checkTriggerPermissions($services);
  121. }
  122. /**
  123. * Creates the given services.
  124. *
  125. * @param array $services
  126. *
  127. * @return array
  128. */
  129. public function create(array $services) {
  130. $services = zbx_toArray($services);
  131. $this->validateCreate($services);
  132. // save the services
  133. $serviceIds = DB::insert($this->tableName(), $services);
  134. $dependencies = array();
  135. $serviceTimes = array();
  136. foreach ($services as $key => $service) {
  137. $serviceId = $serviceIds[$key];
  138. // save dependencies
  139. if (!empty($service['dependencies'])) {
  140. foreach ($service['dependencies'] as $dependency) {
  141. $dependency['serviceid'] = $serviceId;
  142. $dependencies[] = $dependency;
  143. }
  144. }
  145. // save parent service
  146. if (!empty($service['parentid'])) {
  147. $dependencies[] = array(
  148. 'serviceid' => $service['parentid'],
  149. 'dependsOnServiceid' => $serviceId,
  150. 'soft' => 0
  151. );
  152. }
  153. // save service times
  154. if (isset($service['times'])) {
  155. foreach ($service['times'] as $serviceTime) {
  156. $serviceTime['serviceid'] = $serviceId;
  157. $serviceTimes[] = $serviceTime;
  158. }
  159. }
  160. }
  161. if ($dependencies) {
  162. $this->addDependencies($dependencies);
  163. }
  164. if ($serviceTimes) {
  165. $this->addTimes($serviceTimes);
  166. }
  167. update_services_status_all();
  168. return array('serviceids' => $serviceIds);
  169. }
  170. /**
  171. * Validates the input parameters for the update() method.
  172. *
  173. * @throws APIException if the input is invalid
  174. *
  175. * @param array $services
  176. *
  177. * @return void
  178. */
  179. public function validateUpdate(array $services) {
  180. foreach ($services as $service) {
  181. if (empty($service['serviceid'])) {
  182. self::exception(ZBX_API_ERROR_PARAMETERS, _('Invalid method parameters.'));
  183. }
  184. }
  185. $this->checkServicePermissions(zbx_objectValues($services, 'serviceid'));
  186. $services = $this->extendObjects($this->tableName(), $services, array('name'));
  187. foreach ($services as $service) {
  188. $this->checkName($service);
  189. if (isset($service['algorithm'])) {
  190. $this->checkAlgorithm($service);
  191. }
  192. if (isset($service['showsla'])) {
  193. $this->checkShowSla($service);
  194. }
  195. if (isset($service['goodsla'])) {
  196. $this->checkGoodSla($service);
  197. }
  198. if (isset($service['sortorder'])) {
  199. $this->checkSortOrder($service);
  200. }
  201. if (isset($service['triggerid'])) {
  202. $this->checkTriggerId($service);
  203. }
  204. if (isset($service['status'])) {
  205. $this->checkStatus($service);
  206. }
  207. if (isset($service['parentid'])) {
  208. $this->checkParentId($service);
  209. }
  210. $error = _s('Wrong fields for service "%1$s".', $service['name']);
  211. $this->checkUnsupportedFields($this->tableName(), $service, $error, array(
  212. 'parentid', 'dependencies', 'times'
  213. ));
  214. }
  215. $this->checkTriggerPermissions($services);
  216. }
  217. /**
  218. * Updates the given services.
  219. *
  220. * @param array $services
  221. *
  222. * @return array
  223. */
  224. public function update(array $services) {
  225. $services = zbx_toArray($services);
  226. $this->validateUpdate($services);
  227. // save the services
  228. foreach ($services as $service) {
  229. DB::updateByPk($this->tableName(), $service['serviceid'], $service);
  230. }
  231. // update dependencies
  232. $dependencies = array();
  233. $parentDependencies = array();
  234. $serviceTimes = array();
  235. $deleteParentsForServiceIds = array();
  236. $deleteDependenciesForServiceIds = array();
  237. $deleteTimesForServiceIds = array();
  238. foreach ($services as $service) {
  239. if (isset($service['dependencies'])) {
  240. $deleteDependenciesForServiceIds[] = $service['serviceid'];
  241. if ($service['dependencies']) {
  242. foreach ($service['dependencies'] as $dependency) {
  243. $dependency['serviceid'] = $service['serviceid'];
  244. $dependencies[] = $dependency;
  245. }
  246. }
  247. }
  248. // update parent
  249. if (isset($service['parentid'])) {
  250. $deleteParentsForServiceIds[] = $service['serviceid'];
  251. if ($service['parentid']) {
  252. $parentDependencies[] = array(
  253. 'serviceid' => $service['parentid'],
  254. 'dependsOnServiceid' => $service['serviceid'],
  255. 'soft' => 0
  256. );
  257. }
  258. }
  259. // save service times
  260. if (isset($service['times'])) {
  261. $deleteTimesForServiceIds[] = $service['serviceid'];
  262. foreach ($service['times'] as $serviceTime) {
  263. $serviceTime['serviceid'] = $service['serviceid'];
  264. $serviceTimes[] = $serviceTime;
  265. }
  266. }
  267. }
  268. // replace dependencies
  269. if ($deleteParentsForServiceIds) {
  270. $this->deleteParentDependencies(zbx_objectValues($services, 'serviceid'));
  271. }
  272. if ($deleteDependenciesForServiceIds) {
  273. $this->deleteDependencies(array_unique($deleteDependenciesForServiceIds));
  274. }
  275. if ($parentDependencies || $dependencies) {
  276. $this->addDependencies(array_merge($parentDependencies, $dependencies));
  277. }
  278. // replace service times
  279. if ($deleteTimesForServiceIds) {
  280. $this->deleteTimes($deleteTimesForServiceIds);
  281. }
  282. if ($serviceTimes) {
  283. $this->addTimes($serviceTimes);
  284. }
  285. update_services_status_all();
  286. return array('serviceids' => zbx_objectValues($services, 'serviceid'));
  287. }
  288. /**
  289. * Validates the input parameters for the delete() method.
  290. *
  291. * @throws APIException if the input is invalid
  292. *
  293. * @param array $serviceIds
  294. *
  295. * @return void
  296. */
  297. public function validateDelete($serviceIds) {
  298. if (!$serviceIds) {
  299. self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.'));
  300. }
  301. $this->checkServicePermissions($serviceIds);
  302. $this->checkThatServicesDontHaveChildren($serviceIds);
  303. }
  304. /**
  305. * Delete services.
  306. *
  307. * @param $serviceIds
  308. *
  309. * @return array
  310. */
  311. public function delete($serviceIds) {
  312. $serviceIds = zbx_toArray($serviceIds);
  313. $this->validateDelete($serviceIds);
  314. DB::delete($this->tableName(), array('serviceid' => $serviceIds));
  315. update_services_status_all();
  316. return array('serviceids' => $serviceIds);
  317. }
  318. /**
  319. * Validates the input parameters for the addDependencies() method.
  320. *
  321. * @throws APIException if the input is invalid
  322. *
  323. * @param array $dependencies
  324. *
  325. * @return void
  326. */
  327. protected function validateAddDependencies(array $dependencies) {
  328. if (!$dependencies) {
  329. self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.'));
  330. }
  331. foreach ($dependencies as $dependency) {
  332. if (empty($dependency['serviceid']) || empty($dependency['dependsOnServiceid'])) {
  333. self::exception(ZBX_API_ERROR_PARAMETERS, _('Invalid method parameters.'));
  334. }
  335. }
  336. $serviceIds = array_merge(
  337. zbx_objectValues($dependencies, 'serviceid'),
  338. zbx_objectValues($dependencies, 'dependsOnServiceid')
  339. );
  340. $serviceIds = array_unique($serviceIds);
  341. $this->checkServicePermissions($serviceIds);
  342. foreach ($dependencies as $dependency) {
  343. $this->checkDependency($dependency);
  344. $this->checkUnsupportedFields('services_links', $dependency,
  345. _s('Wrong fields for dependency for service "%1$s".', $dependency['serviceid']),
  346. array('dependsOnServiceid', 'serviceid')
  347. );
  348. }
  349. $this->checkForHardlinkedDependencies($dependencies);
  350. $this->checkThatParentsDontHaveTriggers($dependencies);
  351. }
  352. /**
  353. * Add the given service dependencies.
  354. *
  355. * @param array $dependencies an array of service dependencies, each pair in the form of
  356. * array('serviceid' => 1, 'dependsOnServiceid' => 2, 'soft' => 0)
  357. *
  358. * @return array
  359. */
  360. public function addDependencies(array $dependencies) {
  361. $dependencies = zbx_toArray($dependencies);
  362. $this->validateAddDependencies($dependencies);
  363. $data = array();
  364. foreach ($dependencies as $dependency) {
  365. $data[] = array(
  366. 'serviceupid' => $dependency['serviceid'],
  367. 'servicedownid' => $dependency['dependsOnServiceid'],
  368. 'soft' => $dependency['soft']
  369. );
  370. }
  371. DB::insert('services_links', $data);
  372. return array('serviceids' => zbx_objectValues($dependencies, 'serviceid'));
  373. }
  374. /**
  375. * Validates the input for the deleteDependencies() method.
  376. *
  377. * @throws APIException if the given input is invalid
  378. *
  379. * @param array $serviceIds
  380. *
  381. * @return void
  382. */
  383. protected function validateDeleteDependencies(array $serviceIds) {
  384. if (!$serviceIds) {
  385. self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.'));
  386. }
  387. $this->checkServicePermissions($serviceIds);
  388. }
  389. /**
  390. * Deletes all dependencies for the given services.
  391. *
  392. * @param array $serviceIds
  393. *
  394. * @return boolean
  395. */
  396. public function deleteDependencies($serviceIds) {
  397. $serviceIds = zbx_toArray($serviceIds);
  398. $this->validateDeleteDependencies($serviceIds);
  399. DB::delete('services_links', array(
  400. 'serviceupid' => $serviceIds
  401. ));
  402. return array('serviceids' => $serviceIds);
  403. }
  404. /**
  405. * Validates the input for the addTimes() method.
  406. *
  407. * @throws APIException if the given input is invalid
  408. *
  409. * @param array $serviceTimes
  410. *
  411. * @return void
  412. */
  413. public function validateAddTimes(array $serviceTimes) {
  414. foreach ($serviceTimes as $serviceTime) {
  415. $this->checkTime($serviceTime);
  416. $this->checkUnsupportedFields('services_times', $serviceTime,
  417. _s('Wrong fields for time for service "%1$s".', $serviceTime['serviceid'])
  418. );
  419. }
  420. $this->checkServicePermissions(array_unique(zbx_objectValues($serviceTimes, 'serviceid')));
  421. }
  422. /**
  423. * Adds the given service times.
  424. *
  425. * @param array $serviceTimes an array of service times
  426. *
  427. * @return array
  428. */
  429. public function addTimes(array $serviceTimes) {
  430. $serviceTimes = zbx_toArray($serviceTimes);
  431. $this->validateAddTimes($serviceTimes);
  432. DB::insert('services_times', $serviceTimes);
  433. return array('serviceids' => zbx_objectValues($serviceTimes, 'serviceid'));
  434. }
  435. /**
  436. * Validates the input for the deleteTimes() method.
  437. *
  438. * @throws APIException if the given input is invalid
  439. *
  440. * @param array $serviceIds
  441. *
  442. * @return void
  443. */
  444. protected function validateDeleteTimes(array $serviceIds) {
  445. if (!$serviceIds) {
  446. self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.'));
  447. }
  448. $this->checkServicePermissions($serviceIds);
  449. }
  450. /**
  451. * Returns availability-related information about the given services during the given time intervals.
  452. *
  453. * Available options:
  454. * - serviceids - a single service ID or an array of service IDs;
  455. * - intervals - a single time interval or an array of time intervals, each containing:
  456. * - from - the beginning of the interval, timestamp;
  457. * - to - the end of the interval, timestamp.
  458. *
  459. * Returns the following availability information for each service:
  460. * - status - the current status of the service;
  461. * - problems - an array of triggers that are currently in problem state and belong to the given service
  462. * or it's descendants;
  463. * - sla - an array of requested intervals with SLA information:
  464. * - from - the beginning of the interval;
  465. * - to - the end of the interval;
  466. * - okTime - the time the service was in OK state, in seconds;
  467. * - problemTime - the time the service was in problem state, in seconds;
  468. * - downtimeTime - the time the service was down, in seconds.
  469. *
  470. * If the service calculation algorithm is set to SERVICE_ALGORITHM_NONE, the method will return an empty 'problems'
  471. * array and null for all of the calculated values.
  472. *
  473. * @param array $options
  474. *
  475. * @return array as array(serviceId2 => data1, serviceId2 => data2, ...)
  476. */
  477. public function getSla(array $options) {
  478. $serviceIds = (isset($options['serviceids'])) ? zbx_toArray($options['serviceids']) : null;
  479. $intervals = (isset($options['intervals'])) ? zbx_toArray($options['intervals']) : array();
  480. // fetch services
  481. $services = $this->get(array(
  482. 'output' => array('serviceid', 'name', 'status', 'algorithm'),
  483. 'selectTimes' => API_OUTPUT_EXTEND,
  484. 'selectParentDependencies' => array('serviceupid'),
  485. 'selectTrigger' => API_OUTPUT_EXTEND,
  486. 'serviceids' => $serviceIds,
  487. 'preservekeys' => true
  488. ));
  489. $rs = array();
  490. if ($services) {
  491. $usedSeviceIds = array();
  492. $problemServiceIds = array();
  493. foreach ($services as &$service) {
  494. $service['alarms'] = array();
  495. // don't calculate SLA for services with disabled status calculation
  496. if ($this->isStatusEnabled($service)) {
  497. $usedSeviceIds[$service['serviceid']] = $service['serviceid'];
  498. if ($service['status'] > 0) {
  499. $problemServiceIds[] = $service['serviceid'];
  500. }
  501. }
  502. }
  503. unset($service);
  504. // initial data
  505. foreach ($services as $service) {
  506. $rs[$service['serviceid']] = array(
  507. 'status' => ($this->isStatusEnabled($service)) ? $service['status'] : null,
  508. 'problems' => array(),
  509. 'sla' => array()
  510. );
  511. }
  512. if ($usedSeviceIds) {
  513. // add service alarms
  514. $intervalConditions = array();
  515. foreach ($intervals as $interval) {
  516. $intervalConditions[] = 'sa.clock BETWEEN '.zbx_dbstr($interval['from']).' AND '.zbx_dbstr($interval['to']);
  517. }
  518. $query = DBselect(
  519. 'SELECT *'.
  520. ' FROM service_alarms sa'.
  521. ' WHERE '.DBcondition('sa.serviceid', $usedSeviceIds).
  522. ' AND ('.implode(' OR ', $intervalConditions).')'.
  523. ' ORDER BY sa.clock,sa.servicealarmid'
  524. );
  525. while ($data = DBfetch($query)) {
  526. $services[$data['serviceid']]['alarms'][] = $data;
  527. }
  528. // add problem triggers
  529. if ($problemServiceIds) {
  530. $problemTriggers = $this->fetchProblemTriggers($problemServiceIds);
  531. $rs = $this->escalateProblems($services, $problemTriggers, $rs);
  532. }
  533. // calculate SLAs
  534. foreach ($intervals as $interval) {
  535. $latestValues = $this->fetchLatestValues($usedSeviceIds, $interval['from']);
  536. foreach ($services as $service) {
  537. $serviceId = $service['serviceid'];
  538. // only calculate the sla for services which require it
  539. if (isset($usedSeviceIds[$serviceId])) {
  540. $latestValue = (isset($latestValues[$serviceId])) ? $latestValues[$serviceId] : 0;
  541. $intervalSla = $this->calculateSla($service, $interval['from'], $interval['to'], $latestValue);
  542. }
  543. else {
  544. $intervalSla = array(
  545. 'ok' => null,
  546. 'okTime' => null,
  547. 'problemTime' => null,
  548. 'downtimeTime' => null
  549. );
  550. }
  551. $rs[$service['serviceid']]['sla'][] = array(
  552. 'from' => $interval['from'],
  553. 'to' => $interval['to'],
  554. 'sla' => $intervalSla['ok'],
  555. 'okTime' => $intervalSla['okTime'],
  556. 'problemTime' => $intervalSla['problemTime'],
  557. 'downtimeTime' => $intervalSla['downtimeTime']
  558. );
  559. }
  560. }
  561. }
  562. }
  563. return $rs;
  564. }
  565. /**
  566. * Deletes all service times for the given services.
  567. *
  568. * @param array $serviceIds
  569. *
  570. * @return boolean
  571. */
  572. public function deleteTimes($serviceIds) {
  573. $serviceIds = zbx_toArray($serviceIds);
  574. $this->validateDeleteTimes($serviceIds);
  575. DB::delete('services_times', array(
  576. 'serviceid' => $serviceIds
  577. ));
  578. return array('serviceids' => $serviceIds);
  579. }
  580. /**
  581. * Returns true if all of the given objects are available for reading.
  582. *
  583. * @param $ids
  584. *
  585. * @return bool
  586. */
  587. public function isReadable(array $ids) {
  588. if (empty($ids)) {
  589. return true;
  590. }
  591. $ids = array_unique($ids);
  592. $count = $this->get(array(
  593. 'serviceids' => $ids,
  594. 'output' => API_OUTPUT_SHORTEN,
  595. 'countOutput' => true
  596. ));
  597. return count($ids) == $count;
  598. }
  599. /**
  600. * Returns true if all of the given objects are available for writing.
  601. *
  602. * @param $ids
  603. *
  604. * @return bool
  605. */
  606. public function isWritable(array $ids) {
  607. return $this->isReadable($ids);
  608. }
  609. /**
  610. * Deletes the the dependencies of the parent services on the given services.
  611. *
  612. * @param $serviceIds
  613. *
  614. * @return void
  615. */
  616. protected function deleteParentDependencies($serviceIds) {
  617. DB::delete('services_links', array(
  618. 'servicedownid' => $serviceIds,
  619. 'soft' => 0
  620. ));
  621. }
  622. /**
  623. * Calculates the SLA for the given service during the given period.
  624. *
  625. * Returns the following information:
  626. * - ok - the percentage of time the service was in OK state;
  627. * - problem - the percentage of time the service was in problem state;
  628. * - okTime - the time the service was in OK state, in seconds;
  629. * - problemTime - the time the service was in problem state, in seconds;
  630. * - downtimeTime - the time the service was down, in seconds;
  631. * - dt;
  632. * - ut.
  633. *
  634. * @param array $service
  635. * @param $periodStart
  636. * @param $periodEnd
  637. * @param $prevAlarm the value of the last service alarm
  638. *
  639. * @return array
  640. */
  641. protected function calculateSla(array $service, $periodStart, $periodEnd, $prevAlarm) {
  642. /**
  643. * structure of "$data":
  644. * - key - time stamp
  645. * - alarm - on/off status (0,1 - off; >1 - on)
  646. * - dt_s - count of downtime starts
  647. * - dt_e - count of downtime ends
  648. * - ut_s - count of uptime starts
  649. * - ut_e - count of uptime ends
  650. */
  651. foreach ($service['alarms'] as $alarm) {
  652. if ($alarm['clock'] >= $periodStart && $alarm['clock'] <= $periodEnd) {
  653. $data[$alarm['clock']]['alarm'] = $alarm['value'];
  654. }
  655. }
  656. $unmarkedPeriodType = 'ut';
  657. foreach ($service['times'] as $time) {
  658. if ($time['type'] == SERVICE_TIME_TYPE_UPTIME) {
  659. $this->expandPeriodicalTimes($data, $periodStart, $periodEnd, $time['ts_from'], $time['ts_to'], 'ut');
  660. // if exist any uptime - unmarked time is downtime
  661. $unmarkedPeriodType = 'dt';
  662. }
  663. elseif ($time['type'] == SERVICE_TIME_TYPE_DOWNTIME) {
  664. $this->expandPeriodicalTimes($data, $periodStart, $periodEnd, $time['ts_from'], $time['ts_to'], 'dt');
  665. }
  666. elseif($time['type'] == SERVICE_TIME_TYPE_ONETIME_DOWNTIME && $time['ts_to'] >= $periodStart && $time['ts_from'] <= $periodEnd) {
  667. if ($time['ts_from'] < $periodStart) {
  668. $time['ts_from'] = $periodStart;
  669. }
  670. if ($time['ts_to'] > $periodEnd) {
  671. $time['ts_to'] = $periodEnd;
  672. }
  673. if (isset($data[$time['ts_from']]['dt_s'])) {
  674. $data[$time['ts_from']]['dt_s']++;
  675. }
  676. else {
  677. $data[$time['ts_from']]['dt_s'] = 1;
  678. }
  679. if (isset($data[$time['ts_to']]['dt_e'])) {
  680. $data[$time['ts_to']]['dt_e']++;
  681. }
  682. else {
  683. $data[$time['ts_to']]['dt_e'] = 1;
  684. }
  685. }
  686. }
  687. if (!isset($data[$periodEnd])) {
  688. $data[$periodEnd] = array();
  689. }
  690. // sort by time stamp
  691. ksort($data);
  692. // calculate times
  693. $dtCnt = 0;
  694. $utCnt = 0;
  695. $slaTime = array(
  696. 'dt' => array('problemTime' => 0, 'okTime' => 0),
  697. 'ut' => array('problemTime' => 0, 'okTime' => 0)
  698. );
  699. $prevTime = $periodStart;
  700. if (isset($data[$periodStart]['ut_s'])) {
  701. $utCnt += $data[$periodStart]['ut_s'];
  702. }
  703. if (isset($data[$periodStart]['ut_e'])) {
  704. $utCnt -= $data[$periodStart]['ut_e'];
  705. }
  706. if (isset($data[$periodStart]['dt_s'])) {
  707. $dtCnt += $data[$periodStart]['dt_s'];
  708. }
  709. if (isset($data[$periodStart]['dt_e'])) {
  710. $dtCnt -= $data[$periodStart]['dt_e'];
  711. }
  712. foreach ($data as $ts => $val) {
  713. // skip first data [already read]
  714. if ($ts == $periodStart) {
  715. continue;
  716. }
  717. if ($dtCnt > 0) {
  718. $periodType = 'dt';
  719. }
  720. elseif ($utCnt > 0) {
  721. $periodType = 'ut';
  722. }
  723. else {
  724. $periodType = $unmarkedPeriodType;
  725. }
  726. // state=0,1 [OK] (1 - information severity of trigger), >1 [PROBLEMS] (trigger severity)
  727. if ($prevAlarm > 1) {
  728. $slaTime[$periodType]['problemTime'] += $ts - $prevTime;
  729. }
  730. else {
  731. $slaTime[$periodType]['okTime'] += $ts - $prevTime;
  732. }
  733. if (isset($val['ut_s'])) {
  734. $utCnt += $val['ut_s'];
  735. }
  736. if (isset($val['ut_e'])) {
  737. $utCnt -= $val['ut_e'];
  738. }
  739. if (isset($val['dt_s'])) {
  740. $dtCnt += $val['dt_s'];
  741. }
  742. if (isset($val['dt_e'])) {
  743. $dtCnt -= $val['dt_e'];
  744. }
  745. if (isset($val['alarm'])) {
  746. $prevAlarm = $val['alarm'];
  747. }
  748. $prevTime = $ts;
  749. }
  750. $slaTime['problemTime'] = &$slaTime['ut']['problemTime'];
  751. $slaTime['okTime'] = &$slaTime['ut']['okTime'];
  752. $slaTime['downtimeTime'] = $slaTime['dt']['okTime'] + $slaTime['dt']['problemTime'];
  753. $fullTime = $slaTime['problemTime'] + $slaTime['okTime'];
  754. if ($fullTime > 0) {
  755. $slaTime['problem'] = 100 * $slaTime['problemTime'] / $fullTime;
  756. $slaTime['ok'] = 100 * $slaTime['okTime'] / $fullTime;
  757. }
  758. else {
  759. $slaTime['problem'] = 100;
  760. $slaTime['ok'] = 100;
  761. }
  762. return $slaTime;
  763. }
  764. /**
  765. * @see calculateSla()
  766. *
  767. * @param $data
  768. * @param $period_start
  769. * @param $period_end
  770. * @param $ts_from
  771. * @param $ts_to
  772. * @param $type
  773. *
  774. * @return void
  775. */
  776. function expandPeriodicalTimes(&$data, $period_start, $period_end, $ts_from, $ts_to, $type) {
  777. $week = getdate($period_start);
  778. $week = $period_start - $week['wday'] * SEC_PER_DAY - $week['hours'] * SEC_PER_HOUR - $week['minutes'] * SEC_PER_MIN - $week['seconds'];
  779. for (; $week < $period_end; $week += SEC_PER_WEEK) {
  780. $_s = $week + $ts_from;
  781. $_e = $week + $ts_to;
  782. if ($period_end < $_s || $period_start >= $_e) {
  783. continue;
  784. }
  785. if ($_s < $period_start) {
  786. $_s = $period_start;
  787. }
  788. if ($_e > $period_end) {
  789. $_e = $period_end;
  790. }
  791. if (isset($data[$_s][$type.'_s'])) {
  792. $data[$_s][$type.'_s']++;
  793. }
  794. else {
  795. $data[$_s][$type.'_s'] = 1;
  796. }
  797. if (isset($data[$_e][$type.'_e'])) {
  798. $data[$_e][$type.'_e']++;
  799. }
  800. else {
  801. $data[$_e][$type.'_e'] = 1;
  802. }
  803. }
  804. }
  805. /**
  806. * Returns an array of triggers which are in a problem state and are linked to the given services.
  807. *
  808. * @param array $serviceIds
  809. *
  810. * @return array in the form of array(serviceId1 => array(triggerId => trigger), ...)
  811. */
  812. protected function fetchProblemTriggers(array $serviceIds) {
  813. // get service reason
  814. $triggers = DBfetchArray(DBSelect(
  815. 'SELECT s.serviceid,t.*'.
  816. ' FROM services s,triggers t'.
  817. ' WHERE s.status>0'.
  818. ' AND t.triggerid=s.triggerid'.
  819. ' AND '.DBcondition('t.triggerid', get_accessible_triggers(PERM_READ_ONLY)).
  820. ' AND '.DBcondition('s.serviceid', $serviceIds).
  821. ' ORDER BY s.status DESC,t.description'
  822. ));
  823. $rs = array();
  824. foreach ($triggers as $trigger) {
  825. $serviceId = $trigger['serviceid'];
  826. unset($trigger['serviceid']);
  827. $rs[$serviceId] = array($trigger['triggerid'] => $trigger);
  828. }
  829. return $rs;
  830. }
  831. /**
  832. * Escalates the problem triggers from the child services to their parents and adds them to $slaData.
  833. * The escalation will stop if a service has status calculation disabled or is in OK state.
  834. *
  835. * @param array $services
  836. * @param array $serviceProblems an array of service triggers defines as
  837. * array(serviceId1 => array(triggerId => trigger), ...)
  838. * @param array $slaData
  839. *
  840. * @return array
  841. */
  842. protected function escalateProblems(array $services, array $serviceProblems, array $slaData) {
  843. $parentProblems = array();
  844. foreach ($serviceProblems as $serviceId => $problemTriggers) {
  845. $service = $services[$serviceId];
  846. // add the problem trigger of the current service to the data
  847. $slaData[$serviceId]['problems'] = zbx_array_merge($slaData[$serviceId]['problems'], $problemTriggers);
  848. // add the same trigger to the parent services
  849. foreach ($service['parentDependencies'] as $dependency) {
  850. $parentServiceId = $dependency['serviceupid'];
  851. if (isset($services[$parentServiceId])) {
  852. $parentService = $services[$parentServiceId];
  853. // escalate only if status calculation is enabled for the parent service and it's in problem state
  854. if ($this->isStatusEnabled($parentService) && $parentService['status']) {
  855. if (!isset($parentProblems[$parentServiceId])) {
  856. $parentProblems[$parentServiceId] = array();
  857. }
  858. $parentProblems[$parentServiceId] = zbx_array_merge($parentProblems[$parentServiceId], $problemTriggers);
  859. }
  860. }
  861. }
  862. }
  863. // propagate the problems to the parents
  864. if ($parentProblems) {
  865. $slaData = $this->escalateProblems($services, $parentProblems, $slaData);
  866. }
  867. return $slaData;
  868. }
  869. /**
  870. * Returns the value of the latest service alarm before the given time.
  871. *
  872. * @param array $serviceIds
  873. * @param int $beforeTime
  874. *
  875. * @return array
  876. */
  877. protected function fetchLatestValues(array $serviceIds, $beforeTime) {
  878. // the query will return the alarms with the maximum timestamp for each service
  879. // since multiple alarms can have the same timestamp, we only need to save the last one
  880. $query = DBSelect(
  881. 'SELECT sa.serviceid,sa.value
  882. FROM service_alarms sa
  883. LEFT OUTER JOIN service_alarms sa2 ON (sa.serviceid=sa2.serviceid AND sa.clock<sa2.clock AND sa2.clock<'.zbx_dbstr($beforeTime).')
  884. WHERE sa2.servicealarmid IS NULL
  885. AND sa.clock<'.zbx_dbstr($beforeTime).'
  886. AND '.DBcondition('sa.serviceid', $serviceIds).'
  887. ORDER BY sa.servicealarmid'
  888. );
  889. $rs = array();
  890. while ($alarm = DBfetch($query)) {
  891. $rs[$alarm['serviceid']] = $alarm['value'];
  892. }
  893. return $rs;
  894. }
  895. /**
  896. * Returns an array of dependencies that are children of the given services. Performs permission checks.
  897. *
  898. * @param array $parentServiceIds
  899. * @param $output
  900. *
  901. * @return array an array of service links sorted by "sortorder" in ascending order
  902. */
  903. protected function fetchChildDependencies(array $parentServiceIds, $output) {
  904. $sqlParts = API::getApi()->createSelectQueryParts('services_links', 'sl', array(
  905. 'output' => $output,
  906. 'filter' => array('serviceupid' => $parentServiceIds)
  907. ));
  908. // sort by sortorder
  909. $sqlParts['from'][] = $this->tableName().' '.$this->tableAlias();
  910. $sqlParts['where'][] = 'sl.servicedownid='.$this->fieldId('serviceid');
  911. $sqlParts = $this->addQueryOrder($this->fieldId('sortorder'), $sqlParts);
  912. $sqlParts = $this->addQueryOrder($this->fieldId('serviceid'), $sqlParts);
  913. // add permission filter
  914. if (CWebUser::getType() != USER_TYPE_SUPER_ADMIN) {
  915. $sqlParts['where'][] = '('.$this->fieldId('triggerid').' IS NULL OR '.DBcondition($this->fieldId('triggerid'), get_accessible_triggers(PERM_READ_ONLY)).')';
  916. }
  917. $sql = $this->createSelectQueryFromParts($sqlParts);
  918. return DBfetchArray(DBselect($sql));
  919. }
  920. /**
  921. * Returns an array of dependencies from the parent services to the given services.
  922. * Performs permission checks.
  923. *
  924. * @param array $childServiceIds
  925. * @param $output
  926. *
  927. * @return array an array of service links sorted by "sortorder" in ascending order
  928. */
  929. protected function fetchParentDependencies(array $childServiceIds, $output) {
  930. $sqlParts = API::getApi()->createSelectQueryParts('services_links', 'sl', array(
  931. 'output' => $output,
  932. 'filter' => array('servicedownid' => $childServiceIds)
  933. ));
  934. // sort by sortorder
  935. $sqlParts['from'][] = $this->tableName().' '.$this->tableAlias();
  936. $sqlParts['where'][] = 'sl.serviceupid='.$this->fieldId('serviceid');
  937. $sqlParts = $this->addQueryOrder($this->fieldId('sortorder'), $sqlParts);
  938. $sqlParts = $this->addQueryOrder($this->fieldId('serviceid'), $sqlParts);
  939. // add permission filter
  940. if (CWebUser::getType() != USER_TYPE_SUPER_ADMIN) {
  941. $sqlParts['where'][] = '('.$this->fieldId('triggerid').' IS NULL OR '.DBcondition($this->fieldId('triggerid'), get_accessible_triggers(PERM_READ_ONLY)).')';
  942. }
  943. $sql = $this->createSelectQueryFromParts($sqlParts);
  944. return DBfetchArray(DBselect($sql));
  945. }
  946. /**
  947. * Returns true if status calculation is enabled for the given service.
  948. *
  949. * @param array $service
  950. *
  951. * @return bool
  952. */
  953. protected function isStatusEnabled(array $service) {
  954. return ($service['algorithm'] != SERVICE_ALGORITHM_NONE);
  955. }
  956. /**
  957. * Validates the "name" field.
  958. *
  959. * @throws APIException if the name is missing
  960. *
  961. * @param array $service
  962. *
  963. * @return void
  964. */
  965. protected function checkName(array $service) {
  966. if (!isset($service['name']) || zbx_empty($service['name'])) {
  967. self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty name.'));
  968. }
  969. }
  970. /**
  971. * Validates the "algorithm" field. Assumes the "name" field is valid.
  972. *
  973. * @throws APIException if the name is missing or invalid
  974. *
  975. * @param array $service
  976. *
  977. * @return void
  978. */
  979. protected function checkAlgorithm(array $service) {
  980. if (!isset($service['algorithm']) || !serviceAlgorythm($service['algorithm'])) {
  981. self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect algorithm for service "%1$s".', $service['name']));
  982. }
  983. }
  984. /**
  985. * Validates the "showsla" field. Assumes the "name" field is valid.
  986. *
  987. * @throws APIException if the name is missing or is not a boolean value
  988. *
  989. * @param array $service
  990. *
  991. * @return void
  992. */
  993. protected function checkShowSla(array $service) {
  994. $showSlaValues = array(
  995. SERVICE_SHOW_SLA_OFF => true,
  996. SERVICE_SHOW_SLA_ON => true
  997. );
  998. if (!isset($service['showsla']) || !isset($showSlaValues[$service['showsla']])) {
  999. self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect calculate SLA value for service "%1$s".', $service['name']));
  1000. }
  1001. }
  1002. /**
  1003. * Validates the "showsla" field. Assumes the "name" field is valid.
  1004. *
  1005. * @throws APIException if the value is missing, or is out of bounds
  1006. *
  1007. * @param array $service
  1008. *
  1009. * @return void
  1010. */
  1011. protected function checkGoodSla(array $service) {
  1012. if ((!empty($service['showsla']) && empty($service['goodsla']))
  1013. || (isset($service['goodsla'])
  1014. && (!is_numeric($service['goodsla']) || $service['goodsla'] < 0 || $service['goodsla'] > 100))) {
  1015. self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect acceptable SLA for service "%1$s".', $service['name']));
  1016. }
  1017. }
  1018. /**
  1019. * Validates the "sortorder" field. Assumes the "name" field is valid.
  1020. *
  1021. * @throws APIException if the value is missing, or is out of bounds
  1022. *
  1023. * @param array $service
  1024. *
  1025. * @return void
  1026. */
  1027. protected function checkSortOrder(array $service) {
  1028. if (!isset($service['sortorder']) || !zbx_is_int($service['sortorder'])
  1029. || $service['sortorder'] < 0 || $service['sortorder'] > 999) {
  1030. self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect sorder order for service "%1$s".', $service['name']));
  1031. }
  1032. }
  1033. /**
  1034. * Validates the "triggerid" field. Assumes the "name" field is valid.
  1035. *
  1036. * @throws APIException if the value is incorrect
  1037. *
  1038. * @param array $service
  1039. *
  1040. * @return void
  1041. */
  1042. protected function checkTriggerId(array $service) {
  1043. if (!empty($service['triggerid']) && !zbx_is_int($service['triggerid'])) {
  1044. self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect trigger ID for service "%1$s".', $service['name']));
  1045. }
  1046. }
  1047. /**
  1048. * Validates the "parentid" field. Assumes the "name" field is valid.
  1049. *
  1050. * @throws APIException if the value is incorrect
  1051. *
  1052. * @param array $service
  1053. *
  1054. * @return void
  1055. */
  1056. protected function checkParentId(array $service) {
  1057. if (!empty($service['parentid']) && !zbx_is_int($service['parentid'])) {
  1058. if (isset($service['name'])) {
  1059. self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect parent for service "%1$s".', $service['name']));
  1060. }
  1061. else {
  1062. self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect parent service.'));
  1063. }
  1064. }
  1065. if (isset($service['serviceid']) && idcmp($service['serviceid'], $service['parentid'])) {
  1066. self::exception(ZBX_API_ERROR_PARAMETERS, _('Service cannot be parent and child at the same time.'));
  1067. }
  1068. }
  1069. /**
  1070. * Validates the "status" field. Assumes the "name" field is valid.
  1071. *
  1072. * @throws APIException if the value is incorrect
  1073. *
  1074. * @param array $service
  1075. *
  1076. * @return void
  1077. */
  1078. protected function checkStatus(array $service) {
  1079. if (!empty($service['status']) && !zbx_is_int($service['status'])) {
  1080. self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect status for service "%1$s".', $service['name']));
  1081. }
  1082. }
  1083. /**
  1084. * Checks that the user has read access to the given triggers.
  1085. *
  1086. * @throws APIException if the user doesn't have permission to access any of the triggers
  1087. *
  1088. * @param array $services
  1089. *
  1090. * @return void
  1091. */
  1092. protected function checkTriggerPermissions(array $services) {
  1093. $triggerIds = array();
  1094. foreach ($services as $service) {
  1095. if (!empty($service['triggerid'])) {
  1096. $triggerIds[] = $service['triggerid'];
  1097. }
  1098. }
  1099. if (!API::Trigger()->isReadable($triggerIds)) {
  1100. self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
  1101. }
  1102. }
  1103. /**
  1104. * Checks that all of the given services are readable.
  1105. *
  1106. * @throws APIException if at least one of the services doesn't exist
  1107. *
  1108. * @param array $serviceIds
  1109. *
  1110. * @return void
  1111. */
  1112. protected function checkServicePermissions(array $serviceIds) {
  1113. if (!$this->isReadable($serviceIds)) {
  1114. self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
  1115. }
  1116. }
  1117. /**
  1118. * Checks that none of the given services have any children.
  1119. *
  1120. * @throws APIException if at least one of the services has a child service
  1121. *
  1122. * @param array $serviceIds
  1123. *
  1124. * @return void
  1125. */
  1126. protected function checkThatServicesDontHaveChildren(array $serviceIds) {
  1127. $child = API::getApi()->select('services_links', array(
  1128. 'output' => array('serviceupid'),
  1129. 'filter' => array(
  1130. 'serviceupid' => $serviceIds,
  1131. 'soft' => 0
  1132. ),
  1133. 'limit' => 1
  1134. ));
  1135. $child = reset($child);
  1136. if ($child) {
  1137. $service = API::getApi()->select($this->tableName(), array(
  1138. 'output' => array('name'),
  1139. 'serviceids' => $child['serviceupid'],
  1140. 'limit' => 1
  1141. ));
  1142. $service = reset($service);
  1143. self::exception(ZBX_API_ERROR_PERMISSIONS,
  1144. _s('Service "%1$s" cannot be deleted, because it is dependent on another service.', $service['name'])
  1145. );
  1146. }
  1147. }
  1148. /**
  1149. * Checks that the given dependency is valid.
  1150. *
  1151. * @throws APIException if the dependency is invalid
  1152. *
  1153. * @param array $dependency
  1154. *
  1155. * @return void
  1156. */
  1157. protected function checkDependency(array $dependency) {
  1158. if (idcmp($dependency['serviceid'], $dependency['dependsOnServiceid'])) {
  1159. $service = API::getApi()->select($this->tableName(), array(
  1160. 'output' => array('name'),
  1161. 'serviceids' => $dependency['serviceid']
  1162. ));
  1163. $service = reset($service);
  1164. self::exception(ZBX_API_ERROR_PARAMETERS, _s('Service "%1$s" cannot be dependent on itself.', $service['name']));
  1165. }
  1166. // check 'soft' field value
  1167. if (!isset($dependency['soft']) || !in_array((int) $dependency['soft'], array(0, 1), true)) {
  1168. $service = API::getApi()->select($this->tableName(), array(
  1169. 'output' => array('name'),
  1170. 'serviceids' => $dependency['serviceid']
  1171. ));
  1172. $service = reset($service);
  1173. self::exception(ZBX_API_ERROR_PARAMETERS,
  1174. _s('Incorrect "soft" field value for dependency for service "%1$s".', $service['name'])
  1175. );
  1176. }
  1177. }
  1178. /**
  1179. * Checks that that none of the given services are hard linked to a different service.
  1180. * Assumes the dependencies are valid.
  1181. *
  1182. * @throws APIException if at a least one service is hard linked to another service
  1183. *
  1184. * @param array $dependencies
  1185. *
  1186. * @return void
  1187. */
  1188. protected function checkForHardlinkedDependencies(array $dependencies) {
  1189. // only check hard dependencies
  1190. $softDepServiceIds = array();
  1191. foreach ($dependencies as $dependency) {
  1192. if (!$dependency['soft']) {
  1193. $softDepServiceIds[] = $dependency['dependsOnServiceid'];
  1194. }
  1195. }
  1196. if ($softDepServiceIds) {
  1197. // look for at least one hardlinked service among the given
  1198. $softDepServiceIds = array_unique($softDepServiceIds);
  1199. $dep = API::getApi()->select('services_links', array(
  1200. 'output' => array('servicedownid'),
  1201. 'filter' => array(
  1202. 'soft' => 0,
  1203. 'servicedownid' => $softDepServiceIds
  1204. ),
  1205. 'limit' => 1
  1206. ));
  1207. if ($dep) {
  1208. $dep = reset($dep);
  1209. $service = API::getApi()->select($this->tableName(), array(
  1210. 'output' => array('name'),
  1211. 'serviceids' => $dep['servicedownid']
  1212. ));
  1213. $service = reset($service);
  1214. self::exception(ZBX_API_ERROR_PARAMETERS,
  1215. _s('Service "%1$s" is already hardlinked to a different service.', $service['name'])
  1216. );
  1217. }
  1218. }
  1219. }
  1220. /**
  1221. * Checks that none of the parent services are linked to a trigger. Assumes the dependencies are valid.
  1222. *
  1223. * @throws APIException if at least one of the parent services is linked to a trigger
  1224. *
  1225. * @param array $dependencies
  1226. *
  1227. * @return void
  1228. */
  1229. protected function checkThatParentsDontHaveTriggers(array $dependencies) {
  1230. $parentServiceIds = array_unique(zbx_objectValues($dependencies, 'serviceid'));
  1231. if ($parentServiceIds) {
  1232. $query = DBselect(
  1233. 'SELECT s.triggerid,s.name'.
  1234. ' FROM services s '.
  1235. ' WHERE '.DBcondition('s.serviceid', $parentServiceIds).
  1236. ' AND s.triggerid IS NOT NULL', 1);
  1237. if ($parentService = DBfetch($query)) {
  1238. self::exception(ZBX_API_ERROR_PARAMETERS,
  1239. _s('Service "%1$s" cannot be linked to a trigger and have children at the same time.', $parentService['name']));
  1240. }
  1241. }
  1242. }
  1243. /**
  1244. * Checks that the given service time is valid.
  1245. *
  1246. * @throws APIException if the service time is invalid
  1247. *
  1248. * @param array $serviceTime
  1249. *
  1250. * @return void
  1251. */
  1252. protected function checkTime(array $serviceTime) {
  1253. if (empty($serviceTime['serviceid'])) {
  1254. self::exception(ZBX_API_ERROR_PARAMETERS, _('Invalid method parameters.'));
  1255. }
  1256. checkServiceTime($serviceTime);
  1257. }
  1258. protected function applyQueryFilterOptions($tableName, $tableAlias, array $options, array $sqlParts) {
  1259. if (CWebUser::getType() != USER_TYPE_SUPER_ADMIN) {
  1260. $accessibleTriggers = get_accessible_triggers(PERM_READ_ONLY);
  1261. // if services with specific trigger IDs were requested, return only the ones accessible to the current user.
  1262. if ($options['filter']['triggerid']) {
  1263. $options['filter']['triggerid'] = array_intersect($accessibleTriggers, $options['filter']['triggerid']);
  1264. }
  1265. // otherwise return services with either no triggers, or any trigger accessible to the current user
  1266. else {
  1267. $sqlParts['where'][] = '('.$this->fieldId('triggerid').' IS NULL OR '.DBcondition($this->fieldId('triggerid'), $accessibleTriggers).')';
  1268. }
  1269. }
  1270. $sqlParts = parent::applyQueryFilterOptions($tableName, $tableAlias, $options, $sqlParts);
  1271. // parentids
  1272. if ($options['parentids'] !== null) {
  1273. $sqlParts['from'][] = 'services_links slp';
  1274. $sqlParts['where'][] = $this->fieldId('serviceid').'=slp.servicedownid AND slp.soft=0';
  1275. $sqlParts['where'][] = DBcondition('slp.serviceupid', (array) $options['parentids']);
  1276. }
  1277. // childids
  1278. if ($options['childids'] !== null) {
  1279. $sqlParts['from'][] = 'services_links slc';
  1280. $sqlParts['where'][] = $this->fieldId('serviceid').'=slc.serviceupid AND slc.soft=0';
  1281. $sqlParts['where'][] = DBcondition('slc.servicedownid', (array) $options['childids']);
  1282. }
  1283. return $sqlParts;
  1284. }
  1285. protected function addRelatedObjects(array $options, array $result) {
  1286. $result = parent::addRelatedObjects($options, $result);
  1287. $serviceIds = array_keys($result);
  1288. // selectDependencies
  1289. if ($options['selectDependencies'] !== null) {
  1290. $dependencyOutput = $this->extendOutputOption('services_links', 'serviceupid', $options['selectDependencies']);
  1291. $dependencies = $this->fetchChildDependencies($serviceIds, $dependencyOutput);
  1292. foreach ($result as &$service) {
  1293. $service['dependencies'] = array();
  1294. }
  1295. unset($service);
  1296. foreach ($dependencies as $dependency) {
  1297. $refId = $dependency['serviceupid'];
  1298. $dependency = $this->unsetExtraFields('services_links', $dependency, $options['selectDependencies']);
  1299. $result[$refId]['dependencies'][] = $dependency;
  1300. }
  1301. }
  1302. // selectParentDependencies
  1303. if ($options['selectParentDependencies'] !== null) {
  1304. $dependencyOutput = $this->extendOutputOption('services_links', 'servicedownid', $options['selectParentDependencies']);
  1305. $dependencies = $this->fetchParentDependencies($serviceIds, $dependencyOutput);
  1306. foreach ($result as &$service) {
  1307. $service['parentDependencies'] = array();
  1308. }
  1309. unset($service);
  1310. foreach ($dependencies as $dependency) {
  1311. $refId = $dependency['servicedownid'];
  1312. $dependency = $this->unsetExtraFields('services_links', $dependency, $options['selectParentDependencies']);
  1313. $result[$refId]['parentDependencies'][] = $dependency;
  1314. }
  1315. }
  1316. // selectParent
  1317. if ($options['selectParent'] !== null) {
  1318. $parents = $this->get(array(
  1319. 'output' => $options['selectParent'],
  1320. 'childids' => $serviceIds,
  1321. 'selectDependencies' => array('servicedownid', 'soft')
  1322. ));
  1323. foreach ($result as &$service) {
  1324. $service['parent'] = array();
  1325. }
  1326. unset($service);
  1327. // map the parents to their children, look for the first hard linked dependency
  1328. foreach ($parents as $parent) {
  1329. foreach ($parent['dependencies'] as $dependency) {
  1330. if (!$dependency['soft']) {
  1331. unset($parent['dependencies']);
  1332. if (isset($result[$dependency['servicedownid']])) {
  1333. $result[$dependency['servicedownid']]['parent'] = $parent;
  1334. }
  1335. }
  1336. }
  1337. }
  1338. }
  1339. // selectTimes
  1340. if ($options['selectTimes'] !== null) {
  1341. $timesOutput = $this->extendOutputOption('services_times', 'serviceid', $options['selectTimes']);
  1342. $serviceTimes = API::getApi()->select('services_times', array(
  1343. 'output' => $timesOutput,
  1344. 'filter' => array('serviceid' => $serviceIds)
  1345. ));
  1346. foreach ($result as &$service) {
  1347. $service['times'] = array();
  1348. }
  1349. unset($service);
  1350. foreach ($serviceTimes as $serviceTime) {
  1351. $refId = $serviceTime['serviceid'];
  1352. $serviceTime = $this->unsetExtraFields('services_times', $serviceTime, $options['selectTimes']);
  1353. $result[$refId]['times'][] = $serviceTime;
  1354. }
  1355. }
  1356. // selectAlarms
  1357. if ($options['selectAlarms'] !== null) {
  1358. $alarmsOutput = $this->extendOutputOption('service_alarms', 'serviceid', $options['selectAlarms']);
  1359. $alarmsTimes = API::getApi()->select('service_alarms', array(
  1360. 'output' => $alarmsOutput,
  1361. 'filter' => array('serviceid' => $serviceIds)
  1362. ));
  1363. foreach ($result as &$service) {
  1364. $service['times'] = array();
  1365. }
  1366. unset($service);
  1367. foreach ($alarmsTimes as $serviceAlarm) {
  1368. $refId = $serviceAlarm['serviceid'];
  1369. $serviceAlarm = $this->unsetExtraFields('service_alarms', $serviceAlarm, $options['selectAlarms']);
  1370. $result[$refId]['times'][] = $serviceAlarm;
  1371. }
  1372. }
  1373. // selectTrigger
  1374. if ($options['selectTrigger'] !== null) {
  1375. $triggers = API::getApi()->select('triggers', array(
  1376. 'output' => $options['selectTrigger'],
  1377. 'triggerids' => array_unique(zbx_objectValues($result, 'triggerid')),
  1378. 'preservekeys' => true
  1379. ));
  1380. foreach ($result as &$service) {
  1381. $service['trigger'] = ($service['triggerid']) ? $triggers[$service['triggerid']] : array();
  1382. }
  1383. unset($service);
  1384. }
  1385. return $result;
  1386. }
  1387. }