package org.ovirt.engine.api.restapi.resource;
import static org.ovirt.engine.api.restapi.resource.BackendStorageDomainResource.getLinksToExclude;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.api.model.HostStorage;
import org.ovirt.engine.api.model.LogicalUnit;
import org.ovirt.engine.api.model.LogicalUnits;
import org.ovirt.engine.api.model.StorageDomain;
import org.ovirt.engine.api.model.StorageDomainStatus;
import org.ovirt.engine.api.model.StorageDomains;
import org.ovirt.engine.api.model.VolumeGroup;
import org.ovirt.engine.api.resource.StorageDomainResource;
import org.ovirt.engine.api.resource.StorageDomainsResource;
import org.ovirt.engine.api.restapi.types.StorageDomainMapper;
import org.ovirt.engine.api.restapi.util.StorageDomainHelper;
import org.ovirt.engine.core.common.action.AddSANStorageDomainParameters;
import org.ovirt.engine.core.common.action.StorageDomainManagementParameter;
import org.ovirt.engine.core.common.action.StorageServerConnectionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.businessentities.StorageDomainSharedStatus;
import org.ovirt.engine.core.common.businessentities.StorageDomainStatic;
import org.ovirt.engine.core.common.businessentities.StorageDomainType;
import org.ovirt.engine.core.common.businessentities.StorageServerConnections;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.VdsStatic;
import org.ovirt.engine.core.common.businessentities.storage.LUNs;
import org.ovirt.engine.core.common.businessentities.storage.StorageType;
import org.ovirt.engine.core.common.interfaces.SearchType;
import org.ovirt.engine.core.common.queries.GetDeviceListQueryParameters;
import org.ovirt.engine.core.common.queries.GetExistingStorageDomainListParameters;
import org.ovirt.engine.core.common.queries.GetLunsByVgIdParameters;
import org.ovirt.engine.core.common.queries.GetUnregisteredBlockStorageDomainsParameters;
import org.ovirt.engine.core.common.queries.IdQueryParameters;
import org.ovirt.engine.core.common.queries.NameQueryParameters;
import org.ovirt.engine.core.common.queries.StorageServerConnectionQueryParametersBase;
import org.ovirt.engine.core.common.queries.VdcQueryParametersBase;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.compat.Guid;
public class BackendStorageDomainsResource
extends AbstractBackendCollectionResource<StorageDomain, org.ovirt.engine.core.common.businessentities.StorageDomain>
implements StorageDomainsResource {
private final EntityIdResolver<Guid> ID_RESOLVER =
new QueryIdResolver<>(VdcQueryType.GetStorageDomainById, IdQueryParameters.class);
public BackendStorageDomainsResource() {
super(StorageDomain.class, org.ovirt.engine.core.common.businessentities.StorageDomain.class);
}
@Override
public StorageDomains list() {
if (isFiltered()) {
return mapCollection(getBackendCollection(VdcQueryType.GetAllStorageDomains,
new VdcQueryParametersBase(), SearchType.StorageDomain));
} else {
return mapCollection(getBackendCollection(SearchType.StorageDomain));
}
}
@Override
public StorageDomainResource getStorageDomainResource(String id) {
return inject(new BackendStorageDomainResource(id, this));
}
private Response addDomain(VdcActionType action,
StorageDomain model,
StorageDomainStatic entity,
Guid hostId,
StorageServerConnections connection) {
Response response = null;
boolean isConnNew = false;
if (connection.getStorageType().isFileDomain() && StringUtils.isEmpty(connection.getId())) {
isConnNew = true;
connection.setId(addStorageServerConnection(connection, hostId));
}
entity.setStorage(connection.getId());
if (action == VdcActionType.AddNFSStorageDomain || action == VdcActionType.AddPosixFsStorageDomain ||
action == VdcActionType.AddGlusterFsStorageDomain) {
org.ovirt.engine.core.common.businessentities.StorageDomain existing =
getExistingStorageDomain(hostId,
entity.getStorageType(),
entity.getStorageDomainType(),
connection);
if (existing != null) {
StorageDomainMapper.map(model, existing.getStorageStaticData());
entity = existing.getStorageStaticData();
action = VdcActionType.AddExistingFileStorageDomain;
}
}
try {
if (action != VdcActionType.AddExistingFileStorageDomain) {
validateParameters(model, 2, "name");
}
response = performCreate(action, getAddParams(entity, hostId), ID_RESOLVER);
} catch (WebApplicationException e) {
// cleanup of created connection
if (isConnNew) {
removeStorageServerConnection(connection, hostId);
}
throw e;
}
return response;
}
private Response addSAN(StorageDomain model, StorageType storageType, StorageDomainStatic entity, Guid hostId) {
boolean overrideLuns = model.getStorage().isSetOverrideLuns() ? model.getStorage().isOverrideLuns() : false;
return performCreate(VdcActionType.AddSANStorageDomain,
getSanAddParams(entity,
hostId,
getLunIds(model.getStorage(), storageType, hostId),
overrideLuns),
ID_RESOLVER);
}
private Response addExistingSAN(StorageDomain model, StorageType storageType, Guid hostId) {
getEntity(VDS.class,
VdcQueryType.GetVdsByVdsId,
new IdQueryParameters(hostId),
"Host: id=" + hostId);
List<LUNs> existingLuns = getDeviceList(hostId, storageType);
List<StorageServerConnections> existingStorageServerConnections =
getLunsWithInitializedStorageType(existingLuns, storageType);
List<org.ovirt.engine.core.common.businessentities.StorageDomain> existingStorageDomains =
getExistingBlockStorageDomain(hostId,
storageType,
existingStorageServerConnections);
StorageDomainStatic storageDomainToImport =
getMatchingStorageDomain(asGuid(model.getId()), existingStorageDomains);
if (storageDomainToImport == null) {
throw new WebFaultException(new WebApplicationException(), "Storage Domain id " + model.getId()
+ " Does not exists", Status.NOT_FOUND);
}
StorageDomainMapper.map(model, storageDomainToImport);
StorageDomainManagementParameter parameters =
new StorageDomainManagementParameter(storageDomainToImport);
parameters.setVdsId(hostId);
return performCreate(VdcActionType.AddExistingBlockStorageDomain, parameters, ID_RESOLVER);
}
private StorageDomainStatic getMatchingStorageDomain(Guid storageId,
List<org.ovirt.engine.core.common.businessentities.StorageDomain> existingStorageDomains) {
StorageDomainStatic storageDomainStatic = new StorageDomainStatic();
for (org.ovirt.engine.core.common.businessentities.StorageDomain storageDomain : existingStorageDomains) {
if (storageDomain.getStorageStaticData().getId().equals(storageId)) {
storageDomainStatic = storageDomain.getStorageStaticData();
break;
}
}
return storageDomainStatic;
}
private List<StorageServerConnections> getLunsWithInitializedStorageType(List<LUNs> luns, StorageType storageType) {
List<StorageServerConnections> existingStorageServerConnections = new ArrayList<>();
for (LUNs lun : luns) {
for (StorageServerConnections storageServerConnection : lun.getLunConnections()) {
storageServerConnection.setStorageType(storageType);
existingStorageServerConnections.add(storageServerConnection);
}
}
return existingStorageServerConnections;
}
private List<org.ovirt.engine.core.common.businessentities.StorageDomain> getExistingBlockStorageDomain(Guid hostId,
StorageType storageType,
List<StorageServerConnections> cnxList) {
Pair<List<org.ovirt.engine.core.common.businessentities.StorageDomain>, List<StorageServerConnections>> pair =
getEntity(Pair.class,
VdcQueryType.GetUnregisteredBlockStorageDomains,
new GetUnregisteredBlockStorageDomainsParameters(hostId, storageType, cnxList),
"GetUnregisteredBlockStorageDomains", true);
List<org.ovirt.engine.core.common.businessentities.StorageDomain> existingStorageDomains = pair.getFirst();
return existingStorageDomains;
}
private List<LUNs> getDeviceList(Guid hostId, StorageType storageType) {
return getEntity(List.class,
VdcQueryType.GetDeviceList,
new GetDeviceListQueryParameters(hostId, storageType, false, null),
"GetDeviceList", true);
}
private ArrayList<String> getLunIds(HostStorage storage, StorageType storageType, Guid hostId) {
List<LogicalUnit> logicalUnits = new ArrayList<>();
if (storage.isSetLogicalUnits() && storage.getLogicalUnits().isSetLogicalUnits()) {
logicalUnits = storage.getLogicalUnits().getLogicalUnits();
} else if (storage.isSetVolumeGroup() && storage.getVolumeGroup().isSetLogicalUnits() &&
storage.getVolumeGroup().getLogicalUnits().isSetLogicalUnits()) {
logicalUnits = storage.getVolumeGroup().getLogicalUnits().getLogicalUnits();
}
ArrayList<String> lunIds = new ArrayList<>();
for (LogicalUnit unit : logicalUnits) {
validateParameters(unit, 4, "id");
// if the address and target were not supplied, we understand from this that
// the user assumes that the host is already logged-in to the target of this lun.
// so in this case we do not need (and do not have the required information) to login
// to the target.
if ((storageType == StorageType.ISCSI) && !isConnectionAssumed(unit)) {
connectStorageToHost(hostId, storageType, unit);
}
lunIds.add(unit.getId());
}
refreshHostStorage(hostId);
return !lunIds.isEmpty() ? lunIds : null;
}
private boolean isConnectionAssumed(LogicalUnit unit) {
// either 'target' and 'address' should both be provided, or none. Validate this
if (unit.getAddress() != null || unit.getTarget() != null) {
validateParameters(unit, "address", "target");
}
boolean connectionAssumed = unit.getAddress() == null || unit.getTarget() == null;
return connectionAssumed;
}
/**
* This is a work-around for a VDSM bug. The call to GetDeviceList causes a necessary refresh in the VDSM, without
* which the creation will fail.
*/
private void refreshHostStorage(Guid hostId) {
getBackendCollection(VdcQueryType.GetDeviceList, new GetDeviceListQueryParameters(hostId,
StorageType.ISCSI,
false, null));
}
private void connectStorageToHost(Guid hostId, StorageType storageType, LogicalUnit unit) {
StorageServerConnections cnx =
StorageDomainHelper.getConnection(storageType,
unit.getAddress(),
unit.getTarget(),
unit.getUsername(),
unit.getPassword(),
unit.getPort());
performAction(VdcActionType.ConnectStorageToVds,
new StorageServerConnectionParametersBase(cnx, hostId, false));
}
@Override
public Response add(StorageDomain storageDomain) {
validateParameters(storageDomain, "host.id|name", "type", "storage");
validateRange("nfs_timeio", storageDomain.getStorage().getNfsTimeo(), 0, 65535);
validateRange("nfs_retrans", storageDomain.getStorage().getNfsRetrans(), 0, 65535);
HostStorage storageConnectionFromUser = storageDomain.getStorage();
Guid hostId = getHostId(storageDomain);
StorageServerConnections cnx = null;
if (!storageConnectionFromUser.isSetId()) {
validateParameters(storageDomain, "storage.type");
cnx = mapToCnx(storageDomain);
if (cnx.getStorageType().isFileDomain()) {
validateParameters(storageConnectionFromUser, "path");
}
}
else {
cnx = getStorageServerConnection(storageConnectionFromUser.getId());
storageDomain.getStorage().setType(mapType(cnx.getStorageType()));
}
StorageDomainStatic entity = mapToStatic(storageDomain);
Response resp = null;
switch (entity.getStorageType()) {
case ISCSI:
case FCP:
if (storageDomain.isSetImport() && storageDomain.isImport()) {
validateParameters(storageDomain, "id");
resp = addExistingSAN(storageDomain, entity.getStorageType(), hostId);
} else {
resp = addSAN(storageDomain, entity.getStorageType(), entity, hostId);
}
break;
case NFS:
if (!storageConnectionFromUser.isSetId()) {
validateParameters(storageDomain.getStorage(), "address");
}
resp = addDomain(VdcActionType.AddNFSStorageDomain, storageDomain, entity, hostId, cnx);
break;
case LOCALFS:
resp = addDomain(VdcActionType.AddLocalStorageDomain, storageDomain, entity, hostId, cnx);
break;
case POSIXFS:
if (!storageConnectionFromUser.isSetId()) {
validateParameters(storageDomain.getStorage(), "vfsType");
}
resp = addDomain(VdcActionType.AddPosixFsStorageDomain, storageDomain, entity, hostId, cnx);
break;
case GLUSTERFS:
if (!storageConnectionFromUser.isSetId()) {
validateParameters(storageDomain.getStorage(), "vfsType");
}
resp = addDomain(VdcActionType.AddGlusterFsStorageDomain, storageDomain, entity, hostId, cnx);
break;
default:
break;
}
if (resp != null) {
addLinks((StorageDomain) resp.getEntity(), getLinksToExclude(storageDomain));
}
return resp;
}
protected StorageDomainStatic mapToStatic(StorageDomain model) {
return getMapper(modelType, StorageDomainStatic.class).map(model, null);
}
protected org.ovirt.engine.api.model.StorageType mapType(StorageType type) {
return getMapper(StorageType.class, org.ovirt.engine.api.model.StorageType.class).map(type, null);
}
@Override
protected StorageDomain map(org.ovirt.engine.core.common.businessentities.StorageDomain entity,
StorageDomain template) {
StorageDomain model = super.map(entity, template);
// Mapping the connection properties only in case it is a non-filtered session
if (!isFiltered()) {
switch (entity.getStorageType()) {
case ISCSI:
mapVolumeGroupIscsi(model, entity);
break;
case FCP:
mapVolumeGroupFcp(model, entity);
break;
case NFS:
case LOCALFS:
case POSIXFS:
case GLUSTERFS:
mapFileDomain(model, entity);
break;
}
}
return model;
}
protected void mapFileDomain(StorageDomain model,
org.ovirt.engine.core.common.businessentities.StorageDomain entity) {
final HostStorage storage = model.getStorage();
StorageServerConnections cnx = getStorageServerConnection(entity.getStorage());
if (cnx.getConnection().contains(":")) {
String[] parts = cnx.getConnection().split(":");
storage.setAddress(parts[0]);
storage.setPath(parts[1]);
} else {
storage.setPath(cnx.getConnection());
}
storage.setMountOptions(cnx.getMountOptions());
storage.setVfsType(cnx.getVfsType());
if (entity.getStorageType() == StorageType.NFS) {
if (cnx.getNfsRetrans() != null) {
storage.setNfsRetrans(cnx.getNfsRetrans().intValue());
}
if (cnx.getNfsTimeo() != null) {
storage.setNfsTimeo(cnx.getNfsTimeo().intValue());
}
if (cnx.getNfsVersion() != null) {
storage.setNfsVersion(StorageDomainMapper.map(cnx.getNfsVersion(), null));
}
}
}
protected void mapVolumeGroupIscsi(StorageDomain model,
org.ovirt.engine.core.common.businessentities.StorageDomain entity) {
VolumeGroup vg = model.getStorage().getVolumeGroup();
List<LUNs> luns = getLunsByVgId(vg.getId());
if (luns != null && !luns.isEmpty()) {
vg.setLogicalUnits(new LogicalUnits());
for (LUNs lun : luns) {
List<StorageServerConnections> lunConnections = lun.getLunConnections();
if (lunConnections != null) {
for (StorageServerConnections cnx : lunConnections) {
LogicalUnit unit = map(lun);
unit = map(cnx, unit);
vg.getLogicalUnits().getLogicalUnits().add(unit);
}
}
}
}
}
protected void mapVolumeGroupFcp(StorageDomain model,
org.ovirt.engine.core.common.businessentities.StorageDomain entity) {
VolumeGroup vg = model.getStorage().getVolumeGroup();
List<LUNs> luns = getLunsByVgId(vg.getId());
if (luns != null && !luns.isEmpty()) {
vg.setLogicalUnits(new LogicalUnits());
for (LUNs lun : luns) {
LogicalUnit unit = map(lun);
vg.getLogicalUnits().getLogicalUnits().add(unit);
}
}
}
protected LogicalUnit map(LUNs lun) {
return getMapper(LUNs.class, LogicalUnit.class).map(lun, null);
}
protected LogicalUnit map(StorageServerConnections cnx, LogicalUnit template) {
return getMapper(StorageServerConnections.class, LogicalUnit.class).map(cnx, template);
}
protected StorageType map(org.ovirt.engine.api.model.StorageType type) {
return getMapper(org.ovirt.engine.api.model.StorageType.class, StorageType.class).map(type, null);
}
protected org.ovirt.engine.api.model.StorageType map(StorageType type) {
return getMapper(StorageType.class, org.ovirt.engine.api.model.StorageType.class).map(type, null);
}
private StorageDomains mapCollection(List<org.ovirt.engine.core.common.businessentities.StorageDomain> entities) {
StorageDomains collection = new StorageDomains();
for (org.ovirt.engine.core.common.businessentities.StorageDomain entity : entities) {
StorageDomain storageDomain = map(entity);
// status is only relevant in the context of a data-center, so it can either be 'Unattached' or null.
if (StorageDomainSharedStatus.Unattached.equals(entity.getStorageDomainSharedStatus())) {
storageDomain.setStatus(StorageDomainStatus.UNATTACHED);
} else {
storageDomain.setStatus(null);
}
collection.getStorageDomains().add(addLinks(storageDomain, getLinksToExclude(storageDomain)));
}
return collection;
}
protected StorageServerConnections mapToCnx(StorageDomain model) {
return getMapper(StorageDomain.class,
StorageServerConnections.class).map(model, null);
}
private Guid getHostId(StorageDomain storageDomain) {
// presence of host ID or name already validated
return storageDomain.getHost().isSetId()
? new Guid(storageDomain.getHost().getId())
: storageDomain.getHost().isSetName()
? getEntity(VdsStatic.class,
VdcQueryType.GetVdsStaticByName,
new NameQueryParameters(storageDomain.getHost().getName()),
"Hosts: name=" + storageDomain.getHost().getName()).getId()
: null;
}
private String addStorageServerConnection(StorageServerConnections cnx, Guid hostId) {
return performAction(VdcActionType.AddStorageServerConnection,
new StorageServerConnectionParametersBase(cnx, hostId, false),
String.class);
}
private String removeStorageServerConnection(StorageServerConnections cnx, Guid hostId) {
return performAction(VdcActionType.RemoveStorageServerConnection,
new StorageServerConnectionParametersBase(cnx, hostId, false),
String.class);
}
private StorageServerConnections getStorageServerConnection(String id) {
return getEntity(StorageServerConnections.class,
VdcQueryType.GetStorageServerConnectionById,
new StorageServerConnectionQueryParametersBase(id),
"Storage server connection: id=" + id);
}
private List<LUNs> getLunsByVgId(String vgId) {
return asCollection(LUNs.class,
getEntity(List.class,
VdcQueryType.GetLunsByVgId,
new GetLunsByVgIdParameters(vgId),
"LUNs for volume group: id=" + vgId));
}
private org.ovirt.engine.core.common.businessentities.StorageDomain getExistingStorageDomain(Guid hostId,
StorageType storageType,
StorageDomainType domainType,
StorageServerConnections cnx) {
List<org.ovirt.engine.core.common.businessentities.StorageDomain> existing =
asCollection(org.ovirt.engine.core.common.businessentities.StorageDomain.class,
getEntity(ArrayList.class,
VdcQueryType.GetExistingStorageDomainList,
new GetExistingStorageDomainListParameters(hostId,
storageType,
domainType,
cnx.getConnection()),
"Existing storage domains: path=" + cnx.getConnection()));
return existing.size() != 0 ? existing.get(0) : null;
}
private StorageDomainManagementParameter getAddParams(StorageDomainStatic entity, Guid hostId) {
StorageDomainManagementParameter params = new StorageDomainManagementParameter(entity);
params.setVdsId(hostId);
return params;
}
private AddSANStorageDomainParameters getSanAddParams(StorageDomainStatic entity,
Guid hostId,
ArrayList<String> lunIds,
boolean force) {
AddSANStorageDomainParameters params = new AddSANStorageDomainParameters(entity);
params.setVdsId(hostId);
params.setLunIds(lunIds);
params.setForce(force);
return params;
}
@Override
protected StorageDomain addParents(StorageDomain model) {
StorageDomainHelper.addAttachedDataCenterReferences(this, model);
return model;
}
}