/* * Copyright (c) 2016 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.block.taskcompleter; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.jetty.util.log.Log; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.model.ExportGroup; import com.emc.storageos.db.client.model.ExportPathParams; import com.emc.storageos.db.client.model.Operation; import com.emc.storageos.db.client.model.StringMap; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.services.OperationTypeEnum; import com.emc.storageos.svcs.errorhandling.model.ServiceCoded; public class ExportPortRebalanceCompleter extends ExportTaskCompleter{ private static final org.slf4j.Logger log = LoggerFactory.getLogger(ExportMaskAddPathsCompleter.class); private static final String EXPORT_PORT_REBALANCE_MSG = "Path adjustment to ExportGroup %s"; private static final String EXPORT_PORT_REBALANCE_FAILED_MSG = "Failed path adjustment to ExportGroup %s"; private static final String EXPORT_PORT_REBALANCE_SUSPENDED_MSG = "Path adjustment to ExportGroup %s suspended"; private ExportPathParams newPathParam; private URI systemURI; private Set<URI> affectedExportGroups; public ExportPortRebalanceCompleter(URI systemURI, URI id, String opId, ExportPathParams exportPathParam) { super(ExportGroup.class, id, opId); this.newPathParam = exportPathParam; this.systemURI = systemURI; } public void setAffectedExportGroups(Set<URI> exportGroups) { this.affectedExportGroups = exportGroups; } @Override protected void complete(DbClient dbClient, Operation.Status status, ServiceCoded coded) throws DeviceControllerException { try { String eventMessage = null; ExportGroup exportGroup = dbClient.queryObject(ExportGroup.class, getId()); Operation operation = new Operation(); switch (status) { case error: operation.error(coded); eventMessage = String.format(EXPORT_PORT_REBALANCE_FAILED_MSG, exportGroup.getLabel()); break; case ready: operation.ready(); updateVolumeExportPathParam(dbClient); eventMessage = String.format(EXPORT_PORT_REBALANCE_MSG, exportGroup.getLabel()); break; case suspended_no_error: operation.suspendedNoError(); eventMessage = String.format(EXPORT_PORT_REBALANCE_SUSPENDED_MSG, exportGroup.getLabel()); break; case suspended_error: operation.suspendedError(coded); eventMessage = String.format(EXPORT_PORT_REBALANCE_SUSPENDED_MSG, exportGroup.getLabel()); break; default: break; } exportGroup.getOpStatus().updateTaskStatus(getOpId(), operation); dbClient.updateObject(exportGroup); log.info(String.format("Done Export path adjustment - Id: %s, OpId: %s, status: %s", getId().toString(), getOpId(), status.name())); recordBlockExportOperation(dbClient, OperationTypeEnum.EXPORT_PATH_ADJUSTMENT, status, eventMessage, exportGroup); } catch (Exception e) { log.error(String.format("Failed updating status for Export path adjustment - Id: %s, OpId: %s", getId().toString(), getOpId()), e); } finally { super.complete(dbClient, status, coded); } } /** * Update export group pathParameters for the impacted volumes * @param dbClient */ private void updateVolumeExportPathParam(DbClient dbClient) { log.info("updating path param map."); if (affectedExportGroups == null || affectedExportGroups.isEmpty()) { // not export group is affected, return return; } for (URI egURI : affectedExportGroups) { ExportGroup exportGroup = dbClient.queryObject(ExportGroup.class, egURI); log.info(String.format("Updating export group %s", exportGroup.getId().toString())); StringMap volumes = exportGroup.getVolumes(); StringMap existingPathMap = exportGroup.getPathParameters(); List<URI> impactedVolumes = new ArrayList<URI>(); Map<URI, List<URI>> vpoolVolumes = new HashMap<URI, List<URI>>(); Map<URI, List<URI>> volumePath = new HashMap<URI, List<URI>> (); if (volumes == null || volumes.isEmpty()) { continue; } Set<String> volSet = volumes.keySet(); for (String volId : volSet) { URI volURI = URI.create(volId); Volume volume = dbClient.queryObject(Volume.class, volURI); if (volume == null || !volume.getStorageController().equals(systemURI)) { continue; } log.info(String.format("Checking for the volume %s", volume.getLabel())); // Check if the volume is in the pathMap String pathParm = existingPathMap.get(volId); if (pathParm != null) { URI pathId = URI.create(pathParm); List<URI> vols = volumePath.get(pathId); if (vols == null) { vols = new ArrayList<URI>(); volumePath.put(pathId, vols); } vols.add(volURI); } else { // check if the volumes' export path parameters are the same URI vpId = volume.getVirtualPool(); List<URI> vols = vpoolVolumes.get(vpId); if (vols == null) { vols = new ArrayList<URI>(); vpoolVolumes.put(vpId, vols); } vols.add(volURI); } } if (!vpoolVolumes.isEmpty()) { log.info("check for vpool"); for (Map.Entry<URI, List<URI>> entry : vpoolVolumes.entrySet()) { URI vpoolId = entry.getKey(); VirtualPool vpool = dbClient.queryObject(VirtualPool.class, vpoolId); if (vpool != null) { log.info(String.format("vpool path param : %d, %d, %d", vpool.getMinPaths(), vpool.getNumPaths(), vpool.getPathsPerInitiator())); log.info(String.format("New path param %d, %d, %d", newPathParam.getMinPaths(), newPathParam.getMaxPaths(), newPathParam.getPathsPerInitiator())); if (vpool.getMinPaths() != newPathParam.getMinPaths() || vpool.getNumPaths() != newPathParam.getMaxPaths() || vpool.getPathsPerInitiator() != newPathParam.getPathsPerInitiator()) { impactedVolumes.addAll(entry.getValue()); } } } } if (!volumePath.isEmpty()) { for (Map.Entry<URI, List<URI>> entry : volumePath.entrySet()) { URI pathParamURI = entry.getKey(); ExportPathParams pathParam = dbClient.queryObject(ExportPathParams.class, pathParamURI); if (pathParam == null || pathParam.getMinPaths() != newPathParam.getMinPaths() || pathParam.getMaxPaths() != newPathParam.getMaxPaths() || pathParam.getPathsPerInitiator() != newPathParam.getPathsPerInitiator()) { //remove the path entry for this volume for (URI volURI : entry.getValue()) { exportGroup.removeFromPathParameters(volURI); } // If there are no more entries for the given ExportPathParam, mark it for deletion if (!exportGroup.getPathParameters().containsValue(pathParamURI.toString()) && pathParam != null) { dbClient.markForDeletion(pathParam); } impactedVolumes.addAll(entry.getValue()); } } } if (!impactedVolumes.isEmpty()) { ExportPathParams pathParam = new ExportPathParams(); pathParam.setMaxPaths(newPathParam.getMaxPaths()); pathParam.setMinPaths(newPathParam.getMinPaths()); pathParam.setPathsPerInitiator(newPathParam.getPathsPerInitiator()); pathParam.setExportGroupType(exportGroup.getType()); pathParam.setLabel(exportGroup.getLabel()); if (newPathParam.getStoragePorts() != null) { pathParam.setStoragePorts(newPathParam.getStoragePorts()); } pathParam.setExplicitlyCreated(false); pathParam.setId(URIUtil.createId(ExportPathParams.class)); pathParam.setInactive(false); dbClient.createObject(pathParam); for (URI volId : impactedVolumes) { exportGroup.addToPathParameters(volId, pathParam.getId()); } } dbClient.updateObject(exportGroup); } } }