blob: e12c6433c976ebc50f9a6cba76c8d63974068974 [file] [log] [blame]
/**
* 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.hdds.scm.node;
import org.apache.hadoop.fs.StorageType;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos;
import org.apache.hadoop.hdds.protocol.proto.
StorageContainerDatanodeProtocolProtos.NodeReportProto;
import org.apache.hadoop.hdds.protocol.proto.
StorageContainerDatanodeProtocolProtos.StorageReportProto;
import org.apache.hadoop.hdds.scm.TestUtils;
import org.apache.hadoop.hdds.scm.exceptions.SCMException;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.container.common.impl.StorageLocationReport;
import org.apache.hadoop.test.GenericTestUtils;
import org.junit.*;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.Set;
import java.util.ArrayList;
import java.util.HashSet;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
/**
* Test Node Storage Map.
*/
public class TestSCMNodeStorageStatMap {
private final static int DATANODE_COUNT = 100;
private final long capacity = 10L * OzoneConsts.GB;
private final long used = 2L * OzoneConsts.GB;
private final long remaining = capacity - used;
private static OzoneConfiguration conf = new OzoneConfiguration();
private final Map<UUID, Set<StorageLocationReport>> testData =
new ConcurrentHashMap<>();
@Rule
public ExpectedException thrown = ExpectedException.none();
private void generateData() {
for (int dnIndex = 1; dnIndex <= DATANODE_COUNT; dnIndex++) {
UUID dnId = UUID.randomUUID();
Set<StorageLocationReport> reportSet = new HashSet<>();
String path = GenericTestUtils.getTempPath(
TestSCMNodeStorageStatMap.class.getSimpleName() + "-" +
Integer.toString(dnIndex));
StorageLocationReport.Builder builder =
StorageLocationReport.newBuilder();
builder.setStorageType(StorageType.DISK).setId(dnId.toString())
.setStorageLocation(path).setScmUsed(used).setRemaining(remaining)
.setCapacity(capacity).setFailed(false);
reportSet.add(builder.build());
testData.put(UUID.randomUUID(), reportSet);
}
}
private UUID getFirstKey() {
return testData.keySet().iterator().next();
}
@Before
public void setUp() throws Exception {
generateData();
}
@After
public void tearDown() throws Exception {
}
@Test
public void testIsKnownDatanode() throws SCMException {
SCMNodeStorageStatMap map = new SCMNodeStorageStatMap(conf);
UUID knownNode = getFirstKey();
UUID unknownNode = UUID.randomUUID();
Set<StorageLocationReport> report = testData.get(knownNode);
map.insertNewDatanode(knownNode, report);
Assert.assertTrue("Not able to detect a known node",
map.isKnownDatanode(knownNode));
Assert.assertFalse("Unknown node detected",
map.isKnownDatanode(unknownNode));
}
@Test
public void testInsertNewDatanode() throws SCMException {
SCMNodeStorageStatMap map = new SCMNodeStorageStatMap(conf);
UUID knownNode = getFirstKey();
Set<StorageLocationReport> report = testData.get(knownNode);
map.insertNewDatanode(knownNode, report);
Assert.assertEquals(map.getStorageVolumes(knownNode),
testData.get(knownNode));
thrown.expect(SCMException.class);
thrown.expectMessage("already exists");
map.insertNewDatanode(knownNode, report);
}
@Test
public void testUpdateUnknownDatanode() throws SCMException {
SCMNodeStorageStatMap map = new SCMNodeStorageStatMap(conf);
UUID unknownNode = UUID.randomUUID();
String path = GenericTestUtils.getTempPath(
TestSCMNodeStorageStatMap.class.getSimpleName() + "-" + unknownNode
.toString());
Set<StorageLocationReport> reportSet = new HashSet<>();
StorageLocationReport.Builder builder = StorageLocationReport.newBuilder();
builder.setStorageType(StorageType.DISK).setId(unknownNode.toString())
.setStorageLocation(path).setScmUsed(used).setRemaining(remaining)
.setCapacity(capacity).setFailed(false);
reportSet.add(builder.build());
thrown.expect(SCMException.class);
thrown.expectMessage("No such datanode");
map.updateDatanodeMap(unknownNode, reportSet);
}
@Test
public void testProcessNodeReportCheckOneNode() throws IOException {
UUID key = getFirstKey();
List<StorageReportProto> reportList = new ArrayList<>();
Set<StorageLocationReport> reportSet = testData.get(key);
SCMNodeStorageStatMap map = new SCMNodeStorageStatMap(conf);
map.insertNewDatanode(key, reportSet);
Assert.assertTrue(map.isKnownDatanode(key));
UUID storageId = UUID.randomUUID();
String path =
GenericTestUtils.getRandomizedTempPath().concat("/" + storageId);
StorageLocationReport report = reportSet.iterator().next();
long reportCapacity = report.getCapacity();
long reportScmUsed = report.getScmUsed();
long reportRemaining = report.getRemaining();
StorageReportProto storageReport = TestUtils.createStorageReport(storageId,
path, reportCapacity, reportScmUsed, reportRemaining, null);
StorageReportResult result =
map.processNodeReport(key, TestUtils.createNodeReport(storageReport));
Assert.assertEquals(SCMNodeStorageStatMap.ReportStatus.ALL_IS_WELL,
result.getStatus());
StorageContainerDatanodeProtocolProtos.NodeReportProto.Builder nrb =
NodeReportProto.newBuilder();
StorageReportProto srb = reportSet.iterator().next().getProtoBufMessage();
reportList.add(srb);
result = map.processNodeReport(key, TestUtils.createNodeReport(reportList));
Assert.assertEquals(SCMNodeStorageStatMap.ReportStatus.ALL_IS_WELL,
result.getStatus());
reportList.add(TestUtils
.createStorageReport(UUID.randomUUID(), path, reportCapacity,
reportCapacity, 0, null));
result = map.processNodeReport(key, TestUtils.createNodeReport(reportList));
Assert.assertEquals(SCMNodeStorageStatMap.ReportStatus.STORAGE_OUT_OF_SPACE,
result.getStatus());
// Mark a disk failed
StorageReportProto srb2 = StorageReportProto.newBuilder()
.setStorageUuid(UUID.randomUUID().toString())
.setStorageLocation(srb.getStorageLocation()).setScmUsed(reportCapacity)
.setCapacity(reportCapacity).setRemaining(0).setFailed(true).build();
reportList.add(srb2);
nrb.addAllStorageReport(reportList);
result = map.processNodeReport(key, nrb.addStorageReport(srb).build());
Assert.assertEquals(SCMNodeStorageStatMap.ReportStatus
.FAILED_AND_OUT_OF_SPACE_STORAGE, result.getStatus());
}
@Test
public void testProcessMultipleNodeReports() throws SCMException {
SCMNodeStorageStatMap map = new SCMNodeStorageStatMap(conf);
int counter = 1;
// Insert all testData into the SCMNodeStorageStatMap Map.
for (Map.Entry<UUID, Set<StorageLocationReport>> keyEntry : testData
.entrySet()) {
map.insertNewDatanode(keyEntry.getKey(), keyEntry.getValue());
}
Assert.assertEquals(DATANODE_COUNT * capacity, map.getTotalCapacity());
Assert.assertEquals(DATANODE_COUNT * remaining, map.getTotalFreeSpace());
Assert.assertEquals(DATANODE_COUNT * used, map.getTotalSpaceUsed());
// upadate 1/4th of the datanode to be full
for (Map.Entry<UUID, Set<StorageLocationReport>> keyEntry : testData
.entrySet()) {
Set<StorageLocationReport> reportSet = new HashSet<>();
String path = GenericTestUtils.getTempPath(
TestSCMNodeStorageStatMap.class.getSimpleName() + "-" + keyEntry
.getKey().toString());
StorageLocationReport.Builder builder =
StorageLocationReport.newBuilder();
builder.setStorageType(StorageType.DISK)
.setId(keyEntry.getKey().toString()).setStorageLocation(path)
.setScmUsed(capacity).setRemaining(0).setCapacity(capacity)
.setFailed(false);
reportSet.add(builder.build());
map.updateDatanodeMap(keyEntry.getKey(), reportSet);
counter++;
if (counter > DATANODE_COUNT / 4) {
break;
}
}
Assert.assertEquals(DATANODE_COUNT / 4,
map.getDatanodeList(SCMNodeStorageStatMap.UtilizationThreshold.CRITICAL)
.size());
Assert.assertEquals(0,
map.getDatanodeList(SCMNodeStorageStatMap.UtilizationThreshold.WARN)
.size());
Assert.assertEquals(0.75 * DATANODE_COUNT,
map.getDatanodeList(SCMNodeStorageStatMap.UtilizationThreshold.NORMAL)
.size(), 0);
Assert.assertEquals(DATANODE_COUNT * capacity, map.getTotalCapacity(), 0);
Assert.assertEquals(0.75 * DATANODE_COUNT * remaining,
map.getTotalFreeSpace(), 0);
Assert.assertEquals(
0.75 * DATANODE_COUNT * used + (0.25 * DATANODE_COUNT * capacity),
map.getTotalSpaceUsed(), 0);
counter = 1;
// Remove 1/4 of the DataNodes from the Map
for (Map.Entry<UUID, Set<StorageLocationReport>> keyEntry : testData
.entrySet()) {
map.removeDatanode(keyEntry.getKey());
counter++;
if (counter > DATANODE_COUNT / 4) {
break;
}
}
Assert.assertEquals(0,
map.getDatanodeList(SCMNodeStorageStatMap.UtilizationThreshold.CRITICAL)
.size());
Assert.assertEquals(0,
map.getDatanodeList(SCMNodeStorageStatMap.UtilizationThreshold.WARN)
.size());
Assert.assertEquals(0.75 * DATANODE_COUNT,
map.getDatanodeList(SCMNodeStorageStatMap.UtilizationThreshold.NORMAL)
.size(), 0);
Assert
.assertEquals(0.75 * DATANODE_COUNT * capacity, map.getTotalCapacity(),
0);
Assert.assertEquals(0.75 * DATANODE_COUNT * remaining,
map.getTotalFreeSpace(), 0);
Assert
.assertEquals(0.75 * DATANODE_COUNT * used, map.getTotalSpaceUsed(), 0);
}
}