/* * Copyright (c) 2016 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.vnxunity; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; 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.model.BlockConsistencyGroup; import com.emc.storageos.db.client.model.BlockConsistencyGroup.Types; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.ExportMask; import com.emc.storageos.db.client.model.Initiator; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringMap; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.StringSetMap; import com.emc.storageos.db.client.model.TenantOrg; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.model.util.BlockConsistencyGroupUtils; import com.emc.storageos.db.client.util.NameGenerator; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.db.exceptions.DatabaseException; import com.emc.storageos.exceptions.DeviceControllerErrors; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.plugins.common.Constants; import com.emc.storageos.svcs.errorhandling.model.ServiceError; import com.emc.storageos.util.InvokeTestFailure; import com.emc.storageos.vnxe.VNXeApiClient; import com.emc.storageos.vnxe.VNXeConstants; import com.emc.storageos.vnxe.VNXeException; import com.emc.storageos.vnxe.models.BlockHostAccess; import com.emc.storageos.vnxe.models.HostTypeEnum; import com.emc.storageos.vnxe.models.StorageResource; import com.emc.storageos.vnxe.models.VNXeBase; import com.emc.storageos.vnxe.models.VNXeCommandJob; import com.emc.storageos.vnxe.models.VNXeCommandResult; import com.emc.storageos.vnxe.models.VNXeHost; import com.emc.storageos.volumecontroller.BlockStorageDevice; import com.emc.storageos.volumecontroller.ControllerException; import com.emc.storageos.volumecontroller.SnapshotOperations; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl; import com.emc.storageos.volumecontroller.impl.ControllerUtils; import com.emc.storageos.volumecontroller.impl.VolumeURIHLU; import com.emc.storageos.volumecontroller.impl.block.ExportMaskPolicy; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.CleanupMetaVolumeMembersCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.VolumeCreateCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.VolumeExpandCompleter; import com.emc.storageos.volumecontroller.impl.job.QueueJob; import com.emc.storageos.volumecontroller.impl.smis.ExportMaskOperations; import com.emc.storageos.volumecontroller.impl.smis.MetaVolumeRecommendation; import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper; import com.emc.storageos.volumecontroller.impl.vnxe.VNXeUtils; import com.emc.storageos.volumecontroller.impl.vnxe.job.VNXeCreateVolumesJob; import com.emc.storageos.volumecontroller.impl.vnxe.job.VNXeExpandVolumeJob; import com.emc.storageos.volumecontroller.impl.vnxe.job.VNXeJob; import com.emc.storageos.workflow.WorkflowService; import com.google.common.collect.Lists; public class VNXUnityBlockStorageDevice extends VNXUnityOperations implements BlockStorageDevice { private static final Logger logger = LoggerFactory.getLogger(VNXUnityBlockStorageDevice.class); private SnapshotOperations snapshotOperations; private NameGenerator nameGenerator; private WorkflowService workflowService; public NameGenerator getNameGenerator() { return nameGenerator; } public void setNameGenerator(NameGenerator nameGenerator) { this.nameGenerator = nameGenerator; } private ExportMaskOperations exportMaskOperationsHelper; public ExportMaskOperations getExportMaskOperationsHelper() { return exportMaskOperationsHelper; } public void setExportMaskOperationsHelper( ExportMaskOperations exportMaskOperationsHelper) { this.exportMaskOperationsHelper = exportMaskOperationsHelper; } public void setSnapshotOperations(final SnapshotOperations snapshotOperations) { this.snapshotOperations = snapshotOperations; } public void setWorkflowService(WorkflowService workflowService) { this.workflowService = workflowService; } @Override public void doConnect(StorageSystem storage) throws ControllerException { try { logger.info("doConnect {} - start", storage.getId()); VNXeApiClient client = getVnxUnityClient(storage); client.getStorageSystem(); String msg = String.format("doConnect %1$s - complete", storage.getId()); logger.info(msg); } catch (VNXeException e) { logger.error("doConnect failed.", e); throw DeviceControllerException.exceptions.connectStorageFailed(e); } } @Override public void doDisconnect(StorageSystem storage) { try { logger.info("doDisconnect {} - start", storage.getId()); VNXeApiClient client = getVnxUnityClient(storage); client.logout(); String msg = String.format("doDisconnect %1$s - complete", storage.getId()); logger.info(msg); } catch (VNXeException e) { logger.error("doDisconnect failed.", e); throw DeviceControllerException.exceptions.disconnectStorageFailed(e); } } @Override public void doCreateVolumes(StorageSystem storage, StoragePool storagePool, String opId, List<Volume> volumes, VirtualPoolCapabilityValuesWrapper capabilities, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("creating volumes, array: {}, pool : {}", storage.getSerialNumber(), storagePool.getNativeId()); VNXeApiClient apiClient = getVnxUnityClient(storage); List<String> jobs = new ArrayList<String>(); boolean opFailed = false; try { boolean isCG = false; Volume vol = volumes.get(0); String cgName = vol.getReplicationGroupInstance(); if (vol.getConsistencyGroup() != null && NullColumnValueGetter.isNotNullValue(cgName)) { isCG = true; } List<String> volNames = new ArrayList<String>(); String autoTierPolicyName = null; InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_022); for (Volume volume : volumes) { String tenantName = ""; try { TenantOrg tenant = dbClient.queryObject(TenantOrg.class, volume.getTenant() .getURI()); tenantName = tenant.getLabel(); } catch (DatabaseException e) { logger.error("Error lookup TenantOrb object", e); } String label = nameGenerator.generate(tenantName, volume.getLabel(), volume.getId() .toString(), '-', VNXeConstants.MAX_NAME_LENGTH); autoTierPolicyName = ControllerUtils.getAutoTieringPolicyName(volume.getId(), dbClient); if (autoTierPolicyName.equals(Constants.NONE)) { autoTierPolicyName = null; } volume.setNativeGuid(label); dbClient.updateObject(volume); if (!isCG) { VNXeCommandJob job = apiClient.createLun(label, storagePool.getNativeId(), volume.getCapacity(), volume.getThinlyProvisioned(), autoTierPolicyName); jobs.add(job.getId()); } else { volNames.add(label); } } if (isCG) { logger.info(String.format("cg %s for the volume", cgName)); String cgId = apiClient.getConsistencyGroupIdByName(cgName); VNXeUtils.getCGLock(workflowService, storage, cgName, opId); VNXeCommandJob job = apiClient.createLunsInConsistencyGroup(volNames, storagePool.getNativeId(), vol.getCapacity(), vol.getThinlyProvisioned(), autoTierPolicyName, cgId); jobs.add(job.getId()); } VNXeCreateVolumesJob createVolumesJob = new VNXeCreateVolumesJob(jobs, storage.getId(), taskCompleter, storagePool.getId(), isCG); ControllerServiceImpl.enqueueJob(new QueueJob(createVolumesJob)); InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_023); } catch (VNXeException e) { logger.error("Create volumes got the exception", e); opFailed = true; taskCompleter.error(dbClient, e); } catch (Exception ex) { logger.error("Create volumes got the exception", ex); opFailed = true; ServiceError error = DeviceControllerErrors.vnxe.jobFailed("CreateVolumes", ex.getMessage()); taskCompleter.error(dbClient, error); } if (opFailed) { for (Volume vol : volumes) { vol.setInactive(true); dbClient.updateObject(vol); } } } @Override public void doCreateMetaVolume(StorageSystem storage, StoragePool storagePool, Volume volume, VirtualPoolCapabilityValuesWrapper capabilities, MetaVolumeRecommendation recommendation, VolumeCreateCompleter completer) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doExpandVolume(StorageSystem storage, StoragePool pool, Volume volume, Long size, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info(String.format("Expand Volume Start - Array: %s, Pool: %s, Volume: %s, New size: %d", storage.getSerialNumber(), pool.getNativeGuid(), volume.getLabel(), size)); String cgName = volume.getReplicationGroupInstance(); String consistencyGroupId = null; try { VNXeApiClient apiClient = getVnxUnityClient(storage); if (NullColumnValueGetter.isNotNullValue(cgName)) { consistencyGroupId = apiClient.getConsistencyGroupIdByName(cgName); VNXeUtils.getCGLock(workflowService, storage, cgName, taskCompleter.getOpId()); } VNXeCommandJob commandJob = apiClient.expandLun(volume.getNativeId(), size, consistencyGroupId); VNXeExpandVolumeJob expandVolumeJob = new VNXeExpandVolumeJob(commandJob.getId(), storage.getId(), taskCompleter); ControllerServiceImpl.enqueueJob(new QueueJob(expandVolumeJob)); } catch (VNXeException e) { logger.error("Expand volume got the exception", e); taskCompleter.error(dbClient, e); } catch (Exception ex) { logger.error("Expand volume got the exception", ex); ServiceError error = DeviceControllerErrors.vnxe.jobFailed("ExpandVolume", ex.getMessage()); taskCompleter.error(dbClient, error); } } @Override public void doDeleteVolumes(StorageSystem storageSystem, String opId, List<Volume> volumes, TaskCompleter completer) throws DeviceControllerException { logger.info("deleting volumes, array: {}", storageSystem.getSerialNumber()); VNXeApiClient apiClient = getVnxUnityClient(storageSystem); Map<String, List<String>> cgNameMap = new HashMap<String, List<String>>(); try { Set<URI> updateStoragePools = new HashSet<URI>(); // Invoke a test failure if testing for (Volume volume : volumes) { String lunId = volume.getNativeId(); if (NullColumnValueGetter.isNullValue(lunId)) { logger.info(String.format("The volume %s does not have native id, do nothing", volume.getLabel())); continue; } updateStoragePools.add(volume.getPool()); if (!apiClient.checkLunExists(lunId)) { logger.info(String.format("The volume %s does not exist in the array, do nothing", volume.getLabel())); continue; } String cgName = volume.getReplicationGroupInstance(); if (NullColumnValueGetter.isNotNullValue(cgName)) { List<String> lunIds = cgNameMap.get(cgName); if (lunIds == null) { lunIds = new ArrayList<String>(); cgNameMap.put(cgName, lunIds); } lunIds.add(volume.getNativeId()); } else { InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_035); apiClient.deleteLunSync(volume.getNativeId(), false); InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_036); } } for (Map.Entry<String, List<String>> entry : cgNameMap.entrySet()) { String cgName = entry.getKey(); List<String> lunIDs = entry.getValue(); String cgId = apiClient.getConsistencyGroupIdByName(cgName); boolean isRP = false; if (cgId != null && !cgId.isEmpty()) { // Check if the CG has blockHostAccess to a RP host. if the CG is exported to a RP, we could not delete the lun // directly, we have to remove the volume from the CG first, then delete it. StorageResource cg = apiClient.getStorageResource(cgId); List<BlockHostAccess> hosts = cg.getBlockHostAccess(); if (hosts != null && !hosts.isEmpty()) { for (BlockHostAccess hostAccess : hosts) { VNXeBase hostId = hostAccess.getHost(); if (hostId != null) { VNXeHost host = apiClient.getHostById(hostId.getId()); if (host != null) { if (host.getType() == HostTypeEnum.RPA.getValue()) { // Remove the luns from the CG isRP = true; logger.info(String.format("Removing volumes from CG because the CG %sis exported to RP", cgName)); VNXeUtils.getCGLock(workflowService, storageSystem, cgName, opId); InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_034); apiClient.removeLunsFromConsistencyGroup(cgId, lunIDs); for (String lunId : lunIDs) { logger.info(String.format("Deleting the volume %s", lunId)); InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_035); apiClient.deleteLunSync(lunId, false); InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_036); } break; } } } } } } if (!isRP) { VNXeUtils.getCGLock(workflowService, storageSystem, cgName, opId); InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_034); apiClient.deleteLunsFromConsistencyGroup(cgId, lunIDs); InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_037); } } for (URI pool : updateStoragePools) { VNXeJob.updateStoragePoolCapacity(dbClient, apiClient, pool, null); } completer.ready(dbClient); } catch (VNXeException e) { logger.error("Delete volumes got the exception", e); completer.error(dbClient, e); } catch (Exception ex) { logger.error("Delete volumes got the exception", ex); ServiceError error = DeviceControllerErrors.vnxe.jobFailed("DeleteVolumes", ex.getMessage()); completer.error(dbClient, error); } } @Override public void doExportCreate(StorageSystem storage, ExportMask exportMask, Map<URI, Integer> volumeMap, List<Initiator> initiators, List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doExportGroupCreate START ...", storage.getSerialNumber()); VolumeURIHLU[] volumeLunArray = ControllerUtils.getVolumeURIHLUArray( storage.getSystemType(), volumeMap, dbClient); exportMaskOperationsHelper.createExportMask(storage, exportMask.getId(), volumeLunArray, targets, initiators, taskCompleter); logger.info("{} doExportGroupCreate END ...", storage.getSerialNumber()); } @Override public void doExportDelete(StorageSystem storage, ExportMask exportMask, List<URI> volumeURIs, List<URI> initiatorURIs, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doExportGroupDelete START ...", storage.getSerialNumber()); List<URI> volumes = new ArrayList<URI>(); StringMap maskVolumes = exportMask.getVolumes(); if (maskVolumes != null && !maskVolumes.isEmpty()) { for (String volURI : maskVolumes.keySet()) { volumes.add(URI.create(volURI)); } } List<Initiator> initiators = Lists.newArrayList(); if (initiatorURIs != null) { initiators.addAll(dbClient.queryObject(Initiator.class, initiatorURIs)); } exportMaskOperationsHelper.deleteExportMask(storage, exportMask.getId(), volumes, new ArrayList<URI>(), initiators, taskCompleter); logger.info("{} doExportGroupDelete END ...", storage.getSerialNumber()); } @Override public void doExportAddVolume(StorageSystem storage, ExportMask exportMask, URI volume, Integer lun, List<Initiator> initiators, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doExportAddVolume START ...", storage.getSerialNumber()); Map<URI, Integer> map = new HashMap<URI, Integer>(); map.put(volume, lun); VolumeURIHLU[] volumeLunArray = ControllerUtils.getVolumeURIHLUArray( storage.getSystemType(), map, dbClient); exportMaskOperationsHelper.addVolumes(storage, exportMask.getId(), volumeLunArray, initiators, taskCompleter); logger.info("{} doExportAddVolume END ...", storage.getSerialNumber()); } @Override public void doExportAddVolumes(StorageSystem storage, ExportMask exportMask, List<Initiator> initiators, Map<URI, Integer> volumes, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doExportAddVolume START ...", storage.getSerialNumber()); VolumeURIHLU[] volumeLunArray = ControllerUtils.getVolumeURIHLUArray( storage.getSystemType(), volumes, dbClient); exportMaskOperationsHelper.addVolumes(storage, exportMask.getId(), volumeLunArray, initiators, taskCompleter); logger.info("{} doExportAddVolume END ...", storage.getSerialNumber()); } @Override public void doExportRemoveVolume(StorageSystem storage, ExportMask exportMask, URI volume, List<Initiator> initiators, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doExportRemoveVolume START ...", storage.getSerialNumber()); exportMaskOperationsHelper.removeVolumes(storage, exportMask.getId(), Arrays.asList(volume), initiators, taskCompleter); logger.info("{} doExportRemoveVolume END ...", storage.getSerialNumber()); } @Override public void doExportRemoveVolumes(StorageSystem storage, ExportMask exportMask, List<URI> volumes, List<Initiator> initiators, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doExportRemoveVolume START ...", storage.getSerialNumber()); exportMaskOperationsHelper.removeVolumes(storage, exportMask.getId(), volumes, initiators, taskCompleter); logger.info("{} doExportRemoveVolume END ...", storage.getSerialNumber()); } @Override public void doExportAddInitiator(StorageSystem storage, ExportMask exportMask, List<URI> volumeURIs, Initiator initiator, List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doExportAddInitiator START ...", storage.getSerialNumber()); exportMaskOperationsHelper.addInitiators(storage, exportMask.getId(), volumeURIs, Arrays.asList(initiator), targets, taskCompleter); logger.info("{} doExportAddInitiator END ...", storage.getSerialNumber()); } @Override public void doExportAddInitiators(StorageSystem storage, ExportMask exportMask, List<URI> volumeURIs, List<Initiator> initiators, List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doExportAddInitiator START ...", storage.getSerialNumber()); exportMaskOperationsHelper.addInitiators(storage, exportMask.getId(), volumeURIs, initiators, targets, taskCompleter); logger.info("{} doExportAddInitiator END ...", storage.getSerialNumber()); } @Override public void doExportRemoveInitiator(StorageSystem storage, ExportMask exportMask, List<URI> volumeURIs, Initiator initiator, List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doExportRemoveInitiator START ...", storage.getSerialNumber()); exportMaskOperationsHelper.removeInitiators(storage, exportMask.getId(), volumeURIs, Arrays.asList(initiator), targets, taskCompleter); logger.info("{} doExportRemoveInitiator END ...", storage.getSerialNumber()); } @Override public void doExportRemoveInitiators(StorageSystem storage, ExportMask exportMask, List<URI> volumeURIs, List<Initiator> initiators, List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doExportRemoveInitiator START ...", storage.getSerialNumber()); exportMaskOperationsHelper.removeInitiators(storage, exportMask.getId(), volumeURIs, initiators, targets, taskCompleter); logger.info("{} doExportRemoveInitiator END ...", storage.getSerialNumber()); } @Override public void doCreateSingleSnapshot(StorageSystem storage, List<URI> snapshotList, Boolean createInactive, Boolean readOnly, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doCreateSingleSnapshot START ...", storage.getSerialNumber()); List<BlockSnapshot> snapshots = dbClient .queryObject(BlockSnapshot.class, snapshotList); URI snapshot = snapshots.get(0).getId(); snapshotOperations.createSingleVolumeSnapshot(storage, snapshot, createInactive, readOnly, taskCompleter); logger.info("{} doCreateSingleSnapshot END ...", storage.getSerialNumber()); } @Override public void doCreateSnapshot(StorageSystem storage, List<URI> snapshotList, Boolean createInactive, Boolean readOnly, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doCreateSnapshot START ...", storage.getSerialNumber()); List<BlockSnapshot> snapshots = dbClient .queryObject(BlockSnapshot.class, snapshotList); if (ControllerUtils.checkSnapshotsInConsistencyGroup(snapshots, dbClient, taskCompleter)) { snapshotOperations.createGroupSnapshots(storage, snapshotList, createInactive, readOnly, taskCompleter); } else { URI snapshot = snapshots.get(0).getId(); snapshotOperations.createSingleVolumeSnapshot(storage, snapshot, createInactive, readOnly, taskCompleter); } logger.info("{} doCreateSnapshot END ...", storage.getSerialNumber()); } @Override public void doActivateSnapshot(StorageSystem storage, List<URI> snapshotList, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doActivateSnapshot START ...", storage.getSerialNumber()); List<BlockSnapshot> snapshots = dbClient.queryObject(BlockSnapshot.class, snapshotList); URI snapshot = snapshots.get(0).getId(); if (ControllerUtils.checkSnapshotsInConsistencyGroup(snapshots, dbClient, taskCompleter)) { snapshotOperations.activateGroupSnapshots(storage, snapshot, taskCompleter); } else { snapshotOperations.activateSingleVolumeSnapshot(storage, snapshot, taskCompleter); } logger.info("{} doActivateSnapshot END ...", storage.getSerialNumber()); } @Override public void doDeleteSnapshot(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doDeleteSnapshot START ...", storage.getSerialNumber()); List<BlockSnapshot> snapshots = dbClient.queryObject(BlockSnapshot.class, Arrays.asList(snapshot)); if (ControllerUtils.checkSnapshotsInConsistencyGroup(snapshots, dbClient, taskCompleter)) { snapshotOperations.deleteGroupSnapshots(storage, snapshot, taskCompleter); } else { snapshotOperations.deleteSingleVolumeSnapshot(storage, snapshot, taskCompleter); } logger.info("{} doDeleteSnapshot END ...", storage.getSerialNumber()); } @Override public void doDeleteSelectedSnapshot(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doDeleteSelectedSnapshot START ...", storage.getSerialNumber()); try { snapshotOperations.deleteSingleVolumeSnapshot(storage, snapshot, taskCompleter); } catch (DatabaseException e) { String message = String.format( "IO exception when trying to delete snapshot(s) on array %s", storage.getSerialNumber()); logger.error(message, e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doDeleteSnapshot", e.getMessage()); taskCompleter.error(dbClient, error); } logger.info("{} doDeleteSelectedSnapshot END ...", storage.getSerialNumber()); } @Override public void doRestoreFromSnapshot(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("{} doRestoreFromSnapshot START ...", storage.getSerialNumber()); List<BlockSnapshot> snapshots = dbClient.queryObject(BlockSnapshot.class, Arrays.asList(snapshot)); if (ControllerUtils.checkSnapshotsInConsistencyGroup(snapshots, dbClient, taskCompleter)) { snapshotOperations.restoreGroupSnapshots(storage, volume, snapshot, taskCompleter); } else { snapshotOperations.restoreSingleVolumeSnapshot(storage, volume, snapshot, taskCompleter); } logger.info("{} doRestoreFromSnapshot END ...", storage.getSerialNumber()); } @Override public void doFractureMirror(StorageSystem storage, URI mirror, Boolean sync, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doDetachMirror(StorageSystem storage, URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doResumeNativeContinuousCopy(StorageSystem storage, URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doEstablishVolumeNativeContinuousCopyGroupRelation( StorageSystem storage, URI sourceVolume, URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doEstablishVolumeSnapshotGroupRelation( StorageSystem storage, URI sourceVolume, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doDeleteMirror(StorageSystem storage, URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doCreateClone(StorageSystem storageSystem, URI sourceVolume, URI cloneVolume, Boolean createInactive, TaskCompleter taskCompleter) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doDetachClone(StorageSystem storage, URI cloneVolume, TaskCompleter taskCompleter) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doCreateConsistencyGroup(StorageSystem storage, URI consistencyGroup, String replicationGroupName, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("creating consistency group, array: {}", storage.getSerialNumber()); BlockConsistencyGroup consistencyGroupObj = dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroup); VNXeApiClient apiClient = getVnxUnityClient(storage); String label = null; if (NullColumnValueGetter.isNotNullValue(replicationGroupName)) { label = replicationGroupName; } else { label = consistencyGroupObj.getLabel(); } try { VNXeCommandResult result = apiClient.createConsistencyGroup(label); if (result.getStorageResource() != null) { consistencyGroupObj.addSystemConsistencyGroup(storage.getId().toString(), label); consistencyGroupObj.addConsistencyGroupTypes(Types.LOCAL.name()); if (NullColumnValueGetter.isNullURI(consistencyGroupObj.getStorageController())) { consistencyGroupObj.setStorageController(storage.getId()); } dbClient.updateObject(consistencyGroupObj); taskCompleter.ready(dbClient); logger.info("Consistency group {} created", label); } else { logger.error("No storage resource Id returned"); BlockConsistencyGroupUtils.cleanUpCGAndUpdate(consistencyGroupObj, storage.getId(), null, false, dbClient); ServiceError error = DeviceControllerErrors.vnxe.jobFailed("CreateConsistencyGroup failed"); taskCompleter.error(dbClient, error); } } catch (Exception e) { logger.error("Exception caught when creating consistency group ", e); BlockConsistencyGroupUtils.cleanUpCGAndUpdate(consistencyGroupObj, storage.getId(), null, false, dbClient); ServiceError error = DeviceControllerErrors.vnxe.jobFailed("CreateConsistencyGroup", e.getMessage()); taskCompleter.error(dbClient, error); } } @Override public void doDeleteConsistencyGroup(StorageSystem storage, URI consistencyGroupId, String replicationGroupName, Boolean keepRGName, Boolean markInactive, TaskCompleter taskCompleter) throws DeviceControllerException { logger.info("Deleting consistency group, array: {}", storage.getSerialNumber()); BlockConsistencyGroup consistencyGroup = dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId); StringSet cgNames = new StringSet(); if (NullColumnValueGetter.isNullValue(replicationGroupName)) { StringSetMap ssm = consistencyGroup.getSystemConsistencyGroups(); if (ssm != null) { cgNames = ssm.get(storage.getId().toString()); if (cgNames == null || cgNames.isEmpty()) { logger.info("There is no array consistency group to be deleted."); // Clean up the system consistency group references BlockConsistencyGroupUtils.cleanUpCGAndUpdate(consistencyGroup, storage.getId(), null, markInactive, dbClient); taskCompleter.ready(dbClient); return; } } } else { cgNames.add(replicationGroupName); } VNXeApiClient apiClient = getVnxUnityClient(storage); try { for (String cgName : cgNames) { logger.info("Deleting the consistency group {}", cgName); String id = apiClient.getConsistencyGroupIdByName(cgName); if (id != null && !id.isEmpty()) { apiClient.deleteConsistencyGroup(id, false, false); } if (!keepRGName) { // Clean up the system consistency group references BlockConsistencyGroupUtils.cleanUpCGAndUpdate(consistencyGroup, storage.getId(), cgName, markInactive, dbClient); if (consistencyGroup.getInactive()) { logger.info("CG is deleted"); } } } taskCompleter.ready(dbClient); } catch (Exception e) { logger.info("Failed to delete consistency group: " + e); // Set task to error ServiceError error = DeviceControllerErrors.vnxe.jobFailed( "doDeleteConsistencyGroup", e.getMessage()); taskCompleter.error(dbClient, error); } } @Override public void doDeleteConsistencyGroup(StorageSystem storage, final URI consistencyGroupId, String replicationGroupName, Boolean keepRGName, Boolean markInactive, String sourceReplicationGroup, final TaskCompleter taskCompleter) throws DeviceControllerException { doDeleteConsistencyGroup(storage, consistencyGroupId, replicationGroupName, keepRGName, markInactive, taskCompleter); } @Override public String doAddStorageSystem(StorageSystem storage) throws DeviceControllerException { // TODO Auto-generated method stub return null; } @Override public void doRemoveStorageSystem(StorageSystem storage) throws DeviceControllerException { // TODO Auto-generated method stub } @Override public void doCopySnapshotsToTarget(StorageSystem storage, List<URI> snapshotList, TaskCompleter taskCompleter) throws DeviceControllerException { // TODO Auto-generated method stub } @Override public Map<String, Set<URI>> findExportMasks(StorageSystem storage, List<String> initiatorNames, boolean mustHaveAllPorts) throws DeviceControllerException { // TODO Auto-generated method stub return null; } @Override public Set<Integer> findHLUsForInitiators(StorageSystem storage, List<String> initiatorNames, boolean mustHaveAllPorts) { return exportMaskOperationsHelper.findHLUsForInitiators(storage, initiatorNames, mustHaveAllPorts); } @Override public ExportMask refreshExportMask(StorageSystem storage, ExportMask mask) throws DeviceControllerException { return exportMaskOperationsHelper.refreshExportMask(storage, mask); } @Override public void doActivateFullCopy(StorageSystem storageSystem, URI fullCopy, TaskCompleter completer) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doCleanupMetaMembers(StorageSystem storageSystem, Volume volume, CleanupMetaVolumeMembersCompleter cleanupCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public Integer checkSyncProgress(URI storage, URI source, URI target) { // TODO Auto-generated method stub return null; } @Override public void doWaitForSynchronized(Class<? extends BlockObject> clazz, StorageSystem storageObj, URI target, TaskCompleter completer) { return; } @Override public void doWaitForGroupSynchronized(StorageSystem storageObj, List<URI> target, TaskCompleter completer) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doAddToConsistencyGroup(StorageSystem storage, URI consistencyGroupId, String replicationGroupName, List<URI> blockObjects, TaskCompleter taskCompleter) throws DeviceControllerException { BlockConsistencyGroup consistencyGroup = dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId); VNXeApiClient apiClient = getVnxUnityClient(storage); try { List<String> luns = new ArrayList<String>(); for (URI volume : blockObjects) { BlockObject blockObject = BlockObject.fetch(dbClient, volume); if (blockObject instanceof Volume) { Volume lun = (Volume) blockObject; luns.add(lun.getNativeId()); } else { String errorStr = String.format("The blockObject %s is not a volume. it is not supported.", volume.toString()); logger.error(errorStr); handleAddToCGError(blockObjects, taskCompleter, consistencyGroup.getLabel(), replicationGroupName, errorStr); return; } } String cgNativeId = apiClient.getConsistencyGroupIdByName(replicationGroupName); if (cgNativeId == null || cgNativeId.isEmpty()) { String errorStr = String.format("Could not find the consistency group %s in the error", replicationGroupName); logger.error(errorStr); handleAddToCGError(blockObjects, taskCompleter, consistencyGroup.getLabel(), replicationGroupName, errorStr); return; } apiClient.addLunsToConsistencyGroup(cgNativeId, luns); for (URI blockObjectURI : blockObjects) { BlockObject blockObject = BlockObject.fetch(dbClient, blockObjectURI); if (blockObject != null) { blockObject.setConsistencyGroup(consistencyGroupId); } dbClient.updateObject(blockObject); } taskCompleter.ready(dbClient); logger.info("Added volumes to the consistency group successfully"); } catch (Exception e) { logger.error("Exception caught when adding volumes to the consistency group ", e); handleAddToCGError(blockObjects, taskCompleter, consistencyGroup.getLabel(), replicationGroupName, e.getMessage()); } } /** * Handle the error case when adding volumes to a consistency group * * @param blockObjects * The list of block objects URIs that are adding to the consistency group * @param taskCompleter * The task completer instance * @param cgName * The consistency group name * @param replicationGroupName * The replication group name in the array * @param msg * The error message */ private void handleAddToCGError(List<URI> blockObjects, TaskCompleter taskCompleter, String cgName, String replicationGroupName, String msg) { // Remove any references to the consistency group for (URI blockObjectURI : blockObjects) { BlockObject blockObject = BlockObject.fetch(dbClient, blockObjectURI); if (blockObject != null) { blockObject.setConsistencyGroup(NullColumnValueGetter.getNullURI()); } dbClient.updateObject(blockObject); } taskCompleter.error(dbClient, DeviceControllerException.exceptions .failedToAddMembersToConsistencyGroup(cgName, replicationGroupName, msg)); } @Override public void doRemoveFromConsistencyGroup(StorageSystem storage, URI consistencyGroupId, List<URI> blockObjects, TaskCompleter taskCompleter) throws DeviceControllerException { BlockConsistencyGroup consistencyGroup = dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId); VNXeApiClient apiClient = getVnxUnityClient(storage); try { List<String> luns = new ArrayList<String>(); String cgName = null; // All volumes belongs to the same array consistency group. for (URI volume : blockObjects) { BlockObject blockObject = BlockObject.fetch(dbClient, volume); if (blockObject != null && !blockObject.getInactive() && !NullColumnValueGetter.isNullValue(blockObject.getReplicationGroupInstance())) { luns.add(blockObject.getNativeId()); if (cgName == null) { cgName = blockObject.getReplicationGroupInstance(); } } } if (cgName != null) { String cgId = apiClient.getConsistencyGroupIdByName(cgName); apiClient.removeLunsFromConsistencyGroup(cgId, luns); } else { logger.warn(String.format("The block object %s is not in a CG", blockObjects.get(0).toString())); } taskCompleter.ready(dbClient); logger.info("Remove volumes from the consistency group successfully"); } catch (Exception e) { logger.error("Exception caught when removing volumes from the consistency group ", e); taskCompleter.error(dbClient, DeviceControllerException.exceptions .failedToRemoveMembersToConsistencyGroup(consistencyGroup.getLabel(), consistencyGroup.getCgNameOnStorageSystem(storage.getId()), e.getMessage())); } } @Override public void doAddToReplicationGroup(StorageSystem storage, URI consistencyGroupId, String replicationGroupName, List<URI> blockObjects, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doRemoveFromReplicationGroup(StorageSystem storage, URI consistencyGroupId, String replicationGroupName, List<URI> blockObjects, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public boolean validateStorageProviderConnection(String ipAddress, Integer portNumber) { return false; } @Override public void doCreateMetaVolumes(StorageSystem storage, StoragePool storagePool, List<Volume> volumes, VirtualPoolCapabilityValuesWrapper capabilities, MetaVolumeRecommendation recommendation, TaskCompleter completer) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doExpandAsMetaVolume(StorageSystem storageSystem, StoragePool storagePool, Volume volume, long size, MetaVolumeRecommendation recommendation, VolumeExpandCompleter volumeCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void updatePolicyAndLimits(StorageSystem storage, ExportMask exportMask, List<URI> volumeURIs, VirtualPool newVpool, boolean rollback, TaskCompleter taskCompleter) throws Exception { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doTerminateAnyRestoreSessions(StorageSystem storageDevice, URI source, BlockObject snapshot, TaskCompleter completer) throws Exception { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doModifyVolumes(StorageSystem storage, StoragePool storagePool, String opId, List<Volume> volumes, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public ExportMaskPolicy getExportMaskPolicy(StorageSystem storage, ExportMask mask) { // No special policy for this device type yet. return new ExportMaskPolicy(); } @Override public void doFractureClone(StorageSystem storageDevice, URI source, URI clone, TaskCompleter completer) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doRestoreFromClone(StorageSystem storage, URI cloneVolume, TaskCompleter taskCompleter) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doResyncClone(StorageSystem storage, URI cloneVolume, TaskCompleter taskCompleter) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doCreateGroupClone(StorageSystem storageDevice, List<URI> clones, Boolean createInactive, TaskCompleter completer) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doDetachGroupClone(StorageSystem storage, List<URI> cloneVolume, TaskCompleter taskCompleter) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doEstablishVolumeFullCopyGroupRelation( StorageSystem storage, URI sourceVolume, URI fullCopy, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doRestoreFromGroupClone(StorageSystem storageSystem, List<URI> cloneVolume, TaskCompleter taskCompleter) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doActivateGroupFullCopy(StorageSystem storageSystem, List<URI> fullCopy, TaskCompleter completer) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doResyncGroupClone(StorageSystem storageDevice, List<URI> clone, TaskCompleter completer) throws Exception { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public Map<URI, Integer> getExportMaskHLUs(StorageSystem storage, ExportMask exportMask) { return Collections.EMPTY_MAP; } @Override public void doFractureGroupClone(StorageSystem storageDevice, List<URI> clone, TaskCompleter completer) throws Exception { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doResyncSnapshot(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doCreateGroupMirrors(StorageSystem storage, List<URI> mirrorList, Boolean createInactive, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doFractureGroupMirrors(StorageSystem storage, List<URI> mirrorList, Boolean sync, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doDetachGroupMirrors(StorageSystem storage, List<URI> mirrorList, Boolean deleteGroup, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doResumeGroupNativeContinuousCopies(StorageSystem storage, List<URI> mirrorList, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doDeleteGroupMirrors(StorageSystem storage, List<URI> mirrorList, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doRemoveMirrorFromDeviceMaskingGroup(StorageSystem system, List<URI> mirrors, TaskCompleter completer) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doCreateListReplica(StorageSystem storage, List<URI> replicaList, /* String repGroupoName, */Boolean createInactive, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doDetachListReplica(StorageSystem storage, List<URI> replicaList, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doFractureListReplica(StorageSystem storage, List<URI> replicaList, Boolean sync, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doDeleteListReplica(StorageSystem storage, List<URI> replicaList, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doUntagVolumes(StorageSystem storageSystem, String opId, List<Volume> volumes, TaskCompleter taskCompleter) throws DeviceControllerException { // If this operation is unsupported by default it's not necessarily an error return; } // file mirror related operations @Override public void doCreateMirror(StorageSystem storage, URI mirror, Boolean createInactive, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.operationNotSupported(); } @Override public void doCreateSnapshotSession(StorageSystem system, URI snapSessionURI, String groupName, TaskCompleter completer) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } /** * {@inheritDoc} */ @Override public void doLinkBlockSnapshotSessionTarget(StorageSystem system, URI snapSessionURI, URI snapShotURI, String copyMode, Boolean targetExists, TaskCompleter completer) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doLinkBlockSnapshotSessionTargetGroup(StorageSystem system, URI snapshotSessionURI, List<URI> snapSessionSnapshotURIs, String copyMode, Boolean targetsExist, TaskCompleter completer) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } /** * {@inheritDoc} */ @Override public void doRelinkBlockSnapshotSessionTarget(StorageSystem system, URI tgtSnapSessionURI, URI snapshotURI, TaskCompleter completer) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } /** * {@inheritDoc} */ @Override public void doUnlinkBlockSnapshotSessionTarget(StorageSystem system, URI snapSessionURI, URI snapshotURI, Boolean deleteTarget, TaskCompleter completer) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } /** * {@inheritDoc} */ @Override public void doRestoreBlockSnapshotSession(StorageSystem system, URI snapSessionURI, TaskCompleter completer) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } /** * {@inheritDoc} */ @Override public void doDeleteBlockSnapshotSession(StorageSystem system, URI snapSessionURI, String groupName, TaskCompleter completer) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doAddSnapshotSessionsToConsistencyGroup(StorageSystem storageSystem, URI consistencyGroup, List<URI> addVolumesList, TaskCompleter taskCompleter) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public Map<URI, List<Integer>> doFindHostHLUs(StorageSystem storage, Collection<URI> initiatorURIs) throws DeviceControllerException { logger.error("This method is not implemented"); return null; } @Override public void doInitiatorAliasSet(StorageSystem storage, Initiator initiator, String initiatorAlias) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public String doInitiatorAliasGet(StorageSystem storage, Initiator initiator) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doExportAddPaths(StorageSystem storage, URI exportMask, Map<URI, List<URI>>addedPaths, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void doExportRemovePaths(StorageSystem storage, URI exportMask, Map<URI, List<URI>> adjustedPaths, Map<URI, List<URI>>removedPaths, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } }