blob: fa280ddb7308440cf9bab7c9b824fa4d7b3df231 [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.ozone.container.common.volume;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.ozone.container.common.utils.HddsVolumeUtil;
import org.apache.hadoop.test.GenericTestUtils;
import org.apache.hadoop.test.GenericTestUtils.LogCapturer;
import static org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_KEY;
import static org.apache.hadoop.ozone.container.common.volume.HddsVolume
.HDDS_VOLUME_DIR;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Tests {@link VolumeSet} operations.
*/
public class TestVolumeSet {
private OzoneConfiguration conf;
private VolumeSet volumeSet;
private final String baseDir = MiniDFSCluster.getBaseDirectory();
private final String volume1 = baseDir + "disk1";
private final String volume2 = baseDir + "disk2";
private final List<String> volumes = new ArrayList<>();
private static final String DUMMY_IP_ADDR = "0.0.0.0";
private void initializeVolumeSet() throws Exception {
volumeSet = new VolumeSet(UUID.randomUUID().toString(), conf);
}
@Rule
public Timeout testTimeout = new Timeout(300000);
@Before
public void setup() throws Exception {
conf = new OzoneConfiguration();
String dataDirKey = volume1 + "," + volume2;
volumes.add(volume1);
volumes.add(volume2);
conf.set(DFSConfigKeys.DFS_DATANODE_DATA_DIR_KEY, dataDirKey);
initializeVolumeSet();
}
@After
public void shutdown() throws IOException {
// Delete the hdds volume root dir
List<HddsVolume> hddsVolumes = new ArrayList<>();
hddsVolumes.addAll(volumeSet.getVolumesList());
hddsVolumes.addAll(volumeSet.getFailedVolumesList());
for (HddsVolume volume : hddsVolumes) {
FileUtils.deleteDirectory(volume.getHddsRootDir());
}
volumeSet.shutdown();
FileUtil.fullyDelete(new File(baseDir));
}
private boolean checkVolumeExistsInVolumeSet(String volume) {
for (HddsVolume hddsVolume : volumeSet.getVolumesList()) {
if (hddsVolume.getHddsRootDir().getPath().equals(
HddsVolumeUtil.getHddsRoot(volume))) {
return true;
}
}
return false;
}
@Test
public void testVolumeSetInitialization() throws Exception {
List<HddsVolume> volumesList = volumeSet.getVolumesList();
// VolumeSet initialization should add volume1 and volume2 to VolumeSet
assertEquals("VolumeSet intialization is incorrect",
volumesList.size(), volumes.size());
assertTrue("VolumeSet not initailized correctly",
checkVolumeExistsInVolumeSet(volume1));
assertTrue("VolumeSet not initailized correctly",
checkVolumeExistsInVolumeSet(volume2));
}
@Test
public void testAddVolume() {
assertEquals(2, volumeSet.getVolumesList().size());
// Add a volume to VolumeSet
String volume3 = baseDir + "disk3";
boolean success = volumeSet.addVolume(volume3);
assertTrue(success);
assertEquals(3, volumeSet.getVolumesList().size());
assertTrue("AddVolume did not add requested volume to VolumeSet",
checkVolumeExistsInVolumeSet(volume3));
}
@Test
public void testFailVolume() throws Exception {
//Fail a volume
volumeSet.failVolume(volume1);
// Failed volume should not show up in the volumeList
assertEquals(1, volumeSet.getVolumesList().size());
// Failed volume should be added to FailedVolumeList
assertEquals("Failed volume not present in FailedVolumeMap",
1, volumeSet.getFailedVolumesList().size());
assertEquals("Failed Volume list did not match",
HddsVolumeUtil.getHddsRoot(volume1),
volumeSet.getFailedVolumesList().get(0).getHddsRootDir().getPath());
assertTrue(volumeSet.getFailedVolumesList().get(0).isFailed());
// Failed volume should not exist in VolumeMap
assertFalse(volumeSet.getVolumeMap().containsKey(volume1));
}
@Test
public void testRemoveVolume() throws Exception {
assertEquals(2, volumeSet.getVolumesList().size());
// Remove a volume from VolumeSet
volumeSet.removeVolume(volume1);
assertEquals(1, volumeSet.getVolumesList().size());
// Attempting to remove a volume which does not exist in VolumeSet should
// log a warning.
LogCapturer logs = LogCapturer.captureLogs(
LogFactory.getLog(VolumeSet.class));
volumeSet.removeVolume(volume1);
assertEquals(1, volumeSet.getVolumesList().size());
String expectedLogMessage = "Volume : " +
HddsVolumeUtil.getHddsRoot(volume1) + " does not exist in VolumeSet";
assertTrue("Log output does not contain expected log message: "
+ expectedLogMessage, logs.getOutput().contains(expectedLogMessage));
}
@Test
public void testVolumeInInconsistentState() throws Exception {
assertEquals(2, volumeSet.getVolumesList().size());
// Add a volume to VolumeSet
String volume3 = baseDir + "disk3";
// Create the root volume dir and create a sub-directory within it.
File newVolume = new File(volume3, HDDS_VOLUME_DIR);
System.out.println("new volume root: " + newVolume);
newVolume.mkdirs();
assertTrue("Failed to create new volume root", newVolume.exists());
File dataDir = new File(newVolume, "chunks");
dataDir.mkdirs();
assertTrue(dataDir.exists());
// The new volume is in an inconsistent state as the root dir is
// non-empty but the version file does not exist. Add Volume should
// return false.
boolean success = volumeSet.addVolume(volume3);
assertFalse(success);
assertEquals(2, volumeSet.getVolumesList().size());
assertTrue("AddVolume should fail for an inconsistent volume",
!checkVolumeExistsInVolumeSet(volume3));
// Delete volume3
File volume = new File(volume3);
FileUtils.deleteDirectory(volume);
}
@Test
public void testShutdown() throws Exception {
List<HddsVolume> volumesList = volumeSet.getVolumesList();
volumeSet.shutdown();
// Verify that volume usage can be queried during shutdown.
for (HddsVolume volume : volumesList) {
Assert.assertNotNull(volume.getVolumeInfo().getUsageForTesting());
volume.getAvailable();
}
}
@Test
public void testFailVolumes() throws Exception{
VolumeSet volSet = null;
File readOnlyVolumePath = new File(baseDir);
//Set to readonly, so that this volume will be failed
readOnlyVolumePath.setReadOnly();
File volumePath = GenericTestUtils.getRandomizedTestDir();
OzoneConfiguration ozoneConfig = new OzoneConfiguration();
ozoneConfig.set(HDDS_DATANODE_DIR_KEY, readOnlyVolumePath.getAbsolutePath()
+ "," + volumePath.getAbsolutePath());
volSet = new VolumeSet(UUID.randomUUID().toString(), ozoneConfig);
assertEquals(1, volSet.getFailedVolumesList().size());
assertEquals(readOnlyVolumePath, volSet.getFailedVolumesList().get(0)
.getHddsRootDir());
//Set back to writable
try {
readOnlyVolumePath.setWritable(true);
volSet.shutdown();
} finally {
FileUtil.fullyDelete(volumePath);
}
}
}