2663 lines
100 KiB
PHP
2663 lines
100 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
|
|
{
|
|
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;
|
|
|
|
private ?string $os = null;
|
|
private SSH2|WinRMClient|null $process = null;
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
// Login SSH
|
|
public function loginSSH(): bool
|
|
{
|
|
$conn = $this->LoginAuth();
|
|
|
|
if ($conn) {
|
|
Log::info("✅ Connected to vCenter via SSH successfully");
|
|
return true;
|
|
}
|
|
|
|
Log::error("❌ Failed to connect to vCenter via SSH");
|
|
return false;
|
|
}
|
|
|
|
|
|
public function runPsCommand(string $psCommand): string
|
|
{
|
|
$auth = $this->LoginAuth();
|
|
if (!$auth)
|
|
return "Login_Fail";
|
|
|
|
// gọi trực tiếp -Command, thu cả STDERR
|
|
$action = 'powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass '
|
|
. '-Command "' . str_replace('"', '\"', $psCommand) . '"';
|
|
|
|
try {
|
|
$stream = ssh2_exec($auth, $action);
|
|
if (!$stream)
|
|
throw new \RuntimeException("ssh2_exec failed");
|
|
$err = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
|
|
stream_set_blocking($stream, true);
|
|
stream_set_blocking($err, true);
|
|
$out = stream_get_contents($stream) ?: '';
|
|
$errOut = stream_get_contents($err) ?: '';
|
|
return $out . ($errOut ? "\n[STDERR]\n" . $errOut : '');
|
|
} catch (\Throwable $e) {
|
|
return "Error: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
|
|
protected function CommandRSP(string $cmd)
|
|
{
|
|
$auth = $this->LoginAuth();
|
|
if (!$auth)
|
|
return "Login_Fail";
|
|
|
|
// Escape để dùng trong chuỗi single-quoted của PowerShell
|
|
$esc = fn(string $v) => str_replace("'", "''", $v);
|
|
|
|
$server = $esc($this->apiUri ?? '');
|
|
$user = $esc($this->username ?? '');
|
|
$pass = $esc($this->password ?? '');
|
|
|
|
// Ghép script PowerShell hoàn chỉnh, chạy trong 1 scriptblock
|
|
// Lưu ý: dùng placeholder rồi strtr để tránh lẫn dấu ngoặc
|
|
$psTemplate = <<<'PS'
|
|
$ProgressPreference = 'SilentlyContinue'
|
|
$InformationPreference = 'SilentlyContinue'
|
|
$WarningPreference = 'SilentlyContinue'
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
Import-Module VMware.PowerCLI -ErrorAction Stop
|
|
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$false | Out-Null
|
|
Connect-VIServer -Server '<SERVER>' -User '<USER>' -Password '<PASS>' | Out-Null
|
|
|
|
# Thực thi lệnh công việc (đã được xây dựng sẵn, ví dụ: & 'C:\path\script.ps1' -Param 'Value' ... )
|
|
& {
|
|
<CMD>
|
|
} | Out-String
|
|
PS;
|
|
|
|
// Chèn dữ liệu
|
|
$psScript = strtr($psTemplate, [
|
|
'<SERVER>' => $server,
|
|
'<USER>' => $user,
|
|
'<PASS>' => $pass,
|
|
'<CMD>' => $cmd, // ở đây $cmd KHÔNG cần ngoặc kép bao ngoài
|
|
]);
|
|
|
|
// Encode UTF-16LE → Base64 cho -EncodedCommand
|
|
$encoded = base64_encode(mb_convert_encoding($psScript, 'UTF-16LE', 'UTF-8'));
|
|
|
|
// Dùng -EncodedCommand để tránh mọi lỗi escape/quote
|
|
$action = "powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand $encoded";
|
|
|
|
try {
|
|
$stream = ssh2_exec($auth, $action);
|
|
if (!$stream)
|
|
return "ssh2_exec failed";
|
|
$err = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
|
|
stream_set_blocking($stream, true);
|
|
stream_set_blocking($err, true);
|
|
$out = stream_get_contents($stream) ?: '';
|
|
$errOut = stream_get_contents($err) ?: '';
|
|
return $out . ($errOut ? "\n[STDERR]\n" . $errOut : '');
|
|
} catch (\Throwable $e) {
|
|
return "Error: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
protected function CommandChangePort(string $cmd, bool $expectJson = false)
|
|
{
|
|
$auth = $this->LoginAuth();
|
|
if (!$auth)
|
|
return "Login_Fail";
|
|
|
|
$esc = fn(string $v) => str_replace("'", "''", $v);
|
|
|
|
$server = $esc($this->apiUri ?? '');
|
|
$user = $esc($this->username ?? '');
|
|
$pass = $esc($this->password ?? '');
|
|
|
|
// PowerShell script template
|
|
$psTemplate = <<<'PS'
|
|
$ProgressPreference = 'SilentlyContinue'
|
|
$InformationPreference = 'SilentlyContinue'
|
|
$WarningPreference = 'SilentlyContinue'
|
|
$ErrorActionPreference = 'Stop'
|
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
|
|
Import-Module VMware.PowerCLI -ErrorAction Stop
|
|
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$false | Out-Null
|
|
Connect-VIServer -Server '<SERVER>' -User '<USER>' -Password '<PASS>' | Out-Null
|
|
|
|
& {
|
|
<CMD>
|
|
}
|
|
PS;
|
|
|
|
$psScript = strtr($psTemplate, [
|
|
'<SERVER>' => $server,
|
|
'<USER>' => $user,
|
|
'<PASS>' => $pass,
|
|
'<CMD>' => $cmd,
|
|
]);
|
|
|
|
// Encode UTF-16LE cho -EncodedCommand
|
|
$encoded = base64_encode(mb_convert_encoding($psScript, 'UTF-16LE', 'UTF-8'));
|
|
$action = "powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand $encoded";
|
|
|
|
try {
|
|
$stream = ssh2_exec($auth, $action);
|
|
if (!$stream)
|
|
return "ssh2_exec failed";
|
|
$err = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
|
|
stream_set_blocking($stream, true);
|
|
stream_set_blocking($err, true);
|
|
|
|
$out = stream_get_contents($stream) ?: '';
|
|
$errOut = stream_get_contents($err) ?: '';
|
|
|
|
if ($expectJson) {
|
|
// CẮT JSON cuối: {...} hoặc [...]
|
|
$trimmed = trim($out);
|
|
// đơn giản: bắt khối { ... } hoặc [ ... ] ở cuối chuỗi
|
|
if (preg_match('~(\{.*\}|\[.*\])\s*$~s', $trimmed, $m)) {
|
|
return $m[1];
|
|
}
|
|
// không tìm thấy → trả nguyên STDOUT (để caller log raw)
|
|
return $trimmed;
|
|
}
|
|
|
|
// Trường hợp không kỳ vọng JSON: giữ nguyên hành vi cũ (append STDERR)
|
|
return $out . ($errOut ? "\n[STDERR]\n" . $errOut : '');
|
|
} catch (\Throwable $e) {
|
|
return "Error: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
public function resetGuestPasswordViaPs1(string $vmName, string $guestUser, string $guestPassword, string $newPassword, string $targetUser = 'Administrator'): array|string
|
|
{
|
|
// File ps1 chạy được ngoài PowerShell: bạn đã test ok
|
|
$psFile = "C:\\CLISave\\AssignIP\\SetGuestPwd.ps1";
|
|
|
|
// Escape chuỗi cho PowerShell single-quoted
|
|
$q = fn(string $v) => str_replace("'", "''", $v);
|
|
|
|
// Xây args kiểu: -Key 'Value'
|
|
$args = " -VMName '" . $q($vmName) . "'";
|
|
$args .= " -GuestUser '" . $q($guestUser) . "'";
|
|
$args .= " -GuestPassword '" . $q($guestPassword) . "'";
|
|
$args .= " -TargetUser '" . $q($targetUser) . "'";
|
|
$args .= " -NewPassword '" . $q($newPassword) . "'";
|
|
|
|
// Gọi file trong cùng phiên PS đã Connect-VIServer (nhờ Command())
|
|
// Thêm Out-String để ép JSON nằm trên STDOUT gọn
|
|
$cmd = "& '$psFile'$args | Out-String";
|
|
|
|
$raw = $this->CommandRSP($cmd); // <-- dùng lại Command() của bạn
|
|
if ($raw === 'Login_Fail' || $raw === false || $raw === null) {
|
|
return "Login_Fail";
|
|
}
|
|
|
|
// Thử parse JSON (có thể lẫn banner/log)
|
|
$trim = trim($raw);
|
|
$data = json_decode($trim, true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
if (preg_match('/(\{.*\}|\[.*\])/s', $trim, $m)) {
|
|
$data = json_decode($m[1], true);
|
|
}
|
|
}
|
|
|
|
return $data ?: $raw; // nếu chưa parse được, trả raw để debug
|
|
}
|
|
|
|
|
|
public function changeRDPPortWindows(string $vmName, string $guestUser, string $guestPassword, string $newPort): array|string
|
|
{
|
|
$psFile = "C:\\CLISave\\AssignIP\\change-rdp-port.ps1";
|
|
$q = fn(string $v) => str_replace("'", "''", $v);
|
|
|
|
// Dùng -VMName (string). Alias -VMNames cũng ok, nhưng mình set đúng tên tham số.
|
|
$args = " -VMName '" . $q($vmName) . "'";
|
|
$args .= " -GuestUser '" . $q($guestUser) . "'";
|
|
$args .= " -GuestPassword '" . $q($guestPassword) . "'"; // khớp tên tham số trong script 1-VM
|
|
$args .= " -NewPort " . (int) $newPort;
|
|
|
|
$cmd = "& '$psFile'$args";
|
|
|
|
// Nếu đã có phiên bản CommandRSP($cmd, bool $expectJson) → bật expectJson để không trộn STDERR
|
|
$raw = method_exists($this, 'CommandRSP')
|
|
? ($this->CommandChangePort($cmd, true) ?? '')
|
|
: '';
|
|
|
|
if ($raw === 'Login_Fail' || $raw === false || $raw === null) {
|
|
return "Login_Fail";
|
|
}
|
|
$raw = trim($raw);
|
|
|
|
// CẮT LẤY KHỐI JSON CUỐI (object hoặc array) nếu có noise
|
|
if (!preg_match('~(\{(?:[^{}]|(?1))*\}|\[(?:[^\[\]]|(?0))*\])\s*$~s', $raw, $m)) {
|
|
// fallback đơn giản hơn
|
|
if (preg_match('~(\{.*\}|\[.*\])\s*$~s', $raw, $m)) {
|
|
$raw = $m[1];
|
|
}
|
|
} else {
|
|
$raw = $m[1];
|
|
}
|
|
|
|
$data = json_decode($raw, true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'JSON parsing failed',
|
|
'raw_output' => $raw,
|
|
'json_error' => json_last_error_msg()
|
|
];
|
|
}
|
|
|
|
// Script 1-VM trả về 1 object, nhưng nếu lỡ trả mảng thì lấy phần tử đầu
|
|
if (isset($data[0])) {
|
|
$data = $data[0];
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
/*
|
|
* ip = 103.42.58.37
|
|
* pwd = TGS@123
|
|
*/
|
|
public function change_password_linux(array $data = ['ip' => '10.10.10.173', 'user' => 'admin', '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;
|
|
}
|
|
}
|