feat: add bulk delete with validation to IP Pool and update provisioning logic and email notifications
This commit is contained in:
@@ -9,6 +9,7 @@ use App\Models\IpPool;
|
||||
use App\Models\ModuleIpAddress;
|
||||
use App\Models\ModuleVmware;
|
||||
use App\Models\Os;
|
||||
use App\Http\Services\vCenter as vCenterService;
|
||||
use App\Models\vCenter;
|
||||
use App\Services\VsphereHostSshService;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@@ -29,13 +30,12 @@ use Illuminate\Support\Facades\Log;
|
||||
* [1] Resolve network config from order item → product → ipAddress template
|
||||
* [2] for attempt 1..MAX_ATTEMPTS:
|
||||
* [3] Pick a free IP with DB transaction + lock (reserveIp)
|
||||
* [4] Pre-check: ping IP — if reachable → release and try next
|
||||
* [5] Call assignIPLinux.ps1 / assignIPWindows.ps1 via SSH
|
||||
* [6] On FAIL → release IP, try next
|
||||
* [7] Call verifyIPLinux.ps1 / verifyIPWindows.ps1 via SSH
|
||||
* [8] On FAIL → release IP, try next
|
||||
* [9] SUCCESS → persistIp() → dispatch SendVmCredentialsEmailJob
|
||||
* [10] All attempts exhausted → mark VM inactive (status=0)
|
||||
* [4] Call assignIPLinux.ps1 / assignIPWindows.ps1 via SSH
|
||||
* [5] On FAIL → release IP, try next
|
||||
* [6] Call verifyIPLinux.ps1 / verifyIPWindows.ps1 via SSH
|
||||
* [7] On FAIL → release IP, try next
|
||||
* [8] SUCCESS → persistIp() → dispatch SendVmCredentialsEmailJob
|
||||
* [9] All attempts exhausted → mark VM inactive (status=0)
|
||||
*/
|
||||
class AssignIpToGuestJob implements ShouldQueue
|
||||
{
|
||||
@@ -58,7 +58,7 @@ class AssignIpToGuestJob implements ShouldQueue
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
public function handle(VsphereHostSshService $ssh, vCenter $vCenterService): void
|
||||
public function handle(VsphereHostSshService $ssh, vCenterService $vCenterService): void
|
||||
{
|
||||
Log::info('AssignIpToGuestJob: start', [
|
||||
'module_vmware_id' => $this->moduleVmwareId,
|
||||
@@ -118,16 +118,6 @@ class AssignIpToGuestJob implements ShouldQueue
|
||||
|
||||
$ip = $reservation->ip;
|
||||
|
||||
// [4] Pre-check: skip IPs that are already reachable (in use by another host)
|
||||
if ($this->isIpReachable($ip)) {
|
||||
Log::warning('AssignIpToGuestJob: IP reachable (likely in use), skipping', [
|
||||
'ip' => $ip,
|
||||
'attempt' => $attempt,
|
||||
]);
|
||||
$this->releaseIp($reservation);
|
||||
continue;
|
||||
}
|
||||
|
||||
// [5] Assign IP into VM guest OS via PS1 script
|
||||
$assignOutput = $ssh->assignIp(
|
||||
vmId: $this->vmId,
|
||||
@@ -339,16 +329,6 @@ class AssignIpToGuestJob implements ShouldQueue
|
||||
// Micro-helpers
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Ping the IP from the Laravel host (1 packet, 1 s timeout).
|
||||
* Returns true if reachable → the IP is already in use by something else.
|
||||
*/
|
||||
private function isIpReachable(string $ip): bool
|
||||
{
|
||||
exec(sprintf('ping -c 1 -W 1 %s 2>/dev/null', escapeshellarg($ip)), $output, $exitCode);
|
||||
return $exitCode === 0;
|
||||
}
|
||||
|
||||
/** PS1 scripts output exactly "SUCCESS" or "FAIL". */
|
||||
private function isSuccess(string $output): bool
|
||||
{
|
||||
|
||||
@@ -64,19 +64,23 @@ class SendVmCredentialsEmailJob implements ShouldQueue
|
||||
$orderItem = $vm->order?->orderItems?->first();
|
||||
$months = (int) ($orderItem?->months ?? 0);
|
||||
|
||||
$osGroupName = $vm->os?->osGroup?->name ?? '';
|
||||
$isWindows = str_contains(strtolower($osGroupName), 'windows');
|
||||
$osUsername = $isWindows ? 'Administrator' : 'root';
|
||||
|
||||
$emailData = [
|
||||
'company_name' => $settingValue->company ?? 'Thế Giới Số',
|
||||
'company_logo' => $settingValue->logo ?? '',
|
||||
'client_name' => trim($customer->firstname . ' ' . $customer->lastname),
|
||||
'client_name' => $customer->fullname ?? '',
|
||||
'product_name' => $orderItem?->getProductAttribute()?->name ?? 'Cloud VPS',
|
||||
'product_billing' => $this->billingLabel($months),
|
||||
'product_price' => $orderItem ? number_format((float) $orderItem->price, 0, ',', '.') : 'N/A',
|
||||
'service_host' => $vm->ip ?? 'N/A',
|
||||
'service_username' => $vm->user,
|
||||
'service_username' => $osUsername,
|
||||
'service_port' => $vm->port,
|
||||
'service_password' => $vm->plain_password ?? '(xem tài khoản quản lý)',
|
||||
'os_name' => $vm->os
|
||||
? (($vm->os->osGroup?->name ?? '') . ' ' . $vm->os->version)
|
||||
? (($osGroupName . ' ' . $vm->os->version))
|
||||
: 'Unknown OS',
|
||||
'service_due_exp' => $vm->next_due_date
|
||||
? date('d/m/Y', (int) $vm->next_due_date)
|
||||
@@ -117,7 +121,7 @@ class SendVmCredentialsEmailJob implements ShouldQueue
|
||||
// Notify admin with VM credentials after successful provisioning
|
||||
$order = $vm->order;
|
||||
AdminOrderNotificationJob::dispatch([
|
||||
'customer_name' => trim($customer->firstname . ' ' . $customer->lastname),
|
||||
'customer_name' => $customer->fullname ?? '',
|
||||
'customer_email' => $customer->email,
|
||||
'client_ip' => null,
|
||||
'order_num' => $order?->order_num ?? $order?->id ?? 'N/A',
|
||||
@@ -127,7 +131,7 @@ class SendVmCredentialsEmailJob implements ShouldQueue
|
||||
'total' => $orderItem?->price ?? 0,
|
||||
'created_at' => $order?->created_at?->format('d/m/Y H:i:s') ?? now()->format('d/m/Y H:i:s'),
|
||||
'ip' => $vm->ip ?? '',
|
||||
'username' => $vm->user ?? '',
|
||||
'username' => $osUsername,
|
||||
'password' => $vm->plain_password ?? '',
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user