1383 lines
58 KiB
Python
1383 lines
58 KiB
Python
import jingrow
|
||
import os
|
||
import sys
|
||
import json
|
||
import random
|
||
import time
|
||
import uuid
|
||
from datetime import datetime
|
||
from typing import Dict, Any
|
||
|
||
from alibabacloud_swas_open20200601.client import Client as SWAS_OPEN20200601Client
|
||
from alibabacloud_credentials.client import Client as CredentialClient
|
||
from alibabacloud_tea_openapi import models as open_api_models
|
||
from alibabacloud_swas_open20200601 import models as swas__open20200601_models
|
||
from alibabacloud_tea_util import models as util_models
|
||
from alibabacloud_tea_util.client import Client as UtilClient
|
||
|
||
from jcloud.utils import get_current_team
|
||
|
||
|
||
class AliyunLightServerManager:
|
||
"""阿里云轻量应用服务器管理器"""
|
||
_instance = None
|
||
_clients = {} # 缓存不同地域的客户端
|
||
|
||
@classmethod
|
||
def get_instance(cls):
|
||
"""获取单例实例"""
|
||
if cls._instance is None:
|
||
cls._instance = cls()
|
||
return cls._instance
|
||
|
||
def __init__(self):
|
||
"""初始化管理器"""
|
||
if AliyunLightServerManager._instance is not None:
|
||
raise Exception("请使用 AliyunLightServerManager.get_instance() 获取实例")
|
||
|
||
# 初始化配置
|
||
self._initialize_credentials()
|
||
|
||
def _initialize_credentials(self):
|
||
"""初始化阿里云凭据"""
|
||
try:
|
||
settings = jingrow.get_single("Jcloud Settings")
|
||
if not settings:
|
||
jingrow.log_error("阿里云凭据初始化失败", "无法获取Jcloud Settings配置")
|
||
raise Exception("Jcloud Settings配置不存在")
|
||
|
||
self.access_key_id = settings.get("aliyun_access_key_id")
|
||
self.access_secret = settings.get_password("aliyun_access_secret") if settings.get("aliyun_access_secret") else None
|
||
|
||
if not self.access_key_id or not self.access_secret:
|
||
jingrow.log_error("阿里云凭据初始化失败", "阿里云凭据未配置,请在Jcloud Settings中配置aliyun_access_key_id和aliyun_access_secret")
|
||
raise Exception("阿里云凭据未配置")
|
||
|
||
except Exception as e:
|
||
jingrow.log_error(f"阿里云凭据初始化失败: {str(e)}")
|
||
self.access_key_id = None
|
||
self.access_secret = None
|
||
raise
|
||
|
||
def _get_client(self, region_id: str = 'cn-shanghai') -> SWAS_OPEN20200601Client:
|
||
"""获取指定地域的客户端,使用缓存机制"""
|
||
if region_id not in self._clients:
|
||
try:
|
||
# 直接使用凭据创建配置
|
||
config = open_api_models.Config(
|
||
access_key_id=self.access_key_id,
|
||
access_key_secret=self.access_secret
|
||
)
|
||
config.endpoint = f'swas.{region_id}.aliyuncs.com'
|
||
self._clients[region_id] = SWAS_OPEN20200601Client(config)
|
||
except Exception as e:
|
||
jingrow.log_error("阿里云客户端创建失败", f"创建客户端时发生错误: {str(e)}")
|
||
raise
|
||
|
||
return self._clients[region_id]
|
||
|
||
def _convert_response_to_dict(self, response_body):
|
||
"""将阿里云SDK响应对象转换为可序列化的字典"""
|
||
if response_body is None:
|
||
return None
|
||
|
||
result = {}
|
||
# 获取所有公共属性
|
||
for attr_name in dir(response_body):
|
||
if not attr_name.startswith('_') and not callable(getattr(response_body, attr_name)):
|
||
attr_value = getattr(response_body, attr_name)
|
||
if attr_value is not None:
|
||
if hasattr(attr_value, '__dict__') and not isinstance(attr_value, (str, int, float, bool)):
|
||
# 如果是复杂对象,递归转换
|
||
result[attr_name] = self._convert_response_to_dict(attr_value)
|
||
elif isinstance(attr_value, list):
|
||
# 如果是列表,转换列表中的每个元素
|
||
converted_list = []
|
||
for item in attr_value:
|
||
if hasattr(item, '__dict__') and not isinstance(item, (str, int, float, bool)):
|
||
converted_list.append(self._convert_response_to_dict(item))
|
||
else:
|
||
converted_list.append(item)
|
||
result[attr_name] = converted_list
|
||
else:
|
||
# 基本类型直接赋值
|
||
result[attr_name] = attr_value
|
||
|
||
return result
|
||
|
||
def create_instance(self, plan_id, image_id, period=1, region_id='cn-shanghai'):
|
||
"""创建轻量应用服务器实例"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.CreateInstancesRequest(
|
||
region_id=region_id,
|
||
plan_id=plan_id,
|
||
image_id=image_id,
|
||
period=period
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.create_instances_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例创建成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("创建实例失败", f"创建实例时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '实例创建失败'}
|
||
|
||
def start_instance(self, instance_id, region_id='cn-shanghai'):
|
||
"""启动实例"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.StartInstanceRequest(
|
||
region_id=region_id,
|
||
instance_id=instance_id
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.start_instance_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例启动成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("启动实例失败", f"启动实例 {instance_id} 时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '实例启动失败'}
|
||
|
||
def stop_instance(self, instance_id, region_id='cn-shanghai'):
|
||
"""停止实例"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.StopInstanceRequest(
|
||
region_id=region_id,
|
||
instance_id=instance_id
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.stop_instance_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例停止成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("停止实例失败", f"停止实例 {instance_id} 时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '实例停止失败'}
|
||
|
||
def reboot_instance(self, instance_id, region_id='cn-shanghai'):
|
||
"""重启实例"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.RebootInstanceRequest(
|
||
region_id=region_id,
|
||
instance_id=instance_id
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.reboot_instance_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例重启成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("重启实例失败", f"重启实例 {instance_id} 时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '实例重启失败'}
|
||
|
||
def upgrade_instance(self, instance_id, plan_id, region_id='cn-shanghai'):
|
||
"""升级实例配置"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.UpgradeInstanceRequest(
|
||
region_id=region_id,
|
||
instance_id=instance_id,
|
||
plan_id=plan_id
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.upgrade_instance_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例升级成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("升级实例失败", f"升级实例 {instance_id} 时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '实例升级失败'}
|
||
|
||
def renew_instance(self, instance_id, period=1, region_id='cn-shanghai'):
|
||
"""续费实例"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.RenewInstanceRequest(
|
||
region_id=region_id,
|
||
instance_id=instance_id,
|
||
period=period
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.renew_instance_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例续费成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("续费实例失败", f"续费实例 {instance_id} 时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '实例续费失败'}
|
||
|
||
def reset_system(self, instance_id, image_id=None, password=None, region_id='cn-shanghai'):
|
||
"""重置系统"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.ResetSystemRequest(
|
||
region_id=region_id,
|
||
instance_id=instance_id
|
||
)
|
||
if image_id:
|
||
request.image_id = image_id
|
||
if password:
|
||
request.password = password
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.reset_system_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '系统重置成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("重置系统失败", f"重置实例 {instance_id} 系统时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '系统重置失败'}
|
||
|
||
def update_instance_password(self, instance_id, password, region_id='cn-shanghai'):
|
||
"""更新实例密码"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.UpdateInstanceAttributeRequest(
|
||
region_id=region_id,
|
||
instance_id=instance_id,
|
||
password=password
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.update_instance_attribute_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '密码更新成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("更新实例密码失败", f"更新实例 {instance_id} 密码时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '密码更新失败'}
|
||
|
||
|
||
|
||
def list_instances(self, page_number=1, page_size=20, region_id='cn-shanghai'):
|
||
"""获取实例列表"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.ListInstancesRequest(
|
||
region_id=region_id,
|
||
page_number=page_number,
|
||
page_size=page_size
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.list_instances_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '获取实例列表成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("获取实例列表失败", f"获取实例列表时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '获取实例列表失败'}
|
||
|
||
def delete_instance(self, instance_id, region_id='cn-shanghai'):
|
||
"""删除实例"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.DeleteInstanceRequest(
|
||
region_id=region_id,
|
||
instance_id=instance_id
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.delete_instance_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '实例删除成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("删除实例失败", f"删除实例 {instance_id} 时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '实例删除失败'}
|
||
|
||
def get_plans(self, region_id='cn-shanghai'):
|
||
"""获取可用套餐列表"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.ListPlansRequest(
|
||
region_id=region_id
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.list_plans_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '获取套餐列表成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("获取套餐列表失败", f"获取套餐列表时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '获取套餐列表失败'}
|
||
|
||
def get_images(self, image_type='system', region_id='cn-shanghai'):
|
||
"""获取可用镜像列表"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.ListImagesRequest(
|
||
region_id=region_id,
|
||
image_type=image_type
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.list_images_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '获取镜像列表成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("获取镜像列表失败", f"获取镜像列表时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '获取镜像列表失败'}
|
||
|
||
def get_regions(self):
|
||
"""获取可用地域列表"""
|
||
try:
|
||
# 直接使用凭据创建配置
|
||
config = open_api_models.Config(
|
||
access_key_id=self.access_key_id,
|
||
access_key_secret=self.access_secret
|
||
)
|
||
# 默认用上海endpoint,获取所有地域
|
||
config.endpoint = 'swas.cn-shanghai.aliyuncs.com'
|
||
client = SWAS_OPEN20200601Client(config)
|
||
request = swas__open20200601_models.ListRegionsRequest(accept_language='zh-CN')
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.list_regions_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '获取地域列表成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("获取地域列表失败", f"获取地域列表时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '获取地域列表失败'}
|
||
|
||
def create_instance_key_pair(self, instance_id, key_pair_name, region_id='cn-shanghai'):
|
||
"""为实例创建密钥对"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.CreateInstanceKeyPairRequest(
|
||
region_id=region_id,
|
||
instance_id=instance_id,
|
||
key_pair_name=key_pair_name
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.create_instance_key_pair_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '密钥对创建成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("创建密钥对失败", f"为实例 {instance_id} 创建密钥对 {key_pair_name} 时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '密钥对创建失败'}
|
||
|
||
|
||
|
||
def delete_instance_key_pair(self, instance_id, region_id='cn-shanghai'):
|
||
"""解绑实例的密钥对"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.DeleteInstanceKeyPairRequest(
|
||
region_id=region_id,
|
||
instance_id=instance_id
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.delete_instance_key_pair_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '密钥对解绑成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("解绑密钥对失败", f"解绑实例 {instance_id} 密钥对时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '密钥对解绑失败'}
|
||
|
||
def delete_key_pairs(self, key_pair_names, region_id='cn-shanghai'):
|
||
"""删除密钥对"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.DeleteKeyPairsRequest(
|
||
region_id=region_id,
|
||
key_pair_names=key_pair_names
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.delete_key_pairs_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '密钥对删除成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("删除密钥对失败", f"删除密钥对 {key_pair_names} 时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '密钥对删除失败'}
|
||
|
||
def get_instance_key_pair(self, instance_id, region_id='cn-shanghai'):
|
||
"""获取实例的密钥对详细信息"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.DescribeInstanceKeyPairRequest(
|
||
region_id=region_id,
|
||
instance_id=instance_id
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.describe_instance_key_pair_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '获取密钥对详细信息成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("获取密钥对详细信息失败", f"获取实例 {instance_id} 密钥对详细信息时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '获取密钥对详细信息失败'}
|
||
|
||
def get_instance_details(self, instance_ids, region_id='cn-shanghai'):
|
||
"""获取实例详细信息(支持批量查询)"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
# 如果instance_ids是列表,转换为JSON字符串
|
||
if isinstance(instance_ids, list):
|
||
instance_ids_str = json.dumps(instance_ids)
|
||
else:
|
||
instance_ids_str = instance_ids
|
||
|
||
request = swas__open20200601_models.ListInstancesRequest(
|
||
region_id=region_id,
|
||
instance_ids=instance_ids_str
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.list_instances_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '获取实例详细信息成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("获取实例详细信息失败", f"获取实例详细信息时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '获取实例详细信息失败'}
|
||
|
||
def get_instance_upgrade_plans(self, instance_id, region_id='cn-shanghai'):
|
||
"""获取指定实例可升级的套餐列表"""
|
||
client = self._get_client(region_id)
|
||
try:
|
||
request = swas__open20200601_models.ListInstancePlansModificationRequest(
|
||
region_id=region_id,
|
||
instance_id=instance_id
|
||
)
|
||
runtime = util_models.RuntimeOptions()
|
||
response = client.list_instance_plans_modification_with_options(request, runtime)
|
||
return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '获取可升级套餐列表成功'}
|
||
except Exception as e:
|
||
jingrow.log_error("获取可升级套餐列表失败", f"获取实例 {instance_id} 可升级套餐时发生错误: {str(e)}")
|
||
return {'success': False, 'error': str(e), 'message': '获取可升级套餐列表失败'}
|
||
|
||
|
||
# 全局管理器实例
|
||
_aliyun_manager = None
|
||
|
||
def _get_manager():
|
||
"""获取阿里云管理器实例"""
|
||
global _aliyun_manager
|
||
if _aliyun_manager is None:
|
||
_aliyun_manager = AliyunLightServerManager.get_instance()
|
||
return _aliyun_manager
|
||
|
||
|
||
# API函数 - 使用@jingrow.whitelist()装饰器
|
||
@jingrow.whitelist()
|
||
def create_aliyun_instance(plan_id, image_id, period=1, region_id='cn-shanghai'):
|
||
"""创建轻量应用服务器实例"""
|
||
manager = _get_manager()
|
||
return manager.create_instance(plan_id, image_id, period, region_id)
|
||
|
||
@jingrow.whitelist()
|
||
def start_aliyun_instance(instance_id, region_id='cn-shanghai'):
|
||
"""启动实例"""
|
||
manager = _get_manager()
|
||
return manager.start_instance(instance_id, region_id)
|
||
|
||
@jingrow.whitelist()
|
||
def stop_aliyun_instance(instance_id, region_id='cn-shanghai'):
|
||
"""停止实例"""
|
||
manager = _get_manager()
|
||
return manager.stop_instance(instance_id, region_id)
|
||
|
||
@jingrow.whitelist()
|
||
def reboot_aliyun_instance(instance_id, region_id='cn-shanghai'):
|
||
"""重启实例并更新状态"""
|
||
try:
|
||
# 1. 查找对应的Jsite Server记录
|
||
server = jingrow.get_pg("Jsite Server", {"instance_id": instance_id})
|
||
if not server:
|
||
return {"success": False, "message": "找不到对应的服务器记录"}
|
||
|
||
# 2. 立即更新状态为"重启中"
|
||
server.status = "Starting"
|
||
server.save(ignore_permissions=True)
|
||
jingrow.db.commit()
|
||
|
||
# 3. 调用阿里云API重启实例
|
||
manager = _get_manager()
|
||
result = manager.reboot_instance(instance_id, region_id)
|
||
|
||
# 4. 启动后台任务监控重启状态(延迟10秒开始)
|
||
time.sleep(10)
|
||
jingrow.enqueue(
|
||
"jcloud.api.aliyun_server_light.monitor_server_status",
|
||
instance_id=instance_id,
|
||
region_id=region_id,
|
||
timeout=600
|
||
)
|
||
|
||
return {
|
||
"success": True,
|
||
"message": "重启命令已发送,正在监控重启状态...",
|
||
"server_name": server.name
|
||
}
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("重启实例失败", f"重启实例 {instance_id} 时发生错误: {str(e)}")
|
||
return {"success": False, "error": str(e), "message": "重启实例失败"}
|
||
|
||
|
||
def monitor_server_status(instance_id, region_id):
|
||
"""通用的服务器状态监控 - 监控服务器状态直到Running"""
|
||
try:
|
||
# 通过instance_id查找服务器记录
|
||
server = jingrow.get_pg("Jsite Server", {"instance_id": instance_id})
|
||
if not server:
|
||
jingrow.log_error("找不到服务器记录", f"无法找到实例ID为 {instance_id} 的服务器记录")
|
||
return
|
||
|
||
manager = _get_manager()
|
||
max_retries = 60 # 最多查询60次(10分钟)
|
||
# 阿里云轻量应用服务器可能的状态值
|
||
waiting_states = ['Starting', 'Stopping', 'Resetting', 'Stopped', 'Pending', 'Upgrading']
|
||
|
||
for retry in range(max_retries):
|
||
# 查询实例状态
|
||
instance_info = manager.get_instance_details([instance_id], region_id)
|
||
|
||
if not instance_info or not instance_info.get('success'):
|
||
jingrow.log_error("获取实例状态失败", f"无法获取实例 {instance_id} 的状态信息")
|
||
time.sleep(10)
|
||
continue
|
||
|
||
# 解析实例状态
|
||
instances = instance_info.get('data', {}).get('instances', [])
|
||
if not instances:
|
||
jingrow.log_error("获取实例状态失败", f"实例 {instance_id} 的状态信息为空")
|
||
time.sleep(10)
|
||
continue
|
||
|
||
instance = instances[0] # 取第一个实例
|
||
status = instance.get('status', '')
|
||
|
||
# 更新服务器状态
|
||
if status == 'Running':
|
||
server.status = 'Running'
|
||
server.save(ignore_permissions=True)
|
||
jingrow.db.commit()
|
||
return
|
||
elif status in waiting_states:
|
||
# 继续等待
|
||
time.sleep(10)
|
||
else:
|
||
server.status = status
|
||
server.save(ignore_permissions=True)
|
||
jingrow.db.commit()
|
||
return
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("监控状态失败", f"监控实例 {instance_id} 状态时发生错误: {str(e)}")
|
||
|
||
@jingrow.whitelist()
|
||
def upgrade_aliyun_instance(instance_id, plan_id, region_id='cn-shanghai'):
|
||
"""升级实例配置并更新状态"""
|
||
try:
|
||
# 1. 查找对应的Jsite Server记录
|
||
server = jingrow.get_pg("Jsite Server", {"instance_id": instance_id})
|
||
if not server:
|
||
return {"success": False, "message": "找不到对应的服务器记录"}
|
||
|
||
# 2. 立即更新状态为"升级中"
|
||
server.status = "Upgrading"
|
||
server.save(ignore_permissions=True)
|
||
jingrow.db.commit()
|
||
|
||
# 3. 调用阿里云API升级实例
|
||
manager = _get_manager()
|
||
result = manager.upgrade_instance(instance_id, plan_id, region_id)
|
||
|
||
if not result or not result.get('success'):
|
||
# 如果升级失败,恢复状态
|
||
server.status = "Running"
|
||
server.save(ignore_permissions=True)
|
||
jingrow.db.commit()
|
||
return result
|
||
|
||
# 4. 启动后台任务监控升级状态(延迟10秒开始)
|
||
time.sleep(10)
|
||
jingrow.enqueue(
|
||
"jcloud.api.aliyun_server_light.monitor_server_status",
|
||
instance_id=instance_id,
|
||
region_id=region_id,
|
||
timeout=600
|
||
)
|
||
|
||
return {
|
||
"success": True,
|
||
"message": "升级命令已发送,正在监控升级状态...",
|
||
"server_name": server.name
|
||
}
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("升级实例失败", f"升级实例 {instance_id} 时发生错误: {str(e)}")
|
||
return {"success": False, "error": str(e), "message": "升级实例失败"}
|
||
|
||
@jingrow.whitelist()
|
||
def renew_aliyun_instance(instance_id, period=1, region_id='cn-shanghai'):
|
||
"""续费实例"""
|
||
manager = _get_manager()
|
||
return manager.renew_instance(instance_id, period, region_id)
|
||
|
||
@jingrow.whitelist()
|
||
def reset_aliyun_instance_system(instance_id, image_id=None, password=None, region_id='cn-shanghai'):
|
||
"""重置系统并更新状态"""
|
||
try:
|
||
# 1. 查找对应的Jsite Server记录
|
||
server = jingrow.get_pg("Jsite Server", {"instance_id": instance_id})
|
||
if not server:
|
||
return {"success": False, "message": "找不到对应的服务器记录"}
|
||
|
||
# 2. 立即更新状态为"重置中"
|
||
server.status = "Resetting"
|
||
server.save(ignore_permissions=True)
|
||
jingrow.db.commit()
|
||
|
||
# 3. 调用阿里云API重置系统
|
||
manager = _get_manager()
|
||
result = manager.reset_system(instance_id, image_id, password, region_id)
|
||
|
||
# 4. 启动后台任务监控重置状态(延迟10秒开始)
|
||
time.sleep(10)
|
||
jingrow.enqueue(
|
||
"jcloud.api.aliyun_server_light.monitor_server_status",
|
||
instance_id=instance_id,
|
||
region_id=region_id,
|
||
timeout=600
|
||
)
|
||
|
||
return {
|
||
"success": True,
|
||
"message": "重置命令已发送,正在监控重置状态...",
|
||
"server_name": server.name
|
||
}
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("重置系统失败", f"重置实例 {instance_id} 系统时发生错误: {str(e)}")
|
||
return {"success": False, "error": str(e), "message": "重置系统失败"}
|
||
|
||
@jingrow.whitelist()
|
||
def update_aliyun_instance_password(instance_id, password, region_id='cn-shanghai'):
|
||
"""更新实例密码"""
|
||
manager = _get_manager()
|
||
return manager.update_instance_password(instance_id, password, region_id)
|
||
|
||
@jingrow.whitelist()
|
||
def reset_aliyun_instance_password(instance_id, password, region_id='cn-shanghai'):
|
||
"""重置实例密码并更新Jsite Server记录"""
|
||
try:
|
||
# 1. 查找对应的Jsite Server记录
|
||
server = jingrow.get_pg("Jsite Server", {"instance_id": instance_id})
|
||
if not server:
|
||
return {"success": False, "message": "找不到对应的服务器记录"}
|
||
|
||
# 2. 调用阿里云API更新密码
|
||
manager = _get_manager()
|
||
result = manager.update_instance_password(instance_id, password, region_id)
|
||
|
||
if not result or not result.get('success'):
|
||
error_msg = f"阿里云密码更新失败: {result.get('message', '未知错误')}"
|
||
jingrow.log_error("重置密码失败", error_msg)
|
||
return {"success": False, "message": error_msg}
|
||
|
||
# 3. 更新Jsite Server记录中的password字段
|
||
server.password = password
|
||
server.save(ignore_permissions=True)
|
||
jingrow.db.commit()
|
||
|
||
# 4. 自动重启实例
|
||
reboot_aliyun_instance(instance_id, region_id)
|
||
|
||
return {
|
||
"success": True,
|
||
"message": "密码重置成功",
|
||
"server_name": server.name,
|
||
"instance_id": instance_id
|
||
}
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("重置密码失败", f"重置实例 {instance_id} 密码时发生错误: {str(e)}")
|
||
return {"success": False, "error": str(e), "message": "密码重置失败"}
|
||
|
||
|
||
@jingrow.whitelist()
|
||
def list_aliyun_instances(page_number=1, page_size=20, region_id='cn-shanghai'):
|
||
"""获取实例列表"""
|
||
manager = _get_manager()
|
||
return manager.list_instances(page_number, page_size, region_id)
|
||
|
||
@jingrow.whitelist()
|
||
def delete_aliyun_instance(instance_id, region_id='cn-shanghai'):
|
||
"""删除实例"""
|
||
manager = _get_manager()
|
||
return manager.delete_instance(instance_id, region_id)
|
||
|
||
@jingrow.whitelist()
|
||
def get_aliyun_plans(region_id='cn-shanghai'):
|
||
"""获取可用套餐列表"""
|
||
manager = _get_manager()
|
||
result = manager.get_plans(region_id)
|
||
|
||
# 过滤指定类型的套餐
|
||
if result.get('success') and result.get('data') and 'plans' in result['data']:
|
||
allowed_types = ['NORMAL', 'MULTI_IP', 'INTERNATIONAL', 'CAPACITY']
|
||
filtered_plans = [
|
||
plan for plan in result['data']['plans']
|
||
if plan.get('plan_type') in allowed_types
|
||
]
|
||
|
||
# 在origin_price上统一增加20%作为利润
|
||
for plan in filtered_plans:
|
||
if 'origin_price' in plan and plan['origin_price'] is not None:
|
||
original_price = float(plan['origin_price'])
|
||
plan['origin_price'] = int(original_price / (1 - 0.2)) # 确保20%利润率且价格为整数
|
||
|
||
result['data']['plans'] = filtered_plans
|
||
|
||
return result
|
||
|
||
@jingrow.whitelist()
|
||
def get_aliyun_images(image_type='system', region_id='cn-shanghai'):
|
||
"""获取可用镜像列表"""
|
||
manager = _get_manager()
|
||
result = manager.get_images(image_type, region_id)
|
||
|
||
# 如果获取成功且有镜像数据,过滤并排序
|
||
if result.get('success') and result.get('data') and 'images' in result['data']:
|
||
images = result['data']['images']
|
||
# 过滤掉以 "alibaba" 开头的镜像(不区分大小写)
|
||
filtered_images = [
|
||
image for image in images
|
||
if not image.get('image_name', '').lower().startswith('alibaba')
|
||
]
|
||
# 按镜像名称(image_name)进行字母排序
|
||
sorted_images = sorted(filtered_images, key=lambda x: x.get('image_name', '').lower())
|
||
result['data']['images'] = sorted_images
|
||
|
||
return result
|
||
|
||
@jingrow.whitelist()
|
||
def get_aliyun_regions():
|
||
"""通过阿里云SDK实时获取可用地域列表"""
|
||
manager = _get_manager()
|
||
return manager.get_regions()
|
||
|
||
@jingrow.whitelist()
|
||
def create_aliyun_instance_key_pair(instance_id, key_pair_name, region_id='cn-shanghai'):
|
||
"""为实例创建密钥对"""
|
||
manager = _get_manager()
|
||
return manager.create_instance_key_pair(instance_id, key_pair_name, region_id)
|
||
|
||
|
||
|
||
@jingrow.whitelist()
|
||
def delete_aliyun_instance_key_pair(instance_id):
|
||
"""解绑并删除实例的密钥对"""
|
||
try:
|
||
# 1. 查找对应的Jsite Server记录获取key_pair_name和region_id
|
||
server = jingrow.get_pg("Jsite Server", {"instance_id": instance_id})
|
||
if not server:
|
||
return {"success": False, "message": "找不到对应的服务器记录"}
|
||
|
||
key_pair_name = server.key_pair_name
|
||
region_id = server.region
|
||
|
||
if not key_pair_name:
|
||
return {"success": True, "message": "实例没有关联的密钥对"}
|
||
|
||
manager = _get_manager()
|
||
|
||
# 2. 解绑实例的密钥对
|
||
unbind_result = manager.delete_instance_key_pair(instance_id, region_id)
|
||
if not unbind_result or not unbind_result.get('success'):
|
||
return {"success": False, "message": f"解绑密钥对失败: {unbind_result.get('message', '未知错误')}"}
|
||
|
||
# 3. 删除密钥对
|
||
delete_result = manager.delete_key_pairs([key_pair_name], region_id)
|
||
if not delete_result or not delete_result.get('success'):
|
||
return {"success": False, "message": f"删除密钥对失败: {delete_result.get('message', '未知错误')}"}
|
||
|
||
# 4. 清除服务器记录中的密钥对信息
|
||
server.key_pair_name = None
|
||
server.private_key = None
|
||
server.save(ignore_permissions=True)
|
||
jingrow.db.commit()
|
||
|
||
return {"success": True, "message": "密钥对解绑并删除成功"}
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("删除实例密钥对失败", f"删除实例 {instance_id} 密钥对时发生错误: {str(e)}")
|
||
return {"success": False, "message": str(e)}
|
||
|
||
|
||
@jingrow.whitelist()
|
||
def get_aliyun_instance_key_pair(instance_id, region_id='cn-shanghai'):
|
||
"""获取实例的密钥对详细信息"""
|
||
manager = _get_manager()
|
||
return manager.get_instance_key_pair(instance_id, region_id)
|
||
|
||
@jingrow.whitelist()
|
||
def get_aliyun_instance_details(instance_ids, region_id='cn-shanghai'):
|
||
"""获取实例详细信息(支持批量查询)"""
|
||
manager = _get_manager()
|
||
return manager.get_instance_details(instance_ids, region_id)
|
||
|
||
@jingrow.whitelist()
|
||
def get_aliyun_instance_upgrade_plans(instance_id):
|
||
"""获取指定实例可升级的套餐列表"""
|
||
try:
|
||
# 获取当前实例信息
|
||
server = jingrow.get_pg("Jsite Server", {"instance_id": instance_id})
|
||
if not server:
|
||
return {"success": False, "message": "找不到服务器记录"}
|
||
|
||
# 从实例记录中获取region_id
|
||
region_id = server.region
|
||
if not region_id:
|
||
return {"success": False, "message": "服务器记录缺少region信息"}
|
||
|
||
# 从get_plans获取所有套餐
|
||
manager = _get_manager()
|
||
result = manager.get_plans(region_id)
|
||
|
||
if not result or not result.get('success'):
|
||
return result
|
||
|
||
# 过滤套餐:只返回PlanType值一致的套餐,且价格大于等于当前套餐价格
|
||
if result.get('data') and 'plans' in result['data']:
|
||
filtered_plans = []
|
||
current_plan_price = server.plan_price or 0
|
||
|
||
for plan in result['data']['plans']:
|
||
# 检查plan_type是否与服务器的PlanType一致
|
||
if plan.get('plan_type') == server.plan_type:
|
||
# 检查support_platform是否包含当前实例的os_type
|
||
support_platform_str = plan.get('support_platform', '')
|
||
try:
|
||
support_platform_list = json.loads(support_platform_str) if support_platform_str else []
|
||
if server.os_type and server.os_type in support_platform_list:
|
||
# 在origin_price上统一增加20%作为利润
|
||
if 'origin_price' in plan and plan['origin_price'] is not None:
|
||
original_price = float(plan['origin_price'])
|
||
adjusted_price = int(original_price / (1 - 0.2)) # 确保20%利润率且价格为整数
|
||
plan['origin_price'] = adjusted_price
|
||
|
||
# 只显示价格大于等于当前套餐价格的套餐
|
||
if adjusted_price >= current_plan_price:
|
||
filtered_plans.append(plan)
|
||
except:
|
||
continue
|
||
|
||
result['data']['plans'] = filtered_plans
|
||
|
||
return result
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("获取可升级套餐列表失败", f"获取实例 {instance_id} 可升级套餐时发生错误: {str(e)}")
|
||
return {"success": False, "error": str(e), "message": "获取可升级套餐列表失败"}
|
||
|
||
|
||
# 服务器订单和创建相关
|
||
|
||
@jingrow.whitelist()
|
||
def create_server_order(**kwargs):
|
||
"""创建服务器订单"""
|
||
try:
|
||
plan_id = kwargs.get('plan_id')
|
||
image_id = kwargs.get('image_id')
|
||
period = kwargs.get('period', 1)
|
||
region_id = kwargs.get('region_id', 'cn-shanghai')
|
||
|
||
if not plan_id or not image_id:
|
||
jingrow.throw("缺少必要参数")
|
||
|
||
team = get_current_team(True)
|
||
|
||
# 获取套餐价格
|
||
plans_response = get_aliyun_plans(region_id)
|
||
selected_plan = None
|
||
if plans_response and plans_response.get('success'):
|
||
plans = plans_response['data'].get('plans', [])
|
||
selected_plan = next((p for p in plans if p.get('plan_id') == plan_id), None)
|
||
|
||
if not selected_plan:
|
||
jingrow.throw("套餐不存在")
|
||
|
||
# 计算价格
|
||
monthly_price = float(selected_plan.get('origin_price', 0))
|
||
total_amount = monthly_price * period
|
||
|
||
# 生成订单号
|
||
order_id = datetime.now().strftime('%Y%m%d%H%M%S%f')[:-3] + ''.join(random.choices('0123456789', k=6))
|
||
|
||
# 创建订单记录
|
||
order = jingrow.get_pg({
|
||
"pagetype": "Order",
|
||
"order_id": order_id,
|
||
"order_type": "新建服务器",
|
||
"team": team.name,
|
||
"status": "待支付",
|
||
"total_amount": total_amount,
|
||
"title": f"{region_id}",
|
||
"description": f"{selected_plan.get('core')}核/{selected_plan.get('memory')}GB/{selected_plan.get('disk_size')}GB, {period}个月"
|
||
})
|
||
order.insert(ignore_permissions=True)
|
||
|
||
# 创建服务器记录
|
||
server = jingrow.get_pg({
|
||
"pagetype": "Jsite Server",
|
||
"team": team.name,
|
||
"order_id": order_id,
|
||
"status": "Pending",
|
||
"region": region_id,
|
||
"image_id": image_id,
|
||
"end_date": jingrow.utils.add_months(jingrow.utils.nowdate(), period),
|
||
"title": f"{region_id} - {selected_plan.get('core')}核/{selected_plan.get('memory')}GB",
|
||
"planid": plan_id,
|
||
"period": period,
|
||
"plan_price": monthly_price,
|
||
"cpu": selected_plan.get('core'),
|
||
"memory": selected_plan.get('memory'),
|
||
"disk_size": selected_plan.get('disk_size'),
|
||
"bandwidth": selected_plan.get('bandwidth')
|
||
})
|
||
server.insert(ignore_permissions=True)
|
||
|
||
jingrow.db.commit()
|
||
|
||
return {"success": True, "order": order.as_dict(), "server": server.as_dict()}
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("创建服务器订单失败", str(e))
|
||
return {"success": False, "message": str(e)}
|
||
|
||
@jingrow.whitelist()
|
||
def create_server_renew_order(**kwargs):
|
||
"""创建服务器续费订单"""
|
||
try:
|
||
server = kwargs.get('server')
|
||
renewal_months = kwargs.get('renewal_months', 1)
|
||
|
||
if not server:
|
||
jingrow.throw("缺少服务器信息")
|
||
|
||
# 验证输入
|
||
server_pg = jingrow.get_pg("Jsite Server", server)
|
||
if not server_pg:
|
||
jingrow.throw("服务器不存在")
|
||
|
||
team = server_pg.team
|
||
|
||
# 验证当前用户权限
|
||
current_team = get_current_team(True)
|
||
if current_team.name != team:
|
||
jingrow.throw("您没有权限为此服务器创建续费订单")
|
||
|
||
# 计算续费金额
|
||
renewal_months = int(renewal_months)
|
||
plan_price = server_pg.plan_price or 0
|
||
total_amount = plan_price * renewal_months
|
||
|
||
# 生成唯一订单号
|
||
order_id = datetime.now().strftime('%Y%m%d%H%M%S%f')[:-3] + ''.join(random.choices('0123456789', k=6))
|
||
|
||
# 创建订单记录
|
||
order = jingrow.get_pg({
|
||
"pagetype": "Order",
|
||
"order_id": order_id,
|
||
"order_type": "服务器续费",
|
||
"team": team,
|
||
"status": "待支付",
|
||
"total_amount": total_amount,
|
||
"title": server_pg.instance_id,
|
||
"description": str(renewal_months) # 存储续费月数
|
||
})
|
||
|
||
order.insert(ignore_permissions=True)
|
||
jingrow.db.commit()
|
||
|
||
return {
|
||
"success": True,
|
||
"order": order.as_dict()
|
||
}
|
||
except Exception as e:
|
||
jingrow.log_error("续费订单错误", f"创建续费订单失败: {str(e)}")
|
||
return {
|
||
"success": False,
|
||
"message": f"创建续费订单失败: {str(e)}"
|
||
}
|
||
|
||
@jingrow.whitelist()
|
||
def create_server_upgrade_order(**kwargs):
|
||
"""创建服务器升级订单"""
|
||
try:
|
||
server = kwargs.get('server')
|
||
new_plan_id = kwargs.get('new_plan_id')
|
||
|
||
if not server or not new_plan_id:
|
||
jingrow.throw("缺少必要参数")
|
||
|
||
# 验证输入
|
||
server_pg = jingrow.get_pg("Jsite Server", server)
|
||
if not server_pg:
|
||
jingrow.throw("服务器不存在")
|
||
|
||
team = server_pg.team
|
||
|
||
# 验证当前用户权限
|
||
current_team = get_current_team(True)
|
||
if current_team.name != team:
|
||
jingrow.throw("您没有权限为此服务器创建升级订单")
|
||
|
||
# 获取当前套餐和新套餐信息
|
||
current_plan_price = server_pg.plan_price or 0
|
||
|
||
# 获取新套餐价格
|
||
plans_response = get_aliyun_plans(server_pg.region)
|
||
new_plan = None
|
||
if plans_response and plans_response.get('success'):
|
||
plans = plans_response['data'].get('plans', [])
|
||
new_plan = next((p for p in plans if p.get('plan_id') == new_plan_id), None)
|
||
|
||
if not new_plan:
|
||
jingrow.throw("新套餐不存在")
|
||
|
||
new_plan_price = float(new_plan.get('origin_price', 0))
|
||
|
||
# 计算剩余天数
|
||
if server_pg.end_date:
|
||
try:
|
||
# 确保end_date是datetime.date对象
|
||
if isinstance(server_pg.end_date, str):
|
||
end_date = jingrow.utils.getdate(server_pg.end_date)
|
||
else:
|
||
end_date = server_pg.end_date
|
||
current_date = jingrow.utils.nowdate()
|
||
remaining_days = (end_date - current_date).days
|
||
if remaining_days <= 0:
|
||
remaining_days = 1 # 至少1天
|
||
except Exception as e:
|
||
jingrow.log_error("计算剩余天数失败", f"计算实例 {server_pg.instance_id} 剩余天数时发生错误: {str(e)}")
|
||
remaining_days = 30 # 出错时使用默认值
|
||
else:
|
||
remaining_days = 30 # 默认30天
|
||
|
||
# 计算升级费用:按天计费,全额补差价
|
||
daily_price_diff = (new_plan_price - current_plan_price) / 30
|
||
upgrade_amount = daily_price_diff * remaining_days
|
||
|
||
# 确保升级费用不为负数
|
||
if upgrade_amount < 0:
|
||
upgrade_amount = 0
|
||
|
||
# 生成唯一订单号
|
||
order_id = datetime.now().strftime('%Y%m%d%H%M%S%f')[:-3] + ''.join(random.choices('0123456789', k=6))
|
||
|
||
# 创建订单记录
|
||
order = jingrow.get_pg({
|
||
"pagetype": "Order",
|
||
"order_id": order_id,
|
||
"order_type": "服务器升级",
|
||
"team": team,
|
||
"status": "待支付",
|
||
"total_amount": upgrade_amount,
|
||
"title": server_pg.instance_id,
|
||
"description": new_plan_id # 存储新套餐ID
|
||
})
|
||
|
||
order.insert(ignore_permissions=True)
|
||
jingrow.db.commit()
|
||
|
||
return {
|
||
"success": True,
|
||
"order": order.as_dict(),
|
||
"upgrade_info": {
|
||
"current_plan_price": current_plan_price,
|
||
"new_plan_price": new_plan_price,
|
||
"remaining_days": remaining_days,
|
||
"daily_price_diff": daily_price_diff,
|
||
"upgrade_amount": upgrade_amount
|
||
}
|
||
}
|
||
except Exception as e:
|
||
jingrow.log_error("升级订单错误", f"创建升级订单失败: {str(e)}")
|
||
return {
|
||
"success": False,
|
||
"message": f"创建升级订单失败: {str(e)}"
|
||
}
|
||
|
||
def renew_server(order_name):
|
||
"""支付成功后异步续费服务器"""
|
||
try:
|
||
order = jingrow.get_pg("Order", order_name)
|
||
if not order:
|
||
raise Exception("订单不存在")
|
||
|
||
# 从订单中获取信息
|
||
instance_id = order.title # 实例ID
|
||
renewal_months = int(order.description) # 续费月数
|
||
|
||
# 查找服务器记录获取区域信息
|
||
server = jingrow.get_pg("Jsite Server", {"instance_id": instance_id})
|
||
if not server:
|
||
raise Exception("找不到对应的服务器记录")
|
||
|
||
# 调用阿里云API续费
|
||
manager = _get_manager()
|
||
result = manager.renew_instance(instance_id, renewal_months, server.region)
|
||
|
||
if not result or not result.get('success'):
|
||
raise Exception(f"阿里云续费失败: {result.get('message', '未知错误')}")
|
||
|
||
# 更新服务器到期时间
|
||
server.end_date = jingrow.utils.add_months(server.end_date or jingrow.utils.nowdate(), renewal_months)
|
||
server.save(ignore_permissions=True)
|
||
|
||
# 更新订单状态
|
||
order.status = "交易成功"
|
||
order.save(ignore_permissions=True)
|
||
|
||
jingrow.db.commit()
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("服务器续费失败", f"订单 {order_name}: {str(e)}")
|
||
raise e
|
||
|
||
def upgrade_server(order_name):
|
||
"""支付成功后异步升级服务器"""
|
||
try:
|
||
order = jingrow.get_pg("Order", order_name)
|
||
if not order:
|
||
raise Exception("订单不存在")
|
||
|
||
# 从订单中获取信息
|
||
instance_id = order.title # 实例ID
|
||
new_plan_id = order.description # 新套餐ID
|
||
|
||
# 查找服务器记录获取区域信息
|
||
server = jingrow.get_pg("Jsite Server", {"instance_id": instance_id})
|
||
if not server:
|
||
raise Exception("找不到对应的服务器记录")
|
||
|
||
# 调用阿里云API升级实例
|
||
result = upgrade_aliyun_instance(instance_id, new_plan_id, server.region)
|
||
|
||
# 打印result到后台日志
|
||
jingrow.log_error("阿里云升级实例结果", f"订单 {order_name} 的升级结果: {result}")
|
||
|
||
if not result or not result.get('success'):
|
||
raise Exception(f"阿里云升级失败: {result.get('message', '未知错误')}")
|
||
|
||
# 更新订单状态
|
||
order.status = "交易成功"
|
||
order.save(ignore_permissions=True)
|
||
|
||
jingrow.db.commit()
|
||
|
||
# 延迟60秒执行update_server_record来更新服务器详细信息
|
||
time.sleep(60)
|
||
jingrow.enqueue(
|
||
"jcloud.api.aliyun_server_light.update_server_record",
|
||
instance_ids=json.dumps([instance_id]),
|
||
timeout=120
|
||
)
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("服务器升级失败", f"订单 {order_name}: {str(e)}")
|
||
raise e
|
||
|
||
def create_aliyun_server(order_name):
|
||
"""异步创建服务器"""
|
||
try:
|
||
order = jingrow.get_pg("Order", order_name)
|
||
if not order:
|
||
raise Exception("订单不存在")
|
||
|
||
# 查找对应的服务器记录(通过订单ID)
|
||
server = jingrow.get_pg("Jsite Server", {"order_id": order.order_id})
|
||
if not server:
|
||
raise Exception("找不到对应的服务器记录")
|
||
|
||
# 从服务器记录中获取配置信息
|
||
region_id = server.region
|
||
image_id = server.image_id or 'e9363571cf2444aba422b17470285465'
|
||
plan_id = server.planid or 'swas.s.c2m1s30b1.linux'
|
||
period = server.period or 1
|
||
|
||
# 调用阿里云API创建实例
|
||
result = create_aliyun_instance(plan_id, image_id, period, region_id)
|
||
|
||
# 打印result到后台日志
|
||
jingrow.log_error("阿里云创建实例结果", f"订单 {order_name} 的创建结果: {result}")
|
||
|
||
if not result or not result.get('success'):
|
||
raise Exception(f"阿里云创建失败: {result.get('message', '未知错误')}")
|
||
|
||
# 阿里云API返回的是instance_ids数组,取第一个实例ID
|
||
instance_id = result['data']['instance_ids'][0]
|
||
|
||
# 更新服务器记录状态
|
||
server.status = "Running"
|
||
server.instance_id = instance_id
|
||
server.save(ignore_permissions=True)
|
||
|
||
# 更新订单状态
|
||
order.status = "交易成功"
|
||
order.save(ignore_permissions=True)
|
||
|
||
jingrow.db.commit()
|
||
|
||
# 延迟60秒执行update_server_record来更新服务器详细信息
|
||
time.sleep(60)
|
||
jingrow.enqueue(
|
||
"jcloud.api.aliyun_server_light.update_server_record",
|
||
instance_ids=json.dumps([instance_id]),
|
||
timeout=120
|
||
)
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("服务器创建失败", f"订单 {order_name}: {str(e)}")
|
||
raise e
|
||
|
||
@jingrow.whitelist()
|
||
def create_server_key_pair(instance_id):
|
||
"""为实例创建密钥对并保存私钥"""
|
||
try:
|
||
# 查找对应的服务器记录获取region_id
|
||
server = jingrow.get_pg("Jsite Server", {"instance_id": instance_id})
|
||
if not server:
|
||
jingrow.log_error("找不到对应的服务器记录")
|
||
|
||
region_id = server.region
|
||
uuid_suffix = str(uuid.uuid4())[:4]
|
||
key_pair_name = f"{region_id}-{instance_id[:8]}-{uuid_suffix}"
|
||
|
||
# 直接调用管理器方法创建密钥对
|
||
manager = _get_manager()
|
||
key_pair_result = manager.create_instance_key_pair(instance_id, key_pair_name, region_id)
|
||
|
||
if not key_pair_result or not key_pair_result.get('success'):
|
||
error_msg = key_pair_result.get('message', '未知错误') if key_pair_result else '返回结果为空'
|
||
jingrow.log_error(f"密钥对创建失败: {error_msg}")
|
||
return {"success": False, "message": f"密钥对创建失败: {error_msg}"}
|
||
|
||
# 获取私钥 - 直接访问data字段
|
||
key_pair_data = key_pair_result.get('data', {})
|
||
private_key = key_pair_data.get('private_key')
|
||
|
||
if private_key:
|
||
# 保存私钥到服务器记录
|
||
server.key_pair_name = key_pair_name
|
||
server.private_key = private_key
|
||
server.save(ignore_permissions=True)
|
||
jingrow.db.commit()
|
||
|
||
return {
|
||
"success": True,
|
||
"message": "密钥对创建成功",
|
||
"key_pair_name": key_pair_name,
|
||
"private_key": private_key
|
||
}
|
||
else:
|
||
jingrow.log_error("密钥对创建成功但未获取到私钥", f"完整的key_pair_result: {key_pair_result}")
|
||
return {"success": False, "message": "密钥对创建成功但未获取到私钥"}
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("创建密钥对失败", f"实例 {instance_id}: {str(e)}")
|
||
return {"success": False, "message": str(e)}
|
||
|
||
@jingrow.whitelist()
|
||
def update_server_record(instance_ids):
|
||
"""根据阿里云实例信息更新Jsite Server记录"""
|
||
try:
|
||
# 解析instance_ids(JSON字符串格式)
|
||
if isinstance(instance_ids, str):
|
||
instance_ids = json.loads(instance_ids)
|
||
|
||
# 获取第一个实例ID
|
||
instance_id = instance_ids[0]
|
||
|
||
# 查找对应的Jsite Server记录获取region_id
|
||
server = jingrow.get_pg("Jsite Server", {"instance_id": instance_id})
|
||
if not server:
|
||
jingrow.log_error("更新服务器信息失败", f"找不到实例ID为 {instance_id} 的Jsite Server记录")
|
||
return {"success": False, "message": f"找不到实例ID为 {instance_id} 的Jsite Server记录"}
|
||
|
||
region_id = server.region
|
||
|
||
# 获取阿里云实例详细信息
|
||
instance_details = get_aliyun_instance_details(instance_ids, region_id)
|
||
|
||
if not instance_details or not instance_details.get('success'):
|
||
error_msg = f"获取阿里云实例信息失败: {instance_details.get('message', '未知错误')}"
|
||
jingrow.log_error("更新服务器信息失败", error_msg)
|
||
return {"success": False, "message": error_msg}
|
||
|
||
# 解析实例信息
|
||
instances = instance_details.get('data', {}).get('instances', [])
|
||
if not instances:
|
||
jingrow.log_error("更新服务器信息失败", "未找到实例信息")
|
||
return {"success": False, "message": "未找到实例信息"}
|
||
|
||
instance_info = instances[0] # 取第一个实例
|
||
|
||
# 更新public_ip
|
||
public_ip = instance_info.get('public_ip_address')
|
||
if public_ip:
|
||
server.public_ip = public_ip
|
||
|
||
# 更新end_date(从expired_time转换)
|
||
expired_time = instance_info.get('expired_time')
|
||
if expired_time:
|
||
try:
|
||
expired_datetime = datetime.fromisoformat(expired_time.replace('Z', '+00:00'))
|
||
server.end_date = expired_datetime.strftime('%Y-%m-%d %H:%M:%S')
|
||
except Exception as e:
|
||
jingrow.log_error("时间格式转换失败", f"转换expired_time {expired_time} 时发生错误: {str(e)}")
|
||
|
||
# 更新其他相关信息
|
||
server.status = instance_info.get('status', '')
|
||
|
||
# 更新系统信息
|
||
image_info = instance_info.get('image', {})
|
||
if image_info:
|
||
image_name = image_info.get('image_name', '')
|
||
image_version = image_info.get('image_version', '')
|
||
os_type = image_info.get('os_type', '')
|
||
|
||
server.system = f"{image_name} {image_version}".strip()
|
||
server.os_type = os_type
|
||
|
||
# 更新资源规格信息
|
||
resource_spec = instance_info.get('resource_spec', {})
|
||
if resource_spec:
|
||
server.cpu = resource_spec.get('cpu')
|
||
server.memory = resource_spec.get('memory')
|
||
server.disk_size = resource_spec.get('disk_size')
|
||
server.bandwidth = resource_spec.get('bandwidth')
|
||
|
||
# 更新套餐价格信息
|
||
plan_id = instance_info.get('plan_id')
|
||
if plan_id:
|
||
# 获取套餐信息来计算价格
|
||
plans_result = get_aliyun_plans(region_id)
|
||
if plans_result and plans_result.get('success') and plans_result.get('data', {}).get('plans'):
|
||
for plan in plans_result['data']['plans']:
|
||
if plan.get('plan_id') == plan_id:
|
||
# 使用调整后的价格(已经包含20%利润)
|
||
if 'origin_price' in plan and plan['origin_price'] is not None:
|
||
server.plan_price = float(plan['origin_price'])
|
||
break
|
||
|
||
# 保存更新
|
||
server.save(ignore_permissions=True)
|
||
jingrow.db.commit()
|
||
|
||
return {
|
||
"success": True,
|
||
"message": "服务器信息更新成功",
|
||
"updated_fields": {
|
||
"public_ip": public_ip,
|
||
"end_date": server.end_date,
|
||
"status": server.status,
|
||
"system": server.system,
|
||
"os_type": server.os_type,
|
||
"plan_price": server.plan_price
|
||
}
|
||
}
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("更新服务器信息失败", f"更新服务器信息时发生错误: {str(e)}")
|
||
return {"success": False, "message": str(e)}
|
||
|
||
@jingrow.whitelist()
|
||
def reset_server_key_pair(instance_id):
|
||
"""重置服务器密钥对:先删除旧的,再创建新的"""
|
||
try:
|
||
# 第一步:删除旧的密钥对(如果存在)
|
||
delete_aliyun_instance_key_pair(instance_id)
|
||
|
||
# 第二步:创建新的密钥对
|
||
result = create_server_key_pair(instance_id)
|
||
|
||
return result
|
||
|
||
except Exception as e:
|
||
jingrow.log_error("重置密钥对失败", f"重置实例 {instance_id} 密钥对时发生错误: {str(e)}")
|
||
return {"success": False, "message": str(e)}
|
||
|
||
@jingrow.whitelist()
|
||
def get_password(pagetype, name, fieldname):
|
||
|
||
return jingrow.get_pg(pagetype, name).get_password(fieldname) |