예제 #1
0
    /**
     * Given a new or modified port newPort, returns the list of PortChangeEvents to "transform" the
     * current ports stored by this switch to include / represent the new port. The ports stored by
     * this switch are <b>NOT</b> updated.
     *
     * <p>This method acquires the readlock and is thread-safe by itself. Most callers will need to
     * acquire the write lock before calling this method though (if the caller wants to update the
     * ports stored by this switch)
     *
     * @param newPort the new or modified port.
     * @return the list of changes
     */
    public OrderedCollection<PortChangeEvent> getSinglePortChanges(OFPortDesc newPort) {
      lock.readLock().lock();
      try {
        OrderedCollection<PortChangeEvent> events = new LinkedHashSetWrapper<PortChangeEvent>();
        // Check if we have a port by the same number in our
        // old map.
        OFPortDesc prevPort = portsByNumber.get(newPort.getPortNo());
        if (newPort.equals(prevPort)) {
          // nothing has changed
          return events;
        }

        if (prevPort != null && prevPort.getName().equals(newPort.getName())) {
          // A simple modify of a exiting port
          // A previous port with this number exists and it's name
          // also matches the new port. Find the differences
          if ((!prevPort.getState().contains(OFPortState.LINK_DOWN)
                  && !prevPort.getConfig().contains(OFPortConfig.PORT_DOWN))
              && (newPort.getState().contains(OFPortState.LINK_DOWN)
                  || newPort.getConfig().contains(OFPortConfig.PORT_DOWN))) {
            events.add(new PortChangeEvent(newPort, PortChangeType.DOWN));
          } else if ((prevPort.getState().contains(OFPortState.LINK_DOWN)
                  || prevPort.getConfig().contains(OFPortConfig.PORT_DOWN))
              && (!newPort.getState().contains(OFPortState.LINK_DOWN)
                  && !newPort.getConfig().contains(OFPortConfig.PORT_DOWN))) {
            events.add(new PortChangeEvent(newPort, PortChangeType.UP));
          } else {
            events.add(new PortChangeEvent(newPort, PortChangeType.OTHER_UPDATE));
          }
          return events;
        }

        if (prevPort != null) {
          // There exists a previous port with the same port
          // number but the port name is different (otherwise we would
          // never have gotten here)
          // Remove the port. Name-number mapping(s) have changed
          events.add(new PortChangeEvent(prevPort, PortChangeType.DELETE));
        }

        // We now need to check if there exists a previous port sharing
        // the same name as the new/updated port.
        prevPort = portsByName.get(newPort.getName().toLowerCase());
        if (prevPort != null) {
          // There exists a previous port with the same port
          // name but the port number is different (otherwise we
          // never have gotten here).
          // Remove the port. Name-number mapping(s) have changed
          events.add(new PortChangeEvent(prevPort, PortChangeType.DELETE));
        }

        // We always need to add the new port. Either no previous port
        // existed or we just deleted previous ports with inconsistent
        // name-number mappings
        events.add(new PortChangeEvent(newPort, PortChangeType.ADD));
        return events;
      } finally {
        lock.readLock().unlock();
      }
    }
예제 #2
0
    /**
     * Handle a OFPortStatus message, update the internal data structures that store ports and
     * return the list of OFChangeEvents.
     *
     * <p>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();
      }
    }
예제 #3
0
 /**
  * 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.
  *
  * <p>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();
   }
 }
예제 #4
0
    /**
     * Set the internal data structure storing this switch's port to the ports specified by
     * newPortsByNumber
     *
     * <p>CALLER MUST HOLD WRITELOCK
     *
     * @param newPortsByNumber
     * @throws IllegaalStateException if called without holding the writelock
     */
    private void updatePortsWithNewPortsByNumber(Map<OFPort, OFPortDesc> newPortsByNumber) {
      if (!lock.writeLock().isHeldByCurrentThread()) {
        throw new IllegalStateException("Method called without " + "holding writeLock");
      }
      Map<String, OFPortDesc> newPortsByName = new HashMap<String, OFPortDesc>();
      List<OFPortDesc> newPortList = new ArrayList<OFPortDesc>();
      List<OFPortDesc> newEnabledPortList = new ArrayList<OFPortDesc>();
      List<OFPort> newEnabledPortNumbers = new ArrayList<OFPort>();

      for (OFPortDesc p : newPortsByNumber.values()) {
        newPortList.add(p);
        newPortsByName.put(p.getName().toLowerCase(), p);
        if (!p.getState().contains(OFPortState.LINK_DOWN)
            && !p.getConfig().contains(OFPortConfig.PORT_DOWN)) {
          newEnabledPortList.add(p);
          newEnabledPortNumbers.add(p.getPortNo());
        }
      }
      portsByName = Collections.unmodifiableMap(newPortsByName);
      portsByNumber = Collections.unmodifiableMap(newPortsByNumber);
      enabledPortList = Collections.unmodifiableList(newEnabledPortList);
      enabledPortNumbers = Collections.unmodifiableList(newEnabledPortNumbers);
      portList = Collections.unmodifiableList(newPortList);
    }
