This tutorial will show you how to create a CLI command to print the endpoints found by the reactive forwarding application from the Application tutorial. After completing this tutorial, you will understand:
If you want your module to be able to provide services to other modules, you should define a service interface and have your module class implement it.
We start by defining a new interface for the service in the same location as our application (~/onos-next/apps/ifwd/src/main/java/org/onlab/onos/ifwd/):
package org.onlab.onos.ifwd; import java.util.Map; import org.onlab.onos.net.HostId; /** * A demonstrative service for the intent reactive forwarding application to * export */ public interface ForwardingMapService { /** * Get the endpoints of the host-to-host intents that were installed * * @return maps of source to destination */ public Map<HostId, HostId> getEndPoints(); } |
Next, we implement our service in IntentReactiveForwarding
. We also indicate to Karaf that the application exports a service, using the the Felix SCR annotation @Service
:
@Component(immediate = true) @Service public class IntentReactiveForwarding implements ForwardingMapService { private final Logger log = getLogger(getClass()); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected CoreService coreService; // ...<snip>... // Install a rule forwarding the packet to the specified port. private void setUpConnectivity(PacketContext context, HostId srcId, HostId dstId) { TrafficSelector selector = DefaultTrafficSelector.builder().build(); TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); HostToHostIntent intent = new HostToHostIntent(appId, srcId, dstId, selector, treatment); intentService.submit(intent); } // the new service method, to be filled out @Override public Map<HostId, HostId> getEndPoints() { return null; } } |
Although we won't be using it here in this manner, the @Service
annotation enables another class to reference the service through the @Reference
annotation:
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ForwardingMapService fwdMapService; |
We can now define the new method. We add a new Map, endPoints
, to IntentReactiveForwarding
. The map is populated when the ReactivePacketProcessor
's process()
method finds endpoints known by the HostService.
@Component(immediate = true) @Service public class IntentReactiveForwarding implements ForwardingMapService { // ...<snip>... private ApplicationId appId; // Map for storing found endpoints, for our service. It is protected // so that process() can access it. protected final HashMap<HostId, HostId> endPoints = new HashMap<>(); // ...<snip>... /** * Packet processor responsible for forwarding packets along their paths. */ private class ReactivePacketProcessor implements PacketProcessor { @Override public void process(PacketContext context) { // Stop processing if the packet has been handled, since we // can't do any more to it. if (context.isHandled()) { return; } InboundPacket pkt = context.inPacket(); Ethernet ethPkt = pkt.parsed(); HostId srcId = HostId.hostId(ethPkt.getSourceMAC()); HostId dstId = HostId.hostId(ethPkt.getDestinationMAC()); // Do we know who this is for? If not, flood and bail. Host dst = hostService.getHost(dstId); if (dst == null) { flood(context); return; } // Add found endpoints to map. endPoints.put(srcId, dstId); // Otherwise forward and be done with it. setUpConnectivity(context, srcId, dstId); forwardPacketToDst(context, dst); } } // ...<snip>... @Override public Map<HostId, HostId> getEndPoints() { // Return our map as a read-only structure. return Collections.unmodifiableMap(endPoints); } } |
Now, a module referencing the ForwardingMapService may call getEndPoints()
to get a list of directional endpoints for which intents were installed, and traffic can flow between.
Next, we will create a CLI command to use this new service. This command will list the contents of this map, and also provide the option to take a parameter (a host ID), to filter on that host as a source.
The CLI commands are defined in the project directory onos_next/cli/. There are two types of commands, with their source files located in the following locations:
Since our command will display network-related information, we will add our command to the second directory.
We create the following class skeleton for our new command, tentatively named ForwardingMapCommand
. Our class is a child of AbstractShellCommand
, and the @Command
annotation is used to set its name, scope, and description.
/* * Copyright 2014 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onlab.onos.cli.net; import org.apache.karaf.shell.commands.Argument; import org.apache.karaf.shell.commands.Command; import org.onlab.onos.cli.AbstractShellCommand; import org.onlab.onos.net.HostId; /** * Lists the endpoints for which intents are installed */ @Command(scope = "onos", name = "fwdmap", description = "Lists the endpoints for which intents are installed") public class ForwardingMapCommand extends AbstractShellCommand { @Override protected void execute() { } } |
The annotation enables this particular command to be invoked as fwdmap
or onos:fwdmap
at the CLI.
Next, we need to tell Karaf about our new command by editing shell-config.xml, located in ONOS_ROOT/cli/src/main/resources/OSGI-INF/blueprint/. We append the following to the contents between the <command-bundle></command-bundle>
clause:
<command> <!--Our command implementation's FQDN--> <action class="org.onlab.onos.cli.net.ForwardingMapCommand"/> <!--A command completer for Host IDs--> <completers> <ref component-id="hostIdCompleter"/> <null/> </completers> </command> |