feat: Implement comprehensive vCenter integration with new services for VM configuration, power management, templates, snapshots, and resource management, alongside infrastructure management and synchronization job updates.
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Services\vCenter;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Http\Facade\SignRSA;
|
||||
|
||||
class SnapshotService
|
||||
{
|
||||
protected Client $client;
|
||||
protected PowerShellService $powerShellService;
|
||||
|
||||
public function __construct(Client $client, PowerShellService $powerShellService)
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->powerShellService = $powerShellService;
|
||||
}
|
||||
|
||||
public function CreateSnapShot($product): string|array
|
||||
{
|
||||
try {
|
||||
// Note: credentials setup logic from original vCenter.php seemed redundant with Client logic,
|
||||
// but CreateSnapShot was overwriting $this->username/password.
|
||||
// Here we assume Client is used, or we need to update Client.
|
||||
// Client stores token. If we need username/password for something else, Client has them now via getters.
|
||||
// But CreateSnapshot used $product->ipaddress...->vCenter->user/password.
|
||||
|
||||
// Re-login to ensure we have the right credentials for the vCenter associated with the product
|
||||
$vcenterModel = $product->ipaddress->host->datacenter->vCenter;
|
||||
$this->client->loginServer($vcenterModel);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return ['error' => true, 'message' => 'Lỗi setup credentials: ' . $e->getMessage()];
|
||||
}
|
||||
|
||||
$vmware_name = (string) $product->name;
|
||||
// In original code, it checks checkApiExits(), if true proceed.
|
||||
|
||||
if ($this->client->checkApiExits()) {
|
||||
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");
|
||||
|
||||
$Command = "\$vm_name = '$vmware_name';\$name = '$Snapshot'; cd /; C:\CLISave\createsnapshot.ps1;";
|
||||
Log::channel('Custom')->info("$Command");
|
||||
|
||||
$this->powerShellService->executePowerCLI($Command);
|
||||
|
||||
$this->clearSnapshotCache($product);
|
||||
|
||||
return [
|
||||
'error' => false,
|
||||
'data' => $Snapshot
|
||||
];
|
||||
} catch (\Exception $error) {
|
||||
return [
|
||||
'error' => true,
|
||||
'message' => $error->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
return "Login_Again";
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
// ... (Similar logic, using SOAP)
|
||||
// Since SOAP logic is quite complex and self-contained in vCenter.php,
|
||||
// I'll copy it here but need to adapt $this->callSoap etc.
|
||||
// Actually, callSoap uses curl with manual implementation.
|
||||
// I should probably simplify or copy that helper too.
|
||||
|
||||
// This method heavily relies on internal SOAP helpers.
|
||||
// For simplicity in this 'splitting' task, I will implement the public method
|
||||
// and add the private helpers it depends on.
|
||||
|
||||
try {
|
||||
$vcenterModel = $product->ipaddress->host->datacenter->vCenter;
|
||||
$this->client->loginServer($vcenterModel);
|
||||
} catch (\Exception $e) {
|
||||
return ['error' => true, 'message' => 'Lỗi setup credentials: ' . $e->getMessage()];
|
||||
}
|
||||
|
||||
$vmware_name = (string) $product->name;
|
||||
$vmId = $product->vmware_id;
|
||||
|
||||
$cacheKey = "snapshots_{$product->id}_{$vmware_name}";
|
||||
$cachedResult = Cache::get($cacheKey);
|
||||
|
||||
if ($cachedResult !== null) {
|
||||
return $cachedResult;
|
||||
}
|
||||
|
||||
try {
|
||||
$sessionToken = $this->soapLogin();
|
||||
if (!$sessionToken) {
|
||||
return "Login_Again";
|
||||
}
|
||||
|
||||
$snapshotInfo = $this->getVMSnapshotInfoSoap($sessionToken, $vmId);
|
||||
|
||||
$snapshots = [];
|
||||
if (isset($snapshotInfo['rootSnapshotList'])) {
|
||||
$roots = $snapshotInfo['rootSnapshotList'];
|
||||
if (isset($roots['snapshot'])) {
|
||||
$snapshots = $this->flattenSnapshotTree($roots);
|
||||
} elseif (is_array($roots)) {
|
||||
foreach ($roots as $root) {
|
||||
$snapshots = array_merge($snapshots, $this->flattenSnapshotTree($root));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$formattedSnapshots = [];
|
||||
foreach ($snapshots as $snap) {
|
||||
$formattedSnapshots[] = [
|
||||
'name' => $snap['name'],
|
||||
'description' => $snap['description'] ?? '',
|
||||
'created' => $snap['createTime'],
|
||||
'id' => $snap['snapshot']['_'] ?? $snap['snapshot'],
|
||||
'is_current' => false,
|
||||
'parent_snapshot_id' => '',
|
||||
'size_gb' => 0
|
||||
];
|
||||
}
|
||||
|
||||
$response = [
|
||||
'error' => false,
|
||||
'data' => $formattedSnapshots
|
||||
];
|
||||
|
||||
Cache::put($cacheKey, $response, now()->addMinutes(5));
|
||||
return $response;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::channel('Custom')->error("❌ SOAP Snapshot Error: " . $e->getMessage());
|
||||
return [
|
||||
'error' => true,
|
||||
'message' => 'Lỗi lấy snapshot qua SOAP: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function soapLogin()
|
||||
{
|
||||
$username = $this->client->getUsername();
|
||||
$password = $this->client->getPassword();
|
||||
|
||||
$body = '<soapenv:Body>
|
||||
<Login xmlns="urn:vim25">
|
||||
<_this type="SessionManager">SessionManager</_this>
|
||||
<userName>' . htmlspecialchars($username) . '</userName>
|
||||
<password>' . htmlspecialchars($password) . '</password>
|
||||
</Login>
|
||||
</soapenv:Body>';
|
||||
|
||||
$result = $this->callSoap($body);
|
||||
return $result['cookie'] ?? null;
|
||||
}
|
||||
|
||||
private function getVMSnapshotInfoSoap($cookie, $vmId)
|
||||
{
|
||||
$body = '<soapenv:Body>
|
||||
<RetrievePropertiesEx xmlns="urn:vim25">
|
||||
<_this type="PropertyCollector">propertyCollector</_this>
|
||||
<specSet>
|
||||
<propSet>
|
||||
<type>VirtualMachine</type>
|
||||
<all>false</all>
|
||||
<pathSet>snapshot</pathSet>
|
||||
</propSet>
|
||||
<objectSet>
|
||||
<obj type="VirtualMachine">' . $vmId . '</obj>
|
||||
</objectSet>
|
||||
</specSet>
|
||||
<options></options>
|
||||
</RetrievePropertiesEx>
|
||||
</soapenv:Body>';
|
||||
|
||||
$result = $this->callSoap($body, $cookie);
|
||||
|
||||
if (isset($result['body'])) {
|
||||
try {
|
||||
$xml = simplexml_load_string($result['body']);
|
||||
if ($xml === false) return [];
|
||||
|
||||
$xml->registerXPathNamespace('u', 'urn:vim25');
|
||||
$json = json_encode($xml);
|
||||
$array = json_decode($json, true);
|
||||
|
||||
return $this->findSnapshotPropertyInArray($array);
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private function findSnapshotPropertyInArray($array)
|
||||
{
|
||||
if (isset($array['rootSnapshotList'])) {
|
||||
return $array;
|
||||
}
|
||||
|
||||
foreach ($array as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$found = $this->findSnapshotPropertyInArray($value);
|
||||
if ($found) {
|
||||
return $found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private function flattenSnapshotTree($node)
|
||||
{
|
||||
$list = [];
|
||||
$list[] = $node;
|
||||
|
||||
if (isset($node['childSnapshotList'])) {
|
||||
$children = $node['childSnapshotList'];
|
||||
|
||||
// Check if 'snapshot' key exists directly (single child wrapped)
|
||||
if (isset($children['snapshot'])) {
|
||||
// If it's an array of snapshots or single object?
|
||||
// XML conversion: if strictly one child, might be direct. if multiple, array.
|
||||
// But simplexml usually produces array if multiple same tags.
|
||||
// However, structure is usually childSnapshotList -> snapshot (array or single)
|
||||
|
||||
$snaps = $children['snapshot'];
|
||||
// If $snaps has numeric keys, it's array. Else single.
|
||||
if (array_keys($snaps) !== range(0, count($snaps) - 1)) {
|
||||
$snaps = [$snaps];
|
||||
}
|
||||
|
||||
foreach ($snaps as $child) {
|
||||
$list = array_merge($list, $this->flattenSnapshotTree($child));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
private function callSoap($soapBody, $cookie = null)
|
||||
{
|
||||
$curl = curl_init();
|
||||
$host = $this->client->apiUri;
|
||||
|
||||
$headers = [
|
||||
'Content-Type: text/xml',
|
||||
'SOAPAction: "urn:vim25/2.0"'
|
||||
];
|
||||
if ($cookie) {
|
||||
$headers[] = "Cookie: $cookie";
|
||||
}
|
||||
|
||||
$payload = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:vim25">
|
||||
' . $soapBody . '
|
||||
</soapenv:Envelope>';
|
||||
|
||||
curl_setopt_array($curl, array(
|
||||
CURLOPT_URL => "https://$host/sdk",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => '',
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_POSTFIELDS => $payload,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
CURLOPT_SSL_VERIFYHOST => false,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_HEADER => true // We need headers for cookie
|
||||
));
|
||||
|
||||
$response = curl_exec($curl);
|
||||
|
||||
$header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
|
||||
$header = substr($response, 0, $header_size);
|
||||
$body = substr($response, $header_size);
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
// Parse cookie
|
||||
$cookieValue = null;
|
||||
if (preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $header, $matches)) {
|
||||
foreach ($matches[1] as $c) {
|
||||
if (strpos($c, 'vmware_soap_session') !== false) {
|
||||
$cookieValue = $c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ['body' => $body, 'cookie' => $cookieValue];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user