/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.utils.attrmatchers; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.model.RemoteDirectorGroup; import com.emc.storageos.db.client.model.RemoteDirectorGroup.SupportedCopyModes; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StorageSystem.SupportedReplicationTypes; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.util.CommonTransformerFunctions; import com.emc.storageos.volumecontroller.AttributeMatcher; import com.google.common.base.Joiner; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Collections2; import com.google.common.collect.ListMultimap; public class RemoteMirrorProtectionMatcher extends AttributeMatcher { private static final Logger _logger = LoggerFactory.getLogger(RemoteMirrorProtectionMatcher.class); private static final String STORAGE_DEVICE = "storageDevice"; @Override protected boolean isAttributeOn(Map<String, Object> attributeMap) { return (null != attributeMap && attributeMap.containsKey(Attributes.remote_copy.toString())); } private Set<String> getPoolUris(List<StoragePool> matchedPools) { Set<String> poolUris = new HashSet<String>(); for (StoragePool pool : matchedPools) { poolUris.add(pool.getId().toString()); } return poolUris; } @Override protected List<StoragePool> matchStoragePoolsWithAttributeOn( List<StoragePool> allPools, Map<String, Object> attributeMap, StringBuffer errorMessage) { Map<String, List<String>> remoteCopySettings = (Map<String, List<String>>) attributeMap.get(Attributes.remote_copy.toString()); _logger.info("Pools matching remote protection Started : {} ", Joiner.on("\t").join(getNativeGuidFromPools(allPools))); // group by storage system List<StoragePool> matchedPools = new ArrayList<StoragePool>(); ListMultimap<URI, StoragePool> storageToPoolMap = ArrayListMultimap.create(); for (StoragePool pool : allPools) { storageToPoolMap.put(pool.getStorageDevice(), pool); } _logger.info("Grouped Source Storage Devices : {}", storageToPoolMap.asMap().keySet()); Set<String> remotePoolUris = returnRemotePoolsAssociatedWithRemoteCopySettings(remoteCopySettings, getPoolUris(allPools)); _logger.info("Remote Pools found : {}", remotePoolUris); // get Remote Storage Systems associated with given remote Settings via VPool's matched or // assigned Pools ListMultimap<String, URI> remotestorageToPoolMap = groupStoragePoolsByStorageSystem(remotePoolUris); _logger.info("Grouped Remote Storage Devices : {}", remotestorageToPoolMap.asMap().keySet()); for (Entry<URI, Collection<StoragePool>> storageToPoolsEntry : storageToPoolMap .asMap().entrySet()) { StorageSystem system = _objectCache.queryObject(StorageSystem.class, storageToPoolsEntry.getKey()); if (null == system.getSupportedReplicationTypes()) { continue; } if (system.getSupportedReplicationTypes().contains(SupportedReplicationTypes.SRDF.toString()) && null != system.getRemotelyConnectedTo()) { _logger.info("Remotely Connected To : {}", Joiner.on("\t").join(system.getRemotelyConnectedTo())); Set<String> copies = new HashSet<String>(system.getRemotelyConnectedTo()); copies.retainAll(remotestorageToPoolMap.asMap().keySet()); _logger.info("Remotely Connected Systems Matched with Remote VArray : {}", Joiner.on("\t").join(copies)); if (!copies.isEmpty() && isRemotelyConnectedViaExpectedCopyMode(system, remoteCopySettings)) { _logger.info(String.format("Adding Pools %s, as associated Storage System %s is connected to any remote Storage System", Joiner.on("\t").join(storageToPoolsEntry.getValue()), system.getNativeGuid())); matchedPools.addAll(storageToPoolsEntry.getValue()); } else { _logger.info(String.format("Skipping Pools %s, as associated Storage System %s is not connected to any remote Storage System", Joiner.on("\t").join(storageToPoolsEntry.getValue()), system.getNativeGuid())); } } else { _logger.info( "Skipping Pools {}, as associated Storage System is not SRDF supported or there are no available active RA Groups", Joiner.on("\t").join(storageToPoolsEntry.getValue())); } } _logger.info("Pools matching remote mirror protection Ended: {}", Joiner.on("\t").join(getNativeGuidFromPools(matchedPools))); if (CollectionUtils.isEmpty(matchedPools)) { Set<String> copyModes = getSupportedCopyModesFromGivenRemoteSettings(remoteCopySettings); errorMessage.append(String.format("No matching storage pools as associated Storage System is not SRDF supported or " + "there are no available active RA Groups with the expected copy mode %s. ", copyModes)); _logger.error(errorMessage.toString()); } return matchedPools; } private Set<String> getSupportedCopyModesFromGivenRemoteSettings(Map<String, List<String>> remoteCopySettings) { Set<String> copyModes = new HashSet<String>(); for (Entry<String, List<String>> entry : remoteCopySettings.entrySet()) { copyModes.addAll(entry.getValue()); } return copyModes; } private boolean isRemotelyConnectedViaExpectedCopyMode(StorageSystem system, Map<String, List<String>> remoteCopySettings) { List<URI> raGroupUris = _objectCache.getDbClient().queryByConstraint( ContainmentConstraint.Factory.getStorageDeviceRemoteGroupsConstraint(system.getId())); _logger.info("List of RA Groups {}", Joiner.on("\t").join(raGroupUris)); Set<String> copyModes = getSupportedCopyModesFromGivenRemoteSettings(remoteCopySettings); _logger.info("Supported Copy Modes from Given Settings {}", Joiner.on("\t").join(copyModes)); for (URI raGroupUri : raGroupUris) { RemoteDirectorGroup raGroup = _objectCache.queryObject(RemoteDirectorGroup.class, raGroupUri); if (null == raGroup || raGroup.getInactive()) { continue; } if (system.getRemotelyConnectedTo() != null && system.getRemotelyConnectedTo().contains(raGroup.getRemoteStorageSystemUri().toString())) { if (SupportedCopyModes.ALL.toString().equalsIgnoreCase(raGroup.getSupportedCopyMode()) || copyModes.contains(raGroup.getSupportedCopyMode())) { _logger.info("Found Mode {} with RA Group {}", raGroup.getSupportedCopyMode(), raGroup.getNativeGuid()); return true; } } } return false; } /** * Choose Pools based on remote VPool's matched or assigned Pools * * @param remoteCopySettings * @return */ private Set<String> returnRemotePoolsAssociatedWithRemoteCopySettings( Map<String, List<String>> remoteCopySettings, Set<String> poolUris) { Set<String> remotePoolUris = new HashSet<String>(); for (Entry<String, List<String>> entry : remoteCopySettings.entrySet()) { VirtualPool vPool = _objectCache.queryObject(VirtualPool.class, URI.create(entry.getKey())); if (null == vPool) { remotePoolUris.addAll(poolUris); } else if (null != vPool.getUseMatchedPools() && vPool.getUseMatchedPools()) { if (null != vPool.getMatchedStoragePools()) { remotePoolUris.addAll(vPool.getMatchedStoragePools()); } } else if (null != vPool.getAssignedStoragePools()) { remotePoolUris.addAll(vPool.getAssignedStoragePools()); } } return remotePoolUris; } /** * Group Storage Pools by Storage System * * @param allPoolUris * @return */ private ListMultimap<String, URI> groupStoragePoolsByStorageSystem(Set<String> allPoolUris) { Set<String> columnNames = new HashSet<String>(); columnNames.add(STORAGE_DEVICE); Collection<StoragePool> storagePools = _objectCache.getDbClient().queryObjectFields(StoragePool.class, columnNames, new ArrayList<URI>( Collections2.transform(allPoolUris, CommonTransformerFunctions.FCTN_STRING_TO_URI))); ListMultimap<String, URI> storageToPoolMap = ArrayListMultimap.create(); for (StoragePool pool : storagePools) { storageToPoolMap.put(pool.getStorageDevice().toString(), pool.getId()); } return storageToPoolMap; } @Override public Map<String, Set<String>> getAvailableAttribute(List<StoragePool> neighborhoodPools, URI vArrayId) { Map<String, Set<String>> availableAttrMap = new HashMap<String, Set<String>>(1); try { ListMultimap<URI, StoragePool> storageToPoolMap = ArrayListMultimap.create(); for (StoragePool pool : neighborhoodPools) { storageToPoolMap.put(pool.getStorageDevice(), pool); } boolean foundCopyModeAll = false; for (Entry<URI, Collection<StoragePool>> storageToPoolsEntry : storageToPoolMap .asMap().entrySet()) { StorageSystem system = _objectCache.queryObject(StorageSystem.class, storageToPoolsEntry.getKey()); if (null == system.getSupportedReplicationTypes()) { continue; } if (system.getSupportedReplicationTypes().contains(SupportedReplicationTypes.SRDF.toString()) && null != system.getRemotelyConnectedTo()) { List<URI> raGroupUris = _objectCache.getDbClient().queryByConstraint( ContainmentConstraint.Factory.getStorageDeviceRemoteGroupsConstraint(system .getId())); List<RemoteDirectorGroup> RemoteDirectorGroup = _objectCache.queryObject(RemoteDirectorGroup.class, raGroupUris); Set<String> copyModes = new HashSet<String>(); for (RemoteDirectorGroup rg : RemoteDirectorGroup) { if (SupportedCopyModes.ALL.toString().equalsIgnoreCase(rg.getSupportedCopyMode())) { _logger.info("found Copy Mode ALL with RA Group {} ", rg.getId()); foundCopyModeAll = true; copyModes.add(SupportedCopyModes.SYNCHRONOUS.toString()); copyModes.add(SupportedCopyModes.ASYNCHRONOUS.toString()); copyModes.add(SupportedCopyModes.ACTIVE.toString()); break; } else { copyModes.add(rg.getSupportedCopyMode()); } } if (availableAttrMap.get(Attributes.remote_copy.toString()) == null) { availableAttrMap.put(Attributes.remote_copy.toString(), new HashSet<String>()); } availableAttrMap.get(Attributes.remote_copy.toString()).addAll(copyModes); if (foundCopyModeAll) { return availableAttrMap; } } } } catch (Exception e) { _logger.error("Available Attribute failed in remote mirror protection matcher", e); } return availableAttrMap; } }