diff --git a/pandora_console/images/widgets/ModulesByStatus.png b/pandora_console/images/widgets/ModulesByStatus.png new file mode 100644 index 0000000000..796ff6382a Binary files /dev/null and b/pandora_console/images/widgets/ModulesByStatus.png differ diff --git a/pandora_console/include/ajax/module.php b/pandora_console/include/ajax/module.php index ca6d948117..bd356894ad 100755 --- a/pandora_console/include/ajax/module.php +++ b/pandora_console/include/ajax/module.php @@ -68,6 +68,11 @@ if (check_login()) { 0 ); + $get_data_ModulesByStatus = (bool) get_parameter( + 'get_data_ModulesByStatus', + 0 + ); + $load_filter_modal = get_parameter('load_filter_modal', 0); $save_filter_modal = get_parameter('save_filter_modal', 0); $get_monitor_filters = get_parameter('get_monitor_filters', 0); @@ -1665,6 +1670,259 @@ if (check_login()) { return; } + if ($get_data_ModulesByStatus === true) { + global $config; + $data = []; + + $table_id = get_parameter('table_id', ''); + $search = get_parameter('search', ''); + $status = get_parameter('status', ''); + $start = get_parameter('start', 0); + $length = get_parameter('length', $config['block_size']); + // There is a limit of (2^32)^2 (18446744073709551615) rows in a MyISAM table, show for show all use max nrows. + $length = ($length != '-1') ? $length : '18446744073709551615'; + $order = get_datatable_order(true); + $nodes = get_parameter('nodes', 0); + + $where = ''; + $recordsTotal = 0; + + + if (empty($search) === false) { + $where = 'tagente_modulo.nombre LIKE "%%'.$search.'%%" AND '; + } + + $where .= sprintf( + 'tagente_estado.estado IN (%s) + AND tagente_modulo.delete_pending = 0', + $status + ); + + if (is_metaconsole() === false) { + $order_by = ''; + switch ($order['field']) { + case 'nombre': + $order_by = 'tagente_modulo.'.$order['field'].' '.$order['direction']; + break; + + case 'alias': + $order_by = 'tagente.'.$order['field'].' '.$order['direction']; + break; + + case 'last_status_change': + $order_by = 'tagente_estado.'.$order['field'].' '.$order['direction']; + break; + + case 'estado': + $order_by = 'tagente_estado.'.$order['field'].' '.$order['direction']; + break; + + default: + $order_by = 'tagente_estado.last_status_change desc'; + break; + } + + $sql = sprintf( + 'SELECT + tagente_modulo.nombre, + tagente.alias, + tagente.id_agente, + tagente_estado.last_status_change, + tagente_estado.estado + FROM tagente_modulo + INNER JOIN tagente + ON tagente_modulo.id_agente = tagente.id_agente + INNER JOIN tagente_estado + ON tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + WHERE %s + ORDER BY %s + LIMIT %d, %d', + $where, + $order_by, + $start, + $length + ); + $data = db_get_all_rows_sql($sql); + + $sql_count = sprintf( + 'SELECT COUNT(*) AS "total" + FROM tagente_modulo + INNER JOIN tagente + ON tagente_modulo.id_agente = tagente.id_agente + INNER JOIN tagente_estado + ON tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo' + ); + $recordsTotal = db_get_value_sql($sql_count); + + // Metaconsole. + } else { + // $servers_ids = array_column(metaconsole_get_servers(), 'id'); + $servers_ids = explode(',', $nodes); + + foreach ($servers_ids as $server_id) { + try { + $node = new Node((int) $server_id); + $node->connect(); + + $sql = sprintf( + 'SELECT + tagente_modulo.nombre, + tagente.alias, + tagente.id_agente, + tagente_estado.last_status_change, + tagente_estado.estado + FROM tagente_modulo + INNER JOIN tagente + ON tagente_modulo.id_agente = tagente.id_agente + INNER JOIN tagente_estado + ON tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + WHERE %s', + $where + ); + + $res_sql = db_get_all_rows_sql($sql); + + foreach ($res_sql as $row_sql) { + $row_sql['server_name'] = $node->server_name(); + $row_sql['server_url'] = $node->server_url(); + array_push($data, $row_sql); + } + + $node->disconnect(); + } catch (\Exception $e) { + // Unexistent modules. + $node->disconnect(); + } + } + + // Drop temporary table if exist. + db_process_sql('DROP TEMPORARY TABLE IF EXISTS temp_modules_status;'); + + $table_temporary = 'CREATE TEMPORARY TABLE IF NOT EXISTS temp_modules_status ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + nombre VARCHAR(600), + alias VARCHAR(600), + id_agente INT, + last_status_change INT, + estado INT, + server_name VARCHAR(100), + server_url VARCHAR(200), + PRIMARY KEY (`id`), + KEY `nombre` (`nombre`(600)) + )'; + db_process_sql($table_temporary); + + $result = db_process_sql_insert_multiple('temp_modules_status', $data); + + if (empty($result) === false) { + $data = []; + $sql = ''; + $where = ''; + + if (empty($search) === false) { + $where = 'nombre LIKE "%%'.$search.'%%" AND '; + } + + $where .= sprintf( + 'estado IN (%s)', + $status + ); + + $order_by = $order['field'].' '.$order['direction']; + + $sql = sprintf( + 'SELECT + nombre, + alias, + id_agente, + last_status_change, + estado, + server_name, + server_url + FROM temp_modules_status + WHERE %s + ORDER BY %s + LIMIT %d, %d', + $where, + $order_by, + $start, + $length + ); + $data = db_get_all_rows_sql($sql); + + $sql_count = sprintf( + 'SELECT COUNT(*) AS "total" + FROM temp_modules_status' + ); + + $recordsTotal = db_get_value_sql($sql_count); + } + } + + if ($data === false) { + $data = []; + } + + foreach ($data as $key => $row) { + $data[$key]['nombre'] = html_ellipsis_characters($row['nombre'], 35, true); + + if (is_metaconsole() === false) { + $name_link = ''; + $name_link .= ''; + + $data[$key]['alias'] = $name_link; + + $data[$key]['last_status_change'] = ui_print_timestamp( + $row['last_status_change'], + true + ); + + switch ((int) $row['estado']) { + case 0: + $status_img = ui_print_status_image(STATUS_MODULE_OK, __('Normal'), true); + break; + + case 1: + case 6: + $status_img = ui_print_status_image(STATUS_MODULE_CRITICAL, __('Critical'), true); + break; + + case 2: + $status_img = ui_print_status_image(STATUS_MODULE_WARNING, __('Warning'), true); + break; + + case 3: + $status_img = ui_print_status_image(STATUS_MODULE_UNKNOWN, __('Unknown'), true); + break; + + case 5: + $status_img = ui_print_status_image(STATUS_MODULE_NO_DATA, __('Not init'), true); + break; + + default: + $status_img = ''; + break; + } + + $data[$key]['estado'] = $status_img; + } + + echo json_encode( + [ + 'data' => $data, + 'recordsTotal' => $recordsTotal, + 'recordsFiltered' => $recordsTotal, + ] + ); + } + if ($get_children_modules === true) { $parent_modules = get_parameter('parent_modulues', false); $children_selected = []; diff --git a/pandora_console/include/functions_html.php b/pandora_console/include/functions_html.php index 01d8172633..2be81a9800 100644 --- a/pandora_console/include/functions_html.php +++ b/pandora_console/include/functions_html.php @@ -6740,6 +6740,32 @@ function html_print_extended_select_for_downtime_cron( } +/** + * Ellipse string to x characters. + * + * @param string $string String to ellipsis. + * @param integer $characters Characters size to show. + * @return string String ellipsed. + */ +function html_ellipsis_characters( + string $string, + int $characters, + bool $return=false +) { + $out = $string; + + if (strlen($string) > $characters) { + $out = substr($string, 0, $characters).'...'; + } + + if ($return === true) { + return $out; + } else { + echo $out; + } +} + + /** * Return formed subtitle with the new Pandora's style. * diff --git a/pandora_console/include/lib/Dashboard/Widget.php b/pandora_console/include/lib/Dashboard/Widget.php index 51600d5df4..b4705007b0 100644 --- a/pandora_console/include/lib/Dashboard/Widget.php +++ b/pandora_console/include/lib/Dashboard/Widget.php @@ -421,6 +421,7 @@ class Widget case 'BlockHistogram': case 'DataMatrix': case 'EventCardboard': + case 'ModulesByStatus': case 'AvgSumMaxMinModule': $className .= '\\'.$name; break; diff --git a/pandora_console/include/lib/Dashboard/Widgets/ModulesByStatus.php b/pandora_console/include/lib/Dashboard/Widgets/ModulesByStatus.php new file mode 100644 index 0000000000..21fa53d21c --- /dev/null +++ b/pandora_console/include/lib/Dashboard/Widgets/ModulesByStatus.php @@ -0,0 +1,595 @@ +width = $width; + + // Height. + $this->height = $height; + + // Grid Width. + $this->gridWidth = $gridWidth; + + // Cell Id. + $this->cellId = $cellId; + + // Options. + $this->values = $this->decoders($this->getOptionsWidget()); + + // Positions. + $this->position = $this->getPositionWidget(); + + // Page. + $this->page = basename(__FILE__); + + // ClassName. + $class = new \ReflectionClass($this); + $this->className = $class->getShortName(); + + // Title. + $this->title = __('Module status'); + + // Name. + if (empty($this->name) === true) { + $this->name = 'ModulesByStatus'; + } + + // This forces at least a first configuration. + // This forces at least a first configuration. + $this->configurationRequired = false; + if (empty($this->values['status']) === true) { + $this->configurationRequired = true; + } + + $this->overflow_scrollbars = false; + } + + + /** + * Decoders hack for retrocompability. + * + * @param array $decoder Values. + * + * @return array Returns the values ​​with the correct key. + */ + public function decoders(array $decoder): array + { + $values = []; + // Retrieve global - common inputs. + $values = parent::decoders($decoder); + + if (isset($decoder['search']) === true) { + $values['search'] = $decoder['search']; + } + + if (isset($decoder['status']) === true) { + $values['status'] = $decoder['status']; + } + + if (isset($decoder['limit']) === true) { + $values['limit'] = $decoder['limit']; + } + + if (isset($decoder['nodes']) === true) { + $values['nodes'] = $decoder['nodes']; + } + + return $values; + } + + + /** + * Generates inputs for form (specific). + * + * @return array Of inputs. + * + * @throws Exception On error. + */ + public function getFormInputs(): array + { + global $config; + + $values = $this->values; + + // Retrieve global - common inputs. + $inputs = parent::getFormInputs(); + + // Search. + $inputs[] = [ + 'label' => __('Free search').ui_print_help_tip(__('Search filter by Module name field content'), true), + 'arguments' => [ + 'name' => 'search', + 'type' => 'text', + 'value' => $values['search'], + 'return' => true, + 'size' => 0, + ], + ]; + + // Status fields. + $status_fields = []; + $status_fields[AGENT_MODULE_STATUS_NORMAL] = __('Normal'); + $status_fields[AGENT_MODULE_STATUS_CRITICAL_BAD] = __('Critical'); + $status_fields[AGENT_MODULE_STATUS_WARNING] = __('Warning'); + $status_fields[AGENT_MODULE_STATUS_UNKNOWN] = __('Unknown'); + $status_fields[AGENT_MODULE_STATUS_NOT_INIT] = __('Not init'); + $status_fields[AGENT_MODULE_STATUS_NOT_NORMAL] = __('Not normal'); + $status_selected = explode(',', $values['status']); + + (isset($values['status']) === false) ? $status_selected = AGENT_MODULE_STATUS_CRITICAL_BAD : ''; + + $inputs[] = [ + 'label' => __('Status'), + 'arguments' => [ + 'name' => 'status', + 'type' => 'select', + 'fields' => $status_fields, + 'selected' => $status_selected, + 'return' => true, + 'multiple' => true, + 'class' => 'overflow-hidden', + 'size' => count($status_fields), + 'select_all' => false, + 'required' => true, + ], + ]; + + // Limit fields. + $limit_fields = []; + $limit_fields[5] = 5; + $limit_fields[10] = 10; + $limit_fields[25] = 25; + $limit_fields[100] = 100; + $limit_fields[200] = 200; + $limit_fields[500] = 500; + $limit_fields[1000] = 1000; + $limit_selected = explode(',', $values['limit']); + + (isset($values['limit']) === false) ? $limit_selected = 5 : ''; + + $inputs[] = [ + 'label' => __('Limit'), + 'arguments' => [ + 'name' => 'limit', + 'type' => 'select', + 'fields' => $limit_fields, + 'selected' => $limit_selected, + 'return' => true, + 'required' => true, + 'select2_enable' => false, + ], + ]; + + // Nodes. + if (is_metaconsole() === true) { + $nodes_fields = []; + $servers_ids = metaconsole_get_servers(); + + foreach ($servers_ids as $server) { + $nodes_fields[$server['id']] = $server['server_name']; + } + + $nodes_selected = explode(',', $values['nodes']); + + (isset($values['nodes']) === false) ? $nodes_selected = $servers_ids : ''; + + $nodes_height = count($nodes_fields); + if (count($nodes_fields) > 5) { + $nodes_height = 5; + } + + $inputs[] = [ + 'label' => __('Nodes'), + 'arguments' => [ + 'name' => 'nodes', + 'type' => 'select', + 'fields' => $nodes_fields, + 'selected' => $nodes_selected, + 'return' => true, + 'multiple' => true, + 'class' => 'overflow-hidden', + 'size' => $nodes_height, + 'select_all' => false, + 'required' => true, + ], + ]; + } + + return $inputs; + } + + + /** + * Get Post for widget. + * + * @return array + */ + public function getPost():array + { + // Retrieve global - common inputs. + $values = parent::getPost(); + + $values['search'] = \get_parameter('search', ''); + $values['status'] = \get_parameter('status', ''); + $values['limit'] = \get_parameter('limit', ''); + $values['nodes'] = \get_parameter('nodes', ''); + + return $values; + } + + + /** + * Draw widget. + * + * @return string; + */ + public function load() + { + $this->size = parent::getSize(); + + $output = ''; + + if (is_metaconsole() === true) { + $modules = []; + + $servers_ids = array_column(metaconsole_get_servers(), 'id'); + + foreach ($servers_ids as $server_id) { + try { + $node = new Node((int) $server_id); + + $node->connect(); + $modules_tmp = $this->getInfoModules( + $this->values['search'], + $this->values['status'], + $this->values['nodes'] + ); + $modules[$node->id()] = $modules_tmp[0]; + $node->disconnect(); + } catch (\Exception $e) { + // Unexistent modules. + $node->disconnect(); + } + } + } else { + $modules = $this->getInfoModules( + $this->values['search'], + $this->values['status'] + ); + } + + if ($modules !== false && empty($modules) === false) { + // Datatables list. + try { + $info_columns = $this->columns(); + $column_names = $info_columns['column_names']; + $columns = $info_columns['columns']; + + $tableId = 'ModuleByStatus_'.$this->dashboardId.'_'.$this->cellId; + // Load datatables user interface. + ui_print_datatable( + [ + 'id' => $tableId, + 'class' => 'info_table align-left-important', + 'style' => 'width: 100%', + 'columns' => $columns, + 'column_names' => $column_names, + 'ajax_url' => 'include/ajax/module', + 'ajax_data' => [ + 'get_data_ModulesByStatus' => 1, + 'table_id' => $tableId, + 'search' => $this->values['search'], + 'status' => $this->values['status'], + 'nodes' => $this->values['nodes'], + ], + 'default_pagination' => $this->values['limit'], + 'order' => [ + 'field' => 'last_status_change', + 'direction' => 'desc', + ], + 'csv' => 0, + ] + ); + } catch (\Exception $e) { + echo $e->getMessage(); + } + } else { + $output = ''; + $output .= '
'; + $output .= \ui_print_info_message( + __('Not found modules'), + '', + true + ); + $output .= '
'; + + return $output; + } + } + + + /** + * Get info modules. + * + * @param string $search Free search. + * @param string $status Modules status. + * + * @return array Data. + */ + private function getInfoModules(string $search, string $status): array + { + if (empty($search) === false) { + $where = 'tagente_modulo.nombre LIKE "%%'.$search.'%%" AND '; + } + + $where .= sprintf( + 'tagente_estado.estado IN (%s) + AND tagente_modulo.delete_pending = 0', + $status + ); + + $sql = sprintf( + 'SELECT + COUNT(*) AS "modules" + FROM tagente_modulo + INNER JOIN tagente + ON tagente_modulo.id_agente = tagente.id_agente + INNER JOIN tagente_estado + ON tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + WHERE %s', + $where + ); + + $modules = db_get_all_rows_sql($sql); + + if ($modules === false) { + $modules = []; + } + + return $modules; + } + + + /** + * Get columns. + * + * @return array + */ + private function columns() + { + $columns = []; + $column_names = []; + + if (is_metaconsole() === true) { + $column_names = [ + __('Module name'), + __('Agent'), + __('Node'), + __('Last status change'), + __('Status'), + ]; + + $columns = [ + 'nombre', + 'alias', + 'server_name', + 'last_status_change', + 'estado', + ]; + } else { + $column_names = [ + __('Module name'), + __('Agent'), + __('Last status change'), + __('Status'), + ]; + + $columns = [ + 'nombre', + 'alias', + 'last_status_change', + 'estado', + ]; + } + + $data = [ + 'columns' => $columns, + 'column_names' => $column_names, + ]; + + return $data; + } + + + /** + * Get description. + * + * @return string. + */ + public static function getDescription() + { + return __('Modules by status'); + } + + + /** + * Get Name. + * + * @return string. + */ + public static function getName() + { + return 'ModulesByStatus'; + } + + + /** + * Get size Modal Configuration. + * + * @return array + */ + public function getSizeModalConfiguration(): array + { + if (is_metaconsole() === true) { + $nodes_fields = array_column(metaconsole_get_servers(), 'id'); + + $height_counter = (((int) count($nodes_fields)) * 20); + + $size = [ + 'width' => 450, + 'height' => (520 + $height_counter), + ]; + } else { + $size = [ + 'width' => 450, + 'height' => 480, + ]; + } + + return $size; + } + + +}