blob: 3640a2ae850deec4aaaca31b993370fee0b3ab4e [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.ConnectPoint;
20import org.onosproject.net.Device;
21import org.onosproject.net.DeviceId;
22import org.onosproject.net.Link;
23import org.onosproject.net.device.PortDescription;
24import org.onosproject.net.PortNumber;
25import org.onosproject.net.Port;
26import org.onosproject.net.DefaultAnnotations;
27import org.onosproject.net.behaviour.LinkDiscovery;
28import org.onosproject.net.device.DeviceService;
29import org.onosproject.net.driver.AbstractHandlerBehaviour;
30import org.onosproject.net.driver.DriverHandler;
31import org.onosproject.net.link.DefaultLinkDescription;
32import org.onosproject.net.link.LinkDescription;
33import org.onosproject.netconf.NetconfController;
34import org.onosproject.netconf.NetconfSession;
35import org.slf4j.Logger;
36
37import java.util.Set;
38import java.util.HashSet;
39import java.util.List;
40
41import com.fasterxml.jackson.databind.ObjectMapper;
42import com.fasterxml.jackson.databind.JsonNode;
43import com.fasterxml.jackson.core.JsonProcessingException;
44
45import static com.google.common.base.Preconditions.checkNotNull;
46import static org.slf4j.LoggerFactory.getLogger;
47
48import static org.onosproject.drivers.polatis.netconf.PolatisUtility.*;
49
50import static org.onosproject.drivers.polatis.netconf.PolatisNetconfUtility.netconfGet;
51import static org.onosproject.drivers.polatis.netconf.PolatisNetconfUtility.KEY_PORTPEER;
52import static org.onosproject.drivers.polatis.netconf.PolatisNetconfUtility.KEY_PORTDIR;
53
54
55/**
56 * Reads peer-port fields from a Polatis switch, parses them and returns a set of LinkDescriptions.
57 */
58public class PolatisLinkDiscovery extends AbstractHandlerBehaviour implements LinkDiscovery {
59
60 private final Logger log = getLogger(getClass());
61
62 private static final String JSON_PEERPORT_PEER_KEY = "peer";
63 private static final String JSON_PEERPORT_PEER_DEVICEID_KEY = "id";
64 private static final String JSON_PEERPORT_PEER_PORTID_KEY = "port";
65 private static final String SCHEME_NAME = "linkdiscovery";
66
67 /**
68 * Constructor just used to initiate logging when LinkDiscovery behaviour is invoked.
69 */
70 public PolatisLinkDiscovery() {
71 log.debug("Running PolatisLinkDiscovery handler");
72 }
73
74 /**
75 * Returns the set of LinkDescriptions originating from a Polatis switch.
76 * <p>
77 * This is the callback required by the LinkDiscovery behaviour.
78 * @return Set of outbound unidirectional links as LinkDescriptions
79 */
80 @Override
81 public Set<LinkDescription> getLinks() {
82 Set<LinkDescription> links = new HashSet<>();
83 DeviceId deviceID = handler().data().deviceId();
84 log.debug("*** Checking peer-port fields on device {}", deviceID.toString());
85 NetconfController controller = checkNotNull(handler().get(NetconfController.class));
86 if (controller == null || controller.getDevicesMap() == null
87 || controller.getDevicesMap().get(deviceID) == null) {
88 log.warn("NETCONF session to device {} not yet established, will try again...", deviceID);
89 return links;
90 }
91 DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
92 Device device = deviceService.getDevice(deviceID);
93 int numInputPorts = Integer.parseInt(device.annotations().value(KEY_INPUTPORTS));
94 int numOutputPorts = Integer.parseInt(device.annotations().value(KEY_OUTPUTPORTS));
95 log.trace("Talking to device " + handler().data().deviceId().toString());
96 String reply = netconfGet(handler(), getPortsFilter());
97 // Get port details from switch as PortDescription objects
98 List<PortDescription> ports = parsePorts(reply, numInputPorts, numOutputPorts);
99 int numPeerPortEntries = 0;
100 int numPortsScanned = 0;
101 ObjectMapper mapper = new ObjectMapper();
102 for (PortDescription port : ports) {
103 numPortsScanned++;
104 if (deviceService.getPort(new ConnectPoint(deviceID, port.portNumber())).isEnabled()) {
105 String peerPortData = port.annotations().value(KEY_PORTPEER);
106 if (!peerPortData.equals("")) {
107 numPeerPortEntries++;
108 if (peerPortData.charAt(0) == '{') {
109 ConnectPoint nearEndCP = new ConnectPoint(deviceID, port.portNumber());
110 ConnectPoint farEndCP = null;
111 try {
112 farEndCP = parsePeerportDataForCP(mapper.readTree(peerPortData));
113 } catch (JsonProcessingException jpe) {
114 log.debug("Error processing peer-port JSON: {}", jpe.toString());
115 }
116 if (farEndCP != null) {
117 log.trace("Found ref on port {} to peer ConnectPoint: {}", port.portNumber(),
118 farEndCP.toString());
119 if (checkPeer(nearEndCP, farEndCP, this.handler(), true)) {
120 log.trace("Peer {} checks out", farEndCP.toString());
121 // now add link to Set<LinkDescription>
122 DefaultAnnotations annotations = DefaultAnnotations.builder()
123 .set(KEY_LINKBIDIR, VALUE_FALSE)
124 .set(KEY_LINKALLOWED, VALUE_TRUE)
125 .build();
126 ConnectPoint aEndCP = nearEndCP;
127 ConnectPoint bEndCP = farEndCP;
128 // reverse direction of unidirectional link if near-end port is INPUT
129 if (port.annotations().value(KEY_PORTDIR).equals(VALUE_INPUT)) {
130 aEndCP = farEndCP;
131 bEndCP = nearEndCP;
132 }
133 LinkDescription newLinkDesc = new DefaultLinkDescription(aEndCP, bEndCP,
134 Link.Type.OPTICAL, true, annotations);
135 links.add(newLinkDesc);
136 log.debug("Adding link {}", newLinkDesc);
137 }
138 }
139 }
140 }
141 }
142 }
143 log.debug("Scanned {} ports, {} had peer-port entries, {} {} valid", numPortsScanned, numPeerPortEntries,
144 links.size(), links.size() == 1 ? "is" : "are");
145 log.trace("Links found on this iteration: {}", links);
146 return links;
147 }
148
149 private ConnectPoint parsePeerportDataForCP(JsonNode peerData) {
150 DeviceId idTo = DeviceId.NONE;
151 PortNumber portNumTo = PortNumber.portNumber(0L);
152 JsonNode peer = peerData.get(JSON_PEERPORT_PEER_KEY);
153 if (peer != null) {
154 log.trace("Found peer element: {}", peer.toString());
155 JsonNode devIDNode = peer.get(JSON_PEERPORT_PEER_DEVICEID_KEY);
156 if (devIDNode != null) {
157 String devID = peer.get(JSON_PEERPORT_PEER_DEVICEID_KEY).asText();
158 log.trace("Found devID: {}", devID);
159 idTo = DeviceId.deviceId(devID);
160 JsonNode portNode = peer.get(JSON_PEERPORT_PEER_PORTID_KEY);
161 if (portNode != null) {
162 portNumTo = PortNumber.portNumber(portNode.asInt());
163 if (portNumTo.toLong() != 0) {
164 log.trace("Found legal peer JSON element: {}={}, {}={}", JSON_PEERPORT_PEER_DEVICEID_KEY,
165 idTo.toString(), JSON_PEERPORT_PEER_PORTID_KEY, portNumTo.toString());
166 } else {
167 log.trace("Malformed peer-port JSON: non-numerical or zero port value");
168 }
169 } else {
170 log.trace("Malformed peer-port JSON: unable to find \"{}\" key in {}",
171 JSON_PEERPORT_PEER_PORTID_KEY, peer.toString());
172 }
173 } else {
174 log.trace("Malformed peer-port JSON: unable to find \"{}\" key in {}",
175 JSON_PEERPORT_PEER_DEVICEID_KEY, peer.toString());
176 }
177 } else {
178 log.trace("Malformed peer-port JSON: unable to find \"{}\" key in {}", JSON_PEERPORT_PEER_KEY,
179 peerData.toString());
180 }
181 return (idTo.equals(DeviceId.NONE) || portNumTo.toLong() == 0) ? null : new ConnectPoint(idTo, portNumTo);
182 }
183
184 private boolean checkPeer(ConnectPoint nearEndCP, ConnectPoint peerCP, DriverHandler handler, boolean direct) {
185 // check peerCP exists and is available (either via device service or direct from device)
186 DeviceId peerDeviceID = peerCP.deviceId();
187 boolean result = false;
188 DeviceService deviceService = checkNotNull(handler.get(DeviceService.class));
189 if (deviceService.isAvailable(peerDeviceID)) {
190 log.trace("Peer device {} exists", peerDeviceID.toString());
191 Device device = deviceService.getDevice(peerDeviceID);
192 int numInputPorts = Integer.parseInt(device.annotations().value(KEY_INPUTPORTS));
193 int numOutputPorts = Integer.parseInt(device.annotations().value(KEY_OUTPUTPORTS));
194 List<Port> ports = deviceService.getPorts(peerDeviceID);
195 PortNumber farEndPortNum = peerCP.port();
196 Port port = deviceService.getPort(peerCP);
197 if (port != null) {
198 if (port.isEnabled()) {
199 log.trace("Peer port {} exists", port.number().toLong());
200 // check far end peer-port entry (use device service or retrieve direct from switch)
201 Port peerPort = deviceService.getPort(peerDeviceID, farEndPortNum);
202 String farEndPortPeerportData = peerPort.annotations().value(KEY_PORTPEER);
203 if (direct) {
204 log.trace("Checking device {} DIRECT", handler.data().deviceId());
205 //A bit of a cludge it seems but temporarily open a new NETCONF session to far-end device
206 NetconfController controller = checkNotNull(handler.get(NetconfController.class));
207 NetconfSession farEndDeviceSession = controller.getDevicesMap().get(peerDeviceID).getSession();
208 String reply = netconfGet(farEndDeviceSession, getPortFilter(farEndPortNum));
209 PortDescription peerPortDescDirect = parsePorts(reply, numInputPorts, numOutputPorts).get(0);
210 log.trace("peerPortDesc from device: " + peerPortDescDirect.toString());
211 String farEndPortPeerportDataDirect = peerPortDescDirect.annotations().value(KEY_PORTPEER);
212 farEndPortPeerportData = farEndPortPeerportDataDirect;
213 }
214 if (!farEndPortPeerportData.equals("")) {
215 if (farEndPortPeerportData.charAt(0) == '{') {
216 log.trace("Far-end peer-port value:" + farEndPortPeerportData);
217 ObjectMapper mapper = new ObjectMapper();
218 ConnectPoint checkNearEndCP = null;
219 try {
220 checkNearEndCP = parsePeerportDataForCP(mapper.readTree(farEndPortPeerportData));
221 } catch (JsonProcessingException jpe) {
222 log.trace("Error processing peer-port JSON: {}", jpe.toString());
223 }
224 if (nearEndCP.equals(checkNearEndCP)) {
225 log.trace("Reciprocal peer port entries match: nearEnd={}, farEnd={}", nearEndCP,
226 checkNearEndCP);
227 result = true;
228 } else {
229 log.trace("Peer-port entry for far-end port ({}) does not match near-end " +
230 "port number ({})", checkNearEndCP, nearEndCP);
231 }
232 }
233 } else {
234 log.trace("Null peer-port entry for far-end port ({})", peerCP);
235 }
236 } else {
237 log.trace("Peer port {} is DISABLED", port);
238 }
239 } else {
240 log.trace("Peer port {} does not exist", port);
241 }
242 } else {
243 log.trace("Far end device does not exist or is not available");
244 }
245 return result;
246 }
247}