Java tutorial
/* * Copyright 2018-present Open Networking Foundation * * 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.onosproject.drivers.odtn; import static com.google.common.base.Preconditions.checkNotNull; import static org.slf4j.LoggerFactory.getLogger; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.HierarchicalConfiguration; import org.apache.commons.configuration.XMLConfiguration; import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine; import org.onosproject.net.DefaultAnnotations; import org.onosproject.net.DeviceId; import org.onosproject.net.Port.Type; import org.onosproject.net.PortNumber; import org.onosproject.net.device.DefaultPortDescription; import org.onosproject.net.device.DefaultPortDescription.Builder; import org.onosproject.net.device.DeviceDescription; import org.onosproject.net.device.DeviceDescriptionDiscovery; import org.onosproject.net.device.PortDescription; import org.onosproject.net.driver.AbstractHandlerBehaviour; import org.onosproject.netconf.NetconfController; import org.onosproject.netconf.NetconfDevice; import org.onosproject.netconf.NetconfSession; import org.onosproject.odtn.behaviour.OdtnDeviceDescriptionDiscovery; import org.slf4j.Logger; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.io.CharSource; /** * OpenConfig based device and port discovery. */ public class OpenConfigDeviceDiscovery extends AbstractHandlerBehaviour implements OdtnDeviceDescriptionDiscovery, DeviceDescriptionDiscovery { private static final Logger log = getLogger(OpenConfigDeviceDiscovery.class); @Override public DeviceDescription discoverDeviceDetails() { // TODO Auto-generated method stub // Not really used right now return null; } @Override public List<PortDescription> discoverPortDetails() { try { return discoverPorts(); } catch (Exception e) { log.error("Error discovering port details on {}", data().deviceId(), e); return ImmutableList.of(); } } private List<PortDescription> discoverPorts() throws ConfigurationException, IOException { DeviceId did = data().deviceId(); NetconfSession ns = Optional.ofNullable(handler().get(NetconfController.class)) .map(c -> c.getNetconfDevice(did)).map(NetconfDevice::getSession) .orElseThrow(() -> new IllegalStateException("No NetconfSession found for " + did)); // TODO convert this method into non-blocking form? String reply = ns.asyncGet().join().toString(); // workaround until asyncGet().join() start failing exceptionally String data = null; if (reply.startsWith("<data")) { data = reply; } if (data == null) { log.error("No valid response found from {}:\n{}", did, reply); return ImmutableList.of(); } XMLConfiguration cfg = new XMLConfiguration(); cfg.load(CharSource.wrap(data).openStream()); return discoverPorts(cfg); } /** * Parses port information from OpenConfig XML configuration. * * @param cfg tree where the root node is {@literal <data>} * @return List of ports */ @VisibleForTesting protected List<PortDescription> discoverPorts(XMLConfiguration cfg) { // If we want to use XPath cfg.setExpressionEngine(new XPathExpressionEngine()); // converting components into PortDescription. List<HierarchicalConfiguration> components = cfg.configurationsAt("components/component"); return components.stream().map(this::toPortDescription).filter(Objects::nonNull) .collect(Collectors.toList()); } // wrapper to make parsing exception safe private PortDescription toPortDescription(HierarchicalConfiguration component) { try { return toPortDescriptionInternal(component); } catch (Exception e) { log.error("Unexpected exception parsing component {} on {}", component.getString("name"), data().deviceId(), e); return null; } } /** * Converts Component subtree to PortDescription. * * @param component subtree to parse * @return PortDescription or null if component is not an ONOS Port */ private PortDescription toPortDescriptionInternal(HierarchicalConfiguration component) { // to access other part of <data> tree: //log.warn("parent data Node: {}", // ((SubnodeConfiguration) component).getParent().getRootNode().getName()); Map<String, String> props = new HashMap<>(); String name = component.getString("name"); String type = component.getString("state/type"); checkNotNull(name, "name not found"); checkNotNull(type, "state/type not found"); props.put(OdtnDeviceDescriptionDiscovery.OC_NAME, name); props.put(OdtnDeviceDescriptionDiscovery.OC_TYPE, type); component.configurationsAt("properties/property").forEach(prop -> { String pName = prop.getString("name"); String pValue = prop.getString("config/value"); props.put(pName, pValue); }); if (!props.containsKey(ONOS_PORT_INDEX)) { log.info("DEBUG: Component {} does not include onos-index, skipping", name); // ODTN: port must have onos-index property return null; } Builder builder = DefaultPortDescription.builder(); builder.withPortNumber(PortNumber.portNumber(Long.parseLong(props.get(ONOS_PORT_INDEX)), name)); switch (type) { case "oc-platform-types:PORT": case "oc-opt-types:OPTICAL_CHANNEL": // TODO assign appropriate port type & annotations at some point // for now we just need a Port with annotations builder.type(Type.OCH); props.putIfAbsent(PORT_TYPE, OdtnPortType.LINE.value()); // Just a heuristics to deal with simple transponder // if the device declare odtn-connection-id, just use them // if not assign same value to relevant ports types props.putIfAbsent(CONNECTION_ID, "the-only-one"); break; case "oc-platform-types:TRANSCEIVER": // TODO assign appropriate port type & annotations at some point // for now we just need a Port with annotations builder.type(Type.PACKET); props.putIfAbsent(PORT_TYPE, OdtnPortType.CLIENT.value()); // Just a heuristics to deal with simple transponder // if the device declare odtn-connection-id, just use them // if not assign same value to relevant ports types props.putIfAbsent(CONNECTION_ID, "the-only-one"); break; default: log.info("DEBUG: Unknown component type {}", type); return null; } builder.annotations(DefaultAnnotations.builder().putAll(props).build()); return builder.build(); } }