use of com.emc.storageos.vplex.api.clientdata.VolumeInfo in project coprhd-controller by CoprHD.
the class VPlexApiVirtualVolumeManager method deleteVirtualVolume.
/**
* Deletes the virtual volume by destroying all the components (i.e,
* extents, devices) that were created in the process of creating the
* virtual and unclaiming the storage volume(s).
*
* @param nativeVolumeInfoList The same native volume info that was passed
* when the virtual volume was created.
*
* @throws VPlexApiException When an error occurs deleted the virtual
* volume.
*/
void deleteVirtualVolume(List<VolumeInfo> nativeVolumeInfoList) throws VPlexApiException {
s_logger.info("Deleting virtual volume using native volume info");
// Get the name(s) of the volume(s) that were used to create
// the virtual volume.
List<String> nativeVolumeNames = new ArrayList<String>();
for (VolumeInfo nativeVolumeInfo : nativeVolumeInfoList) {
nativeVolumeNames.add(nativeVolumeInfo.getVolumeName());
}
// Build the virtual volume name from the names of the
// passed volumes and delete it.
deleteVirtualVolume(buildVirtualVolumeName(nativeVolumeNames), true, false);
}
use of com.emc.storageos.vplex.api.clientdata.VolumeInfo in project coprhd-controller by CoprHD.
the class VPlexApiVirtualVolumeManager method createVirtualVolume.
/**
* Creates a VPlex virtual volume using the passed native volume
* information. The passed native volume information should identify a
* single volume on a backend storage array connected to a VPlex cluster
* when a simple, non-distributed virtual volume is desired. When a
* distributed virtual volume is desired the native volume info should
* identify two volumes. One volume should reside on a backend storage array
* connected to one VPlex cluster in a VPlex metro configuration. The other
* volumes should reside on a backend storage array connected to the other
* VPlex cluster in a VPlex Metro configuration.
*
* NOTE: Currently, backend volumes newly exported to the VPlex must be
* discovered prior to creating a virtual volume using them by invoking the
* rediscoverStorageSystems API.
*
* @param nativeVolumeInfoList The native volume information.
* @param isDistributed true for a distributed virtual volume, false
* otherwise.
* @param discoveryRequired true if the passed native volumes are newly
* exported and need to be discovered by the VPlex.
* @param preserveData true if the native volume data should be preserved
* during virtual volume creation.
* @param winningClusterId Used to set detach rules for distributed volumes.
* @param clusterInfoList A list of VPlexClusterInfo specifying the info for the VPlex
* clusters.
* @param findVirtualVolume If true findVirtualVolume method is called after virtual volume is created.
* @param thinEnabled If true, the virtual volume should be created as a thin-enabled virtual volume.
* @param clusterName The clusterName the volume is on. if non-null, backend volume
* search will be restricted to the named cluster.
*
* @return The information for the created virtual volume.
*
* @throws VPlexApiException When an error occurs creating the virtual
* volume.
*/
VPlexVirtualVolumeInfo createVirtualVolume(List<VolumeInfo> nativeVolumeInfoList, boolean isDistributed, boolean discoveryRequired, boolean preserveData, String winningClusterId, List<VPlexClusterInfo> clusterInfoList, boolean findVirtualVolume, boolean thinEnabled, String clusterName) throws VPlexApiException {
s_logger.info("Request to create {} virtual volume.", (isDistributed ? "distributed" : "local"));
if ((isDistributed) && (nativeVolumeInfoList.size() != 2)) {
throw VPlexApiException.exceptions.twoDevicesRequiredForDistVolume();
} else if ((!isDistributed) && (nativeVolumeInfoList.size() != 1)) {
throw VPlexApiException.exceptions.oneDevicesRequiredForLocalVolume();
}
if (null == clusterInfoList) {
clusterInfoList = new ArrayList<VPlexClusterInfo>();
}
// Find the storage volumes corresponding to the passed native
// volume information, discover them if required.
Map<VolumeInfo, VPlexStorageVolumeInfo> storageVolumeInfoMap = findStorageVolumes(nativeVolumeInfoList, discoveryRequired, clusterInfoList, clusterName);
// have been configured on each cluster.
if (isDistributed) {
for (VPlexClusterInfo clusterInfo : clusterInfoList) {
if (!clusterInfo.hasLoggingVolume()) {
throw VPlexApiException.exceptions.clusterHasNoLoggingVolumes(clusterInfo.getName());
}
}
s_logger.info("Verified logging volumes");
}
// Claim the storage volumes
claimStorageVolumes(storageVolumeInfoMap, preserveData);
s_logger.info("Claimed storage volumes");
// clean up the VPLEX artifacts and unclaim the storage volumes.
try {
// Create extents
List<VPlexStorageVolumeInfo> storageVolumeInfoList = new ArrayList<VPlexStorageVolumeInfo>();
for (VolumeInfo nativeVolumeInfo : nativeVolumeInfoList) {
storageVolumeInfoList.add(storageVolumeInfoMap.get(nativeVolumeInfo));
}
createExtents(storageVolumeInfoList);
s_logger.info("Created extents on storage volumes");
// Find the extents just created and create local devices on
// those extents.
VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager();
List<VPlexExtentInfo> extentInfoList = discoveryMgr.findExtents(storageVolumeInfoList);
createLocalDevices(extentInfoList);
s_logger.info("Created local devices on extents");
// Find the local devices just created. If the virtual volume is
// to be distributed, first create a distributed device from the
// local devices, then create a virtual volumes from the distributed
// device. Otherwise, create a virtual volume from the local device.
String clusterId;
String deviceName;
String devicePath;
List<VPlexDeviceInfo> localDevices = discoveryMgr.findLocalDevices(extentInfoList);
if (isDistributed) {
// Create and find the distributed device using the local devices.
String distributedDeviceName = createDistributedDevice(localDevices, winningClusterId);
s_logger.info("Created distributed device on local devices");
VPlexDistributedDeviceInfo distDeviceInfo = discoveryMgr.findNewDistributedDevice(distributedDeviceName);
if (distDeviceInfo == null) {
s_logger.error("Distributed device {} was successfully created but not returned by the VPLEX system", distributedDeviceName);
throw VPlexApiException.exceptions.failedGettingDistributedDevice(distributedDeviceName);
}
distDeviceInfo.setLocalDeviceInfo(localDevices);
clusterId = distDeviceInfo.getClusterId();
deviceName = distDeviceInfo.getName();
devicePath = distDeviceInfo.getPath();
} else {
// Should only be a single local device.
VPlexDeviceInfo deviceInfo = localDevices.get(0);
clusterId = deviceInfo.getCluster();
deviceName = deviceInfo.getName();
devicePath = deviceInfo.getPath();
}
// Create virtual volume
createVirtualVolume(devicePath, thinEnabled);
s_logger.info("Created virtual volume on device {}", devicePath);
VPlexVirtualVolumeInfo virtualVolumeInfo = new VPlexVirtualVolumeInfo();
StringBuilder volumeNameBuilder = new StringBuilder();
volumeNameBuilder.append(deviceName);
volumeNameBuilder.append(VPlexApiConstants.VIRTUAL_VOLUME_SUFFIX);
if (findVirtualVolume) {
// For bulk volume creation we shouldn't use findVirtualVolume as true, rather findVirtualVolumes should be called
// separately after createVirtualVolumes.
virtualVolumeInfo = discoveryMgr.findVirtualVolume(clusterId, volumeNameBuilder.toString(), true, true);
} else {
virtualVolumeInfo.setName(volumeNameBuilder.toString());
virtualVolumeInfo.addCluster(clusterId);
}
return virtualVolumeInfo;
} catch (Exception e) {
// An error occurred. Clean up any VPLEX artifacts created for
// virtual volume and unclaim the storage volumes.
s_logger.info("Exception occurred creating virtual volume, attempting to cleanup VPLEX artifacts");
try {
// This will look for any artifacts, starting with a virtual
// volume, that use the passed native volume info and destroy
// them and then unclaim the volume.
deleteVirtualVolume(nativeVolumeInfoList);
} catch (Exception ex) {
s_logger.error("Failed attempting to cleanup VPLEX after failed attempt " + "to create a new virtual volume", ex);
}
throw e;
}
}
use of com.emc.storageos.vplex.api.clientdata.VolumeInfo in project coprhd-controller by CoprHD.
the class VPlexApiVirtualVolumeManager method findStorageVolumes.
/**
* Find the storage volumes identified by the passed native volume info,
* discovering the storage volumes if required.
*
* @param nativeVolumeInfoList The native volume information.
* @param discoveryRequired true if the passed native volumes are newly
* exported and need to be discovered by the VPlex.
* @param clusterInfoList [OUT] param set to the cluster information.
* @param clusterName if non-null, search will be restricted to the named cluster
*
* @throws VPlexApiException When an error occurs finding the storage
* volumes or the storage volumes are not all found.
*/
Map<VolumeInfo, VPlexStorageVolumeInfo> findStorageVolumes(List<VolumeInfo> nativeVolumeInfoList, boolean discoveryRequired, List<VPlexClusterInfo> clusterInfoList, String clusterName) throws VPlexApiException {
// If the volume(s) passed are newly exported to the VPlex, they may
// need to be discovered before they can be used. If the discovery
// required flag is true, we execute a discovery step so that the
// volumes are available to the VPlex. Note that we will try for a while
// to give the newly exported volumes some time to be accessible by
// the VPlex.
Map<VolumeInfo, VPlexStorageVolumeInfo> storageVolumeInfoMap = null;
VPlexApiDiscoveryManager discoveryMgr = _vplexApiClient.getDiscoveryManager();
if (discoveryRequired) {
s_logger.info("Storage volume discovery is required.");
int retryCount = 0;
while (++retryCount <= VPlexApiConstants.FIND_STORAGE_VOLUME_RETRY_COUNT) {
try {
// Execute the re-discover command.
s_logger.info("Executing storage volume discovery try {} of {}", retryCount, VPlexApiConstants.FIND_STORAGE_VOLUME_RETRY_COUNT);
List<String> storageSystemGuids = new ArrayList<String>();
for (VolumeInfo nativeVolumeInfo : nativeVolumeInfoList) {
String storageSystemGuid = nativeVolumeInfo.getStorageSystemNativeGuid();
if (!storageSystemGuids.contains(storageSystemGuid)) {
s_logger.info("Discover storage volumes on array {}", storageSystemGuid);
storageSystemGuids.add(storageSystemGuid);
}
}
discoveryMgr.rediscoverStorageSystems(storageSystemGuids);
s_logger.info("Discovery completed");
// Get the cluster information.
clusterInfoList.addAll(discoveryMgr.getClusterInfo(false, true, clusterName));
s_logger.info("Retrieved storage volume info for VPlex clusters");
// Find the back-end storage volumes. If a volume cannot be
// found, an exception is thrown.
storageVolumeInfoMap = discoveryMgr.findStorageVolumes(nativeVolumeInfoList, clusterInfoList);
s_logger.info("Found storage volumes to use for virtual volume");
// Exit, no exceptions means all volumes found.
break;
} catch (VPlexApiException vae) {
// the exception.
if (retryCount == VPlexApiConstants.FIND_STORAGE_VOLUME_RETRY_COUNT) {
throw vae;
}
// Otherwise, if an exception occurs, it is likely because a
// storage volume was not found. Wait for a bit and execute
// the the discovery again.
clusterInfoList.clear();
VPlexApiUtils.pauseThread(VPlexApiConstants.FIND_STORAGE_VOLUME_SLEEP_TIME_MS);
}
}
} else {
s_logger.info("Storage volume discovery is not required.");
// Get the cluster information.
if (clusterInfoList.isEmpty()) {
clusterInfoList.addAll(discoveryMgr.getClusterInfo(false, true, clusterName));
s_logger.info("Retrieved storage volume info for VPlex clusters");
}
// Find the backend storage volumes. If a volume cannot be
// found, then an exception will be thrown.
storageVolumeInfoMap = discoveryMgr.findStorageVolumes(nativeVolumeInfoList, clusterInfoList);
s_logger.info("Found storage volumes");
}
return storageVolumeInfoMap;
}
use of com.emc.storageos.vplex.api.clientdata.VolumeInfo in project coprhd-controller by CoprHD.
the class VPlexApiTest method testMigrateVirtualVolumeSimple.
/**
* Tests the API migrateVirtualVolume for a simple virtual volume.
*/
@Test
public void testMigrateVirtualVolumeSimple() {
boolean wasException = false;
try {
// Create the simple virtual volume.
// Create the virtual volume.
String volumeInfo = _properties.getProperty(SIMPLE_VV_INFO_PROP_KEY);
StringTokenizer tokenizer = new StringTokenizer(volumeInfo, ",");
String systemGuid = tokenizer.nextToken();
tokenizer.nextToken();
String volumeNativeId = tokenizer.nextToken();
VPlexVirtualVolumeInfo vvInfo = createSimpleVirtualVolume();
Assert.assertNotNull(vvInfo);
StringBuilder vvNameBuilder = new StringBuilder();
vvNameBuilder.append(VPlexApiConstants.DEVICE_PREFIX);
vvNameBuilder.append(VPlexApiConstants.VOLUME_NAME_PREFIX);
vvNameBuilder.append(systemGuid.substring(systemGuid.indexOf("+") + 1));
vvNameBuilder.append("-");
vvNameBuilder.append(volumeNativeId);
vvNameBuilder.append(VPlexApiConstants.VIRTUAL_VOLUME_SUFFIX);
String vvName = vvNameBuilder.toString();
Assert.assertEquals(vvName, vvInfo.getName());
// Migrate the virtual volume
List<VolumeInfo> nativeVolumeInfoList = new ArrayList<VolumeInfo>();
String migrationVolumeInfo = _properties.getProperty(SIMPLE_MIGRATION_VV_INFO_PROP_KEY);
StringTokenizer volumeInfoTokenizer = new StringTokenizer(migrationVolumeInfo, ",");
systemGuid = volumeInfoTokenizer.nextToken();
String volumeId = volumeInfoTokenizer.nextToken();
volumeNativeId = volumeInfoTokenizer.nextToken();
nativeVolumeInfoList.add(new VolumeInfo(systemGuid, "vmax", volumeId, volumeNativeId, false, Collections.<String>emptyList()));
vvNameBuilder = new StringBuilder();
vvNameBuilder.append(VPlexApiConstants.DEVICE_PREFIX);
vvNameBuilder.append(VPlexApiConstants.VOLUME_NAME_PREFIX);
vvNameBuilder.append(systemGuid.substring(systemGuid.indexOf("+") + 1));
vvNameBuilder.append("-");
vvNameBuilder.append(volumeNativeId);
vvNameBuilder.append(VPlexApiConstants.VIRTUAL_VOLUME_SUFFIX);
String migrationName = _properties.getProperty(MIGRATION_NAME_PROP_KEY);
List<VPlexMigrationInfo> migrationInfoList = _client.migrateVirtualVolume(migrationName, vvName, nativeVolumeInfoList, false, false, false, true, null);
Assert.assertEquals(migrationInfoList.size(), 1);
// Wait until migrations complete and commit the migrations with
// automatic clean and remove.
Thread.sleep(15000);
List<String> migrationNames = new ArrayList<String>();
for (VPlexMigrationInfo migrationInfo : migrationInfoList) {
migrationNames.add(migrationInfo.getName());
}
migrationInfoList = _client.commitMigrations(vvName, migrationNames, true, true, true);
Assert.assertEquals(migrationInfoList.size(), 1);
// Clean up the virtual volume.
vvInfo = migrationInfoList.get(0).getVirtualVolumeInfo();
Assert.assertEquals(vvNameBuilder.toString(), vvInfo.getName());
_client.deleteVirtualVolume(vvInfo.getName(), true, false);
} catch (Exception e) {
wasException = true;
}
Assert.assertFalse(wasException);
}
use of com.emc.storageos.vplex.api.clientdata.VolumeInfo in project coprhd-controller by CoprHD.
the class VPlexDeviceController method migrateVirtualVolume.
/**
* Creates and starts a VPlex data migration for the passed virtual volume
* on the passed VPlex storage system. The passed target is a newly created
* backend volume to which the data will be migrated. The source for the
* data migration is the current backend volume for the virtual volume that
* is in the same varray as the passed target. The method also creates
* a migration job to monitor the progress of the migration. The workflow
* step will complete when the migration completes, at which point the
* migration is automatically committed.
*
* @param vplexURI
* The URI of the VPlex storage system.
* @param virtualVolumeURI
* The URI of the virtual volume.
* @param targetVolumeURI
* The URI of the migration target.
* @param migrationURI
* The URI of the migration.
* @param newNhURI
* The URI of the new varray for the virtual volume
* when a local virtual volume is being migrated to the other
* cluster, or null.
* @param stepId
* The workflow step identifier.
* @throws WorkflowException
*/
public void migrateVirtualVolume(URI vplexURI, URI virtualVolumeURI, URI targetVolumeURI, URI migrationURI, URI newNhURI, String stepId) throws WorkflowException {
_log.info("Migration {} using target {}", migrationURI, targetVolumeURI);
try {
// Update step state to executing.
WorkflowStepCompleter.stepExecuting(stepId);
// Initialize the step data. The step data indicates if we
// successfully started the migration and is used in
// rollback.
_workflowService.storeStepData(stepId, Boolean.FALSE);
// Get the virtual volume.
Volume virtualVolume = getDataObject(Volume.class, virtualVolumeURI, _dbClient);
String virtualVolumeName = virtualVolume.getDeviceLabel();
_log.info("Virtual volume name is {}", virtualVolumeName);
// Setup the native volume info for the migration target.
Volume migrationTarget = getDataObject(Volume.class, targetVolumeURI, _dbClient);
StorageSystem targetStorageSystem = getDataObject(StorageSystem.class, migrationTarget.getStorageController(), _dbClient);
_log.info("Storage system for migration target is {}", migrationTarget.getStorageController());
List<String> itls = VPlexControllerUtils.getVolumeITLs(migrationTarget);
VolumeInfo nativeVolumeInfo = new VolumeInfo(targetStorageSystem.getNativeGuid(), targetStorageSystem.getSystemType(), migrationTarget.getWWN().toUpperCase().replaceAll(":", ""), migrationTarget.getNativeId(), migrationTarget.getThinlyProvisioned().booleanValue(), itls);
// Get the migration associated with the target.
Migration migration = getDataObject(Migration.class, migrationURI, _dbClient);
// Determine the unique name for the migration. We identifying
// the migration source and target, using array serial number
// and volume native id, in the migration name. This was fine
// for VPlex extent migration, which has a max length of 63
// for the migration name. However, for remote migrations,
// which require VPlex device migration, the max length is much
// more restrictive, like 20 characters. So, we switched over
// timestamps.
StringBuilder migrationNameBuilder = new StringBuilder(MIGRATION_NAME_PREFIX);
DateFormat dateFormatter = new SimpleDateFormat(MIGRATION_NAME_DATE_FORMAT);
migrationNameBuilder.append(dateFormatter.format(new Date()));
String migrationName = migrationNameBuilder.toString();
migration.setLabel(migrationName);
_dbClient.updateObject(migration);
_log.info("Migration name is {}", migrationName);
// Get the VPlex API client.
StorageSystem vplexSystem = getDataObject(StorageSystem.class, vplexURI, _dbClient);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplexSystem, _dbClient);
_log.info("Got VPlex API client for VPlex {}", vplexURI);
// Get the configured migration speed
String speed = customConfigHandler.getComputedCustomConfigValue(CustomConfigConstants.MIGRATION_SPEED, vplexSystem.getSystemType(), null);
_log.info("Migration speed is {}", speed);
String transferSize = migrationSpeedToTransferSizeMap.get(speed);
// Make a call to the VPlex API client to migrate the virtual
// volume. Note that we need to do a remote migration when a
// local virtual volume is being migrated to the other VPlex
// cluster. If the passed new varray is not null, then
// this is the case.
Boolean isRemoteMigration = newNhURI != null;
// We support both device and extent migrations, however,
// when we don't know anything about the backend volumes
// we must use device migration.
Boolean useDeviceMigration = migration.getSource() == null;
List<VPlexMigrationInfo> migrationInfoList = client.migrateVirtualVolume(migrationName, virtualVolumeName, Arrays.asList(nativeVolumeInfo), isRemoteMigration, useDeviceMigration, true, true, transferSize);
_log.info("Started VPlex migration");
// We store step data indicating that the migration was successfully
// create and started. We will use this to determine the behavior
// on rollback. If we never got to the point that the migration
// was created and started, then there is no rollback to attempt
// on the VLPEX as the migrate API already tried to clean everything
// up on the VLPEX.
_workflowService.storeStepData(stepId, Boolean.TRUE);
// Initialize the migration info in the database.
VPlexMigrationInfo migrationInfo = migrationInfoList.get(0);
migration.setMigrationStatus(VPlexMigrationInfo.MigrationStatus.READY.getStatusValue());
migration.setPercentDone("0");
migration.setStartTime(migrationInfo.getStartTime());
_dbClient.updateObject(migration);
_log.info("Update migration info");
// Create a migration task completer and queue a job to monitor
// the migration progress. The completer will be invoked by the
// job when the migration completes.
MigrationTaskCompleter migrationCompleter = new MigrationTaskCompleter(migrationURI, stepId);
VPlexMigrationJob migrationJob = new VPlexMigrationJob(migrationCompleter);
migrationJob.setTimeoutTimeMsec(MINUTE_TO_MILLISECONDS * Long.valueOf(ControllerUtils.getPropertyValueFromCoordinator(coordinator, CONTROLLER_VPLEX_MIGRATION_TIMEOUT_MINUTES)));
ControllerServiceImpl.enqueueJob(new QueueJob(migrationJob));
_log.info("Queued job to monitor migration progress.");
} catch (VPlexApiException vae) {
_log.error("Exception migrating VPlex virtual volume: " + vae.getMessage(), vae);
WorkflowStepCompleter.stepFailed(stepId, vae);
} catch (Exception ex) {
_log.error("Exception migrating VPlex virtual volume: " + ex.getMessage(), ex);
String opName = ResourceOperationTypeEnum.MIGRATE_VIRTUAL_VOLUME.getName();
ServiceError serviceError = VPlexApiException.errors.migrateVirtualVolume(opName, ex);
WorkflowStepCompleter.stepFailed(stepId, serviceError);
}
}
Aggregations