
com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSComputeStateCreationAdapterService 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.AWSResourceType.ec2_instance;
import static com.vmware.photon.controller.model.adapters.awsadapter.AWSConstants.AWSResourceType.ec2_net_interface;
import static com.vmware.photon.controller.model.adapters.awsadapter.util.AWSEnumerationUtils.getCDsRepresentingVMsInLocalSystemCreatedByEnumerationQuery;
import static com.vmware.photon.controller.model.adapters.awsadapter.util.AWSEnumerationUtils.getKeyForComputeDescriptionFromCD;
import static com.vmware.photon.controller.model.adapters.awsadapter.util.AWSEnumerationUtils.getRepresentativeListOfCDsFromInstanceList;
import static com.vmware.photon.controller.model.adapters.awsadapter.util.AWSEnumerationUtils.mapInstanceToComputeState;
import static com.vmware.photon.controller.model.adapters.util.AdapterUtils.createDeleteOperation;
import static com.vmware.photon.controller.model.adapters.util.AdapterUtils.createPatchOperation;
import static com.vmware.photon.controller.model.adapters.util.AdapterUtils.createPostOperation;
import static com.vmware.photon.controller.model.adapters.util.AdapterUtils.getDeletionState;
import static com.vmware.photon.controller.model.adapters.util.TagsUtil.newTagState;
import static com.vmware.photon.controller.model.adapters.util.TagsUtil.updateLocalTagStates;
import static com.vmware.photon.controller.model.constants.PhotonModelConstants.TAG_KEY_TYPE;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
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.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import com.amazonaws.services.ec2.model.GroupIdentifier;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceNetworkInterface;
import com.amazonaws.services.ec2.model.Tag;
import org.apache.commons.lang3.StringUtils;
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.enumeration.AWSNetworkStateEnumerationAdapterService.AWSNetworkEnumerationResponse;
import com.vmware.photon.controller.model.adapters.awsadapter.enumeration.AWSSecurityGroupEnumerationAdapterService.AWSSecurityGroupEnumerationResponse;
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;
import com.vmware.photon.controller.model.adapters.awsadapter.util.AWSEnumerationUtils.InstanceDescKey;
import com.vmware.photon.controller.model.adapters.awsadapter.util.AWSEnumerationUtils.ZoneData;
import com.vmware.photon.controller.model.query.QueryUtils;
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.photon.controller.model.resources.DiskService.DiskState;
import com.vmware.photon.controller.model.resources.NetworkInterfaceService;
import com.vmware.photon.controller.model.resources.NetworkInterfaceService.NetworkInterfaceState;
import com.vmware.photon.controller.model.resources.ResourceState;
import com.vmware.photon.controller.model.resources.TagService;
import com.vmware.photon.controller.model.resources.TagService.TagState;
import com.vmware.xenon.common.DeferredResult;
import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.OperationJoin;
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.AuthCredentialsServiceState;
import com.vmware.xenon.services.common.QueryTask;
import com.vmware.xenon.services.common.QueryTask.Query;
import com.vmware.xenon.services.common.QueryTask.QuerySpecification.QueryOption;
/**
* Stateless service for the creation of compute states. It accepts a list of AWS instances that
* need to be created in the local system.It also accepts a few additional fields required for
* mapping the referential integrity relationships for the compute state when it is persisted in the
* local system.
*/
public class AWSComputeStateCreationAdapterService extends StatelessService {
public static final String SELF_LINK = AWSUriPaths.AWS_COMPUTE_STATE_CREATION_ADAPTER;
private static final String UPDATE_NIC_STATES = "update";
private static final String REMOVE_NIC_STATES = "remove";
private static final String ADD_NIC_STATES = "add";
private static final long QUERY_TASK_EXPIRY_MICROS = TimeUnit.MINUTES.toMicros(1);
public static final List internalTagList = Arrays.asList(ec2_instance.toString(),
ec2_net_interface.toString());
private AWSClientManager clientManager;
/**
* Request accepted by this service to trigger create or update of Compute states representing
* compute instances in Amazon.
*/
public static class AWSComputeStateCreationRequest {
public List instancesToBeCreated;
public Map instancesToBeUpdated;
public Map computeStatesToBeUpdated;
// Expands computeStatesToBeUpdated with corresponding NICs
public Map> nicStatesToBeUpdated;
public Map> nicStatesToBeDeleted;
/**
* Discovered/Enumerated networks in Amazon.
*/
public AWSNetworkEnumerationResponse enumeratedNetworks;
public AWSSecurityGroupEnumerationResponse enumeratedSecurityGroups;
public Map zones;
public String resourcePoolLink;
public String endpointLink;
public String parentComputeLink;
public AuthCredentialsServiceState endpointAuth;
public String regionId;
public URI parentTaskLink;
public List tenantLinks;
boolean isMock;
public Set parentCDStatsAdapterReferences;
public long deletedResourceExpirationMicros;
}
/**
* The service context that is created for representing the list of instances received into a
* list of compute states that will be persisted in the system.
*
*/
private static class AWSComputeStateCreationContext {
public AWSComputeStateCreationRequest request;
public List enumerationOperations;
public AWSComputeStateCreationStage creationStage;
// Holds the mapping between the instanceType (t2.micro etc) and the document self link to
// that compute description.
public Map computeDescriptionMap;
// Cached operation to signal completion to the AWS instance adapter once all the compute
// states are successfully created.
public Operation operation;
// maintains internal tagLinks Example, link for tagState for type=ec2_instances or
// type=ec2_net_interface
public Map> internalTagLinksMap = new HashMap<>();
// Holds tags of newly discovered instaces that got successfully POSTed (with 200 or 304)
List createdExternalTags;
// maintains internal tagLinks Example, link for tagState for type=ec2_instances
public ResourceState resourceDeletionState;
public Map> diskLinksByInstances;
public AWSComputeStateCreationContext(AWSComputeStateCreationRequest request,
Operation op) {
this.request = request;
this.enumerationOperations = new ArrayList<>();
this.createdExternalTags = new ArrayList<>();
this.computeDescriptionMap = new HashMap<>();
this.diskLinksByInstances = new HashMap<>();
this.creationStage = AWSComputeStateCreationStage.GET_RELATED_COMPUTE_DESCRIPTIONS;
this.operation = op;
this.resourceDeletionState = getDeletionState(request.deletedResourceExpirationMicros);
}
}
public static enum AWSComputeStateCreationStage {
GET_RELATED_COMPUTE_DESCRIPTIONS,
/**
* Create internal tag for types
*/
CREATE_INTERNAL_TYPE_TAGS,
/**
* Create internal tag for type
*/
CREATE_INTERNAL_TYPE_TAG,
/**
* Create the operations for creating ComputeStates and their corresponding
* NetworkInterfaceStates
*/
CREATE_COMPUTESTATES_OPERATIONS,
/**
* For each ComputeState which needs an update create post operation. Update corresponding
* NetworkInterfaceStates
*/
UPDATE_COMPUTESTATES_OPERATIONS,
/**
* Execute all the crete and update operations, generated during the previous stages
*/
CREATE_COMPUTESTATES,
SIGNAL_COMPLETION,
CREATE_EXTERNAL_TAGS,
UPDATE_EXTERNAL_TAGS,
/**
* Collect respective EBS selfLinks for every instance.
*/
COLLECT_EBS_DISK_LINKS,
}
public AWSComputeStateCreationAdapterService() {
super.toggleOption(ServiceOption.INSTRUMENTATION, true);
}
@Override
public void handleStart(Operation startPost) {
this.clientManager = AWSClientManagerFactory
.getClientManager(AWSConstants.AwsClientType.EC2);
super.handleStart(startPost);
}
@Override
public void handleStop(Operation op) {
AWSClientManagerFactory.returnClientManager(this.clientManager,
AWSConstants.AwsClientType.EC2);
super.handleStop(op);
}
@Override
public void handlePatch(Operation op) {
setOperationHandlerInvokeTimeStat(op);
if (!op.hasBody()) {
op.fail(new IllegalArgumentException("body is required"));
return;
}
AWSComputeStateCreationRequest cs = op.getBody(AWSComputeStateCreationRequest.class);
AWSComputeStateCreationContext context = new AWSComputeStateCreationContext(cs, op);
if (cs.isMock) {
op.complete();
}
handleComputeStateCreateOrUpdate(context);
}
/**
* Creates the compute states in the local document store based on the AWS instances received
* from the remote endpoint.
*
* @param context
* The local service context that has all the information needed to create the
* additional compute states in the local system.
*/
private void handleComputeStateCreateOrUpdate(AWSComputeStateCreationContext context) {
this.logFine(() -> String.format("Transition to: %s", context.creationStage));
switch (context.creationStage) {
case GET_RELATED_COMPUTE_DESCRIPTIONS:
getRelatedComputeDescriptions(context,
AWSComputeStateCreationStage.CREATE_INTERNAL_TYPE_TAG);
break;
case CREATE_INTERNAL_TYPE_TAG:
createInternalTypeTags(context, AWSComputeStateCreationStage.CREATE_EXTERNAL_TAGS);
break;
case CREATE_EXTERNAL_TAGS:
createTags(context, AWSComputeStateCreationStage.COLLECT_EBS_DISK_LINKS);
break;
case COLLECT_EBS_DISK_LINKS:
collectEbsDiskLinks(context, AWSComputeStateCreationStage.CREATE_COMPUTESTATES_OPERATIONS);
break;
case CREATE_COMPUTESTATES_OPERATIONS:
populateCreateOperations(context,
AWSComputeStateCreationStage.UPDATE_EXTERNAL_TAGS);
break;
case UPDATE_EXTERNAL_TAGS:
updateTagLinks(context).whenComplete(thenComputeStateCreateOrUpdate(context,
AWSComputeStateCreationStage.UPDATE_COMPUTESTATES_OPERATIONS));
break;
case UPDATE_COMPUTESTATES_OPERATIONS:
populateUpdateOperations(context, AWSComputeStateCreationStage.CREATE_COMPUTESTATES);
break;
case CREATE_COMPUTESTATES:
kickOffComputeStateCreation(context, AWSComputeStateCreationStage.SIGNAL_COMPLETION);
break;
case SIGNAL_COMPLETION:
setOperationDurationStat(context.operation);
context.operation.complete();
break;
default:
Throwable t = new IllegalArgumentException("Unknown AWS enumeration:compute state"
+ " creation stage");
finishWithFailure(context, t);
break;
}
}
/**
* {@code handleComputeStateCreateOrUpdate} version suitable for chaining to
* {@code DeferredResult.whenComplete}.
*/
private BiConsumer thenComputeStateCreateOrUpdate(
AWSComputeStateCreationContext context,
AWSComputeStateCreationStage next) {
// NOTE: In case of error 'ignoreCtx' is null so use passed context!
return (ignoreCtx, exc) -> {
if (exc != null) {
logSevere(() -> String.format("Error updating tag Links for compute states: %s",
Utils.toString(exc)));
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
return;
}
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
};
}
private void createInternalTypeTags(AWSComputeStateCreationContext context,
AWSComputeStateCreationStage next) {
// Go over the list of internal tags to be created. Find whatever already does not have an
// associated tag state and create an operation for its creation.
List joinOperations = new ArrayList<>();
for (String resourceType : internalTagList) {
TagState typeTag = newTagState(TAG_KEY_TYPE, resourceType, false,
context.request.tenantLinks);
Operation op = Operation.createPost(this, TagService.FACTORY_LINK)
.setBody(typeTag);
joinOperations.add(op);
}
OperationJoin.create(joinOperations)
.setCompletion((ops, exs) -> {
if (exs != null) {
exs.values()
.forEach(ex -> logWarning(
() -> String.format("Error creating internal tag%s",
ex.getMessage())));
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
return;
}
for (String internalTagValue : internalTagList) {
TagState tagState = newTagState(TAG_KEY_TYPE, internalTagValue, false,
context.request.tenantLinks);
context.internalTagLinksMap.put(tagState.value,
new HashSet<>(Arrays.asList(tagState.documentSelfLink)));
}
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
}).sendWith(this);
}
/**
* POSTs all tags for newly discovered instances. Even if some tags already exist we rely on
* IDEMPOTENT_POST behaviour and POST them again. All tags that got created successfully are
* stored in createdExternalTags list.
*/
private void createTags(AWSComputeStateCreationContext context,
AWSComputeStateCreationStage next) {
// Get all tags from the instances to be created
Set create = context.request.instancesToBeCreated.stream()
.flatMap(i -> i.getTags().stream())
.collect(Collectors.toSet());
// Put them in a set to remove the duplicates
Set allTags = new HashSet<>();
allTags.addAll(create);
// POST each of the tags. If a tag exists it won't be created again. We don't want the name
// tags, so filter them out
List operations = new ArrayList<>();
Map tagsCreationOperationIdsMap = new ConcurrentHashMap<>();
allTags.stream()
.filter(t -> !AWSConstants.AWS_TAG_NAME.equals(t.getKey()))
.forEach(t -> {
TagState tagState = newTagState(t.getKey(), t.getValue(), true,
context.request.tenantLinks);
Operation op = Operation.createPost(this, TagService.FACTORY_LINK)
.setBody(tagState);
operations.add(op);
tagsCreationOperationIdsMap.put(op.getId(), t);
});
if (operations.isEmpty()) {
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
} else {
OperationJoin.create(operations).setCompletion((ops, exs) -> {
if (exs != null && !exs.isEmpty()) {
logSevere(() -> String.format("Error creating %s external tags for compute"
+ "states: %s", exs.size(), Utils.toString(exs.get(0))));
}
ops.values().stream()
.filter(operation -> operation.getStatusCode() == Operation.STATUS_CODE_OK
|| operation.getStatusCode() == Operation.STATUS_CODE_NOT_MODIFIED)
.forEach(operation -> {
if (tagsCreationOperationIdsMap.containsKey(operation.getId())) {
context.createdExternalTags.add(tagsCreationOperationIdsMap
.get(operation.getId()));
}
});
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
}).sendWith(this);
}
}
/**
* Collect EBS selfLinks by querying for all EBS disk IDs. Map these selfLinks to
* their respective instances. These will be used for reconciliation with local state while
* creating/updating compute state's diskLinks.
*/
private void collectEbsDiskLinks(AWSComputeStateCreationContext context,
AWSComputeStateCreationStage next) {
List diskIds = new ArrayList<>();
Map instancesByDiskIds = new HashMap<>();
context.request.instancesToBeCreated.stream()
.forEach(instance -> {
instance.getBlockDeviceMappings().stream()
.forEach(instanceBlockDeviceMapping -> {
String id = instanceBlockDeviceMapping.getEbs().getVolumeId();
instancesByDiskIds.put(id, instance);
diskIds.add(id);
});
});
context.request.instancesToBeUpdated.values().stream()
.forEach(instance -> {
instance.getBlockDeviceMappings().stream()
.forEach(instanceBlockDeviceMapping -> {
String id = instanceBlockDeviceMapping.getEbs().getVolumeId();
instancesByDiskIds.put(id, instance);
diskIds.add(id);
});
});
// No disks found for current page of instances.
if (diskIds.isEmpty()) {
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
return;
}
Query ebsQuery = Query.Builder.create()
.addKindFieldClause(DiskState.class)
.addCollectionItemClause(ResourceState.FIELD_NAME_ENDPOINT_LINKS,
context.request.endpointLink)
.addInClause(DiskState.FIELD_NAME_ID, diskIds)
.build();
QueryTask ebsQueryTask = QueryTask.Builder.createDirectTask()
.setQuery(ebsQuery)
.addOption(QueryOption.EXPAND_CONTENT)
.build();
ebsQueryTask.tenantLinks = context.request.tenantLinks;
QueryUtils.startInventoryQueryTask(this, ebsQueryTask)
.whenComplete((qrt, e) -> {
if (e != null) {
logWarning("Error querying diskLinks for endpoint %s",
context.request.endpointLink);
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
return;
}
if (qrt.results != null && qrt.results.documentCount > 0) {
qrt.results.documents.entrySet()
.forEach(entry -> {
DiskState state = Utils
.fromJson(entry.getValue(), DiskState.class);
Instance instance = instancesByDiskIds.get(state.id);
if (instance != null) {
if (context.diskLinksByInstances
.containsKey(instance)) {
context.diskLinksByInstances.get(instance)
.add(entry.getKey());
} else {
context.diskLinksByInstances
.put(instance, new ArrayList<>(Arrays
.asList(entry.getKey())));
}
}
});
}
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
});
}
/**
* Looks up the compute descriptions associated with the compute states to be created in the
* system.
*/
private void getRelatedComputeDescriptions(AWSComputeStateCreationContext context,
AWSComputeStateCreationStage next) {
// Get the related compute descriptions for all the compute states are to be updated and
// created.
Set representativeCDSet = getRepresentativeListOfCDsFromInstanceList(
context.request.instancesToBeCreated, context.request.zones);
representativeCDSet.addAll(getRepresentativeListOfCDsFromInstanceList(
context.request.instancesToBeUpdated.values(), context.request.zones));
if (representativeCDSet.isEmpty()) {
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
return;
}
QueryTask queryTask = getCDsRepresentingVMsInLocalSystemCreatedByEnumerationQuery(
representativeCDSet, context.request.tenantLinks, context.request.regionId,
context.request.parentComputeLink, context.request.endpointLink);
queryTask.documentExpirationTimeMicros = Utils.getNowMicrosUtc() + QUERY_TASK_EXPIRY_MICROS;
// create the query to find an existing compute description
QueryUtils.startInventoryQueryTask(this, queryTask)
.whenComplete((qrt, e) -> {
if (e != null) {
logWarning(() -> String.format("Failure retrieving query results: %s",
e.toString()));
finishWithFailure(context, e);
return;
}
if (qrt != null && qrt.results.documentCount > 0) {
for (Object s : qrt.results.documents.values()) {
ComputeDescription localComputeDescription = Utils.fromJson(s,
ComputeDescription.class);
context.computeDescriptionMap.put(
getKeyForComputeDescriptionFromCD(localComputeDescription),
localComputeDescription.documentSelfLink);
}
logFine(() -> String.format("%d compute descriptions found",
context.computeDescriptionMap.size()));
} else {
logFine(() -> "No compute descriptions found");
}
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
});
}
/**
* Method to create Compute States associated with the instances received from the AWS host.
*/
private void populateCreateOperations(AWSComputeStateCreationContext context,
AWSComputeStateCreationStage next) {
if (context.request.instancesToBeCreated == null
|| context.request.instancesToBeCreated.size() == 0) {
logFine(() -> "No local compute states to be created");
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
} else {
logFine(() -> String.format("Need to create %d local compute states",
context.request.instancesToBeCreated.size()));
for (int i = 0; i < context.request.instancesToBeCreated.size(); i++) {
Instance instance = context.request.instancesToBeCreated.get(i);
String zoneId = instance.getPlacement().getAvailabilityZone();
ZoneData zoneData = context.request.zones.get(zoneId);
String regionId = zoneData.regionId;
InstanceDescKey descKey = InstanceDescKey.build(regionId, zoneId,
instance.getInstanceType());
Set endpointLinks = new HashSet<>();
endpointLinks.add(context.request.endpointLink);
ComputeState computeStateToBeCreated = mapInstanceToComputeState(
this.getHost(), instance,
context.request.parentComputeLink, zoneData.computeLink,
context.request.resourcePoolLink,
null,
endpointLinks,
context.computeDescriptionMap.get(descKey),
context.request.parentCDStatsAdapterReferences,
context.internalTagLinksMap.get(ec2_instance.toString()),
regionId, zoneId,
context.request.tenantLinks,
context.createdExternalTags, true,
context.diskLinksByInstances.get(instance));
computeStateToBeCreated.networkInterfaceLinks = new ArrayList<>();
if (!AWSEnumerationUtils.instanceIsInStoppedState(instance)) {
// for each NIC create Description and State create operations. Link the
// ComputeState to be created to the NIC State
for (InstanceNetworkInterface awsNic : instance.getNetworkInterfaces()) {
if (context.request.enumeratedNetworks != null
&& context.request.enumeratedNetworks.subnets != null
&& context.request.enumeratedNetworks.subnets
.containsKey(awsNic.getSubnetId())) {
NetworkInterfaceState nicState = createNICStateAndDescription(
context, awsNic, null, endpointLinks);
computeStateToBeCreated.networkInterfaceLinks.add(UriUtils.buildUriPath(
NetworkInterfaceService.FACTORY_LINK,
nicState.documentSelfLink));
}
}
}
Operation postComputeState = createPostOperation(this, computeStateToBeCreated,
ComputeService.FACTORY_LINK);
context.enumerationOperations.add(postComputeState);
}
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
}
}
// Utility method which creates a new NetworkInterface State and Descriptions
// from provided AWS Nic, and adds them to the enumerationOperations list
private NetworkInterfaceState createNICStateAndDescription(
AWSComputeStateCreationContext context, InstanceNetworkInterface awsNic,
String existingEndpointLink, Set endpointLinks) {
final NetworkInterfaceState nicState;
{
nicState = new NetworkInterfaceState();
nicState.id = awsNic.getNetworkInterfaceId();
nicState.name = nicState.id;
nicState.address = awsNic.getPrivateIpAddress();
if (context.request.enumeratedNetworks != null &&
context.request.enumeratedNetworks.subnets != null) {
nicState.subnetLink = context.request.enumeratedNetworks.subnets
.get(awsNic.getSubnetId());
}
nicState.tenantLinks = context.request.tenantLinks;
// retain the existing link, if it is not null
nicState.endpointLink = existingEndpointLink;
if (nicState.endpointLink == null ) {
nicState.endpointLink = context.request.endpointLink;
}
if (nicState.endpointLinks == null) {
nicState.endpointLinks = new HashSet();
}
nicState.endpointLinks.addAll(endpointLinks);
nicState.regionId = context.request.regionId;
nicState.computeHostLink = context.request.parentComputeLink;
Set internalTagLinks = context.internalTagLinksMap
.get(ec2_net_interface.toString());
// append internal tagLinks to any existing ones
if (internalTagLinks != null && !internalTagLinks.isEmpty()) {
if (nicState.tagLinks != null && !nicState.tagLinks.isEmpty()) {
nicState.tagLinks.addAll(internalTagLinks);
} else {
nicState.tagLinks = new HashSet<>();
nicState.tagLinks.addAll(internalTagLinks);
}
}
if (context.request.enumeratedSecurityGroups != null) {
for (GroupIdentifier awsSG : awsNic.getGroups()) {
// we should have updated the list of SG Ids before this step and
// should have ensured that all the SGs exist locally
String securityGroupLink = context.request.enumeratedSecurityGroups.securityGroupStates
.get(awsSG.getGroupId());
if (securityGroupLink == null || securityGroupLink.isEmpty()) {
continue;
}
if (nicState.securityGroupLinks == null) {
nicState.securityGroupLinks = new ArrayList<>();
}
nicState.securityGroupLinks.add(securityGroupLink);
}
}
nicState.deviceIndex = awsNic.getAttachment().getDeviceIndex();
// Link is set, because it's referenced by CS before post
nicState.documentSelfLink = UUID.randomUUID().toString();
Operation postNetworkInterfaceState = createPostOperation(this, nicState,
NetworkInterfaceService.FACTORY_LINK);
context.enumerationOperations
.add(postNetworkInterfaceState);
}
return nicState;
}
/**
* Updates tag links of existing computes using TagsUtil.
*/
private DeferredResult updateTagLinks(
AWSComputeStateCreationContext context) {
if (context.request.instancesToBeUpdated == null
|| context.request.instancesToBeUpdated.size() == 0) {
logFine(() -> "No local compute states to be updated so there are no tags to update.");
return DeferredResult.completed(context);
} else {
List>> updateCSTagLinksOps = new ArrayList<>();
for (String instanceId : context.request.instancesToBeUpdated.keySet()) {
Instance instance = context.request.instancesToBeUpdated.get(instanceId);
ComputeState existingComputeState = context.request.computeStatesToBeUpdated
.get(instanceId);
Map remoteTags = new HashMap<>();
for (Tag awsInstanceTag : instance.getTags()) {
if (!awsInstanceTag.getKey().equals(AWSConstants.AWS_TAG_NAME)) {
remoteTags.put(awsInstanceTag.getKey(), awsInstanceTag.getValue());
}
}
updateCSTagLinksOps
.add(updateLocalTagStates(this, existingComputeState, remoteTags, null));
}
return DeferredResult.allOf(updateCSTagLinksOps).thenApply(gnore -> context);
}
}
private void populateUpdateOperations(AWSComputeStateCreationContext context,
AWSComputeStateCreationStage next) {
if (context.request.instancesToBeUpdated == null
|| context.request.instancesToBeUpdated.size() == 0) {
logFine(() -> "No local compute states to be updated");
context.creationStage = next;
handleComputeStateCreateOrUpdate(context);
} else {
logFine(() -> String.format("Need to update %d local compute states",
context.request.instancesToBeUpdated.size()));
for (String instanceId : context.request.instancesToBeUpdated.keySet()) {
Instance instance = context.request.instancesToBeUpdated.get(instanceId);
ComputeState existingComputeState = context.request.computeStatesToBeUpdated
.get(instanceId);
if (StringUtils.isEmpty(existingComputeState.endpointLink)) {
existingComputeState.endpointLink = context.request.endpointLink;
}
Set endpointLinks = new HashSet<>();
if (existingComputeState.endpointLinks != null) {
endpointLinks.addAll(existingComputeState.endpointLinks);
}
endpointLinks.add(context.request.endpointLink);
// Calculate NICs delta - collection of NIC States to add, to update and to delete
Map> deviceIndexesDelta = new HashMap<>();
Map>> linksToNICSToAddOrRemove = new HashMap<>();
// The stopped or stopping instance does not have full network settings.
if (!AWSEnumerationUtils.instanceIsInStoppedState(instance)) {
// Get existing NetworkInterfaceStates for this ComputeState
List existingNicStates = context.request.nicStatesToBeUpdated
.get(instanceId);
deviceIndexesDelta = calculateNICsDeviceIndexesDelta(instance,
existingNicStates);
linksToNICSToAddOrRemove = addUpdateOrRemoveNICStates(context, instance,
deviceIndexesDelta, existingComputeState.endpointLink, endpointLinks);
}
// Create dedicated PATCH operation for updating NIC Links {{
if (linksToNICSToAddOrRemove.get(ADD_NIC_STATES) != null
|| linksToNICSToAddOrRemove.get(REMOVE_NIC_STATES) != null) {
ServiceStateCollectionUpdateRequest updateComputeStateRequest = ServiceStateCollectionUpdateRequest
.create(linksToNICSToAddOrRemove.get(ADD_NIC_STATES),
linksToNICSToAddOrRemove.get(REMOVE_NIC_STATES));
Operation patchComputeStateNICLinks = Operation
.createPatch(UriUtils.buildUri(this.getHost(),
existingComputeState.documentSelfLink))
.setBody(updateComputeStateRequest)
.setReferer(this.getUri());
context.enumerationOperations.add(patchComputeStateNICLinks);
}
// Update ComputeState
String zoneId = instance.getPlacement().getAvailabilityZone();
ZoneData zoneData = context.request.zones.get(zoneId);
ComputeState computeStateToBeUpdated = mapInstanceToComputeState(
this.getHost(), instance,
context.request.parentComputeLink, zoneData.computeLink,
existingComputeState.resourcePoolLink != null
? existingComputeState.resourcePoolLink
: context.request.resourcePoolLink,
existingComputeState.endpointLink,
endpointLinks,
existingComputeState.descriptionLink,
context.request.parentCDStatsAdapterReferences,
context.internalTagLinksMap.get(ec2_instance.toString()),
zoneData.regionId, zoneId,
context.request.tenantLinks,
null,
false, null);
computeStateToBeUpdated.documentSelfLink = existingComputeState.documentSelfLink;
Operation patchComputeState = createPatchOperation(this, computeStateToBeUpdated,
existingComputeState.documentSelfLink);
context.enumerationOperations.add(patchComputeState);
// Reconcile remote diskLinks with current diskLinks of existing computeState and
// update them with collection update request if needed.
List remoteDiskLinks = context.diskLinksByInstances.get(instance);
List currentDiskLinks = existingComputeState.diskLinks;
Collection
© 2015 - 2025 Weber Informatics LLC | Privacy Policy