Files
portal-api/app/Http/Services/vCenter.php
T
2025-07-29 23:49:33 +07:00

2400 lines
91 KiB
PHP

<?php
namespace App\Http\Services;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;
use App\Models\ModuleFolder;
use Carbon\Carbon;
use Exception;
use Illuminate\Support\Facades\Http;
use Illuminate\Http\Client\ConnectionException;
use App\Http\Facade\SignRSA;
use Illuminate\Support\Str;
use phpseclib3\Net\SSH2;
class vCenter
{
protected $process = null;
public ?string $token = null;
public string $apiUri = '10.10.10.6';
private string $username = 'administrator@vsphere.local';
private string $password = 'Tgs@1238989$%^';
private int $diskCapacity = 1073741824;
private int $memory = 1024;
protected string $os;
public function loginServer(\App\Models\Vcenter $vcenterModel): bool|string
{
$this->apiUri = $vcenterModel->host;
$this->username = $vcenterModel->user;
$this->password = SignRSA::DecryptFunc($vcenterModel->password); // nếu đã mã hóa
// Log::info('Đăng nhập với user: ' . $this->username);
$response = Http::withoutVerifying()
->withBasicAuth($this->username, $this->password)
->post("https://{$this->apiUri}/rest/com/vmware/cis/session");
// Log::info('Response login: ' . $response->body());
if ($response->successful()) {
$json = $response->json();
if (isset($json['value'])) {
$this->token = $json['value'];
$vcenterModel->update(['token' => $this->token]);
// Log::info('token', ['token' => $this->token]);
return $this->token;
}
$body = $response->body();
if (!empty($body)) {
$this->token = $body;
$vcenterModel->update(['token' => $this->token]);
// Log::info('token raw string', ['token' => $this->token]);
return $this->token;
}
}
return false;
}
public function getAllDatacenter()
{
$check = $this->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/vcenter/datacenter")->json();
if (!isset($response['type'])) {
return $response['value'];
}
return false;
}
return "Login_Again";
}
public function checkApiExits(): bool
{
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/com/vmware/content/library")->json();
if (isset($response['type'])) {
return false;
}
return true;
}
public function getAllHost($datacenter = '')
{
$check = $this->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/vcenter/host", [
'filter.datacenters.1' => $datacenter
])->json();
if (!isset($response['type'])) {
return $response['value'];
}
return false;
}
return "Login_Again";
}
public function getAllFolder($datacenter = '')
{
$check = $this->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/vcenter/folder", [
'filter.datacenters.1' => $datacenter,
'filter.type' => "VIRTUAL_MACHINE",
'filter.names' => "vm"
])->json();
if (!isset($response['type'])) {
return $response['value'];
}
return false;
}
return "Login_Again";
}
public function getTemplateFromDatastore($datastore = '')
{
$allTemplate = $this->getAllTemplate();
if (!$allTemplate) {
return false;
}
foreach ($allTemplate as $template) {
$detailTemplate = $this->getTemplatebyID($template);
if ($detailTemplate && $detailTemplate === $datastore) {
return $template;
}
}
return false;
}
public function getAllTemplate()
{
$check = $this->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/com/vmware/content/library")->json();
if (!isset($response['type'])) {
return $response['value'];
}
return false;
}
return "Login_Again";
}
protected function getTemplatebyID($template = '')
{
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/com/vmware/content/library/id:$template")->json();
if (!isset($response['type'])) {
return $response['value']['storage_backings'][0]['datastore_id'];
}
return false;
}
public function getAllVersion($library = ''): bool|array|string
{
$check = $this->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/com/vmware/content/library/item", [
'library_id' => $library
])->json();
if (!isset($response['type'])) {
$library_item = [];
foreach ($response['value'] as $value) {
$value_detail = $this->getVersionDetail($value);
if ($value_detail) {
$library_item[] = [
'name' => $value_detail['name'],
'id' => $value_detail['id']
];
}
}
return $library_item;
}
return false;
}
return "Login_Again";
}
protected function getVersionDetail($version_id = '')
{
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/com/vmware/content/library/item/id:$version_id")->json();
if (!isset($response['type'])) {
return $response['value'];
}
return false;
}
public function loadNetWork($data = [], $host_id = '')
{
$this->username = $data->user;
$this->password = SignRSA::DecryptFunc($data->password);
$this->apiUri = $data->host;
$information = $this->loadInformation($host_id);
if ($information) {
if (is_array($information->Network)) {
$information = $information->Network;
} else {
$information = array($information->Network);
}
$netWork = [];
$allNetwork = $this->getAllNetwork();
if ($allNetwork && $allNetwork !== "Login_Again") {
foreach ($allNetwork as $_subItem) {
if (in_array($_subItem['network'], $information)) {
$netWork[] = $_subItem;
}
}
return $netWork;
}
return $allNetwork;
}
return $information;
}
protected function loadInformation($host_id = '')
{
try {
Log::info('host_id', ['data' => $host_id]);
$cmd = "\$vmhost='$host_id'; cd /; C:\CLISave\getvmhostinfo.ps1;";
$results = $this->Command($cmd);
if (!$results) {
return null;
}
$startPos = strpos($results, '{');
$endPos = strrpos($results, '}');
if ($startPos === false || $endPos === false) {
return null;
}
$jsonData = substr($results, $startPos, $endPos - $startPos + 1);
$jsonData = trim($jsonData);
try {
$decoded = json_decode($jsonData, false, 512, JSON_THROW_ON_ERROR);
return $decoded;
} catch (\JsonException $e) {
return null;
}
} catch (\Throwable $e) {
Log::error('Error in loadInformation: ' . $e->getMessage());
return null;
}
}
protected function Command($cmd)
{
$auth = $this->LoginAuth();
if (!$auth) {
return "Login_Fail";
}
try {
//Action connect to viserver
$action = "powershell.exe; Connect-viserver -Server '$this->apiUri' -User '$this->username' -Password \"'$this->password'\" -SaveCredentials; $cmd";
//Run action
$stream = ssh2_exec($auth, $action);
stream_set_blocking($stream, true);
return stream_get_contents($stream);
} catch (Exception $error) {
return $error->getCode();
}
}
private function LoginAuth()
{
try {
$connection = ssh2_connect('10.10.10.173', 22);
if (ssh2_auth_password($connection, 'administrator', 'Thegioiso@123**$$8899TgTk2')) {
return $connection;
}
return false;
} catch (Exception $error) {
return $error->getCode();
}
}
private function getAllNetwork()
{
$check = $this->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/vcenter/network")->json();
if (!isset($response['type'])) {
return $response['value'];
}
return false;
}
return "Login_Again";
}
public function loadDatastore($data = [], $host_id = '')
{
$this->username = $data->user;
$this->password = SignRSA::DecryptFunc($data->password);
$this->apiUri = $data->host;
$information = $this->loadInformation($host_id);
if ($information) {
if (is_array($information->Datastore)) {
$information = $information->Datastore;
} else {
$information = array($information->Datastore);
}
$dataStore = [];
$allDatastore = $this->getAllDataStore();
if ($allDatastore && $allDatastore !== "Login_Again") {
foreach ($allDatastore as $_item) {
if (in_array($_item['datastore'], $information)) {
$dataStore[] = $_item;
}
}
return $dataStore;
}
return $allDatastore;
}
return $information;
}
public function getAllDataStore()
{
$check = $this->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/vcenter/datastore")->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->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];
// Lấy cấu hình gốc từ item hoặc product
$base_cpu = $item->cpu ?? ($host->cpu ?? 1);
$base_ram = $item->ram ?? ($host->ram ?? 1);
$base_disk = $item->disk ?? ($host->disk ?? 20);
// Đảm bảo giá trị không bị null hoặc 0
$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
]);
// Lấy tất cả datastore liên quan host, không lấy first()
$datastores = \App\Models\ModuleDatastore::where('module_host_id', $host->id)->get();
// Nếu muốn chọn 1 datastore để truyền (ví dụ lấy cái đầu tiên)
$datastoreId = $datastores->isNotEmpty()
? $datastores->first()->datastore
: 'datastore-3004'; // fallback nếu không có
$body = [
'spec' => [
'name' => $item->server_name ?? ($userName . '-vm-' . $item->id),
'source' => $template, // sử dụng template của host, thay 'vm-3007' cứng thành biến
'placement' => [
'datacenter' => $datacenter->datacenter_id,
'folder' => 'group-v1008', // hoặc lấy từ DB nếu cần
'host' => $host->host_id,
'resource_pool' => 'resgroup-3002', // nếu có thể lấy động thì tốt hơn
'datastore' => $datastoreId, // chuỗi datastore đúng định dạng
],
'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) // Tăng timeout lên 5 phút cho quá trình clone
->connectTimeout(30) // Giữ connect timeout ngắn để phát hiện lỗi kết nối sớm
->withHeaders([
'vmware-api-session-id' => $this->token,
'Content-Type' => 'application/json',
])
->post("https://{$this->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
]);
// Cập nhật cấu hình phần cứng sau khi clone thành công
// Thay đổi điều kiện: chạy nếu có bất kỳ addon nào > 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}", [
'vm_id' => $vmId,
'addon' => $addon,
'condition_check' => $vmId !== 'UNKNOWN' && ($addon['cpu'] > 0 || $addon['ram'] > 0 || $addon['disk'] > 0)
]);
}
// 🟢 Gửi thông báo Discord
$discord = app(\App\Http\Services\Discord::class);
// Thông tin addon
$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()];
}
}
/**
* Cập nhật cấu hình phần cứng VM sau khi clone
*/
private function updateVMHardware($vmId, $addon, $base_cpu, $base_ram): void
{
try {
Log::info("=== BẮT ĐẦU CẬP NHẬT ADDON ===");
Log::info("Thông tin VM và Addon", [
'vm_id' => $vmId,
'addon' => $addon,
'base_cpu' => $base_cpu,
'base_ram' => $base_ram,
'addon_cpu' => $addon['cpu'],
'addon_ram' => $addon['ram'],
'addon_disk' => $addon['disk']
]);
// Tính toán cấu hình mới
$new_cpu = $base_cpu + $addon['cpu'];
// Sửa: RAM đầu vào là GB, convert sang MiB
$new_ram_mib = ($base_ram + $addon['ram']) * 1024; // 1 GB = 1024 MiB
Log::info("Tính toán cấu hình mới", [
'vm_id' => $vmId,
'base_cpu' => $base_cpu,
'addon_cpu' => $addon['cpu'],
'new_cpu' => $new_cpu,
'base_ram_gb' => $base_ram,
'addon_ram_gb' => $addon['ram'],
'new_ram_mib' => $new_ram_mib
]);
// Cập nhật CPU nếu có addon
if ($addon['cpu'] > 0) {
Log::info("🔄 Bắt đầu cập nhật CPU cho VM {$vmId}");
$cpuResult = $this->updateVMCPU($vmId, $new_cpu);
if ($cpuResult) {
Log::info("✅ Cập nhật CPU thành công: {$base_cpu} → {$new_cpu} cores");
} else {
Log::error("❌ Cập nhật CPU thất bại cho VM {$vmId}");
}
} else {
Log::info("⏭️ Bỏ qua cập nhật CPU (không có addon CPU)");
}
// Cập nhật RAM nếu có addon
if ($addon['ram'] > 0) {
Log::info("🔄 Bắt đầu cập nhật RAM cho VM {$vmId}");
$ramResult = $this->updateVMMemory($vmId, $new_ram_mib);
if ($ramResult) {
Log::info("✅ Cập nhật RAM thành công: {$base_ram}GB → " . ($base_ram + $addon['ram']) . "GB ({$new_ram_mib}MiB)");
} else {
Log::error("❌ Cập nhật RAM thất bại cho VM {$vmId}");
}
} else {
Log::info("⏭️ Bỏ qua cập nhật RAM (không có addon RAM)");
}
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());
Log::error("Stack trace: " . $e->getTraceAsString());
}
}
/**
* Cập nhật CPU cho VM
*/
private function updateVMCPU($vmId, $cpuCount): bool
{
try {
Log::info("🔄 Gọi API cập nhật CPU", [
'vm_id' => $vmId,
'new_cpu_count' => $cpuCount
]);
// Tính toán cores_per_socket dựa trên count
// Đảm bảo count % cores_per_socket == 0
$coresPerSocket = 1;
if ($cpuCount > 1) {
// Tìm ước số lớn nhất <= cpuCount/2 để tối ưu phân bổ socket
$maxCoresPerSocket = floor($cpuCount / 2);
for ($i = $maxCoresPerSocket; $i >= 1; $i--) {
if ($cpuCount % $i === 0) {
$coresPerSocket = $i;
break;
}
}
// Nếu không tìm được ước số phù hợp, sử dụng cpuCount
if ($coresPerSocket === 1 && $cpuCount > 2) {
$coresPerSocket = $cpuCount;
}
}
$payload = [
'spec' => [
'count' => $cpuCount,
'cores_per_socket' => $coresPerSocket,
'hot_add_enabled' => true
]
];
Log::info("📤 Payload CPU", [
'payload' => $payload,
'cpu_count' => $cpuCount,
'cores_per_socket' => $coresPerSocket
]);
$response = Http::withoutVerifying()
->timeout(30)
->withHeaders([
'vmware-api-session-id' => $this->token,
'Content-Type' => 'application/json',
])
->patch("https://{$this->apiUri}/rest/vcenter/vm/{$vmId}/hardware/cpu", $payload);
Log::info("📥 Response CPU", [
'status' => $response->status(),
'body' => $response->body()
]);
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(),
'url' => "https://{$this->apiUri}/rest/vcenter/vm/{$vmId}/hardware/cpu"
]);
return false;
}
} catch (\Exception $e) {
Log::error("❌ Exception khi cập nhật CPU cho VM {$vmId}: " . $e->getMessage());
Log::error("Stack trace: " . $e->getTraceAsString());
return false;
}
}
/**
* Cập nhật RAM cho VM
*/
private function updateVMMemory($vmId, $ramSizeMB): bool
{
try {
Log::info("🔄 Gọi API cập nhật RAM", [
'vm_id' => $vmId,
'new_ram_mb' => $ramSizeMB
]);
$payload = [
'spec' => [
'size_MiB' => $ramSizeMB
]
];
Log::info("📤 Payload RAM", ['payload' => $payload]);
$response = Http::withoutVerifying()
->timeout(30)
->withHeaders([
'vmware-api-session-id' => $this->token,
'Content-Type' => 'application/json',
])
->patch("https://{$this->apiUri}/rest/vcenter/vm/{$vmId}/hardware/memory", $payload);
Log::info("📥 Response RAM", [
'status' => $response->status(),
'body' => $response->body()
]);
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(),
'url' => "https://{$this->apiUri}/rest/vcenter/vm/{$vmId}/hardware/memory"
]);
return false;
}
} catch (\Exception $e) {
Log::error("❌ Exception khi cập nhật RAM cho VM {$vmId}: " . $e->getMessage());
Log::error("Stack trace: " . $e->getTraceAsString());
return false;
}
}
/**
* Cập nhật Disk cho VM (mở rộng disk)
*/
private function updateVMDisk($vmId, $diskSizeGB): bool
{
try {
// Tính toán dung lượng disk mới (GB sang bytes)
$diskSizeBytes = $diskSizeGB * 1024 * 1024 * 1024; // GB to bytes
$payload = [
'spec' => [
'capacity' => $diskSizeBytes
]
];
// Lấy thông tin disk hiện tại để biết disk ID
$diskResponse = Http::withoutVerifying()
->timeout(30)
->withHeaders([
'vmware-api-session-id' => $this->token,
])
->get("https://{$this->apiUri}/rest/vcenter/vm/{$vmId}/hardware/disk");
if (!$diskResponse->successful()) {
Log::error("Không thể lấy thông tin disk hiện tại cho VM {$vmId}");
return false;
}
$diskData = $diskResponse->json();
if (!isset($diskData['value']) || empty($diskData['value'])) {
Log::error("Không tìm thấy disk nào cho VM {$vmId}");
return false;
}
// Lấy disk đầu tiên (thường là disk chính)
$firstDisk = $diskData['value'][0];
$diskKey = $firstDisk['key'];
$response = Http::withoutVerifying()
->timeout(30)
->withHeaders([
'vmware-api-session-id' => $this->token,
'Content-Type' => 'application/json',
])
->patch("https://{$this->apiUri}/rest/vcenter/vm/{$vmId}/hardware/disk/{$diskKey}", $payload);
if ($response->successful()) {
Log::info("API call cập nhật Disk thành công cho VM {$vmId}");
return true;
} else {
Log::error("API call cập nhật Disk 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 Disk cho VM {$vmId}: " . $e->getMessage());
return false;
}
}
private function GetFolderByHost($datacenter = '')
{
return ModuleFolder::where('module_datacenter_id', $datacenter)->first();
}
public function CheckVirtualMachine($vmware_id = ''): string|array
{
$check = $this->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/vcenter/vm/$vmware_id/guest/power")->json();
if (!isset($response['type'])) {
return [
'error' => false,
'data' => $response['value']
];
}
return [
'error' => true,
'message' => $response['value']
];
}
return "Login_Again";
}
public function GetNetworkVirtualMachine($vmware_id = ''): array
{
$response = $this->GetDetailVirtualMachine($vmware_id);
if ($response['error'] === false) {
return [
'error' => false,
'data' => $response['data']['nics'][0]['value']['backing']
];
}
return [
'error' => true,
'message' => $response['data']
];
}
public function ConfigNetworkVirtualMachine($vmware_id = '', $config = []): int
{
return Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->patch("https://$this->apiUri/rest/vcenter/vm/$vmware_id/hardware/ethernet/4000", $config)
->status();
}
public function ConfigVirtualMachineUbuntu($vmware_id = '', $product = [], $password = ''): void
{
$this->SetConfig($product->IpAddress->Host->Datacenter->vCenter);
$VirtualMachine = "VirtualMachine-$vmware_id";
$IPAddress = $product->IpAddress->ip;
$Gateway = $product->IpAddress->gateway;
$HostName = strtolower($product->hostname);
Log::channel('Custom')->info("\r\n");
try {
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Get Config information for VirtualMachine with #ID $VirtualMachine");
Log::channel('Custom')->info("\r\n");
$Command = "\$vm = get-vm -Id '$VirtualMachine';\$hostname ='$HostName'; \$ipadd = '$IPAddress'; \$gw = '$Gateway'; \$password = \"'$password'\"; cd /; C:\CLISave\createUbuntu1line.ps1;";
Log::channel('Custom')->info("$Command");
$this->Command($Command);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("After run Command 1 |" . Carbon::now());
$Command = "\$vm = get-vm -Id '$VirtualMachine'; \$ipadd = '$IPAddress'; \$gw = '$Gateway'; \$password = \"'$password'\"; cd /; C:\CLISave\changeLinux.ps1;";
Log::channel('Custom')->info("$Command");
$this->Command($Command);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("After run Command 2 |" . Carbon::now());
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
} catch (\Exception $error) {
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Error Log: ");
Log::channel('Custom')->info($error->getMessage());
Log::channel('Custom')->info("===================================================================");
}
}
public function GetDetailVirtualMachine($vmware_id = ''): array
{
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/vcenter/vm/$vmware_id")->json();
if (!isset($response['type'])) {
return [
'error' => false,
'data' => $response['value']
];
}
return [
'error' => true,
'message' => $response['value']
];
}
public function GetIPAddressVirtualMachine($vmware_id = ''): array
{
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get("https://$this->apiUri/rest/vcenter/vm/$vmware_id/guest/identity")->json();
if (!isset($response['type'])) {
return [
'error' => false,
'data' => $response['value']
];
}
return [
'error' => true,
'message' => $response['value']
];
}
public function ConfigCentOSVersion8($vmware_id = '', $product = [], $password = ''): void
{
$this->SetConfig($product->IpAddress->Host->Datacenter->vCenter);
$VirtualMachine = "VirtualMachine-$vmware_id";
$IPAddress = $product->IpAddress->ip;
$Gateway = $product->IpAddress->gateway;
$HostName = strtolower($product->hostname);
Log::channel('Custom')->info("\r\n");
try {
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Get Config information for VirtualMachine with #ID $VirtualMachine");
Log::channel('Custom')->info("\r\n");
$Command = "\$vm = get-vm -Id '$VirtualMachine';\$hostname ='$HostName'; \$ipadd = '$IPAddress'; \$gw = '$Gateway'; \$password = \"'$password'\"; cd /; C:\CLISave\createCentOS81line.ps1;";
Log::channel('Custom')->info("$Command");
$this->Command($Command);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("After run Command |" . Carbon::now());
Log::channel('Custom')->info("===================================================================");
} catch (\Exception $error) {
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Error Log: ");
Log::channel('Custom')->info($error->getMessage());
Log::channel('Custom')->info("===================================================================");
}
}
public function ConfigCentOSVersion7($vmware_id = '', $product = [], $password = ''): void
{
$this->SetConfig($product->IpAddress->Host->Datacenter->vCenter);
$VirtualMachine = "VirtualMachine-$vmware_id";
$IPAddress = $product->IpAddress->ip;
$Gateway = $product->IpAddress->gateway;
$HostName = strtolower($product->hostname);
Log::channel('Custom')->info("\r\n");
try {
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Get Config information for VirtualMachine with #ID $VirtualMachine");
Log::channel('Custom')->info("\r\n");
$Command = "\$vm = get-vm -Id '$VirtualMachine'; \$hostname ='$HostName';\$ipadd = '$IPAddress'; \$gw = '$Gateway'; \$password = \"'$password'\"; cd /; C:\CLISave\createCentOS71line.ps1;";
Log::channel('Custom')->info($Command);
$this->Command($Command);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("After run Command |" . Carbon::now());
Log::channel('Custom')->info("===================================================================");
} catch (\Exception $error) {
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Error Log: ");
Log::channel('Custom')->info($error->getMessage());
Log::channel('Custom')->info("===================================================================");
}
}
public function ConfigWindowsVersion10($vmware_id = '', $product = [], $password = ''): void
{
try {
// Kiểm tra null safety
if (!$product || !$product->IpAddress || !$product->IpAddress->Host || !$product->IpAddress->Host->Datacenter || !$product->IpAddress->Host->Datacenter->vCenter) {
Log::error('❌ ConfigWindowsVersion10: Missing required data', [
'vmware_id' => $vmware_id,
'product_id' => $product?->id ?? 'unknown',
'ip_address' => $product?->IpAddress?->ip ?? 'unknown',
'host' => $product?->IpAddress?->Host?->name ?? 'unknown',
'datacenter' => $product?->IpAddress?->Host?->Datacenter?->name ?? 'unknown',
'vcenter' => $product?->IpAddress?->Host?->Datacenter?->vCenter?->host ?? 'unknown'
]);
return;
}
$this->SetConfig($product->IpAddress->Host->Datacenter->vCenter);
$VirtualMachine = "VirtualMachine-$vmware_id";
$IPAddress = $product->IpAddress->ip;
$Gateway = $product->IpAddress->gateway;
$HostName = strtolower($product->hostname);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Get Config information for VirtualMachine with #ID $VirtualMachine");
Log::channel('Custom')->info("\r\n");
$Command = "\$vm = get-vm -Id '$VirtualMachine';\$hostname ='$HostName'; \$ipadd = '$IPAddress'; \$gw = '$Gateway'; \$password = \"'$password'\"; cd /; C:\CLISave\changeWindows10.ps1;";
Log::channel('Custom')->info("$Command");
$this->Command($Command);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("After run Command |" . Carbon::now());
Log::channel('Custom')->info("===================================================================");
} catch (\Exception $error) {
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Error Log: ");
Log::channel('Custom')->info($error->getMessage());
Log::channel('Custom')->info("===================================================================");
}
}
public function ConfigWindowsServer($vmware_id = '', $product = [], $password = ''): void
{
try {
// Kiểm tra null safety
if (!$product || !$product->IpAddress || !$product->IpAddress->Host || !$product->IpAddress->Host->Datacenter || !$product->IpAddress->Host->Datacenter->vCenter) {
Log::error('❌ ConfigWindowsServer: Missing required data', [
'vmware_id' => $vmware_id,
'product_id' => $product?->id ?? 'unknown',
'ip_address' => $product?->IpAddress?->ip ?? 'unknown',
'host' => $product?->IpAddress?->Host?->name ?? 'unknown',
'datacenter' => $product?->IpAddress?->Host?->Datacenter?->name ?? 'unknown',
'vcenter' => $product?->IpAddress?->Host?->Datacenter?->vCenter?->host ?? 'unknown'
]);
return;
}
$this->SetConfig($product->IpAddress->Host->Datacenter->vCenter);
$VirtualMachine = "VirtualMachine-$vmware_id";
$IPAddress = $product->IpAddress->ip;
$Gateway = $product->IpAddress->gateway;
$HostName = strtolower($product->hostname);
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Get Config information for VirtualMachine with #ID $VirtualMachine");
Log::channel('Custom')->info("\r\n");
$Command = "\$vm = get-vm -Id '$VirtualMachine';\$hostname ='$HostName'; \$ipadd = '$IPAddress'; \$gw = '$Gateway'; \$password = \"'$password'\"; cd /; C:\CLISave\changeWindows1line.ps1;";
Log::channel('Custom')->info("$Command");
$this->Command($Command);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("After run Command |" . Carbon::now());
Log::channel('Custom')->info("===================================================================");
} catch (\Exception $error) {
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Error Log: ");
Log::channel('Custom')->info($error->getMessage());
Log::channel('Custom')->info("===================================================================");
}
}
public function ConfigFixWindowsClick($vmware_id = '', $product = []): void
{
try {
// Kiểm tra null safety
if (!$product || !$product->IpAddress || !$product->IpAddress->Host || !$product->IpAddress->Host->Datacenter || !$product->IpAddress->Host->Datacenter->vCenter) {
Log::error('❌ ConfigFixWindowsClick: Missing required data', [
'vmware_id' => $vmware_id,
'product_id' => $product?->id ?? 'unknown',
'ip_address' => $product?->IpAddress?->ip ?? 'unknown',
'host' => $product?->IpAddress?->Host?->name ?? 'unknown',
'datacenter' => $product?->IpAddress?->Host?->Datacenter?->name ?? 'unknown',
'vcenter' => $product?->IpAddress?->Host?->Datacenter?->vCenter?->host ?? 'unknown'
]);
return;
}
$this->SetConfig($product->IpAddress->Host->Datacenter->vCenter);
$VirtualMachine = "VirtualMachine-$vmware_id";
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Get Config information for VirtualMachine with #ID $VirtualMachine");
Log::channel('Custom')->info("\r\n");
$Command = "\$vm = get-vm -Id '$VirtualMachine'; cd /; C:\CLISave\FixTwoClickWindows.ps1;";
Log::channel('Custom')->info("$Command");
$this->Command($Command);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("After run Command |" . Carbon::now());
Log::channel('Custom')->info("===================================================================");
} catch (\Exception $error) {
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Error Log: ");
Log::channel('Custom')->info($error->getMessage());
Log::channel('Custom')->info("===================================================================");
}
}
public function ConfigRockyLinuxVersion9($vmware_id = '', $product = [], $password = ''): void
{
try {
// Kiểm tra null safety
if (!$product || !$product->IpAddress || !$product->IpAddress->Host || !$product->IpAddress->Host->Datacenter || !$product->IpAddress->Host->Datacenter->vCenter) {
Log::error('❌ ConfigRockyLinuxVersion9: Missing required data', [
'vmware_id' => $vmware_id,
'product_id' => $product?->id ?? 'unknown',
'ip_address' => $product?->IpAddress?->ip ?? 'unknown',
'host' => $product?->IpAddress?->Host?->name ?? 'unknown',
'datacenter' => $product?->IpAddress?->Host?->Datacenter?->name ?? 'unknown',
'vcenter' => $product?->IpAddress?->Host?->Datacenter?->vCenter?->host ?? 'unknown'
]);
return;
}
$this->SetConfig($product->IpAddress->Host->Datacenter->vCenter);
$VirtualMachine = "VirtualMachine-$vmware_id";
$IPAddress = $product->IpAddress->ip;
$Gateway = $product->IpAddress->gateway;
$HostName = strtolower($product->hostname);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Get Config information for VirtualMachine with #ID $VirtualMachine");
Log::channel('Custom')->info("\r\n");
$Command = "\$vm = get-vm -Id '$VirtualMachine'; \$hostname ='$HostName';\$ipadd = '$IPAddress'; \$gw = '$Gateway'; \$password = \"'$password'\"; cd /; C:\CLISave\createRocky1line.ps1;";
Log::channel('Custom')->info($Command);
$this->Command($Command);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("After run Command |" . Carbon::now());
Log::channel('Custom')->info("===================================================================");
} catch (\Exception $error) {
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Error Log: ");
Log::channel('Custom')->info($error->getMessage());
Log::channel('Custom')->info("===================================================================");
}
}
public function GetConsoleTickets($vmware_id = ''): array|string
{
$check = $this->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->post("https://$this->apiUri/api/vcenter/vm/$vmware_id/console/tickets", [
"type" => "WEBMKS"
])->json();
if (!isset($response['error_type'])) {
$start = strpos($response['ticket'], '/ticket/');
$ticket = str_replace('/ticket/', '', substr($response['ticket'], $start));
return [
'error' => false,
'data' => $ticket
];
}
return [
'error' => true,
'data' => $response['messages']
];
}
return "Login_Again";
}
public function SuspendVirtualMachine($vmware_id = ''): array|string
{
$check = $this->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->post("https://$this->apiUri/rest/vcenter/vm/$vmware_id/power/suspend");
if ($response->status() === 200) {
return [
'error' => false,
'data' => 'success'
];
}
return [
'error' => true,
'data' => $response['value']
];
}
return "Login_Again";
}
public function RebootVirtualMachine($product, $vmware_id = ''): array|string
{
$check = $this->loginServer($product->ipaddress->host->datacenter->vCenter);
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->post("https://$this->apiUri/rest/vcenter/vm/$vmware_id/guest/power?action=reboot");
if ($response->status() === 200) {
return [
'error' => false,
'data' => 'success'
];
}
return [
'error' => true,
'data' => $response['value']
];
}
return "Login_Again";
}
public function StartVirtualMachine($product, $vmware_id = ''): array|string
{
$check = $this->loginServer($product->ipaddress->host->datacenter->vCenter);
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->post("https://$this->apiUri/rest/vcenter/vm/$vmware_id/power/start");
if ($response->status() === 200) {
return [
'error' => false,
'data' => 'success'
];
}
return [
'error' => true,
'data' => $response['value']
];
}
return "Login_Again";
}
public function ShutdownGuestVirtualMachine($product, $vmware_id = ''): array|string
{
$check = $this->loginServer($product->ipaddress->host->datacenter->vCenter);
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->post("https://$this->apiUri/rest/vcenter/vm/$vmware_id/guest/power?action=shutdown");
if ($response->status() === 200) {
return [
'error' => false,
'data' => 'success'
];
}
return [
'error' => true,
'data' => $response['value']
];
}
return "Login_Again";
}
public function PowerOffVirtualMachine($vmware_id = ''): array|string
{
$check = $this->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->post("https://$this->apiUri/rest/vcenter/vm/$vmware_id/power/stop");
if ($response->status() === 200) {
return [
'error' => false,
'data' => 'success'
];
}
return [
'error' => true,
'data' => $response['value']
];
}
return "Login_Again";
}
public function DeleteVirtualMachine($vmware_id = ''): array|string
{
$check = $this->checkApiExits();
if ($check) {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->delete("https://$this->apiUri/rest/vcenter/vm/$vmware_id");
if ($response->status() === 200) {
return [
'error' => false,
'data' => 'success'
];
}
return [
'error' => true,
'data' => $response['value']
];
}
return "Login_Again";
}
public function CreateSnapShot($product): string|array
{
try {
$this->username = $product->ipaddress->host->datacenter->vCenter->user;
$this->password = SignRSA::DecryptFunc($product->ipaddress->host->datacenter->vCenter->password);
$this->apiUri = $product->ipaddress->host->datacenter->vCenter->host;
} catch (\Exception $e) {
return ['error' => true, 'message' => 'Lỗi setup credentials: ' . $e->getMessage()];
}
$vmware_name = (string) $product->name;
$check = $this->checkApiExits();
if ($check) {
try {
$Snapshot = $vmware_name . "_" . date('d_m_Y_H_i_s');
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Create Snapshot with VirtualMachine Name - $vmware_name - Snap #$Snapshot");
Log::channel('Custom')->info("\r\n");
$Command = "\$vm_name = '$vmware_name';\$name = '$Snapshot'; cd /; C:\CLISave\createsnapshot.ps1;";
Log::channel('Custom')->info("$Command");
$this->Command($Command);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("After run Command |" . Carbon::now());
Log::channel('Custom')->info("===================================================================");
// Clear snapshot cache for this VM
$this->clearSnapshotCache($product);
return [
'error' => false,
'data' => $Snapshot
];
} catch (\Exception $error) {
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Error Log: ");
Log::channel('Custom')->info($error->getMessage());
Log::channel('Custom')->info("===================================================================");
return [
'error' => true,
'message' => $error->getMessage()
];
}
}
return "Login_Again";
}
/**
* Clear snapshot cache for a specific VM
*/
private function clearSnapshotCache($product): void
{
$vmware_name = (string) $product->name;
$cacheKey = "snapshots_{$product->id}_{$vmware_name}";
Cache::forget($cacheKey);
Log::channel('Custom')->info("🗑️ Cleared snapshot cache for VM: {$vmware_name}");
}
public function getSnapShot($product): string|array
{
$startTime = microtime(true);
try {
$this->username = $product->ipaddress->host->datacenter->vCenter->user;
$this->password = SignRSA::DecryptFunc($product->ipaddress->host->datacenter->vCenter->password);
$this->apiUri = $product->ipaddress->host->datacenter->vCenter->host;
} catch (\Exception $e) {
return ['error' => true, 'message' => 'Lỗi setup credentials: ' . $e->getMessage()];
}
$vmware_name = (string) $product->name;
// Check cache first
$cacheKey = "snapshots_{$product->id}_{$vmware_name}";
$cachedResult = Cache::get($cacheKey);
if ($cachedResult !== null) {
$executionTime = round((microtime(true) - $startTime) * 1000, 2);
Log::channel('Custom')->info("📸 Snapshot cache hit - Execution time: {$executionTime}ms");
return $cachedResult;
}
$check = $this->checkApiExits();
if ($check) {
try {
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Get Snapshots for VirtualMachine Name - $vmware_name");
Log::channel('Custom')->info("\r\n");
// Use the getsnapshot.ps1 script file
$Command = "\$vm_name = '$vmware_name'; cd /; C:\CLISave\getsnapshot.ps1;";
Log::channel('Custom')->info("$Command");
$commandStartTime = microtime(true);
$result = $this->Command($Command);
$commandTime = round((microtime(true) - $commandStartTime) * 1000, 2);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("Command Result: " . $result);
Log::channel('Custom')->info("Command Execution Time: {$commandTime}ms");
Log::channel('Custom')->info("After run Command |" . Carbon::now());
Log::channel('Custom')->info("===================================================================");
// Parse result
if ($result && $result !== "Login_Fail") {
try {
// Clean up the result - remove any PowerShell formatting
$cleanResult = trim($result);
// Parse the text output from the script
$snapshots = $this->parseSnapshotTextOutput($cleanResult);
if (!empty($snapshots)) {
$response = [
'error' => false,
'data' => $snapshots
];
// Cache the result for 5 minutes
Cache::put($cacheKey, $response, now()->addMinutes(5));
$totalTime = round((microtime(true) - $startTime) * 1000, 2);
Log::channel('Custom')->info("📸 Snapshot retrieved successfully - Total time: {$totalTime}ms, Command time: {$commandTime}ms");
return $response;
} else {
$response = [
'error' => true,
'message' => 'Không tìm thấy snapshot nào hoặc lỗi parse dữ liệu',
'raw_result' => $result
];
$totalTime = round((microtime(true) - $startTime) * 1000, 2);
Log::channel('Custom')->info("📸 No snapshots found - Total time: {$totalTime}ms");
return $response;
}
} catch (\Exception $parseError) {
Log::channel('Custom')->info("Parse Error: " . $parseError->getMessage());
$response = [
'error' => true,
'message' => 'Lỗi parse kết quả snapshot: ' . $parseError->getMessage(),
'raw_result' => $result
];
$totalTime = round((microtime(true) - $startTime) * 1000, 2);
Log::channel('Custom')->info("📸 Parse error - Total time: {$totalTime}ms");
return $response;
}
} else {
$response = [
'error' => true,
'message' => 'Không thể thực thi lệnh hoặc đăng nhập thất bại',
'raw_result' => $result
];
$totalTime = round((microtime(true) - $startTime) * 1000, 2);
Log::channel('Custom')->info("📸 Command failed - Total time: {$totalTime}ms");
return $response;
}
} catch (\Exception $error) {
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Error Log: ");
Log::channel('Custom')->info($error->getMessage());
Log::channel('Custom')->info("===================================================================");
$totalTime = round((microtime(true) - $startTime) * 1000, 2);
Log::channel('Custom')->info("📸 Exception occurred - Total time: {$totalTime}ms");
return [
'error' => true,
'message' => $error->getMessage()
];
}
}
$totalTime = round((microtime(true) - $startTime) * 1000, 2);
Log::channel('Custom')->info("📸 Login again required - Total time: {$totalTime}ms");
return "Login_Again";
}
public function RemoveSnapShot($product, $snap = ''): string|array
{
$this->username = $product->IpAddress->Host->Datacenter->vCenter->user;
$this->password = SignRSA::DecryptFunc($product->IpAddress->Host->Datacenter->vCenter->password);
$vmware_name = (string) $product->name;
$check = $this->checkApiExits();
if ($check) {
try {
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Remove Snapshot with VirtualMachine Name - $vmware_name - Snap #$snap");
Log::channel('Custom')->info("\r\n");
$Command = "\$vm_name = '$vmware_name';\$name = '$snap'; cd /; C:\CLISave\deletesnap.ps1;";
Log::channel('Custom')->info("$Command");
$this->Command($Command);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("After run Command |" . Carbon::now());
Log::channel('Custom')->info("===================================================================");
// Clear snapshot cache for this VM
$this->clearSnapshotCache($product);
return [
'error' => false
];
} catch (\Exception $error) {
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Error Log: ");
Log::channel('Custom')->info($error->getMessage());
Log::channel('Custom')->info("===================================================================");
return [
'error' => true,
'message' => $error->getMessage()
];
}
}
return "Login_Again";
}
public function RevertSnapShot($product, $snap = ''): string|array
{
$this->username = $product->IpAddress->Host->Datacenter->vCenter->user;
$this->password = SignRSA::DecryptFunc($product->IpAddress->Host->Datacenter->vCenter->password);
$this->apiUri = $product->IpAddress->Host->Datacenter->vCenter->host;
$vmware_name = (string) $product->name;
$check = $this->checkApiExits();
if ($check) {
try {
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Revert Snapshot with VirtualMachine Name - $vmware_name - Snap #$snap");
Log::channel('Custom')->info("\r\n");
$Command = "\$vm_name = '$vmware_name';\$name = '$snap'; cd /; C:\CLISave\backSnap.ps1;";
Log::channel('Custom')->info("$Command");
$this->Command($Command);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("After run Command |" . Carbon::now());
Log::channel('Custom')->info("===================================================================");
// Clear snapshot cache for this VM
$this->clearSnapshotCache($product);
return [
'error' => false
];
} catch (\Exception $error) {
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Error Log: ");
Log::channel('Custom')->info($error->getMessage());
Log::channel('Custom')->info("===================================================================");
return [
'error' => true,
'message' => $error->getMessage()
];
}
}
return "Login_Again";
}
public function UpgradeMemoryVirtualMachine($vmware_id = '', $ram = 0): array|string
{
$Check = $this->checkApiExits();
if ($Check) {
$RAMConfig = [
"spec" => array(
"size_MiB" => $ram * $this->memory,
)
];
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->patch("https://$this->apiUri/rest/vcenter/vm/$vmware_id/hardware/memory", $RAMConfig);
if ($response->status() === 200) {
return [
'error' => false,
'data' => 'success'
];
}
return [
'error' => true,
'data' => $response['value']
];
}
return "Login_Again";
}
public function UpgradeCPUVirtualMachine($vmware_id = '', $cpu = 0): array|string
{
$Check = $this->checkApiExits();
if ($Check) {
$CPUConfig = [
"spec" => array(
"cores_per_socket" => 1,
"count" => $cpu
)
];
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->patch("https://$this->apiUri/rest/vcenter/vm/$vmware_id/hardware/cpu", $CPUConfig);
if ($response->status() === 200) {
return [
'error' => false,
'data' => 'success'
];
}
return [
'error' => true,
'data' => $response['value']
];
}
return "Login_Again";
}
public function UpgradeDiskVirtualMachine($product, $disk = 0): array|string
{
$this->username = $product->IpAddress->Host->Datacenter->vCenter->user;
$this->password = SignRSA::DecryptFunc($product->IpAddress->Host->Datacenter->vCenter->password);
$vmware_id = $product->vmware_id;
$check = $this->checkApiExits();
if ($check) {
try {
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Upgrade Disk with VirtualMachine ID # - $vmware_id");
Log::channel('Custom')->info("\r\n");
$Command = "\$vm = 'VirtualMachine-$vmware_id'; \$disksize = $disk; cd /; C:\CLISave\diskresize.ps1;";
Log::channel('Custom')->info("$Command");
$this->Command($Command);
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("After run Command |" . Carbon::now());
Log::channel('Custom')->info("===================================================================");
return [
'error' => false
];
} catch (\Exception $error) {
Log::channel('Custom')->info("\r\n");
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Error Log: ");
Log::channel('Custom')->info($error->getMessage());
Log::channel('Custom')->info("===================================================================");
return [
'error' => true,
'message' => $error->getMessage()
];
}
}
return "Login_Again";
}
public function FindVirtualMachine($name = ''): string|bool
{
$check = $this->checkApiExits();
if ($check) {
try {
$response = Http::withoutVerifying()
->withHeaders([
'vmware-api-session-id' => $this->token
])
->get(
"https://$this->apiUri/rest/vcenter/vm?filter.names.1=$name"
)->json();
} catch (ConnectionException $error) {
return false;
}
if ($response['value']) {
return $response['value'][0]['vm'];
}
return false;
}
return "Login_Again";
}
protected function SetConfig($data = []): static
{
$this->username = $data['user'];
$this->password = SignRSA::DecryptFunc($data['password']);
return $this;
}
// New Version Update 07.02.2025
protected function loginVPS(array|null $data = []): ?SSH2
{
try {
// Lấy thông tin từ $data - Không có thì lấy giá trị mặc định
$user = $data['user'] ?? 'root';
$ip = $data['ip'] ?? '103.42.58.37';
$password = $data['password'] ?? 'TGS@1232';
// Kiểm tra hệ điều hành
if (str_contains($this->os, "Windows")) {
$user = $data['user'] ?? 'administartor';
$this->process = new WinRMClient($ip, $user, $password);
;
return $this->process;
}
// Tạo kết nối SSH
$this->process = new SSH2($ip, 22, 10);
// Kiểm tra đăng nhập
if (!$this->process->login($user, $password)) {
throw new Exception("Cannot login to VPS with IP: $ip, User: $user");
}
Log::info("Logged into VPS successfully: IP = $ip, User = $user");
return $this->process;
} catch (Exception $exception) {
// Ghi log lỗi chi tiết
Log::error('Login VPS failed: ' . $exception->getMessage());
$this->process = null;
return null;
}
}
protected function config_hosname_linux(string $hostname = '')
{
try {
// Xử lý hostname
$hostname = preg_replace('/[^a-zA-Z0-9-]/', '', $hostname);
$hostname = trim($hostname, '-');
$hostname = substr($hostname, 0, 63);
// Đăng nhập VPS
$this->loginVPS();
if (!$this->process) {
throw new Exception("Cannot login to VPS");
}
// Set Host name
$this->process->exec("hostnamectl set-hostname " . escapeshellarg($hostname));
Log::info("Hostname changed successfully to: $hostname");
return true;
} catch (Exception $exception) {
Log::error('Failed to change hostname: ' . $exception->getMessage());
return false;
}
}
protected function set_ip_linux(string $ipaddress = '', string $gateway = ''): bool
{
try {
// Kiểm tra IP address
if (!filter_var($ipaddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
throw new Exception("Invalid IP address: $ipaddress");
}
// Kiểm tra Gateway
if (!filter_var($gateway, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
throw new Exception("Invalid Gateway address: $gateway");
}
// Đăng nhập VPS
$this->loginVPS();
if (!$this->process) {
throw new Exception("Cannot login to VPS");
}
// Thực hiện thay đổi IP và Gateway
$output1 = $this->process->exec("nmcli con mod ens192 ipv4.addresses $ipaddress/24");
$output2 = $this->process->exec("nmcli con mod ens192 ipv4.gateway $gateway");
$output3 = $this->process->exec("nmcli con up ens192");
if (!$output1 || !$output2 || !$output3) {
throw new Exception("Failed to execute one or more network commands.");
}
Log::info("IP Address changed successfully to: $ipaddress with Gateway: $gateway");
return true;
} catch (Exception $exception) {
Log::error('Failed to configure IP address: ' . $exception->getMessage());
return false;
}
}
public function change_password_linux(array $data = ['ip' => '103.42.58.37', 'user' => 'root', 'password' => 'TGS@123', 'new_password' => '']): bool
{
try {
// Kiểm tra mật khẩu mới
if (empty($data['new_password'])) {
throw new Exception("New password cannot be empty.");
}
// Đăng nhập VPS
$this->loginVPS($data);
if (!$this->process) {
throw new Exception("Cannot login to VPS.");
}
// Lấy thông tin từ $data
$username = $data['user'] ?? 'root';
$newPassword = $data['new_password'];
// Đảm bảo username không rỗng
if (empty($username)) {
throw new Exception("Username is invalid.");
}
// Đổi mật khẩu
$this->process->exec("echo " . escapeshellarg("$username:$newPassword") . " | chpasswd");
// Log trạng thái
Log::info("Password for user $username changed successfully.");
return true;
} catch (Exception $exception) {
// Ghi log lỗi
Log::error('Failed to change password: ' . $exception->getMessage());
return false;
}
}
public function extend_disk_centos_7(): bool
{
try {
// Đăng nhập VPS
$this->loginVPS();
if (!$this->process) {
throw new Exception("Cannot login to VPS");
}
// Kiểm tra xem thiết bị `/dev/sda2` có tồn tại không
$checkDisk = $this->process->exec("lsblk | grep -w 'sda2'");
if (empty(trim($checkDisk))) {
throw new Exception("Disk /dev/sda2 not found");
}
// Thực hiện các lệnh để mở rộng đĩa
$this->process->exec("sudo tee /sys/class/block/sda/device/rescan");
$this->process->exec("sudo pvcreate /dev/sda2");
$this->process->exec("sudo vgextend /dev/centos /dev/sda2");
$this->process->exec("sudo lvextend /dev/centos/root /dev/sda2");
$this->process->exec("sudo xfs_growfs /dev/centos/root");
Log::info("Disk extended successfully");
return true;
} catch (Exception $exception) {
// Ghi log lỗi
Log::error('Failed to extend disk: ' . $exception->getMessage());
return false;
}
}
public function extend_disk_centos_8(): bool
{
try {
// Đăng nhập VPS
$this->loginVPS();
if (!$this->process) {
throw new Exception("Cannot login to VPS");
}
// Kiểm tra xem thiết bị `/dev/sda2` có tồn tại không
$checkDisk = $this->process->exec("lsblk | grep -w 'sda3'");
if (empty(trim($checkDisk))) {
throw new Exception("Disk /dev/sda3 not found");
}
// Thực hiện các lệnh để mở rộng đĩa
$this->process->exec("sudo pvcreate /dev/sda3");
$this->process->exec("sudo vgextend /dev/cl /dev/sda3");
$this->process->exec("sudo lvextend /dev/cl/root /dev/sd3");
$this->process->exec("sudo xfs_growfs /dev/cl/root");
Log::info("Disk extended successfully");
return true;
} catch (Exception $exception) {
// Ghi log lỗi
Log::error('Failed to extend disk: ' . $exception->getMessage());
return false;
}
}
public function extend_disk_ubuntu(): bool
{
try {
// Đăng nhập VPS
$this->loginVPS();
if (!$this->process) {
throw new Exception("Cannot login to VPS");
}
// Kiểm tra xem thiết bị `/dev/sda3` có tồn tại không
$checkDisk = $this->process->exec("lsblk | grep -w 'sda3'");
if (empty(trim($checkDisk))) {
throw new Exception("Disk /dev/sda3 not found");
}
// Thực hiện các lệnh để mở rộng đĩa
$this->process->exec("sudo tee /sys/class/block/sda/device/rescan");
$this->process->exec("sudo growpart /dev/sda 3");
$this->process->exec("sudo pvresize /dev/sda3");
$this->process->exec("sudo lvresize -l+100%FREE --resizefs /dev/mapper/ubuntu--vg-ubuntu--lv");
Log::info("Disk extended successfully");
return true;
} catch (Exception $exception) {
// Ghi log lỗi
Log::error('Failed to extend disk: ' . $exception->getMessage());
return false;
}
}
public function set_ip_window(string $ipaddress = '', string $gateway = '')
{
try {
// Kiểm tra IP address
if (!filter_var($ipaddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
throw new Exception("Invalid IP address: $ipaddress");
}
// Kiểm tra Gateway
if (!filter_var($gateway, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
throw new Exception("Invalid Gateway address: $gateway");
}
// Đăng nhập VPS
$this->loginVPS();
if (!$this->process) {
throw new Exception("Cannot login to VPS");
}
// Thực hiện thay đổi IP và Gateway
$output1 = $this->process->execute_command("netsh int ip set address Ethernet0 static $ipaddress 255.255.255.0 $gateway");
if (!$output1) {
throw new Exception("Failed to execute one or more network commands.");
}
Log::info("IP Address changed successfully to: $ipaddress with Gateway: $gateway");
return true;
} catch (Exception $exception) {
Log::error('Failed to configure IP address: ' . $exception->getMessage());
return false;
}
}
public function change_password_windows(array $data = ['ip' => '103.42.58.37', 'user' => 'administrator', 'password' => 'TGS@123', 'new_password' => '']): bool
{
try {
// Kiểm tra mật khẩu mới
if (empty($data['new_password'])) {
throw new Exception("New password cannot be empty.");
}
// Đăng nhập VPS
$this->loginVPS($data);
if (!$this->process) {
throw new Exception("Cannot login to VPS.");
}
// Lấy thông tin từ $data
$username = $data['user'] ?? 'administrator';
$newPassword = $data['new_password'];
// Đảm bảo username không rỗng
if (empty($username)) {
throw new Exception("Username is invalid.");
}
// Đổi mật khẩu
$response = $this->process->execute_command("net user \"$username\" \"$newPassword\"");
if (stripos($response, 'successfully') === false) {
throw new Exception("Failed to change password. Response: $response");
}
Log::info("Password changed successfully for user: $username");
return true;
} catch (Exception $exception) {
// Ghi log lỗi
Log::error('Failed to change password: ' . $exception->getMessage());
return false;
}
}
/**
* Test method để debug addon updates
*/
public function testAddonUpdate($vmId, $addon, $base_cpu = 1, $base_ram = 1): array
{
try {
Log::info("🧪 TEST: Bắt đầu test addon update", [
'vm_id' => $vmId,
'addon' => $addon,
'base_cpu' => $base_cpu,
'base_ram' => $base_ram
]);
$this->updateVMHardware($vmId, $addon, $base_cpu, $base_ram);
return [
'error' => false,
'message' => 'Test addon update completed'
];
} catch (\Exception $e) {
Log::error("🧪 TEST: Lỗi trong test addon update: " . $e->getMessage());
return [
'error' => true,
'message' => $e->getMessage()
];
}
}
/**
* Test method để debug getSnapShot function
*/
public function testGetSnapShot($product): array
{
try {
Log::info("🧪 TEST: Bắt đầu test getSnapShot", [
'product_id' => $product->id ?? 'unknown',
'product_name' => $product->name ?? 'unknown',
'vmware_id' => $product->vmware_id ?? 'unknown'
]);
$result = $this->getSnapShot($product);
Log::info("🧪 TEST: Kết quả getSnapShot", [
'result' => $result,
'product_id' => $product->id ?? 'unknown'
]);
return [
'error' => false,
'message' => 'Test getSnapShot completed',
'result' => $result
];
} catch (\Exception $e) {
Log::error("🧪 TEST: Lỗi trong test getSnapShot: " . $e->getMessage());
return [
'error' => true,
'message' => $e->getMessage()
];
}
}
/**
* Assign IP to Windows VM using PowerShell script
*/
public function assignIPWindows($product, $vmware_id = ''): array|string
{
try {
// Support both old Product structure and new ModuleVmware structure
$ipAddress = null;
$gateway = null;
$netmask = '255.255.255.0';
$dns = '8.8.8.8';
$vmware_name = '';
$vCenter = null;
// Check if it's ModuleVmware (new structure)
if ($product && isset($product->ipAddress) && $product->ipAddress) {
Log::info('✅ Using ModuleVmware structure for assignIPWindows', [
'module_vmware_id' => $product->id,
'vmware_id' => $vmware_id
]);
// Extract from ModuleVmware->ipAddress relationship
$ipAddress = $product->ipAddress->ip ?? null;
$gateway = $product->ipAddress->gateway ?? null;
$netmask = $product->ipAddress->netmask ?? '255.255.255.0';
$dns = $product->ipAddress->dns ?? '8.8.8.8';
$vmware_name = $product->name ?? $vmware_id;
$vCenter = $product->ipAddress->host->datacenter->vCenter ?? null;
} elseif ($product && isset($product->IpAddress) && $product->IpAddress) {
Log::info('✅ Using old Product structure for assignIPWindows', [
'product_id' => $product->id,
'vmware_id' => $vmware_id
]);
// Extract from old Product->IpAddress relationship
$ipAddress = $product->IpAddress->ip ?? null;
$gateway = $product->IpAddress->gateway ?? null;
$netmask = $product->IpAddress->netmask ?? '255.255.255.0';
$dns = $product->IpAddress->dns ?? '8.8.8.8';
$vmware_name = $product->name ?? $vmware_id;
$vCenter = $product->IpAddress->Host->Datacenter->vCenter ?? null;
}
// Validate required data
if (!$ipAddress || !$gateway || !$vCenter) {
Log::error('❌ assignIPWindows: Missing required data', [
'vmware_id' => $vmware_id,
'ip_address' => $ipAddress,
'gateway' => $gateway,
'has_vcenter' => $vCenter ? 'yes' : 'no',
'product_class' => get_class($product)
]);
return [
'error' => true,
'data' => 'Missing required data for IP assignment (IP, Gateway, or vCenter)'
];
}
$this->SetConfig($vCenter);
// Convert netmask to prefix length (e.g., 255.255.255.0 = 24)
$maskBits = $this->convertNetmaskToPrefixLength($netmask);
Log::channel('Custom')->info("===================================================================");
Log::channel('Custom')->info("Assign IP to Windows VM - $vmware_name");
Log::channel('Custom')->info("IP: $ipAddress, Gateway: $gateway, DNS: $dns, MaskBits: $maskBits");
Log::channel('Custom')->info("\r\n");
// Create PowerShell command with dynamic values
$Command = "\$vmName = '$vmware_name'; \$IP = '$ipAddress'; \$MaskBits = $maskBits; \$Gateway = '$gateway'; \$DNS = '$dns'; cd /; C:\CLISave\AssignIP\assignIPWindows.ps1;";
Log::channel('Custom')->info("Executing Command: $Command");
$output = $this->Command($Command);
Log::channel('Custom')->info("Script output: " . $output);
Log::channel('Custom')->info("===================================================================");
// Check if execution was successful
if (strpos($output, '🎉') !== false || strpos($output, 'successfully') !== false) {
return [
'error' => false,
'data' => 'IP assigned successfully',
'output' => $output
];
} else {
return [
'error' => true,
'data' => 'Failed to assign IP',
'output' => $output
];
}
} catch (\Exception $e) {
Log::channel('Custom')->error("Exception in assignIPWindows: " . $e->getMessage());
return [
'error' => true,
'data' => $e->getMessage()
];
}
}
/**
* Convert netmask to prefix length (e.g., 255.255.255.0 = 24)
*/
private function convertNetmaskToPrefixLength($netmask): int
{
$netmaskMap = [
'255.255.255.255' => 32,
'255.255.255.254' => 31,
'255.255.255.252' => 30,
'255.255.255.248' => 29,
'255.255.255.240' => 28,
'255.255.255.224' => 27,
'255.255.255.192' => 26,
'255.255.255.128' => 25,
'255.255.255.0' => 24,
'255.255.254.0' => 23,
'255.255.252.0' => 22,
'255.255.248.0' => 21,
'255.255.240.0' => 20,
'255.255.224.0' => 19,
'255.255.192.0' => 18,
'255.255.128.0' => 17,
'255.255.0.0' => 16,
'255.254.0.0' => 15,
'255.252.0.0' => 14,
'255.248.0.0' => 13,
'255.240.0.0' => 12,
'255.224.0.0' => 11,
'255.192.0.0' => 10,
'255.128.0.0' => 9,
'255.0.0.0' => 8
];
return $netmaskMap[$netmask] ?? 24; // Default to /24 if not found
}
/**
* Parse snapshot output from PowerShell text format
*/
private function parseSnapshotTextOutput($output): array
{
$snapshots = [];
// Split the output into individual snapshot blocks
$blocks = preg_split('/\n\s*\n/', $output);
foreach ($blocks as $block) {
$block = trim($block);
if (empty($block))
continue;
// Skip header lines and warnings
if (
strpos($block, 'Name Port User') !== false ||
strpos($block, '---- ---- ----') !== false ||
strpos($block, 'WARNING:') !== false
) {
continue;
}
$snapshot = [];
// Parse each line of the snapshot block
$lines = explode("\n", $block);
foreach ($lines as $line) {
$line = trim($line);
if (empty($line))
continue;
// Parse key-value pairs
if (strpos($line, ' : ') !== false) {
list($key, $value) = explode(' : ', $line, 2);
$key = trim($key);
$value = trim($value);
switch ($key) {
case 'Name':
$snapshot['name'] = $value;
break;
case 'Description':
$snapshot['description'] = $value;
break;
case 'Created':
$snapshot['created'] = $value;
break;
case 'PowerState':
$snapshot['power_state'] = $value;
break;
case 'VM':
$snapshot['vm_name'] = $value;
break;
case 'VMId':
$snapshot['vm_id'] = $value;
break;
case 'Parent':
$snapshot['parent'] = $value;
break;
case 'ParentSnapshotId':
$snapshot['parent_snapshot_id'] = $value;
break;
case 'ParentSnapshot':
$snapshot['parent_snapshot'] = $value;
break;
case 'Children':
$snapshot['children'] = $value;
break;
case 'SizeMB':
$snapshot['size_mb'] = (float) $value;
break;
case 'SizeGB':
$snapshot['size_gb'] = (float) $value;
break;
case 'IsCurrent':
$snapshot['is_current'] = strtolower($value) === 'true';
break;
case 'IsReplaySupported':
$snapshot['is_replay_supported'] = strtolower($value) === 'true';
break;
case 'Id':
$snapshot['id'] = $value;
break;
case 'Uid':
$snapshot['uid'] = $value;
break;
default:
// Store any other properties
$snapshot[strtolower(str_replace(' ', '_', $key))] = $value;
break;
}
}
}
// Only add if we have at least a name
if (!empty($snapshot['name'])) {
$snapshots[] = $snapshot;
}
}
return $snapshots;
}
/**
* Parse snapshot output when JSON parsing fails (legacy method)
*/
private function parseSnapshotOutput($output): array
{
$snapshots = [];
// Split by lines and look for snapshot information
$lines = explode("\n", $output);
foreach ($lines as $line) {
$line = trim($line);
if (empty($line))
continue;
// Look for snapshot name patterns
if (preg_match('/(\w+_\d{2}_\d{2}_\d{4}_\d{2}_\d{2}_\d{2})/', $line, $matches)) {
$snapshots[] = [
'name' => $matches[1],
'raw_line' => $line
];
}
}
return $snapshots;
}
}