package org.ovirt.engine.api.restapi.resource; 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.ovirt.engine.api.common.util.StatusUtils; import org.ovirt.engine.api.model.Fault; import org.ovirt.engine.api.model.LogicalUnit; import org.ovirt.engine.api.model.Storage; 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.core.common.action.AddSANStorageDomainParameters; import org.ovirt.engine.core.common.action.RemoveStorageDomainParameters; import org.ovirt.engine.core.common.action.StorageDomainManagementParameter; import org.ovirt.engine.core.common.action.StorageDomainParametersBase; import org.ovirt.engine.core.common.action.StorageServerConnectionParametersBase; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.businessentities.LUNs; import org.ovirt.engine.core.common.businessentities.StorageDomainSharedStatus; import org.ovirt.engine.core.common.businessentities.StorageDomainType; import org.ovirt.engine.core.common.businessentities.StorageType; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.storage_domain_static; import org.ovirt.engine.core.common.businessentities.storage_domains; import org.ovirt.engine.core.common.businessentities.storage_server_connections; 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.StorageDomainQueryParametersBase; import org.ovirt.engine.core.common.queries.StorageServerConnectionQueryParametersBase; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.api.restapi.util.StorageDomainHelper; import static org.ovirt.engine.api.restapi.resource.BackendStorageDomainResource.getLinksToExclude; public class BackendStorageDomainsResource extends AbstractBackendCollectionResource<StorageDomain, storage_domains> implements StorageDomainsResource { static final String[] SUB_COLLECTIONS = { "permissions", "files", "templates", "vms"}; private StorageDomain storageDomain = null; //utility variable; used in the context of a single activation of remove() private final EntityIdResolver ID_RESOLVER = new QueryIdResolver(VdcQueryType.GetStorageDomainById, StorageDomainQueryParametersBase.class); public BackendStorageDomainsResource() { super(StorageDomain.class, storage_domains.class, SUB_COLLECTIONS); } @Override public StorageDomains list() { return mapCollection(getBackendCollection(SearchType.StorageDomain)); } @Override @SingleEntityResource public StorageDomainResource getStorageDomainSubResource(String id) { return inject(new BackendStorageDomainResource(id, this)); } private Response addNfsOrLocal(VdcActionType action, StorageDomain model, storage_domain_static entity, Guid hostId) { storage_server_connections cnx = mapToCnx(model); entity.setstorage(addStorageServerConnection(cnx, hostId)); if (action == VdcActionType.AddNFSStorageDomain) { storage_domains existing = getExistingStorageDomain(hostId, entity.getstorage_type(), entity.getstorage_domain_type(), cnx); if (existing != null) { entity = existing.getStorageStaticData(); action = VdcActionType.AddExistingNFSStorageDomain; } } if (action != VdcActionType.AddExistingNFSStorageDomain) { validateParameters(model, 2, "name"); } return performCreation(action, getAddParams(entity, hostId), ID_RESOLVER); } private Response addSAN(StorageDomain model, StorageType storageType, storage_domain_static entity, Guid hostId) { return performCreation(VdcActionType.AddSANStorageDomain, getSanAddParams(entity, hostId, getLunIds(model.getStorage(), storageType, hostId)), ID_RESOLVER); } private ArrayList<String> getLunIds(Storage storage, StorageType storageType, Guid hostId) { List<LogicalUnit> logicalUnits = new ArrayList<LogicalUnit>(); if (storage.isSetLogicalUnits()) { logicalUnits = storage.getLogicalUnits(); } else if (storage.isSetVolumeGroup() && storage.getVolumeGroup().isSetLogicalUnits()) { logicalUnits = storage.getVolumeGroup().getLogicalUnits(); } ArrayList<String> lunIds = new ArrayList<String>(); 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. * @param hostId */ private void refreshHostStorage(Guid hostId) { getBackendCollection(VdcQueryType.GetDeviceList, new GetDeviceListQueryParameters(hostId, StorageType.ISCSI)); } private void connectStorageToHost(Guid hostId, StorageType storageType, LogicalUnit unit) { storage_server_connections cnx = StorageDomainHelper.getConnection(storageType, unit.getAddress(), unit.getTarget(), unit.getUsername(), unit.getPassword(), unit.getPort()); performAction(VdcActionType.ConnectStorageToVds, new StorageServerConnectionParametersBase(cnx, hostId)); } @Override public Response add(StorageDomain storageDomain) { validateParameters(storageDomain, "host.id|name", "type", "storage.type"); validateEnum(org.ovirt.engine.api.model.StorageDomainType.class, storageDomain.getType().toUpperCase()); validateEnum(org.ovirt.engine.api.model.StorageType.class, storageDomain.getStorage().getType().toUpperCase()); storage_domain_static entity = mapToStatic(storageDomain); Guid hostId = getHostId(storageDomain); Response resp = null; switch (entity.getstorage_type()) { case ISCSI: case FCP: resp = addSAN(storageDomain, entity.getstorage_type(), entity, hostId); break; case NFS: validateParameters(storageDomain.getStorage(), "address", "path"); resp = addNfsOrLocal(VdcActionType.AddNFSStorageDomain, storageDomain, entity, hostId); break; case LOCALFS: validateParameters(storageDomain.getStorage(), "path"); resp = addNfsOrLocal(VdcActionType.AddLocalStorageDomain, storageDomain, entity, hostId); break; default: break; } addLinks(((StorageDomain) resp.getEntity()), getLinksToExclude(storageDomain)); return resp; } @Override public Response remove(String id, StorageDomain storageDomain) { if (storageDomain==null) { Fault fault = new Fault(); fault.setReason("storage-domain parameter is missing"); throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(fault).build()); } validateParameters(storageDomain, "host.id|name"); this.storageDomain = storageDomain; return super.remove(id); } protected storage_domain_static mapToStatic(StorageDomain model) { return getMapper(modelType, storage_domain_static.class).map(model, null); } @Override protected StorageDomain map(storage_domains entity, StorageDomain template) { StorageDomain model = super.map(entity, template); switch (entity.getstorage_type()) { case ISCSI: case FCP: mapVolumeGroup(model, entity); break; case NFS: case LOCALFS: mapNfsOrLocal(model, entity); break; } return model; } protected void mapNfsOrLocal(StorageDomain model, storage_domains entity) { storage_server_connections cnx = getStorageServerConnection(entity.getstorage()); if (cnx.getconnection().contains(":")) { String[] parts = cnx.getconnection().split(":"); model.getStorage().setAddress(parts[0]); model.getStorage().setPath(parts[1]); } else { model.getStorage().setPath(cnx.getconnection()); } } protected void mapVolumeGroup(StorageDomain model, storage_domains entity) { VolumeGroup vg = model.getStorage().getVolumeGroup(); for (LUNs lun : getLunsByVgId(vg.getId())) { List<storage_server_connections> lunConnections = lun.getLunConnections(); if (lunConnections!=null) { for (storage_server_connections cnx : lunConnections) { LogicalUnit unit = map(lun); unit = map(cnx, unit); vg.getLogicalUnits().add(unit); } } } } protected LogicalUnit map(LUNs lun) { return getMapper(LUNs.class, LogicalUnit.class).map(lun, null); } protected LogicalUnit map(storage_server_connections cnx, LogicalUnit template) { return getMapper(storage_server_connections.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<storage_domains> entities) { StorageDomains collection = new StorageDomains(); for (storage_domains 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.getstorage_domain_shared_status())) { storageDomain.setStatus(StatusUtils.create(StorageDomainStatus.UNATTACHED)); } else { storageDomain.setStatus(null); } collection.getStorageDomains().add(addLinks(storageDomain, getLinksToExclude(storageDomain))); } return collection; } protected storage_server_connections mapToCnx(StorageDomain model) { return getMapper(StorageDomain.class, storage_server_connections.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(VDS.class, SearchType.VDS, "Hosts: name=" + storageDomain.getHost().getName()).getvds_id() : null; } private String addStorageServerConnection(storage_server_connections cnx, Guid hostId) { return performAction(VdcActionType.AddStorageServerConnection, new StorageServerConnectionParametersBase(cnx, hostId), String.class); } private storage_server_connections getStorageServerConnection(String id) { return getEntity(storage_server_connections.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 storage_domains getExistingStorageDomain(Guid hostId, StorageType storageType, StorageDomainType domainType, storage_server_connections cnx) { List<storage_domains> existing = asCollection(storage_domains.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(storage_domain_static entity, Guid hostId) { StorageDomainManagementParameter params = new StorageDomainManagementParameter(entity); params.setVdsId(hostId); return params; } private AddSANStorageDomainParameters getSanAddParams(storage_domain_static entity, Guid hostId, ArrayList<String> lunIds) { AddSANStorageDomainParameters params = new AddSANStorageDomainParameters(entity); params.setVdsId(hostId); params.setLunIds(lunIds); return params; } @Override protected Response performRemove(String id) { if (storageDomain.isSetDestroy() && storageDomain.isDestroy()) { StorageDomainParametersBase parameters = new StorageDomainParametersBase(asGuidOr404(id)); parameters.setVdsId(getHostId(storageDomain)); return performAction(VdcActionType.ForceRemoveStorageDomain, parameters); } else { RemoveStorageDomainParameters parameters = new RemoveStorageDomainParameters(asGuidOr404(id)); parameters.setVdsId(getHostId(storageDomain)); if (storageDomain.isSetFormat()) { parameters.setDoFormat(storageDomain.isFormat()); } return performAction(VdcActionType.RemoveStorageDomain, parameters); } } }