blob: 42fd5bbfc953f06ec548dc70acdb9ade4f383138 [file] [log] [blame]
/*
* Copyright 2016-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.polatis.netconf;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.behaviour.InternalConnectivity;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.PortCriterion;
import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
import java.util.Set;
import java.util.HashSet;
import java.util.Collection;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import static org.onosproject.drivers.polatis.netconf.PolatisUtility.*;
import static org.onosproject.drivers.polatis.netconf.PolatisNetconfUtility.KEY_PORTDIR;
/**
* Implements InternalConnectivity behaviour for Polatis optical switches.
*/
public class PolatisInternalConnectivity extends AbstractHandlerBehaviour
implements InternalConnectivity {
private final Logger log = getLogger(getClass());
private static final String JSON_PEERPORT_PEER_KEY = "peer";
private static final String JSON_PEERPORT_PEER_DEVICEID_KEY = "id";
private static final String JSON_PEERPORT_PEER_PORTID_KEY = "port";
private static final String SCHEME_NAME = "linkdiscovery";
/**
* Constructor just used to initiate logging when InternalConnectivity behaviour is invoked.
*/
public PolatisInternalConnectivity() {
log.debug("Running PolatisInternalConnectivity handler");
}
/**
* Returns boolean in response to test of whether 2 ports on a given device can be internally connected.
* <p>
* This is a callback method required by the InternalConnectivity behaviour.
* @param inputPortNum Input port number on device
* @param outputPortNum Output port number on device
* @return Indication whether internal connection can be made
*/
@Override
public boolean testConnectivity(PortNumber inputPortNum, PortNumber outputPortNum) {
// NOTE: this is implemented symmetrically reflecting the fact that reverse propagation
// is possible through a Polatis switch
if (inputPortNum.equals(outputPortNum)) {
log.debug("Input and output ports cannot be one and the same");
return false;
}
DeviceId deviceID = handler().data().deviceId();
DeviceService deviceService = checkNotNull(this.handler().get(DeviceService.class));
Port inputPort = deviceService.getPort(new ConnectPoint(deviceID, inputPortNum));
Port outputPort = deviceService.getPort(new ConnectPoint(deviceID, outputPortNum));
if (!inputPort.isEnabled()) {
log.debug("Input port is DISABLED");
return false;
}
if (!outputPort.isEnabled()) {
log.debug("Output port is DISABLED");
return false;
}
if (!inputPort.annotations().value(KEY_PORTDIR).equals(VALUE_CC)) {
if (inputPort.annotations().value(KEY_PORTDIR).equals(outputPort.annotations().value(KEY_PORTDIR))) {
log.debug("Dual sided switch and provided input & output ports on same side");
return false;
}
}
// Check if either port is used in an active cross-connect
Set<PortNumber> usedPorts = getUsedPorts();
if (usedPorts.contains(inputPortNum)) {
log.debug("Input port {} is used in an active cross-connect", inputPortNum);
return false;
}
if (usedPorts.contains(outputPortNum)) {
log.debug("Output port {} is used in an active cross-connect", outputPortNum);
return false;
}
return true;
}
/**
* Returns a set of possible output PortNumbers to which a given input port can be internally connected
* on a given device.
* <p>
* This is a callback method required by the InternalConnectivity behaviour.
* @param inputPortNum Input port number on device
* @return List of possible output ports
*/
@Override
public Set<PortNumber> getOutputPorts(PortNumber inputPortNum) {
// NOTE: in this implementation, inputPortNum is the supplied port number and
// can be an "input" port OR an "output" port
// This reflects the fact that reverse propagation is possible through a Polatis switch
// (output port -> input port)
Set<PortNumber> ports = new HashSet<PortNumber>();
Set<PortNumber> enabledPorts = new HashSet<PortNumber>();
Collection<FlowEntry> flowEntries = parseConnections(this);
// Flow entries are very simple for an all-optical circuit switch which doesn't multicast
// e.g. one selector (IN_PORT) and one action (set output port)
if (flowEntries.stream().map(flow -> ((PortCriterion) flow.selector()
.getCriterion(Criterion.Type.IN_PORT)).port())
.collect(Collectors.toSet())
.contains(inputPortNum) ||
flowEntries.stream().map(flow -> ((OutputInstruction) flow.treatment()
.allInstructions().get(0)).port())
.collect(Collectors.toSet())
.contains(inputPortNum)) {
log.warn("Queried port {} is already used in a cross-connect", inputPortNum);
return ports;
} else {
DeviceId deviceID = handler().data().deviceId();
DeviceService deviceService = checkNotNull(this.handler().get(DeviceService.class));
Port inputPort = deviceService.getPort(new ConnectPoint(deviceID, inputPortNum));
if (inputPort.annotations().value(KEY_PORTDIR).equals(VALUE_CC)) {
ports = deviceService.getPorts(deviceID).stream()
.filter(p -> !p.equals(inputPortNum))
.map(p -> p.number())
.collect(Collectors.toSet());
} else {
ports = deviceService.getPorts(deviceID).stream()
.filter((p) -> {
Port port = deviceService.getPort(new ConnectPoint(deviceID, p.number()));
return !port.annotations().value(KEY_PORTDIR).equals(
inputPort.annotations().value(KEY_PORTDIR));
})
.map(p -> p.number())
.collect(Collectors.toSet());
}
// Remove disabled ports
enabledPorts = ports.stream()
.filter(p -> deviceService.getPort(new ConnectPoint(deviceID, p)).isEnabled())
.collect(Collectors.toSet());
}
log.debug("Ports before filtering out used and disabled ones: " + ports);
return filterUsedPorts(enabledPorts, flowEntries);
}
/**
* Returns a set of possible input PortNumbers to which a given output port can be internally connected
* on a given device.
* <p>
* This is a callback method required by the InternalConnectivity behaviour.
* This just calls the getOutputPorts method since reverse path optical transmission is possible through
* a Polatis switch.
* @param outputPortNum Input port number on device
* @return List of possible input ports
*/
@Override
public Set<PortNumber> getInputPorts(PortNumber outputPortNum) {
return getOutputPorts(outputPortNum);
}
private Set<PortNumber> filterUsedPorts(Set<PortNumber> ports, Collection<FlowEntry> flowEntries) {
if (ports.isEmpty() || flowEntries.isEmpty()) {
return ports;
}
for (FlowEntry flowEntry : flowEntries) {
PortNumber inputPort = ((PortCriterion) flowEntry.selector().getCriterion(Criterion.Type.IN_PORT)).port();
ports.remove(inputPort);
PortNumber outputPort = ((OutputInstruction) flowEntry.treatment().allInstructions().get(0)).port();
ports.remove(outputPort);
log.debug("Cross-connection {}-{} removed from output port list", inputPort, outputPort);
}
log.debug("Ports after filtering out used ones: " + ports);
return ports;
}
private Set<PortNumber> getUsedPorts() {
Set<PortNumber> usedPorts = new HashSet<PortNumber>();
Collection<FlowEntry> flowEntries = parseConnections(this);
for (FlowEntry flowEntry : flowEntries) {
usedPorts.add(((PortCriterion) flowEntry.selector().getCriterion(Criterion.Type.IN_PORT)).port());
usedPorts.add(((OutputInstruction) flowEntry.treatment().allInstructions().get(0)).port());
}
return usedPorts;
}
}