/* * Copyright (c) 2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.utils; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.util.CollectionUtils; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.svcs.errorhandling.resources.ServiceCode; import com.emc.storageos.svcs.errorhandling.resources.ServiceCodeException; import com.emc.storageos.volumecontroller.AttributeMatcher; /** * AttributeMatcherFramework is an framework class which abstract the matcher execution. * All clients should use this class to invoke matchAttributes to run matcher algorithm. * All the matchers are grouped by its nature. Each group will be executed in the order * they have added to the LinkedList. Since the beans are injected to a LinkedList using * spring configuration, we always guarantee the order. * The current sequence of attributeMatcher execution: * 1. ActivePoolMatcher * 2. NeighborhoodsMatcher * 3. CoSTypeAttributeMatcher * 4. ProtocolsAttrMatcher * */ public class AttributeMatcherFramework implements ApplicationContextAware { private static final Logger _logger = LoggerFactory .getLogger(AttributeMatcherFramework.class); private static volatile ApplicationContext _context; @Override public void setApplicationContext(ApplicationContext appContext) throws BeansException { _context = appContext; } public static ApplicationContext getApplicationContext() { return _context; } /** * Match all attributes of CoS container & volumeParam container values against the given pools. * * @param allPools : list of pools * @param cos : vpool * @param objValueToCompare : volumeParam container values. * @param dbClient * @param matcherGroupName : groupName to execute the matchers.matchers are grouped by its relativity * @param errorMessage : will contain error message * @return Returns list of matched StoragePool instances */ public List<StoragePool> matchAttributes(List<StoragePool> allPools, Map<String, Object> attributeMap, DbClient dbClient, CoordinatorClient coordinator, String matcherGroupName, StringBuffer errorMessage) { List<StoragePool> matchedPools = new ArrayList<StoragePool>(); if (!CollectionUtils.isEmpty(allPools)) { matchedPools.addAll(allPools); try { _logger.info("Starting execution of {} group matchers .", matcherGroupName); @SuppressWarnings("unchecked") List<AttributeMatcher> attrMatcherList = (List<AttributeMatcher>) getBeanFromContext(matcherGroupName); ObjectLocalCache cache = new ObjectLocalCache(dbClient); initializeCommonReferencesForAllMatchers(cache, coordinator); errorMessage.setLength(0); // Clear the existing content before matcher for (AttributeMatcher matcher : attrMatcherList) { int poolSizeAtTheStart = matchedPools.size(); if (!matchedPools.isEmpty()) { _logger.debug("passing {} pools to match", matchedPools.size()); matchedPools = matcher.runMatchStoragePools(matchedPools, attributeMap, errorMessage); if (matchedPools.isEmpty()) { _logger.info(String.format("Failed to find match because of %s", matcher.getClass().getSimpleName())); } else if (matchedPools.size() < poolSizeAtTheStart) { _logger.info(String.format("%s eliminated %d pools from the matched list", matcher.getClass().getSimpleName(), poolSizeAtTheStart - matchedPools.size())); } } else { _logger.info("No storage pools found matching with attributeMap passed"); break; } } cache.clearCache(); } catch (Exception ex) { // Clearing the matched pools as there is an exception occurred while processing. matchedPools.clear(); _logger.error("Exception occurred while matching pools with vPools", ex); } finally { _logger.info("Ended execution of {} group matchers .", matcherGroupName); } } else { errorMessage.append("Virtual pool does not have matching Storage pool."); _logger.error(errorMessage.toString()); } return matchedPools; } /** * Method will iterate through all AttributeMatchers (not a subset) and apply common references to them. * * @param cache [IN] - ObjectLocalCache to be applied to matchers * @param coordinator [IN] - Coordinator reference to be applied to matchers */ private void initializeCommonReferencesForAllMatchers(ObjectLocalCache cache, CoordinatorClient coordinator) { Map<String, AttributeMatcher> uniqueMatcherMap = new HashMap<>(); @SuppressWarnings("unchecked") List<AttributeMatcher> vpoolMatchers = (List<AttributeMatcher>) getBeanFromContext(AttributeMatcher.VPOOL_MATCHERS); for (AttributeMatcher matcher : vpoolMatchers) { uniqueMatcherMap.put(matcher.getClass().getSimpleName(), matcher); } @SuppressWarnings("unchecked") List<AttributeMatcher> basicMatchers = (List<AttributeMatcher>) getBeanFromContext(AttributeMatcher.BASIC_PLACEMENT_MATCHERS); for (AttributeMatcher matcher : basicMatchers) { uniqueMatcherMap.put(matcher.getClass().getSimpleName(), matcher); } @SuppressWarnings("unchecked") List<AttributeMatcher> placementMatchers = (List<AttributeMatcher>) getBeanFromContext(AttributeMatcher.PLACEMENT_MATCHERS); for (AttributeMatcher matcher : placementMatchers) { uniqueMatcherMap.put(matcher.getClass().getSimpleName(), matcher); } // The matchers have been gathered in the Map by class name, making it a unique list of // matchers. Now iterate through them applying there references ... for (AttributeMatcher matcher : uniqueMatcherMap.values()) { matcher.setCoordinatorClient(coordinator); matcher.setObjectCache(cache); } } /** * Sometimes context is not loading properly resulting the beanFactory to null. * To avoid this, we should reload the context using refresh. * and the return the bean by its matcherGroupName. * * @param matcherGroupName * @return beanObj */ private Object getBeanFromContext(String matcherGroupName) { Object beanObj = _context.getBean(matcherGroupName); if (null == beanObj) { _logger.error("No bean found for groupName {0} to match Pools for give attributesMap", matcherGroupName); throw new ServiceCodeException(ServiceCode.CONTROLLER_STORAGE_ERROR, "No bean found for groupName {0} to match Pools for give attributesMap", new Object[] { matcherGroupName }); } return beanObj; } /** * Find the available attributes in a given varray. * * @param vArrayId * @param neighborhoodPools * @param dbClient * @param matcherGroupName */ public Map<String, Set<String>> getAvailableAttributes(URI vArrayId, List<StoragePool> neighborhoodPools, ObjectLocalCache cache, String matcherGroupName) { Map<String, Set<String>> vArrayAvailableAttrs = new HashMap<String, Set<String>>(); try { @SuppressWarnings("unchecked") List<AttributeMatcher> attrMatcherList = (List<AttributeMatcher>) getBeanFromContext(matcherGroupName); for (AttributeMatcher matcher : attrMatcherList) { matcher.setObjectCache(cache); Map<String, Set<String>> availableAttribute = matcher.getAvailableAttribute(neighborhoodPools, vArrayId); if (!availableAttribute.isEmpty()) { _logger.info("Found available attributes using matcher {}", matcher); vArrayAvailableAttrs.putAll(availableAttribute); } } _logger.info("Found {} available attributes for vArray {}", vArrayAvailableAttrs, vArrayId); } catch (Exception ex) { _logger.error("Exception occurred while getting available attributes for vArray {}", vArrayId, ex); vArrayAvailableAttrs.clear(); throw new ServiceCodeException(ServiceCode.CONTROLLER_STORAGE_ERROR, "Exception occurred while getting available attributes for vArray.", new Object[] { vArrayId }); } return vArrayAvailableAttrs; } }