com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSEnumerationAndCreationAdapterService.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSEnumerationAndCreationAdapterService.java

Source

/*
 * Copyright (c) 2015-2016 VMware, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy of
 * the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, without warranties or
 * conditions of any kind, EITHER EXPRESS OR IMPLIED.  See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.vmware.photon.controller.model.adapters.awsadapter.enumeration;

import static com.vmware.photon.controller.model.adapters.awsadapter.AWSConstants.getQueryPageSize;
import static com.vmware.photon.controller.model.adapters.awsadapter.AWSUtils.getAWSNonTerminatedInstancesFilter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentSkipListMap;

import com.amazonaws.handlers.AsyncHandler;
import com.amazonaws.services.ec2.AmazonEC2AsyncClient;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.DescribeInstancesResult;
import com.amazonaws.services.ec2.model.Filter;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.Reservation;

import com.vmware.photon.controller.model.adapterapi.ComputeEnumerateResourceRequest;
import com.vmware.photon.controller.model.adapterapi.EnumerationAction;
import com.vmware.photon.controller.model.adapters.awsadapter.AWSUriPaths;
import com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSComputeDescriptionCreationAdapterService.AWSComputeDescriptionCreationState;
import com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSComputeStateCreationAdapterService.AWSComputeStateForCreation;
import com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSEnumerationAdapterService.AWSEnumerationRequest;
import com.vmware.photon.controller.model.adapters.awsadapter.util.AWSClientManager;
import com.vmware.photon.controller.model.adapters.awsadapter.util.AWSClientManagerFactory;
import com.vmware.photon.controller.model.adapters.util.AdapterUtils;
import com.vmware.photon.controller.model.resources.ComputeDescriptionService.ComputeDescription;
import com.vmware.photon.controller.model.resources.ComputeService;
import com.vmware.photon.controller.model.resources.ComputeService.ComputeState;
import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.OperationContext;
import com.vmware.xenon.common.StatelessService;
import com.vmware.xenon.common.UriUtils;
import com.vmware.xenon.common.Utils;
import com.vmware.xenon.services.common.AuthCredentialsService;
import com.vmware.xenon.services.common.QueryTask;
import com.vmware.xenon.services.common.QueryTask.Query;
import com.vmware.xenon.services.common.QueryTask.Query.Occurance;
import com.vmware.xenon.services.common.QueryTask.QuerySpecification.QueryOption;
import com.vmware.xenon.services.common.ServiceUriPaths;

/**
 * Enumeration Adapter for the Amazon Web Services. Performs a list call to the AWS API
 * and reconciles the local state with the state on the remote system. It lists the instances on the remote system.
 * Compares those with the local system and creates the instances that are missing in the local system.
 *
 */
public class AWSEnumerationAndCreationAdapterService extends StatelessService {
    public static final String SELF_LINK = AWSUriPaths.AWS_ENUMERATION_CREATION_ADAPTER;
    private AWSClientManager clientManager;

    public static enum AWSEnumerationCreationStages {
        CLIENT, ENUMERATE, ERROR
    }

    public static enum AWSEnumerationCreationSubStage {
        QUERY_LOCAL_RESOURCES, COMPARE, CREATE_COMPUTE_DESCRIPTIONS, CREATE_COMPUTE_STATES, GET_NEXT_PAGE, ENUMERATION_STOP
    }

    public AWSEnumerationAndCreationAdapterService() {
        super.toggleOption(ServiceOption.INSTRUMENTATION, true);
        this.clientManager = AWSClientManagerFactory.getClientManager(false);
    }

    /**
     * The enumeration service context that holds all the information needed to determine the list of instances
     * that need to be represented in the system.
     */
    public static class EnumerationCreationContext {
        public AmazonEC2AsyncClient amazonEC2Client;
        public ComputeEnumerateResourceRequest computeEnumerationRequest;
        public AuthCredentialsService.AuthCredentialsServiceState parentAuth;
        public ComputeDescription computeHostDescription;
        public ComputeState hostComputeState;
        public AWSEnumerationCreationStages stage;
        public AWSEnumerationCreationSubStage subStage;
        public Throwable error;
        public int pageNo;
        // Mapping of instance Id and the compute state in the local system.
        public Map<String, ComputeState> localAWSInstanceMap;
        public Map<String, Instance> remoteAWSInstances;
        public List<Instance> instancesToBeCreated;
        // Mappings of the instanceId ,the local compute state and the associated instance on AWS.
        public Map<String, Instance> instancesToBeUpdated;
        public Map<String, ComputeState> computeStatesToBeUpdated;
        // Synchronized map to keep track if an enumeration service has been started in listening
        // mode for a host
        public Map<String, Boolean> enumerationHostMap;
        // The request object that is populated and sent to AWS to get the list of instances.
        public DescribeInstancesRequest describeInstancesRequest;
        // The async handler that works with the response received from AWS
        public AsyncHandler<DescribeInstancesRequest, DescribeInstancesResult> resultHandler;
        // The token to use to retrieve the next page of results from AWS. This value is null when
        // there are no more results to return.
        public String nextToken;
        public Operation awsAdapterOperation;

