package io.fathom.cloud.compute.services;
import io.fathom.cloud.CloudException;
import io.fathom.cloud.compute.state.ComputeRepository;
import io.fathom.cloud.protobuf.CloudModel.InstanceData;
import io.fathom.cloud.protobuf.CloudModel.SecurityGroupData;
import io.fathom.cloud.protobuf.CloudModel.SecurityGroupRuleData;
import io.fathom.cloud.server.auth.Auth;
import io.fathom.cloud.server.model.Project;
import io.fathom.cloud.state.NumberedItemCollection;
import io.fathom.cloud.state.StateStoreException;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response.Status;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.persist.Transactional;
@Transactional
@Singleton
public class SecurityGroups {
@Inject
ComputeRepository repository;
@Inject
AsyncTasks asyncTasks;
@Inject
ComputeServices computeServices;
protected NumberedItemCollection<SecurityGroupData> getStore(Project project) throws CloudException {
return repository.getSecurityGroups(project.getId());
}
public SecurityGroupData find(Project project, long id) throws CloudException {
return getStore(project).find(id);
}
// public SecurityGroupData update(Project project,
// SecurityGroupData.Builder securityGroup) throws CloudException {
// SecurityGroupData updated = getStore(project).update(securityGroup);
//
// applySecurityGroup(project, updated);
//
// return updated;
// }
private void applySecurityGroup(Auth auth, Project project, SecurityGroupData securityGroup) throws CloudException {
long securityGroupId = securityGroup.getId();
Set<Long> hostIds = Sets.newHashSet();
List<InstanceData> instances = computeServices.listInstances(auth, project);
for (InstanceData instance : instances) {
// TODO: Only if instance alive??
boolean dead = false;
switch (instance.getInstanceState()) {
case STOPPED:
case TERMINATED:
dead = true;
break;
default:
// Apply (even if stopping, because it's still running)
break;
}
if (dead) {
continue;
}
if (instance.getSecurityGroupIdList().contains(securityGroupId)) {
long hostId = instance.getHostId();
hostIds.add(hostId);
}
}
for (long hostId : hostIds) {
asyncTasks.updateSecurityGroupDefinition(project, securityGroup, hostId);
}
}
public List<SecurityGroupData> list(Project project) throws CloudException {
return getStore(project).list();
}
public SecurityGroupData delete(Project project, long id) throws StateStoreException, CloudException {
return getStore(project).delete(id);
}
public List<SecurityGroupData> getSecurityGroups(Project project, InstanceData instance) throws CloudException {
if (instance.getProjectId() != project.getId()) {
throw new IllegalArgumentException();
}
List<SecurityGroupData> securityGroups = Lists.newArrayList();
{
NumberedItemCollection<SecurityGroupData> securityGroupsStore = getStore(project);
for (long securityGroupId : instance.getSecurityGroupIdList()) {
SecurityGroupData securityGroup = securityGroupsStore.find(securityGroupId);
if (securityGroup == null) {
throw new IllegalArgumentException();
}
securityGroups.add(securityGroup);
}
}
return securityGroups;
}
public SecurityGroupRuleData addRule(Auth auth, Project project, long securityGroupId,
SecurityGroupRuleData.Builder sgb) throws CloudException {
NumberedItemCollection<SecurityGroupData> store = getStore(project);
SecurityGroupData securityGroupData = store.find(securityGroupId);
if (securityGroupData == null) {
throw new WebApplicationException(Status.NOT_FOUND);
}
SecurityGroupData.Builder b = SecurityGroupData.newBuilder(securityGroupData);
SecurityGroupRuleData rule = sgb.build();
b.addRules(rule);
SecurityGroupData updated = store.update(b);
applySecurityGroup(auth, project, updated);
return rule;
}
public SecurityGroupData create(Project project, SecurityGroupData.Builder b) throws CloudException {
b.setProjectId(project.getId());
return getStore(project).create(b);
}
public SecurityGroupData deleteRule(Auth auth, Project project, long ruleId) throws CloudException {
// This sort of sucks, because we don't have an index on ruleId
NumberedItemCollection<SecurityGroupData> store = getStore(project);
SecurityGroupData securityGroupData = null;
int ruleIndex = -1;
for (SecurityGroupData g : store.list()) {
for (int i = 0; i < g.getRulesCount(); i++) {
SecurityGroupRuleData r = g.getRules(i);
if (r.getId() == ruleId) {
securityGroupData = g;
ruleIndex = i;
break;
}
}
}
if (securityGroupData == null) {
throw new WebApplicationException(Status.NOT_FOUND);
}
SecurityGroupData.Builder b = SecurityGroupData.newBuilder(securityGroupData);
b.removeRules(ruleIndex);
SecurityGroupData updated = store.update(b);
applySecurityGroup(auth, project, updated);
return updated;
}
public SecurityGroupData find(Project project, String name) throws CloudException {
if (Strings.isNullOrEmpty(name)) {
return null;
}
for (SecurityGroupData sg : getStore(project).list()) {
if (name.equals(sg.getName())) {
return sg;
}
}
return null;
}
public InstanceData addRemoveSecurityGroup(Project project, long instanceId, SecurityGroupData sg, boolean remove)
throws CloudException {
NumberedItemCollection<InstanceData> store = repository.getInstances(project.getId());
InstanceData instance = store.find(instanceId);
if (instance == null) {
throw new IllegalArgumentException();
}
InstanceData.Builder b = InstanceData.newBuilder(instance);
Set<Long> securityGroups = Sets.newHashSet(b.getSecurityGroupIdList());
if (remove) {
securityGroups.remove(sg.getId());
} else {
securityGroups.add(sg.getId());
}
b.clearSecurityGroupId();
b.addAllSecurityGroupId(securityGroups);
InstanceData updated = store.update(b);
asyncTasks.updateInstanceSecurityGroups(project, updated);
return updated;
}
}