All Products
Search
Document Center

Elastic Compute Service:Execute commands using SDK

Last Updated:Feb 25, 2025

Cloud Assistant facilitates the simultaneous execution of commands across multiple Elastic Compute Service (ECS) instances. This topic details how to use the ECS SDK to run Cloud Assistant commands and verify their outcomes.

Prerequisites

  • Your ECS instance must be in the Running state (Running) and must have the Cloud Assistant Agent installed. If the agent is not installed, please see Install Cloud Assistant Agent.

  • Ensure the runtime environment is set with the environment variables ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET. For configuration details, see Configure environment variables in Linux, macOS, and Windows.

    Note

    To avoid security risks from primary account AccessKey exposure, it's advisable to create a Resource Access Management (RAM) user and grant it the necessary ECS access privileges. Use the RAM user's AccessKey for SDK operations. For more information, see RAM users.

  • Grant the RAM user the required permissions for Cloud Assistant. For more information, see Authorize RAM users to use Cloud Assistant.

  • Prepare the Shell, Bat, or PowerShell commands for execution by Cloud Assistant.

  • Incorporate the ECS SDK into your project. For more information, see and ECS_SDK.

Example scenario

In today's cloud computing environments, operations and maintenance (O&M) management systems are essential for business continuity. Regular monitoring of resource usage, including CPU, memory, and disk space, is vital for the performance and stability of ECS instances. Imagine developing an automated O&M system using Cloud Assistant's password-free feature, which allows remote execution of user-defined commands on ECS instances to meet various O&M requirements. This system can perform tasks such as resource monitoring, log collection, and troubleshooting, thereby enhancing O&M efficiency and supporting effective business operations.

import com.aliyun.ecs20140526.Client;
import com.aliyun.ecs20140526.models.*;
import com.aliyun.teaopenapi.models.Config;
import com.google.gson.Gson;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;


public class CloudAssistantService {

    /**
     * Retrieve AccessKeyId and AccessKeySecret from environment variables
     */
    private static final String ACCESS_KEY_ID = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
    private static final String ACCESS_KEY_SECRET = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
    private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);
    private static volatile Client ecsClient;

    private CloudAssistantService() {
    }

    /**
     * Initialize ECS client
     *
     * @param regionId Region ID, used to specify the region of the ECS client
     * @return Returns the initialized ECS client instance
     * 
     * This method uses a double-checked locking pattern to ensure thread-safe creation of a singleton ECS client.
     * It first checks if the client already exists, and if not, checks again within a synchronized block and creates a new client instance.
     */
    public static Client getEcsClient(String regionId) throws Exception {
        if (ecsClient == null) {
            synchronized (CloudAssistantService.class) {
                if (ecsClient == null) {
                    Config config = new Config().setAccessKeyId(ACCESS_KEY_ID).setAccessKeySecret(ACCESS_KEY_SECRET).setRegionId(regionId);
                    ecsClient = new Client(config);
                }
            }
        }
        return ecsClient;
    }

    public static void main(String[] args_) {
        try {
            // Region ID
            String regionId = "cn-chengdu";
            getEcsClient(regionId);
            // ECS instance IDs for command execution
            List<String> instanceIds = Arrays.asList("i-2vcXXXXXXXXXXXXXXXb8", "i-2vcXXXXXXXXXXXXXXXot");
            // Command content
            String commandContent = "#!/bin/bash\n cat /proc/meminfo";
            // Command execution timeout
            long commandTimeOut = 60;


            // Execute command
            String invokeId = runCommand(commandContent, regionId, instanceIds, commandTimeOut);
            // Query command execution result
            DescribeInvocationsResponse invocationResult = describeInvocations(regionId, invokeId, commandTimeOut);
            System.out.println("The command execution result:" + new Gson().toJson(invocationResult));
            // Omit log file recording operation

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            SCHEDULER.shutdown();
        }
    }

    /**
     * Execute command on specified instances
     *
     * @param commandContent The content of the command to be executed
     * @param regionId       The region ID where the instance is located
     * @param instanceIds    The collection of ECS instance IDs to execute the command
     * @param commandTimeOut The timeout for command execution
     * @return Returns the invoke ID of the command execution
     */
    public static String runCommand(String commandContent, String regionId, List<String> instanceIds, long commandTimeOut) {
        try {
            System.out.println("runCommand start...");
            RunCommandRequest request = new RunCommandRequest();
            request.setRegionId(regionId);
            request.setType(Constants.COMMAND_TYPE.RUN_SHELL_SCRIPT);
            request.setCommandContent(commandContent);
            request.setInstanceId(instanceIds);
            request.setTimeout(commandTimeOut);
            RunCommandResponse runCommandResponse = ecsClient.runCommand(request);
            return runCommandResponse.body.invokeId;
        } catch (Exception e) {
            throw new RuntimeException("runCommand failed", e);
        }
    }

    /**
     * Query Cloud Assistant command execution result
     *
     * @param regionId       The region ID used to specify the area to which the invocation result belongs
     * @param invokeId       The invoke ID used to uniquely identify an invocation
     * @param commandTimeOut The timeout for command execution
     */
    public static DescribeInvocationsResponse describeInvocations(String regionId, String invokeId, long commandTimeOut) {
        DescribeInvocationsRequest describeInvocationsRequest = new DescribeInvocationsRequest()
                .setRegionId(regionId)
                .setInvokeId(invokeId);

        long delay = 2;
        // Set maximum retry count
        int maxRetries = (int) (commandTimeOut / delay); 
        int retryCount = 0;

        try {
            while (retryCount < maxRetries) {
                ScheduledFuture<DescribeInvocationsResponse> future = SCHEDULER.schedule(() ->
                        ecsClient.describeInvocations(describeInvocationsRequest), delay, TimeUnit.SECONDS);
                DescribeInvocationsResponse results = future.get();
                List<DescribeInvocationsResponseBody.DescribeInvocationsResponseBodyInvocationsInvocation> invocationList = results.body.invocations.invocation;
                if (invocationList.isEmpty()) {
                    throw new RuntimeException("The command execution result was not found.");
                }
                DescribeInvocationsResponseBody.DescribeInvocationsResponseBodyInvocationsInvocation invocationResult = results.body.invocations.invocation.get(0);
                String invocationStatus = invocationResult.invocationStatus;
                switch (invocationStatus) {
                    case Constants.INVOCATION_STATUS.PENDING:
                    case Constants.INVOCATION_STATUS.RUNNING:
                    case Constants.INVOCATION_STATUS.STOPPING:
                        retryCount++;
                        continue;
                    default:
                        return results;
                }
            }
            throw new RuntimeException("Max retries exceeded for command execution result.");
        } catch (Exception e) {
            throw new RuntimeException("describeInvocationResults failed", e);
        }
    }


    public static class Constants {
        // Command types
        public static final class COMMAND_TYPE {
            // Shell command, applicable to Linux instances.
            public static final String RUN_SHELL_SCRIPT = "RunShellScript";
            // Batch command, applicable to Windows instances.
            public static final String RUN_BAT_SCRIPT = "RunBatScript";
            // PowerShell command, applicable to Windows instances.
            public static final String RUN_POWERSHELL_SCRIPT = "RunPowerShellScript";
        }

        // Cloud Assistant command execution results
        public static final class INVOCATION_STATUS {
            // The system is verifying or sending the command.
            public static final String PENDING = "Pending";
            // The command is being executed on the instance.
            public static final String RUNNING = "Running";
            // The command is being stopped.
            public static final String STOPPING = "Stopping";
        }
    }
}
import os
import time
import logging
from alibabacloud_ecs20140526 import models as ecs_20140526_models
from alibabacloud_ecs20140526.client import Client as Ecs20140526Client
from alibabacloud_tea_openapi import models as open_api_models

