...
Code Block |
---|
onos> create-network test2 Created network test2 onos> add-host test2 00:00:00:00:00:01/-1 Added host 00:00:00:00:00:01/-1 to testtest2 onos> add-host test2 00:00:00:00:00:02/-1 Added host 00:00:00:00:00:02/-1 to testtest2 onos> list-networks test2 00:00:00:00:00:01/-1 00:00:00:00:00:02/-1 onos> intents id=0x0, state=INSTALLED, type=HostToHostIntent, appId=org.onos.byon constraints=[LinkTypeConstraint{inclusive=false, types=[OPTICAL]}] |
...
Next, let's recompile your application and push it to the ONOS cluster. You should now be able to create, update, and delete networks from any node, and your distributed store will the application running on all instances in sync. Open a few terminals and test this on the different instances.
Lab 6: Network Events
In order to be able to communicate between ONOS instances we are going to make use of events. Components in ONOS can use events to asynchronously notify other components when their state has changed. We will demonstrate how this can be done by creating a new event type, NetworkEvent, to notify listeners when a virtual network has been updated. These events will be fired by the distributed store and caught forwarded by the manager to listeners in the peer ONOS instance. At this point the peer manager will notify any local listeners of the network event.
Ok so letLet's start by adding a NetworkEvent class.:
Code Block | ||
---|---|---|
| ||
public class NetworkEvent extends AbstractEvent<NetworkEvent.Type, String> { enum Type { NETWORK_ADDED, NETWORK_REMOVED, NETWORK_UPDATED } public NetworkEvent(Type type, String subject) { super(type, subject); } } |
...
And since we changed the interface of the Store we will have to add something to the implementationsimplementation, namely SimpleNetworkStoreDistributedNetworkStore. This abstract class is used to define the methods for posting event to its delegates.
Code Block |
---|
public class SimpleNetworkStoreDistributedNetworkStore extends AbstractStore<NetworkEvent, NetworkStoreDelegate> |
...
Code Block | ||
---|---|---|
| ||
/** * Register a listener for network events. * * @param listener listener */ void addListener(NetworkListener listener); /** * Unregister a listener for network events. * * @param listener listener */ void removeListener(NetworkListener listener); |
Now since the store is making use of the delegates, we need to make the stores' implementation delegate-aware by notifying the delegates when network elements are added or removed. For example, the putNetwork method no notifies the delegate that a new network has been added.
The ConsistentMap will already generate notifications when updates are made locally and remotely, so we can mostly leverage that feature to regenerate our events.
To do this, we will need to create a MapEventListener<String, Set<HostId>> class in the DistributedNetworkStore.
Code Block | ||
---|---|---|
private class InternalListener implements MapEventListener<String, Set<HostId>> {
| ||
Code Block | ||
| ||
@Override public void putNetwork(String networkevent(MapEvent<String, Set<HostId>> mapEvent) { final intentsPerNet.putIfAbsent(network, Sets.<Intent>newHashSet())NetworkEvent.Type type; ifswitch (networks.putIfAbsent(network, Sets.<HostId>newHashSetmapEvent.type()) == null) { { case INSERT: notifyDelegate(new NetworkEvent( type = NETWORK_ADDED, network)); } } |
Try to change the implementation of the remaining methods to notify the delegates as well.
Alright so now that the stores emit events the managers should be made to receive them. For this we are going to have to add a reference on the EventDeliveryService an AbstractListenerRegistry to keep track of listeners and NetworkStoreDelegate as shown in the code snippet below.
Code Block | ||
---|---|---|
| ||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected EventDeliveryService eventDispatcher; break; private ApplicationId appId; case UPDATE: private final AbstractListenerRegistry<NetworkEvent, NetworkListener> listenerRegistrytype = new AbstractListenerRegistry<>()NETWORK_UPDATED; private final NetworkStoreDelegate delegate = new InternalStoreDelegate(); |
Then we need to add a couple lines in the activate in order to register our Event type for our listeners and set our delegate.
Code Block |
---|
eventDispatcher.addSink(NetworkEvent.class, listenerRegistry) break; store.setDelegate(delegate); |
and vice versa deactivate
Code Block |
---|
eventDispatcher.removeSink(NetworkEvent.class);
store.unsetDelegate(delegate); |
Finally add the methods to add listeners and dispatch events coming from the store:
Code Block | ||
---|---|---|
| ||
case @OverrideREMOVE: public void addListener(NetworkListener listener) { default: listenerRegistry.addListener(listener); } @Override type public void removeListener(NetworkListener listener) { = NETWORK_REMOVED; listenerRegistry.removeListener(listener); } private class InternalStoreDelegate implements NetworkStoreDelegate { break; @Override} public void notify(NetworkEvent event) { eventDispatcher.post(event); notifyDelegate(new NetworkEvent(type, mapEvent.key())); } } |
To test this you We will need to complete the extra credit section .
Part 5: Distributed Store
So we can finally start implementing DistributedStore. To this start by disabling your SimpleNetworkStore:
Code Block |
---|
@Component(immediate = false, enabled = false)
@Service
public class SimpleNetworkStore |
Then copy the contents of the SimpleNetworkStore to a new file called DistributedNetworkStore (surprise!). First start by striping all the events that you generate in the methods of the store. These will be generated by the Remote Listener. Start by adding the Remote Listener to the distributed store class as an internal class.
Code Block | ||
---|---|---|
| ||
private class RemoteListener extends EntryAdapter<String, Set<HostId>> {
@Override
public void entryAdded(EntryEvent<String, Set<HostId>> event) {
notifyDelegate(new NetworkEvent(NETWORK_ADDED, event.getKey()));
}
@Override
public void entryUpdated(EntryEvent<String, Set<HostId>> event) {
notifyDelegate(new NetworkEvent(NETWORK_UPDATED, event.getKey()));
}
@Override
public void entryRemoved(EntryEvent<String, Set<HostId>> event) {
notifyDelegate(new NetworkEvent(NETWORK_REMOVED, event.getKey()));
}
} |
Replace your maps, activate and deactivate methods by the following code snippet:
Code Block | ||
---|---|---|
| ||
private SMap<String, Set<HostId>> networks;
private SMap<String, Set<Intent>> intentsPerNet;
private String listenerId;
@Activate
public void activate() {
super.activate();
networks = new SMap<>(theInstance.<byte[], byte[]>getMap("byon-networks"), this.serializer);
intentsPerNet = new SMap<>(theInstance.<byte[], byte[]>getMap("byon-network-intents"), this.serializer);
EntryListener<String, Set<HostId>> listener = new RemoteListener();
listenerId = networks.addEntryListener(listener, true);
log.info("Started");
}
@Deactivate
public void deactivate() {
networks.removeEntryListener(listenerId);
log.info("Stopped");
}
|
Verify your implementation
You can do this by recompiling, deploying, and launching another console to a second onos instance:
Code Block |
---|
distributed@mininet-vm:~/ $ onos $OC2 |
When you add and remove networks and/or hosts you should see this being reflected on the other ONOS instances.
Part 6: Event Listener
We can create the byon application using a Maven archetype:
Code Block |
---|
distributed@mininet-vm:~/onos-byon/ $ mvn archetype:generate -DarchetypeGroupId=org.onosproject -DarchetypeArtifactId=onos-bundle-archetype -DarchetypeVersion=1.0.0p1 |
Make sure you are in the onos-byon directory
When prompted for the groupId, enter org.onos.byon. When prompted for the artifactId, enter byon-monitor. The remaining fields can the default values that maven suggests.
This will create a directory called byon-monitor that contains a pom.xml file as well as some starter Java files. Let's delete the "test" subtree for now.<w
Info |
---|
IntelliJ should detect the new module and ask you to import the changes but if it does not then right click on the top-level project, select Maven followed by re-import. |
Open the pom.xml in the byon-monitor module and add the following snippet to the bottom of the dependencies block: (can either be done in IntelliJ or by hand)
Code Block |
---|
<dependency>
<groupId>org.onos.byon</groupId>
<artifactId>byon-app</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency> |
The archetype comes with a file called AppComponent.java. Refactor this class and call it NetworkEventMonitor. And add the following code to it.
construct an instance of the InternalListener.
Code Block |
---|
private final InternalListener listener = new InternalListener(); |
This listener will need to be added and removed to the networks map during activation and deactivation respectively.
Alright, so now that the stores emit events the managers should be made to receive them. For this we are going to have to add a reference on the EventDeliveryService an AbstractListenerRegistry to keep track of listeners and NetworkStoreDelegate as shown in the code snippet below.
Code Block | ||
---|---|---|
| ||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected EventDeliveryService eventDispatcher;
private final AbstractListenerRegistry<NetworkEvent, NetworkListener>
listenerRegistry = new AbstractListenerRegistry<>();
private final NetworkStoreDelegate delegate = new InternalStoreDelegate();
|
Then we need to add a couple lines in the activate in order to register our Event type for our listeners and set our delegate.
Code Block |
---|
eventDispatcher.addSink(NetworkEvent.class, listenerRegistry);
store.setDelegate(delegate);
|
and vice versa deactivate
Code Block |
---|
eventDispatcher.removeSink(NetworkEvent.class);
store.unsetDelegate(delegate); |
Finally add the methods to add listeners and dispatch events coming from the store:
Code Block | ||
---|---|---|
| ||
@Override
public void addListener(NetworkListener listener | ||
Code Block | ||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected NetworkService service; private final Listener listener = new Listener(); @Activate protected void activate() { service listenerRegistry.addListener(listener); log.info("Started"); } @Deactivate@Override protectedpublic void deactivateremoveListener(NetworkListener listener) { service listenerRegistry.removeListener(listener); log.info("Stopped"); } private class ListenerInternalStoreDelegate implements NetworkListenerNetworkStoreDelegate { @Override public void eventnotify(NetworkEvent event) { logeventDispatcher.info("{}", post(event); } } |
To test this, you will need to create a new component that registers to the NetworkEvents with the NetworkService. We will leave this as an extra credit exercise.
Then update the bundles line in byon-push-bits:
Code Block |
---|
bundles="byon-app byon-cli byon-monitor" |
Verify what you built
So once again recompile and redeploy byon by using the byon-push-bits command.
Info |
---|
Make sure to use the @Component annotation to start an instance of the class automatically.You will also want to register and deregister with the NetworkService in the activate and deactivate methods of your component. |
Finally, build and push the app to the ONOS cluster. You should try to test your implementation. When you add and remove networks and/or hosts you should see this being reflected on the other ONOS instances.
Furthermore, you add, remove, or update When you add a network you should see the following line in the ONOS log. You can get the ONOS log by running:
Code Block |
---|
distributed@mininet-vm:~/ $ ol |
And you should see a line that look like:
Code Block |
---|
INFO | event-dispatch-0 | NetworkEventMonitor | 187 - org.onos.byon.monitor - 1.0.0.SNAPSHOT | NetworkEvent{time=<time&date>, type=NETWORK_ADDED, subject=test} |
Conclusion
Congratulations! You have learned how to develop an ONOS distributed application on ONOS.