
com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSEnumerationAndCreationAdapterService Maven / Gradle / Ivy
The newest version!
/*
* 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.AWSConstants.getQueryResultLimit;
import static com.vmware.photon.controller.model.adapters.awsadapter.AWSUtils.getAWSNonTerminatedInstancesFilter;
import static com.vmware.photon.controller.model.constants.PhotonModelConstants.SOURCE_TASK_LINK;
import static com.vmware.photon.controller.model.util.StartServicesHelper.ServiceMetadata.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.amazonaws.handlers.AsyncHandler;
import com.amazonaws.services.ec2.AmazonEC2AsyncClient;
import com.amazonaws.services.ec2.model.AvailabilityZone;
import com.amazonaws.services.ec2.model.DescribeAvailabilityZonesRequest;
import com.amazonaws.services.ec2.model.DescribeAvailabilityZonesResult;
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 org.apache.commons.lang3.StringUtils;
import com.vmware.photon.controller.model.adapterapi.EnumerationAction;
import com.vmware.photon.controller.model.adapters.awsadapter.AWSConstants;
import com.vmware.photon.controller.model.adapters.awsadapter.AWSUriPaths;
import com.vmware.photon.controller.model.adapters.awsadapter.AWSUtils;
import com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSComputeDescriptionEnumerationAdapterService.AWSComputeDescriptionCreationState;
import com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSComputeStateCreationAdapterService.AWSComputeStateCreationRequest;
import com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSLoadBalancerEnumerationAdapterService.AWSLoadBalancerEnumerationRequest;
import com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSNetworkStateEnumerationAdapterService.AWSNetworkEnumerationRequest;
import com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSNetworkStateEnumerationAdapterService.AWSNetworkEnumerationResponse;
import com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSSecurityGroupEnumerationAdapterService.AWSSecurityGroupEnumerationResponse;
import com.vmware.photon.controller.model.adapters.awsadapter.util.AWSAsyncHandler;
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.awsadapter.util.AWSEnumerationUtils.ZoneData;
import com.vmware.photon.controller.model.adapters.util.AdapterUtils;
import com.vmware.photon.controller.model.adapters.util.ComputeEnumerateAdapterRequest;
import com.vmware.photon.controller.model.query.QueryUtils;
import com.vmware.photon.controller.model.resources.ComputeDescriptionService;
import com.vmware.photon.controller.model.resources.ComputeDescriptionService.ComputeDescription;
import com.vmware.photon.controller.model.resources.ComputeDescriptionService.ComputeDescription.ComputeType;
import com.vmware.photon.controller.model.resources.ComputeService;
import com.vmware.photon.controller.model.resources.ComputeService.ComputeState;
import com.vmware.photon.controller.model.resources.ComputeService.ComputeStateWithDescription;
import com.vmware.photon.controller.model.resources.ComputeService.PowerState;
import com.vmware.photon.controller.model.resources.NetworkInterfaceService.NetworkInterfaceState;
import com.vmware.photon.controller.model.resources.ResourceState;
import com.vmware.photon.controller.model.tasks.ResourceEnumerationTaskService;
import com.vmware.photon.controller.model.util.StartServicesHelper;
import com.vmware.photon.controller.model.util.StartServicesHelper.ServiceMetadata;
import com.vmware.xenon.common.DeferredResult;
import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.OperationContext;
import com.vmware.xenon.common.OperationJoin;
import com.vmware.xenon.common.OperationSequence;
import com.vmware.xenon.common.ServiceStateCollectionUpdateRequest;
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.QuerySpecification.QueryOption;
/**
* 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;
/**
* 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 ComputeEnumerateAdapterRequest request;
public AuthCredentialsService.AuthCredentialsServiceState endpointAuth;
public ComputeStateWithDescription parentCompute;
public AWSEnumerationCreationStages stage;
public AWSEnumerationRefreshSubStage refreshSubStage;
public AWSComputeEnumerationCreationSubStage subStage;
public Throwable error;
public int pageNo;
// Mapping of instance Id and the compute state in the local system.
public Map localAWSInstanceMap;
public Map remoteAWSInstances;
public List instancesToBeCreated;
// Mappings of the instanceId ,the local compute state and the associated instance on AWS.
public Map instancesToBeUpdated;
public Map> nicStatesToBeUpdated;
// Mapping of compute state and its NICs to be deleted.
public Map> nicStatesToBeDeleted;
public Map computeStatesToBeUpdated;
// 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 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 operation;
/**
* Discovered/Enumerated networks in Amazon.
*/
public AWSNetworkEnumerationResponse enumeratedNetworks;
/**
* Discovered/Enumerated security groups in Amazon
*/
public AWSSecurityGroupEnumerationResponse enumeratedSecurityGroups;
public Map zones;
public EnumerationCreationContext(ComputeEnumerateAdapterRequest request,
Operation op) {
this.operation = op;
this.request = request;
this.endpointAuth = request.endpointAuth;
this.parentCompute = request.parentCompute;
this.localAWSInstanceMap = new ConcurrentSkipListMap<>();
this.instancesToBeUpdated = new ConcurrentSkipListMap<>();
this.nicStatesToBeUpdated = new ConcurrentSkipListMap<>();
this.nicStatesToBeDeleted = new ConcurrentSkipListMap<>();
this.computeStatesToBeUpdated = new ConcurrentSkipListMap<>();
this.remoteAWSInstances = new ConcurrentSkipListMap<>();
this.instancesToBeCreated = new ArrayList<>();
this.zones = new HashMap<>();
this.stage = AWSEnumerationCreationStages.CLIENT;
this.refreshSubStage = AWSEnumerationRefreshSubStage.ZONES;
this.subStage = AWSComputeEnumerationCreationSubStage.QUERY_LOCAL_RESOURCES;
this.pageNo = 1;
}
}
/**
* The async handler to handle the success and errors received after invoking the describe
* AvailabilityZones API on AWS
*/
private static class AWSAvailabilityZoneAsyncHandler extends
AWSAsyncHandler {
private AWSEnumerationAndCreationAdapterService service;
private EnumerationCreationContext context;
private AWSEnumerationRefreshSubStage next;
private AWSAvailabilityZoneAsyncHandler(AWSEnumerationAndCreationAdapterService service,
EnumerationCreationContext context, AWSEnumerationRefreshSubStage next) {
super();
this.service = service;
this.context = context;
this.next = next;
}
@Override
public void handleSuccess(DescribeAvailabilityZonesRequest request,
DescribeAvailabilityZonesResult result) {
List zones = result.getAvailabilityZones();
if (zones == null || zones.isEmpty()) {
this.service
.logFine(() -> "No AvailabilityZones found. Nothing to be created locally");
this.context.refreshSubStage = this.next;
this.service.processRefreshSubStages(this.context);
return;
}
loadLocalResources(this.service, this.context,
zones.stream()
.map(AvailabilityZone::getZoneName)
.collect(Collectors.toList()),
cm -> createMissingLocalInstances(zones, cm),
cm -> {
this.service.logFine(() -> "No AvailabilityZones found. Nothing to be"
+ " created locally");
this.context.refreshSubStage = this.next;
this.service.processRefreshSubStages(this.context);
});
}
private void proceedWithRefresh() {
this.context.refreshSubStage = this.next;
this.service.processRefreshSubStages(this.context);
}
private void createMissingLocalInstances(List zones,
Map cm) {
// For existing compute & description, update the endpoint links
List descUpdateOps = new ArrayList<>();
zones.stream()
.filter(z -> cm.containsKey(z.getZoneName()))
.forEach(z -> {
ComputeState c = cm.get(z.getZoneName());
if (StringUtils.isEmpty(c.endpointLink)) {
c.endpointLink = this.context.request.original.endpointLink;
}
this.context.zones.put(c.id,
ZoneData.build(this.context.request.regionId,
c.id, c.documentSelfLink));
if (!c.endpointLinks.contains(this.context.request.original.endpointLink)) {
descUpdateOps.add(updateResourceState(c.documentSelfLink));
descUpdateOps.add(updateResourceState(c.descriptionLink));
}
});
List descOps = zones.stream()
.filter(z -> !cm.containsKey(z.getZoneName()))
.map(this::createComputeDescription)
.map(cd -> Operation
.createPost(this.service, ComputeDescriptionService.FACTORY_LINK)
.setBody(cd))
.collect(Collectors.toList());
if (descOps.isEmpty() && descUpdateOps.isEmpty()) {
proceedWithRefresh();
return;
}
OperationSequence operationSequence = null;
if (!descOps.isEmpty()) {
operationSequence = OperationSequence.create(OperationJoin.create(descOps))
.setCompletion(
(ops, exs) -> {
if (exs != null) {
this.service
.logSevere(() -> String.format("Error creating a"
+ " compute descriptions for "
+ "discovered AvailabilityZone: %s",
Utils.toString(exs)));
this.context.operation.fail(exs.values().iterator().next());
return;
}
List computeOps = ops
.values()
.stream()
.map(o -> o.getBody(ComputeDescription.class))
.map(this::createComputeInstanceForAvailabilityZone)
.map(c -> Operation
.createPost(this.service,
ComputeService.FACTORY_LINK)
.setBody(c))
.collect(Collectors.toList());
boolean refresh = descUpdateOps.isEmpty() ? true : false;
invokeComputeOps(computeOps, refresh);
});
}
if (!descUpdateOps.isEmpty()) {
OperationJoin descUpdateJoin = OperationJoin.create(descUpdateOps);
if (operationSequence == null) {
operationSequence = OperationSequence.create(descUpdateJoin);
} else {
operationSequence = operationSequence.next(descUpdateJoin);
}
operationSequence
.setCompletion(
(ops, exs) -> {
if (exs != null) {
this.service
.logSevere(() -> String.format("Error updating a"
+ " compute descriptions for "
+ "discovered AvailabilityZone: %s",
Utils.toString(exs)));
this.context.operation.fail(exs.values().iterator().next());
return;
}
proceedWithRefresh();
return;
});
}
operationSequence.sendWith(this.service);
}
private void invokeComputeOps(List computeOps, boolean refresh) {
if (computeOps.isEmpty()) {
if (refresh) {
proceedWithRefresh();
}
return;
}
OperationJoin.create(computeOps)
.setCompletion((ops, exs) -> {
if (exs != null) {
this.service.logSevere(() -> String.format("Error creating a compute"
+ " states for discovered AvailabilityZone: %s",
Utils.toString(exs)));
this.context.operation.fail(exs.values().iterator().next());
return;
}
ops.values().stream()
.map(o -> o.getBody(ComputeState.class))
.forEach(c -> this.context.zones.put(c.id,
ZoneData.build(
this.context.request.regionId,
c.id, c.documentSelfLink)));
if (refresh) {
proceedWithRefresh();
}
})
.sendWith(this.service);
}
private ComputeState createComputeInstanceForAvailabilityZone(ComputeDescription cd) {
ComputeService.ComputeState computeState = new ComputeService.ComputeState();
computeState.name = cd.name;
computeState.id = cd.id;
computeState.adapterManagementReference = this.context.parentCompute.adapterManagementReference;
computeState.parentLink = this.context.parentCompute.documentSelfLink;
computeState.resourcePoolLink = this.context.request.original.resourcePoolLink;
computeState.endpointLink = this.context.request.original.endpointLink;
computeState.endpointLinks = this.context.parentCompute.endpointLinks;
if (computeState.endpointLinks == null) {
computeState.endpointLinks = new HashSet<>();
}
computeState.endpointLinks.add(this.context.request.original.endpointLink);
computeState.descriptionLink = cd.documentSelfLink;
computeState.type = ComputeType.ZONE;
computeState.regionId = cd.regionId;
computeState.environmentName = ComputeDescription.ENVIRONMENT_NAME_AWS;
computeState.computeHostLink = this.context.request.parentCompute.documentSelfLink;
computeState.powerState = PowerState.ON;
computeState.customProperties = this.context.parentCompute.customProperties;
if (computeState.customProperties == null) {
computeState.customProperties = new HashMap<>();
}
computeState.customProperties.put(SOURCE_TASK_LINK,
ResourceEnumerationTaskService.FACTORY_LINK);
computeState.tenantLinks = this.context.parentCompute.tenantLinks;
return computeState;
}
private Operation updateResourceState(String docLink) {
Map> collectionsToAddMap = Collections.singletonMap
(ResourceState.FIELD_NAME_ENDPOINT_LINKS,
Collections.singletonList(this.context.request.original.endpointLink));
Map> collectionsToRemoveMap = Collections.singletonMap
(ResourceState.FIELD_NAME_ENDPOINT_LINKS, Collections.emptyList());
ServiceStateCollectionUpdateRequest updateEndpointLinksRequest = ServiceStateCollectionUpdateRequest
.create(collectionsToAddMap, collectionsToRemoveMap);
return Operation.createPatch(this.service.getHost(), docLink)
.setReferer(this.service.getUri())
.setBody(updateEndpointLinksRequest);
}
private ComputeDescription createComputeDescription(AvailabilityZone z) {
ComputeDescriptionService.ComputeDescription cd = Utils
.clone(this.context.parentCompute.description);
cd.supportedChildren = new ArrayList<>();
cd.supportedChildren.add(ComputeType.VM_GUEST.toString());
cd.documentSelfLink = null;
cd.id = z.getZoneName();
cd.zoneId = z.getZoneName();
cd.name = z.getZoneName();
cd.regionId = z.getRegionName();
cd.endpointLink = this.context.request.original.endpointLink;
if (cd.endpointLinks == null) {
cd.endpointLinks = new HashSet<>();
}
cd.computeHostLink = this.context.request.parentCompute.documentSelfLink;
cd.endpointLinks.add(this.context.request.original.endpointLink);
// Book keeping information about the creation of the compute description in the system.
if (cd.customProperties == null) {
cd.customProperties = new HashMap<>();
}
cd.customProperties.put(SOURCE_TASK_LINK,
ResourceEnumerationTaskService.FACTORY_LINK);
return cd;
}
@Override
protected void handleError(Exception exception) {
this.context.error = exception;
proceedWithRefresh();
}
}
/**
* The async handler to handle the success and errors received after invoking the describe
* instances API on AWS
*/
public static class AWSEnumerationAsyncHandler implements
AsyncHandler {
private AWSEnumerationAndCreationAdapterService service;
private EnumerationCreationContext context;
private AWSEnumerationRefreshSubStage nextRefreshSubStage;
private OperationContext opContext;
private AWSEnumerationAsyncHandler(AWSEnumerationAndCreationAdapterService service,
EnumerationCreationContext context, AWSEnumerationRefreshSubStage
nextRefreshSubStage) {
this.service = service;
this.context = context;
this.nextRefreshSubStage = nextRefreshSubStage;
this.opContext = OperationContext.getOperationContext();
}
@Override
public void onError(Exception exception) {
OperationContext.restoreOperationContext(this.opContext);
this.context.error = exception;
this.context.refreshSubStage = AWSEnumerationRefreshSubStage.ERROR;
this.service.processRefreshSubStages(this.context);
}
@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()) {
++totalNumberOfInstances;
final int finalTotal1 = totalNumberOfInstances;
this.service.logFine(() -> String.format("%d=====Instance details %s =====",
finalTotal1, i.getInstanceId()));
this.context.remoteAWSInstances.put(i.getInstanceId(), i);
}
}
final int finalTotal2 = totalNumberOfInstances;
this.service.logFine(() -> String.format("Successfully enumerated %d instances on the"
+ " AWS host", finalTotal2));
// Save the reference to the next token that will be used to retrieve the next page of
// results from AWS.
this.context.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.context.remoteAWSInstances.size() == 0) {
if (this.context.nextToken != null) {
this.context.subStage = AWSComputeEnumerationCreationSubStage.GET_NEXT_PAGE;
} else {
this.context.subStage = AWSComputeEnumerationCreationSubStage.NEXT_REFRESH_SUB_STAGE;
}
}
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.context.subStage) {
case QUERY_LOCAL_RESOURCES:
getLocalResources(AWSComputeEnumerationCreationSubStage.COMPARE);
break;
case COMPARE:
compareLocalStateWithEnumerationData(
AWSComputeEnumerationCreationSubStage.CREATE_COMPUTE_DESCRIPTIONS);
break;
case CREATE_COMPUTE_DESCRIPTIONS:
if (this.context.instancesToBeCreated.size() > 0
|| this.context.instancesToBeUpdated.size() > 0) {
createComputeDescriptions(
AWSComputeEnumerationCreationSubStage.CREATE_COMPUTE_STATES);
} else {
if (this.context.nextToken == null) {
this.context.subStage = AWSComputeEnumerationCreationSubStage.NEXT_REFRESH_SUB_STAGE;
} else {
this.context.subStage = AWSComputeEnumerationCreationSubStage.GET_NEXT_PAGE;
}
handleReceivedEnumerationData();
}
break;
case CREATE_COMPUTE_STATES:
AWSComputeEnumerationCreationSubStage next;
if (this.context.nextToken == null) {
next = AWSComputeEnumerationCreationSubStage.NEXT_REFRESH_SUB_STAGE;
} else {
next = AWSComputeEnumerationCreationSubStage.GET_NEXT_PAGE;
}
createComputeStates(next);
break;
case GET_NEXT_PAGE:
getNextPageFromEnumerationAdapter(
AWSComputeEnumerationCreationSubStage.QUERY_LOCAL_RESOURCES);
break;
case NEXT_REFRESH_SUB_STAGE:
processNextRefreshSubStage();
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.
*/
private void getLocalResources(AWSComputeEnumerationCreationSubStage next) {
loadLocalResources(this.service, this.context, this.context.remoteAWSInstances.keySet(),
lcsm -> {
this.context.localAWSInstanceMap.putAll(lcsm);
this.context.subStage = next;
handleReceivedEnumerationData();
},
lcsm -> {
if (this.context.nextToken == null) {
this.service.logFine(() -> "Completed enumeration");
this.context.subStage = AWSComputeEnumerationCreationSubStage.NEXT_REFRESH_SUB_STAGE;
} else {
this.service.logFine(() -> "No remote resources found, proceeding to"
+ " next page");
this.context.subStage = AWSComputeEnumerationCreationSubStage.GET_NEXT_PAGE;
}
handleReceivedEnumerationData();
});
}
/**
* 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(
AWSComputeEnumerationCreationSubStage next) {
// No remote instances
if (this.context.remoteAWSInstances == null
|| this.context.remoteAWSInstances.size() == 0) {
this.service
.logFine(() -> "No remote resources found. Nothing to be created locally");
// no local instances
} else if (this.context.localAWSInstanceMap == null
|| this.context.localAWSInstanceMap.size() == 0) {
for (String key : this.context.remoteAWSInstances.keySet()) {
this.context.instancesToBeCreated.add(this.context.remoteAWSInstances.get(key));
}
// compare and add the ones that do not exist locally for creation. Mark others
// for updates.
} else {
for (String key : this.context.remoteAWSInstances.keySet()) {
if (!this.context.localAWSInstanceMap.containsKey(key)) {
this.context.instancesToBeCreated
.add(this.context.remoteAWSInstances.get(key));
// A map of the local compute state id and the corresponding latest
// state on AWS
} else {
this.context.instancesToBeUpdated.put(key,
this.context.remoteAWSInstances.get(key));
this.context.computeStatesToBeUpdated.put(key,
this.context.localAWSInstanceMap.get(key));
}
}
queryAndCollectAllNICStatesToBeUpdated(next);
return;
}
this.context.subStage = next;
handleReceivedEnumerationData();
}
private void queryAndCollectAllNICStatesToBeUpdated(
AWSComputeEnumerationCreationSubStage next) {
List> getNICsDR = this.context.computeStatesToBeUpdated
.entrySet()
.stream()
// Get those computes which have NICs assigned
.filter(en -> en.getValue().networkInterfaceLinks != null
&& !en.getValue().networkInterfaceLinks.isEmpty())
// Merge NICs across all computes
.flatMap(
en -> {
this.context.nicStatesToBeUpdated
.put(en.getKey(), new ArrayList<>());
Stream> getNICsPerComputeDRs = en
.getValue().networkInterfaceLinks
.stream()
.map(nicLink -> {
Operation op = Operation
.createGet(this.service.getHost(), nicLink);
return this.service
.sendWithDeferredResult(op,
NetworkInterfaceState.class)
.thenAccept(
nic -> this.context.nicStatesToBeUpdated
.get(en.getKey()).add(nic))
.whenComplete((nic, e) -> {
// if an exception occurs while fetching an
// existing NIC for a compute state, mark it
// for deletion as enumeration logic will re-create it.
if (e != null) {
this.service.logSevere(() -> String
.format("GET Operation %s failed, %s",
nicLink,
e.getMessage()));
this.context.nicStatesToBeDeleted
.computeIfAbsent(en.getKey(),
k -> new ArrayList<>());
this.context.nicStatesToBeDeleted
.get(en.getKey()).add(nicLink);
}
});
});
return getNICsPerComputeDRs;
})
.collect(Collectors.toList());
if (getNICsDR.isEmpty()) {
this.context.subStage = next;
handleReceivedEnumerationData();
} else {
DeferredResult.allOf(getNICsDR).whenComplete((o, e) -> {
if (e != null) {
this.service.logSevere(() -> String.format("Failure querying"
+ " NetworkInterfaceStates for update %s", Utils.toString(e)));
}
this.service.logFine(() -> String.format("Populated NICs to be updated: %s",
this.context.nicStatesToBeUpdated));
this.context.subStage = next;
handleReceivedEnumerationData();
});
}
}
/**
* Posts a compute description to the compute description service for creation.
*/
private void createComputeDescriptions(AWSComputeEnumerationCreationSubStage next) {
this.service.logFine(() -> "Creating compute descriptions for enumerated VMs");
AWSComputeDescriptionCreationState cd = new AWSComputeDescriptionCreationState();
cd.instancesToBeCreated = this.context.instancesToBeCreated;
cd.instancesToBeUpdated = new ArrayList<>(this.context.instancesToBeUpdated.values());
cd.parentTaskLink = this.context.request.original.taskReference;
cd.authCredentiaslLink = this.context.endpointAuth.documentSelfLink;
cd.tenantLinks = this.context.parentCompute.tenantLinks;
cd.parentDescription = this.context.parentCompute.description;
cd.endpointLink = this.context.request.original.endpointLink;
cd.parentComputeLink = this.context.request.parentCompute.documentSelfLink;
cd.regionId = this.context.request.regionId;
cd.zones = this.context.zones;
this.service.sendRequest(Operation
.createPatch(this.service,
AWSComputeDescriptionEnumerationAdapterService.SELF_LINK)
.setBody(cd)
.setCompletion((o, e) -> {
if (e != null) {
this.service.logSevere(() -> String.format("Failure creating compute"
+ " descriptions %s", Utils.toString(e)));
signalErrorToEnumerationAdapter(e);
} else {
this.service
.logFine(() -> "Successfully created compute descriptions.");
this.context.subStage = next;
handleReceivedEnumerationData();
}
}));
}
/**
* Creates the compute states that represent the instances received from AWS during
* enumeration.
*/
private void createComputeStates(AWSComputeEnumerationCreationSubStage next) {
this.service.logFine(() -> "Creating compute states for enumerated VMs");
AWSComputeStateCreationRequest awsComputeState = new AWSComputeStateCreationRequest();
awsComputeState.instancesToBeCreated = this.context.instancesToBeCreated;
awsComputeState.instancesToBeUpdated = this.context.instancesToBeUpdated;
awsComputeState.nicStatesToBeUpdated = this.context.nicStatesToBeUpdated;
awsComputeState.nicStatesToBeDeleted = this.context.nicStatesToBeDeleted;
awsComputeState.computeStatesToBeUpdated = this.context.computeStatesToBeUpdated;
awsComputeState.parentComputeLink = this.context.parentCompute.documentSelfLink;
awsComputeState.resourcePoolLink = this.context.request.original.resourcePoolLink;
awsComputeState.endpointLink = this.context.request.original.endpointLink;
awsComputeState.parentTaskLink = this.context.request.original.taskReference;
awsComputeState.parentCDStatsAdapterReferences = this.context.parentCompute.description.statsAdapterReferences;
awsComputeState.tenantLinks = this.context.parentCompute.tenantLinks;
awsComputeState.endpointAuth = this.context.endpointAuth;
awsComputeState.regionId = this.context.request.regionId;
awsComputeState.enumeratedNetworks = this.context.enumeratedNetworks;
awsComputeState.enumeratedSecurityGroups = this.context.enumeratedSecurityGroups;
awsComputeState.zones = this.context.zones;
awsComputeState.deletedResourceExpirationMicros = this.context.request.original.deletedResourceExpirationMicros;
this.service.sendRequest(Operation
.createPatch(this.service, AWSComputeStateCreationAdapterService.SELF_LINK)
.setBody(awsComputeState)
.setCompletion((o, e) -> {
if (e != null) {
this.service.logSevere(() -> String.format("Failure creating compute"
+ " states %s", Utils.toString(e)));
signalErrorToEnumerationAdapter(e);
} else {
this.service.logFine(() -> "Successfully created compute states.");
this.context.subStage = next;
handleReceivedEnumerationData();
}
}));
}
/**
* Process the next refresh sub stage after the compute enumeration
*/
private void processNextRefreshSubStage() {
this.context.refreshSubStage = this.nextRefreshSubStage;
this.service.processRefreshSubStages(this.context);
}
/**
* 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.context.error = t;
this.context.stage = AWSEnumerationCreationStages.ERROR;
this.service.handleEnumerationRequest(this.context);
}
/**
* Calls the AWS enumeration adapter to get the next page from AWSs
*/
private void getNextPageFromEnumerationAdapter(AWSComputeEnumerationCreationSubStage next) {
// Reset all the results from the last page that was processed.
this.context.remoteAWSInstances.clear();
this.context.instancesToBeCreated.clear();
this.context.instancesToBeUpdated.clear();
this.context.nicStatesToBeUpdated.clear();
this.context.nicStatesToBeDeleted.clear();
this.context.computeStatesToBeUpdated.clear();
this.context.localAWSInstanceMap.clear();
this.context.describeInstancesRequest.setNextToken(this.context.nextToken);
this.context.subStage = next;
this.service.handleEnumerationRequest(this.context);
}
}
public enum AWSEnumerationCreationStages {
CLIENT,
ENUMERATE,
ERROR
}
public enum AWSComputeEnumerationCreationSubStage {
QUERY_LOCAL_RESOURCES,
COMPARE,
CREATE_COMPUTE_DESCRIPTIONS,
CREATE_COMPUTE_STATES,
GET_NEXT_PAGE,
NEXT_REFRESH_SUB_STAGE
}
private enum AWSEnumerationRefreshSubStage {
ZONES,
VPC,
SECURITY_GROUP,
COMPUTE,
LOAD_BALANCER,
ENUMERATION_STOP,
ERROR
}
public AWSEnumerationAndCreationAdapterService() {
super.toggleOption(ServiceOption.INSTRUMENTATION, true);
}
/**
* Signals error to the AWS enumeration adapter. The adapter will in turn clean up resources and
* signal error to the parent task.
*/
private static void signalErrorToEnumerationAdapter(Throwable t, EnumerationCreationContext aws,
AWSEnumerationAndCreationAdapterService service) {
aws.error = t;
aws.stage = AWSEnumerationCreationStages.ERROR;
service.handleEnumerationRequest(aws);
}
/**
* 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.
*/
private static void loadLocalResources(AWSEnumerationAndCreationAdapterService service,
EnumerationCreationContext context, Collection remoteIds,
Consumer
© 2015 - 2025 Weber Informatics LLC | Privacy Policy