Link to detail documetation: https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:L2_Switch
It has following components:
PacketHandler examines Ethernet packets to find information about Mac-Port pairings.
-informs AddressTracker about new Mac-Port pairings.
-informs FlowWriterService about new flows, when the source & destination of a packet are known.
-uses InventoryService to determine external ports and only send packets to those ports when flooding packets in the network.
AddressTracker stores the Mac-Port pairings in the MD-SAL data tree.
InventoryService provides information about the nodes and node connectors in the network.
FlowWriterService adds packet forwarding (mac-to-mac) flows to the MD-SAL data tree.
-uses NetworkGraphDijkstra to determine all the intermediate nodes along a path.
TopologyLinkDataChangeHandler listens to topology updates and informs NetworkGraphDijkstra of these updates.
NetworkGraphDijkstra maintains the network graph and computes the shortest path between each node.
Change-Id: I33497a9e2136316de1db16e1c7f916cbc13f437f
Signed-off-by: Amit Mandke <ammandke@cisco.com>
--- /dev/null
+<?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/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>sal-samples</artifactId>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <version>1.1-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
+ <artifactId>l2switch-impl</artifactId>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+
+ <instructions>
+ <Bundle-Activator>org.opendaylight.controller.sample.l2switch.md.L2SwitchProvider</Bundle-Activator>
+ </instructions>
+ <manifestLocation>${project.build.directory}/META-INF</manifestLocation>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>${junit.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
+ <artifactId>l2switch-model</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-inventory</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-flow-service</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>net.sf.jung2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-topology</artifactId>
+ <version>1.1-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md;
+
+import org.opendaylight.controller.sample.l2switch.md.addresstracker.AddressTracker;
+import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService;
+import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterServiceImpl;
+import org.opendaylight.controller.sample.l2switch.md.inventory.InventoryService;
+import org.opendaylight.controller.sample.l2switch.md.packet.PacketHandler;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphDijkstra;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService;
+import org.opendaylight.controller.sample.l2switch.md.topology.TopologyLinkDataChangeHandler;
+import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareConsumer;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * L2SwitchProvider serves as the Activator for our L2Switch OSGI bundle.
+ */
+public class L2SwitchProvider extends AbstractBindingAwareConsumer
+ implements AutoCloseable {
+
+ private final static Logger _logger = LoggerFactory.getLogger(L2SwitchProvider.class);
+
+ private Registration<NotificationListener> listenerRegistration;
+ private AddressTracker addressTracker;
+ private TopologyLinkDataChangeHandler topologyLinkDataChangeHandler;
+
+
+ /**
+ * Setup the L2Switch.
+ * @param consumerContext The context of the L2Switch.
+ */
+ @Override
+ public void onSessionInitialized(BindingAwareBroker.ConsumerContext consumerContext) {
+ DataBrokerService dataService = consumerContext.<DataBrokerService>getSALService(DataBrokerService.class);
+ addressTracker = new AddressTracker(dataService);
+
+ NetworkGraphService networkGraphService = new NetworkGraphDijkstra();
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataService, networkGraphService);
+
+ NotificationService notificationService =
+ consumerContext.<NotificationService>getSALService(NotificationService.class);
+ PacketProcessingService packetProcessingService =
+ consumerContext.<PacketProcessingService>getRpcService(PacketProcessingService.class);
+ PacketHandler packetHandler = new PacketHandler();
+ packetHandler.setAddressTracker(addressTracker);
+ packetHandler.setFlowWriterService(flowWriterService);
+ packetHandler.setPacketProcessingService(packetProcessingService);
+ packetHandler.setInventoryService(new InventoryService(dataService));
+
+ this.listenerRegistration = notificationService.registerNotificationListener(packetHandler);
+ this.topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataService, networkGraphService);
+ topologyLinkDataChangeHandler.registerAsDataChangeListener();
+ }
+
+ /**
+ * Cleanup the L2Switch.
+ * @throws Exception occurs when the NotificationListener is closed
+ */
+ @Override
+ public void close() throws Exception {
+ if (listenerRegistration != null)
+ listenerRegistration.close();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.addresstracker;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.L2Addresses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2AddressBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2AddressKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Future;
+
+/**
+ * AddressTracker manages the MD-SAL data tree for L2Address (mac, node connector pairings) information.
+ */
+public class AddressTracker {
+
+ private final static Logger _logger = LoggerFactory.getLogger(AddressTracker.class);
+ private DataBrokerService dataService;
+
+ /**
+ * Construct an AddressTracker with the specified inputs
+ * @param dataService The DataBrokerService for the AddressTracker
+ */
+ public AddressTracker(DataBrokerService dataService) {
+ this.dataService = dataService;
+ }
+
+ /**
+ * Get all the L2 Addresses in the MD-SAL data tree
+ * @return All the L2 Addresses in the MD-SAL data tree
+ */
+ public L2Addresses getAddresses() {
+ return (L2Addresses)dataService.readOperationalData(InstanceIdentifier.<L2Addresses>builder(L2Addresses.class).toInstance());
+ }
+
+ /**
+ * Get a specific L2 Address in the MD-SAL data tree
+ * @param macAddress A MacAddress associated with an L2 Address object
+ * @return The L2 Address corresponding to the specified macAddress
+ */
+ public L2Address getAddress(MacAddress macAddress) {
+ return (L2Address) dataService.readOperationalData(createPath(macAddress));
+ }
+
+ /**
+ * Add L2 Address into the MD-SAL data tree
+ * @param macAddress The MacAddress of the new L2Address object
+ * @param nodeConnectorRef The NodeConnectorRef of the new L2Address object
+ * @return Future containing the result of the add operation
+ */
+ public Future<RpcResult<TransactionStatus>> addAddress(MacAddress macAddress, NodeConnectorRef nodeConnectorRef) {
+ if(macAddress == null || nodeConnectorRef == null) {
+ return null;
+ }
+
+ // Create L2Address
+ final L2AddressBuilder builder = new L2AddressBuilder();
+ builder.setKey(new L2AddressKey(macAddress))
+ .setMac(macAddress)
+ .setNodeConnectorRef(nodeConnectorRef);
+
+ // Add L2Address to MD-SAL data tree
+ final DataModificationTransaction it = dataService.beginTransaction();
+ it.putOperationalData(createPath(macAddress), builder.build());
+ return it.commit();
+ }
+
+ /**
+ * Remove L2Address from the MD-SAL data tree
+ * @param macAddress The MacAddress of an L2Address object
+ * @return Future containing the result of the remove operation
+ */
+ public Future<RpcResult<TransactionStatus>> removeHost(MacAddress macAddress) {
+ final DataModificationTransaction it = dataService.beginTransaction();
+ it.removeOperationalData(createPath(macAddress));
+ return it.commit();
+ }
+
+ /**
+ * Create InstanceIdentifier path for an L2Address in the MD-SAL data tree
+ * @param macAddress The MacAddress of an L2Address object
+ * @return InstanceIdentifier of the L2Address corresponding to the specified macAddress
+ */
+ private InstanceIdentifier<L2Address> createPath(MacAddress macAddress) {
+ return InstanceIdentifier.<L2Addresses>builder(L2Addresses.class)
+ .<L2Address, L2AddressKey>child(L2Address.class, new L2AddressKey(macAddress)).toInstance();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.flow;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+
+/**
+ * Service that adds packet forwarding flows to configuration data store.
+ */
+public interface FlowWriterService {
+
+ /**
+ * Writes a flow that forwards packets to destPort if destination mac in packet is destMac and
+ * source Mac in packet is sourceMac. If sourceMac is null then flow would not set any source mac,
+ * resulting in all packets with destMac being forwarded to destPort.
+ *
+ * @param sourceMac
+ * @param destMac
+ * @param destNodeConnectorRef
+ */
+ public void addMacToMacFlow(MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destNodeConnectorRef);
+
+ /**
+ * Writes mac-to-mac flow on all ports that are in the path between given source and destination ports.
+ * It uses path provided by NetworkGraphService{@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService} to find a links{@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * between given ports. And then writes appropriate flow on each port that is covered in that path.
+ *
+ * @param sourceMac
+ * @param sourceNodeConnectorRef
+ * @param destMac
+ * @param destNodeConnectorRef
+ */
+ public void addMacToMacFlowsUsingShortestPath(MacAddress sourceMac, NodeConnectorRef sourceNodeConnectorRef, MacAddress destMac, NodeConnectorRef destNodeConnectorRef);
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.flow;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.util.List;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Implementation of FlowWriterService{@link org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService},
+ * that builds required flow and writes to configuration data store using provided DataBrokerService
+ * {@link org.opendaylight.controller.sal.binding.api.data.DataBrokerService}
+ */
+public class FlowWriterServiceImpl implements FlowWriterService {
+ private static final Logger _logger = LoggerFactory.getLogger(FlowWriterServiceImpl.class);
+ private final DataBrokerService dataBrokerService;
+ private final NetworkGraphService networkGraphService;
+ private AtomicLong flowIdInc = new AtomicLong();
+ private AtomicLong flowCookieInc = new AtomicLong(0x2a00000000000000L);
+
+
+ public FlowWriterServiceImpl(DataBrokerService dataBrokerService, NetworkGraphService networkGraphService) {
+ Preconditions.checkNotNull(dataBrokerService, "dataBrokerService should not be null.");
+ Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
+ this.dataBrokerService = dataBrokerService;
+ this.networkGraphService = networkGraphService;
+ }
+
+ /**
+ * Writes a flow that forwards packets to destPort if destination mac in packet is destMac and
+ * source Mac in packet is sourceMac. If sourceMac is null then flow would not set any source mac,
+ * resulting in all packets with destMac being forwarded to destPort.
+ *
+ * @param sourceMac
+ * @param destMac
+ * @param destNodeConnectorRef
+ */
+ @Override
+ public void addMacToMacFlow(MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destNodeConnectorRef) {
+
+ Preconditions.checkNotNull(destMac, "Destination mac address should not be null.");
+ Preconditions.checkNotNull(destNodeConnectorRef, "Destination port should not be null.");
+
+
+ // do not add flow if both macs are same.
+ if(sourceMac != null && destMac.equals(sourceMac)) {
+ _logger.info("In addMacToMacFlow: No flows added. Source and Destination mac are same.");
+ return;
+ }
+
+ // get flow table key
+ TableKey flowTableKey = new TableKey((short) 0); //TODO: Hard coded Table Id 0, need to get it from Configuration data.
+
+ //build a flow path based on node connector to program flow
+ InstanceIdentifier<Flow> flowPath = buildFlowPath(destNodeConnectorRef, flowTableKey);
+
+ // build a flow that target given mac id
+ Flow flowBody = createMacToMacFlow(flowTableKey.getId(), 0, sourceMac, destMac, destNodeConnectorRef);
+
+ // commit the flow in config data
+ writeFlowToConfigData(flowPath, flowBody);
+ }
+
+ /**
+ * Writes mac-to-mac flow on all ports that are in the path between given source and destination ports.
+ * It uses path provided by NetworkGraphService
+ * {@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService} to find a links
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * between given ports. And then writes appropriate flow on each port that is covered in that path.
+ *
+ * @param sourceMac
+ * @param sourceNodeConnectorRef
+ * @param destMac
+ * @param destNodeConnectorRef
+ */
+ @Override
+ public void addMacToMacFlowsUsingShortestPath(MacAddress sourceMac,
+ NodeConnectorRef sourceNodeConnectorRef,
+ MacAddress destMac,
+ NodeConnectorRef destNodeConnectorRef) {
+ Preconditions.checkNotNull(sourceMac, "Source mac address should not be null.");
+ Preconditions.checkNotNull(sourceNodeConnectorRef, "Source port should not be null.");
+ Preconditions.checkNotNull(destMac, "Destination mac address should not be null.");
+ Preconditions.checkNotNull(destNodeConnectorRef, "Destination port should not be null.");
+
+ if(sourceNodeConnectorRef.equals(destNodeConnectorRef)) {
+ _logger.info("In addMacToMacFlowsUsingShortestPath: No flows added. Source and Destination ports are same.");
+ return;
+
+ }
+ NodeId sourceNodeId = new NodeId(sourceNodeConnectorRef.getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ NodeId destNodeId = new NodeId(destNodeConnectorRef.getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+
+ // add destMac-To-sourceMac flow on source port
+ addMacToMacFlow(destMac, sourceMac, sourceNodeConnectorRef);
+
+ // add sourceMac-To-destMac flow on destination port
+ addMacToMacFlow(sourceMac, destMac, destNodeConnectorRef);
+
+ if(!sourceNodeId.equals(destNodeId)) {
+ List<Link> linksInBeween = networkGraphService.getPath(sourceNodeId, destNodeId);
+
+ if(linksInBeween != null) {
+ // assumes the list order is maintained and starts with link that has source as source node
+ for(Link link : linksInBeween) {
+ // add sourceMac-To-destMac flow on source port
+ addMacToMacFlow(sourceMac, destMac, getSourceNodeConnectorRef(link));
+
+ // add destMac-To-sourceMac flow on destination port
+ addMacToMacFlow(destMac, sourceMac, getDestNodeConnectorRef(link));
+ }
+ }
+ }
+ }
+
+ private NodeConnectorRef getSourceNodeConnectorRef(Link link) {
+ InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
+ = InstanceIdentifierUtils.createNodeConnectorIdentifier(
+ link.getSource().getSourceNode().getValue(),
+ link.getSource().getSourceTp().getValue());
+ return new NodeConnectorRef(nodeConnectorInstanceIdentifier);
+ }
+
+ private NodeConnectorRef getDestNodeConnectorRef(Link link) {
+ InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
+ = InstanceIdentifierUtils.createNodeConnectorIdentifier(
+ link.getDestination().getDestNode().getValue(),
+ link.getDestination().getDestTp().getValue());
+
+ return new NodeConnectorRef(nodeConnectorInstanceIdentifier);
+ }
+
+ /**
+ * @param nodeConnectorRef
+ * @return
+ */
+ private InstanceIdentifier<Flow> buildFlowPath(NodeConnectorRef nodeConnectorRef, TableKey flowTableKey) {
+
+ // generate unique flow key
+ FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
+ FlowKey flowKey = new FlowKey(flowId);
+
+ return InstanceIdentifierUtils.generateFlowInstanceIdentifier(nodeConnectorRef, flowTableKey, flowKey);
+ }
+
+ /**
+ * @param tableId
+ * @param priority
+ * @param sourceMac
+ * @param destMac
+ * @param destPort
+ * @return {@link org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder}
+ * builds flow that forwards all packets with destMac to given port
+ */
+ private Flow createMacToMacFlow(Short tableId, int priority,
+ MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destPort) {
+
+ // start building flow
+ FlowBuilder macToMacFlow = new FlowBuilder() //
+ .setTableId(tableId) //
+ .setFlowName("mac2mac");
+
+ // use its own hash code for id.
+ macToMacFlow.setId(new FlowId(Long.toString(macToMacFlow.hashCode())));
+
+ // create a match that has mac to mac ethernet match
+ EthernetMatchBuilder ethernetMatchBuilder = new EthernetMatchBuilder() //
+ .setEthernetDestination(new EthernetDestinationBuilder() //
+ .setAddress(destMac) //
+ .build());
+ // set source in the match only if present
+ if(sourceMac != null) {
+ ethernetMatchBuilder.setEthernetSource(new EthernetSourceBuilder()
+ .setAddress(sourceMac)
+ .build());
+ }
+ EthernetMatch ethernetMatch = ethernetMatchBuilder.build();
+ Match match = new MatchBuilder()
+ .setEthernetMatch(ethernetMatch)
+ .build();
+
+
+ Uri destPortUri = destPort.getValue().firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
+
+ Action outputToControllerAction = new ActionBuilder() //
+ .setAction(new OutputActionCaseBuilder() //
+ .setOutputAction(new OutputActionBuilder() //
+ .setMaxLength(new Integer(0xffff)) //
+ .setOutputNodeConnector(destPortUri) //
+ .build()) //
+ .build()) //
+ .build();
+
+ // Create an Apply Action
+ ApplyActions applyActions = new ApplyActionsBuilder().setAction(ImmutableList.of(outputToControllerAction))
+ .build();
+
+ // Wrap our Apply Action in an Instruction
+ Instruction applyActionsInstruction = new InstructionBuilder() //
+ .setInstruction(new ApplyActionsCaseBuilder()//
+ .setApplyActions(applyActions) //
+ .build()) //
+ .build();
+
+ // Put our Instruction in a list of Instructions
+ macToMacFlow
+ .setMatch(match) //
+ .setInstructions(new InstructionsBuilder() //
+ .setInstruction(ImmutableList.of(applyActionsInstruction)) //
+ .build()) //
+ .setPriority(priority) //
+ .setBufferId(0L) //
+ .setHardTimeout(0) //
+ .setIdleTimeout(0) //
+ .setCookie(BigInteger.valueOf(flowCookieInc.getAndIncrement()))
+ .setFlags(new FlowModFlags(false, false, false, false, false));
+
+ return macToMacFlow.build();
+ }
+
+ /**
+ * Starts and commits data change transaction which
+ * modifies provided flow path with supplied body.
+ *
+ * @param flowPath
+ * @param flowBody
+ * @return transaction commit
+ */
+ private Future<RpcResult<TransactionStatus>> writeFlowToConfigData(InstanceIdentifier<Flow> flowPath,
+ Flow flowBody) {
+ DataModificationTransaction addFlowTransaction = dataBrokerService.beginTransaction();
+ addFlowTransaction.putConfigurationData(flowPath, flowBody);
+ return addFlowTransaction.commit();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.inventory;
+
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.*;
+
+/**
+ * InventoryService provides functions related to Nodes & NodeConnectors.
+ */
+public class InventoryService {
+ private DataBrokerService dataService;
+ // Key: SwitchId, Value: NodeConnectorRef that corresponds to NC between controller & switch
+ private HashMap<String, NodeConnectorRef> controllerSwitchConnectors;
+
+ /**
+ * Construct an InventoryService object with the specified inputs.
+ * @param dataService The DataBrokerService associated with the InventoryService.
+ */
+ public InventoryService(DataBrokerService dataService) {
+ this.dataService = dataService;
+ controllerSwitchConnectors = new HashMap<String, NodeConnectorRef>();
+ }
+
+ public HashMap<String, NodeConnectorRef> getControllerSwitchConnectors() {
+ return controllerSwitchConnectors;
+ }
+
+ // ToDo: Improve performance for thousands of switch ports
+ /**
+ * Get the External NodeConnectors of the network, which are the NodeConnectors connected to hosts.
+ * @return The list of external node connectors.
+ */
+ public List<NodeConnectorRef> getExternalNodeConnectors() {
+ // External NodeConnectors = All - Internal
+ ArrayList<NodeConnectorRef> externalNodeConnectors = new ArrayList<NodeConnectorRef>();
+ Set<String> internalNodeConnectors = new HashSet<>();
+
+ // Read Topology -- find list of switch-to-switch internal node connectors
+ NetworkTopology networkTopology =
+ (NetworkTopology)dataService.readOperationalData(
+ InstanceIdentifier.<NetworkTopology>builder(NetworkTopology.class).toInstance());
+
+ for (Topology topology : networkTopology.getTopology()) {
+ Topology completeTopology =
+ (Topology)dataService.readOperationalData(
+ InstanceIdentifierUtils.generateTopologyInstanceIdentifier(
+ topology.getTopologyId().getValue()));
+
+ for (Link link : completeTopology.getLink()) {
+ internalNodeConnectors.add(link.getDestination().getDestTp().getValue());
+ internalNodeConnectors.add(link.getSource().getSourceTp().getValue());
+ }
+ }
+
+ // Read Inventory -- contains list of all nodeConnectors
+ InstanceIdentifier.InstanceIdentifierBuilder<Nodes> nodesInsIdBuilder = InstanceIdentifier.<Nodes>builder(Nodes.class);
+ Nodes nodes = (Nodes)dataService.readOperationalData(nodesInsIdBuilder.toInstance());
+ if (nodes != null) {
+ for (Node node : nodes.getNode()) {
+ Node completeNode = (Node)dataService.readOperationalData(InstanceIdentifierUtils.createNodePath(node.getId()));
+ for (NodeConnector nodeConnector : completeNode.getNodeConnector()) {
+ // NodeConnector isn't switch-to-switch, so it must be controller-to-switch (internal) or external
+ if (!internalNodeConnectors.contains(nodeConnector.getId().getValue())) {
+ NodeConnectorRef ncRef = new NodeConnectorRef(
+ InstanceIdentifier.<Nodes>builder(Nodes.class).<Node, NodeKey>child(Node.class, node.getKey())
+ .<NodeConnector, NodeConnectorKey>child(NodeConnector.class, nodeConnector.getKey()).toInstance());
+
+ // External node connectors have "-" in their name for mininet, i.e. "s1-eth1"
+ if (nodeConnector.getAugmentation(FlowCapableNodeConnector.class).getName().contains("-")) {
+ externalNodeConnectors.add(ncRef);
+ }
+ // Controller-to-switch internal node connectors
+ else {
+ controllerSwitchConnectors.put(node.getId().getValue(), ncRef);
+ }
+ }
+ }
+ }
+ }
+
+ return externalNodeConnectors;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.packet;
+
+import org.opendaylight.controller.sample.l2switch.md.addresstracker.AddressTracker;
+import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService;
+import org.opendaylight.controller.sample.l2switch.md.inventory.InventoryService;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.LLDP;
+import org.opendaylight.controller.sal.packet.LinkEncap;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.*;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * PacketHandler examines Ethernet packets to find L2Addresses (mac, nodeConnector) pairings
+ * of the sender and learns them.
+ * It also forwards the data packets appropriately dependending upon whether it knows about the
+ * target or not.
+ */
+public class PacketHandler implements PacketProcessingListener {
+
+ private final static Logger _logger = LoggerFactory.getLogger(PacketHandler.class);
+
+ private PacketProcessingService packetProcessingService;
+ private AddressTracker addressTracker;
+ private FlowWriterService flowWriterService;
+ private InventoryService inventoryService;
+
+ public void setAddressTracker(AddressTracker addressTracker) {
+ this.addressTracker = addressTracker;
+ }
+
+ public void setPacketProcessingService(PacketProcessingService packetProcessingService) {
+ this.packetProcessingService = packetProcessingService;
+ }
+
+ public void setFlowWriterService(FlowWriterService flowWriterService) {
+ this.flowWriterService = flowWriterService;
+ }
+
+ public void setInventoryService(InventoryService inventoryService) {
+ this.inventoryService = inventoryService;
+ }
+
+ /**
+ * The handler function for all incoming packets.
+ * @param packetReceived The incoming packet.
+ */
+ @Override
+ public void onPacketReceived(PacketReceived packetReceived) {
+
+ if(packetReceived == null) return;
+
+ try {
+ byte[] payload = packetReceived.getPayload();
+ RawPacket rawPacket = new RawPacket(payload);
+ NodeConnectorRef ingress = packetReceived.getIngress();
+
+ Packet packet = decodeDataPacket(rawPacket);
+
+ if(!(packet instanceof Ethernet)) return;
+
+ handleEthernetPacket(packet, ingress);
+
+ } catch(Throwable _e) {
+ _e.printStackTrace();
+ }
+ }
+
+ /**
+ * The handler function for Ethernet packets.
+ * @param packet The incoming Ethernet packet.
+ * @param ingress The NodeConnector where the Ethernet packet came from.
+ */
+ private void handleEthernetPacket(Packet packet, NodeConnectorRef ingress) {
+ byte[] srcMac = ((Ethernet) packet).getSourceMACAddress();
+ byte[] destMac = ((Ethernet) packet).getDestinationMACAddress();
+
+ if (srcMac == null || srcMac.length == 0) return;
+
+ Object enclosedPacket = packet.getPayload();
+
+ if (enclosedPacket instanceof LLDP)
+ return; // LLDP packets are handled by OpenFlowPlugin
+
+ // get l2address by src mac
+ // if unknown, add l2address
+ MacAddress srcMacAddress = toMacAddress(srcMac);
+ L2Address src = addressTracker.getAddress(srcMacAddress);
+ boolean isSrcKnown = (src != null);
+ if (!isSrcKnown) {
+ addressTracker.addAddress(srcMacAddress, ingress);
+ }
+
+ // get host by dest mac
+ // if known set dest known to true
+ MacAddress destMacAddress = toMacAddress(destMac);
+ L2Address dest = addressTracker.getAddress(destMacAddress);
+ boolean isDestKnown = (dest != null);
+
+ byte[] payload = packet.getRawPayload();
+ // if (src and dest known)
+ // sendpacket to dest and add src<->dest flow
+ if(isSrcKnown & isDestKnown) {
+ flowWriterService.addMacToMacFlowsUsingShortestPath(srcMacAddress, src.getNodeConnectorRef(),
+ destMacAddress, dest.getNodeConnectorRef());
+ sendPacketOut(payload, getControllerNodeConnector(dest.getNodeConnectorRef()), dest.getNodeConnectorRef());
+ } else {
+ // if (dest unknown)
+ // sendpacket to external links minus ingress
+ floodExternalPorts(payload, ingress);
+ }
+ }
+
+ /**
+ * Floods the specified payload on external ports, which are ports not connected to switches.
+ * @param payload The payload to be flooded.
+ * @param ingress The NodeConnector where the payload came from.
+ */
+ private void floodExternalPorts(byte[] payload, NodeConnectorRef ingress) {
+ List<NodeConnectorRef> externalPorts = inventoryService.getExternalNodeConnectors();
+ externalPorts.remove(ingress);
+
+ for (NodeConnectorRef egress : externalPorts) {
+ sendPacketOut(payload, getControllerNodeConnector(egress), egress);
+ }
+ }
+
+ /**
+ * Sends the specified packet on the specified port.
+ * @param payload The payload to be sent.
+ * @param ingress The NodeConnector where the payload came from.
+ * @param egress The NodeConnector where the payload will go.
+ */
+ private void sendPacketOut(byte[] payload, NodeConnectorRef ingress, NodeConnectorRef egress) {
+ if (ingress == null || egress == null) return;
+ InstanceIdentifier<Node> egressNodePath = InstanceIdentifierUtils.getNodePath(egress.getValue());
+ TransmitPacketInput input = new TransmitPacketInputBuilder() //
+ .setPayload(payload) //
+ .setNode(new NodeRef(egressNodePath)) //
+ .setEgress(egress) //
+ .setIngress(ingress) //
+ .build();
+ packetProcessingService.transmitPacket(input);
+ }
+
+ /**
+ * Decodes an incoming packet.
+ * @param raw The raw packet to be decoded.
+ * @return The decoded form of the raw packet.
+ */
+ private Packet decodeDataPacket(RawPacket raw) {
+ if(raw == null) {
+ return null;
+ }
+ byte[] data = raw.getPacketData();
+ if(data.length <= 0) {
+ return null;
+ }
+ if(raw.getEncap().equals(LinkEncap.ETHERNET)) {
+ Ethernet res = new Ethernet();
+ try {
+ res.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
+ res.setRawPayload(raw.getPacketData());
+ } catch(Exception e) {
+ _logger.warn("Failed to decode packet: {}", e.getMessage());
+ }
+ return res;
+ }
+ return null;
+ }
+
+ /**
+ * Creates a MacAddress object out of a byte array.
+ * @param dataLinkAddress The byte-array form of a MacAddress
+ * @return MacAddress of the specified dataLinkAddress.
+ */
+ private MacAddress toMacAddress(byte[] dataLinkAddress) {
+ return new MacAddress(HexEncode.bytesToHexStringFormat(dataLinkAddress));
+ }
+
+ /**
+ * Gets the NodeConnector that connects the controller & switch for a specified switch port/node connector.
+ * @param nodeConnectorRef The nodeConnector of a switch.
+ * @return The NodeConnector that that connects the controller & switch.
+ */
+ private NodeConnectorRef getControllerNodeConnector(NodeConnectorRef nodeConnectorRef) {
+ NodeConnectorRef controllerSwitchNodeConnector = null;
+ HashMap<String, NodeConnectorRef> controllerSwitchConnectors = inventoryService.getControllerSwitchConnectors();
+ InstanceIdentifier<Node> nodePath = InstanceIdentifierUtils.getNodePath(nodeConnectorRef.getValue());
+ if (nodePath != null) {
+ NodeKey nodeKey = InstanceIdentifierUtils.getNodeKey(nodePath);
+ if (nodeKey != null) {
+ controllerSwitchNodeConnector = controllerSwitchConnectors.get(nodeKey.getId().getValue());
+ }
+ }
+ return controllerSwitchNodeConnector;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.topology;
+
+import com.google.common.base.Preconditions;
+import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath;
+import edu.uci.ics.jung.graph.DirectedSparseGraph;
+import edu.uci.ics.jung.graph.Graph;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Implementation of NetworkGraphService{@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService}.
+ * It uses Jung graph library internally to maintain a graph and optimum way to return shortest path using
+ * Dijkstra algorithm.
+ */
+public class NetworkGraphDijkstra implements NetworkGraphService {
+
+ private static final Logger _logger = LoggerFactory.getLogger(NetworkGraphDijkstra.class);
+
+ DijkstraShortestPath<NodeId, Link> shortestPath = null;
+ Graph<NodeId, Link> networkGraph = null;
+
+ /**
+ * Adds links to existing graph or creates new directed graph with given links if graph was not initialized.
+ * @param links
+ */
+ @Override
+ public synchronized void addLinks(List<Link> links) {
+ if(links == null || links.isEmpty()) {
+ _logger.info("In addLinks: No link added as links is null or empty.");
+ return;
+ }
+
+ if(networkGraph == null) {
+ networkGraph = new DirectedSparseGraph<>();
+ }
+
+ for(Link link : links) {
+ NodeId sourceNodeId = link.getSource().getSourceNode();
+ NodeId destinationNodeId = link.getDestination().getDestNode();
+ networkGraph.addVertex(sourceNodeId);
+ networkGraph.addVertex(destinationNodeId);
+ networkGraph.addEdge(link, sourceNodeId, destinationNodeId);
+ }
+ if(shortestPath == null) {
+ shortestPath = new DijkstraShortestPath<>(networkGraph);
+ } else {
+ shortestPath.reset();
+ }
+ }
+
+ /**
+ * removes links from existing graph.
+ * @param links
+ */
+ @Override
+ public synchronized void removeLinks(List<Link> links) {
+ Preconditions.checkNotNull(networkGraph, "Graph is not initialized, add links first.");
+
+ if(links == null || links.isEmpty()) {
+ _logger.info("In removeLinks: No link removed as links is null or empty.");
+ return;
+ }
+
+ for(Link link : links) {
+ networkGraph.removeEdge(link);
+ }
+
+ if(shortestPath == null) {
+ shortestPath = new DijkstraShortestPath<>(networkGraph);
+ } else {
+ shortestPath.reset();
+ }
+ }
+
+ /**
+ * returns a path between 2 nodes. Uses Dijkstra's algorithm to return shortest path.
+ * @param sourceNodeId
+ * @param destinationNodeId
+ * @return
+ */
+ @Override
+ public synchronized List<Link> getPath(NodeId sourceNodeId, NodeId destinationNodeId) {
+ Preconditions.checkNotNull(shortestPath, "Graph is not initialized, add links first.");
+
+ if(sourceNodeId == null || destinationNodeId == null) {
+ _logger.info("In getPath: returning null, as sourceNodeId or destinationNodeId is null.");
+ return null;
+ }
+
+ return shortestPath.getPath(sourceNodeId, destinationNodeId);
+ }
+
+ /**
+ * Clears the prebuilt graph, in case same service instance is required to process a new graph.
+ */
+ @Override
+ public synchronized void clear() {
+ networkGraph = null;
+ shortestPath = null;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.topology;
+
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+
+import java.util.List;
+
+/**
+ * Service that allows to build a network graph using Topology links
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * and exposes operation that can be performed on such graph.
+ */
+public interface NetworkGraphService {
+
+ /**
+ * Adds links to existing graph or creates new graph with given links if graph was not initialized.
+ * @param links
+ */
+ public void addLinks(List<Link> links);
+
+ /**
+ * removes links from existing graph.
+ * @param links
+ */
+ public void removeLinks(List<Link> links);
+
+ /**
+ * returns a path between 2 nodes. Implementation should ideally return shortest path.
+ * @param sourceNodeId
+ * @param destinationNodeId
+ * @return
+ */
+ public List<Link> getPath(NodeId sourceNodeId, NodeId destinationNodeId);
+
+ /**
+ * Clears the prebuilt graph, in case same service instance is required to process a new graph.
+ */
+ public void clear();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.topology;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Listens to data change events on topology links
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * and maintains a topology graph using provided NetworkGraphService
+ * {@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService}.
+ * It refreshes the graph after a delay(default 10 sec) to accommodate burst of change events if they come in bulk.
+ * This is to avoid continuous refresh of graph on a series of change events in short time.
+ */
+public class TopologyLinkDataChangeHandler implements DataChangeListener {
+ private static final Logger _logger = LoggerFactory.getLogger(TopologyLinkDataChangeHandler.class);
+ private static final String DEFAULT_TOPOLOGY_ID = "flow:1";
+
+ private boolean networkGraphRefreshScheduled = false;
+ private final ScheduledExecutorService networkGraphRefreshScheduler = Executors.newScheduledThreadPool(1);
+ private final long DEFAULT_GRAPH_REFRESH_DELAY = 10;
+ private final long graphRefreshDelayInSec;
+
+ private final NetworkGraphService networkGraphService;
+ private final DataBrokerService dataBrokerService;
+
+ /**
+ * Uses default delay to refresh topology graph if this constructor is used.
+ * @param dataBrokerService
+ * @param networkGraphService
+ */
+ public TopologyLinkDataChangeHandler(DataBrokerService dataBrokerService, NetworkGraphService networkGraphService) {
+ Preconditions.checkNotNull(dataBrokerService, "dataBrokerService should not be null.");
+ Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
+ this.dataBrokerService = dataBrokerService;
+ this.networkGraphService = networkGraphService;
+ this.graphRefreshDelayInSec = DEFAULT_GRAPH_REFRESH_DELAY;
+ }
+
+ /**
+ *
+ * @param dataBrokerService
+ * @param networkGraphService
+ * @param graphRefreshDelayInSec
+ */
+ public TopologyLinkDataChangeHandler(DataBrokerService dataBrokerService, NetworkGraphService networkGraphService,
+ long graphRefreshDelayInSec) {
+ Preconditions.checkNotNull(dataBrokerService, "dataBrokerService should not be null.");
+ Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
+ this.dataBrokerService = dataBrokerService;
+ this.networkGraphService = networkGraphService;
+ this.graphRefreshDelayInSec = graphRefreshDelayInSec;
+ }
+
+ /**
+ * Based on if links have been added or removed in topology data store, schedules a refresh of network graph.
+ * @param dataChangeEvent
+ */
+ @Override
+ public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> dataChangeEvent) {
+ if(dataChangeEvent == null) {
+ _logger.info("In onDataChanged: No Processing done as dataChangeEvent is null.");
+ }
+ Map<InstanceIdentifier<?>, DataObject> linkOriginalData = dataChangeEvent.getOriginalOperationalData();
+ Map<InstanceIdentifier<?>, DataObject> linkUpdatedData = dataChangeEvent.getUpdatedOperationalData();
+ // change this logic, once MD-SAL start populating DeletedOperationData Set
+ if(linkOriginalData != null && linkUpdatedData != null
+ && (linkOriginalData.size() != 0 || linkUpdatedData.size() != 0)
+ && !networkGraphRefreshScheduled) {
+ networkGraphRefreshScheduled = linkOriginalData.size() != linkUpdatedData.size();
+ if(networkGraphRefreshScheduled) {
+ networkGraphRefreshScheduler.schedule(new NetworkGraphRefresher(), graphRefreshDelayInSec, TimeUnit.SECONDS);
+ }
+ }
+
+ }
+
+ /**
+ * Registers as a data listener to receive changes done to
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * under {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology}
+ * operation data root.
+ */
+
+ public void registerAsDataChangeListener() {
+ InstanceIdentifier<Link> linkInstance = InstanceIdentifier.builder(NetworkTopology.class)
+ .child(Topology.class, new TopologyKey(new TopologyId(DEFAULT_TOPOLOGY_ID))).child(Link.class).toInstance();
+ dataBrokerService.registerDataChangeListener(linkInstance, this);
+ }
+
+ /**
+ *
+ */
+ private class NetworkGraphRefresher implements Runnable {
+ /**
+ *
+ */
+ @Override
+ public void run() {
+ networkGraphRefreshScheduled = false;
+ //TODO: it should refer to changed links only from DataChangeEvent above.
+ List<Link> links = getLinksFromTopology(DEFAULT_TOPOLOGY_ID);
+ networkGraphService.clear();// can remove this once changed links are addressed
+ if(links != null && !links.isEmpty()) {
+ networkGraphService.addLinks(links);
+ }
+ }
+
+ /**
+ * @param topologyId
+ * @return
+ */
+ private List<Link> getLinksFromTopology(String topologyId) {
+ InstanceIdentifier<Topology> topologyInstanceIdentifier = InstanceIdentifierUtils.generateTopologyInstanceIdentifier(topologyId);
+ Topology topology = (Topology) dataBrokerService.readOperationalData(topologyInstanceIdentifier);
+ return topology.getLink();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.util;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/* InstanceIdentifierUtils provides utility functions related to InstanceIdentifiers.
+ */
+public class InstanceIdentifierUtils {
+
+ /**
+ * Creates an Instance Identifier (path) for node with specified id
+ *
+ * @param nodeId
+ * @return
+ */
+ public static final InstanceIdentifier<Node> createNodePath(NodeId nodeId) {
+ return InstanceIdentifier.builder(Nodes.class) //
+ .child(Node.class, new NodeKey(nodeId)) //
+ .build();
+ }
+
+ /**
+ * Shorten's node child path to node path.
+ *
+ * @param nodeChild child of node, from which we want node path.
+ * @return
+ */
+ public static final InstanceIdentifier<Node> getNodePath(InstanceIdentifier<?> nodeChild) {
+ return nodeChild.firstIdentifierOf(Node.class);
+ }
+
+
+ /**
+ * Creates a table path by appending table specific location to node path
+ *
+ * @param nodePath
+ * @param tableKey
+ * @return
+ */
+ public static final InstanceIdentifier<Table> createTablePath(InstanceIdentifier<Node> nodePath, TableKey tableKey) {
+ return InstanceIdentifier.builder(nodePath)
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, tableKey)
+ .build();
+ }
+
+ /**
+ * Creates a path for particular flow, by appending flow-specific information
+ * to table path.
+ *
+ * @param table
+ * @param flowKey
+ * @return
+ */
+ public static InstanceIdentifier<Flow> createFlowPath(InstanceIdentifier<Table> table, FlowKey flowKey) {
+ return InstanceIdentifier.builder(table)
+ .child(Flow.class, flowKey)
+ .build();
+ }
+
+ /**
+ * Extract table id from table path.
+ *
+ * @param tablePath
+ * @return
+ */
+ public static Short getTableId(InstanceIdentifier<Table> tablePath) {
+ return tablePath.firstKeyOf(Table.class, TableKey.class).getId();
+ }
+
+ /**
+ * Extracts NodeConnectorKey from node connector path.
+ */
+ public static NodeConnectorKey getNodeConnectorKey(InstanceIdentifier<?> nodeConnectorPath) {
+ return nodeConnectorPath.firstKeyOf(NodeConnector.class, NodeConnectorKey.class);
+ }
+
+ /**
+ * Extracts NodeKey from node path.
+ */
+ public static NodeKey getNodeKey(InstanceIdentifier<?> nodePath) {
+ return nodePath.firstKeyOf(Node.class, NodeKey.class);
+ }
+
+
+ //
+ public static final InstanceIdentifier<NodeConnector> createNodeConnectorIdentifier(String nodeIdValue,
+ String nodeConnectorIdValue) {
+ return InstanceIdentifier.builder(createNodePath(new NodeId(nodeIdValue))) //
+ .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(nodeConnectorIdValue))) //
+ .build();
+ }
+
+ /**
+ * @param nodeConnectorRef
+ * @return
+ */
+ public static InstanceIdentifier<Node> generateNodeInstanceIdentifier(NodeConnectorRef nodeConnectorRef) {
+ return nodeConnectorRef.getValue().firstIdentifierOf(Node.class);
+ }
+
+ /**
+ * @param nodeConnectorRef
+ * @param flowTableKey
+ * @return
+ */
+ public static InstanceIdentifier<Table> generateFlowTableInstanceIdentifier(NodeConnectorRef nodeConnectorRef, TableKey flowTableKey) {
+ return InstanceIdentifier.builder(generateNodeInstanceIdentifier(nodeConnectorRef))
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, flowTableKey)
+ .build();
+ }
+
+ /**
+ * @param nodeConnectorRef
+ * @param flowTableKey
+ * @param flowKey
+ * @return
+ */
+ public static InstanceIdentifier<Flow> generateFlowInstanceIdentifier(NodeConnectorRef nodeConnectorRef,
+ TableKey flowTableKey,
+ FlowKey flowKey) {
+ return InstanceIdentifier.builder(generateFlowTableInstanceIdentifier(nodeConnectorRef, flowTableKey))
+ .child(Flow.class, flowKey)
+ .build();
+ }
+
+ public static InstanceIdentifier<Topology> generateTopologyInstanceIdentifier(String topologyId) {
+ return InstanceIdentifier.builder(NetworkTopology.class)
+ .child(Topology.class, new TopologyKey(new TopologyId(topologyId)))
+ .build();
+ }
+}
+
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.flow;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ */
+public class FlowWriterServiceImplTest {
+ private DataBrokerService dataBrokerService;
+ private NodeConnectorRef srcNodeConnectorRef;
+ private NodeConnectorRef destNodeConnectorRef;
+ private MacAddress destMacAddress;
+ private MacAddress srcMacAddress;
+ private DataModificationTransaction dataModificationTransaction;
+ private NetworkGraphService networkGraphService;
+
+ @Before
+ public void init() {
+ dataBrokerService = mock(DataBrokerService.class);
+ networkGraphService = mock(NetworkGraphService.class);
+ //build source node connector ref
+ InstanceIdentifier<Nodes> srcNodesInstanceIdentifier
+ = InstanceIdentifier.builder(Nodes.class)
+ .build();
+ InstanceIdentifier<Node> srcNodeInstanceIdentifier
+ = InstanceIdentifier.builder(srcNodesInstanceIdentifier)
+ .child(Node.class, new NodeKey(new NodeId("openflow:1")))
+ .build();
+ InstanceIdentifier<NodeConnector> srcNodeConnectorInstanceIdentifier
+ = InstanceIdentifier.builder(srcNodeInstanceIdentifier)
+ .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("openflow:1:2")))
+ .build();
+ srcNodeConnectorRef = new NodeConnectorRef(srcNodeConnectorInstanceIdentifier);
+
+ //build dest node connector ref
+ InstanceIdentifier<Nodes> nodesInstanceIdentifier
+ = InstanceIdentifier.builder(Nodes.class)
+ .build();
+ InstanceIdentifier<Node> nodeInstanceIdentifier
+ = InstanceIdentifier.builder(nodesInstanceIdentifier)
+ .child(Node.class, new NodeKey(new NodeId("openflow:2")))
+ .build();
+ InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
+ = InstanceIdentifier.builder(nodeInstanceIdentifier)
+ .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("openflow:2:2")))
+ .build();
+ destNodeConnectorRef = new NodeConnectorRef(nodeConnectorInstanceIdentifier);
+ destMacAddress = new MacAddress("00:0a:95:9d:68:16");
+ srcMacAddress = new MacAddress("00:0a:95:8c:97:24");
+ dataModificationTransaction = mock(DataModificationTransaction.class);
+ when(dataBrokerService.beginTransaction()).thenReturn(dataModificationTransaction);
+ }
+
+ @Test
+ public void testFlowWriterServiceImpl_NPEWhenDataBrokerServiceIsNull() throws Exception {
+ try {
+ new FlowWriterServiceImpl(null, networkGraphService);
+ fail("Expected null pointer exception.");
+ } catch(NullPointerException npe) {
+ assertEquals("dataBrokerService should not be null.", npe.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddMacToMacFlow_NPEWhenNullSourceMacDestMacAndNodeConnectorRef() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ try {
+ flowWriterService.addMacToMacFlow(null, null, null);
+ fail("Expected null pointer exception.");
+ } catch(NullPointerException npe) {
+ assertEquals("Destination mac address should not be null.", npe.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddMacToMacFlow_NPEWhenSourceMacNullMac() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ try {
+ flowWriterService.addMacToMacFlow(null, null, destNodeConnectorRef);
+ fail("Expected null pointer exception.");
+ } catch(NullPointerException npe) {
+ assertEquals("Destination mac address should not be null.", npe.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddMacToMacFlow_NPEWhenNullSourceMacNodeConnectorRef() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ try {
+ flowWriterService.addMacToMacFlow(null, destMacAddress, null);
+ fail("Expected null pointer exception.");
+ } catch(NullPointerException npe) {
+ assertEquals("Destination port should not be null.", npe.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddMacToMacFlow_WhenNullSourceMac() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ flowWriterService.addMacToMacFlow(null, destMacAddress, destNodeConnectorRef);
+ verify(dataBrokerService, times(1)).beginTransaction();
+ verify(dataModificationTransaction, times(1)).commit();
+ }
+
+ @Test
+ public void testAddMacToMacFlow_WhenSrcAndDestMacAreSame() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ flowWriterService.addMacToMacFlow(new MacAddress(destMacAddress.getValue()), destMacAddress, destNodeConnectorRef);
+ verify(dataBrokerService, never()).beginTransaction();
+ verify(dataModificationTransaction, never()).commit();
+
+ }
+
+ @Test
+ public void testAddMacToMacFlow_SunnyDay() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ flowWriterService.addMacToMacFlow(srcMacAddress, destMacAddress, destNodeConnectorRef);
+ verify(dataBrokerService, times(1)).beginTransaction();
+ verify(dataModificationTransaction, times(1)).commit();
+ }
+
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.topology;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Destination;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Source;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static junit.framework.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ */
+public class NetworkGraphDijkstraTest {
+ Link link1, link2, link3, link4, link5, link6, link7, link8, link9, link10,link11,link12;
+ Destination dest1, dest2, dest3, dest4, dest5, dest6,dest7,dest8,dest9,dest10,dest11,dest12;
+ Source src1, src2, src3, src4, src5, src6,src7,src8,src9,src10,src11,src12;
+ NodeId nodeId1 = new NodeId("openflow:1");
+ NodeId nodeId2 = new NodeId("openflow:2");
+ NodeId nodeId3 = new NodeId("openflow:3");
+ NodeId nodeId4 = new NodeId("openflow:4");
+ NodeId nodeId5 = new NodeId("openflow:5");
+ NodeId nodeId6 = new NodeId("openflow:6");
+ NodeId nodeId7 = new NodeId("openflow:7");
+ List<Link> links = new ArrayList<>();
+
+ @Before
+ public void init() {
+ link1 = mock(Link.class);
+ link2 = mock(Link.class);
+ link3 = mock(Link.class);
+ link4 = mock(Link.class);
+ link5 = mock(Link.class);
+ link6 = mock(Link.class);
+ link7 = mock(Link.class);
+ link8 = mock(Link.class);
+ link9 = mock(Link.class);
+ link10 = mock(Link.class);
+ link11 = mock(Link.class);
+ link12 = mock(Link.class);
+ dest1 = mock(Destination.class);
+ dest2 = mock(Destination.class);
+ dest3 = mock(Destination.class);
+ dest4 = mock(Destination.class);
+ dest5 = mock(Destination.class);
+ dest6 = mock(Destination.class);
+ dest7 = mock(Destination.class);
+ dest8 = mock(Destination.class);
+ dest9 = mock(Destination.class);
+ dest10 = mock(Destination.class);
+ dest11 = mock(Destination.class);
+ dest12 = mock(Destination.class);
+ src1 = mock(Source.class);
+ src2 = mock(Source.class);
+ src3 = mock(Source.class);
+ src4 = mock(Source.class);
+ src5 = mock(Source.class);
+ src6 = mock(Source.class);
+ src7 = mock(Source.class);
+ src8 = mock(Source.class);
+ src9 = mock(Source.class);
+ src10 = mock(Source.class);
+ src11 = mock(Source.class);
+ src12 = mock(Source.class);
+ when(link1.getSource()).thenReturn(src1);
+ when(link2.getSource()).thenReturn(src2);
+ when(link3.getSource()).thenReturn(src3);
+ when(link4.getSource()).thenReturn(src4);
+ when(link5.getSource()).thenReturn(src5);
+ when(link6.getSource()).thenReturn(src6);
+ when(link7.getSource()).thenReturn(src7);
+ when(link8.getSource()).thenReturn(src8);
+ when(link9.getSource()).thenReturn(src9);
+ when(link10.getSource()).thenReturn(src10);
+ when(link11.getSource()).thenReturn(src11);
+ when(link12.getSource()).thenReturn(src12);
+ when(link1.getDestination()).thenReturn(dest1);
+ when(link2.getDestination()).thenReturn(dest2);
+ when(link3.getDestination()).thenReturn(dest3);
+ when(link4.getDestination()).thenReturn(dest4);
+ when(link5.getDestination()).thenReturn(dest5);
+ when(link6.getDestination()).thenReturn(dest6);
+ when(link7.getDestination()).thenReturn(dest7);
+ when(link8.getDestination()).thenReturn(dest8);
+ when(link9.getDestination()).thenReturn(dest9);
+ when(link10.getDestination()).thenReturn(dest10);
+ when(link11.getDestination()).thenReturn(dest11);
+ when(link12.getDestination()).thenReturn(dest12);
+ when(src1.getSourceNode()).thenReturn(nodeId1);
+ when(dest1.getDestNode()).thenReturn(nodeId2);
+ when(src2.getSourceNode()).thenReturn(nodeId2);
+ when(dest2.getDestNode()).thenReturn(nodeId1);
+ when(src3.getSourceNode()).thenReturn(nodeId1);
+ when(dest3.getDestNode()).thenReturn(nodeId3);
+ when(src4.getSourceNode()).thenReturn(nodeId3);
+ when(dest4.getDestNode()).thenReturn(nodeId1);
+ when(src5.getSourceNode()).thenReturn(nodeId2);
+ when(dest5.getDestNode()).thenReturn(nodeId4);
+ when(src6.getSourceNode()).thenReturn(nodeId4);
+ when(dest6.getDestNode()).thenReturn(nodeId2);
+ when(src7.getSourceNode()).thenReturn(nodeId2);
+ when(dest7.getDestNode()).thenReturn(nodeId5);
+ when(src8.getSourceNode()).thenReturn(nodeId5);
+ when(dest8.getDestNode()).thenReturn(nodeId2);
+ when(src9.getSourceNode()).thenReturn(nodeId6);
+ when(dest9.getDestNode()).thenReturn(nodeId3);
+ when(src10.getSourceNode()).thenReturn(nodeId3);
+ when(dest10.getDestNode()).thenReturn(nodeId6);
+ when(src11.getSourceNode()).thenReturn(nodeId7);
+ when(dest11.getDestNode()).thenReturn(nodeId3);
+ when(src12.getSourceNode()).thenReturn(nodeId3);
+ when(dest12.getDestNode()).thenReturn(nodeId7);
+ links.add(link1);
+ links.add(link2);
+ links.add(link3);
+ links.add(link4);
+ links.add(link5);
+ links.add(link6);
+ links.add(link7);
+ links.add(link8);
+ links.add(link9);
+ links.add(link10);
+ links.add(link11);
+ links.add(link12);
+
+ }
+
+ @Test
+ public void testAddLinksAndGetPath() throws Exception {
+ NetworkGraphService networkGraphService = new NetworkGraphDijkstra();
+ networkGraphService.addLinks(links);
+ List<Link> path = networkGraphService.getPath(nodeId2, nodeId3);
+ assertEquals("path size is not as expected.", 2, path.size());
+ assertEquals("link source is not as expected.", nodeId2, path.get(0).getSource().getSourceNode());
+ assertEquals("link destination is not as expected.", nodeId1, path.get(0).getDestination().getDestNode());
+ path = networkGraphService.getPath(nodeId3, nodeId2);
+ assertEquals("path size is not as expected.", 2, path.size());
+ assertEquals("link source is not as expected.", nodeId3, path.get(0).getSource().getSourceNode());
+ assertEquals("link destination is not as expected.", nodeId1, path.get(0).getDestination().getDestNode());
+
+ path = networkGraphService.getPath(nodeId4, nodeId6);
+ assertEquals("path size is not as expected.", 4, path.size());
+ assertEquals("link source is not as expected.", nodeId4, path.get(0).getSource().getSourceNode());
+ assertEquals("link destination is not as expected.", nodeId2, path.get(0).getDestination().getDestNode());
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sample.l2switch.md.topology;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ */
+public class TopologyLinkDataChangeHandlerTest {
+ NetworkGraphService networkGraphService;
+ DataBrokerService dataBrokerService;
+ DataChangeEvent dataChangeEvent;
+ Topology topology;
+ Link link;
+
+ @Before
+ public void init() {
+ networkGraphService = mock(NetworkGraphService.class);
+ dataBrokerService = mock(DataBrokerService.class);
+ dataChangeEvent = mock(DataChangeEvent.class);
+ link = mock(Link.class);
+ topology = mock(Topology.class);
+ }
+
+ @Test
+ public void testOnDataChange() throws Exception {
+ TopologyLinkDataChangeHandler topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataBrokerService, networkGraphService, 2);
+ Map<InstanceIdentifier<?>, DataObject> original = new HashMap<InstanceIdentifier<?>, DataObject>();
+ InstanceIdentifier<?> instanceIdentifier = InstanceIdentifierUtils.generateTopologyInstanceIdentifier("flow:1");
+ DataObject dataObject = mock(DataObject.class);
+ Map<InstanceIdentifier<?>, DataObject> updated = new HashMap<InstanceIdentifier<?>, DataObject>();
+ updated.put(instanceIdentifier, dataObject);
+ when(dataChangeEvent.getUpdatedOperationalData()).thenReturn(updated);
+ when(dataChangeEvent.getOriginalOperationalData()).thenReturn(original);
+ List<Link> links = new ArrayList<>();
+ links.add(link);
+ when(dataBrokerService.readOperationalData(instanceIdentifier)).thenReturn(topology);
+ when(topology.getLink()).thenReturn(links);
+
+ topologyLinkDataChangeHandler.onDataChanged(dataChangeEvent);
+ Thread.sleep(2100);
+ verify(networkGraphService, times(1)).addLinks(links);
+ }
+}
--- /dev/null
+<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/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>sal-samples</artifactId>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <version>1.1-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
+ <artifactId>l2switch-model</artifactId>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Import-Package>org.opendaylight.yangtools.yang.binding.annotations, *</Import-Package>
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <version>${yangtools.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>src/main/yang</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ target/generated-sources/sal
+ </outputBaseDir>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>target/site/models</outputBaseDir>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.yang.wadl.generator.maven.WadlGenerator</codeGeneratorClass>
+ <outputBaseDir>target/site/models</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>maven-sal-api-gen-plugin</artifactId>
+ <version>${yangtools.version}</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ <version>${yangtools.version}</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-yang-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-inventory</artifactId>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+module l2-address-tracker {
+ yang-version 1;
+ namespace "urn:opendaylight:l2-address-tracker";
+ prefix l2-address-tracker;
+
+ import ietf-yang-types {
+ prefix yang;
+ revision-date 2010-09-24;
+ }
+ import opendaylight-inventory {
+ prefix inv;
+ revision-date 2013-08-19;
+ }
+
+ organization "Cisco Systems Inc";
+ contact
+ "Alex Fan <alefan@cisco.com>";
+ description
+ "YANG version of the L2 Address Tracker Data Model";
+
+ revision 2014-04-02 {
+ description
+ "L2 Address Tracker module draft.";
+ }
+
+ grouping l2-address {
+ leaf mac {
+ type yang:mac-address;
+ mandatory true;
+ description
+ "the mac address of the host.";
+ }
+ leaf node-connector-ref {
+ type inv:node-connector-ref;
+ }
+ }
+
+ container l2-addresses {
+ config false;
+ list l2-address {
+ key "mac";
+ uses l2-address;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+
+ <artifactId>l2switch.aggregator</artifactId>
+ <groupId>org.opendaylight.controller.samples.l2switch</groupId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>model</module>
+ <module>implementation</module>
+ </modules>
+
+</project>
<module>toaster</module>
<module>toaster-consumer</module>
<module>toaster-provider</module>
+ <module>l2switch</module>
</modules>
<profiles>