/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.scaleio; import java.net.URI; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.model.Volume.ReplicationState; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.exceptions.DeviceControllerErrors; import com.emc.storageos.scaleio.api.restapi.ScaleIORestClient; import com.emc.storageos.scaleio.api.restapi.response.ScaleIOSnapshotVolumeResponse; import com.emc.storageos.scaleio.api.restapi.response.ScaleIOVolume; import com.emc.storageos.svcs.errorhandling.model.ServiceCoded; import com.emc.storageos.volumecontroller.CloneOperations; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; public class ScaleIOCloneOperations implements CloneOperations { private static Logger log = LoggerFactory.getLogger(ScaleIOCloneOperations.class); private DbClient dbClient; private ScaleIOHandleFactory scaleIOHandleFactory; public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } @SuppressWarnings("UnusedDeclaration") public void setScaleIOHandleFactory(ScaleIOHandleFactory scaleIOHandleFactory) { this.scaleIOHandleFactory = scaleIOHandleFactory; } @Override public void createSingleClone(StorageSystem storageSystem, URI sourceVolume, URI cloneVolume, Boolean createInactive, TaskCompleter taskCompleter) { try { ScaleIORestClient scaleIOHandle = scaleIOHandleFactory.using(dbClient).getClientHandle(storageSystem); Volume cloneObj = dbClient.queryObject(Volume.class, cloneVolume); BlockObject parent = BlockObject.fetch(dbClient, sourceVolume); String systemId = scaleIOHandle.getSystemId(); // Note: ScaleIO snapshots can be treated as full copies, hence re-use of #snapshotVolume here. ScaleIOSnapshotVolumeResponse result = scaleIOHandle.snapshotVolume(parent.getNativeId(), cloneObj.getLabel(), systemId); String nativeId = result.getVolumeIdList().get(0); ScaleIOHelper.updateSnapshotWithSnapshotVolumeResult(dbClient, cloneObj, systemId, nativeId, storageSystem); // Snapshots result does not provide capacity info, so we need to perform a queryVolume updateCloneFromQueryVolume(scaleIOHandle, cloneObj); dbClient.updateObject(cloneObj); StoragePool pool = dbClient.queryObject(StoragePool.class, cloneObj.getPool()); pool.removeReservedCapacityForVolumes(Arrays.asList(cloneObj.getId().toString())); ScaleIOHelper.updateStoragePoolCapacity(dbClient, scaleIOHandle, cloneObj); taskCompleter.ready(dbClient); } catch (Exception e) { Volume clone = dbClient.queryObject(Volume.class, cloneVolume); if (clone != null) { clone.setInactive(true); dbClient.updateObject(clone); } log.error("Encountered an exception", e); ServiceCoded code = DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation("createSingleClone", e.getMessage()); taskCompleter.error(dbClient, code); } } @Override public void detachSingleClone(StorageSystem storageSystem, URI cloneVolume, TaskCompleter taskCompleter) { log.info("START detachSingleClone operation"); // no operation, set to ready Volume clone = dbClient.queryObject(Volume.class, cloneVolume); ReplicationUtils.removeDetachedFullCopyFromSourceFullCopiesList(clone, dbClient); clone.setAssociatedSourceVolume(NullColumnValueGetter.getNullURI()); clone.setReplicaState(ReplicationState.DETACHED.name()); dbClient.updateObject(clone); taskCompleter.ready(dbClient); } @Override public void activateSingleClone(StorageSystem storageSystem, URI fullCopy, TaskCompleter completer) { // Not supported } private void updateCloneFromQueryVolume(ScaleIORestClient scaleIOHandle, Volume cloneObj) throws Exception { try { ScaleIOVolume vol = scaleIOHandle.queryVolume(cloneObj.getNativeId()); long size = Long.parseLong(vol.getSizeInKb()) * 1024L; cloneObj.setAllocatedCapacity(size); cloneObj.setProvisionedCapacity(size); } catch (Exception e) { log.warn("Failed to update full copy {} with size information: {}", cloneObj.getId(), e.getMessage()); throw e; } } @Override public void restoreFromSingleClone(StorageSystem storageSystem, URI clone, TaskCompleter completer) { throw new UnsupportedOperationException("Not yet implemented"); } @Override public void fractureSingleClone(StorageSystem storageSystem, URI sourceVolume, URI clone, TaskCompleter completer) { throw new UnsupportedOperationException("Not yet implemented"); } @Override public void resyncSingleClone(StorageSystem storageSystem, URI clone, TaskCompleter completer) { // no support } @Override public void createGroupClone(StorageSystem storage, List<URI> cloneList, Boolean createInactive, TaskCompleter taskCompleter) { try { ScaleIORestClient scaleIOHandle = scaleIOHandleFactory.using(dbClient).getClientHandle(storage); List<Volume> clones = dbClient.queryObject(Volume.class, cloneList); Map<String, String> parent2snap = new HashMap<>(); Set<URI> poolsToUpdate = new HashSet<>(); for (Volume clone : clones) { Volume parent = dbClient.queryObject(Volume.class, clone.getAssociatedSourceVolume()); parent2snap.put(parent.getNativeId(), clone.getLabel()); poolsToUpdate.add(parent.getPool()); } String systemId = scaleIOHandle.getSystemId(); ScaleIOSnapshotVolumeResponse result = scaleIOHandle.snapshotMultiVolume(parent2snap, systemId); List<String> nativeIds = result.getVolumeIdList(); Map<String, ScaleIOVolume> cloneNameMap = scaleIOHandle.getVolumeNameMap(nativeIds); Multimap<URI, String> poolToVolumesMap = ArrayListMultimap.create(); for (Volume clone : clones) { String name = clone.getLabel(); ScaleIOVolume sioVolume = cloneNameMap.get(name); ScaleIOHelper.updateSnapshotWithSnapshotVolumeResult(dbClient, clone, systemId, sioVolume.getId(), storage); clone.setAllocatedCapacity(Long.parseLong(sioVolume.getSizeInKb()) * 1024L); clone.setProvisionedCapacity(clone.getAllocatedCapacity()); clone.setCapacity(clone.getAllocatedCapacity()); clone.setReplicationGroupInstance(result.getSnapshotGroupId()); poolToVolumesMap.put(clone.getPool(), clone.getId().toString()); } dbClient.updateObject(clones); List<StoragePool> pools = dbClient.queryObject(StoragePool.class, Lists.newArrayList(poolsToUpdate)); for (StoragePool pool : pools) { pool.removeReservedCapacityForVolumes(poolToVolumesMap.get(pool.getId())); ScaleIOHelper.updateStoragePoolCapacity(dbClient, scaleIOHandle, pool, storage); } taskCompleter.ready(dbClient); } catch (Exception e) { log.error("Encountered an exception", e); ServiceCoded code = DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation("createGroupClone", e.getMessage()); taskCompleter.error(dbClient, code); } } @Override public void activateGroupClones(StorageSystem storage, List<URI> clone, TaskCompleter taskCompleter) { throw new UnsupportedOperationException("Not yet implemented"); } @Override public void restoreGroupClones(StorageSystem storageSystem, List<URI> clones, TaskCompleter completer) { throw new UnsupportedOperationException("Not yet implemented"); } @Override public void fractureGroupClones(StorageSystem storageSystem, List<URI> clones, TaskCompleter completer) { throw new UnsupportedOperationException("Not yet implemented"); } @Override public void resyncGroupClones(StorageSystem storageSystem, List<URI> clones, TaskCompleter completer) { throw new UnsupportedOperationException("Not yet implemented"); } @Override public void detachGroupClones(StorageSystem storageSystem, List<URI> clones, TaskCompleter completer) { List<Volume> cloneVolumes = dbClient.queryObject(Volume.class, clones); for (Volume theClone : cloneVolumes) { URI source = theClone.getAssociatedSourceVolume(); Volume sourceVol = dbClient.queryObject(Volume.class, source); if (sourceVol != null && sourceVol.getFullCopies() != null) { sourceVol.getFullCopies().remove(theClone.getId().toString()); } theClone.setAssociatedSourceVolume(NullColumnValueGetter.getNullURI()); theClone.setReplicaState(ReplicationState.DETACHED.name()); dbClient.updateObject(sourceVol); } dbClient.updateObject(cloneVolumes); if (completer != null) { completer.ready(dbClient); } } @Override public void establishVolumeCloneGroupRelation(StorageSystem storage, URI sourceVolume, URI clone, TaskCompleter completer) { // no support } }