Example #1
0
  private void updateOMSPorts(int numChls, ConnectPoint srcCp, ConnectPoint dstCp) {
    // round down to largest slot that allows numChl channels to fit into C band range
    ChannelSpacing chl = null;
    Frequency perChl = TOTAL.floorDivision(numChls);
    for (int i = 0; i < ChannelSpacing.values().length; i++) {
      Frequency val = ChannelSpacing.values()[i].frequency();
      if (val.isLessThan(perChl)) {
        chl = ChannelSpacing.values()[i];
        break;
      }
    }
    if (chl == null) {
      chl = ChannelSpacing.CHL_6P25GHZ;
    }

    // if true, there was less channels than can be tightly packed.
    Frequency grid = chl.frequency();
    // say Linc's 1st slot starts at CENTER and goes up from there.
    Frequency min = CENTER.add(grid);
    Frequency max = CENTER.add(grid.multiply(numChls));

    PortDescription srcPortDesc = new OmsPortDescription(srcCp.port(), true, min, max, grid);
    PortDescription dstPortDesc = new OmsPortDescription(dstCp.port(), true, min, max, grid);
    descriptions.put(srcCp, srcPortDesc);
    descriptions.put(dstCp, dstPortDesc);
    deviceProviderService.portStatusChanged(srcCp.deviceId(), srcPortDesc);
    deviceProviderService.portStatusChanged(dstCp.deviceId(), dstPortDesc);
  }
Example #2
0
 // uses 'bandwidth' annotation to determine the channel spacing.
 private void updateOchPort(double bw, ConnectPoint srcCp, ConnectPoint dstCp) {
   Device src = deviceService.getDevice(srcCp.deviceId());
   Device dst = deviceService.getDevice(dstCp.deviceId());
   // bandwidth in MHz (assuming Hz - linc is not clear if that or Mb).
   Frequency spacing = Frequency.ofMHz(bw);
   // channel bandwidth is smaller than smallest standard channel spacing.
   ChannelSpacing chsp = null;
   if (spacing.compareTo(ChannelSpacing.CHL_6P25GHZ.frequency()) <= 0) {
     chsp = ChannelSpacing.CHL_6P25GHZ;
   }
   for (int i = 1; i < ChannelSpacing.values().length; i++) {
     Frequency val = ChannelSpacing.values()[i].frequency();
     // pick the next highest or equal channel interval.
     if (val.isLessThan(spacing)) {
       chsp = ChannelSpacing.values()[i - 1];
       break;
     }
   }
   if (chsp == null) {
     log.warn("Invalid channel spacing ({}), can't configure port(s)", spacing);
     return;
   }
   OchSignal signal = new OchSignal(GridType.DWDM, chsp, 1, 1);
   if (src.type() == Device.Type.ROADM) {
     PortDescription portDesc =
         new OchPortDescription(srcCp.port(), true, OduSignalType.ODU4, true, signal);
     descriptions.put(srcCp, portDesc);
     deviceProviderService.portStatusChanged(srcCp.deviceId(), portDesc);
   }
   if (dst.type() == Device.Type.ROADM) {
     PortDescription portDesc =
         new OchPortDescription(dstCp.port(), true, OduSignalType.ODU4, true, signal);
     descriptions.put(dstCp, portDesc);
     deviceProviderService.portStatusChanged(dstCp.deviceId(), portDesc);
   }
 }
Example #3
0
/** Provider of devices and links parsed from a JSON configuration structure. */
class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {

  private final Logger log = LoggerFactory.getLogger(getClass());

  private static final ProviderId PID = new ProviderId("cfg", "org.onosproject.rest", true);

  private static final String UNKNOWN = "unknown";

  private static final Frequency CENTER = Frequency.ofTHz(193.1);
  // C-band has 4.4 THz (4,400 GHz) total bandwidth
  private static final Frequency TOTAL = Frequency.ofTHz(4.4);

  private CountDownLatch deviceLatch;

  private final JsonNode cfg;
  private final DeviceService deviceService;

  private final DeviceProviderRegistry deviceProviderRegistry;
  private final LinkProviderRegistry linkProviderRegistry;
  private final HostProviderRegistry hostProviderRegistry;

