diff --git a/of/api/pom.xml b/of/api/pom.xml index 8f123e137f..050fedd72d 100644 --- a/of/api/pom.xml +++ b/of/api/pom.xml @@ -63,7 +63,7 @@ - org.projectfloodlight.openflow.* + org.onlab.onos.of.*,org.projectfloodlight.openflow.* diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/DeleteMe.java b/of/api/src/main/java/org/onlab/onos/of/controller/DeleteMe.java deleted file mode 100644 index 1db6eae4a1..0000000000 --- a/of/api/src/main/java/org/onlab/onos/of/controller/DeleteMe.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.onlab.onos.of.controller; - -/** - * Created by tom on 8/21/14. - */ -public interface DeleteMe { -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/Dpid.java b/of/api/src/main/java/org/onlab/onos/of/controller/Dpid.java similarity index 96% rename from of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/Dpid.java rename to of/api/src/main/java/org/onlab/onos/of/controller/Dpid.java index dec84242ab..02a957e6cc 100644 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/Dpid.java +++ b/of/api/src/main/java/org/onlab/onos/of/controller/Dpid.java @@ -1,4 +1,4 @@ -package org.onlab.onos.of.controller.impl.util; +package org.onlab.onos.of.controller; import org.projectfloodlight.openflow.util.HexString; diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowController.java b/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowController.java new file mode 100644 index 0000000000..5aec885e61 --- /dev/null +++ b/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowController.java @@ -0,0 +1,100 @@ +package org.onlab.onos.of.controller; + +import org.projectfloodlight.openflow.protocol.OFMessage; + +/** + * Abstraction of an OpenFlow controller. Serves as a one stop + * shop for obtaining OpenFlow devices and (un)register listeners + * on OpenFlow events + */ +public interface OpenFlowController { + + /** + * Returns all switches known to this OF controller. + * @return Iterable of dpid elements + */ + public Iterable getSwitches(); + + /** + * Returns all master switches known to this OF controller. + * @return Iterable of dpid elements + */ + public Iterable getMasterSwitches(); + + /** + * Returns all equal switches known to this OF controller. + * @return Iterable of dpid elements + */ + public Iterable getEqualSwitches(); + + + /** + * Returns the actual switch for the given Dpid. + * @param dpid the switch to fetch + * @return the interface to this switch + */ + public OpenFlowSwitch getSwitch(Dpid dpid); + + /** + * Returns the actual master switch for the given Dpid, if one exists. + * @param dpid the switch to fetch + * @return the interface to this switch + */ + public OpenFlowSwitch getMasterSwitch(Dpid dpid); + + /** + * Returns the actual equal switch for the given Dpid, if one exists. + * @param dpid the switch to fetch + * @return the interface to this switch + */ + public OpenFlowSwitch getEqualSwitch(Dpid dpid); + + /** + * Register a listener for meta events that occur to OF + * devices. + * @param listener the listener to notify + */ + public void addListener(OpenFlowSwitchListener listener); + + /** + * Unregister a listener. + * + * @param listener the listener to unregister + */ + public void removeListener(OpenFlowSwitchListener listener); + + /** + * Register a listener for packet events. + * @param priority the importance of this listener, lower values are more important + * @param listener the listener to notify + */ + public void addPacketListener(int priority, PacketListener listener); + + /** + * Unregister a listener. + * + * @param listener the listener to unregister + */ + public void removePacketListener(PacketListener listener); + + /** + * Send a message to a particular switch. + * @param dpid the switch to send to. + * @param msg the message to send + */ + public void write(Dpid dpid, OFMessage msg); + + /** + * Process a message and notify the appropriate listeners. + * + * @param msg the message to process. + */ + public void processPacket(OFMessage msg); + + /** + * Sets the role for a given switch. + * @param role the desired role + * @param dpid the switch to set the role for. + */ + public void setRole(Dpid dpid, RoleState role); +} diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowSwitch.java b/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowSwitch.java new file mode 100644 index 0000000000..e6f52bda0d --- /dev/null +++ b/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowSwitch.java @@ -0,0 +1,23 @@ +package org.onlab.onos.of.controller; + +import org.projectfloodlight.openflow.protocol.OFMessage; + +/** + * Abstract model of an OpenFlow Switch. + * + */ +public interface OpenFlowSwitch { + + /** + * Writes the message to this switch. + * + * @param msg the message to write + */ + public void write(OFMessage msg); + + /** + * Handle a message from the switch. + * @param fromSwitch the message to handle + */ + public void handleMessage(OFMessage fromSwitch); +} diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowSwitchEvent.java b/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowSwitchEvent.java new file mode 100644 index 0000000000..281d5050f4 --- /dev/null +++ b/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowSwitchEvent.java @@ -0,0 +1,17 @@ +package org.onlab.onos.of.controller; + +/** + * Meta events that can happen at a switch. + * + */ +public enum OpenFlowSwitchEvent { + /** + * The switch connected. + */ + SWITCH_CONNECTED, + + /** + * The switch disconnected. + */ + SWITCH_DISCONNECTED +} diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowSwitchListener.java b/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowSwitchListener.java new file mode 100644 index 0000000000..7ccba6d269 --- /dev/null +++ b/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowSwitchListener.java @@ -0,0 +1,20 @@ +package org.onlab.onos.of.controller; + +/** + * Allows for providers interested in Switch events to be notified. + */ +public interface OpenFlowSwitchListener { + + /** + * Notify that the switch was added. + * @param dpid the switch where the event occurred + */ + public void switchAdded(Dpid dpid); + + /** + * Notify that the switch was removed. + * @param dpid the switch where the event occurred. + */ + public void switchRemoved(Dpid dpid); + +} diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/PacketContext.java b/of/api/src/main/java/org/onlab/onos/of/controller/PacketContext.java new file mode 100644 index 0000000000..bc06e93e08 --- /dev/null +++ b/of/api/src/main/java/org/onlab/onos/of/controller/PacketContext.java @@ -0,0 +1,50 @@ +package org.onlab.onos.of.controller; + +import org.projectfloodlight.openflow.types.OFPort; + +/** + * A representation of a packet context which allows any provider + * to view the packet in event but may block the response to the + * event if blocked has been called. + */ +public interface PacketContext { + + //TODO: may want to support sending packet out other switches than + // the one it came in on. + /** + * Blocks further responses (ie. send() calls) on this + * packet in event. + */ + public void block(); + + /** + * Provided build has been called send the packet + * out the switch it came in on. + */ + public void send(); + + /** + * Build the packet out in response to this packet in event. + * @param outPort the out port to send to packet out of. + */ + public void build(OFPort outPort); + + /** + * Build the packet out in response to this packet in event. + * @param ethFrame the actual packet to send out. + * @param outPort the out port to send to packet out of. + */ + public void build(Object ethFrame, OFPort outPort); + + /** + * Provided a handle onto the parsed payload. + * @return the parsed form of the payload. + */ + public Object parsed(); + + /** + * Provide the dpid of the switch where the packet in arrived. + * @return the dpid of the switch. + */ + public Dpid dpid(); +} diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/PacketListener.java b/of/api/src/main/java/org/onlab/onos/of/controller/PacketListener.java new file mode 100644 index 0000000000..5c6d73f01b --- /dev/null +++ b/of/api/src/main/java/org/onlab/onos/of/controller/PacketListener.java @@ -0,0 +1,13 @@ +package org.onlab.onos.of.controller; + +/** + * Notifies providers about Packet in events. + */ +public interface PacketListener { + + /** + * Handle the packet. + * @param pktCtx the packet context ({@link } + */ + public void handlePacket(PacketContext pktCtx); +} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/Role.java b/of/api/src/main/java/org/onlab/onos/of/controller/RoleState.java similarity index 53% rename from of/ctl/src/main/java/org/onlab/onos/of/controller/impl/Role.java rename to of/api/src/main/java/org/onlab/onos/of/controller/RoleState.java index 081ea105fd..527a91c032 100644 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/Role.java +++ b/of/api/src/main/java/org/onlab/onos/of/controller/RoleState.java @@ -1,4 +1,4 @@ -package org.onlab.onos.of.controller.impl; +package org.onlab.onos.of.controller; import org.projectfloodlight.openflow.protocol.OFControllerRole; @@ -10,27 +10,14 @@ import org.projectfloodlight.openflow.protocol.OFControllerRole; * to the OF1.3 enum, before role messages are sent to the switch. * See sendRoleRequestMessage method in OFSwitchImpl */ -public enum Role { +public enum RoleState { EQUAL(OFControllerRole.ROLE_EQUAL), MASTER(OFControllerRole.ROLE_MASTER), SLAVE(OFControllerRole.ROLE_SLAVE); - private Role(OFControllerRole nxRole) { + private RoleState(OFControllerRole nxRole) { nxRole.ordinal(); } - /* - private static Map nxRoleToEnum - = new HashMap(); - static { - for(Role r: Role.values()) - nxRoleToEnum.put(r.toNxRole(), r); - } - public int toNxRole() { - return nxRole; - } - // Return the enum representing the given nxRole or null if no - // such role exists - public static Role fromNxRole(int nxRole) { - return nxRoleToEnum.get(nxRole); - }*/ + } + diff --git a/of/ctl/pom.xml b/of/ctl/pom.xml index 5c090cbe9e..88bdcd91fd 100644 --- a/of/ctl/pom.xml +++ b/of/ctl/pom.xml @@ -38,6 +38,10 @@ + + org.onlab.onos + onos-of-api + org.apache.felix diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/IOFSwitch.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/IOFSwitch.java deleted file mode 100644 index afa3703795..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/IOFSwitch.java +++ /dev/null @@ -1,584 +0,0 @@ -/** - * Copyright 2011, Big Switch Networks, Inc. - * Originally created by David Erickson, Stanford 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.onlab.onos.of.controller.impl; - -import java.io.IOException; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Future; - -import org.onlab.onos.of.controller.impl.debugcounter.IDebugCounterService; -import org.onlab.onos.of.controller.impl.debugcounter.IDebugCounterService.CounterException; -import org.onlab.onos.of.controller.impl.util.OrderedCollection; - -import org.jboss.netty.channel.Channel; -import org.projectfloodlight.openflow.protocol.OFActionType; -import org.projectfloodlight.openflow.protocol.OFCapabilities; -import org.projectfloodlight.openflow.protocol.OFDescStatsReply; -import org.projectfloodlight.openflow.protocol.OFFeaturesReply; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; -import org.projectfloodlight.openflow.protocol.OFPortStatus; -import org.projectfloodlight.openflow.protocol.OFStatsReply; -import org.projectfloodlight.openflow.protocol.OFStatsRequest; -import org.projectfloodlight.openflow.protocol.OFVersion; -import org.projectfloodlight.openflow.types.U64; - - -public interface IOFSwitch { - - /** - * OF1.3 switches should support role-request messages as in the 1.3 spec. - * OF1.0 switches may or may not support the Nicira role request extensions. - * To indicate the support, this property should be set by the associated - * OF1.0 switch driver in the net.onrc.onos.core.drivermanager package. - * The property will be ignored for OF1.3 switches. - */ - public static final String SWITCH_SUPPORTS_NX_ROLE = "supportsNxRole"; - - - //************************ - // Channel related - //************************ - - /** - * Disconnects the switch by closing the TCP connection. Results in a call - * to the channel handler's channelDisconnected method for cleanup - * @throws IOException - */ - public void disconnectSwitch(); - - /** - * Writes to the OFMessage to the output stream. - * - * @param m - * @throws IOException - */ - public void write(OFMessage m) throws IOException; - - /** - * Writes the list of messages to the output stream. - * - * @param msglist - * @throws IOException - */ - public void write(List msglist) throws IOException; - - /** - * Gets the date the switch connected to this controller. - * - * @return the date - */ - public Date getConnectedSince(); - - /** - * Gets the next available transaction id. - * - * @return the next transaction ID - */ - public int getNextTransactionId(); - - /** - * Checks if the switch is still connected. - * Only call while holding processMessageLock - * - * @return whether the switch is still disconnected - */ - public boolean isConnected(); - - /** - * Sets whether the switch is connected. - * Only call while holding modifySwitchLock - * - * @param connected whether the switch is connected - */ - public void setConnected(boolean connected); - - /** - * Flushes all flows queued for this switch in the current thread. - * NOTE: The contract is limited to the current thread - */ - public void flush(); - - /** - * Sets the Netty Channel this switch instance is associated with. - *

- * Called immediately after instantiation - * - * @param channel the channel - */ - public void setChannel(Channel channel); - - //************************ - // Switch features related - //************************ - - /** - * Gets the datapathId of the switch. - * - * @return the switch buffers - */ - public long getId(); - - /** - * Gets a string version of the ID for this switch. - * - * @return string version of the ID - */ - public String getStringId(); - - /** - * Gets the number of buffers. - * - * @return the number of buffers - */ - public int getNumBuffers(); - - public Set getCapabilities(); - - public byte getNumTables(); - - /** - * Returns an OFDescStatsReply message object. Use the methods contained - * to retrieve switch descriptions for Manufacturer, Hw/Sw version etc. - */ - public OFDescStatsReply getSwitchDescription(); - - /** - * Cancel features reply with a specific transaction ID. - * @param transactionId the transaction ID - */ - public void cancelFeaturesReply(int transactionId); - - /** - * Gets the OFActionType set. - *

- * getActions has relevance only for an OpenFlow 1.0 switch. - * For OF1.3, each table can support different actions - * - * @return the action set - */ - public Set getActions(); - - public void setOFVersion(OFVersion ofv); - - public OFVersion getOFVersion(); - - - //************************ - // Switch port related - //************************ - - /** - * the type of change that happened to an open flow port. - */ - public enum PortChangeType { - /** Either a new port has been added by the switch, or we are - * adding a port we just deleted (via a prior notification) due to - * a change in the portNumber-portName mapping. - */ - ADD, - /** some other feature of the port has changed (eg. speed)*/ - OTHER_UPDATE, - /** Either a port has been deleted by the switch, or we are deleting - * a port whose portNumber-portName mapping has changed. Note that in - * the latter case, a subsequent notification will be sent out to add a - * port with the new portNumber-portName mapping. - */ - DELETE, - /** Port is up (i.e. enabled). Presumably an earlier notification had - * indicated that it was down. To be UP implies that the port is - * administratively considered UP (see ofp_port_config) AND the port - * link is up AND the port is no longer blocked (see ofp_port_state). - */ - UP, - /** Port is down (i.e. disabled). Presumably an earlier notification had - * indicated that it was up, or the port was always up. - * To be DOWN implies that the port has been either - * administratively brought down (see ofp_port_config) OR the port - * link is down OR the port is blocked (see ofp_port_state). - */ - DOWN, - } - - /** - * Describes a change of an open flow port. - */ - public static class PortChangeEvent { - public final OFPortDesc port; - public final PortChangeType type; - /** - * @param port - * @param type - */ - public PortChangeEvent(OFPortDesc port, - PortChangeType type) { - this.port = port; - this.type = type; - } - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((port == null) ? 0 : port.hashCode()); - result = prime * result + ((type == null) ? 0 : type.hashCode()); - return result; - } - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - PortChangeEvent other = (PortChangeEvent) obj; - if (port == null) { - if (other.port != null) { - return false; - } - } else if (!port.equals(other.port)) { - return false; - } - if (type != other.type) { - return false; - } - return true; - } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "[" + type + " " + port.toString() + "]"; - } - } - - - /** - * Get list of all enabled ports. This will typically be different from - * the list of ports in the OFFeaturesReply, since that one is a static - * snapshot of the ports at the time the switch connected to the controller - * whereas this port list also reflects the port status messages that have - * been received. - * - * @return Unmodifiable list of ports not backed by the underlying collection - */ - public Collection getEnabledPorts(); - - /** - * Get list of the port numbers of all enabled ports. This will typically - * be different from the list of ports in the OFFeaturesReply, since that - * one is a static snapshot of the ports at the time the switch connected - * to the controller whereas this port list also reflects the port status - * messages that have been received. - * - * @return Unmodifiable list of ports not backed by the underlying collection - */ - public Collection getEnabledPortNumbers(); - - /** - * Retrieve the port object by the port number. The port object - * is the one that reflects the port status updates that have been - * received, not the one from the features reply. - * - * @param portNumber - * @return port object - */ - public OFPortDesc getPort(int portNumber); - - /** - * Retrieve the port object by the port name. The port object - * is the one that reflects the port status updates that have been - * received, not the one from the features reply. - * - * @param portName - * @return port object - */ - public OFPortDesc getPort(String portName); - - /** - * Add or modify a switch port. This is called by the core controller - * code in response to a OFPortStatus message. - * - * OFPPR_MODIFY and OFPPR_ADD will be treated as equivalent. The OpenFlow - * spec is not clear on whether portNames are portNumbers are considered - * authoritative identifiers. We treat portNames <-> portNumber mappings - * as fixed. If they change, we delete all previous conflicting ports and - * add all new ports. - * - * @param ps the port status message - * @return the ordered Collection of changes "applied" to the old ports - * of the switch according to the PortStatus message. A single PortStatus - * message can result in multiple changes. - * If portName <-> portNumber mappings have - * changed, the iteration order ensures that delete events for old - * conflicting appear before before events adding new ports - */ - public OrderedCollection processOFPortStatus(OFPortStatus ps); - - /** - * Get list of all ports. This will typically be different from - * the list of ports in the OFFeaturesReply, since that one is a static - * snapshot of the ports at the time the switch connected to the controller - * whereas this port list also reflects the port status messages that have - * been received. - * - * @return Unmodifiable list of ports - */ - public Collection getPorts(); - - /** - * @param portName - * @return Whether a port is enabled per latest port status message - * (not configured down nor link down nor in spanning tree blocking state) - */ - public boolean portEnabled(int portName); - - /** - * @param portName - * @return Whether a port is enabled per latest port status message - * (not configured down nor link down nor in spanning tree blocking state) - */ - public boolean portEnabled(String portName); - - /** - * Compute the changes that would be required to replace the old ports - * of this switch with the new ports. - * @param ports new ports to set - * @return the ordered collection of changes "applied" to the old ports - * of the switch in order to set them to the new set. - * If portName <-> portNumber mappings have - * changed, the iteration order ensures that delete events for old - * conflicting appear before before events adding new ports - */ - public OrderedCollection - comparePorts(Collection ports); - - /** - * Replace the ports of this switch with the given ports. - * @param ports new ports to set - * @return the ordered collection of changes "applied" to the old ports - * of the switch in order to set them to the new set. - * If portName <-> portNumber mappings have - * changed, the iteration order ensures that delete events for old - * conflicting appear before before events adding new ports - */ - public OrderedCollection - setPorts(Collection ports); - - //******************************************* - // IOFSwitch object attributes - //************************ - - /** - * Gets attributes of this switch. - * - * @return attributes of the switch - */ - public Map getAttributes(); - - /** - * Checks if a specific switch property exists for this switch. - * - * @param name name of property - * @return value for name - */ - boolean hasAttribute(String name); - - /** - * Gets properties for switch specific behavior. - * - * @param name name of property - * @return 'value' for 'name', or null if no entry for 'name' exists - */ - Object getAttribute(String name); - - /** - * Sets properties for switch specific behavior. - * - * @param name name of property - * @param value value for name - */ - void setAttribute(String name, Object value); - - /** - * Removes properties for switch specific behavior. - * - * @param name name of property - * @return current value for name or null (if not present) - */ - Object removeAttribute(String name); - - //************************ - // Switch statistics - //************************ - - /** - * Delivers the statistics future reply. - * - * @param reply the reply to deliver - */ - public void deliverStatisticsReply(OFMessage reply); - - /** - * Cancels the statistics reply with the given transaction ID. - * - * @param transactionId the transaction ID - */ - public void cancelStatisticsReply(int transactionId); - - /** - * Cancels all statistics replies. - */ - public void cancelAllStatisticsReplies(); - - /** - * Gets a Future object that can be used to retrieve the asynchronous. - * OFStatisticsReply when it is available. - * - * @param request statistics request - * @return Future object wrapping OFStatisticsReply - * @throws IOException - */ - public Future> getStatistics(OFStatsRequest request) - throws IOException; - - //************************ - // Switch other utilities - //************************ - - /** - * Clears all flowmods on this switch. - */ - public void clearAllFlowMods(); - - /** - * Gets the current role of this controller for this IOFSwitch. - */ - public Role getRole(); - - /** - * Sets this controller's Role for this IOFSwitch to role. - * - * @param role - */ - public void setRole(Role role); - - /** - * Gets the next generation ID. - *

- * Note: relevant for role request messages in OF1.3 - * - * @return next generation ID - */ - public U64 getNextGenerationId(); - - - /** - * Set debug counter service for per-switch counters. - * Called immediately after instantiation. - * @param debugCounter - * @throws CounterException - */ - public void setDebugCounterService(IDebugCounterService debugCounter) - throws CounterException; - - /** - * Start this switch driver's sub handshake. This might be a no-op but - * this method must be called at least once for the switch to be become - * ready. - * This method must only be called from the I/O thread - * @throws IOException - * @throws org.onlab.onos.of.controller.impl.internal.SwitchDriverSubHandshakeAlreadyStarted - * if the sub-handshake has - * already been started - */ - public void startDriverHandshake() throws IOException; - - /** - * Check if the sub-handshake for this switch driver has been completed. - * This method can only be called after startDriverHandshake() - * - * This methods must only be called from the I/O thread - * @return true if the sub-handshake has been completed. False otherwise - * @throws org.onlab.onos.of.controller.impl.internal.SwitchDriverSubHandshakeNotStarted - * if startDriverHandshake() has - * not been called yet. - */ - public boolean isDriverHandshakeComplete(); - - /** - * Pass the given OFMessage to the driver as part of this driver's - * sub-handshake. Must not be called after the handshake has been completed - * This methods must only be called from the I/O thread - * @param m The message that the driver should process - * @throws org.onlab.onos.of.controller.impl.internal.SwitchDriverSubHandshakeCompleted - * if isDriverHandshake() returns - * false before this method call - * @throws org.onlab.onos.of.controller.impl.internal.SwitchDriverSubHandshakeNotStarted - * if startDriverHandshake() has - * not been called yet. - */ - public void processDriverHandshakeMessage(OFMessage m); - - /** - * Set the flow table full flag in the switch. - * XXX S Rethink this for multiple tables - */ - public void setTableFull(boolean isFull); - - /** - * Save the features reply for this switch. - * - * @param featuresReply - */ - public void setFeaturesReply(OFFeaturesReply featuresReply); - - /** - * Save the portset for this switch. - * - * @param portDescReply - */ - public void setPortDescReply(OFPortDescStatsReply portDescReply); - - //************************ - // Message handling - //************************ - /** - * Handle the message coming from the dataplane. - * - * @param m the actual message - */ - public void handleMessage(OFMessage m); - - -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/debugcounter/DebugCounter.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/debugcounter/DebugCounter.java deleted file mode 100644 index f88a43e892..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/debugcounter/DebugCounter.java +++ /dev/null @@ -1,727 +0,0 @@ -package org.onlab.onos.of.controller.impl.debugcounter; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -import com.google.common.collect.Sets; - - - -/** - * This class implements a central store for all counters used for debugging the - * system. For counters based on traffic-type, see ICounterStoreService. - * - */ -//CHECKSTYLE:OFF -public class DebugCounter implements IDebugCounterService { - protected static final Logger log = LoggerFactory.getLogger(DebugCounter.class); - - /** - * registered counters need a counter id. - */ - protected AtomicInteger counterIdCounter = new AtomicInteger(); - - /** - * The counter value. - */ - protected static class MutableLong { - long value = 0; - public void increment() { value += 1; } - public void increment(long incr) { value += incr; } - public long get() { return value; } - public void set(long val) { value = val; } - } - - /** - * protected class to store counter information. - */ - public static class CounterInfo { - String moduleCounterHierarchy; - String counterDesc; - CounterType ctype; - String moduleName; - String counterHierarchy; - int counterId; - boolean enabled; - String[] metaData; - - public CounterInfo(int counterId, boolean enabled, - String moduleName, String counterHierarchy, - String desc, CounterType ctype, String... metaData) { - this.moduleCounterHierarchy = moduleName + "/" + counterHierarchy; - this.moduleName = moduleName; - this.counterHierarchy = counterHierarchy; - this.counterDesc = desc; - this.ctype = ctype; - this.counterId = counterId; - this.enabled = enabled; - this.metaData = metaData; - } - - public String getModuleCounterHierarchy() { return moduleCounterHierarchy; } - public String getCounterDesc() { return counterDesc; } - public CounterType getCtype() { return ctype; } - public String getModuleName() { return moduleName; } - public String getCounterHierarchy() { return counterHierarchy; } - public int getCounterId() { return counterId; } - public boolean isEnabled() { return enabled; } - public String[] getMetaData() { return this.metaData.clone(); } - } - - //****************** - // Global stores - //****************** - - /** - * Counter info for a debug counter. - */ - public static class DebugCounterInfo { - CounterInfo cinfo; - AtomicLong cvalue; - - public DebugCounterInfo(CounterInfo cinfo) { - this.cinfo = cinfo; - this.cvalue = new AtomicLong(); - } - public CounterInfo getCounterInfo() { - return cinfo; - } - public Long getCounterValue() { - return cvalue.get(); - } - } - - /** - * Global debug-counter storage across all threads. These are - * updated from the local per thread counters by the flush counters method. - */ - private static final DebugCounterInfo[] ALLCOUNTERS = - new DebugCounterInfo[MAX_COUNTERS]; - - - /** - * per module counters, indexed by the module name and storing three levels - * of Counter information in the form of CounterIndexStore. - */ - protected ConcurrentHashMap> - moduleCounters = new ConcurrentHashMap>(); - - protected static class CounterIndexStore { - int index; - Map nextLevel; - - public CounterIndexStore(int index, Map cis) { - this.index = index; - this.nextLevel = cis; - } - } - - /** - * fast global cache for counter ids that are currently active. - */ - protected Set currentCounters = Collections.newSetFromMap( - new ConcurrentHashMap()); - - //****************** - // Thread local stores - //****************** - - /** - * Thread local storage of counter info. - */ - protected static class LocalCounterInfo { - boolean enabled; - MutableLong cvalue; - - public LocalCounterInfo(boolean enabled) { - this.enabled = enabled; - this.cvalue = new MutableLong(); - } - } - - /** - * Thread local debug counters used for maintaining counters local to a thread. - */ - protected final ThreadLocal threadlocalCounters = - new ThreadLocal() { - @Override - protected LocalCounterInfo[] initialValue() { - return new LocalCounterInfo[MAX_COUNTERS]; - } - }; - - /** - * Thread local cache for counter ids that are currently active. - */ - protected final ThreadLocal> threadlocalCurrentCounters = - new ThreadLocal>() { - @Override - protected Set initialValue() { - return new HashSet(); - } - }; - - //******************************* - // IDebugCounter - //******************************* - - protected class CounterImpl implements IDebugCounter { - private final int counterId; - - public CounterImpl(int counterId) { - this.counterId = counterId; - } - - @Override - public void updateCounterWithFlush() { - if (!validCounterId()) { - return; - } - updateCounter(counterId, 1, true); - } - - @Override - public void updateCounterNoFlush() { - if (!validCounterId()) { - return; - } - updateCounter(counterId, 1, false); - } - - @Override - public void updateCounterWithFlush(int incr) { - if (!validCounterId()) { - return; - } - updateCounter(counterId, incr, true); - } - - @Override - public void updateCounterNoFlush(int incr) { - if (!validCounterId()) { - return; - } - updateCounter(counterId, incr, false); - } - - @Override - public long getCounterValue() { - if (!validCounterId()) { - return -1; - } - return ALLCOUNTERS[counterId].cvalue.get(); - } - - /** - * Checks if this is a valid counter. - * @return true if the counter id is valid - */ - private boolean validCounterId() { - if (counterId < 0 || counterId >= MAX_COUNTERS) { - log.error("Invalid counterId invoked"); - return false; - } - return true; - } - - } - - //******************************* - // IDebugCounterService - //******************************* - - @Override - public IDebugCounter registerCounter(String moduleName, String counterHierarchy, - String counterDescription, CounterType counterType, - String... metaData) - throws CounterException { - // check if counter already exists - if (!moduleCounters.containsKey(moduleName)) { - moduleCounters.putIfAbsent(moduleName, - new ConcurrentHashMap()); - } - RetCtrInfo rci = getCounterId(moduleName, counterHierarchy); - if (rci.allLevelsFound) { - // counter exists - log.info("Counter exists for {}/{} -- resetting counters", moduleName, - counterHierarchy); - resetCounterHierarchy(moduleName, counterHierarchy); - return new CounterImpl(rci.ctrIds[rci.foundUptoLevel - 1]); - } - // check for validity of counter - if (rci.levels.length > MAX_HIERARCHY) { - String err = "Registry of counterHierarchy " + counterHierarchy + - " exceeds max hierachy " + MAX_HIERARCHY + ".. aborting"; - throw new MaxHierarchyRegistered(err); - } - if (rci.foundUptoLevel < rci.levels.length - 1) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i <= rci.foundUptoLevel; i++) { - sb.append(rci.levels[i]); - } - String needToRegister = sb.toString(); - String err = "Attempting to register hierarchical counterHierarchy " + - counterHierarchy + " but parts of hierarchy missing. " + - "Please register " + needToRegister + " first"; - throw new MissingHierarchicalLevel(err); - } - - // get a new counter id - int counterId = counterIdCounter.getAndIncrement(); - if (counterId >= MAX_COUNTERS) { - throw new MaxCountersRegistered("max counters reached"); - } - // create storage for counter - boolean enabled = (counterType == CounterType.ALWAYS_COUNT) ? true : false; - CounterInfo ci = new CounterInfo(counterId, enabled, moduleName, - counterHierarchy, counterDescription, - counterType, metaData); - ALLCOUNTERS[counterId] = new DebugCounterInfo(ci); - - // account for the new counter in the module counter hierarchy - addToModuleCounterHierarchy(moduleName, counterId, rci); - - // finally add to active counters - if (enabled) { - currentCounters.add(counterId); - } - return new CounterImpl(counterId); - } - - private void updateCounter(int counterId, int incr, boolean flushNow) { - if (counterId < 0 || counterId >= MAX_COUNTERS) { - return; - } - - LocalCounterInfo[] thiscounters = this.threadlocalCounters.get(); - if (thiscounters[counterId] == null) { - // seeing this counter for the first time in this thread - create local - // store by consulting global store - DebugCounterInfo dc = ALLCOUNTERS[counterId]; - if (dc != null) { - thiscounters[counterId] = new LocalCounterInfo(dc.cinfo.enabled); - if (dc.cinfo.enabled) { - Set thisset = this.threadlocalCurrentCounters.get(); - thisset.add(counterId); - } - } else { - log.error("updateCounter seen locally for counter {} but no global" - + "storage exists for it yet .. not updating", counterId); - return; - } - } - - // update local store if enabled locally for updating - LocalCounterInfo lc = thiscounters[counterId]; - if (lc.enabled) { - lc.cvalue.increment(incr); - if (flushNow) { - DebugCounterInfo dc = ALLCOUNTERS[counterId]; - if (dc.cinfo.enabled) { - // globally enabled - flush now - dc.cvalue.addAndGet(lc.cvalue.get()); - lc.cvalue.set(0); - } else { - // global counter is disabled - don't flush, disable locally - lc.enabled = false; - Set thisset = this.threadlocalCurrentCounters.get(); - thisset.remove(counterId); - } - } - } - } - - @Override - public void flushCounters() { - LocalCounterInfo[] thiscounters = this.threadlocalCounters.get(); - Set thisset = this.threadlocalCurrentCounters.get(); - ArrayList temp = new ArrayList(); - - for (int counterId : thisset) { - LocalCounterInfo lc = thiscounters[counterId]; - if (lc.cvalue.get() > 0) { - DebugCounterInfo dc = ALLCOUNTERS[counterId]; - if (dc.cinfo.enabled) { - // globally enabled - flush now - dc.cvalue.addAndGet(lc.cvalue.get()); - lc.cvalue.set(0); - } else { - // global counter is disabled - don't flush, disable locally - lc.enabled = false; - temp.add(counterId); - } - } - } - for (int cId : temp) { - thisset.remove(cId); - } - - // At this point it is possible that the thread-local set does not - // include a counter that has been enabled and is present in the global set. - // We need to sync thread-local currently enabled set of counterIds with - // the global set. - Sets.SetView sv = Sets.difference(currentCounters, thisset); - for (int counterId : sv) { - if (thiscounters[counterId] != null) { - thiscounters[counterId].enabled = true; - thisset.add(counterId); - } - } - } - - @Override - public void resetCounterHierarchy(String moduleName, String counterHierarchy) { - RetCtrInfo rci = getCounterId(moduleName, counterHierarchy); - if (!rci.allLevelsFound) { - String missing = rci.levels[rci.foundUptoLevel]; - log.error("Cannot reset counter hierarchy - missing counter {}", missing); - return; - } - // reset at this level - ALLCOUNTERS[rci.ctrIds[rci.foundUptoLevel - 1]].cvalue.set(0); - // reset all levels below - ArrayList resetIds = getHierarchyBelow(moduleName, rci); - for (int index : resetIds) { - ALLCOUNTERS[index].cvalue.set(0); - } - } - - @Override - public void resetAllCounters() { - RetCtrInfo rci = new RetCtrInfo(); - rci.levels = "".split("/"); - for (String moduleName : moduleCounters.keySet()) { - ArrayList resetIds = getHierarchyBelow(moduleName, rci); - for (int index : resetIds) { - ALLCOUNTERS[index].cvalue.set(0); - } - } - } - - @Override - public void resetAllModuleCounters(String moduleName) { - Map target = moduleCounters.get(moduleName); - RetCtrInfo rci = new RetCtrInfo(); - rci.levels = "".split("/"); - - if (target != null) { - ArrayList resetIds = getHierarchyBelow(moduleName, rci); - for (int index : resetIds) { - ALLCOUNTERS[index].cvalue.set(0); - } - } else { - if (log.isDebugEnabled()) { - log.debug("No module found with name {}", moduleName); - } - } - } - - @Override - public void enableCtrOnDemand(String moduleName, String counterHierarchy) { - RetCtrInfo rci = getCounterId(moduleName, counterHierarchy); - if (!rci.allLevelsFound) { - String missing = rci.levels[rci.foundUptoLevel]; - log.error("Cannot enable counter - counter not found {}", missing); - return; - } - // enable specific counter - DebugCounterInfo dc = ALLCOUNTERS[rci.ctrIds[rci.foundUptoLevel - 1]]; - dc.cinfo.enabled = true; - currentCounters.add(dc.cinfo.counterId); - } - - @Override - public void disableCtrOnDemand(String moduleName, String counterHierarchy) { - RetCtrInfo rci = getCounterId(moduleName, counterHierarchy); - if (!rci.allLevelsFound) { - String missing = rci.levels[rci.foundUptoLevel]; - log.error("Cannot disable counter - counter not found {}", missing); - return; - } - // disable specific counter - DebugCounterInfo dc = ALLCOUNTERS[rci.ctrIds[rci.foundUptoLevel - 1]]; - if (dc.cinfo.ctype == CounterType.COUNT_ON_DEMAND) { - dc.cinfo.enabled = false; - dc.cvalue.set(0); - currentCounters.remove(dc.cinfo.counterId); - } - } - - @Override - public List getCounterHierarchy(String moduleName, - String counterHierarchy) { - RetCtrInfo rci = getCounterId(moduleName, counterHierarchy); - if (!rci.allLevelsFound) { - String missing = rci.levels[rci.foundUptoLevel]; - log.error("Cannot fetch counter - counter not found {}", missing); - return Collections.emptyList(); - } - ArrayList dcilist = new ArrayList(); - // get counter and all below it - DebugCounterInfo dc = ALLCOUNTERS[rci.ctrIds[rci.foundUptoLevel - 1]]; - dcilist.add(dc); - ArrayList belowIds = getHierarchyBelow(moduleName, rci); - for (int index : belowIds) { - dcilist.add(ALLCOUNTERS[index]); - } - return dcilist; - } - - @Override - public List getAllCounterValues() { - List dcilist = new ArrayList(); - RetCtrInfo rci = new RetCtrInfo(); - rci.levels = "".split("/"); - - for (String moduleName : moduleCounters.keySet()) { - ArrayList resetIds = getHierarchyBelow(moduleName, rci); - for (int index : resetIds) { - dcilist.add(ALLCOUNTERS[index]); - } - } - return dcilist; - } - - @Override - public List getModuleCounterValues(String moduleName) { - List dcilist = new ArrayList(); - RetCtrInfo rci = new RetCtrInfo(); - rci.levels = "".split("/"); - - if (moduleCounters.containsKey(moduleName)) { - ArrayList resetIds = getHierarchyBelow(moduleName, rci); - for (int index : resetIds) { - dcilist.add(ALLCOUNTERS[index]); - } - } - return dcilist; - } - - @Override - public boolean containsModuleCounterHierarchy(String moduleName, - String counterHierarchy) { - if (!moduleCounters.containsKey(moduleName)) { - return false; - } - RetCtrInfo rci = getCounterId(moduleName, counterHierarchy); - return rci.allLevelsFound; - } - - @Override - public boolean containsModuleName(String moduleName) { - return (moduleCounters.containsKey(moduleName)) ? true : false; - } - - @Override - public List getModuleList() { - List retval = new ArrayList(); - retval.addAll(moduleCounters.keySet()); - return retval; - } - - @Override - public List getModuleCounterList(String moduleName) { - if (!moduleCounters.containsKey(moduleName)) { - return Collections.emptyList(); - } - - List retval = new ArrayList(); - RetCtrInfo rci = new RetCtrInfo(); - rci.levels = "".split("/"); - - ArrayList cids = getHierarchyBelow(moduleName, rci); - for (int index : cids) { - retval.add(ALLCOUNTERS[index].cinfo.counterHierarchy); - } - return retval; - } - - //******************************* - // Internal Methods - //******************************* - - protected class RetCtrInfo { - boolean allLevelsFound; // counter indices found all the way down the hierarchy - boolean hierarchical; // true if counterHierarchy is hierarchical - int foundUptoLevel; - int[] ctrIds; - String[] levels; - - public RetCtrInfo() { - ctrIds = new int[MAX_HIERARCHY]; - for (int i = 0; i < MAX_HIERARCHY; i++) { - ctrIds[i] = -1; - } - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + getOuterType().hashCode(); - result = prime * result + (allLevelsFound ? 1231 : 1237); - result = prime * result + Arrays.hashCode(ctrIds); - result = prime * result + foundUptoLevel; - result = prime * result + (hierarchical ? 1231 : 1237); - result = prime * result + Arrays.hashCode(levels); - return result; - } - - @Override - public boolean equals(Object oth) { - if (!(oth instanceof RetCtrInfo)) { - return false; - } - RetCtrInfo other = (RetCtrInfo) oth; - if (other.allLevelsFound != this.allLevelsFound) { - return false; - } - if (other.hierarchical != this.hierarchical) { - return false; - } - if (other.foundUptoLevel != this.foundUptoLevel) { - return false; - } - if (!Arrays.equals(other.ctrIds, this.ctrIds)) { - return false; - } - if (!Arrays.equals(other.levels, this.levels)) { - return false; - } - return true; - } - - private DebugCounter getOuterType() { - return DebugCounter.this; - } - - - - } - - protected RetCtrInfo getCounterId(String moduleName, String counterHierarchy) { - RetCtrInfo rci = new RetCtrInfo(); - Map templevel = moduleCounters.get(moduleName); - rci.levels = counterHierarchy.split("/"); - if (rci.levels.length > 1) { - rci.hierarchical = true; - } - if (templevel == null) { - log.error("moduleName {} does not exist in debugCounters", moduleName); - return rci; - } - - /* - if (rci.levels.length > MAX_HIERARCHY) { - // chop off all array elems greater that MAX_HIERARCHY - String[] temp = new String[MAX_HIERARCHY]; - System.arraycopy(rci.levels, 0, temp, 0, MAX_HIERARCHY); - rci.levels = temp; - } - */ - for (int i = 0; i < rci.levels.length; i++) { - if (templevel != null) { - CounterIndexStore cis = templevel.get(rci.levels[i]); - if (cis == null) { - // could not find counterHierarchy part at this level - break; - } else { - rci.ctrIds[i] = cis.index; - templevel = cis.nextLevel; - rci.foundUptoLevel++; - if (i == rci.levels.length - 1) { - rci.allLevelsFound = true; - } - } - } else { - // there are no more levels, which means that some part of the - // counterHierarchy has no corresponding map - break; - } - } - return rci; - } - - protected void addToModuleCounterHierarchy(String moduleName, int counterId, - RetCtrInfo rci) { - Map target = moduleCounters.get(moduleName); - if (target == null) { - return; - } - CounterIndexStore cis = null; - - for (int i = 0; i < rci.foundUptoLevel; i++) { - cis = target.get(rci.levels[i]); - target = cis.nextLevel; - } - if (cis != null) { - if (cis.nextLevel == null) { - cis.nextLevel = new ConcurrentHashMap(); - } - cis.nextLevel.put(rci.levels[rci.foundUptoLevel], - new CounterIndexStore(counterId, null)); - } else { - target.put(rci.levels[rci.foundUptoLevel], - new CounterIndexStore(counterId, null)); - } - } - - // given a partial hierarchical counter, return the rest of the hierarchy - protected ArrayList getHierarchyBelow(String moduleName, RetCtrInfo rci) { - Map target = moduleCounters.get(moduleName); - CounterIndexStore cis = null; - ArrayList retval = new ArrayList(); - if (target == null) { - return retval; - } - - // get to the level given - for (int i = 0; i < rci.foundUptoLevel; i++) { - cis = target.get(rci.levels[i]); - target = cis.nextLevel; - } - - if (target == null || rci.foundUptoLevel == MAX_HIERARCHY) { - // no more levels - return retval; - } else { - // recursively get all ids - getIdsAtLevel(target, retval, rci.foundUptoLevel + 1); - } - - return retval; - } - - protected void getIdsAtLevel(Map hcy, - ArrayList retval, int level) { - if (level > MAX_HIERARCHY) { - return; - } - if (hcy == null || retval == null) { - return; - } - - // Can return the counter names as well but for now ids are enough. - for (CounterIndexStore cistemp : hcy.values()) { - retval.add(cistemp.index); // value at this level - if (cistemp.nextLevel != null) { - getIdsAtLevel(cistemp.nextLevel, retval, level + 1); - } - } - } - -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/debugcounter/IDebugCounter.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/debugcounter/IDebugCounter.java deleted file mode 100644 index 2dc0c0af2a..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/debugcounter/IDebugCounter.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.onlab.onos.of.controller.impl.debugcounter; - -public interface IDebugCounter { - /** - * Increments the counter by 1 thread-locally, and immediately flushes to - * the global counter storage. This method should be used for counters that - * are updated outside the OF message processing pipeline. - */ - void updateCounterWithFlush(); - - /** - * Increments the counter by 1 thread-locally. Flushing to the global - * counter storage is delayed (happens with flushCounters() in IDebugCounterService), - * resulting in higher performance. This method should be used for counters - * updated in the OF message processing pipeline. - */ - void updateCounterNoFlush(); - - /** - * Increments the counter thread-locally by the 'incr' specified, and immediately - * flushes to the global counter storage. This method should be used for counters - * that are updated outside the OF message processing pipeline. - */ - void updateCounterWithFlush(int incr); - - /** - * Increments the counter thread-locally by the 'incr' specified. Flushing to the global - * counter storage is delayed (happens with flushCounters() in IDebugCounterService), - * resulting in higher performance. This method should be used for counters - * updated in the OF message processing pipeline. - */ - void updateCounterNoFlush(int incr); - - /** - * Retrieve the value of the counter from the global counter store. - */ - long getCounterValue(); -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/debugcounter/IDebugCounterService.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/debugcounter/IDebugCounterService.java deleted file mode 100644 index 216ee741cb..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/debugcounter/IDebugCounterService.java +++ /dev/null @@ -1,262 +0,0 @@ -package org.onlab.onos.of.controller.impl.debugcounter; - - - -import java.util.List; - -import org.onlab.onos.of.controller.impl.debugcounter.DebugCounter.DebugCounterInfo; - -//CHECKSTYLE:OFF -public interface IDebugCounterService { - - /** - * Different counter types. Counters that are meant to be counted-on-demand - * need to be separately enabled/disabled. - */ - public enum CounterType { - ALWAYS_COUNT, - COUNT_ON_DEMAND - } - - /** - * Debug Counter Qualifiers. - */ - public static final String CTR_MDATA_WARN = "warn"; - public static final String CTR_MDATA_ERROR = "error"; - public static final String CTR_MDATA_DROP = "drop"; - - /** - * A limit on the maximum number of counters that can be created. - */ - public static final int MAX_COUNTERS = 5000; - - /** - * Exception thrown when MAX_COUNTERS have been registered. - */ - public class MaxCountersRegistered extends CounterException { - private static final long serialVersionUID = 3173747663719376745L; - String errormsg; - public MaxCountersRegistered(String errormsg) { - this.errormsg = errormsg; - } - @Override - public String getMessage() { - return this.errormsg; - } - } - /** - * Exception thrown when MAX_HIERARCHY has been reached. - */ - public class MaxHierarchyRegistered extends CounterException { - private static final long serialVersionUID = 967431358683523871L; - private String errormsg; - public MaxHierarchyRegistered(String errormsg) { - this.errormsg = errormsg; - } - @Override - public String getMessage() { - return this.errormsg; - } - } - /** - * Exception thrown when attempting to register a hierarchical counter - * where higher levels of the hierarchy have not been pre-registered. - */ - public class MissingHierarchicalLevel extends CounterException { - private static final long serialVersionUID = 517315311533995739L; - private String errormsg; - public MissingHierarchicalLevel(String errormsg) { - this.errormsg = errormsg; - } - @Override - public String getMessage() { - return this.errormsg; - } - } - - public class CounterException extends Exception { - private static final long serialVersionUID = 2219781500857866035L; - } - - /** - * maximum levels of hierarchy. - * Example of moduleName/counterHierarchy: - * switch/00:00:00:00:01:02:03:04/pktin/drops where - * moduleName ==> "switch" and - * counterHierarchy of 3 ==> "00:00:00:00:01:02:03:04/pktin/drops" - */ - public static final int MAX_HIERARCHY = 3; - - /** - * All modules that wish to have the DebugCounterService count for them, must - * register their counters by making this call (typically from that module's - * 'startUp' method). The counter can then be updated, displayed, reset etc. - * using the registered moduleName and counterHierarchy. - * - * @param moduleName the name of the module which is registering the - * counter eg. linkdiscovery or controller or switch - * @param counterHierarchy the hierarchical counter name specifying all - * the hierarchical levels that come above it. - * For example: to register a drop counter for - * packet-ins from a switch, the counterHierarchy - * can be "00:00:00:00:01:02:03:04/pktin/drops" - * It is necessary that counters in hierarchical levels - * above have already been pre-registered - in this - * example: "00:00:00:00:01:02:03:04/pktin" and - * "00:00:00:00:01:02:03:04" - * @param counterDescription a descriptive string that gives more information - * of what the counter is measuring. For example, - * "Measures the number of incoming packets seen by - * this module". - * @param counterType One of CounterType. On-demand counter types - * need to be explicitly enabled/disabled using other - * methods in this API -- i.e. registering them is - * not enough to start counting. - * @param metaData variable arguments that qualify a counter - * eg. warn, error etc. - * @return IDebugCounter with update methods that can be - * used to update a counter. - * @throws MaxCountersRegistered - * @throws MaxHierarchyRegistered - * @throws MissingHierarchicalLevel - */ - public IDebugCounter registerCounter(String moduleName, String counterHierarchy, - String counterDescription, CounterType counterType, - String... metaData) - throws CounterException; - - /** - * Flush all thread-local counter values (from the current thread) - * to the global counter store. This method is not intended for use by any - * module. It's typical usage is from core and it is meant - * to flush those counters that are updated in the packet-processing pipeline, - * typically with the 'updateCounterNoFlush" methods in IDebugCounter. - */ - public void flushCounters(); - - /** - * Resets the value of counters in the hierarchy to zero. Note that the reset - * applies to the level of counter hierarchy specified AND ALL LEVELS BELOW it - * in the hierarchy. - * For example: If a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops" - * specifying a reset hierarchy: "00:00:00:00:01:02:03:04" - * will reset all counters for the switch dpid specified; - * while specifying a reset hierarchy: ""00:00:00:00:01:02:03:04/pktin" - * will reset the pktin counter and all levels below it (like drops) - * for the switch dpid specified. - */ - void resetCounterHierarchy(String moduleName, String counterHierarchy); - - /** - * Resets the values of all counters in the system. - */ - public void resetAllCounters(); - - /** - * Resets the values of all counters belonging - * to a module with the given 'moduleName'. - */ - public void resetAllModuleCounters(String moduleName); - - /** - * This method applies only to CounterType.COUNT_ON_DEMAND. It is used to - * enable counting on the counter. Note that this step is necessary to start - * counting for these counter types - merely registering the counter is not - * enough (as is the case for CounterType.ALWAYS_COUNT). Newly - * enabled counters start from an initial value of zero. - * - * Enabling a counter in a counterHierarchy enables only THAT counter. It - * does not enable any other part of the counterHierarchy. For example, if - * a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops", where the - * 'pktin' and 'drops' counters are CounterType.COUNT_ON_DEMAND, then enabling - * the 'pktin' counter by specifying the counterHierarchy as - * "00:00:00:00:01:02:03:04/pktin" does NOT enable the 'drops' counter. - */ - public void enableCtrOnDemand(String moduleName, String counterHierarchy); - - /** - * This method applies only to CounterType.COUNT_ON_DEMAND. It is used to - * enable counting on the counter. Note that disabling a counter results in a loss - * of the counter value. When re-enabled the counter will restart from zero. - * - * Disabling a counter in a counterHierarchy disables only THAT counter. It - * does not disable any other part of the counterHierarchy. For example, if - * a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops", where the - * 'pktin' and 'drops' counters are CounterType.COUNT_ON_DEMAND, then disabling - * the 'pktin' counter by specifying the counterHierarchy as - * "00:00:00:00:01:02:03:04/pktin" does NOT disable the 'drops' counter. - */ - public void disableCtrOnDemand(String moduleName, String counterHierarchy); - - /** - * Get counter value and associated information for the specified counterHierarchy. - * Note that information on the level of counter hierarchy specified - * AND ALL LEVELS BELOW it in the hierarchy will be returned. - * - * For example, - * if a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops", then - * specifying a counterHierarchy of "00:00:00:00:01:02:03:04/pktin" in the - * get call will return information on the 'pktin' as well as the 'drops' - * counters for the switch dpid specified. - * - * @return A list of DebugCounterInfo or an empty list if the counter - * could not be found - */ - public List getCounterHierarchy(String moduleName, - String counterHierarchy); - - /** - * Get counter values and associated information for all counters in the - * system. - * - * @return the list of values/info or an empty list - */ - public List getAllCounterValues(); - - /** - * Get counter values and associated information for all counters associated - * with a module. - * - * @param moduleName - * @return the list of values/info or an empty list - */ - public List getModuleCounterValues(String moduleName); - - /** - * Convenience method to figure out if the the given 'counterHierarchy' corresponds - * to a registered counterHierarchy for 'moduleName'. Note that the counter may or - * may not be enabled for counting, but if it is registered the method will - * return true. - * - * @param moduleName - * @param counterHierarchy - * @return false if moduleCounterHierarchy is not a registered counter - */ - public boolean containsModuleCounterHierarchy(String moduleName, - String counterHierarchy); - - /** - * Convenience method to figure out if the the given 'moduleName' corresponds - * to a registered moduleName or not. Note that the module may or may not have - * a counter enabled for counting, but if it is registered the method will - * return true. - * - * @param moduleName - * @return false if moduleName is not a registered counter - */ - public boolean containsModuleName(String moduleName); - - /** - * Returns a list of moduleNames registered for debug counters or an empty - * list if no counters have been registered in the system. - */ - public List getModuleList(); - - /** - * Returns a list of all counters registered for a specific moduleName - * or a empty list. - */ - public List getModuleCounterList(String moduleName); - - -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/debugcounter/NullDebugCounter.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/debugcounter/NullDebugCounter.java deleted file mode 100644 index ae476ea30e..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/debugcounter/NullDebugCounter.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.onlab.onos.of.controller.impl.debugcounter; - -import java.util.Collections; -import java.util.List; - -import org.onlab.onos.of.controller.impl.debugcounter.DebugCounter.DebugCounterInfo; - -//CHECKSTYLE:OFF -public class NullDebugCounter implements IDebugCounterService { - - @Override - public void flushCounters() { - - } - - @Override - public void resetAllCounters() { - - } - - @Override - public void resetAllModuleCounters(String moduleName) { - - } - - - @Override - public void resetCounterHierarchy(String moduleName, String counterHierarchy) { - - } - - @Override - public void enableCtrOnDemand(String moduleName, String counterHierarchy) { - - } - - @Override - public void disableCtrOnDemand(String moduleName, String counterHierarchy) { - - } - - @Override - public List getCounterHierarchy(String moduleName, - String counterHierarchy) { - return Collections.emptyList(); - } - - @Override - public List getAllCounterValues() { - return Collections.emptyList(); - } - - @Override - public List getModuleCounterValues(String moduleName) { - return Collections.emptyList(); - } - - @Override - public boolean containsModuleCounterHierarchy(String moduleName, - String counterHierarchy) { - return false; - } - - @Override - public boolean containsModuleName(String moduleName) { - return false; - } - - @Override - public - IDebugCounter - registerCounter(String moduleName, String counterHierarchy, - String counterDescription, - CounterType counterType, String... metaData) - throws MaxCountersRegistered { - return new NullCounterImpl(); - } - - @Override - public List getModuleList() { - return Collections.emptyList(); - } - - @Override - public List getModuleCounterList(String moduleName) { - return Collections.emptyList(); - } - - public static class NullCounterImpl implements IDebugCounter { - - @Override - public void updateCounterWithFlush() { - - } - - @Override - public void updateCounterNoFlush() { - - } - - @Override - public void updateCounterWithFlush(int incr) { - } - - @Override - public void updateCounterNoFlush(int incr) { - - } - - @Override - public long getCounterValue() { - return -1; - } - - } - -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/AbstractOpenFlowSwitch.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/AbstractOpenFlowSwitch.java new file mode 100644 index 0000000000..7f4830d60c --- /dev/null +++ b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/AbstractOpenFlowSwitch.java @@ -0,0 +1,279 @@ +/** + * Copyright 2011, Big Switch Networks, Inc. + * Originally created by David Erickson, Stanford 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.onlab.onos.of.controller.impl.internal; + +import java.io.IOException; +import java.util.List; + +import org.jboss.netty.channel.Channel; +import org.onlab.onos.of.controller.Dpid; +import org.onlab.onos.of.controller.OpenFlowSwitch; +import org.onlab.onos.of.controller.RoleState; +import org.onlab.onos.of.controller.impl.internal.OpenFlowControllerImpl.OpenFlowSwitchAgent; +import org.onlab.onos.of.controller.impl.internal.RoleManager.RoleRecvStatus; +import org.onlab.onos.of.controller.impl.internal.RoleManager.RoleReplyInfo; +import org.projectfloodlight.openflow.protocol.OFErrorMsg; +import org.projectfloodlight.openflow.protocol.OFExperimenter; +import org.projectfloodlight.openflow.protocol.OFFactories; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.protocol.OFFeaturesReply; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; +import org.projectfloodlight.openflow.protocol.OFRoleReply; +import org.projectfloodlight.openflow.protocol.OFVersion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public abstract class AbstractOpenFlowSwitch implements OpenFlowSwitch { + + private static Logger log = + LoggerFactory.getLogger(AbstractOpenFlowSwitch.class); + + private Channel channel; + private boolean connected; + private Dpid dpid; + private OpenFlowSwitchAgent agent; + + private OFVersion ofVersion; + + protected OFPortDescStatsReply ports; + + protected boolean tableFull; + + private final RoleManager roleMan = new RoleManager(this); + + protected AbstractOpenFlowSwitch(long dpid) { + this.dpid = new Dpid(dpid); + } + + //************************ + // Channel related + //************************ + + /** + * Disconnects the switch by closing the TCP connection. Results in a call + * to the channel handler's channelDisconnected method for cleanup + * @throws IOException + */ + public final void disconnectSwitch() { + this.channel.close(); + } + + /** + * Writes to the OFMessage to the output stream. + * + * @param m the message to be written + */ + public abstract void write(OFMessage m); + + /** + * Writes to the OFMessage list to the output stream. + * + * @param msgs the messages to be written + */ + public abstract void write(List msgs); + + + /** + * Checks if the switch is still connected. + * Only call while holding processMessageLock + * + * @return whether the switch is still disconnected + */ + public final boolean isConnected() { + return this.connected; + } + + /** + * Sets whether the switch is connected. + * Only call while holding modifySwitchLock + * + * @param connected whether the switch is connected + */ + final void setConnected(boolean connected) { + this.connected = connected; + }; + + /** + * Sets the Netty Channel this switch instance is associated with. + *

+ * Called immediately after instantiation + * + * @param channel the channel + */ + public final void setChannel(Channel channel) { + this.channel = channel; + }; + + //************************ + // Switch features related + //************************ + + /** + * Gets the datapathId of the switch. + * + * @return the switch buffers + */ + public final long getId() { + return this.dpid.value(); + }; + + /** + * Gets a string version of the ID for this switch. + * + * @return string version of the ID + */ + public final String getStringId() { + return this.dpid.toString(); + } + + public final void setOFVersion(OFVersion ofV) { + this.ofVersion = ofV; + } + + void setTableFull(boolean full) { + this.tableFull = full; + } + + public abstract void setFeaturesReply(OFFeaturesReply featuresReply); + + /** + * Let peoeple know if you support Nicira style role requests. + * + * @return support Nicira roles or not. + */ + public abstract Boolean supportNxRole(); + + //************************ + // Message handling + //************************ + /** + * Handle the message coming from the dataplane. + * + * @param m the actual message + */ + public final void handleMessage(OFMessage m) { + this.agent.processMessage(m); + } + + public abstract RoleState getRole(); + + final boolean addConnectedSwitch() { + return this.agent.addConnectedSwitch(this.getId(), this); + } + + final boolean addActivatedMasterSwitch() { + return this.agent.addActivatedMasterSwitch(this.getId(), this); + } + + final boolean addActivatedEqualSwitch() { + return this.agent.addActivatedEqualSwitch(this.getId(), this); + } + + final void transitionToEqualSwitch() { + this.agent.transitionToEqualSwitch(this.getId()); + } + + final void transitionToMasterSwitch() { + this.agent.transitionToMasterSwitch(this.getId()); + } + + final void removeConnectedSwitch() { + this.agent.removeConnectedSwitch(this.getId()); + } + + protected OFFactory factory() { + return OFFactories.getFactory(ofVersion); + } + + public void setPortDescReply(OFPortDescStatsReply portDescReply) { + this.ports = portDescReply; + } + + public abstract void startDriverHandshake(); + + public abstract boolean isDriverHandshakeComplete(); + + public abstract void processDriverHandshakeMessage(OFMessage m); + + public void setRole(RoleState role) { + try { + this.roleMan.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE); + } catch (IOException e) { + log.error("Unable to write to switch {}.", this.dpid); + } + } + + // Role Handling + + void handleRole(OFMessage m) throws SwitchStateException { + RoleReplyInfo rri = roleMan.extractOFRoleReply((OFRoleReply) m); + RoleRecvStatus rrs = roleMan.deliverRoleReply(rri); + if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) { + if (rri.getRole() == RoleState.MASTER) { + this.transitionToMasterSwitch(); + } else if (rri.getRole() == RoleState.EQUAL || + rri.getRole() == RoleState.MASTER) { + this.transitionToEqualSwitch(); + } + } + } + + void handleNiciraRole(OFMessage m) throws SwitchStateException { + RoleState role = this.roleMan.extractNiciraRoleReply((OFExperimenter) m); + if (role == null) { + // The message wasn't really a Nicira role reply. We just + // dispatch it to the OFMessage listeners in this case. + this.handleMessage(m); + } + + RoleRecvStatus rrs = this.roleMan.deliverRoleReply( + new RoleReplyInfo(role, null, m.getXid())); + if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) { + if (role == RoleState.MASTER) { + this.transitionToMasterSwitch(); + } else if (role == RoleState.EQUAL || + role == RoleState.SLAVE) { + this.transitionToEqualSwitch(); + } + } + } + + boolean handleRoleError(OFErrorMsg error) { + try { + return RoleRecvStatus.OTHER_EXPECTATION != this.roleMan.deliverError(error); + } catch (SwitchStateException e) { + this.disconnectSwitch(); + } + return true; + } + + void reassertRole() { + if (this.getRole() == RoleState.MASTER) { + this.setRole(RoleState.MASTER); + } + } + + void setAgent(OpenFlowSwitchAgent ag) { + this.agent = ag; + } + + + +} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/Controller.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/Controller.java index 1f0b36db4b..6ae3abb90c 100644 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/Controller.java +++ b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/Controller.java @@ -20,47 +20,22 @@ package org.onlab.onos.of.controller.impl.internal; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; -import org.onlab.onos.of.controller.impl.IOFSwitchManager; -import org.onlab.onos.of.controller.impl.Role; -import org.onlab.onos.of.controller.impl.annotations.LogMessageDoc; -import org.onlab.onos.of.controller.impl.annotations.LogMessageDocs; -import org.onlab.onos.of.controller.impl.debugcounter.DebugCounter; -import org.onlab.onos.of.controller.impl.debugcounter.IDebugCounter; -import org.onlab.onos.of.controller.impl.debugcounter.IDebugCounterService; -import org.onlab.onos.of.controller.impl.debugcounter.IDebugCounterService.CounterException; -import org.onlab.onos.of.controller.impl.debugcounter.IDebugCounterService.CounterType; -import org.onlab.onos.of.controller.impl.internal.OFChannelHandler.RoleRecvStatus; -import org.onlab.onos.of.controller.impl.registry.IControllerRegistry; -import org.onlab.onos.of.controller.impl.registry.RegistryException; -import org.onlab.onos.of.controller.impl.registry.IControllerRegistry.ControlChangeCallback; -import org.onlab.onos.of.controller.impl.util.Dpid; -import org.onlab.onos.of.controller.impl.util.DummySwitchForTesting; -import org.onlab.onos.of.controller.impl.util.InstanceId; -import org.onlab.onos.of.controller.impl.IOFSwitch; -import org.onlab.onos.of.controller.impl.IOFSwitch.PortChangeType; - -import org.apache.felix.scr.annotations.Activate; -import org.apache.felix.scr.annotations.Component; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.group.ChannelGroup; import org.jboss.netty.channel.group.DefaultChannelGroup; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; +import org.onlab.onos.of.controller.impl.annotations.LogMessageDoc; +import org.onlab.onos.of.controller.impl.annotations.LogMessageDocs; +import org.onlab.onos.of.controller.impl.internal.OpenFlowControllerImpl.OpenFlowSwitchAgent; import org.projectfloodlight.openflow.protocol.OFDescStatsReply; import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFFactory; -import org.projectfloodlight.openflow.protocol.OFPortDesc; import org.projectfloodlight.openflow.protocol.OFVersion; -import org.projectfloodlight.openflow.util.HexString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,7 +44,6 @@ import org.slf4j.LoggerFactory; * The main controller class. Handles all setup and network listeners * - Distributed ownership control of switch through IControllerRegistryService */ -@Component(immediate = true) public class Controller { protected static final Logger log = LoggerFactory.getLogger(Controller.class); @@ -78,189 +52,33 @@ public class Controller { protected static final OFFactory FACTORY13 = OFFactories.getFactory(OFVersion.OF_13); protected static final OFFactory FACTORY10 = OFFactories.getFactory(OFVersion.OF_10); - // connectedSwitches cache contains all connected switch's channelHandlers - // including ones where this controller is a master/equal/slave controller - // as well as ones that have not been activated yet - protected ConcurrentHashMap connectedSwitches; - // These caches contains only those switches that are active - protected ConcurrentHashMap activeMasterSwitches; - protected ConcurrentHashMap activeEqualSwitches; - // lock to synchronize on, when manipulating multiple caches above - private Object multiCacheLock; + // The controllerNodeIPsCache maps Controller IDs to their IP address. // It's only used by handleControllerNodeIPsChanged protected HashMap controllerNodeIPsCache; - // Module dependencies - - protected IControllerRegistry registryService; - protected IDebugCounterService debugCounters; - private IOFSwitchManager switchManager; + private ChannelGroup cg; + // Configuration options protected int openFlowPort = 6633; protected int workerThreads = 0; - // defined counters - private Counters counters; - // Start time of the controller protected long systemStartTime; // Flag to always flush flow table on switch reconnect (HA or otherwise) protected boolean alwaysClearFlowsOnSwAdd = false; - private InstanceId instanceId; + private OpenFlowSwitchAgent agent; // Perf. related configuration protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024; protected static final int BATCH_MAX_SIZE = 100; protected static final boolean ALWAYS_DECODE_ETH = true; - protected boolean addConnectedSwitch(long dpid, OFChannelHandler h) { - if (connectedSwitches.get(dpid) != null) { - log.error("Trying to add connectedSwitch but found a previous " - + "value for dpid: {}", dpid); - return false; - } else { - log.error("Added switch {}", dpid); - connectedSwitches.put(dpid, h); - return true; - } - } - - private boolean validActivation(long dpid) { - if (connectedSwitches.get(dpid) == null) { - log.error("Trying to activate switch but is not in " - + "connected switches: dpid {}. Aborting ..", - HexString.toHexString(dpid)); - return false; - } - if (activeMasterSwitches.get(dpid) != null || - activeEqualSwitches.get(dpid) != null) { - log.error("Trying to activate switch but it is already " - + "activated: dpid {}. Found in activeMaster: {} " - + "Found in activeEqual: {}. Aborting ..", new Object[] { - HexString.toHexString(dpid), - (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y', - (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y'}); - counters.switchWithSameDpidActivated.updateCounterWithFlush(); - return false; - } - return true; - } - - /** - * Called when a switch is activated, with this controller's role as MASTER. - */ - protected boolean addActivatedMasterSwitch(long dpid, IOFSwitch sw) { - synchronized (multiCacheLock) { - if (!validActivation(dpid)) { - return false; - } - activeMasterSwitches.put(dpid, sw); - } - //update counters and events - counters.switchActivated.updateCounterWithFlush(); - - return true; - } - - /** - * Called when a switch is activated, with this controller's role as EQUAL. - */ - protected boolean addActivatedEqualSwitch(long dpid, IOFSwitch sw) { - synchronized (multiCacheLock) { - if (!validActivation(dpid)) { - return false; - } - activeEqualSwitches.put(dpid, sw); - } - //update counters and events - counters.switchActivated.updateCounterWithFlush(); - return true; - } - - /** - * Called when this controller's role for a switch transitions from equal - * to master. For 1.0 switches, we internally refer to the role 'slave' as - * 'equal' - so this transition is equivalent to 'addActivatedMasterSwitch'. - */ - protected void transitionToMasterSwitch(long dpid) { - synchronized (multiCacheLock) { - IOFSwitch sw = activeEqualSwitches.remove(dpid); - if (sw == null) { - log.error("Transition to master called on sw {}, but switch " - + "was not found in controller-cache", dpid); - return; - } - activeMasterSwitches.put(dpid, sw); - } - } - - - /** - * Called when this controller's role for a switch transitions to equal. - * For 1.0 switches, we internally refer to the role 'slave' as - * 'equal'. - */ - protected void transitionToEqualSwitch(long dpid) { - synchronized (multiCacheLock) { - IOFSwitch sw = activeMasterSwitches.remove(dpid); - if (sw == null) { - log.error("Transition to equal called on sw {}, but switch " - + "was not found in controller-cache", dpid); - return; - } - activeEqualSwitches.put(dpid, sw); - } - - } - - /** - * Clear all state in controller switch maps for a switch that has - * disconnected from the local controller. Also release control for - * that switch from the global repository. Notify switch listeners. - */ - protected void removeConnectedSwitch(long dpid) { - releaseRegistryControl(dpid); - connectedSwitches.remove(dpid); - IOFSwitch sw = activeMasterSwitches.remove(dpid); - if (sw == null) { - sw = activeEqualSwitches.remove(dpid); - } - if (sw != null) { - sw.cancelAllStatisticsReplies(); - sw.setConnected(false); // do we need this? - } - counters.switchDisconnected.updateCounterWithFlush(); - - } - - /** - * Indicates that ports on the given switch have changed. Enqueue a - * switch update. - * @param dpid - * @param port - * @param changeType - */ - protected void notifyPortChanged(long dpid, OFPortDesc port, - PortChangeType changeType) { - if (port == null || changeType == null) { - String msg = String.format("Switch port or changetType must not " - + "be null in port change notification"); - throw new NullPointerException(msg); - } - if (connectedSwitches.get(dpid) == null || getSwitch(dpid) == null) { - log.warn("Port change update on switch {} not connected or activated " - + "... Aborting.", HexString.toHexString(dpid)); - return; - } - - } - // *************** // Getters/Setters // *************** @@ -268,180 +86,8 @@ public class Controller { public synchronized void setIOFSwitchManager(IOFSwitchManager swManager) { this.switchManager = swManager; - this.registryService = swManager.getRegistry(); } - - public void setDebugCounter(IDebugCounterService dcs) { - this.debugCounters = dcs; - } - - IDebugCounterService getDebugCounter() { - return this.debugCounters; - } - - // ********************** - // Role Handling - // ********************** - - /** - * created by ONOS - works with registry service. - */ - protected class RoleChangeCallback implements ControlChangeCallback { - @Override - public void controlChanged(long dpidLong, boolean hasControl) { - Dpid dpid = new Dpid(dpidLong); - log.info("Role change callback for switch {}, hasControl {}", - dpid, hasControl); - - Role role = null; - - /* - * issue #229 - * Cannot rely on sw.getRole() as it can be behind due to pending - * role changes in the queue. Just submit it and late the - * RoleChanger handle duplicates. - */ - - if (hasControl) { - role = Role.MASTER; - } else { - role = Role.EQUAL; // treat the same as Role.SLAVE - } - - OFChannelHandler swCh = connectedSwitches.get(dpid.value()); - if (swCh == null) { - log.warn("Switch {} not found in connected switches", dpid); - return; - } - - log.debug("Sending role request {} msg to {}", role, dpid); - swCh.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE); - } - } - - /** - * Submit request to the registry service for mastership of the - * switch. - * @param dpid this datapath to get role for - */ - public synchronized void submitRegistryRequest(long dpid) { - if (registryService == null) { - /* - * If we have no registry then simply assign - * mastership to this controller. - */ - new RoleChangeCallback().controlChanged(dpid, true); - return; - } - OFChannelHandler h = connectedSwitches.get(dpid); - if (h == null) { - log.error("Trying to request registry control for switch {} " - + "not in connected switches. Aborting.. ", - HexString.toHexString(dpid)); - connectedSwitches.get(dpid).disconnectSwitch(); - return; - } - //Request control of the switch from the global registry - try { - h.controlRequested = Boolean.TRUE; - registryService.requestControl(dpid, new RoleChangeCallback()); - } catch (RegistryException e) { - log.debug("Registry error: {}", e.getMessage()); - h.controlRequested = Boolean.FALSE; - } - if (!h.controlRequested) { // XXX what is being attempted here? - // yield to allow other thread(s) to release control - // TODO AAS: this is awful and needs to be fixed - Thread.yield(); - // safer to bounce the switch to reconnect here than proceeding further - // XXX S why? can't we just try again a little later? - log.debug("Closing sw:{} because we weren't able to request control " + - "successfully" + dpid); - connectedSwitches.get(dpid).disconnectSwitch(); - } - } - - /** - * Relinquish role for the switch. - * @param dpidLong the controlled datapath - */ - public synchronized void releaseRegistryControl(long dpidLong) { - OFChannelHandler h = connectedSwitches.get(dpidLong); - if (h == null) { - log.error("Trying to release registry control for switch {} " - + "not in connected switches. Aborting.. ", - HexString.toHexString(dpidLong)); - return; - } - if (registryService != null && h.controlRequested) { - //TODO the above is not good for testing need to change controlrequest to method call. - registryService.releaseControl(dpidLong); - } - } - - - // FIXME: remove this method - public Map getSwitches() { - return getMasterSwitches(); - } - - // FIXME: remove this method - public Map getMasterSwitches() { - return Collections.unmodifiableMap(activeMasterSwitches); - } - - - - public Set getAllSwitchDpids() { - Set dpids = new HashSet(); - dpids.addAll(activeMasterSwitches.keySet()); - dpids.addAll(activeEqualSwitches.keySet()); - return dpids; - } - - - public Set getAllMasterSwitchDpids() { - Set dpids = new HashSet(); - dpids.addAll(activeMasterSwitches.keySet()); - return dpids; - } - - - public Set getAllEqualSwitchDpids() { - Set dpids = new HashSet(); - dpids.addAll(activeEqualSwitches.keySet()); - return dpids; - } - - - public IOFSwitch getSwitch(long dpid) { - IOFSwitch sw = null; - sw = activeMasterSwitches.get(dpid); - if (sw != null) { - return sw; - } - sw = activeEqualSwitches.get(dpid); - if (sw != null) { - return sw; - } - return sw; - } - - - public IOFSwitch getMasterSwitch(long dpid) { - return activeMasterSwitches.get(dpid); - } - - - public IOFSwitch getEqualSwitch(long dpid) { - return activeEqualSwitches.get(dpid); - } - - - - - public OFFactory getOFMessageFactory10() { return FACTORY10; } @@ -469,12 +115,6 @@ public class Controller { return (this.systemStartTime); } - - public InstanceId getInstanceId() { - return instanceId; - } - - // ************** // Initialization // ************** @@ -509,7 +149,7 @@ public class Controller { new OpenflowPipelineFactory(this, null); bootstrap.setPipelineFactory(pfact); InetSocketAddress sa = new InetSocketAddress(openFlowPort); - final ChannelGroup cg = new DefaultChannelGroup(); + cg = new DefaultChannelGroup(); cg.add(bootstrap.bind(sa)); log.info("Listening for switch connections on {}", sa); @@ -544,20 +184,6 @@ public class Controller { this.workerThreads = Integer.parseInt(threads); } log.debug("Number of worker threads set to {}", this.workerThreads); - String controllerId = configParams.get("controllerid"); - if (controllerId != null) { - this.instanceId = new InstanceId(controllerId); - } else { - //Try to get the hostname of the machine and use that for controller ID - try { - String hostname = java.net.InetAddress.getLocalHost().getHostName(); - this.instanceId = new InstanceId(hostname); - } catch (UnknownHostException e) { - log.warn("Can't get hostname, using the default"); - } - } - - log.debug("ControllerId set to {}", this.instanceId); } @@ -567,16 +193,11 @@ public class Controller { public void init(Map configParams) { // These data structures are initialized here because other // module's startUp() might be called before ours - this.activeMasterSwitches = new ConcurrentHashMap(); - this.activeEqualSwitches = new ConcurrentHashMap(); - this.connectedSwitches = new ConcurrentHashMap(); this.controllerNodeIPsCache = new HashMap(); setConfigParams(configParams); this.systemStartTime = System.currentTimeMillis(); - this.setDebugCounter(new DebugCounter()); - this.counters = new Counters(); - this.multiCacheLock = new Object(); + } @@ -589,214 +210,9 @@ public class Controller { "that the system database has failed to start. " + LogMessageDoc.CHECK_CONTROLLER) public synchronized void startupComponents() { - try { - if (registryService != null) { - registryService.registerController(instanceId.toString()); - } - } catch (RegistryException e) { - log.warn("Registry service error: {}", e.getMessage()); - } - - // register counters and events - try { - this.counters.createCounters(debugCounters); - } catch (CounterException e) { - log.warn("Counters unavailable: {}", e.getMessage()); - } + //TODO do something maybe } - // ************** - // debugCounter registrations - // ************** - - public static class Counters { - public static final String PREFIX = "controller"; - public IDebugCounter switchActivated; - public IDebugCounter switchWithSameDpidActivated; // warn - public IDebugCounter switchDisconnected; - public IDebugCounter messageReceived; - public IDebugCounter switchDisconnectReadTimeout; - public IDebugCounter switchDisconnectHandshakeTimeout; - public IDebugCounter switchDisconnectIOError; - public IDebugCounter switchDisconnectParseError; - public IDebugCounter switchDisconnectSwitchStateException; - public IDebugCounter rejectedExecutionException; - public IDebugCounter switchDisconnectOtherException; - public IDebugCounter switchConnected; - public IDebugCounter unhandledMessage; - public IDebugCounter packetInWhileSwitchIsSlave; - public IDebugCounter epermErrorWhileSwitchIsMaster; - public IDebugCounter roleReplyTimeout; - public IDebugCounter roleReplyReceived; // expected RoleReply received - public IDebugCounter roleReplyErrorUnsupported; - public IDebugCounter switchCounterRegistrationFailed; - - void createCounters(IDebugCounterService debugCounters) throws CounterException { - - switchActivated = - debugCounters.registerCounter( - PREFIX, "switch-activated", - "A switch connected to this controller is now " + - "in MASTER role", - CounterType.ALWAYS_COUNT); - - switchWithSameDpidActivated = // warn - debugCounters.registerCounter( - PREFIX, "switch-with-same-dpid-activated", - "A switch with the same DPID as another switch " + - "connected to the controller. This can be " + - "caused by multiple switches configured with " + - "the same DPID or by a switch reconnecting very " + - "quickly.", - CounterType.COUNT_ON_DEMAND, - IDebugCounterService.CTR_MDATA_WARN); - - switchDisconnected = - debugCounters.registerCounter( - PREFIX, "switch-disconnected", - "FIXME: switch has disconnected", - CounterType.ALWAYS_COUNT); - - //------------------------ - // channel handler counters. Factor them out ?? - messageReceived = - debugCounters.registerCounter( - PREFIX, "message-received", - "Number of OpenFlow messages received. Some of " + - "these might be throttled", - CounterType.ALWAYS_COUNT); - - switchDisconnectReadTimeout = - debugCounters.registerCounter( - PREFIX, "switch-disconnect-read-timeout", - "Number of times a switch was disconnected due " + - "due the switch failing to send OpenFlow " + - "messages or responding to OpenFlow ECHOs", - CounterType.ALWAYS_COUNT, - IDebugCounterService.CTR_MDATA_ERROR); - switchDisconnectHandshakeTimeout = - debugCounters.registerCounter( - PREFIX, "switch-disconnect-handshake-timeout", - "Number of times a switch was disconnected " + - "because it failed to complete the handshake " + - "in time.", - CounterType.ALWAYS_COUNT, - IDebugCounterService.CTR_MDATA_ERROR); - switchDisconnectIOError = - debugCounters.registerCounter( - PREFIX, "switch-disconnect-io-error", - "Number of times a switch was disconnected " + - "due to IO errors on the switch connection.", - CounterType.ALWAYS_COUNT, - IDebugCounterService.CTR_MDATA_ERROR); - switchDisconnectParseError = - debugCounters.registerCounter( - PREFIX, "switch-disconnect-parse-error", - "Number of times a switch was disconnected " + - "because it sent an invalid packet that could " + - "not be parsed", - CounterType.ALWAYS_COUNT, - IDebugCounterService.CTR_MDATA_ERROR); - - switchDisconnectSwitchStateException = - debugCounters.registerCounter( - PREFIX, "switch-disconnect-switch-state-exception", - "Number of times a switch was disconnected " + - "because it sent messages that were invalid " + - "given the switch connection's state.", - CounterType.ALWAYS_COUNT, - IDebugCounterService.CTR_MDATA_ERROR); - rejectedExecutionException = - debugCounters.registerCounter( - PREFIX, "rejected-execution-exception", - "TODO", - CounterType.ALWAYS_COUNT, - IDebugCounterService.CTR_MDATA_ERROR); - - switchDisconnectOtherException = - debugCounters.registerCounter( - PREFIX, "switch-disconnect-other-exception", - "Number of times a switch was disconnected " + - "due to an exceptional situation not covered " + - "by other counters", - CounterType.ALWAYS_COUNT, - IDebugCounterService.CTR_MDATA_ERROR); - - switchConnected = - debugCounters.registerCounter( - PREFIX, "switch-connected", - "Number of times a new switch connection was " + - "established", - CounterType.ALWAYS_COUNT); - - unhandledMessage = - debugCounters.registerCounter( - PREFIX, "unhandled-message", - "Number of times an OpenFlow message was " + - "received that the controller ignored because " + - "it was inapproriate given the switch " + - "connection's state.", - CounterType.ALWAYS_COUNT, - IDebugCounterService.CTR_MDATA_WARN); - // might be less than warning - - packetInWhileSwitchIsSlave = - debugCounters.registerCounter( - PREFIX, "packet-in-while-switch-is-slave", - "Number of times a packet in was received " + - "from a switch that was in SLAVE role. " + - "Possibly inidicates inconsistent roles.", - CounterType.ALWAYS_COUNT); - epermErrorWhileSwitchIsMaster = - debugCounters.registerCounter( - PREFIX, "eperm-error-while-switch-is-master", - "Number of times a permission error was " + - "received while the switch was in MASTER role. " + - "Possibly inidicates inconsistent roles.", - CounterType.ALWAYS_COUNT, - IDebugCounterService.CTR_MDATA_WARN); - - roleReplyTimeout = - debugCounters.registerCounter( - PREFIX, "role-reply-timeout", - "Number of times a role request message did not " + - "receive the expected reply from a switch", - CounterType.ALWAYS_COUNT, - IDebugCounterService.CTR_MDATA_WARN); - - roleReplyReceived = // expected RoleReply received - debugCounters.registerCounter( - PREFIX, "role-reply-received", - "Number of times the controller received the " + - "expected role reply message from a switch", - CounterType.ALWAYS_COUNT); - - roleReplyErrorUnsupported = - debugCounters.registerCounter( - PREFIX, "role-reply-error-unsupported", - "Number of times the controller received an " + - "error from a switch in response to a role " + - "request indicating that the switch does not " + - "support roles.", - CounterType.ALWAYS_COUNT); - - switchCounterRegistrationFailed = - debugCounters.registerCounter(PREFIX, - "switch-counter-registration-failed", - "Number of times the controller failed to " + - "register per-switch debug counters", - CounterType.ALWAYS_COUNT, - IDebugCounterService.CTR_MDATA_WARN); - - - } - } - - public Counters getCounters() { - return this.counters; - } - - // ************** // Utility methods // ************** @@ -820,20 +236,24 @@ public class Controller { * @param desc * @return switch instance */ - protected IOFSwitch getOFSwitchInstance(OFDescStatsReply desc, OFVersion ofv) { - if (switchManager == null) { - return new DummySwitchForTesting(); - } - return switchManager.getSwitchImpl(desc.getMfrDesc(), desc.getHwDesc(), + protected AbstractOpenFlowSwitch getOFSwitchInstance(OFDescStatsReply desc, OFVersion ofv) { + AbstractOpenFlowSwitch sw = switchManager.getSwitchImpl(desc.getMfrDesc(), desc.getHwDesc(), desc.getSwDesc(), ofv); + sw.setAgent(agent); + return sw; } - @Activate - public void activate() { + public void start(OpenFlowSwitchAgent ag) { log.info("Initialising OpenFlow Lib and IO"); + this.agent = ag; this.init(new HashMap()); this.startupComponents(); this.run(); } + + public void stop() { + cg.close(); + } + } diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/IOFSwitchManager.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/IOFSwitchManager.java similarity index 61% rename from of/ctl/src/main/java/org/onlab/onos/of/controller/impl/IOFSwitchManager.java rename to of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/IOFSwitchManager.java index 0acb321ad5..4b2f7c3120 100644 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/IOFSwitchManager.java +++ b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/IOFSwitchManager.java @@ -1,8 +1,7 @@ -package org.onlab.onos.of.controller.impl; +package org.onlab.onos.of.controller.impl.internal; import org.projectfloodlight.openflow.protocol.OFVersion; -import org.onlab.onos.of.controller.impl.registry.IControllerRegistry; /** * Interface to passed to controller class in order to allow @@ -22,12 +21,6 @@ public interface IOFSwitchManager { * @param ofv openflow version * @return A switch of type IOFSwitch. */ - public IOFSwitch getSwitchImpl(String mfr, String hwDesc, String swDesc, OFVersion ofv); - - /** - * Returns the mastership registry used during controller-switch role election. - * @return the registry - */ - public IControllerRegistry getRegistry(); + public AbstractOpenFlowSwitch getSwitchImpl(String mfr, String hwDesc, String swDesc, OFVersion ofv); } diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/OFChannelHandler.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/OFChannelHandler.java index 667a0510f8..5db1fc38be 100644 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/OFChannelHandler.java +++ b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/OFChannelHandler.java @@ -4,21 +4,11 @@ package org.onlab.onos.of.controller.impl.internal; import java.io.IOException; import java.nio.channels.ClosedChannelException; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.RejectedExecutionException; -import org.onlab.onos.of.controller.impl.IOFSwitch; -import org.onlab.onos.of.controller.impl.IOFSwitch.PortChangeEvent; -import org.onlab.onos.of.controller.impl.Role; -import org.onlab.onos.of.controller.impl.annotations.LogMessageDoc; -import org.onlab.onos.of.controller.impl.annotations.LogMessageDocs; -import org.onlab.onos.of.controller.impl.debugcounter.IDebugCounterService.CounterException; -import org.onlab.onos.of.controller.impl.internal.Controller.Counters; -import org.onlab.onos.of.controller.impl.internal.OFChannelHandler.ChannelState.RoleReplyInfo; - import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelStateEvent; @@ -27,12 +17,14 @@ import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler; import org.jboss.netty.handler.timeout.IdleStateEvent; import org.jboss.netty.handler.timeout.ReadTimeoutException; +import org.onlab.onos.of.controller.RoleState; +import org.onlab.onos.of.controller.impl.annotations.LogMessageDoc; +import org.onlab.onos.of.controller.impl.annotations.LogMessageDocs; import org.projectfloodlight.openflow.exceptions.OFParseError; import org.projectfloodlight.openflow.protocol.OFAsyncGetReply; import org.projectfloodlight.openflow.protocol.OFBadRequestCode; import org.projectfloodlight.openflow.protocol.OFBarrierReply; import org.projectfloodlight.openflow.protocol.OFBarrierRequest; -import org.projectfloodlight.openflow.protocol.OFControllerRole; import org.projectfloodlight.openflow.protocol.OFDescStatsReply; import org.projectfloodlight.openflow.protocol.OFDescStatsRequest; import org.projectfloodlight.openflow.protocol.OFEchoReply; @@ -49,15 +41,12 @@ import org.projectfloodlight.openflow.protocol.OFGetConfigRequest; import org.projectfloodlight.openflow.protocol.OFHello; import org.projectfloodlight.openflow.protocol.OFHelloElem; import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole; -import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply; import org.projectfloodlight.openflow.protocol.OFPacketIn; import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest; import org.projectfloodlight.openflow.protocol.OFPortStatus; import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply; import org.projectfloodlight.openflow.protocol.OFRoleReply; -import org.projectfloodlight.openflow.protocol.OFRoleRequest; import org.projectfloodlight.openflow.protocol.OFSetConfig; import org.projectfloodlight.openflow.protocol.OFStatsReply; import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags; @@ -66,11 +55,10 @@ import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg; import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg; -import org.projectfloodlight.openflow.protocol.errormsg.OFRoleRequestFailedErrorMsg; import org.projectfloodlight.openflow.types.U32; -import org.projectfloodlight.openflow.types.U64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + /** * Channel handler deals with the switch connection and dispatches * switch messages to the appropriate locations. @@ -79,18 +67,13 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class); private static final long DEFAULT_ROLE_TIMEOUT_MS = 2 * 1000; // 10 sec private final Controller controller; - private final Counters counters; - private IOFSwitch sw; + private AbstractOpenFlowSwitch sw; private long thisdpid; // channelHandler cached value of connected switch id private Channel channel; // State needs to be volatile because the HandshakeTimeoutHandler // needs to check if the handshake is complete private volatile ChannelState state; - // All role messaging is handled by the roleChanger. The channel state machine - // coordinates between the roleChanger and the controller-global-registry-service - // to determine controller roles per switch. - private RoleChanger roleChanger; // Used to coordinate between the controller and the cleanup thread(?) // for access to the global registry on a per switch basis. volatile Boolean controlRequested; @@ -125,8 +108,6 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { */ OFChannelHandler(Controller controller) { this.controller = controller; - this.counters = controller.getCounters(); - this.roleChanger = new RoleChanger(DEFAULT_ROLE_TIMEOUT_MS); this.state = ChannelState.INIT; this.pendingPortStatusMsg = new CopyOnWriteArrayList(); factory13 = controller.getOFMessageFactory13(); @@ -135,392 +116,14 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { duplicateDpidFound = Boolean.FALSE; } - //******************* - // Role Handling - //******************* - /** - * When we remove a pending role request we use this enum to indicate how we - * arrived at the decision. When we send a role request to the switch, we - * also use this enum to indicate what we expect back from the switch, so the - * role changer can match the reply to our expectation. - */ - public enum RoleRecvStatus { - /** The switch returned an error indicating that roles are not. - * supported*/ - UNSUPPORTED, - /** The request timed out. */ - NO_REPLY, - /** The reply was old, there is a newer request pending. */ - OLD_REPLY, - /** - * The reply's role matched the role that this controller set in the - * request message - invoked either initially at startup or to reassert - * current role. - */ - MATCHED_CURRENT_ROLE, - /** - * The reply's role matched the role that this controller set in the - * request message - this is the result of a callback from the - * global registry, followed by a role request sent to the switch. - */ - MATCHED_SET_ROLE, - /** - * The reply's role was a response to the query made by this controller. - */ - REPLY_QUERY, - /** We received a role reply message from the switch - * but the expectation was unclear, or there was no expectation. - */ - OTHER_EXPECTATION, - } - - /** - * Forwards to RoleChanger. See there. - * @param role - */ - public void sendRoleRequest(Role role, RoleRecvStatus expectation) { - try { - roleChanger.sendRoleRequest(role, expectation); - } catch (IOException e) { - log.error("Disconnecting switch {} due to IO Error: {}", - getSwitchInfoString(), e.getMessage()); - channel.close(); - } - } // XXX S consider if necessary public void disconnectSwitch() { sw.disconnectSwitch(); } - /** - * A utility class to handle role requests and replies for this channel. - * After a role request is submitted the role changer keeps track of the - * pending request, collects the reply (if any) and times out the request - * if necessary. - * - * To simplify role handling we only keep track of the /last/ pending - * role reply send to the switch. If multiple requests are pending and - * we receive replies for earlier requests we ignore them. However, this - * way of handling pending requests implies that we could wait forever if - * a new request is submitted before the timeout triggers. If necessary - * we could work around that though. - */ - private class RoleChanger { - // indicates that a request is currently pending - // needs to be volatile to allow correct double-check idiom - private volatile boolean requestPending; - // the transaction Id of the pending request - private int pendingXid; - // the role that's pending - private Role pendingRole; - // system time in MS when we send the request - private long roleSubmitTime; - // the timeout to use - private final long roleTimeoutMs; - // the expectation set by the caller for the returned role - private RoleRecvStatus expectation; - public RoleChanger(long roleTimeoutMs) { - this.requestPending = false; - this.roleSubmitTime = 0; - this.pendingXid = -1; - this.pendingRole = null; - this.roleTimeoutMs = roleTimeoutMs; - this.expectation = RoleRecvStatus.MATCHED_CURRENT_ROLE; - } - - /** - * Send NX role request message to the switch requesting the specified - * role. - * - * @param sw switch to send the role request message to - * @param role role to request - */ - private int sendNxRoleRequest(Role role) throws IOException { - // Convert the role enum to the appropriate role to send - OFNiciraControllerRole roleToSend = OFNiciraControllerRole.ROLE_OTHER; - switch (role) { - case MASTER: - roleToSend = OFNiciraControllerRole.ROLE_MASTER; - break; - case SLAVE: - case EQUAL: - default: - // ensuring that the only two roles sent to 1.0 switches with - // Nicira role support, are MASTER and SLAVE - roleToSend = OFNiciraControllerRole.ROLE_SLAVE; - log.warn("Sending Nx Role.SLAVE to switch {}.", sw); - } - int xid = sw.getNextTransactionId(); - OFExperimenter roleRequest = factory10 - .buildNiciraControllerRoleRequest() - .setXid(xid) - .setRole(roleToSend) - .build(); - sw.write(Collections.singletonList(roleRequest)); - return xid; - } - - private int sendOF13RoleRequest(Role role) throws IOException { - // Convert the role enum to the appropriate role to send - OFControllerRole roleToSend = OFControllerRole.ROLE_NOCHANGE; - switch (role) { - case EQUAL: - roleToSend = OFControllerRole.ROLE_EQUAL; - break; - case MASTER: - roleToSend = OFControllerRole.ROLE_MASTER; - break; - case SLAVE: - roleToSend = OFControllerRole.ROLE_SLAVE; - break; - default: - log.warn("Sending default role.noChange to switch {}." - + " Should only be used for queries.", sw); - } - - int xid = sw.getNextTransactionId(); - OFRoleRequest rrm = factory13 - .buildRoleRequest() - .setRole(roleToSend) - .setXid(xid) - .setGenerationId(sw.getNextGenerationId()) - .build(); - sw.write(rrm); - return xid; - } - - /** - * Send a role request with the given role to the switch and update - * the pending request and timestamp. - * Sends an OFPT_ROLE_REQUEST to an OF1.3 switch, OR - * Sends an NX_ROLE_REQUEST to an OF1.0 switch if configured to support it - * in the IOFSwitch driver. If not supported, this method sends nothing - * and returns 'false'. The caller should take appropriate action. - * - * One other optimization we do here is that for OF1.0 switches with - * Nicira role message support, we force the Role.EQUAL to become - * Role.SLAVE, as there is no defined behavior for the Nicira role OTHER. - * We cannot expect it to behave like SLAVE. We don't have this problem with - * OF1.3 switches, because Role.EQUAL is well defined and we can simulate - * SLAVE behavior by using ASYNC messages. - * - * @param role - * @throws IOException - * @returns false if and only if the switch does not support role-request - * messages, according to the switch driver; true otherwise. - */ - synchronized boolean sendRoleRequest(Role role, RoleRecvStatus exp) - throws IOException { - this.expectation = exp; - - if (ofVersion == OFVersion.OF_10) { - Boolean supportsNxRole = (Boolean) - sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE); - if (!supportsNxRole) { - log.debug("Switch driver indicates no support for Nicira " - + "role request messages. Not sending ..."); - state.handleUnsentRoleMessage(OFChannelHandler.this, role, - expectation); - return false; - } - // OF1.0 switch with support for NX_ROLE_REQUEST vendor extn. - // make Role.EQUAL become Role.SLAVE - role = (role == Role.EQUAL) ? Role.SLAVE : role; - pendingXid = sendNxRoleRequest(role); - pendingRole = role; - roleSubmitTime = System.currentTimeMillis(); - requestPending = true; - } else { - // OF1.3 switch, use OFPT_ROLE_REQUEST message - pendingXid = sendOF13RoleRequest(role); - pendingRole = role; - roleSubmitTime = System.currentTimeMillis(); - requestPending = true; - } - return true; - } - - /** - * Deliver a received role reply. - * - * Check if a request is pending and if the received reply matches the - * the expected pending reply (we check both role and xid) we set - * the role for the switch/channel. - * - * If a request is pending but doesn't match the reply we ignore it, and - * return - * - * If no request is pending we disconnect with a SwitchStateException - * - * @param RoleReplyInfo information about role-reply in format that - * controller can understand. - * @throws SwitchStateException if no request is pending - */ - synchronized RoleRecvStatus deliverRoleReply(RoleReplyInfo rri) - throws SwitchStateException { - if (!requestPending) { - Role currentRole = (sw != null) ? sw.getRole() : null; - if (currentRole != null) { - if (currentRole == rri.getRole()) { - // Don't disconnect if the role reply we received is - // for the same role we are already in. - log.debug("Received unexpected RoleReply from " - + "Switch: {} in State: {}. " - + "Role in reply is same as current role of this " - + "controller for this sw. Ignoring ...", - getSwitchInfoString(), state.toString()); - return RoleRecvStatus.OTHER_EXPECTATION; - } else { - String msg = String.format("Switch: [%s], State: [%s], " - + "received unexpected RoleReply[%s]. " - + "No roles are pending, and this controller's " - + "current role:[%s] does not match reply. " - + "Disconnecting switch ... ", - OFChannelHandler.this.getSwitchInfoString(), - OFChannelHandler.this.state.toString(), - rri, currentRole); - throw new SwitchStateException(msg); - } - } - log.debug("Received unexpected RoleReply {} from " - + "Switch: {} in State: {}. " - + "This controller has no current role for this sw. " - + "Ignoring ...", new Object[] {rri, - getSwitchInfoString(), state}); - return RoleRecvStatus.OTHER_EXPECTATION; - } - - int xid = (int) rri.getXid(); - Role role = rri.getRole(); - // XXX S should check generation id meaningfully and other cases of expectations - // U64 genId = rri.getGenId(); - - if (pendingXid != xid) { - log.debug("Received older role reply from " + - "switch {} ({}). Ignoring. " + - "Waiting for {}, xid={}", - new Object[] {getSwitchInfoString(), rri, - pendingRole, pendingXid }); - return RoleRecvStatus.OLD_REPLY; - } - - if (pendingRole == role) { - log.debug("Received role reply message from {} that matched " - + "expected role-reply {} with expectations {}", - new Object[] {getSwitchInfoString(), role, expectation}); - counters.roleReplyReceived.updateCounterWithFlush(); - //setSwitchRole(role, RoleRecvStatus.RECEIVED_REPLY); dont want to set state here - if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE || - expectation == RoleRecvStatus.MATCHED_SET_ROLE) { - return expectation; - } else { - return RoleRecvStatus.OTHER_EXPECTATION; - } - } - - // if xids match but role's don't, perhaps its a query (OF1.3) - if (expectation == RoleRecvStatus.REPLY_QUERY) { - return expectation; - } - - return RoleRecvStatus.OTHER_EXPECTATION; - } - - /** - * Called if we receive an error message. If the xid matches the - * pending request we handle it otherwise we ignore it. - * - * Note: since we only keep the last pending request we might get - * error messages for earlier role requests that we won't be able - * to handle - */ - synchronized RoleRecvStatus deliverError(OFErrorMsg error) - throws SwitchStateException { - if (!requestPending) { - log.debug("Received an error msg from sw {}, but no pending " - + "requests in role-changer; not handling ...", - getSwitchInfoString()); - return RoleRecvStatus.OTHER_EXPECTATION; - } - if (pendingXid != error.getXid()) { - if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) { - log.debug("Received an error msg from sw {} for a role request," - + " but not for pending request in role-changer; " - + " ignoring error {} ...", - getSwitchInfoString(), error); - } - return RoleRecvStatus.OTHER_EXPECTATION; - } - // it is an error related to a currently pending role request message - if (error.getErrType() == OFErrorType.BAD_REQUEST) { - counters.roleReplyErrorUnsupported.updateCounterWithFlush(); - log.error("Received a error msg {} from sw {} in state {} for " - + "pending role request {}. Switch driver indicates " - + "role-messaging is supported. Possible issues in " - + "switch driver configuration?", new Object[] { - ((OFBadRequestErrorMsg) error).toString(), - getSwitchInfoString(), state, pendingRole - }); - return RoleRecvStatus.UNSUPPORTED; - } - - if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) { - OFRoleRequestFailedErrorMsg rrerr = - (OFRoleRequestFailedErrorMsg) error; - switch (rrerr.getCode()) { - case BAD_ROLE: - // switch says that current-role-req has bad role? - // for now we disconnect - // fall-thru - case STALE: - // switch says that current-role-req has stale gen-id? - // for now we disconnect - // fall-thru - case UNSUP: - // switch says that current-role-req has role that - // cannot be supported? for now we disconnect - String msgx = String.format("Switch: [%s], State: [%s], " - + "received Error to for pending role request [%s]. " - + "Error:[%s]. Disconnecting switch ... ", - OFChannelHandler.this.getSwitchInfoString(), - OFChannelHandler.this.state.toString(), - pendingRole, rrerr); - throw new SwitchStateException(msgx); - default: - break; - } - } - - // This error message was for a role request message but we dont know - // how to handle errors for nicira role request messages - return RoleRecvStatus.OTHER_EXPECTATION; - } - - /** - * Check if a pending role request has timed out. - */ - void checkTimeout() { - if (!requestPending) { - return; - } - synchronized (this) { - if (!requestPending) { - return; - } - long now = System.currentTimeMillis(); - if (now - roleSubmitTime > roleTimeoutMs) { - // timeout triggered. - counters.roleReplyTimeout.updateCounterWithFlush(); - //setSwitchRole(pendingRole, RoleRecvStatus.NO_REPLY); - // XXX S come back to this - } - } - } - - } //************************* // Channel State Machine @@ -628,8 +231,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { log.info("Received features reply for switch at {} with dpid {}", h.getSwitchInfoString(), h.thisdpid); //update the controller about this connected switch - boolean success = h.controller.addConnectedSwitch( - h.thisdpid, h); + boolean success = h.sw.addConnectedSwitch(); if (!success) { disconnectDuplicate(h); return; @@ -825,41 +427,16 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { h.sw.setConnected(true); h.sw.setChannel(h.channel); - try { - h.sw.setDebugCounterService(h.controller.getDebugCounter()); - } catch (CounterException e) { - h.counters.switchCounterRegistrationFailed - .updateCounterNoFlush(); - log.warn("Could not register counters for switch {} ", - h.getSwitchInfoString(), e); - } log.info("Switch {} bound to class {}, description {}", new Object[] {h.sw, h.sw.getClass(), drep }); //Put switch in EQUAL mode until we hear back from the global registry log.debug("Setting new switch {} to EQUAL and sending Role request", h.sw.getStringId()); - h.setSwitchRole(Role.EQUAL); - try { - boolean supportsRRMsg = h.roleChanger.sendRoleRequest(Role.EQUAL, - RoleRecvStatus.MATCHED_CURRENT_ROLE); - if (!supportsRRMsg) { - log.warn("Switch {} does not support role request messages " - + "of any kind. No role messages were sent. " - + "This controller instance SHOULD become MASTER " - + "from the registry process. ", - h.getSwitchInfoString()); - } - h.setState(WAIT_INITIAL_ROLE); - // request control of switch from global registry - - // necessary even if this is the only controller the - // switch is connected to. - h.controller.submitRegistryRequest(h.sw.getId()); - } catch (IOException e) { - log.error("Exception when sending role request: {} ", - e.getMessage()); - // FIXME shouldn't we disconnect? - } + h.setSwitchRole(RoleState.EQUAL); + h.sw.startDriverHandshake(); + h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE); + } @Override @@ -880,133 +457,6 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { } }, - /** - * We are waiting for a role reply message in response to a role request - * sent after hearing back from the registry service -- OR -- we are - * just waiting to hear back from the registry service in the case that - * the switch does not support role messages. If completed successfully, - * the controller's role for this switch will be set here. - * Before we move to the state corresponding to the role, we allow the - * switch specific driver to complete its configuration. This configuration - * typically depends on the role the controller is playing for this switch. - * And so we set the switch role (for 'this' controller) before we start - * the driver-sub-handshake. - * Next State: WAIT_SWITCH_DRIVER_SUB_HANDSHAKE - */ - WAIT_INITIAL_ROLE(false) { - @Override - void processOFError(OFChannelHandler h, OFErrorMsg m) - throws SwitchStateException { - // role changer will ignore the error if it isn't for it - RoleRecvStatus rrstatus = h.roleChanger.deliverError(m); - if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) { - logError(h, m); - } - } - - @Override - void processOFExperimenter(OFChannelHandler h, OFExperimenter m) - throws IOException, SwitchStateException { - Role role = extractNiciraRoleReply(h, m); - // If role == null it means the vendor (experimenter) message - // wasn't really a Nicira role reply. We ignore this case. - if (role != null) { - RoleReplyInfo rri = new RoleReplyInfo(role, null, m.getXid()); - RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri); - if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) { - setRoleAndStartDriverHandshake(h, rri.getRole()); - } // else do nothing - wait for the correct expected reply - } else { - unhandledMessageReceived(h, m); - } - } - - @Override - void processOFRoleReply(OFChannelHandler h, OFRoleReply m) - throws SwitchStateException, IOException { - RoleReplyInfo rri = extractOFRoleReply(h, m); - RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri); - if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) { - setRoleAndStartDriverHandshake(h, rri.getRole()); - } // else do nothing - wait for the correct expected reply - } - - @Override - void handleUnsentRoleMessage(OFChannelHandler h, Role role, - RoleRecvStatus expectation) throws IOException { - // typically this is triggered for a switch where role messages - // are not supported - we confirm that the role being set is - // master and move to the next state - if (expectation == RoleRecvStatus.MATCHED_SET_ROLE) { - if (role == Role.MASTER) { - setRoleAndStartDriverHandshake(h, role); - } else { - log.error("Expected MASTER role from registry for switch " - + "which has no support for role-messages." - + "Received {}. It is possible that this switch " - + "is connected to other controllers, in which " - + "case it should support role messages - not " - + "moving forward.", role); - } - } // else do nothing - wait to hear back from registry - - } - - private void setRoleAndStartDriverHandshake(OFChannelHandler h, - Role role) throws IOException { - h.setSwitchRole(role); - h.sw.startDriverHandshake(); - if (h.sw.isDriverHandshakeComplete()) { - Role mySwitchRole = h.sw.getRole(); - if (mySwitchRole == Role.MASTER) { - log.info("Switch-driver sub-handshake complete. " - + "Activating switch {} with Role: MASTER", - h.getSwitchInfoString()); - handlePendingPortStatusMessages(h); //before activation - boolean success = h.controller.addActivatedMasterSwitch( - h.sw.getId(), h.sw); - if (!success) { - disconnectDuplicate(h); - return; - } - h.setState(MASTER); - } else { - log.info("Switch-driver sub-handshake complete. " - + "Activating switch {} with Role: EQUAL", - h.getSwitchInfoString()); - handlePendingPortStatusMessages(h); //before activation - boolean success = h.controller.addActivatedEqualSwitch( - h.sw.getId(), h.sw); - if (!success) { - disconnectDuplicate(h); - return; - } - h.setState(EQUAL); - } - } else { - h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE); - } - } - - @Override - void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) - throws IOException, SwitchStateException { - illegalMessageReceived(h, m); - } - - @Override - void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m) - throws SwitchStateException { - illegalMessageReceived(h, m); - } - - @Override - void processOFPortStatus(OFChannelHandler h, OFPortStatus m) - throws IOException, SwitchStateException { - h.pendingPortStatusMsg.add(m); - - } - }, /** * We are waiting for the respective switch driver to complete its @@ -1026,39 +476,27 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { @Override void processOFMessage(OFChannelHandler h, OFMessage m) - throws IOException { + throws IOException, SwitchStateException { if (m.getType() == OFType.ECHO_REQUEST) { processOFEchoRequest(h, (OFEchoRequest) m); + } else if (m.getType() == OFType.ROLE_REPLY) { + h.sw.handleRole(m); + } else if (m.getType() == OFType.ERROR) { + if (!h.sw.handleRoleError((OFErrorMsg)m)) { + h.sw.processDriverHandshakeMessage(m); + if (h.sw.isDriverHandshakeComplete()) { + h.setState(ACTIVE); + } + } } else { - // FIXME: other message to handle here? - h.sw.processDriverHandshakeMessage(m); - if (h.sw.isDriverHandshakeComplete()) { - // consult the h.sw role and goto that state - Role mySwitchRole = h.sw.getRole(); - if (mySwitchRole == Role.MASTER) { - log.info("Switch-driver sub-handshake complete. " - + "Activating switch {} with Role: MASTER", - h.getSwitchInfoString()); - handlePendingPortStatusMessages(h); //before activation - boolean success = h.controller.addActivatedMasterSwitch( - h.sw.getId(), h.sw); - if (!success) { - disconnectDuplicate(h); - return; - } - h.setState(MASTER); - } else { - log.info("Switch-driver sub-handshake complete. " - + "Activating switch {} with Role: EQUAL", - h.getSwitchInfoString()); - handlePendingPortStatusMessages(h); //before activation - boolean success = h.controller.addActivatedEqualSwitch( - h.sw.getId(), h.sw); - if (!success) { - disconnectDuplicate(h); - return; - } - h.setState(EQUAL); + if (m.getType() == OFType.EXPERIMENTER && + ((OFExperimenter) m).getExperimenter() == + RoleManager.NICIRA_EXPERIMENTER) { + h.sw.handleNiciraRole(m); + } else { + h.sw.processDriverHandshakeMessage(m); + if (h.sw.isDriverHandshakeComplete()) { + h.setState(ACTIVE); } } } @@ -1084,7 +522,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { * if we send a role request for SLAVE /and/ receive the role reply for * SLAVE. */ - MASTER(true) { + ACTIVE(true) { @LogMessageDoc(level = "WARN", message = "Received permission error from switch {} while" + "being master. Reasserting master role.", @@ -1097,13 +535,6 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { @Override void processOFError(OFChannelHandler h, OFErrorMsg m) throws IOException, SwitchStateException { - // first check if the error msg is in response to a role-request message - RoleRecvStatus rrstatus = h.roleChanger.deliverError(m); - if (rrstatus != RoleRecvStatus.OTHER_EXPECTATION) { - // rolechanger has handled the error message - we are done - return; - } - // if we get here, then the error message is for something else if (m.getErrType() == OFErrorType.BAD_REQUEST && ((OFBadRequestErrorMsg) m).getCode() == @@ -1116,13 +547,10 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { // if two controllers are master (even if its only for // a brief period). We might need to see if these errors // persist before we reassert - h.counters.epermErrorWhileSwitchIsMaster.updateCounterWithFlush(); log.warn("Received permission error from switch {} while" + "being master. Reasserting master role.", h.getSwitchInfoString()); - //h.controller.reassertRole(h, Role.MASTER); - // XXX S reassert in role changer or reconsider if all this - // stuff is really needed + h.sw.reassertRole(); } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED && ((OFFlowModFailedErrorMsg) m).getCode() == OFFlowModFailedCode.ALL_TABLES_FULL) { @@ -1136,35 +564,19 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { @Override void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m) { - h.sw.deliverStatisticsReply(m); + h.sw.handleMessage(m); } @Override void processOFExperimenter(OFChannelHandler h, OFExperimenter m) throws IOException, SwitchStateException { - Role role = extractNiciraRoleReply(h, m); - if (role == null) { - // The message wasn't really a Nicira role reply. We just - // dispatch it to the OFMessage listeners in this case. - h.dispatchMessage(m); - return; - } - - RoleRecvStatus rrs = h.roleChanger.deliverRoleReply( - new RoleReplyInfo(role, null, m.getXid())); - if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) { - checkAndSetRoleTransition(h, role); - } + h.sw.handleNiciraRole(m); } @Override void processOFRoleReply(OFChannelHandler h, OFRoleReply m) throws SwitchStateException, IOException { - RoleReplyInfo rri = extractOFRoleReply(h, m); - RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri); - if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) { - checkAndSetRoleTransition(h, rri.getRole()); - } + h.sw.handleRole(m); } @Override @@ -1192,95 +604,6 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { h.dispatchMessage(m); } - }, - - /** - * This controller is in EQUAL role for this switch. We enter this state - * after some /other/ controller instance wins mastership-role over this - * switch. The EQUAL role can be considered the same as the SLAVE role - * if this controller does NOT send commands or packets to the switch. - * This should always be true for OF1.0 switches. XXX S need to enforce. - * - * For OF1.3 switches, choosing this state as EQUAL instead of SLAVE, - * gives us the flexibility that if an app wants to send commands/packets - * to switches, it can, even thought it is running on a controller instance - * that is not in a MASTER role for this switch. Of course, it is the job - * of the app to ensure that commands/packets sent by this (EQUAL) controller - * instance does not clash/conflict with commands/packets sent by the MASTER - * controller for this switch. Neither the controller instances, nor the - * switch provides any kind of resolution mechanism should conflicts occur. - */ - EQUAL(true) { - @Override - void processOFError(OFChannelHandler h, OFErrorMsg m) - throws IOException, SwitchStateException { - // role changer will ignore the error if it isn't for it - RoleRecvStatus rrstatus = h.roleChanger.deliverError(m); - if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) { - logError(h, m); - h.dispatchMessage(m); - } - } - - @Override - void processOFStatisticsReply(OFChannelHandler h, - OFStatsReply m) { - h.sw.deliverStatisticsReply(m); - } - - @Override - void processOFExperimenter(OFChannelHandler h, OFExperimenter m) - throws IOException, SwitchStateException { - Role role = extractNiciraRoleReply(h, m); - // If role == null it means the message wasn't really a - // Nicira role reply. We ignore it in this state. - if (role != null) { - RoleRecvStatus rrs = h.roleChanger.deliverRoleReply( - new RoleReplyInfo(role, null, m.getXid())); - if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) { - checkAndSetRoleTransition(h, role); - } - } else { - unhandledMessageReceived(h, m); - } - } - - @Override - void processOFRoleReply(OFChannelHandler h, OFRoleReply m) - throws SwitchStateException, IOException { - RoleReplyInfo rri = extractOFRoleReply(h, m); - RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri); - if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) { - checkAndSetRoleTransition(h, rri.getRole()); - } - } - - // XXX S needs more handlers for 1.3 switches in equal role - - @Override - void processOFPortStatus(OFChannelHandler h, OFPortStatus m) - throws IOException, SwitchStateException { - handlePortStatusMessage(h, m, true); - } - - @Override - @LogMessageDoc(level = "WARN", - message = "Received PacketIn from switch {} while " - + "being slave. Reasserting slave role.", - explanation = "The switch has receive a PacketIn despite being " - + "in slave role indicating inconsistent controller roles", - recommendation = "This situation can occurs transiently during role" - + " changes. If, however, the condition persists or happens" - + " frequently this indicates a role inconsistency. " - + LogMessageDoc.CHECK_CONTROLLER) - void processOFPacketIn(OFChannelHandler h, OFPacketIn m) throws IOException { - // we don't expect packetIn while slave, reassert we are slave - h.counters.packetInWhileSwitchIsSlave.updateCounterNoFlush(); - log.warn("Received PacketIn from switch {} while" + - "being slave. Reasserting slave role.", h.sw); - //h.controller.reassertRole(h, Role.SLAVE); - // XXX reassert in role changer - } }; private final boolean handshakeComplete; @@ -1345,7 +668,6 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { */ protected void unhandledMessageReceived(OFChannelHandler h, OFMessage m) { - h.counters.unhandledMessage.updateCounterNoFlush(); if (log.isDebugEnabled()) { String msg = getSwitchStateMessage(h, m, "Ignoring unexpected message"); @@ -1398,108 +720,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { h.channel.disconnect(); } - /** - * Extract the role from an OFVendor message. - * - * Extract the role from an OFVendor message if the message is a - * Nicira role reply. Otherwise return null. - * - * @param h The channel handler receiving the message - * @param vendorMessage The vendor message to parse. - * @return The role in the message if the message is a Nicira role - * reply, null otherwise. - * @throws SwitchStateException If the message is a Nicira role reply - * but the numeric role value is unknown. - */ - protected Role extractNiciraRoleReply(OFChannelHandler h, - OFExperimenter experimenterMsg) throws SwitchStateException { - int vendor = (int) experimenterMsg.getExperimenter(); - if (vendor != 0x2320) { - return null; - } - OFNiciraControllerRoleReply nrr = - (OFNiciraControllerRoleReply) experimenterMsg; - Role role = null; - OFNiciraControllerRole ncr = nrr.getRole(); - switch(ncr) { - case ROLE_MASTER: - role = Role.MASTER; - break; - case ROLE_OTHER: - role = Role.EQUAL; - break; - case ROLE_SLAVE: - role = Role.SLAVE; - break; - default: //handled below - } - - if (role == null) { - String msg = String.format("Switch: [%s], State: [%s], " - + "received NX_ROLE_REPLY with invalid role " - + "value %s", - h.getSwitchInfoString(), - this.toString(), - nrr.getRole()); - throw new SwitchStateException(msg); - } - return role; - } - - /** - * Helper class returns role reply information in the format understood - * by the controller. - */ - protected static class RoleReplyInfo { - private Role role; - private U64 genId; - private long xid; - - RoleReplyInfo(Role role, U64 genId, long xid) { - this.role = role; - this.genId = genId; - this.xid = xid; - } - public Role getRole() { return role; } - public U64 getGenId() { return genId; } - public long getXid() { return xid; } - @Override - public String toString() { - return "[Role:" + role + " GenId:" + genId + " Xid:" + xid + "]"; - } - } - - /** - * Extract the role information from an OF1.3 Role Reply Message. - * @param h - * @param rrmsg - * @return RoleReplyInfo object - * @throws SwitchStateException - */ - protected RoleReplyInfo extractOFRoleReply(OFChannelHandler h, - OFRoleReply rrmsg) throws SwitchStateException { - OFControllerRole cr = rrmsg.getRole(); - Role role = null; - switch(cr) { - case ROLE_EQUAL: - role = Role.EQUAL; - break; - case ROLE_MASTER: - role = Role.MASTER; - break; - case ROLE_SLAVE: - role = Role.SLAVE; - break; - case ROLE_NOCHANGE: // switch should send current role - default: - String msg = String.format("Unknown controller role %s " - + "received from switch %s", cr, h.sw); - throw new SwitchStateException(msg); - } - - return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid()); - } /** * Handles all pending port status messages before a switch is declared @@ -1567,52 +788,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { throw new SwitchStateException(msg); } - Collection changes = h.sw.processOFPortStatus(m); - if (doNotify) { - for (PortChangeEvent ev: changes) { - h.controller.notifyPortChanged(h.sw.getId(), ev.port, ev.type); - } - } + h.sw.handleMessage(m); } - /** - * Checks if the role received (from the role-reply msg) is different - * from the existing role in the IOFSwitch object for this controller. - * If so, it transitions the controller to the new role. Note that - * the caller should have already verified that the role-reply msg - * received was in response to a role-request msg sent out by this - * controller after hearing from the registry service. - * - * @param h the ChannelHandler that received the message - * @param role the role in the recieved role reply message - */ - protected void checkAndSetRoleTransition(OFChannelHandler h, Role role) { - // we received a role-reply in response to a role message - // sent after hearing from the registry service. It is - // possible that the role of this controller instance for - // this switch has changed: - // for 1.0 switch: from MASTER to SLAVE - // for 1.3 switch: from MASTER to EQUAL - if ((h.sw.getRole() == Role.MASTER && role == Role.SLAVE) || - (h.sw.getRole() == Role.MASTER && role == Role.EQUAL)) { - // the mastership has changed - h.sw.setRole(role); - h.setState(EQUAL); - h.controller.transitionToEqualSwitch(h.sw.getId()); - return; - } - - // or for both 1.0 and 1.3 switches from EQUAL to MASTER. - // note that for 1.0, even though we mean SLAVE, - // internally we call the role EQUAL. - if (h.sw.getRole() == Role.EQUAL && role == Role.MASTER) { - // the mastership has changed - h.sw.setRole(role); - h.setState(MASTER); - h.controller.transitionToMasterSwitch(h.sw.getId()); - return; - } - } /** * Process an OF message received on the channel and @@ -1635,7 +813,6 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { */ void processOFMessage(OFChannelHandler h, OFMessage m) throws IOException, SwitchStateException { - h.roleChanger.checkTimeout(); switch(m.getType()) { case HELLO: processOFHello(h, (OFHello) m); @@ -1812,10 +989,6 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { unhandledMessageReceived(h, m); } - void handleUnsentRoleMessage(OFChannelHandler h, Role role, - RoleRecvStatus expectation) throws IOException { - // do nothing in most states - } } @@ -1830,7 +1003,6 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { + "specified IP address") public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - counters.switchConnected.updateCounterWithFlush(); channel = e.getChannel(); log.info("New switch connection from {}", channel.getRemoteAddress()); @@ -1853,7 +1025,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { // switch was a duplicate-dpid, calling the method below would clear // all state for the original switch (with the same dpid), // which we obviously don't want. - controller.removeConnectedSwitch(thisdpid); + sw.removeConnectedSwitch(); } else { // A duplicate was disconnected on this ChannelHandler, // this is the same switch reconnecting, but the original state was @@ -1914,12 +1086,10 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { // switch timeout log.error("Disconnecting switch {} due to read timeout", getSwitchInfoString()); - counters.switchDisconnectReadTimeout.updateCounterWithFlush(); ctx.getChannel().close(); } else if (e.getCause() instanceof HandshakeTimeoutException) { log.error("Disconnecting switch {}: failed to complete handshake", getSwitchInfoString()); - counters.switchDisconnectHandshakeTimeout.updateCounterWithFlush(); ctx.getChannel().close(); } else if (e.getCause() instanceof ClosedChannelException) { log.debug("Channel for sw {} already closed", getSwitchInfoString()); @@ -1930,7 +1100,6 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { // still print stack trace if debug is enabled log.debug("StackTrace for previous Exception: ", e.getCause()); } - counters.switchDisconnectIOError.updateCounterWithFlush(); ctx.getChannel().close(); } else if (e.getCause() instanceof SwitchStateException) { log.error("Disconnecting switch {} due to switch state error: {}", @@ -1939,23 +1108,19 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { // still print stack trace if debug is enabled log.debug("StackTrace for previous Exception: ", e.getCause()); } - counters.switchDisconnectSwitchStateException.updateCounterWithFlush(); ctx.getChannel().close(); } else if (e.getCause() instanceof OFParseError) { log.error("Disconnecting switch " + getSwitchInfoString() + " due to message parse failure", e.getCause()); - counters.switchDisconnectParseError.updateCounterWithFlush(); ctx.getChannel().close(); } else if (e.getCause() instanceof RejectedExecutionException) { log.warn("Could not process message: queue full"); - counters.rejectedExecutionException.updateCounterWithFlush(); } else { log.error("Error while processing message from switch " + getSwitchInfoString() + "state " + this.state, e.getCause()); - counters.switchDisconnectOtherException.updateCounterWithFlush(); ctx.getChannel().close(); } } @@ -1986,12 +1151,10 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { for (OFMessage ofm : msglist) { - counters.messageReceived.updateCounterNoFlush(); // Do the actual packet processing state.processOFMessage(this, ofm); } } else { - counters.messageReceived.updateCounterNoFlush(); state.processOFMessage(this, (OFMessage) e.getMessage()); } } @@ -2080,7 +1243,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { channel.write(Collections.singletonList(m)); } - private void setSwitchRole(Role role) { + private void setSwitchRole(RoleState role) { sw.setRole(role); } @@ -2146,9 +1309,4 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { return state; } - void useRoleChangerWithOtherTimeoutForTesting(long roleTimeoutMs) { - roleChanger = new RoleChanger(roleTimeoutMs); - } - - } diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/OpenFlowControllerImpl.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/OpenFlowControllerImpl.java new file mode 100644 index 0000000000..976e3478f9 --- /dev/null +++ b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/OpenFlowControllerImpl.java @@ -0,0 +1,263 @@ +package org.onlab.onos.of.controller.impl.internal; + +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Service; +import org.onlab.onos.of.controller.Dpid; +import org.onlab.onos.of.controller.OpenFlowController; +import org.onlab.onos.of.controller.OpenFlowSwitch; +import org.onlab.onos.of.controller.OpenFlowSwitchListener; +import org.onlab.onos.of.controller.PacketListener; +import org.onlab.onos.of.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.util.HexString; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component(immediate = true) +@Service +public class OpenFlowControllerImpl implements OpenFlowController { + + protected ConcurrentHashMap connectedSwitches; + protected ConcurrentHashMap activeMasterSwitches; + protected ConcurrentHashMap activeEqualSwitches; + + protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent(); + protected ArrayList ofEventListener; + + private final Controller ctrl = new Controller(); + + @Activate + public void activate() { + ctrl.start(agent); + } + + @Deactivate + public void deactivate() { + ctrl.stop(); + } + + @Override + public Iterable getSwitches() { + return connectedSwitches.values(); + } + + @Override + public Iterable getMasterSwitches() { + return activeMasterSwitches.values(); + } + + @Override + public Iterable getEqualSwitches() { + return activeEqualSwitches.values(); + } + + @Override + public OpenFlowSwitch getSwitch(Dpid dpid) { + return connectedSwitches.get(dpid.value()); + } + + @Override + public OpenFlowSwitch getMasterSwitch(Dpid dpid) { + return activeMasterSwitches.get(dpid.value()); + } + + @Override + public OpenFlowSwitch getEqualSwitch(Dpid dpid) { + return activeEqualSwitches.get(dpid.value()); } + + @Override + public void addListener(OpenFlowSwitchListener listener) { + if (!ofEventListener.contains(listener)) { + this.ofEventListener.add(listener); + } + } + + @Override + public void removeListener(OpenFlowSwitchListener listener) { + this.ofEventListener.remove(listener); + } + + @Override + public void addPacketListener(int priority, PacketListener listener) { + // TODO Auto-generated method stub + + } + + @Override + public void removePacketListener(PacketListener listener) { + // TODO Auto-generated method stub + + } + + @Override + public void write(Dpid dpid, OFMessage msg) { + this.getSwitch(dpid).write(msg); + } + + @Override + public void processPacket(OFMessage msg) { + } + + @Override + public void setRole(Dpid dpid, RoleState role) { + switch (role) { + case MASTER: + agent.transitionToMasterSwitch(dpid.value()); + break; + case EQUAL: + agent.transitionToEqualSwitch(dpid.value()); + break; + case SLAVE: + //agent.transitionToSlaveSwitch(dpid.value()); + break; + default: + //WTF role is this? + } + + } + + public class OpenFlowSwitchAgent { + + private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class); + private Lock switchLock = new ReentrantLock(); + + public boolean addConnectedSwitch(long dpid, AbstractOpenFlowSwitch sw) { + if (connectedSwitches.get(dpid) != null) { + log.error("Trying to add connectedSwitch but found a previous " + + "value for dpid: {}", dpid); + return false; + } else { + log.error("Added switch {}", dpid); + connectedSwitches.put(dpid, sw); + for (OpenFlowSwitchListener l : ofEventListener) { + l.switchAdded(new Dpid(dpid)); + } + return true; + } + } + + private boolean validActivation(long dpid) { + if (connectedSwitches.get(dpid) == null) { + log.error("Trying to activate switch but is not in " + + "connected switches: dpid {}. Aborting ..", + HexString.toHexString(dpid)); + return false; + } + if (activeMasterSwitches.get(dpid) != null || + activeEqualSwitches.get(dpid) != null) { + log.error("Trying to activate switch but it is already " + + "activated: dpid {}. Found in activeMaster: {} " + + "Found in activeEqual: {}. Aborting ..", new Object[] { + HexString.toHexString(dpid), + (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y', + (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y'}); + return false; + } + return true; + } + + /** + * Called when a switch is activated, with this controller's role as MASTER. + */ + protected boolean addActivatedMasterSwitch(long dpid, AbstractOpenFlowSwitch sw) { + switchLock.lock(); + try { + if (!validActivation(dpid)) { + return false; + } + activeMasterSwitches.put(dpid, sw); + return true; + } finally { + switchLock.unlock(); + } + } + + /** + * Called when a switch is activated, with this controller's role as EQUAL. + */ + protected boolean addActivatedEqualSwitch(long dpid, AbstractOpenFlowSwitch sw) { + switchLock.lock(); + try { + if (!validActivation(dpid)) { + return false; + } + activeEqualSwitches.put(dpid, sw); + return true; + } finally { + switchLock.unlock(); + } + } + + /** + * Called when this controller's role for a switch transitions from equal + * to master. For 1.0 switches, we internally refer to the role 'slave' as + * 'equal' - so this transition is equivalent to 'addActivatedMasterSwitch'. + */ + protected void transitionToMasterSwitch(long dpid) { + switchLock.lock(); + try { + OpenFlowSwitch sw = activeEqualSwitches.remove(dpid); + if (sw == null) { + log.error("Transition to master called on sw {}, but switch " + + "was not found in controller-cache", dpid); + return; + } + activeMasterSwitches.put(dpid, sw); + } finally { + switchLock.unlock(); + } + } + + + /** + * Called when this controller's role for a switch transitions to equal. + * For 1.0 switches, we internally refer to the role 'slave' as + * 'equal'. + */ + protected void transitionToEqualSwitch(long dpid) { + switchLock.lock(); + try { + OpenFlowSwitch sw = activeMasterSwitches.remove(dpid); + if (sw == null) { + log.error("Transition to equal called on sw {}, but switch " + + "was not found in controller-cache", dpid); + return; + } + activeEqualSwitches.put(dpid, sw); + } finally { + switchLock.unlock(); + } + + } + + /** + * Clear all state in controller switch maps for a switch that has + * disconnected from the local controller. Also release control for + * that switch from the global repository. Notify switch listeners. + */ + public void removeConnectedSwitch(long dpid) { + connectedSwitches.remove(dpid); + OpenFlowSwitch sw = activeMasterSwitches.remove(dpid); + if (sw == null) { + sw = activeEqualSwitches.remove(dpid); + } + for (OpenFlowSwitchListener l : ofEventListener) { + l.switchRemoved(new Dpid(dpid)); + } + } + + public void processMessage(OFMessage m) { + processPacket(m); + } + } + + + +} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/RoleManager.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/RoleManager.java new file mode 100644 index 0000000000..f31487a2bc --- /dev/null +++ b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/internal/RoleManager.java @@ -0,0 +1,708 @@ +package org.onlab.onos.of.controller.impl.internal; + +import java.io.IOException; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; + +import org.onlab.onos.of.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFControllerRole; +import org.projectfloodlight.openflow.protocol.OFErrorMsg; +import org.projectfloodlight.openflow.protocol.OFErrorType; +import org.projectfloodlight.openflow.protocol.OFExperimenter; +import org.projectfloodlight.openflow.protocol.OFFactories; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole; +import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply; +import org.projectfloodlight.openflow.protocol.OFRoleReply; +import org.projectfloodlight.openflow.protocol.OFRoleRequest; +import org.projectfloodlight.openflow.protocol.OFVersion; +import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg; +import org.projectfloodlight.openflow.protocol.errormsg.OFRoleRequestFailedErrorMsg; +import org.projectfloodlight.openflow.types.U64; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * A utility class to handle role requests and replies for this channel. + * After a role request is submitted the role changer keeps track of the + * pending request, collects the reply (if any) and times out the request + * if necessary. + * + * To simplify role handling we only keep track of the /last/ pending + * role reply send to the switch. If multiple requests are pending and + * we receive replies for earlier requests we ignore them. However, this + * way of handling pending requests implies that we could wait forever if + * a new request is submitted before the timeout triggers. If necessary + * we could work around that though. + */ +class RoleManager { + protected static final long NICIRA_EXPERIMENTER = 0x2320; + + private static Logger log = LoggerFactory.getLogger(RoleManager.class); + // indicates that a request is currently pending + // needs to be volatile to allow correct double-check idiom + private volatile boolean requestPending; + // the transaction Id of the pending request + private int pendingXid; + // the role that's pending + private RoleState pendingRole; + + // the expectation set by the caller for the returned role + private RoleRecvStatus expectation; + private AtomicInteger xidCounter; + private AbstractOpenFlowSwitch sw; + + + public RoleManager(AbstractOpenFlowSwitch sw) { + this.requestPending = false; + this.pendingXid = -1; + this.pendingRole = null; + this.xidCounter = new AtomicInteger(0); + this.expectation = RoleRecvStatus.MATCHED_CURRENT_ROLE; + this.sw = sw; + } + + /** + * Send NX role request message to the switch requesting the specified + * role. + * + * @param sw switch to send the role request message to + * @param role role to request + */ + private int sendNxRoleRequest(RoleState role) throws IOException { + // Convert the role enum to the appropriate role to send + OFNiciraControllerRole roleToSend = OFNiciraControllerRole.ROLE_OTHER; + switch (role) { + case MASTER: + roleToSend = OFNiciraControllerRole.ROLE_MASTER; + break; + case SLAVE: + case EQUAL: + default: + // ensuring that the only two roles sent to 1.0 switches with + // Nicira role support, are MASTER and SLAVE + roleToSend = OFNiciraControllerRole.ROLE_SLAVE; + log.warn("Sending Nx Role.SLAVE to switch {}.", sw); + } + int xid = xidCounter.getAndIncrement(); + OFExperimenter roleRequest = OFFactories.getFactory(OFVersion.OF_10) + .buildNiciraControllerRoleRequest() + .setXid(xid) + .setRole(roleToSend) + .build(); + sw.write(Collections.singletonList(roleRequest)); + return xid; + } + + private int sendOF13RoleRequest(RoleState role) throws IOException { + // Convert the role enum to the appropriate role to send + OFControllerRole roleToSend = OFControllerRole.ROLE_NOCHANGE; + switch (role) { + case EQUAL: + roleToSend = OFControllerRole.ROLE_EQUAL; + break; + case MASTER: + roleToSend = OFControllerRole.ROLE_MASTER; + break; + case SLAVE: + roleToSend = OFControllerRole.ROLE_SLAVE; + break; + default: + log.warn("Sending default role.noChange to switch {}." + + " Should only be used for queries.", sw); + } + + int xid = xidCounter.getAndIncrement(); + OFRoleRequest rrm = OFFactories.getFactory(OFVersion.OF_13) + .buildRoleRequest() + .setRole(roleToSend) + .setXid(xid) + //FIXME fix below when we actually use generation ids + .setGenerationId(U64.ZERO) + .build(); + sw.write(rrm); + return xid; + } + + /** + * Send a role request with the given role to the switch and update + * the pending request and timestamp. + * Sends an OFPT_ROLE_REQUEST to an OF1.3 switch, OR + * Sends an NX_ROLE_REQUEST to an OF1.0 switch if configured to support it + * in the IOFSwitch driver. If not supported, this method sends nothing + * and returns 'false'. The caller should take appropriate action. + * + * One other optimization we do here is that for OF1.0 switches with + * Nicira role message support, we force the Role.EQUAL to become + * Role.SLAVE, as there is no defined behavior for the Nicira role OTHER. + * We cannot expect it to behave like SLAVE. We don't have this problem with + * OF1.3 switches, because Role.EQUAL is well defined and we can simulate + * SLAVE behavior by using ASYNC messages. + * + * @param role + * @throws IOException + * @returns false if and only if the switch does not support role-request + * messages, according to the switch driver; true otherwise. + */ + synchronized boolean sendRoleRequest(RoleState role, RoleRecvStatus exp) + throws IOException { + this.expectation = exp; + + if (sw.factory().getVersion() == OFVersion.OF_10) { + Boolean supportsNxRole = (Boolean) + sw.supportNxRole(); + if (!supportsNxRole) { + log.debug("Switch driver indicates no support for Nicira " + + "role request messages. Not sending ..."); + handleUnsentRoleMessage(role, + expectation); + return false; + } + // OF1.0 switch with support for NX_ROLE_REQUEST vendor extn. + // make Role.EQUAL become Role.SLAVE + role = (role == RoleState.EQUAL) ? RoleState.SLAVE : role; + pendingXid = sendNxRoleRequest(role); + pendingRole = role; + requestPending = true; + } else { + // OF1.3 switch, use OFPT_ROLE_REQUEST message + pendingXid = sendOF13RoleRequest(role); + pendingRole = role; + requestPending = true; + } + return true; + } + + private void handleUnsentRoleMessage(RoleState role, + RoleRecvStatus exp) throws IOException { + // typically this is triggered for a switch where role messages + // are not supported - we confirm that the role being set is + // master + if (exp != RoleRecvStatus.MATCHED_SET_ROLE) { + + log.error("Expected MASTER role from registry for switch " + + "which has no support for role-messages." + + "Received {}. It is possible that this switch " + + "is connected to other controllers, in which " + + "case it should support role messages - not " + + "moving forward.", role); + + } + + } + + /** + * Deliver a received role reply. + * + * Check if a request is pending and if the received reply matches the + * the expected pending reply (we check both role and xid) we set + * the role for the switch/channel. + * + * If a request is pending but doesn't match the reply we ignore it, and + * return + * + * If no request is pending we disconnect with a SwitchStateException + * + * @param RoleReplyInfo information about role-reply in format that + * controller can understand. + * @throws SwitchStateException if no request is pending + */ + synchronized RoleRecvStatus deliverRoleReply(RoleReplyInfo rri) + throws SwitchStateException { + if (!requestPending) { + RoleState currentRole = (sw != null) ? sw.getRole() : null; + if (currentRole != null) { + if (currentRole == rri.getRole()) { + // Don't disconnect if the role reply we received is + // for the same role we are already in. + log.debug("Received unexpected RoleReply from " + + "Switch: {}. " + + "Role in reply is same as current role of this " + + "controller for this sw. Ignoring ...", + sw.getStringId()); + return RoleRecvStatus.OTHER_EXPECTATION; + } else { + String msg = String.format("Switch: [%s], " + + "received unexpected RoleReply[%s]. " + + "No roles are pending, and this controller's " + + "current role:[%s] does not match reply. " + + "Disconnecting switch ... ", + sw.getStringId(), + rri, currentRole); + throw new SwitchStateException(msg); + } + } + log.debug("Received unexpected RoleReply {} from " + + "Switch: {}. " + + "This controller has no current role for this sw. " + + "Ignoring ...", new Object[] {rri, + sw.getStringId(), }); + return RoleRecvStatus.OTHER_EXPECTATION; + } + + int xid = (int) rri.getXid(); + RoleState role = rri.getRole(); + // XXX S should check generation id meaningfully and other cases of expectations + // U64 genId = rri.getGenId(); + + if (pendingXid != xid) { + log.debug("Received older role reply from " + + "switch {} ({}). Ignoring. " + + "Waiting for {}, xid={}", + new Object[] {sw.getStringId(), rri, + pendingRole, pendingXid }); + return RoleRecvStatus.OLD_REPLY; + } + + if (pendingRole == role) { + log.debug("Received role reply message from {} that matched " + + "expected role-reply {} with expectations {}", + new Object[] {sw.getStringId(), role, expectation}); + + //setSwitchRole(role, RoleRecvStatus.RECEIVED_REPLY); dont want to set state here + if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE || + expectation == RoleRecvStatus.MATCHED_SET_ROLE) { + return expectation; + } else { + return RoleRecvStatus.OTHER_EXPECTATION; + } + } + + // if xids match but role's don't, perhaps its a query (OF1.3) + if (expectation == RoleRecvStatus.REPLY_QUERY) { + return expectation; + } + + return RoleRecvStatus.OTHER_EXPECTATION; + } + + /** + * Called if we receive an error message. If the xid matches the + * pending request we handle it otherwise we ignore it. + * + * Note: since we only keep the last pending request we might get + * error messages for earlier role requests that we won't be able + * to handle + */ + synchronized RoleRecvStatus deliverError(OFErrorMsg error) + throws SwitchStateException { + if (!requestPending) { + log.debug("Received an error msg from sw {}, but no pending " + + "requests in role-changer; not handling ...", + sw.getStringId()); + return RoleRecvStatus.OTHER_EXPECTATION; + } + if (pendingXid != error.getXid()) { + if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) { + log.debug("Received an error msg from sw {} for a role request," + + " but not for pending request in role-changer; " + + " ignoring error {} ...", + sw.getStringId(), error); + } + return RoleRecvStatus.OTHER_EXPECTATION; + } + // it is an error related to a currently pending role request message + if (error.getErrType() == OFErrorType.BAD_REQUEST) { + log.error("Received a error msg {} from sw {} for " + + "pending role request {}. Switch driver indicates " + + "role-messaging is supported. Possible issues in " + + "switch driver configuration?", new Object[] { + ((OFBadRequestErrorMsg) error).toString(), + sw.getStringId(), pendingRole + }); + return RoleRecvStatus.UNSUPPORTED; + } + + if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) { + OFRoleRequestFailedErrorMsg rrerr = + (OFRoleRequestFailedErrorMsg) error; + switch (rrerr.getCode()) { + case BAD_ROLE: + // switch says that current-role-req has bad role? + // for now we disconnect + // fall-thru + case STALE: + // switch says that current-role-req has stale gen-id? + // for now we disconnect + // fall-thru + case UNSUP: + // switch says that current-role-req has role that + // cannot be supported? for now we disconnect + String msgx = String.format("Switch: [%s], " + + "received Error to for pending role request [%s]. " + + "Error:[%s]. Disconnecting switch ... ", + sw.getStringId(), + pendingRole, rrerr); + throw new SwitchStateException(msgx); + default: + break; + } + } + + // This error message was for a role request message but we dont know + // how to handle errors for nicira role request messages + return RoleRecvStatus.OTHER_EXPECTATION; + } + + /** + * Extract the role from an OFVendor message. + * + * Extract the role from an OFVendor message if the message is a + * Nicira role reply. Otherwise return null. + * + * @param h The channel handler receiving the message + * @param vendorMessage The vendor message to parse. + * @return The role in the message if the message is a Nicira role + * reply, null otherwise. + * @throws SwitchStateException If the message is a Nicira role reply + * but the numeric role value is unknown. + */ + protected RoleState extractNiciraRoleReply(OFExperimenter experimenterMsg) + throws SwitchStateException { + int vendor = (int) experimenterMsg.getExperimenter(); + if (vendor != 0x2320) { + return null; + } + OFNiciraControllerRoleReply nrr = + (OFNiciraControllerRoleReply) experimenterMsg; + + RoleState role = null; + OFNiciraControllerRole ncr = nrr.getRole(); + switch(ncr) { + case ROLE_MASTER: + role = RoleState.MASTER; + break; + case ROLE_OTHER: + role = RoleState.EQUAL; + break; + case ROLE_SLAVE: + role = RoleState.SLAVE; + break; + default: //handled below + } + + if (role == null) { + String msg = String.format("Switch: [%s], " + + "received NX_ROLE_REPLY with invalid role " + + "value %s", + sw.getStringId(), + nrr.getRole()); + throw new SwitchStateException(msg); + } + return role; + } + + /** + * When we remove a pending role request we use this enum to indicate how we + * arrived at the decision. When we send a role request to the switch, we + * also use this enum to indicate what we expect back from the switch, so the + * role changer can match the reply to our expectation. + */ + public enum RoleRecvStatus { + /** The switch returned an error indicating that roles are not. + * supported*/ + UNSUPPORTED, + /** The request timed out. */ + NO_REPLY, + /** The reply was old, there is a newer request pending. */ + OLD_REPLY, + /** + * The reply's role matched the role that this controller set in the + * request message - invoked either initially at startup or to reassert + * current role. + */ + MATCHED_CURRENT_ROLE, + /** + * The reply's role matched the role that this controller set in the + * request message - this is the result of a callback from the + * global registry, followed by a role request sent to the switch. + */ + MATCHED_SET_ROLE, + /** + * The reply's role was a response to the query made by this controller. + */ + REPLY_QUERY, + /** We received a role reply message from the switch + * but the expectation was unclear, or there was no expectation. + */ + OTHER_EXPECTATION, + } + + /** + * Helper class returns role reply information in the format understood + * by the controller. + */ + protected static class RoleReplyInfo { + private RoleState role; + private U64 genId; + private long xid; + + RoleReplyInfo(RoleState role, U64 genId, long xid) { + this.role = role; + this.genId = genId; + this.xid = xid; + } + public RoleState getRole() { return role; } + public U64 getGenId() { return genId; } + public long getXid() { return xid; } + @Override + public String toString() { + return "[Role:" + role + " GenId:" + genId + " Xid:" + xid + "]"; + } + } + + /** + * Extract the role information from an OF1.3 Role Reply Message. + * @param h + * @param rrmsg + * @return RoleReplyInfo object + * @throws SwitchStateException + */ + protected RoleReplyInfo extractOFRoleReply(OFRoleReply rrmsg) + throws SwitchStateException { + OFControllerRole cr = rrmsg.getRole(); + RoleState role = null; + switch(cr) { + case ROLE_EQUAL: + role = RoleState.EQUAL; + break; + case ROLE_MASTER: + role = RoleState.MASTER; + break; + case ROLE_SLAVE: + role = RoleState.SLAVE; + break; + case ROLE_NOCHANGE: // switch should send current role + default: + String msg = String.format("Unknown controller role %s " + + "received from switch %s", cr, sw); + throw new SwitchStateException(msg); + } + + return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid()); + } + +} + + +///** +// * We are waiting for a role reply message in response to a role request +// * sent after hearing back from the registry service -- OR -- we are +// * just waiting to hear back from the registry service in the case that +// * the switch does not support role messages. If completed successfully, +// * the controller's role for this switch will be set here. +// * Before we move to the state corresponding to the role, we allow the +// * switch specific driver to complete its configuration. This configuration +// * typically depends on the role the controller is playing for this switch. +// * And so we set the switch role (for 'this' controller) before we start +// * the driver-sub-handshake. +// * Next State: WAIT_SWITCH_DRIVER_SUB_HANDSHAKE +// */ +//WAIT_INITIAL_ROLE(false) { +// @Override +// void processOFError(OFChannelHandler h, OFErrorMsg m) +// throws SwitchStateException { +// // role changer will ignore the error if it isn't for it +// RoleRecvStatus rrstatus = h.roleChanger.deliverError(m); +// if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) { +// logError(h, m); +// } +// } +// +// @Override +// void processOFExperimenter(OFChannelHandler h, OFExperimenter m) +// throws IOException, SwitchStateException { +// Role role = extractNiciraRoleReply(h, m); +// // If role == null it means the vendor (experimenter) message +// // wasn't really a Nicira role reply. We ignore this case. +// if (role != null) { +// RoleReplyInfo rri = new RoleReplyInfo(role, null, m.getXid()); +// RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri); +// if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) { +// setRoleAndStartDriverHandshake(h, rri.getRole()); +// } // else do nothing - wait for the correct expected reply +// } else { +// unhandledMessageReceived(h, m); +// } +// } +// +// @Override +// void processOFRoleReply(OFChannelHandler h, OFRoleReply m) +// throws SwitchStateException, IOException { +// RoleReplyInfo rri = extractOFRoleReply(h, m); +// RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri); +// if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) { +// setRoleAndStartDriverHandshake(h, rri.getRole()); +// } // else do nothing - wait for the correct expected reply +// } +// +// @Override +// void handleUnsentRoleMessage(OFChannelHandler h, Role role, +// RoleRecvStatus expectation) throws IOException { +// // typically this is triggered for a switch where role messages +// // are not supported - we confirm that the role being set is +// // master and move to the next state +// if (expectation == RoleRecvStatus.MATCHED_SET_ROLE) { +// if (role == Role.MASTER) { +// setRoleAndStartDriverHandshake(h, role); +// } else { +// log.error("Expected MASTER role from registry for switch " +// + "which has no support for role-messages." +// + "Received {}. It is possible that this switch " +// + "is connected to other controllers, in which " +// + "case it should support role messages - not " +// + "moving forward.", role); +// } +// } // else do nothing - wait to hear back from registry +// +// } +// +// private void setRoleAndStartDriverHandshake(OFChannelHandler h, +// Role role) throws IOException { +// h.setSwitchRole(role); +// h.sw.startDriverHandshake(); +// if (h.sw.isDriverHandshakeComplete()) { +// Role mySwitchRole = h.sw.getRole(); +// if (mySwitchRole == Role.MASTER) { +// log.info("Switch-driver sub-handshake complete. " +// + "Activating switch {} with Role: MASTER", +// h.sw.getStringId()); +// handlePendingPortStatusMessages(h); //before activation +// boolean success = h.sw.addActivatedMasterSwitch(); +// if (!success) { +// disconnectDuplicate(h); +// return; +// } +// h.setState(MASTER); +// } else { +// log.info("Switch-driver sub-handshake complete. " +// + "Activating switch {} with Role: EQUAL", +// h.sw.getStringId()); +// handlePendingPortStatusMessages(h); //before activation +// boolean success = h.sw.addActivatedEqualSwitch(); +// if (!success) { +// disconnectDuplicate(h); +// return; +// } +// h.setState(EQUAL); +// } +// } else { +// h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE); +// } +// } +// +// @Override +// void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) +// throws IOException, SwitchStateException { +// illegalMessageReceived(h, m); +// } +// +// @Override +// void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m) +// throws SwitchStateException { +// illegalMessageReceived(h, m); +// } +// +// @Override +// void processOFPortStatus(OFChannelHandler h, OFPortStatus m) +// throws IOException, SwitchStateException { +// h.pendingPortStatusMsg.add(m); +// +// } +//}, + + + + + + + +///** +// * This controller is in EQUAL role for this switch. We enter this state +// * after some /other/ controller instance wins mastership-role over this +// * switch. The EQUAL role can be considered the same as the SLAVE role +// * if this controller does NOT send commands or packets to the switch. +// * This should always be true for OF1.0 switches. XXX S need to enforce. +// * +// * For OF1.3 switches, choosing this state as EQUAL instead of SLAVE, +// * gives us the flexibility that if an app wants to send commands/packets +// * to switches, it can, even thought it is running on a controller instance +// * that is not in a MASTER role for this switch. Of course, it is the job +// * of the app to ensure that commands/packets sent by this (EQUAL) controller +// * instance does not clash/conflict with commands/packets sent by the MASTER +// * controller for this switch. Neither the controller instances, nor the +// * switch provides any kind of resolution mechanism should conflicts occur. +// */ +//EQUAL(true) { +// @Override +// void processOFError(OFChannelHandler h, OFErrorMsg m) +// throws IOException, SwitchStateException { +// // role changer will ignore the error if it isn't for it +// RoleRecvStatus rrstatus = h.roleChanger.deliverError(m); +// if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) { +// logError(h, m); +// h.dispatchMessage(m); +// } +// } +// +// @Override +// void processOFStatisticsReply(OFChannelHandler h, +// OFStatsReply m) { +// h.sw.handleMessage(m); +// } +// +// @Override +// void processOFExperimenter(OFChannelHandler h, OFExperimenter m) +// throws IOException, SwitchStateException { +// Role role = extractNiciraRoleReply(h, m); +// // If role == null it means the message wasn't really a +// // Nicira role reply. We ignore it in this state. +// if (role != null) { +// RoleRecvStatus rrs = h.roleChanger.deliverRoleReply( +// new RoleReplyInfo(role, null, m.getXid())); +// if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) { +// checkAndSetRoleTransition(h, role); +// } +// } else { +// unhandledMessageReceived(h, m); +// } +// } +// +// @Override +// void processOFRoleReply(OFChannelHandler h, OFRoleReply m) +// throws SwitchStateException, IOException { +// RoleReplyInfo rri = extractOFRoleReply(h, m); +// RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri); +// if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) { +// checkAndSetRoleTransition(h, rri.getRole()); +// } +// } +// +// // XXX S needs more handlers for 1.3 switches in equal role +// +// @Override +// void processOFPortStatus(OFChannelHandler h, OFPortStatus m) +// throws IOException, SwitchStateException { +// handlePortStatusMessage(h, m, true); +// } +// +// @Override +// @LogMessageDoc(level = "WARN", +// message = "Received PacketIn from switch {} while " +// + "being slave. Reasserting slave role.", +// explanation = "The switch has receive a PacketIn despite being " +// + "in slave role indicating inconsistent controller roles", +// recommendation = "This situation can occurs transiently during role" +// + " changes. If, however, the condition persists or happens" +// + " frequently this indicates a role inconsistency. " +// + LogMessageDoc.CHECK_CONTROLLER) +// void processOFPacketIn(OFChannelHandler h, OFPacketIn m) throws IOException { +// // we don't expect packetIn while slave, reassert we are slave +// h.counters.packetInWhileSwitchIsSlave.updateCounterNoFlush(); +// log.warn("Received PacketIn from switch {} while" + +// "being slave. Reasserting slave role.", h.sw); +// //h.controller.reassertRole(h, Role.SLAVE); +// // XXX reassert in role changer +// } +//}; diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/registry/ControllerRegistryEntry.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/registry/ControllerRegistryEntry.java deleted file mode 100644 index 5e83348401..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/registry/ControllerRegistryEntry.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.onlab.onos.of.controller.impl.registry; - - - -public class ControllerRegistryEntry implements Comparable { - // - // TODO: Refactor the implementation and decide whether controllerId - // is needed. If "yes", we might need to consider it inside the - // compareTo(), equals() and hashCode() implementations. - // - private final String controllerId; - private final int sequenceNumber; - - public ControllerRegistryEntry(String controllerId, int sequenceNumber) { - this.controllerId = controllerId; - this.sequenceNumber = sequenceNumber; - } - - public String getControllerId() { - return controllerId; - } - - /** - * Compares this object with the specified object for order. - * NOTE: the test is based on ControllerRegistryEntry sequence numbers, - * and doesn't include the controllerId. - * - * @param o the object to be compared. - * @return a negative integer, zero, or a positive integer as this object - * is less than, equal to, or greater than the specified object. - */ - @Override - public int compareTo(ControllerRegistryEntry o) { - return this.sequenceNumber - o.sequenceNumber; - } - - /** - * Test whether some other object is "equal to" this one. - * NOTE: the test is based on ControllerRegistryEntry sequence numbers, - * and doesn't include the controllerId. - * - * @param obj the reference object with which to compare. - * @return true if this object is the same as the obj argument; false - * otherwise. - */ - @Override - public boolean equals(Object obj) { - if (obj instanceof ControllerRegistryEntry) { - ControllerRegistryEntry other = (ControllerRegistryEntry) obj; - return this.sequenceNumber == other.sequenceNumber; - } - return false; - } - - /** - * Get the hash code for the object. - * NOTE: the computation is based on ControllerRegistryEntry sequence - * numbers, and doesn't include the controller ID. - * - * @return a hash code value for this object. - */ - @Override - public int hashCode() { - return Integer.valueOf(this.sequenceNumber).hashCode(); - } -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/registry/IControllerRegistry.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/registry/IControllerRegistry.java deleted file mode 100644 index 21b1709426..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/registry/IControllerRegistry.java +++ /dev/null @@ -1,155 +0,0 @@ -package org.onlab.onos.of.controller.impl.registry; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import org.onlab.onos.of.controller.impl.util.InstanceId; - -/** - * A registry service that allows ONOS to register controllers and switches in a - * way that is global to the entire ONOS cluster. The registry is the arbiter - * for allowing controllers to control switches. - *

- * The OVS/OF1.{2,3} fault tolerance model is a switch connects to multiple - * controllers, and the controllers send role requests to tell the switch their - * role in controlling the switch. - *

- * The ONOS fault tolerance model allows only a single controller to have - * control of a switch (MASTER role) at once. Controllers therefore need a - * mechanism that enables them to decide who should control a each switch. The - * registry service provides this mechanism. - */ -public interface IControllerRegistry { - - /** - * Callback interface for control change events. - */ - public interface ControlChangeCallback { - /** - * Called whenever the control changes from the point of view of the - * registry. The callee can check whether they have control or not using - * the hasControl parameter. - * - * @param dpid The switch that control has changed for - * @param hasControl Whether the listener now has control or not - */ - void controlChanged(long dpid, boolean hasControl); - } - - /** - * Request for control of a switch. This method does not block. When control - * for a switch changes, the controlChanged method on the callback object - * will be called. This happens any time the control changes while the - * request is still active (until releaseControl is called) - * - * @param dpid Switch to request control for - * @param cb Callback that will be used to notify caller of control changes - * @throws RegistryException Errors contacting the registry service - */ - public void requestControl(long dpid, ControlChangeCallback cb) - throws RegistryException; - - /** - * Stop trying to take control of a switch. This removes the entry for this - * controller requesting this switch in the registry. If the controller had - * control when this is called, another controller will now gain control of - * the switch. This call doesn't block. - * - * @param dpid Switch to release control of - */ - public void releaseControl(long dpid); - - /** - * Check whether the controller has control of the switch This call doesn't - * block. - * - * @param dpid Switch to check control of - * @return true if controller has control of the switch. - */ - public boolean hasControl(long dpid); - - /** - * Check whether this instance is the leader for the cluster. This call - * doesn't block. - * - * @return true if the instance is the leader for the cluster, otherwise - * false. - */ - public boolean isClusterLeader(); - - /** - * Gets the unique ID used to identify this ONOS instance in the cluster. - * - * @return Instance ID. - */ - public InstanceId getOnosInstanceId(); - - /** - * Register a controller to the ONOS cluster. Must be called before the - * registry can be used to take control of any switches. - * - * @param controllerId A unique string ID identifying this controller in the - * cluster - * @throws RegistryException for errors connecting to registry service, - * controllerId already registered - */ - public void registerController(String controllerId) - throws RegistryException; - - /** - * Get all controllers in the cluster. - * - * @return Collection of controller IDs - * @throws RegistryException on error - */ - public Collection getAllControllers() throws RegistryException; - - /** - * Get all switches in the cluster, along with which controller is in - * control of them (if any) and any other controllers that have requested - * control. - * - * @return Map of all switches. - */ - public Map> getAllSwitches(); - - /** - * Get the controller that has control of a given switch. - * - * @param dpid Switch to find controller for - * @return controller ID - * @throws RegistryException Errors contacting registry service - */ - public String getControllerForSwitch(long dpid) throws RegistryException; - - /** - * Get all switches controlled by a given controller. - * - * @param controllerId ID of the controller - * @return Collection of dpids - */ - public Collection getSwitchesControlledByController(String controllerId); - - /** - * Get a unique Id Block. - * - * @return Id Block. - */ - public IdBlock allocateUniqueIdBlock(); - - /** - * Get next unique id and retrieve a new range of ids if needed. - * - * @param range range to use for the identifier - * @return Id Block. - */ - public IdBlock allocateUniqueIdBlock(long range); - - /** - * Get a globally unique ID. - * - * @return a globally unique ID. - */ - public long getNextUniqueId(); -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/registry/IdBlock.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/registry/IdBlock.java deleted file mode 100644 index 406a60d99d..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/registry/IdBlock.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.onlab.onos.of.controller.impl.registry; - -public class IdBlock { - private final long start; - private final long end; - private final long size; - - public IdBlock(long start, long end, long size) { - this.start = start; - this.end = end; - this.size = size; - } - - public long getStart() { - return start; - } - - public long getEnd() { - return end; - } - - public long getSize() { - return size; - } - - @Override - public String toString() { - return "IdBlock [start=" + start + ", end=" + end + ", size=" + size - + "]"; - } -} - diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/registry/RegistryException.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/registry/RegistryException.java deleted file mode 100644 index 029f028b0a..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/registry/RegistryException.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.onlab.onos.of.controller.impl.registry; - -public class RegistryException extends Exception { - - private static final long serialVersionUID = -8276300722010217913L; - - public RegistryException(String message) { - super(message); - } - - public RegistryException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/DummySwitchForTesting.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/DummySwitchForTesting.java deleted file mode 100644 index 2d925f8d2f..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/DummySwitchForTesting.java +++ /dev/null @@ -1,360 +0,0 @@ -package org.onlab.onos.of.controller.impl.util; - -import java.io.IOException; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Future; - -import org.jboss.netty.channel.Channel; -import org.projectfloodlight.openflow.protocol.OFActionType; -import org.projectfloodlight.openflow.protocol.OFCapabilities; -import org.projectfloodlight.openflow.protocol.OFDescStatsReply; -import org.projectfloodlight.openflow.protocol.OFFeaturesReply; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; -import org.projectfloodlight.openflow.protocol.OFPortStatus; -import org.projectfloodlight.openflow.protocol.OFStatsReply; -import org.projectfloodlight.openflow.protocol.OFStatsRequest; -import org.projectfloodlight.openflow.protocol.OFVersion; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.U64; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.onlab.onos.of.controller.impl.IOFSwitch; -import org.onlab.onos.of.controller.impl.Role; -import org.onlab.onos.of.controller.impl.debugcounter.IDebugCounterService; -import org.onlab.onos.of.controller.impl.debugcounter.IDebugCounterService.CounterException; - -public class DummySwitchForTesting implements IOFSwitch { - - protected static final Logger log = LoggerFactory.getLogger(DummySwitchForTesting.class); - - private Channel channel; - private boolean connected = false; - private OFVersion ofv = OFVersion.OF_10; - - private Collection ports; - - private DatapathId datapathId; - - private Set capabilities; - - private int buffers; - - private byte tables; - - private String stringId; - - private Role role; - - @Override - public void disconnectSwitch() { - this.channel.close(); - } - - @Override - public void write(OFMessage m) throws IOException { - this.channel.write(m); - - } - - @Override - public void write(List msglist) throws IOException { - for (OFMessage m : msglist) { - this.channel.write(m); - } - - } - - @Override - public Date getConnectedSince() { - // TODO Auto-generated method stub - return null; - } - - @Override - public int getNextTransactionId() { - return 0; - } - - @Override - public boolean isConnected() { - return this.connected; - } - - @Override - public void setConnected(boolean connected) { - this.connected = connected; - - } - - @Override - public void flush() { - // TODO Auto-generated method stub - - } - - @Override - public void setChannel(Channel channel) { - this.channel = channel; - - } - - @Override - public long getId() { - if (this.stringId == null) { - throw new RuntimeException("Features reply has not yet been set"); - } - return this.datapathId.getLong(); - } - - @Override - public String getStringId() { - // TODO Auto-generated method stub - return "DummySwitch"; - } - - @Override - public int getNumBuffers() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public Set getCapabilities() { - // TODO Auto-generated method stub - return null; - } - - @Override - public byte getNumTables() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public OFDescStatsReply getSwitchDescription() { - // TODO Auto-generated method stub - return null; - } - - @Override - public void cancelFeaturesReply(int transactionId) { - // TODO Auto-generated method stub - - } - - @Override - public Set getActions() { - // TODO Auto-generated method stub - return null; - } - - @Override - public void setOFVersion(OFVersion version) { - // TODO Auto-generated method stub - - } - - @Override - public OFVersion getOFVersion() { - return this.ofv; - } - - @Override - public Collection getEnabledPorts() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection getEnabledPortNumbers() { - // TODO Auto-generated method stub - return null; - } - - @Override - public OFPortDesc getPort(int portNumber) { - // TODO Auto-generated method stub - return null; - } - - @Override - public OFPortDesc getPort(String portName) { - // TODO Auto-generated method stub - return null; - } - - @Override - public OrderedCollection processOFPortStatus( - OFPortStatus ps) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection getPorts() { - return ports; - } - - @Override - public boolean portEnabled(int portName) { - // TODO Auto-generated method stub - return false; - } - - @Override - public OrderedCollection setPorts( - Collection p) { - this.ports = p; - return null; - } - - @Override - public Map getAttributes() { - return null; - } - - @Override - public boolean hasAttribute(String name) { - // TODO Auto-generated method stub - return false; - } - - @Override - public Object getAttribute(String name) { - return Boolean.FALSE; - } - - @Override - public void setAttribute(String name, Object value) { - // TODO Auto-generated method stub - - } - - @Override - public Object removeAttribute(String name) { - // TODO Auto-generated method stub - return null; - } - - @Override - public void deliverStatisticsReply(OFMessage reply) { - // TODO Auto-generated method stub - - } - - @Override - public void cancelStatisticsReply(int transactionId) { - // TODO Auto-generated method stub - - } - - @Override - public void cancelAllStatisticsReplies() { - // TODO Auto-generated method stub - - } - - @Override - public Future> getStatistics(OFStatsRequest request) - throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void clearAllFlowMods() { - // TODO Auto-generated method stub - - } - - @Override - public Role getRole() { - return this.role; - } - - @Override - public void setRole(Role role) { - this.role = role; - } - - @Override - public U64 getNextGenerationId() { - // TODO Auto-generated method stub - return null; - } - - @Override - public void setDebugCounterService(IDebugCounterService debugCounter) - throws CounterException { - // TODO Auto-generated method stub - - } - - @Override - public void startDriverHandshake() throws IOException { - // TODO Auto-generated method stub - - } - - @Override - public boolean isDriverHandshakeComplete() { - return true; - } - - @Override - public void processDriverHandshakeMessage(OFMessage m) { - - } - - @Override - public void setTableFull(boolean isFull) { - // TODO Auto-generated method stub - - } - - @Override - public void setFeaturesReply(OFFeaturesReply featuresReply) { - if (featuresReply == null) { - log.error("Error setting featuresReply for switch: {}", getStringId()); - return; - } - this.datapathId = featuresReply.getDatapathId(); - this.capabilities = featuresReply.getCapabilities(); - this.buffers = (int) featuresReply.getNBuffers(); - this.tables = (byte) featuresReply.getNTables(); - this.stringId = this.datapathId.toString(); - - } - - @Override - public void setPortDescReply(OFPortDescStatsReply portDescReply) { - // TODO Auto-generated method stub - - } - - @Override - public void handleMessage(OFMessage m) { - log.info("Got packet {} but I am dumb so I don't know what to do.", m); - } - - @Override - public boolean portEnabled(String portName) { - // TODO Auto-generated method stub - return false; - } - - @Override - public OrderedCollection comparePorts( - Collection p) { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/EnumBitmaps.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/EnumBitmaps.java deleted file mode 100644 index c7734c0f10..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/EnumBitmaps.java +++ /dev/null @@ -1,149 +0,0 @@ -package org.onlab.onos.of.controller.impl.util; - -import java.util.EnumSet; -import java.util.Set; - -/** - * A utility class to convert between integer based bitmaps for (OpenFlow) - * flags and Enum and EnumSet based representations. - * - * The enum used to represent individual flags needs to implement the - * BitmapableEnum interface. - * - * Example: - * {@code - * int bitmap = 0x11; // OFPPC_PORT_DOWN | OFPPC_NO_STP - * EnumSet s = toEnumSet(OFPortConfig.class, bitmap); - * // s will contain OFPPC_PORT_DOWN and OFPPC_NO_STP - * } - * - * {@code - * EnumSet s = EnumSet.of(OFPPC_NO_STP, OFPPC_PORT_DOWN); - * int bitmap = toBitmap(s); // returns 0x11 - * } - * - */ -public final class EnumBitmaps { - - - private EnumBitmaps() { } - - /** - * Enums used to represent individual flags needs to implement this - * interface. - */ - public interface BitmapableEnum { - /** Return the value in the bitmap that the enum constant represents. - * The returned value must have only a single bit set. E.g.,1 << 3 - */ - int getValue(); - } - - - /** - * Convert an integer bitmap to an EnumSet. - * - * See class description for example - * @param type The Enum class to use. Must implement BitmapableEnum - * @param bitmap The integer bitmap - * @return A newly allocated EnumSet representing the bits set in the - * bitmap - * @throws NullPointerException if type is null - * @throws IllegalArgumentException if any enum constant from type has - * more than one bit set. - * @throws IllegalArgumentException if the bitmap has any bits set not - * represented by an enum constant. - */ - public static & BitmapableEnum> - EnumSet toEnumSet(Class type, int bitmap) { - if (type == null) { - throw new NullPointerException("Given enum type must not be null"); - } - EnumSet s = EnumSet.noneOf(type); - // allSetBitmap will eventually have all valid bits for the given - // type set. - int allSetBitmap = 0; - for (E element: type.getEnumConstants()) { - if (Integer.bitCount(element.getValue()) != 1) { - String msg = String.format("The %s (%x) constant of the " + - "enum %s is supposed to represent a bitmap entry but " + - "has more than one bit set.", - element.toString(), element.getValue(), type.getName()); - throw new IllegalArgumentException(msg); - } - allSetBitmap |= element.getValue(); - if ((bitmap & element.getValue()) != 0) { - s.add(element); - } - } - if (((~allSetBitmap) & bitmap) != 0) { - // check if only valid flags are set in the given bitmap - String msg = String.format("The bitmap %x for enum %s has " + - "bits set that are presented by any enum constant", - bitmap, type.getName()); - throw new IllegalArgumentException(msg); - } - return s; - } - - /** - * Return the bitmap mask with all possible bits set. E.g., If a bitmap - * has the individual flags 0x1, 0x2, and 0x8 (note the missing 0x4) then - * the mask will be 0xb (1011 binary) - * - * @param type The Enum class to use. Must implement BitmapableEnum - * @throws NullPointerException if type is null - * @throws IllegalArgumentException if any enum constant from type has - * more than one bit set - * @return an integer with all possible bits for the given bitmap enum - * type set. - */ - public static & BitmapableEnum> - int getMask(Class type) { - if (type == null) { - throw new NullPointerException("Given enum type must not be null"); - } - // allSetBitmap will eventually have all valid bits for the given - // type set. - int allSetBitmap = 0; - for (E element: type.getEnumConstants()) { - if (Integer.bitCount(element.getValue()) != 1) { - String msg = String.format("The %s (%x) constant of the " + - "enum %s is supposed to represent a bitmap entry but " + - "has more than one bit set.", - element.toString(), element.getValue(), type.getName()); - throw new IllegalArgumentException(msg); - } - allSetBitmap |= element.getValue(); - } - return allSetBitmap; - } - - /** - * Convert the given EnumSet to the integer bitmap representation. - * @param set The EnumSet to convert. The enum must implement - * BitmapableEnum - * @return the integer bitmap - * @throws IllegalArgumentException if an enum constant from the set (!) has - * more than one bit set - * @throws NullPointerException if the set is null - */ - public static & BitmapableEnum> - int toBitmap(Set set) { - if (set == null) { - throw new NullPointerException("Given set must not be null"); - } - int bitmap = 0; - for (E element: set) { - if (Integer.bitCount(element.getValue()) != 1) { - String msg = String.format("The %s (%x) constant in the set " + - "is supposed to represent a bitmap entry but " + - "has more than one bit set.", - element.toString(), element.getValue()); - throw new IllegalArgumentException(msg); - } - bitmap |= element.getValue(); - } - return bitmap; - } -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/FilterIterator.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/FilterIterator.java deleted file mode 100644 index 319aae87a0..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/FilterIterator.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright 2012, Big Switch Networks, Inc. - * Originally created by David Erickson, Stanford 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.onlab.onos.of.controller.impl.util; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * An iterator that will filter values from an iterator and return only - * those values that match the predicate. - */ -public abstract class FilterIterator implements Iterator { - protected Iterator subIterator; - protected T next; - - /** - * Construct a filter iterator from the given sub iterator. - * - * @param subIterator the sub iterator over which we'll filter - */ - public FilterIterator(Iterator subIterator) { - super(); - this.subIterator = subIterator; - } - - /** - * Check whether the given value should be returned by the - * filter. - * - * @param value the value to check - * @return true if the value should be included - */ - protected abstract boolean matches(T value); - - // *********** - // Iterator - // *********** - - @Override - public boolean hasNext() { - if (next != null) { - return true; - } - - while (subIterator.hasNext()) { - next = subIterator.next(); - if (matches(next)) { - return true; - } - } - next = null; - return false; - } - - @Override - public T next() { - if (hasNext()) { - T cur = next; - next = null; - return cur; - } - throw new NoSuchElementException(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/InstanceId.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/InstanceId.java deleted file mode 100644 index 4a7892e611..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/InstanceId.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.onlab.onos.of.controller.impl.util; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkArgument; - -/** - * The class representing an ONOS Instance ID. - * - * This class is immutable. - */ -public final class InstanceId { - private final String id; - - /** - * Constructor from a string value. - * - * @param id the value to use. - */ - public InstanceId(String id) { - this.id = checkNotNull(id); - checkArgument(!id.isEmpty(), "Empty ONOS Instance ID"); - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - - if (!(obj instanceof InstanceId)) { - return false; - } - - InstanceId that = (InstanceId) obj; - return this.id.equals(that.id); - } - - @Override - public String toString() { - return id; - } -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/IterableIterator.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/IterableIterator.java deleted file mode 100644 index a59b244e78..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/IterableIterator.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2012 Big Switch Networks, Inc. - * Originally created by David Erickson, Stanford 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.onlab.onos.of.controller.impl.util; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * Iterator over all values in an iterator of iterators. - * - * @param the type of elements returned by this iterator - */ -public class IterableIterator implements Iterator { - Iterator> subIterator; - Iterator current = null; - - public IterableIterator(Iterator> subIterator) { - super(); - this.subIterator = subIterator; - } - - @Override - public boolean hasNext() { - if (current == null) { - if (subIterator.hasNext()) { - current = subIterator.next().iterator(); - } else { - return false; - } - } - while (!current.hasNext() && subIterator.hasNext()) { - current = subIterator.next().iterator(); - } - - return current.hasNext(); - } - - @Override - public T next() { - if (hasNext()) { - return current.next(); - } - throw new NoSuchElementException(); - } - - @Override - public void remove() { - if (hasNext()) { - current.remove(); - } - throw new NoSuchElementException(); - } -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/LRUHashMap.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/LRUHashMap.java deleted file mode 100644 index 866794fdcd..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/LRUHashMap.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2011, Big Switch Networks, Inc. - * Originally created by David Erickson, Stanford 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.onlab.onos.of.controller.impl.util; - -import java.util.LinkedHashMap; -import java.util.Map; - -public class LRUHashMap extends LinkedHashMap { - - private static final long serialVersionUID = 1L; - - private final int capacity; - - public LRUHashMap(int capacity) { - super(capacity + 1, 0.75f, true); - this.capacity = capacity; - } - - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > capacity; - } - -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/LinkedHashSetWrapper.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/LinkedHashSetWrapper.java deleted file mode 100644 index 1778d06206..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/LinkedHashSetWrapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.onlab.onos.of.controller.impl.util; - -import java.util.Collection; -import java.util.LinkedHashSet; - -import com.google.common.collect.ForwardingCollection; - -/** - * A simple wrapper / forwarder that forwards all calls to a LinkedHashSet. - * This wrappers sole reason for existence is to implement the - * OrderedCollection marker interface. - * - */ -public class LinkedHashSetWrapper - extends ForwardingCollection implements OrderedCollection { - private final Collection delegate; - - public LinkedHashSetWrapper() { - super(); - this.delegate = new LinkedHashSet(); - } - - public LinkedHashSetWrapper(Collection c) { - super(); - this.delegate = new LinkedHashSet(c); - } - - @Override - protected Collection delegate() { - return this.delegate; - } -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/MultiIterator.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/MultiIterator.java deleted file mode 100644 index 7b709e9586..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/MultiIterator.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2012 Big Switch Networks, Inc. - * Originally created by David Erickson, Stanford 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.onlab.onos.of.controller.impl.util; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * Iterator over all values in an iterator of iterators. - * - * @param the type of elements returned by this iterator - */ -public class MultiIterator implements Iterator { - Iterator> subIterator; - Iterator current = null; - - public MultiIterator(Iterator> subIterator) { - super(); - this.subIterator = subIterator; - } - - @Override - public boolean hasNext() { - if (current == null) { - if (subIterator.hasNext()) { - current = subIterator.next(); - } else { - return false; - } - } - while (!current.hasNext() && subIterator.hasNext()) { - current = subIterator.next(); - } - - return current.hasNext(); - } - - @Override - public T next() { - if (hasNext()) { - return current.next(); - } - throw new NoSuchElementException(); - } - - @Override - public void remove() { - if (hasNext()) { - current.remove(); - } - throw new NoSuchElementException(); - } -} diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/OrderedCollection.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/OrderedCollection.java deleted file mode 100644 index 9dce568683..0000000000 --- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/util/OrderedCollection.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.onlab.onos.of.controller.impl.util; - -import java.util.Collection; - -/** - * A marker interface indicating that this Collection defines a particular - * iteration order. The details about the iteration order are specified by - * the concrete implementation. - * - * @param - */ -public interface OrderedCollection extends Collection { - -} diff --git a/of/ctl/src/test/java/org/onlab/onos/of/controller/impl/internal/ControllerTest.java b/of/ctl/src/test/java/org/onlab/onos/of/controller/impl/internal/ControllerTest.java deleted file mode 100644 index 5de36476f8..0000000000 --- a/of/ctl/src/test/java/org/onlab/onos/of/controller/impl/internal/ControllerTest.java +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright 2011, Big Switch Networks, Inc. - * Originally created by David Erickson, Stanford 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.onlab.onos.of.controller.impl.internal; - -import junit.framework.TestCase; -import org.onlab.onos.of.controller.impl.IOFSwitch; - -import org.easymock.EasyMock; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - - -public class ControllerTest extends TestCase { - - private Controller controller; - private IOFSwitch sw; - private OFChannelHandler h; - - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - sw = EasyMock.createMock(IOFSwitch.class); - h = EasyMock.createMock(OFChannelHandler.class); - controller = new Controller(); - ControllerRunThread t = new ControllerRunThread(); - t.start(); - /* - * Making sure the thread is properly started before making calls - * to controller class. - */ - Thread.sleep(200); - } - - /** - * Starts the base mocks used in these tests. - */ - private void startMocks() { - EasyMock.replay(sw, h); - } - - /** - * Reset the mocks to a known state. - * Automatically called after tests. - */ - @After - private void resetMocks() { - EasyMock.reset(sw); - } - - /** - * Fetches the controller instance. - * @return the controller - */ - public Controller getController() { - return controller; - } - - /** - * Run the controller's main loop so that updates are processed. - */ - protected class ControllerRunThread extends Thread { - @Override - public void run() { - controller.openFlowPort = 0; // Don't listen - controller.activate(); - } - } - - /** - * Verify that we are able to add a switch that just connected. - * If it already exists then this should fail - * - * @throws Exception error - */ - @Test - public void testAddConnectedSwitches() throws Exception { - startMocks(); - assertTrue(controller.addConnectedSwitch(0, h)); - assertFalse(controller.addConnectedSwitch(0, h)); - } - - /** - * Add active master but cannot re-add active master. - * @throws Exception an error occurred. - */ - @Test - public void testAddActivatedMasterSwitch() throws Exception { - startMocks(); - controller.addConnectedSwitch(0, h); - assertTrue(controller.addActivatedMasterSwitch(0, sw)); - assertFalse(controller.addActivatedMasterSwitch(0, sw)); - } - - /** - * Tests that an activated switch can be added but cannot be re-added. - * - * @throws Exception an error occurred - */ - @Test - public void testAddActivatedEqualSwitch() throws Exception { - startMocks(); - controller.addConnectedSwitch(0, h); - assertTrue(controller.addActivatedEqualSwitch(0, sw)); - assertFalse(controller.addActivatedEqualSwitch(0, sw)); - } - - /** - * Move an equal switch to master. - * @throws Exception an error occurred - */ - @Test - public void testTranstitionToMaster() throws Exception { - startMocks(); - controller.addConnectedSwitch(0, h); - controller.addActivatedEqualSwitch(0, sw); - controller.transitionToMasterSwitch(0); - assertNotNull(controller.getMasterSwitch(0)); - } - - /** - * Transition a master switch to equal state. - * @throws Exception an error occurred - */ - @Test - public void testTranstitionToEqual() throws Exception { - startMocks(); - controller.addConnectedSwitch(0, h); - controller.addActivatedMasterSwitch(0, sw); - controller.transitionToEqualSwitch(0); - assertNotNull(controller.getEqualSwitch(0)); - } - - /** - * Remove the switch from the controller instance. - * @throws Exception an error occurred - */ - @Test - public void testRemoveSwitch() throws Exception { - sw.cancelAllStatisticsReplies(); - EasyMock.expectLastCall().once(); - sw.setConnected(false); - EasyMock.expectLastCall().once(); - startMocks(); - controller.addConnectedSwitch(0, h); - controller.addActivatedMasterSwitch(0, sw); - controller.removeConnectedSwitch(0); - assertNull(controller.getSwitch(0)); - EasyMock.verify(sw, h); - } -} diff --git a/of/ctl/src/test/java/org/onlab/onos/of/controller/impl/internal/OFChannelHandlerTest.java b/of/ctl/src/test/java/org/onlab/onos/of/controller/impl/internal/OFChannelHandlerTest.java deleted file mode 100644 index f267923770..0000000000 --- a/of/ctl/src/test/java/org/onlab/onos/of/controller/impl/internal/OFChannelHandlerTest.java +++ /dev/null @@ -1,1569 +0,0 @@ -package org.onlab.onos.of.controller.impl.internal; - -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 static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.onlab.onos.of.controller.impl.IOFSwitch; -import org.onlab.onos.of.controller.impl.Role; -import org.onlab.onos.of.controller.impl.debugcounter.DebugCounter; -import org.onlab.onos.of.controller.impl.debugcounter.IDebugCounterService; -import org.onlab.onos.of.controller.impl.internal.OFChannelHandler.RoleRecvStatus; - -import org.easymock.Capture; -import org.easymock.CaptureType; -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelHandlerContext; -import org.jboss.netty.channel.ChannelPipeline; -import org.jboss.netty.channel.ChannelStateEvent; -import org.jboss.netty.channel.ExceptionEvent; -import org.jboss.netty.channel.MessageEvent; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.projectfloodlight.openflow.protocol.OFDescStatsReply; -import org.projectfloodlight.openflow.protocol.OFExperimenter; -import org.projectfloodlight.openflow.protocol.OFFactories; -import org.projectfloodlight.openflow.protocol.OFFactory; -import org.projectfloodlight.openflow.protocol.OFFeaturesReply; -import org.projectfloodlight.openflow.protocol.OFGetConfigReply; -import org.projectfloodlight.openflow.protocol.OFHelloElem; -import org.projectfloodlight.openflow.protocol.OFMessage; -import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole; -import org.projectfloodlight.openflow.protocol.OFPacketIn; -import org.projectfloodlight.openflow.protocol.OFPacketInReason; -import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; -import org.projectfloodlight.openflow.protocol.OFSetConfig; -import org.projectfloodlight.openflow.protocol.OFStatsReply; -import org.projectfloodlight.openflow.protocol.OFStatsRequest; -import org.projectfloodlight.openflow.protocol.OFStatsType; -import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.protocol.OFVersion; -import org.projectfloodlight.openflow.types.DatapathId; -import org.projectfloodlight.openflow.types.U32; - -/** - * Channel handler deals with the switch connection and dispatches - * switch messages to the appropriate locations. These Unit Testing cases - * test the channeler state machine and role changer. In the first release, - * we will focus on OF version 1.0. we will add the testing case for - * version 1.3 later. - */ -public class OFChannelHandlerTest { - private Controller controller; - private IDebugCounterService debugCounterService; - private OFChannelHandler handler; - private Channel channel; - private ChannelHandlerContext ctx; - private MessageEvent messageEvent; - private ChannelStateEvent channelStateEvent; - private ChannelPipeline pipeline; - private Capture exceptionEventCapture; - private Capture> writeCapture; - private OFFeaturesReply featuresReply; - private Set seenXids = null; - private IOFSwitch swImplBase; - private OFVersion ofVersion = OFVersion.OF_10; - private OFFactory factory13; - private OFFactory factory10; - private OFFactory factory; - - @Before - public void setUp() throws Exception { - controller = createMock(Controller.class); - ctx = createMock(ChannelHandlerContext.class); - channelStateEvent = createMock(ChannelStateEvent.class); - channel = createMock(Channel.class); - messageEvent = createMock(MessageEvent.class); - exceptionEventCapture = new Capture(CaptureType.ALL); - pipeline = createMock(ChannelPipeline.class); - writeCapture = new Capture>(CaptureType.ALL); - swImplBase = createMock(IOFSwitch.class); - seenXids = null; - factory13 = OFFactories.getFactory(OFVersion.OF_13); - factory10 = OFFactories.getFactory(OFVersion.OF_10); - factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10; - - // TODO: should mock IDebugCounterService and make sure - // the expected counters are updated. - debugCounterService = new DebugCounter(); - Controller.Counters counters = - new Controller.Counters(); - counters.createCounters(debugCounterService); - expect(controller.getCounters()).andReturn(counters).anyTimes(); - expect(controller.getOFMessageFactory10()).andReturn(factory10) - .anyTimes(); - expect(controller.getOFMessageFactory13()).andReturn(factory13) - .anyTimes(); - expect(controller.addConnectedSwitch(2000, handler)).andReturn(true) - .anyTimes(); - replay(controller); - handler = new OFChannelHandler(controller); - verify(controller); - reset(controller); - - resetChannel(); - - // replay controller. Reset it if you need more specific behavior - replay(controller); - - // replay switch. Reset it if you need more specific behavior - replay(swImplBase); - - // Mock ctx and channelStateEvent - expect(ctx.getChannel()).andReturn(channel).anyTimes(); - expect(channelStateEvent.getChannel()).andReturn(channel).anyTimes(); - replay(ctx, channelStateEvent); - - /* Setup an exception event capture on the channel. Right now - * we only expect exception events to be send up the channel. - * However, it's easy to extend to other events if we need it - */ - pipeline.sendUpstream(capture(exceptionEventCapture)); - expectLastCall().anyTimes(); - replay(pipeline); - featuresReply = (OFFeaturesReply) buildOFMessage(OFType.FEATURES_REPLY); - } - - @After - public void tearDown() { - /* ensure no exception was thrown */ - if (exceptionEventCapture.hasCaptured()) { - Throwable ex = exceptionEventCapture.getValue().getCause(); - throw new AssertionError("Unexpected exception: " + - ex.getClass().getName() + "(" + ex + ")"); - } - assertFalse("Unexpected messages have been captured", - writeCapture.hasCaptured()); - // verify all mocks. - verify(channel); - verify(messageEvent); - verify(controller); - verify(ctx); - verify(channelStateEvent); - verify(pipeline); - verify(swImplBase); - - } - - /** - * Reset the channel mock and set basic method call expectations. - * - **/ - void resetChannel() { - reset(channel); - expect(channel.getPipeline()).andReturn(pipeline).anyTimes(); - expect(channel.getRemoteAddress()).andReturn(null).anyTimes(); - } - - /** - * reset, setup, and replay the messageEvent mock for the given - * messages. - */ - void setupMessageEvent(List messages) { - reset(messageEvent); - expect(messageEvent.getMessage()).andReturn(messages).atLeastOnce(); - replay(messageEvent); - } - - /** - * reset, setup, and replay the messageEvent mock for the given - * messages, mock controller send message to channel handler. - * - * This method will reset, start replay on controller, and then verify - */ - void sendMessageToHandlerWithControllerReset(List messages) - throws Exception { - verify(controller); - reset(controller); - - sendMessageToHandlerNoControllerReset(messages); - } - - /** - * reset, setup, and replay the messageEvent mock for the given - * messages, mock controller send message to channel handler. - * - * This method will start replay on controller, and then verify - */ - void sendMessageToHandlerNoControllerReset(List messages) - throws Exception { - setupMessageEvent(messages); - - expect(controller.addConnectedSwitch(1000, handler)) - .andReturn(true).anyTimes(); - replay(controller); - - handler.messageReceived(ctx, messageEvent); - verify(controller); - } - - /** - * Extract the list of OFMessages that was captured by the Channel.write() - * capture. Will check that something was actually captured first. We'll - * collapse the messages from multiple writes into a single list of - * OFMessages. - * Resets the channelWriteCapture. - */ - List getMessagesFromCapture() { - List msgs = new ArrayList(); - - assertTrue("No write on channel was captured", - writeCapture.hasCaptured()); - List> capturedVals = writeCapture.getValues(); - - for (List oneWriteList: capturedVals) { - msgs.addAll(oneWriteList); - } - writeCapture.reset(); - return msgs; - } - - - /** - * Verify that the given exception event capture (as returned by - * getAndInitExceptionCapture) has thrown an exception of the given - * expectedExceptionClass. - * Resets the capture - */ - void verifyExceptionCaptured( - Class expectedExceptionClass) { - assertTrue("Excpected exception not thrown", - exceptionEventCapture.hasCaptured()); - Throwable caughtEx = exceptionEventCapture.getValue().getCause(); - assertEquals(expectedExceptionClass, caughtEx.getClass()); - exceptionEventCapture.reset(); - } - - /** - * Make sure that the transaction ids in the given messages are - * not 0 and differ between each other. - * While it's not a defect per se if the xids are we want to ensure - * we use different ones for each message we send. - */ - void verifyUniqueXids(List msgs) { - if (seenXids == null) { - seenXids = new HashSet(); - } - for (OFMessage m: msgs) { - int xid = (int) m.getXid(); - assertTrue("Xid in messags is 0", xid != 0); - assertFalse("Xid " + xid + " has already been used", - seenXids.contains(xid)); - seenXids.add(xid); - } - } - - - - public void testInitState() throws Exception { - OFMessage m = buildOFMessage(OFType.HELLO); - - expect(messageEvent.getMessage()).andReturn(null); - replay(channel, messageEvent); - - // We don't expect to receive /any/ messages in init state since - // channelConnected moves us to a different state - sendMessageToHandlerWithControllerReset(Collections.singletonList(m)); - - verifyExceptionCaptured(SwitchStateException.class); - assertEquals(OFChannelHandler.ChannelState.INIT, - handler.getStateForTesting()); - } - - /** - * move the channel from scratch to WAIT_HELLO state. - * - */ - @Test - public void moveToWaitHello() throws Exception { - resetChannel(); - channel.write(capture(writeCapture)); - expectLastCall().andReturn(null).once(); - replay(channel); - // replay unused mocks - replay(messageEvent); - - handler.channelConnected(ctx, channelStateEvent); - - List msgs = getMessagesFromCapture(); - assertEquals(1, msgs.size()); - assertEquals(OFType.HELLO, msgs.get(0).getType()); - assertEquals(OFChannelHandler.ChannelState.WAIT_HELLO, - handler.getStateForTesting()); - //Should verify that the Hello received from the controller - //is ALWAYS OF1.3 hello regardless of the switch version - assertEquals(OFVersion.OF_13, msgs.get(0).getVersion()); - verifyUniqueXids(msgs); - } - - - /** - * Move the channel from scratch to WAIT_FEATURES_REPLY state. - * Builds on moveToWaitHello(). - * adds testing for WAIT_HELLO state. - */ - @Test - public void moveToWaitFeaturesReply() throws Exception { - moveToWaitHello(); - resetChannel(); - channel.write(capture(writeCapture)); - expectLastCall().andReturn(null).atLeastOnce(); - replay(channel); - - OFMessage hello = buildOFMessage(OFType.HELLO); - sendMessageToHandlerWithControllerReset(Collections.singletonList(hello)); - - List msgs = getMessagesFromCapture(); - assertEquals(1, msgs.size()); - assertEquals(OFType.FEATURES_REQUEST, msgs.get(0).getType()); - if (ofVersion == OFVersion.OF_10) { - assertEquals(OFVersion.OF_10, msgs.get(0).getVersion()); - } - verifyUniqueXids(msgs); - - assertEquals(OFChannelHandler.ChannelState.WAIT_FEATURES_REPLY, - handler.getStateForTesting()); - } - - /** - * Move the channel from scratch to WAIT_CONFIG_REPLY state. - * Builds on moveToWaitFeaturesReply. - * adds testing for WAIT_FEATURES_REPLY state. - */ - @Test - public void moveToWaitConfigReply() throws Exception { - moveToWaitFeaturesReply(); - - resetChannel(); - channel.write(capture(writeCapture)); - expectLastCall().andReturn(null).atLeastOnce(); - replay(channel); - - sendMessageToHandlerWithControllerReset(Collections.singletonList(featuresReply)); - List msgs = getMessagesFromCapture(); - assertEquals(3, msgs.size()); - assertEquals(OFType.SET_CONFIG, msgs.get(0).getType()); - OFSetConfig sc = (OFSetConfig) msgs.get(0); - assertEquals((short) 0xffff, sc.getMissSendLen()); - assertEquals(OFType.BARRIER_REQUEST, msgs.get(1).getType()); - assertEquals(OFType.GET_CONFIG_REQUEST, msgs.get(2).getType()); - verifyUniqueXids(msgs); - assertEquals(OFChannelHandler.ChannelState.WAIT_CONFIG_REPLY, - handler.getStateForTesting()); - } - - /** - * Move the channel from scratch to WAIT_DESCRIPTION_STAT_REPLY state. - * Builds on moveToWaitConfigReply(). - * adds testing for WAIT_CONFIG_REPLY state. - */ - @Test - public void moveToWaitDescriptionStatReply() throws Exception { - moveToWaitConfigReply(); - resetChannel(); - channel.write(capture(writeCapture)); - expectLastCall().andReturn(null).atLeastOnce(); - replay(channel); - - OFGetConfigReply cr = (OFGetConfigReply) buildOFMessage(OFType.GET_CONFIG_REPLY); - - sendMessageToHandlerWithControllerReset(Collections.singletonList(cr)); - - List msgs = getMessagesFromCapture(); - assertEquals(1, msgs.size()); - assertEquals(OFType.STATS_REQUEST, msgs.get(0).getType()); - OFStatsRequest sr = (OFStatsRequest) msgs.get(0); - assertEquals(OFStatsType.DESC, sr.getStatsType()); - verifyUniqueXids(msgs); - assertEquals(OFChannelHandler.ChannelState.WAIT_DESCRIPTION_STAT_REPLY, - handler.getStateForTesting()); - } - - - private OFStatsReply createDescriptionStatsReply() throws IOException { - OFStatsReply sr = (OFStatsReply) buildOFMessage(OFType.STATS_REPLY); - return sr; - } - - /** - * Move the channel from scratch to WAIT_INITIAL_ROLE state. - * for a switch that does not have a sub-handshake. - * Builds on moveToWaitDescriptionStatReply(). - * adds testing for WAIT_DESCRIPTION_STAT_REPLY state. - * - */ - @Test - public void moveToWaitInitialRole() - throws Exception { - moveToWaitDescriptionStatReply(); - - long xid = 2000; - - // build the stats reply - OFStatsReply sr = createDescriptionStatsReply(); - - resetChannel(); - replay(channel); - - setupMessageEvent(Collections.singletonList(sr)); - - // mock controller - reset(controller); - reset(swImplBase); - - expect(controller.getOFSwitchInstance((OFDescStatsReply) sr, ofVersion)) - .andReturn(swImplBase).anyTimes(); - expect(controller.getDebugCounter()) - .andReturn(debugCounterService).anyTimes(); - controller.submitRegistryRequest(1000); - expectLastCall().once(); - replay(controller); - - //TODO: With the description stats message you are sending in the test, - //you will end up with an OFSwitchImplBase object - //which by default does NOT support the nicira role messages. - //If you wish to test the case where Nicira role messages are supported, - //then make a comment here that states that this is different - //from the default behavior of switchImplbase /or/ - //send the right desc-stats (for example send what is expected from OVS 1.0) - - if (ofVersion == OFVersion.OF_10) { - expect(swImplBase.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) - .andReturn(true).once(); - - swImplBase.write(capture(writeCapture)); - expectLastCall().anyTimes(); - } - - swImplBase.setOFVersion(ofVersion); - expectLastCall().once(); - swImplBase.setConnected(true); - expectLastCall().once(); - swImplBase.setChannel(channel); - expectLastCall().once(); - swImplBase.setDebugCounterService(controller.getDebugCounter()); - expectLastCall().once(); - expect(swImplBase.getStringId()) - .andReturn(null).anyTimes(); - swImplBase.setRole(Role.EQUAL); - expectLastCall().once(); - - expect(swImplBase.getNextTransactionId()) - .andReturn((int) xid).anyTimes(); - expect(swImplBase.getId()) - .andReturn(1000L).once(); - - swImplBase.setFeaturesReply(featuresReply); - expectLastCall().once(); - swImplBase.setPortDescReply((OFPortDescStatsReply) null); - replay(swImplBase); - - // send the description stats reply - handler.messageReceived(ctx, messageEvent); - - List msgs = getMessagesFromCapture(); - assertEquals(1, msgs.size()); - assertEquals(OFType.EXPERIMENTER, msgs.get(0).getType()); - verifyNiciraMessage((OFExperimenter) msgs.get(0)); - - verify(controller); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - } - - /** - * Move the channel from scratch to. - * WAIT_SWITCH_DRIVER_SUB_HANDSHAKE state. - * Builds on moveToWaitInitialRole(). - */ - @Test - public void moveToWaitSubHandshake() - throws Exception { - moveToWaitInitialRole(); - - int xid = 2000; - resetChannel(); - replay(channel); - - reset(swImplBase); - // Set the role - setupSwitchSendRoleRequestAndVerify(true, xid, Role.SLAVE); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - // build the stats reply - OFStatsReply sr = createDescriptionStatsReply(); - OFMessage rr = getRoleReply(xid, Role.SLAVE); - setupMessageEvent(Collections.singletonList(rr)); - - // mock controller - reset(controller); - reset(swImplBase); - - expect(controller.getOFSwitchInstance((OFDescStatsReply) sr, ofVersion)) - .andReturn(swImplBase).anyTimes(); - expect(controller.getDebugCounter()) - .andReturn(debugCounterService).anyTimes(); - - replay(controller); - - expect(swImplBase.getStringId()) - .andReturn(null).anyTimes(); - swImplBase.setRole(Role.SLAVE); - expectLastCall().once(); - expect(swImplBase.getNextTransactionId()) - .andReturn(xid).anyTimes(); - swImplBase.startDriverHandshake(); - expectLastCall().once(); - - //when this flag is false, state machine will move to - //WAIT_SWITCH_DRIVER_SUB_HANDSHAKE state - expect(swImplBase.isDriverHandshakeComplete()) - .andReturn(false).once(); - - replay(swImplBase); - - // send the description stats reply - handler.messageReceived(ctx, messageEvent); - - assertEquals(OFChannelHandler.ChannelState.WAIT_SWITCH_DRIVER_SUB_HANDSHAKE, - handler.getStateForTesting()); - } - - /** - * Move the channel from scratch to WAIT_INITIAL_ROLE state, - * then move the channel to EQUAL state based on the switch Role. - * This test basically test the switch with role support. - * Builds on moveToWaitInitialRole(). - * - * In WAIT_INITIAL_ROLE state, when any messages (except ECHO_REQUEST - * and PORT_STATUS), state machine will transit to MASTER or - * EQUAL state based on the switch role. - */ - @Test - public void moveToSlaveWithHandshakeComplete() - throws Exception { - - moveToWaitInitialRole(); - - int xid = 2000; - resetChannel(); - replay(channel); - - reset(swImplBase); - // Set the role - setupSwitchSendRoleRequestAndVerify(true, xid, Role.SLAVE); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - // build the stats reply - OFStatsReply sr = createDescriptionStatsReply(); - OFMessage rr = getRoleReply(xid, Role.SLAVE); - setupMessageEvent(Collections.singletonList(rr)); - - // mock controller - reset(controller); - reset(swImplBase); - - expect(controller.getOFSwitchInstance((OFDescStatsReply) sr, ofVersion)) - .andReturn(swImplBase).anyTimes(); - - expect(controller.getDebugCounter()) - .andReturn(debugCounterService).anyTimes(); - - expect(controller.addActivatedEqualSwitch(1000, swImplBase)) - .andReturn(true).once(); - replay(controller); - - expect(swImplBase.getStringId()) - .andReturn(null).anyTimes(); - //consult the role in sw to determine the next state. - //in this testing case, we are testing that channel handler - // will move to EQUAL state when switch role is in SLAVE. - expect(swImplBase.getRole()).andReturn(Role.SLAVE).once(); - swImplBase.setRole(Role.SLAVE); - expectLastCall().once(); - - expect(swImplBase.getNextTransactionId()) - .andReturn(xid).anyTimes(); - expect(swImplBase.getId()) - .andReturn(1000L).once(); - swImplBase.startDriverHandshake(); - expectLastCall().once(); - - //when this flag is true, don't need to move interim state - //WAIT_SWITCH_DRIVER_SUB_HANDSHAKE. channel handler will - //move to corresponding state after consulting the role in sw - //This is essentially the same test as the one above, - //except for this line - expect(swImplBase.isDriverHandshakeComplete()) - .andReturn(true).once(); - - replay(swImplBase); - - // send the description stats reply - handler.messageReceived(ctx, messageEvent); - - assertEquals(OFChannelHandler.ChannelState.EQUAL, - handler.getStateForTesting()); - } - - /** - * Move the channel from scratch to WAIT_INITIAL_ROLE state, - * then to MASTERL state based on the switch Role. - * This test basically test the switch with role support. - * Builds on moveToWaitInitialRole(). - * - * In WAIT_INITIAL_ROLE state, when any messages (except ECHO_REQUEST - * and PORT_STATUS), state machine will transit to MASTER or - * EQUAL state based on the switch role. - */ - @Test - public void moveToMasterWithHandshakeComplete() - throws Exception { - - moveToWaitInitialRole(); - - int xid = 2000; - resetChannel(); - replay(channel); - - reset(swImplBase); - // Set the role - setupSwitchSendRoleRequestAndVerify(true, xid, Role.MASTER); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - // build the stats reply - OFStatsReply sr = createDescriptionStatsReply(); - OFMessage rr = getRoleReply(xid, Role.MASTER); - setupMessageEvent(Collections.singletonList(rr)); - - // mock controller - reset(controller); - reset(swImplBase); - - expect(controller.getOFSwitchInstance((OFDescStatsReply) sr, ofVersion)) - .andReturn(swImplBase).anyTimes(); - - expect(controller.getDebugCounter()) - .andReturn(debugCounterService).anyTimes(); - - expect(controller.addActivatedMasterSwitch(1000, swImplBase)) - .andReturn(true).once(); - replay(controller); - - expect(swImplBase.getStringId()) - .andReturn(null).anyTimes(); - expect(swImplBase.getRole()).andReturn(Role.MASTER).once(); - swImplBase.setRole(Role.MASTER); - expectLastCall().once(); - - expect(swImplBase.getNextTransactionId()) - .andReturn(xid).anyTimes(); - expect(swImplBase.getId()) - .andReturn(1000L).once(); - swImplBase.startDriverHandshake(); - expectLastCall().once(); - expect(swImplBase.isDriverHandshakeComplete()) - .andReturn(true).once(); - - replay(swImplBase); - - - // send the description stats reply - handler.messageReceived(ctx, messageEvent); - - assertEquals(OFChannelHandler.ChannelState.MASTER, - handler.getStateForTesting()); - } - - /** - * Move the channel from scratch to - * WAIT_SWITCH_DRIVER_SUB_HANDSHAKE state. - * Builds on moveToWaitSubHandshake(). - */ - @Test - public void moveToEqualViaWaitSubHandshake() - throws Exception { - moveToWaitSubHandshake(); - - long xid = 2000; - resetChannel(); - replay(channel); - - // build the stats reply - OFStatsReply sr = createDescriptionStatsReply(); - - setupMessageEvent(Collections.singletonList(sr)); - - // mock controller - reset(controller); - reset(swImplBase); - - expect(controller.getOFSwitchInstance((OFDescStatsReply) sr, ofVersion)) - .andReturn(swImplBase).anyTimes(); - expect(controller.getDebugCounter()) - .andReturn(debugCounterService).anyTimes(); - - expect(controller.addActivatedEqualSwitch(1000, swImplBase)) - .andReturn(true).once(); - replay(controller); - - expect(swImplBase.getStringId()) - .andReturn(null).anyTimes(); - expect(swImplBase.getRole()).andReturn(Role.SLAVE).once(); - expect(swImplBase.getNextTransactionId()) - .andReturn((int) xid).anyTimes(); - expect(swImplBase.getId()) - .andReturn(1000L).once(); - - swImplBase.processDriverHandshakeMessage(sr); - expectLastCall().once(); - expect(swImplBase.isDriverHandshakeComplete()) - .andReturn(true).once(); - - replay(swImplBase); - - // send the description stats reply - handler.messageReceived(ctx, messageEvent); - - assertEquals(OFChannelHandler.ChannelState.EQUAL, - handler.getStateForTesting()); - } - - /** - * Move the channel from scratch to - * WAIT_SWITCH_DRIVER_SUB_HANDSHAKE state. - * Builds on moveToWaitSubHandshake(). - */ - @Test - public void moveToMasterViaWaitSubHandshake() - throws Exception { - moveToWaitSubHandshake(); - - long xid = 2000; - resetChannel(); - replay(channel); - - // In this state, any messages except echo request, port status and - // error go to the switch sub driver handshake. Once the switch reports - // that its sub driver handshake is complete (#isDriverHandshakeComplete - // return true) then the channel handle consults the switch role and - // moves the state machine to the appropriate state (MASTER or EQUALS). - // In this test we expect the state machine to end up in MASTER state. - OFStatsReply sr = createDescriptionStatsReply(); - - setupMessageEvent(Collections.singletonList(sr)); - - // mock controller - reset(controller); - reset(swImplBase); - - expect(controller.getOFSwitchInstance((OFDescStatsReply) sr, ofVersion)) - .andReturn(swImplBase).anyTimes(); - - expect(controller.getDebugCounter()) - .andReturn(debugCounterService).anyTimes(); - expect(controller.addActivatedMasterSwitch(1000, swImplBase)) - .andReturn(true).once(); - replay(controller); - - expect(swImplBase.getStringId()) - .andReturn(null).anyTimes(); - expect(swImplBase.getRole()).andReturn(Role.MASTER).once(); - expect(swImplBase.getNextTransactionId()) - .andReturn((int) xid).anyTimes(); - expect(swImplBase.getId()) - .andReturn(1000L).once(); - - swImplBase.processDriverHandshakeMessage(sr); - expectLastCall().once(); - expect(swImplBase.isDriverHandshakeComplete()) - .andReturn(true).once(); - - replay(swImplBase); - - // send the description stats reply - handler.messageReceived(ctx, messageEvent); - verify(controller); - assertEquals(OFChannelHandler.ChannelState.MASTER, - handler.getStateForTesting()); - } - - /** - * Test the behavior in WAIT_SWITCH_DRIVER_SUB_HANDSHAKE state. - * ECHO_REQUEST message received case. - */ - @Test - public void testWaitSwitchDriverSubhandshake() throws Exception { - moveToWaitSubHandshake(); - - long xid = 2000; - resetChannel(); - channel.write(capture(writeCapture)); - expectLastCall().andReturn(null).atLeastOnce(); - replay(channel); - - OFMessage er = buildOFMessage(OFType.ECHO_REQUEST); - - setupMessageEvent(Collections.singletonList(er)); - - // mock controller - reset(controller); - reset(swImplBase); - - expect(controller.getOFMessageFactory10()).andReturn(factory10); - expect(controller.getDebugCounter()) - .andReturn(debugCounterService).anyTimes(); - - replay(controller); - - expect(swImplBase.getStringId()) - .andReturn(null).anyTimes(); - expect(swImplBase.getNextTransactionId()) - .andReturn((int) xid).anyTimes(); - - replay(swImplBase); - - handler.messageReceived(ctx, messageEvent); - - List msgs = getMessagesFromCapture(); - assertEquals(1, msgs.size()); - assertEquals(OFType.ECHO_REPLY, msgs.get(0).getType()); - verifyUniqueXids(msgs); - assertEquals(OFChannelHandler.ChannelState.WAIT_SWITCH_DRIVER_SUB_HANDSHAKE, - handler.getStateForTesting()); - } - - /** - * Helper. - * Verify that the given OFMessage is a correct Nicira RoleRequest message. - */ - private void verifyNiciraMessage(OFExperimenter ofMessage) { - - int vendor = (int) ofMessage.getExperimenter(); - assertEquals(vendor, 0x2320); // magic number representing nicira - } - - /** - * Setup the mock switch and write capture for a role request, set the - * role and verify mocks. - * @param supportsNxRole whether the switch supports role request messages - * to setup the attribute. This must be null (don't yet know if roles - * supported: send to check) or true. - * @param xid The xid to use in the role request - * @param role The role to send - * @throws IOException - */ - private void setupSwitchSendRoleRequestAndVerify(Boolean supportsNxRole, - int xid, - Role role) throws IOException { - - RoleRecvStatus expectation = RoleRecvStatus.MATCHED_SET_ROLE; - - expect(swImplBase.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) - .andReturn(supportsNxRole).atLeastOnce(); - - if (supportsNxRole != null && supportsNxRole) { - expect(swImplBase.getNextTransactionId()).andReturn(xid).once(); - swImplBase.write(capture(writeCapture)); - expectLastCall().anyTimes(); - } - replay(swImplBase); - - handler.sendRoleRequest(role, expectation); - - if (supportsNxRole != null && supportsNxRole) { - List msgs = getMessagesFromCapture(); - assertEquals(1, msgs.size()); - verifyNiciraMessage((OFExperimenter) msgs.get(0)); - } - } - - /** - * Setup the mock switch for a role change request where the switch - * does not support roles. - * - * Needs to verify and reset the controller since we need to set - * an expectation - */ - private void setupSwitchRoleChangeUnsupported(int xid, - Role role) { - boolean supportsNxRole = false; - RoleRecvStatus expectation = RoleRecvStatus.NO_REPLY; - reset(swImplBase); - expect(swImplBase.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) - .andReturn(supportsNxRole).atLeastOnce(); - // TODO: hmmm. While it's not incorrect that we set the attribute - // again it looks odd. Maybe change - swImplBase.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, supportsNxRole); - expectLastCall().anyTimes(); - - replay(swImplBase); - - handler.sendRoleRequest(role, expectation); - - verify(swImplBase); - } - - /* - * Return a Nicira RoleReply message for the given role. - */ - private OFMessage getRoleReply(long xid, Role role) { - - OFNiciraControllerRole nr = null; - - switch(role) { - case MASTER: - nr = OFNiciraControllerRole.ROLE_MASTER; - break; - case EQUAL: - nr = OFNiciraControllerRole.ROLE_SLAVE; - break; - case SLAVE: - nr = OFNiciraControllerRole.ROLE_SLAVE; - break; - default: //handled below - } - OFMessage m = factory10.buildNiciraControllerRoleReply() - .setRole(nr) - .setXid(xid) - .build(); - return m; - } - - /** - * Move the channel from scratch to MASTER state. - * Builds on moveToWaitInitialRole(). - * adds testing for WAIT_INITAL_ROLE state. - * - * This method tests the case that the switch does NOT support roles. - * In ONOS if the switch-driver says that nicira-role messages are not - * supported, then ONOS does NOT send role-request messages - * (see handleUnsentRoleMessage()) - */ - @Test - public void testInitialMoveToMasterNoRole() throws Exception { - int xid = 43; - // first, move us to WAIT_INITIAL_ROLE_STATE - - moveToWaitInitialRole(); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - OFStatsReply sr = createDescriptionStatsReply(); - - reset(controller); - reset(swImplBase); - - expect(controller.getOFSwitchInstance((OFDescStatsReply) sr, ofVersion)) - .andReturn(swImplBase).anyTimes(); - - expect(controller.getDebugCounter()) - .andReturn(debugCounterService).anyTimes(); - - expect(controller.addActivatedMasterSwitch(1000, swImplBase)) - .andReturn(true).once(); - replay(controller); - - reset(swImplBase); - swImplBase.setRole(Role.MASTER); - expectLastCall().once(); - swImplBase.startDriverHandshake(); - expectLastCall().once(); - expect(swImplBase.isDriverHandshakeComplete()) - .andReturn(true).once(); - expect(swImplBase.getStringId()) - .andReturn(null).anyTimes(); - expect(swImplBase.getRole()).andReturn(Role.MASTER).once(); - - expect(swImplBase.getId()) - .andReturn(1000L).once(); - // Set the role - setupSwitchSendRoleRequestAndVerify(false, xid, Role.MASTER); - - assertEquals(OFChannelHandler.ChannelState.MASTER, - handler.getStateForTesting()); - } - - /** - * Move the channel from scratch to WAIT_INITIAL_ROLE state. - * Builds on moveToWaitInitialRole(). - * adds testing for WAIT_INITAL_ROLE state - * - * We let the initial role request time out. Role support should be - * disabled but the switch should be activated. - */ - /* TBD - @Test - public void testInitialMoveToMasterTimeout() throws Exception { - int timeout = 50; - handler.useRoleChangerWithOtherTimeoutForTesting(timeout); - int xid = 4343; - - // first, move us to WAIT_INITIAL_ROLE_STATE - - moveToWaitInitialRole(); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - // prepare mocks and inject the role reply message - reset(swImplBase); - // Set the role - swImplBase.setRole(Role.MASTER); - expectLastCall().once(); - swImplBase.startDriverHandshake(); - expectLastCall().once(); - expect(swImplBase.isDriverHandshakeComplete()) - .andReturn(false).once(); - if (ofVersion == OFVersion.OF_10) { - expect(swImplBase.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) - .andReturn(true).once(); - - swImplBase.write(capture(writeCapture), - EasyMock.anyObject()); - expectLastCall().anyTimes(); - } - expect(swImplBase.getNextTransactionId()).andReturn(xid).once(); - - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - // Set the role - setupSwitchSendRoleRequestAndVerify(null, xid, Role.MASTER); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - OFMessage m = buildOFMessage(OFType.ECHO_REPLY); - - setupMessageEvent(Collections.singletonList(m)); - - Thread.sleep(timeout+5); - - verify(controller); - reset(controller); - - expect(controller.addActivatedMasterSwitch(1000, swImplBase)) - .andReturn(true).once(); - controller.flushAll(); - expectLastCall().once(); - - replay(controller); - - handler.messageReceived(ctx, messageEvent); - - assertEquals(OFChannelHandler.ChannelState.MASTER, - handler.getStateForTesting()); - - } - - */ - /** - * Move the channel from scratch to SLAVE state. - * Builds on doMoveToWaitInitialRole(). - * adds testing for WAIT_INITAL_ROLE state - * - * This method tests the case that the switch does NOT support roles. - * The channel handler still needs to send the initial request to find - * out that whether the switch supports roles. - * - */ - @Test - public void testInitialMoveToSlaveNoRole() throws Exception { - int xid = 44; - // first, move us to WAIT_INITIAL_ROLE_STATE - moveToWaitInitialRole(); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - reset(swImplBase); - // Set the role - setupSwitchSendRoleRequestAndVerify(false, xid, Role.SLAVE); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - } - - /** - * Move the channel from scratch to SLAVE state. - * Builds on doMoveToWaitInitialRole(). - * adds testing for WAIT_INITAL_ROLE state - * - * We let the initial role request time out. The switch should be - * disconnected - */ - /* TBD - @Test - public void testInitialMoveToSlaveTimeout() throws Exception { - int timeout = 50; - handler.useRoleChangerWithOtherTimeoutForTesting(timeout); - int xid = 4444; - - // first, move us to WAIT_INITIAL_ROLE_STATE - moveToWaitInitialRole(); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - // Set the role - setupSwitchSendRoleRequestAndVerify(null, xid, Role.SLAVE); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - // prepare mocks and inject the role reply message - reset(sw); - sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); - expectLastCall().once(); - sw.setRole(Role.SLAVE); - expectLastCall().once(); - sw.disconnectSwitch(); // Make sure we disconnect - expectLastCall().once(); - replay(sw); - - OFMessage m = buildOFMessage(OFType.ECHO_REPLY); - - Thread.sleep(timeout+5); - - sendMessageToHandlerWithControllerReset(Collections.singletonList(m)); - } - - */ - /** - * Move channel from scratch to WAIT_INITIAL_STATE, then MASTER, - * then SLAVE for cases where the switch does not support roles. - * I.e., the final SLAVE transition should disconnect the switch. - */ - @Test - public void testNoRoleInitialToMasterToSlave() throws Exception { - int xid = 46; - reset(swImplBase); - replay(swImplBase); - - reset(controller); - replay(controller); - - // First, lets move the state to MASTER without role support - testInitialMoveToMasterNoRole(); - assertEquals(OFChannelHandler.ChannelState.MASTER, - handler.getStateForTesting()); - - // try to set master role again. should be a no-op - setupSwitchRoleChangeUnsupported(xid, Role.MASTER); - - assertEquals(OFChannelHandler.ChannelState.MASTER, - handler.getStateForTesting()); - - setupSwitchRoleChangeUnsupported(xid, Role.SLAVE); - //switch does not support role message. there is no role set - assertEquals(OFChannelHandler.ChannelState.MASTER, - handler.getStateForTesting()); - - } - - /** - * Move the channel to MASTER state. - * Expects that the channel is in MASTER or SLAVE state. - * - */ - public void changeRoleToMasterWithRequest() throws Exception { - int xid = 4242; - - assertTrue("This method can only be called when handler is in " + - "MASTER or SLAVE role", handler.isHandshakeComplete()); - - reset(swImplBase); - reset(controller); - // Set the role - setupSwitchSendRoleRequestAndVerify(true, xid, Role.MASTER); - - // prepare mocks and inject the role reply message - - reset(controller); - expect(controller.addActivatedMasterSwitch(1000, swImplBase)) - .andReturn(true).once(); - OFMessage reply = getRoleReply(xid, Role.MASTER); - - // sendMessageToHandler will verify and rest controller mock - - OFStatsReply sr = createDescriptionStatsReply(); - setupMessageEvent(Collections.singletonList(reply)); - - // mock controller - reset(controller); - reset(swImplBase); - - expect(controller.getOFSwitchInstance((OFDescStatsReply) sr, ofVersion)) - .andReturn(swImplBase).anyTimes(); - - expect(controller.getDebugCounter()) - .andReturn(debugCounterService).anyTimes(); - controller.transitionToMasterSwitch(1000); - expectLastCall().once(); - - replay(controller); - - expect(swImplBase.getStringId()) - .andReturn(null).anyTimes(); - expect(swImplBase.getRole()).andReturn(Role.EQUAL).atLeastOnce(); - expect(swImplBase.getNextTransactionId()) - .andReturn(xid).anyTimes(); - expect(swImplBase.getId()) - .andReturn(1000L).once(); - - swImplBase.setRole(Role.MASTER); - expectLastCall().once(); - replay(swImplBase); - - // send the description stats reply - handler.messageReceived(ctx, messageEvent); - - assertEquals(OFChannelHandler.ChannelState.MASTER, - handler.getStateForTesting()); - } - - /** - * Move the channel to SLAVE state. - * Expects that the channel is in MASTER or SLAVE state. - * - */ - public void changeRoleToSlaveWithRequest() throws Exception { - int xid = 2323; - - assertTrue("This method can only be called when handler is in " + - "MASTER or SLAVE role", handler.isHandshakeComplete()); - - // Set the role - reset(controller); - reset(swImplBase); - - swImplBase.write(capture(writeCapture)); - expectLastCall().anyTimes(); - - expect(swImplBase.getNextTransactionId()) - .andReturn(xid).anyTimes(); - - - if (ofVersion == OFVersion.OF_10) { - expect(swImplBase.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) - .andReturn(true).once(); - - swImplBase.write(capture(writeCapture)); - expectLastCall().anyTimes(); - } - replay(swImplBase); - - handler.sendRoleRequest(Role.SLAVE, RoleRecvStatus.MATCHED_SET_ROLE); - - List msgs = getMessagesFromCapture(); - assertEquals(1, msgs.size()); - verifyNiciraMessage((OFExperimenter) msgs.get(0)); - - - OFMessage reply = getRoleReply(xid, Role.SLAVE); - OFStatsReply sr = createDescriptionStatsReply(); - setupMessageEvent(Collections.singletonList(reply)); - - // mock controller - reset(controller); - reset(swImplBase); - - controller.transitionToEqualSwitch(1000); - expectLastCall().once(); - expect(controller.getOFSwitchInstance((OFDescStatsReply) sr, ofVersion)) - .andReturn(swImplBase).anyTimes(); - - expect(controller.getDebugCounter()) - .andReturn(debugCounterService).anyTimes(); - - replay(controller); - - expect(swImplBase.getStringId()) - .andReturn(null).anyTimes(); - expect(swImplBase.getRole()).andReturn(Role.MASTER).atLeastOnce(); - expect(swImplBase.getNextTransactionId()) - .andReturn(xid).anyTimes(); - - // prepare mocks and inject the role reply message - swImplBase.setRole(Role.SLAVE); - expectLastCall().once(); - expect(swImplBase.getId()) - .andReturn(1000L).once(); - replay(swImplBase); - - handler.messageReceived(ctx, messageEvent); - - assertEquals(OFChannelHandler.ChannelState.EQUAL, - handler.getStateForTesting()); - } - - @Test - public void testMultiRoleChange1() throws Exception { - moveToMasterWithHandshakeComplete(); - changeRoleToMasterWithRequest(); - changeRoleToSlaveWithRequest(); - changeRoleToSlaveWithRequest(); - changeRoleToMasterWithRequest(); - changeRoleToSlaveWithRequest(); - } - - @Test - public void testMultiRoleChange2() throws Exception { - moveToSlaveWithHandshakeComplete(); - changeRoleToMasterWithRequest(); - changeRoleToSlaveWithRequest(); - changeRoleToSlaveWithRequest(); - changeRoleToMasterWithRequest(); - changeRoleToSlaveWithRequest(); - } - - /** - * Start from scratch and reply with an unexpected error to the role - * change request. - * Builds on doMoveToWaitInitialRole() - * adds testing for WAIT_INITAL_ROLE state - */ - /* TBD - @Test - public void testInitialRoleChangeOtherError() throws Exception { - int xid = 4343; - // first, move us to WAIT_INITIAL_ROLE_STATE - moveToWaitInitialRole(); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - reset(swImplBase); - // Set the role - setupSwitchSendRoleRequestAndVerify(true, xid, Role.MASTER); - assertEquals(OFChannelHandler.ChannelState.WAIT_INITIAL_ROLE, - handler.getStateForTesting()); - - - // FIXME: shouldn't use ordinal(), but OFError is broken - - OFMessage err = factory.errorMsgs().buildBadActionErrorMsg() - .setCode(OFBadActionCode.BAD_LEN) - .setXid(2000) - .build(); - verify(swImplBase); - reset(swImplBase); - replay(swImplBase); - sendMessageToHandlerWithControllerReset(Collections.singletonList(err)); - - verifyExceptionCaptured(SwitchStateException.class); - } - */ - /** - * Test dispatch of messages while in MASTER role. - */ - @Test - public void testMessageDispatchMaster() throws Exception { - - moveToMasterWithHandshakeComplete(); - - // Send packet in. expect dispatch - OFPacketIn pi = (OFPacketIn) - buildOFMessage(OFType.PACKET_IN); - setupMessageEvent(Collections.singletonList(pi)); - - reset(swImplBase); - swImplBase.handleMessage(pi); - expectLastCall().once(); - replay(swImplBase); - // send the description stats reply - handler.messageReceived(ctx, messageEvent); - - assertEquals(OFChannelHandler.ChannelState.MASTER, - handler.getStateForTesting()); - - verify(controller); - // TODO: many more to go - } - - /** - * Test port status message handling while MASTER. - * - */ - /* Patrick: TBD - @Test - public void testPortStatusMessageMaster() throws Exception { - long dpid = featuresReply.getDatapathId().getLong(); - testInitialMoveToMasterWithRole(); - List ports = new ArrayList(); - // A dummy port. - OFPortDesc p = factory.buildPortDesc() - .setName("Eth1") - .setPortNo(OFPort.ofInt(1)) - .build(); - ports.add(p); - - p.setName("Port1"); - p.setPortNumber((short)1); - - OFPortStatus ps = (OFPortStatus)buildOFMessage(OFType.PORT_STATUS); - ps.setDesc(p); - - // The events we expect sw.handlePortStatus to return - // We'll just use the same list for all valid OFPortReasons and add - // arbitrary events for arbitrary ports that are not necessarily - // related to the port status message. Our goal - // here is not to return the correct set of events but the make sure - // that a) sw.handlePortStatus is called - // b) the list of events sw.handlePortStatus returns is sent - // as IOFSwitchListener notifications. - OrderedCollection events = - new LinkedHashSetWrapper(); - ImmutablePort p1 = ImmutablePort.create("eth1", (short)1); - ImmutablePort p2 = ImmutablePort.create("eth2", (short)2); - ImmutablePort p3 = ImmutablePort.create("eth3", (short)3); - ImmutablePort p4 = ImmutablePort.create("eth4", (short)4); - ImmutablePort p5 = ImmutablePort.create("eth5", (short)5); - events.add(new PortChangeEvent(p1, PortChangeType.ADD)); - events.add(new PortChangeEvent(p2, PortChangeType.DELETE)); - events.add(new PortChangeEvent(p3, PortChangeType.UP)); - events.add(new PortChangeEvent(p4, PortChangeType.DOWN)); - events.add(new PortChangeEvent(p5, PortChangeType.OTHER_UPDATE)); - - - for (OFPortReason reason: OFPortReason.values()) { - ps.setReason(reason.getReasonCode()); - - reset(sw); - expect(sw.getId()).andReturn(dpid).anyTimes(); - - expect(sw.processOFPortStatus(ps)).andReturn(events).once(); - replay(sw); - - reset(controller); - controller.notifyPortChanged(sw, p1, PortChangeType.ADD); - controller.notifyPortChanged(sw, p2, PortChangeType.DELETE); - controller.notifyPortChanged(sw, p3, PortChangeType.UP); - controller.notifyPortChanged(sw, p4, PortChangeType.DOWN); - controller.notifyPortChanged(sw, p5, PortChangeType.OTHER_UPDATE); - sendMessageToHandlerNoControllerReset( - Collections.singletonList(ps)); - verify(sw); - verify(controller); - } - } - - */ - /** - * Build an OF message. - * @throws IOException - */ - private OFMessage buildOFMessage(OFType t) throws IOException { - OFMessage m = null; - switch (t) { - - case HELLO: - // The OF protocol requires us to start things off by sending the highest - // version of the protocol supported. - - // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04) - // see Sec. 7.5.1 of the OF1.3.4 spec - if (ofVersion == OFVersion.OF_13) { - U32 bitmap = U32.ofRaw(0x00000012); - OFHelloElem hem = factory13.buildHelloElemVersionbitmap() - .setBitmaps(Collections.singletonList(bitmap)) - .build(); - m = factory13.buildHello() - .setXid(2000) - .setElements(Collections.singletonList(hem)) - .build(); - } else { - m = factory10.buildHello() - .setXid(2000) - .build(); - } - break; - case FEATURES_REQUEST: - m = factory.buildFeaturesRequest() - .setXid(2000) - .build(); - break; - case FEATURES_REPLY: - - m = factory.buildFeaturesReply() - .setDatapathId(DatapathId.of(1000L)) - .setXid(2000) - .build(); - break; - case SET_CONFIG: - m = factory.buildSetConfig() - .setMissSendLen((short) 0xffff) - .setXid(2000) - .build(); - break; - case BARRIER_REQUEST: - m = factory.buildBarrierRequest() - .setXid(2000) - .build(); - break; - case GET_CONFIG_REQUEST: - m = factory.buildGetConfigRequest() - .setXid(2000) - .build(); - break; - case GET_CONFIG_REPLY: - m = factory.buildGetConfigReply() - .setMissSendLen((short) 0xffff) - .setXid(2000) - .build(); - break; - case STATS_REQUEST: - break; - case STATS_REPLY: - m = factory.buildDescStatsReply() - .setDpDesc("Datapath Description") - .setHwDesc("Hardware Secription") - .setMfrDesc("Manufacturer Desctiption") - .setSerialNum("Serial Number") - .setSwDesc("Software Desription") - .build(); - break; - case ECHO_REQUEST: - m = factory.buildEchoRequest() - .setXid(2000) - .build(); - break; - case FLOW_REMOVED: - break; - - case PACKET_IN: - m = factory.buildPacketIn() - .setReason(OFPacketInReason.NO_MATCH) - .setTotalLen(1500) - .setXid(2000) - .build(); - break; - case PORT_STATUS: - m = factory.buildPortStatus() - .setXid(2000) - .build(); - break; - - default: - m = factory.buildFeaturesRequest() - .setXid(2000) - .build(); - break; - } - - return (m); - } -}