package io.cattle.platform.process.externalevent;
import static io.cattle.platform.core.constants.ExternalEventConstants.*;
import static io.cattle.platform.core.model.tables.AgentTable.*;
import static io.cattle.platform.core.model.tables.StackTable.*;
import static io.cattle.platform.core.model.tables.ServiceTable.*;
import io.cattle.platform.core.constants.CommonStatesConstants;
import io.cattle.platform.core.constants.ExternalEventConstants;
import io.cattle.platform.core.constants.VolumeConstants;
import io.cattle.platform.core.dao.AccountDao;
import io.cattle.platform.core.dao.GenericResourceDao;
import io.cattle.platform.core.dao.HostDao;
import io.cattle.platform.core.dao.StoragePoolDao;
import io.cattle.platform.core.dao.VolumeDao;
import io.cattle.platform.core.model.Agent;
import io.cattle.platform.core.model.Stack;
import io.cattle.platform.core.model.ExternalEvent;
import io.cattle.platform.core.model.Host;
import io.cattle.platform.core.model.Service;
import io.cattle.platform.core.model.StoragePool;
import io.cattle.platform.core.model.StoragePoolHostMap;
import io.cattle.platform.core.model.Volume;
import io.cattle.platform.docker.process.lock.DockerStoragePoolVolumeCreateLock;
import io.cattle.platform.engine.handler.HandlerResult;
import io.cattle.platform.engine.process.ProcessInstance;
import io.cattle.platform.engine.process.ProcessState;
import io.cattle.platform.engine.process.impl.ProcessCancelException;
import io.cattle.platform.lock.LockCallbackNoReturn;
import io.cattle.platform.lock.LockManager;
import io.cattle.platform.object.meta.ObjectMetaDataManager;
import io.cattle.platform.object.process.StandardProcess;
import io.cattle.platform.object.util.DataAccessor;
import io.cattle.platform.object.util.DataUtils;
import io.cattle.platform.object.util.ObjectUtils;
import io.cattle.platform.process.base.AbstractDefaultProcessHandler;
import io.cattle.platform.util.type.CollectionUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Named
public class ExternalEventCreate extends AbstractDefaultProcessHandler {
public static final String FIELD_AGENT_ID = "agentId";
private static final Logger log = LoggerFactory.getLogger(ExternalEventCreate.class);
@Inject
AccountDao accountDao;
@Inject
LockManager lockManager;
@Inject
GenericResourceDao resourceDao;
@Inject
StoragePoolDao storagePoolDao;
@Inject
VolumeDao volumeDao;
@Inject
HostDao hostDao;
@Override
public HandlerResult handle(ProcessState state, ProcessInstance process) {
ExternalEvent event = (ExternalEvent)state.getResource();
if (StringUtils.isEmpty(event.getExternalId())) {
log.debug("External event doesn't have an external id: {}", event.getId());
return null;
}
if (ExternalEventConstants.KIND_VOLUME_EVENT.equals(event.getKind())) {
handleVolumeEvent(event, state, process);
} else if (ExternalEventConstants.KIND_STORAGE_POOL_EVENT.equals(event.getKind())) {
handleStoragePoolEvent(event, state, process);
} else if (ExternalEventConstants.KIND_EXTERNAL_DNS_EVENT.equals(event.getKind())) {
handleExternalDnsEvent(event, state, process);
}
return null;
}
// TODO We don't use this logic. Consider completely removing it.
protected void handleVolumeEvent(final ExternalEvent event, ProcessState state, ProcessInstance process) {
//new DockerStoragePoolVolumeCreateLock(storagePool, dVol.getUri())
Object driver = CollectionUtils.getNestedValue(DataUtils.getFields(event), FIELD_VOLUME, VolumeConstants.FIELD_VOLUME_DRIVER);
final Object name = CollectionUtils.getNestedValue(DataUtils.getFields(event), FIELD_VOLUME, FIELD_NAME);
if (driver == null || name == null) {
log.warn("Driver or volume name not specified. Returning. Event: {}", event);
return;
}
String driverName = driver.toString();
List<? extends StoragePool> pools = storagePoolDao.findStoragePoolByDriverName(event.getAccountId(), driverName);
if (pools.size() == 0) {
log.warn("Unknown storage pool. Returning. Driver name: {}", driverName);
return;
}
final StoragePool storagePool = pools.get(0);
lockManager.lock(new DockerStoragePoolVolumeCreateLock(storagePool, event.getExternalId()), new LockCallbackNoReturn() {
@Override
public void doWithLockNoResult() {
Volume volume = volumeDao.findVolumeByExternalId(storagePool.getId(), event.getExternalId());
switch (event.getEventType()) {
case ExternalEventConstants.TYPE_VOLUME_CREATE:
if (volume == null) {
Map<String, Object> volumeData = CollectionUtils.toMap(DataUtils.getFields(event).get(FIELD_VOLUME));
if (volumeData.isEmpty()) {
log.warn("Empty volume for externalVolumeEvent: {}. StoragePool: {}", event, volumeData);
return;
}
volumeData.put(ObjectMetaDataManager.ACCOUNT_FIELD, event.getAccountId());
volumeData.put(FIELD_ATTACHED_STATE, CommonStatesConstants.INACTIVE);
volumeData.put(FIELD_ALLOC_STATE, CommonStatesConstants.ACTIVE);
volumeData.put(FIELD_ZONE_ID, 1L);
volumeData.put(FIELD_DEV_NUM, -1);
try {
volumeDao.createVolumeInStoragePool(volumeData, name.toString(), storagePool);
} catch (ProcessCancelException e) {
log.info("Create process cancelled for volumeData {}. ProcessCancelException message: {}", volumeData, e.getMessage());
}
}
break;
default:
log.error("Unknown event type: {} for event {}", event.getEventType(), event);
return;
}
}
});
}
protected void handleStoragePoolEvent(final ExternalEvent event, ProcessState state, ProcessInstance process) {
lockManager.lock(new ExternalEventLock(STORAGE_POOL_LOCK_NAME, event.getAccountId(), event.getExternalId()), new LockCallbackNoReturn() {
@Override
public void doWithLockNoResult() {
Object driver = CollectionUtils.getNestedValue(DataUtils.getFields(event), FIELD_STORAGE_POOL, FIELD_DRIVER_NAME);
if (driver == null) {
log.warn("Driver not specified. Returning. Event: {}", ObjectUtils.toStringWrapper(event));
return;
}
String driverName = driver.toString();
List<? extends StoragePool> pools = storagePoolDao.findStoragePoolByDriverName(event.getAccountId(), driverName);
StoragePool storagePool = pools.size() > 0 ? pools.get(0) : null;
Map<String, Object> spData = CollectionUtils.toMap(DataUtils.getFields(event).get(FIELD_STORAGE_POOL));
if (spData.isEmpty()) {
log.warn("Null or empty storagePool for externalStoragePoolEvent: {}. StoragePool: {}", event, spData);
return;
}
if (storagePool == null) {
spData.put(ObjectMetaDataManager.ACCOUNT_FIELD, event.getAccountId());
spData.put(FIELD_ZONE_ID, 1L);
Agent agent = objectManager.findOne(Agent.class, AGENT.ACCOUNT_ID, event.getReportedAccountId(),
AGENT.STATE, CommonStatesConstants.ACTIVE);
spData.put(FIELD_AGENT_ID, agent.getId());
try {
storagePool = resourceDao.createAndSchedule(StoragePool.class, spData);
} catch (ProcessCancelException e) {
log.info("Create process cancelled for storagePool {}. ProcessCancelException message: {}", storagePool, e.getMessage());
}
} else {
Agent agent = objectManager.findOne(Agent.class, AGENT.ACCOUNT_ID, event.getReportedAccountId(),
AGENT.STATE, CommonStatesConstants.ACTIVE);
spData.put(FIELD_AGENT_ID, agent.getId());
storagePool.setAgentId(agent.getId());
try {
storagePool = resourceDao.updateAndSchedule(storagePool);
} catch (ProcessCancelException e) {
log.info("Create process cancelled for storagePool {}. ProcessCancelException message: {}", storagePool, e.getMessage());
}
}
List<String> hostUuids = new ArrayList<String>();
for (Object item : CollectionUtils.toList(DataUtils.getFields(event).get(FIELD_HOST_UUIDS))) {
if (item != null)
hostUuids.add(item.toString());
}
Map<Long, StoragePoolHostMap> maps = constructStoragePoolMaps(storagePool, hostUuids);
try {
removeOldMaps(storagePool, maps);
createNewMaps(storagePool, maps);
} catch (ProcessCancelException e) {
log.info("Process cancelled while syncing volumes to storagePool {}. ProcessCancelException message: {}", storagePool, e.getMessage());
}
}
});
}
protected void handleExternalDnsEvent(final ExternalEvent event, ProcessState state, ProcessInstance process) {
lockManager.lock(new ExternalEventLock(EXERNAL_DNS_LOCK_NAME, event.getAccountId(), event.getExternalId()),
new LockCallbackNoReturn() {
@Override
public void doWithLockNoResult() {
String fqdn = DataAccessor.fieldString(event, ExternalEventConstants.FIELD_FQDN);
String serviceName = DataAccessor
.fieldString(event, ExternalEventConstants.FIELD_SERVICE_NAME);
String stackName = DataAccessor.fieldString(event, ExternalEventConstants.FIELD_STACK_NAME);
if (fqdn == null || serviceName == null || stackName == null) {
log.info("External DNS [event: " + event.getId() + "] misses some fields");
return;
}
Stack stack = objectManager.findAny(Stack.class, STACK.ACCOUNT_ID,
event.getAccountId(), STACK.REMOVED, null, STACK.NAME, stackName);
if (stack == null) {
log.info("Stack not found for external DNS [event: " + event.getId() + "]");
return;
}
Service service = objectManager.findAny(Service.class, SERVICE.ACCOUNT_ID,
event.getAccountId(), SERVICE.REMOVED, null, SERVICE.STACK_ID, stack.getId(),
SERVICE.NAME, serviceName);
if (service == null) {
log.info("Service not found for external DNS [event: " + event.getId() + "]");
return;
}
Map<String, Object> data = new HashMap<>();
data.put(ExternalEventConstants.FIELD_FQDN, fqdn);
DataUtils.getWritableFields(service).putAll(data);
objectManager.persist(service);
objectProcessManager.scheduleStandardProcessAsync(StandardProcess.UPDATE, service, data);
}
});
}
protected void createNewMaps(StoragePool storagePool, Map<Long, StoragePoolHostMap> maps) {
for (StoragePoolHostMap m : maps.values()) {
storagePoolDao.createStoragePoolHostMap(m);
}
}
protected void removeOldMaps(StoragePool storagePool, Map<Long, StoragePoolHostMap> newMaps) {
List<? extends StoragePoolHostMap> existingMaps = storagePoolDao.findMapsToRemove(storagePool.getId());
List<StoragePoolHostMap> toRemove = new ArrayList<StoragePoolHostMap>();
for (StoragePoolHostMap m : existingMaps) {
if (!newMaps.containsKey(m.getHostId())) {
toRemove.add(m);
}
}
for (StoragePoolHostMap m : toRemove) {
StoragePoolHostMap remove = storagePoolDao.findNonremovedMap(m.getStoragePoolId(), m.getHostId());
if (remove != null) {
objectProcessManager.scheduleStandardProcess(StandardProcess.REMOVE, remove, null);
}
}
}
protected Map<Long, StoragePoolHostMap> constructStoragePoolMaps(StoragePool storagePool, List<String> hostUuids) {
List<? extends Host> hosts = hostDao.getHosts(storagePool.getAccountId(), hostUuids);
Map<Long, StoragePoolHostMap> maps = new HashMap<Long, StoragePoolHostMap>();
for (Host h : hosts) {
StoragePoolHostMap sphm = objectManager.newRecord(StoragePoolHostMap.class);
sphm.setHostId(h.getId());
sphm.setStoragePoolId(storagePool.getId());
maps.put(h.getId(), sphm);
}
return maps;
}
}