/* * Copyright (c) 2008-2012 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.placement; import java.net.URI; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; 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.api.service.authorization.PermissionsHelper; import com.emc.storageos.api.service.impl.placement.FileRecommendation.FileType; import com.emc.storageos.api.service.impl.resource.utils.ProjectUtility; import com.emc.storageos.customconfigcontroller.CustomConfigConstants; import com.emc.storageos.customconfigcontroller.impl.CustomConfigHandler; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.PrefixConstraint; import com.emc.storageos.db.client.model.DiscoveredDataObject; import com.emc.storageos.db.client.model.DiscoveredDataObject.DiscoveryStatus; import com.emc.storageos.db.client.model.DiscoveredDataObject.RegistrationStatus; import com.emc.storageos.db.client.model.DiscoveredDataObject.Type; import com.emc.storageos.db.client.model.FileShare; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.StorageHADomain; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StoragePort; import com.emc.storageos.db.client.model.StorageProtocol; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.VirtualArray; import com.emc.storageos.db.client.model.VirtualNAS; import com.emc.storageos.db.client.model.VirtualNAS.VirtualNasState; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.model.TaskList; import com.emc.storageos.model.TaskResourceRep; import com.emc.storageos.model.file.FileSystemParam; import com.emc.storageos.svcs.errorhandling.resources.APIException; import com.emc.storageos.volumecontroller.AttributeMatcher; import com.emc.storageos.volumecontroller.Recommendation; import com.emc.storageos.volumecontroller.impl.StoragePortAssociationHelper; import com.emc.storageos.volumecontroller.impl.plugins.metering.smis.processor.MetricsKeys; import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper; /** * StorageScheduler service for block and file storage. StorageScheduler is done * based on desired class-of-service parameters for the provisioned storage. */ public class FileStorageScheduler implements Scheduler { public final Logger _log = LoggerFactory .getLogger(FileStorageScheduler.class); private static final String SCHEDULER_NAME = "filestorage"; private static final String ENABLE_METERING = "enable-metering"; private DbClient _dbClient; private StorageScheduler _scheduler; private CustomConfigHandler customConfigHandler; private Map<String, String> configInfo; private PermissionsHelper permissionsHelper; public void setDbClient(DbClient dbClient) { _dbClient = dbClient; } public void setScheduleUtils(StorageScheduler scheduleUtils) { _scheduler = scheduleUtils; } public void setCustomConfigHandler(CustomConfigHandler customConfigHandler) { this.customConfigHandler = customConfigHandler; } public Map<String, String> getConfigInfo() { return configInfo; } public void setConfigInfo(Map<String, String> configInfo) { this.configInfo = configInfo; } public void setPermissionsHelper(PermissionsHelper permissionsHelper) { this.permissionsHelper = permissionsHelper; } /** * Schedule storage for fileshare in the varray with the given CoS * capabilities. * * @param vArray * @param vPool * @param capabilities * @param project * @return list of recommended storage ports for VNAS */ public List<FileRecommendation> placeFileShare(VirtualArray vArray, VirtualPool vPool, VirtualPoolCapabilityValuesWrapper capabilities, Project project, Map<String, Object> optionalAttributes) { _log.debug("Schedule storage for {} resource(s) of size {}.", capabilities.getResourceCount(), capabilities.getSize()); // create map object if null, it used to receive the error message if (optionalAttributes == null) { optionalAttributes = new HashMap<String, Object>(); } if (capabilities.getFileProtectionSourceStorageDevice() != null || capabilities.getTargetStorageSystem() != null) { Set<String> storageSystemSet = new HashSet<String>(); if (capabilities.getFileProtectionSourceStorageDevice() != null) { storageSystemSet.add(capabilities.getFileProtectionSourceStorageDevice().toString()); } else if (capabilities.getTargetStorageSystem() != null) { storageSystemSet.add(capabilities.getTargetStorageSystem().toString()); } optionalAttributes.put(AttributeMatcher.Attributes.storage_system.name(), storageSystemSet); } // Get all storage pools that match the passed vpool params and // protocols. In addition, the pool must have enough capacity // to hold at least one resource of the requested size. List<StoragePool> candidatePools = _scheduler.getMatchingPools(vArray, vPool, capabilities, optionalAttributes); if (CollectionUtils.isEmpty(candidatePools)) { StringBuffer errorMessage = new StringBuffer(); if (optionalAttributes.get(AttributeMatcher.ERROR_MESSAGE) != null) { errorMessage = (StringBuffer) optionalAttributes.get(AttributeMatcher.ERROR_MESSAGE); } throw APIException.badRequests.noStoragePools(vArray.getLabel(), vPool.getLabel(), errorMessage.toString()); } // Holds the invalid virtual nas servers from both // assigned and un-assigned list. // the invalid vnas server clould be // over loaded or protocol not supported or // the ports from these invalid vnas servers // should not be considered for file provisioning!!! List<VirtualNAS> invalidNasServers = new ArrayList<VirtualNAS>(); boolean provisioningOnVirtualNAS = true; VirtualNAS sourcevNAsServer = null; if (capabilities.getPersonality() != null && capabilities.getPersonality().equalsIgnoreCase(VirtualPoolCapabilityValuesWrapper.FILE_REPLICATION_TARGET)) { // Get the source nas server, if no source vnas server, then need storage from physical nas server!! URI sourceVNas = capabilities.getSourceVirtualNasServer(); if (sourceVNas == null) { provisioningOnVirtualNAS = false; } else { sourcevNAsServer = _dbClient.queryObject(VirtualNAS.class, sourceVNas); if (sourcevNAsServer == null || sourcevNAsServer.getInactive()) { provisioningOnVirtualNAS = false; } } } List<FileRecommendation> fileRecommendations = new ArrayList<FileRecommendation>(); List<FileRecommendation> recommendations = null; if (provisioningOnVirtualNAS) { // Get the recommendation based on virtual nas servers Map<VirtualNAS, List<StoragePool>> vNASPoolMap = getRecommendedVirtualNASBasedOnCandidatePools( vPool, vArray.getId(), candidatePools, project, invalidNasServers); VirtualNAS currvNAS = null; if (!vNASPoolMap.isEmpty()) { for (Entry<VirtualNAS, List<StoragePool>> eachVNASEntry : vNASPoolMap.entrySet()) { // If No storage pools recommended!!! if (eachVNASEntry.getValue().isEmpty()) { continue; } currvNAS = eachVNASEntry.getKey(); if (currvNAS != null) { if (capabilities.getPersonality() != null && capabilities.getPersonality() .equalsIgnoreCase(VirtualPoolCapabilityValuesWrapper.FILE_REPLICATION_TARGET)) { if (sourcevNAsServer != null && sourcevNAsServer.getBaseDirPath().equalsIgnoreCase(currvNAS.getBaseDirPath())) { _log.info("Target Nas server path {} is similar to source nas server {}, so considering this nas server", currvNAS.getBaseDirPath(), sourcevNAsServer.getBaseDirPath()); } else if (capabilities.getTargetNasServer() != null && capabilities.getTargetNasServer().equals(currvNAS.getId())) { _log.info("Nas server {} is same as required target Nas server {}, so considering this nas server", currvNAS.getId(), capabilities.getTargetNasServer()); } else { _log.info("Nas server {} is not the required target Nas server, so ignoring this nas server", currvNAS.getId()); continue; } } _log.info("Best vNAS selected: {}", currvNAS.getNasName()); List<StoragePool> recommendedPools = eachVNASEntry.getValue(); // Get the recommendations for the current vnas pools. List<Recommendation> poolRecommendations = _scheduler .getRecommendationsForPools(vArray.getId().toString(), recommendedPools, capabilities); // If we did not find pool recommendation for current vNAS // Pick the pools from next available vNas recommended pools!!! if (poolRecommendations.isEmpty()) { _log.info("Skipping vNAS {}, as pools are not having enough resources", currvNAS.getNasName()); continue; } // Get the file recommendations for pool recommendation!!! recommendations = getFileRecommendationsForVNAS(currvNAS, vArray.getId(), vPool, poolRecommendations); if (!recommendations.isEmpty()) { fileRecommendations.addAll(recommendations); if (!capabilities.isVpoolProjectPolicyAssign() && !capabilities.getAllSourceRecommnedations()) { _log.info("Selected vNAS {} for placement", currvNAS.getNasName()); break; } else { // Policy assignment required to create the policy on all applicable vNAS servers!!! _log.info(" vNAS {} for Added to the list of recommendations", currvNAS.getNasName()); } } } } } } // In case of // 1. vNAS does not provide file recommendations or // 2. vpool does not have storage pools from vnx or // 3. vnx does not have vdms // Get the file recommendations if (fileRecommendations == null || fileRecommendations.isEmpty() || capabilities.isVpoolProjectPolicyAssign() || capabilities.getAllSourceRecommnedations() || isTargetRequiredOnPhyscialNAS(capabilities)) { // Get the recommendations for the candidate pools. _log.info("Placement on HADomain matching pools"); List<Recommendation> poolRecommendations = _scheduler .getRecommendationsForPools(vArray.getId().toString(), candidatePools, capabilities); recommendations = selectStorageHADomainMatchingVpool(vPool, vArray.getId(), poolRecommendations, invalidNasServers); if (recommendations != null && !recommendations.isEmpty()) { if (fileRecommendations != null) { fileRecommendations.addAll(recommendations); } } } // We need to place all the resources. If we can't then // log an error and clear the list of recommendations. if (fileRecommendations == null || fileRecommendations.isEmpty()) { _log.error( "Could not find matching pools for virtual array {} & vpool {}", vArray.getId(), vPool.getId()); } else { // add code for file for default recommendations for file data for (FileRecommendation recommendation : fileRecommendations) { FileRecommendation fileRecommendation = recommendation; fileRecommendation.setFileType(FileType.FILE_SYSTEM_DATA); StorageSystem system = _dbClient.queryObject(StorageSystem.class, recommendation.getSourceStorageSystem()); fileRecommendation.setDeviceType(system.getSystemType()); } } return fileRecommendations; } private boolean isTargetRequiredOnPhyscialNAS(VirtualPoolCapabilityValuesWrapper capabilities) { if (NullColumnValueGetter.isNullURI(capabilities.getTargetNasServer())) { return false; } if (capabilities.getTargetNasServer() != null && capabilities.getTargetNasServer().toString().contains("VirtualNAS")) { return false; } return true; } /** * Returns the File recommendations for vNAS * * @param vNAS * @param vArrayURI * @param vPool * @param poolRecommendations * @return */ private List<FileRecommendation> getFileRecommendationsForVNAS(VirtualNAS vNAS, URI vArrayURI, VirtualPool vPool, List<Recommendation> poolRecommendations) { List<FileRecommendation> fileRecommendations = new ArrayList<FileRecommendation>(); List<StoragePort> ports = getAssociatedStoragePorts(vNAS, vArrayURI); List<URI> storagePortURIList = new ArrayList<URI>(); for (Iterator<StoragePort> iterator = ports.iterator(); iterator.hasNext();) { StoragePort storagePort = iterator.next(); // storageport must be part of network if (!NullColumnValueGetter.isNullURI(storagePort.getNetwork())) { storagePortURIList.add(storagePort.getId()); } } for (Iterator<Recommendation> iterator = poolRecommendations.iterator(); iterator.hasNext();) { Recommendation recommendation = iterator.next(); FileRecommendation fRec = new FileRecommendation(recommendation); URI storageDevice = fRec.getSourceStorageSystem(); if (vNAS.getStorageDeviceURI().equals(storageDevice)) { fRec.setStoragePorts(storagePortURIList); fRec.setvNAS(vNAS.getId()); fileRecommendations.add(fRec); } } return fileRecommendations; } /** * Select storage port for exporting file share to the given client. One IP * transport zone per varray. Selects only one storage port for all exports * of a file share * * @param fs * file share being exported * @param protocol * file storage protocol for this export * @param clients * client network address or name * @return storgaePort * @throws badRequestException */ public StoragePort placeFileShareExport(FileShare fs, String protocol, List<String> clients) { StoragePort sp; if (fs.getStoragePort() == null) { _log.info("Placement for file system {} with no assigned port.", fs.getName()); // if no storage port is selected yet, select one and record the // selection List<StoragePort> ports = getStorageSystemPortsInVarray( fs.getStorageDevice(), fs.getVirtualArray()); StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, fs.getStorageDevice()); if (Type.isilon.name().equals(storageSystem.getSystemType())) { if (ports != null && !ports.isEmpty()) { // Check if these ports are associated with vNAS for (Iterator<StoragePort> iterator = ports.iterator(); iterator.hasNext();) { StoragePort storagePort = iterator.next(); List<VirtualNAS> vNASList = StoragePortAssociationHelper.getStoragePortVirtualNAS(storagePort, _dbClient); if (vNASList != null && !vNASList.isEmpty()) { /* * Remove the associated port. Because during file system placement, * storage port will already be assigned to FS. In that case, this block won't * be executed. */ _log.info("Removing port {} as it is assigned to a vNAS.", storagePort.getNativeGuid()); iterator.remove(); } } } } // Filter ports based on protocol (for example, if CIFS or NFS is // required) if ((null != protocol) && (!protocol.isEmpty())) { getPortsWithFileSharingProtocol(protocol, ports); } if (ports == null || ports.isEmpty()) { _log.error(MessageFormat .format("There are no active and registered storage ports assigned to virtual array {0}", fs.getVirtualArray())); throw APIException.badRequests.noStoragePortFoundForVArray(fs .getVirtualArray().toString()); } Collections.shuffle(ports); sp = ports.get(0); // update storage port selections for file share exports fs.setStoragePort(sp.getId()); fs.setPortName(sp.getPortName()); _dbClient.persistObject(fs); } else { // if a storage port is already selected for the fileshare, use that // port for all exports sp = _dbClient.queryObject(StoragePort.class, fs.getStoragePort()); _log.info("Placement for file system {} with port {}.", fs.getName(), sp.getPortName()); // verify port supports new request. if ((null != protocol) && (!protocol.isEmpty())) { List<StoragePort> ports = new ArrayList<StoragePort>(); ports.add(sp); getPortsWithFileSharingProtocol(protocol, ports); if (ports.isEmpty()) { _log.error(MessageFormat .format("There are no active and registered storage ports assigned to virtual array {0}", fs.getVirtualArray())); throw APIException.badRequests .noStoragePortFoundForVArray(fs.getVirtualArray() .toString()); } } } return sp; } /** * Retrieve list of recommended storage ports for file placement * Follows the step to get recommendations for file placement. * 1. Get the valid vNas servers assigned to given project. * a. Get the active vNas assigned to project. * b. filter out vNas, if any of them are overloaded. * c. filter out vNas which based on vPool protocols. * 2. if any valid assigned vNas found for project, goto step #3 * a. get all vNas servers in given virtual array * b. filter out vNas server which are assigned to some project * c. filter out vNas, if any of them are overloaded. * d. filter out vNas which based on vPool protocols * 3. If No - vNas servers found, goto step # * 3. if the dynamic metric collection enabled * a. sort the list of qualified vNas servers based on * i. vNas - avgPercentageBusy * ii. vNas - StorageObjects * iii. vNas - Storage capacity * 4. sort the list of qualified vNas servers based on static load * i. vNas - StorageObjects * ii. vNas - Storage capacity * 5. Pick the overlapping vNas server in the order and recommended by vPool. * 6. Pick the overlapping StorageHADomian recommended by vPool. * * @param vPool * @param vArrayURI * virtual array URI * @param candidatePools * @param project * @return list of recommended storage ports for VNAS * */ private Map<VirtualNAS, List<StoragePool>> getRecommendedVirtualNASBasedOnCandidatePools( VirtualPool vPool, URI vArrayURI, List<StoragePool> candidatePools, Project project, List<VirtualNAS> invalidNasServers) { Map<VirtualNAS, List<StoragePool>> map = new LinkedHashMap<VirtualNAS, List<StoragePool>>(); List<VirtualNAS> vNASList = null; if (project != null) { _log.info( "Get matching recommendations based on assigned VNAS to project {}", project.getLabel()); vNASList = getVNASServersInProject(project, vArrayURI, vPool, invalidNasServers); } if (vNASList == null || vNASList.isEmpty()) { _log.info( "Get matching recommendations based on un-assigned VNAS in varray {}", vArrayURI); vNASList = getUnassignedVNASServers(vArrayURI, vPool, project, invalidNasServers); } if (vNASList != null && !vNASList.isEmpty()) { boolean meteringEnabled = Boolean.parseBoolean(configInfo.get(ENABLE_METERING)); if (meteringEnabled) { _log.info("Metering collection is enabled. Sort vNAS list based on performance."); String dynamicPerformanceEnabled = customConfigHandler.getComputedCustomConfigValue( CustomConfigConstants.NAS_DYNAMIC_PERFORMANCE_PLACEMENT_ENABLED, "vnxfile", null); _log.info("NAS dynamic performance placement enabled? : {}", dynamicPerformanceEnabled); if (Boolean.valueOf(dynamicPerformanceEnabled)) { _log.debug("Considering dynamic load to sort virtual NASs"); sortVNASListOnDyanamicLoad(vNASList); } else { _log.debug("Considering static load to sort virtual NASs"); sortVNASListOnStaticLoad(vNASList); } } else { Collections.shuffle(vNASList); } } for (VirtualNAS vNAS : vNASList) { List<StoragePool> storagePools = new ArrayList<StoragePool>(); for (StoragePool storagePool : candidatePools) { if (vNAS.getStorageDeviceURI().equals(storagePool.getStorageDevice())) { storagePools.add(storagePool); } } if (!storagePools.isEmpty()) { map.put(vNAS, storagePools); } } return map; } /** * Sort list of VNAS servers based on dynamic load on each VNAS * * @param vNASList * list of VNAS servers * */ private void sortVNASListOnDyanamicLoad(List<VirtualNAS> vNASList) { Collections.sort(vNASList, new Comparator<VirtualNAS>() { @Override public int compare(VirtualNAS v1, VirtualNAS v2) { // 1. Sort virtual nas servers based on dynamic load factor 'avgPercentBusy'. // 2. If multiple virtual nas servers found to be similar performance, // sort the virtual nas based on capacity and then number of storage objects!!! int value = 0; Double avgUsedPercentV1 = MetricsKeys.getDoubleOrNull(MetricsKeys.avgPortPercentBusy, v1.getMetrics()); Double avgUsedPercentV2 = MetricsKeys.getDoubleOrNull(MetricsKeys.avgPortPercentBusy, v2.getMetrics()); if (avgUsedPercentV1 != null && avgUsedPercentV2 != null) { value = avgUsedPercentV1.compareTo(avgUsedPercentV2); } if (value == 0) { Long storageCapacityOfV1 = MetricsKeys.getLong(MetricsKeys.usedStorageCapacity, v1.getMetrics()); Long storageCapacityOfV2 = MetricsKeys.getLong(MetricsKeys.usedStorageCapacity, v2.getMetrics()); value = storageCapacityOfV1.compareTo(storageCapacityOfV2); } if (value == 0) { Long storageObjectsOfV1 = MetricsKeys.getLong(MetricsKeys.storageObjects, v1.getMetrics()); Long storageObjectsOfV2 = MetricsKeys.getLong(MetricsKeys.storageObjects, v2.getMetrics()); value = storageObjectsOfV1.compareTo(storageObjectsOfV2); } return value; } }); } /** * Sort list of VNAS servers based on static load on each VNAS * * @param vNASList * list of VNAS servers * */ private void sortVNASListOnStaticLoad(List<VirtualNAS> vNASList) { Collections.sort(vNASList, new Comparator<VirtualNAS>() { @Override public int compare(VirtualNAS v1, VirtualNAS v2) { int value = 0; Double percentLoadV1 = MetricsKeys.getDoubleOrNull(MetricsKeys.percentLoad, v1.getMetrics()); Double percentLoadV2 = MetricsKeys.getDoubleOrNull(MetricsKeys.percentLoad, v2.getMetrics()); if (percentLoadV1 != null && percentLoadV2 != null) { value = percentLoadV1.compareTo(percentLoadV2); } if (value == 0) { // 1. Sort virtual nas servers based on static load factor 'storageCapacity'. // 2. If multiple virtual nas servers found to be similar performance, // sort the virtual nas based on number of storage objects!!! Long storageCapacityOfV1 = MetricsKeys.getLong(MetricsKeys.usedStorageCapacity, v1.getMetrics()); Long storageCapacityOfV2 = MetricsKeys.getLong(MetricsKeys.usedStorageCapacity, v2.getMetrics()); value = storageCapacityOfV1.compareTo(storageCapacityOfV2); } if (value == 0) { Long storageObjectsOfV1 = MetricsKeys.getLong(MetricsKeys.storageObjects, v1.getMetrics()); Long storageObjectsOfV2 = MetricsKeys.getLong(MetricsKeys.storageObjects, v2.getMetrics()); return storageObjectsOfV1.compareTo(storageObjectsOfV2); } return value; } }); } /** * Get list of unassigned VNAS servers * * @param vArrayURI * @param vpool * @return vNASList * */ private List<VirtualNAS> getUnassignedVNASServers(URI vArrayURI, VirtualPool vpool, Project project, List<VirtualNAS> invalidNasServers) { _log.info("Get vNAS servers from the unreserved list..."); _log.info("Get vNAS servers from the unreserved list..."); List<VirtualNAS> vNASList = new ArrayList<VirtualNAS>(); _log.debug("Get VNAS servers in the vArray {}", vArrayURI); List<URI> vNASURIList = _dbClient .queryByConstraint(ContainmentConstraint.Factory .getVirtualNASInVirtualArrayConstraint(vArrayURI)); vNASList = _dbClient.queryObject(VirtualNAS.class, vNASURIList); if (vNASList != null && !vNASList.isEmpty()) { Set<String> projectDomains = ProjectUtility.getDomainsOfProject(permissionsHelper, project); for (Iterator<VirtualNAS> iterator = vNASList.iterator(); iterator .hasNext();) { VirtualNAS vNAS = iterator.next(); _log.info("Checking vNAS - {} : {}", vNAS.getNasName(), vNAS.getId()); if (!isVNASActive(vNAS)) { _log.info("Removing vNAS {} as it is inactive", vNAS.getNasName()); iterator.remove(); invalidNasServers.add(vNAS); } else if (MetricsKeys.getBoolean(MetricsKeys.overLoaded, vNAS.getMetrics())) { _log.info("Removing vNAS {} as it is overloaded", vNAS.getNasName()); iterator.remove(); invalidNasServers.add(vNAS); } else if (!vNAS.getProtocols().containsAll( vpool.getProtocols())) { _log.info("Removing vNAS {} as it does not support vpool protocols: {}", vNAS.getNasName(), vpool.getProtocols()); iterator.remove(); invalidNasServers.add(vNAS); } else if (!vNAS.isNotAssignedToProject()) { if (project != null && !vNAS.getAssociatedProjects().contains(project.getId())) { _log.info("Removing vNAS {} as it is assigned to project", vNAS.getNasName()); iterator.remove(); invalidNasServers.add(vNAS); } } else if (!ProjectUtility.doesProjectDomainMatchesWithVNASDomain(projectDomains, vNAS)) { _log.info("Removing vNAS {} as its domain does not match with project's domain: {}", vNAS.getNasName(), projectDomains); iterator.remove(); invalidNasServers.add(vNAS); } } } if (vNASList != null) { _log.info("Got {} un-assigned vNas servers in the vArray {}", vNASList.size(), vArrayURI); } return vNASList; } /** * Get list of associated storage ports of VNAS server which are part of given virtual array. * * @param vNAS * @param vArrayURI * virtual array * @return spList * */ private List<StoragePort> getAssociatedStoragePorts(VirtualNAS vNAS, URI vArrayURI) { StringSet spIdSet = vNAS.getStoragePorts(); List<URI> spURIList = new ArrayList<URI>(); if (spIdSet != null && !spIdSet.isEmpty()) { for (String id : spIdSet) { spURIList.add(URI.create(id)); } } List<StoragePort> spList = _dbClient.queryObject(StoragePort.class, spURIList); if (spList != null && !spList.isEmpty()) { for (Iterator<StoragePort> iterator = spList.iterator(); iterator .hasNext();) { StoragePort storagePort = iterator.next(); if (storagePort.getInactive() || storagePort.getTaggedVirtualArrays() == null || !storagePort.getTaggedVirtualArrays().contains( vArrayURI.toString()) || !RegistrationStatus.REGISTERED.toString() .equalsIgnoreCase( storagePort.getRegistrationStatus()) || (StoragePort.OperationalStatus.valueOf(storagePort .getOperationalStatus())) .equals(StoragePort.OperationalStatus.NOT_OK) || !DiscoveredDataObject.CompatibilityStatus.COMPATIBLE .name().equals( storagePort.getCompatibilityStatus()) || !DiscoveryStatus.VISIBLE.name().equals( storagePort.getDiscoveryStatus()) || (storagePort.getTag() != null && storagePort.getTag().contains("dr_port"))) { iterator.remove(); } } } if (spList != null && !spList.isEmpty()) { Collections.sort(spList, new Comparator<StoragePort>() { @Override public int compare(StoragePort sp1, StoragePort sp2) { if (sp1.getMetrics() != null && sp2.getMetrics() != null) { Double sp1UsedPercent = MetricsKeys.getDoubleOrNull(MetricsKeys.avgPortPercentBusy, sp1.getMetrics()); Double sp2UsedPercent = MetricsKeys.getDoubleOrNull(MetricsKeys.avgPortPercentBusy, sp2.getMetrics()); if (sp1UsedPercent != null && sp2UsedPercent != null) { return sp1UsedPercent.compareTo(sp2UsedPercent); } } return 0; } }); } return spList; } /** * Get list of VNAS servers assigned to a project * * @param project * @param varrayUri * @param vpool * @return vNASList * */ private List<VirtualNAS> getVNASServersInProject(Project project, URI varrayUri, VirtualPool vpool, List<VirtualNAS> invalidNasServers) { List<VirtualNAS> vNASList = null; _log.debug("Get VNAS servers assigned to project {}", project); StringSet vNASServerIdSet = project.getAssignedVNasServers(); if (vNASServerIdSet != null && !vNASServerIdSet.isEmpty()) { _log.info("Number of vNAS servers assigned to this project: {}", vNASServerIdSet.size()); List<URI> vNASURIList = new ArrayList<URI>(); for (String vNASId : vNASServerIdSet) { vNASURIList.add(URI.create(vNASId)); } vNASList = _dbClient.queryObject(VirtualNAS.class, vNASURIList); for (Iterator<VirtualNAS> iterator = vNASList.iterator(); iterator .hasNext();) { VirtualNAS virtualNAS = iterator.next(); // Remove inactive, incompatible, invisible vNAS _log.info("Checking vNAS - {} : {}", virtualNAS.getNasName(), virtualNAS.getId()); if (!isVNASActive(virtualNAS)) { _log.info("Removing vNAS {} as it is inactive", virtualNAS.getNasName()); iterator.remove(); invalidNasServers.add(virtualNAS); } else if (!virtualNAS.getAssignedVirtualArrays().contains( varrayUri.toString())) { _log.info("Removing vNAS {} as it is not part of varray: {}", virtualNAS.getNasName(), varrayUri.toString()); iterator.remove(); invalidNasServers.add(virtualNAS); } else if (MetricsKeys.getBoolean(MetricsKeys.overLoaded, virtualNAS.getMetrics())) { _log.info("Removing vNAS {} as it is overloaded", virtualNAS.getNasName()); iterator.remove(); invalidNasServers.add(virtualNAS); } else if (null != virtualNAS.getProtocols() && null != vpool.getProtocols() && !virtualNAS.getProtocols().containsAll(vpool.getProtocols())) { _log.info("Removing vNAS {} as it does not support vpool protocols: {}", virtualNAS.getNasName(), vpool.getProtocols()); iterator.remove(); invalidNasServers.add(virtualNAS); } } } if (vNASList != null) { _log.info( "Got {} assigned VNAS servers for project {}", vNASList.size(), project); } return vNASList; } /** * Validate whether VNAS is active or not * * @param virtualNAS * @return true if VNAS is active else false * */ private boolean isVNASActive(VirtualNAS virtualNAS) { if (virtualNAS.getInactive() || virtualNAS.getAssignedVirtualArrays() == null || virtualNAS.getAssignedVirtualArrays().isEmpty() || !RegistrationStatus.REGISTERED.toString().equalsIgnoreCase( virtualNAS.getRegistrationStatus()) || !DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name() .equals(virtualNAS.getCompatibilityStatus()) || !VirtualNasState.LOADED.name().equals(virtualNAS.getVNasState()) || !DiscoveryStatus.VISIBLE.name().equals( virtualNAS.getDiscoveryStatus())) { return false; } return true; } /** * Fetches and returns all the storage ports for a given storage system that * are in a given varray * * @param storageSystemUri * the storage system URI * @param varray * the varray URI * @return a list of all the storage ports for a given storage system */ private List<StoragePort> getStorageSystemPortsInVarray( URI storageSystemUri, URI varray) { List<URI> allPorts = _dbClient .queryByConstraint(ContainmentConstraint.Factory .getStorageDeviceStoragePortConstraint(storageSystemUri)); List<StoragePort> ports = _dbClient.queryObject(StoragePort.class, allPorts); Iterator<StoragePort> itr = ports.iterator(); StoragePort temp = null; while (itr.hasNext()) { temp = itr.next(); if (temp.getInactive() || temp.getTaggedVirtualArrays() == null || !temp.getTaggedVirtualArrays().contains( varray.toString()) || !RegistrationStatus.REGISTERED.toString() .equalsIgnoreCase(temp.getRegistrationStatus()) || (StoragePort.OperationalStatus.valueOf(temp .getOperationalStatus())) .equals(StoragePort.OperationalStatus.NOT_OK) || !DiscoveredDataObject.CompatibilityStatus.COMPATIBLE .name().equals(temp.getCompatibilityStatus()) || !DiscoveryStatus.VISIBLE.name().equals( temp.getDiscoveryStatus()) || (temp.getTag() != null && temp.getTag().contains("dr_port"))) { itr.remove(); } } return ports; } /** * Removes storage ports that do not support the specified file sharing * protocol. * * @param protocol * the required protocol that the port must support. * @param ports * the list of available ports. */ private void getPortsWithFileSharingProtocol(String protocol, List<StoragePort> ports) { if (null == protocol || null == ports || ports.isEmpty()) { return; } _log.debug("Validate protocol: {}", protocol); if (!StorageProtocol.File.NFS.name().equalsIgnoreCase(protocol) && !StorageProtocol.File.CIFS.name().equalsIgnoreCase(protocol)) { _log.warn("Not a valid file sharing protocol: {}", protocol); return; } StoragePort tempPort = null; StorageHADomain haDomain = null; Iterator<StoragePort> itr = ports.iterator(); while (itr.hasNext()) { tempPort = itr.next(); haDomain = null; URI domainUri = tempPort.getStorageHADomain(); if (null != domainUri) { haDomain = _dbClient.queryObject(StorageHADomain.class, domainUri); } if (null != haDomain) { StringSet supportedProtocols = haDomain .getFileSharingProtocols(); if (supportedProtocols == null || !supportedProtocols.contains(protocol)) { itr.remove(); _log.debug("Removing port {}", tempPort.getPortName()); } } _log.debug("Number ports remainng: {}", ports.size()); } } private List<URI> getvNasStoragePortUris(List<VirtualNAS> invalidNasServers) { List<URI> spUriList = new ArrayList<URI>(); for (VirtualNAS vNas : invalidNasServers) { StringSet spIdSet = vNas.getStoragePorts(); if (spIdSet != null && !spIdSet.isEmpty()) { for (String id : spIdSet) { spUriList.add(URI.create(id)); } } } return spUriList; } /** * Select the right StorageHADomain matching vpool protocols. * * @param vpool * @param vArray * @param poolRecommends * recommendations after selecting matching storage pools. * @return list of FileRecommendation */ private List<FileRecommendation> selectStorageHADomainMatchingVpool( VirtualPool vpool, URI vArray, List<Recommendation> poolRecommends, List<VirtualNAS> invalidNasServers) { // Get the storage ports from invalid vnas servers!!! List<URI> invalidPorts = getvNasStoragePortUris(invalidNasServers); _log.debug("select matching StorageHADomain"); List<FileRecommendation> result = new ArrayList<FileRecommendation>(); for (Recommendation recommendation : poolRecommends) { FileRecommendation rec = new FileRecommendation(recommendation); URI storageUri = recommendation.getSourceStorageSystem(); StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageUri); // Same check for VNXe will be done here. // TODO: normalize behavior across file arrays so that this check is // not required. // TODO: Implement fake storageHADomain for DD to fit the viPR model // For unity, file system can be created only on vNas. There is no reason to find a matching HADomain if no // vnas servers were // found if (storage.getSystemType().equals(Type.unity.toString())) { continue; } if (!storage.getSystemType().equals(Type.netapp.toString()) && !storage.getSystemType().equals(Type.netappc.toString()) && !storage.getSystemType().equals(Type.vnxe.toString()) && !storage.getSystemType().equals(Type.vnxfile.toString()) && !storage.getSystemType().equals( Type.datadomain.toString())) { result.add(rec); continue; } List<StoragePort> portList = getStorageSystemPortsInVarray( storageUri, vArray); if (portList == null || portList.isEmpty()) { _log.info("No valid storage port found from the virtual array: " + vArray); continue; } List<URI> storagePorts = new ArrayList<URI>(); boolean foundValidPort = false; for (StoragePort port : portList) { if (invalidPorts.contains(port.getId())) { _log.debug("Storage port {} belongs to invalid vNas server ", port.getIpAddress()); continue; } foundValidPort = true; _log.debug("Looking for port {}", port.getLabel()); URI haDomainUri = port.getStorageHADomain(); // Data Domain does not have a filer entity. if ((haDomainUri == null) && (!storage.getSystemType().equals( Type.datadomain.toString()))) { _log.info("No StorageHADomain URI for port {}", port.getLabel()); continue; } StorageHADomain haDomain = null; if (haDomainUri != null) { haDomain = _dbClient.queryObject(StorageHADomain.class, haDomainUri); } if (haDomain != null) { StringSet protocols = haDomain.getFileSharingProtocols(); // to see if it matches virtualPool's protocols StringSet vpoolProtocols = vpool.getProtocols(); if (protocols != null && protocols.containsAll(vpoolProtocols)) { _log.info( "Found the StorageHADomain {} for recommended storagepool: {}", haDomain.getName(), recommendation.getSourceStoragePool()); storagePorts.add(port.getId()); } } else if (storage.getSystemType().equals( Type.datadomain.toString())) { // The same file system on DD can support NFS and CIFS storagePorts.add(port.getId()); } else { _log.error("No StorageHADomain for port {}", port.getIpAddress()); } } // select storage port randomly from all candidate ports (to // minimize collisions). if (foundValidPort) { Collections.shuffle(storagePorts); rec.setStoragePorts(storagePorts); result.add(rec); } else { _log.info("No valid storage port found from the storage system : " + storageUri + ", All ports belongs to invalid vNas "); } } return result; } public List<FileRecommendation> placeFileShare(VirtualArray vArray, VirtualPool vPool, VirtualPoolCapabilityValuesWrapper capabilities, Project project) { return placeFileShare(vArray, vPool, capabilities, project, null); } @Override public List getRecommendationsForResources(VirtualArray vArray, Project project, VirtualPool vPool, VirtualPoolCapabilityValuesWrapper capabilities) { return placeFileShare(vArray, vPool, capabilities, project, null); } /** * create fileshare from the Recommendation object * * @param param * -file share create param * @param task * -task id * @param taskList * - task list * @param project * -project * @param varray * - Virtual Array * @param vpool * - Virtual Pool * @param recommendations * - recommendation structure * @param cosCapabilities * - Virtual pool wrapper * @param createInactive * - create device sync inactive * @return */ public List<FileShare> prepareFileSystems(FileSystemParam param, String task, TaskList taskList, Project project, VirtualArray varray, VirtualPool vpool, List<Recommendation> recommendations, VirtualPoolCapabilityValuesWrapper cosCapabilities, Boolean createInactive) { List<FileShare> preparedFileSystems = new ArrayList<>(); Iterator<Recommendation> recommendationsIter = recommendations.iterator(); while (recommendationsIter.hasNext()) { FileRecommendation recommendation = (FileRecommendation) recommendationsIter.next(); // If id is already set in recommendation, do not prepare the fileSystem (fileSystem already exists) if (recommendation.getId() != null) { continue; } if (recommendation.getFileType().toString().equals( FileRecommendation.FileType.FILE_SYSTEM_DATA.toString())) { // Grab the existing fileshare and task object from the incoming task list FileShare fileShare = getPrecreatedFile(taskList, param.getLabel()); // Set the recommendation _log.info(String.format("createFileSystem --- FileShare: %1$s, StoragePool: %2$s, StorageSystem: %3$s", fileShare.getId(), recommendation.getSourceStoragePool(), recommendation.getSourceStorageSystem())); setFileRecommendation(recommendation, fileShare, vpool, createInactive); preparedFileSystems.add(fileShare); } } return preparedFileSystems; } public void setFileRecommendation(FileRecommendation placement, FileShare fileShare, VirtualPool vpool, Boolean createInactive) { // Now check whether the label used in the storage system or not StorageSystem system = _dbClient.queryObject(StorageSystem.class, placement.getSourceStorageSystem()); List<FileShare> fileShareList = CustomQueryUtility.queryActiveResourcesByConstraint(_dbClient, FileShare.class, PrefixConstraint.Factory.getFullMatchConstraint(FileShare.class, "label", fileShare.getLabel())); if (fileShareList != null && !fileShareList.isEmpty()) { for (FileShare fs : fileShareList) { if (fs.getStorageDevice() != null) { if (fs.getStorageDevice().equals(system.getId())) { _log.info("Duplicate label found {} on Storage System {}", fileShare.getLabel(), system.getId()); throw APIException.badRequests.duplicateLabel(fileShare.getLabel()); } } } } // Set the storage pool StoragePool pool = null; if (null != placement.getSourceStoragePool()) { pool = _dbClient.queryObject(StoragePool.class, placement.getSourceStoragePool()); if (null != pool) { fileShare.setProtocol(new StringSet()); fileShare.getProtocol().addAll(VirtualPoolUtil.getMatchingProtocols(vpool.getProtocols(), pool.getProtocols())); } } fileShare.setStorageDevice(placement.getSourceStorageSystem()); fileShare.setPool(placement.getSourceStoragePool()); if (placement.getStoragePorts() != null && !placement.getStoragePorts().isEmpty()) { fileShare.setStoragePort(placement.getStoragePorts().get(0)); } if (placement.getvNAS() != null) { fileShare.setVirtualNAS(placement.getvNAS()); } _dbClient.updateObject(fileShare); // finally set file share id in recommendation placement.setId(fileShare.getId()); } /** * Convenience method to return a file from a task list with a pre-labeled fileshare. * * @param dbClient * dbclient * @param taskList * task list * @param label * base label * @return file object */ public FileShare getPrecreatedFile(TaskList taskList, String label) { for (TaskResourceRep task : taskList.getTaskList()) { FileShare fileShare = _dbClient.queryObject(FileShare.class, task.getResource().getId()); if (fileShare.getLabel().equalsIgnoreCase(label)) { return fileShare; } } return null; } @Override public List<Recommendation> getRecommendationsForVpool(VirtualArray vArray, Project project, VirtualPool vPool, VpoolUse vPoolUse, VirtualPoolCapabilityValuesWrapper capabilities, Map<VpoolUse, List<Recommendation>> currentRecommendations) { throw DeviceControllerException.exceptions.operationNotSupported(); } @Override public String getSchedulerName() { return SCHEDULER_NAME; } @Override public boolean handlesVpool(VirtualPool vPool, VpoolUse vPoolUse) { // not implemented return false; } }