/** * Handle a OFPortStatus delete message for the given port. * Updates the internal port maps/lists of this switch and returns * the PortChangeEvents caused by the delete. If the given port * exists as it, it will be deleted. If the name<->number for the * given port is inconsistent with the ports stored by this switch * the method will delete all ports with the number or name of the * given port. * * This method will increment error/warn counters and log * * @param delPort the port from the port status message that should * be deleted. * @return ordered collection of port changes applied to this switch */ private OrderedCollection<PortChangeEvent> handlePortStatusDelete(OFPortDesc delPort) { OrderedCollection<PortChangeEvent> events = new LinkedHashSetWrapper<PortChangeEvent>(); lock.writeLock().lock(); try { Map<OFPort,OFPortDesc> newPortByNumber = new HashMap<OFPort, OFPortDesc>(portsByNumber); OFPortDesc prevPort = portsByNumber.get(delPort.getPortNo()); if (prevPort == null) { // so such port. Do we have a port with the name? prevPort = portsByName.get(delPort.getName()); if (prevPort != null) { newPortByNumber.remove(prevPort.getPortNo()); events.add(new PortChangeEvent(prevPort, PortChangeType.DELETE)); } } else if (prevPort.getName().equals(delPort.getName())) { // port exists with consistent name-number mapping newPortByNumber.remove(delPort.getPortNo()); events.add(new PortChangeEvent(delPort, PortChangeType.DELETE)); } else { // port with same number exists but its name differs. This // is weird. The best we can do is to delete the existing // port(s) that have delPort's name and number. newPortByNumber.remove(delPort.getPortNo()); events.add(new PortChangeEvent(prevPort, PortChangeType.DELETE)); // is there another port that has delPort's name? prevPort = portsByName.get(delPort.getName().toLowerCase()); if (prevPort != null) { newPortByNumber.remove(prevPort.getPortNo()); events.add(new PortChangeEvent(prevPort, PortChangeType.DELETE)); } } updatePortsWithNewPortsByNumber(newPortByNumber); return events; } finally { lock.writeLock().unlock(); } }
/** * Handle a OFPortStatus message, update the internal data structures * that store ports and return the list of OFChangeEvents. * * This method will increment error/warn counters and log * * @param ps * @return */ @SuppressFBWarnings(value="SF_SWITCH_FALLTHROUGH") public OrderedCollection<PortChangeEvent> handlePortStatusMessage(OFPortStatus ps) { if (ps == null) { throw new NullPointerException("OFPortStatus message must " + "not be null"); } lock.writeLock().lock(); try { OFPortDesc port = ps.getDesc(); OFPortReason reason = ps.getReason(); if (reason == null) { throw new IllegalArgumentException("Unknown PortStatus " + "reason code " + ps.getReason()); } if (log.isDebugEnabled()) { log.debug("Handling OFPortStatus: {} for {}", reason, String.format("%s (%d)", port.getName(), port.getPortNo().getPortNumber())); } if (reason == OFPortReason.DELETE) return handlePortStatusDelete(port); // We handle ADD and MODIFY the same way. Since OpenFlow // doesn't specify what uniquely identifies a port the // notion of ADD vs. MODIFY can also be hazy. So we just // compare the new port to the existing ones. Map<OFPort,OFPortDesc> newPortByNumber = new HashMap<OFPort, OFPortDesc>(portsByNumber); OrderedCollection<PortChangeEvent> events = getSinglePortChanges(port); for (PortChangeEvent e: events) { switch(e.type) { case DELETE: newPortByNumber.remove(e.port.getPortNo()); break; case ADD: if (reason != OFPortReason.ADD) { // weird case } // fall through case DOWN: case OTHER_UPDATE: case UP: // update or add the port in the map newPortByNumber.put(e.port.getPortNo(), e.port); break; } } updatePortsWithNewPortsByNumber(newPortByNumber); return events; } finally { lock.writeLock().unlock(); } }
@Override public OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps) { return portManager.handlePortStatusMessage(ps); }
@Override public OrderedCollection<PortChangeEvent> comparePorts(Collection<OFPortDesc> ports) { return portManager.comparePorts(ports); }
@Override public OrderedCollection<PortChangeEvent> setPorts(Collection<OFPortDesc> ports) { return portManager.updatePorts(ports); }
/** * Test port status message handling while MASTER * */ @Test public void testPortStatusMessageMaster() throws Exception { DatapathId dpid = featuresReply.getDatapathId(); testInitialMoveToMasterWithRole(); OFPortDesc portDesc = factory.buildPortDesc() .setName("Port1") .setPortNo(OFPort.of(1)) .build(); OFPortStatus.Builder portStatusBuilder = factory.buildPortStatus() .setDesc(portDesc); // 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<PortChangeEvent> events = new LinkedHashSetWrapper<PortChangeEvent>(); OFPortDesc.Builder pb = factory.buildPortDesc(); OFPortDesc p1 = pb.setName("eth1").setPortNo(OFPort.of(1)).build(); OFPortDesc p2 = pb.setName("eth2").setPortNo(OFPort.of(2)).build(); OFPortDesc p3 = pb.setName("eth3").setPortNo(OFPort.of(3)).build(); OFPortDesc p4 = pb.setName("eth4").setPortNo(OFPort.of(4)).build(); OFPortDesc p5 = pb.setName("eth5").setPortNo(OFPort.of(5)).build(); 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()) { OFPortStatus portStatus = portStatusBuilder.setReason(reason).build(); reset(sw); expect(sw.getId()).andReturn(dpid).anyTimes(); expect(sw.processOFPortStatus(portStatus)).andReturn(events).once(); replay(sw); reset(switchManager); switchManager.notifyPortChanged(sw, p1, PortChangeType.ADD); switchManager.notifyPortChanged(sw, p2, PortChangeType.DELETE); switchManager.notifyPortChanged(sw, p3, PortChangeType.UP); switchManager.notifyPortChanged(sw, p4, PortChangeType.DOWN); switchManager.notifyPortChanged(sw, p5, PortChangeType.OTHER_UPDATE); replay(switchManager); switchHandler.processOFMessage(portStatus); verify(sw); } }