CluPilotCloud/app/Services/Proxmox/VmTemplateCheck.php

73 lines
2.4 KiB
PHP

<?php
namespace App\Services\Proxmox;
use App\Models\Host;
use App\Models\PlanVersion;
use Throwable;
/**
* Whether every published plan's VM template actually EXISTS on a node, not
* merely that a `template_vmid` is filled in somewhere.
*
* Asks the same question as the onboarding step VerifyVmTemplate, but over
* ALL active hosts instead of one, and BEFORE a purchase instead of after:
* ProvisioningChecks::vm_template (Task 8) is a field check only — it cannot
* tell a `template_vmid` that was typed by mistake from one that is really
* there, and asking Proxmox on every page load does not belong on a
* readiness page. This is the on-demand button that actually asks.
*/
final class VmTemplateCheck
{
public function __construct(private ProxmoxClient $pve) {}
/** @return array<string, mixed> */
public function run(): array
{
$required = PlanVersion::query()
->whereNotNull('published_at')
->whereNotNull('template_vmid')
->where(fn ($q) => $q->whereNull('available_until')->orWhere('available_until', '>', now()))
->pluck('template_vmid')->map(fn ($v) => (int) $v)->unique()->values()->all();
// A catalogue with no published version requires no template. The
// same case VerifyVmTemplate explicitly lets through.
if ($required === []) {
return ['ok' => true, 'reason' => 'nothing_published'];
}
$hosts = Host::query()->where('status', 'active')->whereNotNull('api_token_ref')->get();
if ($hosts->isEmpty()) {
return ['ok' => false, 'reason' => 'no_usable_host'];
}
$missing = [];
foreach ($required as $vmid) {
$found = false;
foreach ($hosts as $host) {
try {
if ($this->pve->forHost($host)->vmExists($host->node ?? 'pve', $vmid)) {
$found = true;
break;
}
} catch (Throwable) {
// A host that is not answering right now is not proof of
// a missing template. Keep looking at the others.
continue;
}
}
if (! $found) {
$missing[] = $vmid;
}
}
return $missing === []
? ['ok' => true, 'reason' => 'present']
: ['ok' => false, 'reason' => 'template_missing', 'vmids' => $missing];
}
}