/**
* VMware Continuent Tungsten Replicator
* Copyright (C) 2015 VMware, Inc. 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Initial developer(s): Robert Hodges
* Contributor(s):
*/
package com.continuent.tungsten.common.network;
import java.net.InetAddress;
import java.util.List;
import junit.framework.TestCase;
import org.apache.log4j.Logger;
/**
* Implements a unit test for time intervals, which are a Tungsten property
* type.
*
* @author <a href="mailto:robert.hodges@continuent.com">Robert Hodges</a>
* @version 1.0
*/
public class HostAddressServiceTest extends TestCase
{
private static Logger logger = Logger.getLogger(HostAddressServiceTest.class);
// An IP address we hope is unknown. This is the IP address test range
// 198.51.100.0/24 (aka TEST-NET-2 in RFC5735, used for sample code and
// documentation). It should not be allocated.
private static String UNKNOWN_IP = "198.51.100.100";
/**
* Verify ability to instantiate a host address service and find all ping
* methods.
*/
public void testAddressServiceInstantiation() throws Exception
{
HostAddressService has = new HostAddressService(true);
List<String> methodNames = has.getEnabledMethodNames();
assertTrue("Have at least one method", methodNames.size() > 0);
for (String methodName : methodNames)
{
String pingMethod = has.getMethodName(methodName);
assertNotNull("Checking method availability: " + methodName,
pingMethod);
}
}
/**
* Verify ability to ping local host using the default method.
*/
public void testDefaultToLocalHost() throws Exception
{
HostAddressService has = new HostAddressService(true);
has.setTimeout(3000);
HostAddress address = HostAddressService.getByName(InetAddress
.getLocalHost().getHostName());
PingResponse response = has.isReachableByMethod(
HostAddressService.DEFAULT, address);
assertTrue("Can ping localhost", response.isReachable());
}
/**
* Verify ability to ping local host using the OS ping method.
*/
public void testOsPingToLocalHost() throws Exception
{
HostAddressService has = new HostAddressService(true);
has.setTimeout(3000);
HostAddress address = HostAddressService.getByName(InetAddress
.getLocalHost().getHostName());
PingResponse response = has.isReachableByMethod(
HostAddressService.PING, address);
assertTrue("Can ping localhost", response.isReachable());
}
/**
* Verify that we cannot ping a non-existent Internet address.
*/
public void testDefaultToUnknown() throws Exception
{
HostAddressService has = new HostAddressService(true);
has.setTimeout(1500);
HostAddress address = HostAddressService.getByName(UNKNOWN_IP);
PingResponse response = has.isReachableByMethod(
HostAddressService.DEFAULT, address);
assertFalse("Cannot ping unknown address: " + address.toString(),
response.isReachable());
}
/**
* Verify that we cannot ping a non-existent Internet address.
*/
public void testOsPingToUnknown() throws Exception
{
HostAddressService has = new HostAddressService(true);
has.setTimeout(2000);
HostAddress address = HostAddressService.getByName(UNKNOWN_IP);
PingResponse response = has.isReachableByMethod(
HostAddressService.PING, address);
assertFalse("Cannot ping unknown address: " + address.toString(),
response.isReachable());
}
/**
* Verify that pinging a known host results in a notification for each
* method used, which should be a single method in each case.
*/
public void testSuccessNotifications() throws Exception
{
HostAddressService has = new HostAddressService(true);
has.setTimeout(3000);
HostAddress address = HostAddressService.getByName(InetAddress
.getLocalHost().getHostName());
// Ping with a single method.
PingResponse response = has.isReachableByMethod(
HostAddressService.PING, address);
assertEquals("Direct method invocation", 1, response.getNotifications()
.size());
for (PingNotification notification : response.getNotifications())
{
logger.info("Notification: " + notification.toString());
}
// Ping with all methods. Only one should be needed.
response = has.isReachable(address);
assertEquals("General invocation", 1, response.getNotifications()
.size());
for (PingNotification notification : response.getNotifications())
{
logger.info("Notification: " + notification.toString());
}
}
/**
* Verify that pinging an unknown host results in a notification for each
* available method, as all will be used.
*/
public void testFailureNotifications() throws Exception
{
HostAddressService has = new HostAddressService(true);
has.setTimeout(1000);
HostAddress address = HostAddressService.getByName(UNKNOWN_IP);
// Ping with all methods. All should be used.
PingResponse response = has.isReachable(address);
assertEquals("General invocation", has.getEnabledMethodNames().size(),
response.getNotifications().size());
for (PingNotification notification : response.getNotifications())
{
logger.info("Notification: " + notification.toString());
}
}
/**
* Verify that when a single ping method fails we get false as the response
* with an exception in the notification. This also tests enabling of
* non-default methods.
*/
public void testMethodException() throws Exception
{
HostAddressService has = new HostAddressService(false);
has.setTimeout(1000);
HostAddress address = HostAddressService.getByName(InetAddress
.getLocalHost().getHostName());
has.addMethod("sample", SamplePingMethod.class.getName(), true);
has.addMethod(HostAddressService.DEFAULT,
InetAddressPing.class.getName(), true);
// Show that a successful ping does not result in an exception.
SamplePingMethod.exception = false;
PingResponse response = has.isReachableByMethod("sample", address);
assertTrue("Success invocation", response.isReachable());
assertNull("Expect null exception on success", response
.getNotifications().get(0).getException());
// Show that a ping that blows up does result in an exception as well
// as a failure overall.
SamplePingMethod.exception = true;
PingResponse response2 = has.isReachableByMethod("sample", address);
assertFalse("Exception on invocation", response2.isReachable());
assertNotNull("Expect exception on failures", response2
.getNotifications().get(0).getException());
// For good measure show that a method can blow up but a later method
// can prove availability.
SamplePingMethod.exception = true;
PingResponse response3 = has.isReachable(address);
assertTrue("Exception on invocation", response3.isReachable());
assertNotNull("Expect exception on first method notification",
response3.getNotifications().get(0).getException());
}
/**
* Verify ability to perform ping requests in short order. Use all methods.
*/
public void testPingPerformance() throws Exception
{
// Set up service.
HostAddressService has = new HostAddressService(true);
has.setTimeout(3000);
HostAddress address = HostAddressService.getByName(InetAddress
.getLocalHost().getHostName());
String[] methods = {HostAddressService.DEFAULT, HostAddressService.PING};
int count = 100;
// Do successive pings using each method.
for (int i = 0; i < methods.length; i++)
{
String method = methods[i];
long startMillis = System.currentTimeMillis();
for (int j = 0; j < count; j++)
{
PingResponse response = has
.isReachableByMethod(method, address);
assertTrue("Can ping localhost: method=" + method
+ " iteration=" + j, response.isReachable());
}
double duration = (System.currentTimeMillis() - startMillis) / 1000.0;
logger.info("Completed ping using method: name=" + method
+ " pings=" + count + " duration=" + duration);
}
}
/**
* Verify ability of multiple threads to issue localhost ping calls without
* serializing.
*/
public void testConcurrentLocalHostPing() throws Exception
{
// Set up service, an address to ping, and a number of iterations to
// try.
HostAddressService has = new HostAddressService(true);
has.setTimeout(2000);
HostAddress address = HostAddressService.getByName(InetAddress
.getLocalHost().getHostName());
// Test with one task and 200 pings.
doConcurrentPing(has, address, 1, 200, true);
// Test with 10 tasks with 20 pings each. This fails on Mac OS X, so
// skip it on that platform.
String os = System.getProperty("os.name");
if ("Mac OS X".equals(os))
logger.info("Skipping concurrent ping on Mac OS X platform...");
else
this.doConcurrentPing(has, address, 10, 20, true);
}
/**
* Verify ability of multiple threads to issue localhost ping calls without
* serializing even when the ping call is to an unknown host.
*/
public void testConcurrentUnknownHostPing() throws Exception
{
// Set up service, an address to ping, and a number of iterations to
// try.
HostAddressService has = new HostAddressService(true);
has.setTimeout(2000);
HostAddress address = HostAddressService.getByName(UNKNOWN_IP);
// Test 10 tasks with 2 pings each. This should take about 4
// seconds per thread with full concurrency.
this.doConcurrentPing(has, address, 10, 2, false);
}
/**
* Positive and negative test of method to determine if a pair of host
* addresses are equal.
*
* @throws Exception
*/
public void testHostAddressesAreEqual() throws Exception
{
String myHostName = InetAddress.getLocalHost().getHostName();
String google = "www.google.com";
assertTrue("Same host is equal",
HostAddressService.addressesAreEqual(myHostName, myHostName));
assertFalse("My host compared to google.com is not equal",
HostAddressService.addressesAreEqual(google, myHostName));
}
/**
* Tests for hosts on the same network and not on the same network.
*
* @throws Exception
*/
public void testHostAddressesAreInTheSameNetwork() throws Exception
{
String myHostName = InetAddress.getLocalHost().getHostName();
String google = "www.google.com";
short prefix = HostAddressService.getLocalNetworkPrefix(myHostName);
assertTrue("Same host is on the same network",
HostAddressService.addressesAreInSameSubnet(myHostName,
myHostName, prefix));
assertFalse(
"My host compared to google.com is not on the same network",
HostAddressService.addressesAreInSameSubnet(google, myHostName,
prefix));
try
{
HostAddressService.addressesAreInSameSubnet(myHostName, myHostName,
(short) 0);
assertFalse("Should not get here...", true);
}
catch (Exception expected)
{
assertTrue("0 for prefix causes an exception", expected
.getLocalizedMessage().contains("Invalid prefix"));
}
try
{
assertFalse("Pass -1 for the prefix",
HostAddressService.addressesAreInSameSubnet(myHostName,
myHostName, (short) 0));
assertFalse("Should not get here...", true);
}
catch (Exception expected)
{
assertTrue("1 for prefix causes an exception", expected
.getLocalizedMessage().contains("Invalid prefix"));
}
}
// Private utility method to run concurrent ping test.
private void doConcurrentPing(HostAddressService has, HostAddress address,
int taskCount, int pingCount, boolean succeed)
throws InterruptedException
{
// Start a group of threads
long startMillis = System.currentTimeMillis();
Thread[] threads = new Thread[taskCount];
HostPingTask[] tasks = new HostPingTask[taskCount];
for (int i = 0; i < threads.length; i++)
{
tasks[i] = new HostPingTask(has, address, pingCount);
threads[i] = new Thread(tasks[i], "task-" + i);
threads[i].start();
}
// Compute expected failures.
int failures;
if (succeed)
failures = 0;
else
failures = pingCount;
// Wait for threads to finish.
int iterations = 0;
for (int i = 0; i < threads.length; i++)
{
threads[i].join(60000);
assertEquals(
"Checking task completed iterations: "
+ threads[i].getName(), pingCount,
tasks[i].iterations);
assertEquals("Checking task failures: " + threads[i].getName(),
failures, tasks[i].failures);
assertTrue("Checking done-ness of task: " + threads[i].getName(),
tasks[i].done);
iterations += tasks[i].iterations;
}
// Show duration and number of pings.
double duration = (System.currentTimeMillis() - startMillis) / 1000.0;
logger.info("Completed parallel ping: taskCount=" + tasks.length
+ " pingCount=" + pingCount + " actual pings=" + iterations
+ " succeed=" + succeed + " duration=" + duration);
}
}
// Host ping task for concurrent testing.
class HostPingTask implements Runnable
{
private static Logger logger = Logger.getLogger(HostPingTask.class);
final HostAddressService has;
final HostAddress address;
final int count;
volatile int iterations;
volatile boolean done;
volatile int failures = 0;
HostPingTask(HostAddressService has, HostAddress address, int count)
{
this.has = has;
this.address = address;
this.count = count;
}
// Execute ping operations count times, then exit.
public synchronized void run()
{
iterations = 0;
for (int i = 0; i < count; i++)
{
try
{
// PingResponse response = has.isReachable(address);
PingResponse response = has.isReachableByMethod(
HostAddressService.DEFAULT, address);
iterations++;
if (!response.isReachable())
failures++;
}
catch (Throwable t)
{
logger.fatal(
"Host ping task failed: address=" + address.toString(),
t);
break;
}
}
done = true;
}
}