diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java index fb7cd7d44d2167386a78534725956737adffdaad..428dc7f7e07ceb0598b2854668d6705a2cfa7efe 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java @@ -59,7 +59,8 @@ public interface IOFSwitch { NORMAL("normal"), // normal port (default) TUNNEL("tunnel"), // tunnel port UPLINK("uplink"), // uplink port (on a virtual switch) - MANAGEMENT("management"); // for in-band management + MANAGEMENT("management"), // for in-band management + TUNNEL_LOOPBACK("tunnel-loopback"); private String value; OFPortType(String v) { diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java index 8998472cfc808bbcf5dac4e8b4f1c08f953626d4..17bbcf07e292b25b7ad2dae2c675a80389aa199e 100644 --- a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java +++ b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java @@ -97,9 +97,9 @@ public abstract class OFSwitchBase implements IOFSwitch { // Lock to protect modification of the port maps. We only need to // synchronize on modifications. For read operations we are fine since // we rely on ConcurrentMaps which works for our use case. - private Object portLock; + protected Object portLock; // Map port numbers to the appropriate OFPhysicalPort - private ConcurrentHashMap<Short, OFPhysicalPort> portsByNumber; + protected ConcurrentHashMap<Short, OFPhysicalPort> portsByNumber; // Map port names to the appropriate OFPhyiscalPort // XXX: The OF spec doesn't specify if port names need to be unique but // according it's always the case in practice. diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java index 84a76fd17d272312fc66f1fe0e0ab98440abb34d..84d1cc958ef8354819e5eabe3faf04ecb915e5a6 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java @@ -19,7 +19,6 @@ package net.floodlightcontroller.linkdiscovery; import java.util.Map; import java.util.Set; - import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.routing.Link; import net.floodlightcontroller.topology.NodePortTuple; @@ -87,4 +86,10 @@ public interface ILinkDiscoveryService extends IFloodlightService { * @param autoPortFastFeature */ public void setAutoPortFastFeature(boolean autoPortFastFeature); + + /** + * Get the map of node-port tuples from link DB + */ + public Map<NodePortTuple, Set<Link>> getPortLinks(); + } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java index 24dd4442584015900fa3adfc991f42851fcd52f8..3191ab7a9a724c599704122bc22ea90e5cfa614e 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java @@ -599,10 +599,10 @@ public class LinkDiscoveryManager implements IOFMessageListener, * @param port * @return */ - protected List<OFAction> getDiscoveryActions (short port){ + protected List<OFAction> getDiscoveryActions (IOFSwitch sw, OFPhysicalPort port){ // set actions List<OFAction> actions = new ArrayList<OFAction>(); - actions.add(new OFActionOutput(port, (short) 0)); + actions.add(new OFActionOutput(port.getPortNumber(), (short) 0)); return actions; } @@ -731,9 +731,15 @@ public class LinkDiscoveryManager implements IOFMessageListener, po.setBufferId(OFPacketOut.BUFFER_ID_NONE); po.setInPort(OFPort.OFPP_NONE); - List<OFAction> actions = getDiscoveryActions(port); + List<OFAction> actions = getDiscoveryActions(iofSwitch, ofpPort); po.setActions(actions); - po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH); + + short actionLength = 0; + Iterator <OFAction> actionIter = actions.iterator(); + while (actionIter.hasNext()) { + actionLength += actionIter.next().getLength(); + } + po.setActionsLength(actionLength); // set data po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() @@ -2284,4 +2290,4 @@ public class LinkDiscoveryManager implements IOFMessageListener, else log.info("Setting autoportfast feature to OFF"); } -} \ No newline at end of file +} diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LBVip.java b/src/main/java/net/floodlightcontroller/loadbalancer/LBVip.java index fd8c0bd451ab87c75bdecbcc94016126006bb945..7872175d45517fa9f21a1fcceee20a06087f76e7 100644 --- a/src/main/java/net/floodlightcontroller/loadbalancer/LBVip.java +++ b/src/main/java/net/floodlightcontroller/loadbalancer/LBVip.java @@ -32,6 +32,8 @@ public class LBVip { protected MACAddress proxyMac; + public static String LB_PROXY_MAC= "12:34:56:78:90:12"; + public LBVip() { this.id = String.valueOf((int) (Math.random()*10000)); this.name = null; @@ -47,7 +49,7 @@ public class LBVip { this.address = 0; this.status = 0; - this.proxyMac = MACAddress.valueOf("12:34:56:78:90:12"); + this.proxyMac = MACAddress.valueOf(LB_PROXY_MAC); } public String pickPool(IPClient client) { diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java index 3893ed5708d29acf52e80c78b922ee261cee1d88..25303373c5b363b85a408e30cd1e4b01e155d61e 100644 --- a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java +++ b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java @@ -133,7 +133,8 @@ public class LoadBalancer implements IFloodlightModule, public boolean isCallbackOrderingPrereq(OFType type, String name) { return (type.equals(OFType.PACKET_IN) && (name.equals("topology") || - name.equals("devicemanager"))); + name.equals("devicemanager") || + name.equals("virtualizer"))); } @Override @@ -504,8 +505,8 @@ public class LoadBalancer implements IFloodlightModule, fm.setPriority(Short.MAX_VALUE); if (inBound) { - entryName = "inbound-vip-"+ member.vipId+"client-"+client.ipAddress+"-port-"+client.targetPort - +"srcswitch-"+path.get(0).getNodeId()+"sw-"+sw; + entryName = "inbound-vip-"+ member.vipId+"-client-"+client.ipAddress+"-port-"+client.targetPort + +"-srcswitch-"+path.get(0).getNodeId()+"-sw-"+sw; matchString = "nw_src="+IPv4.fromIPv4Address(client.ipAddress)+"," + "nw_proto="+String.valueOf(client.nw_proto)+"," + "tp_src="+String.valueOf(client.srcPort & 0xffff)+"," @@ -521,8 +522,8 @@ public class LoadBalancer implements IFloodlightModule, "output="+path.get(i+1).getPortId(); } } else { - entryName = "outbound-vip-"+ member.vipId+"client-"+client.ipAddress+"-port-"+client.targetPort - +"srcswitch-"+path.get(0).getNodeId()+"sw-"+sw; + entryName = "outbound-vip-"+ member.vipId+"-client-"+client.ipAddress+"-port-"+client.targetPort + +"-srcswitch-"+path.get(0).getNodeId()+"-sw-"+sw; matchString = "nw_dst="+IPv4.fromIPv4Address(client.ipAddress)+"," + "nw_proto="+String.valueOf(client.nw_proto)+"," + "tp_dst="+String.valueOf(client.srcPort & 0xffff)+"," @@ -619,11 +620,13 @@ public class LoadBalancer implements IFloodlightModule, pool = new LBPool(); pools.put(pool.id, pool); - if (pool.vipId != null) + if (pool.vipId != null && vips.containsKey(pool.vipId)) vips.get(pool.vipId).pools.add(pool.id); - else - log.error("pool must be specified with non-null vip-id"); - + else { + log.error("specified vip-id must exist"); + pool.vipId = null; + pools.put(pool.id, pool); + } return pool; } @@ -635,7 +638,12 @@ public class LoadBalancer implements IFloodlightModule, @Override public int removePool(String poolId) { - if(pools.containsKey(poolId)){ + LBPool pool; + pool = pools.get(poolId); + + if(pools!=null){ + if (pool.vipId != null) + vips.get(pool.vipId).pools.remove(poolId); pools.remove(poolId); return 0; } else { @@ -671,12 +679,14 @@ public class LoadBalancer implements IFloodlightModule, public LBMember createMember(LBMember member) { if (member == null) member = new LBMember(); - + + members.put(member.id, member); + memberIpToId.put(member.address, member.id); + if (member.poolId != null && pools.get(member.poolId) != null) { member.vipId = pools.get(member.poolId).vipId; - members.put(member.id, member); - pools.get(member.poolId).members.add(member.id); - memberIpToId.put(member.address, member.id); + if (!pools.get(member.poolId).members.contains(member.id)) + pools.get(member.poolId).members.add(member.id); } else log.error("member must be specified with non-null pool_id"); @@ -691,7 +701,12 @@ public class LoadBalancer implements IFloodlightModule, @Override public int removeMember(String memberId) { - if(members.containsKey(memberId)){ + LBMember member; + member = members.get(memberId); + + if(member != null){ + if (member.poolId != null) + pools.get(member.poolId).members.remove(memberId); members.remove(memberId); return 0; } else { diff --git a/src/main/java/net/floodlightcontroller/packet/IPv4.java b/src/main/java/net/floodlightcontroller/packet/IPv4.java index 81d2c6c079308d268b68bcda1f7707ed492160ad..76a140ed002bad4ab93db3488a771b2141223376 100644 --- a/src/main/java/net/floodlightcontroller/packet/IPv4.java +++ b/src/main/java/net/floodlightcontroller/packet/IPv4.java @@ -403,7 +403,11 @@ public class IPv4 extends BasePacket { int result = 0; for (int i = 0; i < 4; ++i) { - result |= Integer.valueOf(octets[i]) << ((3-i)*8); + int oct = Integer.valueOf(octets[i]); + if (oct > 255 || oct < 0) + throw new IllegalArgumentException("Octet values in specified" + + " IPv4 address must be 0 <= value <= 255"); + result |= oct << ((3-i)*8); } return result; } diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java index 5eacd5895d2c194062d26a0de3fbb7d247153de5..e0496a4e54530193e2c307540d8f191c26710efb 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java @@ -120,6 +120,10 @@ public class TopologyManager implements */ protected boolean dtLinksUpdated; + /** Flag that indicates if tunnel ports were updated or not + */ + protected boolean tunnelPortsUpdated; + protected final int TOPOLOGY_COMPUTE_INTERVAL_MS = 500; /** @@ -152,6 +156,7 @@ public class TopologyManager implements boolean newInstanceFlag; linksUpdated = false; dtLinksUpdated = false; + tunnelPortsUpdated = false; applyUpdates(); newInstanceFlag = createNewInstance(); lastUpdateTime = new Date(); @@ -952,11 +957,13 @@ public class TopologyManager implements public void addTunnelPort(long sw, short port) { NodePortTuple npt = new NodePortTuple(sw, port); tunnelPorts.add(npt); + tunnelPortsUpdated = true; } public void removeTunnelPort(long sw, short port) { NodePortTuple npt = new NodePortTuple(sw, port); tunnelPorts.remove(npt); + tunnelPortsUpdated = true; } /** @@ -1257,6 +1264,7 @@ public class TopologyManager implements this.clear(); linksUpdated = true; dtLinksUpdated = true; + tunnelPortsUpdated = true; createNewInstance(); lastUpdateTime = new Date(); } diff --git a/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java b/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java index 9b3a6ca608aee1b37498e1b53f623ab28cb1aa10..e5a9c01e0093e29055bb75943909860490347e43 100644 --- a/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java +++ b/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java @@ -88,7 +88,10 @@ public abstract class OFStatisticsMessageBase extends OFMessage implements * element */ public OFStatistics getFirstStatistics() { - if (statistics == null || statistics.size() != 1) { + if (statistics == null ) { + throw new IllegalArgumentException("Invariant violation: statistics message of type "+statisticType+" is null"); + } + if (statistics.size() != 1) { throw new IllegalArgumentException("Invariant violation: statistics message of type "+statisticType+" contains "+statistics.size() +" statreq/reply messages in its body (should be 1)"); } diff --git a/src/main/java/org/openflow/protocol/action/OFActionType.java b/src/main/java/org/openflow/protocol/action/OFActionType.java index b0c2c47d9af7b3181539f9f7a9174bf709a96bc4..18229170b77dc1a01984cfc9f156272f5ad858c5 100644 --- a/src/main/java/org/openflow/protocol/action/OFActionType.java +++ b/src/main/java/org/openflow/protocol/action/OFActionType.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior * University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -94,7 +94,7 @@ public enum OFActionType { VENDOR (0xffff, OFActionVendor.class, new Instantiable<OFAction>() { @Override public OFAction instantiate() { - return new OFActionVendor(); + return new OFActionVendorGeneric(); }}); protected static OFActionType[] mapping; diff --git a/src/main/java/org/openflow/protocol/action/OFActionVendor.java b/src/main/java/org/openflow/protocol/action/OFActionVendor.java index b5a15c280cd5342cd28171f378f0afa212a1d3f3..5860ef1160ae43fe3606a582c8e7d7ae842820eb 100644 --- a/src/main/java/org/openflow/protocol/action/OFActionVendor.java +++ b/src/main/java/org/openflow/protocol/action/OFActionVendor.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior * University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -24,7 +24,7 @@ import org.jboss.netty.buffer.ChannelBuffer; * * @author David Erickson (daviderickson@cs.stanford.edu) */ -public class OFActionVendor extends OFAction { +public abstract class OFActionVendor extends OFAction { public static int MINIMUM_LENGTH = 8; protected int vendor; @@ -86,4 +86,9 @@ public class OFActionVendor extends OFAction { } return true; } + + @Override + public String toString() { + return super.toString() + "; vendor=" + vendor; + } } diff --git a/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java b/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java new file mode 100644 index 0000000000000000000000000000000000000000..4f7859f5ec43e8e13ff8340c50f5cda6f2d2311c --- /dev/null +++ b/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java @@ -0,0 +1,96 @@ +/** +* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior +* University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package org.openflow.protocol.action; + + +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** A generic / unparsed vendor action. This action is returned by + * BasicFactory.readFromWire if no more specific OFVendorActionFactory + * is registered. + * + * @author David Erickson (daviderickson@cs.stanford.edu) + * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> + */ +public class OFActionVendorGeneric extends OFActionVendor { + public static int MINIMUM_LENGTH = 8; + + private final static byte[] EMPTY_ARRAY = new byte[0]; + + protected byte[] vendorData; + + public OFActionVendorGeneric() { + super(); + } + + public byte[] getVendorData() { + return vendorData; + } + + public void setVendorData(byte[] vendorData) { + this.vendorData = vendorData; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + + int vendorDataLength = this.getLength() - MINIMUM_LENGTH; + if (vendorDataLength > 0) { + vendorData = new byte[vendorDataLength]; + data.readBytes(vendorData); + } else { + vendorData = EMPTY_ARRAY; + } + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(this.vendor); + data.writeBytes(vendorData); + } + + @Override + public int hashCode() { + final int prime = 379; + int result = super.hashCode(); + result = prime * result + Arrays.hashCode(vendorData); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionVendorGeneric)) { + return false; + } + OFActionVendorGeneric other = (OFActionVendorGeneric) obj; + if (!Arrays.equals(vendorData, other.vendorData)) { + return false; + } + return true; + } +} diff --git a/src/main/java/org/openflow/protocol/factory/BasicFactory.java b/src/main/java/org/openflow/protocol/factory/BasicFactory.java index 7b06f2c0c91a5a8a966be3430d34f4dbbb9f23cc..c4d148337526ecbae5ea8d9aa67d89e657404da7 100644 --- a/src/main/java/org/openflow/protocol/factory/BasicFactory.java +++ b/src/main/java/org/openflow/protocol/factory/BasicFactory.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior * University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -25,6 +25,7 @@ import org.openflow.protocol.OFMessage; import org.openflow.protocol.OFType; import org.openflow.protocol.action.OFAction; import org.openflow.protocol.action.OFActionType; +import org.openflow.protocol.action.OFActionVendor; import org.openflow.protocol.statistics.OFStatistics; import org.openflow.protocol.statistics.OFStatisticsType; import org.openflow.protocol.statistics.OFVendorStatistics; @@ -45,6 +46,12 @@ import org.openflow.protocol.vendor.OFVendorId; public class BasicFactory implements OFMessageFactory, OFActionFactory, OFStatisticsFactory, OFVendorDataFactory { + private final OFVendorActionRegistry vendorActionRegistry; + + public BasicFactory() { + vendorActionRegistry = OFVendorActionRegistry.getInstance(); + } + /** * create and return a new instance of a message for OFType t. Also injects * factories for those message types that implement the *FactoryAware @@ -169,19 +176,42 @@ public class BasicFactory implements OFMessageFactory, OFActionFactory, (data.readerIndex() + demux.getLengthU()) > end)) return results; - ofa = getAction(demux.getType()); - ofa.readFrom(data); - if (OFAction.class.equals(ofa.getClass())) { - // advance the position for un-implemented messages - data.readerIndex(data.readerIndex()+(ofa.getLengthU() - - OFAction.MINIMUM_LENGTH)); - } + ofa = parseActionOne(demux.getType(), data); results.add(ofa); } return results; } + private OFAction parseActionOne(OFActionType type, ChannelBuffer data) { + OFAction ofa; + data.markReaderIndex(); + ofa = getAction(type); + ofa.readFrom(data); + + if(type == OFActionType.VENDOR) { + OFActionVendor vendorAction = (OFActionVendor) ofa; + + OFVendorActionFactory vendorActionFactory = vendorActionRegistry.get(vendorAction.getVendor()); + + if(vendorActionFactory != null) { + // if we have a specific vendorActionFactory for this vendor id, + // delegate to it for vendor-specific reparsing of the message + data.resetReaderIndex(); + OFActionVendor newAction = vendorActionFactory.readFrom(data); + if(newAction != null) + ofa = newAction; + } + } + + if (OFAction.class.equals(ofa.getClass())) { + // advance the position for un-implemented messages + data.readerIndex(data.readerIndex()+(ofa.getLengthU() - + OFAction.MINIMUM_LENGTH)); + } + return ofa; + } + @Override public OFActionFactory getActionFactory() { return this; diff --git a/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java b/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..ea27b6119820c834a00b5723f8c73efd910fbefc --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java @@ -0,0 +1,27 @@ +package org.openflow.protocol.factory; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.action.OFActionVendor; + +/** Interface contract for an actionfactory that creates vendor-specific actions. + * VendorActionFactories are registered with the BasicFactory for a specific + * vendor id. + * <p> + * <b>Note:</b> Implementations are expected to be thread-safe. + * + * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> + */ +public interface OFVendorActionFactory { + + /** parse the data from the wire, create and return a vendor-specific action. + * + * @param data contains a serialized vendor action at the current readerPosition. + * The full message is guaranteed to be available in the buffer. + * + * @return upon success returns a newly allocated vendor-specific + * action instance, and advances the readerPosition in data for the + * entire length. Upon failure, returns null and leaves the readerPosition + * in data unmodified. + */ + OFActionVendor readFrom(ChannelBuffer data); +} diff --git a/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java b/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java new file mode 100644 index 0000000000000000000000000000000000000000..190bdd4fd42f7bf4ef58dc88a9ea52399eb5a512 --- /dev/null +++ b/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java @@ -0,0 +1,34 @@ +package org.openflow.protocol.factory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** Singleton registry object that holds a mapping from vendor ids to vendor-specific + * mapping factories. Threadsafe. + * + * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> + */ +public class OFVendorActionRegistry { + private static class InstanceHolder { + private final static OFVendorActionRegistry instance = new OFVendorActionRegistry(); + } + + public static OFVendorActionRegistry getInstance() { + return InstanceHolder.instance; + } + private final Map <Integer, OFVendorActionFactory> vendorActionFactories; + + public OFVendorActionRegistry() { + vendorActionFactories = new ConcurrentHashMap<Integer, OFVendorActionFactory>(); + } + + public OFVendorActionFactory register(int vendorId, OFVendorActionFactory factory) { + return vendorActionFactories.put(vendorId, factory); + } + + public OFVendorActionFactory get(int vendorId) { + return vendorActionFactories.get(vendorId); + } + + +} diff --git a/src/main/java/org/openflow/util/IProducer.java b/src/main/java/org/openflow/util/IProducer.java index 789f5a2ba9ebb0a41f3bf3a54ffd3cde08636d56..52ae79a52b5dc47af63f71d0aceeb3ee26105727 100644 --- a/src/main/java/org/openflow/util/IProducer.java +++ b/src/main/java/org/openflow/util/IProducer.java @@ -2,7 +2,8 @@ package org.openflow.util; public interface IProducer { - public void registerConsumer (Class<?> iface, Object anObj); - public void deregisterConsumer (Class<?> iface, Object anObj); + public void registerConsumer(Class<?> iface, Object anObj); + + public void deregisterConsumer(Class<?> iface, Object anObj); } diff --git a/src/main/java/org/openflow/util/ProducerConsumer.java b/src/main/java/org/openflow/util/ProducerConsumer.java index 16f71868f07ee94c7feb3def7bf1b3539e7030bb..f2244ef3524483b0a2fc70e8f2ba433efd363c01 100644 --- a/src/main/java/org/openflow/util/ProducerConsumer.java +++ b/src/main/java/org/openflow/util/ProducerConsumer.java @@ -5,213 +5,219 @@ import java.util.Hashtable; import java.util.Map; import java.util.Set; - /** - * The following implement a producer/consumer design pattern in which - * both producers and consumers explicitly employ a centralized - * registration mechanism, and java Interfaces are used as contracts.<br> - * + * The following implement a producer/consumer design pattern in which both + * producers and consumers explicitly employ a centralized registration + * mechanism, and java Interfaces are used as contracts.<br> */ public class ProducerConsumer { - /* - * Class variables - */ - protected static ProducerConsumer singleton; - - /* - * Default constructor - */ - protected ProducerConsumer () - { - producerMap = new Hashtable<Class<?>, Set<IProducer>> (); - } - - /* - * Instance variables - */ - - // Interface/IProducer map - protected Map<Class<?>, Set<IProducer>> producerMap; - - - /* - * Protected methods - */ - - protected void _registerConsumer (Object consumer, Class<?> [] interfaces, Set<Class<?>> iSet, Set<Class<?>> iUniqueSet) - { - //*...Process all interfaces...*/ - for (Class<?> iface: interfaces) { - - //*...Protect against repeated interfaces...*/ - if (!iUniqueSet.contains (iface)) { - iUniqueSet.add (iface); - - Set<IProducer> producers = producerMap.get (iface); - - if (producers != null) { - for (IProducer producer: producers) - producer.registerConsumer (iface, consumer); - iSet.add (iface); - } - - //*...Recurse...*/ - _registerConsumer (consumer, iface.getInterfaces (), iSet, iUniqueSet); - } - } - } - - protected void _registerConsumer (Object consumer, Class<?> clazz, Set<Class<?>> iSet, Set<Class<?>> iUniqueSet) - { - if (clazz != null) { - //*...Process all interfaces...*/ - _registerConsumer (consumer, clazz.getInterfaces (), iSet, iUniqueSet); - - //*...Recurse the class hierarchy...*/ - _registerConsumer (consumer, clazz.getSuperclass (), iSet, iUniqueSet); - } - } - - protected int _deregisterConsumer (Object consumer, Class<?> [] interfaces, Set<Class<?>> iUniqueSet) - { - int count = 0; - - //*...Process all interfaces...*/ - for (Class<?> iface: interfaces) { - - //*...Protect against repeated interfaces...*/ - if (!iUniqueSet.contains (iface)) { - iUniqueSet.add (iface); - - Set<IProducer> producers = producerMap.get (iface); - - if (producers != null) { - for (IProducer producer: producers) - producer.deregisterConsumer (iface, consumer); - - count ++; - } - - //*...Recurse...*/ - count += _deregisterConsumer (consumer, iface.getInterfaces (), iUniqueSet); - } - } - - return count; - } - - protected int _deregisterConsumer (Object consumer, Class<?> clazz, Set<Class<?>> iUniqueSet) - { - int count = 0; - - if (clazz != null) { - //*...Process all interfaces...*/ - count += _deregisterConsumer (consumer, clazz.getInterfaces (), iUniqueSet); - - //*...Recurse the class hierarchy...*/ - count += _deregisterConsumer (consumer, clazz.getSuperclass (), iUniqueSet); - } - - return count; - } - - /* - * Singleton API - */ - - /** - * @return singleton ProducerConsumer - */ - public static synchronized ProducerConsumer getSingleton () - { - if (singleton == null) - singleton = new ProducerConsumer (); - - return singleton; - } - - /* - * Producer APIs - */ - - /** - * Producer registration - * - * @param producer object that implements IProducer - * @param iface interface supported by the producer - * @return whether there was a previously registered producer, - * or true if one or more the arguments were invalid - */ - public boolean registerProducer (IProducer producer, Class<?> iface) - { - if (producer != null && iface != null && iface.isInterface ()) { - Set<IProducer> producers = producerMap.get (iface); - - if (producers == null) { - producers = new HashSet<IProducer> (); - producerMap.put (iface, producers); - } - - return producers.add (producer); - } - else - return true; - } - - /** - * Producer deregistration - * - * @param producer object that implements IProducer - * @param iface interface supported by the producer - * @return whether the interface/producer pair was removed, - * or false if one or more the arguments were invalid - */ - public boolean deregisterProducer (IProducer producer, Class<?> iface) - { - if (producer != null && iface != null && iface.isInterface ()) { - Set<IProducer> producers = producerMap.get (iface); - - if (producers != null) - return producers.remove (producer); - } - - return false; - } - - - /* - * Consumer APIs - */ - - /** - * Consumer registration - * - * @param consumer object that implements producer-specific interfaces - * @return set of supported interfaces - */ - public Set<Class<?>> registerConsumer (Object consumer) - { - Set<Class<?>> iSet = new HashSet<Class<?>> (); - - if (consumer != null) - _registerConsumer (consumer, consumer.getClass (), iSet, new HashSet<Class<?>> ()); - - return iSet; - } - - /** - * Consumer deregistration - * - * @param consumer object to deregister - * @return number of unregistered interfaces - */ - public int deregisterConsumer (Object consumer) - { - if (consumer != null) - return _deregisterConsumer (consumer, consumer.getClass (), new HashSet<Class<?>> ()); - else - return 0; - } + /* + * Class variables + */ + protected static ProducerConsumer singleton; + + /* + * Default constructor + */ + protected ProducerConsumer() { + producerMap = new Hashtable<Class<?>, Set<IProducer>>(); + } + + /* + * Instance variables + */ + + // Interface/IProducer map + protected Map<Class<?>, Set<IProducer>> producerMap; + + /* + * Protected methods + */ + + protected void _registerConsumer(Object consumer, Class<?>[] interfaces, + Set<Class<?>> iSet, + Set<Class<?>> iUniqueSet) { + // *...Process all interfaces...*/ + for (Class<?> iface : interfaces) { + + // *...Protect against repeated interfaces...*/ + if (!iUniqueSet.contains(iface)) { + iUniqueSet.add(iface); + + Set<IProducer> producers = producerMap.get(iface); + + if (producers != null) { + for (IProducer producer : producers) + producer.registerConsumer(iface, consumer); + iSet.add(iface); + } + + // *...Recurse...*/ + _registerConsumer(consumer, iface.getInterfaces(), iSet, + iUniqueSet); + } + } + } + + protected void _registerConsumer(Object consumer, Class<?> clazz, + Set<Class<?>> iSet, + Set<Class<?>> iUniqueSet) { + if (clazz != null) { + // *...Process all interfaces...*/ + _registerConsumer(consumer, clazz.getInterfaces(), iSet, + iUniqueSet); + + // *...Recurse the class hierarchy...*/ + _registerConsumer(consumer, clazz.getSuperclass(), iSet, + iUniqueSet); + } + } + + protected int _deregisterConsumer(Object consumer, + Class<?>[] interfaces, + Set<Class<?>> iUniqueSet) { + int count = 0; + + // *...Process all interfaces...*/ + for (Class<?> iface : interfaces) { + + // *...Protect against repeated interfaces...*/ + if (!iUniqueSet.contains(iface)) { + iUniqueSet.add(iface); + + Set<IProducer> producers = producerMap.get(iface); + + if (producers != null) { + for (IProducer producer : producers) + producer.deregisterConsumer(iface, consumer); + + count++; + } + + // *...Recurse...*/ + count += _deregisterConsumer(consumer, + iface.getInterfaces(), + iUniqueSet); + } + } + + return count; + } + + protected int _deregisterConsumer(Object consumer, Class<?> clazz, + Set<Class<?>> iUniqueSet) { + int count = 0; + + if (clazz != null) { + // *...Process all interfaces...*/ + count += _deregisterConsumer(consumer, clazz.getInterfaces(), + iUniqueSet); + + // *...Recurse the class hierarchy...*/ + count += _deregisterConsumer(consumer, clazz.getSuperclass(), + iUniqueSet); + } + + return count; + } + + /* + * Singleton API + */ + + /** + * @return singleton ProducerConsumer + */ + public static synchronized ProducerConsumer getSingleton() { + if (singleton == null) singleton = new ProducerConsumer(); + + return singleton; + } + + /* + * Producer APIs + */ + + /** + * Producer registration + * + * @param producer + * object that implements IProducer + * @param iface + * interface supported by the producer + * @return whether there was a previously registered producer, or true if + * one or more the arguments were invalid + */ + public boolean registerProducer(IProducer producer, Class<?> iface) { + if (producer != null && iface != null && iface.isInterface()) { + Set<IProducer> producers = producerMap.get(iface); + + if (producers == null) { + producers = new HashSet<IProducer>(); + producerMap.put(iface, producers); + } + + return producers.add(producer); + } else + return true; + } + + /** + * Producer deregistration + * + * @param producer + * object that implements IProducer + * @param iface + * interface supported by the producer + * @return whether the interface/producer pair was removed, or false if one + * or more the arguments were invalid + */ + public boolean deregisterProducer(IProducer producer, Class<?> iface) { + if (producer != null && iface != null && iface.isInterface()) { + Set<IProducer> producers = producerMap.get(iface); + + if (producers != null) return producers.remove(producer); + } + + return false; + } + + /* + * Consumer APIs + */ + + /** + * Consumer registration + * + * @param consumer + * object that implements producer-specific interfaces + * @return set of supported interfaces + */ + public Set<Class<?>> registerConsumer(Object consumer) { + Set<Class<?>> iSet = new HashSet<Class<?>>(); + + if (consumer != null) + _registerConsumer(consumer, + consumer.getClass(), iSet, + new HashSet<Class<?>>()); + + return iSet; + } + + /** + * Consumer deregistration + * + * @param consumer + * object to deregister + * @return number of unregistered interfaces + */ + public int deregisterConsumer(Object consumer) { + if (consumer != null) + return _deregisterConsumer(consumer, consumer.getClass(), + new HashSet<Class<?>>()); + else + return 0; + } } diff --git a/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8c97018385eb21e710cd503422259f46d7a99a10 --- /dev/null +++ b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java @@ -0,0 +1,677 @@ +package net.floodlightcontroller.loadbalancer; + +import static org.easymock.EasyMock.anyLong; +import static org.easymock.EasyMock.anyShort; +import static org.easymock.EasyMock.capture; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.reset; +import static org.easymock.EasyMock.verify; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.easymock.Capture; +import org.easymock.CaptureType; +import org.easymock.EasyMock; +import org.junit.Before; +import org.junit.Test; +import org.openflow.protocol.OFFlowMod; +import org.openflow.protocol.OFMatch; +import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFPacketOut; +import org.openflow.protocol.OFPort; +import org.openflow.protocol.OFType; +import org.openflow.protocol.OFPacketIn.OFPacketInReason; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionOutput; +import org.openflow.util.HexString; + +import net.floodlightcontroller.core.FloodlightContext; +import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.core.module.FloodlightModuleContext; +import net.floodlightcontroller.core.test.MockFloodlightProvider; +import net.floodlightcontroller.core.test.MockThreadPoolService; +import net.floodlightcontroller.counter.CounterStore; +import net.floodlightcontroller.counter.ICounterStoreService; +import net.floodlightcontroller.devicemanager.IDeviceService; +import net.floodlightcontroller.devicemanager.IEntityClassifierService; +import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier; +import net.floodlightcontroller.devicemanager.test.MockDeviceManager; +import net.floodlightcontroller.flowcache.FlowReconcileManager; +import net.floodlightcontroller.flowcache.IFlowReconcileService; +import net.floodlightcontroller.packet.ARP; +import net.floodlightcontroller.packet.Ethernet; +import net.floodlightcontroller.packet.ICMP; +import net.floodlightcontroller.packet.IPacket; +import net.floodlightcontroller.packet.IPv4; +import net.floodlightcontroller.restserver.IRestApiService; +import net.floodlightcontroller.restserver.RestApiServer; +import net.floodlightcontroller.routing.IRoutingService; +import net.floodlightcontroller.routing.Route; +import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService; +import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher; +import net.floodlightcontroller.storage.IStorageSourceService; +import net.floodlightcontroller.storage.memory.MemoryStorageSource; +import net.floodlightcontroller.test.FloodlightTestCase; +import net.floodlightcontroller.threadpool.IThreadPoolService; +import net.floodlightcontroller.topology.ITopologyService; +import net.floodlightcontroller.topology.NodePortTuple; + +public class LoadBalancerTest extends FloodlightTestCase { + protected LoadBalancer lb; + + protected FloodlightContext cntx; + protected FloodlightModuleContext fmc; + protected MockFloodlightProvider mockFloodlightProvider; + protected MockDeviceManager deviceManager; + protected MockThreadPoolService tps; + protected FlowReconcileManager frm; + protected DefaultEntityClassifier entityClassifier; + protected IRoutingService routingEngine; + protected ITopologyService topology; + protected StaticFlowEntryPusher sfp; + protected MemoryStorageSource storage; + protected RestApiServer restApi; + protected VipsResource vipsResource; + protected PoolsResource poolsResource; + protected MembersResource membersResource; + + protected LBVip vip1, vip2; + protected LBPool pool1, pool2, pool3; + protected LBMember member1, member2, member3, member4; + + @Before + public void setUp() throws Exception { + super.setUp(); + + lb = new LoadBalancer(); + + cntx = new FloodlightContext(); + fmc = new FloodlightModuleContext(); + entityClassifier = new DefaultEntityClassifier(); // dependency for device manager + frm = new FlowReconcileManager(); //dependency for device manager + tps = new MockThreadPoolService(); //dependency for device manager + deviceManager = new MockDeviceManager(); + topology = createMock(ITopologyService.class); + routingEngine = createMock(IRoutingService.class); + restApi = new RestApiServer(); + sfp = new StaticFlowEntryPusher(); + storage = new MemoryStorageSource(); //dependency for sfp + + fmc.addService(IRestApiService.class, restApi); + fmc.addService(IFloodlightProviderService.class, getMockFloodlightProvider()); + fmc.addService(IEntityClassifierService.class, entityClassifier); + fmc.addService(IFlowReconcileService.class, frm); + fmc.addService(IThreadPoolService.class, tps); + fmc.addService(IDeviceService.class, deviceManager); + fmc.addService(ITopologyService.class, topology); + fmc.addService(IRoutingService.class, routingEngine); + fmc.addService(ICounterStoreService.class, new CounterStore()); + fmc.addService(IStaticFlowEntryPusherService.class, sfp); + fmc.addService(ILoadBalancerService.class, lb); + fmc.addService(IStorageSourceService.class, storage); + + lb.init(fmc); + getMockFloodlightProvider().init(fmc); + entityClassifier.init(fmc); + frm.init(fmc); + tps.init(fmc); + deviceManager.init(fmc); + restApi.init(fmc); + sfp.init(fmc); + storage.init(fmc); + + topology.addListener(deviceManager); + expectLastCall().times(1); + replay(topology); + + lb.startUp(fmc); + getMockFloodlightProvider().startUp(fmc); + entityClassifier.startUp(fmc); + frm.startUp(fmc); + tps.startUp(fmc); + deviceManager.startUp(fmc); + restApi.startUp(fmc); + sfp.startUp(fmc); + storage.startUp(fmc); + + verify(topology); + + vipsResource = new VipsResource(); + poolsResource = new PoolsResource(); + membersResource = new MembersResource(); + + vip1=null; + vip2=null; + + pool1=null; + pool2=null; + pool3=null; + + member1=null; + member2=null; + member3=null; + member4=null; + } + + @Test + public void testCreateVip() { + String postData1, postData2; + IOException error = null; + + postData1 = "{\"id\":\"1\",\"name\":\"vip1\",\"protocol\":\"icmp\",\"address\":\"10.0.0.100\",\"port\":\"8\"}"; + postData2 = "{\"id\":\"2\",\"name\":\"vip2\",\"protocol\":\"tcp\",\"address\":\"10.0.0.200\",\"port\":\"100\"}"; + + try { + vip1 = vipsResource.jsonToVip(postData1); + } catch (IOException e) { + error = e; + } + try { + vip2 = vipsResource.jsonToVip(postData2); + } catch (IOException e) { + error = e; + } + + // verify correct parsing + assertFalse(vip1==null); + assertFalse(vip2==null); + assertTrue(error==null); + + lb.createVip(vip1); + lb.createVip(vip2); + + // verify correct creation + assertTrue(lb.vips.containsKey(vip1.id)); + assertTrue(lb.vips.containsKey(vip2.id)); + } + + @Test + public void testRemoveVip() { + + testCreateVip(); + + // verify correct initial condition + assertFalse(vip1==null); + assertFalse(vip2==null); + + lb.removeVip(vip1.id); + lb.removeVip(vip2.id); + + // verify correct removal + assertFalse(lb.vips.containsKey(vip1.id)); + assertFalse(lb.vips.containsKey(vip2.id)); + + } + + @Test + public void testCreatePool() { + String postData1, postData2, postData3; + IOException error = null; + + testCreateVip(); + + postData1 = "{\"id\":\"1\",\"name\":\"pool1\",\"protocol\":\"icmp\",\"vip_id\":\"1\"}"; + postData2 = "{\"id\":\"2\",\"name\":\"pool2\",\"protocol\":\"tcp\",\"vip_id\":\"2\"}"; + postData3 = "{\"id\":\"3\",\"name\":\"pool3\",\"protocol\":\"udp\",\"vip_id\":\"3\"}"; + + try { + pool1 = poolsResource.jsonToPool(postData1); + } catch (IOException e) { + error = e; + } + try { + pool2 = poolsResource.jsonToPool(postData2); + } catch (IOException e) { + error = e; + } + try { + pool3 = poolsResource.jsonToPool(postData3); + } catch (IOException e) { + error = e; + } + + // verify correct parsing + assertFalse(pool1==null); + assertFalse(pool2==null); + assertFalse(pool3==null); + assertTrue(error==null); + + lb.createPool(pool1); + lb.createPool(pool2); + lb.createPool(pool3); + + // verify successful creates; two registered with vips and one not + assertTrue(lb.pools.containsKey(pool1.id)); + assertTrue(lb.vips.get(pool1.vipId).pools.contains(pool1.id)); + assertTrue(lb.pools.containsKey(pool2.id)); + assertTrue(lb.vips.get(pool2.vipId).pools.contains(pool2.id)); + assertTrue(lb.pools.containsKey(pool3.id)); + assertFalse(lb.vips.containsKey(pool3.vipId)); + + } + + @Test + public void testRemovePool() { + testCreateVip(); + testCreatePool(); + + // verify correct initial condition + assertFalse(vip1==null); + assertFalse(vip2==null); + assertFalse(pool1==null); + assertFalse(pool2==null); + assertFalse(pool3==null); + + lb.removePool(pool1.id); + lb.removePool(pool2.id); + lb.removePool(pool3.id); + + // verify correct removal + assertFalse(lb.pools.containsKey(pool1.id)); + assertFalse(lb.pools.containsKey(pool2.id)); + assertFalse(lb.pools.containsKey(pool3.id)); + + //verify pool cleanup from vip + assertFalse(lb.vips.get(pool1.vipId).pools.contains(pool1.id)); + assertFalse(lb.vips.get(pool2.vipId).pools.contains(pool2.id)); + } + + @Test + public void testCreateMember() { + String postData1, postData2, postData3, postData4; + IOException error = null; + + testCreateVip(); + testCreatePool(); + + postData1 = "{\"id\":\"1\",\"address\":\"10.0.0.3\",\"port\":\"8\",\"pool_id\":\"1\"}"; + postData2 = "{\"id\":\"2\",\"address\":\"10.0.0.4\",\"port\":\"8\",\"pool_id\":\"1\"}"; + postData3 = "{\"id\":\"3\",\"address\":\"10.0.0.5\",\"port\":\"100\",\"pool_id\":\"2\"}"; + postData4 = "{\"id\":\"4\",\"address\":\"10.0.0.6\",\"port\":\"100\",\"pool_id\":\"2\"}"; + + try { + member1 = membersResource.jsonToMember(postData1); + } catch (IOException e) { + error = e; + } + try { + member2 = membersResource.jsonToMember(postData2); + } catch (IOException e) { + error = e; + } + try { + member3 = membersResource.jsonToMember(postData3); + } catch (IOException e) { + error = e; + } + try { + member4 = membersResource.jsonToMember(postData4); + } catch (IOException e) { + error = e; + } + + // verify correct parsing + assertFalse(member1==null); + assertFalse(member2==null); + assertFalse(member3==null); + assertFalse(member4==null); + assertTrue(error==null); + + lb.createMember(member1); + lb.createMember(member2); + lb.createMember(member3); + lb.createMember(member4); + + // add the same server a second time + lb.createMember(member1); + + // verify successful creates + assertTrue(lb.members.containsKey(member1.id)); + assertTrue(lb.members.containsKey(member2.id)); + assertTrue(lb.members.containsKey(member3.id)); + assertTrue(lb.members.containsKey(member4.id)); + + assertTrue(lb.pools.get(member1.poolId).members.size()==2); + assertTrue(lb.pools.get(member3.poolId).members.size()==2); + + // member1 should inherit valid vipId from pool + assertTrue(lb.vips.get(member1.vipId)!=null); + } + + @Test + public void testRemoveMember() { + testCreateVip(); + testCreatePool(); + testCreateMember(); + + // verify correct initial condition + assertFalse(vip1==null); + assertFalse(vip2==null); + assertFalse(pool1==null); + assertFalse(pool2==null); + assertFalse(pool3==null); + assertFalse(member1==null); + assertFalse(member2==null); + assertFalse(member3==null); + assertFalse(member4==null); + + lb.removeMember(member1.id); + lb.removeMember(member2.id); + lb.removeMember(member3.id); + lb.removeMember(member4.id); + + // verify correct removal + assertFalse(lb.members.containsKey(member1.id)); + assertFalse(lb.members.containsKey(member2.id)); + assertFalse(lb.members.containsKey(member3.id)); + assertFalse(lb.members.containsKey(member4.id)); + + //verify member cleanup from pool + assertFalse(lb.pools.get(member1.poolId).members.contains(member1.id)); + assertFalse(lb.pools.get(member2.poolId).members.contains(member2.id)); + assertFalse(lb.pools.get(member3.poolId).members.contains(member3.id)); + assertFalse(lb.pools.get(member4.poolId).members.contains(member4.id)); + + } + + @Test + public void testTwoSubsequentIcmpRequests() throws Exception { + testCreateVip(); + testCreatePool(); + testCreateMember(); + + IOFSwitch sw1; + + IPacket arpRequest1, arpReply1, icmpPacket1, icmpPacket2; + + byte[] arpRequest1Serialized; + byte[] arpReply1Serialized; + byte[] icmpPacket1Serialized, icmpPacket2Serialized; + + OFPacketIn arpRequestPacketIn1; + OFPacketIn icmpPacketIn1, icmpPacketIn2; + + OFPacketOut arpReplyPacketOut1; + + Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); + Capture<FloodlightContext> bc1 = + new Capture<FloodlightContext>(CaptureType.ALL); + + int fastWildcards = + OFMatch.OFPFW_IN_PORT | + OFMatch.OFPFW_NW_PROTO | + OFMatch.OFPFW_TP_SRC | + OFMatch.OFPFW_TP_DST | + OFMatch.OFPFW_NW_SRC_ALL | + OFMatch.OFPFW_NW_DST_ALL | + OFMatch.OFPFW_NW_TOS; + + sw1 = EasyMock.createNiceMock(IOFSwitch.class); + expect(sw1.getId()).andReturn(1L).anyTimes(); + expect(sw1.getStringId()).andReturn("00:00:00:00:00:01").anyTimes(); + expect(sw1.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn((Integer)fastWildcards).anyTimes(); + expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes(); + sw1.write(capture(wc1), capture(bc1)); + expectLastCall().anyTimes(); + sw1.flush(); + expectLastCall().anyTimes(); + + replay(sw1); + sfp.addedSwitch(sw1); + verify(sw1); + + /* Test plan: + * - two clients and two servers on sw1 port 1, 2, 3, 4 + * - mock arp request received towards vip1 from (1L, 1) + * - proxy arp got pushed out to (1L, 1)- check sw1 getting the packetout + * - mock icmp request received towards vip1 from (1L, 1) + * - device manager list of devices queried to identify source and dest devices + * - routing engine queried to get inbound and outbound routes + * - check getRoute calls and responses + * - sfp called to install flows + * - check sfp calls + */ + + // Build topology + reset(topology); + expect(topology.isIncomingBroadcastAllowed(anyLong(), anyShort())).andReturn(true).anyTimes(); + expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes(); + expect(topology.isAttachmentPointPort(1L, (short)1)).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(1L, (short)2)).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(1L, (short)3)).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(1L, (short)4)).andReturn(true).anyTimes(); + replay(topology); + + + + // Build arp packets + arpRequest1 = new Ethernet() + .setSourceMACAddress("00:00:00:00:00:01") + .setDestinationMACAddress("ff:ff:ff:ff:ff:ff") + .setEtherType(Ethernet.TYPE_ARP) + .setVlanID((short) 0) + .setPriorityCode((byte) 0) + .setPayload( + new ARP() + .setHardwareType(ARP.HW_TYPE_ETHERNET) + .setProtocolType(ARP.PROTO_TYPE_IP) + .setHardwareAddressLength((byte) 6) + .setProtocolAddressLength((byte) 4) + .setOpCode(ARP.OP_REQUEST) + .setSenderHardwareAddress(HexString.fromHexString("00:00:00:00:00:01")) + .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.1")) + .setTargetHardwareAddress(HexString.fromHexString("00:00:00:00:00:00")) + .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.100"))); + + arpRequest1Serialized = arpRequest1.serialize(); + + arpRequestPacketIn1 = + ((OFPacketIn) getMockFloodlightProvider().getOFMessageFactory(). + getMessage(OFType.PACKET_IN)) + .setBufferId(-1) + .setInPort((short) 1) + .setPacketData(arpRequest1Serialized) + .setReason(OFPacketInReason.NO_MATCH) + .setTotalLength((short) arpRequest1Serialized.length); + + IFloodlightProviderService.bcStore.put(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD, + (Ethernet) arpRequest1); + + // Mock proxy arp packet-out + arpReply1 = new Ethernet() + .setSourceMACAddress(LBVip.LB_PROXY_MAC) + .setDestinationMACAddress(HexString.fromHexString("00:00:00:00:00:01")) + .setEtherType(Ethernet.TYPE_ARP) + .setVlanID((short) 0) + .setPriorityCode((byte) 0) + .setPayload( + new ARP() + .setHardwareType(ARP.HW_TYPE_ETHERNET) + .setProtocolType(ARP.PROTO_TYPE_IP) + .setHardwareAddressLength((byte) 6) + .setProtocolAddressLength((byte) 4) + .setOpCode(ARP.OP_REPLY) + .setSenderHardwareAddress(HexString.fromHexString(LBVip.LB_PROXY_MAC)) + .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.100")) + .setTargetHardwareAddress(HexString.fromHexString("00:00:00:00:00:01")) + .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.1"))); + + arpReply1Serialized = arpReply1.serialize(); + + arpReplyPacketOut1 = + (OFPacketOut) getMockFloodlightProvider().getOFMessageFactory(). + getMessage(OFType.PACKET_OUT); + arpReplyPacketOut1.setBufferId(OFPacketOut.BUFFER_ID_NONE) + .setInPort(OFPort.OFPP_NONE.getValue()); + List<OFAction> poactions = new ArrayList<OFAction>(); + poactions.add(new OFActionOutput(arpRequestPacketIn1.getInPort(), (short) 0xffff)); + arpReplyPacketOut1.setActions(poactions) + .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH) + .setPacketData(arpReply1Serialized) + .setLengthU(OFPacketOut.MINIMUM_LENGTH+ + arpReplyPacketOut1.getActionsLength()+ + arpReply1Serialized.length); + + lb.receive(sw1, arpRequestPacketIn1, cntx); + verify(sw1, topology); + + assertTrue(wc1.hasCaptured()); // wc1 should get packetout + + List<OFMessage> msglist1 = wc1.getValues(); + + for (OFMessage m: msglist1) { + if (m instanceof OFPacketOut) + assertEquals(arpReplyPacketOut1, m); + else + assertTrue(false); // unexpected message + } + + // + // Skip arpRequest2 test - in reality this will happen, but for unit test the same logic + // is already validated with arpRequest1 test above + // + + // Build icmp packets + icmpPacket1 = new Ethernet() + .setSourceMACAddress("00:00:00:00:00:01") + .setDestinationMACAddress(LBVip.LB_PROXY_MAC) + .setEtherType(Ethernet.TYPE_IPv4) + .setVlanID((short) 0) + .setPriorityCode((byte) 0) + .setPayload( + new IPv4() + .setSourceAddress("10.0.0.1") + .setDestinationAddress("10.0.0.100") + .setProtocol(IPv4.PROTOCOL_ICMP) + .setPayload(new ICMP() + .setIcmpCode((byte) 0) + .setIcmpType((byte) 0))); + + icmpPacket1Serialized = icmpPacket1.serialize(); + + icmpPacketIn1 = + ((OFPacketIn) getMockFloodlightProvider().getOFMessageFactory(). + getMessage(OFType.PACKET_IN)) + .setBufferId(-1) + .setInPort((short) 1) + .setPacketData(icmpPacket1Serialized) + .setReason(OFPacketInReason.NO_MATCH) + .setTotalLength((short) icmpPacket1Serialized.length); + + icmpPacket2 = new Ethernet() + .setSourceMACAddress("00:00:00:00:00:02") + .setDestinationMACAddress(LBVip.LB_PROXY_MAC) + .setEtherType(Ethernet.TYPE_IPv4) + .setVlanID((short) 0) + .setPriorityCode((byte) 0) + .setPayload( + new IPv4() + .setSourceAddress("10.0.0.2") + .setDestinationAddress("10.0.0.100") + .setProtocol(IPv4.PROTOCOL_ICMP) + .setPayload(new ICMP() + .setIcmpCode((byte) 0) + .setIcmpType((byte) 0))); + + icmpPacket2Serialized = icmpPacket2.serialize(); + + icmpPacketIn2 = + ((OFPacketIn) getMockFloodlightProvider().getOFMessageFactory(). + getMessage(OFType.PACKET_IN)) + .setBufferId(-1) + .setInPort((short) 2) + .setPacketData(icmpPacket2Serialized) + .setReason(OFPacketInReason.NO_MATCH) + .setTotalLength((short) icmpPacket2Serialized.length); + + byte[] dataLayerSource1 = ((Ethernet)icmpPacket1).getSourceMACAddress(); + int networkSource1 = ((IPv4)((Ethernet)icmpPacket1).getPayload()).getSourceAddress(); + byte[] dataLayerDest1 = HexString.fromHexString("00:00:00:00:00:03"); + int networkDest1 = IPv4.toIPv4Address("10.0.0.3"); + byte[] dataLayerSource2 = ((Ethernet)icmpPacket2).getSourceMACAddress(); + int networkSource2 = ((IPv4)((Ethernet)icmpPacket2).getPayload()).getSourceAddress(); + byte[] dataLayerDest2 = HexString.fromHexString("00:00:00:00:00:04"); + int networkDest2 = IPv4.toIPv4Address("10.0.0.4"); + + deviceManager.learnEntity(Ethernet.toLong(dataLayerSource1), + null, networkSource1, + 1L, 1); + deviceManager.learnEntity(Ethernet.toLong(dataLayerSource2), + null, networkSource2, + 1L, 2); + deviceManager.learnEntity(Ethernet.toLong(dataLayerDest1), + null, networkDest1, + 1L, 3); + deviceManager.learnEntity(Ethernet.toLong(dataLayerDest2), + null, networkDest2, + 1L, 4); + + // in bound #1 + Route route1 = new Route(1L, 1L); + List<NodePortTuple> nptList1 = new ArrayList<NodePortTuple>(); + nptList1.add(new NodePortTuple(1L, (short)1)); + nptList1.add(new NodePortTuple(1L, (short)3)); + route1.setPath(nptList1); + expect(routingEngine.getRoute(1L, (short)1, 1L, (short)3, 0)).andReturn(route1).atLeastOnce(); + + // outbound #1 + Route route2 = new Route(1L, 1L); + List<NodePortTuple> nptList2 = new ArrayList<NodePortTuple>(); + nptList2.add(new NodePortTuple(1L, (short)3)); + nptList2.add(new NodePortTuple(1L, (short)1)); + route2.setPath(nptList2); + expect(routingEngine.getRoute(1L, (short)3, 1L, (short)1, 0)).andReturn(route2).atLeastOnce(); + + // inbound #2 + Route route3 = new Route(1L, 1L); + List<NodePortTuple> nptList3 = new ArrayList<NodePortTuple>(); + nptList3.add(new NodePortTuple(1L, (short)2)); + nptList3.add(new NodePortTuple(1L, (short)4)); + route3.setPath(nptList3); + expect(routingEngine.getRoute(1L, (short)2, 1L, (short)4, 0)).andReturn(route3).atLeastOnce(); + + // outbound #2 + Route route4 = new Route(1L, 1L); + List<NodePortTuple> nptList4 = new ArrayList<NodePortTuple>(); + nptList4.add(new NodePortTuple(1L, (short)4)); + nptList4.add(new NodePortTuple(1L, (short)2)); + route4.setPath(nptList3); + expect(routingEngine.getRoute(1L, (short)4, 1L, (short)2, 0)).andReturn(route4).atLeastOnce(); + + replay(routingEngine); + + wc1.reset(); + + IFloodlightProviderService.bcStore.put(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD, + (Ethernet) icmpPacket1); + lb.receive(sw1, icmpPacketIn1, cntx); + + IFloodlightProviderService.bcStore.put(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD, + (Ethernet) icmpPacket2); + lb.receive(sw1, icmpPacketIn2, cntx); + + assertTrue(wc1.hasCaptured()); // wc1 should get packetout + + List<OFMessage> msglist2 = wc1.getValues(); + + assertTrue(msglist2.size()==2); // has inbound and outbound packetouts + // TODO: not seeing flowmods yet ... + + Map<String, OFFlowMod> map = sfp.getFlows("00:00:00:00:00:00:00:01"); + + assertTrue(map.size()==4); + } + + +} diff --git a/src/test/java/org/openflow/protocol/BasicFactoryTest.java b/src/test/java/org/openflow/protocol/BasicFactoryTest.java index 6825008d98b0f9c0134b77a3f13dc39b54911fd7..312fcd3f57d616c6716e7f702e56a086a44609d0 100644 --- a/src/test/java/org/openflow/protocol/BasicFactoryTest.java +++ b/src/test/java/org/openflow/protocol/BasicFactoryTest.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior * University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -17,16 +17,23 @@ package org.openflow.protocol; +import static org.junit.Assert.assertArrayEquals; + import java.util.List; +import junit.framework.TestCase; + import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; +import org.openflow.protocol.action.MockVendorAction; +import org.openflow.protocol.action.MockVendorActionFactory; +import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionVendorGeneric; import org.openflow.protocol.factory.BasicFactory; import org.openflow.protocol.factory.MessageParseException; +import org.openflow.protocol.factory.OFVendorActionRegistry; import org.openflow.util.U16; -import junit.framework.TestCase; - public class BasicFactoryTest extends TestCase { public void testCreateAndParse() throws MessageParseException { @@ -78,4 +85,50 @@ public class BasicFactoryTest extends TestCase { } } + public void testCustomVendorAction() throws MessageParseException { + BasicFactory factory = new BasicFactory(); + OFVendorActionRegistry.getInstance().register( + MockVendorAction.VENDOR_ID, new MockVendorActionFactory()); + + + byte[] deadBeefMessage = { + (byte) 0xff, (byte) 0xff, // action vendor + 0x00, 0x10, // length + (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte)0xef, // deadbeaf + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08 // pad + }; + + ChannelBuffer buf = ChannelBuffers.copiedBuffer(deadBeefMessage); + + List<OFAction> actions = factory.parseActions(buf,deadBeefMessage.length); + assertEquals(1, actions.size()); + OFAction ofAction = actions.get(0); + assertTrue("Action should be MockVendorAction, but is "+ofAction.getClass(), ofAction instanceof MockVendorAction); + assertArrayEquals( new byte[] { 1,2,3,4,5,6,7,8}, ((MockVendorAction)ofAction).getMockData()); + + + } + + public void testGenericVendorAction() throws MessageParseException { + byte[] nonDeadBeefMessage = { + (byte) 0xff, (byte) 0xff, // action vendor + 0x00, 0x10, // length + (byte) 0x7e, (byte) 0xe7, (byte) 0xbe, (byte)0xef, // deadbeaf + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08 // pad + }; + + BasicFactory factory = new BasicFactory(); + OFVendorActionRegistry.getInstance().register( + MockVendorAction.VENDOR_ID, new MockVendorActionFactory()); + + ChannelBuffer buf = ChannelBuffers.copiedBuffer(nonDeadBeefMessage); + + List<OFAction> actions = factory.parseActions(buf,nonDeadBeefMessage.length); + assertEquals(1, actions.size()); + OFAction ofAction = actions.get(0); + assertTrue("Action should be OFActionVendorGeneric, but is "+ofAction.getClass(), ofAction instanceof OFActionVendorGeneric); + } + } diff --git a/src/test/java/org/openflow/protocol/action/MockVendorAction.java b/src/test/java/org/openflow/protocol/action/MockVendorAction.java new file mode 100644 index 0000000000000000000000000000000000000000..49b69fb8181d4a6dd6959a40786d643fe05ead20 --- /dev/null +++ b/src/test/java/org/openflow/protocol/action/MockVendorAction.java @@ -0,0 +1,41 @@ +package org.openflow.protocol.action; + +import org.jboss.netty.buffer.ChannelBuffer; + + +public class MockVendorAction extends OFActionVendor { + public static final int VENDOR_ID = 0xdeadbeef; + + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + private byte[] mockData; + + public byte[] getMockData() { + return mockData; + } + + public void setMockData(byte[] mockData) { + this.mockData = mockData; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + + int dataLength = getLength() - MINIMUM_LENGTH; + if(dataLength > 0) { + mockData = new byte[dataLength]; + data.readBytes(mockData); + } else { + mockData = EMPTY_BYTE_ARRAY; + } + + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeBytes(mockData); + } + + +} diff --git a/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java b/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..bbc254ca0074cc9e418c5541f976d000074cf0b1 --- /dev/null +++ b/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java @@ -0,0 +1,15 @@ +package org.openflow.protocol.action; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.factory.OFVendorActionFactory; + +public class MockVendorActionFactory implements OFVendorActionFactory { + + @Override + public OFActionVendor readFrom(ChannelBuffer data) { + MockVendorAction action = new MockVendorAction(); + action.readFrom(data); + return action; + } + +} diff --git a/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java b/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..31ad675d6a0a708c5a69a7ae3b0dcb21bc24e5c5 --- /dev/null +++ b/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java @@ -0,0 +1,17 @@ +package org.openflow.protocol.action; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.openflow.protocol.factory.OFVendorActionRegistry; + +public class OFVendorActionRegistryTest { + + @Test + public void test() { + MockVendorActionFactory factory = new MockVendorActionFactory(); + OFVendorActionRegistry.getInstance().register(MockVendorAction.VENDOR_ID, factory); + assertEquals(factory, OFVendorActionRegistry.getInstance().get(MockVendorAction.VENDOR_ID)); + } + +}