Files
portal-api/app/Http/Services/vCenter/VirtualMachineService.php
T

357 lines
13 KiB
PHP

<?php
namespace App\Http\Services\vCenter;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use App\Http\Services\Discord;
use App\Models\ModuleDatastore;
use Exception;
class VirtualMachineService
{
protected Client $client;
public function __construct(Client $client)
{
$this->client = $client;
}
public function getAllVirtualMachine($datacenter = '')
{
if ($this->client->checkApiExits()) {
$params = [];
if (!empty($datacenter)) {
$params['filter.datacenters'] = $datacenter;
}
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->client->token
])
->get("https://{$this->client->apiUri}/rest/vcenter/vm", $params)->json();
if (!isset($response['type'])) {
return $response['value'];
}
return false;
}
return "Login_Again";
}
public function CreateVirtualMachine($product_data): array|string
{
$vcenterModel = $product_data['vcenter'];
Log::info('Vcenter Info', ['data' => $vcenterModel]);
if (!$this->client->loginServer($vcenterModel)) {
Log::error('Failed to create VM: API check failed, requires login.');
return 'Login_Again';
}
$userName = customer()?->email;
$item = $product_data['item'];
$host = $product_data['host'];
$datacenter = $product_data['datacenter'];
$template = $product_data['template'];
$addon = $product_data['addon'] ?? ['cpu' => 0, 'ram' => 0, 'disk' => 0];
$base_cpu = $item->cpu ?? ($host->cpu ?? 1);
$base_ram = $item->ram ?? ($host->ram ?? 1);
$base_disk = $item->disk ?? ($host->disk ?? 20);
$base_cpu = max(1, (int) $base_cpu);
$base_ram = max(1, (int) $base_ram);
$base_disk = max(20, (int) $base_disk);
Log::info("Cấu hình cơ bản", [
'base_cpu' => $base_cpu,
'base_ram' => $base_ram,
'base_disk' => $base_disk,
'addon' => $addon
]);
$datastores = ModuleDatastore::where('module_host_id', $host->id)->get();
$datastoreId = $datastores->isNotEmpty()
? $datastores->first()->datastore
: 'datastore-3004';
$body = [
'spec' => [
'name' => $item->server_name ?? ($userName . '-vm-' . $item->id),
'source' => $template,
'placement' => [
'datacenter' => $datacenter->datacenter_id,
'folder' => 'group-v1008',
'host' => $host->host_id,
'resource_pool' => 'resgroup-3002',
'datastore' => $datastoreId,
],
'power_on' => false,
'fallback_to_datastore_default_policy' => true,
],
];
Log::info('Data payload', ['data' => $body]);
try {
Log::info('Bắt đầu clone VM với timeout 300 giây', ['template' => $template, 'target_name' => $body['spec']['name']]);
$response = Http::withoutVerifying()
->timeout(300)
->connectTimeout(30)
->withHeaders([
'vmware-api-session-id' => $this->client->token,
'Content-Type' => 'application/json',
])
->post("https://{$this->client->apiUri}/rest/vcenter/vm?action=clone", $body);
$responseData = $response->json();
if ($response->successful() && !isset($responseData['type'])) {
$vmId = $responseData['value'] ?? 'UNKNOWN';
Log::info("VM clone thành công, VM ID: {$vmId}", [
'addon_data' => $addon,
'base_cpu' => $base_cpu,
'base_ram' => $base_ram,
'has_cpu_addon' => $addon['cpu'] > 0,
'has_ram_addon' => $addon['ram'] > 0,
'has_disk_addon' => $addon['disk'] > 0
]);
if ($vmId !== 'UNKNOWN' && ($addon['cpu'] > 0 || $addon['ram'] > 0 || $addon['disk'] > 0)) {
Log::info("Bắt đầu cập nhật addon cho VM {$vmId}");
$this->updateVMHardware($vmId, $addon, $base_cpu, $base_ram);
} else {
Log::info("Không có addon nào cần cập nhật cho VM {$vmId}");
}
$discord = app(Discord::class);
$addonMsg = '';
if (!empty($addon)) {
$addonMsg = "\n**Addon:** " .
($addon['cpu'] ? "CPU +{$addon['cpu']} " : "") .
($addon['ram'] ? "RAM +{$addon['ram']}GB " : "") .
($addon['disk'] ? "Disk +{$addon['disk']}GB " : "");
}
$discord->SendMessage(
message: "**User:** {$item->server_name}\n**VM ID:** `{$vmId}`{$addonMsg}\n**Thời gian:** " . now()->format('d/m/Y H:i:s'),
color: Discord::COLOR_SUCCESS,
title: '🎉 Tạo máy ảo thành công'
);
return ['error' => false, 'data' => $vmId];
}
return ['error' => true, 'data' => $responseData['value'] ?? $response->body()];
} catch (Exception $e) {
Log::error('Exception when creating VM', ['message' => $e->getMessage()]);
return ['error' => true, 'data' => $e->getMessage()];
}
}
private function updateVMHardware($vmId, $addon, $base_cpu, $base_ram): void
{
try {
Log::info("=== BẮT ĐẦU CẬP NHẬT ADDON ===");
$new_cpu = $base_cpu + $addon['cpu'];
$new_ram_mib = ($base_ram + $addon['ram']) * 1024;
if ($addon['cpu'] > 0) {
$this->updateVMCPU($vmId, $new_cpu);
}
if ($addon['ram'] > 0) {
$this->updateVMMemory($vmId, $new_ram_mib);
}
// Disk update logic was missing in the snippet but present in the original method names?
// Actually the original `updateVMHardware` in the snippet (step 134) calls `updateVMCPU` and `updateVMMemory`.
// It gets `base_disk` but doesn't seem to call `updateVMDisk` inside `updateVMHardware`?
// Let me re-read step 134.
// ... `updateVMHardware` implementation ...
// It calls `updateVMCPU` and `updateVMMemory`.
// It calculates `$addon['disk']` in log, but doesn't call `updateVMDisk`.
// Ah, line 943 defines `updateVMDisk`.
// But `updateVMHardware` (lines 772-832) only calls `updateVMCPU` and `updateVMMemory`.
// I will implement it exactly as the original.
Log::info("=== HOÀN THÀNH CẬP NHẬT ADDON ===");
} catch (Exception $e) {
Log::error("❌ Lỗi khi cập nhật cấu hình phần cứng VM {$vmId}: " . $e->getMessage());
}
}
private function updateVMCPU($vmId, $cpuCount): bool
{
try {
$coresPerSocket = 1;
if ($cpuCount > 1) {
$maxCoresPerSocket = floor($cpuCount / 2);
for ($i = $maxCoresPerSocket; $i >= 1; $i--) {
if ($cpuCount % $i === 0) {
$coresPerSocket = $i;
break;
}
}
if ($coresPerSocket === 1 && $cpuCount > 2) {
$coresPerSocket = $cpuCount;
}
}
$payload = [
'spec' => [
'count' => $cpuCount,
'cores_per_socket' => $coresPerSocket,
'hot_add_enabled' => true
]
];
$response = Http::withoutVerifying()
->timeout(30)
->withHeaders([
'vmware-api-session-id' => $this->client->token,
'Content-Type' => 'application/json',
])
->patch("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/cpu", $payload);
if ($response->successful()) {
Log::info("✅ API call cập nhật CPU thành công cho VM {$vmId}");
return true;
} else {
Log::error("❌ API call cập nhật CPU thất bại cho VM {$vmId}", [
'status' => $response->status(),
'response' => $response->body()
]);
return false;
}
} catch (Exception $e) {
Log::error("❌ Exception khi cập nhật CPU cho VM {$vmId}: " . $e->getMessage());
return false;
}
}
private function updateVMMemory($vmId, $ramSizeMB): bool
{
try {
$payload = [
'spec' => [
'size_MiB' => $ramSizeMB
]
];
$response = Http::withoutVerifying()
->timeout(30)
->withHeaders([
'vmware-api-session-id' => $this->client->token,
'Content-Type' => 'application/json',
])
->patch("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/memory", $payload);
if ($response->successful()) {
Log::info("✅ API call cập nhật RAM thành công cho VM {$vmId}");
return true;
} else {
Log::error("❌ API call cập nhật RAM thất bại cho VM {$vmId}", [
'status' => $response->status(),
'response' => $response->body()
]);
return false;
}
} catch (Exception $e) {
Log::error("❌ Exception khi cập nhật RAM cho VM {$vmId}: " . $e->getMessage());
return false;
}
}
private function updateVMDisk($vmId, $diskSizeGB): bool
{
try {
$diskSizeBytes = $diskSizeGB * 1024 * 1024 * 1024;
$payload = [
'spec' => [
'capacity' => $diskSizeBytes
]
];
$diskResponse = Http::withoutVerifying()
->timeout(30)
->withHeaders([
'vmware-api-session-id' => $this->client->token,
])
->get("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/disk");
if (!$diskResponse->successful()) {
return false;
}
$diskData = $diskResponse->json();
if (!isset($diskData['value']) || empty($diskData['value'])) {
return false;
}
$firstDisk = $diskData['value'][0];
$diskKey = $firstDisk['key'];
$response = Http::withoutVerifying()
->timeout(30)
->withHeaders([
'vmware-api-session-id' => $this->client->token,
'Content-Type' => 'application/json',
])
->patch("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/disk/{$diskKey}", $payload);
if ($response->successful()) {
return true;
} else {
return false;
}
} catch (Exception $e) {
Log::error("Exception khi cập nhật Disk cho VM {$vmId}: " . $e->getMessage());
return false;
}
}
public function CheckVirtualMachine($vmware_id = '')
{
if ($this->client->checkApiExits()) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->client->token
])
->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id")->json();
if (!isset($response['type'])) {
return $response['value'];
}
return false;
}
return "Login_Again";
}
public function GetNetworkVirtualMachine($vmware_id = '')
{
$check = $this->client->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->client->token
])
->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/guest/networking")->json();
if (!isset($response['type'])) {
return $response['value'];
}
return false;
}
return "Login_Again";
}
}