package io.fathom.cloud.compute.api.os.resources;
import io.fathom.cloud.CloudException;
import io.fathom.cloud.compute.api.os.model.FloatingIp;
import io.fathom.cloud.compute.api.os.model.FloatingIps;
import io.fathom.cloud.compute.api.os.model.WrappedFloatingIp;
import io.fathom.cloud.compute.networks.VirtualIp;
import io.fathom.cloud.compute.services.IpPools;
import io.fathom.cloud.protobuf.CloudModel.VirtualIpData;
import io.fathom.cloud.protobuf.CloudModel.VirtualIpPoolData;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.inject.Inject;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response.Status;
import com.fathomdb.utils.Hex;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.net.InetAddresses;
@Path("/openstack/compute/{project}/os-floating-ips")
public class FloatingIpsResource extends ComputeResourceBase {
@Inject
IpPools ipPools;
@GET
public FloatingIps list() throws CloudException {
FloatingIps response = new FloatingIps();
response.floatingIps = Lists.newArrayList();
for (VirtualIp vip : ipPools.listVirtualIps(getProject())) {
response.floatingIps.add(toModel(vip));
}
return response;
}
@GET
@Path("{id}")
public WrappedFloatingIp find(@PathParam("id") String id) throws CloudException {
String address = toAddress(id);
VirtualIp vip = ipPools.findVirtualIp(getProject(), address);
if (vip == null) {
throw new WebApplicationException(Status.NOT_FOUND);
}
WrappedFloatingIp response = new WrappedFloatingIp();
response.floatingIp = toModel(vip);
return response;
}
@DELETE
@Path("{id}")
public void delete(@PathParam("id") String id) throws CloudException {
String address = toAddress(id);
VirtualIp vip = ipPools.findVirtualIp(getProject(), address);
if (vip == null) {
throw new WebApplicationException(Status.NOT_FOUND);
}
ipPools.deallocateFloatingIp(getProject(), vip.getData().getIp());
}
@POST
public WrappedFloatingIp allocate(FloatingIp template) throws CloudException {
VirtualIpPoolData pool = findPool(template.pool);
if (pool == null) {
throw new IllegalArgumentException();
}
VirtualIp vip = ipPools.allocateFloatingIp(getProject(), pool);
WrappedFloatingIp response = new WrappedFloatingIp();
response.floatingIp = toModel(vip);
return response;
}
private VirtualIpPoolData findPool(String label) throws CloudException {
if (label == null) {
throw new IllegalArgumentException();
}
for (VirtualIpPoolData poolData : ipPools.listVirtualIpPools(getProject())) {
if (label.equals(getLabel(poolData))) {
return poolData;
}
}
return null;
}
private FloatingIp toModel(VirtualIp vip) {
VirtualIpData data = vip.getData();
FloatingIp model = new FloatingIp();
// model.id = vip.getId();
model.id = toId(vip.getData());
if (data.hasInstanceId()) {
model.instanceId = Long.toString(data.getInstanceId());
}
InetAddress address = InetAddresses.forString(data.getIp());
String ip;
if (address instanceof Inet4Address) {
ip = InetAddresses.toAddrString(address);
} else {
throw new UnsupportedOperationException();
// ip = InetAddresses.toAddrString(address) + "/112";
}
model.ip = ip;
model.pool = getLabel(vip.getPoolData());
return model;
}
public static String toId(VirtualIpData data) {
// We make it look like a UUID
InetAddress address = InetAddresses.forString(data.getIp());
if (address instanceof Inet4Address) {
Inet4Address inet4 = (Inet4Address) address;
byte[] b = new byte[16];
System.arraycopy(inet4.getAddress(), 0, b, 12, 4);
return asUuid(b);
} else {
Inet6Address inet6 = (Inet6Address) address;
byte[] b = new byte[16];
System.arraycopy(inet6.getAddress(), 0, b, 0, 16);
return asUuid(b);
}
}
private String toAddress(String id) {
String s = id.replace("-", "");
byte[] b = Hex.fromHex(s);
if (b.length != 16) {
throw new IllegalArgumentException();
}
boolean ipv4 = true;
for (int i = 0; i < 12; i++) {
if (b[i] != 0) {
ipv4 = false;
break;
}
}
if (ipv4) {
byte[] data = new byte[4];
System.arraycopy(b, 12, data, 0, 4);
Inet4Address addr;
try {
addr = (Inet4Address) InetAddress.getByAddress(data);
} catch (UnknownHostException e) {
throw new IllegalArgumentException();
}
return addr.getHostAddress();
} else {
Inet6Address addr;
try {
addr = (Inet6Address) InetAddress.getByAddress(b);
} catch (UnknownHostException e) {
throw new IllegalArgumentException();
}
return addr.getHostAddress();
}
}
private static String asUuid(byte[] b) {
return Hex.toHex(b, 0, 4) + "-" + Hex.toHex(b, 4, 2) + "-" + Hex.toHex(b, 6, 2) + "-" + Hex.toHex(b, 8, 2)
+ "-" + Hex.toHex(b, 10, 6);
}
public static String getLabel(VirtualIpPoolData pool) {
String name = pool.getLabel();
if (Strings.isNullOrEmpty(name)) {
if (pool.hasCidr()) {
name = "CIDR " + pool.getCidr();
} else if (pool.hasType()) {
switch (pool.getType()) {
case AMAZON_EC2:
name = "AWS Elastic IPs";
break;
default:
name = pool.getType().name().toLowerCase();
break;
}
} else {
throw new IllegalStateException();
}
}
return name;
}
}