        public EnumerationCreationContext(AWSEnumerationRequest request, Operation op) {
            this.awsAdapterOperation = op;
            this.computeEnumerationRequest = request.computeEnumerateResourceRequest;
            this.parentAuth = request.parentAuth;
            this.computeHostDescription = request.computeHostDescription;
            this.enumerationHostMap = new ConcurrentSkipListMap<String, Boolean>();
            this.localAWSInstanceMap = new ConcurrentSkipListMap<String, ComputeState>();
            this.instancesToBeUpdated = new ConcurrentSkipListMap<String, Instance>();
            this.computeStatesToBeUpdated = new ConcurrentSkipListMap<String, ComputeState>();
            this.remoteAWSInstances = new ConcurrentSkipListMap<String, Instance>();
            this.instancesToBeCreated = new ArrayList<Instance>();
            this.stage = AWSEnumerationCreationStages.CLIENT;
            this.subStage = AWSEnumerationCreationSubStage.QUERY_LOCAL_RESOURCES;
            this.pageNo = 1;
        }
    }

    @Override
    public void handleStart(Operation startPost) {
        startHelperServices(startPost);
        super.handleStart(startPost);
    }

    @Override
    public void handleStop(Operation op) {
        AWSClientManagerFactory.returnClientManager(this.clientManager, false);
        super.handleStop(op);
    }

    @Override
    public void handlePatch(Operation op) {
        setOperationHandlerInvokeTimeStat(op);
        if (!op.hasBody()) {
            op.fail(new IllegalArgumentException("body is required"));
            return;
        }
        EnumerationCreationContext awsEnumerationContext = new EnumerationCreationContext(
                op.getBody(AWSEnumerationRequest.class), op);
        if (awsEnumerationContext.computeEnumerationRequest.isMockRequest) {
            // patch status to parent task
            AdapterUtils.sendPatchToEnumerationTask(this,
                    awsEnumerationContext.computeEnumerationRequest.taskReference);
            return;
        }
        handleEnumerationRequest(awsEnumerationContext);
    }

    /**
     * Starts the related services for the Enumeration Service
     */
    private void startHelperServices(Operation startPost) {
        Operation postAWScomputeDescriptionService = Operation
                .createPost(
                        UriUtils.buildUri(this.getHost(), AWSComputeDescriptionCreationAdapterService.SELF_LINK))
                .setReferer(this.getUri());

        Operation postAWscomputeStateService = Operation
                .createPost(UriUtils.buildUri(this.getHost(), AWSComputeStateCreationAdapterService.SELF_LINK))
                .setReferer(this.getUri());

        this.getHost().startService(postAWScomputeDescriptionService,
                new AWSComputeDescriptionCreationAdapterService());
        this.getHost().startService(postAWscomputeStateService, new AWSComputeStateCreationAdapterService());

        getHost().registerForServiceAvailability((o, e) -> {
            if (e != null) {
                String message = "Failed to start up all the services related to the AWS Enumeration Creation Adapter Service";
                this.logInfo(message);
                throw new IllegalStateException(message);
            }
            this.logInfo(
                    "Successfully started up all the services related to the AWS Enumeration Creation Adapter Service");
        }, AWSComputeDescriptionCreationAdapterService.SELF_LINK, AWSComputeStateCreationAdapterService.SELF_LINK);
    }

