package io.fathom.cloud.compute.actions;
import io.fathom.cloud.CloudException;
import io.fathom.cloud.compute.networks.VirtualIp;
import io.fathom.cloud.compute.scheduler.SchedulerHost;
import io.fathom.cloud.protobuf.CloudModel.InstanceData;
import io.fathom.cloud.protobuf.CloudModel.NetworkAddressData;
import io.fathom.cloud.protobuf.CloudModel.VirtualIpData;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.List;
import com.google.common.collect.Lists;
import com.google.common.net.InetAddresses;
public class ConfigureVirtualIp {
private final ApplydContext applyd;
private final SchedulerHost host;
public ConfigureVirtualIp(SchedulerHost host, ApplydContext applyd) {
super();
this.host = host;
this.applyd = applyd;
}
public boolean updateConfig(InstanceData instance, VirtualIp vip, String hostIp) throws CloudException {
boolean dirty = false;
String key = getKey(vip);
dirty |= applyd.updateConfig("iptables/50-" + "os-compute-vip-" + key,
buildIptables(instance, vip.getData(), hostIp, false));
// dirty |= applyd.updateConfig("arp/os-compute-vip-" + key,
// buildArp(instance, vip, true));
dirty |= applyd
.updateConfig("vips/" + vip.getData().getIp(), buildIpAssignment(instance, vip.getData(), false));
return dirty;
}
private String getKey(VirtualIp vip) {
return vip.getPoolData().getId() + "-" + vip.getData().getIp().replace('.', '_').replace(':', '_');
}
public boolean removeConfig(VirtualIp vip) throws CloudException {
boolean dirty = false;
String key = getKey(vip);
dirty |= applyd.removeConfig("iptables/50-" + "os-compute-vip-" + key);
// dirty |= applyd.removeConfig("arp/os-compute-vip-" + key);
dirty |= applyd.updateConfig("vips/" + vip.getData().getIp(), "");
return dirty;
}
private String buildIptables(InstanceData instance, VirtualIpData vipData, String hostIp, boolean ipv6) {
StringBuilder conf = new StringBuilder();
List<String> ips = Lists.newArrayList();
for (NetworkAddressData addressInfo : instance.getNetwork().getAddressesList()) {
InetAddress address = InetAddresses.forString(addressInfo.getIp());
boolean isIpv6 = address instanceof Inet6Address;
if (isIpv6 != ipv6) {
continue;
}
ips.add(InetAddresses.toAddrString(address));
}
if (ips.size() == 0) {
throw new IllegalStateException("Cannot find any IPs for VIP redirect");
}
if (ips.size() != 1) {
throw new IllegalStateException("Found multiple IPs for VIP redirect");
}
String ip = ips.get(0);
conf.append("*nat\n");
// We remap all traffic, replacing the default NAT rule
// We do this because otherwise we have to be stateful, which sucks
conf.append(String.format("-A PREROUTING -d %s -j DNAT --to-destination %s\n", hostIp, ip));
conf.append(String.format("-A POSTROUTING -s %s -j SNAT --to-source %s\n", ip, hostIp));
conf.append("COMMIT\n");
return conf.toString();
}
private String buildIpAssignment(InstanceData instance, VirtualIpData vipData, boolean ipv6) {
StringBuilder conf = new StringBuilder();
String networkDevice = host.getNetworkDevice();
String vip = vipData.getIp();
// TODO: We should try to make sure that applyd doesn't do this on
// reboot without double-checking (or at least doesn't do an ARP
// broadcast!)
// conf.append(String.format("ip addr add %s/32 dev %s\n", vip,
// networkDevice));
conf.append(String.format("%s %s\n", networkDevice, vip));
return conf.toString();
}
}