package io.fathom.cloud.compute.actions.network; import io.fathom.cloud.CloudException; import io.fathom.cloud.compute.networks.VirtualIp; import io.fathom.cloud.compute.scheduler.SchedulerHost; import io.fathom.cloud.compute.services.DatacenterManager; import io.fathom.cloud.compute.services.Ec2DatacenterManager; import io.fathom.cloud.protobuf.CloudModel.InstanceData; import java.util.Collections; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.AssignPrivateIpAddressesRequest; import com.amazonaws.services.ec2.model.AssociateAddressRequest; import com.amazonaws.services.ec2.model.AssociateAddressResult; 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.InstanceNetworkInterface; import com.amazonaws.services.ec2.model.InstanceNetworkInterfaceAssociation; import com.amazonaws.services.ec2.model.InstanceNetworkInterfaceAttachment; import com.amazonaws.services.ec2.model.InstancePrivateIpAddress; import com.amazonaws.services.ec2.model.NetworkInterface; import com.amazonaws.services.ec2.model.Reservation; import com.amazonaws.services.ec2.model.Tag; import com.google.common.collect.Lists; public class Ec2VirtualIpMapper extends VirtualIpMapper { private static final Logger log = LoggerFactory.getLogger(Ec2VirtualIpMapper.class); // void go() { // AmazonEC2Client ec2; // // { // DescribeAddressesRequest request = new DescribeAddressesRequest(); // DescribeAddressesResult response = ec2.describeAddresses(request); // for (Address address : response.getAddresses()) { // if ("vpc".equalsIgnoreCase(address.getDomain())) { // } else if ("standard".equalsIgnoreCase(address.getDomain())) { // } else { // log.warn("Unknown address type: {}", address); // } // } // } // // { // AllocateAddressRequest request = new AllocateAddressRequest(); // request.setDomain(DomainType.Vpc); // AllocateAddressResult response = ec2.allocateAddress(request); // String publicIp = response.getPublicIp(); // } // // // } @Override public String mapIp(SchedulerHost host, InstanceData guest, VirtualIp vip) throws CloudException { DatacenterManager datacenterManager = host.getDatacenterManager(); if (!(datacenterManager instanceof Ec2DatacenterManager)) { throw new IllegalStateException("Expected EC2 datacenter manager, found: " + datacenterManager); } Ec2DatacenterManager manager = (Ec2DatacenterManager) datacenterManager; AmazonEC2Client ec2 = manager.getEc2Client(host); String ec2InstanceId = manager.findHost(host); if (ec2InstanceId == null) { throw new IllegalStateException("Unable to find EC2 instance for host: " + host); } Instance ec2Instance = describeInstance(ec2, ec2InstanceId); String subnetId = ec2Instance.getSubnetId(); // Use the default network interface int networkInterfaceIndex = 0; InstanceNetworkInterface networkInterface = findNetworkInterface(ec2Instance, networkInterfaceIndex); if (networkInterface == null) { throw new UnsupportedOperationException(); /* * // TODO: Reuse unattached network interfaces (with the * fathomcloud // tag)??? * * NetworkInterface created; { CreateNetworkInterfaceRequest request * = new CreateNetworkInterfaceRequest(); * request.setSubnetId(subnetId); * * CreateNetworkInterfaceResult response = * ec2.createNetworkInterface(request); created = * response.getNetworkInterface(); * log.info("Created network interface {}", * created.getNetworkInterfaceId()); } * * addTag(ec2, created, "fathomcloud", "1"); * * { AttachNetworkInterfaceRequest request = new * AttachNetworkInterfaceRequest(); request.setDeviceIndex(1); * request.setInstanceId(ec2InstanceId); * request.setNetworkInterfaceId(created.getNetworkInterfaceId()); * * AttachNetworkInterfaceResult response = * ec2.attachNetworkInterface(request); * log.info("Attached network interface as {}", * response.getAttachmentId()); } * * ec2Instance = describeInstance(ec2, ec2InstanceId); * * networkInterface = findNetworkInterface(ec2Instance, 1); * * if (networkInterface == null) { throw new IllegalStateException( * "Did not find network interface after attaching: " + * created.getNetworkInterfaceId()); } */ } InstancePrivateIpAddress privateIp = findUnusedIp(networkInterface); if (privateIp == null) { // TODO: Prune private ip addresses from NICs? // TODO: Need to tag?? { AssignPrivateIpAddressesRequest request = new AssignPrivateIpAddressesRequest(); request.setNetworkInterfaceId(networkInterface.getNetworkInterfaceId()); request.setSecondaryPrivateIpAddressCount(1); ec2.assignPrivateIpAddresses(request); } ec2Instance = describeInstance(ec2, ec2InstanceId); networkInterface = findNetworkInterface(ec2Instance, networkInterfaceIndex); privateIp = findUnusedIp(networkInterface); if (privateIp == null) { throw new IllegalStateException("Unable to find private IP address"); } } String privateIpAddress = privateIp.getPrivateIpAddress(); { AssociateAddressRequest request = new AssociateAddressRequest(); request.setPublicIp(vip.getData().getIp()); request.setPrivateIpAddress(privateIpAddress); request.setNetworkInterfaceId(networkInterface.getNetworkInterfaceId()); request.setInstanceId(ec2InstanceId); AssociateAddressResult response = ec2.associateAddress(request); log.info("Associated public IP with assocation id: {}", response.getAssociationId()); } return privateIpAddress; } private void addTag(AmazonEC2Client ec2, NetworkInterface o, String key, String value) { addTag(ec2, o.getNetworkInterfaceId(), key, value); } private void addTag(AmazonEC2Client ec2, String id, String key, String value) { Tag tag = new Tag(key, value); List<Tag> tags = Lists.newArrayList(); tags.add(tag); CreateTagsRequest request = new CreateTagsRequest(); request.setResources(Collections.singletonList(id)); request.setTags(tags); ec2.createTags(request); log.info("Added tag: {}={} to {}", key, value, id); } private InstancePrivateIpAddress findUnusedIp(InstanceNetworkInterface networkInterface) { InstanceNetworkInterfaceAssociation unused = null; for (InstancePrivateIpAddress privateIpAddress : networkInterface.getPrivateIpAddresses()) { InstanceNetworkInterfaceAssociation association = privateIpAddress.getAssociation(); if (association == null) { return privateIpAddress; } } return null; } private InstanceNetworkInterface findNetworkInterface(Instance ec2Instance, int deviceIndex) { InstanceNetworkInterface networkInterface = null; for (InstanceNetworkInterface i : ec2Instance.getNetworkInterfaces()) { InstanceNetworkInterfaceAttachment attachment = i.getAttachment(); if (attachment == null) { log.error("EC2 network attachment on instance was null"); continue; } Integer attachmentDeviceIndex = attachment.getDeviceIndex(); if (attachmentDeviceIndex == null) { log.error("EC2 device index was null"); continue; } if (attachmentDeviceIndex.intValue() == deviceIndex) { if (networkInterface != null) { throw new IllegalStateException(); } networkInterface = i; } } return networkInterface; } private Instance describeInstance(AmazonEC2Client ec2, String ec2InstanceId) { Instance ec2Instance = null; { DescribeInstancesRequest request = new DescribeInstancesRequest(); request.setInstanceIds(Collections.singletonList(ec2InstanceId)); DescribeInstancesResult response = ec2.describeInstances(request); List<Reservation> reservations = response.getReservations(); for (Reservation reservation : reservations) { for (Instance i : reservation.getInstances()) { if (ec2Instance != null) { throw new IllegalStateException(); } ec2Instance = i; } } if (ec2Instance == null) { throw new IllegalStateException("EC2 instance not found: " + ec2InstanceId); } } return ec2Instance; } @Override public void unmapIp(SchedulerHost host, InstanceData instance, VirtualIp vip) throws CloudException { } }