/***********************************************************************************************************************
*
* Copyright (C) 2010 by the Stratosphere project (http://stratosphere.eu)
*
* 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. See the License for the
* specific language governing permissions and limitations under the License.
*
**********************************************************************************************************************/
package eu.stratosphere.nephele.instance.cluster;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
import static org.junit.Assert.*;
import eu.stratosphere.nephele.configuration.ConfigConstants;
import eu.stratosphere.nephele.configuration.Configuration;
import eu.stratosphere.nephele.configuration.GlobalConfiguration;
import eu.stratosphere.nephele.instance.AllocatedResource;
import eu.stratosphere.nephele.instance.AllocationID;
import eu.stratosphere.nephele.instance.HardwareDescription;
import eu.stratosphere.nephele.instance.HardwareDescriptionFactory;
import eu.stratosphere.nephele.instance.InstanceConnectionInfo;
import eu.stratosphere.nephele.instance.InstanceException;
import eu.stratosphere.nephele.instance.InstanceRequestMap;
import eu.stratosphere.nephele.instance.InstanceType;
import eu.stratosphere.nephele.instance.InstanceTypeDescription;
import eu.stratosphere.nephele.instance.cluster.ClusterManager;
import eu.stratosphere.nephele.jobgraph.JobID;
/**
* Tests for {@link ClusterManager}.
*
* @author warneke
*/
public class ClusterManagerTest {
/**
* The system property key to retrieve the user directory.
*/
private static final String USER_DIR_KEY = "user.dir";
/**
* The directory containing the correct configuration file to be used during the tests.
*/
private static final String CORRECT_CONF_DIR = "/correct-conf";
/**
* The name of the small instance type.
*/
private static final String SMALL_INSTANCE_TYPE_NAME = "small";
/**
* The name of the medium instance type.
*/
private static final String MEDIUM_INSTANCE_TYPE_NAME = "medium";
/**
* The name of the large instance type.
*/
private static final String LARGE_INSTANCE_TYPE_NAME = "large";
/**
* The maximum time to wait for instance arrivals in milliseconds.
*/
private static final long MAX_WAIT_TIME = 1000L;
/**
* The clean up interval in milliseconds.
*/
private static final long CLEAN_UP_INTERVAL = 5000L;
/**
* The directory the configuration directory is expected in when test are executed using Eclipse.
*/
private static final String ECLIPSE_PATH_EXTENSION = "/src/test/resources";
/**
* This test covers the parsing of instance types from the configuration and the default instance type.
*/
@Test
public void testInstanceConfiguration() {
final String configDir = getConfigDir();
if (configDir == null) {
fail("Cannot find configuration directory for cluster manager test");
}
GlobalConfiguration.loadConfiguration(configDir);
final TestInstanceListener testInstanceListener = new TestInstanceListener();
final ClusterManager cm = new ClusterManager();
cm.setInstanceListener(testInstanceListener);
try {
final InstanceType defaultIT = cm.getDefaultInstanceType();
assertNotNull(defaultIT);
assertEquals(SMALL_INSTANCE_TYPE_NAME, defaultIT.getIdentifier());
assertEquals(2, defaultIT.getNumberOfComputeUnits());
assertEquals(2, defaultIT.getNumberOfCores());
assertEquals(2048, defaultIT.getMemorySize());
assertEquals(10, defaultIT.getDiskCapacity());
assertEquals(10, defaultIT.getPricePerHour());
// Check number of different instance types available to cluster manager
final Map<InstanceType, InstanceTypeDescription> instanceTypeDescriptions = cm
.getMapOfAvailableInstanceTypes();
assertEquals(3, instanceTypeDescriptions.size());
} finally {
if (cm != null) {
cm.shutdown();
}
}
}
/**
* This test covers the matching of instances to instance types It addresses the automatic matching through the
* hardware description as well as user-defined instance type matching.
*/
@Test
public void testInstanceMatching() {
final String configDir = getConfigDir();
if (configDir == null) {
fail("Cannot find configuration directory for cluster manager test");
}
GlobalConfiguration.loadConfiguration(configDir);
final TestInstanceListener testInstanceListener = new TestInstanceListener();
final ClusterManager cm = new ClusterManager();
cm.setInstanceListener(testInstanceListener);
Map<InstanceType, InstanceTypeDescription> instanceTypeDescriptions = null;
try {
final int ipcPort = ConfigConstants.DEFAULT_TASK_MANAGER_IPC_PORT;
final int dataPort = ConfigConstants.DEFAULT_TASK_MANAGER_DATA_PORT;
HardwareDescription hardwareDescription = HardwareDescriptionFactory.construct(2,
2L * 1024L * 1024L * 1024L,
2L * 1024L * 1024L * 1024L);
String ipAddress = "192.168.198.1";
InstanceConnectionInfo ici = new InstanceConnectionInfo(InetAddress.getByName(ipAddress), ipAddress, null,
ipcPort, dataPort);
// Although the hardware description indicates an instance of type "small", the cluster manager is supposed
// to take the user-defined instance type "high"
cm.reportHeartBeat(ici, hardwareDescription);
instanceTypeDescriptions = cm.getMapOfAvailableInstanceTypes();
assertEquals(3, instanceTypeDescriptions.size());
Iterator<Map.Entry<InstanceType, InstanceTypeDescription>> it = instanceTypeDescriptions.entrySet()
.iterator();
while (it.hasNext()) {
final Map.Entry<InstanceType, InstanceTypeDescription> entry = it.next();
if (LARGE_INSTANCE_TYPE_NAME.equals(entry.getKey().getIdentifier())) {
assertEquals(1, entry.getValue().getMaximumNumberOfAvailableInstances());
} else if (MEDIUM_INSTANCE_TYPE_NAME.equals(entry.getKey().getIdentifier())) {
assertEquals(2, entry.getValue().getMaximumNumberOfAvailableInstances());
} else if (SMALL_INSTANCE_TYPE_NAME.equals(entry.getKey().getIdentifier())) {
assertEquals(4, entry.getValue().getMaximumNumberOfAvailableInstances());
} else {
fail("Unexpected instance type: " + entry.getKey());
}
}
// Now we add a second cluster instance which is expected to identified as of type "small" as a result of
// its hardware description
hardwareDescription = HardwareDescriptionFactory.construct(3, 2L * 1024L * 1024L * 1024L,
1024L * 1024L * 1024L);
ipAddress = "192.168.198.3";
ici = new InstanceConnectionInfo(InetAddress.getByName(ipAddress), ipAddress, null, ipcPort, dataPort);
cm.reportHeartBeat(ici, hardwareDescription);
instanceTypeDescriptions = cm.getMapOfAvailableInstanceTypes();
assertEquals(3, instanceTypeDescriptions.size());
it = instanceTypeDescriptions.entrySet().iterator();
while (it.hasNext()) {
final Map.Entry<InstanceType, InstanceTypeDescription> entry = it.next();
if (LARGE_INSTANCE_TYPE_NAME.equals(entry.getKey().getIdentifier())) {
assertEquals(1, entry.getValue().getMaximumNumberOfAvailableInstances());
} else if (MEDIUM_INSTANCE_TYPE_NAME.equals(entry.getKey().getIdentifier())) {
assertEquals(2, entry.getValue().getMaximumNumberOfAvailableInstances());
} else if (SMALL_INSTANCE_TYPE_NAME.equals(entry.getKey().getIdentifier())) {
assertEquals(5, entry.getValue().getMaximumNumberOfAvailableInstances());
} else {
fail("Unexpected instance type: " + entry.getKey());
}
}
} catch (UnknownHostException e) {
fail(e.getMessage());
} finally {
if (cm != null) {
cm.shutdown();
}
}
}
/**
* This test checks the correctness of extracting instance types from the configuration, mapping IPs to instance
* types from the slave file, instance slicing and allocation/deallocation.
*/
@Test
public void testAllocationDeallocation() {
final String configDir = getConfigDir();
if (configDir == null) {
fail("Cannot find configuration directory for cluster manager test");
}
GlobalConfiguration.loadConfiguration(configDir);
final TestInstanceListener testInstanceListener = new TestInstanceListener();
final ClusterManager cm = new ClusterManager();
cm.setInstanceListener(testInstanceListener);
try {
final String ipAddress = "192.168.198.1";
final InstanceConnectionInfo instanceConnectionInfo = new InstanceConnectionInfo(
InetAddress.getByName(ipAddress), ipAddress, null, 1234, 1235);
final HardwareDescription hardwareDescription = HardwareDescriptionFactory.construct(8,
8L * 1024L * 1024L * 1024L, 8L * 1024L * 1024L * 1024L);
cm.reportHeartBeat(instanceConnectionInfo, hardwareDescription);
// now we should be able to request two instances of type small and one of type medium
final JobID jobID = new JobID();
final Configuration conf = new Configuration();
final InstanceRequestMap instanceRequestMap = new InstanceRequestMap();
instanceRequestMap.setNumberOfInstances(cm.getInstanceTypeByName(SMALL_INSTANCE_TYPE_NAME), 2);
instanceRequestMap.setNumberOfInstances(cm.getInstanceTypeByName(MEDIUM_INSTANCE_TYPE_NAME), 1);
try {
cm.requestInstance(jobID, conf, instanceRequestMap, null);
} catch (InstanceException ie) {
fail(ie.getMessage());
}
ClusterManagerTestUtils.waitForInstances(jobID, testInstanceListener, 3, MAX_WAIT_TIME);
final List<AllocatedResource> allocatedResources = testInstanceListener.getAllocatedResourcesForJob(jobID);
assertEquals(3, allocatedResources.size());
Iterator<AllocatedResource> it = allocatedResources.iterator();
final Set<AllocationID> allocationIDs = new HashSet<AllocationID>();
while (it.hasNext()) {
final AllocatedResource allocatedResource = it.next();
if (!LARGE_INSTANCE_TYPE_NAME.equals(allocatedResource.getInstance().getType().getIdentifier())) {
fail("Allocated unexpected instance of type "
+ allocatedResource.getInstance().getType().getIdentifier());
}
if (allocationIDs.contains(allocatedResource.getAllocationID())) {
fail("Discovered allocation ID " + allocatedResource.getAllocationID() + " at least twice");
} else {
allocationIDs.add(allocatedResource.getAllocationID());
}
}
// Try to allocate more resources which must result in an error
try {
InstanceRequestMap instancem = new InstanceRequestMap();
instancem.setNumberOfInstances(cm.getInstanceTypeByName(MEDIUM_INSTANCE_TYPE_NAME), 1);
cm.requestInstance(jobID, conf, instancem, null);
fail("ClusterManager allowed to request more instances than actually available");
} catch (InstanceException ie) {
// Exception is expected and correct behavior here
}
// Release all allocated resources
it = allocatedResources.iterator();
try {
while (it.hasNext()) {
final AllocatedResource allocatedResource = it.next();
cm.releaseAllocatedResource(jobID, conf, allocatedResource);
}
} catch (InstanceException ie) {
fail(ie.getMessage());
}
// Now further allocations should be possible
try {
InstanceRequestMap instancem = new InstanceRequestMap();
instancem.setNumberOfInstances(cm.getInstanceTypeByName(LARGE_INSTANCE_TYPE_NAME), 1);
cm.requestInstance(jobID, conf, instancem, null);
} catch (InstanceException ie) {
fail(ie.getMessage());
}
} catch (UnknownHostException e) {
fail(e.getMessage());
} finally {
if (cm != null) {
cm.shutdown();
}
}
}
/**
* This test checks the clean-up routines of the cluster manager.
*/
@Test
public void testCleanUp() {
GlobalConfiguration.loadConfiguration(System.getProperty(USER_DIR_KEY) + CORRECT_CONF_DIR);
final TestInstanceListener testInstanceListener = new TestInstanceListener();
final ClusterManager cm = new ClusterManager();
cm.setInstanceListener(testInstanceListener);
try {
final String ipAddress = "192.168.198.3";
final InstanceConnectionInfo instanceConnectionInfo = new InstanceConnectionInfo(
InetAddress.getByName(ipAddress), ipAddress, null, 1234, 1235);
final HardwareDescription hardwareDescription = HardwareDescriptionFactory.construct(8,
8L * 1024L * 1024L * 1024L, 8L * 1024L * 1024L * 1024L);
cm.reportHeartBeat(instanceConnectionInfo, hardwareDescription);
final JobID jobID = new JobID();
final Configuration conf = new Configuration();
try {
InstanceRequestMap instancem = new InstanceRequestMap();
instancem.setNumberOfInstances(cm.getInstanceTypeByName(LARGE_INSTANCE_TYPE_NAME), 1);
cm.requestInstance(jobID, conf, instancem, null);
} catch (InstanceException ie) {
fail(ie.getMessage());
}
ClusterManagerTestUtils.waitForInstances(jobID, testInstanceListener, 1, MAX_WAIT_TIME);
assertEquals(1, testInstanceListener.getNumberOfAllocatedResourcesForJob(jobID));
try {
Thread.sleep(CLEAN_UP_INTERVAL);
} catch (InterruptedException ie) {
fail(ie.getMessage());
}
ClusterManagerTestUtils.waitForInstances(jobID, testInstanceListener, 0, MAX_WAIT_TIME);
assertEquals(0, testInstanceListener.getNumberOfAllocatedResourcesForJob(jobID));
} catch (UnknownHostException e) {
fail(e.getMessage());
} finally {
if (cm != null) {
cm.shutdown();
}
}
}
/**
* Returns the directory containing the configuration files that shall be used for the test.
*
* @return the directory containing the configuration files or <code>null</code> if the configuration directory
* could not be located
*/
private static String getConfigDir() {
// This is the correct path for Maven-based tests
String configDir = System.getProperty(USER_DIR_KEY) + CORRECT_CONF_DIR;
if (new File(configDir).exists()) {
return configDir;
}
configDir = System.getProperty(USER_DIR_KEY) + ECLIPSE_PATH_EXTENSION + CORRECT_CONF_DIR;
if (new File(configDir).exists()) {
return configDir;
}
return null;
}
}