73 lines
2.4 KiB
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];
|
|
}
|
|
}
|