';
-
- if ($default) {
- $output_news .= '
';
- $output_news .= '
';
- $output_news .= '
';
- $output_news .= '
';
-
- $output_news .= '
-
'.__('Welcome to our monitoring tool so grand,').'
- '.__('Where data insights are at your command.').'
- '.__('Sales, marketing, operations too,').'
- '.__("Customer support, we've got you.").'
-
-
-
'.__('Our interface is user-friendly,').'
- '.__("Customize your dashboard, it's easy.").'
- '.__('Set up alerts and gain insights so keen,').'
- '.__("Optimize your data, like you've never seen.").'
-
-
-
'.__('Unleash its power now, and join the pro league,').'
- '.__('Unlock the potential of your data to intrigue.').'
- '.__('Monitoring made simple, efficient and fun,').'
- '.__('Discover a whole new way to get things done.').'
-
-
-
'.__('And take control of your IT once and for all.').'
-
-
'.__('You can replace this message with a personalized one at Admin tools -> Site news.').'
- ';
-
- $output_news .= '
';
- } else {
- $text = str_replace('';
+ return $js;
+ }
+
+
+ /**
+ * Return the welcome message.
+ *
+ * @return string
+ */
+ private function getWelcomeMessage():string
+ {
+ global $config;
+ $user = users_get_user_by_id($config['id_user']);
+ if (is_array($user) === true && count($user) > 0) {
+ $name = $user['fullname'];
+ } else {
+ $name = '';
+ }
+
+ if (empty($name) === true) {
+ $message = __('Welcome back! 👋');
+ } else {
+ $message = __('Welcome back %s! 👋', $name);
+ }
+
+ return html_print_div(
+ [
+ 'content' => $message,
+ 'class' => 'message-welcome',
+ ],
+ true
+ );
+ }
+
+
+}
diff --git a/pandora_console/include/lib/TacticalView/elements/Agents.php b/pandora_console/include/lib/TacticalView/elements/Agents.php
new file mode 100644
index 0000000000..97063ec300
--- /dev/null
+++ b/pandora_console/include/lib/TacticalView/elements/Agents.php
@@ -0,0 +1,446 @@
+title = __('Agents');
+ $this->ajaxMethods = ['getGroups'];
+ }
+
+
+ /**
+ * Get total number of agents.
+ *
+ * @return string
+ */
+ public function getTotalAgents():string
+ {
+ $agents = agents_get_agents();
+ if (is_array($agents) === true) {
+ $total = count($agents);
+ } else {
+ $total = 0;
+ }
+
+ return html_print_div(
+ [
+ 'content' => format_numeric($total, 0),
+ 'class' => 'text-l',
+ 'style' => 'margin: 0px 10px 10px 10px;',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Get total alerts of agents.
+ *
+ * @return string
+ */
+ public function getAlerts():string
+ {
+ global $config;
+ $id_groups = array_keys(users_get_groups($config['id_user'], 'AR', false));
+ if (in_array(0, $id_groups) === false) {
+ foreach ($id_groups as $key => $id_group) {
+ if ((bool) check_acl_restricted_all($config['id_user'], $id_group, 'AR') === false) {
+ unset($id_groups[$key]);
+ }
+ }
+ }
+
+ if (users_can_manage_group_all() === true) {
+ $id_groups[] = 0;
+ }
+
+ $id_groups = implode(',', $id_groups);
+
+ $group_query = ' AND (
+ t3.id_grupo IN ('.$id_groups.')
+ OR tasg.id_group IN ('.$id_groups.')
+ )';
+ $sql = 'SELECT count(t0.id)
+ FROM talert_template_modules t0
+ INNER JOIN talert_templates t1
+ ON t0.id_alert_template = t1.id
+ INNER JOIN tagente_modulo t2
+ ON t0.id_agent_module = t2.id_agente_modulo
+ INNER JOIN tagente t3
+ ON t2.id_agente = t3.id_agente
+ LEFT JOIN tagent_secondary_group tasg
+ ON tasg.id_agent = t3.id_agente
+ WHERE last_fired >=UNIX_TIMESTAMP(NOW() - INTERVAL 1 DAY) '.$group_query;
+
+ $total = db_get_value_sql($sql);
+ return html_print_div(
+ [
+ 'content' => format_numeric($total, 0),
+ 'class' => 'text-l',
+ 'style' => 'margin: 0px 10px 10px 10px;',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Get a datatable with the top groups with more agents.
+ *
+ * @return string
+ */
+ public function getDataTableGroups():string
+ {
+ $columns = [
+ 'nombre',
+ 'total',
+ ];
+
+ $columnNames = [
+ __('Group alias'),
+ __('Agents'),
+ ];
+
+ return ui_print_datatable(
+ [
+ 'id' => 'list_groups',
+ 'class' => 'info_table',
+ 'style' => 'width: 90%',
+ 'dom_elements' => 'tfp',
+ 'filter_main_class' => 'box-flat white_table_graph fixed_filter_bar',
+ 'columns' => $columns,
+ 'column_names' => $columnNames,
+ 'ajax_url' => $this->ajaxController,
+ 'no_sortable_columns' => [
+ 0,
+ 1,
+ ],
+ 'ajax_data' => [
+ 'method' => 'getGroups',
+ 'class' => static::class,
+ ],
+ 'order' => [
+ 'field' => 'title',
+ 'direction' => 'asc',
+ ],
+ 'default_pagination' => 8,
+ 'search_button_class' => 'sub filter float-right',
+ 'return' => true,
+ ]
+ );
+ }
+
+
+ /**
+ * Return top 20 groups with more agents for ajax datatable.
+ *
+ * @return string
+ */
+ public function getGroups():string
+ {
+ global $config;
+
+ $start = get_parameter('start', 0);
+ $length = get_parameter('length', $config['block_size']);
+ $pagination = '';
+
+ $id_groups = array_keys(users_get_groups($config['id_user'], 'AR', false));
+
+ if (in_array(0, $id_groups) === false) {
+ foreach ($id_groups as $key => $id_group) {
+ if ((bool) check_acl_restricted_all($config['id_user'], $id_group, 'AR') === false) {
+ unset($id_groups[$key]);
+ }
+ }
+ }
+
+ $id_groups = implode(',', $id_groups);
+
+ try {
+ ob_start();
+
+ if (isset($length) && $length > 0
+ && isset($start) && $start >= 0
+ ) {
+ $pagination = sprintf(
+ ' LIMIT %d OFFSET %d ',
+ $length,
+ $start
+ );
+ }
+
+ $sql = sprintf(
+ 'SELECT gr.nombre, count(*) +
+ IFNULL((SELECT count(*) AS total
+ FROM tagente second_a
+ LEFT JOIN tagent_secondary_group second_g ON second_g.id_agent = second_a.id_agente
+ WHERE a.id_grupo = second_g.id_group
+ GROUP BY second_g.id_group
+ ), 0) AS total
+ FROM tagente a
+ LEFT JOIN tagent_secondary_group g ON g.id_agent = a.id_agente
+ LEFT JOIN tgrupo gr ON gr.id_grupo = a.id_grupo
+ INNER JOIN(
+ SELECT gr.id_grupo, count(*) AS total
+ FROM tagente a LEFT JOIN tagent_secondary_group g ON g.id_agent = a.id_agente
+ LEFT JOIN tgrupo gr ON gr.id_grupo = a.id_grupo
+ WHERE a.id_grupo IN ('.$id_groups.') OR g.id_group IN ('.$id_groups.')
+ GROUP BY a.id_grupo ORDER BY total DESC LIMIT 20
+ ) top_groups ON top_groups.id_grupo = gr.id_grupo
+ WHERE a.id_grupo IN ('.$id_groups.') OR g.id_group IN ('.$id_groups.')
+ GROUP BY a.id_grupo
+ ORDER BY total DESC
+ %s',
+ $pagination
+ );
+
+ $rows = db_process_sql($sql);
+
+ $sql_count = 'SELECT gr.nombre,
+ IFNULL((SELECT count(*) AS total
+ FROM tagente second_a
+ LEFT JOIN tagent_secondary_group second_g ON second_g.id_agent = second_a.id_agente
+ WHERE a.id_grupo = second_g.id_group
+ GROUP BY second_g.id_group
+ ), 0) AS total
+ FROM tagente a
+ LEFT JOIN tagent_secondary_group g ON g.id_agent = a.id_agente
+ LEFT JOIN tgrupo gr ON gr.id_grupo = a.id_grupo
+ INNER JOIN(
+ SELECT gr.id_grupo, count(*) AS total
+ FROM tagente a LEFT JOIN tagent_secondary_group g ON g.id_agent = a.id_agente
+ LEFT JOIN tgrupo gr ON gr.id_grupo = a.id_grupo
+ WHERE a.id_grupo IN ('.$id_groups.') OR g.id_group IN ('.$id_groups.')
+ GROUP BY a.id_grupo ORDER BY total DESC LIMIT 20
+ ) top_groups ON top_groups.id_grupo = gr.id_grupo
+ WHERE a.id_grupo IN ('.$id_groups.') OR g.id_group IN ('.$id_groups.')
+ GROUP BY a.id_grupo
+ ORDER BY total DESC';
+
+ $total = db_get_num_rows($sql_count);
+
+ // Capture output.
+ $response = ob_get_clean();
+
+ return json_encode(
+ [
+ 'data' => $rows,
+ 'recordsTotal' => $total,
+ 'recordsFiltered' => $total,
+ ]
+ );
+ } catch (Exception $e) {
+ return json_encode(['error' => $e->getMessage()]);
+ }
+
+ return json_decode($response);
+ if (json_last_error() === JSON_ERROR_NONE) {
+ return $response;
+ } else {
+ return json_encode(
+ [
+ 'success' => false,
+ 'error' => $response,
+ ]
+ );
+ }
+ }
+
+
+ /**
+ * Return the html graph of number agents by os.
+ *
+ * @return string
+ */
+ public function getOperatingSystemGraph():string
+ {
+ global $config;
+ $id_groups = array_keys(users_get_groups($config['id_user'], 'AR', false));
+
+ if (in_array(0, $id_groups) === false) {
+ foreach ($id_groups as $key => $id_group) {
+ if ((bool) check_acl_restricted_all($config['id_user'], $id_group, 'AR') === false) {
+ unset($id_groups[$key]);
+ }
+ }
+ }
+
+ $id_groups = implode(',', $id_groups);
+
+ $sql = 'SELECT name, count(*) AS total
+ FROM tagente a
+ LEFT JOIN tagent_secondary_group g ON g.id_agent = a.id_agente
+ LEFT JOIN tgrupo gr ON gr.id_grupo = a.id_grupo
+ LEFT JOIN tconfig_os os ON os.id_os = a.id_os
+ WHERE a.id_grupo IN ('.$id_groups.') OR g.id_group IN ('.$id_groups.')
+ GROUP BY a.id_os
+ ORDER BY total DESC';
+ $rows = db_process_sql($sql);
+
+ $labels = [];
+ $data = [];
+ foreach ($rows as $key => $row) {
+ if (empty($row['name']) === true) {
+ continue;
+ }
+
+ $labels[] = $this->controlSizeText($row['name']);
+ $data[] = $row['total'];
+ }
+
+ $options = [
+ 'labels' => $labels,
+ 'legend' => [
+ 'position' => 'bottom',
+ 'align' => 'right',
+ 'display' => false,
+ ],
+ 'cutout' => 80,
+ 'nodata_image' => ['width' => '100%'],
+ ];
+ $pie = ring_graph($data, $options);
+ $output = html_print_div(
+ [
+ 'content' => $pie,
+ 'style' => 'margin: 0 auto; max-width: 80%; max-height: 220px;',
+ ],
+ true
+ );
+
+ return $output;
+ }
+
+
+ /**
+ * Return the html graph of number agents by status.
+ *
+ * @return string
+ */
+ public function getStatusGraph():string
+ {
+ $agents = agents_get_agents(
+ false,
+ [
+ 'id_agente',
+ 'id_grupo',
+ 'nombre',
+ 'alias',
+ 'id_os',
+ 'ultimo_contacto',
+ 'intervalo',
+ 'comentarios description',
+ 'quiet',
+ 'normal_count',
+ 'warning_count',
+ 'critical_count',
+ 'unknown_count',
+ 'notinit_count',
+ 'total_count',
+ 'fired_count',
+ 'ultimo_contacto_remoto',
+ 'remote',
+ 'agent_version',
+ ]
+ );
+ $labels = [
+ __('No Monitors'),
+ __('CRITICAL'),
+ __('WARNING'),
+ __('UKNOWN'),
+ __('NORMAL'),
+ ];
+ $totals = [
+ 'no_monitors' => 0,
+ 'critical' => 0,
+ 'warning' => 0,
+ 'unknown' => 0,
+ 'ok' => 0,
+ ];
+
+ $colors = [
+ COL_NOTINIT,
+ COL_CRITICAL,
+ COL_WARNING,
+ COL_UNKNOWN,
+ COL_NORMAL,
+ ];
+
+ foreach ($agents as $key => $agent) {
+ if ($agent['total_count'] == 0 || $agent['total_count'] == $agent['notinit_count']) {
+ $totals['no_monitors']++;
+ }
+
+ if ($agent['critical_count'] > 0) {
+ $totals['critical']++;
+ } else if ($agent['warning_count'] > 0) {
+ $totals['warning']++;
+ } else if ($agent['unknown_count'] > 0) {
+ $totals['unknown']++;
+ } else {
+ $totals['ok']++;
+ }
+ }
+
+ foreach ($totals as $key => $total) {
+ $data[] = $total;
+ }
+
+ $options = [
+ 'labels' => $labels,
+ 'legend' => [
+ 'position' => 'bottom',
+ 'align' => 'right',
+ 'display' => false,
+ ],
+ 'cutout' => 80,
+ 'nodata_image' => ['width' => '80%'],
+ 'colors' => $colors,
+ ];
+ $pie = ring_graph($data, $options);
+ $output = html_print_div(
+ [
+ 'content' => $pie,
+ 'style' => 'margin: 0 auto; max-width: 80%; max-height: 220px;',
+ ],
+ true
+ );
+
+ return $output;
+ }
+
+
+}
diff --git a/pandora_console/include/lib/TacticalView/elements/Alerts.php b/pandora_console/include/lib/TacticalView/elements/Alerts.php
new file mode 100644
index 0000000000..1c4388e708
--- /dev/null
+++ b/pandora_console/include/lib/TacticalView/elements/Alerts.php
@@ -0,0 +1,274 @@
+title = __('Alerts');
+ $this->ajaxMethods = ['getUsers'];
+ $this->ajaxMethods = [
+ 'getUsers',
+ 'getCurrentlyTriggered',
+ 'getActiveAlerts',
+ ];
+ $this->interval = 300000;
+ $this->refreshConfig = [
+ 'triggered' => [
+ 'id' => 'currently-triggered',
+ 'method' => 'getCurrentlyTriggered',
+ ],
+ 'active-correlation' => [
+ 'id' => 'active-correlation',
+ 'method' => 'getActiveAlerts',
+ ],
+ ];
+ }
+
+
+ /**
+ * Returns the html of currently triggered.
+ *
+ * @return string
+ */
+ public function getCurrentlyTriggered():string
+ {
+ $total = alerts_get_alerts(0, '', 'fired', -1, 'AR', true);
+ return html_print_div(
+ [
+ 'content' => format_numeric($total, 0),
+ 'class' => 'text-l',
+ 'id' => 'currently-triggered',
+ 'style' => 'margin: 0px 10px 10px 10px;',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Returns the html of active correlation.
+ *
+ * @return string
+ */
+ public function getActiveAlerts():string
+ {
+ $total = alerts_get_alerts(0, '', 'all', -1, 'AR', true);
+ return html_print_div(
+ [
+ 'content' => format_numeric($total, 0),
+ 'class' => 'text-l',
+ 'id' => 'active-correlation',
+ 'style' => 'margin: 0px 10px 10px 10px;',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Return a datatable with de users lists.
+ *
+ * @return string
+ */
+ public function getDataTableUsers():string
+ {
+ $columns = [
+ 'id_user',
+ 'is_admin',
+ 'last_connect',
+ ];
+
+ $columnNames = [
+ __('User'),
+ __('Role'),
+ __('Last seen'),
+ ];
+
+ return ui_print_datatable(
+ [
+ 'id' => 'list_users',
+ 'class' => 'info_table',
+ 'style' => 'width: 90%',
+ 'dom_elements' => 'tfp',
+ 'filter_main_class' => 'box-flat white_table_graph fixed_filter_bar',
+ 'columns' => $columns,
+ 'column_names' => $columnNames,
+ 'ajax_url' => $this->ajaxController,
+ 'ajax_data' => [
+ 'method' => 'getUsers',
+ 'class' => static::class,
+ ],
+ 'order' => [
+ 'field' => 'title',
+ 'direction' => 'asc',
+ ],
+ 'default_pagination' => 10,
+ 'search_button_class' => 'sub filter float-right',
+ 'return' => true,
+ ]
+ );
+ }
+
+
+ /**
+ * Return all users for ajax.
+ *
+ * @return string
+ */
+ public function getUsers():string
+ {
+ global $config;
+
+ $start = get_parameter('start', 0);
+ $length = get_parameter('length', $config['block_size']);
+ $orderDatatable = get_datatable_order(true);
+ $pagination = '';
+ $order = '';
+
+ try {
+ ob_start();
+ if (isset($orderDatatable)) {
+ $order = sprintf(
+ ' ORDER BY %s %s',
+ $orderDatatable['field'],
+ $orderDatatable['direction']
+ );
+ }
+
+ if (isset($length) && $length > 0
+ && isset($start) && $start >= 0
+ ) {
+ $pagination = sprintf(
+ ' LIMIT %d OFFSET %d ',
+ $length,
+ $start
+ );
+ }
+
+ $id_groups = array_keys(users_get_groups($config['id_user'], 'AR', false));
+ if (in_array(0, $id_groups) === false) {
+ foreach ($id_groups as $key => $id_group) {
+ if ((bool) check_acl_restricted_all($config['id_user'], $id_group, 'AR') === false) {
+ unset($id_groups[$key]);
+ }
+ }
+ }
+
+ if (users_can_manage_group_all() === true) {
+ $id_groups[] = 0;
+ }
+
+ $id_groups = implode(',', $id_groups);
+
+ $sql = sprintf(
+ 'SELECT DISTINCT id_user, is_admin ,last_connect
+ FROM tusuario u
+ LEFT JOIN tusuario_perfil p ON p.id_usuario = u.id_user
+ WHERE id_grupo IN ('.$id_groups.')
+ GROUP BY id_user
+ %s %s',
+ $order,
+ $pagination
+ );
+
+ $rows = db_process_sql($sql);
+
+ foreach ($rows as $key => $row) {
+ $rows[$key]['id_user'] = '
'.$row['id_user'].' ';
+ if ((bool) $row['is_admin'] === true) {
+ $rows[$key]['is_admin'] = '
'.__('Admin').' ';
+ } else {
+ $rows[$key]['is_admin'] = '
'.__('User').' ';
+ }
+
+ if ($row['last_connect'] > 0) {
+ $rows[$key]['last_connect'] = ui_print_timestamp($row['last_connect'], true, ['prominent' => 'compact']);
+ } else {
+ $rows[$key]['last_connect'] = __('Unknown');
+ }
+ }
+
+ $sql_count = sprintf(
+ 'SELECT DISTINCT id_user, count(*) as total
+ FROM tusuario u
+ LEFT JOIN tusuario_perfil p ON p.id_usuario = u.id_user
+ WHERE id_grupo IN ('.$id_groups.')
+ %s',
+ $order,
+ );
+
+ $total = db_process_sql($sql_count);
+
+ // Capture output.
+ $response = ob_get_clean();
+
+ return json_encode(
+ [
+ 'data' => $rows,
+ 'recordsTotal' => $total[0]['total'],
+ 'recordsFiltered' => $total[0]['total'],
+ ]
+ );
+ } catch (Exception $e) {
+ return json_encode(['error' => $e->getMessage()]);
+ }
+
+ json_decode($response);
+ if (json_last_error() === JSON_ERROR_NONE) {
+ return $response;
+ } else {
+ return json_encode(
+ [
+ 'success' => false,
+ 'error' => $response,
+ ]
+ );
+ }
+ }
+
+
+ /**
+ * Check if user can manager users.
+ *
+ * @return boolean
+ */
+ public function checkAclUserList():bool
+ {
+ global $config;
+ $user_m = (bool) check_acl($config['id_user'], 0, 'UM');
+ return $user_m;
+ }
+
+
+}
diff --git a/pandora_console/include/lib/TacticalView/elements/Configurations.php b/pandora_console/include/lib/TacticalView/elements/Configurations.php
new file mode 100644
index 0000000000..3fb4f5bec3
--- /dev/null
+++ b/pandora_console/include/lib/TacticalView/elements/Configurations.php
@@ -0,0 +1,252 @@
+title = __('Configurations');
+ }
+
+
+ /**
+ * Get total groups from automonitorization.
+ *
+ * @return string
+ */
+ public function getTotalGroups():string
+ {
+ $value = $this->valueMonitoring('total_groups');
+ $total = round($value[0]['datos']);
+ $image = html_print_image('images/Tactical_Groups.svg', true);
+ $text = '
'.__('Groups').' ';
+ $number = html_print_div(
+ [
+ 'content' => format_numeric($total, 0),
+ 'class' => 'text-l text_center',
+ 'style' => '',
+ ],
+ true
+ );
+ $output = $image.$text.$number;
+ return $output;
+ }
+
+
+ /**
+ * Get total modules from automonitorization.
+ *
+ * @return string
+ */
+ public function getTotalModules():string
+ {
+ $value = $this->valueMonitoring('total_modules');
+ $total = round($value[0]['datos']);
+ $image = html_print_image('images/Tactical_Modules.svg', true);
+ $text = '
'.__('Modules').' ';
+ $number = html_print_div(
+ [
+ 'content' => format_numeric($total, 0),
+ 'class' => 'text-l text_center',
+ 'style' => '',
+ ],
+ true
+ );
+ $output = $image.$text.$number;
+ return $output;
+ }
+
+
+ /**
+ * Get total policies from automonitorization.
+ *
+ * @return string
+ */
+ public function getTotalPolicies():string
+ {
+ $totalPolicies = db_get_value(
+ 'count(*)',
+ 'tpolicies'
+ );
+
+ $image = html_print_image('images/Tactical_Policies.svg', true);
+ $text = '
'.__('Policies').' ';
+ $number = html_print_div(
+ [
+ 'content' => format_numeric($totalPolicies, 0),
+ 'class' => 'text-l text_center',
+ 'style' => '',
+ ],
+ true
+ );
+ $output = $image.$text.$number;
+ return $output;
+ }
+
+
+ /**
+ * Get total remote plugins from automonitorization.
+ *
+ * @return string
+ */
+ public function getTotalRemotePlugins():string
+ {
+ $totalPLugins = db_get_value(
+ 'count(*)',
+ 'tplugin',
+ 'plugin_type',
+ 1,
+ );
+
+ $sql = 'SELECT count(*) AS total FROM tplugin WHERE plugin_type = 1;';
+ $rows = db_process_sql($sql);
+ $totalPLugins = 0;
+ if (is_array($rows) === true && count($rows) > 0) {
+ $totalPLugins = $rows[0]['total'];
+ }
+
+ $image = html_print_image('images/Tactical_Plugins.svg', true);
+ $text = '
'.__('Remote plugins').' ';
+ $number = html_print_div(
+ [
+ 'content' => format_numeric($totalPLugins, 0),
+ 'class' => 'text-l text_center',
+ 'style' => '',
+ ],
+ true
+ );
+ $output = $image.$text.$number;
+ return $output;
+ }
+
+
+ /**
+ * Get total module templates from automonitorization.
+ *
+ * @return string
+ */
+ public function getTotalModuleTemplate():string
+ {
+ $countModuleTemplates = db_get_value(
+ 'count(*)',
+ 'tnetwork_profile'
+ );
+
+ $image = html_print_image('images/Tactical_Module_template.svg', true);
+ $text = '
'.__('Module templates').' ';
+ $number = html_print_div(
+ [
+ 'content' => format_numeric($countModuleTemplates, 0),
+ 'class' => 'text-l text_center',
+ 'style' => '',
+ ],
+ true
+ );
+ $output = $image.$text.$number;
+ return $output;
+ }
+
+
+ /**
+ * Get total not unit modules from automonitorization.
+ *
+ * @return string
+ */
+ public function getNotInitModules():string
+ {
+ $value = $this->valueMonitoring('total_notinit');
+ $total = round($value[0]['datos']);
+ $image = html_print_image('images/Tactical_Not_init_module.svg', true);
+ $text = '
'.__('Not-init modules').' ';
+ $number = html_print_div(
+ [
+ 'content' => format_numeric($total, 0),
+ 'class' => 'text-l text_center',
+ 'style' => '',
+ ],
+ true
+ );
+ $output = $image.$text.$number;
+ return $output;
+ }
+
+
+ /**
+ * Get total unknow agents from automonitorization.
+ *
+ * @return string
+ */
+ public function getTotalUnknowAgents():string
+ {
+ $value = $this->valueMonitoring('total_unknown');
+ $total = round($value[0]['datos']);
+ $image = html_print_image('images/Tactical_Unknown_agent.svg', true);
+ $text = '
'.__('Unknown agents').' ';
+ $number = html_print_div(
+ [
+ 'content' => format_numeric($total, 0),
+ 'class' => 'text-l text_center',
+ 'style' => '',
+ ],
+ true
+ );
+ $output = $image.$text.$number;
+ return $output;
+ }
+
+
+ /**
+ * Returns the html of total events.
+ *
+ * @return string
+ */
+ public function getTotalEvents():string
+ {
+ $data = $this->valueMonitoring('last_events_24h');
+ $total = $data[0]['datos'];
+ $image = html_print_image('images/system_event.svg', true);
+ $text = '
'.__('Events in last 24 hrs').' ';
+ $number = html_print_div(
+ [
+ 'content' => format_numeric($total, 0),
+ 'class' => 'text-l text_center',
+ 'style' => '',
+ ],
+ true
+ );
+ $output = $image.$text.$number;
+ return $output;
+ }
+
+
+}
diff --git a/pandora_console/include/lib/TacticalView/elements/Database.php b/pandora_console/include/lib/TacticalView/elements/Database.php
new file mode 100644
index 0000000000..21f8053163
--- /dev/null
+++ b/pandora_console/include/lib/TacticalView/elements/Database.php
@@ -0,0 +1,348 @@
+title = __('Database');
+ $this->ajaxMethods = [
+ 'getStatus',
+ 'getDataRecords',
+ 'getEvents',
+ 'getStringRecords',
+ 'getReadsGraph',
+ 'getWritesGraph',
+ ];
+ $this->interval = 300000;
+ $this->refreshConfig = [
+ 'status' => [
+ 'id' => 'status-database',
+ 'method' => 'getStatus',
+ ],
+ 'records' => [
+ 'id' => 'data-records',
+ 'method' => 'getDataRecords',
+ ],
+ 'events' => [
+ 'id' => 'total-events',
+ 'method' => 'getEvents',
+ ],
+ 'totalRecords' => [
+ 'id' => 'total-records',
+ 'method' => 'getStringRecords',
+
+ ],
+ 'reads' => [
+ 'id' => 'database-reads',
+ 'method' => 'getReadsGraph',
+ ],
+ 'writes' => [
+ 'id' => 'database-writes',
+ 'method' => 'getWritesGraph',
+ ],
+ ];
+ }
+
+
+ /**
+ * Returns the html status of database.
+ *
+ * @return string
+ */
+ public function getStatus():string
+ {
+ // TODO connect to automonitorization.
+ $status = true;
+
+ if ($status === true) {
+ $image_status = html_print_image('images/status_check@svg.svg', true);
+ $text = html_print_div(
+ [
+ 'content' => __('Everything’s OK!'),
+ 'class' => 'status-text',
+ ],
+ true
+ );
+ } else {
+ $image_status = html_print_image('images/status_error@svg.svg', true);
+ $text = html_print_div(
+ [
+ 'content' => __('Something’s wrong'),
+ 'class' => 'status-text',
+ ],
+ true
+ );
+ }
+
+ $output = $image_status.$text;
+
+ return html_print_div(
+ [
+ 'content' => $output,
+ 'class' => 'flex_center margin-top-5',
+ 'id' => 'status-database',
+ 'style' => 'margin: 0px 10px 10px 10px;',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Returns the html records data of database.
+ *
+ * @return string
+ */
+ public function getDataRecords():string
+ {
+ $data = $this->valueMonitoring('mysql_size_of_data');
+ $value = format_numeric($data[0]['datos'], 2).' MB';
+ return html_print_div(
+ [
+ 'content' => $value,
+ 'class' => 'text-l',
+ 'id' => 'data-records',
+ 'style' => 'margin: 0px 10px 10px 10px;',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Returns the html of total events.
+ *
+ * @return string
+ */
+ public function getEvents():string
+ {
+ $data = $this->valueMonitoring('last_events_24h');
+ $value = format_numeric($data[0]['datos']);
+ return html_print_div(
+ [
+ 'content' => $value,
+ 'class' => 'text-l',
+ 'id' => 'total-events',
+ 'style' => 'margin: 0px 10px 10px 10px;',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Returns the html of total records.
+ *
+ * @return string
+ */
+ public function getStringRecords():string
+ {
+ $data = $this->valueMonitoring('total_string_data');
+ $value = format_numeric($data[0]['datos']);
+ return html_print_div(
+ [
+ 'content' => $value,
+ 'class' => 'text-l',
+ 'id' => 'total-records',
+ 'style' => 'margin: 0px 10px 10px 10px;',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Returns the html of total reads database in a graph.
+ *
+ * @return string
+ */
+ public function getReadsGraph():string
+ {
+ $dateInit = (time() - 86400);
+ $reads = $this->valueMonitoring('mysql_questions_reads', $dateInit, time());
+ $dates = [];
+ $string_reads = [];
+ $total = 0;
+ foreach ($reads as $key => $read) {
+ $dates[] = date('d-m-Y H:i:s', $read['utimestamp']);
+ $string_reads[] = $read['datos'];
+ $total += $read['datos'];
+ }
+
+ $options = [
+ 'labels' => $dates,
+ 'legend' => [ 'display' => false ],
+ 'tooltips' => [ 'display' => false ],
+ 'scales' => [
+ 'y' => [
+ 'grid' => ['display' => false],
+ 'ticks' => ['display' => false],
+ 'display' => false,
+ ],
+ 'x' => [
+ 'grid' => ['display' => false],
+ 'display' => false,
+ ],
+ ],
+ 'elements' => [ 'point' => [ 'radius' => 0 ] ],
+ ];
+
+ $data = [
+ [
+ 'backgroundColor' => '#EC7176',
+ 'borderColor' => '#EC7176',
+ 'pointBackgroundColor' => '#EC7176',
+ 'pointHoverBorderColor' => '#EC7176',
+ 'data' => $string_reads,
+ ],
+ ];
+
+ $graph_area = html_print_div(
+ [
+ 'content' => line_graph($data, $options),
+ 'class' => 'w100p h100p centered',
+ 'style' => 'max-height: 83px; max-width: 93%; margin-bottom: 10px;',
+ ],
+ true
+ );
+
+ $total = html_print_div(
+ [
+ 'content' => format_numeric($total),
+ 'class' => 'text-xl',
+ ],
+ true
+ );
+
+ $output = html_print_div(
+ [
+ 'content' => $total.$graph_area,
+ 'id' => 'database-reads',
+ ],
+ true
+ );
+
+ return $output;
+ }
+
+
+ /**
+ * Returns the html of total writes database in a graph.
+ *
+ * @return string
+ */
+ public function getWritesGraph():string
+ {
+ $dateInit = (time() - 86400);
+ $writes = $this->valueMonitoring('mysql_questions_writes', $dateInit, time());
+ $dates = [];
+ $string_writes = [];
+ $total = 0;
+ foreach ($writes as $key => $write) {
+ $dates[] = date('d-m-Y H:i:s', $write['utimestamp']);
+ $string_writes[] = $write['datos'];
+ $total += $write['datos'];
+ }
+
+ $options = [
+ 'labels' => $dates,
+ 'legend' => [ 'display' => false ],
+ 'tooltips' => [ 'display' => false ],
+ 'scales' => [
+ 'y' => [
+ 'grid' => ['display' => false],
+ 'ticks' => ['display' => false],
+ 'display' => false,
+ ],
+ 'x' => [
+ 'grid' => ['display' => false],
+ 'display' => false,
+ ],
+ ],
+ 'elements' => [ 'point' => [ 'radius' => 0 ] ],
+ ];
+
+ $data = [
+ [
+ 'backgroundColor' => '#009D9E',
+ 'borderColor' => '#009D9E',
+ 'pointBackgroundColor' => '#009D9E',
+ 'pointHoverBorderColor' => '#009D9E',
+ 'data' => $string_writes,
+ ],
+ ];
+
+ $graph_area = html_print_div(
+ [
+ 'content' => line_graph($data, $options),
+ 'class' => 'w100p h100p centered',
+ 'style' => 'max-height: 83px; max-width: 93%; margin-bottom: 10px;',
+ ],
+ true
+ );
+
+ $total = html_print_div(
+ [
+ 'content' => format_numeric($total),
+ 'class' => 'text-xl',
+ ],
+ true
+ );
+
+ $output = html_print_div(
+ [
+ 'content' => $total.$graph_area,
+ 'id' => 'database-writes',
+ ],
+ true
+ );
+
+ return $output;
+ }
+
+
+ /**
+ * Check if user can manage database
+ *
+ * @return boolean
+ */
+ public function checkAcl():bool
+ {
+ global $config;
+ $db_m = (bool) check_acl($config['id_user'], 0, 'DM');
+ return $db_m;
+ }
+
+
+}
diff --git a/pandora_console/include/lib/TacticalView/elements/Events.php b/pandora_console/include/lib/TacticalView/elements/Events.php
new file mode 100644
index 0000000000..a7f9970ee3
--- /dev/null
+++ b/pandora_console/include/lib/TacticalView/elements/Events.php
@@ -0,0 +1,485 @@
+title = __('Events');
+ $this->ajaxMethods = [
+ 'getEventsGraph',
+ 'getEventsCriticalityGraph',
+ 'getEventsStatusValidateGraph',
+ 'getEventsStatusGraph',
+ ];
+ }
+
+
+ /**
+ * Return the html graph of events in last 24h.
+ *
+ * @return string
+ */
+ public function getEventsGraph():string
+ {
+ global $config;
+ $id_groups = array_keys(users_get_groups($config['id_user'], 'AR', false));
+ if (in_array(0, $id_groups) === false) {
+ foreach ($id_groups as $key => $id_group) {
+ if ((bool) check_acl_restricted_all($config['id_user'], $id_group, 'AR') === false) {
+ unset($id_groups[$key]);
+ }
+ }
+ }
+
+ if (users_can_manage_group_all() === true) {
+ $id_groups[] = 0;
+ }
+
+ $id_groups = implode(',', $id_groups);
+ $event_view_h = (int) ($config['event_view_hr'] > 24) ? 24 : $config['event_view_hr'];
+ $time_events = ($event_view_h * 3600);
+ $intervalh = (time() - $time_events);
+ $sql = 'SELECT utimestamp from tevento WHERE utimestamp >= '.$intervalh.' ORDER BY utimestamp DESC;';
+ $rows = db_process_sql($sql);
+ $cut_seconds = ($time_events / 24);
+ $now = (time() - 300);
+ $cuts_intervals = [];
+ for ($i = 0; $i < 24; $i++) {
+ $cuts_intervals[$now] = 0;
+ $now -= $cut_seconds;
+ }
+
+ foreach ($rows as $key => $row) {
+ foreach ($cuts_intervals as $time => $count) {
+ if ($row['utimestamp'] > $time) {
+ $cuts_intervals[$time]++;
+ break;
+ }
+ }
+ }
+
+ $cuts_intervals = array_reverse($cuts_intervals, true);
+ $graph_values = [];
+ $colors = [];
+ $max_value = 0;
+ foreach ($cuts_intervals as $utimestamp => $count) {
+ if ($max_value < $count) {
+ $max_value = $count;
+ }
+
+ $graph_values[] = [
+ 'y' => $count,
+ 'x' => date('d-m-Y H:i:s', $utimestamp),
+ ];
+ }
+
+ $danger = $max_value;
+ $ok = ($max_value / 3);
+
+ foreach ($graph_values as $key => $value) {
+ if ($value['y'] >= $danger) {
+ $colors[] = '#EC7176';
+ }
+
+ if ($value['y'] >= $ok && $value['y'] < $danger) {
+ $colors[] = '#FCAB10';
+ }
+
+ if ($value['y'] < $ok) {
+ $colors[] = '#82B92E';
+ }
+ }
+
+ $options = [
+ 'height' => 237,
+ 'legend' => ['display' => false],
+ 'scales' => [
+ 'x' => [
+ 'bounds' => 'data',
+ 'grid' => ['display' => false],
+ 'display' => false,
+ ],
+ 'y' => [
+ 'grid' => ['display' => false],
+ ],
+ ],
+ 'colors' => $colors,
+ 'borderColors' => ['#ffffff'],
+ ];
+
+ $bar = vbar_graph($graph_values, $options);
+
+ $output = html_print_div(
+ [
+ 'content' => $bar,
+ 'class' => 'margin-top-5 w100p relative',
+ 'style' => 'max-height: 250px;',
+ ],
+ true
+ );
+
+ return $output;
+ }
+
+
+ /**
+ * Return the html graph of events in last 8h grouped by criticity.
+ *
+ * @return string
+ */
+ public function getEventsCriticalityGraph():string
+ {
+ global $config;
+ $id_groups = array_keys(users_get_groups($config['id_user'], 'AR', false));
+ if (in_array(0, $id_groups) === false) {
+ foreach ($id_groups as $key => $id_group) {
+ if ((bool) check_acl_restricted_all($config['id_user'], $id_group, 'AR') === false) {
+ unset($id_groups[$key]);
+ }
+ }
+ }
+
+ if (users_can_manage_group_all() === true) {
+ $id_groups[] = 0;
+ }
+
+ $id_groups = implode(',', $id_groups);
+ $event_view_h = (int) ($config['event_view_hr'] > 24) ? 24 : $config['event_view_hr'];
+ $time_events = ($event_view_h * 3600);
+ $intervalh = (time() - $time_events);
+ $sql = 'SELECT criticity, count(*) AS total
+ FROM tevento
+ WHERE utimestamp >= '.$intervalh.' AND id_grupo IN ('.$id_groups.')
+ group by criticity';
+
+ $rows = db_process_sql($sql);
+
+ $labels = [];
+ $data = [];
+ $colors = [];
+ foreach ($rows as $key => $row) {
+ switch ($row['criticity']) {
+ case EVENT_CRIT_CRITICAL:
+ $label = __('CRITICAL');
+ $colors[] = COL_CRITICAL;
+ break;
+
+ case EVENT_CRIT_MAINTENANCE:
+ $label = __('MAINTENANCE');
+ $colors[] = COL_MAINTENANCE;
+ break;
+
+ case EVENT_CRIT_INFORMATIONAL:
+ $label = __('INFORMATIONAL');
+ $colors[] = COL_INFORMATIONAL;
+ break;
+
+ case EVENT_CRIT_MAJOR:
+ $label = __('MAJOR');
+ $colors[] = COL_MAJOR;
+ break;
+
+ case EVENT_CRIT_MINOR:
+ $label = __('MINOR');
+ $colors[] = COL_MINOR;
+ break;
+
+ case EVENT_CRIT_NORMAL:
+ $label = __('NORMAL');
+ $colors[] = COL_NORMAL;
+ break;
+
+ case EVENT_CRIT_WARNING:
+ $label = __('WARNING');
+ $colors[] = COL_WARNING;
+ break;
+
+ default:
+ $colors[] = COL_UNKNOWN;
+ $label = __('UNKNOWN');
+ break;
+ }
+
+ $labels[] = $this->controlSizeText($label);
+ $data[] = $row['total'];
+ }
+
+ $options = [
+ 'waterMark' => false,
+ 'labels' => $labels,
+ 'legend' => ['display' => false],
+ 'cutout' => 80,
+ 'nodata_image' => ['width' => '100%'],
+ 'colors' => $colors,
+ ];
+ $pie = ring_graph($data, $options);
+ $output = html_print_div(
+ [
+ 'content' => $pie,
+ 'style' => 'margin: 0 auto; max-width: 80%; max-height: 220px;',
+ ],
+ true
+ );
+
+ return $output;
+ }
+
+
+ /**
+ * Return the html graph of events in last 8h grouped by status validate.
+ *
+ * @return string
+ */
+ public function getEventsStatusValidateGraph():string
+ {
+ global $config;
+ $id_groups = array_keys(users_get_groups($config['id_user'], 'AR', false));
+ if (in_array(0, $id_groups) === false) {
+ foreach ($id_groups as $key => $id_group) {
+ if ((bool) check_acl_restricted_all($config['id_user'], $id_group, 'AR') === false) {
+ unset($id_groups[$key]);
+ }
+ }
+ }
+
+ if (users_can_manage_group_all() === true) {
+ $id_groups[] = 0;
+ }
+
+ $id_groups = implode(',', $id_groups);
+ $event_view_h = (int) ($config['event_view_hr'] > 24) ? 24 : $config['event_view_hr'];
+ $time_events = ($event_view_h * 3600);
+ $intervalh = (time() - $time_events);
+ $sql = 'SELECT estado, count(*) AS total
+ FROM tevento
+ WHERE utimestamp >= '.$intervalh.' AND id_grupo IN ('.$id_groups.')
+ group by estado';
+
+ $rows = db_process_sql($sql);
+
+ $labels = [];
+ $data = [];
+ foreach ($rows as $key => $row) {
+ switch ($row['estado']) {
+ case '2':
+ $label = _('In process');
+ break;
+
+ case '0':
+ $label = _('New events');
+ break;
+
+ case '3':
+ $label = _('Not validated');
+ break;
+
+ case '1':
+ $label = _('Validated events');
+ break;
+
+ default:
+ $label = __('Unknow');
+ break;
+ }
+
+ $labels[] = $label;
+ $data[] = $row['total'];
+ }
+
+ $options = [
+ 'waterMark' => false,
+ 'labels' => $labels,
+ 'legend' => ['display' => false],
+ 'cutout' => 80,
+ 'nodata_image' => ['width' => '100%'],
+ ];
+
+ $pie = ring_graph($data, $options);
+ $output = html_print_div(
+ [
+ 'content' => $pie,
+ 'style' => 'margin: 0 auto; max-width: 80%; max-height: 220px;',
+ ],
+ true
+ );
+
+ return $output;
+ }
+
+
+ /**
+ * Return the html graph of events in last 8h grouped by status.
+ *
+ * @return string
+ */
+ public function getEventsStatusGraph():string
+ {
+ global $config;
+ $id_groups = array_keys(users_get_groups($config['id_user'], 'AR', false));
+ if (in_array(0, $id_groups) === false) {
+ foreach ($id_groups as $key => $id_group) {
+ if ((bool) check_acl_restricted_all($config['id_user'], $id_group, 'AR') === false) {
+ unset($id_groups[$key]);
+ }
+ }
+ }
+
+ if (users_can_manage_group_all() === true) {
+ $id_groups[] = 0;
+ }
+
+ $id_groups = implode(',', $id_groups);
+ $event_view_h = (int) ($config['event_view_hr'] > 24) ? 24 : $config['event_view_hr'];
+ $time_events = ($event_view_h * 3600);
+ $intervalh = (time() - $time_events);
+ $sql = 'SELECT criticity, count(*) AS total
+ FROM tevento
+ WHERE utimestamp >= '.$intervalh.' AND id_grupo IN ('.$id_groups.')
+ group by criticity';
+
+ $rows = db_process_sql($sql);
+
+ $labels = [];
+ $data = [];
+ $colors = [];
+ foreach ($rows as $key => $row) {
+ switch ($row['criticity']) {
+ case EVENT_CRIT_CRITICAL:
+ $label = __('CRITICAL');
+ $colors[] = COL_CRITICAL;
+ break;
+
+ case EVENT_CRIT_NORMAL:
+ $label = __('NORMAL');
+ $colors[] = COL_NORMAL;
+ break;
+
+ case EVENT_CRIT_WARNING:
+ $label = __('WARNING');
+ $colors[] = COL_WARNING;
+ break;
+
+ default:
+ $colors[] = COL_UNKNOWN;
+ $label = __('UNKNOWN');
+ break;
+ }
+
+ $labels[] = $this->controlSizeText($label);
+ $data[] = $row['total'];
+ }
+
+ $options = [
+ 'labels' => $labels,
+ 'legend' => ['display' => false],
+ 'cutout' => 80,
+ 'nodata_image' => ['width' => '100%'],
+ 'colors' => $colors,
+ ];
+ $pie = ring_graph($data, $options);
+ $output = html_print_div(
+ [
+ 'content' => $pie,
+ 'style' => 'margin: 0 auto; max-width: 80%; max-height: 220px;',
+ ],
+ true
+ );
+
+ return $output;
+ }
+
+
+ /**
+ * Return the datatable events in last 8 hours.
+ *
+ * @return string
+ */
+ public function getDataTableEvents()
+ {
+ $column_names = [
+ __('S'),
+ __('Event'),
+ __('Date'),
+ ];
+
+ $fields = [
+ 'mini_severity',
+ 'evento',
+ 'timestamp',
+ ];
+ return ui_print_datatable(
+ [
+ 'id' => 'datatable_events',
+ 'class' => 'info_table events',
+ 'style' => 'width: 90%;',
+ 'ajax_url' => 'operation/events/events',
+ 'ajax_data' => [
+ 'get_events' => 1,
+ 'compact_date' => 1,
+ 'external_url' => 1,
+ ],
+ 'order' => [
+ 'field' => 'timestamp',
+ 'direction' => 'desc',
+ ],
+ 'column_names' => $column_names,
+ 'columns' => $fields,
+ 'ajax_return_operation' => 'buffers',
+ 'ajax_return_operation_function' => 'process_buffers',
+ 'return' => true,
+ 'csv' => 0,
+ 'dom_elements' => 'tfp',
+ 'default_pagination' => 8,
+ ]
+ );
+ }
+
+
+ /**
+ * Check permission user for view events section.
+ *
+ * @return boolean
+ */
+ public function checkAcl():bool
+ {
+ global $config;
+ $event_a = (bool) check_acl($config['id_user'], 0, 'ER');
+ return $event_a;
+ }
+
+
+}
diff --git a/pandora_console/include/lib/TacticalView/elements/Groups.php b/pandora_console/include/lib/TacticalView/elements/Groups.php
new file mode 100644
index 0000000000..82b0f358b1
--- /dev/null
+++ b/pandora_console/include/lib/TacticalView/elements/Groups.php
@@ -0,0 +1,563 @@
+ajaxMethods = ['getStatusHeatMap'];
+ ui_require_css_file('heatmap');
+ $this->title = __('Groups');
+ $this->total = $this->calculateTotalGroups();
+ }
+
+
+ /**
+ * Return the total groups.
+ *
+ * @return integer
+ */
+ public function calculateTotalGroups():int
+ {
+ $total = db_get_value_sql('SELECT count(*) FROM tgrupo');
+ return $total;
+ }
+
+
+ /**
+ * Return the status groups in heat map.
+ *
+ * @return string
+ */
+ public function getStatusHeatMap():string
+ {
+ global $config;
+
+ $groups = users_get_groups($config['id_group'], 'AR', false);
+ if (is_array($groups) === true && count($groups) >= 10) {
+ return $this->getStatusHeatMapGroup();
+ }
+
+ $agents = agents_get_agents();
+ if (is_array($agents) === true && count($agents) >= 10) {
+ $this->title = __('My monitored agents');
+ return $this->getStatusHeatMapAgents().'
'.$this->title.' ';
+ }
+
+ $this->title = __('My monitored modules');
+ return $this->getStatusHeatMapModules().'
'.$this->title.' ';
+ }
+
+
+ /**
+ * Return the status modules in heatmap.
+ *
+ * @return string
+ */
+ public function getStatusHeatMapModules():string
+ {
+ global $config;
+ $width = get_parameter('width', 350);
+ $height = get_parameter('height', 275);
+
+ $id_groups = array_keys(users_get_groups($config['id_user'], 'AR', false));
+
+ if (in_array(0, $id_groups) === false) {
+ foreach ($id_groups as $key => $id_group) {
+ if ((bool) check_acl_restricted_all($config['id_user'], $id_group, 'AR') === false) {
+ unset($id_groups[$key]);
+ }
+ }
+ }
+
+ $id_groups = implode(',', $id_groups);
+
+ $modules = modules_get_modules_in_group($id_groups);
+ $total_groups = count($modules);
+ if ($total_groups === 0) {
+ return graph_nodata_image(['width' => '400']);
+ }
+
+ $groups = $modules;
+ // Best square.
+ $high = (float) max($width, $height);
+ $low = 0.0;
+
+ while (abs($high - $low) > 0.000001) {
+ $mid = (($high + $low) / 2.0);
+ $midval = (floor($width / $mid) * floor($height / $mid));
+ if ($midval >= $total_groups) {
+ $low = $mid;
+ } else {
+ $high = $mid;
+ }
+ }
+
+ $square_length = min(($width / floor($width / $low)), ($height / floor($height / $low)));
+ // Print starmap.
+ $heatmap = sprintf(
+ '
',
+ $width,
+ $height
+ );
+
+ $heatmap .= '';
+ $row = 0;
+ $column = 0;
+ $x = 0;
+ $y = 0;
+ $cont = 1;
+ foreach ($groups as $key => $value) {
+ $module_id = $value['id_agente_modulo'];
+ $db_status = modules_get_agentmodule_status($module_id);
+ $module_value = modules_get_last_value($module_id);
+ $status = '';
+ $title = '';
+ modules_get_status($module_id, $db_status, $module_value, $status, $title);
+ switch ($status) {
+ case STATUS_MODULE_NO_DATA:
+ // Not init status.
+ $status = 'notinit';
+ break;
+
+ case STATUS_MODULE_CRITICAL:
+ // Critical status.
+ $status = 'critical';
+ break;
+
+ case STATUS_MODULE_WARNING:
+ // Warning status.
+ $status = 'warning';
+ break;
+
+ case STATUS_MODULE_OK:
+ // Normal status.
+ $status = 'normal';
+ break;
+
+ case 3:
+ case -1:
+ default:
+ // Unknown status.
+ $status = 'unknown';
+ break;
+ }
+
+ $heatmap .= sprintf(
+ ' ',
+ 'rect_'.$cont,
+ $x,
+ $y,
+ $row,
+ $column,
+ $square_length,
+ $square_length,
+ $status,
+ random_int(1, 10)
+ );
+
+ $y += $square_length;
+ $row++;
+ if ((int) ($y + $square_length) > (int) $height) {
+ $y = 0;
+ $x += $square_length;
+ $row = 0;
+ $column++;
+ }
+
+ if ((int) ($x + $square_length) > (int) $width) {
+ $x = 0;
+ $y += $square_length;
+ $column = 0;
+ $row++;
+ }
+
+ $cont++;
+ }
+
+ $heatmap .= '';
+ $heatmap .= ' ';
+ $heatmap .= ' ';
+
+ return html_print_div(
+ [
+ 'content' => $heatmap,
+ 'style' => 'margin: 0 auto; width: fit-content; min-height: 285px;',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Return the status agents in heat map.
+ *
+ * @return string
+ */
+ public function getStatusHeatMapAgents():string
+ {
+ global $config;
+ $width = get_parameter('width', 350);
+ $height = get_parameter('height', 275);
+
+ $id_groups = array_keys(users_get_groups($config['id_user'], 'AR', false));
+
+ if (in_array(0, $id_groups) === false) {
+ foreach ($id_groups as $key => $id_group) {
+ if ((bool) check_acl_restricted_all($config['id_user'], $id_group, 'AR') === false) {
+ unset($id_groups[$key]);
+ }
+ }
+ }
+
+ $id_groups = implode(',', $id_groups);
+
+ $sql = 'SELECT * FROM tagente a
+ LEFT JOIN tagent_secondary_group g ON g.id_agent = a.id_agente
+ WHERE g.id_group IN ('.$id_groups.') OR a.id_grupo IN ('.$id_groups.')';
+ $all_agents = db_get_all_rows_sql($sql);
+ if (empty($all_agents)) {
+ return null;
+ }
+
+ $total_agents = count($all_agents);
+
+ // Best square.
+ $high = (float) max($width, $height);
+ $low = 0.0;
+
+ while (abs($high - $low) > 0.000001) {
+ $mid = (($high + $low) / 2.0);
+ $midval = (floor($width / $mid) * floor($height / $mid));
+ if ($midval >= $total_agents) {
+ $low = $mid;
+ } else {
+ $high = $mid;
+ }
+ }
+
+ $square_length = min(($width / floor($width / $low)), ($height / floor($height / $low)));
+ // Print starmap.
+ $heatmap = sprintf(
+ '
',
+ $width,
+ $height
+ );
+
+ $heatmap .= '';
+ $row = 0;
+ $column = 0;
+ $x = 0;
+ $y = 0;
+ $cont = 1;
+
+ foreach ($all_agents as $key => $value) {
+ // Colour by status.
+ $status = agents_get_status_from_counts($value);
+
+ switch ($status) {
+ case 5:
+ // Not init status.
+ $status = 'notinit';
+ break;
+
+ case 1:
+ // Critical status.
+ $status = 'critical';
+ break;
+
+ case 2:
+ // Warning status.
+ $status = 'warning';
+ break;
+
+ case 0:
+ // Normal status.
+ $status = 'normal';
+ break;
+
+ case 3:
+ case -1:
+ default:
+ // Unknown status.
+ $status = 'unknown';
+ break;
+ }
+
+ $heatmap .= sprintf(
+ ' ',
+ 'rect_'.$cont,
+ $x,
+ $y,
+ $row,
+ $column,
+ $square_length,
+ $square_length,
+ $status,
+ random_int(1, 10)
+ );
+
+ $y += $square_length;
+ $row++;
+ if ((int) ($y + $square_length) > (int) $height) {
+ $y = 0;
+ $x += $square_length;
+ $row = 0;
+ $column++;
+ }
+
+ if ((int) ($x + $square_length) > (int) $width) {
+ $x = 0;
+ $y += $square_length;
+ $column = 0;
+ $row++;
+ }
+
+ $cont++;
+ }
+
+ $heatmap .= '';
+ $heatmap .= ' ';
+ $heatmap .= ' ';
+
+ return html_print_div(
+ [
+ 'content' => $heatmap,
+ 'style' => 'margin: 0 auto; width: fit-content; min-height: 285px;',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Return the status groups in heat map.
+ *
+ * @return string
+ */
+ public function getStatusHeatMapGroup():string
+ {
+ global $config;
+ $width = get_parameter('width', 350);
+ $height = get_parameter('height', 275);
+
+ // ACL Check.
+ $agent_a = check_acl($config['id_user'], 0, 'AR');
+ $agent_w = check_acl($config['id_user'], 0, 'AW');
+
+ $groups_list = groupview_get_groups_list(
+ $config['id_user'],
+ ($agent_a == true) ? 'AR' : (($agent_w == true) ? 'AW' : 'AR'),
+ true
+ );
+
+ $total_groups = $groups_list['counter'];
+ $groups = $groups_list['groups'];
+ // Best square.
+ $high = (float) max($width, $height);
+ $low = 0.0;
+
+ while (abs($high - $low) > 0.000001) {
+ $mid = (($high + $low) / 2.0);
+ $midval = (floor($width / $mid) * floor($height / $mid));
+ if ($midval >= $total_groups) {
+ $low = $mid;
+ } else {
+ $high = $mid;
+ }
+ }
+
+ $square_length = min(($width / floor($width / $low)), ($height / floor($height / $low)));
+ // Print starmap.
+ $heatmap = sprintf(
+ '
',
+ $width,
+ $height
+ );
+
+ $heatmap .= '';
+ $row = 0;
+ $column = 0;
+ $x = 0;
+ $y = 0;
+ $cont = 1;
+ foreach ($groups as $key => $value) {
+ if ($value['_name_'] === 'All') {
+ continue;
+ }
+
+ if ($value['_monitors_critical_'] > 0) {
+ $status = 'critical';
+ } else if ($value['_monitors_warning_'] > 0) {
+ $status = 'warning';
+ } else if (($value['_monitors_unknown_'] > 0) || ($value['_agents_unknown_'] > 0)) {
+ $status = 'unknown';
+ } else if ($value['_monitors_ok_'] > 0) {
+ $status = 'normal';
+ } else {
+ $status = 'unknown';
+ }
+
+ $heatmap .= sprintf(
+ ' ',
+ 'rect_'.$cont,
+ $x,
+ $y,
+ $row,
+ $column,
+ $square_length,
+ $square_length,
+ $status,
+ random_int(1, 10)
+ );
+
+ $y += $square_length;
+ $row++;
+ if ((int) ($y + $square_length) > (int) $height) {
+ $y = 0;
+ $x += $square_length;
+ $row = 0;
+ $column++;
+ }
+
+ if ((int) ($x + $square_length) > (int) $width) {
+ $x = 0;
+ $y += $square_length;
+ $column = 0;
+ $row++;
+ }
+
+ $cont++;
+ }
+
+ $heatmap .= '';
+ $heatmap .= ' ';
+ $heatmap .= ' ';
+
+ return html_print_div(
+ [
+ 'content' => $heatmap,
+ 'style' => 'margin: 0 auto; width: fit-content; min-height: 285px;',
+ ],
+ true
+ );
+ }
+
+
+}
diff --git a/pandora_console/include/lib/TacticalView/elements/LogStorage.php b/pandora_console/include/lib/TacticalView/elements/LogStorage.php
new file mode 100644
index 0000000000..427b3c93c6
--- /dev/null
+++ b/pandora_console/include/lib/TacticalView/elements/LogStorage.php
@@ -0,0 +1,219 @@
+title = __('Log storage');
+ $this->ajaxMethods = [
+ 'getStatus',
+ 'getTotalSources',
+ 'getStoredData',
+ 'getAgeOfStoredData',
+ ];
+ $this->interval = 300000;
+ $this->refreshConfig = [
+ 'status' => [
+ 'id' => 'status-log-storage',
+ 'method' => 'getStatus',
+ ],
+ 'total-source' => [
+ 'id' => 'total-source-log-storage',
+ 'method' => 'getTotalSources',
+ ],
+ 'total-lines' => [
+ 'id' => 'total-lines-log-storage',
+ 'method' => 'getStoredData',
+ ],
+ 'age' => [
+ 'id' => 'age-of-stored',
+ 'method' => 'getAgeOfStoredData',
+ ],
+ ];
+ }
+
+
+ /**
+ * Check if log storage module exist.
+ *
+ * @return boolean
+ */
+ public function isEnabled():bool
+ {
+ global $config;
+ if ((bool) $config['log_collector'] === true) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Returns the html status of log storage.
+ *
+ * @return string
+ */
+ public function getStatus():string
+ {
+ $classDisabled = '';
+ if ($this->isEnabled() === true) {
+ $value = $this->valueMonitoring('Log server connection');
+ $status = ((int) $value[0]['datos'] === 1) ? true : false;
+ if ($status === true) {
+ $image_status = html_print_image('images/status_check@svg.svg', true);
+ $text = html_print_div(
+ [
+ 'content' => __('Everything’s OK!'),
+ 'class' => 'status-text',
+ ],
+ true
+ );
+ } else {
+ $image_status = html_print_image('images/status_error@svg.svg', true);
+ $text = html_print_div(
+ [
+ 'content' => __('Something’s wrong'),
+ 'class' => 'status-text',
+ ],
+ true
+ );
+ }
+ } else {
+ $image_status = html_print_image('images/status_check@svg.svg', true);
+ $text = html_print_div(
+ [
+ 'content' => __('Everything’s OK!'),
+ 'class' => 'status-text',
+ ],
+ true
+ );
+ $classDisabled = 'alpha50';
+ }
+
+ $output = $image_status.$text;
+
+ return html_print_div(
+ [
+ 'content' => $output,
+ 'class' => 'flex_center margin-top-5 '.$classDisabled,
+ 'style' => 'margin: 0px 10px 10px 10px;',
+ 'id' => 'status-log-storage',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Returns the html of total sources in log storage.
+ *
+ * @return string
+ */
+ public function getTotalSources():string
+ {
+ if ($this->isEnabled() === true) {
+ $data = $this->valueMonitoring('Total sources');
+ $value = format_numeric($data[0]['datos']);
+ } else {
+ $value = __('N/A');
+ }
+
+ return html_print_div(
+ [
+ 'content' => $value,
+ 'class' => 'text-l',
+ 'style' => 'margin: 0px 10px 0px 10px;',
+ 'id' => 'total-source-log-storage',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Returns the html of lines in log storage.
+ *
+ * @return string
+ */
+ public function getStoredData():string
+ {
+ if ($this->isEnabled() === true) {
+ $data = $this->valueMonitoring('Total lines of data');
+ $value = format_numeric($data[0]['datos']);
+ } else {
+ $value = __('N/A');
+ }
+
+ return html_print_div(
+ [
+ 'content' => $value,
+ 'class' => 'text-l',
+ 'style' => 'margin: 0px 10px 0px 10px;',
+ 'id' => 'total-lines-log-storage',
+ ],
+ true
+ );
+ }
+
+
+ /**
+ * Returns the html of age of stored data.
+ *
+ * @return string
+ */
+ public function getAgeOfStoredData():string
+ {
+ $data = $this->valueMonitoring('Longest data archived');
+ $date = $data[0]['datos'];
+ if ($date > 0 && $this->isEnabled() === true) {
+ $interval = (time() - strtotime($date));
+ $days = format_numeric(($interval / 86400), 0);
+ } else {
+ $days = 'N/A';
+ }
+
+ return html_print_div(
+ [
+ 'content' => $days,
+ 'class' => 'text-l',
+ 'style' => 'margin: 0px 10px 0px 10px;',
+ 'id' => 'age-of-stored',
+ ],
+ true
+ );
+ }
+
+
+}
diff --git a/pandora_console/include/lib/TacticalView/elements/MonitoringElements.php b/pandora_console/include/lib/TacticalView/elements/MonitoringElements.php
new file mode 100644
index 0000000000..eece06f65f
--- /dev/null
+++ b/pandora_console/include/lib/TacticalView/elements/MonitoringElements.php
@@ -0,0 +1,246 @@
+title = __('Monitoring elements');
+ }
+
+
+ /**
+ * Returns the html of the tags grouped by modules.
+ *
+ * @return string
+ */
+ public function getTagsGraph():string
+ {
+ $sql = 'SELECT name, count(*) AS total
+ FROM ttag_module t
+ LEFT JOIN ttag ta ON ta.id_tag = t.id_tag
+ GROUP BY t.id_tag
+ ORDER BY total DESC
+ LIMIT 10;';
+ $rows = db_process_sql($sql);
+
+ $labels = [];
+ $data = [];
+ foreach ($rows as $key => $row) {
+ if (empty($row['name']) === true) {
+ continue;
+ }
+
+ $labels[] = $this->controlSizeText($row['name']);
+ $data[] = $row['total'];
+ }
+
+ $options = [
+ 'labels' => $labels,
+ 'legend' => [
+ 'position' => 'bottom',
+ 'align' => 'right',
+ 'display' => false,
+ ],
+ 'cutout' => 80,
+ 'nodata_image' => ['width' => '100%'],
+ ];
+ $pie = ring_graph($data, $options);
+ $output = html_print_div(
+ [
+ 'content' => $pie,
+ 'style' => 'margin: 0 auto; max-width: 80%; max-height: 220px;',
+ ],
+ true
+ );
+
+ return $output;
+ }
+
+
+ /**
+ * Returns the html of the groups grouped by modules.
+ *
+ * @return string
+ */
+ public function getModuleGroupGraph():string
+ {
+ global $config;
+ $id_groups = array_keys(users_get_groups($config['id_user'], 'AR', false));
+
+ if (in_array(0, $id_groups) === false) {
+ foreach ($id_groups as $key => $id_group) {
+ if ((bool) check_acl_restricted_all($config['id_user'], $id_group, 'AR') === false) {
+ unset($id_groups[$key]);
+ }
+ }
+ }
+
+ $id_groups = implode(',', $id_groups);
+ $sql = 'SELECT name, count(*) AS total
+ FROM tagente_modulo m
+ LEFT JOIN tagente a on a.id_agente = m.id_agente
+ LEFT JOIN tagent_secondary_group gs ON gs.id_agent = a.id_agente
+ LEFT JOIN tmodule_group g ON g.id_mg = m.id_module_group
+ WHERE name <> "" AND (a.id_grupo IN ('.$id_groups.') OR gs.id_group IN ('.$id_groups.'))
+ GROUP BY m.id_module_group
+ ORDER BY total DESC
+ LIMIT 10';
+ $rows = db_process_sql($sql);
+
+ $labels = [];
+ $data = [];
+ foreach ($rows as $key => $row) {
+ if (empty($row['name']) === true) {
+ continue;
+ }
+
+ $labels[] = $this->controlSizeText($row['name']);
+ $data[] = $row['total'];
+ }
+
+ $options = [
+ 'labels' => $labels,
+ 'legend' => [
+ 'position' => 'bottom',
+ 'align' => 'right',
+ 'display' => false,
+ ],
+ 'cutout' => 80,
+ 'nodata_image' => ['width' => '100%'],
+ ];
+ $pie = ring_graph($data, $options);
+ $output = html_print_div(
+ [
+ 'content' => $pie,
+ 'style' => 'margin: 0 auto; max-width: 80%; max-height: 220px;',
+ ],
+ true
+ );
+
+ return $output;
+ }
+
+
+ /**
+ * Returns the html of the agent grouped by modules.
+ *
+ * @return string
+ */
+ public function getAgentGroupsGraph():string
+ {
+ global $config;
+ $id_groups = array_keys(users_get_groups($config['id_user'], 'AR', false));
+
+ if (in_array(0, $id_groups) === false) {
+ foreach ($id_groups as $key => $id_group) {
+ if ((bool) check_acl_restricted_all($config['id_user'], $id_group, 'AR') === false) {
+ unset($id_groups[$key]);
+ }
+ }
+ }
+
+ $id_groups = implode(',', $id_groups);
+
+ $sql = 'SELECT gr.nombre, count(*) +
+ IFNULL((SELECT count(*) AS total
+ FROM tagente second_a
+ LEFT JOIN tagent_secondary_group second_g ON second_g.id_agent = second_a.id_agente
+ WHERE a.id_grupo = second_g.id_group AND second_g.id_group IN ('.$id_groups.')
+ GROUP BY second_g.id_group
+ ), 0) AS total
+ FROM tagente a
+ LEFT JOIN tgrupo gr ON gr.id_grupo = a.id_grupo
+ WHERE a.id_grupo IN ('.$id_groups.')
+ GROUP BY a.id_grupo
+ ORDER BY total DESC
+ LIMIT 10';
+ $rows = db_process_sql($sql);
+
+ $labels = [];
+ $data = [];
+ foreach ($rows as $key => $row) {
+ if (empty($row['nombre']) === true) {
+ continue;
+ }
+
+ $labels[] = $this->controlSizeText(io_safe_output($row['nombre']));
+ $data[] = $row['total'];
+ }
+
+ $options = [
+ 'labels' => $labels,
+ 'legend' => [
+ 'position' => 'bottom',
+ 'align' => 'right',
+ 'display' => false,
+ ],
+ 'cutout' => 80,
+ 'nodata_image' => ['width' => '100%'],
+ ];
+ $pie = ring_graph($data, $options);
+ $output = html_print_div(
+ [
+ 'content' => $pie,
+ 'style' => 'margin: 0 auto; max-width: 80%; max-height: 220px;',
+ ],
+ true
+ );
+
+ return $output;
+ }
+
+
+ /**
+ * Returns the html of monitoring by status.
+ *
+ * @return string
+ */
+ public function getMonitoringStatusGraph():string
+ {
+ $pie = graph_agent_status(false, '', '', true, true, false, true);
+ $output = html_print_div(
+ [
+ 'content' => $pie,
+ 'style' => 'margin: 0 auto; max-width: 80%; max-height: 220px;',
+ ],
+ true
+ );
+
+ return $output;
+ }
+
+
+}
diff --git a/pandora_console/include/lib/TacticalView/elements/NewsBoard.php b/pandora_console/include/lib/TacticalView/elements/NewsBoard.php
new file mode 100644
index 0000000000..3be2dd685f
--- /dev/null
+++ b/pandora_console/include/lib/TacticalView/elements/NewsBoard.php
@@ -0,0 +1,124 @@
+title = __('News Board');
+ }
+
+
+ /**
+ * Returns the html of the latest news.
+ *
+ * @return string
+ */
+ public function getNews():string
+ {
+ global $config;
+ $options = [];
+ $options['id_user'] = $config['id_user'];
+ $options['modal'] = false;
+ $options['limit'] = 7;
+ $news = get_news($options);
+
+ if (!empty($news)) {
+ $output = '
';
+ foreach ($news as $article) {
+ $default = false;
+ if ($article['text'] == '<p style="text-align: center; font-size: 13px;">Hello, congratulations, if you've arrived here you already have an operational monitoring console. Remember that our forums and online documentation are available 24x7 to get you out of any trouble. You can replace this message with a personalized one at Admin tools -&gt; Site news.</p> ') {
+ $article['subject'] = __('Welcome to Pandora FMS Console');
+ $default = true;
+ }
+
+ $text_bbdd = io_safe_output($article['text']);
+ $text = html_entity_decode($text_bbdd);
+
+ $output .= '
';
+ $output .= '';
+ $output .= '
';
+
+ if ($default) {
+ $output .= '
';
+ $output .= '
';
+ $output .= '
';
+ $output .= '
';
+
+ $output .= '
+
'.__('Welcome to our monitoring tool so grand,').'
+ '.__('Where data insights are at your command.').'
+ '.__('Sales, marketing, operations too,').'
+ '.__("Customer support, we've got you.").'
+
+
+
'.__('Our interface is user-friendly,').'
+ '.__("Customize your dashboard, it's easy.").'
+ '.__('Set up alerts and gain insights so keen,').'
+ '.__("Optimize your data, like you've never seen.").'
+
+
+
'.__('Unleash its power now, and join the pro league,').'
+ '.__('Unlock the potential of your data to intrigue.').'
+ '.__('Monitoring made simple, efficient and fun,').'
+ '.__('Discover a whole new way to get things done.').'
+
+
+
'.__('And take control of your IT once and for all.').'
+
+
'.__('You can replace this message with a personalized one at Admin tools -> Site news.').'
+ ';
+
+ $output .= '
';
+ } else {
+ $text = str_replace('
+$tacticalView = new GeneralTacticalView();
+$tacticalView->render();
diff --git a/pandora_console/operation/events/events.php b/pandora_console/operation/events/events.php
index bda58c8670..4c3eae145e 100644
--- a/pandora_console/operation/events/events.php
+++ b/pandora_console/operation/events/events.php
@@ -338,8 +338,10 @@ if (is_metaconsole() === true
// Ajax responses.
if (is_ajax() === true) {
$get_events = (int) get_parameter('get_events', 0);
+ $external_url = (bool) get_parameter('external_url', 0);
$table_id = get_parameter('table_id', '');
$groupRecursion = (bool) get_parameter('groupRecursion', false);
+ $compact_date = (int) get_parameter('compact_date', 0);
// Datatables offset, limit.
$start = (int) get_parameter('start', 0);
@@ -469,7 +471,7 @@ if (is_ajax() === true) {
$data = array_reduce(
$events,
- function ($carry, $item) use ($table_id, &$redirection_form_id, $filter) {
+ function ($carry, $item) use ($table_id, &$redirection_form_id, $filter, $compact_date, $external_url) {
global $config;
$tmp = (object) $item;
@@ -610,6 +612,12 @@ if (is_ajax() === true) {
);
$user_timezone = users_get_user_by_id($_SESSION['id_usuario'])['timezone'];
+ if ($compact_date === 1) {
+ $options = ['prominent' => 'compact'];
+ } else {
+ $options = [];
+ }
+
if (empty($user_timezone) === true) {
if (date_default_timezone_get() !== $config['timezone']) {
$timezone = timezone_open(date_default_timezone_get());
@@ -624,16 +632,16 @@ if (is_ajax() === true) {
$total_sec = strtotime($tmp->timestamp);
$total_sec += $dif;
$last_contact = date($config['date_format'], $total_sec);
- $last_contact_value = ui_print_timestamp($last_contact, true);
+ $last_contact_value = ui_print_timestamp($last_contact, true, $options);
} else {
$title = date($config['date_format'], strtotime($tmp->timestamp));
- $value = ui_print_timestamp(strtotime($tmp->timestamp), true);
+ $value = ui_print_timestamp(strtotime($tmp->timestamp), true, $options);
$last_contact_value = '
'.$value.' ';
}
} else {
date_default_timezone_set($user_timezone);
$title = date($config['date_format'], strtotime($tmp->timestamp));
- $value = ui_print_timestamp(strtotime($tmp->timestamp), true);
+ $value = ui_print_timestamp(strtotime($tmp->timestamp), true, $options);
$last_contact_value = '
'.$value.' ';
}
@@ -734,8 +742,13 @@ if (is_ajax() === true) {
$criticity .= $color.'" data-title="'.$text.'" data-use_title_for_force_title="1">'.$text.'
';
$tmp->criticity = $criticity;
- // Add event severity to end of text.
- $evn = '
';
+ if (isset($external_url) === true && $external_url === true) {
+ $url = ui_get_full_url('index.php?sec=eventos&sec2=operation/events/events');
+ $evn = ' ';
+ } else {
+ // Add event severity to end of text.
+ $evn = ' ';
+ }
// Grouped events.
if ((int) $filter['group_rep'] === EVENT_GROUP_REP_EXTRAIDS) {
@@ -3562,7 +3575,7 @@ function show_event_dialo(event, dialog_page) {
// History mode flag
var history = $("#hidden-history").val();
-
+ console.log(event);
jQuery.post(
ajax_file,
{
@@ -3580,7 +3593,7 @@ function show_event_dialo(event, dialog_page) {
.empty()
.append(data)
.dialog({
- title: event.evento,
+ title: event.event_title,
resizable: true,
draggable: true,
modal: true,
diff --git a/pandora_console/pandora_console.redhat.spec b/pandora_console/pandora_console.redhat.spec
index 7ea59b60da..8b0081f01f 100644
--- a/pandora_console/pandora_console.redhat.spec
+++ b/pandora_console/pandora_console.redhat.spec
@@ -6,7 +6,7 @@
%define debug_package %{nil}
%define name pandorafms_console
%define version 7.0NG.773.3
-%define release 231020
+%define release 231024
# User and Group under which Apache is running
%define httpd_name httpd
diff --git a/pandora_console/pandora_console.rhel7.spec b/pandora_console/pandora_console.rhel7.spec
index 4c05e526f9..c79e771a12 100644
--- a/pandora_console/pandora_console.rhel7.spec
+++ b/pandora_console/pandora_console.rhel7.spec
@@ -6,7 +6,7 @@
%define debug_package %{nil}
%define name pandorafms_console
%define version 7.0NG.773.3
-%define release 231020
+%define release 231024
# User and Group under which Apache is running
%define httpd_name httpd
diff --git a/pandora_console/pandora_console.spec b/pandora_console/pandora_console.spec
index 60c55f0fc9..bac11dfdc8 100644
--- a/pandora_console/pandora_console.spec
+++ b/pandora_console/pandora_console.spec
@@ -3,7 +3,7 @@
#
%define name pandorafms_console
%define version 7.0NG.773.3
-%define release 231020
+%define release 231024
%define httpd_name httpd
# User and Group under which Apache is running
%define httpd_name apache2
diff --git a/pandora_console/pandoradb.sql b/pandora_console/pandoradb.sql
index 1243daf341..7f3690f276 100644
--- a/pandora_console/pandoradb.sql
+++ b/pandora_console/pandoradb.sql
@@ -276,6 +276,7 @@ CREATE TABLE IF NOT EXISTS `tagente_modulo` (
`warning_time` INT UNSIGNED DEFAULT 0,
`quiet_by_downtime` TINYINT NOT NULL DEFAULT 0,
`disabled_by_downtime` TINYINT NOT NULL DEFAULT 0,
+ `last_compact` TIMESTAMP NOT NULL DEFAULT 0,
`made_enabled` TINYINT UNSIGNED DEFAULT 0,
PRIMARY KEY (`id_agente_modulo`),
KEY `main_idx` (`id_agente_modulo`,`id_agente`),
@@ -3133,6 +3134,110 @@ CREATE TABLE IF NOT EXISTS `tevent_alert_action` (
) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
+-- -----------------------------------------------------
+-- Table `tlog_alert`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `tlog_alert` (
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ `name` TEXT ,
+ `description` MEDIUMTEXT,
+ `order` INT UNSIGNED DEFAULT 0,
+ `mode` ENUM('PASS','DROP'),
+ `field1` TEXT ,
+ `field2` TEXT ,
+ `field3` TEXT ,
+ `field4` TEXT ,
+ `field5` TEXT ,
+ `field6` TEXT ,
+ `field7` TEXT ,
+ `field8` TEXT ,
+ `field9` TEXT ,
+ `field10` TEXT ,
+ `time_threshold` INT NOT NULL DEFAULT 86400,
+ `max_alerts` INT UNSIGNED NOT NULL DEFAULT 1,
+ `min_alerts` INT UNSIGNED NOT NULL DEFAULT 0,
+ `time_from` time DEFAULT '00:00:00',
+ `time_to` time DEFAULT '00:00:00',
+ `monday` TINYINT DEFAULT 1,
+ `tuesday` TINYINT DEFAULT 1,
+ `wednesday` TINYINT DEFAULT 1,
+ `thursday` TINYINT DEFAULT 1,
+ `friday` TINYINT DEFAULT 1,
+ `saturday` TINYINT DEFAULT 1,
+ `sunday` TINYINT DEFAULT 1,
+ `recovery_notify` TINYINT DEFAULT 0,
+ `field1_recovery` TEXT,
+ `field2_recovery` TEXT,
+ `field3_recovery` TEXT,
+ `field4_recovery` TEXT,
+ `field5_recovery` TEXT,
+ `field6_recovery` TEXT,
+ `field7_recovery` TEXT,
+ `field8_recovery` TEXT,
+ `field9_recovery` TEXT,
+ `field10_recovery` TEXT,
+ `id_group` MEDIUMINT UNSIGNED NULL DEFAULT 0,
+ `internal_counter` INT DEFAULT 0,
+ `last_fired` BIGINT NOT NULL DEFAULT 0,
+ `last_reference` BIGINT NOT NULL DEFAULT 0,
+ `times_fired` INT NOT NULL DEFAULT 0,
+ `disabled` TINYINT DEFAULT 0,
+ `standby` TINYINT DEFAULT 0,
+ `priority` TINYINT DEFAULT 0,
+ `force_execution` TINYINT DEFAULT 0,
+ `group_by` enum ('','id_agente','id_agentmodule','id_alert_am','id_grupo') DEFAULT '',
+ `special_days` TINYINT DEFAULT 0,
+ `disable_event` TINYINT DEFAULT 0,
+ `id_template_conditions` INT UNSIGNED NOT NULL DEFAULT 0,
+ `id_template_fields` INT UNSIGNED NOT NULL DEFAULT 0,
+ `last_evaluation` BIGINT NOT NULL DEFAULT 0,
+ `pool_occurrences` INT UNSIGNED NOT NULL DEFAULT 0,
+ `schedule` TEXT,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
+
+
+-- -----------------------------------------------------
+-- Table `tlog_rule`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `tlog_rule` (
+ `id_log_rule` INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ `id_log_alert` INT UNSIGNED NOT NULL,
+ `operation` ENUM('NOP', 'AND','OR','XOR','NAND','NOR','NXOR'),
+ `order` INT UNSIGNED DEFAULT 0,
+ `window` INT NOT NULL DEFAULT 0,
+ `count` INT NOT NULL DEFAULT 1,
+ `name` TEXT,
+ `log_content` TEXT,
+ `log_source` TEXT,
+ `log_agent` TEXT,
+ `operator_log_content` TEXT COMMENT 'Operator for log_content',
+ `operator_log_source` TEXT COMMENT 'Operator for log_source',
+ `operator_log_agent` TEXT COMMENT 'Operator for log_agent',
+ PRIMARY KEY (`id_log_rule`),
+ KEY `idx_id_log_alert` (`id_log_alert`)
+) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
+
+
+-- -----------------------------------------------------
+-- Table `tevent_alert_action`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `tlog_alert_action` (
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ `id_log_alert` INT UNSIGNED NOT NULL,
+ `id_alert_action` INT UNSIGNED NOT NULL,
+ `fires_min` INT UNSIGNED DEFAULT 0,
+ `fires_max` INT UNSIGNED DEFAULT 0,
+ `module_action_threshold` INT NOT NULL DEFAULT 0,
+ `last_execution` BIGINT NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`),
+ FOREIGN KEY (`id_log_alert`) REFERENCES tlog_alert(`id`)
+ ON DELETE CASCADE ON UPDATE CASCADE,
+ FOREIGN KEY (`id_alert_action`) REFERENCES talert_actions(`id`)
+ ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
+
+
-- -----------------------------------------------------
-- Table `tmodule_synth`
-- -----------------------------------------------------
diff --git a/pandora_console/pandoradb_data.sql b/pandora_console/pandoradb_data.sql
index 7200d0348a..47365fcc6e 100644
--- a/pandora_console/pandoradb_data.sql
+++ b/pandora_console/pandoradb_data.sql
@@ -120,10 +120,10 @@ INSERT INTO `tconfig` (`token`, `value`) VALUES
('custom_report_front_logo', 'images/pandora_logo_white.jpg'),
('custom_report_front_header', ''),
('custom_report_front_footer', ''),
-('MR', 65),
+('MR', 66),
('identification_reminder', 1),
('identification_reminder_timestamp', 0),
-('current_package', 773),
+('current_package', 774),
('post_process_custom_values', '{"0.00000038580247":"Seconds to months","0.00000165343915":"Seconds to weeks","0.00001157407407":"Seconds to days","0.01666666666667":"Seconds to minutes","0.00000000093132":"Bytes to Gigabytes","0.00000095367432":"Bytes to Megabytes","0.00097656250000":"Bytes to Kilobytes","0.00000001653439":"Timeticks to weeks","0.00000011574074":"Timeticks to days"}'),
('custom_docs_logo', 'default_docs.png'),
('custom_support_logo', 'default_support.png'),
diff --git a/pandora_console/views/tacticalView/view.php b/pandora_console/views/tacticalView/view.php
new file mode 100644
index 0000000000..e09e5f16ae
--- /dev/null
+++ b/pandora_console/views/tacticalView/view.php
@@ -0,0 +1,391 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ title; ?>
+
+
+
+
+
+
+
+
+
+
+ getLogSizeStatus(); ?>
+
+
+
+
+
+
+
+ getServerStatus(); ?>
+
+
+
+
+
+
+
+ getCPULoadGraph(); ?>
+
+
+
+
+
+
+
+
+ getLicenseUsageGraph(); ?>
+
+
+
+
+
+
+
+
+
+
+
+ title; ?>
+
+
+
+
+
+
+ getModuleGroupGraph(); ?>
+
+
+
+
+
+ getMonitoringStatusGraph(); ?>
+
+
+
+ getAgentGroupsGraph(); ?>
+
+
+
+
+
+
+
+
+
+ title; ?>
+
+
+
+
+
+
+
+ getStatus(); ?>
+
+
+
+
+
+ getDataRecords(); ?>
+
+
+
+
+
+
+
+ getStringRecords(); ?>
+
+
+
+
+
+ getEvents(); ?>
+
+
+
+
+
+
+ getReadsGraph(); ?>
+
+
+
+
+
+ getWritesGraph(); ?>
+
+
+
+
+
+
+
+
+
+ title; ?>
+
+ getNews(); ?>
+
+
+
+
+
+ title; ?>
+
+
+
+ loading(); ?>
+
+
+
+
+
+
+ title; ?>
+
+
+
+
+
+
+ getStatus(); ?>
+
+
+
+
+
+ getTotalSources(); ?>
+
+
+
+
+
+
+
+ getStoredData(); ?>
+
+
+
+
+
+
+ getAgeOfStoredData(); ?>
+
+
+
+
+
+
+ title; ?>
+
+
+
+
+
+
+ getQueues(); ?>
+
+
+
+
+
+ getTotalSources(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ title; ?>
+
+
+
+
+
+
+ getCurrentlyTriggered(); ?>
+
+
+
+
+
+ getActiveAlerts(); ?>
+
+
+ checkAclUserList() === true) : ?>
+
+
+ getDataTableUsers(); ?>
+
+
+
+
+ checkAcl() === true) : ?>
+
+
+
+ title; ?>
+
+
+
+
+
+ getDataTableEvents(); ?>
+
+
+
+
+
+
+
+
+
+
+
+ title; ?>
+
+
+
+
+
+
+
+
+ getTotalAgents(); ?>
+
+
+
+
+
+ getAlerts(); ?>
+
+
+
+ getDataTableGroups(); ?>
+
+
+
+
+
+ getOperatingSystemGraph(); ?>
+
+
+
+ getStatusGraph(); ?>
+
+
+
+
+
+
+ checkAcl() === true) : ?>
+
+
+ title; ?>
+
+ list(); ?>
+
+
+
+
+
+{"google_maps_description"} = 0;
$pa_config->{'openstreetmaps_description'} = 0;
$pa_config->{"eventserver"} = 1; # 4.0
- $pa_config->{"correlationserver"} = 0; # 757
- $pa_config->{"correlation_threshold"} = 30; # 757
+ $pa_config->{"eventserver_threads"} = 1; # 4.0
+ $pa_config->{"logserver"} = 1; # 7.774
+ $pa_config->{"logserver_threads"} = 1; # 7.774
$pa_config->{"event_window"} = 3600; # 4.0
$pa_config->{"log_window"} = 3600; # 7.741
$pa_config->{"elastic_query_size"} = 10; # 7.754 Elements per request (ELK)
@@ -580,6 +581,8 @@ sub pandora_load_config {
$pa_config->{"repl_dbuser"} = undef; # 7.0.770
$pa_config->{"repl_dbpass"} = undef; # 7.0.770
+ $pa_config->{"ssl_verify"} = 0; # 7.0 774
+
$pa_config->{"madeserver"} = 0; # 774.
# Check for UID0
@@ -803,14 +806,17 @@ sub pandora_load_config {
$pa_config->{"transactional_pool"} = $pa_config->{"incomingdir"} . "/" . $tbuf;
}
}
- elsif ($parametro =~ m/^eventserver\s+([0-9]*)/i) {
+ elsif ($parametro =~ m/^eventserver\s+([0-1])/i) {
$pa_config->{'eventserver'}= clean_blank($1);
}
- elsif ($parametro =~ m/^correlationserver\s+([0-9]*)/i) {
- $pa_config->{'correlationserver'}= clean_blank($1);
+ elsif ($parametro =~ m/^eventserver_threads\s+([0-9]*)/i) {
+ $pa_config->{'eventserver_threads'}= clean_blank($1);
}
- elsif ($parametro =~ m/^correlation_threshold\s+([0-9]*)/i) {
- $pa_config->{'correlation_threshold'}= clean_blank($1);
+ elsif ($parametro =~ m/^logserver\s+([0-1])/i) {
+ $pa_config->{'logserver'}= clean_blank($1);
+ }
+ elsif ($parametro =~ m/^logserver_threads\s+([0-9]*)/i) {
+ $pa_config->{'logserver_threads'}= clean_blank($1);
}
elsif ($parametro =~ m/^icmpserver\s+([0-9]*)/i) {
$pa_config->{'icmpserver'}= clean_blank($1);
@@ -1393,6 +1399,9 @@ sub pandora_load_config {
elsif ($parametro =~ m/^repl_dbpass\s(.*)/i) {
$pa_config->{'repl_dbpass'} = clean_blank($1);
}
+ elsif ($parametro =~ m/^ssl_verify\s+([0-1])/i) {
+ $pa_config->{'ssl_verify'} = clean_blank($1);
+ }
elsif ($parametro =~ m/^madeserver\s+([0-1])/i){
$pa_config->{'madeserver'}= clean_blank($1);
}
diff --git a/pandora_server/lib/PandoraFMS/Core.pm b/pandora_server/lib/PandoraFMS/Core.pm
index 4de60cb66b..90b039a078 100644
--- a/pandora_server/lib/PandoraFMS/Core.pm
+++ b/pandora_server/lib/PandoraFMS/Core.pm
@@ -313,6 +313,7 @@ our @ServerTypes = qw (
icmpserver
snmpserver
satelliteserver
+ transactionalserver
mfserver
syncserver
wuxserver
@@ -324,6 +325,7 @@ our @ServerTypes = qw (
ncmserver
netflowserver
logserver
+ logserver
madeserver
);
our @AlertStatus = ('Execute the alert', 'Do not execute the alert', 'Do not execute the alert, but increment its internal counter', 'Cease the alert', 'Recover the alert', 'Reset internal counter');
@@ -806,9 +808,9 @@ Process an alert given the status returned by pandora_evaluate_alert.
=cut
##########################################################################
-sub pandora_process_alert ($$$$$$$$;$$) {
+sub pandora_process_alert ($$$$$$$$;$) {
my ($pa_config, $data, $agent, $module, $alert, $rc, $dbh, $timestamp,
- $extra_macros, $is_correlated_alert) = @_;
+ $extra_macros) = @_;
if (defined ($agent)) {
logger ($pa_config, "Processing alert '" . safe_output($alert->{'name'}) . "' for agent '" . safe_output($agent->{'nombre'}) . "': " . (defined ($AlertStatus[$rc]) ? $AlertStatus[$rc] : 'Unknown status') . ".", 10);
@@ -816,15 +818,21 @@ sub pandora_process_alert ($$$$$$$$;$$) {
else {
logger ($pa_config, "Processing alert '" . safe_output($alert->{'name'}) . "': " . (defined ($AlertStatus[$rc]) ? $AlertStatus[$rc] : 'Unknown status') . ".", 10);
}
-
+
# Simple or event alert?
my ($id, $table) = (undef, undef);
if (defined ($alert->{'id_template_module'})) {
$id = $alert->{'id_template_module'};
$table = 'talert_template_modules';
- } else {
+ } elsif (defined ($alert->{'_log_alert'})) {
+ $id = $alert->{'id'};
+ $table = 'tlog_alert';
+ } elsif (defined ($alert->{'_event_alert'})) {
$id = $alert->{'id'};
$table = 'tevent_alert';
+ } else {
+ logger($pa_config, "pandora_process_alert received invalid data", 10);
+ return;
}
# Do not execute
@@ -876,10 +884,10 @@ sub pandora_process_alert ($$$$$$$$;$$) {
if ($pa_config->{'alertserver'} == 1 || $pa_config->{'alertserver_queue'} == 1) {
pandora_queue_alert($pa_config, $dbh, [$data, $agent, $module,
- $alert, 0, $timestamp, 0, $extra_macros, $is_correlated_alert]);
+ $alert, 0, $timestamp, 0, $extra_macros]);
} else {
pandora_execute_alert ($pa_config, $data, $agent, $module, $alert, 0, $dbh,
- $timestamp, 0, $extra_macros, $is_correlated_alert);
+ $timestamp, 0, $extra_macros);
}
return;
}
@@ -922,10 +930,10 @@ sub pandora_process_alert ($$$$$$$$;$$) {
if ($pa_config->{'alertserver'} == 1 || $pa_config->{'alertserver_queue'} == 1) {
pandora_queue_alert($pa_config, $dbh, [$data, $agent, $module,
- $alert, 1, $timestamp, 0, $extra_macros, $is_correlated_alert]);
+ $alert, 1, $timestamp, 0, $extra_macros]);
} else {
pandora_execute_alert ($pa_config, $data, $agent, $module, $alert, 1,
- $dbh, $timestamp, 0, $extra_macros, $is_correlated_alert);
+ $dbh, $timestamp, 0, $extra_macros);
}
return;
}
@@ -941,7 +949,7 @@ Execute the given alert.
sub pandora_execute_alert {
my ($pa_config, $data, $agent, $module,
$alert, $alert_mode, $dbh, $timestamp, $forced_alert,
- $extra_macros, $is_correlated_alert) = @_;
+ $extra_macros) = @_;
# 'in-process' events can inhibit alers too.
if ($pa_config->{'event_inhibit_alerts'} == 1 && $alert_mode != RECOVERED_ALERT) {
@@ -1031,7 +1039,7 @@ sub pandora_execute_alert {
}
}
# Event alert
- else {
+ elsif (defined($alert->{'_event_alert'})) {
if ($alert_mode == RECOVERED_ALERT) {
@actions = get_db_rows ($dbh, 'SELECT talert_actions.name as action_name, tevent_alert_action.*, talert_actions.*, talert_commands.*
FROM tevent_alert_action, talert_actions, talert_commands
@@ -1062,6 +1070,38 @@ sub pandora_execute_alert {
$alert->{'id_alert_action'});
}
}
+ # Log alert.
+ elsif (defined($alert->{'_log_alert'})) {
+ if ($alert_mode == RECOVERED_ALERT) {
+ @actions = get_db_rows ($dbh, 'SELECT talert_actions.name as action_name, tlog_alert_action.*, talert_actions.*, talert_commands.*
+ FROM tlog_alert_action, talert_actions, talert_commands
+ WHERE tlog_alert_action.id_alert_action = talert_actions.id
+ AND talert_actions.id_alert_command = talert_commands.id
+ AND tlog_alert_action.id_log_alert = ?
+ AND ((fires_min = 0 AND fires_max = 0)
+ OR ? >= fires_min)',
+ $alert->{'id'}, $alert->{'times_fired'});
+ } else {
+ @actions = get_db_rows ($dbh, 'SELECT talert_actions.name as action_name, tlog_alert_action.*, talert_actions.*, talert_commands.*
+ FROM tlog_alert_action, talert_actions, talert_commands
+ WHERE tlog_alert_action.id_alert_action = talert_actions.id
+ AND talert_actions.id_alert_command = talert_commands.id
+ AND tlog_alert_action.id_log_alert = ?
+ AND ((fires_min = 0 AND fires_max = 0)
+ OR (fires_min <= fires_max AND ? >= fires_min AND ? <= fires_max)
+ OR (fires_min > fires_max AND ? >= fires_min))',
+ $alert->{'id'}, $alert->{'times_fired'}, $alert->{'times_fired'}, $alert->{'times_fired'});
+ }
+
+ # Get default action
+ if ($#actions < 0) {
+ @actions = get_db_rows ($dbh, 'SELECT talert_actions.name as action_name, talert_actions.*, talert_commands.*
+ FROM talert_actions, talert_commands
+ WHERE talert_actions.id = ?
+ AND talert_actions.id_alert_command = talert_commands.id',
+ $alert->{'id_alert_action'});
+ }
+ }
# No actions defined
if ($#actions < 0) {
@@ -1150,8 +1190,33 @@ sub pandora_execute_alert {
#If we've spotted an alert recovered, we set the new event's severity to 2 (NORMAL), otherwise the original value is maintained.
my ($text, $event, $severity) = ($alert_mode == RECOVERED_ALERT) ? ('recovered', 'alert_recovered', 2) : ('fired', 'alert_fired', $alert->{'priority'});
- if (defined($is_correlated_alert) && $is_correlated_alert == 1) {
- $text = "Correlated alert $text";
+ if (defined($alert->{'_event_alert'})) {
+ $text = "Event alert $text";
+ pandora_event (
+ $pa_config,
+ "$text (" . safe_output($alert->{'name'}) . ") ",
+ (defined ($agent) ? $agent->{'id_grupo'} : 0),
+ # id agent.
+ 0,
+ $severity,
+ (defined ($alert->{'id_template_module'}) ? $alert->{'id_template_module'} : 0),
+ # id agent module.
+ 0,
+ $event,
+ 0,
+ $dbh,
+ 'monitoring_server',
+ '',
+ '',
+ '',
+ '',
+ $critical_instructions,
+ $warning_instructions,
+ $unknown_instructions,
+ p_encode_json($pa_config, $custom_data)
+ );
+ } elsif (defined($alert->{'_log_alert'})) {
+ $text = "Log alert $text";
pandora_event (
$pa_config,
"$text (" . safe_output($alert->{'name'}) . ") ",
diff --git a/pandora_server/lib/PandoraFMS/PluginTools.pm b/pandora_server/lib/PandoraFMS/PluginTools.pm
index 7f14166c8c..79ae4f205f 100644
--- a/pandora_server/lib/PandoraFMS/PluginTools.pm
+++ b/pandora_server/lib/PandoraFMS/PluginTools.pm
@@ -34,7 +34,7 @@ our @ISA = qw(Exporter);
# version: Defines actual version of Pandora Server for this module only
my $pandora_version = "7.0NG.773.3";
-my $pandora_build = "231020";
+my $pandora_build = "231024";
our $VERSION = $pandora_version." ".$pandora_build;
our %EXPORT_TAGS = ( 'all' => [ qw() ] );
diff --git a/pandora_server/lib/PandoraFMS/Tools.pm b/pandora_server/lib/PandoraFMS/Tools.pm
index 70cd710ab4..7c5a823a1d 100755
--- a/pandora_server/lib/PandoraFMS/Tools.pm
+++ b/pandora_server/lib/PandoraFMS/Tools.pm
@@ -67,7 +67,6 @@ our @EXPORT = qw(
INVENTORYSERVER
WEBSERVER
EVENTSERVER
- CORRELATIONSERVER
ICMPSERVER
SNMPSERVER
SATELLITESERVER
@@ -80,6 +79,7 @@ our @EXPORT = qw(
NCMSERVER
NETFLOWSERVER
LOGSERVER
+ LOGSERVER
MADESERVER
METACONSOLE_LICENSE
OFFLINE_LICENSE
diff --git a/pandora_server/pandora_server.redhat.spec b/pandora_server/pandora_server.redhat.spec
index 53feba1d17..01b956752d 100644
--- a/pandora_server/pandora_server.redhat.spec
+++ b/pandora_server/pandora_server.redhat.spec
@@ -7,7 +7,7 @@
%define debug_package %{nil}
%define name pandorafms_server
%define version 7.0NG.773.3
-%define release 231020
+%define release 231024
Summary: Pandora FMS Server
Name: %{name}
diff --git a/pandora_server/pandora_server.spec b/pandora_server/pandora_server.spec
index 20dd513592..98fb1220f9 100644
--- a/pandora_server/pandora_server.spec
+++ b/pandora_server/pandora_server.spec
@@ -4,7 +4,7 @@
%global __os_install_post %{nil}
%define name pandorafms_server
%define version 7.0NG.773.3
-%define release 231020
+%define release 231024
Summary: Pandora FMS Server
Name: %{name}
diff --git a/pandora_server/pandora_server_installer b/pandora_server/pandora_server_installer
index 489d7651fe..bb81f5cb29 100755
--- a/pandora_server/pandora_server_installer
+++ b/pandora_server/pandora_server_installer
@@ -9,7 +9,7 @@
# **********************************************************************
PI_VERSION="7.0NG.773.3"
-PI_BUILD="231020"
+PI_BUILD="231024"
MODE=$1
if [ $# -gt 1 ]; then
diff --git a/pandora_server/util/pandora_db.pl b/pandora_server/util/pandora_db.pl
index 8e3d3015b8..73b8ba5808 100755
--- a/pandora_server/util/pandora_db.pl
+++ b/pandora_server/util/pandora_db.pl
@@ -26,6 +26,9 @@ use POSIX qw(strftime);
use File::Path qw(rmtree);
use Time::HiRes qw(usleep);
+use List::Util qw(min);
+use List::Util qw(sum);
+
# Default lib dir for RPM and DEB packages
BEGIN { push @INC, '/usr/lib/perl5'; }
@@ -35,7 +38,7 @@ use PandoraFMS::Config;
use PandoraFMS::DB;
# version: define current version
-my $version = "7.0NG.773.3 Build 231020";
+my $version = "7.0NG.773.3 Build 231024";
# Pandora server configuration
my %conf;
@@ -443,116 +446,187 @@ sub pandora_purgedb ($$$) {
###############################################################################
# Compact agent data.
###############################################################################
-sub pandora_compactdb ($$$) {
- my ($conf, $dbh, $dbh_conf) = @_;
+sub pandora_compactdb {
+ my ($conf, $dbh, $dbh_conf) = @_;
+
+ my $total_modules = get_db_value($dbh, "SELECT COUNT(id_agente_modulo) FROM tagente_modulo");
+
+ # Interval in hours to compact.
+ my $compaction_interval = 24;
+ my $compaction_factor = (3600 * $compaction_interval) / 300;
+
+ # Number of agents to be proceced on this execution
+ my $agents_limit = int($total_modules / $compaction_factor);
+
+ my $last_compact_offset = pandora_get_config_value($dbh, "last_compact_offset");
+
+ unless ($last_compact_offset) {
+ db_do($dbh, "INSERT INTO tconfig (token, value) VALUES ('last_compact_offset', '0')");
+ $last_compact_offset = 0;
+ }
+
+ # Obtain a group of modules to compact.
+ my @module_groups = get_db_rows(
+ $dbh,
+ 'SELECT id_agente_modulo, id_tipo_modulo, UNIX_TIMESTAMP(last_compact) as last_compact FROM tagente_modulo WHERE id_agente_modulo > ? LIMIT ?',
+ $last_compact_offset,
+ $agents_limit
+ );
+
+ # Compact the group of modules.
+ my $starting_time = time();
+ pandora_compact_modules($dbh, $conf, @module_groups);
+ my $ending_time = time();
+ log_message('COMPACT', "Time taken: " . ($ending_time - $starting_time) . " seconds.");
+
+ # Add the offset.
+ $last_compact_offset += $agents_limit;
+
+ # If the offset is higher than the max module start again
+ $last_compact_offset = 0 if ($last_compact_offset >= $total_modules);
+
+ # Save the new offset
+ db_do($dbh, "UPDATE tconfig SET value = ? WHERE token = 'last_compact_offset'", $last_compact_offset);
+}
+
+sub pandora_compact_modules {
+ my ($dbh, $conf, @module_groups) = @_;
+
+ # Obtain the minimun last compact from the block
+ my $min_compact = min(map { $_->{"last_compact"} } @module_groups);
+
+ # if the min has not been setted
+ if($min_compact == 0){
+ $min_compact = get_db_value(
+ $dbh,
+ 'SELECT MIN(td.utimestamp)
+ FROM tagente_datos td, tagente_modulo tm
+ WHERE td.id_agente_modulo = tm.id_agente_modulo
+ AND tm.id_tipo_modulo not in (2, 6, 9, 18, 21, 31, 35, 100)
+ AND td.id_agente_modulo BETWEEN ? AND ?',
+ $module_groups[0]->{"id_agente_modulo"}, $module_groups[-1]->{"id_agente_modulo"}
+ );
+ }
+
+ # One week of data of data as max from the last min compact.
+ my $max_compact = $min_compact + (60 * 60 * 24 * 7);
+
+ # If the last compact is on the future set the current time as max.
+ $max_compact = time() if($max_compact > time());
+
+ log_message('COMPACT', "Compacting data of agents agents $module_groups[0]->{'id_agente_modulo'} to $module_groups[-1]->{'id_agente_modulo'}, data between " . strftime('%Y-%m-%d %H:%M:%S', localtime($min_compact)) . " and " . strftime('%Y-%m-%d %H:%M:%S', localtime($max_compact)));
+
+ # Obtain all the data from the interval to avoid multiple requests
+ # Avoid pull data from modules that should be skipped.
+ my @data = get_db_rows (
+ $dbh,
+ 'SELECT td.id_agente_modulo, td.datos, td.utimestamp
+ FROM tagente_datos td, tagente_modulo tm
+ WHERE td.id_agente_modulo = tm.id_agente_modulo
+ AND tm.id_tipo_modulo not in (2, 6, 9, 18, 21, 31, 35, 100)
+ AND td.utimestamp < ? AND td.utimestamp >= ?
+ AND td.id_agente_modulo BETWEEN ? AND ?
+ ',
+ $max_compact, $min_compact, $module_groups[0]->{"id_agente_modulo"}, $module_groups[-1]->{"id_agente_modulo"}
+ );
+
+ my $total_data = scalar(@data);
+ my $proceced_total_data = 0;
+ my $progress = 0;
+ my $compactations = 0;
- my %count_hash;
- my %id_agent_hash;
- my %value_hash;
- my %module_proc_hash;
-
- return if ($conf->{'_days_compact'} == 0 || $conf->{'_step_compact'} < 1);
-
# Convert compact interval length from hours to seconds
my $step = $conf->{'_step_compact'} * 3600;
- # The oldest timestamp will be the lower limit
- my $limit_utime = get_db_value ($dbh, 'SELECT min(utimestamp) as min FROM tagente_datos');
- return unless (defined ($limit_utime) && $limit_utime > 0);
+ # Max. 168 steps or 7 days in one hour steps.
+ # This avoids blocking of old modules without last compact
+ my $step_limit = 24 * 7;
- # Calculate the start date
- my $start_utime = time() - $conf->{'_days_compact'} * 24 * 60 * 60;
- my $last_compact = $start_utime;
- my $stop_utime;
+ # Compact the modules in this block
+ foreach my $module (@module_groups) {
+ $progress = $total_data == 0 ? 0 : ($proceced_total_data / $total_data) * 100;
+ printf(strftime("\r" . "%H:%M:%S", localtime()) . ' [COMPACT] ' . "Progress: %.2f%%", $progress);
+
+ my $id = $module->{"id_agente_modulo"};
+ my $module_type = $module->{"id_tipo_modulo"};
- # Do not compact the same data twice!
- if (defined ($conf->{'_last_compact'}) && $conf->{'_last_compact'} > $limit_utime) {
- $limit_utime = $conf->{'_last_compact'};
- }
-
- if ($start_utime <= $limit_utime || ( defined ($conf->{'_last_compact'}) && (($conf->{'_last_compact'} + 24 * 60 * 60) > $start_utime))) {
- log_message ('COMPACT', "Data already compacted.");
- return;
- }
-
- log_message ('COMPACT', "Compacting data from " . strftime ("%Y-%m-%d %H:%M:%S", localtime($limit_utime)) . " to " . strftime ("%Y-%m-%d %H:%M:%S", localtime($start_utime)) . '.', '');
+ next unless defined ($module_type);
+ next if ($module_type == 2 || $module_type == 6 || $module_type == 9 || $module_type == 18 || $module_type == 21 || $module_type == 31 || $module_type == 35 || $module_type == 100);
- # Prepare the query to retrieve data from an interval
- while (1) {
+ # Obtain the data just for this module
+ my @module_data = grep { $_->{"id_agente_modulo"} == $id } @data;
+ my $total_elements = scalar(@module_data);
- # Calculate the stop date for the interval
- $stop_utime = $start_utime - $step;
+ # No data for this module.
+ next if ($total_elements == 0);
- # Out of limits
- last if ($start_utime < $limit_utime);
+ # Obtain the last compact of this module to avoid compact something previously compacted
+ my $last_compact = $module ->{"last_compact"};
- # Mark the progress
- log_message ('', ".");
+ # In case that the last compact has not been defined take the minimun data as last compact
+ $last_compact = min(map { $_->{"utimestamp"} } @module_data)-1 if($last_compact == 0);
+
+ # Create an array to store INSERT commands
+ my @insert_commands;
+ my $insert_command = 'INSERT INTO tagente_datos (id_agente_modulo, datos, utimestamp) VALUES (?, ?, ?)';
+
+ my $step_number = 0;
+ my $first_compact = $last_compact;
+
+ # Compact using the steps
+ while($step_number < $step_limit){
+
+ my $next_compact = $last_compact + $step;
- my @data = get_db_rows ($dbh, 'SELECT * FROM tagente_datos WHERE utimestamp < ? AND utimestamp >= ?', $start_utime, $stop_utime);
- # No data, move to the next interval
- if ($#data == 0) {
- $start_utime = $stop_utime;
+ last if($next_compact > time());
+
+ # Obtain the data between the last compact and the next step.
+ my @data_in_range = grep { $_->{"utimestamp"} > $last_compact && $_->{"utimestamp"} <= $next_compact } @module_data;
+
+ my $total_range_elements = scalar(@data_in_range);
+
+ # Nothing to compress, skip this step.
+ if($total_range_elements == 0){
+ $last_compact = $next_compact;
next;
}
- # Get interval data
- foreach my $data (@data) {
- my $id_module = $data->{'id_agente_modulo'};
- if (! defined($module_proc_hash{$id_module})) {
- my $module_type = get_db_value ($dbh, 'SELECT id_tipo_modulo FROM tagente_modulo WHERE id_agente_modulo = ?', $id_module);
- next unless defined ($module_type);
+ my $total_data = sum(map { $_->{"datos"} } @data_in_range);
- # Mark proc modules.
- if ($module_type == 2 || $module_type == 6 || $module_type == 9 || $module_type == 18 || $module_type == 21 || $module_type == 31 || $module_type == 35 || $module_type == 100) {
- $module_proc_hash{$id_module} = 1;
- }
- else {
- $module_proc_hash{$id_module} = 0;
- }
- }
+ my $avg = $total_data / $total_range_elements;
- # Skip proc modules!
- next if ($module_proc_hash{$id_module} == 1);
+ $proceced_total_data += $total_range_elements;
+
+ push @insert_commands, [$insert_command, $id, $avg, int($last_compact + ($step / 2))];
- if (! defined($value_hash{$id_module})) {
- $value_hash{$id_module} = 0;
- $count_hash{$id_module} = 0;
+ $last_compact = $next_compact;
+
+ $step_number +=1;
+ # Small sleep to don't burn the DB
+ usleep (1000);
+
+ }
- if (! defined($id_agent_hash{$id_module})) {
- $id_agent_hash{$id_module} = $data->{'id_agente'};
- }
- }
+ $dbh->begin_work;
- $value_hash{$id_module} += $data->{'datos'};
- $count_hash{$id_module}++;
- }
+ db_do ($dbh, 'DELETE FROM tagente_datos WHERE utimestamp > ? AND utimestamp <= ? AND id_agente_modulo = ?', $first_compact, $last_compact, $id);
- # Delete interval from the database
- db_do ($dbh, 'DELETE ad FROM tagente_datos ad
- INNER JOIN tagente_modulo am ON ad.id_agente_modulo = am.id_agente_modulo AND am.id_tipo_modulo NOT IN (2,6,9,18,21,31,35,100)
- WHERE ad.utimestamp < ? AND ad.utimestamp >= ?', $start_utime, $stop_utime);
+ # Execute the INSERT commands
+ foreach my $command (@insert_commands) {
+ my ($sql, @params) = @$command;
+ db_do($dbh, $sql, @params);
+ }
- # Insert interval average value
- foreach my $key (keys(%value_hash)) {
- $value_hash{$key} /= $count_hash{$key};
- db_do ($dbh, 'INSERT INTO tagente_datos (id_agente_modulo, datos, utimestamp) VALUES (?, ?, ?)', $key, $value_hash{$key}, $stop_utime);
- delete($value_hash{$key});
- delete($count_hash{$key});
- }
-
- usleep (1000); # Very small usleep, just to don't burn the DB
- # Move to the next interval
- $start_utime = $stop_utime;
+ $dbh->commit;
+ # Update the last compacted timestamp.
+ db_do($dbh, "UPDATE tagente_modulo SET last_compact = FROM_UNIXTIME(?) WHERE id_agente_modulo = ?", $last_compact, $module ->{"id_agente_modulo"});
+ $compactations += $step_number;
}
- log_message ('', "\n");
- # Mark the last compact date
- if (defined ($conf->{'_last_compact'})) {
- db_do ($dbh_conf, 'UPDATE tconfig SET value=? WHERE token=?', $last_compact, 'last_compact');
- } else {
- db_do ($dbh_conf, 'INSERT INTO tconfig (value, token) VALUES (?, ?)', $last_compact, 'last_compact');
- }
+ printf(strftime("\r" . "%H:%M:%S", localtime()) . ' [COMPACT] ' . "Progress: %.2f%%", 100);
+ print("\n");
+ log_message('COMPACT', "A total of $proceced_total_data elements has been compacted into $compactations elements");
+
}
########################################################################
@@ -1234,9 +1308,8 @@ sub pandoradb_main {
# Only active database should be compacted. Disabled for historical database.
# Compact on if enable and DaysCompact are below DaysPurge
if (($conf->{'_onlypurge'} == 0)
- && ($conf->{'_days_compact'} < $conf->{'_days_purge'})
) {
- pandora_compactdb ($conf, defined ($history_dbh) ? $history_dbh : $dbh, $dbh);
+ pandora_compactdb ($conf, $dbh, $dbh);
}
# Update tconfig with last time of database maintance time (now)
diff --git a/pandora_server/util/pandora_manage.pl b/pandora_server/util/pandora_manage.pl
index cdbb058236..6db7bad0b1 100755
--- a/pandora_server/util/pandora_manage.pl
+++ b/pandora_server/util/pandora_manage.pl
@@ -36,7 +36,7 @@ use Encode::Locale;
Encode::Locale::decode_argv;
# version: define current version
-my $version = "7.0NG.773.3 Build 231020";
+my $version = "7.0NG.773.3 Build 231024";
# save program name for logging
my $progname = basename($0);