/**
* Copyright 2016 LinkedIn Corp. All rights reserved.
*
* Licensed 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.
*/
package com.github.ambry.clustermap;
import com.github.ambry.config.ClusterMapConfig;
import com.github.ambry.config.VerifiableProperties;
import java.util.Properties;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
import static org.junit.Assert.*;
// TestDisk permits Disk to be constructed with a null DataNode.
class TestDisk extends Disk {
public TestDisk(JSONObject jsonObject, ClusterMapConfig clusterMapConfig) throws JSONException {
super(null, jsonObject, clusterMapConfig);
}
@Override
public void validateDataNode() {
// Null DataNodeId OK for test.
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TestDisk testDisk = (TestDisk) o;
if (!getMountPath().equals(testDisk.getMountPath())) {
return false;
}
if (getRawCapacityInBytes() != testDisk.getRawCapacityInBytes()) {
return false;
}
return getHardState() == testDisk.getHardState();
}
@Override
public HardwareState getState() {
return isDown() ? HardwareState.UNAVAILABLE : HardwareState.AVAILABLE;
}
}
/**
* Tests {@link Disk} class.
*/
public class DiskTest {
private Properties props;
public DiskTest() {
props = new Properties();
props.setProperty("clustermap.cluster.name", "test");
props.setProperty("clustermap.datacenter.name", "dc1");
props.setProperty("clustermap.host.name", "localhost");
}
@Test
public void basics() throws JSONException {
JSONObject jsonObject = TestUtils.getJsonDisk("/mnt1", HardwareState.AVAILABLE, 100 * 1024 * 1024 * 1024L);
ClusterMapConfig clusterMapConfig = new ClusterMapConfig(new VerifiableProperties(props));
Disk testDisk = new TestDisk(jsonObject, clusterMapConfig);
assertEquals(testDisk.getMountPath(), "/mnt1");
assertEquals(testDisk.getHardState(), HardwareState.AVAILABLE);
assertEquals(testDisk.getRawCapacityInBytes(), 100 * 1024 * 1024 * 1024L);
assertEquals(testDisk.toJSONObject().toString(), jsonObject.toString());
assertEquals(testDisk, new TestDisk(testDisk.toJSONObject(), clusterMapConfig));
}
public void failValidation(JSONObject jsonObject, ClusterMapConfig clusterMapConfig) throws JSONException {
try {
new TestDisk(jsonObject, clusterMapConfig);
fail("Construction of TestDisk should have failed validation.");
} catch (IllegalStateException e) {
// Expected.
}
}
@Test
public void validation() throws JSONException {
ClusterMapConfig clusterMapConfig = new ClusterMapConfig(new VerifiableProperties(props));
try {
// Null DataNode
new Disk(null, TestUtils.getJsonDisk("/mnt1", HardwareState.AVAILABLE, 100 * 1024 * 1024 * 1024L),
clusterMapConfig);
fail("Construction of Disk should have failed validation.");
} catch (IllegalStateException e) {
// Expected.
}
// Bad mount path (empty)
failValidation(TestUtils.getJsonDisk("", HardwareState.AVAILABLE, 100 * 1024 * 1024 * 1024L), clusterMapConfig);
// Bad mount path (relative path)
failValidation(TestUtils.getJsonDisk("mnt1", HardwareState.AVAILABLE, 100 * 1024 * 1024 * 1024L), clusterMapConfig);
// Bad capacity (too small)
failValidation(TestUtils.getJsonDisk("/mnt1", HardwareState.UNAVAILABLE, 0), clusterMapConfig);
// Bad capacity (too big)
failValidation(TestUtils.getJsonDisk("/mnt1", HardwareState.UNAVAILABLE,
1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024L), clusterMapConfig);
}
@Test
public void testDiskSoftState() throws JSONException, InterruptedException {
JSONObject jsonObject = TestUtils.getJsonDisk("/mnt1", HardwareState.AVAILABLE, 100 * 1024 * 1024 * 1024L);
Properties props = new Properties();
props.setProperty("clustermap.fixedtimeout.disk.retry.backoff.ms", Integer.toString(2000));
props.setProperty("clustermap.cluster.name", "test");
props.setProperty("clustermap.datacenter.name", "dc1");
props.setProperty("clustermap.host.name", "localhost");
ClusterMapConfig clusterMapConfig = new ClusterMapConfig(new VerifiableProperties(props));
int threshold = clusterMapConfig.clusterMapFixedTimeoutDiskErrorThreshold;
long retryBackoffMs = clusterMapConfig.clusterMapFixedTimeoutDiskRetryBackoffMs;
Disk testDisk = new TestDisk(jsonObject, clusterMapConfig);
for (int i = 0; i < threshold; i++) {
assertEquals(testDisk.getState(), HardwareState.AVAILABLE);
testDisk.onDiskError();
}
assertEquals(testDisk.getState(), HardwareState.UNAVAILABLE);
Thread.sleep(retryBackoffMs + 1);
assertEquals(testDisk.getState(), HardwareState.AVAILABLE);
//A single error should make it unavailable
testDisk.onDiskError();
assertEquals(testDisk.getState(), HardwareState.UNAVAILABLE);
testDisk.onDiskOk();
assertEquals(testDisk.getState(), HardwareState.AVAILABLE);
}
}