blob: 42fd5bbfc953f06ec548dc70acdb9ade4f383138 [file] [log] [blame]
Michael Enrico584eebd2021-07-22 08:04:13 +00001/*
2 * Copyright 2016-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.drivers.polatis.netconf;
18
19import org.onosproject.net.DeviceId;
20import org.onosproject.net.Port;
21import org.onosproject.net.PortNumber;
22import org.onosproject.net.ConnectPoint;
23import org.onosproject.net.behaviour.InternalConnectivity;
24import org.onosproject.net.device.DeviceService;
25import org.onosproject.net.driver.AbstractHandlerBehaviour;
26import org.onosproject.net.flow.FlowEntry;
27import org.onosproject.net.flow.criteria.Criterion;
28import org.onosproject.net.flow.criteria.PortCriterion;
29import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
30import java.util.Set;
31import java.util.HashSet;
32import java.util.Collection;
33import java.util.stream.Collectors;
34import org.slf4j.Logger;
35
36import static com.google.common.base.Preconditions.checkNotNull;
37import static org.slf4j.LoggerFactory.getLogger;
38import static org.onosproject.drivers.polatis.netconf.PolatisUtility.*;
39import static org.onosproject.drivers.polatis.netconf.PolatisNetconfUtility.KEY_PORTDIR;
40
41
42/**
43 * Implements InternalConnectivity behaviour for Polatis optical switches.
44 */
45public class PolatisInternalConnectivity extends AbstractHandlerBehaviour
46 implements InternalConnectivity {
47
48 private final Logger log = getLogger(getClass());
49
50 private static final String JSON_PEERPORT_PEER_KEY = "peer";
51 private static final String JSON_PEERPORT_PEER_DEVICEID_KEY = "id";
52 private static final String JSON_PEERPORT_PEER_PORTID_KEY = "port";
53 private static final String SCHEME_NAME = "linkdiscovery";
54
55 /**
56 * Constructor just used to initiate logging when InternalConnectivity behaviour is invoked.
57 */
58 public PolatisInternalConnectivity() {
59 log.debug("Running PolatisInternalConnectivity handler");
60 }
61
62 /**
63 * Returns boolean in response to test of whether 2 ports on a given device can be internally connected.
64 * <p>
65 * This is a callback method required by the InternalConnectivity behaviour.
66 * @param inputPortNum Input port number on device
67 * @param outputPortNum Output port number on device
68 * @return Indication whether internal connection can be made
69 */
70 @Override
71 public boolean testConnectivity(PortNumber inputPortNum, PortNumber outputPortNum) {
72
73 // NOTE: this is implemented symmetrically reflecting the fact that reverse propagation
74 // is possible through a Polatis switch
75 if (inputPortNum.equals(outputPortNum)) {
76 log.debug("Input and output ports cannot be one and the same");
77 return false;
78 }
79 DeviceId deviceID = handler().data().deviceId();
80 DeviceService deviceService = checkNotNull(this.handler().get(DeviceService.class));
81 Port inputPort = deviceService.getPort(new ConnectPoint(deviceID, inputPortNum));
82 Port outputPort = deviceService.getPort(new ConnectPoint(deviceID, outputPortNum));
83 if (!inputPort.isEnabled()) {
84 log.debug("Input port is DISABLED");
85 return false;
86 }
87 if (!outputPort.isEnabled()) {
88 log.debug("Output port is DISABLED");
89 return false;
90 }
91 if (!inputPort.annotations().value(KEY_PORTDIR).equals(VALUE_CC)) {
92 if (inputPort.annotations().value(KEY_PORTDIR).equals(outputPort.annotations().value(KEY_PORTDIR))) {
93 log.debug("Dual sided switch and provided input & output ports on same side");
94 return false;
95 }
96 }
97 // Check if either port is used in an active cross-connect
98 Set<PortNumber> usedPorts = getUsedPorts();
99 if (usedPorts.contains(inputPortNum)) {
100 log.debug("Input port {} is used in an active cross-connect", inputPortNum);
101 return false;
102 }
103 if (usedPorts.contains(outputPortNum)) {
104 log.debug("Output port {} is used in an active cross-connect", outputPortNum);
105 return false;
106 }
107 return true;
108 }
109
110 /**
111 * Returns a set of possible output PortNumbers to which a given input port can be internally connected
112 * on a given device.
113 * <p>
114 * This is a callback method required by the InternalConnectivity behaviour.
115 * @param inputPortNum Input port number on device
116 * @return List of possible output ports
117 */
118 @Override
119 public Set<PortNumber> getOutputPorts(PortNumber inputPortNum) {
120
121 // NOTE: in this implementation, inputPortNum is the supplied port number and
122 // can be an "input" port OR an "output" port
123 // This reflects the fact that reverse propagation is possible through a Polatis switch
124 // (output port -> input port)
125
126 Set<PortNumber> ports = new HashSet<PortNumber>();
127 Set<PortNumber> enabledPorts = new HashSet<PortNumber>();
128 Collection<FlowEntry> flowEntries = parseConnections(this);
129 // Flow entries are very simple for an all-optical circuit switch which doesn't multicast
130 // e.g. one selector (IN_PORT) and one action (set output port)
131 if (flowEntries.stream().map(flow -> ((PortCriterion) flow.selector()
132 .getCriterion(Criterion.Type.IN_PORT)).port())
133 .collect(Collectors.toSet())
134 .contains(inputPortNum) ||
135 flowEntries.stream().map(flow -> ((OutputInstruction) flow.treatment()
136 .allInstructions().get(0)).port())
137 .collect(Collectors.toSet())
138 .contains(inputPortNum)) {
139 log.warn("Queried port {} is already used in a cross-connect", inputPortNum);
140 return ports;
141 } else {
142 DeviceId deviceID = handler().data().deviceId();
143 DeviceService deviceService = checkNotNull(this.handler().get(DeviceService.class));
144 Port inputPort = deviceService.getPort(new ConnectPoint(deviceID, inputPortNum));
145 if (inputPort.annotations().value(KEY_PORTDIR).equals(VALUE_CC)) {
146 ports = deviceService.getPorts(deviceID).stream()
147 .filter(p -> !p.equals(inputPortNum))
148 .map(p -> p.number())
149 .collect(Collectors.toSet());
150 } else {
151 ports = deviceService.getPorts(deviceID).stream()
152 .filter((p) -> {
153 Port port = deviceService.getPort(new ConnectPoint(deviceID, p.number()));
154 return !port.annotations().value(KEY_PORTDIR).equals(
155 inputPort.annotations().value(KEY_PORTDIR));
156 })
157 .map(p -> p.number())
158 .collect(Collectors.toSet());
159 }
160 // Remove disabled ports
161 enabledPorts = ports.stream()
162 .filter(p -> deviceService.getPort(new ConnectPoint(deviceID, p)).isEnabled())
163 .collect(Collectors.toSet());
164 }
165 log.debug("Ports before filtering out used and disabled ones: " + ports);
166 return filterUsedPorts(enabledPorts, flowEntries);
167 }
168
169 /**
170 * Returns a set of possible input PortNumbers to which a given output port can be internally connected
171 * on a given device.
172 * <p>
173 * This is a callback method required by the InternalConnectivity behaviour.
174 * This just calls the getOutputPorts method since reverse path optical transmission is possible through
175 * a Polatis switch.
176 * @param outputPortNum Input port number on device
177 * @return List of possible input ports
178 */
179 @Override
180 public Set<PortNumber> getInputPorts(PortNumber outputPortNum) {
181
182 return getOutputPorts(outputPortNum);
183 }
184
185 private Set<PortNumber> filterUsedPorts(Set<PortNumber> ports, Collection<FlowEntry> flowEntries) {
186
187 if (ports.isEmpty() || flowEntries.isEmpty()) {
188 return ports;
189 }
190 for (FlowEntry flowEntry : flowEntries) {
191 PortNumber inputPort = ((PortCriterion) flowEntry.selector().getCriterion(Criterion.Type.IN_PORT)).port();
192 ports.remove(inputPort);
193 PortNumber outputPort = ((OutputInstruction) flowEntry.treatment().allInstructions().get(0)).port();
194 ports.remove(outputPort);
195 log.debug("Cross-connection {}-{} removed from output port list", inputPort, outputPort);
196 }
197 log.debug("Ports after filtering out used ones: " + ports);
198 return ports;
199 }
200
201 private Set<PortNumber> getUsedPorts() {
202 Set<PortNumber> usedPorts = new HashSet<PortNumber>();
203 Collection<FlowEntry> flowEntries = parseConnections(this);
204 for (FlowEntry flowEntry : flowEntries) {
205 usedPorts.add(((PortCriterion) flowEntry.selector().getCriterion(Criterion.Type.IN_PORT)).port());
206 usedPorts.add(((OutputInstruction) flowEntry.treatment().allInstructions().get(0)).port());
207 }
208 return usedPorts;
209 }
210}