  private DeviceProviderService deviceProviderService;
  private LinkProviderService linkProviderService;
  private HostProviderService hostProviderService;

  private DeviceListener deviceEventCounter = new DeviceEventCounter();
  private List<ConnectPoint> connectPoints = Lists.newArrayList();
  private Map<ConnectPoint, PortDescription> descriptions = Maps.newHashMap();

  /**
   * Creates a new configuration provider.
   *
   * @param cfg JSON configuration
   * @param deviceService device service
   * @param deviceProviderRegistry device provider registry
   * @param linkProviderRegistry link provider registry
   * @param hostProviderRegistry host provider registry
   */
  ConfigProvider(
      JsonNode cfg,
      DeviceService deviceService,
      DeviceProviderRegistry deviceProviderRegistry,
      LinkProviderRegistry linkProviderRegistry,
      HostProviderRegistry hostProviderRegistry) {
    this.cfg = checkNotNull(cfg, "Configuration cannot be null");
    this.deviceService = checkNotNull(deviceService, "Device service cannot be null");
    this.deviceProviderRegistry =
        checkNotNull(deviceProviderRegistry, "Device provider registry cannot be null");
    this.linkProviderRegistry =
        checkNotNull(linkProviderRegistry, "Link provider registry cannot be null");
    this.hostProviderRegistry =
        checkNotNull(hostProviderRegistry, "Host provider registry cannot be null");
  }

  /** Parses the given JSON and provides links as configured. */
  void parse() {
    try {
      register();
      parseDevices();
      parseLinks();
      parseHosts();
      addMissingPorts();
    } finally {
      unregister();
    }
  }

  private void register() {
    deviceProviderService = deviceProviderRegistry.register(this);
    linkProviderService = linkProviderRegistry.register(this);
    hostProviderService = hostProviderRegistry.register(this);
  }

  private void unregister() {
    deviceProviderRegistry.unregister(this);
    linkProviderRegistry.unregister(this);
    hostProviderRegistry.unregister(this);
  }

  // Parses the given JSON and provides devices.
  private void parseDevices() {
    try {
      JsonNode nodes = cfg.get("devices");
      if (nodes != null) {
        prepareForDeviceEvents(nodes.size());
        for (JsonNode node : nodes) {
          parseDevice(node);

          // FIXME: hack to make sure device attributes take
          // This will be fixed when GossipDeviceStore uses ECM
          parseDevice(node);
        }
      }
    } finally {
      waitForDeviceEvents();
    }
  }

  // Parses the given node with device data and supplies the device.
  private void parseDevice(JsonNode node) {
    URI uri = URI.create(get(node, "uri"));
    Device.Type type = Device.Type.valueOf(get(node, "type", "SWITCH"));
    String mfr = get(node, "mfr", UNKNOWN);
    String hw = get(node, "hw", UNKNOWN);
    String sw = get(node, "sw", UNKNOWN);
    String serial = get(node, "serial", UNKNOWN);
    ChassisId cid = new ChassisId(get(node, "mac", "000000000000"));
    SparseAnnotations annotations = annotations(node.get("annotations"));

    DeviceDescription desc =
        new DefaultDeviceDescription(uri, type, mfr, hw, sw, serial, cid, annotations);
    DeviceId deviceId = deviceId(uri);
    deviceProviderService.deviceConnected(deviceId, desc);

    JsonNode ports = node.get("ports");
    if (ports != null) {
      parsePorts(deviceId, ports);
    }
  }

  // Parses the given node with list of device ports.
  private void parsePorts(DeviceId deviceId, JsonNode nodes) {
    List<PortDescription> ports = new ArrayList<>();
    for (JsonNode node : nodes) {
      ports.add(parsePort(deviceId, node));
    }
    deviceProviderService.updatePorts(deviceId, ports);
  }

