alshabib

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 60 changed files with 1738 additions and 191 deletions
......@@ -3,8 +3,6 @@ package org.onlab.onos.config;
import java.util.List;
import org.codehaus.jackson.annotate.JsonProperty;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
/**
* Represents a set of addresses bound to a port.
......@@ -12,8 +10,8 @@ import org.onlab.packet.MacAddress;
public class AddressEntry {
private String dpid;
private short portNumber;
private List<IpPrefix> ipAddresses;
private MacAddress macAddress;
private List<String> ipAddresses;
private String macAddress;
public String getDpid() {
return dpid;
......@@ -33,21 +31,21 @@ public class AddressEntry {
this.portNumber = portNumber;
}
public List<IpPrefix> getIpAddresses() {
public List<String> getIpAddresses() {
return ipAddresses;
}
@JsonProperty("ips")
public void setIpAddresses(List<IpPrefix> ipAddresses) {
this.ipAddresses = ipAddresses;
public void setIpAddresses(List<String> strIps) {
this.ipAddresses = strIps;
}
public MacAddress getMacAddress() {
public String getMacAddress() {
return macAddress;
}
@JsonProperty("mac")
public void setMacAddress(MacAddress macAddress) {
public void setMacAddress(String macAddress) {
this.macAddress = macAddress;
}
}
......
......@@ -5,6 +5,8 @@ import static org.slf4j.LoggerFactory.getLogger;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
......@@ -17,10 +19,10 @@ import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.host.HostAdminService;
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.slf4j.Logger;
import com.google.common.collect.Sets;
/**
* Simple configuration module to read in supplementary network configuration
* from a file.
......@@ -51,9 +53,29 @@ public class NetworkConfigReader {
DeviceId.deviceId(dpidToUri(entry.getDpid())),
PortNumber.portNumber(entry.getPortNumber()));
PortAddresses addresses = new PortAddresses(cp,
Sets.newHashSet(entry.getIpAddresses()),
Set<IpPrefix> ipAddresses = new HashSet<IpPrefix>();
for (String strIp : entry.getIpAddresses()) {
try {
IpPrefix address = IpPrefix.valueOf(strIp);
ipAddresses.add(address);
} catch (IllegalArgumentException e) {
log.warn("Bad format for IP address in config: {}", strIp);
}
}
MacAddress macAddress = null;
if (entry.getMacAddress() != null) {
try {
macAddress = MacAddress.valueOf(entry.getMacAddress());
} catch (IllegalArgumentException e) {
log.warn("Bad format for MAC address in config: {}",
entry.getMacAddress());
}
}
PortAddresses addresses = new PortAddresses(cp,
ipAddresses, macAddress);
hostAdminService.bindAddressesToPort(addresses);
}
......
package org.onlab.onos.net.intent;
//TODO is this the right package?
import static com.google.common.base.Preconditions.checkNotNull;
......
package org.onlab.onos.net.intent;
//TODO is this the right package?
import java.util.Objects;
......
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* An interface of the class which is assigned to BatchOperation.
......
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* A generalized interface for ID generation
*
* {@link #getNewId()} generates a globally unique ID instance on
* each invocation.
*
* @param <T> the type of ID
*/
// TODO: do we need to define a base marker interface for ID,
// then changed the type parameter to <T extends BaseId> something
// like that?
public interface IdGenerator<T> {
/**
* Returns a globally unique ID instance.
*
* @return globally unique ID instance
*/
T getNewId();
}
package org.onlab.onos.net.intent;
import java.util.Set;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
/**
* Base facilities to test various connectivity tests.
*/
public abstract class ConnectivityIntentTest extends IntentTest {
public static final IntentId IID = new IntentId(123);
public static final TrafficSelector MATCH = (new DefaultTrafficSelector.Builder()).build();
public static final TrafficTreatment NOP = (new DefaultTrafficTreatment.Builder()).build();
public static final ConnectPoint P1 = new ConnectPoint(DeviceId.deviceId("111"), PortNumber.portNumber(0x1));
public static final ConnectPoint P2 = new ConnectPoint(DeviceId.deviceId("222"), PortNumber.portNumber(0x2));
public static final ConnectPoint P3 = new ConnectPoint(DeviceId.deviceId("333"), PortNumber.portNumber(0x3));
public static final Set<ConnectPoint> PS1 = itemSet(new ConnectPoint[]{P1, P3});
public static final Set<ConnectPoint> PS2 = itemSet(new ConnectPoint[]{P2, P3});
}
package org.onlab.onos.net.intent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Fake implementation of the intent service to assist in developing tests
* of the interface contract.
*/
public class FakeIntentManager implements TestableIntentService {
private final Map<IntentId, Intent> intents = new HashMap<>();
private final Map<IntentId, IntentState> intentStates = new HashMap<>();
private final Map<IntentId, List<InstallableIntent>> installables = new HashMap<>();
private final Set<IntentEventListener> listeners = new HashSet<>();
private final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers = new HashMap<>();
private final Map<Class<? extends InstallableIntent>,
IntentInstaller<? extends InstallableIntent>> installers = new HashMap<>();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final List<IntentException> exceptions = new ArrayList<>();
@Override
public List<IntentException> getExceptions() {
return exceptions;
}
// Provides an out-of-thread simulation of intent submit life-cycle
private void executeSubmit(final Intent intent) {
registerSubclassCompilerIfNeeded(intent);
executor.execute(new Runnable() {
@Override
public void run() {
try {
List<InstallableIntent> installable = compileIntent(intent);
installIntents(intent, installable);
} catch (IntentException e) {
exceptions.add(e);
}
}
});
}
// Provides an out-of-thread simulation of intent withdraw life-cycle
private void executeWithdraw(final Intent intent) {
executor.execute(new Runnable() {
@Override
public void run() {
try {
List<InstallableIntent> installable = getInstallable(intent.getId());
uninstallIntents(intent, installable);
} catch (IntentException e) {
exceptions.add(e);
}
}
});
}
private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
@SuppressWarnings("unchecked")
IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
if (compiler == null) {
throw new IntentException("no compiler for class " + intent.getClass());
}
return compiler;
}
private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
@SuppressWarnings("unchecked")
IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
if (installer == null) {
throw new IntentException("no installer for class " + intent.getClass());
}
return installer;
}
private <T extends Intent> List<InstallableIntent> compileIntent(T intent) {
try {
// For the fake, we compile using a single level pass
List<InstallableIntent> installable = new ArrayList<>();
for (Intent compiled : getCompiler(intent).compile(intent)) {
installable.add((InstallableIntent) compiled);
}
setState(intent, IntentState.COMPILED);
return installable;
} catch (IntentException e) {
setState(intent, IntentState.FAILED);
throw e;
}
}
private void installIntents(Intent intent, List<InstallableIntent> installable) {
try {
for (InstallableIntent ii : installable) {
registerSubclassInstallerIfNeeded(ii);
getInstaller(ii).install(ii);
}
setState(intent, IntentState.INSTALLED);
putInstallable(intent.getId(), installable);
} catch (IntentException e) {
setState(intent, IntentState.FAILED);
throw e;
}
}
private void uninstallIntents(Intent intent, List<InstallableIntent> installable) {
try {
for (InstallableIntent ii : installable) {
getInstaller(ii).uninstall(ii);
}
setState(intent, IntentState.WITHDRAWN);
removeInstallable(intent.getId());
} catch (IntentException e) {
setState(intent, IntentState.FAILED);
throw e;
}
}
// Sets the internal state for the given intent and dispatches an event
private void setState(Intent intent, IntentState state) {
IntentState previous = intentStates.get(intent.getId());
intentStates.put(intent.getId(), state);
dispatch(new IntentEvent(intent, state, previous, System.currentTimeMillis()));
}
private void putInstallable(IntentId id, List<InstallableIntent> installable) {
installables.put(id, installable);
}
private void removeInstallable(IntentId id) {
installables.remove(id);
}
private List<InstallableIntent> getInstallable(IntentId id) {
List<InstallableIntent> installable = installables.get(id);
if (installable != null) {
return installable;
} else {
return Collections.emptyList();
}
}
@Override
public void submit(Intent intent) {
intents.put(intent.getId(), intent);
setState(intent, IntentState.SUBMITTED);
executeSubmit(intent);
}
@Override
public void withdraw(Intent intent) {
intents.remove(intent.getId());
setState(intent, IntentState.WITHDRAWING);
executeWithdraw(intent);
}
@Override
public void execute(IntentOperations operations) {
// TODO: implement later
}
@Override
public Set<Intent> getIntents() {
return Collections.unmodifiableSet(new HashSet<>(intents.values()));
}
@Override
public Intent getIntent(IntentId id) {
return intents.get(id);
}
@Override
public IntentState getIntentState(IntentId id) {
return intentStates.get(id);
}
@Override
public void addListener(IntentEventListener listener) {
listeners.add(listener);
}
@Override
public void removeListener(IntentEventListener listener) {
listeners.remove(listener);
}
private void dispatch(IntentEvent event) {
for (IntentEventListener listener : listeners) {
listener.event(event);
}
}
@Override
public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
compilers.put(cls, compiler);
}
@Override
public <T extends Intent> void unregisterCompiler(Class<T> cls) {
compilers.remove(cls);
}
@Override
public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
return Collections.unmodifiableMap(compilers);
}
@Override
public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
installers.put(cls, installer);
}
@Override
public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
installers.remove(cls);
}
@Override
public Map<Class<? extends InstallableIntent>,
IntentInstaller<? extends InstallableIntent>> getInstallers() {
return Collections.unmodifiableMap(installers);
}
private void registerSubclassCompilerIfNeeded(Intent intent) {
if (!compilers.containsKey(intent.getClass())) {
Class<?> cls = intent.getClass();
while (cls != Object.class) {
// As long as we're within the Intent class descendants
if (Intent.class.isAssignableFrom(cls)) {
IntentCompiler<?> compiler = compilers.get(cls);
if (compiler != null) {
compilers.put(intent.getClass(), compiler);
return;
}
}
cls = cls.getSuperclass();
}
}
}
private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
if (!installers.containsKey(intent.getClass())) {
Class<?> cls = intent.getClass();
while (cls != Object.class) {
// As long as we're within the InstallableIntent class descendants
if (InstallableIntent.class.isAssignableFrom(cls)) {
IntentInstaller<?> installer = installers.get(cls);
if (installer != null) {
installers.put(intent.getClass(), installer);
return;
}
}
cls = cls.getSuperclass();
}
}
}
}
package org.onlab.onos.net.intent;
//TODO is this the right package?
import org.hamcrest.Description;
import org.hamcrest.StringDescription;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* Hamcrest style class for verifying that a class follows the
* accepted rules for immutable classes.
*
* The rules that are enforced for immutable classes:
* - the class must be declared final
* - all data members of the class must be declared private and final
* - the class must not define any setter methods
*/
public class ImmutableClassChecker {
private String failureReason = "";
/**
* Method to determine if a given class is a properly specified
* immutable class.
*
* @param clazz the class to check
* @return true if the given class is a properly specified immutable class.
*/
private boolean isImmutableClass(Class<?> clazz) {
// class must be declared final
if (!Modifier.isFinal(clazz.getModifiers())) {
failureReason = "a class that is not final";
return false;
}
// class must have only final and private data members
for (final Field field : clazz.getDeclaredFields()) {
if (field.getName().startsWith("__cobertura")) {
// cobertura sticks these fields into classes - ignore them
continue;
}
if (!Modifier.isFinal(field.getModifiers())) {
failureReason = "a field named '" + field.getName() +
"' that is not final";
return false;
}
if (!Modifier.isPrivate(field.getModifiers())) {
//
// NOTE: We relax the recommended rules for defining immutable
// objects and allow "static final" fields that are not
// private. The "final" check was already done above so we
// don't repeat it here.
//
if (!Modifier.isStatic(field.getModifiers())) {
failureReason = "a field named '" + field.getName() +
"' that is not private and is not static";
return false;
}
}
}
// class must not define any setters
for (final Method method : clazz.getMethods()) {
if (method.getDeclaringClass().equals(clazz)) {
if (method.getName().startsWith("set")) {
failureReason = "a class with a setter named '" + method.getName() + "'";
return false;
}
}
}
return true;
}
/**
* Describe why an error was reported. Uses Hamcrest style Description
* interfaces.
*
* @param description the Description object to use for reporting the
* mismatch
*/
public void describeMismatch(Description description) {
description.appendText(failureReason);
}
/**
* Describe the source object that caused an error, using a Hamcrest
* Matcher style interface. In this case, it always returns
* that we are looking for a properly defined utility class.
*
* @param description the Description object to use to report the "to"
* object
*/
public void describeTo(Description description) {
description.appendText("a properly defined immutable class");
}
/**
* Assert that the given class adheres to the utility class rules.
*
* @param clazz the class to check
*
* @throws java.lang.AssertionError if the class is not a valid
* utility class
*/
public static void assertThatClassIsImmutable(Class<?> clazz) {
final ImmutableClassChecker checker = new ImmutableClassChecker();
if (!checker.isImmutableClass(clazz)) {
final Description toDescription = new StringDescription();
final Description mismatchDescription = new StringDescription();
checker.describeTo(toDescription);
checker.describeMismatch(mismatchDescription);
final String reason =
"\n" +
"Expected: is \"" + toDescription.toString() + "\"\n" +
" but : was \"" + mismatchDescription.toString() + "\"";
throw new AssertionError(reason);
}
}
}
package org.onlab.onos.net.intent;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Test of the intent exception.
*/
public class IntentExceptionTest {
@Test
public void basics() {
validate(new IntentException(), null, null);
validate(new IntentException("foo"), "foo", null);
Throwable cause = new NullPointerException("bar");
validate(new IntentException("foo", cause), "foo", cause);
}
/**
* Validates that the specified exception has the correct message and cause.
*
* @param e exception to test
* @param message expected message
* @param cause expected cause
*/
protected void validate(RuntimeException e, String message, Throwable cause) {
assertEquals("incorrect message", message, e.getMessage());
assertEquals("incorrect cause", cause, e.getCause());
}
}
package org.onlab.onos.net.intent;
/**
* This interface is for generator of IntentId. It is defined only for
* testing purpose to keep type safety on mock creation.
*
* <p>
* {@link #getNewId()} generates a globally unique {@link IntentId} instance
* on each invocation. Application developers should not generate IntentId
* by themselves. Instead use an implementation of this interface.
* </p>
*/
public interface IntentIdGenerator extends IdGenerator<IntentId> {
}
package org.onlab.onos.net.intent;
import org.junit.Test;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
/**
* This class tests the immutability, equality, and non-equality of
* {@link IntentId}.
*/
public class IntentIdTest {
/**
* Tests the immutability of {@link IntentId}.
*/
@Test
public void intentIdFollowsGuidelineForImmutableObject() {
ImmutableClassChecker.assertThatClassIsImmutable(IntentId.class);
}
/**
* Tests equality of {@link IntentId}.
*/
@Test
public void testEquality() {
IntentId id1 = new IntentId(1L);
IntentId id2 = new IntentId(1L);
assertThat(id1, is(id2));
}
/**
* Tests non-equality of {@link IntentId}.
*/
@Test
public void testNonEquality() {
IntentId id1 = new IntentId(1L);
IntentId id2 = new IntentId(2L);
assertThat(id1, is(not(id2)));
}
@Test
public void valueOf() {
IntentId id = new IntentId(12345);
assertEquals("incorrect valueOf", id, IntentId.valueOf("12345"));
}
@Test
public void valueOfHex() {
IntentId id = new IntentId(0xdeadbeefL);
assertEquals("incorrect valueOf", id, IntentId.valueOf(id.toString()));
}
}
package org.onlab.onos.net.intent;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import static org.onlab.onos.net.intent.IntentState.*;
import static org.junit.Assert.*;
// TODO: consider make it categorized as integration test when it become
// slow test or fragile test
/**
* Suite of tests for the intent service contract.
*/
public class IntentServiceTest {
public static final IntentId IID = new IntentId(123);
public static final IntentId INSTALLABLE_IID = new IntentId(234);
protected static final int GRACE_MS = 500; // millis
protected TestableIntentService service;
protected TestListener listener = new TestListener();
@Before
public void setUp() {
service = createIntentService();
service.addListener(listener);
}
@After
public void tearDown() {
service.removeListener(listener);
}
/**
* Creates a service instance appropriately instrumented for testing.
*
* @return testable intent service
*/
protected TestableIntentService createIntentService() {
return new FakeIntentManager();
}
@Test
public void basics() {
// Make sure there are no intents
assertEquals("incorrect intent count", 0, service.getIntents().size());
// Register a compiler and an installer both setup for success.
service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
service.registerInstaller(TestInstallableIntent.class, new TestInstaller(false));
final Intent intent = new TestIntent(IID);
service.submit(intent);
// Allow a small window of time until the intent is in the expected state
TestTools.assertAfter(GRACE_MS, new Runnable() {
@Override
public void run() {
assertEquals("incorrect intent state", INSTALLED,
service.getIntentState(intent.getId()));
}
});
// Make sure that all expected events have been emitted
validateEvents(intent, SUBMITTED, COMPILED, INSTALLED);
// Make sure there is just one intent (and is ours)
assertEquals("incorrect intent count", 1, service.getIntents().size());
assertEquals("incorrect intent", intent, service.getIntent(intent.getId()));
// Reset the listener events
listener.events.clear();
// Now withdraw the intent
service.withdraw(intent);
// Allow a small window of time until the event is in the expected state
TestTools.assertAfter(GRACE_MS, new Runnable() {
@Override
public void run() {
assertEquals("incorrect intent state", WITHDRAWN,
service.getIntentState(intent.getId()));
}
});
// Make sure that all expected events have been emitted
validateEvents(intent, WITHDRAWING, WITHDRAWN);
// TODO: discuss what is the fate of intents after they have been withdrawn
// Make sure that the intent is no longer in the system
// assertEquals("incorrect intent count", 0, service.getIntents().size());
// assertNull("intent should not be found", service.getIntent(intent.getId()));
// assertNull("intent state should not be found", service.getIntentState(intent.getId()));
}
@Test
public void failedCompilation() {
// Register a compiler programmed for success
service.registerCompiler(TestIntent.class, new TestCompiler(true));
// Submit an intent
final Intent intent = new TestIntent(IID);
service.submit(intent);
// Allow a small window of time until the intent is in the expected state
TestTools.assertAfter(GRACE_MS, new Runnable() {
@Override
public void run() {
assertEquals("incorrect intent state", FAILED,
service.getIntentState(intent.getId()));
}
});
// Make sure that all expected events have been emitted
validateEvents(intent, SUBMITTED, FAILED);
}
@Test
public void failedInstallation() {
// Register a compiler programmed for success and installer for failure
service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
service.registerInstaller(TestInstallableIntent.class, new TestInstaller(true));
// Submit an intent
final Intent intent = new TestIntent(IID);
service.submit(intent);
// Allow a small window of time until the intent is in the expected state
TestTools.assertAfter(GRACE_MS, new Runnable() {
@Override
public void run() {
assertEquals("incorrect intent state", FAILED,
service.getIntentState(intent.getId()));
}
});
// Make sure that all expected events have been emitted
validateEvents(intent, SUBMITTED, COMPILED, FAILED);
}
/**
* Validates that the test event listener has received the following events
* for the specified intent. Events received for other intents will not be
* considered.
*
* @param intent intent subject
* @param states list of states for which events are expected
*/
protected void validateEvents(Intent intent, IntentState... states) {
Iterator<IntentEvent> events = listener.events.iterator();
for (IntentState state : states) {
IntentEvent event = events.hasNext() ? events.next() : null;
if (event == null) {
fail("expected event not found: " + state);
} else if (intent.equals(event.getIntent())) {
assertEquals("incorrect state", state, event.getState());
}
}
// Remainder of events should not apply to this intent; make sure.
while (events.hasNext()) {
assertFalse("unexpected event for intent",
intent.equals(events.next().getIntent()));
}
}
@Test
public void compilerBasics() {
// Make sure there are no compilers
assertEquals("incorrect compiler count", 0, service.getCompilers().size());
// Add a compiler and make sure that it appears in the map
IntentCompiler<TestIntent> compiler = new TestCompiler(false);
service.registerCompiler(TestIntent.class, compiler);
assertEquals("incorrect compiler", compiler,
service.getCompilers().get(TestIntent.class));
// Remove the same and make sure that it no longer appears in the map
service.unregisterCompiler(TestIntent.class);
assertNull("compiler should not be registered",
service.getCompilers().get(TestIntent.class));
}
@Test
public void installerBasics() {
// Make sure there are no installers
assertEquals("incorrect installer count", 0, service.getInstallers().size());
// Add an installer and make sure that it appears in the map
IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
service.registerInstaller(TestInstallableIntent.class, installer);
assertEquals("incorrect installer", installer,
service.getInstallers().get(TestInstallableIntent.class));
// Remove the same and make sure that it no longer appears in the map
service.unregisterInstaller(TestInstallableIntent.class);
assertNull("installer should not be registered",
service.getInstallers().get(TestInstallableIntent.class));
}
@Test
public void implicitRegistration() {
// Add a compiler and make sure that it appears in the map
IntentCompiler<TestIntent> compiler = new TestCompiler(new TestSubclassInstallableIntent(INSTALLABLE_IID));
service.registerCompiler(TestIntent.class, compiler);
assertEquals("incorrect compiler", compiler,
service.getCompilers().get(TestIntent.class));
// Add a installer and make sure that it appears in the map
IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false);
service.registerInstaller(TestInstallableIntent.class, installer);
assertEquals("incorrect installer", installer,
service.getInstallers().get(TestInstallableIntent.class));
// Submit an intent which is a subclass of the one we registered
final Intent intent = new TestSubclassIntent(IID);
service.submit(intent);
// Allow some time for the intent to be compiled and installed
TestTools.assertAfter(GRACE_MS, new Runnable() {
@Override
public void run() {
assertEquals("incorrect intent state", INSTALLED,
service.getIntentState(intent.getId()));
}
});
// Make sure that now we have an implicit registration of the compiler
// under the intent subclass
assertEquals("incorrect compiler", compiler,
service.getCompilers().get(TestSubclassIntent.class));
// Make sure that now we have an implicit registration of the installer
// under the intent subclass
assertEquals("incorrect installer", installer,
service.getInstallers().get(TestSubclassInstallableIntent.class));
// TODO: discuss whether or if implicit registration should require implicit unregistration
// perhaps unregister by compiler or installer itself, rather than by class would be better
}
// Fixture to track emitted intent events
protected class TestListener implements IntentEventListener {
final List<IntentEvent> events = new ArrayList<>();
@Override
public void event(IntentEvent event) {
events.add(event);
}
}
// Controllable compiler
private class TestCompiler implements IntentCompiler<TestIntent> {
private final boolean fail;
private final List<Intent> result;
TestCompiler(boolean fail) {
this.fail = fail;
this.result = Collections.emptyList();
}
TestCompiler(Intent... result) {
this.fail = false;
this.result = Arrays.asList(result);
}
@Override
public List<Intent> compile(TestIntent intent) {
if (fail) {
throw new IntentException("compile failed by design");
}
List<Intent> compiled = new ArrayList<>(result);
return compiled;
}
}
// Controllable installer
private class TestInstaller implements IntentInstaller<TestInstallableIntent> {
private final boolean fail;
TestInstaller(boolean fail) {
this.fail = fail;
}
@Override
public void install(TestInstallableIntent intent) {
if (fail) {
throw new IntentException("install failed by design");
}
}
@Override
public void uninstall(TestInstallableIntent intent) {
if (fail) {
throw new IntentException("remove failed by design");
}
}
}
}
package org.onlab.onos.net.intent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
/**
* Base facilities to test various intent tests.
*/
public abstract class IntentTest {
/**
* Produces a set of items from the supplied items.
*
* @param items items to be placed in set
* @param <T> item type
* @return set of items
*/
protected static <T> Set<T> itemSet(T[] items) {
return new HashSet<>(Arrays.asList(items));
}
@Test
public void equalsAndHashCode() {
Intent one = createOne();
Intent like = createOne();
Intent another = createAnother();
assertTrue("should be equal", one.equals(like));
assertEquals("incorrect hashCode", one.hashCode(), like.hashCode());
assertFalse("should not be equal", one.equals(another));
assertFalse("should not be equal", one.equals(null));
assertFalse("should not be equal", one.equals("foo"));
}
@Test
public void testToString() {
Intent one = createOne();
Intent like = createOne();
assertEquals("incorrect toString", one.toString(), like.toString());
}
/**
* Creates a new intent, but always a like intent, i.e. all instances will
* be equal, but should not be the same.
*
* @return intent
*/
protected abstract Intent createOne();
/**
* Creates another intent, not equals to the one created by
* {@link #createOne()} and with a different hash code.
*
* @return another intent
*/
protected abstract Intent createAnother();
}
package org.onlab.onos.net.intent;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Suite of tests of the multi-to-single point intent descriptor.
*/
public class MultiPointToSinglePointIntentTest extends ConnectivityIntentTest {
@Test
public void basics() {
MultiPointToSinglePointIntent intent = createOne();
assertEquals("incorrect id", IID, intent.getId());
assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
assertEquals("incorrect ingress", PS1, intent.getIngressPorts());
assertEquals("incorrect egress", P2, intent.getEgressPort());
}
@Override
protected MultiPointToSinglePointIntent createOne() {
return new MultiPointToSinglePointIntent(IID, MATCH, NOP, PS1, P2);
}
@Override
protected MultiPointToSinglePointIntent createAnother() {
return new MultiPointToSinglePointIntent(IID, MATCH, NOP, PS2, P1);
}
}
package org.onlab.onos.net.intent;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.onlab.onos.net.NetTestTools;
import org.onlab.onos.net.Path;
public class PathIntentTest extends ConnectivityIntentTest {
// 111:11 --> 222:22
private static final Path PATH1 = NetTestTools.createPath("111", "222");
// 111:11 --> 333:33
private static final Path PATH2 = NetTestTools.createPath("222", "333");
@Test
public void basics() {
PathIntent intent = createOne();
assertEquals("incorrect id", IID, intent.getId());
assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
assertEquals("incorrect action", NOP, intent.getTrafficTreatment());
assertEquals("incorrect ingress", P1, intent.getIngressPort());
assertEquals("incorrect egress", P2, intent.getEgressPort());
assertEquals("incorrect path", PATH1, intent.getPath());
}
@Override
protected PathIntent createOne() {
return new PathIntent(IID, MATCH, NOP, P1, P2, PATH1);
}
@Override
protected PathIntent createAnother() {
return new PathIntent(IID, MATCH, NOP, P1, P3, PATH2);
}
}
package org.onlab.onos.net.intent;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Suite of tests of the point-to-point intent descriptor.
*/
public class PointToPointIntentTest extends ConnectivityIntentTest {
@Test
public void basics() {
PointToPointIntent intent = createOne();
assertEquals("incorrect id", IID, intent.getId());
assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
assertEquals("incorrect ingress", P1, intent.getIngressPort());
assertEquals("incorrect egress", P2, intent.getEgressPort());
}
@Override
protected PointToPointIntent createOne() {
return new PointToPointIntent(IID, MATCH, NOP, P1, P2);
}
@Override
protected PointToPointIntent createAnother() {
return new PointToPointIntent(IID, MATCH, NOP, P2, P1);
}
}
package org.onlab.onos.net.intent;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Suite of tests of the single-to-multi point intent descriptor.
*/
public class SinglePointToMultiPointIntentTest extends ConnectivityIntentTest {
@Test
public void basics() {
SinglePointToMultiPointIntent intent = createOne();
assertEquals("incorrect id", IID, intent.getId());
assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
assertEquals("incorrect ingress", P1, intent.getIngressPort());
assertEquals("incorrect egress", PS2, intent.getEgressPorts());
}
@Override
protected SinglePointToMultiPointIntent createOne() {
return new SinglePointToMultiPointIntent(IID, MATCH, NOP, P1, PS2);
}
@Override
protected SinglePointToMultiPointIntent createAnother() {
return new SinglePointToMultiPointIntent(IID, MATCH, NOP, P2, PS1);
}
}
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* An installable intent used in the unit test.
*
* FIXME: we don't want to expose this class publicly, but the current Kryo
* serialization mechanism does not allow this class to be private and placed
* on testing directory.
*/
public class TestInstallableIntent extends AbstractIntent implements InstallableIntent {
/**
* Constructs an instance with the specified intent ID.
*
* @param id intent ID
*/
public TestInstallableIntent(IntentId id) {
super(id);
}
/**
* Constructor for serializer.
*/
protected TestInstallableIntent() {
super();
}
}
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* An intent used in the unit test.
*
* FIXME: we don't want to expose this class publicly, but the current Kryo
* serialization mechanism does not allow this class to be private and placed
* on testing directory.
*/
public class TestIntent extends AbstractIntent {
/**
* Constructs an instance with the specified intent ID.
*
* @param id intent ID
*/
public TestIntent(IntentId id) {
super(id);
}
/**
* Constructor for serializer.
*/
protected TestIntent() {
super();
}
}
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* An intent used in the unit test.
*
* FIXME: we don't want to expose this class publicly, but the current Kryo
* serialization mechanism does not allow this class to be private and placed
* on testing directory.
*/
public class TestSubclassInstallableIntent extends TestInstallableIntent implements InstallableIntent {
/**
* Constructs an instance with the specified intent ID.
*
* @param id intent ID
*/
public TestSubclassInstallableIntent(IntentId id) {
super(id);
}
/**
* Constructor for serializer.
*/
protected TestSubclassInstallableIntent() {
super();
}
}
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* An intent used in the unit test.
*
* FIXME: we don't want to expose this class publicly, but the current Kryo
* serialization mechanism does not allow this class to be private and placed
* on testing directory.
*/
public class TestSubclassIntent extends TestIntent {
/**
* Constructs an instance with the specified intent ID.
*
* @param id intent ID
*/
public TestSubclassIntent(IntentId id) {
super(id);
}
/**
* Constructor for serializer.
*/
protected TestSubclassIntent() {
super();
}
}
package org.onlab.onos.net.intent;
import static org.junit.Assert.fail;
/**
* Set of test tools.
*/
public final class TestTools {
// Disallow construction
private TestTools() {
}
/**
* Utility method to pause the current thread for the specified number of
* milliseconds.
*
* @param ms number of milliseconds to pause
*/
public static void delay(int ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
fail("unexpected interrupt");
}
}
/**
* Periodically runs the given runnable, which should contain a series of
* test assertions until all the assertions succeed, in which case it will
* return, or until the the time expires, in which case it will throw the
* first failed assertion error.
*
* @param start start time, in millis since start of epoch from which the
* duration will be measured
* @param delay initial delay (in milliseconds) before the first assertion
* attempt
* @param step delay (in milliseconds) between successive assertion
* attempts
* @param duration number of milliseconds beyond the given start time,
* after which the failed assertions will be propagated and allowed
* to fail the test
* @param assertions runnable housing the test assertions
*/
public static void assertAfter(long start, int delay, int step,
int duration, Runnable assertions) {
delay(delay);
while (true) {
try {
assertions.run();
break;
} catch (AssertionError e) {
if (System.currentTimeMillis() - start > duration) {
throw e;
}
}
delay(step);
}
}
/**
* Periodically runs the given runnable, which should contain a series of
* test assertions until all the assertions succeed, in which case it will
* return, or until the the time expires, in which case it will throw the
* first failed assertion error.
* <p>
* The start of the period is the current time.
*
* @param delay initial delay (in milliseconds) before the first assertion
* attempt
* @param step delay (in milliseconds) between successive assertion
* attempts
* @param duration number of milliseconds beyond the current time time,
* after which the failed assertions will be propagated and allowed
* to fail the test
* @param assertions runnable housing the test assertions
*/
public static void assertAfter(int delay, int step, int duration,
Runnable assertions) {
assertAfter(System.currentTimeMillis(), delay, step, duration,
assertions);
}
/**
* Periodically runs the given runnable, which should contain a series of
* test assertions until all the assertions succeed, in which case it will
* return, or until the the time expires, in which case it will throw the
* first failed assertion error.
* <p>
* The start of the period is the current time and the first assertion
* attempt is delayed by the value of {@code step} parameter.
*
* @param step delay (in milliseconds) between successive assertion
* attempts
* @param duration number of milliseconds beyond the current time time,
* after which the failed assertions will be propagated and allowed
* to fail the test
* @param assertions runnable housing the test assertions
*/
public static void assertAfter(int step, int duration,
Runnable assertions) {
assertAfter(step, step, duration, assertions);
}
/**
* Periodically runs the given runnable, which should contain a series of
* test assertions until all the assertions succeed, in which case it will
* return, or until the the time expires, in which case it will throw the
* first failed assertion error.
* <p>
* The start of the period is the current time and each successive
* assertion attempt is delayed by at least 10 milliseconds unless the
* {@code duration} is less than that, in which case the one and only
* assertion is made after that delay.
*
* @param duration number of milliseconds beyond the current time,
* after which the failed assertions will be propagated and allowed
* to fail the test
* @param assertions runnable housing the test assertions
*/
public static void assertAfter(int duration, Runnable assertions) {
int step = Math.min(duration, Math.max(10, duration / 10));
assertAfter(step, duration, assertions);
}
}
package org.onlab.onos.net.intent;
import java.util.List;
/**
* Abstraction of an extensible intent service enabled for unit tests.
*/
public interface TestableIntentService extends IntentService, IntentExtensionService {
List<IntentException> getExceptions();
}
package org.onlab.onos.net.host.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Set;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -12,6 +17,7 @@ import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.device.DeviceService;
import org.onlab.onos.net.host.HostAdminService;
import org.onlab.onos.net.host.HostDescription;
import org.onlab.onos.net.host.HostEvent;
......@@ -23,6 +29,7 @@ import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.host.HostStore;
import org.onlab.onos.net.host.HostStoreDelegate;
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.net.packet.PacketService;
import org.onlab.onos.net.provider.AbstractProviderRegistry;
import org.onlab.onos.net.provider.AbstractProviderService;
import org.onlab.packet.IpAddress;
......@@ -31,11 +38,6 @@ import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.slf4j.Logger;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provides basic implementation of the host SB &amp; NB APIs.
*/
......@@ -59,12 +61,22 @@ public class HostManager
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected EventDeliveryService eventDispatcher;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;
private HostMonitor monitor;
@Activate
public void activate() {
log.info("Started");
store.setDelegate(delegate);
eventDispatcher.addSink(HostEvent.class, listenerRegistry);
log.info("Started");
monitor = new HostMonitor(deviceService, packetService, this);
}
@Deactivate
......@@ -76,6 +88,8 @@ public class HostManager
@Override
protected HostProviderService createProviderService(HostProvider provider) {
monitor.registerHostProvider(provider);
return new InternalHostProviderService(provider);
}
......@@ -126,12 +140,12 @@ public class HostManager
@Override
public void startMonitoringIp(IpAddress ip) {
// TODO pass through to HostMonitor
monitor.addMonitoringFor(ip);
}
@Override
public void stopMonitoringIp(IpAddress ip) {
// TODO pass through to HostMonitor
monitor.stopMonitoring(ip);
}
@Override
......
......@@ -2,10 +2,11 @@ package org.onlab.onos.net.host.impl;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.jboss.netty.util.Timeout;
......@@ -21,19 +22,19 @@ import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.flow.instructions.Instruction;
import org.onlab.onos.net.flow.instructions.Instructions;
import org.onlab.onos.net.host.HostProvider;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.host.HostStore;
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.net.packet.DefaultOutboundPacket;
import org.onlab.onos.net.packet.OutboundPacket;
import org.onlab.onos.net.packet.PacketService;
import org.onlab.onos.net.topology.TopologyService;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.packet.ARP;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.util.Timer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Monitors hosts on the dataplane to detect changes in host data.
......@@ -43,9 +44,7 @@ import org.onlab.util.Timer;
* probe for hosts that have not yet been detected (specified by IP address).
*/
public class HostMonitor implements TimerTask {
private static final byte[] DEFAULT_MAC_ADDRESS =
MacAddress.valueOf("00:00:00:00:00:01").getAddress();
private static final Logger log = LoggerFactory.getLogger(HostMonitor.class);
private static final byte[] ZERO_MAC_ADDRESS =
MacAddress.valueOf("00:00:00:00:00:00").getAddress();
......@@ -54,59 +53,77 @@ public class HostMonitor implements TimerTask {
private static final byte[] BROADCAST_MAC =
MacAddress.valueOf("ff:ff:ff:ff:ff:ff").getAddress();
private final HostService hostService;
private final TopologyService topologyService;
private final DeviceService deviceService;
private final HostProvider hostProvider;
private final PacketService packetService;
private final HostStore hostStore;
private DeviceService deviceService;
private PacketService packetService;
private HostManager hostManager;
private final Set<IpAddress> monitoredAddresses;
private final Map<ProviderId, HostProvider> hostProviders;
private final long probeRate;
private final Timeout timeout;
public HostMonitor(HostService hostService, TopologyService topologyService,
public HostMonitor(
DeviceService deviceService,
HostProvider hostProvider, PacketService packetService,
HostStore hostStore) {
this.hostService = hostService;
this.topologyService = topologyService;
PacketService packetService,
HostManager hostService) {
this.deviceService = deviceService;
this.hostProvider = hostProvider;
this.packetService = packetService;
this.hostStore = hostStore;
this.hostManager = hostService;
monitoredAddresses = new HashSet<>();
hostProviders = new ConcurrentHashMap<>();
probeRate = 30000; // milliseconds
timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
addDefaultAddresses();
}
public void addMonitoringFor(IpAddress ip) {
private void addDefaultAddresses() {
//monitoredAddresses.add(IpAddress.valueOf("10.0.0.1"));
}
void addMonitoringFor(IpAddress ip) {
monitoredAddresses.add(ip);
}
public void stopMonitoring(IpAddress ip) {
void stopMonitoring(IpAddress ip) {
monitoredAddresses.remove(ip);
}
public void shutdown() {
void shutdown() {
timeout.cancel();
}
void registerHostProvider(HostProvider provider) {
hostProviders.put(provider.id(), provider);
}
void unregisterHostProvider(HostProvider provider) {
// TODO find out how to call this
}
@Override
public void run(Timeout timeout) throws Exception {
for (IpAddress ip : monitoredAddresses) {
Set<Host> hosts = Collections.emptySet(); //TODO hostService.getHostsByIp(ip);
// TODO have to convert right now because the HostService API uses IpPrefix
IpPrefix prefix = IpPrefix.valueOf(ip.toOctets());
Set<Host> hosts = hostManager.getHostsByIp(prefix);
if (hosts.isEmpty()) {
sendArpRequest(ip);
} else {
for (Host host : hosts) {
hostProvider.triggerProbe(host);
HostProvider provider = hostProviders.get(host.providerId());
if (provider != null) {
provider.triggerProbe(host);
}
}
}
}
......@@ -120,29 +137,26 @@ public class HostMonitor implements TimerTask {
* @param targetIp IP address to ARP for
*/
private void sendArpRequest(IpAddress targetIp) {
// Find ports with an IP address in the target's subnet and sent ARP
// probes out those ports.
for (Device device : deviceService.getDevices()) {
for (Port port : deviceService.getPorts(device.id())) {
ConnectPoint cp = new ConnectPoint(device.id(), port.number());
PortAddresses addresses = hostStore.getAddressBindingsForPort(cp);
PortAddresses addresses = hostManager.getAddressBindingsForPort(cp);
/*for (IpPrefix prefix : addresses.ips()) {
for (IpPrefix prefix : addresses.ips()) {
if (prefix.contains(targetIp)) {
sendProbe(device.id(), port, addresses, targetIp);
sendProbe(device.id(), port, targetIp,
prefix.toIpAddress(), addresses.mac());
}
}
}*/
}
}
// TODO case where no address was found.
// Broadcast out internal edge ports?
}
private void sendProbe(DeviceId deviceId, Port port, PortAddresses portAddresses,
IpAddress targetIp) {
Ethernet arpPacket = createArpFor(targetIp, portAddresses);
private void sendProbe(DeviceId deviceId, Port port, IpAddress targetIp,
IpAddress sourceIp, MacAddress sourceMac) {
Ethernet arpPacket = buildArpRequest(targetIp, sourceIp, sourceMac);
List<Instruction> instructions = new ArrayList<>();
instructions.add(Instructions.createOutput(port.number()));
......@@ -158,30 +172,25 @@ public class HostMonitor implements TimerTask {
packetService.emit(outboundPacket);
}
private Ethernet createArpFor(IpAddress targetIp, PortAddresses portAddresses) {
private Ethernet buildArpRequest(IpAddress targetIp, IpAddress sourceIp,
MacAddress sourceMac) {
ARP arp = new ARP();
arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
.setProtocolType(ARP.PROTO_TYPE_IP)
.setProtocolAddressLength((byte) IpPrefix.INET_LEN);
byte[] sourceMacAddress;
if (portAddresses.mac() == null) {
sourceMacAddress = DEFAULT_MAC_ADDRESS;
} else {
sourceMacAddress = portAddresses.mac().getAddress();
}
.setProtocolAddressLength((byte) IpPrefix.INET_LEN)
.setOpCode(ARP.OP_REQUEST);
arp.setSenderHardwareAddress(sourceMacAddress)
//TODO .setSenderProtocolAddress(portAddresses.ips().toOctets())
arp.setSenderHardwareAddress(sourceMac.getAddress())
.setSenderProtocolAddress(sourceIp.toOctets())
.setTargetHardwareAddress(ZERO_MAC_ADDRESS)
.setTargetProtocolAddress(targetIp.toOctets());
Ethernet ethernet = new Ethernet();
ethernet.setEtherType(Ethernet.TYPE_ARP)
.setDestinationMACAddress(BROADCAST_MAC)
.setSourceMACAddress(sourceMacAddress)
.setSourceMACAddress(sourceMac.getAddress())
.setPayload(arp);
return ethernet;
......
......@@ -35,6 +35,12 @@
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-netty</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
......@@ -51,15 +57,6 @@
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
</dependencies>
<build>
......
......@@ -20,7 +20,7 @@ import org.onlab.onos.cluster.DefaultControllerNode;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.store.AbstractStore;
import org.onlab.onos.store.cluster.messaging.ClusterCommunicationAdminService;
import org.onlab.onos.store.cluster.messaging.impl.OnosClusterCommunicationManager;
import org.onlab.onos.store.cluster.messaging.impl.ClusterCommunicationManager;
import org.onlab.packet.IpPrefix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -50,7 +50,7 @@ public class DistributedClusterStore
private final Map<NodeId, State> states = new ConcurrentHashMap<>();
private final Cache<NodeId, ControllerNode> livenessCache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(OnosClusterCommunicationManager.HEART_BEAT_INTERVAL_MILLIS * 3, TimeUnit.MILLISECONDS)
.expireAfterWrite(ClusterCommunicationManager.HEART_BEAT_INTERVAL_MILLIS * 3, TimeUnit.MILLISECONDS)
.removalListener(new LivenessCacheRemovalListener()).build();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
......
package org.onlab.onos.store.cluster.messaging;
/**
* Interface for handling cluster messages.
*/
public interface ClusterMessageHandler {
/**
* Handles/Processes the cluster message.
* @param message cluster message.
*/
public void handle(ClusterMessage message);
}
\ No newline at end of file
......
......@@ -2,6 +2,8 @@ package org.onlab.onos.store.cluster.messaging;
/**
* Representation of a message subject.
* Cluster messages have associated subjects that dictate how they get handled
* on the receiving side.
*/
public class MessageSubject {
......
package org.onlab.onos.store.cluster.messaging;
import org.onlab.onos.cluster.NodeId;
/**
* Represents a message consumer.
*/
public interface MessageSubscriber {
/**
* Receives the specified cluster message.
*
* @param message message to be received
* @param fromNodeId node from which the message was received
*/
void receive(Object messagePayload, NodeId fromNodeId);
}
......@@ -23,16 +23,16 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
import org.onlab.onos.store.cluster.messaging.ClusterMessage;
import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
import org.onlab.onos.store.cluster.messaging.MessageSubject;
import org.onlab.onos.store.messaging.Endpoint;
import org.onlab.onos.store.messaging.Message;
import org.onlab.onos.store.messaging.MessageHandler;
import org.onlab.onos.store.messaging.MessagingService;
import org.onlab.netty.Endpoint;
import org.onlab.netty.Message;
import org.onlab.netty.MessageHandler;
import org.onlab.netty.MessagingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(immediate = true)
@Service
public class OnosClusterCommunicationManager
public class ClusterCommunicationManager
implements ClusterCommunicationService, ClusterCommunicationAdminService {
private final Logger log = LoggerFactory.getLogger(getClass());
......
......@@ -6,8 +6,8 @@ import org.junit.Ignore;
import org.junit.Test;
import org.onlab.onos.cluster.DefaultControllerNode;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.store.cluster.messaging.impl.OnosClusterCommunicationManager;
import org.onlab.onos.store.messaging.impl.NettyMessagingService;
import org.onlab.onos.store.cluster.messaging.impl.ClusterCommunicationManager;
import org.onlab.netty.NettyMessagingService;
import org.onlab.packet.IpPrefix;
import java.util.concurrent.CountDownLatch;
......@@ -29,8 +29,8 @@ public class ClusterCommunicationManagerTest {
private static final IpPrefix IP = IpPrefix.valueOf("127.0.0.1");
private OnosClusterCommunicationManager ccm1;
private OnosClusterCommunicationManager ccm2;
private ClusterCommunicationManager ccm1;
private ClusterCommunicationManager ccm2;
private TestDelegate cnd1 = new TestDelegate();
private TestDelegate cnd2 = new TestDelegate();
......@@ -46,11 +46,11 @@ public class ClusterCommunicationManagerTest {
NettyMessagingService messagingService = new NettyMessagingService();
messagingService.activate();
ccm1 = new OnosClusterCommunicationManager();
ccm1 = new ClusterCommunicationManager();
// ccm1.serializationService = messageSerializer;
ccm1.activate();
ccm2 = new OnosClusterCommunicationManager();
ccm2 = new ClusterCommunicationManager();
// ccm2.serializationService = messageSerializer;
ccm2.activate();
......
......@@ -11,7 +11,7 @@
<bundle>mvn:io.netty/netty/3.9.2.Final</bundle>
<bundle>mvn:com.hazelcast/hazelcast/3.3</bundle>
<bundle>mvn:com.codahale.metrics/metrics-core/3.0.2</bundle>
<bundle>mvn:io.dropwizard.metrics/metrics-core/3.1.0</bundle>
<bundle>mvn:com.eclipsesource.minimal-json/minimal-json/0.9.1</bundle>
<bundle>mvn:com.esotericsoftware.kryo/kryo/2.24.0</bundle>
......
......@@ -169,7 +169,12 @@ public class OpenFlowControllerImpl implements OpenFlowController {
@Override
public void setRole(Dpid dpid, RoleState role) {
getSwitch(dpid).setRole(role);
final OpenFlowSwitch sw = getSwitch(dpid);
if (sw == null) {
log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
return;
}
sw.setRole(role);
}
/**
......
......@@ -48,6 +48,19 @@
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.6</version>
......@@ -235,6 +248,11 @@
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
</dependencies>
</dependencyManagement>
......@@ -244,6 +262,14 @@
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
</dependency>
......@@ -320,6 +346,35 @@
</plugin>
<!-- TODO: add findbugs plugin for static code analysis; for explicit invocation only -->
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.jacoco</groupId>
<artifactId>
jacoco-maven-plugin
</artifactId>
<versionRange>
[0.7.1.201405082137,)
</versionRange>
<goals>
<goal>prepare-agent</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
......
......@@ -6,5 +6,10 @@
<groupId>org.onlab.tools</groupId>
<artifactId>onos-build-conf</artifactId>
<version>1.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
......
......@@ -56,9 +56,13 @@
<artifactId>objenesis</artifactId>
</dependency>
<dependency>
<groupId>com.codahale.metrics</groupId>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>3.0.2</version>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
</dependencies>
......
package org.onlab.metrics;
import java.io.File;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import com.codahale.metrics.Counter;
import com.codahale.metrics.CsvReporter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
......@@ -45,24 +53,44 @@ import com.codahale.metrics.Timer;
* </code>
* </pre>
*/
@Component(immediate = true)
public final class MetricsManager implements MetricsService {
/**
* Registry to hold the Components defined in the system.
*/
private ConcurrentMap<String, MetricsComponent> componentsRegistry =
new ConcurrentHashMap<>();
private ConcurrentMap<String, MetricsComponent> componentsRegistry;
/**
* Registry for the Metrics objects created in the system.
*/
private final MetricRegistry metricsRegistry = new MetricRegistry();
private final MetricRegistry metricsRegistry;
/**
* Hide constructor. The only way to get the registry is through the
* singleton getter.
* Default Reporter for this metrics manager.
*/
private MetricsManager() {}
private final CsvReporter reporter;
public MetricsManager() {
this.componentsRegistry = new ConcurrentHashMap<>();
this.metricsRegistry = new MetricRegistry();
this.reporter = CsvReporter.forRegistry(metricsRegistry)
.formatFor(Locale.US)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MICROSECONDS)
.build(new File("/tmp/"));
reporter.start(10, TimeUnit.SECONDS);
}
@Activate
public void activate() {
}
@Deactivate
public void deactivate() {
}
/**
* Registers a component.
......
......@@ -250,6 +250,17 @@ public final class IpPrefix {
return new IpPrefix(version, host, netmask);
}
/**
* Returns an IpAddress of the bytes contained in this prefix.
* FIXME this is a hack for now and only works because IpPrefix doesn't
* mask the input bytes on creation.
*
* @return the IpAddress
*/
public IpAddress toIpAddress() {
return IpAddress.valueOf(octets);
}
public boolean isMasked() {
return mask() != 0;
}
......@@ -278,6 +289,17 @@ public final class IpPrefix {
return false;
}
public boolean contains(IpAddress address) {
// Need to get the network address because prefixes aren't automatically
// masked on creation
IpPrefix meMasked = network();
IpPrefix otherMasked =
IpPrefix.valueOf(address.octets, netmask).network();
return Arrays.equals(meMasked.octets, otherMasked.octets);
}
@Override
public int hashCode() {
final int prime = 31;
......@@ -303,6 +325,7 @@ public final class IpPrefix {
if (netmask != other.netmask) {
return false;
}
// TODO not quite right until we mask the input
if (!Arrays.equals(octets, other.octets)) {
return false;
}
......
......@@ -76,7 +76,7 @@ public class IpPrefixTest {
}
@Test
public void testContains() {
public void testContainsIpPrefix() {
IpPrefix slash31 = IpPrefix.valueOf(BYTES1, 31);
IpPrefix slash32 = IpPrefix.valueOf(BYTES1, 32);
IpPrefix differentSlash32 = IpPrefix.valueOf(BYTES2, 32);
......@@ -96,4 +96,17 @@ public class IpPrefixTest {
assertTrue(slash8.contains(slash31));
assertFalse(slash31.contains(slash8));
}
@Test
public void testContainsIpAddress() {
IpPrefix slash31 = IpPrefix.valueOf(BYTES1, 31);
IpAddress slash32 = IpAddress.valueOf(BYTES1, 32);
assertTrue(slash31.contains(slash32));
IpPrefix intf = IpPrefix.valueOf("192.168.10.101/24");
IpAddress addr = IpAddress.valueOf("192.168.10.1");
assertTrue(intf.contains(addr));
}
}
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-utils</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onlab-netty</artifactId>
<packaging>bundle</packaging>
<description>Network I/O using Netty framework</description>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-testlib</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-misc</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
</dependency>
</dependencies>
</project>
package org.onlab.onos.store.messaging.impl;
package org.onlab.netty;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.onlab.onos.store.messaging.Response;
/**
* An asynchronous response.
* This class provides a base implementation of Response, with methods to retrieve the
......
package org.onlab.onos.store.messaging.impl;
package org.onlab.netty;
import java.io.IOException;
import org.onlab.onos.store.messaging.Message;
import org.onlab.onos.store.messaging.MessageHandler;
/**
* Message handler that echos the message back to the sender.
*/
......
package org.onlab.onos.store.messaging;
package org.onlab.netty;
/**
* Representation of a TCP/UDP communication end point.
......
package org.onlab.onos.store.messaging.impl;
package org.onlab.netty;
import java.io.IOException;
import org.onlab.onos.store.messaging.Endpoint;
import org.onlab.onos.store.messaging.Message;
/**
* Internal message representation with additional attributes
* for supporting, synchronous request/reply behavior.
......
package org.onlab.netty;
import org.onlab.util.KryoPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Kryo Serializer.
*/
public class KryoSerializer implements Serializer {
private final Logger log = LoggerFactory.getLogger(getClass());
private KryoPool serializerPool;
public KryoSerializer() {
setupKryoPool();
}
/**
* Sets up the common serialzers pool.
*/
protected void setupKryoPool() {
// FIXME Slice out types used in common to separate pool/namespace.
serializerPool = KryoPool.newBuilder()
.register(ArrayList.class,
HashMap.class,
ArrayList.class
)
.build()
.populate(1);
}
@Override
public Object decode(byte[] data) {
return serializerPool.deserialize(data);
}
@Override
public byte[] encode(Object payload) {
return serializerPool.serialize(payload);
}
}
package org.onlab.onos.store.messaging.impl;
import org.onlab.onos.store.messaging.Message;
import org.onlab.onos.store.messaging.MessageHandler;
package org.onlab.netty;
/**
* A MessageHandler that simply logs the information.
......
package org.onlab.onos.store.messaging.impl;
package org.onlab.netty;
import java.util.Arrays;
import java.util.List;
import static com.google.common.base.Preconditions.checkState;
import org.onlab.onos.store.cluster.messaging.SerializationService;
import org.onlab.onos.store.messaging.Endpoint;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
/**
* Decode bytes into a InrenalMessage.
* Decode bytes into a InternalMessage.
*/
public class MessageDecoder extends ByteToMessageDecoder {
private final NettyMessagingService messagingService;
private final SerializationService serializationService;
private final Serializer serializer;
public MessageDecoder(NettyMessagingService messagingService, SerializationService serializationService) {
public MessageDecoder(NettyMessagingService messagingService, Serializer serializer) {
this.messagingService = messagingService;
this.serializationService = serializationService;
this.serializer = serializer;
}
@Override
......@@ -47,7 +44,7 @@ public class MessageDecoder extends ByteToMessageDecoder {
Endpoint sender = new Endpoint(host, port);
// read message payload; first read size and then bytes.
Object payload = serializationService.decode(in.readBytes(in.readInt()).array());
Object payload = serializer.decode(in.readBytes(in.readInt()).array());
InternalMessage message = new InternalMessage.Builder(messagingService)
.withId(id)
......
package org.onlab.onos.store.messaging.impl;
import org.onlab.onos.store.cluster.messaging.SerializationService;
package org.onlab.netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
......@@ -14,10 +12,10 @@ public class MessageEncoder extends MessageToByteEncoder<InternalMessage> {
// onosiscool in ascii
public static final byte[] PREAMBLE = "onosiscool".getBytes();
private final SerializationService serializationService;
private final Serializer serializer;
public MessageEncoder(SerializationService serializationService) {
this.serializationService = serializationService;
public MessageEncoder(Serializer serializer) {
this.serializer = serializer;
}
@Override
......@@ -46,12 +44,12 @@ public class MessageEncoder extends MessageToByteEncoder<InternalMessage> {
out.writeInt(message.sender().port());
try {
serializationService.encode(message.payload());
serializer.encode(message.payload());
} catch (Exception e) {
e.printStackTrace();
}
byte[] payload = serializationService.encode(message.payload());
byte[] payload = serializer.encode(message.payload());
// write payload length.
out.writeInt(payload.length);
......
package org.onlab.onos.store.messaging.impl;
package org.onlab.netty;
import java.io.IOException;
import java.net.UnknownHostException;
......@@ -25,17 +25,6 @@ import org.apache.commons.lang.math.RandomUtils;
import org.apache.commons.pool.KeyedObjectPool;
import org.apache.commons.pool.KeyedPoolableObjectFactory;
import org.apache.commons.pool.impl.GenericKeyedObjectPool;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.store.cluster.messaging.SerializationService;
import org.onlab.onos.store.messaging.Endpoint;
import org.onlab.onos.store.messaging.MessageHandler;
import org.onlab.onos.store.messaging.MessagingService;
import org.onlab.onos.store.messaging.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -45,8 +34,6 @@ import com.google.common.cache.CacheBuilder;
/**
* A Netty based implementation of MessagingService.
*/
@Component(immediate = true)
@Service
public class NettyMessagingService implements MessagingService {
private final Logger log = LoggerFactory.getLogger(getClass());
......@@ -60,8 +47,7 @@ public class NettyMessagingService implements MessagingService {
private Cache<Long, AsyncResponse<?>> responseFutures;
private final Endpoint localEp;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected SerializationService serializationService;
protected Serializer serializer;
public NettyMessagingService() {
// TODO: Default port should be configurable.
......@@ -79,7 +65,6 @@ public class NettyMessagingService implements MessagingService {
}
}
@Activate
public void activate() throws Exception {
responseFutures = CacheBuilder.newBuilder()
.maximumSize(100000)
......@@ -90,7 +75,6 @@ public class NettyMessagingService implements MessagingService {
startAcceptingConnections();
}
@Deactivate
public void deactivate() throws Exception {
channels.close();
bossGroup.shutdownGracefully();
......@@ -213,8 +197,8 @@ public class NettyMessagingService implements MessagingService {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline()
.addLast(new MessageEncoder(serializationService))
.addLast(new MessageDecoder(NettyMessagingService.this, serializationService))
.addLast(new MessageEncoder(serializer))
.addLast(new MessageDecoder(NettyMessagingService.this, serializer))
.addLast(new NettyMessagingService.InboundMessageDispatcher());
}
}
......
package org.onlab.onos.store.messaging;
package org.onlab.netty;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
......
package org.onlab.netty;
/**
* Interface for encoding/decoding message payloads.
*/
public interface Serializer {
/**
* Decodes the specified byte array to a POJO.
*
* @param data byte array.
* @return POJO
*/
Object decode(byte[] data);
/**
* Encodes the specified POJO into a byte array.
*
* @param data POJO to be encoded
* @return byte array.
*/
byte[] encode(Object message);
}
package org.onlab.onos.store.messaging.impl;
package org.onlab.netty;
import java.util.concurrent.TimeUnit;
import org.onlab.onos.store.cluster.impl.MessageSerializer;
import org.onlab.onos.store.messaging.Endpoint;
import org.onlab.onos.store.messaging.Response;
public final class SimpleClient {
private SimpleClient() {}
......@@ -21,9 +17,8 @@ public final class SimpleClient {
public static class TestNettyMessagingService extends NettyMessagingService {
public TestNettyMessagingService(int port) throws Exception {
super(port);
MessageSerializer mgr = new MessageSerializer();
mgr.activate();
this.serializationService = mgr;
Serializer serializer = new KryoSerializer();
this.serializer = serializer;
}
}
}
......
package org.onlab.onos.store.messaging.impl;
import org.onlab.onos.store.cluster.impl.MessageSerializer;
package org.onlab.netty;
public final class SimpleServer {
private SimpleServer() {}
......@@ -14,9 +12,8 @@ public final class SimpleServer {
public static class TestNettyMessagingService extends NettyMessagingService {
protected TestNettyMessagingService() {
MessageSerializer mgr = new MessageSerializer();
mgr.activate();
this.serializationService = mgr;
Serializer serializer = new KryoSerializer();
this.serializer = serializer;
}
}
}
......
/**
* Asynchronous messaging APIs implemented using the Netty framework.
*/
package org.onlab.netty;
\ No newline at end of file
......@@ -19,6 +19,7 @@
<modules>
<module>junit</module>
<module>misc</module>
<module>netty</module>
<module>nio</module>
<module>osgi</module>
<module>rest</module>
......