    /**
     * Handles the different steps required to hit the AWS endpoint and get the set of resources available and
     * proceed to update the state in the local system based on the received data.
     *
     */
    private void handleEnumerationRequest(EnumerationCreationContext aws) {
        switch (aws.stage) {
        case CLIENT:
            getAWSAsyncClient(aws, AWSEnumerationCreationStages.ENUMERATE);
            break;
        case ENUMERATE:
            switch (aws.computeEnumerationRequest.enumerationAction) {
            case START:
                if (aws.enumerationHostMap.containsKey(getHostEnumKey(aws.computeHostDescription))) {
                    logInfo("Enumeration for creation already started for %s",
                            aws.computeHostDescription.environmentName);
                } else {
                    aws.enumerationHostMap.put(getHostEnumKey(aws.computeHostDescription), true);
                    logInfo("Started enumeration for creation for %s", aws.computeHostDescription.environmentName);
                }
                aws.computeEnumerationRequest.enumerationAction = EnumerationAction.REFRESH;
                handleEnumerationRequest(aws);
                break;
            case REFRESH:
                if (aws.pageNo == 1) {
                    logInfo("Running enumeration service for creation in refresh mode for %s",
                            aws.computeHostDescription.environmentName);
                }
                logInfo("Processing page %d ", aws.pageNo);
                aws.pageNo++;
                if (aws.describeInstancesRequest == null) {
                    creatAWSRequestAndAsyncHandler(aws);
                }
                aws.amazonEC2Client.describeInstancesAsync(aws.describeInstancesRequest, aws.resultHandler);
                break;
            case STOP:
                if (!aws.enumerationHostMap.containsKey(getHostEnumKey(aws.computeHostDescription))) {
                    logInfo("Enumeration for creation is not running or has already been stopped for %s",
                            aws.computeHostDescription.environmentName);
                } else {
                    aws.enumerationHostMap.remove(getHostEnumKey(aws.computeHostDescription));
                    logInfo("Stopping enumeration service for creation for %s",
                            aws.computeHostDescription.environmentName);
                }
                setOperationDurationStat(aws.awsAdapterOperation);
                aws.awsAdapterOperation.complete();
                break;
            default:
                break;
            }
            break;
        case ERROR:
            AdapterUtils.sendFailurePatchToEnumerationTask(this, aws.computeEnumerationRequest.taskReference,
                    aws.error);
            break;
        default:
            logSevere("Unknown AWS enumeration stage %s ", aws.stage.toString());
            aws.error = new Exception("Unknown AWS enumeration stage %s");
            AdapterUtils.sendFailurePatchToEnumerationTask(this, aws.computeEnumerationRequest.taskReference,
                    aws.error);
            break;
        }
    }

    /**
     * Method to instantiate the AWS Async client for future use
     * @param aws
     */
    private void getAWSAsyncClient(EnumerationCreationContext aws, AWSEnumerationCreationStages next) {
        aws.amazonEC2Client = this.clientManager.getOrCreateEC2Client(aws.parentAuth,
                aws.computeHostDescription.zoneId, this, aws.computeEnumerationRequest.taskReference, true);
        aws.stage = next;
        handleEnumerationRequest(aws);
    }

    /**
     * Method that generates a string key to represent the host for which the enumeration task is being performed.
     * @param computeHost The compute host representing the Amzon EC2 cloud.
     * @return
     */
    private String getHostEnumKey(ComputeDescription computeHost) {
        return ("hostLink:" + computeHost.documentSelfLink + "-enumerationAdapterReference:"
                + computeHost.enumerationAdapterReference);
    }

    /**
     * Initializes and saves a reference to the request object that is sent to AWS to get a page of instances. Also saves an instance
     * to the async handler that will be used to handle the responses received from AWS. It sets the nextToken value in the request
     * object sent to AWS for getting the next page of results from AWS.
     * @param aws
     */
    private void creatAWSRequestAndAsyncHandler(EnumerationCreationContext aws) {
        DescribeInstancesRequest request = new DescribeInstancesRequest();
        Filter runningInstanceFilter = getAWSNonTerminatedInstancesFilter();
        request.getFilters().add(runningInstanceFilter);
        request.setMaxResults(getQueryPageSize());
        request.setNextToken(aws.nextToken);
        aws.describeInstancesRequest = request;
        AsyncHandler<DescribeInstancesRequest, DescribeInstancesResult> resultHandler = new AWSEnumerationAsyncHandler(
                this, aws);
        aws.resultHandler = resultHandler;
    }