  // Parses the given node with port information.
  private PortDescription parsePort(DeviceId deviceId, JsonNode node) {
    Port.Type type = Port.Type.valueOf(node.path("type").asText("COPPER"));
    // TL1-based ports have a name
    PortNumber port = null;
    if (node.has("name")) {
      for (Port p : deviceService.getPorts(deviceId)) {
        if (p.number().name().equals(node.get("name").asText())) {
          port = p.number();
          break;
        }
      }
    } else {
      port = portNumber(node.path("port").asLong(0));
    }

    if (port == null) {
      log.error("Cannot find port given in node {}", node);
      return null;
    }

    String portName = Strings.emptyToNull(port.name());
    SparseAnnotations annotations = null;
    if (portName != null) {
      annotations = DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, portName).build();
    }
    switch (type) {
      case COPPER:
        return new DefaultPortDescription(
            port,
            node.path("enabled").asBoolean(true),
            type,
            node.path("speed").asLong(1_000),
            annotations);
      case FIBER:
        // Currently, assume OMS when FIBER. Provide sane defaults.
        annotations = annotations(node.get("annotations"));
        return new OmsPortDescription(
            port,
            node.path("enabled").asBoolean(true),
            CENTER,
            CENTER.add(TOTAL),
            Frequency.ofGHz(100),
            annotations);
      case ODUCLT:
        annotations = annotations(node.get("annotations"));
        OduCltPort oduCltPort = (OduCltPort) deviceService.getPort(deviceId, port);
        return new OduCltPortDescription(
            port, node.path("enabled").asBoolean(true), oduCltPort.signalType(), annotations);
      case OCH:
        annotations = annotations(node.get("annotations"));
        OchPort ochPort = (OchPort) deviceService.getPort(deviceId, port);
        return new OchPortDescription(
            port,
            node.path("enabled").asBoolean(true),
            ochPort.signalType(),
            ochPort.isTunable(),
            ochPort.lambda(),
            annotations);
      case OMS:
        annotations = annotations(node.get("annotations"));
        OmsPort omsPort = (OmsPort) deviceService.getPort(deviceId, port);
        return new OmsPortDescription(
            port,
            node.path("enabled").asBoolean(true),
            omsPort.minFrequency(),
            omsPort.maxFrequency(),
            omsPort.grid(),
            annotations);
      default:
        log.warn("{}: Unsupported Port Type");
    }
    return new DefaultPortDescription(
        port,
        node.path("enabled").asBoolean(true),
        type,
        node.path("speed").asLong(1_000),
        annotations);
  }

  // Parses the given JSON and provides links as configured.
  private void parseLinks() {
    JsonNode nodes = cfg.get("links");
    if (nodes != null) {
      for (JsonNode node : nodes) {
        parseLink(node, false);
        if (!node.has("halfplex")) {
          parseLink(node, true);
        }
      }
    }
  }

  // Parses the given node with link data and supplies the link.
  private void parseLink(JsonNode node, boolean reverse) {
    ConnectPoint src = connectPoint(get(node, "src"));
    ConnectPoint dst = connectPoint(get(node, "dst"));
    Link.Type type = Link.Type.valueOf(get(node, "type", "DIRECT"));
    SparseAnnotations annotations = annotations(node.get("annotations"));
    // take annotations to update optical ports with correct attributes.
    updatePorts(src, dst, annotations);
    DefaultLinkDescription desc =
        reverse
            ? new DefaultLinkDescription(dst, src, type, annotations)
            : new DefaultLinkDescription(src, dst, type, annotations);
    linkProviderService.linkDetected(desc);

    connectPoints.add(src);
    connectPoints.add(dst);
  }

  private void updatePorts(ConnectPoint src, ConnectPoint dst, SparseAnnotations annotations) {
    final String linkType = annotations.value("optical.type");
    if ("cross-connect".equals(linkType)) {
      String value = annotations.value("bandwidth").trim();
      try {
        double bw = Double.parseDouble(value);
        updateOchPort(bw, src, dst);
      } catch (NumberFormatException e) {
        log.warn("Invalid bandwidth ({}), can't configure port(s)", value);
        return;
      }
    } else if ("WDM".equals(linkType)) {
      String value = annotations.value("optical.waves").trim();
      try {
        int numChls = Integer.parseInt(value);
        updateOMSPorts(numChls, src, dst);
      } catch (NumberFormatException e) {
        log.warn("Invalid channel ({}), can't configure port(s)", value);
        return;
      }
    }
  }

  // uses 'bandwidth' annotation to determine the channel spacing.
  private void updateOchPort(double bw, ConnectPoint srcCp, ConnectPoint dstCp) {
    Device src = deviceService.getDevice(srcCp.deviceId());
    Device dst = deviceService.getDevice(dstCp.deviceId());
    // bandwidth in MHz (assuming Hz - linc is not clear if that or Mb).
    Frequency spacing = Frequency.ofMHz(bw);
    // channel bandwidth is smaller than smallest standard channel spacing.
    ChannelSpacing chsp = null;
    if (spacing.compareTo(ChannelSpacing.CHL_6P25GHZ.frequency()) <= 0) {
      chsp = ChannelSpacing.CHL_6P25GHZ;
    }
    for (int i = 1; i < ChannelSpacing.values().length; i++) {
      Frequency val = ChannelSpacing.values()[i].frequency();
      // pick the next highest or equal channel interval.
      if (val.isLessThan(spacing)) {
        chsp = ChannelSpacing.values()[i - 1];
        break;
      }
    }
    if (chsp == null) {
      log.warn("Invalid channel spacing ({}), can't configure port(s)", spacing);
      return;
    }
    OchSignal signal = new OchSignal(GridType.DWDM, chsp, 1, 1);
    if (src.type() == Device.Type.ROADM) {
      PortDescription portDesc =
          new OchPortDescription(srcCp.port(), true, OduSignalType.ODU4, true, signal);
      descriptions.put(srcCp, portDesc);
      deviceProviderService.portStatusChanged(srcCp.deviceId(), portDesc);
    }
    if (dst.type() == Device.Type.ROADM) {
      PortDescription portDesc =
          new OchPortDescription(dstCp.port(), true, OduSignalType.ODU4, true, signal);
      descriptions.put(dstCp, portDesc);
      deviceProviderService.portStatusChanged(dstCp.deviceId(), portDesc);
    }
  }

  private void updateOMSPorts(int numChls, ConnectPoint srcCp, ConnectPoint dstCp) {
    // round down to largest slot that allows numChl channels to fit into C band range
    ChannelSpacing chl = null;
    Frequency perChl = TOTAL.floorDivision(numChls);
    for (int i = 0; i < ChannelSpacing.values().length; i++) {
      Frequency val = ChannelSpacing.values()[i].frequency();
      if (val.isLessThan(perChl)) {
        chl = ChannelSpacing.values()[i];
        break;
      }
    }
    if (chl == null) {
      chl = ChannelSpacing.CHL_6P25GHZ;
    }

    // if true, there was less channels than can be tightly packed.
    Frequency grid = chl.frequency();
    // say Linc's 1st slot starts at CENTER and goes up from there.
    Frequency min = CENTER.add(grid);
    Frequency max = CENTER.add(grid.multiply(numChls));

    PortDescription srcPortDesc = new OmsPortDescription(srcCp.port(), true, min, max, grid);
    PortDescription dstPortDesc = new OmsPortDescription(dstCp.port(), true, min, max, grid);
    descriptions.put(srcCp, srcPortDesc);
    descriptions.put(dstCp, dstPortDesc);
    deviceProviderService.portStatusChanged(srcCp.deviceId(), srcPortDesc);
    deviceProviderService.portStatusChanged(dstCp.deviceId(), dstPortDesc);
  }

  // Parses the given JSON and provides hosts as configured.
  private void parseHosts() {
    try {
      JsonNode nodes = cfg.get("hosts");
      if (nodes != null) {
        for (JsonNode node : nodes) {
          parseHost(node);

          // FIXME: hack to make sure host attributes take
          // This will be fixed when GossipHostStore uses ECM
          parseHost(node);
        }
      }
    } finally {
      hostProviderRegistry.unregister(this);
    }
  }

  // Parses the given node with host data and supplies the host.
  private void parseHost(JsonNode node) {
    MacAddress mac = MacAddress.valueOf(get(node, "mac"));
    VlanId vlanId = VlanId.vlanId((short) node.get("vlan").asInt(VlanId.UNTAGGED));
    HostId hostId = HostId.hostId(mac, vlanId);
    SparseAnnotations annotations = annotations(node.get("annotations"));
    HostLocation location = new HostLocation(connectPoint(get(node, "location")), 0);

    String[] ipStrings = get(node, "ip", "").split(",");
    Set<IpAddress> ips = new HashSet<>();
    for (String ip : ipStrings) {
      ips.add(IpAddress.valueOf(ip.trim()));
    }

    DefaultHostDescription desc =
        new DefaultHostDescription(mac, vlanId, location, ips, annotations);
    hostProviderService.hostDetected(hostId, desc);

    connectPoints.add(location);
  }

  // Adds any missing device ports for configured links and host locations.
  private void addMissingPorts() {
    deviceService.getDevices().forEach(this::addMissingPorts);
  }

  // Adds any missing device ports.
  private void addMissingPorts(Device device) {
    try {
      List<Port> ports = deviceService.getPorts(device.id());
      Set<ConnectPoint> existing =
          ports
              .stream()
              .map(p -> new ConnectPoint(device.id(), p.number()))
              .collect(Collectors.toSet());
      Set<ConnectPoint> missing =
          connectPoints
              .stream()
              .filter(cp -> cp.deviceId().equals(device.id()))
              .filter(cp -> !existing.contains(cp))
              .collect(Collectors.toSet());

      if (!missing.isEmpty()) {
        List<PortDescription> newPorts =
            Stream.concat(
                    ports.stream().map(this::description), missing.stream().map(this::description))
                .collect(Collectors.toList());
        deviceProviderService.updatePorts(device.id(), newPorts);
      }
    } catch (IllegalArgumentException e) {
      log.warn("Error pushing ports: {}", e.getMessage());
    }
  }

  // Creates a port description from the specified port.
  private PortDescription description(Port p) {
    switch (p.type()) {
      case OMS:
        OmsPort op = (OmsPort) p;
        return new OmsPortDescription(
            op.number(), op.isEnabled(), op.minFrequency(), op.maxFrequency(), op.grid());
      case OCH:
        OchPort ochp = (OchPort) p;
        return new OchPortDescription(
            ochp.number(), ochp.isEnabled(), ochp.signalType(), ochp.isTunable(), ochp.lambda());
      case ODUCLT:
        OduCltPort odup = (OduCltPort) p;
        return new OduCltPortDescription(odup.number(), odup.isEnabled(), odup.signalType());
      default:
        return new DefaultPortDescription(p.number(), p.isEnabled(), p.type(), p.portSpeed());
    }
  }

  // Creates a port description from the specified connection point if none created earlier.
  private PortDescription description(ConnectPoint cp) {
    PortDescription saved = descriptions.get(cp);
    if (saved != null) {
      return saved;
    }
    Port p = deviceService.getPort(cp.deviceId(), cp.port());
    if (p == null) {
      return new DefaultPortDescription(cp.port(), true);
    }
    return description(p);
  }

  // Produces set of annotations from the given JSON node.
  private SparseAnnotations annotations(JsonNode node) {
    if (node == null) {
      return DefaultAnnotations.EMPTY;
    }

    DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
    Iterator<String> it = node.fieldNames();
    while (it.hasNext()) {
      String k = it.next();
      builder.set(k, node.get(k).asText());
    }
    return builder.build();
  }

  // Produces a connection point from the specified uri/port text.
  private ConnectPoint connectPoint(String text) {
    int i = text.lastIndexOf("/");
    String portName = text.substring(i + 1);
    DeviceId deviceId = deviceId(text.substring(0, i));

    for (Port port : deviceService.getPorts(deviceId)) {
      PortNumber pn = port.number();
      if (pn.name().equals(portName)) {
        return new ConnectPoint(deviceId, pn);
      }
    }

    long portNum;
    try {
      portNum = Long.parseLong(portName);
    } catch (NumberFormatException e) {
      portNum = 0;
    }

    return new ConnectPoint(deviceId, portNumber(portNum, portName));
  }

  // Returns string form of the named property in the given JSON object.
  private String get(JsonNode node, String name) {
    return node.path(name).asText();
  }

  // Returns string form of the named property in the given JSON object.
  private String get(JsonNode node, String name, String defaultValue) {
    return node.path(name).asText(defaultValue);
  }

  @Override
  public void roleChanged(DeviceId device, MastershipRole newRole) {
    deviceProviderService.receivedRoleReply(device, newRole, newRole);
  }

  @Override
  public void triggerProbe(DeviceId deviceId) {}

  @Override
  public void triggerProbe(Host host) {}

  @Override
  public ProviderId id() {
    return PID;
  }

  @Override
  public boolean isReachable(DeviceId device) {
    return true;
  }

  /**
   * Prepares to count device added/available/removed events.
   *
   * @param count number of events to count
   */
  protected void prepareForDeviceEvents(int count) {
    deviceLatch = new CountDownLatch(count);
    deviceService.addListener(deviceEventCounter);
  }

  /** Waits for all expected device added/available/removed events. */
  protected void waitForDeviceEvents() {
    try {
      deviceLatch.await(2, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
      log.warn("Device events did not arrive in time");
    }
    deviceService.removeListener(deviceEventCounter);
  }

  // Counts down number of device added/available/removed events.
  private class DeviceEventCounter implements DeviceListener {
    @Override
    public void event(DeviceEvent event) {
      DeviceEvent.Type type = event.type();
      if (type == DEVICE_ADDED || type == DEVICE_AVAILABILITY_CHANGED) {
        deviceLatch.countDown();
      }
    }
  }
}
Example #4
0
  // Parses the given node with port information.
  private PortDescription parsePort(DeviceId deviceId, JsonNode node) {
    Port.Type type = Port.Type.valueOf(node.path("type").asText("COPPER"));
    // TL1-based ports have a name
    PortNumber port = null;
    if (node.has("name")) {
      for (Port p : deviceService.getPorts(deviceId)) {
        if (p.number().name().equals(node.get("name").asText())) {
          port = p.number();
          break;
        }
      }
    } else {
      port = portNumber(node.path("port").asLong(0));
    }

    if (port == null) {
      log.error("Cannot find port given in node {}", node);
      return null;
    }

    String portName = Strings.emptyToNull(port.name());
    SparseAnnotations annotations = null;
    if (portName != null) {
      annotations = DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, portName).build();
    }
    switch (type) {
      case COPPER:
        return new DefaultPortDescription(
            port,
            node.path("enabled").asBoolean(true),
            type,
            node.path("speed").asLong(1_000),
            annotations);
      case FIBER:
        // Currently, assume OMS when FIBER. Provide sane defaults.
        annotations = annotations(node.get("annotations"));
        return new OmsPortDescription(
            port,
            node.path("enabled").asBoolean(true),
            CENTER,
            CENTER.add(TOTAL),
            Frequency.ofGHz(100),
            annotations);
      case ODUCLT:
        annotations = annotations(node.get("annotations"));
        OduCltPort oduCltPort = (OduCltPort) deviceService.getPort(deviceId, port);
        return new OduCltPortDescription(
            port, node.path("enabled").asBoolean(true), oduCltPort.signalType(), annotations);
      case OCH:
        annotations = annotations(node.get("annotations"));
        OchPort ochPort = (OchPort) deviceService.getPort(deviceId, port);
        return new OchPortDescription(
            port,
            node.path("enabled").asBoolean(true),
            ochPort.signalType(),
            ochPort.isTunable(),
            ochPort.lambda(),
            annotations);
      case OMS:
        annotations = annotations(node.get("annotations"));
        OmsPort omsPort = (OmsPort) deviceService.getPort(deviceId, port);
        return new OmsPortDescription(
            port,
            node.path("enabled").asBoolean(true),
            omsPort.minFrequency(),
            omsPort.maxFrequency(),
            omsPort.grid(),
            annotations);
      default:
        log.warn("{}: Unsupported Port Type");
    }
    return new DefaultPortDescription(
        port,
        node.path("enabled").asBoolean(true),
        type,
        node.path("speed").asLong(1_000),
        annotations);
  }