/* * Copyright (c) 2016 EMC * All Rights Reserved */ package com.emc.sa.service.vipr.migration; import java.net.URI; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.emc.sa.asset.providers.BlockProvider; import com.emc.sa.asset.providers.VirtualDataCenterProvider; import com.emc.sa.engine.ExecutionUtils; import com.emc.sa.engine.bind.Param; import com.emc.sa.engine.service.Service; import com.emc.sa.service.ServiceParams; import com.emc.sa.service.vipr.ViPRService; import com.emc.sa.service.vipr.block.BlockStorageUtils; import com.emc.sa.service.vipr.block.tasks.ChangeBlockVolumeVirtualPoolNoWait; import com.emc.sa.service.vipr.block.tasks.GetMobilityGroup; import com.emc.sa.service.vipr.block.tasks.GetMobilityGroupClusters; import com.emc.sa.service.vipr.block.tasks.GetMobilityGroupHosts; import com.emc.sa.service.vipr.block.tasks.GetMobilityGroupVolumes; import com.emc.sa.service.vipr.block.tasks.GetMobilityGroupVolumesByCluster; import com.emc.sa.service.vipr.block.tasks.GetMobilityGroupVolumesByHost; import com.emc.sa.service.vipr.block.tasks.GetUnmanagedVolumesByHostOrCluster; import com.emc.sa.service.vipr.block.tasks.IngestExportedUnmanagedVolumes; import com.emc.sa.service.vipr.compute.ComputeUtils; import com.emc.sa.util.IngestionMethodEnum; import com.emc.storageos.db.client.model.VolumeGroup; import com.emc.storageos.model.NamedRelatedResourceRep; import com.emc.storageos.model.application.VolumeGroupRestRep; import com.emc.storageos.model.block.UnManagedVolumeRestRep; import com.emc.storageos.model.block.VolumeRestRep; import com.emc.vipr.client.Task; import com.emc.vipr.client.Tasks; import com.emc.vipr.client.exceptions.TimeoutException; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @Service("MobilityGroupMigration") public class MobilityGroupMigrationService extends ViPRService { @Param(ServiceParams.MOBILITY_GROUP) private URI mobilityGroupId; @Param(ServiceParams.TARGET_VIRTUAL_POOL) private URI targetVirtualPool; @Param(value = ServiceParams.MOBILITY_GROUP_METHOD) private String mobilityGroupMethod; @Param(value = ServiceParams.PROJECT, required = false) private URI project; @Param(value = ServiceParams.VIRTUAL_ARRAY, required = false) private URI virtualArray; @Param(value = ServiceParams.VIRTUAL_POOL, required = false) private URI virtualPool; private VolumeGroupRestRep mobilityGroup; @Override public void precheck() throws Exception { mobilityGroup = execute(new GetMobilityGroup(mobilityGroupId)); } @Override public void execute() throws Exception { boolean migrationTasksFailed = false; if (mobilityGroupMethod != null && mobilityGroupMethod.equalsIgnoreCase(BlockProvider.INGEST_AND_MIGRATE_OPTION_KEY)) { ingestVolumes(); } List<Task<VolumeRestRep>> tasks = new ArrayList<>(); Tasks<VolumeRestRep> migrationTasks = execute(new ChangeBlockVolumeVirtualPoolNoWait(mapVpoolVolumes(), targetVirtualPool)); tasks.addAll(migrationTasks.getTasks()); if (tasks.isEmpty()) { ExecutionUtils.fail("failTask.mobilityGroupMigration.noVolumesMigrated", new Object[] {}, new Object[] {}); } while (!tasks.isEmpty()) { waitAndRefresh(tasks); for (Task<VolumeRestRep> successfulTask : ComputeUtils.getSuccessfulTasks(tasks)) { URI volumeId = successfulTask.getResourceId(); addAffectedResource(volumeId); tasks.remove(successfulTask); } for (Task<VolumeRestRep> failedTask : ComputeUtils.getFailedTasks(tasks)) { String errorMessage = failedTask.getMessage() == null ? "" : failedTask.getMessage(); ExecutionUtils.currentContext().logError("mobilityGroupMigration.changeVirtualPool.failure", failedTask.getResource().getName(), errorMessage); tasks.remove(failedTask); migrationTasksFailed = true; } } if (migrationTasksFailed) { ExecutionUtils.fail("failTask.MobilityGroupMigration.migrationsFailed", new Object[] {}, new Object[] {}); } } private static <T> void waitAndRefresh(List<Task<T>> tasks) { for (Task<T> task : tasks) { try { task.waitFor(); } catch (TimeoutException te) { // ignore timeout after polling interval } catch (Exception e) { ExecutionUtils.currentContext().logError("mobilityGroupMigration.task.exception", e.getMessage()); } } } private void ingestVolumes() { List<NamedRelatedResourceRep> hostsOrClusters = Lists.newArrayList(); if (mobilityGroup.getMigrationGroupBy().equals(VolumeGroup.MigrationGroupBy.HOSTS.name())) { hostsOrClusters = execute(new GetMobilityGroupHosts(mobilityGroup.getId())); } else if (mobilityGroup.getMigrationGroupBy().equals(VolumeGroup.MigrationGroupBy.CLUSTERS.name())) { hostsOrClusters = execute(new GetMobilityGroupClusters(mobilityGroup.getId())); } for (NamedRelatedResourceRep hostOrCluster : hostsOrClusters) { int remaining = execute(new GetUnmanagedVolumesByHostOrCluster( hostOrCluster.getId())).size(); logInfo("ingest.exported.unmanaged.volume.service.remaining", remaining); } for (NamedRelatedResourceRep hostOrCluster : hostsOrClusters) { URI host = BlockStorageUtils.isHost(hostOrCluster.getId()) ? hostOrCluster.getId() : null; URI cluster = BlockStorageUtils.isCluster(hostOrCluster.getId()) ? hostOrCluster.getId() : null; List<UnManagedVolumeRestRep> volumeIds = execute(new GetUnmanagedVolumesByHostOrCluster( hostOrCluster.getId())); List<URI> ingestVolumeIds = Lists.newArrayList(); for (UnManagedVolumeRestRep unmanaged : volumeIds) { if (VirtualDataCenterProvider.matchesVpool(unmanaged, virtualPool)) { ingestVolumeIds.add(unmanaged.getId()); } } int succeed = execute(new IngestExportedUnmanagedVolumes(virtualPool, virtualArray, project, host == null ? null : host, cluster == null ? null : cluster, ingestVolumeIds, IngestionMethodEnum.VIRTUAL_VOLUMES_ONLY.toString() )).getTasks().size(); logInfo("ingest.exported.unmanaged.volume.service.ingested", succeed); logInfo("ingest.exported.unmanaged.volume.service.skipped", volumeIds.size() - succeed); } } /** * Get map of virtual pool id to list of volumes in the virtual pool * * @return map of virtual pool to volumes */ private Map<URI, Set<URI>> mapVpoolVolumes() { Set<URI> volumes = getVolumes(); Map<URI, Set<URI>> vpoolVolumes = Maps.newHashMap(); for (URI volume : volumes) { VolumeRestRep vol = getClient().blockVolumes().get(volume); if (!vpoolVolumes.containsKey(vol.getVirtualPool().getId())) { vpoolVolumes.put(vol.getVirtualPool().getId(), new HashSet<URI>()); } vpoolVolumes.get(vol.getVirtualPool().getId()).add(vol.getId()); } return vpoolVolumes; } /** * Get volumes that belong to the mobility group or are exported to hosts or clusters in the mobility group * * @return volumes */ private Set<URI> getVolumes() { if (mobilityGroup.getMigrationGroupBy().equalsIgnoreCase(VolumeGroup.MigrationGroupBy.VOLUMES.name())) { return execute(new GetMobilityGroupVolumes(Lists.newArrayList(mobilityGroupId))); } else if (mobilityGroup.getMigrationGroupBy().equalsIgnoreCase(VolumeGroup.MigrationGroupBy.HOSTS.name())) { List<NamedRelatedResourceRep> hosts = execute(new GetMobilityGroupHosts(mobilityGroupId)); return execute(new GetMobilityGroupVolumesByHost(mobilityGroup, hosts)); } else if (mobilityGroup.getMigrationGroupBy().equalsIgnoreCase(VolumeGroup.MigrationGroupBy.CLUSTERS.name())) { List<NamedRelatedResourceRep> clusters = execute(new GetMobilityGroupClusters(mobilityGroupId)); return execute(new GetMobilityGroupVolumesByCluster(mobilityGroup, clusters)); } return Sets.newHashSet(); } }