939 lines
32 KiB
PHP
939 lines
32 KiB
PHP
<?php
|
||
require_once __DIR__ . '/app/db_helper.php';
|
||
require_once __DIR__ . '/app/logger.php';
|
||
|
||
/**
|
||
* 阿里云ECS和轻量应用服务器API适配器
|
||
* 支持ECS云服务器和轻量应用服务器(SWAS)的查询和管理
|
||
*/
|
||
|
||
/**
|
||
* URL编码函数(RFC3986规范)
|
||
*/
|
||
function aliyunEncode($str) {
|
||
$res = urlencode($str);
|
||
$res = preg_replace('/\+/', '%20', $res);
|
||
$res = preg_replace('/\*/', '%2A', $res);
|
||
$res = preg_replace('/%7E/', '~', $res);
|
||
return $res;
|
||
}
|
||
|
||
/**
|
||
* 生成阿里云API签名 (HMAC-SHA1)
|
||
*/
|
||
function generateAliyunSignature($method, $params, $secretKey) {
|
||
// 按参数名排序
|
||
ksort($params);
|
||
|
||
// 构建规范化请求字符串
|
||
$canonicalizedQueryString = '';
|
||
foreach ($params as $key => $value) {
|
||
if ($canonicalizedQueryString !== '') {
|
||
$canonicalizedQueryString .= '&';
|
||
}
|
||
$canonicalizedQueryString .= aliyunEncode($key) . '=' . aliyunEncode($value);
|
||
}
|
||
|
||
// 构造待签名字符串
|
||
$stringToSign = $method . '&%2F&' . aliyunEncode($canonicalizedQueryString);
|
||
|
||
// 计算HMAC-SHA1签名
|
||
$signature = base64_encode(hash_hmac('sha1', $stringToSign, $secretKey . '&', true));
|
||
|
||
return $signature;
|
||
}
|
||
|
||
/**
|
||
* 发送阿里云API请求
|
||
* @param string $configId 配置ID
|
||
* @param string $action API动作
|
||
* @param array $params 请求参数
|
||
* @param string $region 区域
|
||
* @param string $serviceType 服务类型: ecs 或 swas
|
||
* @return array|null
|
||
*/
|
||
function aliyunApiRequest($configId, $action, $params = [], $region = 'cn-hangzhou', $serviceType = 'ecs') {
|
||
$db = getVpsDB();
|
||
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
|
||
|
||
if (!$config) {
|
||
Logger::error("配置ID {$configId} 不存在", 'aliyunApiRequest');
|
||
return null;
|
||
}
|
||
|
||
$accessKeyId = $config['account'];
|
||
$accessKeySecret = $config['api_key'];
|
||
|
||
Logger::debug("[aliyunApiRequest] Config ID: {$configId}, AccessKeyId: " . substr($accessKeyId, 0, 8) . "***", 'aliyunApiRequest');
|
||
|
||
// 确定API域名
|
||
if (!$region || empty($region)) {
|
||
Logger::error("Region参数为空,无法构建API域名", 'aliyunApiRequest');
|
||
return ['status' => 400, 'error' => 'Region参数为空'];
|
||
}
|
||
|
||
if ($serviceType === 'swas') {
|
||
$host = 'swas.' . $region . '.aliyuncs.com';
|
||
} else {
|
||
$host = 'ecs.' . $region . '.aliyuncs.com';
|
||
}
|
||
|
||
// 公共参数
|
||
$commonParams = [
|
||
'Action' => $action,
|
||
'Version' => ($serviceType === 'swas') ? '2020-06-01' : '2014-05-26',
|
||
'Format' => 'JSON',
|
||
'AccessKeyId' => $accessKeyId,
|
||
'SignatureMethod' => 'HMAC-SHA1',
|
||
'SignatureVersion' => '1.0',
|
||
'SignatureNonce' => uniqid(),
|
||
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
|
||
'RegionId' => $region,
|
||
];
|
||
|
||
// 合并参数
|
||
$allParams = array_merge($commonParams, $params);
|
||
|
||
// 生成签名
|
||
$signature = generateAliyunSignature(
|
||
'GET',
|
||
$allParams,
|
||
$accessKeySecret
|
||
);
|
||
|
||
$allParams['Signature'] = $signature;
|
||
|
||
// 构建URL
|
||
$url = 'https://' . $host . '/?' . http_build_query($allParams);
|
||
|
||
Logger::debug("[aliyunApiRequest] URL: {$url}", 'aliyunApiRequest');
|
||
Logger::debug("[aliyunApiRequest] Action: {$action}, Region: {$region}, ServiceType: {$serviceType}", 'aliyunApiRequest');
|
||
|
||
// 发送请求
|
||
$ch = curl_init();
|
||
curl_setopt($ch, CURLOPT_URL, $url);
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||
|
||
$response = curl_exec($ch);
|
||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
$curlError = curl_error($ch);
|
||
curl_close($ch);
|
||
|
||
if ($curlError) {
|
||
Logger::error("cURL错误: {$curlError}", 'aliyunApiRequest');
|
||
return ['status' => 0, 'error' => $curlError];
|
||
}
|
||
|
||
if ($httpCode !== 200) {
|
||
Logger::error("HTTP请求失败: {$httpCode}, 响应: {$response}", 'aliyunApiRequest');
|
||
|
||
// 尝试解析错误响应
|
||
$errorResult = json_decode($response, true);
|
||
if ($errorResult && isset($errorResult['Message'])) {
|
||
return ['status' => $httpCode, 'error' => $errorResult['Message'], 'code' => $errorResult['Code'] ?? 'Unknown'];
|
||
}
|
||
|
||
return ['status' => $httpCode, 'error' => 'HTTP请求失败 (' . $httpCode . ')'];
|
||
}
|
||
|
||
$result = json_decode($response, true);
|
||
|
||
if (!$result) {
|
||
Logger::error("JSON解析失败", 'aliyunApiRequest');
|
||
return ['status' => 500, 'error' => '响应解析失败'];
|
||
}
|
||
|
||
// 检查响应
|
||
if (isset($result['Code'])) {
|
||
Logger::error("API错误: {$result['Code']} - {$result['Message']}", 'aliyunApiRequest');
|
||
return ['status' => 400, 'error' => $result['Message'], 'code' => $result['Code']];
|
||
}
|
||
|
||
return ['status' => 200, 'data' => $result, 'region' => $region, 'service_type' => $serviceType];
|
||
}
|
||
|
||
/**
|
||
* 获取所有可用区域列表(ECS)
|
||
*/
|
||
function getAliyunRegions($configId) {
|
||
return [
|
||
'cn-qingdao', // 华北1(青岛)
|
||
'cn-beijing', // 华北2(北京)
|
||
'cn-zhangjiakou', // 华北3(张家口)
|
||
'cn-huhehaote', // 华北5(呼和浩特)
|
||
'cn-wulanchabu', // 华北6(乌兰察布)
|
||
'cn-hangzhou', // 华东1(杭州)
|
||
'cn-shanghai', // 华东2(上海)
|
||
'cn-nanjing', // 华东5(南京)
|
||
'cn-fuzhou', // 华东6(福州)
|
||
'cn-shenzhen', // 华南1(深圳)
|
||
'cn-heyuan', // 华南2(河源)
|
||
'cn-guangzhou', // 华南3(广州)
|
||
'cn-chengdu', // 西南1(成都)
|
||
'cn-hongkong', // 中国香港
|
||
'ap-southeast-1', // 新加坡
|
||
'ap-southeast-5', // 印度尼西亚(雅加达)
|
||
'ap-northeast-1', // 日本(东京)
|
||
'ap-northeast-2', // 韩国(首尔)
|
||
'ap-southeast-3', // 马来西亚(吉隆坡)
|
||
'ap-southeast-6', // 菲律宾(马尼拉)
|
||
'ap-southeast-7', // 泰国(曼谷)
|
||
'us-west-1', // 美国(硅谷)
|
||
'us-east-1', // 美国(弗吉尼亚)
|
||
'eu-central-1', // 德国(法兰克福)
|
||
'eu-west-1', // 英国(伦敦)
|
||
'me-east-1', // 阿联酋(迪拜)
|
||
'me-central-1', // 沙特(利雅得)
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 获取轻量应用服务器可用区域列表
|
||
*/
|
||
function getAliyunSwasRegions($configId) {
|
||
return [
|
||
'cn-qingdao', // 华北1(青岛)
|
||
'cn-beijing', // 华北2(北京)
|
||
'cn-zhangjiakou', // 华北3(张家口)
|
||
'cn-huhehaote', // 华北5(呼和浩特)
|
||
'cn-wulanchabu', // 华北6(乌兰察布)
|
||
'cn-hangzhou', // 华东1(杭州)
|
||
'cn-shanghai', // 华东2(上海)
|
||
'cn-nanjing', // 华东5(南京)
|
||
'cn-fuzhou', // 华东6(福州)
|
||
'cn-shenzhen', // 华南1(深圳)
|
||
'cn-heyuan', // 华南2(河源)
|
||
'cn-guangzhou', // 华南3(广州)
|
||
'cn-chengdu', // 西南1(成都)
|
||
'cn-hongkong', // 中国香港
|
||
'ap-southeast-1', // 新加坡
|
||
'ap-northeast-1', // 日本(东京)
|
||
'us-west-1', // 美国(硅谷)
|
||
'eu-central-1', // 德国(法兰克福)
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 获取ECS实例列表(单个区域)
|
||
*/
|
||
function aliyunGetEcsList($configId, $region, $maxResults = 100, $nextToken = null) {
|
||
$params = [
|
||
'MaxResults' => $maxResults,
|
||
];
|
||
|
||
if ($nextToken) {
|
||
$params['NextToken'] = $nextToken;
|
||
}
|
||
|
||
return aliyunApiRequest($configId, 'DescribeInstances', $params, $region, 'ecs');
|
||
}
|
||
|
||
/**
|
||
* 获取轻量应用服务器实例列表(单个区域)
|
||
*/
|
||
function aliyunGetSwasList($configId, $region, $pageNumber = 1, $pageSize = 100) {
|
||
$params = [
|
||
'PageNumber' => $pageNumber,
|
||
'PageSize' => $pageSize,
|
||
];
|
||
|
||
return aliyunApiRequest($configId, 'ListInstances', $params, $region, 'swas');
|
||
}
|
||
|
||
/**
|
||
* 获取ECS实例详细信息
|
||
*/
|
||
function aliyunGetEcsDetails($configId, $instanceId, $region = null, $updateDb = true) {
|
||
$params = [
|
||
'InstanceIds' => json_encode([$instanceId]),
|
||
];
|
||
|
||
$result = aliyunApiRequest($configId, 'DescribeInstances', $params, $region, 'ecs');
|
||
|
||
// 如果获取成功且需要更新数据库
|
||
if ($updateDb && $result && isset($result['status']) && $result['status'] === 200) {
|
||
$instances = $result['data']['Instances']['Instance'] ?? [];
|
||
|
||
if (!empty($instances)) {
|
||
updateAliyunEcsToDatabase($configId, $instances[0]);
|
||
}
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 获取轻量应用服务器实例详细信息
|
||
*/
|
||
function aliyunGetSwasDetails($configId, $instanceId, $region = null, $updateDb = true) {
|
||
$params = [
|
||
'InstanceIds' => json_encode([$instanceId]),
|
||
];
|
||
|
||
$result = aliyunApiRequest($configId, 'ListInstances', $params, $region, 'swas');
|
||
|
||
// 如果获取成功且需要更新数据库
|
||
if ($updateDb && $result && isset($result['status']) && $result['status'] === 200) {
|
||
$instances = $result['data']['Instances'] ?? [];
|
||
|
||
if (!empty($instances)) {
|
||
updateAliyunSwasToDatabase($configId, $instances[0]);
|
||
}
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 获取ECS实例状态
|
||
*/
|
||
function aliyunGetEcsStatus($configId, $instanceId, $region = null, $updateDb = true) {
|
||
$params = [
|
||
'InstanceId.1' => $instanceId,
|
||
];
|
||
|
||
$result = aliyunApiRequest($configId, 'DescribeInstanceStatus', $params, $region, 'ecs');
|
||
|
||
// 如果获取成功且需要更新数据库
|
||
if ($updateDb && $result && isset($result['status']) && $result['status'] === 200) {
|
||
$instanceStatusSet = $result['data']['InstanceStatuses']['InstanceStatus'] ?? [];
|
||
|
||
if (!empty($instanceStatusSet)) {
|
||
$statusInfo = $instanceStatusSet[0];
|
||
$rawStatus = $statusInfo['Status'] ?? 'UNKNOWN';
|
||
|
||
// 状态映射
|
||
$statusMap = [
|
||
'Pending' => 'process',
|
||
'Running' => 'on',
|
||
'Starting' => 'process',
|
||
'Stopping' => 'process',
|
||
'Stopped' => 'off',
|
||
'Rebooting' => 'process',
|
||
'Unknown' => 'unknown'
|
||
];
|
||
|
||
$status = $statusMap[$rawStatus] ?? 'unknown';
|
||
|
||
updateVpsStatusInDatabaseAliyun($configId, $instanceId, $status);
|
||
}
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 获取轻量应用服务器实例状态
|
||
*/
|
||
function aliyunGetSwasStatus($configId, $instanceId, $region = null, $updateDb = true) {
|
||
$params = [
|
||
'InstanceIds' => json_encode([$instanceId]),
|
||
];
|
||
|
||
$result = aliyunApiRequest($configId, 'ListInstanceStatus', $params, $region, 'swas');
|
||
|
||
// 如果获取成功且需要更新数据库
|
||
if ($updateDb && $result && isset($result['status']) && $result['status'] === 200) {
|
||
$instanceStatusSet = $result['data']['InstanceStatuses'] ?? [];
|
||
|
||
if (!empty($instanceStatusSet)) {
|
||
$statusInfo = $instanceStatusSet[0];
|
||
$rawStatus = $statusInfo['Status'] ?? 'UNKNOWN';
|
||
|
||
// 状态映射
|
||
$statusMap = [
|
||
'Pending' => 'process',
|
||
'Starting' => 'process',
|
||
'Running' => 'on',
|
||
'Stopping' => 'process',
|
||
'Stopped' => 'off',
|
||
'Resetting' => 'process',
|
||
'Upgrading' => 'process',
|
||
'Disabled' => 'off',
|
||
'Unknown' => 'unknown'
|
||
];
|
||
|
||
$status = $statusMap[$rawStatus] ?? 'unknown';
|
||
|
||
updateVpsStatusInDatabaseAliyun($configId, $instanceId, $status);
|
||
}
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 启动ECS实例
|
||
*/
|
||
function aliyunPowerOn($configId, $instanceId, $region = null) {
|
||
// 如果未提供region,从数据库获取
|
||
if (!$region) {
|
||
$listDb = getVpsListDB();
|
||
$vpsInfo = $listDb->queryOne(
|
||
'SELECT domain FROM vps_list WHERE config_id = ? AND vps_id = ?',
|
||
[$configId, $instanceId]
|
||
);
|
||
if ($vpsInfo && !empty($vpsInfo['domain'])) {
|
||
$region = $vpsInfo['domain'];
|
||
Logger::info("[aliyunPowerOn] 从数据库获取到 region: {$region}", 'aliyunPowerOn');
|
||
} else {
|
||
Logger::error("[aliyunPowerOn] 无法从数据库获取 region,config_id: {$configId}, instance_id: {$instanceId}", 'aliyunPowerOn');
|
||
return ['status' => 400, 'error' => '无法获取实例区域信息'];
|
||
}
|
||
}
|
||
|
||
$params = [
|
||
'InstanceId' => $instanceId,
|
||
];
|
||
|
||
$result = aliyunApiRequest($configId, 'StartInstance', $params, $region, 'ecs');
|
||
|
||
// 开机操作后立即获取状态并更新数据库
|
||
if ($result && isset($result['status']) && $result['status'] === 200) {
|
||
sleep(2);
|
||
aliyunGetEcsStatus($configId, $instanceId, $region, true);
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 启动轻量应用服务器
|
||
*/
|
||
function aliyunSwasPowerOn($configId, $instanceId, $region = null) {
|
||
// 如果未提供region,从数据库获取
|
||
if (!$region) {
|
||
$listDb = getVpsListDB();
|
||
$vpsInfo = $listDb->queryOne(
|
||
'SELECT domain FROM vps_list WHERE config_id = ? AND vps_id = ?',
|
||
[$configId, $instanceId]
|
||
);
|
||
if ($vpsInfo && !empty($vpsInfo['domain'])) {
|
||
$region = $vpsInfo['domain'];
|
||
Logger::info("[aliyunSwasPowerOn] 从数据库获取到 region: {$region}", 'aliyunSwasPowerOn');
|
||
} else {
|
||
Logger::error("[aliyunSwasPowerOn] 无法从数据库获取 region,config_id: {$configId}, instance_id: {$instanceId}", 'aliyunSwasPowerOn');
|
||
return ['status' => 400, 'error' => '无法获取实例区域信息'];
|
||
}
|
||
}
|
||
|
||
$params = [
|
||
'InstanceId' => $instanceId,
|
||
];
|
||
|
||
$result = aliyunApiRequest($configId, 'StartInstance', $params, $region, 'swas');
|
||
|
||
// 开机操作后立即获取状态并更新数据库
|
||
if ($result && isset($result['status']) && $result['status'] === 200) {
|
||
sleep(2);
|
||
aliyunGetSwasStatus($configId, $instanceId, $region, true);
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 停止ECS实例
|
||
*/
|
||
function aliyunPowerOff($configId, $instanceId, $region = null) {
|
||
// 如果未提供region,从数据库获取
|
||
if (!$region) {
|
||
$listDb = getVpsListDB();
|
||
$vpsInfo = $listDb->queryOne(
|
||
'SELECT domain FROM vps_list WHERE config_id = ? AND vps_id = ?',
|
||
[$configId, $instanceId]
|
||
);
|
||
if ($vpsInfo && !empty($vpsInfo['domain'])) {
|
||
$region = $vpsInfo['domain'];
|
||
} else {
|
||
Logger::error("[aliyunPowerOff] 无法从数据库获取 region", 'aliyunPowerOff');
|
||
return ['status' => 400, 'error' => '无法获取实例区域信息'];
|
||
}
|
||
}
|
||
|
||
$params = [
|
||
'InstanceId' => $instanceId,
|
||
];
|
||
|
||
$result = aliyunApiRequest($configId, 'StopInstance', $params, $region, 'ecs');
|
||
|
||
// 关机操作后立即获取状态并更新数据库
|
||
if ($result && isset($result['status']) && $result['status'] === 200) {
|
||
sleep(2);
|
||
aliyunGetEcsStatus($configId, $instanceId, $region, true);
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 停止轻量应用服务器
|
||
*/
|
||
function aliyunSwasPowerOff($configId, $instanceId, $region = null) {
|
||
// 如果未提供region,从数据库获取
|
||
if (!$region) {
|
||
$listDb = getVpsListDB();
|
||
$vpsInfo = $listDb->queryOne(
|
||
'SELECT domain FROM vps_list WHERE config_id = ? AND vps_id = ?',
|
||
[$configId, $instanceId]
|
||
);
|
||
if ($vpsInfo && !empty($vpsInfo['domain'])) {
|
||
$region = $vpsInfo['domain'];
|
||
} else {
|
||
Logger::error("[aliyunSwasPowerOff] 无法从数据库获取 region", 'aliyunSwasPowerOff');
|
||
return ['status' => 400, 'error' => '无法获取实例区域信息'];
|
||
}
|
||
}
|
||
|
||
$params = [
|
||
'InstanceId' => $instanceId,
|
||
];
|
||
|
||
$result = aliyunApiRequest($configId, 'StopInstance', $params, $region, 'swas');
|
||
|
||
// 关机操作后立即获取状态并更新数据库
|
||
if ($result && isset($result['status']) && $result['status'] === 200) {
|
||
sleep(2);
|
||
aliyunGetSwasStatus($configId, $instanceId, $region, true);
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 重启ECS实例
|
||
*/
|
||
function aliyunReboot($configId, $instanceId, $region = null) {
|
||
// 如果未提供region,从数据库获取
|
||
if (!$region) {
|
||
$listDb = getVpsListDB();
|
||
$vpsInfo = $listDb->queryOne(
|
||
'SELECT domain FROM vps_list WHERE config_id = ? AND vps_id = ?',
|
||
[$configId, $instanceId]
|
||
);
|
||
if ($vpsInfo && !empty($vpsInfo['domain'])) {
|
||
$region = $vpsInfo['domain'];
|
||
} else {
|
||
Logger::error("[aliyunReboot] 无法从数据库获取 region", 'aliyunReboot');
|
||
return ['status' => 400, 'error' => '无法获取实例区域信息'];
|
||
}
|
||
}
|
||
|
||
$params = [
|
||
'InstanceId' => $instanceId,
|
||
];
|
||
|
||
$result = aliyunApiRequest($configId, 'RebootInstance', $params, $region, 'ecs');
|
||
|
||
// 重启操作后立即获取状态并更新数据库
|
||
if ($result && isset($result['status']) && $result['status'] === 200) {
|
||
sleep(3);
|
||
aliyunGetEcsStatus($configId, $instanceId, $region, true);
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 重启轻量应用服务器
|
||
*/
|
||
function aliyunSwasReboot($configId, $instanceId, $region = null) {
|
||
// 如果未提供region,从数据库获取
|
||
if (!$region) {
|
||
$listDb = getVpsListDB();
|
||
$vpsInfo = $listDb->queryOne(
|
||
'SELECT domain FROM vps_list WHERE config_id = ? AND vps_id = ?',
|
||
[$configId, $instanceId]
|
||
);
|
||
if ($vpsInfo && !empty($vpsInfo['domain'])) {
|
||
$region = $vpsInfo['domain'];
|
||
} else {
|
||
Logger::error("[aliyunSwasReboot] 无法从数据库获取 region", 'aliyunSwasReboot');
|
||
return ['status' => 400, 'error' => '无法获取实例区域信息'];
|
||
}
|
||
}
|
||
|
||
$params = [
|
||
'InstanceId' => $instanceId,
|
||
];
|
||
|
||
$result = aliyunApiRequest($configId, 'RebootInstance', $params, $region, 'swas');
|
||
|
||
// 重启操作后立即获取状态并更新数据库
|
||
if ($result && isset($result['status']) && $result['status'] === 200) {
|
||
sleep(3);
|
||
aliyunGetSwasStatus($configId, $instanceId, $region, true);
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 将ECS信息更新到数据库
|
||
*/
|
||
function updateAliyunEcsToDatabase($configId, $instance) {
|
||
$listDb = getVpsListDB();
|
||
|
||
$vpsId = $instance['InstanceId'];
|
||
$region = $instance['RegionId'] ?? '';
|
||
$zoneId = $instance['ZoneId'] ?? '';
|
||
|
||
// 提取CPU和内存信息
|
||
$cpuCores = $instance['Cpu'] ?? null;
|
||
// 注意:阿里云API返回的Memory单位是MB,需要转换为GB
|
||
$memorySize = isset($instance['Memory']) ? round($instance['Memory'] / 1024, 2) . 'G' : null;
|
||
|
||
// 提取系统盘信息
|
||
$diskSize = null;
|
||
if (isset($instance['SystemDisk']) && isset($instance['SystemDisk']['Size'])) {
|
||
$diskSize = $instance['SystemDisk']['Size'] . 'G';
|
||
}
|
||
|
||
// 提取带宽信息
|
||
$bandwidth = null;
|
||
if (isset($instance['InternetMaxBandwidthOut'])) {
|
||
$bandwidth = $instance['InternetMaxBandwidthOut'] . 'Mbps';
|
||
}
|
||
|
||
// 提取操作系统信息
|
||
$osType = $instance['OSName'] ?? ($instance['ImageId'] ?? null);
|
||
|
||
// 提取IP地址
|
||
$ipAddress = null;
|
||
if (isset($instance['PublicIpAddress']) && !empty($instance['PublicIpAddress']['IpAddress'])) {
|
||
$ipAddress = $instance['PublicIpAddress']['IpAddress'][0];
|
||
} elseif (isset($instance['EipAddress']) && isset($instance['EipAddress']['IpAddress'])) {
|
||
$ipAddress = $instance['EipAddress']['IpAddress'];
|
||
}
|
||
|
||
// 提取实例名称
|
||
$productName = $instance['InstanceName'] ?? null;
|
||
|
||
// 提取到期时间
|
||
$nextduedate = null;
|
||
if (isset($instance['ExpiredTime'])) {
|
||
$expiredTime = $instance['ExpiredTime'];
|
||
if ($expiredTime !== '' && $expiredTime !== null) {
|
||
$nextduedate = strtotime($expiredTime);
|
||
}
|
||
}
|
||
|
||
// 检查记录是否存在
|
||
$existing = $listDb->queryOne(
|
||
'SELECT id FROM vps_list WHERE config_id = ? AND vps_id = ?',
|
||
[$configId, $vpsId]
|
||
);
|
||
|
||
if ($existing) {
|
||
// 更新现有记录
|
||
$listDb->execute(
|
||
'UPDATE vps_list SET domain = ?, ip_address = ?, product_name = ?, cpu_cores = ?, memory_size = ?, disk_size = ?, bandwidth = ?, os_type = ?, nextduedate = ?, last_check = CURRENT_TIMESTAMP WHERE id = ?',
|
||
[
|
||
$zoneId,
|
||
$ipAddress,
|
||
$productName,
|
||
$cpuCores,
|
||
$memorySize,
|
||
$diskSize,
|
||
$bandwidth,
|
||
$osType,
|
||
$nextduedate,
|
||
$existing['id']
|
||
]
|
||
);
|
||
Logger::info("[updateAliyunEcsToDatabase] ECS {$vpsId} 详细信息已更新", 'updateAliyunEcsToDatabase');
|
||
} else {
|
||
// 插入新记录
|
||
$listDb->execute(
|
||
'INSERT INTO vps_list (config_id, vps_id, domain, ip_address, product_name, cpu_cores, memory_size, disk_size, bandwidth, os_type, status, nextduedate, last_check) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, CURRENT_TIMESTAMP)',
|
||
[
|
||
$configId,
|
||
$vpsId,
|
||
$zoneId,
|
||
$ipAddress,
|
||
$productName,
|
||
$cpuCores,
|
||
$memorySize,
|
||
$diskSize,
|
||
$bandwidth,
|
||
$osType,
|
||
$nextduedate
|
||
]
|
||
);
|
||
Logger::info("[updateAliyunEcsToDatabase] ECS {$vpsId} 新记录已插入", 'updateAliyunEcsToDatabase');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 将轻量应用服务器信息更新到数据库
|
||
*/
|
||
function updateAliyunSwasToDatabase($configId, $instance) {
|
||
$listDb = getVpsListDB();
|
||
|
||
$vpsId = $instance['InstanceId'];
|
||
$region = $instance['RegionId'] ?? '';
|
||
|
||
// 提取CPU和内存信息(从ResourceSpec中获取)
|
||
$cpuCores = isset($instance['ResourceSpec']) ? $instance['ResourceSpec']['Cpu'] : null;
|
||
$memorySize = isset($instance['ResourceSpec']) ? $instance['ResourceSpec']['Memory'] . 'G' : null;
|
||
|
||
// 提取磁盘信息
|
||
$diskSize = isset($instance['ResourceSpec']) ? $instance['ResourceSpec']['DiskSize'] . 'G' : null;
|
||
|
||
// 提取带宽信息
|
||
$bandwidth = isset($instance['ResourceSpec']) ? $instance['ResourceSpec']['Bandwidth'] . 'Mbps' : null;
|
||
|
||
// 提取操作系统信息
|
||
$osType = null;
|
||
if (isset($instance['Image'])) {
|
||
$osType = $instance['Image']['OsType'] ?? null;
|
||
}
|
||
|
||
// 提取IP地址
|
||
$ipAddress = $instance['PublicIpAddress'] ?? null;
|
||
|
||
// 提取实例名称
|
||
$productName = $instance['InstanceName'] ?? null;
|
||
|
||
// 提取到期时间
|
||
$nextduedate = null;
|
||
if (isset($instance['ExpiredTime'])) {
|
||
$expiredTime = $instance['ExpiredTime'];
|
||
if ($expiredTime !== '' && $expiredTime !== null) {
|
||
$nextduedate = strtotime($expiredTime);
|
||
}
|
||
}
|
||
|
||
// 检查记录是否存在
|
||
$existing = $listDb->queryOne(
|
||
'SELECT id FROM vps_list WHERE config_id = ? AND vps_id = ?',
|
||
[$configId, $vpsId]
|
||
);
|
||
|
||
if ($existing) {
|
||
// 更新现有记录
|
||
$listDb->execute(
|
||
'UPDATE vps_list SET domain = ?, ip_address = ?, product_name = ?, cpu_cores = ?, memory_size = ?, disk_size = ?, bandwidth = ?, os_type = ?, nextduedate = ?, last_check = CURRENT_TIMESTAMP WHERE id = ?',
|
||
[
|
||
$region,
|
||
$ipAddress,
|
||
$productName,
|
||
$cpuCores,
|
||
$memorySize,
|
||
$diskSize,
|
||
$bandwidth,
|
||
$osType,
|
||
$nextduedate,
|
||
$existing['id']
|
||
]
|
||
);
|
||
Logger::info("[updateAliyunSwasToDatabase] SWAS {$vpsId} 详细信息已更新", 'updateAliyunSwasToDatabase');
|
||
} else {
|
||
// 插入新记录
|
||
$listDb->execute(
|
||
'INSERT INTO vps_list (config_id, vps_id, domain, ip_address, product_name, cpu_cores, memory_size, disk_size, bandwidth, os_type, status, nextduedate, last_check) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, CURRENT_TIMESTAMP)',
|
||
[
|
||
$configId,
|
||
$vpsId,
|
||
$region,
|
||
$ipAddress,
|
||
$productName,
|
||
$cpuCores,
|
||
$memorySize,
|
||
$diskSize,
|
||
$bandwidth,
|
||
$osType,
|
||
$nextduedate
|
||
]
|
||
);
|
||
Logger::info("[updateAliyunSwasToDatabase] SWAS {$vpsId} 新记录已插入", 'updateAliyunSwasToDatabase');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新VPS状态到数据库
|
||
*/
|
||
function updateVpsStatusInDatabaseAliyun($configId, $vpsId, $status) {
|
||
$listDb = getVpsListDB();
|
||
|
||
$vpsInfo = $listDb->queryOne(
|
||
'SELECT id FROM vps_list WHERE config_id = ? AND vps_id = ?',
|
||
[$configId, $vpsId]
|
||
);
|
||
|
||
if ($vpsInfo) {
|
||
$listDb->execute(
|
||
'UPDATE vps_list SET status = ?, last_check = CURRENT_TIMESTAMP WHERE id = ?',
|
||
[$status, $vpsInfo['id']]
|
||
);
|
||
Logger::info("[updateVpsStatusInDatabaseAliyun] VPS {$vpsId} 状态已更新为: {$status}", 'updateVpsStatusInDatabaseAliyun');
|
||
} else {
|
||
$listDb->execute(
|
||
'INSERT INTO vps_list (config_id, vps_id, status, last_check) VALUES (?, ?, ?, CURRENT_TIMESTAMP)',
|
||
[$configId, $vpsId, $status]
|
||
);
|
||
Logger::info("[updateVpsStatusInDatabaseAliyun] VPS {$vpsId} 新记录已插入,状态: {$status}", 'updateVpsStatusInDatabaseAliyun');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 刷新指定配置的ECS列表(遍历所有区域)
|
||
*/
|
||
function refreshAliyunEcsListForConfig($configId) {
|
||
$db = getVpsDB();
|
||
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
|
||
|
||
if (!$config || $config['site_type'] !== 'aliyun') {
|
||
return false;
|
||
}
|
||
|
||
// 先获取区域列表
|
||
$regions = getAliyunRegions($configId);
|
||
$totalInstances = 0;
|
||
|
||
Logger::info("[refreshAliyunEcsListForConfig] 开始遍历所有区域获取ECS列表", 'refreshAliyunEcsListForConfig');
|
||
|
||
foreach ($regions as $region) {
|
||
try {
|
||
$nextToken = null;
|
||
do {
|
||
$result = aliyunGetEcsList($configId, $region, 100, $nextToken);
|
||
|
||
if ($result && isset($result['status']) && $result['status'] === 200) {
|
||
$instances = $result['data']['Instances']['Instance'] ?? [];
|
||
|
||
if (!empty($instances)) {
|
||
Logger::info("[refreshAliyunEcsListForConfig] 区域 {$region} 找到 " . count($instances) . " 台ECS", 'refreshAliyunEcsListForConfig');
|
||
|
||
foreach ($instances as $instance) {
|
||
updateAliyunEcsToDatabase($configId, $instance);
|
||
|
||
// 获取状态
|
||
aliyunGetEcsStatus($configId, $instance['InstanceId'], $region, true);
|
||
|
||
$totalInstances++;
|
||
|
||
// 避免频繁请求
|
||
usleep(300000); // 300毫秒
|
||
}
|
||
}
|
||
|
||
// 检查是否有下一页
|
||
$nextToken = $result['data']['NextToken'] ?? null;
|
||
} else {
|
||
Logger::warning("[refreshAliyunEcsListForConfig] 区域 {$region} 获取失败: " . ($result['msg'] ?? '未知错误'), 'refreshAliyunEcsListForConfig');
|
||
break;
|
||
}
|
||
} while ($nextToken);
|
||
} catch (Exception $e) {
|
||
Logger::error("[refreshAliyunEcsListForConfig] 区域 {$region} 异常: " . $e->getMessage(), 'refreshAliyunEcsListForConfig');
|
||
}
|
||
|
||
// 区域之间稍作延迟
|
||
usleep(500000); // 500毫秒
|
||
}
|
||
|
||
Logger::info("[refreshAliyunEcsListForConfig] ECS列表刷新完成,共获取 {$totalInstances} 台实例", 'refreshAliyunEcsListForConfig');
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 刷新指定配置的轻量应用服务器列表(遍历所有区域)
|
||
*/
|
||
function refreshAliyunSwasListForConfig($configId) {
|
||
$db = getVpsDB();
|
||
$config = $db->queryOne('SELECT * FROM configs WHERE id = ?', [$configId]);
|
||
|
||
if (!$config || $config['site_type'] !== 'aliyun_swas') {
|
||
return false;
|
||
}
|
||
|
||
// 先获取区域列表
|
||
$regions = getAliyunSwasRegions($configId);
|
||
$totalInstances = 0;
|
||
|
||
Logger::info("[refreshAliyunSwasListForConfig] 开始遍历所有区域获取轻量应用服务器列表", 'refreshAliyunSwasListForConfig');
|
||
|
||
foreach ($regions as $region) {
|
||
try {
|
||
$pageNumber = 1;
|
||
do {
|
||
$result = aliyunGetSwasList($configId, $region, $pageNumber, 100);
|
||
|
||
if ($result && isset($result['status']) && $result['status'] === 200) {
|
||
$instances = $result['data']['Instances'] ?? [];
|
||
$totalCount = $result['data']['TotalCount'] ?? 0;
|
||
$pageSize = $result['data']['PageSize'] ?? 10;
|
||
|
||
if (!empty($instances)) {
|
||
Logger::info("[refreshAliyunSwasListForConfig] 区域 {$region} 找到 " . count($instances) . " 台轻量服务器", 'refreshAliyunSwasListForConfig');
|
||
|
||
foreach ($instances as $instance) {
|
||
updateAliyunSwasToDatabase($configId, $instance);
|
||
|
||
// 获取状态
|
||
aliyunGetSwasStatus($configId, $instance['InstanceId'], $region, true);
|
||
|
||
$totalInstances++;
|
||
|
||
// 避免频繁请求
|
||
usleep(300000); // 300毫秒
|
||
}
|
||
}
|
||
|
||
// 检查是否有下一页
|
||
$totalPages = ceil($totalCount / $pageSize);
|
||
$pageNumber++;
|
||
|
||
if ($pageNumber > $totalPages) {
|
||
break;
|
||
}
|
||
} else {
|
||
Logger::warning("[refreshAliyunSwasListForConfig] 区域 {$region} 获取失败: " . ($result['msg'] ?? '未知错误'), 'refreshAliyunSwasListForConfig');
|
||
break;
|
||
}
|
||
} while (true);
|
||
} catch (Exception $e) {
|
||
Logger::error("[refreshAliyunSwasListForConfig] 区域 {$region} 异常: " . $e->getMessage(), 'refreshAliyunSwasListForConfig');
|
||
}
|
||
|
||
// 区域之间稍作延迟
|
||
usleep(500000); // 500毫秒
|
||
}
|
||
|
||
Logger::info("[refreshAliyunSwasListForConfig] 轻量应用服务器列表刷新完成,共获取 {$totalInstances} 台实例", 'refreshAliyunSwasListForConfig');
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 刷新所有阿里云配置的实例列表(ECS + SWAS)
|
||
*/
|
||
function refreshAllAliyunVpsLists() {
|
||
$db = getVpsDB();
|
||
$configs = $db->query('SELECT * FROM configs WHERE site_type IN (?, ?)', ['aliyun', 'aliyun_swas']);
|
||
|
||
$successCount = 0;
|
||
|
||
foreach ($configs as $config) {
|
||
if ($config['site_type'] === 'aliyun') {
|
||
// 刷新ECS列表
|
||
if (refreshAliyunEcsListForConfig($config['id'])) {
|
||
$successCount++;
|
||
}
|
||
} elseif ($config['site_type'] === 'aliyun_swas') {
|
||
// 刷新轻量应用服务器列表
|
||
if (refreshAliyunSwasListForConfig($config['id'])) {
|
||
$successCount++;
|
||
}
|
||
}
|
||
}
|
||
|
||
return $successCount;
|
||
}
|
||
?>
|