    /**
     * The async handler to handle the success and errors received after invoking the describe instances
     * API on AWS
     */
    public static class AWSEnumerationAsyncHandler
            implements AsyncHandler<DescribeInstancesRequest, DescribeInstancesResult> {

        private AWSEnumerationAndCreationAdapterService service;
        private EnumerationCreationContext aws;
        private OperationContext opContext;

        private AWSEnumerationAsyncHandler(AWSEnumerationAndCreationAdapterService service,
                EnumerationCreationContext aws) {
            this.service = service;
            this.aws = aws;
            this.opContext = OperationContext.getOperationContext();
        }

        @Override
        public void onError(Exception exception) {
            OperationContext.restoreOperationContext(this.opContext);
            AdapterUtils.sendFailurePatchToEnumerationTask(this.service,
                    this.aws.computeEnumerationRequest.taskReference, exception);

        }

        @Override
        public void onSuccess(DescribeInstancesRequest request, DescribeInstancesResult result) {
            OperationContext.restoreOperationContext(this.opContext);
            int totalNumberOfInstances = 0;
            // Print the details of the instances discovered on the AWS endpoint
            for (Reservation r : result.getReservations()) {
                for (Instance i : r.getInstances()) {
                    this.service.logInfo("%d=====Instance details %s =====", ++totalNumberOfInstances,
                            i.getInstanceId());
                    this.aws.remoteAWSInstances.put(i.getInstanceId(), i);
                }
            }
            this.service.logInfo("Successfully enumerated %d instances on the AWS host", totalNumberOfInstances);
            // Save the reference to the next token that will be used to retrieve the next page of
            // results from AWS.
            this.aws.nextToken = result.getNextToken();
            // Since there is filtering of resources at source, there can be a case when no
            // resources are returned from AWS.
            if (this.aws.remoteAWSInstances.size() == 0) {
                if (this.aws.nextToken != null) {
                    this.aws.subStage = AWSEnumerationCreationSubStage.GET_NEXT_PAGE;
                } else {
                    this.aws.subStage = AWSEnumerationCreationSubStage.ENUMERATION_STOP;
                }
            }
            handleReceivedEnumerationData();
        }

        /**
         * Uses the received enumeration information and compares it against it the state of the local system and then tries to
         * find and fix the gaps. At a high level this is the sequence of steps that is followed:
         * 1) Create a query to get the list of local compute states
         * 2) Compare the list of local resources against the list received from the AWS endpoint.
         * 3) Create the instances not know to the local system. These are represented using a combination
         * of compute descriptions and compute states.
         * 4) Find and create a representative list of compute descriptions.
         * 5) Create compute states to represent each and every VM that was discovered on the AWS endpoint.
         */
        private void handleReceivedEnumerationData() {
            switch (this.aws.subStage) {
            case QUERY_LOCAL_RESOURCES:
                getLocalResources(AWSEnumerationCreationSubStage.COMPARE);
                break;
            case COMPARE:
                compareLocalStateWithEnumerationData(AWSEnumerationCreationSubStage.CREATE_COMPUTE_DESCRIPTIONS);
                break;
            case CREATE_COMPUTE_DESCRIPTIONS:
                if (this.aws.instancesToBeCreated.size() > 0 || this.aws.instancesToBeUpdated.size() > 0) {
                    createComputeDescriptions(AWSEnumerationCreationSubStage.CREATE_COMPUTE_STATES);
                } else {
                    if (this.aws.nextToken == null) {
                        this.aws.subStage = AWSEnumerationCreationSubStage.ENUMERATION_STOP;
                    } else {
                        this.aws.subStage = AWSEnumerationCreationSubStage.GET_NEXT_PAGE;
                    }
                    handleReceivedEnumerationData();
                }
                break;
            case CREATE_COMPUTE_STATES:
                AWSEnumerationCreationSubStage next;
                if (this.aws.nextToken == null) {
                    next = AWSEnumerationCreationSubStage.ENUMERATION_STOP;
                } else {
                    next = AWSEnumerationCreationSubStage.GET_NEXT_PAGE;
                }
                createComputeStates(next);
                break;
            case GET_NEXT_PAGE:
                getNextPageFromEnumerationAdapter(AWSEnumerationCreationSubStage.QUERY_LOCAL_RESOURCES);
                break;
            case ENUMERATION_STOP:
                signalStopToEnumerationAdapter();
                break;
            default:
                Throwable t = new Exception("Unknown AWS enumeration sub stage");
                signalErrorToEnumerationAdapter(t);
            }
        }

        /**
         * Query the local data store and retrieve all the the compute states that exist filtered by the instanceIds
         * that are received in the enumeration data from AWS.
         */
        public void getLocalResources(AWSEnumerationCreationSubStage next) {
            // query all ComputeState resources for the cluster filtered by the received set of
            // instance Ids
            QueryTask q = new QueryTask();
            q.setDirect(true);
            q.querySpec = new QueryTask.QuerySpecification();
            q.querySpec.options.add(QueryOption.EXPAND_CONTENT);
            q.querySpec.query = Query.Builder.create().addKindFieldClause(ComputeService.ComputeState.class)
                    .addFieldClause(ComputeState.FIELD_NAME_PARENT_LINK,
                            this.aws.computeEnumerationRequest.resourceLink())
                    .addFieldClause(ComputeState.FIELD_NAME_RESOURCE_POOL_LINK,
                            this.aws.computeEnumerationRequest.resourcePoolLink)
                    .build();

            QueryTask.Query instanceIdFilterParentQuery = new QueryTask.Query();
            instanceIdFilterParentQuery.occurance = Occurance.MUST_OCCUR;
            for (String instanceId : this.aws.remoteAWSInstances.keySet()) {
                QueryTask.Query instanceIdFilter = new QueryTask.Query()
                        .setTermPropertyName(ComputeState.FIELD_NAME_ID).setTermMatchValue(instanceId);
                instanceIdFilter.occurance = QueryTask.Query.Occurance.SHOULD_OCCUR;
                instanceIdFilterParentQuery.addBooleanClause(instanceIdFilter);
            }
            q.querySpec.query.addBooleanClause(instanceIdFilterParentQuery);
            q.documentSelfLink = UUID.randomUUID().toString();
            q.tenantLinks = this.aws.computeHostDescription.tenantLinks;
            // create the query to find resources
            this.service.sendRequest(Operation.createPost(this.service, ServiceUriPaths.CORE_QUERY_TASKS).setBody(q)
                    .setConnectionSharing(true).setCompletion((o, e) -> {
                        if (e != null) {
                            this.service.logSevere("Failure retrieving query results: %s", e.toString());
                            signalErrorToEnumerationAdapter(e);
                            return;
                        }
                        QueryTask responseTask = o.getBody(QueryTask.class);
                        for (Object s : responseTask.results.documents.values()) {
                            ComputeState localInstance = Utils.fromJson(s, ComputeService.ComputeState.class);
                            this.aws.localAWSInstanceMap.put(localInstance.id, localInstance);
                        }
                        this.service.logInfo(
                                "Got result of the query to get local resources. There are %d instances known to the system.",
                                responseTask.results.documentCount);
                        this.aws.subStage = next;
                        handleReceivedEnumerationData();
                        return;
                    }));
        }

        /**
         * Compares the local list of VMs against what is received from the AWS endpoint. Saves a list of the VMs that
         * have to be created in the local system to correspond to the remote AWS endpoint.
         */
        private void compareLocalStateWithEnumerationData(AWSEnumerationCreationSubStage next) {
            // No remote instances
            if (this.aws.remoteAWSInstances == null || this.aws.remoteAWSInstances.size() == 0) {
                this.service.logInfo("No resources discovered on the remote system. Nothing to be created locally");
                // no local instances
            } else if (this.aws.localAWSInstanceMap == null || this.aws.localAWSInstanceMap.size() == 0) {
                for (String key : this.aws.remoteAWSInstances.keySet()) {
                    this.aws.instancesToBeCreated.add(this.aws.remoteAWSInstances.get(key));
                }
                // compare and add the ones that do not exist locally for creation. Mark others
                // for updates.
            } else {
                for (String key : this.aws.remoteAWSInstances.keySet()) {
                    if (!this.aws.localAWSInstanceMap.containsKey(key)) {
                        this.aws.instancesToBeCreated.add(this.aws.remoteAWSInstances.get(key));
                        // A map of the local compute state id and the corresponding latest
                        // state on AWS
                    } else {
                        this.aws.instancesToBeUpdated.put(key, this.aws.remoteAWSInstances.get(key));
                        this.aws.computeStatesToBeUpdated.put(key, this.aws.localAWSInstanceMap.get(key));
                    }
                }
            }
            this.aws.subStage = next;
            handleReceivedEnumerationData();
        }

        /**
         * Posts a compute description to the compute description service for creation.
         */
        private void createComputeDescriptions(AWSEnumerationCreationSubStage next) {
            AWSComputeDescriptionCreationState cd = new AWSComputeDescriptionCreationState();
            cd.instancesToBeCreated = this.aws.instancesToBeCreated;
            cd.parentTaskLink = this.aws.computeEnumerationRequest.taskReference;
            cd.authCredentiaslLink = this.aws.parentAuth.documentSelfLink;
            cd.tenantLinks = this.aws.computeHostDescription.tenantLinks;
            cd.regionId = this.aws.computeHostDescription.zoneId;

            this.service.sendRequest(
                    Operation.createPatch(this.service, AWSComputeDescriptionCreationAdapterService.SELF_LINK)
                            .setBody(cd).setCompletion((o, e) -> {
                                if (e != null) {
                                    this.service.logSevere("Failure creating compute descriptions %s",
                                            Utils.toString(e));
                                    signalErrorToEnumerationAdapter(e);
                                    return;
                                } else {
                                    this.service.logInfo(
                                            "Successfully created compute descriptions. Proceeding to next state.");
                                    this.aws.subStage = next;
                                    handleReceivedEnumerationData();
                                    return;
                                }
                            }));
        }

        /**
         * Creates the compute states that represent the instances received from AWS during enumeration.
         * @param next
         */
        private void createComputeStates(AWSEnumerationCreationSubStage next) {
            AWSComputeStateForCreation awsComputeState = new AWSComputeStateForCreation();
            awsComputeState.instancesToBeCreated = this.aws.instancesToBeCreated;
            awsComputeState.instancesToBeUpdated = this.aws.instancesToBeUpdated;
            awsComputeState.computeStatesToBeUpdated = this.aws.computeStatesToBeUpdated;
            awsComputeState.parentComputeLink = this.aws.computeEnumerationRequest.resourceLink();
            awsComputeState.resourcePoolLink = this.aws.computeEnumerationRequest.resourcePoolLink;
            awsComputeState.parentTaskLink = this.aws.computeEnumerationRequest.taskReference;
            awsComputeState.tenantLinks = this.aws.computeHostDescription.tenantLinks;
            awsComputeState.parentAuth = this.aws.parentAuth;
            awsComputeState.regionId = this.aws.computeHostDescription.zoneId;

            this.service.sendRequest(
                    Operation.createPatch(this.service, AWSComputeStateCreationAdapterService.SELF_LINK)
                            .setBody(awsComputeState).setCompletion((o, e) -> {
                                if (e != null) {
                                    this.service.logSevere("Failure creating compute states %s", Utils.toString(e));
                                    signalErrorToEnumerationAdapter(e);
                                    return;
                                } else {
                                    this.service.logInfo(
                                            "Successfully created compute states. Proceeding to next state.");
                                    this.aws.subStage = next;
                                    handleReceivedEnumerationData();
                                    return;
                                }
                            }));
        }

        /**
         * Signals Enumeration Stop to the AWS enumeration adapter. The AWS enumeration adapter will in turn patch the
         * parent task to indicate completion.
         */
        private void signalStopToEnumerationAdapter() {
            this.aws.computeEnumerationRequest.enumerationAction = EnumerationAction.STOP;
            this.service.handleEnumerationRequest(this.aws);
        }

        /**
         * Signals error to the AWS enumeration adapter. The adapter will in turn clean up resources and signal error to the parent task.
         */
        private void signalErrorToEnumerationAdapter(Throwable t) {
            this.aws.error = t;
            this.aws.stage = AWSEnumerationCreationStages.ERROR;
            this.service.handleEnumerationRequest(this.aws);
        }

        /**
         * Calls the AWS enumeration adapter to get the next page from AWSs
         * @param next
         */
        private void getNextPageFromEnumerationAdapter(AWSEnumerationCreationSubStage next) {
            // Reset all the results from the last page that was processed.
            this.aws.remoteAWSInstances.clear();
            this.aws.instancesToBeCreated.clear();
            this.aws.instancesToBeUpdated.clear();
            this.aws.computeStatesToBeUpdated.clear();
            this.aws.localAWSInstanceMap.clear();
            this.aws.describeInstancesRequest.setNextToken(this.aws.nextToken);
            this.aws.subStage = next;
            this.service.handleEnumerationRequest(this.aws);
        }
    }
}