package org.ovirt.engine.core.bll.storage.domain;
import static java.util.stream.Collectors.toSet;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.ovirt.engine.core.bll.QueriesCommandBase;
import org.ovirt.engine.core.common.action.StorageServerConnectionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.businessentities.SANState;
import org.ovirt.engine.core.common.businessentities.StorageDomain;
import org.ovirt.engine.core.common.businessentities.StorageDomainStatic;
import org.ovirt.engine.core.common.businessentities.StorageServerConnections;
import org.ovirt.engine.core.common.businessentities.storage.LUNs;
import org.ovirt.engine.core.common.businessentities.storage.StorageType;
import org.ovirt.engine.core.common.queries.GetDeviceListQueryParameters;
import org.ovirt.engine.core.common.queries.GetUnregisteredBlockStorageDomainsParameters;
import org.ovirt.engine.core.common.queries.VdcQueryReturnValue;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.common.vdscommands.GetVGInfoVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.HSMGetStorageDomainInfoVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.LunDao;
import org.ovirt.engine.core.dao.StorageDomainDao;
public class GetUnregisteredBlockStorageDomainsQuery<P extends GetUnregisteredBlockStorageDomainsParameters> extends QueriesCommandBase<P> {
@Inject
private StorageDomainDao storageDomainDao;
@Inject
private LunDao lunDao;
public GetUnregisteredBlockStorageDomainsQuery(P parameters) {
super(parameters);
}
@Override
protected void executeQueryCommand() {
List<StorageServerConnections> connectedTargets = null;
List<StorageDomain> storageDomains;
try {
// iSCSI protocol requires targets connection (as opposed to FCP)
if (getParameters().getStorageType() == StorageType.ISCSI) {
connectedTargets = connectTargets();
}
// Fetch LUNs from GetDeviceList and filter-out irrelevant ones
List<LUNs> lunsFromDeviceList = getDeviceList();
List<LUNs> filteredLUNs = filterIrrelevantLUNs(lunsFromDeviceList, connectedTargets);
// Retrieve storage domains by VG-IDs extracted from the LUNs
List<String> vgIDs = getVolumeGroupIdsByLUNs(filteredLUNs);
storageDomains = getStorageDomainsByVolumeGroupIds(vgIDs);
} catch (RuntimeException e) {
log.error("Failed to retrieve storage domains by connections info: {}", e.getMessage());
log.debug("Exception", e);
getQueryReturnValue().setExceptionString(e.getMessage());
getQueryReturnValue().setSucceeded(false);
return;
}
Pair<List<StorageDomain>, List<StorageServerConnections>> returnValue = new Pair<>(storageDomains, connectedTargets);
getQueryReturnValue().setReturnValue(returnValue);
}
/**
* Connect to the targets (StorageServerConnections) specified in the parameters.
*
* @return A list of targets that's been successfully connected.
*/
protected List<StorageServerConnections> connectTargets() {
List<StorageServerConnections> connectedTargets = new ArrayList<>();
for (StorageServerConnections storageConnection : getParameters().getStorageServerConnections()) {
VdcReturnValueBase returnValue = executeConnectStorageToVds(
new StorageServerConnectionParametersBase(storageConnection, getParameters().getVdsId(), false));
if (returnValue.getSucceeded()) {
connectedTargets.add(storageConnection);
}
else {
log.error("Could not connect to target IQN '{}': {}",
storageConnection.getIqn(), returnValue.getFault().getMessage());
}
}
if (connectedTargets.isEmpty()) {
throw new RuntimeException("Couldn't connect to the specified targets");
}
return connectedTargets;
}
/**
* Get devices (LUNs) that are visible by the host.
*
* @return the list of LUNs.
*/
protected List<LUNs> getDeviceList() {
List<LUNs> luns = new ArrayList<>();
VdcQueryReturnValue returnValue =
executeGetDeviceList(
new GetDeviceListQueryParameters(getParameters().getVdsId(),
getParameters().getStorageType(),
false, null));
if (returnValue.getSucceeded()) {
luns.addAll(returnValue.<List<LUNs>> getReturnValue());
} else {
throw new RuntimeException(String.format("GetDeviceList execution failed. Exception message: %1$s",
returnValue.getExceptionString()));
}
return luns;
}
/**
* Filter out LUNs that aren't part of the specified targets.
*
* @param luns the LUNs list
* @param targets the targets list
*
* @return the filtered LUNs list
*/
protected List<LUNs> filterLUNsByTargets(List<LUNs> luns, final List<StorageServerConnections> targets) {
// Targets should be null only when using StorageType.FCP
if (targets == null) {
return luns;
}
// For iSCSI domains, filter LUNs by the specified targets
final Set<String> targetIQNs = targets.stream().map(StorageServerConnections::getIqn).collect(toSet());
return luns.stream()
.filter(lun -> lun.getLunConnections().stream().anyMatch(c -> targetIQNs.contains(c.getIqn())))
.collect(Collectors.toList());
}
/**
* Filter out LUNs that are already a part of storage domains exist in the system.
*
* @param luns the LUNs list
*
* @return the filtered LUNs list
*/
protected List<LUNs> filterLUNsThatBelongToExistingStorageDomains(List<LUNs> luns) {
List<StorageDomain> existingStorageDomains = storageDomainDao.getAll();
final Set<Guid> existingStorageDomainIDs =
existingStorageDomains.stream().map(StorageDomain::getId).collect(Collectors.toSet());
return luns.stream().filter(lun -> !existingStorageDomainIDs.contains(lun.getStorageDomainId()))
.collect(Collectors.toList());
}
/**
* Filter out irrelevant LUNs using filtering methods:
* 'filterLUNsByTargets' and 'filterLUNsThatBelongToExistingStorageDomains'.
*
* @param luns the LUNs list
* @param connectedTargets the targets list
* @return the filtered LUNs list
*/
protected List<LUNs> filterIrrelevantLUNs(List<LUNs> luns, List<StorageServerConnections> connectedTargets) {
List<LUNs> filteredLUNs = filterLUNsByTargets(luns, connectedTargets);
filteredLUNs = filterLUNsThatBelongToExistingStorageDomains(filteredLUNs);
return filteredLUNs;
}
/**
* Retrieve the volume group ID associated with each LUN in the specified list.
*
* @param luns the LUNs list
*
* @return volume group IDs
*/
protected List<String> getVolumeGroupIdsByLUNs(List<LUNs> luns) {
Set<String> vgSet = new HashSet<>();
for (LUNs lun : luns) {
if (!lun.getVolumeGroupId().isEmpty()) {
vgSet.add(lun.getVolumeGroupId());
}
}
return new ArrayList<>(vgSet);
}
/**
* Create StorageDomain objects according to the specified VG-IDs list.
*
* @param vgIDs the VG-IDs list
*
* @return storage domains list
*/
@SuppressWarnings("unchecked")
protected List<StorageDomain> getStorageDomainsByVolumeGroupIds(List<String> vgIDs) {
List<StorageDomain> storageDomains = new ArrayList<>();
// Get existing PhysicalVolumes.
Set<String> existingLunIds = lunDao.getAll().stream().map(LUNs::getId).collect(Collectors.toSet());
for (String vgID : vgIDs) {
VDSReturnValue returnValue;
try {
returnValue = executeGetVGInfo(
new GetVGInfoVDSCommandParameters(getParameters().getVdsId(), vgID));
} catch (RuntimeException e) {
log.error("Could not get info for VG ID: '{}': {}",
vgID, e.getMessage());
log.debug("Exception", e);
continue;
}
ArrayList<LUNs> luns = (ArrayList<LUNs>) returnValue.getReturnValue();
if (luns.stream().anyMatch(l -> existingLunIds.contains(l.getId()))) {
log.info("There are existing luns in the system which are part of VG id '{}'", vgID);
continue;
}
// Get storage domain ID by a representative LUN
LUNs lun = luns.get(0);
Guid storageDomainId = lun.getStorageDomainId();
// Get storage domain using GetStorageDomainInfo
StorageDomain storageDomain = getStorageDomainById(storageDomainId);
if (storageDomain != null) {
storageDomains.add(storageDomain);
}
}
return storageDomains;
}
/**
* Retrieve a storage domain using a specified storage domain ID.
*
* @param storageDomainId the domain's ID
* @return the storage domain
*/
@SuppressWarnings("unchecked")
protected StorageDomain getStorageDomainById(Guid storageDomainId) {
VDSReturnValue returnValue;
try {
returnValue = executeHSMGetStorageDomainInfo(
new HSMGetStorageDomainInfoVDSCommandParameters(getParameters().getVdsId(), storageDomainId));
} catch (RuntimeException e) {
log.error("Could not get info for storage domain ID: '{}': {}",
storageDomainId, e.getMessage());
log.debug("Exception", e);
return null;
}
Pair<StorageDomainStatic, SANState> result = (Pair<StorageDomainStatic, SANState>) returnValue.getReturnValue();
StorageDomainStatic storageDomainStatic = result.getFirst();
storageDomainStatic.setStorageType(getParameters().getStorageType());
StorageDomain storageDomain = new StorageDomain();
storageDomain.setStorageStaticData(storageDomainStatic);
return storageDomain;
}
/* Execute wrappers (for testing/mocking necessities) */
protected VdcReturnValueBase executeConnectStorageToVds(StorageServerConnectionParametersBase parameters) {
return backend.runInternalAction(VdcActionType.ConnectStorageToVds, parameters);
}
protected VdcQueryReturnValue executeGetDeviceList(GetDeviceListQueryParameters parameters) {
return backend.runInternalQuery(VdcQueryType.GetDeviceList, parameters);
}
protected VDSReturnValue executeGetVGInfo(GetVGInfoVDSCommandParameters parameters) {
return runVdsCommand(VDSCommandType.GetVGInfo, parameters);
}
protected VDSReturnValue executeHSMGetStorageDomainInfo(HSMGetStorageDomainInfoVDSCommandParameters parameters) {
return runVdsCommand(VDSCommandType.HSMGetStorageDomainInfo, parameters);
}
}