DEVOPS-Aliyun-ECS Update Excute(Java)

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;

}

}