<?phpnamespace App\Xapi\Fake;use App\Xapi\Perfmon;use App\Xapi\Rrd;/** * perfmon in the demo pool (part of FakeXapi): every 5 s the alarms of the "perfmon" key of the VMs', hosts' and SRs' other_config * are checked against the same figures /rrd_updates gives; a value over its level for the trigger period makes a message ALARM * whose body is what XAPI writes ("value: 0.953000\nconfig:\n<variable>…"), then nothing for the inhibit period. The demo VM * db-primary comes with a CPU alarm (over 30% for a minute), so the Alerts fill up while the demo runs. */trait AlertsOps{ /** @var array<string, array{above: float, quiet: float}> "ref variable" => seconds over the level, fake time until another alarm may come */ private array $alarmState = []; private float $alarmsIn = 5.0; private float $alarmsElapsed = 0.0; private function buildAlerts(): void { foreach ($this->db['vm'] as $ref => $vm) { if ($vm['name_label'] === 'db-primary') { $other = (array)$vm['other_config']; $other['perfmon'] = Perfmon::build(['cpu_usage' => ['level' => 0.3, 'period' => 60, 'inhibit' => 3600]]); $this->db['vm'][$ref]['other_config'] = $other; } } } private function pollAlerts(float $dt): void { $this->alarmsIn -= $dt; $this->alarmsElapsed += $dt; if ($this->alarmsIn > 0) { return; } $elapsed = $this->alarmsElapsed; $this->alarmsIn = 5.0; $this->alarmsElapsed = 0.0; $watched = []; foreach (['vm' => 'VM', 'host' => 'Host', 'sr' => 'SR'] as $class => $cls) { foreach ($this->db[$class] ?? [] as $ref => $record) { $xml = (string)($record['other_config']['perfmon'] ?? ''); if ($xml !== '' && ($class !== 'vm' || ($record['power_state'] === 'Running' && empty($record['is_a_template'])))) { $watched[] = [$class, $cls, $ref, $record, Perfmon::parse($xml)]; } } } if ($watched === []) { return; } $rrd = Rrd::parse($this->rrdXml($this->now + (int)$this->clock - 30, true, null)); foreach ($watched as [$class, $cls, $ref, $record, $alarms]) { foreach ($alarms as $name => $alarm) { $value = $this->perfmonValue($rrd, $class, $record, $name); $key = "$ref $name"; $state = $this->alarmState[$key] ?? ['above' => 0.0, 'quiet' => 0.0]; $state['quiet'] = max(0.0, $state['quiet'] - $elapsed); $state['above'] = $value !== null && Perfmon::fires($name, $value, $alarm['level']) ? $state['above'] + $elapsed : 0.0; if ($value !== null && $state['above'] >= $alarm['period'] && $state['quiet'] <= 0) { $this->add('message', ['name' => 'ALARM', 'priority' => 3, 'cls' => $cls, 'obj_uuid' => $record['uuid'], 'timestamp' => $this->date(), 'body' => Perfmon::alarmBody($name, $value, $alarm)]); $state = ['above' => 0.0, 'quiet' => (float)$alarm['inhibit']]; } $this->alarmState[$key] = $state; } } } /** The figure perfmon compares: from the RRD (CPU, network, disks, free memory) or the record (storage used). @param array<string, mixed> $record */ private function perfmonValue(Rrd $rrd, string $class, array $record, string $name): ?float { $last = function (string $kind, string $pattern) use ($rrd, $record): array { $values = []; foreach ($rrd->names($kind, (string)$record['uuid']) as $n) { if (preg_match($pattern, $n)) { $series = $rrd->series($kind, (string)$record['uuid'], $n); if ($series !== []) { $values[] = (float)end($series)[1]; } } } return $values; }; return match ("$class $name") { 'vm cpu_usage', 'host cpu_usage' => ($v = $last($class, '/^cpu\d+$/')) === [] ? null : array_sum($v) / count($v), 'vm network_usage' => ($v = $last('vm', '/^vif_\d+_(rx|tx)$/')) === [] ? null : array_sum($v), 'vm disk_usage' => ($v = $last('vm', '/^vbd_\w+_(read|write)$/')) === [] ? null : array_sum($v), 'host network_usage' => ($v = $last('host', '/^pif_\w+_(rx|tx)$/')) === [] ? null : array_sum($v), 'host memory_free_kib' => ($v = $last('host', '/^memory_free_kib$/')) === [] ? null : $v[0], 'sr physical_utilisation' => (float)$record['physical_size'] > 0 ? (float)$record['physical_utilisation'] / (float)$record['physical_size'] : null, 'sr sr_io_throughput_total_per_host' => max(0.0, 3.0 + 2.0 * sin($this->clock / 40)), // MiB/s default => null, }; }}