# Configure logs
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

ACCESS_KEY_ID = os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
ACCESS_KEY_SECRET = os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")

if not ACCESS_KEY_ID or not ACCESS_KEY_SECRET:
    raise EnvironmentError(
        "Missing required environment variables: ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET")


def get_ecs_client(region_id):
    config = open_api_models.Config(
        access_key_id=ACCESS_KEY_ID,
        access_key_secret=ACCESS_KEY_SECRET,
        region_id=region_id
    )
    return Ecs20140526Client(config)


def execute_command(client, command_content, region_id, instance_ids, command_timeout, command_type):
    if not instance_ids:
        raise ValueError("Instance IDs list cannot be empty.")

    valid_command_types = ["RunShellScript", "RunBatScript", "RunPowerShellScript"]
    if command_type not in valid_command_types:
        raise ValueError(f"Invalid command type: {command_type}. Valid types are {valid_command_types}.")

    request = ecs_20140526_models.RunCommandRequest()
    request.region_id = region_id
    request.type = command_type
    request.command_content = command_content
    request.instance_ids = instance_ids
    request.timeout = command_timeout

    try:
        run_command_response = client.run_command(request)
        return run_command_response.to_map()['body']['InvokeId']
    except Exception as e:
        logging.error(f"Failed to execute command: {e}")
        raise


def query_invocations(client, region_id, invoke_id):
    request = ecs_20140526_models.DescribeInvocationsRequest()
    request.region_id = region_id
    request.invoke_ids = [invoke_id]

    try:
        describe_invocations_response = client.describe_invocations(request)
        return describe_invocations_response.to_map()['body']
    except Exception as e:
        logging.error(f"Failed to query invocations: {e}")
        raise


def wait_for_command_completion(client, region_id, invoke_id, max_retries, backoff_factor=2):
    retry_count = 0
    while retry_count < max_retries:
        time.sleep(backoff_factor ** retry_count)
        results = query_invocations(client, region_id, invoke_id)
        invocation_list = results.get('Invocations', {}).get('Invocation', [])
        if not invocation_list:
            raise RuntimeError("The command execution result was not found.")

        invocation_result = invocation_list[0]
        invocation_status = invocation_result.get('InvocationStatus')
        logging.info(f"Current invocation status: {invocation_status}")

        if invocation_status == "Finished":
            print("query_invocations result:", results)
            break
        elif invocation_status in ["Failed", "Stopped"]:
            raise RuntimeError(f"Command execution failed with status: {invocation_status}")
        else:
            retry_count += 1
    else:
        raise TimeoutError("Command execution timed out.")


def main():
    # Region ID
    region_id = "cn-chengdu"
    # ECS instance IDs for command execution
    instance_ids = ["i-2vcXXXXXXXXXXXXXXXb8", "i-2vcXXXXXXXXXXXXXXXot"]
    # Command content
    command_content = "#!/bin/bash\n cat /proc/meminfo"
    # Timeout
    command_timeout = 60
    # Command type, supports RunShellScript, RunBatScript, RunPowerShellScript
    command_type = "RunShellScript"

    client = get_ecs_client(region_id)
    invoke_id = execute_command(client, command_content, region_id, instance_ids, command_timeout, command_type)

    max_retries = max(int(command_timeout // 2), 1)
    wait_for_command_completion(client, region_id, invoke_id, max_retries)


if __name__ == "__main__":
    main()