예제 #5
0
    /**
     * Compare the current ports stored in this switch instance with the new port list given and
     * return the differences in the form of PortChangeEvents. If the doUpdate flag is true,
     * newPortList will replace the current list of this switch (and update the port maps)
     *
     * <p>Implementation note: Since this method can optionally modify the current ports and since
     * it's not possible to upgrade a read-lock to a write-lock we need to hold the write-lock for
     * the entire operation. If this becomes a problem and if compares() are common we can consider
     * splitting in two methods but this requires lots of code duplication
     *
     * @param newPorts the list of new ports.
     * @param doUpdate If true the newPortList will replace the current port list for this switch.
     *     If false this switch will not be changed.
     * @return The list of differences between the current ports and newPorts
     * @throws NullPointerException if newPortsList is null
     * @throws IllegalArgumentException if either port names or port numbers are duplicated in the
     *     newPortsList.
     */
    private OrderedCollection<PortChangeEvent> compareAndUpdatePorts(
        Collection<OFPortDesc> newPorts, boolean doUpdate) {
      if (newPorts == null) {
        throw new NullPointerException("newPortsList must not be null");
      }
      lock.writeLock().lock();
      try {
        OrderedCollection<PortChangeEvent> events = new LinkedHashSetWrapper<PortChangeEvent>();

        Map<OFPort, OFPortDesc> newPortsByNumber = new HashMap<OFPort, OFPortDesc>();
        Map<String, OFPortDesc> newPortsByName = new HashMap<String, OFPortDesc>();
        List<OFPortDesc> newEnabledPortList = new ArrayList<OFPortDesc>();
        List<OFPort> newEnabledPortNumbers = new ArrayList<OFPort>();
        List<OFPortDesc> newPortsList = new ArrayList<OFPortDesc>(newPorts);

        for (OFPortDesc p : newPortsList) {
          if (p == null) {
            throw new NullPointerException("portList must not " + "contain null values");
          }

          // Add the port to the new maps and lists and check
          // that every port is unique
          OFPortDesc duplicatePort;
          duplicatePort = newPortsByNumber.put(p.getPortNo(), p);
          if (duplicatePort != null) {
            String msg =
                String.format(
                    "Cannot have two ports " + "with the same number: %s <-> %s",
                    String.format("%s (%d)", p.getName(), p.getPortNo().getPortNumber()),
                    String.format(
                        "%s (%d)",
                        duplicatePort.getName(), duplicatePort.getPortNo().getPortNumber()));
            throw new IllegalArgumentException(msg);
          }
          duplicatePort = newPortsByName.put(p.getName().toLowerCase(), p);
          if (duplicatePort != null) {
            String msg =
                String.format(
                    "Cannot have two ports " + "with the same name: %s <-> %s",
                    String.format("%s (%d)", p.getName(), p.getPortNo().getPortNumber()),
                    String.format(
                        "%s (%d)",
                        duplicatePort.getName(), duplicatePort.getPortNo().getPortNumber()));
            throw new IllegalArgumentException(msg);
          }
          // Enabled = not down admin (config) or phys (state)
          if (!p.getConfig().contains(OFPortConfig.PORT_DOWN)
              && !p.getState().contains(OFPortState.LINK_DOWN)) {
            newEnabledPortList.add(p);
            newEnabledPortNumbers.add(p.getPortNo());
          }

          // get changes
          events.addAll(getSinglePortChanges(p));
        }
        // find deleted ports
        // We need to do this after looping through all the new ports
        // to we can handle changed name<->number mappings correctly
        // We could pull it into the loop of we address this but
        // it's probably not worth it
        for (OFPortDesc oldPort : this.portList) {
          if (!newPortsByNumber.containsKey(oldPort.getPortNo())) {
            PortChangeEvent ev = new PortChangeEvent(oldPort, PortChangeType.DELETE);
            events.add(ev);
          }
        }

        if (doUpdate) {
          portsByName = Collections.unmodifiableMap(newPortsByName);
          portsByNumber = Collections.unmodifiableMap(newPortsByNumber);
          enabledPortList = Collections.unmodifiableList(newEnabledPortList);
          enabledPortNumbers = Collections.unmodifiableList(newEnabledPortNumbers);
          portList = Collections.unmodifiableList(newPortsList);
        }
        return events;
      } finally {
        lock.writeLock().unlock();
      }
    }