로그인
KO
games/xencenter-xenadmin-xcp-jumphp / src / Xapi / Perfmon.php · commit fb1b7c9
<?phpnamespace App\Xapi;/** * The performance alarms XAPI's perfmon daemon watches: the XML of the key "perfmon" of a VM's, host's or SR's other_config, as * XenCenter writes it (VM.add_to_other_config $vm 'perfmon' $xml): * *   <config><variable><name value="cpu_usage"/><alarm_trigger_level value="0.9"/><alarm_trigger_period value="60"/> *   <alarm_auto_inhibit_period value="3600"/></variable>...</config> * * Levels: a ratio 0..1 for cpu_usage and physical_utilisation, bytes per second for network_usage / disk_usage, KiB for * memory_free_kib (an alarm when it goes BELOW: alarm_trigger_sense low), MiB per second for sr_io_throughput_total_per_host. * When the value stays over the level for the trigger period, perfmon writes a message ALARM (body "value: …\nconfig: <variable>…"), * then keeps quiet for the inhibit period. */final class Perfmon{    /** what XenCenter offers for each kind of object */    public const VARIABLES = [        'vm' => ['cpu_usage', 'network_usage', 'disk_usage'],        'host' => ['cpu_usage', 'memory_free_kib', 'network_usage'],        'sr' => ['physical_utilisation', 'sr_io_throughput_total_per_host'],    ];    /** @return array<string, array{level: float, period: int, inhibit: int}> variable name => its alarm */    public static function parse(string $xml): array    {        $out = [];        preg_match_all('#<variable>(.*?)</variable>#s', $xml, $variables);        foreach ($variables[1] as $variable) {            $value = fn (string $tag): ?string => preg_match('#<' . $tag . '\s+value="([^"]*)"#', $variable, $m) ? html_entity_decode($m[1], ENT_XML1) : null;            $name = $value('name');            $level = $value('alarm_trigger_level');            if ($name === null || $name === '' || $level === null) {                continue;            }            $out[$name] = ['level' => (float)$level, 'period' => (int)($value('alarm_trigger_period') ?? 60), 'inhibit' => (int)($value('alarm_auto_inhibit_period') ?? 3600)];        }        return $out;    }    /** @param array<string, array{level: float, period: int, inhibit: int}> $alarms */    public static function build(array $alarms): string    {        $xml = '<config>';        foreach ($alarms as $name => $a) {            $xml .= self::variable($name, $a, '');        }        return $xml . '</config>';    }    /** One <variable>, $indent between the lines (perfmon's message bodies are indented with tabs, one tag per line). @param array{level: float, period: int, inhibit: int} $a */    public static function variable(string $name, array $a, string $indent = "\n\t"): string    {        $end = $indent === '' ? '' : "\n";        return '<variable>' . $indent . '<name value="' . htmlspecialchars($name, ENT_XML1) . '"/>' . $indent            . '<alarm_trigger_level value="' . self::number($a['level']) . '"/>' . $indent . '<alarm_trigger_period value="' . $a['period'] . '"/>' . $indent            . '<alarm_auto_inhibit_period value="' . $a['inhibit'] . '"/>' . ($name === 'memory_free_kib' ? $indent . '<alarm_trigger_sense value="low"/>' : '') . $end . '</variable>';    }    /** The body of the ALARM message perfmon writes. @param array{level: float, period: int, inhibit: int} $a */    public static function alarmBody(string $name, float $value, array $a): string    {        return 'value: ' . sprintf('%.6F', $value) . "\nconfig:\n" . self::variable($name, $a) . "\n";    }    /** Below the level is the alarm for free memory, above it for the rest. */    public static function fires(string $name, float $value, float $level): bool    {        return $name === 'memory_free_kib' ? $value < $level : $value > $level;    }    private static function number(float $x): string    {        $s = rtrim(rtrim(sprintf('%.6F', $x), '0'), '.');        return $s === '' || $s === '-0' ? '0' : $s;    }}