/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hdfs.server.namenode; import java.text.NumberFormat; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.EnumMap; import java.util.Formatter; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import org.apache.hadoop.fs.StorageType; import org.apache.hadoop.hdfs.protocol.BlockStoragePolicy; /** * Aggregate the storage type information for a set of blocks * */ public class StoragePolicySummary { Map<StorageTypeAllocation, Long> storageComboCounts = new HashMap<>(); final BlockStoragePolicy[] storagePolicies; int totalBlocks; StoragePolicySummary(BlockStoragePolicy[] storagePolicies) { this.storagePolicies = storagePolicies; } // Add a storage type combination void add(StorageType[] storageTypes, BlockStoragePolicy policy) { StorageTypeAllocation storageCombo = new StorageTypeAllocation(storageTypes, policy); Long count = storageComboCounts.get(storageCombo); if (count == null) { storageComboCounts.put(storageCombo, 1l); storageCombo.setActualStoragePolicy( getStoragePolicy(storageCombo.getStorageTypes())); } else { storageComboCounts.put(storageCombo, count.longValue()+1); } totalBlocks++; } // sort the storageType combinations based on the total blocks counts // in descending order static List<Entry<StorageTypeAllocation, Long>> sortByComparator( Map<StorageTypeAllocation, Long> unsortMap) { List<Entry<StorageTypeAllocation, Long>> storageAllocations = new LinkedList<>(unsortMap.entrySet()); // Sorting the list based on values Collections.sort(storageAllocations, new Comparator<Entry<StorageTypeAllocation, Long>>() { public int compare(Entry<StorageTypeAllocation, Long> o1, Entry<StorageTypeAllocation, Long> o2) { return o2.getValue().compareTo(o1.getValue()); } }); return storageAllocations; } public String toString() { StringBuilder compliantBlocksSB = new StringBuilder(); compliantBlocksSB.append("\nBlocks satisfying the specified storage policy:"); compliantBlocksSB.append("\nStorage Policy # of blocks % of blocks\n"); StringBuilder nonCompliantBlocksSB = new StringBuilder(); Formatter compliantFormatter = new Formatter(compliantBlocksSB); Formatter nonCompliantFormatter = new Formatter(nonCompliantBlocksSB); NumberFormat percentFormat = NumberFormat.getPercentInstance(); percentFormat.setMinimumFractionDigits(4); percentFormat.setMaximumFractionDigits(4); for (Map.Entry<StorageTypeAllocation, Long> storageComboCount: sortByComparator(storageComboCounts)) { double percent = (double) storageComboCount.getValue() / (double) totalBlocks; StorageTypeAllocation sta = storageComboCount.getKey(); if (sta.policyMatches()) { compliantFormatter.format("%-25s %10d %20s%n", sta.getStoragePolicyDescriptor(), storageComboCount.getValue(), percentFormat.format(percent)); } else { if (nonCompliantBlocksSB.length() == 0) { nonCompliantBlocksSB.append("\nBlocks NOT satisfying the specified storage policy:"); nonCompliantBlocksSB.append("\nStorage Policy "); nonCompliantBlocksSB.append( "Specified Storage Policy # of blocks % of blocks\n"); } nonCompliantFormatter.format("%-35s %-20s %10d %20s%n", sta.getStoragePolicyDescriptor(), sta.getSpecifiedStoragePolicy().getName(), storageComboCount.getValue(), percentFormat.format(percent)); } } if (nonCompliantBlocksSB.length() == 0) { nonCompliantBlocksSB.append("\nAll blocks satisfy specified storage policy.\n"); } compliantFormatter.close(); nonCompliantFormatter.close(); return compliantBlocksSB.toString() + nonCompliantBlocksSB; } /** * * @param storageTypes - sorted array of storageTypes * @return Storage Policy which matches the specific storage Combination */ private BlockStoragePolicy getStoragePolicy(StorageType[] storageTypes) { for (BlockStoragePolicy storagePolicy:storagePolicies) { StorageType[] policyStorageTypes = storagePolicy.getStorageTypes(); policyStorageTypes = Arrays.copyOf(policyStorageTypes, policyStorageTypes.length); Arrays.sort(policyStorageTypes); if (policyStorageTypes.length <= storageTypes.length) { int i = 0; for (; i < policyStorageTypes.length; i++) { if (policyStorageTypes[i] != storageTypes[i]) { break; } } if (i < policyStorageTypes.length) { continue; } int j=policyStorageTypes.length; for (; j < storageTypes.length; j++) { if (policyStorageTypes[i-1] != storageTypes[j]) { break; } } if (j==storageTypes.length) { return storagePolicy; } } } return null; } /** * Internal class which represents a unique Storage type combination * */ static class StorageTypeAllocation { private final BlockStoragePolicy specifiedStoragePolicy; private final StorageType[] storageTypes; private BlockStoragePolicy actualStoragePolicy; StorageTypeAllocation(StorageType[] storageTypes, BlockStoragePolicy specifiedStoragePolicy) { Arrays.sort(storageTypes); this.storageTypes = storageTypes; this.specifiedStoragePolicy = specifiedStoragePolicy; } StorageType[] getStorageTypes() { return storageTypes; } BlockStoragePolicy getSpecifiedStoragePolicy() { return specifiedStoragePolicy; } void setActualStoragePolicy(BlockStoragePolicy actualStoragePolicy) { this.actualStoragePolicy = actualStoragePolicy; } BlockStoragePolicy getActualStoragePolicy() { return actualStoragePolicy; } private static String getStorageAllocationAsString (Map<StorageType, Integer> storageType_countmap) { StringBuilder sb = new StringBuilder(); for (Map.Entry<StorageType, Integer> storageTypeCountEntry:storageType_countmap.entrySet()) { sb.append(storageTypeCountEntry.getKey().name()+ ":" + storageTypeCountEntry.getValue() + ","); } if (sb.length() > 1) { sb.deleteCharAt(sb.length()-1); } return sb.toString(); } private String getStorageAllocationAsString() { Map<StorageType, Integer> storageType_countmap = new EnumMap<>(StorageType.class); for (StorageType storageType: storageTypes) { Integer count = storageType_countmap.get(storageType); if (count == null) { storageType_countmap.put(storageType, 1); } else { storageType_countmap.put(storageType, count.intValue()+1); } } return (getStorageAllocationAsString(storageType_countmap)); } String getStoragePolicyDescriptor() { StringBuilder storagePolicyDescriptorSB = new StringBuilder(); if (actualStoragePolicy!=null) { storagePolicyDescriptorSB.append(getStorageAllocationAsString()) .append("(") .append(actualStoragePolicy.getName()) .append(")"); } else { storagePolicyDescriptorSB.append(getStorageAllocationAsString()); } return storagePolicyDescriptorSB.toString(); } boolean policyMatches() { return specifiedStoragePolicy.equals(actualStoragePolicy); } @Override public String toString() { return specifiedStoragePolicy.getName() + "|" + getStoragePolicyDescriptor(); } @Override public int hashCode() { return Objects.hash(specifiedStoragePolicy,Arrays.hashCode(storageTypes)); } @Override public boolean equals(Object another) { return (another instanceof StorageTypeAllocation && Objects.equals(specifiedStoragePolicy, ((StorageTypeAllocation)another).specifiedStoragePolicy) && Arrays.equals(storageTypes, ((StorageTypeAllocation)another).storageTypes)); } } }