Sign in
EN
<?phpnamespace App\Xapi;/** * The answer of /rrd_updates (XML): columns named "AVERAGE:host:<uuid>:cpu0", "AVERAGE:vm:<uuid>:memory"... and rows of values. * series('vm', $uuid, 'cpu0') = [[t, value], ...] oldest first. */final class Rrd{    /** @param array<string, list<array{0: int, 1: float}>> $columns legend => points */    public function __construct(public array $columns = [], public int $end = 0, public int $step = 5)    {    }    public static function parse(string $xml): self    {        preg_match_all('#<entry>([^<]*)</entry>#', $xml, $legend);        $names = $legend[1];        $end = preg_match('#<end>(\d+)</end>#', $xml, $m) ? (int)$m[1] : 0;        $step = preg_match('#<step>(\d+)</step>#', $xml, $m) ? (int)$m[1] : 5;        $columns = array_fill_keys($names, []);        preg_match_all('#<row>(.*?)</row>#s', $xml, $rows);        foreach ($rows[1] as $row) {            $t = preg_match('#<t>(\d+)</t>#', $row, $m) ? (int)$m[1] : 0;            preg_match_all('#<v>([^<]*)</v>#', $row, $values);            foreach ($values[1] as $i => $value) {                if (isset($names[$i]) && is_numeric($value)) {                    $columns[$names[$i]][] = [$t, (float)$value];                }            }        }        foreach ($columns as &$points) {            usort($points, fn ($a, $b) => $a[0] <=> $b[0]);        }        return new self($columns, $end, $step);    }    /** @return list<array{0: int, 1: float}> */    public function series(string $kind, string $uuid, string $name): array    {        return $this->columns["AVERAGE:$kind:$uuid:$name"] ?? [];    }    /** @return list<string> the data source names of one object: cpu0, memory, vif_0_rx... */    public function names(string $kind, string $uuid): array    {        $out = [];        $prefix = "AVERAGE:$kind:$uuid:";        foreach (array_keys($this->columns) as $legend) {            if (str_starts_with($legend, $prefix)) {                $out[] = substr($legend, strlen($prefix));            }        }        return $out;    }    /** The sum of several series, point by point (all CPUs, all network cards). @param list<string> $names */    public function sum(string $kind, string $uuid, array $names, float $scale = 1.0): array    {        $by = [];        foreach ($names as $name) {            foreach ($this->series($kind, $uuid, $name) as [$t, $v]) {                $by[$t] = ($by[$t] ?? 0) + $v * $scale;            }        }        ksort($by);        $out = [];        foreach ($by as $t => $v) {            $out[] = [(int)$t, $v];        }        return $out;    }    /** @param list<string> $names */    public function average(string $kind, string $uuid, array $names, float $scale = 1.0): array    {        $sum = $this->sum($kind, $uuid, $names, $scale);        $n = max(1, count($names));        return array_map(fn ($p) => [$p[0], $p[1] / $n], $sum);    }}