customvar)) { return false; } // Iterate through custom variables to find trackvolume foreach ($customvars->customvar as $customvar) { $name = (string)$customvar->name; $value = (string)$customvar->value; if ($name === 'trackvolume' || $name === 'TRACKVOLUME') { // Check if value is 1, "1", "true", or "TRUE" if ($value === '1' || $value === 1 || strtolower($value) === 'true') { return true; } } } return false; } /** * Parse perfdata to extract disk usage information * Supports multiple formats: * 1. check_local_disk format: /=4985MiB;9592;10791;0;11991 * 2. NCPA format: 'used'=0.69GiB;;; 'free'=4.81GiB;;; 'total'=5.82GiB;;; * * @param string $perfdata Performance data string from service status * @param string $check_command Optional check command name to help identify format * @return array|false Array with 'used', 'total', 'percent', 'available' or false if not found */ function parse_disk_perfdata($perfdata, $check_command = '') { if (empty($perfdata)) { return false; } $used_value = null; $used_unit = ''; $total_value = null; $total_unit = ''; // Pattern to match 'used'=value[unit];;; (with quotes) if (preg_match("/'used'\s*=\s*([0-9.]+)([KMGT]?(?:IB|B)?);/i", $perfdata, $used_matches)) { $used_value = floatval($used_matches[1]); $used_unit = isset($used_matches[2]) ? strtoupper(trim($used_matches[2])) : ''; } // Pattern to match 'total'=value[unit];;; (with quotes) if (preg_match("/'total'\s*=\s*([0-9.]+)([KMGT]?(?:IB|B)?);/i", $perfdata, $total_matches)) { $total_value = floatval($total_matches[1]); $total_unit = isset($total_matches[2]) ? strtoupper(trim($total_matches[2])) : ''; } // If we found both used and total, calculate percentage if ($used_value !== null && $total_value !== null && $total_value > 0) { // If total has no unit but used has a unit, assume total uses same unit if (empty($total_unit) && !empty($used_unit) && $total_value > 0) { $total_unit = $used_unit; } // Convert to bytes for calculation $used_bytes = convert_to_bytes($used_value, $used_unit); $total_bytes = convert_to_bytes($total_value, $total_unit); // Calculate percentage if ($total_bytes > 0 && $used_bytes > 0) { $percent = ($used_bytes / $total_bytes) * 100; $available_bytes = $total_bytes - $used_bytes; return array( 'used' => $used_value, 'total' => $total_value, 'available' => $total_value - $used_value, 'percent' => round($percent, 2), 'used_bytes' => $used_bytes, 'total_bytes' => $total_bytes, 'available_bytes' => $available_bytes, 'used_unit' => $used_unit, 'total_unit' => $total_unit, 'label' => '' ); } } // Try alternative NCPA format without quotes: used=0.69GiB;;; free=4.81GiB;;; total=5.82GiB;;; if ($used_value === null || $total_value === null) { $used_value = null; $used_unit = ''; $total_value = null; $total_unit = ''; if (preg_match("/\bused\s*=\s*([0-9.]+)([KMGT]?(?:IB|B)?);/i", $perfdata, $used_matches)) { $used_value = floatval($used_matches[1]); $used_unit = isset($used_matches[2]) ? strtoupper(trim($used_matches[2])) : ''; } if (preg_match("/\btotal\s*=\s*([0-9.]+)([KMGT]?(?:IB|B)?);/i", $perfdata, $total_matches)) { $total_value = floatval($total_matches[1]); $total_unit = isset($total_matches[2]) ? strtoupper(trim($total_matches[2])) : ''; } if ($used_value !== null && $total_value !== null && $total_value > 0) { if (empty($total_unit) && !empty($used_unit) && $total_value > 0) { $total_unit = $used_unit; } $used_bytes = convert_to_bytes($used_value, $used_unit); $total_bytes = convert_to_bytes($total_value, $total_unit); if ($total_bytes > 0 && $used_bytes > 0) { $percent = ($used_bytes / $total_bytes) * 100; $available_bytes = $total_bytes - $used_bytes; return array( 'used' => $used_value, 'total' => $total_value, 'available' => $total_value - $used_value, 'percent' => round($percent, 2), 'used_bytes' => $used_bytes, 'total_bytes' => $total_bytes, 'available_bytes' => $available_bytes, 'used_unit' => $used_unit, 'total_unit' => $total_unit, 'label' => '' ); } } } // Try check_local_disk / single-metric format: 'label'=value[unit];warn;crit;min;max // Supports quoted labels with special chars (e.g. 'C:\_Label:__Serial_Number_2c3f47f4'=91445.6016MB;111560;132477;0;139450) // If multiple metrics present, pick the one with highest percent used (most saturated) $label_pattern = "(?:'([^']*)'|[^=\s]+)"; $value_pattern = "=([0-9.]+)([KMGT]?(?:IB|B)?);[^;]*;[^;]*;[^;]*;([0-9.]+)([KMGT]?(?:IB|B)?)?"; $full_pattern = '/' . $label_pattern . $value_pattern . '/i'; if (preg_match_all($full_pattern, $perfdata, $all_matches, PREG_SET_ORDER)) { $best = null; $best_percent = -1.0; foreach ($all_matches as $m) { $label = isset($m[1]) ? trim($m[1]) : ''; $used = floatval($m[2]); $used_unit = isset($m[3]) ? strtoupper(trim($m[3])) : ''; $total = isset($m[4]) ? floatval($m[4]) : 0; $total_unit = isset($m[5]) ? strtoupper(trim($m[5])) : ''; if (empty($total_unit) && !empty($used_unit) && $total > 0) { $total_unit = $used_unit; } $used_bytes = convert_to_bytes($used, $used_unit); $total_bytes = $total > 0 ? convert_to_bytes($total, $total_unit) : 0; if ($total_bytes <= 0 || $used_bytes <= 0) { continue; } $percent = ($used_bytes / $total_bytes) * 100; if ($percent > $best_percent) { $best_percent = $percent; $available_bytes = $total_bytes - $used_bytes; $best = array( 'used' => $used, 'total' => $total, 'available' => $total - $used, 'percent' => round($percent, 2), 'used_bytes' => $used_bytes, 'total_bytes' => $total_bytes, 'available_bytes' => $available_bytes, 'used_unit' => $used_unit, 'total_unit' => $total_unit, 'label' => $label ); } } if ($best !== null) { return $best; } } // Fallback: single match without label (original pattern) /=4985MiB;9592;10791;0;11991 // Pattern: Handle binary units (KiB, MiB, GiB, TiB) and decimal units (KB, MB, GB, TB) // Matches: label=value[unit];warn;crit;min;max[unit] $pattern = '/[^=]*=([0-9.]+)([KMGT]?(?:IB|B)?);[^;]*;[^;]*;[^;]*;([0-9.]+)([KMGT]?(?:IB|B)?)?/i'; if (preg_match($pattern, $perfdata, $matches)) { $used = floatval($matches[1]); $used_unit = isset($matches[2]) ? strtoupper(trim($matches[2])) : ''; $total = isset($matches[3]) ? floatval($matches[3]) : 0; $total_unit = isset($matches[4]) ? strtoupper(trim($matches[4])) : ''; // If total has no unit but used has a unit, assume total uses same unit if (empty($total_unit) && !empty($used_unit) && $total > 0) { $total_unit = $used_unit; } // Convert to bytes for calculation if units are present $used_bytes = convert_to_bytes($used, $used_unit); $total_bytes = $total > 0 ? convert_to_bytes($total, $total_unit) : 0; // Calculate percentage if we have both used and total if ($total_bytes > 0 && $used_bytes > 0) { $percent = ($used_bytes / $total_bytes) * 100; $available_bytes = $total_bytes - $used_bytes; return array( 'used' => $used, 'total' => $total, 'available' => $total - $used, 'percent' => round($percent, 2), 'used_bytes' => $used_bytes, 'total_bytes' => $total_bytes, 'available_bytes' => $available_bytes, 'used_unit' => $used_unit, 'total_unit' => $total_unit, 'label' => '' ); } // If we have total but couldn't convert (no units), try direct calculation if ($total > 0 && $used > 0 && empty($used_unit) && empty($total_unit)) { $percent = ($used / $total) * 100; $available = $total - $used; return array( 'used' => $used, 'total' => $total, 'available' => $available, 'percent' => round($percent, 2), 'used_bytes' => 0, 'total_bytes' => 0, 'available_bytes' => 0, 'used_unit' => '', 'total_unit' => '', 'label' => '' ); } } return false; } /** * Convert value with unit to bytes * Supports both binary (KiB, MiB, GiB, TiB) and decimal (KB, MB, GB, TB) units * * @param float $value Numeric value * @param string $unit Unit (B, KB, MB, GB, TB, KiB, MiB, GiB, TiB, K, M, G, T, %) * @return float Value in bytes (or original if percentage or no unit) */ function convert_to_bytes($value, $unit) { $unit = strtoupper(trim($unit)); // Handle binary units (KiB, MiB, GiB, TiB) - base 1024 if (strpos($unit, 'IB') !== false || strpos($unit, 'I') !== false) { switch ($unit) { case 'TIB': case 'TI': return $value * 1024 * 1024 * 1024 * 1024; case 'GIB': case 'GI': return $value * 1024 * 1024 * 1024; case 'MIB': case 'MI': return $value * 1024 * 1024; case 'KIB': case 'KI': return $value * 1024; default: return $value; } } // Handle decimal units (KB, MB, GB, TB) or single letter (K, M, G, T) switch ($unit) { case 'TB': case 'T': return $value * 1024 * 1024 * 1024 * 1024; case 'GB': case 'G': return $value * 1024 * 1024 * 1024; case 'MB': case 'M': return $value * 1024 * 1024; case 'KB': case 'K': return $value * 1024; case 'B': case '%': case '': default: return $value; } } /** * Format bytes to human-readable format * * @param float $bytes Number of bytes * @param string $unit Preferred unit (GB, MB, etc.) or empty for auto * @return array Array with 'value' and 'unit' */ function format_bytes($bytes, $unit = '') { if ($bytes == 0) { return array('value' => 0, 'unit' => 'GB'); } // If unit is specified, use it if (!empty($unit)) { $unit = strtoupper($unit); switch ($unit) { case 'TB': return array('value' => round($bytes / (1024 * 1024 * 1024 * 1024), 1), 'unit' => 'TB'); case 'GB': return array('value' => round($bytes / (1024 * 1024 * 1024), 1), 'unit' => 'GB'); case 'MB': return array('value' => round($bytes / (1024 * 1024), 1), 'unit' => 'MB'); case 'KB': return array('value' => round($bytes / 1024, 1), 'unit' => 'KB'); default: break; } } // Auto-format based on size if ($bytes >= 1024 * 1024 * 1024 * 1024) { return array('value' => round($bytes / (1024 * 1024 * 1024 * 1024), 1), 'unit' => 'TB'); } elseif ($bytes >= 1024 * 1024 * 1024) { return array('value' => round($bytes / (1024 * 1024 * 1024), 1), 'unit' => 'GB'); } elseif ($bytes >= 1024 * 1024) { return array('value' => round($bytes / (1024 * 1024), 1), 'unit' => 'MB'); } elseif ($bytes >= 1024) { return array('value' => round($bytes / 1024, 1), 'unit' => 'KB'); } else { return array('value' => round($bytes, 1), 'unit' => 'B'); } } /** * Route the request call from script main.js */ function route_request() { // Set JSON header early, before any output if (!headers_sent()) { header('Content-Type: application/json'); } // grab request values $mode = grab_request_var('mode'); switch ( $mode ) { case 'services': generate_saturationreport_services(); break; default: echo json_encode(array('error' => 'Invalid mode')); break; } } /** * Get storage saturation report service data * Filters services with trackvolume custom variable = 1 or "true" * Returns array of service data */ function get_saturationreport_services_data() { $services = array(); // Check if required functions are available if (!function_exists('get_data_service_status')) { return array('error' => 'get_data_service_status() function not available'); } if (!function_exists('get_xml_custom_service_variable_status')) { return array('error' => 'get_xml_custom_service_variable_status() function not available'); } try { // Get all services $request_args = array(); $service_objects = get_data_service_status($request_args); if (!is_array($service_objects)) { return array('error' => 'Service objects is not an array'); } // Filter services by trackvolume custom variable foreach ($service_objects as $obj) { if (!is_array($obj)) { continue; } $host_name = $obj['host_name']; $service_description = $obj['service_description']; // Get custom variables $args = array( "host_name" => $host_name, "service_description" => $service_description ); $xml_result = get_xml_custom_service_variable_status($args); // Check if trackvolume is enabled $has_trackvolume = false; if ($xml_result && isset($xml_result->customservicevarstatus->customvars)) { $has_trackvolume = has_trackvolume_enabled($xml_result->customservicevarstatus->customvars); } if (!$has_trackvolume) { continue; } // Get service ID and name $id = intval($obj['service_object_id']); $name = isset($obj['display_name']) && !empty($obj['display_name']) ? $obj['display_name'] : $service_description; // Get perfdata - try direct from get_data_service_status() first (like diskpressure) $perfdata = isset($obj['perfdata']) ? $obj['perfdata'] : ''; // If not available, get it separately if (empty($perfdata) && function_exists('get_xml_service_status')) { $backendargs = array( "cmd" => "getservicestatus", "host_name" => "=" . $host_name, "service_description" => "=" . $service_description ); $xml_status = get_xml_service_status($backendargs); if ($xml_status && isset($xml_status->servicestatus)) { $perfdata = strval($xml_status->servicestatus->performance_data); } } // Parse disk usage from perfdata $disk_info = parse_disk_perfdata($perfdata); // Build service data $service_data = array( 'service_id' => $id, 'name' => $name, 'host_name' => $host_name, 'service_description' => $service_description, 'perfdata' => $perfdata ); // Add disk usage information if available if ($disk_info !== false) { $service_data['disk_usage_percent'] = $disk_info['percent']; $service_data['disk_used'] = $disk_info['used']; $service_data['disk_total'] = $disk_info['total']; $service_data['disk_available'] = $disk_info['available']; $service_data['disk_used_bytes'] = $disk_info['used_bytes']; $service_data['disk_total_bytes'] = $disk_info['total_bytes']; $service_data['disk_available_bytes'] = $disk_info['available_bytes']; $service_data['disk_used_unit'] = $disk_info['used_unit']; $service_data['disk_total_unit'] = $disk_info['total_unit']; $service_data['disk_label'] = isset($disk_info['label']) ? $disk_info['label'] : ''; // Caption: use perfdata volume label when present (e.g. C:\_Label:__Serial_Number_...), else service description if (!empty($service_data['disk_label'])) { $service_data['caption_display'] = $service_data['disk_label']; } else { $service_data['caption_display'] = $service_data['name']; } // Format values for display $used_formatted = format_bytes($service_data['disk_used_bytes'], $service_data['disk_used_unit']); $available_formatted = format_bytes($service_data['disk_available_bytes'], $service_data['disk_total_unit']); $service_data['disk_used_display'] = $used_formatted['value'] . ' ' . $used_formatted['unit']; $service_data['disk_available_display'] = $available_formatted['value'] . ' ' . $available_formatted['unit']; $service_data['disk_percent_display'] = number_format($disk_info['percent'], 0) . '%'; } else { // Set defaults if perfdata can't be parsed $service_data['disk_usage_percent'] = 0; $service_data['disk_used'] = 0; $service_data['disk_total'] = 0; $service_data['disk_available'] = 0; $service_data['disk_used_bytes'] = 0; $service_data['disk_total_bytes'] = 0; $service_data['disk_available_bytes'] = 0; $service_data['disk_label'] = ''; $service_data['caption_display'] = $service_data['name']; $service_data['disk_used_display'] = 'N/A'; $service_data['disk_available_display'] = 'N/A'; $service_data['disk_percent_display'] = 'N/A'; } $services[$id] = $service_data; } // Sort by available space (ascending - most critical first) if (count($services) > 0) { usort($services, function($a, $b) { $available_a = isset($a['disk_available_bytes']) ? $a['disk_available_bytes'] : PHP_INT_MAX; $available_b = isset($b['disk_available_bytes']) ? $b['disk_available_bytes'] : PHP_INT_MAX; return $available_a <=> $available_b; }); // Re-index array $services = array_values($services); } } catch (Exception $e) { return array('error' => 'Exception: ' . $e->getMessage()); } catch (Error $e) { return array('error' => 'Fatal error: ' . $e->getMessage()); } // Return empty array if no services found (not an error) return $services; } /** * Generate JSON output for services */ function generate_saturationreport_services() { $services = get_saturationreport_services_data(); echo json_encode($services); }