package io.cattle.platform.allocator.dao.impl;
import static io.cattle.platform.core.model.tables.AgentTable.*;
import static io.cattle.platform.core.model.tables.HostLabelMapTable.*;
import static io.cattle.platform.core.model.tables.HostTable.*;
import static io.cattle.platform.core.model.tables.ImageStoragePoolMapTable.*;
import static io.cattle.platform.core.model.tables.ImageTable.*;
import static io.cattle.platform.core.model.tables.InstanceHostMapTable.*;
import static io.cattle.platform.core.model.tables.InstanceLabelMapTable.*;
import static io.cattle.platform.core.model.tables.InstanceTable.*;
import static io.cattle.platform.core.model.tables.LabelTable.*;
import static io.cattle.platform.core.model.tables.MountTable.*;
import static io.cattle.platform.core.model.tables.PortTable.*;
import static io.cattle.platform.core.model.tables.ServiceExposeMapTable.*;
import static io.cattle.platform.core.model.tables.StorageDriverTable.*;
import static io.cattle.platform.core.model.tables.StoragePoolHostMapTable.*;
import static io.cattle.platform.core.model.tables.StoragePoolTable.*;
import static io.cattle.platform.core.model.tables.VolumeStoragePoolMapTable.*;
import io.cattle.platform.allocator.dao.AllocatorDao;
import io.cattle.platform.allocator.exception.FailedToAllocate;
import io.cattle.platform.allocator.service.AllocationAttempt;
import io.cattle.platform.allocator.service.AllocationCandidate;
import io.cattle.platform.allocator.util.AllocatorUtils;
import io.cattle.platform.core.constants.AgentConstants;
import io.cattle.platform.core.constants.CommonStatesConstants;
import io.cattle.platform.core.constants.HealthcheckConstants;
import io.cattle.platform.core.constants.InstanceConstants;
import io.cattle.platform.core.constants.StorageDriverConstants;
import io.cattle.platform.core.constants.VolumeConstants;
import io.cattle.platform.core.dao.GenericMapDao;
import io.cattle.platform.core.model.Host;
import io.cattle.platform.core.model.Instance;
import io.cattle.platform.core.model.InstanceHostMap;
import io.cattle.platform.core.model.Port;
import io.cattle.platform.core.model.StorageDriver;
import io.cattle.platform.core.model.StoragePool;
import io.cattle.platform.core.model.Volume;
import io.cattle.platform.core.model.VolumeStoragePoolMap;
import io.cattle.platform.core.model.tables.records.HostRecord;
import io.cattle.platform.core.model.tables.records.InstanceRecord;
import io.cattle.platform.core.model.tables.records.PortRecord;
import io.cattle.platform.core.model.tables.records.StoragePoolRecord;
import io.cattle.platform.db.jooq.dao.impl.AbstractJooqDao;
import io.cattle.platform.object.ObjectManager;
import io.cattle.platform.object.util.DataAccessor;
import io.github.ibuildthecloud.gdapi.condition.ConditionType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import org.apache.commons.lang.StringUtils;
import org.jooq.Condition;
import org.jooq.Field;
import org.jooq.Record;
import org.jooq.Record1;
import org.jooq.Record3;
import org.jooq.RecordHandler;
import org.jooq.Result;
import org.jooq.SelectConditionStep;
import org.jooq.exception.InvalidResultException;
import org.jooq.impl.DSL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AllocatorDaoImpl extends AbstractJooqDao implements AllocatorDao {
private static final Logger log = LoggerFactory.getLogger(AllocatorDaoImpl.class);
private static final String ALLOCATED_IP = "allocatedIP";
private static final String PROTOCOL = "protocol";
private static final String PRIVATE_PORT = "privatePort";
private static final String PUBLIC_PORT = "publicPort";
private static final String INSTANCE_ID = "instanceID";
private static final String ALLOCATED_IPS = "allocatedIPs";
private static final String BIND_ADDRESS = "bindAddress";
private static final List<Field<?>> hostAndPortFields;
static {
hostAndPortFields = new ArrayList<Field<?>>(Arrays.asList(PORT.fields()));
hostAndPortFields.add(INSTANCE_HOST_MAP.HOST_ID);
}
static final List<String> IHM_STATES = Arrays.asList(new String[] { CommonStatesConstants.INACTIVE, CommonStatesConstants.DEACTIVATING,
CommonStatesConstants.REMOVED, CommonStatesConstants.REMOVING, CommonStatesConstants.PURGING, CommonStatesConstants.PURGED });
@Inject
ObjectManager objectManager;
@Inject
GenericMapDao mapDao;
@Override
public boolean isInstanceImageKind(long instanceId, String kind) {
return create().select(STORAGE_POOL.fields())
.from(STORAGE_POOL)
.join(IMAGE_STORAGE_POOL_MAP)
.on(STORAGE_POOL.ID.eq(IMAGE_STORAGE_POOL_MAP.STORAGE_POOL_ID))
.join(IMAGE)
.on(IMAGE.ID.eq(IMAGE_STORAGE_POOL_MAP.IMAGE_ID))
.join(INSTANCE)
.on(INSTANCE.IMAGE_ID.eq(IMAGE.ID))
.where(
INSTANCE.ID.eq(instanceId)
.and(IMAGE_STORAGE_POOL_MAP.REMOVED.isNull())
.and(STORAGE_POOL.KIND.eq(kind)))
.fetch().size() > 0;
}
@Override
public boolean isVolumeInstanceImageKind(long volumeId, String kind) {
Volume volume = objectManager.loadResource(Volume.class, volumeId);
Long instanceId = volume.getInstanceId();
return instanceId == null ? false : isInstanceImageKind(instanceId, kind);
}
@Override
public List<? extends StoragePool> getAssociatedPools(Volume volume) {
return create()
.select(STORAGE_POOL.fields())
.from(STORAGE_POOL)
.join(VOLUME_STORAGE_POOL_MAP)
.on(VOLUME_STORAGE_POOL_MAP.STORAGE_POOL_ID.eq(STORAGE_POOL.ID))
.where(
VOLUME_STORAGE_POOL_MAP.REMOVED.isNull()
.and(VOLUME_STORAGE_POOL_MAP.VOLUME_ID.eq(volume.getId())))
.fetchInto(StoragePoolRecord.class);
}
@Override
public List<? extends StoragePool> getAssociatedUnmanagedPools(Host host) {
return create()
.select(STORAGE_POOL.fields())
.from(STORAGE_POOL)
.join(STORAGE_POOL_HOST_MAP)
.on(STORAGE_POOL_HOST_MAP.STORAGE_POOL_ID.eq(STORAGE_POOL.ID))
.where(
STORAGE_POOL_HOST_MAP.REMOVED.isNull()
.and(STORAGE_POOL_HOST_MAP.HOST_ID.eq(host.getId())
.and(STORAGE_POOL.KIND.in(AllocatorUtils.UNMANGED_STORAGE_POOLS))))
.fetchInto(StoragePoolRecord.class);
}
@Override
public Host getHost(Instance instance) {
Condition cond = getInstanceHostConstraint(instance);
try {
return create()
.selectDistinct(HOST.fields())
.from(HOST)
.join(INSTANCE_HOST_MAP)
.on(INSTANCE_HOST_MAP.HOST_ID.eq(HOST.ID))
.join(INSTANCE)
.on(INSTANCE_HOST_MAP.INSTANCE_ID.eq(INSTANCE.ID))
.leftOuterJoin(SERVICE_EXPOSE_MAP)
.on(INSTANCE.ID.eq(SERVICE_EXPOSE_MAP.INSTANCE_ID))
.where(
INSTANCE_HOST_MAP.REMOVED.isNull()
.and(HOST.REMOVED.isNull())
.and(INSTANCE.REMOVED.isNull())
.and(SERVICE_EXPOSE_MAP.REMOVED.isNull())
.and(SERVICE_EXPOSE_MAP.UPGRADE.isNull().or(SERVICE_EXPOSE_MAP.UPGRADE.eq(false)))
.and(cond))
.fetchOneInto(HostRecord.class);
} catch (InvalidResultException e) {
throw new FailedToAllocate("Instances to allocate assigned to different hosts.");
}
}
public Condition getInstanceHostConstraint(Instance instance) {
if (StringUtils.isEmpty(instance.getDeploymentUnitUuid())) {
return INSTANCE_HOST_MAP.INSTANCE_ID.eq(instance.getId());
} else {
return INSTANCE.DEPLOYMENT_UNIT_UUID.eq(instance.getDeploymentUnitUuid());
}
}
@Override
public List<? extends Host> getHosts(Collection<? extends StoragePool> pools) {
if (pools == null || pools.isEmpty()) {
return new ArrayList<>();
}
Collection<Long> poolIds = new HashSet<>();
for (StoragePool p : pools) {
poolIds.add(p.getId());
}
return create()
.select(HOST.fields())
.from(HOST)
.join(STORAGE_POOL_HOST_MAP)
.on(STORAGE_POOL_HOST_MAP.HOST_ID.eq(HOST.ID))
.where(STORAGE_POOL_HOST_MAP.REMOVED.isNull()
.and(STORAGE_POOL_HOST_MAP.STORAGE_POOL_ID.in(poolIds)))
.fetchInto(HostRecord.class);
}
@Override
public boolean recordCandidate(AllocationAttempt attempt, AllocationCandidate candidate) {
Long newHost = candidate.getHost();
if (newHost != null) {
for (Instance instance : attempt.getInstances()) {
log.info("Associating instance [{}] to host [{}]", instance.getId(), newHost);
objectManager.create(InstanceHostMap.class,
INSTANCE_HOST_MAP.HOST_ID, newHost,
INSTANCE_HOST_MAP.INSTANCE_ID, instance.getId());
}
updateVolumeHostInfo(attempt, candidate, newHost);
}
Map<Long, Set<Long>> existingPools = attempt.getPoolIds();
Map<Long, Set<Long>> newPools = candidate.getPools();
if (!existingPools.keySet().equals(newPools.keySet())) {
throw new IllegalStateException(String.format("Volumes don't match. currently %s, new %s", existingPools.keySet(), newPools.keySet()));
}
for (Map.Entry<Long, Set<Long>> entry : newPools.entrySet()) {
long volumeId = entry.getKey();
Set<Long> existingPoolsForVol = existingPools.get(entry.getKey());
Set<Long> newPoolsForVol = entry.getValue();
if (existingPoolsForVol == null || existingPoolsForVol.size() == 0) {
for (long poolId : newPoolsForVol) {
log.info("Associating volume [{}] to storage pool [{}]", volumeId, poolId);
objectManager.create(VolumeStoragePoolMap.class,
VOLUME_STORAGE_POOL_MAP.VOLUME_ID, volumeId,
VOLUME_STORAGE_POOL_MAP.STORAGE_POOL_ID, poolId);
}
} else if (!existingPoolsForVol.equals(newPoolsForVol)) {
throw new IllegalStateException(String.format("Can not move volume %s, currently: %s, new: %s", volumeId, existingPools, newPools));
}
}
if (attempt.getAllocatedIPs() != null) {
updateInstancePorts(attempt.getAllocatedIPs());
}
return true;
}
void updateVolumeHostInfo(AllocationAttempt attempt, AllocationCandidate candidate, Long newHost) {
List<Object> storageDriverIds = new ArrayList<>();
for (Volume v : attempt.getVolumes()) {
if (v.getStorageDriverId() != null) {
storageDriverIds.add(v.getStorageDriverId());
}
}
Map<Object, Object> criteria = new HashMap<Object, Object>();
criteria.put(STORAGE_DRIVER.REMOVED, new io.github.ibuildthecloud.gdapi.condition.Condition(ConditionType.NULL));
criteria.put(STORAGE_DRIVER.ID, new io.github.ibuildthecloud.gdapi.condition.Condition(ConditionType.IN, storageDriverIds));
List<StorageDriver> drivers = objectManager.find(StorageDriver.class, criteria);
Map<Long, StorageDriver> storageDrivers = new HashMap<>();
for (StorageDriver d : drivers) {
storageDrivers.put(d.getId(), d);
}
for (Volume v : attempt.getVolumes()) {
boolean persist = false;
StorageDriver d = v.getStorageDriverId() != null ? storageDrivers.get(v.getStorageDriverId()) : null;
if (d != null && StorageDriverConstants.SCOPE_LOCAL.equals(DataAccessor.fieldString(d, StorageDriverConstants.FIELD_SCOPE))) {
persist = true;
getAllocatedHostUuidProp(v).set(candidate.getHostUuid());
}
if (VolumeConstants.ACCESS_MODE_SINGLE_HOST_RW.equals(v.getAccessMode())) {
persist = true;
DataAccessor.fromDataFieldOf(v).withKey(VolumeConstants.FIELD_LAST_ALLOCATED_HOST_ID).set(newHost);
}
if (persist) {
objectManager.persist(v);
}
}
}
protected boolean isEmtpy(Map<Long,Set<Long>> set) {
for ( Set<Long> value : set.values() ) {
if ( value.size() > 0 ) {
return false;
}
}
return true;
}
@Override
public void releaseAllocation(Instance instance, InstanceHostMap map) {
//Reload for persisting
map = objectManager.loadResource(InstanceHostMap.class, map.getId());
DataAccessor data = getDeallocatedProp(map);
Boolean done = data.as(Boolean.class);
if ( done == null || ! done.booleanValue() ) {
data.set(true);
objectManager.persist(map);
}
}
@Override
public void releaseAllocation(Volume volume) {
//Reload for persisting
volume = objectManager.reload(volume);
DataAccessor data = getDeallocatedProp(volume);
Boolean done = data.as(Boolean.class);
if ( done == null || ! done.booleanValue() ) {
data.set(true);
objectManager.persist(volume);
}
}
@Override
public boolean isAllocationReleased(Object resource) {
DataAccessor done = getDeallocatedProp(resource);
return done.as(Boolean.class);
}
private DataAccessor getDeallocatedProp(Object resource) {
return DataAccessor.fromDataFieldOf(resource)
.withScope(AllocatorDao.class)
.withKey("deallocated");
}
@Override
public String getAllocatedHostUuid(Volume volume) {
return getAllocatedHostUuidProp(volume).as(String.class);
}
protected DataAccessor getAllocatedHostUuidProp(Volume v) {
return DataAccessor.fromDataFieldOf(v)
.withScope(AllocatorDao.class)
.withKey("allocatedHostUuid");
}
@Override
public Map<String, List<InstanceHostMap>> getInstanceHostMapsWithHostUuid(long instanceId) {
Map<Record, List<InstanceHostMap>> result = create()
.select(INSTANCE_HOST_MAP.fields()).select(HOST.UUID)
.from(INSTANCE_HOST_MAP)
.join(HOST).on(INSTANCE_HOST_MAP.HOST_ID.eq(HOST.ID))
.where(INSTANCE_HOST_MAP.INSTANCE_ID.eq(instanceId)
.and(INSTANCE_HOST_MAP.REMOVED.isNull()))
.fetchGroups(new Field[]{HOST.UUID}, InstanceHostMap.class);
Map<String, List<InstanceHostMap>> maps = new HashMap<>();
for (Map.Entry<Record, List<InstanceHostMap>>entry : result.entrySet()) {
String uuid = (String)entry.getKey().getValue(0);
maps.put(uuid, entry.getValue());
}
return maps;
}
@Override
public List<Long> getInstancesWithVolumeMounted(long volumeId, long currentInstanceId) {
return create()
.select(INSTANCE.ID)
.from(INSTANCE)
.join(MOUNT)
.on(MOUNT.INSTANCE_ID.eq(INSTANCE.ID).and(MOUNT.VOLUME_ID.eq(volumeId)))
.join(INSTANCE_HOST_MAP)
.on(INSTANCE_HOST_MAP.INSTANCE_ID.eq(INSTANCE.ID))
.where(INSTANCE.REMOVED.isNull()
.and(INSTANCE.ID.ne(currentInstanceId))
.and(INSTANCE_HOST_MAP.STATE.notIn(IHM_STATES))
.and((INSTANCE.HEALTH_STATE.isNull().or(INSTANCE.HEALTH_STATE.eq(HealthcheckConstants.HEALTH_STATE_HEALTHY)))))
.fetchInto(Long.class);
}
@Override
public boolean isVolumeInUseOnHost(long volumeId, long hostId) {
return create()
.select(INSTANCE.ID)
.from(INSTANCE)
.join(MOUNT)
.on(MOUNT.INSTANCE_ID.eq(INSTANCE.ID).and(MOUNT.VOLUME_ID.eq(volumeId)))
.join(INSTANCE_HOST_MAP)
.on(INSTANCE_HOST_MAP.INSTANCE_ID.eq(INSTANCE.ID).and(INSTANCE_HOST_MAP.HOST_ID.eq(hostId)))
.join(HOST)
.on(HOST.ID.eq(INSTANCE_HOST_MAP.HOST_ID))
.where(INSTANCE.REMOVED.isNull()
.and(INSTANCE_HOST_MAP.STATE.notIn(IHM_STATES))
.and((INSTANCE.HEALTH_STATE.isNull().or(INSTANCE.HEALTH_STATE.eq(HealthcheckConstants.HEALTH_STATE_HEALTHY)))))
.fetch().size() > 0;
}
@Override
public Set<Long> findHostsWithVolumeInUse(long volumeId) {
Set<Long> result = new HashSet<>();
create()
.select(HOST.ID)
.from(INSTANCE)
.join(MOUNT)
.on(MOUNT.INSTANCE_ID.eq(INSTANCE.ID).and(MOUNT.VOLUME_ID.eq(volumeId)))
.join(INSTANCE_HOST_MAP)
.on(INSTANCE_HOST_MAP.INSTANCE_ID.eq(INSTANCE.ID))
.join(HOST)
.on(HOST.ID.eq(INSTANCE_HOST_MAP.HOST_ID))
.where(INSTANCE.REMOVED.isNull()
.and(INSTANCE_HOST_MAP.STATE.notIn(IHM_STATES))
.and((INSTANCE.HEALTH_STATE.isNull().or(INSTANCE.HEALTH_STATE.eq(HealthcheckConstants.HEALTH_STATE_HEALTHY)))))
.fetchInto(new RecordHandler<Record1<Long>>() {
@Override
public void next(Record1<Long> record) {
result.add(record.getValue(HOST.ID));
}
});
return result;
}
@Override
public List<Port> getUsedPortsForHostExcludingInstance(long hostId, long instanceId) {
return create()
.select(PORT.fields())
.from(PORT)
.join(INSTANCE_HOST_MAP)
.on(PORT.INSTANCE_ID.eq(INSTANCE_HOST_MAP.INSTANCE_ID))
.join(INSTANCE)
.on(INSTANCE_HOST_MAP.INSTANCE_ID.eq(INSTANCE.ID))
.leftOuterJoin(SERVICE_EXPOSE_MAP)
.on(SERVICE_EXPOSE_MAP.INSTANCE_ID.eq(INSTANCE.ID))
.where(INSTANCE_HOST_MAP.HOST_ID.eq(hostId)
.and(INSTANCE.REMOVED.isNull())
.and(INSTANCE.ID.ne(instanceId))
.and(INSTANCE.STATE.in(InstanceConstants.STATE_STARTING, InstanceConstants.STATE_RESTARTING, InstanceConstants.STATE_RUNNING))
.and(INSTANCE_HOST_MAP.REMOVED.isNull())
.and(PORT.REMOVED.isNull())
.and(SERVICE_EXPOSE_MAP.UPGRADE.eq(false).or(SERVICE_EXPOSE_MAP.UPGRADE.isNull())))
.fetchInto(Port.class);
}
@Override
public Map<String, String[]> getLabelsForHost(long hostId) {
final Map<String, String[]> labelKeyValueStatusMap = new HashMap<String, String[]>();
create()
.select(LABEL.KEY, LABEL.VALUE, HOST_LABEL_MAP.STATE)
.from(LABEL)
.join(HOST_LABEL_MAP)
.on(LABEL.ID.eq(HOST_LABEL_MAP.LABEL_ID))
.where(HOST_LABEL_MAP.HOST_ID.eq(hostId))
.and(LABEL.REMOVED.isNull())
.and(HOST_LABEL_MAP.REMOVED.isNull())
.fetchInto(new RecordHandler<Record3<String, String, String>>() {
@Override
public void next(Record3<String, String, String> record) {
labelKeyValueStatusMap.put(StringUtils.lowerCase(record.value1()),
new String[] {
StringUtils.lowerCase(record.value2()),
record.value3()
});
}
});
return labelKeyValueStatusMap;
}
@Override
public List<? extends Host> getNonPurgedHosts(long accountId) {
return create()
.select(HOST.fields())
.from(HOST)
.where(HOST.ACCOUNT_ID.eq(accountId)
.and(HOST.STATE.notEqual(CommonStatesConstants.PURGED)))
.fetchInto(Host.class);
}
@Override
public List<? extends Host> getActiveHosts(long accountId) {
return create()
.select(HOST.fields())
.from(HOST)
.leftOuterJoin(AGENT)
.on(AGENT.ID.eq(HOST.AGENT_ID))
.where(
AGENT.ID.isNull()
.or(AGENT.STATE.in(CommonStatesConstants.ACTIVE, AgentConstants.STATE_FINISHING_RECONNECT, AgentConstants.STATE_RECONNECTED))
.and(HOST.REMOVED.isNull())
.and(HOST.ACCOUNT_ID.eq(accountId))
.and(HOST.STATE.in(CommonStatesConstants.ACTIVATING, CommonStatesConstants.ACTIVE, CommonStatesConstants.UPDATING_ACTIVE)))
.fetchInto(Host.class);
}
@Override
public boolean hostHasContainerLabel(long hostId, String labelKey,
String labelValue) {
return create()
.select(LABEL.ID)
.from(LABEL)
.join(INSTANCE_LABEL_MAP)
.on(LABEL.ID.eq(INSTANCE_LABEL_MAP.LABEL_ID))
.join(INSTANCE_HOST_MAP)
.on(INSTANCE_LABEL_MAP.INSTANCE_ID.eq(INSTANCE_HOST_MAP.INSTANCE_ID))
.join(INSTANCE)
.on(INSTANCE_HOST_MAP.INSTANCE_ID.eq(INSTANCE.ID))
.leftOuterJoin(SERVICE_EXPOSE_MAP)
.on(SERVICE_EXPOSE_MAP.INSTANCE_ID.eq(INSTANCE.ID))
.where(INSTANCE_HOST_MAP.HOST_ID.eq(hostId))
.and(LABEL.REMOVED.isNull())
.and(INSTANCE_LABEL_MAP.REMOVED.isNull())
.and(INSTANCE_HOST_MAP.REMOVED.isNull())
.and(INSTANCE.REMOVED.isNull())
.and(INSTANCE.STATE.notIn(InstanceConstants.STATE_ERROR, InstanceConstants.STATE_ERRORING))
.and(LABEL.KEY.equalIgnoreCase(labelKey))
.and(LABEL.VALUE.equalIgnoreCase(labelValue))
.and(SERVICE_EXPOSE_MAP.UPGRADE.eq(false).or(SERVICE_EXPOSE_MAP.UPGRADE.isNull()))
.fetchInto(Long.class).size() > 0;
}
@Override
public List<Instance> getUnmappedDeploymentUnitInstances(String deploymentUnitUuid) {
List<? extends Instance> instanceRecords = create()
.select(INSTANCE.fields())
.from(INSTANCE)
.leftOuterJoin(INSTANCE_HOST_MAP)
.on(INSTANCE_HOST_MAP.INSTANCE_ID.eq(INSTANCE.ID).and(INSTANCE_HOST_MAP.REMOVED.isNull()))
.where(INSTANCE.REMOVED.isNull())
.and(INSTANCE.DEPLOYMENT_UNIT_UUID.eq(deploymentUnitUuid))
.and(INSTANCE_HOST_MAP.ID.isNull())
.fetchInto(InstanceRecord.class);
List<Instance> instances = new ArrayList<Instance>();
for (Instance i : instanceRecords) {
instances.add(i);
}
return instances;
}
/* (non-Java doc)
* @see io.cattle.platform.allocator.dao.AllocatorDao#updateInstancePorts(java.util.Map)
* Scheduler will return a list of map showing the allocated result in the following format:
* {
* "instanceID" : "xx",
* "allocatedIPs" : {
* [
* {
* "allocatedIP" : "xxx.xxx.xxx.xxx",
* "publicPort" : "xxx",
* "privatePort" : "xxx",
* },
* {
* "allocatedIP" : "xxx.xxx.xxx.xxx",
* "publicPort" : "xxx",
* "privatePort" : "xxx",
* }
* ]
* }
* }
*
* Then update
* Port Table. Binding address field in port table and public port field in port table if public port is allocated by external scheduler (for agent)
*/
@SuppressWarnings("unchecked")
private void updateInstancePorts(List<Map<String, Object>> dataList) {
for (Map<String, Object> data: dataList) {
if (data.get(INSTANCE_ID) == null) {
continue;
}
String instanceId = (String) data.get(INSTANCE_ID);
if (data.get(ALLOCATED_IPS) == null) {
continue;
}
List<Map<String, Object>> allocatedIPList = (List<Map<String, Object>>) data.get(ALLOCATED_IPS);
Instance instance = objectManager.loadResource(Instance.class, instanceId);
for (Map<String, Object> allocatedIp: allocatedIPList) {
String ipAddress = (String) allocatedIp.get(ALLOCATED_IP);
String protocol = (String) allocatedIp.get(PROTOCOL);
Integer publicPort = (Integer) allocatedIp.get(PUBLIC_PORT);
Integer privatePort = (Integer) allocatedIp.get(PRIVATE_PORT);
for (Port port: objectManager.children(instance, Port.class)) {
if (port.getPrivatePort().equals(privatePort) && StringUtils.equals(port.getProtocol(), protocol)) {
DataAccessor.setField(port, BIND_ADDRESS, ipAddress);
port.setPublicPort(publicPort);
objectManager.persist(port);
break;
}
}
}
}
return;
}
@Override
public Iterator<AllocationCandidate> iteratorHosts(List<String> orderedHostUuids, List<Long> volumes, QueryOptions options) {
List<CandidateHostInfo> hostInfos = new ArrayList<>();
Set<Long> hostIds = new HashSet<>();
if (orderedHostUuids == null) {
Result<Record3<String, Long, Long>> result = getHostQuery(null, options).fetch();
Collections.shuffle(result);
Map<Long, CandidateHostInfo> infoMap = new HashMap<>();
for (Record3<String, Long, Long> r : result) {
Long hostId = r.value2();
hostIds.add(hostId);
CandidateHostInfo hostInfo = infoMap.get(hostId);
if (hostInfo == null) {
hostInfo = new CandidateHostInfo(hostId, r.value1());
infoMap.put(hostId, hostInfo);
hostInfos.add(hostInfo);
}
hostInfo.getPoolIds().add(r.value3());
}
} else {
Map<String, Result<Record3<String, Long, Long>>> result = getHostQuery(orderedHostUuids, options).fetchGroups(HOST.UUID);
for (String uuid : orderedHostUuids) {
Result<Record3<String, Long, Long>>val = result.get(uuid);
if (val != null) {
Set<Long>poolIds = new HashSet<>();
Long hostId = null;
for (Record3<String, Long, Long>r : val) {
poolIds.add(r.value3());
// host id is same in all records for this group
if (hostId == null) {
hostId = r.value2();
}
}
CandidateHostInfo hostInfo = new CandidateHostInfo(hostId, uuid);
hostInfo.getPoolIds().addAll(poolIds);
hostInfos.add(hostInfo);
hostIds.add(hostId);
}
}
}
if (options.isIncludeUsedPorts()) {
updateHostsWithUsedPorts(hostIds, hostInfos);
}
return new AllocationCandidateIterator(objectManager, hostInfos, volumes);
}
private void updateHostsWithUsedPorts(Set<Long> hostIds, List<CandidateHostInfo> hostInfos) {
Map<Long, Result<Record>> results = create()
.select(hostAndPortFields)
.from(PORT)
.join(INSTANCE_HOST_MAP)
.on(PORT.INSTANCE_ID.eq(INSTANCE_HOST_MAP.INSTANCE_ID))
.join(INSTANCE)
.on(INSTANCE_HOST_MAP.INSTANCE_ID.eq(INSTANCE.ID))
.leftOuterJoin(SERVICE_EXPOSE_MAP)
.on(SERVICE_EXPOSE_MAP.INSTANCE_ID.eq(INSTANCE.ID))
.where(INSTANCE_HOST_MAP.HOST_ID.in(hostIds)
.and(INSTANCE.REMOVED.isNull())
.and(INSTANCE.STATE.in(InstanceConstants.STATE_STARTING, InstanceConstants.STATE_RESTARTING, InstanceConstants.STATE_RUNNING))
.and(INSTANCE_HOST_MAP.REMOVED.isNull())
.and(PORT.REMOVED.isNull())
.and(SERVICE_EXPOSE_MAP.UPGRADE.eq(false).or(SERVICE_EXPOSE_MAP.UPGRADE.isNull())))
.fetchGroups(INSTANCE_HOST_MAP.HOST_ID);
Map<Long, List<Port>> hostToPorts = new HashMap<>();
for (Map.Entry<Long, Result<Record>> entry : results.entrySet()) {
List<Port> ports = new ArrayList<>();
hostToPorts.put(entry.getKey(), ports);
for (Record rec : entry.getValue()) {
PortRecord port = rec.into(PortRecord.class);
ports.add(port);
}
}
for (CandidateHostInfo hostInfo : hostInfos) {
List<Port> ports = hostToPorts.get(hostInfo.getHostId()) != null ? hostToPorts.get(hostInfo.getHostId()) : new ArrayList<Port>();
hostInfo.setUsedPorts(ports);
}
}
protected SelectConditionStep<Record3<String, Long, Long>> getHostQuery(List<String> orderedHostUUIDs, QueryOptions options) {
return create()
.select(HOST.UUID, HOST.ID, STORAGE_POOL.ID)
.from(HOST)
.leftOuterJoin(STORAGE_POOL_HOST_MAP)
.on(STORAGE_POOL_HOST_MAP.HOST_ID.eq(HOST.ID)
.and(STORAGE_POOL_HOST_MAP.REMOVED.isNull()))
.join(STORAGE_POOL)
.on(STORAGE_POOL.ID.eq(STORAGE_POOL_HOST_MAP.STORAGE_POOL_ID))
.leftOuterJoin(AGENT)
.on(AGENT.ID.eq(HOST.AGENT_ID))
.where(getQueryOptionCondition(options, orderedHostUUIDs));
}
protected Condition inHostList(List<String> hostUUIDs) {
if (hostUUIDs == null || hostUUIDs.isEmpty()) {
return DSL.trueCondition();
}
return HOST.UUID.in(hostUUIDs);
}
protected Condition getQueryOptionCondition(QueryOptions options, List<String> orderedHostUUIDs) {
Condition condition = null;
if (options.getAccountId() != null) {
condition = append(condition, HOST.ACCOUNT_ID.eq(options.getAccountId()));
}
if (options.getRequestedHostId() != null) {
condition = append(condition, HOST.ID.eq(options.getRequestedHostId()));
return condition;
}
condition = append(condition, AGENT.ID.isNull().or(AGENT.STATE.eq(CommonStatesConstants.ACTIVE))
.and(HOST.STATE.in(CommonStatesConstants.ACTIVE, CommonStatesConstants.UPDATING_ACTIVE))
.and(STORAGE_POOL.STATE.eq(CommonStatesConstants.ACTIVE))
.and(inHostList(orderedHostUUIDs)));
if ( options.getHosts().size() > 0 ) {
condition = append(condition, HOST.ID.in(options.getHosts()));
}
if ( options.getKind() != null ) {
condition = append(condition,
HOST.KIND.eq(options.getKind()).and(STORAGE_POOL.KIND.eq(options.getKind())));
}
return condition == null ? DSL.trueCondition() : condition;
}
protected Condition append(Condition base, Condition next) {
if ( base == null ) {
return next;
} else {
return base.and(next);
}
}
}