All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.onosproject.rest.resources.ConfigProvider Maven / Gradle / Ivy

There is a newer version: 2.7.0
Show newest version
/*
 * Copyright 2014-2015 Open Networking Laboratory
 *
 * 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.rest.resources;

import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import org.onlab.packet.ChassisId;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onlab.util.Frequency;
import org.onlab.util.Spectrum;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.ChannelSpacing;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.GridType;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.Link;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.OchPort;
import org.onosproject.net.OchSignal;
import org.onosproject.net.OduCltPort;
import org.onosproject.net.OduSignalType;
import org.onosproject.net.OmsPort;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceProvider;
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceProviderService;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.device.OchPortDescription;
import org.onosproject.net.device.OduCltPortDescription;
import org.onosproject.net.device.OmsPortDescription;
import org.onosproject.net.device.PortDescription;
import org.onosproject.net.host.DefaultHostDescription;
import org.onosproject.net.host.HostProvider;
import org.onosproject.net.host.HostProviderRegistry;
import org.onosproject.net.host.HostProviderService;
import org.onosproject.net.link.DefaultLinkDescription;
import org.onosproject.net.link.LinkProvider;
import org.onosproject.net.link.LinkProviderRegistry;
import org.onosproject.net.link.LinkProviderService;
import org.onosproject.net.provider.ProviderId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.net.DeviceId.deviceId;
import static org.onosproject.net.PortNumber.portNumber;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;

/**
 * Provider of devices and links parsed from a JSON configuration structure.
 *
 * @deprecated in 1.5.0 (Falcon)
 */
@Deprecated
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";

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

    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 connectPoints = Lists.newArrayList();
    private Map 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 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),
                        Spectrum.CENTER_FREQUENCY, Spectrum.CENTER_FREQUENCY.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 = Spectrum.CENTER_FREQUENCY.add(grid);
        Frequency max = Spectrum.CENTER_FREQUENCY.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 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, false);

        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 ports = deviceService.getPorts(device.id());
            Set existing = ports.stream()
                    .map(p -> new ConnectPoint(device.id(), p.number()))
                    .collect(Collectors.toSet());
            Set missing = connectPoints.stream()
                    .filter(cp -> cp.deviceId().equals(device.id()))
                    .filter(cp -> !existing.contains(cp))
                    .collect(Collectors.toSet());

            if (!missing.isEmpty()) {
                List 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 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();
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy