package org.ovirt.engine.api.restapi.resource; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.ovirt.engine.api.common.util.StatusUtils; 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.StorageDomainType; import org.ovirt.engine.api.model.Template; import org.ovirt.engine.api.model.Templates; import org.ovirt.engine.api.model.VM; import org.ovirt.engine.api.model.VMs; import org.ovirt.engine.api.resource.AssignedPermissionsResource; import org.ovirt.engine.api.resource.StorageDomainContentsResource; import org.ovirt.engine.api.resource.FilesResource; import org.ovirt.engine.api.resource.StorageDomainResource; import org.ovirt.engine.core.common.VdcObjectType; import org.ovirt.engine.core.common.action.ExtendSANStorageDomainParameters; import org.ovirt.engine.core.common.action.StorageDomainManagementParameter; import org.ovirt.engine.core.common.action.StorageServerConnectionParametersBase; import org.ovirt.engine.core.common.action.VdcActionParametersBase; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.businessentities.StorageDomainSharedStatus; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.storage_domains; import org.ovirt.engine.core.common.businessentities.storage_domain_static; 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.GetPermissionsForObjectParameters; import org.ovirt.engine.core.common.queries.StorageDomainQueryParametersBase; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.common.businessentities.StorageType; import org.ovirt.engine.api.restapi.util.StorageDomainHelper; import static org.ovirt.engine.api.restapi.resource.BackendStorageDomainsResource.SUB_COLLECTIONS; public class BackendStorageDomainResource extends AbstractBackendSubResource<StorageDomain, storage_domains> implements StorageDomainResource { private BackendStorageDomainsResource parent; public BackendStorageDomainResource(String id, BackendStorageDomainsResource parent) { super(id, StorageDomain.class, storage_domains.class, SUB_COLLECTIONS); this.parent = parent; } BackendStorageDomainsResource getParent() { return parent; } @Override public StorageDomain get() { StorageDomain storageDomain = performGet(VdcQueryType.GetStorageDomainById, new StorageDomainQueryParametersBase(guid)); return addLinks(storageDomain, getLinksToExclude(storageDomain)); } @Override public StorageDomain update(StorageDomain incoming) { QueryIdResolver storageDomainResolver = new QueryIdResolver(VdcQueryType.GetStorageDomainById, StorageDomainQueryParametersBase.class); storage_domains entity = getEntity(storageDomainResolver, true); StorageDomain model = map(entity, new StorageDomain()); StorageType storageType = entity.getstorage_type(); if (storageType != null) { switch (storageType) { case ISCSI: case FCP: extendStorageDomain(incoming, model, storageType); break; default: break; } } return addLinks(performUpdate(incoming, entity, model, storageDomainResolver, VdcActionType.UpdateStorageDomain, new UpdateParametersProvider()), new String[]{"templates", "vms"}); } @Override public FilesResource getFilesResource() { return inject(new BackendFilesResource(id)); } public static synchronized boolean isIsoDomain(StorageDomain storageDomain) { StorageDomainType type = StorageDomainType.fromValue(storageDomain.getType()); return type != null && type == StorageDomainType.ISO ? true : false; } public static synchronized boolean isIsoDomain(storage_domains storageDomain) { org.ovirt.engine.core.common.businessentities.StorageDomainType type = storageDomain.getstorage_domain_type() ; return type != null && type == org.ovirt.engine.core.common.businessentities.StorageDomainType.ISO ? true : false; } public static synchronized boolean isExportDomain(StorageDomain storageDomain) { StorageDomainType type = StorageDomainType.fromValue(storageDomain.getType()); return type != null && type == StorageDomainType.EXPORT ? true : false; } public static synchronized String[] getLinksToExclude(StorageDomain storageDomain) { return isIsoDomain(storageDomain) ? new String[]{"templates", "vms"} : isExportDomain(storageDomain) ? new String[]{"files"} : new String[]{"templates", "vms", "files"}; } /** * if user added new LUNs - extend the storage domain. * @param incoming */ private void extendStorageDomain(StorageDomain incoming, StorageDomain storageDomain, StorageType storageType) { List<LogicalUnit> existingLuns = storageDomain.getStorage().getVolumeGroup().getLogicalUnits(); List<LogicalUnit> incomingLuns = getIncomingLuns(incoming.getStorage()); List<LogicalUnit> newLuns = findNewLuns(existingLuns, incomingLuns); if (!newLuns.isEmpty()) { //If there are new LUNs, this means the user wants to extend the storage domain. //Supplying a host is necessary for this operation, but not for regular update //of storage-domain. So only now is the time for this validation. validateParameters(incoming, "host.id|name"); addLunsToStorageDomain(incoming, storageType, newLuns); //Remove the new LUNs from the incoming LUns before update, since they have already been dealt with. incomingLuns.removeAll(newLuns); } } private void addLunsToStorageDomain(StorageDomain incoming, StorageType storageType, List<LogicalUnit> newLuns) { for (LogicalUnit lun : newLuns) { if (lun.isSetAddress() && lun.isSetTarget()) { storage_server_connections connection = StorageDomainHelper.getConnection(storageType, lun.getAddress(), lun.getTarget(), lun.getUsername(), lun.getPassword(), lun.getPort()); performAction(VdcActionType.ConnectStorageToVds, new StorageServerConnectionParametersBase(connection, getHostId(incoming))); } } refreshVDSM(incoming); ExtendSANStorageDomainParameters params = createParameters(guid, newLuns); performAction(VdcActionType.ExtendSANStorageDomain, params); } //This is a work-around for a VDSM bug. The call to GetDeviceList causes a refresh in the VDSM, without which the creation will fail. private void refreshVDSM(StorageDomain incoming) { getEntity(Object.class, VdcQueryType.GetDeviceList, new GetDeviceListQueryParameters(getHostId(incoming), StorageType.ISCSI), ""); } @Override public AssignedPermissionsResource getPermissionsResource() { return inject(new BackendAssignedPermissionsResource(guid, VdcQueryType.GetPermissionsForObject, new GetPermissionsForObjectParameters(guid), StorageDomain.class, VdcObjectType.Storage)); } @Override protected StorageDomain map(storage_domains entity, StorageDomain template) { return parent.map(entity, template); } @Override protected StorageDomain populate(StorageDomain model, storage_domains entity) { if (StorageDomainSharedStatus.Unattached.equals(entity.getstorage_domain_shared_status())) { model.setStatus(StatusUtils.create(StorageDomainStatus.UNATTACHED)); } else { model.setStatus(null); } return super.populate(model, entity); } private List<LogicalUnit> getIncomingLuns(Storage storage) { //user may pass the LUNs under Storage, or Storage-->VolumeGroup; both are supported. return !storage.getLogicalUnits().isEmpty() ? storage.getLogicalUnits() : storage.getVolumeGroup().getLogicalUnits(); } 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 ExtendSANStorageDomainParameters createParameters(Guid storageDomainId, List<LogicalUnit> newLuns) { ExtendSANStorageDomainParameters params = new ExtendSANStorageDomainParameters(); params.setStorageDomainId(storageDomainId); ArrayList<String> lunIds = new ArrayList<String>(); for (LogicalUnit newLun : newLuns) { lunIds.add(newLun.getId()); } params.setLunIds(lunIds); return params; } private List<LogicalUnit> findNewLuns(List<LogicalUnit> existingLuns, List<LogicalUnit> incomingLuns) { List<LogicalUnit> newLuns = new LinkedList<LogicalUnit>(); for (LogicalUnit incomingLun : incomingLuns) { boolean found = false; for (LogicalUnit existingLun : existingLuns) { if (lunsEqual(incomingLun, existingLun)) { found = true; break; } } if (!found) { newLuns.add(incomingLun); } } return newLuns; } private boolean lunsEqual(LogicalUnit firstLun, LogicalUnit secondLun) { return firstLun.getId().equals(secondLun.getId()); } protected class UpdateParametersProvider implements ParametersProvider<StorageDomain, storage_domains> { @Override public VdcActionParametersBase getParameters(StorageDomain incoming, storage_domains entity) { storage_domain_static updated = getMapper(modelType, storage_domain_static.class).map( incoming, entity.getStorageStaticData()); return new StorageDomainManagementParameter(updated); } } @Override public StorageDomainContentsResource<Templates, Template> getStorageDomainTemplatesResource() { return inject(new BackendStorageDomainTemplatesResource(guid)); } @Override public StorageDomainContentsResource<VMs, VM> getStorageDomainVmsResource() { return inject(new BackendStorageDomainVmsResource(guid)); } }