--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>arphandler</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.opendaylight.controller.sal.core,
+ org.opendaylight.controller.sal.utils,
+ org.opendaylight.controller.sal.packet,
+ org.opendaylight.controller.switchmanager,
+ org.opendaylight.controller.hosttracker,
+ org.opendaylight.controller.hosttracker.hostAware,
+ org.apache.felix.dm,
+ org.osgi.service.component,
+ org.slf4j
+ </Import-Package>
+ <Bundle-Activator>
+ org.opendaylight.controller.arphandler.internal.Activator
+ </Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>switchmanager</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>hosttracker</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.arphandler.internal;
+
+import java.util.Hashtable;
+import java.util.Dictionary;
+import org.apache.felix.dm.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.hosttracker.IfHostListener;
+import org.opendaylight.controller.hosttracker.IfIptoHost;
+import org.opendaylight.controller.hosttracker.hostAware.IHostFinder;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+
+public class Activator extends ComponentActivatorAbstractBase {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(Activator.class);
+
+ /**
+ * Function called when the activator starts just after some
+ * initializations are done by the
+ * ComponentActivatorAbstractBase.
+ *
+ */
+ public void init() {
+
+ }
+
+ /**
+ * Function called when the activator stops just before the
+ * cleanup done by ComponentActivatorAbstractBase
+ *
+ */
+ public void destroy() {
+
+ }
+
+ /**
+ * Function that is used to communicate to dependency manager the
+ * list of known implementations for services inside a container
+ *
+ *
+ * @return An array containing all the CLASS objects that will be
+ * instantiated in order to get an fully working implementation
+ * Object
+ */
+ public Object[] getImplementations() {
+ Object[] res = { ArpHandler.class };
+ return res;
+ }
+
+ /**
+ * Function that is called when configuration of the dependencies
+ * is required.
+ *
+ * @param c dependency manager Component object, used for
+ * configuring the dependencies exported and imported
+ * @param imp Implementation class that is being configured,
+ * needed as long as the same routine can configure multiple
+ * implementations
+ * @param containerName The containerName being configured, this allow
+ * also optional per-container different behavior if needed, usually
+ * should not be the case though.
+ */
+ public void configureInstance(Component c, Object imp, String containerName) {
+ if (imp.equals(ArpHandler.class)) {
+ // export the service
+ Dictionary<String, String> props = new Hashtable<String, String>();
+ props.put("salListenerName", "arphandler");
+ c.setInterface(new String[] { IHostFinder.class.getName(),
+ IListenDataPacket.class.getName() }, props);
+
+ c.add(createContainerServiceDependency(containerName).setService(
+ ISwitchManager.class).setCallbacks("setSwitchManager",
+ "unsetSwitchManager").setRequired(true));
+
+ c.add(createContainerServiceDependency(containerName).setService(
+ IDataPacketService.class).setCallbacks(
+ "setDataPacketService", "unsetDataPacketService")
+ .setRequired(true));
+
+ // the Host Listener is optional
+ c.add(createContainerServiceDependency(containerName).setService(
+ IfHostListener.class).setCallbacks("setHostListener",
+ "unsetHostListener").setRequired(false));
+
+ // the IfIptoHost is a required dependency
+ c.add(createContainerServiceDependency(containerName).setService(
+ IfIptoHost.class).setCallbacks("setHostTracker",
+ "unsetHostTracker").setRequired(true));
+ }
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.arphandler.internal;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.hosttracker.IfHostListener;
+import org.opendaylight.controller.hosttracker.IfIptoHost;
+import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
+import org.opendaylight.controller.hosttracker.hostAware.IHostFinder;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.packet.ARP;
+import org.opendaylight.controller.sal.packet.BitBufferHelper;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
+import org.opendaylight.controller.sal.packet.IPv4;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.packet.PacketResult;
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.opendaylight.controller.switchmanager.Subnet;
+
+public class ArpHandler implements IHostFinder, IListenDataPacket {
+ private static final Logger logger = LoggerFactory
+ .getLogger(ArpHandler.class);
+ private IfIptoHost hostTracker = null;
+ private ISwitchManager switchManager = null;
+ private IDataPacketService dataPacketService = null;
+ private Set<IfHostListener> hostListener = Collections
+ .synchronizedSet(new HashSet<IfHostListener>());
+
+ void setHostListener(IfHostListener s) {
+ if (this.hostListener != null) {
+ this.hostListener.add(s);
+ }
+ }
+
+ void unsetHostListener(IfHostListener s) {
+ if (this.hostListener != null) {
+ this.hostListener.remove(s);
+ }
+ }
+
+ void setDataPacketService(IDataPacketService s) {
+ this.dataPacketService = s;
+ }
+
+ void unsetDataPacketService(IDataPacketService s) {
+ if (this.dataPacketService == s) {
+ this.dataPacketService = s;
+ }
+ }
+
+ public IfIptoHost getHostTracker() {
+ return hostTracker;
+ }
+
+ public void setHostTracker(IfIptoHost hostTracker) {
+ logger.debug("Setting HostTracker");
+ this.hostTracker = hostTracker;
+ }
+
+ public void unsetHostTracker(IfIptoHost s) {
+ logger.debug("UNSetting HostTracker");
+ if (this.hostTracker == s) {
+ this.hostTracker = null;
+ }
+ }
+
+ protected void sendARPReply(NodeConnector p, byte[] sMAC, InetAddress sIP,
+ byte[] tMAC, InetAddress tIP) {
+ byte[] senderIP = sIP.getAddress();
+ byte[] targetIP = tIP.getAddress();
+ ARP arp = new ARP();
+ arp.setHardwareType(ARP.HW_TYPE_ETHERNET).setProtocolType(
+ EtherTypes.IPv4.shortValue())
+ .setHardwareAddressLength((byte) 6).setProtocolAddressLength(
+ (byte) 4).setOpCode(ARP.REPLY)
+ .setSenderHardwareAddress(sMAC).setSenderProtocolAddress(
+ senderIP).setTargetHardwareAddress(tMAC)
+ .setTargetProtocolAddress(targetIP);
+
+ Ethernet ethernet = new Ethernet();
+ ethernet.setSourceMACAddress(sMAC).setDestinationMACAddress(tMAC)
+ .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
+
+ RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
+ destPkt.setOutgoingNodeConnector(p);
+
+ this.dataPacketService.transmitDataPacket(destPkt);
+ }
+
+ private boolean isBroadcastMAC(byte[] mac) {
+ if (BitBufferHelper.toNumber(mac) == 0xffffffffffffL) { //TODO: implement this in our Ethernet
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isUnicastMAC(byte[] mac) {
+ if ((BitBufferHelper.toNumber(mac) & 0x010000000000L) == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ protected void handleARPPacket(Ethernet eHeader, ARP pkt, NodeConnector p) {
+ if (pkt.getOpCode() == 0x1) {
+ logger.debug("Received ARP REQUEST Packet from NodeConnector:" + p);
+ } else {
+ logger.debug("Received ARP REPLY Packet from NodeConnector:" + p);
+ }
+ InetAddress targetIP = null;
+ try {
+ targetIP = InetAddress.getByAddress(pkt.getTargetProtocolAddress());
+ } catch (UnknownHostException e1) {
+ return;
+ }
+ InetAddress sourceIP = null;
+ try {
+ sourceIP = InetAddress.getByAddress(pkt.getSenderProtocolAddress());
+ } catch (UnknownHostException e1) {
+ return;
+ }
+ byte[] targetMAC = eHeader.getDestinationMACAddress();
+ byte[] sourceMAC = eHeader.getSourceMACAddress();
+
+ /*
+ * Sanity Check; drop ARP packets originated by the controller itself.
+ * This is to avoid continuous flooding
+ */
+ if (Arrays.equals(sourceMAC, getControllerMAC())) {
+ logger.debug(
+ "Receive the self originated packet (srcMAC {}) --> DROP",
+ HexEncode.bytesToHexString(sourceMAC));
+ return;
+ }
+
+ Subnet subnet = null;
+ if (switchManager != null) {
+ subnet = switchManager.getSubnetByNetworkAddress(sourceIP);
+ }
+ if (subnet == null) {
+ logger.debug("can't find subnet matching {}, drop packet", sourceIP
+ .toString());
+ return;
+ }
+ logger.debug("Found {} matching {}", subnet.toString(), sourceIP
+ .toString());
+ /*
+ * Make sure that the host is a legitimate member of this subnet
+ */
+ if (!subnet.hasNodeConnector(p)) {
+ logger.debug("{} showing up on {} does not belong to {}",
+ new Object[] { sourceIP.toString(), p, subnet.toString() });
+ return;
+ }
+
+ if (isUnicastMAC(sourceMAC)) {
+ // TODO For not this is only OPENFLOW but we need to fix this
+ if (p.getType().equals(
+ NodeConnector.NodeConnectorIDType.OPENFLOW)) {
+ HostNodeConnector host = null;
+ try {
+ host = new HostNodeConnector(sourceMAC, sourceIP, p, subnet
+ .getVlan());
+ } catch (ConstructionException e) {
+ return;
+ }
+ /*
+ * Learn host from the received ARP REQ/REPLY, inform
+ * Host Tracker
+ */
+ logger.debug("Inform Host tracker of new host {}", host);
+ synchronized (this.hostListener) {
+ for (IfHostListener listener : this.hostListener) {
+ listener.hostListener(host);
+ }
+ }
+ }
+ }
+ /*
+ * No further action is needed if this is a gratuitous ARP
+ */
+ if (sourceIP.equals(targetIP)) {
+ return;
+ }
+
+ /*
+ * No further action is needed if this is a ARP Reply
+ */
+ if (pkt.getOpCode() != ARP.REQUEST) {
+ return;
+ }
+ /*
+ * ARP Request Handling:
+ * If targetIP is the IP of the subnet, reply with ARP REPLY
+ * If targetIP is a known host, PROXY ARP (by sending ARP REPLY) on behalf of known target hosts.
+ * For unknown target hosts, generate and send an ARP request to ALL switches/ports using
+ * the IP address defined in the subnet as source address
+ */
+ /*
+ * Send ARP reply if target IP is gateway IP
+ */
+ if ((targetIP.equals(subnet.getNetworkAddress()))
+ && (isBroadcastMAC(targetMAC) || Arrays.equals(targetMAC,
+ getControllerMAC()))) {
+ sendARPReply(p, getControllerMAC(), targetIP, pkt
+ .getSenderHardwareAddress(), sourceIP);
+ return;
+ }
+
+ /*
+ * unknown host, initiate ARP request
+ */
+ HostNodeConnector host = hostTracker.hostQuery(targetIP);
+ if (host == null) {
+ sendBcastARPRequest(targetIP, subnet);
+ return;
+ }
+ /*
+ * Known target host, send ARP REPLY
+ * make sure that targetMAC matches the host's MAC if it is not broadcastMAC
+ */
+ if (isBroadcastMAC(targetMAC)
+ || Arrays.equals(host.getDataLayerAddressBytes(), targetMAC)) {
+ sendARPReply(p, host.getDataLayerAddressBytes(), host
+ .getNetworkAddress(), pkt.getSenderHardwareAddress(),
+ sourceIP);
+ return;
+ } else {
+ /*
+ * target target MAC has been changed. For now, discard it.
+ * TODO: We may need to send unicast ARP REQUEST on behalf of the
+ * target back to the sender to trigger the sender to
+ * update its table
+ */
+ return;
+ }
+ }
+
+ /*
+ * Send a broadcast ARP Request to the switch/ ports using
+ * the networkAddress of the subnet as sender IP
+ * the controller's MAC as sender MAC
+ * the targetIP as the target Network Address
+ */
+ protected void sendBcastARPRequest(InetAddress targetIP, Subnet subnet) {
+ Set<NodeConnector> nodeConnectors;
+ if (subnet.isFlatLayer2()) {
+ nodeConnectors = new HashSet<NodeConnector>();
+ for (Node n : this.switchManager.getNodes()) {
+ nodeConnectors.addAll(this.switchManager
+ .getUpNodeConnectors(n));
+ }
+ } else {
+ nodeConnectors = subnet.getNodeConnectors();
+ }
+ for (NodeConnector p : nodeConnectors) {
+ ARP arp = new ARP();
+ byte[] senderIP = subnet.getNetworkAddress().getAddress();
+ byte[] targetIPB = targetIP.getAddress();
+ arp.setHardwareType(ARP.HW_TYPE_ETHERNET).setProtocolType(
+ EtherTypes.IPv4.shortValue()).setHardwareAddressLength(
+ (byte) 6).setProtocolAddressLength((byte) 4).setOpCode(
+ ARP.REQUEST).setSenderHardwareAddress(getControllerMAC())
+ .setSenderProtocolAddress(senderIP)
+ .setTargetHardwareAddress(
+ new byte[] { (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 0 })
+ .setTargetProtocolAddress(targetIPB);
+
+ Ethernet ethernet = new Ethernet();
+ ethernet.setSourceMACAddress(getControllerMAC())
+ .setDestinationMACAddress(
+ new byte[] { (byte) -1, (byte) -1, (byte) -1,
+ (byte) -1, (byte) -1, (byte) -1 })
+ .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp);
+
+ // TODO For now send port-by-port, see how to optimize to
+ // send to a bunch of port on the same node in a shoot
+ RawPacket destPkt = this.dataPacketService
+ .encodeDataPacket(ethernet);
+ destPkt.setOutgoingNodeConnector(p);
+
+ this.dataPacketService.transmitDataPacket(destPkt);
+ }
+ }
+
+ /*
+ * Send a unicast ARP Request to the known host on a specific switch/port as
+ * defined in the host.
+ * The sender IP is the networkAddress of the subnet
+ * The sender MAC is the controller's MAC
+ */
+ protected void sendUcastARPRequest(HostNodeConnector host, Subnet subnet) {
+ //Long swID = host.getnodeconnectornodeId();
+ //Short portID = host.getnodeconnectorportId();
+ //Node n = NodeCreator.createOFNode(swID);
+ Node n = host.getnodeconnectorNode();
+ if (n == null) {
+ logger.error("cannot send UcastARP because cannot extract node "
+ + "from HostNodeConnector:" + host);
+ return;
+ }
+ NodeConnector outPort = host.getnodeConnector();
+ if (outPort == null) {
+ logger.error("cannot send UcastARP because cannot extract "
+ + "outPort from HostNodeConnector:" + host);
+ return;
+ }
+
+ byte[] senderIP = subnet.getNetworkAddress().getAddress();
+ byte[] targetIP = host.getNetworkAddress().getAddress();
+ byte[] targetMAC = host.getDataLayerAddressBytes();
+ ARP arp = new ARP();
+ arp.setHardwareType(ARP.HW_TYPE_ETHERNET).setProtocolType(
+ EtherTypes.IPv4.shortValue())
+ .setHardwareAddressLength((byte) 6).setProtocolAddressLength(
+ (byte) 4).setOpCode(ARP.REQUEST)
+ .setSenderHardwareAddress(getControllerMAC())
+ .setSenderProtocolAddress(senderIP).setTargetHardwareAddress(
+ targetMAC).setTargetProtocolAddress(targetIP);
+
+ Ethernet ethernet = new Ethernet();
+ ethernet.setSourceMACAddress(getControllerMAC())
+ .setDestinationMACAddress(targetMAC).setEtherType(
+ EtherTypes.ARP.shortValue()).setPayload(arp);
+
+ RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet);
+ destPkt.setOutgoingNodeConnector(outPort);
+
+ this.dataPacketService.transmitDataPacket(destPkt);
+ }
+
+ public void find(InetAddress networkAddress) {
+ logger.debug("Received find IP {}", networkAddress.toString());
+
+ Subnet subnet = null;
+ if (switchManager != null) {
+ subnet = switchManager.getSubnetByNetworkAddress(networkAddress);
+ }
+ if (subnet == null) {
+ logger.debug("can't find subnet matching IP {}", networkAddress
+ .toString());
+ return;
+ }
+ logger.debug("found subnet {}", subnet.toString());
+
+ // send a broadcast ARP Request to this interface
+ sendBcastARPRequest(networkAddress, subnet);
+ }
+
+ /*
+ * Probe the host by sending a unicast ARP Request to the host
+ */
+ public void probe(HostNodeConnector host) {
+ logger.debug("Received probe host {}", host);
+
+ Subnet subnet = null;
+ if (switchManager != null) {
+ subnet = switchManager.getSubnetByNetworkAddress(host
+ .getNetworkAddress());
+ }
+ if (subnet == null) {
+ logger.debug("can't find subnet matching {}", host
+ .getNetworkAddress().toString());
+ return;
+ }
+ sendUcastARPRequest(host, subnet);
+ }
+
+ /*
+ * An IP packet is punted to the controller, this means that the
+ * destination host is not known to the controller.
+ * Need to discover it by sending a Broadcast ARP Request
+ */
+ protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p) {
+ InetAddress dIP = null;
+ try {
+ dIP = InetAddress.getByAddress(NetUtils.intToByteArray4(pkt
+ .getDestinationAddress()));
+ } catch (UnknownHostException e1) {
+ return;
+ }
+
+ Subnet subnet = null;
+ if (switchManager != null) {
+ subnet = switchManager.getSubnetByNetworkAddress(dIP);
+ }
+ if (subnet == null) {
+ logger.debug("can't find subnet matching {}, drop packet", dIP
+ .toString());
+ return;
+ }
+ logger.debug("Found {} matching {}", subnet.toString(), dIP.toString());
+ /*
+ * unknown destination host, initiate ARP request
+ */
+ sendBcastARPRequest(dIP, subnet);
+ return;
+ }
+
+ public byte[] getControllerMAC() {
+ if (switchManager == null) {
+ return null;
+ }
+ return switchManager.getControllerMAC();
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init() {
+ }
+
+ /**
+ * Function called by the dependency manager when at least one
+ * dependency become unsatisfied or when the component is shutting
+ * down because for example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called
+ * and after the services provided by the class are registered in
+ * the service registry
+ *
+ */
+ void start() {
+ }
+
+ /**
+ * Function called by the dependency manager before the services
+ * exported by the component are unregistered, this will be
+ * followed by a "destroy ()" calls
+ *
+ */
+ void stop() {
+ }
+
+ void setSwitchManager(ISwitchManager s) {
+ logger.debug("SwitchManager set");
+ this.switchManager = s;
+ }
+
+ void unsetSwitchManager(ISwitchManager s) {
+ if (this.switchManager == s) {
+ logger.debug("SwitchManager removed!");
+ this.switchManager = null;
+ }
+ }
+
+ @Override
+ public PacketResult receiveDataPacket(RawPacket inPkt) {
+ if (inPkt == null) {
+ return PacketResult.IGNORED;
+ }
+ logger
+ .trace("Received a frame of size:"
+ + inPkt.getPacketData().length);
+ Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
+ if (formattedPak instanceof Ethernet) {
+ Object nextPak = formattedPak.getPayload();
+ if (nextPak instanceof IPv4) {
+ handlePuntedIPPacket((IPv4) nextPak, inPkt
+ .getIncomingNodeConnector());
+ logger.trace("Handled IP packet");
+ }
+ if (nextPak instanceof ARP) {
+ handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt
+ .getIncomingNodeConnector());
+ logger.trace("Handled ARP packet");
+ }
+ }
+ return PacketResult.IGNORED;
+ }
+}
--- /dev/null
+\r
+/*\r
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.controller.arphandler.internal;\r
+\r
+\r
+import org.junit.Assert;\r
+import org.junit.Test;\r
+import junit.framework.TestCase;\r
+\r
+import org.opendaylight.controller.hosttracker.IfIptoHost;\r
+import org.opendaylight.controller.hosttracker.HostTracker;\r
+\r
+import org.opendaylight.controller.switchmanager.ISwitchManager;\r
+import org.opendaylight.controller.switchmanager.internal.SwitchManagerImpl;\r
+\r
+\r
+public class ArphandlerTest extends TestCase {\r
+ \r
+ @Test\r
+ public void testArphandlerCreation() {\r
+ \r
+ ArpHandler ah = null;\r
+ ah = new ArpHandler();\r
+ Assert.assertTrue(ah != null);\r
+ \r
+ HostTracker hostTracker = null;\r
+ hostTracker = new HostTracker();\r
+ ah.setHostTracker(hostTracker);\r
+ IfIptoHost ht= ah.getHostTracker();\r
+ Assert.assertTrue(ht.equals(hostTracker));\r
+ ah.unsetHostTracker(hostTracker);\r
+ ht= ah.getHostTracker();\r
+ Assert.assertTrue(ht == null);\r
+ \r
+ ah.setHostListener(hostTracker);\r
+ ah.unsetHostListener(hostTracker);\r
+ \r
+ ISwitchManager swManager = new SwitchManagerImpl();\r
+ ah.setSwitchManager(swManager);\r
+ ah.unsetSwitchManager(swManager);\r
+ \r
+ }\r
+\r
+\r
+}\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>configuration</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.opendaylight.controller.sal.utils,
+ org.apache.commons.lang3.builder
+ </Import-Package>
+ <Export-Package>
+ org.opendaylight.controller.configuration
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration;
+
+
+/**
+ * Listener Interface for receiving Configuration events.
+ *
+ *
+ */
+public interface IConfigurationAware extends IConfigurationAwareCommon {
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration;
+
+import org.opendaylight.controller.sal.utils.Status;
+
+
+/**
+ * Listener Interface for receiving Configuration events.
+ *
+ *
+ */
+public interface IConfigurationAwareCommon {
+
+ /**
+ * Trigger from configuration component to persist the configuration state.
+ */
+ public Status saveConfiguration();
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration;
+
+
+/**
+ * Listener Interface for receiving Configuration events.
+ *
+ *
+ */
+public interface IConfigurationContainerAware extends IConfigurationAwareCommon {
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration;
+
+/**
+ * Container Manager interface
+ *
+ *
+ */
+public interface IConfigurationContainerService extends
+ IConfigurationServiceCommon {
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration;
+
+
+/**
+ * Container Manager interface
+ *
+ *
+ */
+public interface IConfigurationService extends IConfigurationServiceCommon {
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration;
+
+import org.opendaylight.controller.sal.utils.Status;
+
+/**
+ * Container Manager interface
+ *
+ *
+ */
+public interface IConfigurationServiceCommon {
+ public Status saveConfigurations();
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>configuration.implementation</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.opendaylight.controller.configuration,
+ org.opendaylight.controller.clustering.services,
+ org.opendaylight.controller.sal.utils,
+ org.opendaylight.controller.sal.core,
+ org.osgi.framework,
+ org.slf4j,
+ org.apache.felix.dm
+ </Import-Package>
+ <Export-Package>
+ </Export-Package>
+ <Bundle-Activator>
+ org.opendaylight.controller.configuration.internal.Activator
+ </Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>configuration</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration.internal;
+
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.configuration.IConfigurationAware;
+import org.opendaylight.controller.configuration.IConfigurationContainerAware;
+import org.opendaylight.controller.configuration.IConfigurationContainerService;
+import org.opendaylight.controller.configuration.IConfigurationService;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @file Activator.java
+ *
+ * @brief Component Activator for Configuration Management.
+ *
+ *
+ *
+ */
+public class Activator extends ComponentActivatorAbstractBase {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(Activator.class);
+
+ /**
+ * Function called when the activator starts just after some
+ * initializations are done by the
+ * ComponentActivatorAbstractBase.
+ *
+ */
+ public void init() {
+ }
+
+ /**
+ * Function called when the activator stops just before the
+ * cleanup done by ComponentActivatorAbstractBase
+ *
+ */
+ public void destroy() {
+ }
+
+ /**
+ * Function that is used to communicate to dependency manager the
+ * list of known implementations for services inside a container
+ *
+ *
+ * @return An array containing all the CLASS objects that will be
+ * instantiated in order to get an fully working implementation
+ * Object
+ */
+ public Object[] getImplementations() {
+ Object[] res = { ConfigurationContainerImpl.class };
+ return res;
+ }
+
+ /**
+ * Function that is called when configuration of the dependencies
+ * is required.
+ *
+ * @param c dependency manager Component object, used for
+ * configuring the dependencies exported and imported
+ * @param imp Implementation class that is being configured,
+ * needed as long as the same routine can configure multiple
+ * implementations
+ * @param containerName The containerName being configured, this allow
+ * also optional per-container different behavior if needed, usually
+ * should not be the case though.
+ */
+ public void configureInstance(Component c, Object imp, String containerName) {
+ if (imp.equals(ConfigurationContainerImpl.class)) {
+ // export the service
+ c.setInterface(new String[] {
+ IConfigurationContainerService.class.getName(),
+ IConfigurationAware.class.getName() }, null);
+
+ c.add(createContainerServiceDependency(containerName).setService(
+ IConfigurationContainerAware.class).setCallbacks(
+ "addConfigurationContainerAware",
+ "removeConfigurationContainerAware").setRequired(false));
+ }
+ }
+
+ /**
+ * Method which tells how many Global implementations are
+ * supported by the bundle. This way we can tune the number of
+ * components created. This components will be created ONLY at the
+ * time of bundle startup and will be destroyed only at time of
+ * bundle destruction, this is the major difference with the
+ * implementation retrieved via getImplementations where all of
+ * them are assumed to be in a container!
+ *
+ *
+ * @return The list of implementations the bundle will support,
+ * in Global version
+ */
+ protected Object[] getGlobalImplementations() {
+ Object[] res = { ConfigurationImpl.class };
+ return res;
+ }
+
+ /**
+ * Configure the dependency for a given instance Global
+ *
+ * @param c Component assigned for this instance, this will be
+ * what will be used for configuration
+ * @param imp implementation to be configured
+ * @param containerName container on which the configuration happens
+ */
+ protected void configureGlobalInstance(Component c, Object imp) {
+ if (imp.equals(ConfigurationImpl.class)) {
+
+ // export the service
+ c.setInterface(
+ new String[] { IConfigurationService.class.getName() },
+ null);
+
+ c.add(createServiceDependency().setService(
+ IClusterGlobalServices.class).setCallbacks(
+ "setClusterServices", "unsetClusterServices").setRequired(
+ true));
+
+ c.add(createServiceDependency().setService(
+ IConfigurationAware.class).setCallbacks(
+ "addConfigurationAware", "removeConfigurationAware")
+ .setRequired(false));
+ }
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration.internal;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.configuration.IConfigurationAware;
+import org.opendaylight.controller.configuration.IConfigurationContainerAware;
+import org.opendaylight.controller.configuration.IConfigurationContainerService;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.controller.sal.utils.Status;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @file ConfigurationImpl.java
+ *
+ * @brief Backend functionality for all Configuration related tasks.
+ *
+ *
+ */
+
+public class ConfigurationContainerImpl implements
+ IConfigurationContainerService, IConfigurationAware {
+ private static final Logger logger = LoggerFactory
+ .getLogger(ConfigurationContainerImpl.class);
+ private IClusterGlobalServices clusterServices;
+ /*
+ * Collection containing the configuration objects.
+ * This is configuration world: container names (also the map key)
+ * are maintained as they were configured by user, same case
+ */
+ private Set<IConfigurationContainerAware> configurationAwareList = (Set<IConfigurationContainerAware>) Collections
+ .synchronizedSet(new HashSet<IConfigurationContainerAware>());
+
+ public void addConfigurationContainerAware(
+ IConfigurationContainerAware configurationAware) {
+ if (!this.configurationAwareList.contains(configurationAware)) {
+ this.configurationAwareList.add(configurationAware);
+ }
+ }
+
+ public int getConfigurationAwareListSize() {
+ return this.configurationAwareList.size();
+ }
+
+ public void removeConfigurationContainerAware(
+ IConfigurationContainerAware configurationAware) {
+ this.configurationAwareList.remove(configurationAware);
+ }
+
+ public void setClusterServices(IClusterGlobalServices i) {
+ this.clusterServices = i;
+ logger.debug("IClusterServices set");
+ }
+
+ public void unsetClusterServices(IClusterGlobalServices i) {
+ if (this.clusterServices == i) {
+ this.clusterServices = null;
+ logger.debug("IClusterServices Unset");
+ }
+ }
+
+ public void init() {
+ }
+
+ public void destroy() {
+ // Clear local states
+ this.configurationAwareList.clear();
+ }
+
+ @Override
+ public Status saveConfiguration() {
+ boolean success = true;
+ for (IConfigurationContainerAware configurationAware : configurationAwareList) {
+ logger.info("Save Config triggered for "
+ + configurationAware.getClass().getSimpleName());
+
+ Status status = configurationAware.saveConfiguration();
+ if (!status.isSuccess()) {
+ success = false;
+ logger.info("Failed to save config for "
+ + configurationAware.getClass().getSimpleName());
+ }
+ }
+ if (success) {
+ return new Status(StatusCode.SUCCESS, null);
+ } else {
+ return new Status(StatusCode.INTERNALERROR,
+ "Failed to Save All Configurations");
+ }
+ }
+
+ @Override
+ public Status saveConfigurations() {
+ return saveConfiguration();
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration.internal;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.configuration.IConfigurationAware;
+import org.opendaylight.controller.configuration.IConfigurationService;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.controller.sal.utils.Status;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @file ConfigurationImpl.java
+ *
+ * @brief Backend functionality for all Configuration related tasks.
+ *
+ *
+ */
+
+public class ConfigurationImpl implements IConfigurationService {
+ private static final Logger logger = LoggerFactory
+ .getLogger(ConfigurationImpl.class);
+ private IClusterGlobalServices clusterServices;
+ /*
+ * Collection containing the configuration objects.
+ * This is configuration world: container names (also the map key)
+ * are maintained as they were configured by user, same case
+ */
+ private Set<IConfigurationAware> configurationAwareList = (Set<IConfigurationAware>) Collections
+ .synchronizedSet(new HashSet<IConfigurationAware>());
+
+
+ public int getConfigurationAwareListSize() {
+ return this.configurationAwareList.size();
+ }
+
+ public void addConfigurationAware(IConfigurationAware configurationAware) {
+ if (!this.configurationAwareList.contains(configurationAware)) {
+ this.configurationAwareList.add(configurationAware);
+ }
+ }
+
+ public void removeConfigurationAware(IConfigurationAware configurationAware) {
+ this.configurationAwareList.remove(configurationAware);
+ }
+
+ public void setClusterServices(IClusterGlobalServices i) {
+ this.clusterServices = i;
+ logger.debug("IClusterServices set");
+ }
+
+ public void unsetClusterServices(IClusterGlobalServices i) {
+ if (this.clusterServices == i) {
+ this.clusterServices = null;
+ logger.debug("IClusterServices Unset");
+ }
+ }
+
+ public void init() {
+ logger.info("ContainerManager startup....");
+ }
+
+ public void destroy() {
+ // Clear local states
+ this.configurationAwareList.clear();
+ }
+
+ @Override
+ public Status saveConfigurations() {
+ boolean success = true;
+ for (IConfigurationAware configurationAware : configurationAwareList) {
+ Status status = configurationAware.saveConfiguration();
+ if (!status.isSuccess()) {
+ success = false;
+ logger.info("Failed to save config for "
+ + configurationAware.getClass().getName());
+ }
+ }
+ if (success) {
+ return new Status(StatusCode.SUCCESS, null);
+ } else {
+ return new Status(StatusCode.INTERNALERROR,
+ "Failed to Save All Configurations");
+ }
+ }
+
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration.internal;
+
+import org.opendaylight.controller.configuration.IConfigurationAware;
+import org.opendaylight.controller.sal.utils.Status;
+
+/**
+ * @file TestConfigurationAware.java
+ *
+ * @brief Test Class to create for jUnit test cases for Configuration Implementation
+ *
+ *
+ */
+
+public class ConfigurationAwareTest implements
+IConfigurationAware {
+
+ @Override
+ public Status saveConfiguration() {
+ return null;
+ }
+
+
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration.internal;
+
+import org.opendaylight.controller.configuration.IConfigurationContainerAware;
+import org.opendaylight.controller.sal.utils.Status;
+
+/**
+ * @file TestConfigurationAware.java
+ *
+ * @brief Test Class to create for jUnit test cases for Configuration Implementation
+ *
+ *
+ */
+
+public class ConfigurationContainerAwareTest implements
+ IConfigurationContainerAware {
+
+
+
+
+ @Override
+ public Status saveConfiguration() {
+ return null;
+ }
+
+
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration.internal;
+
+import org.junit.*;
+import org.opendaylight.controller.configuration.IConfigurationContainerAware;
+
+
+
+public class ConfigurationContainerImplTest {
+
+
+ @Test
+ public void testAddRemoveSaveConfiguration() {
+
+ ConfigurationContainerImpl configurationContainerImpl = new ConfigurationContainerImpl();
+ IConfigurationContainerAware testConfigurationContainerAware = new ConfigurationContainerAwareTest();
+
+ configurationContainerImpl.addConfigurationContainerAware(testConfigurationContainerAware);
+ configurationContainerImpl.addConfigurationContainerAware(testConfigurationContainerAware);
+
+ Assert.assertEquals(1, configurationContainerImpl.getConfigurationAwareListSize());
+
+ IConfigurationContainerAware testConfigurationAware1 = new ConfigurationContainerAwareTest();
+ configurationContainerImpl.addConfigurationContainerAware(testConfigurationAware1);
+
+ Assert.assertEquals(2, configurationContainerImpl.getConfigurationAwareListSize());
+
+ IConfigurationContainerAware testConfigurationAware2 = new ConfigurationContainerAwareTest();
+ configurationContainerImpl.addConfigurationContainerAware(testConfigurationAware2);
+
+ Assert.assertEquals(3, configurationContainerImpl.getConfigurationAwareListSize());
+
+ IConfigurationContainerAware testConfigurationAware3 = new ConfigurationContainerAwareTest();
+ configurationContainerImpl.addConfigurationContainerAware(testConfigurationAware3);
+
+ Assert.assertEquals(4, configurationContainerImpl.getConfigurationAwareListSize());
+
+ configurationContainerImpl.removeConfigurationContainerAware(testConfigurationContainerAware);
+ Assert.assertEquals(3, configurationContainerImpl.getConfigurationAwareListSize());
+
+ configurationContainerImpl.removeConfigurationContainerAware(testConfigurationContainerAware);
+ Assert.assertEquals(3, configurationContainerImpl.getConfigurationAwareListSize());
+
+ configurationContainerImpl.removeConfigurationContainerAware(testConfigurationAware3);
+ Assert.assertEquals(2, configurationContainerImpl.getConfigurationAwareListSize());
+
+ configurationContainerImpl.removeConfigurationContainerAware(testConfigurationAware2);
+ Assert.assertEquals(1, configurationContainerImpl.getConfigurationAwareListSize());
+
+ configurationContainerImpl.removeConfigurationContainerAware(testConfigurationAware1);
+ Assert.assertEquals(0, configurationContainerImpl.getConfigurationAwareListSize());
+
+
+ }
+
+}
+
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.configuration.internal;
+
+import org.junit.*;
+import org.opendaylight.controller.configuration.IConfigurationAware;
+
+public class ConfigurationImplTest {
+
+
+ @Test
+ public void testAddRemoveSaveConfiguration() {
+
+ ConfigurationImpl configurationImpl = new ConfigurationImpl();
+ IConfigurationAware testConfigurationAware = new ConfigurationAwareTest();
+
+ configurationImpl.addConfigurationAware(testConfigurationAware);
+ configurationImpl.addConfigurationAware(testConfigurationAware);
+
+ Assert.assertEquals(1, configurationImpl.getConfigurationAwareListSize());
+
+ ConfigurationAwareTest testConfigurationAware1 = new ConfigurationAwareTest();
+ configurationImpl.addConfigurationAware(testConfigurationAware1);
+
+ Assert.assertEquals(2, configurationImpl.getConfigurationAwareListSize());
+
+ ConfigurationAwareTest testConfigurationAware2 = new ConfigurationAwareTest();
+ configurationImpl.addConfigurationAware(testConfigurationAware2);
+
+ Assert.assertEquals(3, configurationImpl.getConfigurationAwareListSize());
+
+ ConfigurationAwareTest testConfigurationAware3 = new ConfigurationAwareTest();
+ configurationImpl.addConfigurationAware(testConfigurationAware3);
+
+ Assert.assertEquals(4, configurationImpl.getConfigurationAwareListSize());
+
+
+ configurationImpl.removeConfigurationAware(testConfigurationAware);
+ Assert.assertEquals(3, configurationImpl.getConfigurationAwareListSize());
+
+ configurationImpl.removeConfigurationAware(testConfigurationAware);
+ Assert.assertEquals(3, configurationImpl.getConfigurationAwareListSize());
+
+ configurationImpl.removeConfigurationAware(testConfigurationAware3);
+ Assert.assertEquals(2, configurationImpl.getConfigurationAwareListSize());
+
+ configurationImpl.removeConfigurationAware(testConfigurationAware1);
+ Assert.assertEquals(1, configurationImpl.getConfigurationAwareListSize());
+
+ configurationImpl.removeConfigurationAware(testConfigurationAware2);
+ Assert.assertEquals(0, configurationImpl.getConfigurationAwareListSize());
+
+ }
+
+}
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.opendaylight.controller.sal.action,
+ org.opendaylight.controller.sal.authorization,
+ org.opendaylight.controller.sal.core,
+ org.opendaylight.controller.sal.flowprogrammer,
+ org.opendaylight.controller.sal.match,
+ org.opendaylight.controller.sal.reader,
+ org.opendaylight.controller.sal.utils,
+ org.apache.commons.lang3.builder
+ </Import-Package>
+ <Export-Package>
+ org.opendaylight.controller.containermanager
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.containermanager;
+
+import org.opendaylight.controller.sal.authorization.IResourceAuthorization;
+
+/**
+ * Container groups and container users authorizations
+ */
+public interface IContainerAuthorization extends IResourceAuthorization {
+
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.containermanager;
+
+import java.util.List;
+
+import org.opendaylight.controller.sal.utils.Status;
+
+/**
+ * Container Manager interface
+ *
+ *
+ */
+public interface IContainerManager {
+
+ /**
+ * Returns a list of Containers that currently exist.
+ *
+ * @return array of String Container names
+ */
+ public boolean hasNonDefaultContainer();
+
+ /**
+ * Returns a list of Containers that currently exist.
+ *
+ * @return array of String Container names
+ */
+ public List<String> getContainerNames();
+
+ /**
+ * Save the current container configuration to disk.
+ * TODO : REMOVE THIS FUNCTION and make Save as a service rather than the
+ * current hack of calling individual save routines.
+ *
+ * @return status code
+ */
+ public Status saveContainerConfig();
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager.implementation</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.opendaylight.controller.containermanager,
+ org.opendaylight.controller.clustering.services,
+ org.opendaylight.controller.sal.packet,
+ org.opendaylight.controller.sal.utils,
+ org.opendaylight.controller.sal.core,
+ org.opendaylight.controller.sal.action,
+ org.opendaylight.controller.sal.flowprogrammer,
+ org.opendaylight.controller.sal.match,
+ org.opendaylight.controller.sal.reader,
+ org.eclipse.osgi.framework.console,
+ org.osgi.framework,
+ org.slf4j,
+ org.apache.felix.dm
+ </Import-Package>
+ <Export-Package>
+ </Export-Package>
+ <Bundle-Activator>
+ org.opendaylight.controller.containermanager.internal.Activator
+ </Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.containermanager.internal;
+
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.containermanager.IContainerManager;
+import org.opendaylight.controller.sal.core.IContainerAware;
+import org.opendaylight.controller.sal.core.IContainer;
+import org.opendaylight.controller.sal.core.IContainerListener;
+import org.apache.felix.dm.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+
+public class Activator extends ComponentActivatorAbstractBase {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(Activator.class);
+
+ /**
+ * Function called when the activator starts just after some
+ * initializations are done by the
+ * ComponentActivatorAbstractBase.
+ *
+ */
+ public void init() {
+ }
+
+ /**
+ * Function called when the activator stops just before the
+ * cleanup done by ComponentActivatorAbstractBase
+ *
+ */
+ public void destroy() {
+ }
+
+ /**
+ * Function that is used to communicate to dependency manager the
+ * list of known implementations for services inside a container
+ *
+ *
+ * @return An array containing all the CLASS objects that will be
+ * instantiated in order to get an fully working implementation
+ * Object
+ */
+ public Object[] getImplementations() {
+ Object[] res = { ContainerImpl.class };
+ return res;
+ }
+
+ /**
+ * Function that is called when configuration of the dependencies
+ * is required.
+ *
+ * @param c dependency manager Component object, used for
+ * configuring the dependencies exported and imported
+ * @param imp Implementation class that is being configured,
+ * needed as long as the same routine can configure multiple
+ * implementations
+ * @param containerName The containerName being configured, this allow
+ * also optional per-container different behavior if needed, usually
+ * should not be the case though.
+ */
+ public void configureInstance(Component c, Object imp, String containerName) {
+ if (imp.equals(ContainerImpl.class)) {
+ // export the service
+ c.setInterface(new String[] { IContainer.class.getName() }, null);
+ }
+ }
+
+ /**
+ * Method which tells how many Global implementations are
+ * supported by the bundle. This way we can tune the number of
+ * components created. This components will be created ONLY at the
+ * time of bundle startup and will be destroyed only at time of
+ * bundle destruction, this is the major difference with the
+ * implementation retrieved via getImplementations where all of
+ * them are assumed to be in a container!
+ *
+ *
+ * @return The list of implementations the bundle will support,
+ * in Global version
+ */
+ protected Object[] getGlobalImplementations() {
+ Object[] res = { ContainerManager.class };
+ return res;
+ }
+
+ /**
+ * Configure the dependency for a given instance Global
+ *
+ * @param c Component assigned for this instance, this will be
+ * what will be used for configuration
+ * @param imp implementation to be configured
+ * @param containerName container on which the configuration happens
+ */
+ protected void configureGlobalInstance(Component c, Object imp) {
+ if (imp.equals(ContainerManager.class)) {
+
+ // export the service
+ c.setInterface(new String[] { IContainerManager.class.getName() },
+ null);
+
+ c.add(createServiceDependency().setService(
+ IClusterGlobalServices.class).setCallbacks(
+ "setClusterServices", "unsetClusterServices").setRequired(
+ true));
+
+ // Key kick-starter for container creation in each component
+ c.add(createServiceDependency().setService(IContainerAware.class)
+ .setCallbacks("setIContainerAware", "unsetIContainerAware")
+ .setRequired(false));
+
+ // Optional interface expected to be exported by the
+ // protocol plugins to setup proper filtering based on
+ // slicing events
+ c.add(createServiceDependency()
+ .setService(IContainerListener.class).setCallbacks(
+ "setIContainerListener", "unsetIContainerListener")
+ .setRequired(false));
+ }
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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
+ */
+
+/**
+ * @file ContainerImpl.java
+ *
+ * @brief Class that instantiated per-container implements the
+ * interface IContainer
+ *
+ *
+ */
+package org.opendaylight.controller.containermanager.internal;
+
+import java.util.Dictionary;
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import java.util.Set;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import java.util.List;
+import org.opendaylight.controller.sal.core.IContainer;
+
+public class ContainerImpl implements IContainer {
+ private String containerName = null;
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init(Component c) {
+ Dictionary<?, ?> props = c.getServiceProperties();
+ if (props != null) {
+ this.containerName = (String) props.get("containerName");
+ }
+ }
+
+ @Override
+ public String getName() {
+ return this.containerName;
+ }
+
+ @Override
+ public List<ContainerFlow> getContainerFlows() {
+ return null;
+ }
+
+ @Override
+ public short getTag(Node n) {
+ return (short) 0;
+ }
+
+ @Override
+ public Set<NodeConnector> getNodeConnectors() {
+ return null;
+ }
+
+ @Override
+ public Set<Node> getNodes() {
+ return null;
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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
+ */
+
+/**
+ * @file ContainerManager.java
+ *
+ * @brief Manage one or many Containers
+ *
+ *
+ */
+package org.opendaylight.controller.containermanager.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.containermanager.IContainerManager;
+import org.opendaylight.controller.sal.core.IContainerAware;
+import org.opendaylight.controller.sal.core.IContainerListener;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.Status;
+
+public class ContainerManager implements IContainerManager {
+ private static final Logger logger = LoggerFactory
+ .getLogger(ContainerManager.class);
+ private IClusterGlobalServices clusterServices;
+ /*
+ * Collection containing the configuration objects.
+ * This is configuration world: container names (also the map key)
+ * are maintained as they were configured by user, same case
+ */
+ private Set<IContainerAware> iContainerAware = (Set<IContainerAware>) Collections
+ .synchronizedSet(new HashSet<IContainerAware>());
+ private Set<IContainerListener> iContainerListener = Collections
+ .synchronizedSet(new HashSet<IContainerListener>());
+
+ void setIContainerListener(IContainerListener s) {
+ if (this.iContainerListener != null) {
+ this.iContainerListener.add(s);
+ }
+ }
+
+ void unsetIContainerListener(IContainerListener s) {
+ if (this.iContainerListener != null) {
+ this.iContainerListener.remove(s);
+ }
+ }
+
+ public void setIContainerAware(IContainerAware iContainerAware) {
+ if (!this.iContainerAware.contains(iContainerAware)) {
+ this.iContainerAware.add(iContainerAware);
+ // Now call the container creation for all the known containers so
+ // far
+ List<String> containerDB = getContainerNames();
+ if (containerDB != null) {
+ for (int i = 0; i < containerDB.size(); i++) {
+ iContainerAware.containerCreate(containerDB.get(i));
+ }
+ }
+ }
+ }
+
+ public void unsetIContainerAware(IContainerAware iContainerAware) {
+ this.iContainerAware.remove(iContainerAware);
+ // There is no need to do cleanup of the component when
+ // unregister because it will be taken care by the Containerd
+ // component itself
+ }
+
+ public void setClusterServices(IClusterGlobalServices i) {
+ this.clusterServices = i;
+ logger.debug("IClusterServices set");
+ }
+
+ public void unsetClusterServices(IClusterGlobalServices i) {
+ if (this.clusterServices == i) {
+ this.clusterServices = null;
+ logger.debug("IClusterServices Unset");
+ }
+ }
+
+ public void init() {
+ logger.info("ContainerManager startup....");
+ }
+
+ public void destroy() {
+ // Clear local states
+ this.iContainerAware.clear();
+ this.iContainerListener.clear();
+
+ logger.info("ContainerManager Shutdown....");
+ }
+
+ @Override
+ public List<String> getContainerNames() {
+ /*
+ * Return container names as they were configured by user (case sensitive)
+ * along with the default container
+ */
+ List<String> containerNameList = new ArrayList<String>();
+ containerNameList.add(GlobalConstants.DEFAULT.toString());
+ return containerNameList;
+ }
+
+ @Override
+ public boolean hasNonDefaultContainer() {
+ return false;
+ }
+
+ @Override
+ public Status saveContainerConfig() {
+ return null;
+ }
+
+}
--- /dev/null
+\r
+/*\r
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.controller.containermanager.internal;\r
+\r
+import static org.junit.Assert.*;\r
+\r
+import java.util.Hashtable;\r
+\r
+import org.apache.felix.dm.impl.ComponentImpl;\r
+import org.junit.Test;\r
+import org.opendaylight.controller.sal.core.Node;\r
+import org.opendaylight.controller.sal.utils.NodeCreator;\r
+\r
+public class ContainerImplTest {\r
+\r
+ @Test\r
+ public void test() {\r
+ \r
+ ContainerImpl container1 = new ContainerImpl();\r
+ \r
+ //Create Component for init\r
+ ComponentImpl component1 = new ComponentImpl(null, null, null);\r
+ component1.setInterface("serviceTestName", null);\r
+\r
+ //container1 does not have name yet\r
+ container1.init(component1);\r
+ assertNull(container1.getName());\r
+ \r
+ //Sets container1 name to TestName\r
+ Hashtable<String, String> properties = new Hashtable<String, String>();\r
+ properties.put("dummyKey", "dummyValue");\r
+ properties.put("containerName", "TestName");\r
+ component1.setInterface("serviceTestName", properties);\r
+\r
+ container1.init(component1);\r
+ assertEquals("TestName", container1.getName());\r
+ \r
+ //getContainerFlows always returns null for now\r
+ assertNull(container1.getContainerFlows());\r
+ \r
+ //getTag always returns 0 for now\r
+ Node n = NodeCreator.createOFNode(1L);\r
+ assertEquals(0, container1.getTag(n));\r
+ \r
+ //getNodeConnectors always returns null for now\r
+ assertNull(container1.getNodeConnectors());\r
+ \r
+ //getNodes always returns null for now\r
+ assertNull(container1.getNodes());\r
+ \r
+ }\r
+\r
+}\r
--- /dev/null
+\r
+/*\r
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.controller.containermanager.internal;\r
+\r
+import static org.junit.Assert.*;\r
+\r
+import java.util.ArrayList;\r
+\r
+import org.junit.Test;\r
+import org.opendaylight.controller.sal.utils.GlobalConstants;\r
+\r
+public class ContainerManagerTest {\r
+\r
+ @Test\r
+ public void test() {\r
+ ContainerManager cm = new ContainerManager();\r
+ \r
+ cm.init();\r
+ \r
+ ArrayList<String> names = (ArrayList<String>) cm.getContainerNames();\r
+ assertEquals(1, names.size());\r
+ assertEquals(GlobalConstants.DEFAULT.toString(), names.get(0));\r
+ \r
+ assertFalse(cm.hasNonDefaultContainer());\r
+ assertNull(cm.saveContainerConfig());\r
+ \r
+ cm.destroy();\r
+\r
+ }\r
+\r
+}\r
<version>0.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
+ <module>../../forwarding/staticrouting</module>
<module>../../clustering/services</module>
<module>../../clustering/services_implementation</module>
<module>../../clustering/stub</module>
<module>../../clustering/test</module>
+ <module>../../configuration/api</module>
+ <module>../../configuration/implementation</module>
+ <module>../../routing/dijkstra_implementation</module>
+ <module>../../arphandler</module>
+ <module>../../forwardingrulesmanager</module>
+ <module>../../hosttracker</module>
+ <module>../../containermanager/api</module>
+ <module>../../containermanager/implementation</module>
+ <module>../../switchmanager</module>
+ <module>../../statisticsmanager</module>
+ <module>../../topologymanager</module>
+ <module>../../usermanager</module>
<module>../../../third-party/openflowj</module>
<module>../../../third-party/net.sf.jung2</module>
<module>../../../third-party/jersey-servlet</module>
<!-- SAL bundles -->
<module>../../sal/api</module>
<module>../../sal/implementation</module>
+
+ <!-- Web bundles -->
+ <module>../../web/root</module>
+ <module>../../web/flows</module>
+ <module>../../web/devices</module>
+ <module>../../web/troubleshoot</module>
+ <module>../../web/topology</module>
+
+ <!-- Northbound bundles -->
+ <module>../../northbound/commons</module>
+ <module>../../northbound/topology</module>
+ <module>../../northbound/staticrouting</module>
+ <module>../../northbound/statistics</module>
+ <module>../../northbound/flowprogrammer</module>
+ <module>../../northbound/hosttracker</module>
+ <module>../../northbound/subnets</module>
+ <module>../../northbound/switchmanager</module>
<!-- Debug and logging -->
<module>../../logging/bridge</module>
+
+ <!-- Southbound bundles -->
+ <module>../../protocol_plugins/openflow</module>
+
+ <!-- Samples -->
+ <module>../../samples/simpleforwarding</module>
</modules>
<build>
<packaging>pom</packaging>
<modules>
<module>../../clustering/services</module>
+ <module>../../containermanager/api</module>
<module>../../sal/api</module>
+
+ <!-- Northbound common hooks -->
+ <module>../../northbound/commons</module>
</modules>
<build>
--- /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>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>forwarding.staticrouting</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.opendaylight.controller.sal.utils,
+ org.opendaylight.controller.sal.core,
+ org.opendaylight.controller.configuration,
+ org.opendaylight.controller.forwardingrulesmanager,
+ org.opendaylight.controller.hosttracker,
+ org.opendaylight.controller.hosttracker.hostAware,
+ org.opendaylight.controller.clustering.services,
+ org.opendaylight.controller.sal.packet,
+ org.opendaylight.controller.sal.routing,
+ org.opendaylight.controller.topologymanager,
+ org.eclipse.osgi.framework.console,
+ org.osgi.framework,
+ org.slf4j,
+ org.apache.felix.dm,
+ org.apache.commons.lang3.builder
+ </Import-Package>
+ <Export-Package>
+ org.opendaylight.controller.forwarding.staticrouting
+ </Export-Package>
+ <Bundle-Activator>
+ org.opendaylight.controller.forwarding.staticrouting.internal.Activator
+ </Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>topologymanager</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>forwardingrulesmanager</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>hosttracker</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>configuration</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.forwarding.staticrouting;
+
+import java.net.InetAddress;
+import java.util.concurrent.ConcurrentMap;
+/**
+ *
+ * This interface provides APIs to configure and manage static routes.
+ *
+ */
+import org.opendaylight.controller.sal.utils.Status;
+
+public interface IForwardingStaticRouting {
+
+ /**
+ * Retrieves the StaticRoute that has the longest prefix matching the ipAddress.
+ * @param ipAddress (InetAddress) the IP address
+ * @return StaticRoute
+ */
+ public StaticRoute getBestMatchStaticRoute(InetAddress ipAddress);
+
+ /**
+ * Returns all the StaticRouteConfig
+ * @return all the StaticRouteConfig
+ */
+ public ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs();
+
+ /**
+ * Adds a StaticRouteConfig
+ * @param config: the StaticRouteConfig to be added
+ * @return a text string indicating the result of the operation..
+ * If the operation is successful, the return string will be "SUCCESS"
+ */
+ public Status addStaticRoute(StaticRouteConfig config);
+
+ /**
+ * Removes the named StaticRouteConfig
+ * @param name: the name of the StaticRouteConfig to be removed
+ * @return a text string indicating the result of the operation.
+ * If the operation is successful, the return string will be "SUCCESS"
+ */
+ public Status removeStaticRoute(String name);
+
+ /**
+ * Saves the config
+ * @return a text string indicating the result of the operation.
+ * If the operation is successful, the return string will be "Success"
+ */
+ Status saveConfig();
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.forwarding.staticrouting;
+
+/**
+ * Interface that will be implemented by the modules that want to
+ * know when a Static Route is added or deleted.
+ *
+ */
+public interface IStaticRoutingAware {
+
+ /**
+ * This method is called when a StaticRoute has added or deleted.
+ * @param s: StaticRoute
+ * @param added: boolean true if the static route is added,
+ */
+ public void staticRouteUpdate(StaticRoute s, boolean added);
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.forwarding.staticrouting;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.packet.BitBufferHelper;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+
+import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
+
+/**
+ * This class defines a static route object.
+ */
+public class StaticRoute {
+ /**
+ * This Enum defines the possible types for the next hop address.
+ */
+ public enum NextHopType {
+ IPADDRESS("nexthop-ip"), SWITCHPORT("nexthop-interface");
+ private NextHopType(String name) {
+ this.name = name;
+ }
+
+ private String name;
+
+ public String toString() {
+ return name;
+ }
+
+ public static NextHopType fromString(String str) {
+ if (str == null)
+ return IPADDRESS;
+ if (str.equals(IPADDRESS.toString()))
+ return IPADDRESS;
+ if (str.equals(SWITCHPORT.toString()))
+ return SWITCHPORT;
+ return IPADDRESS;
+ }
+ }
+
+ InetAddress networkAddress;
+ InetAddress mask;
+ NextHopType type;
+ InetAddress nextHopAddress;
+ Node node;
+ NodeConnector port;
+ HostNodeConnector host;
+
+ /**
+ * Create a static route object with no specific information.
+ */
+ public StaticRoute() {
+
+ }
+
+ /**
+ * Create a static route object from the StaticRouteConfig.
+ * @param: config: StaticRouteConfig
+ */
+ public StaticRoute(StaticRouteConfig config) {
+ networkAddress = config.getStaticRouteIP();
+ mask = StaticRoute.getV4AddressMaskFromDecimal(config
+ .getStaticRouteMask());
+ type = NextHopType.fromString(config.getNextHopType());
+ nextHopAddress = config.getNextHopIP();
+ Map<Long, Short> switchPort = config.getNextHopSwitchPorts();
+ if ((switchPort != null) && (switchPort.size() == 1)) {
+ node = NodeCreator.createOFNode((Long) switchPort.keySet()
+ .toArray()[0]);
+ port = NodeConnectorCreator.createOFNodeConnector(
+ (Short) switchPort.values().toArray()[0], node);
+ }
+ }
+
+ /**
+ * Get the IP address portion of the sub-network of the static route.
+ * @return InetAddress: the IP address portion of the sub-network of the static route
+ */
+ public InetAddress getNetworkAddress() {
+ return networkAddress;
+ }
+
+ /**
+ * Set the IP address portion of the sub-network of the static route.
+ * @param networkAddress The IP address (InetAddress) to be set
+ */
+ public void setNetworkAddress(InetAddress networkAddress) {
+ this.networkAddress = networkAddress;
+ }
+
+ /**
+ * Get the mask of the sub-network of the static route.
+ * @return mask: the mask (InetAddress) of the sub-network of the static route
+ */
+ public InetAddress getMask() {
+ return mask;
+ }
+
+ /**
+ * Set the sub-network's mask of the static route.
+ * @param mask The mask (InetAddress) to be set
+ */
+ public void setMask(InetAddress mask) {
+ this.mask = mask;
+ }
+
+ /**
+ * Get the NextHopeType of the static route.
+ * @return type: NextHopeType
+ */
+ public NextHopType getType() {
+ return type;
+ }
+
+ /**
+ * Set the nextHopType.
+ * @param type The NextHopType to be set
+ */
+ public void setType(NextHopType type) {
+ this.type = type;
+ }
+
+ /**
+ * Get the next hop IP address.
+ * @return: nextHopAddress (InetAddress)
+ */
+ public InetAddress getNextHopAddress() {
+ return nextHopAddress;
+ }
+
+ /**
+ * Set the next hop IP address.
+ * @param nextHopAddress The IP address (InetAddress) to be set
+ */
+ public void setNextHopAddress(InetAddress nextHopAddress) {
+ this.nextHopAddress = nextHopAddress;
+ }
+
+ /**
+ * Get the Node associated with the static route.
+ * @return: Node
+ */
+ public Node getNode() {
+ return node;
+ }
+
+ /**
+ * Set the node associated to the static route.
+ * @param node: The node to be set
+ */
+ public void setNode(Node node) {
+ this.node = node;
+ }
+
+ /**
+ * Set the port associated to the static route.
+ * @param port The port (NodeConnector) to be set
+ */
+ public void setPort(NodeConnector port) {
+ this.port = port;
+ }
+
+ /**
+ * Get the port associated to the static route.
+ * @return port: The port (NodeConnector)
+ */
+ public NodeConnector getPort() {
+ return port;
+ }
+
+ /**
+ * Get the Host associated to static route.
+ * @return host: The host (HostNodeConnector)
+ */
+ public HostNodeConnector getHost() {
+ return host;
+ }
+
+ /**
+ * Set the host associated to the static route.
+ * @param host: (HostNodeConnector) to be set
+ */
+ public void setHost(HostNodeConnector host) {
+ this.host = host;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((host == null) ? 0 : host.hashCode());
+ result = prime * result + ((mask == null) ? 0 : mask.hashCode());
+ result = prime * result
+ + ((networkAddress == null) ? 0 : networkAddress.hashCode());
+ result = prime * result
+ + ((nextHopAddress == null) ? 0 : nextHopAddress.hashCode());
+ result = prime * result + ((port == null) ? 0 : port.hashCode());
+ result = prime * result + ((node == null) ? 0 : node.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "StaticRoute [networkAddress=" + networkAddress + ", mask="
+ + mask + ", type=" + type.toString() + ", nextHopAddress="
+ + nextHopAddress + ", swid=" + node + ", port=" + port
+ + ", host=" + host + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ StaticRoute other = (StaticRoute) obj;
+ if (!networkAddress.equals(other.networkAddress))
+ return false;
+ if (!mask.equals(other.mask))
+ return false;
+ return true;
+ }
+
+ private static InetAddress getV4AddressMaskFromDecimal(int mask) {
+ int netmask = 0;
+ for (int i = 0; i < mask; i++) {
+ netmask |= (1 << 31 - i);
+ }
+
+ try {
+ return InetAddress.getByAddress(BitBufferHelper
+ .toByteArray(netmask));
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ private void applyV4MaskOnByteBuffer(ByteBuffer bb, ByteBuffer bbMask) {
+ for (int i = 0; i < bb.array().length; i++) {
+ bb.put(i, (byte) (bb.get(i) & bbMask.get(i)));
+ }
+ }
+
+ /**
+ * Compute and return the IP address with longest prefix match from the static route based on the
+ * destNetworkAddress. Currently it only take IPv4 address format (Inet4Address)
+ * @param destNetworkAddress: the IP address to be based on
+ * @return: InetAddress: the IPv4 address with the longest prefix matching the static route.
+ * If the destNetworkkAddress is not IPv4 format, it will return null.
+ */
+ public InetAddress longestPrefixMatch(InetAddress destNetworkAddress) {
+ if (destNetworkAddress instanceof Inet4Address) {
+ ByteBuffer bbdest = ByteBuffer
+ .wrap(destNetworkAddress.getAddress());
+ ByteBuffer bbself = ByteBuffer.wrap(networkAddress.getAddress());
+
+ ByteBuffer bbMask = ByteBuffer.wrap(mask.getAddress());
+
+ applyV4MaskOnByteBuffer(bbdest, bbMask);
+ applyV4MaskOnByteBuffer(bbself, bbMask);
+
+ if (bbdest.equals(bbself)) {
+ try {
+ return InetAddress.getByAddress(bbself.array());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Compare the static route with another static route. It only handles(for now) IPv4Address.
+ * @param s: the other StaticRoute
+ * @return: 0 if they are the same
+ */
+ public int compareTo(StaticRoute s) {
+ if (s == null)
+ return 1;
+ if ((networkAddress instanceof Inet6Address)
+ || (s.getNetworkAddress() instanceof Inet6Address)) {
+ // HANDLE IPv6 Later
+ return 1;
+ }
+
+ ByteBuffer bbchallenger = ByteBuffer.wrap(s.getNetworkAddress()
+ .getAddress());
+ ByteBuffer bbself = ByteBuffer.wrap(networkAddress.getAddress());
+ ByteBuffer bbChallengerMask = ByteBuffer.wrap(s.getMask().getAddress());
+ ByteBuffer bbSelfMask = ByteBuffer.wrap(getMask().getAddress());
+
+ applyV4MaskOnByteBuffer(bbchallenger, bbChallengerMask);
+ applyV4MaskOnByteBuffer(bbself, bbSelfMask);
+ return bbself.compareTo(bbchallenger);
+ }
+}
\ No newline at end of file
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.forwarding.staticrouting;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.opendaylight.controller.sal.utils.GUIField;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+
+/**
+ * This class defines all the necessary configuration information for a static route.
+ */
+public class StaticRouteConfig implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private static final String regexSubnet = "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])[/](\\d|[12]\\d|3[0-2])$";
+ private static final String regexIP = "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";
+ private static final String regexDatapathID = "^([0-9a-fA-F]{1,2}[:-]){7}[0-9a-fA-F]{1,2}$";
+ private static final String regexDatapathIDLong = "^[0-9a-fA-F]{1,16}$";
+ private static final String prettyFields[] = { GUIField.NAME.toString(),
+ GUIField.STATICROUTE.toString(), GUIField.NEXTHOP.toString() };
+ private transient String nextHopType; // Ignoring NextHopType for now. Supporting just the next-hop IP-Address feature for now.
+ // Order matters: JSP file expects following fields in the following order
+ private String name;
+ private String staticRoute; // A.B.C.D/MM Where A.B.C.D is the Default Gateway IP (L3) or ARP Querier IP (L2)
+ private String nextHop; // NextHop IP-Address (or) datapath ID/port list: xx:xx:xx:xx:xx:xx:xx:xx/a,b,c-m,r-t,y
+
+ /**
+ * Create a static route configuration with no specific information.
+ */
+ public StaticRouteConfig() {
+ super();
+ nextHopType = StaticRoute.NextHopType.IPADDRESS.toString();
+ }
+
+ /**
+ * Create a static route configuration with all the information.
+ * @param name The name (String) of the static route config
+ * @param staticRoute The string representation of the route. e.g. 192.168.1.1/24
+ * @param nextHop The string representation of the next hop IP address. e.g. 10.10.1.1
+ */
+ public StaticRouteConfig(String name, String staticRoute, String nextHop) {
+ super();
+ this.name = name;
+ this.staticRoute = staticRoute;
+ this.nextHop = nextHop;
+ }
+
+ /**
+ * Get the name of the StaticRouteConfig.
+ * @return: The name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name of the StaticRouteConfig.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the string representation of the static route.
+ * @return The string representation of the static route
+ */
+ public String getStaticRoute() {
+ return staticRoute;
+ }
+
+ /**
+ * Set the static route of the StaticRouteConfig.
+ * @param staticRoute The string representation of the static route
+ */
+ public void setStaticRoute(String staticRoute) {
+ this.staticRoute = staticRoute;
+ }
+
+ /**
+ * Get the string representation of the next hop address type.
+ * @return The string representation of the next hop address type
+ */
+ public String getNextHopType() {
+ if (nextHopType == null)
+ return StaticRoute.NextHopType.IPADDRESS.toString();
+ return nextHopType;
+ }
+
+ /**
+ * Set the next hop address type.
+ * @param nextHopType The string representation of the next hop address type
+ */
+ public void setNextHopType(String nextHopType) {
+ this.nextHopType = nextHopType;
+ }
+
+ /**
+ * Get all the supported next hop address types.
+ * @return The list of supported next hop address types
+ */
+ public static List<String> getSupportedNextHopTypes() {
+ List<String> s = new ArrayList<String>();
+ for (StaticRoute.NextHopType nh : StaticRoute.NextHopType.values()) {
+ s.add(nh.toString());
+ }
+ return s;
+ }
+
+ /**
+ * Get the next hop address
+ * @return The string represenation of the next hop address
+ */
+ public String getNextHop() {
+ return nextHop;
+ }
+
+ /**
+ * Set the next hop address.
+ * @param nextHop: The string representation of the next hop address to be set
+ */
+ public void setNextHop(String nextHop) {
+ this.nextHop = nextHop;
+ }
+
+ /**
+ * Return a string with text indicating if the config is valid.
+ * @return SUCCESS if the config is valid
+ */
+ public Status isValid() {
+ if ((name == null) || (name.trim().length() < 1)) {
+ return new Status(StatusCode.BADREQUEST,
+ "Invalid Static Route name");
+ }
+ if (!isValidStaticRouteEntry()) {
+ return new Status(StatusCode.BADREQUEST,
+ "Invalid Static Route entry. Please use the " +
+ "IPAddress/mask format. Default gateway " +
+ "(0.0.0.0/0) is NOT supported.");
+ }
+ if (!isValidNextHop()) {
+ return new Status(StatusCode.BADREQUEST,
+ "Invalid NextHop IP Address configuration. " +
+ "Please use the X.X.X.X format.");
+ }
+
+ return new Status(StatusCode.SUCCESS, null);
+ }
+
+ private boolean isValidAddress(String address) {
+ if ((address != null) && address.matches(regexIP)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isValidStaticRouteEntry() {
+ if ((staticRoute != null) && staticRoute.matches(regexSubnet)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isValidNextHop() {
+ if (getNextHopType().equalsIgnoreCase(
+ StaticRoute.NextHopType.IPADDRESS.toString())) {
+ return isValidNextHopIP();
+ } else if (getNextHopType().equalsIgnoreCase(
+ StaticRoute.NextHopType.SWITCHPORT.toString())) {
+ return isValidSwitchId();
+ }
+ return false;
+ }
+
+ private boolean isValidNextHopIP() {
+ return isValidAddress(nextHop);
+ }
+
+ private boolean isValidSwitchId(String switchId) {
+ return (switchId != null && (switchId.matches(regexDatapathID) || switchId
+ .matches(regexDatapathIDLong)));
+ }
+
+ private boolean isValidSwitchId() {
+ if (getNextHopType().equalsIgnoreCase(
+ StaticRoute.NextHopType.SWITCHPORT.toString())) {
+ String pieces[] = nextHop.split("/");
+ if (pieces.length < 2)
+ return false;
+ return isValidSwitchId(pieces[0]);
+ }
+ return false;
+ }
+
+ /**
+ * Return the IP address of the static route.
+ * @return The IP address
+ */
+ public InetAddress getStaticRouteIP() {
+ if (!isValidStaticRouteEntry())
+ return null;
+ InetAddress ip = null;
+ try {
+ ip = InetAddress.getByName(staticRoute.split("/")[0]);
+ } catch (UnknownHostException e1) {
+ return null;
+ }
+ return ip;
+ }
+
+ /**
+ * Return the bit-mask length of the static route.
+ * @return The bit-mask length
+ */
+ public Short getStaticRouteMask() {
+ Short maskLen = 0;
+ if (isValidStaticRouteEntry()) {
+ String[] s = staticRoute.split("/");
+ maskLen = (s.length == 2) ? Short.valueOf(s[1]) : 32;
+ }
+ return maskLen;
+ }
+
+ /**
+ * Return the IP address of the next hop.
+ * @return the IP address
+ */
+ public InetAddress getNextHopIP() {
+ if ((getNextHopType()
+ .equalsIgnoreCase(StaticRoute.NextHopType.IPADDRESS.toString()))
+ && isValidNextHopIP()) {
+ InetAddress ip = null;
+ try {
+ ip = InetAddress.getByName(nextHop);
+ } catch (UnknownHostException e1) {
+ return null;
+ }
+ return ip;
+ }
+ return null;
+ }
+
+/**
+ * Return the switch ID and the port ID of the next hop address.
+ * @return The switchID (Long) and PortID (Short) in the map
+ */
+ public Map<Long, Short> getNextHopSwitchPorts() {
+ // codedSwitchPorts = xx:xx:xx:xx:xx:xx:xx:xx/port-number
+ if (getNextHopType().equalsIgnoreCase(
+ StaticRoute.NextHopType.SWITCHPORT.toString())) {
+ Map<Long, Short> sp = new HashMap<Long, Short>(1);
+ String pieces[] = nextHop.split("/");
+ sp.put(getSwitchIDLong(pieces[0]), Short.valueOf(pieces[1]));
+ return sp;
+ }
+ return null;
+ }
+
+ private long getSwitchIDLong(String switchId) {
+ int radix = 16;
+ String switchString = "0";
+
+ if (isValidSwitchId(switchId)) {
+ if (switchId.contains(":")) {
+ // Handle the 00:00:AA:BB:CC:DD:EE:FF notation
+ switchString = switchId.replace(":", "");
+ } else if (switchId.contains("-")) {
+ // Handle the 00-00-AA-BB-CC-DD-EE-FF notation
+ switchString = switchId.replace("-", "");
+ } else {
+ // Handle the 0123456789ABCDEF notation
+ switchString = switchId;
+ }
+ }
+ return Long.parseLong(switchString, radix);
+ }
+
+ /**
+ * Return all the field names of the config.
+ * @return The list containing all the field names
+ */
+ public static List<String> getFieldsNames() {
+ List<String> fieldList = new ArrayList<String>();
+ for (Field fld : StaticRouteConfig.class.getDeclaredFields()) {
+ fieldList.add(fld.getName());
+ }
+ //remove the 6 static fields + NextHopType
+ for (short i = 0; i < 7; i++) {
+ fieldList.remove(0);
+ }
+ return fieldList;
+ }
+
+ /**
+ * Return all the GUI field names of the config.
+ * @return The list containing all the GUI field names
+ */
+ public static List<String> getGuiFieldsNames() {
+ List<String> fieldList = new ArrayList<String>();
+ for (String str : prettyFields) {
+ fieldList.add(str);
+ }
+ return fieldList;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeBuilder.reflectionHashCode(this);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsBuilder.reflectionEquals(this, obj);
+ }
+
+ @Override
+ public String toString() {
+ return "StaticRouteConfig [name=" + name + ", staticRoute="
+ + staticRoute + ", nextHopType=" + nextHopType + ", nextHop="
+ + nextHop + "]";
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.forwarding.staticrouting.internal;
+
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.forwarding.staticrouting.IForwardingStaticRouting;
+import org.opendaylight.controller.forwarding.staticrouting.IStaticRoutingAware;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterContainerServices;
+import org.opendaylight.controller.configuration.IConfigurationContainerAware;
+import org.opendaylight.controller.hosttracker.IfIptoHost;
+import org.opendaylight.controller.hosttracker.IfNewHostNotify;
+
+public class Activator extends ComponentActivatorAbstractBase {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(Activator.class);
+
+ /**
+ * Function called when the activator starts just after some
+ * initializations are done by the
+ * ComponentActivatorAbstractBase.
+ *
+ */
+ public void init() {
+
+ }
+
+ /**
+ * Function called when the activator stops just before the
+ * cleanup done by ComponentActivatorAbstractBase
+ *
+ */
+ public void destroy() {
+
+ }
+
+ /**
+ * Function that is used to communicate to dependency manager the
+ * list of known implementations for services inside a container
+ *
+ *
+ * @return An array containing all the CLASS objects that will be
+ * instantiated in order to get an fully working implementation
+ * Object
+ */
+ public Object[] getImplementations() {
+ Object[] res = { StaticRoutingImplementation.class };
+ return res;
+ }
+
+ /**
+ * Function that is called when configuration of the dependencies
+ * is required.
+ *
+ * @param c dependency manager Component object, used for
+ * configuring the dependencies exported and imported
+ * @param imp Implementation class that is being configured,
+ * needed as long as the same routine can configure multiple
+ * implementations
+ * @param containerName The containerName being configured, this allow
+ * also optional per-container different behavior if needed, usually
+ * should not be the case though.
+ */
+ public void configureInstance(Component c, Object imp, String containerName) {
+ if (imp.equals(StaticRoutingImplementation.class)) {
+ Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
+ Set<String> propSet = new HashSet<String>();
+ propSet.add("forwarding.staticrouting.configSaveEvent");
+ props.put("cachenames", propSet);
+ // export the service
+
+ c.setInterface(new String[] { ICacheUpdateAware.class.getName(),
+ IForwardingStaticRouting.class.getName(),
+ IfNewHostNotify.class.getName(),
+ IConfigurationContainerAware.class.getName() }, props);
+
+ c.add(createContainerServiceDependency(containerName).setService(
+ IClusterContainerServices.class).setCallbacks(
+ "setClusterContainerService",
+ "unsetClusterContainerService").setRequired(true));
+
+ c.add(createContainerServiceDependency(containerName).setService(
+ IfIptoHost.class).setCallbacks("setHostTracker",
+ "unsetHostTracker").setRequired(true));
+
+ // Static routing aware there could be many
+ c.add(createContainerServiceDependency(containerName).setService(
+ IStaticRoutingAware.class).setCallbacks(
+ "setStaticRoutingAware", "unsetStaticRoutingAware")
+ .setRequired(false));
+ }
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.forwarding.staticrouting.internal;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Dictionary;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Future;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterContainerServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.configuration.IConfigurationContainerAware;
+import org.opendaylight.controller.forwarding.staticrouting.IForwardingStaticRouting;
+import org.opendaylight.controller.forwarding.staticrouting.IStaticRoutingAware;
+import org.opendaylight.controller.forwarding.staticrouting.StaticRoute;
+import org.opendaylight.controller.forwarding.staticrouting.StaticRouteConfig;
+import org.opendaylight.controller.hosttracker.IfIptoHost;
+import org.opendaylight.controller.hosttracker.IfNewHostNotify;
+import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.IObjectReader;
+import org.opendaylight.controller.sal.utils.ObjectReader;
+import org.opendaylight.controller.sal.utils.ObjectWriter;
+import org.opendaylight.controller.sal.utils.Status;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Static Routing feature provides the bridge between SDN and Non-SDN networks.
+ *
+ *
+ *
+ */
+public class StaticRoutingImplementation implements IfNewHostNotify,
+ IForwardingStaticRouting, IObjectReader, IConfigurationContainerAware,
+ ICacheUpdateAware<Long, String> {
+ private static Logger log = LoggerFactory
+ .getLogger(StaticRoutingImplementation.class);
+ private static String ROOT = GlobalConstants.STARTUPHOME.toString();
+ private static final String SAVE = "Save";
+ ConcurrentMap<String, StaticRoute> staticRoutes;
+ ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs;
+ private IfIptoHost hostTracker;
+ private Timer gatewayProbeTimer;
+ private String staticRoutesFileName = null;
+ private Map<Long, String> configSaveEvent;
+ private IClusterContainerServices clusterContainerService = null;
+ private Set<IStaticRoutingAware> staticRoutingAware = Collections
+ .synchronizedSet(new HashSet<IStaticRoutingAware>());
+
+ void setStaticRoutingAware(IStaticRoutingAware s) {
+ if (this.staticRoutingAware != null) {
+ this.staticRoutingAware.add(s);
+ }
+ }
+
+ void unsetStaticRoutingAware(IStaticRoutingAware s) {
+ if (this.staticRoutingAware != null) {
+ this.staticRoutingAware.remove(s);
+ }
+ }
+
+ public void setHostTracker(IfIptoHost hostTracker) {
+ log.debug("Setting HostTracker");
+ this.hostTracker = hostTracker;
+ }
+
+ public void unsetHostTracker(IfIptoHost hostTracker) {
+ if (this.hostTracker == hostTracker) {
+ this.hostTracker = null;
+ }
+ }
+
+ public ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs() {
+ return staticRouteConfigs;
+ }
+
+ public void setStaticRouteConfigs(
+ ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs) {
+ this.staticRouteConfigs = staticRouteConfigs;
+ }
+
+ @Override
+ public Object readObject(ObjectInputStream ois)
+ throws FileNotFoundException, IOException, ClassNotFoundException {
+ // Perform the class deserialization locally, from inside the package
+ // where the class is defined
+ return ois.readObject();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void loadConfiguration() {
+ ObjectReader objReader = new ObjectReader();
+ ConcurrentMap<String, StaticRouteConfig> confList = (ConcurrentMap<String, StaticRouteConfig>) objReader
+ .read(this, staticRoutesFileName);
+
+ if (confList == null) {
+ return;
+ }
+
+ for (StaticRouteConfig conf : confList.values()) {
+ addStaticRoute(conf);
+ }
+ }
+
+ @Override
+ public Status saveConfig() {
+ // Publish the save config event to the cluster nodes
+ configSaveEvent.put(new Date().getTime(), SAVE);
+ return saveConfigInternal();
+ }
+
+ public Status saveConfigInternal() {
+ Status status;
+ ObjectWriter objWriter = new ObjectWriter();
+
+ status = objWriter.write(
+ new ConcurrentHashMap<String, StaticRouteConfig>(
+ staticRouteConfigs), staticRoutesFileName);
+
+ if (status.isSuccess()) {
+ return status;
+ } else {
+ return new Status(StatusCode.INTERNALERROR, "Save failed");
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void allocateCaches() {
+ if (this.clusterContainerService == null) {
+ log
+ .info("un-initialized clusterContainerService, can't create cache");
+ return;
+ }
+
+ try {
+ clusterContainerService.createCache(
+ "forwarding.staticrouting.routes", EnumSet
+ .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ clusterContainerService.createCache(
+ "forwarding.staticrouting.configs", EnumSet
+ .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ clusterContainerService.createCache(
+ "forwarding.staticrouting.configSaveEvent", EnumSet
+ .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+
+ } catch (CacheExistException cee) {
+ log
+ .error("\nCache already exists - destroy and recreate if needed");
+ } catch (CacheConfigException cce) {
+ log.error("\nCache configuration invalid - check cache mode");
+ }
+ }
+
+ @SuppressWarnings({ "unchecked", "deprecation" })
+ private void retrieveCaches() {
+ if (this.clusterContainerService == null) {
+ log
+ .info("un-initialized clusterContainerService, can't retrieve cache");
+ return;
+ }
+
+ staticRoutes = (ConcurrentMap<String, StaticRoute>) clusterContainerService
+ .getCache("forwarding.staticrouting.routes");
+ if (staticRoutes == null) {
+ log.error("\nFailed to get rulesDB handle");
+ }
+
+ staticRouteConfigs = (ConcurrentMap<String, StaticRouteConfig>) clusterContainerService
+ .getCache("forwarding.staticrouting.configs");
+ if (staticRouteConfigs == null) {
+ log.error("\nFailed to get rulesDB handle");
+ }
+ configSaveEvent = (ConcurrentMap<Long, String>) clusterContainerService
+ .getCache("forwarding.staticrouting.configSaveEvent");
+ if (configSaveEvent == null) {
+ log.error("\nFailed to get cache for configSaveEvent");
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void destroyCaches() {
+ if (this.clusterContainerService == null) {
+ log
+ .info("un-initialized clusterContainerService, can't destroy cache");
+ return;
+ }
+
+ clusterContainerService.destroyCache("forwarding.staticrouting.routes");
+ clusterContainerService
+ .destroyCache("forwarding.staticrouting.configs");
+ clusterContainerService
+ .destroyCache("forwarding.staticrouting.configSaveEvent");
+
+ }
+
+ @Override
+ public void entryCreated(Long key, String cacheName, boolean local) {
+ }
+
+ @Override
+ public void entryUpdated(Long key, String new_value, String cacheName,
+ boolean originLocal) {
+ saveConfigInternal();
+ }
+
+ @Override
+ public void entryDeleted(Long key, String cacheName, boolean originLocal) {
+ }
+
+ private void notifyStaticRouteUpdate(StaticRoute s, boolean update) {
+ if (this.staticRoutingAware != null) {
+ log.info("Invoking StaticRoutingAware listeners");
+ synchronized (this.staticRoutingAware) {
+ for (IStaticRoutingAware ra : this.staticRoutingAware) {
+ try {
+ ra.staticRouteUpdate(s, update);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+
+ private class NotifyStaticRouteThread extends Thread {
+ private StaticRoute staticRoute;
+ private boolean added;
+
+ public NotifyStaticRouteThread(StaticRoute s, boolean update) {
+ this.staticRoute = s;
+ this.added = update;
+ }
+
+ public void run() {
+ if (!added
+ || (staticRoute.getType() == StaticRoute.NextHopType.SWITCHPORT)) {
+ notifyStaticRouteUpdate(staticRoute, added);
+ } else {
+ HostNodeConnector host = hostTracker.hostQuery(staticRoute
+ .getNextHopAddress());
+ if (host == null) {
+ Future<HostNodeConnector> future = hostTracker
+ .discoverHost(staticRoute.getNextHopAddress());
+ if (future != null) {
+ try {
+ host = future.get();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ if (host != null) {
+ staticRoute.setHost(host);
+ notifyStaticRouteUpdate(staticRoute, added);
+ }
+ }
+ }
+ }
+
+ private void checkAndUpdateListeners(StaticRoute staticRoute, boolean added) {
+ new NotifyStaticRouteThread(staticRoute, added).start();
+ }
+
+ private void notifyHostUpdate(HostNodeConnector host, boolean added) {
+ if (host == null)
+ return;
+ for (StaticRoute s : staticRoutes.values()) {
+ if (s.getType() == StaticRoute.NextHopType.SWITCHPORT)
+ continue;
+ if (s.getNextHopAddress().equals(host.getNetworkAddress())) {
+ if (added) {
+ s.setHost(host);
+ } else {
+ s.setHost(null);
+ }
+ notifyStaticRouteUpdate(s, added);
+ }
+ }
+ }
+
+ @Override
+ public void notifyHTClient(HostNodeConnector host) {
+ notifyHostUpdate(host, true);
+ }
+
+ @Override
+ public void notifyHTClientHostRemoved(HostNodeConnector host) {
+ notifyHostUpdate(host, false);
+ }
+
+ public boolean isIPv4AddressValid(String cidr) {
+ if (cidr == null)
+ return false;
+
+ String values[] = cidr.split("/");
+ Pattern ipv4Pattern = Pattern
+ .compile("(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])");
+ Matcher mm = ipv4Pattern.matcher(values[0]);
+ if (!mm.matches()) {
+ log.debug("IPv4 source address {} is not valid", cidr);
+ return false;
+ }
+ if (values.length >= 2) {
+ int prefix = Integer.valueOf(values[1]);
+ if ((prefix < 0) || (prefix > 32)) {
+ log.debug("prefix {} is not valid", prefix);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static short getUnsignedByte(ByteBuffer bb, int position) {
+ return ((short) (bb.get(position) & (short) 0xff));
+ }
+
+ public static int compareByteBuffers(ByteBuffer buf1, ByteBuffer buf2) {
+ for (int i = 0; i < buf1.array().length; i++) {
+ if (getUnsignedByte(buf1, i) > getUnsignedByte(buf2, i)) {
+ return 1;
+ } else if (getUnsignedByte(buf1, i) < getUnsignedByte(buf2, i)) {
+ return -1;
+ }
+ }
+
+ return 0;
+ }
+
+ public StaticRoute getBestMatchStaticRoute(InetAddress ipAddress) {
+ ByteBuffer bblongestPrefix = null;
+ try {
+ bblongestPrefix = ByteBuffer.wrap(InetAddress.getByName("0.0.0.0")
+ .getAddress());
+ } catch (Exception e) {
+ return null;
+ }
+
+ if (staticRoutes == null) {
+ return null;
+ }
+
+ StaticRoute longestPrefixRoute = null;
+ for (StaticRoute s : staticRoutes.values()) {
+ InetAddress prefix = s.longestPrefixMatch(ipAddress);
+ if ((prefix != null) && (prefix instanceof Inet4Address)) {
+ ByteBuffer bbtmp = ByteBuffer.wrap(prefix.getAddress());
+ if (compareByteBuffers(bbtmp, bblongestPrefix) > 0) {
+ bblongestPrefix = bbtmp;
+ longestPrefixRoute = s;
+ }
+ }
+ }
+ return longestPrefixRoute;
+ }
+
+ public Status addStaticRoute(StaticRouteConfig config) {
+ Status status;
+
+ status = config.isValid();
+ if (!status.isSuccess()) {
+ return status;
+ }
+ if (staticRouteConfigs.get(config.getName()) != null) {
+ return new Status(StatusCode.CONFLICT,
+ "A valid Static Route configuration with this name " +
+ "already exists. Please use a different name");
+ }
+ for (StaticRouteConfig s : staticRouteConfigs.values()) {
+ if (s.equals(config)) {
+ return new Status(StatusCode.CONFLICT,
+ "This conflicts with an existing Static Route " +
+ "Configuration. Please check the configuration " +
+ "and try again");
+ }
+ }
+
+ staticRouteConfigs.put(config.getName(), config);
+ StaticRoute sRoute = new StaticRoute(config);
+ staticRoutes.put(config.getName(), sRoute);
+ checkAndUpdateListeners(sRoute, true);
+ return status;
+ }
+
+ public Status removeStaticRoute(String name) {
+ staticRouteConfigs.remove(name);
+ StaticRoute sRoute = staticRoutes.remove(name);
+ if (sRoute != null) {
+ checkAndUpdateListeners(sRoute, false);
+ return new Status(StatusCode.SUCCESS, null);
+ }
+ return new Status(StatusCode.NOTFOUND,
+ "Static Route with name " + name + " is not found");
+ }
+
+ void setClusterContainerService(IClusterContainerServices s) {
+ log.debug("Cluster Service set");
+ this.clusterContainerService = s;
+ }
+
+ void unsetClusterContainerService(IClusterContainerServices s) {
+ if (this.clusterContainerService == s) {
+ log.debug("Cluster Service removed!");
+ this.clusterContainerService = null;
+ }
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init(Component c) {
+ String containerName = null;
+ Dictionary props = c.getServiceProperties();
+ if (props != null) {
+ containerName = (String) props.get("containerName");
+ } else {
+ // In the Global instance case the containerName is empty
+ containerName = "";
+ }
+
+ staticRoutesFileName = ROOT + "staticRouting_" + containerName
+ + ".conf";
+
+ log.debug("forwarding.staticrouting starting on container "
+ + containerName);
+ //staticRoutes = new ConcurrentHashMap<String, StaticRoute>();
+ allocateCaches();
+ retrieveCaches();
+
+ if (staticRouteConfigs.isEmpty())
+ loadConfiguration();
+
+ /*
+ * Slow probe to identify any gateway that might have silently appeared
+ * after the Static Routing Configuration.
+ */
+ gatewayProbeTimer = new Timer();
+ gatewayProbeTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ for (StaticRoute s : staticRoutes.values()) {
+ if ((s.getType() == StaticRoute.NextHopType.IPADDRESS)
+ && s.getHost() == null) {
+ checkAndUpdateListeners(s, true);
+ }
+ }
+ }
+ }, 60 * 1000, 60 * 1000);
+ }
+
+ /**
+ * Function called by the dependency manager when at least one
+ * dependency become unsatisfied or when the component is shutting
+ * down because for example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ log.debug("Destroy all the Static Routing Rules given we are "
+ + "shutting down");
+
+ destroyCaches();
+ gatewayProbeTimer.cancel();
+
+ // Clear the listener so to be ready in next life
+ this.staticRoutingAware.clear();
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called
+ * and after the services provided by the class are registered in
+ * the service registry
+ *
+ */
+ void start() {
+ }
+
+ /**
+ * Function called by the dependency manager before the services
+ * exported by the component are unregistered, this will be
+ * followed by a "destroy ()" calls
+ *
+ */
+ void stop() {
+ }
+
+ @Override
+ public Status saveConfiguration() {
+ return this.saveConfig();
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.forwarding.staticrouting;
+
+
+import java.net.InetAddress;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.forwarding.staticrouting.StaticRoute.NextHopType;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+
+public class StaticRouteConfigTest {
+
+ @Test
+ public void testStaticRouteSetGet() {
+ StaticRouteConfig staticRouteConfig1 = new StaticRouteConfig();
+ staticRouteConfig1.setName("route");
+ staticRouteConfig1.setStaticRoute("10.1.1.2/32");
+ staticRouteConfig1.setNextHop("200.2.2.2");
+ staticRouteConfig1.setNextHopType(NextHopType.IPADDRESS.toString());
+ StaticRouteConfig staticRouteConfig2 = new StaticRouteConfig("route", "10.1.1.2/32", "200.2.2.2");
+
+ Assert.assertEquals(staticRouteConfig2.getName(), staticRouteConfig1.getName());
+ Assert.assertEquals(staticRouteConfig2.getStaticRoute(), staticRouteConfig1.getStaticRoute());
+ Assert.assertEquals(staticRouteConfig2.getNextHop(), staticRouteConfig1.getNextHop());
+ Assert.assertEquals("nexthop-ip", staticRouteConfig1.getNextHopType());
+ }
+
+ @Test
+ public void testStaticRouteisValid() {
+ StaticRouteConfig staticRouteConfig1 = new StaticRouteConfig("route1", "10.1.1.254/24", "100.1.1.1");
+ Status receivedResponse1 = staticRouteConfig1.isValid();
+ Status expectedResponse1 = new Status(StatusCode.SUCCESS, null);
+ Assert.assertEquals(expectedResponse1.toString(), receivedResponse1.toString());
+
+ StaticRouteConfig staticRouteConfig2 = new StaticRouteConfig("", "", "100.1.1.1");
+ Status receivedResponse2 = staticRouteConfig2.isValid();
+ Status expectedResponse2 = new Status(StatusCode.BADREQUEST,
+ "Invalid Static Route name");
+ Assert.assertEquals(expectedResponse2.toString(), receivedResponse2.toString());
+
+ StaticRouteConfig staticRouteConfig3 = new StaticRouteConfig("route1", "10.1.1.254", "100.1.1.1");
+ Status receivedResponse3 = staticRouteConfig3.isValid();
+ Status expectedResponse3 = new Status(StatusCode.BADREQUEST,
+ "Invalid Static Route entry. Please use the " +
+ "IPAddress/mask format. Default gateway " +
+ "(0.0.0.0/0) is NOT supported.");
+ Assert.assertEquals(expectedResponse3.toString(), receivedResponse3.toString());
+
+ StaticRouteConfig staticRouteConfig4 = new StaticRouteConfig("route1", "289.1.1.254/24", "100.1.1.1");
+ Status receivedResponse4 = staticRouteConfig4.isValid();
+ Status expectedResponse4 = new Status(StatusCode.BADREQUEST,
+ "Invalid Static Route entry. Please use the " +
+ "IPAddress/mask format. Default gateway " +
+ "(0.0.0.0/0) is NOT supported.");
+ Assert.assertEquals(expectedResponse4.toString(), receivedResponse4.toString());
+
+ StaticRouteConfig staticRouteConfig5 = new StaticRouteConfig("route1", "10.1.1.254/24", "100.1.1");
+ Status receivedResponse5 = staticRouteConfig5.isValid();
+ Status expectedResponse5 = new Status(StatusCode.BADREQUEST,
+ "Invalid NextHop IP Address configuration. " +
+ "Please use the X.X.X.X format.");
+ Assert.assertEquals(expectedResponse5.toString(), receivedResponse5.toString());
+ }
+
+ @Test
+ public void testGetStaticRouteIP() {
+ StaticRouteConfig staticRouteConfig1 = new StaticRouteConfig("route1", "10.1.1.0/24", "100.1.1.1");
+ InetAddress ip1 = staticRouteConfig1.getStaticRouteIP();
+ Assert.assertEquals("10.1.1.0", ip1.getHostAddress());
+
+ StaticRouteConfig staticRouteConfig2 = new StaticRouteConfig("route1", "10.1.1.0/80", "100.1.1.1");
+ InetAddress ip2 = staticRouteConfig2.getStaticRouteIP();
+ Assert.assertEquals(null, ip2);
+
+ }
+
+ @Test
+ public void testGetStaticRouteMask() {
+ StaticRouteConfig staticRouteConfig1 = new StaticRouteConfig("route1", "10.1.1.0/24", "100.1.1.1");
+ Short receivedMaskLen1 = staticRouteConfig1.getStaticRouteMask();
+ Short expectedMaskLen1 = 24;
+ Assert.assertEquals(expectedMaskLen1, receivedMaskLen1);
+
+ StaticRouteConfig staticRouteConfig2 = new StaticRouteConfig("route1", "10.1.1.0/40", "100.1.1.1");
+ Short receivedMaskLen2 = staticRouteConfig2.getStaticRouteMask();
+ Short expectedMaskLen2 = 0;
+ Assert.assertEquals(expectedMaskLen2, receivedMaskLen2);
+ }
+
+ @Test
+ public void testGetNextHopIP() {
+ StaticRouteConfig staticRouteConfig1 = new StaticRouteConfig("route1", "10.1.1.254/24", "100.1.1.1");
+ InetAddress ip1 = staticRouteConfig1.getNextHopIP();
+ Assert.assertEquals("100.1.1.1", ip1.getHostAddress());
+
+ StaticRouteConfig staticRouteConfig2 = new StaticRouteConfig("route1", "10.1.1.254/24", "100.1.1");
+ InetAddress ip2 = staticRouteConfig2.getNextHopIP();
+ Assert.assertEquals(null, ip2);
+ }
+
+}
+
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.forwarding.staticrouting;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.forwarding.staticrouting.StaticRoute.NextHopType;
+import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+
+public class StaticRouteTest {
+
+ @Test
+ public void testStaticRouteGetSet() {
+ StaticRoute staticRoute = new StaticRoute();
+ InetAddress networkAddress = null;
+ InetAddress mask = null;
+ InetAddress nextHopAddress = null;
+ try {
+ networkAddress = InetAddress.getByName("10.1.1.0");
+ mask = InetAddress.getByName("255.255.255.0");
+ nextHopAddress = InetAddress.getByName("200.0.0.1");
+
+ } catch (UnknownHostException e) {
+ Assert.assertTrue(false);
+ }
+ staticRoute.setNetworkAddress(networkAddress);
+ Assert.assertEquals(networkAddress.getHostAddress(), staticRoute.getNetworkAddress().getHostAddress());
+ staticRoute.setMask(mask);
+ Assert.assertEquals(mask.getHostAddress(), staticRoute.getMask().getHostAddress());
+ staticRoute.setType(NextHopType.IPADDRESS);
+ Assert.assertEquals("nexthop-ip", staticRoute.getType().toString());
+ staticRoute.setNextHopAddress(nextHopAddress);
+ Assert.assertEquals(nextHopAddress.getHostAddress(), staticRoute.getNextHopAddress().getHostAddress());
+ Node node = NodeCreator.createOFNode(((long)10));
+ staticRoute.setNode(node);
+ Assert.assertEquals(node, staticRoute.getNode());
+ NodeConnector nc0 = NodeConnectorCreator.createOFNodeConnector((short)20, node);
+ staticRoute.setPort(nc0);
+ Assert.assertEquals(nc0, staticRoute.getPort());
+ InetAddress ip1 = null;
+ HostNodeConnector h1 = null;
+ try {
+ ip1 = InetAddress.getByName("192.1.1.1");
+ } catch (UnknownHostException e) {
+ Assert.assertTrue(false);
+ }
+ try {
+ h1 = new HostNodeConnector(ip1);
+ } catch (ConstructionException e) {
+ Assert.assertTrue(false);
+ }
+ staticRoute.setHost(h1);
+ Assert.assertEquals(h1, staticRoute.getHost());
+ }
+
+ @Test
+ public void testStaticRouteComparison() {
+ StaticRouteConfig staticRouteConfig1 = new StaticRouteConfig("route1", "10.1.1.0/24", "100.1.1.1");
+ StaticRouteConfig staticRouteConfig2 = new StaticRouteConfig("route2", "10.1.1.0/24", "100.2.1.1");
+ StaticRouteConfig staticRouteConfig3 = new StaticRouteConfig("route3", "10.2.1.0/24", "100.3.1.1");
+ StaticRouteConfig staticRouteConfig4 = new StaticRouteConfig("route4", "10.1.1.0/31", "");
+ StaticRoute staticRoute1 = new StaticRoute(staticRouteConfig1);
+ StaticRoute staticRoute2 = new StaticRoute(staticRouteConfig2);
+ StaticRoute staticRoute3 = new StaticRoute(staticRouteConfig3);
+ StaticRoute staticRoute4 = new StaticRoute(staticRouteConfig4);
+
+ Assert.assertTrue(staticRoute1.equals(staticRoute2));
+ Assert.assertFalse(staticRoute1.equals(staticRoute3));
+ Assert.assertFalse(staticRoute1.equals(staticRoute4));
+
+ Assert.assertTrue(staticRoute1.compareTo(staticRoute2) == 0 ? true : false);
+ Assert.assertFalse(staticRoute1.compareTo(staticRoute3) == 0 ? true : false);
+ Assert.assertTrue(staticRoute1.compareTo(staticRoute4) == 0 ? true : false);
+
+ }
+
+ @Test
+ public void testLongestPrefixMatch() {
+ StaticRouteConfig staticRouteConfig1 = new StaticRouteConfig("route1", "10.1.1.254/24", "100.1.1.1");
+ StaticRoute staticRoute1 = new StaticRoute(staticRouteConfig1);
+ InetAddress ip1 = null;
+ InetAddress ip2 = null;
+ try {
+ ip1 = InetAddress.getByName("10.1.0.2");
+ ip2 = InetAddress.getByName("10.1.1.2");
+ } catch (UnknownHostException e) {
+ Assert.assertTrue(false);
+ }
+ InetAddress rxdIp1 = staticRoute1.longestPrefixMatch(ip1);
+ InetAddress rxdIp2 = staticRoute1.longestPrefixMatch(ip2);
+ Assert.assertEquals(null, rxdIp1);
+ Assert.assertEquals("10.1.1.0", rxdIp2.getHostAddress());
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.forwarding.staticrouting.internal;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class StaticRoutingImplementationTest {
+
+ @Test
+ public void isIPv4AddressValidTest() {
+ StaticRoutingImplementation staticRouteImpl = new StaticRoutingImplementation();
+ Assert.assertTrue(staticRouteImpl.isIPv4AddressValid("192.168.100.0/24"));
+ Assert.assertFalse(staticRouteImpl.isIPv4AddressValid("192.168.100.0/36"));
+ Assert.assertFalse(staticRouteImpl.isIPv4AddressValid("192.168.300.0/32"));
+ }
+}
+
+
+
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>forwardingrulesmanager</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Include-Resource>
+ </Include-Resource>
+ <Export-Package>
+ org.opendaylight.controller.forwardingrulesmanager
+ </Export-Package>
+ <Import-Package>
+ org.opendaylight.controller.clustering.services,
+ org.opendaylight.controller.configuration,
+ org.opendaylight.controller.hosttracker,
+ org.opendaylight.controller.hosttracker.hostAware,
+ org.opendaylight.controller.switchmanager,
+ org.opendaylight.controller.sal.action,
+ org.opendaylight.controller.sal.core,
+ org.opendaylight.controller.sal.flowprogrammer,
+ org.opendaylight.controller.sal.match,
+ org.opendaylight.controller.sal.utils,
+ org.opendaylight.controller.sal.packet,
+ javax.xml.bind.annotation,
+ javax.xml.bind,
+ org.apache.felix.dm,
+ org.apache.commons.lang3.builder,
+ org.osgi.service.component,
+ org.slf4j,
+ org.eclipse.osgi.framework.console,
+ org.osgi.framework
+ </Import-Package>
+ <Bundle-Activator>
+ org.opendaylight.controller.forwardingrulesmanager.internal.Activator
+ </Bundle-Activator>
+ <Require-Bundle>
+ org.opendaylight.controller.hosttracker
+ </Require-Bundle>
+ <Service-Component>
+ </Service-Component>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>hosttracker</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>configuration</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>switchmanager</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.forwardingrulesmanager;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.ActionType;
+import org.opendaylight.controller.sal.action.Controller;
+import org.opendaylight.controller.sal.action.Drop;
+import org.opendaylight.controller.sal.action.Flood;
+import org.opendaylight.controller.sal.action.HwPath;
+import org.opendaylight.controller.sal.action.Loopback;
+import org.opendaylight.controller.sal.action.Output;
+import org.opendaylight.controller.sal.action.PopVlan;
+import org.opendaylight.controller.sal.action.SetDlDst;
+import org.opendaylight.controller.sal.action.SetDlSrc;
+import org.opendaylight.controller.sal.action.SetNwDst;
+import org.opendaylight.controller.sal.action.SetNwSrc;
+import org.opendaylight.controller.sal.action.SetNwTos;
+import org.opendaylight.controller.sal.action.SetTpDst;
+import org.opendaylight.controller.sal.action.SetTpSrc;
+import org.opendaylight.controller.sal.action.SetVlanId;
+import org.opendaylight.controller.sal.action.SetVlanPcp;
+import org.opendaylight.controller.sal.action.SwPath;
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.IContainer;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.IPProtocols;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.ServiceHelper;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.opendaylight.controller.switchmanager.Switch;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Configuration Java Object which represents a flow configuration information
+ * for Forwarding Rrules Manager.
+ */
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class FlowConfig implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private static final Logger log = LoggerFactory.getLogger(FlowConfig.class);
+ private static final String staticFlowsGroup = "**StaticFlows";
+ private boolean dynamic;
+ private String status;
+
+ /*
+ * The order of the object data defined below is used directly in the UI built using JSP.
+ * Hence try to keep the order in a more logical way.
+ */
+ @XmlElement
+ private String installInHw;
+ @XmlElement
+ private String name;
+ @XmlElement
+ private Node node;
+ @XmlElement
+ private String ingressPort;
+ private String portGroup;
+ @XmlElement
+ private String priority;
+ @XmlElement
+ private String etherType;
+ @XmlElement
+ private String vlanId;
+ @XmlElement
+ private String vlanPriority;
+ @XmlElement
+ private String dlSrc;
+ @XmlElement
+ private String dlDst;
+ @XmlElement
+ private String nwSrc;
+ @XmlElement
+ private String nwDst;
+ @XmlElement
+ private String protocol;
+ @XmlElement
+ private String tosBits;
+ @XmlElement
+ private String tpSrc;
+ @XmlElement
+ private String tpDst;
+ @XmlElement
+ private String cookie;
+ @XmlElement
+ private String idleTimeout;
+ @XmlElement
+ private String hardTimeout;
+ @XmlElement
+ private List<String> actions;
+
+ private enum EtherIPType {
+ ANY, V4, V6;
+ };
+
+ private enum SetNextHopType {
+ CISCO_EXTENSION("Cisco NextHop Extension"), RESOLVE_L2RW(
+ "Resolve L2 Rewrite");
+
+ private SetNextHopType(String name) {
+ this.name = name;
+ }
+
+ private String name;
+
+ public String toString() {
+ return name;
+ }
+
+ public boolean equals(String type) {
+ if (type.trim().equalsIgnoreCase(name))
+ return true;
+ return false;
+ }
+ }
+
+ public FlowConfig() {
+ }
+
+ public FlowConfig(String installInHw, String name, Node node,
+ String priority, String cookie, String ingressPort,
+ String portGroup, String vlanId, String vlanPriority,
+ String etherType, String srcMac, String dstMac, String protocol,
+ String tosBits, String srcIP, String dstIP, String tpSrc,
+ String tpDst, String idleTimeout, String hardTimeout,
+ List<String> actions) {
+ super();
+ this.installInHw = installInHw;
+ this.name = name;
+ this.node = node;
+ this.priority = priority;
+ this.cookie = cookie;
+ this.ingressPort = ingressPort;
+ this.portGroup = portGroup;
+ this.vlanId = vlanId;
+ this.vlanPriority = vlanPriority;
+ this.etherType = etherType;
+ this.dlSrc = srcMac;
+ this.dlDst = dstMac;
+ this.protocol = protocol;
+ this.tosBits = tosBits;
+ this.nwSrc = srcIP;
+ this.nwDst = dstIP;
+ this.tpSrc = tpSrc;
+ this.tpDst = tpDst;
+ this.idleTimeout = idleTimeout;
+ this.hardTimeout = hardTimeout;
+ this.actions = actions;
+ this.status = StatusCode.SUCCESS.toString();
+ }
+
+ public FlowConfig(FlowConfig from) {
+ this.installInHw = from.installInHw;
+ this.name = from.name;
+ this.node = from.node;
+ this.priority = from.priority;
+ this.cookie = from.cookie;
+ this.ingressPort = from.ingressPort;
+ this.portGroup = from.portGroup;
+ this.vlanId = from.vlanId;
+ this.vlanPriority = from.vlanPriority;
+ this.etherType = from.etherType;
+ this.dlSrc = from.dlSrc;
+ this.dlDst = from.dlDst;
+ this.protocol = from.protocol;
+ this.tosBits = from.tosBits;
+ this.nwSrc = from.nwSrc;
+ this.nwDst = from.nwDst;
+ this.tpSrc = from.tpSrc;
+ this.tpDst = from.tpDst;
+ this.idleTimeout = from.idleTimeout;
+ this.hardTimeout = from.hardTimeout;
+ this.actions = new ArrayList<String>(from.actions);
+ }
+
+ public boolean installInHw() {
+ if (installInHw == null) {
+ // backward compatibility
+ installInHw = "true";
+ }
+ return installInHw.equals("true");
+ }
+
+ public void setInstallInHw(boolean inHw) {
+ installInHw = inHw ? "true" : "false";
+ }
+
+ public String getInstallInHw() {
+ return installInHw;
+ }
+
+ public boolean isInternalFlow() {
+ // Controller generated static flows have name starting with "**"
+ if (this.name == null)
+ return false;
+ return this.name.startsWith("**");
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ if (name == null) {
+ return;
+ }
+ this.name = name;
+ }
+
+ public Node getNode() {
+ return this.node;
+ }
+
+ public void setNode(Node node) {
+ this.node = node;
+ }
+
+ public String getPriority() {
+ return priority;
+ }
+
+ public void setPriority(String priority) {
+ this.priority = priority;
+ }
+
+ public String getCookie() {
+ return cookie;
+ }
+
+ public void setCookie(String cookie) {
+ this.cookie = cookie;
+ }
+
+ public String getIngressPort() {
+ return ingressPort;
+ }
+
+ public void setIngressPort(String ingressPort) {
+ this.ingressPort = ingressPort;
+ }
+
+ public String getPortGroup() {
+ return portGroup;
+ }
+
+ @Override
+ public String toString() {
+ return "FlowConfig [dynamic=" + dynamic + ", status=" + status
+ + ", installInHw=" + installInHw + ", name=" + name
+ + ", switchId=" + node + ", ingressPort=" + ingressPort
+ + ", portGroup=" + portGroup + ", etherType=" + etherType
+ + ", priority=" + priority + ", vlanId=" + vlanId
+ + ", vlanPriority=" + vlanPriority + ", dlSrc=" + dlSrc
+ + ", dlDst=" + dlDst + ", nwSrc=" + nwSrc + ", nwDst=" + nwDst
+ + ", protocol=" + protocol + ", tosBits=" + tosBits
+ + ", tpSrc=" + tpSrc + ", tpDst=" + tpDst + ", cookie="
+ + cookie + ", idleTimeout=" + idleTimeout + ", hardTimeout="
+ + hardTimeout + ", actions=" + actions + "]";
+ }
+
+ public void setPortGroup(String portGroup) {
+ this.portGroup = portGroup;
+ }
+
+ public String getVlanId() {
+ return vlanId;
+ }
+
+ public void setVlanId(String vlanId) {
+ this.vlanId = vlanId;
+ }
+
+ public String getVlanPriority() {
+ return vlanPriority;
+ }
+
+ public void setVlanPriority(String vlanPriority) {
+ this.vlanPriority = vlanPriority;
+ }
+
+ public String getEtherType() {
+ return etherType;
+ }
+
+ public void setEtherType(String etherType) {
+ this.etherType = etherType;
+ }
+
+ public String getSrcMac() {
+ return dlSrc;
+ }
+
+ public void setSrcMac(String srcMac) {
+ this.dlSrc = srcMac;
+ }
+
+ public String getDstMac() {
+ return dlDst;
+ }
+
+ public void setDstMac(String dstMac) {
+ this.dlDst = dstMac;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ public String getTosBits() {
+ return tosBits;
+ }
+
+ public void setTosBits(String tos_bits) {
+ this.tosBits = tos_bits;
+ }
+
+ public String getSrcIp() {
+ return nwSrc;
+ }
+
+ public void setSrcIp(String src_ip) {
+ this.nwSrc = src_ip;
+ }
+
+ public String getDstIp() {
+ return nwDst;
+ }
+
+ public void setDstIp(String dst_ip) {
+ this.nwDst = dst_ip;
+ }
+
+ public String getSrcPort() {
+ return tpSrc;
+ }
+
+ public void setSrcPort(String src_port) {
+ this.tpSrc = src_port;
+ }
+
+ public String getDstPort() {
+ return tpDst;
+ }
+
+ public void setDstPort(String dst_port) {
+ this.tpDst = dst_port;
+ }
+
+ public String getIdleTimeout() {
+ return idleTimeout;
+ }
+
+ public void setIdleTimeout(String idleTimeout) {
+ this.idleTimeout = idleTimeout;
+ }
+
+ public String getHardTimeout() {
+ return hardTimeout;
+ }
+
+ public void setHardTimeout(String hardTimeout) {
+ this.hardTimeout = hardTimeout;
+ }
+
+ public boolean isIPv6() {
+ if (NetUtils.isIPv6AddressValid(this.getSrcIp())
+ || NetUtils.isIPv6AddressValid(this.getDstIp())) {
+ return true;
+ }
+ return false;
+ }
+
+ public List<String> getActions() {
+ return actions;
+ }
+
+ public void setActions(List<String> actions) {
+ this.actions = actions;
+ }
+
+ public static List<String> getFieldsNames() {
+ List<String> fieldList = new ArrayList<String>();
+ for (Field fld : FlowConfig.class.getDeclaredFields()) {
+ fieldList.add(fld.getName());
+ }
+ // Remove 4 static fields + 2 internal field
+ for (int i = 0; i < 6; i++)
+ fieldList.remove(0);
+
+ return fieldList;
+ }
+
+ public boolean isPortGroupEnabled() {
+ return (portGroup != null);
+ }
+
+ public boolean isDynamic() {
+ return dynamic;
+ }
+
+ public void setDynamic(boolean dynamic) {
+ this.dynamic = dynamic;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public boolean isStatusSuccessful() {
+ return status.equals(StatusCode.SUCCESS.toString());
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((actions == null) ? 0 : actions.hashCode());
+ result = prime * result + ((cookie == null) ? 0 : cookie.hashCode());
+ result = prime * result + ((dlDst == null) ? 0 : dlDst.hashCode());
+ result = prime * result + ((dlSrc == null) ? 0 : dlSrc.hashCode());
+ result = prime * result + (dynamic ? 1231 : 1237);
+ result = prime * result
+ + ((etherType == null) ? 0 : etherType.hashCode());
+ result = prime * result
+ + ((ingressPort == null) ? 0 : ingressPort.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((nwDst == null) ? 0 : nwDst.hashCode());
+ result = prime * result + ((nwSrc == null) ? 0 : nwSrc.hashCode());
+ result = prime * result
+ + ((portGroup == null) ? 0 : portGroup.hashCode());
+ result = prime * result
+ + ((priority == null) ? 0 : priority.hashCode());
+ result = prime * result
+ + ((protocol == null) ? 0 : protocol.hashCode());
+ result = prime * result + ((node == null) ? 0 : node.hashCode());
+ result = prime * result + ((tosBits == null) ? 0 : tosBits.hashCode());
+ result = prime * result + ((tpDst == null) ? 0 : tpDst.hashCode());
+ result = prime * result + ((tpSrc == null) ? 0 : tpSrc.hashCode());
+ result = prime * result + ((vlanId == null) ? 0 : vlanId.hashCode());
+ result = prime * result
+ + ((vlanPriority == null) ? 0 : vlanPriority.hashCode());
+ result = prime * result
+ + ((idleTimeout == null) ? 0 : idleTimeout.hashCode());
+ result = prime * result
+ + ((hardTimeout == null) ? 0 : hardTimeout.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ FlowConfig other = (FlowConfig) obj;
+ if (actions == null) {
+ if (other.actions != null)
+ return false;
+ } else if (!actions.equals(other.actions))
+ return false;
+ if (cookie == null) {
+ if (other.cookie != null)
+ return false;
+ } else if (!cookie.equals(other.cookie))
+ return false;
+ if (dlDst == null) {
+ if (other.dlDst != null)
+ return false;
+ } else if (!dlDst.equals(other.dlDst))
+ return false;
+ if (dlSrc == null) {
+ if (other.dlSrc != null)
+ return false;
+ } else if (!dlSrc.equals(other.dlSrc))
+ return false;
+ if (dynamic != other.dynamic)
+ return false;
+ if (etherType == null) {
+ if (other.etherType != null)
+ return false;
+ } else if (!etherType.equals(other.etherType))
+ return false;
+ if (ingressPort == null) {
+ if (other.ingressPort != null)
+ return false;
+ } else if (!ingressPort.equals(other.ingressPort))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (nwDst == null) {
+ if (other.nwDst != null)
+ return false;
+ } else if (!nwDst.equals(other.nwDst))
+ return false;
+ if (nwSrc == null) {
+ if (other.nwSrc != null)
+ return false;
+ } else if (!nwSrc.equals(other.nwSrc))
+ return false;
+ if (portGroup == null) {
+ if (other.portGroup != null)
+ return false;
+ } else if (!portGroup.equals(other.portGroup))
+ return false;
+ if (priority == null) {
+ if (other.priority != null)
+ return false;
+ } else if (!priority.equals(other.priority))
+ return false;
+ if (protocol == null) {
+ if (other.protocol != null)
+ return false;
+ } else if (!protocol.equals(other.protocol))
+ return false;
+ if (node == null) {
+ if (other.node != null)
+ return false;
+ } else if (!node.equals(other.node))
+ return false;
+ if (tosBits == null) {
+ if (other.tosBits != null)
+ return false;
+ } else if (!tosBits.equals(other.tosBits))
+ return false;
+ if (tpDst == null) {
+ if (other.tpDst != null)
+ return false;
+ } else if (!tpDst.equals(other.tpDst))
+ return false;
+ if (tpSrc == null) {
+ if (other.tpSrc != null)
+ return false;
+ } else if (!tpSrc.equals(other.tpSrc))
+ return false;
+ if (vlanId == null) {
+ if (other.vlanId != null)
+ return false;
+ } else if (!vlanId.equals(other.vlanId))
+ return false;
+ if (vlanPriority == null) {
+ if (other.vlanPriority != null)
+ return false;
+ } else if (!vlanPriority.equals(other.vlanPriority))
+ return false;
+ if (idleTimeout == null) {
+ if (other.idleTimeout != null)
+ return false;
+ } else if (!idleTimeout.equals(other.idleTimeout))
+ return false;
+ if (hardTimeout == null) {
+ if (other.hardTimeout != null)
+ return false;
+ } else if (!hardTimeout.equals(other.hardTimeout))
+ return false;
+ return true;
+ }
+
+ public InetAddress getNextHopAddressForL2RWAction() {
+ if (actions != null) {
+ Matcher sstr;
+ for (String actiongrp : actions) {
+ sstr = Pattern.compile("SET_NEXT_HOP=(.*)").matcher(actiongrp);
+ if (sstr.matches()) {
+ SetNextHopType setNHType = SetNextHopType.CISCO_EXTENSION;
+ String nextHopInfo = sstr.group(1);
+ String values[] = nextHopInfo.split("//");
+ String address = values[0].trim();
+ String type = null;
+ if (values.length > 1) {
+ type = values[1].trim();
+ }
+
+ if (type != null) {
+ for (SetNextHopType nh : SetNextHopType.values()) {
+ if (nh.equals(type))
+ setNHType = nh;
+ }
+ }
+
+ log.debug("Get Nexthop address = " + address + " Type = "
+ + setNHType.toString());
+ if (setNHType == SetNextHopType.RESOLVE_L2RW) {
+ try {
+ return InetAddress.getByName(address);
+ } catch (Exception e) {
+ log
+ .debug("Exception during nextHopAddress resolution : "
+ + e.getMessage());
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public String getNextHopL2RWUsageError() {
+ return "Could not resolve NextHop IP Address for the selected Switch.<br>"
+ + "Please Check the following configurations.<br>"
+ + "1. Is the NextHop IP address directly connected to the Selected Switch<br>"
+ + "2. If appropriate Subnet Configurations are done in the Switch Manager<br>"
+ + "3. If the Nexthop IP-Address is Correct";
+ }
+
+ public boolean isL2AddressValid(String mac) {
+ if (mac == null) {
+ return false;
+ }
+
+ Pattern macPattern = Pattern
+ .compile("([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}");
+ Matcher mm = macPattern.matcher(mac);
+ if (!mm.matches()) {
+ log
+ .debug(
+ "Ethernet address {} is not valid. Example: 00:05:b9:7c:81:5f",
+ mac);
+ return false;
+ }
+ return true;
+ }
+
+ public boolean isPortValid(Switch sw, Short port) {
+ if (port < 1) {
+ log.debug("port {} is not valid", port);
+ return false;
+ }
+
+ if (sw == null) {
+ log
+ .debug("switch info is not available. Skip checking if port is part of a switch or not.");
+ return true;
+ }
+
+ Set<NodeConnector> nodeConnectorSet = sw.getNodeConnectors();
+ for (NodeConnector nodeConnector : nodeConnectorSet) {
+ if (((Short) nodeConnector.getID()).equals(port)) {
+ return true;
+ }
+ }
+ log.debug("port {} is not a valid port of node {}", port, sw.getNode());
+ return false;
+ }
+
+ public boolean isVlanIdValid(String vlanId) {
+ int vlan = Integer.decode(vlanId);
+ return ((vlan >= 0) && (vlan < 4096));
+ }
+
+ public boolean isVlanPriorityValid(String vlanPriority) {
+ int pri = Integer.decode(vlanPriority);
+ return ((pri >= 0) && (pri < 8));
+ }
+
+ public boolean isTOSBitsValid(String tosBits) {
+ int tos = Integer.decode(tosBits);
+ return ((tos >= 0) && (tos < 64));
+ }
+
+ public boolean isTpPortValid(String tpPort) {
+ int port = Integer.decode(tpPort);
+ return ((port > 0) && (port <= 0xffff));
+ }
+
+ public boolean isTimeoutValid(String timeout) {
+ int to = Integer.decode(timeout);
+ return ((to >= 0) && (to <= 0xffff));
+ }
+
+ private boolean conflictWithContainerFlow(IContainer container,
+ StringBuffer resultStr) {
+ // Return true if it's default container
+ if (container.getName().equals(GlobalConstants.DEFAULT.toString())) {
+ return false;
+ }
+
+ // No container flow = no conflict
+ List<ContainerFlow> cFlowList = container.getContainerFlows();
+ if (((cFlowList == null)) || cFlowList.isEmpty()) {
+ return false;
+ }
+
+ // Check against each container's flow
+ Flow flow = this.getFlow();
+
+ // Configuration is rejected if it conflicts with _all_ the container flows
+ for (ContainerFlow cFlow : cFlowList) {
+ if (cFlow.allowsFlow(flow)) {
+ log
+ .trace("Config is congruent with at least one container flow");
+ return false;
+ }
+ }
+ String msg = "Flow Config conflicts with all existing container flows";
+ resultStr.append(msg);
+ log.trace(msg);
+
+ return true;
+ }
+
+ public boolean isValid(IContainer container, StringBuffer resultStr) {
+ EtherIPType etype = EtherIPType.ANY;
+ EtherIPType ipsrctype = EtherIPType.ANY;
+ EtherIPType ipdsttype = EtherIPType.ANY;
+
+ String containerName = (container == null) ? GlobalConstants.DEFAULT
+ .toString() : container.getName();
+ ISwitchManager switchManager = (ISwitchManager) ServiceHelper
+ .getInstance(ISwitchManager.class, containerName, this);
+
+ Switch sw = null;
+ try {
+ if (name == null) {
+ resultStr.append(String.format("Name is null"));
+ return false;
+ }
+ if (node == null) {
+ resultStr.append(String.format("Node is null"));
+ return false;
+ }
+ if (switchManager != null) {
+ for (Switch device : switchManager.getNetworkDevices()) {
+ if (device.getNode().equals(node)) {
+ sw = device;
+ break;
+ }
+ }
+ if (sw == null) {
+ resultStr
+ .append(String.format("Node %s not found", node));
+ return false;
+ }
+ } else {
+ log.debug("switchmanager is not set yet");
+ }
+
+ if (priority != null) {
+ if (Integer.decode(priority) < 0
+ || (Integer.decode(priority) > 65535)) {
+ resultStr.append(String.format(
+ "priority %s is not in the range 0 - 65535",
+ priority));
+ return false;
+ }
+ }
+
+ // make sure it's a valid number
+ if (cookie != null)
+ Long.decode(cookie);
+
+ if (ingressPort != null) {
+ Short port = Short.decode(ingressPort);
+ if (isPortValid(sw, port) == false) {
+ resultStr
+ .append(String
+ .format(
+ "Ingress port %d is not valid for the Switch",
+ port));
+ if ((container != null)
+ && !container.getName().equals(
+ GlobalConstants.DEFAULT.toString())) {
+ resultStr
+ .append(" in Container " + container.getName());
+ }
+ return false;
+ }
+ }
+
+ if ((vlanId != null) && !isVlanIdValid(vlanId)) {
+ resultStr.append(String.format(
+ "Vlan ID %s is not in the range 0 - 4095", vlanId));
+ return false;
+ }
+
+ if ((vlanPriority != null) && !isVlanPriorityValid(vlanPriority)) {
+ resultStr.append(String.format(
+ "Vlan priority %s is not in the range 0 - 7",
+ vlanPriority));
+ return false;
+ }
+
+ if (etherType != null) {
+ int type = Integer.decode(etherType);
+ if ((type < 0) || (type > 0xffff)) {
+ resultStr.append(String.format(
+ "Ethernet type %s is not valid", etherType));
+ return false;
+ } else {
+ if (type == 0x800)
+ etype = EtherIPType.V4;
+ else if (type == 0x86dd)
+ etype = EtherIPType.V6;
+ }
+ }
+
+ if ((tosBits != null) && !isTOSBitsValid(tosBits)) {
+ resultStr.append(String.format(
+ "IP ToS bits %s is not in the range 0 - 63", tosBits));
+ return false;
+ }
+
+ if ((tpSrc != null) && !isTpPortValid(tpSrc)) {
+ resultStr.append(String.format(
+ "Transport source port %s is not valid", tpSrc));
+ return false;
+ }
+ if ((tpDst != null) && !isTpPortValid(tpDst)) {
+ resultStr.append(String.format(
+ "Transport destination port %s is not valid", tpDst));
+ return false;
+ }
+
+ if ((dlSrc != null) && !isL2AddressValid(dlSrc)) {
+ resultStr
+ .append(String
+ .format(
+ "Ethernet source address %s is not valid. Example: 00:05:b9:7c:81:5f",
+ dlSrc));
+ return false;
+ }
+
+ if ((dlDst != null) && !isL2AddressValid(dlDst)) {
+ resultStr
+ .append(String
+ .format(
+ "Ethernet destination address %s is not valid. Example: 00:05:b9:7c:81:5f",
+ dlDst));
+ return false;
+ }
+
+ if (nwSrc != null) {
+ if (NetUtils.isIPv4AddressValid(nwSrc)) {
+ ipsrctype = EtherIPType.V4;
+ } else if (NetUtils.isIPv6AddressValid(nwSrc)) {
+ ipsrctype = EtherIPType.V6;
+ } else {
+ resultStr.append(String.format(
+ "IP source address %s is not valid", nwSrc));
+ return false;
+ }
+ }
+
+ if (nwDst != null) {
+ if (NetUtils.isIPv4AddressValid(nwDst)) {
+ ipdsttype = EtherIPType.V4;
+ } else if (NetUtils.isIPv6AddressValid(nwDst)) {
+ ipdsttype = EtherIPType.V6;
+ } else {
+ resultStr.append(String.format(
+ "IP destination address %s is not valid", nwDst));
+ return false;
+ }
+ }
+
+ if (etype != EtherIPType.ANY) {
+ if ((ipsrctype != EtherIPType.ANY) && (ipsrctype != etype)) {
+ resultStr.append(String
+ .format("Type mismatch between Ethernet & Src IP"));
+ return false;
+ }
+ if ((ipdsttype != EtherIPType.ANY) && (ipdsttype != etype)) {
+ resultStr.append(String
+ .format("Type mismatch between Ethernet & Dst IP"));
+ return false;
+ }
+ }
+ if (ipsrctype != ipdsttype) {
+ if (!((ipsrctype == EtherIPType.ANY) || (ipdsttype == EtherIPType.ANY))) {
+ resultStr
+ .append(String.format("IP Src Dest Type mismatch"));
+ return false;
+ }
+ }
+
+ if ((idleTimeout != null) && !isTimeoutValid(idleTimeout)) {
+ resultStr.append(String.format(
+ "Idle Timeout value %s is not valid", idleTimeout));
+ return false;
+ }
+
+ if ((hardTimeout != null) && !isTimeoutValid(hardTimeout)) {
+ resultStr.append(String.format(
+ "Hard Timeout value %s is not valid", hardTimeout));
+ return false;
+ }
+
+ Matcher sstr;
+ if (actions != null && !actions.isEmpty()) {
+ for (String actiongrp : actions) {
+ // check output ports
+ sstr = Pattern.compile("OUTPUT=(.*)").matcher(actiongrp);
+ if (sstr.matches()) {
+ for (String t : sstr.group(1).split(",")) {
+ Matcher n = Pattern.compile("(?:(\\d+))")
+ .matcher(t);
+ if (n.matches()) {
+ if (n.group(1) != null) {
+ Short port = Short.parseShort(n.group(1));
+ if (isPortValid(sw, port) == false) {
+ resultStr
+ .append(String
+ .format(
+ "Output port %d is not valid for this switch",
+ port));
+ if ((container != null)
+ && !container.getName().equals(
+ GlobalConstants.DEFAULT
+ .toString())) {
+ resultStr.append(" in Container "
+ + container.getName());
+ }
+ return false;
+ }
+ }
+ }
+ }
+ continue;
+ }
+ // Check src IP
+ sstr = Pattern.compile(ActionType.FLOOD.toString())
+ .matcher(actiongrp);
+ if (sstr.matches()) {
+ if (container != null) {
+ resultStr.append(String.format(
+ "flood is not allowed in container %s",
+ container.getName()));
+ return false;
+ }
+ continue;
+ }
+ // Check src IP
+ sstr = Pattern.compile(
+ ActionType.SET_NW_SRC.toString() + "=(.*)")
+ .matcher(actiongrp);
+ if (sstr.matches()) {
+ if (!NetUtils.isIPv4AddressValid(sstr.group(1))) {
+ resultStr.append(String.format(
+ "IP source address %s is not valid", sstr
+ .group(1)));
+ return false;
+ }
+ continue;
+ }
+ // Check dst IP
+ sstr = Pattern.compile(
+ ActionType.SET_NW_DST.toString() + "=(.*)")
+ .matcher(actiongrp);
+ if (sstr.matches()) {
+ if (!NetUtils.isIPv4AddressValid(sstr.group(1))) {
+ resultStr.append(String.format(
+ "IP destination address %s is not valid",
+ sstr.group(1)));
+ return false;
+ }
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_VLAN_ID.toString() + "=(.*)")
+ .matcher(actiongrp);
+ if (sstr.matches()) {
+ if ((sstr.group(1) != null)
+ && !isVlanIdValid(sstr.group(1))) {
+ resultStr.append(String.format(
+ "Vlan ID %s is not in the range 0 - 4095",
+ sstr.group(1)));
+ return false;
+ }
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_VLAN_PCP.toString() + "=(.*)")
+ .matcher(actiongrp);
+ if (sstr.matches()) {
+ if ((sstr.group(1) != null)
+ && !isVlanPriorityValid(sstr.group(1))) {
+ resultStr
+ .append(String
+ .format(
+ "Vlan priority %s is not in the range 0 - 7",
+ sstr.group(1)));
+ return false;
+ }
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_DL_SRC.toString() + "=(.*)")
+ .matcher(actiongrp);
+ if (sstr.matches()) {
+ if ((sstr.group(1) != null)
+ && !isL2AddressValid(sstr.group(1))) {
+ resultStr
+ .append(String
+ .format(
+ "Ethernet source address %s is not valid. Example: 00:05:b9:7c:81:5f",
+ sstr.group(1)));
+ return false;
+ }
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_DL_DST.toString() + "=(.*)")
+ .matcher(actiongrp);
+ if (sstr.matches()) {
+ if ((sstr.group(1) != null)
+ && !isL2AddressValid(sstr.group(1))) {
+ resultStr
+ .append(String
+ .format(
+ "Ethernet destination address %s is not valid. Example: 00:05:b9:7c:81:5f",
+ sstr.group(1)));
+ return false;
+ }
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_NW_TOS.toString() + "=(.*)")
+ .matcher(actiongrp);
+ if (sstr.matches()) {
+ if ((sstr.group(1) != null)
+ && !isTOSBitsValid(sstr.group(1))) {
+ resultStr
+ .append(String
+ .format(
+ "IP ToS bits %s is not in the range 0 - 63",
+ sstr.group(1)));
+ return false;
+ }
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_TP_SRC.toString() + "=(.*)")
+ .matcher(actiongrp);
+ if (sstr.matches()) {
+ if ((sstr.group(1) != null)
+ && !isTpPortValid(sstr.group(1))) {
+ resultStr.append(String.format(
+ "Transport source port %s is not valid",
+ sstr.group(1)));
+ return false;
+ }
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_TP_DST.toString() + "=(.*)")
+ .matcher(actiongrp);
+ if (sstr.matches()) {
+ if ((sstr.group(1) != null)
+ && !isTpPortValid(sstr.group(1))) {
+ resultStr
+ .append(String
+ .format(
+ "Transport destination port %s is not valid",
+ sstr.group(1)));
+ return false;
+ }
+ continue;
+ }
+ sstr = Pattern.compile(
+ ActionType.SET_NEXT_HOP.toString() + "=(.*)")
+ .matcher(actiongrp);
+ if (sstr.matches()) {
+ String nextHopInfo = sstr.group(1);
+ String values[] = nextHopInfo.split("//");
+ String address = values[0].trim();
+
+ if ((address == null) || !isOutputNextHopValid(address)) {
+ resultStr.append(String.format(
+ "next hop %s is not valid", sstr.group(1)));
+ return false;
+ }
+ continue;
+ }
+ }
+ }
+ // Check against the container flow
+ if ((container != null)
+ && conflictWithContainerFlow(container, resultStr)) {
+ return false;
+ }
+ } catch (NumberFormatException e) {
+ resultStr.append(String.format("Invalid number format %s", e
+ .getMessage()));
+ return false;
+ }
+
+ return true;
+ }
+
+ public FlowEntry getFlowEntry() {
+ return new FlowEntry(FlowConfig.staticFlowsGroup, this.name, this
+ .getFlow(), this.getNode());
+ }
+
+ public Flow getFlow() {
+ Match match = new Match();
+
+ if (this.ingressPort != null) {
+ match.setField(MatchType.IN_PORT, NodeConnectorCreator
+ .createOFNodeConnector(Short.parseShort(ingressPort),
+ getNode()));
+ }
+ if (this.dlSrc != null) {
+ match.setField(MatchType.DL_SRC, HexEncode
+ .bytesFromHexString(this.dlSrc));
+ }
+ if (this.dlDst != null) {
+ match.setField(MatchType.DL_DST, HexEncode
+ .bytesFromHexString(this.dlDst));
+ }
+ if (this.etherType != null) {
+ match.setField(MatchType.DL_TYPE, Integer.decode(etherType)
+ .shortValue());
+ }
+ if (this.vlanId != null) {
+ match.setField(MatchType.DL_VLAN, Short.parseShort(this.vlanId));
+ }
+ if (this.vlanPriority != null) {
+ match.setField(MatchType.DL_VLAN_PR, Byte
+ .parseByte(this.vlanPriority));
+ }
+ if (this.nwSrc != null) {
+ String parts[] = this.nwSrc.split("/");
+ InetAddress ip = NetUtils.parseInetAddress(parts[0]);
+ InetAddress mask = null;
+ if (parts.length > 1) {
+ int maskLen = Integer.parseInt(parts[1]);
+ mask = NetUtils.getInetNetworkMask(maskLen,
+ ip instanceof Inet6Address);
+ }
+ match.setField(MatchType.NW_SRC, ip, mask);
+ }
+ if (this.nwDst != null) {
+ String parts[] = this.nwDst.split("/");
+ InetAddress ip = NetUtils.parseInetAddress(parts[0]);
+ InetAddress mask = null;
+ if (parts.length > 1) {
+ int maskLen = Integer.parseInt(parts[1]);
+ mask = NetUtils.getInetNetworkMask(maskLen,
+ ip instanceof Inet6Address);
+ }
+ match.setField(MatchType.NW_DST, ip, mask);
+ }
+ if (this.protocol != null) {
+ match.setField(MatchType.NW_PROTO, IPProtocols
+ .getProtocolNumberByte(this.protocol));
+ }
+ if (this.tosBits != null) {
+ match.setField(MatchType.NW_TOS, Byte.parseByte(this.tosBits));
+ }
+ if (this.tpSrc != null) {
+ match.setField(MatchType.TP_SRC, Integer.valueOf(this.tpSrc)
+ .shortValue());
+ }
+ if (this.tpDst != null) {
+ match.setField(MatchType.TP_DST, Integer.valueOf(this.tpDst)
+ .shortValue());
+ }
+
+ Flow flow = new Flow(match, getActionList());
+ if (this.cookie != null) {
+ flow.setId(Long.parseLong(cookie));
+ }
+ if (this.hardTimeout != null) {
+ flow.setHardTimeout(Short.parseShort(this.hardTimeout));
+ }
+ if (this.idleTimeout != null) {
+ flow.setIdleTimeout(Short.parseShort(idleTimeout));
+ }
+ if (this.priority != null) {
+ flow.setPriority(Integer.decode(this.priority).shortValue());
+ }
+ return flow;
+ }
+
+ public boolean isOutputNextHopValid(String onh) {
+ if (onh == null) {
+ return false;
+ }
+ /*
+ * For now, only takes IPv4 or IPv6 address
+ */
+ return (NetUtils.isIPv4AddressValid(onh) || NetUtils
+ .isIPv6AddressValid(onh));
+ }
+
+ public boolean isByNameAndNodeIdEqual(FlowConfig that) {
+ return (this.name.equals(that.name) && this.node.equals(that.node));
+ }
+
+ public boolean isByNameAndNodeIdEqual(String name, Node node) {
+ return (this.name.equals(name) && this.node.equals(node));
+ }
+
+ public boolean onNode(Node node) {
+ return this.node.equals(node);
+ }
+
+ public static List<String> getSupportedNextHopTypes() {
+ List<String> s = new ArrayList<String>();
+ for (SetNextHopType nh : SetNextHopType.values()) {
+ s.add(nh.toString());
+ }
+ return s;
+ }
+
+ public void toggleStatus() {
+ installInHw = (installInHw == null) ? "true" : (installInHw
+ .equals("true")) ? "false" : "true";
+ }
+
+ /*
+ * Parses the actions string and return the List of SAL Action
+ * No syntax check run, as this function will be called when the
+ * config validation check has already been performed
+ */
+ private List<Action> getActionList() {
+ List<Action> actionList = new ArrayList<Action>();
+
+ if (actions != null) {
+ Matcher sstr;
+ for (String actiongrp : actions) {
+ sstr = Pattern.compile(ActionType.OUTPUT + "=(.*)").matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ for (String t : sstr.group(1).split(",")) {
+ Matcher n = Pattern.compile("(?:(\\d+))").matcher(t);
+ if (n.matches()) {
+ if (n.group(1) != null) {
+ short ofPort = Short.parseShort(n.group(1));
+ actionList.add(new Output(NodeConnectorCreator
+ .createOFNodeConnector(ofPort, this
+ .getNode())));
+ }
+ }
+ }
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.DROP.toString()).matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new Drop());
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.LOOPBACK.toString()).matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new Loopback());
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.FLOOD.toString()).matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new Flood());
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.SW_PATH.toString()).matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new SwPath());
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.HW_PATH.toString()).matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new HwPath());
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.CONTROLLER.toString())
+ .matcher(actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new Controller());
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_VLAN_ID.toString() + "=(.*)").matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new SetVlanId(Short
+ .parseShort(sstr.group(1))));
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_VLAN_PCP.toString() + "=(.*)").matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList
+ .add(new SetVlanPcp(Byte.parseByte(sstr.group(1))));
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.POP_VLAN.toString()).matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new PopVlan());
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_DL_SRC.toString() + "=(.*)").matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new SetDlSrc(HexEncode
+ .bytesFromHexString(sstr.group(1))));
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_DL_DST.toString() + "=(.*)").matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new SetDlDst(HexEncode
+ .bytesFromHexString(sstr.group(1))));
+ continue;
+ }
+ sstr = Pattern.compile(
+ ActionType.SET_NW_SRC.toString() + "=(.*)").matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new SetNwSrc(NetUtils.parseInetAddress(sstr
+ .group(1))));
+ continue;
+ }
+ sstr = Pattern.compile(
+ ActionType.SET_NW_DST.toString() + "=(.*)").matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new SetNwDst(NetUtils.parseInetAddress(sstr
+ .group(1))));
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_NW_TOS.toString() + "=(.*)").matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new SetNwTos(Byte.parseByte(sstr.group(1))));
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_TP_SRC.toString() + "=(.*)").matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList
+ .add(new SetTpSrc(Integer.valueOf(sstr.group(1))));
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_TP_DST.toString() + "=(.*)").matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ actionList
+ .add(new SetTpDst(Integer.valueOf(sstr.group(1))));
+ continue;
+ }
+
+ sstr = Pattern.compile(
+ ActionType.SET_NEXT_HOP.toString() + "=(.*)").matcher(
+ actiongrp);
+ if (sstr.matches()) {
+ log.warn("We do not handle next hop action yet....");
+ continue;
+ }
+ }
+ }
+ return actionList;
+ }
+
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 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.forwardingrulesmanager;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.match.Match;
+
+/**
+ * Represents a flow applications request Forwarding Rules Manager to install
+ * on a network node. A FlowEntry is constituted of a flow (match + actions),
+ * the target network node, and the flow name. It also includes a group name.
+ * For instance the flows constituting a policy all share the same group name.
+ */
+public class FlowEntry implements Cloneable, Serializable {
+ private static final long serialVersionUID = 1L;
+ private String groupName; // group name
+ private String flowName; // flow name (may be null)
+ private Node node; // network node where to install the flow
+ private Flow flow; // match + action
+
+ public FlowEntry(String groupName, String flowName, Flow flow, Node node) {
+ this.groupName = groupName;
+ this.flow = flow;
+ this.node = node;
+ this.flowName = (flowName != null) ? flowName : constructFlowName();
+ }
+
+ public String getGroupName() {
+ return groupName;
+ }
+
+ public void setGroupName(String name) {
+ this.groupName = name;
+ }
+
+ /**
+ * Return the actual Flow contained in this entry
+ *
+ * @return the flow
+ */
+ public Flow getFlow() {
+ return flow;
+ }
+
+ public Node getNode() {
+ return node;
+ }
+
+ public void setNode(Node n) {
+ this.node = n;
+ }
+
+ public String getFlowName() {
+ return flowName;
+ }
+
+ public void setFlowName(String n) {
+ this.flowName = n;
+ }
+
+ @Override
+ public FlowEntry clone() {
+ FlowEntry cloned = null;
+ try {
+ cloned = (FlowEntry) super.clone();
+ cloned.flow = this.flow.clone();
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ }
+ return cloned;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeBuilder.reflectionHashCode(this);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsBuilder.reflectionEquals(this, obj);
+ }
+
+ @Override
+ public String toString() {
+ return "FlowEntry[flowName = " + flowName + ", groupName = "
+ + groupName + ",node = " + node + ", flow = " + flow + "]";
+ }
+
+ private String constructFlowName() {
+ return this.groupName + "_" + new Date().toString();
+ }
+
+ public boolean equalsByNodeAndName(Node node, String flowName) {
+ return this.node.equals(node) && this.flowName.equals(flowName);