package io.cattle.platform.core.dao.impl;
import static io.cattle.platform.core.model.tables.AccountTable.*;
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.InstanceTable.*;
import static io.cattle.platform.core.model.tables.MountTable.*;
import static io.cattle.platform.core.model.tables.StorageDriverTable.*;
import static io.cattle.platform.core.model.tables.StoragePoolTable.*;
import static io.cattle.platform.core.model.tables.VolumeStoragePoolMapTable.*;
import static io.cattle.platform.core.model.tables.VolumeTable.*;
import io.cattle.platform.core.addon.MountEntry;
import io.cattle.platform.core.constants.CommonStatesConstants;
import io.cattle.platform.core.constants.InstanceConstants;
import io.cattle.platform.core.constants.VolumeConstants;
import io.cattle.platform.core.dao.GenericResourceDao;
import io.cattle.platform.core.dao.VolumeDao;
import io.cattle.platform.core.model.Image;
import io.cattle.platform.core.model.ImageStoragePoolMap;
import io.cattle.platform.core.model.Mount;
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.ImageRecord;
import io.cattle.platform.core.model.tables.records.ImageStoragePoolMapRecord;
import io.cattle.platform.core.model.tables.records.MountRecord;
import io.cattle.platform.core.model.tables.records.VolumeRecord;
import io.cattle.platform.core.model.tables.records.VolumeStoragePoolMapRecord;
import io.cattle.platform.core.util.VolumeUtils;
import io.cattle.platform.db.jooq.dao.impl.AbstractJooqDao;
import io.cattle.platform.deferred.util.DeferredUtils;
import io.cattle.platform.object.ObjectManager;
import io.cattle.platform.object.process.ObjectProcessManager;
import io.github.ibuildthecloud.gdapi.id.IdFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import javax.inject.Inject;
import javax.inject.Named;
import org.jooq.Record;
import org.jooq.Record1;
import org.jooq.Record6;
import org.jooq.RecordHandler;
import org.jooq.Result;
@Named
public class VolumeDaoImpl extends AbstractJooqDao implements VolumeDao {
private static final Set<String> LOCAL_POOL_KINDS = new HashSet<String>(Arrays.asList(new String[]{"docker", "sim"}));
@Inject
GenericResourceDao resourceDao;
@Inject
ObjectManager objectManager;
@Inject
ObjectProcessManager objectProcessManager;
@Override
public Volume findVolumeByExternalId(Long storagePoolId, String externalId) {
Record record = create()
.select(VOLUME.fields())
.from(VOLUME)
.join(VOLUME_STORAGE_POOL_MAP)
.on(VOLUME_STORAGE_POOL_MAP.STORAGE_POOL_ID.eq(storagePoolId))
.where(VOLUME.EXTERNAL_ID.eq(externalId)
.or(VOLUME.EXTERNAL_ID.eq(VolumeUtils.externalId(externalId)))
.and((VOLUME.REMOVED.isNull().or(VOLUME.STATE.eq(CommonStatesConstants.REMOVING)))))
.fetchAny();
return record == null ? null : record.into(VolumeRecord.class);
}
@Override
public void createVolumeInStoragePool(Map<String, Object> volumeData, String volumeName, StoragePool storagePool) {
Record record = create()
.select(VOLUME.fields())
.from(VOLUME)
.join(VOLUME_STORAGE_POOL_MAP)
.on(VOLUME_STORAGE_POOL_MAP.VOLUME_ID.eq(VOLUME.ID)
.and(VOLUME_STORAGE_POOL_MAP.STORAGE_POOL_ID.eq(storagePool.getId())))
.join(STORAGE_POOL)
.on(VOLUME_STORAGE_POOL_MAP.STORAGE_POOL_ID.eq(STORAGE_POOL.ID))
.and(STORAGE_POOL.REMOVED.isNull())
.where(VOLUME.NAME.eq(volumeName)
.and((VOLUME.REMOVED.isNull().or(VOLUME.STATE.eq(CommonStatesConstants.REMOVING)))))
.and(VOLUME.ACCOUNT_ID.eq(storagePool.getAccountId()))
.fetchAny();
if (record != null) {
return;
}
Volume volume = resourceDao.createAndSchedule(Volume.class, volumeData);
Map<String, Object> vspm = new HashMap<String, Object>();
vspm.put("volumeId", volume.getId());
vspm.put("storagePoolId", storagePool.getId());
resourceDao.createAndSchedule(VolumeStoragePoolMap.class, vspm);
}
@Override
public List<? extends Volume> findSharedOrUnmappedVolumes(long accountId, String volumeName) {
List<VolumeRecord> volumes = create()
.selectDistinct(VOLUME.fields())
.from(VOLUME)
.leftOuterJoin(VOLUME_STORAGE_POOL_MAP)
.on(VOLUME_STORAGE_POOL_MAP.VOLUME_ID.eq(VOLUME.ID))
.leftOuterJoin(STORAGE_POOL)
.on(VOLUME_STORAGE_POOL_MAP.STORAGE_POOL_ID.eq(STORAGE_POOL.ID))
.where(VOLUME.NAME.eq(volumeName)
.and((VOLUME.REMOVED.isNull())))
.and(VOLUME.ACCOUNT_ID.eq(accountId))
.and(STORAGE_POOL.KIND.notIn(LOCAL_POOL_KINDS).or(STORAGE_POOL.KIND.isNull()))
.and(STORAGE_POOL.REMOVED.isNull())
.fetchInto(VolumeRecord.class);
return volumes;
}
@Override
public List<? extends Volume> identifyUnmappedVolumes(long accountId, Set<Long> volumeIds) {
List<VolumeRecord> volumes = create()
.selectDistinct(VOLUME.fields())
.from(VOLUME)
.leftOuterJoin(VOLUME_STORAGE_POOL_MAP)
.on(VOLUME_STORAGE_POOL_MAP.VOLUME_ID.eq(VOLUME.ID))
.where(VOLUME.ID.in(volumeIds)
.and((VOLUME.REMOVED.isNull())))
.and(VOLUME.ACCOUNT_ID.eq(accountId))
.and(VOLUME_STORAGE_POOL_MAP.ID.isNull())
.fetchInto(VolumeRecord.class);
return volumes;
}
public static final List<String> INELLIGIBLE_STATES = Arrays.asList(CommonStatesConstants.ACTIVE, CommonStatesConstants.ACTIVATING,
CommonStatesConstants.REQUESTED);
@Override
public Set<? extends Volume> findNonremovedVolumesWithNoOtherMounts(long instanceId) {
List<Long> instanceVolumeIds = create()
.select(MOUNT.VOLUME_ID)
.from(MOUNT)
.where(MOUNT.INSTANCE_ID.eq(instanceId))
.fetchInto(Long.class);
List<Long> inelligibleVolumeIds = create()
.select(MOUNT.VOLUME_ID)
.from(MOUNT)
.where(MOUNT.VOLUME_ID.in(instanceVolumeIds)
.and(MOUNT.INSTANCE_ID.ne(instanceId))
.and(MOUNT.STATE.in(INELLIGIBLE_STATES)))
.fetchInto(Long.class);
Set<Long> volumeIds = new HashSet<Long>(instanceVolumeIds);
volumeIds.removeAll(inelligibleVolumeIds);
List<VolumeRecord> vols = create()
.select(VOLUME.fields())
.from(VOLUME)
.where(VOLUME.ID.in(volumeIds))
.and(VOLUME.REMOVED.isNull())
.fetchInto(VolumeRecord.class);
Set<? extends Volume> volumes = new HashSet<Volume>(vols);
return volumes;
}
private static final Set<String> INSTANCE_STATES = new HashSet<String>(Arrays.asList(new String[] { InstanceConstants.STATE_STOPPED,
InstanceConstants.STATE_CREATED }));
@Override
public boolean isVolumeInUseByRunningInstance(long volumeId) {
Result<Record1<Integer>> result = create()
.selectCount()
.from(INSTANCE)
.join(MOUNT)
.on(MOUNT.INSTANCE_ID.eq(INSTANCE.ID))
.where(INSTANCE.STATE.notIn(INSTANCE_STATES))
.and(INSTANCE.REMOVED.isNull())
.and(MOUNT.REMOVED.isNull())
.and(MOUNT.VOLUME_ID.eq(volumeId))
.fetch();
Integer inUseCount = (Integer)result.getValue(0, 0);
return inUseCount > 0;
}
@Override
public Volume createVolumeForDriver(final long accountId, final String volumeName, final String driverName) {
StorageDriver driver = objectManager.findAny(StorageDriver.class,
STORAGE_DRIVER.NAME, driverName,
STORAGE_DRIVER.ACCOUNT_ID, accountId,
STORAGE_DRIVER.REMOVED, null);
final Long driverId = driver == null ? null : driver.getId();
return DeferredUtils.nest(new Callable<Volume>() {
@Override
public Volume call() throws Exception {
return resourceDao.createAndSchedule(Volume.class,
VOLUME.NAME, volumeName,
VOLUME.ACCOUNT_ID, accountId,
VOLUME.STORAGE_DRIVER_ID, driverId,
VolumeConstants.FIELD_VOLUME_DRIVER, driverName);
}
});
}
@Override
public Map<Long, List<MountEntry>> getMountsForInstances(List<Long> ids, final IdFormatter idF) {
final Map<Long, List<MountEntry>> result = new HashMap<>();
create().select(INSTANCE.NAME, VOLUME.NAME, VOLUME.ID, MOUNT.PERMISSIONS, MOUNT.PATH, MOUNT.INSTANCE_ID)
.from(MOUNT)
.join(VOLUME)
.on(VOLUME.ID.eq(MOUNT.VOLUME_ID))
.join(INSTANCE)
.on(INSTANCE.ID.eq(MOUNT.INSTANCE_ID))
.where(MOUNT.REMOVED.isNull()
.and(VOLUME.REMOVED.isNull())
.and(MOUNT.INSTANCE_ID.in(ids)))
.fetchInto(new RecordHandler<Record6<String, String, Long, String, String, Long>>() {
@Override
public void next(Record6<String, String, Long, String, String, Long> record) {
Long instanceId = record.getValue(MOUNT.INSTANCE_ID);
List<MountEntry> entries = result.get(instanceId);
if (entries == null) {
entries = new ArrayList<>();
result.put(instanceId, entries);
}
MountEntry mount = new MountEntry();
mount.setInstanceName(record.getValue(INSTANCE.NAME));
mount.setInstanceId(idF.formatId(InstanceConstants.TYPE, instanceId));
mount.setPath(record.getValue(MOUNT.PATH));
mount.setPermission(record.getValue(MOUNT.PERMISSIONS));
mount.setVolumeId(idF.formatId(VolumeConstants.TYPE, record.getValue(VOLUME.ID)));
mount.setVolumeName(record.getValue(VOLUME.NAME));
entries.add(mount);
}
});
return result;
}
@Override
public Map<Long, List<MountEntry>> getMountsForVolumes(List<Long> ids, final IdFormatter idF) {
final Map<Long, List<MountEntry>> result = new HashMap<>();
create().select(VOLUME.NAME, MOUNT.PERMISSIONS, MOUNT.PATH, MOUNT.INSTANCE_ID, MOUNT.VOLUME_ID, INSTANCE.NAME)
.from(MOUNT)
.join(INSTANCE)
.on(INSTANCE.ID.eq(MOUNT.INSTANCE_ID))
.join(VOLUME)
.on(VOLUME.ID.eq(MOUNT.VOLUME_ID))
.where(INSTANCE.REMOVED.isNull()
.and(VOLUME.REMOVED.isNull())
.and(MOUNT.VOLUME_ID.in(ids)))
.fetchInto(new RecordHandler<Record6<String, String, String, Long, Long, String>>() {
@Override
public void next(Record6<String, String, String, Long, Long, String> record) {
Long volumeId = record.getValue(MOUNT.VOLUME_ID);
List<MountEntry> entries = result.get(volumeId);
if (entries == null) {
entries = new ArrayList<>();
result.put(volumeId, entries);
}
MountEntry mount = new MountEntry();
mount.setInstanceName(record.getValue(INSTANCE.NAME));
mount.setInstanceId(idF.formatId(InstanceConstants.TYPE, record.getValue(MOUNT.INSTANCE_ID)));
mount.setPath(record.getValue(MOUNT.PATH));
mount.setPermission(record.getValue(MOUNT.PERMISSIONS));
mount.setVolumeId(idF.formatId(VolumeConstants.TYPE, volumeId));
mount.setVolumeName(record.getValue(VOLUME.NAME));
entries.add(mount);
}
});
return result;
}
@Override
public List<? extends Volume> findNonRemovedVolumesOnPool(Long storagePoolId) {
return create().select(VOLUME.fields())
.from(VOLUME)
.join(VOLUME_STORAGE_POOL_MAP)
.on(VOLUME_STORAGE_POOL_MAP.VOLUME_ID.eq(VOLUME.ID))
.join(STORAGE_POOL)
.on(STORAGE_POOL.ID.eq(VOLUME_STORAGE_POOL_MAP.STORAGE_POOL_ID))
.where(VOLUME.REMOVED.isNull()
.and(STORAGE_POOL.REMOVED.isNotNull()))
.fetchInto(VolumeRecord.class);
}
@Override
public List<? extends Volume> findBadVolumes(int count) {
return create().select(VOLUME.fields())
.from(VOLUME)
.join(ACCOUNT)
.on(ACCOUNT.ID.eq(VOLUME.ACCOUNT_ID))
.where(VOLUME.REMOVED.isNull()
.and(ACCOUNT.STATE.eq(CommonStatesConstants.PURGED))
.and(VOLUME.STATE.notIn(CommonStatesConstants.DEACTIVATING, CommonStatesConstants.REMOVING)))
.limit(count)
.fetchInto(VolumeRecord.class);
}
@Override
public List<? extends Volume> findBadNativeVolumes(int count) {
return create().select(VOLUME.fields())
.from(VOLUME)
.join(MOUNT)
.on(MOUNT.VOLUME_ID.eq(VOLUME.ID))
.join(INSTANCE)
.on(MOUNT.INSTANCE_ID.eq(INSTANCE.ID))
.where(INSTANCE.STATE.eq(CommonStatesConstants.PURGED)
.and(VOLUME.STATE.eq(CommonStatesConstants.INACTIVE))
.and(INSTANCE.NATIVE_CONTAINER.isTrue()))
.limit(count)
.fetchInto(VolumeRecord.class);
}
@Override
public List<? extends Image> findBadImages(int count) {
return create().select(IMAGE.fields())
.from(IMAGE)
.leftOuterJoin(INSTANCE)
.on(IMAGE.ID.eq(INSTANCE.IMAGE_ID))
.where((INSTANCE.STATE.eq(CommonStatesConstants.PURGED).or(INSTANCE.ID.isNull()))
.and(IMAGE.REMOVED.isNull())
.and(IMAGE.STATE.notIn(CommonStatesConstants.DEACTIVATING, CommonStatesConstants.REMOVING)))
.limit(count)
.fetchInto(ImageRecord.class);
}
@Override
public List<? extends Mount> findBadMounts(int count) {
return create().select(MOUNT.fields())
.from(MOUNT)
.join(VOLUME)
.on(VOLUME.ID.eq(MOUNT.VOLUME_ID))
.where(VOLUME.STATE.eq(CommonStatesConstants.PURGED)
.and(MOUNT.REMOVED.isNull())
.and(MOUNT.STATE.notIn(CommonStatesConstants.DEACTIVATING, CommonStatesConstants.REMOVING)))
.limit(count)
.fetchInto(MountRecord.class);
}
@Override
public List<? extends VolumeStoragePoolMap> findBandVolumeStoragePoolMap(int count) {
return create().select(VOLUME_STORAGE_POOL_MAP.fields())
.from(VOLUME_STORAGE_POOL_MAP)
.join(VOLUME)
.on(VOLUME.ID.eq(VOLUME_STORAGE_POOL_MAP.VOLUME_ID))
.where(VOLUME.STATE.eq(CommonStatesConstants.PURGED)
.and(VOLUME_STORAGE_POOL_MAP.REMOVED.isNull())
.and(VOLUME_STORAGE_POOL_MAP.STATE.notIn(CommonStatesConstants.DEACTIVATING, CommonStatesConstants.REMOVING)))
.limit(count)
.fetchInto(VolumeStoragePoolMapRecord.class);
}
@Override
public List<? extends ImageStoragePoolMap> findBadImageStoragePoolMaps(int count) {
return create().select(IMAGE_STORAGE_POOL_MAP.fields())
.from(IMAGE_STORAGE_POOL_MAP)
.join(IMAGE)
.on(IMAGE.ID.eq(IMAGE_STORAGE_POOL_MAP.IMAGE_ID))
.where(IMAGE.STATE.eq(CommonStatesConstants.PURGED)
.and(IMAGE_STORAGE_POOL_MAP.REMOVED.isNull())
.and(IMAGE_STORAGE_POOL_MAP.STATE.notIn(CommonStatesConstants.DEACTIVATING, CommonStatesConstants.REMOVING)))
.limit(count)
.fetchInto(ImageStoragePoolMapRecord.class);
}
}