import com.alibaba.fastjson.JSON;
import com.aliyuncs.AcsRequest;
import com.aliyuncs.AcsResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.ecs.model.v20140526.*;
import com.aliyuncs.ecs.model.v20140526.DescribeInstancesResponse.Instance;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import java.util.*;
import java.util.concurrent.*;
public class AliyunModifyPrepayInstanceSpecExample {
private String accessKeyId = “<AccessKey>”;
private String accessSecret = “<AccessSecret>”;
private IAcsClient client = null;
/**
* 实例所属的地域ID
*/
private String regionId = “cn-shenzhen”;
/**
* 实例ID
*/
private String[] instanceIds = new String[] {“i-wz97odvmiskb2a79cw99”};
/**
* 需要变配的目标实例规格
*/
private String instanceType = “ecs.g5.2xlarge”;
/**
* 变配操作类型:升配/降配 — \”upgrade\”/\”downgrade\”,默认为\”upgrade\”
*/
private String operatorType = “upgrade”;
/**
* 是否自动支付,如果设为false,会生成正常的未支付订单,可以登录ECS管理控制台支付
*/
private Boolean autoPay = true;
/**
* 创建一个Map,来记录实例升/降配前后的实例规格
*/
private Map<String, InstanceTypeModel> instanceTypeModelMap = new HashMap<String, InstanceTypeModel>();
private static final ExecutorService MODIFY_PREPAY_INSTANCE_SPEC_POOL = Executors.newFixedThreadPool(500);
private static final int AWAIT_SECOND = 6;
private static final Integer OFFSET = 0;
private static final int PAGE_SIZE = 100;
private static final int INSTANCE_STATUS_CHECK_INTERVAL_MILLISECOND = 3000;
private static final int THREAD_EXECUTE_INTERVAL_MILLISECOND = 2000;
private static final long INSTANCE_STATUS_TOTAL_CHECK_TIME_ELAPSE_MILLISECOND = 60000 * 3;
private static final String INSTANCE_STATUS_RUNNING = “Running”;
private static final String INSTANCE_STATUS_STOPPED = “Stopped”;
private static final String SUCCESS = “success”;
private static final String FAIL = “fail”;
public static void main(String[] args) {
try {
AliyunModifyPrepayInstanceSpecExample modifyPrepayInstanceSpec = new AliyunModifyPrepayInstanceSpecExample();
modifyPrepayInstanceSpec.initClient();
modifyPrepayInstanceSpec.callToModifyPrepayInstanceSpec();
}catch (Exception e) {
e.printStackTrace();
}finally {
try {
MODIFY_PREPAY_INSTANCE_SPEC_POOL.awaitTermination(AWAIT_SECOND, TimeUnit.SECONDS);
MODIFY_PREPAY_INSTANCE_SPEC_POOL.shutdown();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void callToModifyPrepayInstanceSpec() throws InterruptedException {
List<String> instanceIdList = Arrays.asList(instanceIds);
/**
* 1. 查询实例的有效性
*/
List<Instance> instances = describeInstances(instanceIdList);
if (instances.size() == 0) {// 查询结果是空的,则立即return
logInfo(String.format(“Fail. Can not find instance. InstanceIds: %s”, JSON.toJSONString(instanceIds)));
return;
}
/**
* 2. 检查实例的状态,如果是Running状态则要执行停机操作,如果是Starting或者Stopping状态就把这些实例去掉
*/
ArrayList<String> allResponseList = new ArrayList<String>(); // 所有的用DescribeInstances查询出来的,有效的实例ID列表
ArrayList<String> invalidInstancesList = new ArrayList<String>(); // 传入的ID无效的列表
ArrayList<String> invalidStatusIdList = new ArrayList<String>(); // 状态不符合条件的实例ID列表
ArrayList<Instance> readyForModifyList = new ArrayList<Instance>(); // 最终可以做变配操作的实例列表
ArrayList<Instance> needToStopList = new ArrayList<Instance>(); // 需要先做停机才能执行变配的实例列表
/**
* 2.1. 遍历返回的实例列表,对实例进行归类
*/
for (final Instance instance : instances) {
String instanceId = instance.getInstanceId();
String status = instance.getStatus();
allResponseList.add(instanceId);
if (status.equals(INSTANCE_STATUS_STOPPED)) {
readyForModifyList.add(instance); // 实例status为Stopped,放入待变配列表
}else if (status.equals(INSTANCE_STATUS_RUNNING)) {
needToStopList.add(instance); // 实例status为Running,放入待停止列表
}else {
invalidStatusIdList.add(instanceId); // 其它status,放入无效状态列表
}
}
/**
* 2.2. 获得无效的实例ID列表
*/
for (String instanceId : instanceIds) {
if (!allResponseList.contains(instanceId)) {
invalidInstancesList.add(instanceId);
}
}
/**
* 2.3. 输出无效实例ID列表
*/
if (invalidInstancesList.size() > 0) {
logInfo(String.format(“Invalid InstanceId(s): %s”, JSON.toJSONString(invalidInstancesList)));
}
/**
* 2.4. 输出有无效实例状态的实例ID列表
*/
if (invalidStatusIdList.size() > 0) {
logInfo(String.format(“InstanceId(s) which has invalid status : %s”, JSON.toJSONString(invalidStatusIdList)));
}
/**
* 3. 停止实例
*/
if (needToStopList.size() > 0) {
/**
* 3.1. 进行停机操作
*/
stopInstances(convertToIdList(needToStopList));
}
/**
* 3.2. 检查实例是否成功停止
*/
Map<String, List<Instance>> stoppedInstanceMap = checkInstanceStatus(needToStopList, INSTANCE_STATUS_STOPPED);
List<Instance> failToStopList = stoppedInstanceMap.get(FAIL);
List<Instance> successToStopList = stoppedInstanceMap.get(SUCCESS);
/**
* 3.3. 将未成功停止的实例ID打印出来
*/
if (failToStopList.size() > 0) {
logInfo(String.format(“InstanceId(s) which failed to stop : %s”, JSON.toJSONString(convertToIdList(failToStopList))));
}
/**
* 3.4. 将成功停止的实例和原本就停止的实例做并集,为最终的可以变配的实例列表
*/
readyForModifyList.addAll(successToStopList);
/**
* 4. 进行变配操作
*/
Map<String, Future<BaseResponse>> modifyResultMap = new HashMap<String, Future<BaseResponse>>();
if (readyForModifyList.size() == 0) {
logInfo(“No instance could be modified, please try other instances”);
return;
}else {
/**
* 4.1. 更新map,记录变配之前的实例规格
*/
updateInstanceOriginalType(readyForModifyList);
/**
* 4.2. 调用Open API来执行变配操作
*/
modifyResultMap = modifyPrepayInstanceSpecs(convertToIdList(readyForModifyList));
}
/**
* 5. 启动实例
*/
/**
* 5.1. 执行启动操作
*/
List<String> needToVerifyIds = new ArrayList<String>();
for (String id : modifyResultMap.keySet()) {
try {
BaseResponse baseResponse = modifyResultMap.get(id).get();
if (baseResponse.isSuccessful) {
needToVerifyIds.add(id);
}
}catch (InterruptedException e) {
e.printStackTrace();
}catch (ExecutionException e) {
e.printStackTrace();
}
}
if (needToVerifyIds.size() > 0) {
startInstances(needToVerifyIds);
}else {
logInfo(“Failed to modify all of the input instance(s)!”);
return;
}
/**
* 5.2. 检查实例是否成功启动
*/
Map<String, List<Instance>> startedInstancesMap = checkInstanceStatus(readyForModifyList, INSTANCE_STATUS_RUNNING);
List<Instance> failToStartList = startedInstancesMap.get(FAIL);
List<Instance> successToStartList = startedInstancesMap.get(SUCCESS);
/**
* 5.3. 如果实例没有启动成功,视为变配失败
*/
if (failToStartList.size() > 0) {
logInfo(String.format(“InstanceId(s) which failed to start : %s”, JSON.toJSONString(convertToIdList(failToStartList))));
}
if (successToStartList.size() > 0) {
for (Instance instance : successToStartList) {
String originalType = instanceTypeModelMap.get(instance.getInstanceId()).originalInstanceType;
if (instance.getInstanceType().equals(instanceType) && !originalType.equals(instanceType)) {
logInfo(String.format(“Instance %s has been modified to the specified type %s”, instance.getInstanceId(), instance.getInstanceType()));
}else {
logInfo(String.format(“Instance %s failed to be modified to the specified type %s”, instance.getInstanceId(), instanceType));
}
}
}else {
logInfo(“Failed to modify all of the input instance(s)!”);
}
}
/**
* 更改实例规格,调用了ModifyPrepayInstanceSpec
*
* @param instanceIds
*/
private Map<String, Future<BaseResponse>> modifyPrepayInstanceSpecs(List<String> instanceIds) {
Map<String, AcsRequest> modifyRequestMap = new HashMap<String, AcsRequest>();
for (final String instanceId : instanceIds) {
ModifyPrepayInstanceSpecRequest modifyPrepayInstanceSpecRequest = new ModifyPrepayInstanceSpecRequest();
modifyPrepayInstanceSpecRequest.setInstanceId(instanceId);
modifyPrepayInstanceSpecRequest.setAutoPay(autoPay);
modifyPrepayInstanceSpecRequest.setRegionId(regionId);
modifyPrepayInstanceSpecRequest.setInstanceType(instanceType);
modifyPrepayInstanceSpecRequest.setOperatorType(operatorType);
modifyRequestMap.put(instanceId, modifyPrepayInstanceSpecRequest);
}
return concurrentExecuteAPI(modifyRequestMap);
}
/**
* 启动实例,调用了StartInstance
*
* @param instanceIds
*/
private void startInstances(List<String> instanceIds) {
Map<String, AcsRequest> startRequestMap = new HashMap<String, AcsRequest>();
for (final String instanceId : instanceIds) {
StartInstanceRequest request = new StartInstanceRequest();
request.setInstanceId(instanceId);
startRequestMap.put(instanceId, request);
}
concurrentExecuteAPI(startRequestMap);
}
/**
* 停止实例,调用了StopInstance
*
* @param instanceIds
*/
private void stopInstances(List<String> instanceIds) {
Map<String, AcsRequest> stopRequestMap = new HashMap<String, AcsRequest>();
for (final String instanceId : instanceIds) {
StopInstanceRequest request = new StopInstanceRequest();
request.setInstanceId(instanceId);
stopRequestMap.put(instanceId, request);
}
concurrentExecuteAPI(stopRequestMap);
}
/**
* 查询实例详情,调用了DescribeInstances Open API
*
* @param instanceIds 需要检查的实例
* @return
*/
private List<Instance> describeInstances(List<String> instanceIds) {
int offset = OFFSET;
int pageSize = PAGE_SIZE;
int totalCount = instanceIds.size();
List<Instance> instances = new ArrayList<Instance>();
/**
* 由于DescribeInstances这个接口每次最多可返回100条实例,所以如果需要升配的实例数量大于100,则需要发送多次请求
*/
while (offset < totalCount) {
DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest();
describeInstancesRequest.setRegionId(regionId);
describeInstancesRequest.setPageSize(pageSize);
describeInstancesRequest.setInstanceIds(JSON.toJSONString(instanceIds.subList(offset,
Math.min(totalCount, offset + pageSize))));
BaseResponse response = callOpenAPI(describeInstancesRequest);
if (response.isSuccessful) {
if (response.data != null) {
DescribeInstancesResponse describeInstancesResponse = (DescribeInstancesResponse) response.data;
if (describeInstancesResponse.getInstances().size() == 0) {
logInfo(String.format(“Fail. Can not find instance. InstanceIds: %s”, JSON.toJSONString(instanceIds)));
return instances;
}else {
instances.addAll(describeInstancesResponse.getInstances());
offset += describeInstancesResponse.getPageSize();
}
}
}else {
return instances;
}
}
return instances;
}
private static void sleepSomeTime(int sleepTime) {
try {
Thread.sleep(sleepTime);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 每3秒中检查一次实例的状态,超时时间设为3分钟。
* @param instances 需要检查的实例
* @param status 预期的实例状态
* @return
*/
private Map<String, List<Instance>> checkInstanceStatus(List<Instance> instances, String status) {
Long startTime = System.currentTimeMillis();
List<Instance> operatedInstances = new ArrayList<Instance>();
for (; ; ) {
sleepSomeTime(INSTANCE_STATUS_CHECK_INTERVAL_MILLISECOND);
List<Instance> responseInstances = describeInstances(convertToIdList(instances));
Long timeStamp = System.currentTimeMillis();
for (Instance responseInstance : responseInstances) {
if (status.equals(responseInstance.getStatus())) {
String instanceId = responseInstance.getInstanceId();
Iterator<Instance> iterator = instances.iterator();
while (iterator.hasNext()) {
Instance instance = iterator.next();
if (instance.getInstanceId().equals(instanceId)) {
iterator.remove();
}
}
operatedInstances.add(responseInstance);
}
}
if (instances.size() == 0) {
break;
}
if (timeStamp – startTime > INSTANCE_STATUS_TOTAL_CHECK_TIME_ELAPSE_MILLISECOND) {
break;
}
}
Map<String, List<Instance>> map = new HashMap<String, List<Instance>>();
map.put(FAIL, instances);
map.put(SUCCESS, operatedInstances);
return map;
}
/**
* 记录 instance 原始的实例规格,以便在调用变配OpenAPI后检查是否结果是否符合预期
*
*/
private boolean updateInstanceOriginalType(List<Instance> instances) {
for (Instance instance : instances) {
InstanceTypeModel instanceTypeModel = new InstanceTypeModel();
instanceTypeModel.instanceId = instance.getInstanceId();
instanceTypeModel.originalInstanceType = instance.getInstanceType();
instanceTypeModelMap.put(instance.getInstanceId(), instanceTypeModel);
}
return true;
}
private List<String> convertToIdList(List<Instance> instances) {
List<String> list = new ArrayList<String>();
if (instances != null && instances.size() > 0) {
for (Instance instance : instances) {
list.add(instance.getInstanceId());
}
}
return list;
}
private Map<String, Future<BaseResponse>> concurrentExecuteAPI(Map<String, AcsRequest> map) {
Map<String, Future<BaseResponse>> futureMap = new HashMap<String, Future<BaseResponse>>();
for (String id : map.keySet()) {
final AcsRequest request = map.get(id);
Future<BaseResponse> future = MODIFY_PREPAY_INSTANCE_SPEC_POOL.submit(new Callable<BaseResponse>() {
public BaseResponse call() throws Exception {
return callOpenAPI(request);
}
});
futureMap.put(id, future);
}
try {
MODIFY_PREPAY_INSTANCE_SPEC_POOL.awaitTermination(AWAIT_SECOND, TimeUnit.SECONDS);
}catch (InterruptedException e) {
e.printStackTrace();
}
checkConcurrentResult(futureMap);
return futureMap;
}
private Runnable checkConcurrentResult(final Map<String, Future<BaseResponse>> map) {
return new Runnable() {
public void run() {
for (Future<BaseResponse> future : map.values()) {
try {
while (true) {
if (future.isDone() && !future.isCancelled()) {
break;
}else {
sleepSomeTime(THREAD_EXECUTE_INTERVAL_MILLISECOND);
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
};
}
/**
* 调用OpenAPI的方法,这里进行了错误处理
*
* @param request AcsRequest, Open API的请求
* @return BaseResponse
*/
private BaseResponse callOpenAPI(AcsRequest request) {
BaseResponse response = new BaseResponse();
try {
AcsResponse responseData = client.getAcsResponse(request, false, 0);
logInfo(String.format(“Success. OpenAPI Action: %s call successfully.”, request.getActionName()));
setSuccessfulResponse(responseData, response);
}catch (ServerException e) {
logInfo(String.format(“Fail. Something with your connection with Aliyun go incorrect. Error Code: %s, Error message: %s”,
e.getErrCode(), e.getErrMsg()));
setFailedResponse(e, response);
}catch (ClientException e) {
logInfo(String.format(“Fail. Business error. Error Code: %s, Error Message: %s, RequestId: %s”,
e.getErrCode(), e.getErrMsg(), e.getRequestId()));
setFailedResponse(e, response);
}
return response;
}
private void setFailedResponse(ClientException e, BaseResponse response) {
response.code = e.getErrCode();
response.message = e.getMessage();
response.requestId = e.getRequestId();
response.isSuccessful = false;
}
private void setSuccessfulResponse(AcsResponse acsResponse, BaseResponse response) {
response.data = acsResponse;
response.isSuccessful = true;
}
/**
* 需要填充账号的AccessKey ID,以及账号的Access Key Secret
*/
private void initClient() {
IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessSecret);
client = new DefaultAcsClient(profile);
}
private static void logInfo(String message) {
System.out.println(message);
}
class InstanceTypeModel {
public String instanceId;
public String originalInstanceType;
}
class BaseResponse {
public Boolean isSuccessful;
public AcsResponse data;
public String code;
public String requestId;
public String message;
}
}