/include/classes/api/services/CTrend.php

https://bitbucket.org/steme/openshift-zabbix-frontend · PHP · 312 lines · 216 code · 47 blank · 49 comment · 34 complexity · 0ea5a4f056a25033e7277ff14b0ecc7f MD5 · raw file

  1. <?php
  2. /*
  3. ** Zabbix
  4. ** Copyright (C) 2001-2018 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 trends.
  22. */
  23. class CTrend extends CApiService {
  24. public function __construct() {
  25. // the parent::__construct() method should not be called.
  26. }
  27. /**
  28. * Get trend data.
  29. *
  30. * @param array $options
  31. * @param int $options['time_from']
  32. * @param int $options['time_till']
  33. * @param int $options['limit']
  34. * @param string $options['order']
  35. *
  36. * @return array|int trend data as array or false if error
  37. */
  38. public function get($options = []) {
  39. $default_options = [
  40. 'itemids' => null,
  41. // filter
  42. 'time_from' => null,
  43. 'time_till' => null,
  44. // output
  45. 'output' => API_OUTPUT_EXTEND,
  46. 'countOutput' => false,
  47. 'limit' => null
  48. ];
  49. $options = zbx_array_merge($default_options, $options);
  50. $storage_items = [];
  51. $result = ($options['countOutput']) ? 0 : [];
  52. if ($options['itemids'] === null || $options['itemids']) {
  53. // Check if items have read permissions.
  54. $items = API::Item()->get([
  55. 'output' => ['itemid', 'value_type'],
  56. 'itemids' => $options['itemids'],
  57. 'webitems' => true,
  58. 'filter' => ['value_type' => [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64]]
  59. ]);
  60. foreach ($items as $item) {
  61. $history_source = CHistoryManager::getDataSourceType($item['value_type']);
  62. $storage_items[$history_source][$item['value_type']][$item['itemid']] = true;
  63. }
  64. }
  65. foreach ([ZBX_HISTORY_SOURCE_ELASTIC, ZBX_HISTORY_SOURCE_SQL] as $source) {
  66. if (array_key_exists($source, $storage_items)) {
  67. $options['itemids'] = $storage_items[$source];
  68. switch ($source) {
  69. case ZBX_HISTORY_SOURCE_ELASTIC:
  70. $data = $this->getFromElasticsearch($options);
  71. break;
  72. default:
  73. $data = $this->getFromSql($options);
  74. }
  75. if (is_array($result)) {
  76. $result = array_merge($result, $data);
  77. }
  78. else {
  79. $result += $data;
  80. }
  81. }
  82. }
  83. return $result;
  84. }
  85. /**
  86. * SQL specific implementation of get.
  87. *
  88. * @see CTrend::get
  89. */
  90. private function getFromSql($options) {
  91. $sql_where = [];
  92. if ($options['time_from'] !== null) {
  93. $sql_where['clock_from'] = 't.clock>='.zbx_dbstr($options['time_from']);
  94. }
  95. if ($options['time_till'] !== null) {
  96. $sql_where['clock_till'] = 't.clock<='.zbx_dbstr($options['time_till']);
  97. }
  98. if (!$options['countOutput']) {
  99. $sql_limit = ($options['limit'] && zbx_ctype_digit($options['limit'])) ? $options['limit'] : null;
  100. $sql_fields = [];
  101. if (is_array($options['output'])) {
  102. foreach ($options['output'] as $field) {
  103. if ($this->hasField($field, 'trends') && $this->hasField($field, 'trends_uint')) {
  104. $sql_fields[] = 't.'.$field;
  105. }
  106. }
  107. }
  108. elseif ($options['output'] == API_OUTPUT_EXTEND) {
  109. $sql_fields[] = 't.*';
  110. }
  111. // An empty field set or invalid output method (string). Select only "itemid" instead of everything.
  112. if (!$sql_fields) {
  113. $sql_fields[] = 't.itemid';
  114. }
  115. $result = [];
  116. foreach ([ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64] as $value_type) {
  117. if ($sql_limit !== null && $sql_limit <= 0) {
  118. break;
  119. }
  120. $sql_from = ($value_type == ITEM_VALUE_TYPE_FLOAT) ? 'trends' : 'trends_uint';
  121. if ($options['itemids'][$value_type]) {
  122. $sql_where['itemid'] = dbConditionInt('t.itemid', array_keys($options['itemids'][$value_type]));
  123. $res = DBselect(
  124. 'SELECT '.implode(',', $sql_fields).
  125. ' FROM '.$sql_from.' t'.
  126. ' WHERE '.implode(' AND ', $sql_where),
  127. $sql_limit
  128. );
  129. while ($row = DBfetch($res)) {
  130. $result[] = $row;
  131. }
  132. if ($sql_limit !== null) {
  133. $sql_limit -= count($result);
  134. }
  135. }
  136. }
  137. $result = $this->unsetExtraFields($result, ['itemid'], $options['output']);
  138. }
  139. else {
  140. $result = 0;
  141. foreach ([ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64] as $value_type) {
  142. if ($options['itemids'][$value_type]) {
  143. $sql_from = ($value_type == ITEM_VALUE_TYPE_FLOAT) ? 'trends' : 'trends_uint';
  144. $sql_where['itemid'] = dbConditionInt('t.itemid', array_keys($options['itemids'][$value_type]));
  145. $res = DBselect(
  146. 'SELECT COUNT(*) AS rowscount'.
  147. ' FROM '.$sql_from.' t'.
  148. ' WHERE '.implode(' AND ', $sql_where)
  149. );
  150. if ($row = DBfetch($res)) {
  151. $result += $row['rowscount'];
  152. }
  153. }
  154. }
  155. }
  156. return $result;
  157. }
  158. /**
  159. * Elasticsearch specific implementation of get.
  160. *
  161. * @see CTrend::get
  162. */
  163. private function getFromElasticsearch($options) {
  164. $query_must = [];
  165. $value_types = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64];
  166. $query = [
  167. 'aggs' => [
  168. 'group_by_itemid' => [
  169. 'terms' => [
  170. 'field' => 'itemid'
  171. ],
  172. 'aggs' => [
  173. 'group_by_clock' => [
  174. 'date_histogram' => [
  175. 'field' => 'clock',
  176. 'interval' => '1h',
  177. 'min_doc_count' => 1,
  178. ],
  179. 'aggs' => [
  180. 'max_value' => [
  181. 'max' => [
  182. 'field' => 'value'
  183. ]
  184. ],
  185. 'avg_value' => [
  186. 'avg' => [
  187. 'field' => 'value'
  188. ]
  189. ],
  190. 'min_value' => [
  191. 'min' => [
  192. 'field' => 'value'
  193. ]
  194. ]
  195. ]
  196. ]
  197. ]
  198. ]
  199. ],
  200. 'size' => 0
  201. ];
  202. if ($options['time_from'] !== null) {
  203. $query_must[] = [
  204. 'range' => [
  205. 'clock' => [
  206. 'gte' => $options['time_from']
  207. ]
  208. ]
  209. ];
  210. }
  211. if ($options['time_till'] !== null) {
  212. $query_must[] = [
  213. 'range' => [
  214. 'clock' => [
  215. 'lte' => $options['time_till']
  216. ]
  217. ]
  218. ];
  219. }
  220. $limit = ($options['limit'] && zbx_ctype_digit($options['limit'])) ? $options['limit'] : null;
  221. $result = [];
  222. if ($options['countOutput']) {
  223. $result = 0;
  224. }
  225. foreach (CHistoryManager::getElasticsearchEndpoints($value_types) as $type => $endpoint) {
  226. $itemids = array_keys($options['itemids'][$type]);
  227. if (!$itemids) {
  228. continue;
  229. }
  230. $query['query']['bool']['must'] = [
  231. 'terms' => [
  232. 'itemid' => $itemids
  233. ]
  234. ] + $query_must;
  235. $query['aggs']['group_by_itemid']['terms']['size'] = count($itemids);
  236. $data = CElasticsearchHelper::query('POST', $endpoint, $query);
  237. foreach ($data['group_by_itemid']['buckets'] as $item) {
  238. if (!$options['countOutput']) {
  239. foreach ($item['group_by_clock']['buckets'] as $histogram) {
  240. if ($limit !== null) {
  241. // Limit is reached, no need to continue.
  242. if ($limit <= 0) {
  243. break 3;
  244. }
  245. $limit--;
  246. }
  247. $result[] = [
  248. 'itemid' => $item['key'],
  249. // Field key_as_string is used to get seconds instead of milliseconds.
  250. 'clock' => $histogram['key_as_string'],
  251. 'num' => $histogram['doc_count'],
  252. 'min_value' => $histogram['min_value']['value'],
  253. 'avg_value' => $histogram['avg_value']['value'],
  254. 'max_value' => $histogram['max_value']['value']
  255. ];
  256. }
  257. }
  258. else {
  259. $result += count($item['group_by_clock']['buckets']);
  260. }
  261. }
  262. }
  263. return $result;
  264. }
  265. }