package com.loadcenter.common.aliyun.service; import com.alibaba.fastjson2.JSON; import com.aliyun.ecs20140526.Client; import com.aliyun.ecs20140526.models.*; import com.aliyun.tea.TeaException; import com.aliyun.teautil.Common; import com.aliyun.teautil.models.RuntimeOptions; import com.loadcenter.common.aliyun.model.InstanceSpecification; import com.loadcenter.common.aliyun.config.AliConfig; import com.loadcenter.common.domain.InstancesInformation; import com.loadcenter.common.redis.service.RedisService; import com.loadcenter.common.utils.user.UserUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * @ClassName AliYunEcsService * @Description 阿里云ecs服务器openAPI调用 * @Author Can.J * @Date 2024/4/16 */ @Component @Slf4j public class AliYunEcsService { /* *实例规格,用于创建实例 * */ @Autowired private InstanceSpecification instanceSpecification; //应用阿里云客户端配置 private final AliConfig aliConfig; private final Client client; public AliYunEcsService(AliConfig aliConfig, Client client) { this.aliConfig = aliConfig; this.client = client; } /* * redis服务 * */ @Autowired private RedisService redisService; /** * 获取指定区域的实例ID列表 * @return * @throws Exception */ public List getIDList() throws Exception { java.util.List regionIds = com.aliyun.darabonbastring.Client.split(aliConfig.getRegionId(), ",", 50); String regionId = regionIds.get(0); DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest() .setPageSize(100) .setRegionId(regionId); DescribeInstancesResponse resp = this.client.describeInstances(describeInstancesRequest); java.util.List instances = resp.body.instances.instance; com.aliyun.teaconsole.Client.log(regionId + " 下 ECS 实例列表:"); //存储结果的List ArrayList result = new ArrayList<>(); for (DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstancesInstance instance : instances) { com.aliyun.teaconsole.Client.log("主机名:" + instance.hostName + " 实例ID:" + instance.instanceId + " CPU:" + instance.cpu + " 内存:" + instance.memory + " MB 规格:" + instance.instanceType + " 系统:" + instance.OSType + "(" + instance.OSName + ") 状态:" + instance.status); result.add(instance.instanceId); } //将查询到的ecs服务器id列表 打印控制台 log.info(JSON.toJSONString(result)); return result; } /** * 通过实例ID释放实例 多个实例ID,用英文逗号分隔 * @param instanceIds * @throws Exception */ public void releaseInstances(String instanceIds) throws Exception { // 实例名称,支持使用通配符*进行模糊搜索 String instanceName = "*"; // 强制删除有删除保护的机器 String deleteProtected = "true"; // 强制删除运行中的机器 String force = "true"; //判断是否为强制删除 if (Common.equalString(deleteProtected, "true")) { DescribeInstancesResponse describeInstancesResp = DescribeInstances(client, aliConfig.getRegionId(), instanceIds, instanceName); instanceIds = ""; for (DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstancesInstance instance : describeInstancesResp.body.instances.instance) { instanceIds = "" + instance.instanceId + "," + instanceIds + ""; if (instance.deletionProtection) { ModifyInstanceAttribute(client, instance.instanceId); } } instanceIds = com.aliyun.darabonbastring.Client.subString(instanceIds, 0, -1); } //如果入参为空 if (Common.empty(instanceIds)) { com.aliyun.teaconsole.Client.log("--------------------无有效实例可删除--------------------"); return; } DeleteInstances(client, aliConfig.getRegionId(), instanceIds, force); } /** * 查询要删除的实例 * @param client * @param regionId * @param instanceIds * @param instanceName * @return * @throws Exception */ public static DescribeInstancesResponse DescribeInstances(Client client, String regionId, String instanceIds, String instanceName) throws Exception { DescribeInstancesRequest req = new DescribeInstancesRequest() .setRegionId(regionId) .setInstanceName(instanceName); if (!Common.empty(instanceIds)) { req.instanceIds = Common.toJSONString(com.aliyun.darabonbastring.Client.split(instanceIds, ",", 50)); } DescribeInstancesResponse resp = client.describeInstances(req); com.aliyun.teaconsole.Client.log("--------------------查询需要删除的实例--------------------"); return resp; } /** * 修改实例的属性 取消 ‘释放保护’ * @param client * @param instatnceId * @throws Exception */ public static void ModifyInstanceAttribute(Client client, String instatnceId) throws Exception { ModifyInstanceAttributeRequest req = new ModifyInstanceAttributeRequest() .setInstanceId(instatnceId) .setDeletionProtection(false);//设置删除保护为false client.modifyInstanceAttribute(req); com.aliyun.teaconsole.Client.log("--------------------" + instatnceId + "释放保护取消成功--------------------"); } /** * 执行释放实例 * @param client * @param regionId * @param instanceIds * @param force * @throws Exception */ public static void DeleteInstances(com.aliyun.ecs20140526.Client client, String regionId, String instanceIds, String force) throws Exception { DeleteInstancesRequest req = new DeleteInstancesRequest() .setRegionId(regionId) .setInstanceId(com.aliyun.darabonbastring.Client.split(instanceIds, ",", 50)) .setForce(Common.equalString(force, "true")); DeleteInstancesResponse resp = client.deleteInstances(req); com.aliyun.teaconsole.Client.log("--------------------实例释放成功--------------------"); com.aliyun.teaconsole.Client.log(Common.toJSONString(Common.toMap(resp))); } /** * 创建并运行实例 * @return * @throws Exception */ public String createAndRunInstance() throws Exception { //获取实例规格 // 地域Id String regionId = aliConfig.getRegionId(); // 镜像 ID,启动实例时选择的镜像资源。 String imageId = instanceSpecification.getImageId(); // 实例规格 String instanceType = instanceSpecification.getInstanceType(); // 新创建实例所属于的安全组 ID。 String securityGroupId = instanceSpecification.getSecurityGroupId(); // 虚拟交换机 ID。 String vSwitchId = instanceSpecification.getVSwitchId(); // 公网出带宽最大值,单位为 Mbit/s。取值范围:0~100。 默认值:0。 Integer internetMaxBandwidthOut = com.aliyun.darabonbanumber.Client.parseInt(instanceSpecification.getInternetMaxBandwidthOut()); // 网络计费类型。取值范围: // PayByBandwidth: 按固定带宽计费。 // PayByTraffic: 按使用流量计费。 // 默认值:PayByTraffic。 String internetChargeType = instanceSpecification.getInternetChargeType(); // 系统盘大小 String size = instanceSpecification.getSize(); // 系统盘的云盘种类 String category = instanceSpecification.getCategory(); // ECS实例的计费方式 // PrePaid:包年包月 // PostPaid:按量付费 String instanceChargeType = instanceSpecification.getInstanceChargeType(); // 批量创建实例 return RunInstances(client, regionId, imageId, instanceType, securityGroupId, vSwitchId, internetMaxBandwidthOut, internetChargeType, size, category, instanceChargeType); } /** * RunInstances * 通过备选实例规格创建ECS实例最佳实践 * 该场景中,在调用RunInstances创建ECS实例时判断是否发生库存不足等错误, * 如果发生错误,将调用DescribeRecommendInstanceType查询备选实例, * 然后通过备选实例规格重新创建ECS实例。 * @param client * @param regionId * @param imageId * @param instanceType * @param securityGroupId * @param vSwitchId * @param internetMaxBandwidthOut * @param internetChargeType * @param size * @param category * @param instanceChargeType * @return * @throws Exception */ public static String RunInstances(Client client, String regionId, String imageId, String instanceType, String securityGroupId, String vSwitchId, Integer internetMaxBandwidthOut, String internetChargeType, String size, String category, String instanceChargeType) throws Exception { RunInstancesRequest request = new RunInstancesRequest() .setRegionId(regionId) .setImageId(imageId) .setInstanceType(instanceType) .setSecurityGroupId(securityGroupId) .setVSwitchId(vSwitchId) .setInstanceName("自动创建的实例节点") .setDescription(new Date().toLocaleString() + " 创建的实例节点") .setInternetMaxBandwidthOut(internetMaxBandwidthOut) .setInternetChargeType(internetChargeType) .setInstanceChargeType(instanceChargeType) // 批量创建五台ECS实例,如果不设置该参数,默认创建一台ECS实例。 // amount = 5, // 如果缺少库存可以接受的最低创建数量。 // minAmount = 2, // 打开预检参数功能,不会实际创建ECS实例,只检查参数正确性、用户权限或者ECS库存等问题。 // 实际情况下,设置了DryRun参数后,Amount必须为1,MinAmount必须为空,您可以根据实际需求修改代码。 // .setDryRun(true) .setDryRun(false) .setSystemDisk(new RunInstancesRequest.RunInstancesRequestSystemDisk() .setSize(size) .setCategory(category)); String result = ""; try { com.aliyun.teaconsole.Client.log("--------------------批量创建实例开始--------------------"); RunInstancesResponse responces = client.runInstances(request); com.aliyun.teaconsole.Client.log("--------------------创建实例成功,实例ID:" + Common.toJSONString(responces.body.instanceIdSets.instanceIdSet) + "--------------------"); //返回实例ID result = responces.body.instanceIdSets.instanceIdSet + "";//前后带 [] result = UserUtil.removeBrackets(result);//前后不带[] } catch (TeaException error) { com.aliyun.teaconsole.Client.log("--------------------创建实例失败:" + Common.toJSONString(error.code) + "--------------------" + error.message); } catch (Exception _error) { TeaException error = new TeaException(_error.getMessage(), _error); com.aliyun.teaconsole.Client.log("--------------------创建实例失败:" + Common.toJSONString(error.code) + "--------------------" + error.message); } return result; } /** * 查询一台或多台实例的信息 多台实例用英文逗号拼接 * @param instanceIds * @return * @throws Exception */ public List queryInstancesInformation(String instanceIds) throws Exception { //初始化计数器 AtomicInteger count = new AtomicInteger(); DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest() .setRegionId(aliConfig.getRegionId()) .setInstanceName("*") .setInstanceIds(Common.toJSONString(com.aliyun.darabonbastring.Client.split(instanceIds, ",", 50))) .setPageSize(10); RuntimeOptions runtime = new RuntimeOptions(); //初始化返回值 List instances = null; try { // 复制代码运行请自行打印 API 的返回值 DescribeInstancesResponse describeInstancesResponse = client.describeInstancesWithOptions(describeInstancesRequest, runtime); DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstances bodyInstances = describeInstancesResponse.getBody().getInstances(); instances = bodyInstances.getInstance(); //新建List,用于redis存储实例信息 ArrayList instancesInformations = new ArrayList<>(); for (DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstancesInstance item : instances) { log.info("查询第{" + count.get() + 1 + "}个实例的ID:" + item.getInstanceId()); log.info("名称:" + item.getInstanceName()); log.info("地域ID:" + item.getRegionId()); log.info("状态:" + item.getStatus()); log.info("类型:" + item.getInstanceType()); log.info("CPU核心数:" + item.getCpu()); log.info("内存大小:" + item.getMemory() + "MB"); log.info("磁盘大小:" + item.getLocalStorageCapacity() + "G"); log.info("操作系统:" + item.getOSName()); log.info("网络类型:" + item.getInstanceNetworkType()); log.info("公网出带宽值:" + item.getInternetMaxBandwidthOut() + "Mbit/s"); log.info("公网入带宽值:" + item.getInternetMaxBandwidthIn() + "Mbit/s"); log.info("公网IP:" + UserUtil.removeBrackets(item.getPublicIpAddress().getIpAddress().toString())); log.info("私网IP:" + UserUtil.removeBrackets(item.getVpcAttributes().getPrivateIpAddress().ipAddress.toString())); log.info("专有网络VPCID:" + item.getVpcAttributes().getVpcId()); log.info("安全组ID:" + UserUtil.removeBrackets(item.getSecurityGroupIds().getSecurityGroupId().toString())); log.info("创建时间:" + item.getCreationTime()); log.info("到期时间:" + item.getExpiredTime()); log.info("是否可以回收:" + (item.getRecyclable() ? "是" : "否") + "\n\n"); //存入集合 instancesInformations.add( new InstancesInformation( item.getInstanceId(), item.getInstanceName(), item.getStatus(), UserUtil.removeBrackets(item.getPublicIpAddress().getIpAddress().toString()), UserUtil.removeBrackets(item.getVpcAttributes().getPrivateIpAddress().ipAddress.toString()), item.getCreationTime(), item.getExpiredTime(), item.getRecyclable() ) ); count.getAndIncrement(); } //缓存数据 redisService.deleteObject("服务器信息:"); redisService.setCacheList("服务器信息:", instancesInformations); } catch (TeaException error) { // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。 // 错误 message log.error(error.getMessage()); // 诊断地址 log.error(error.getData().get("Recommend").toString()); com.aliyun.teautil.Common.assertAsString(error.message); } catch (Exception _error) { TeaException error = new TeaException(_error.getMessage(), _error); // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。 // 错误 message log.error(error.getMessage()); // 诊断地址 log.error(error.getData().get("Recommend").toString()); Common.assertAsString(error.message); } return instances; } }