package com.sequenceiq.cloudbreak.cloud.aws; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Queue; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import com.amazonaws.services.autoscaling.AmazonAutoScalingClient; import com.amazonaws.services.cloudformation.AmazonCloudFormationClient; import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.CreateTagsRequest; import com.amazonaws.services.ec2.model.DescribeInstancesRequest; import com.amazonaws.services.ec2.model.DescribeInstancesResult; import com.amazonaws.services.ec2.model.Instance; import com.amazonaws.services.ec2.model.Reservation; import com.amazonaws.services.ec2.model.Tag; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.sequenceiq.cloudbreak.cloud.MetadataCollector; import com.sequenceiq.cloudbreak.cloud.aws.task.AwsPollTaskFactory; import com.sequenceiq.cloudbreak.cloud.aws.view.AwsCredentialView; import com.sequenceiq.cloudbreak.cloud.context.AuthenticatedContext; import com.sequenceiq.cloudbreak.cloud.exception.CloudConnectorException; import com.sequenceiq.cloudbreak.cloud.model.CloudInstance; import com.sequenceiq.cloudbreak.cloud.model.CloudInstanceMetaData; import com.sequenceiq.cloudbreak.cloud.model.CloudResource; import com.sequenceiq.cloudbreak.cloud.model.CloudVmInstanceStatus; import com.sequenceiq.cloudbreak.cloud.model.CloudVmMetaDataStatus; import com.sequenceiq.cloudbreak.cloud.model.InstanceStatus; import com.sequenceiq.cloudbreak.cloud.scheduler.SyncPollingScheduler; @Service public class AwsMetadataCollector implements MetadataCollector { private static final Logger LOGGER = LoggerFactory.getLogger(AwsMetadataCollector.class); private static final String TAG_NAME = "cbname"; @Inject private AwsClient awsClient; @Inject private CloudFormationStackUtil cloudFormationStackUtil; @Inject private SyncPollingScheduler<Boolean> syncPollingScheduler; @Inject private AwsPollTaskFactory awsPollTaskFactory; @Override public List<CloudVmMetaDataStatus> collect(AuthenticatedContext ac, List<CloudResource> resources, List<CloudInstance> vms) { List<CloudVmMetaDataStatus> cloudVmMetaDataStatuses = new ArrayList<>(); try { String region = ac.getCloudContext().getLocation().getRegion().value(); AmazonCloudFormationClient amazonCFClient = awsClient.createCloudFormationClient(new AwsCredentialView(ac.getCloudCredential()), region); AmazonAutoScalingClient amazonASClient = awsClient.createAutoScalingClient(new AwsCredentialView(ac.getCloudCredential()), region); AmazonEC2Client amazonEC2Client = awsClient.createAccess(new AwsCredentialView(ac.getCloudCredential()), region); //contains all instances ListMultimap<String, CloudInstance> groupByInstanceGroup = groupByInstanceGroup(vms); for (String key : groupByInstanceGroup.keySet()) { List<CloudInstance> cloudInstances = groupByInstanceGroup.get(key); cloudVmMetaDataStatuses.addAll(collectGroupMetaData(ac, amazonASClient, amazonEC2Client, amazonCFClient, key, cloudInstances)); } return cloudVmMetaDataStatuses; } catch (Exception e) { throw new CloudConnectorException(e.getMessage(), e); } } private List<CloudVmMetaDataStatus> collectGroupMetaData(AuthenticatedContext ac, AmazonAutoScalingClient amazonASClient, AmazonEC2Client amazonEC2Client, AmazonCloudFormationClient amazonCFClient, String groupName, List<CloudInstance> cloudInstances) { List<CloudVmMetaDataStatus> cloudVmMetaDataStatuses = new ArrayList<>(); String asGroupName = cloudFormationStackUtil.getAutoscalingGroupName(ac, amazonCFClient, groupName); List<String> instanceIds = cloudFormationStackUtil.getInstanceIds(amazonASClient, asGroupName); DescribeInstancesRequest instancesRequest = cloudFormationStackUtil.createDescribeInstancesRequest(instanceIds); DescribeInstancesResult instancesResult = amazonEC2Client.describeInstances(instancesRequest); //contains instances with instanceId Map<String, CloudInstance> mapByInstanceId = mapByInstanceId(cloudInstances); //contains instances with privateId (without instanceId) Queue<CloudInstance> untrackedInstances = untrackedInstances(cloudInstances); for (Reservation reservation : instancesResult.getReservations()) { LOGGER.info("Number of instances found in reservation: {}", reservation.getInstances().size()); for (Instance instance : reservation.getInstances()) { String instanceId = instance.getInstanceId(); CloudInstance cloudInstance = ensureInstanceTag(mapByInstanceId, instance, instanceId, untrackedInstances, amazonEC2Client); if (cloudInstance != null) { CloudInstanceMetaData md = new CloudInstanceMetaData(instance.getPrivateIpAddress(), instance.getPublicIpAddress()); CloudVmInstanceStatus cloudVmInstanceStatus = new CloudVmInstanceStatus(cloudInstance, InstanceStatus.CREATED); CloudVmMetaDataStatus cloudVmMetaDataStatus = new CloudVmMetaDataStatus(cloudVmInstanceStatus, md); cloudVmMetaDataStatuses.add(cloudVmMetaDataStatus); } } } return cloudVmMetaDataStatuses; } private CloudInstance ensureInstanceTag(Map<String, CloudInstance> mapByInstanceId, Instance instance, String instanceId, Queue<CloudInstance> untrackedInstances, AmazonEC2Client amazonEC2Client) { // we need to figure out whether it is already tracked or not, if it is already tracked then it has a tag String tag = getTag(instance); CloudInstance cloudInstance = mapByInstanceId.get(instanceId); if (cloudInstance == null) { if (tag == null) { // so it is not tracked at the moment, therefore it considered as a new instance, and we shall track it by tagging it, with the private id of // an untracked CloudInstance if (untrackedInstances.size() != 0) { cloudInstance = untrackedInstances.remove(); cloudInstance = new CloudInstance(instanceId, cloudInstance.getTemplate()); } } } if (cloudInstance != null && tag == null) { addTag(amazonEC2Client, cloudInstance, instance); } return cloudInstance; } private String getTag(Instance instance) { for (Tag tag : instance.getTags()) { if (TAG_NAME.equals(tag.getKey())) { String value = tag.getValue(); LOGGER.info("Instance: {} was already tagged: {}", instance.getInstanceId(), value); return value; } } return null; } private void addTag(AmazonEC2Client amazonEC2Client, CloudInstance cloudInstance, Instance instance) { String tagName = awsClient.getCbName(cloudInstance.getTemplate().getGroupName(), cloudInstance.getTemplate().getPrivateId()); Tag t = new Tag(); t.setKey(TAG_NAME); t.setValue(tagName); CreateTagsRequest ctr = new CreateTagsRequest(); ctr.setTags(Collections.singletonList(t)); ctr.withResources(instance.getInstanceId()); amazonEC2Client.createTags(ctr); } private ListMultimap<String, CloudInstance> groupByInstanceGroup(List<CloudInstance> vms) { ListMultimap<String, CloudInstance> groupByInstanceGroup = ArrayListMultimap.create(); for (CloudInstance vm : vms) { String groupName = vm.getTemplate().getGroupName(); groupByInstanceGroup.put(groupName, vm); } return groupByInstanceGroup; } private Map<String, CloudInstance> mapByInstanceId(List<CloudInstance> vms) { Map<String, CloudInstance> groupByInstanceId = Maps.newHashMap(); for (CloudInstance vm : vms) { String instanceId = vm.getInstanceId(); if (instanceId != null) { groupByInstanceId.put(instanceId, vm); } } return groupByInstanceId; } private Queue<CloudInstance> untrackedInstances(List<CloudInstance> vms) { Queue<CloudInstance> cloudInstances = Lists.newLinkedList(); for (CloudInstance vm : vms) { if (vm.getInstanceId() == null) { cloudInstances.add(vm); } } return cloudInstances; } }