blob: 5cf1a17875235daa2934fe35a50367aca4884072 [file] [log] [blame]
DongRyeol Cha06041fa2018-06-07 10:23:32 +09001/*
2 * Copyright 2018-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 */
16package org.onosproject.drivers.arista;
17
18
19import com.fasterxml.jackson.databind.JsonNode;
20import com.google.common.base.Supplier;
21import com.google.common.collect.Sets;
22import org.onlab.packet.MacAddress;
23import org.onlab.util.Tools;
24import org.onosproject.net.AnnotationKeys;
25import org.onosproject.net.ConnectPoint;
26import org.onosproject.net.DefaultAnnotations;
27import org.onosproject.net.DefaultPort;
28import org.onosproject.net.Device;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.Link;
31import org.onosproject.net.Port;
32import org.onosproject.net.PortNumber;
33import org.onosproject.net.behaviour.LinkDiscovery;
34import org.onosproject.net.device.DeviceService;
35import org.onosproject.net.driver.AbstractHandlerBehaviour;
36import org.onosproject.net.driver.DriverHandler;
37import org.onosproject.net.link.DefaultLinkDescription;
38import org.onosproject.net.link.LinkDescription;
39import org.slf4j.Logger;
40
41import java.util.Iterator;
42import java.util.List;
43import java.util.Map;
44import java.util.Objects;
45import java.util.Optional;
46import java.util.Set;
47import java.util.stream.Stream;
48import java.util.stream.StreamSupport;
49
50import static com.google.common.base.Preconditions.checkNotNull;
51import static org.slf4j.LoggerFactory.getLogger;
52
53public class LinkDiscoveryAristaImpl extends AbstractHandlerBehaviour implements LinkDiscovery {
54
55 private static final String SHOW_LLDP_NEIGHBOR_DETAIL_CMD = "show lldp neighbors detail";
56 private static final String LLDP_NEIGHBORS = "lldpNeighbors";
57 private static final String LLDP_NEIGHBOR_INFO = "lldpNeighborInfo";
58 private static final String CHASSIS_ID = "chassisId";
59 private static final String PORT_ID = "interfaceDescription";
60 private static final String CHASSIS_ID_TYPE = "chassisIdType";
61 private static final String CHASSIS_ID_TYPE_MAC = "macAddress";
62
63 private final Logger log = getLogger(getClass());
64
65 @Override
66 public Set<LinkDescription> getLinks() {
67 return createLinksDescs(AristaUtils.retrieveCommandResult(handler(), SHOW_LLDP_NEIGHBOR_DETAIL_CMD));
68 }
69
70 private Set<LinkDescription> createLinksDescs(Optional<JsonNode> response) {
71 DriverHandler handler = checkNotNull(handler());
72 DeviceId localDeviceId = checkNotNull(handler.data().deviceId());
73 DeviceService deviceService = handler.get(DeviceService.class);
74 Set<LinkDescription> linkDescriptions = Sets.newHashSet();
75 List<Port> ports = deviceService.getPorts(localDeviceId);
76
77 if (ports.isEmpty() || Objects.isNull(response)) {
78 return linkDescriptions;
79 }
80
81 if (!response.isPresent()) {
82 return linkDescriptions;
83 }
84
85 log.debug("response: {}, {}", response, localDeviceId.toString());
86
87 JsonNode res = response.get();
88
89 if (res == null) {
90 log.warn("result is null");
91 return linkDescriptions;
92 }
93
94 JsonNode lldpNeighbors = res.findValue(LLDP_NEIGHBORS);
95
96 if (lldpNeighbors == null) {
97 log.warn("{} is null", LLDP_NEIGHBORS);
98 return linkDescriptions;
99 }
100
101 Iterator<Map.Entry<String, JsonNode>> lldpNeighborsIter = lldpNeighbors.fields();
102
103 while (lldpNeighborsIter.hasNext()) {
104 Map.Entry<String, JsonNode> neighbor = lldpNeighborsIter.next();
105 String lldpLocalPort = neighbor.getKey();
106 JsonNode neighborValue = neighbor.getValue();
107
108 log.debug("lldpLocalPort: {}", lldpLocalPort);
109 log.debug("neighborValue: {}", neighborValue.toString());
110
111 if (lldpLocalPort.isEmpty()) {
112 continue;
113 }
114
115 JsonNode neighborInfo = neighborValue.findValue(LLDP_NEIGHBOR_INFO);
116
117 if (neighborInfo == null) {
118 log.warn("{} is null", LLDP_NEIGHBOR_INFO);
119 continue;
120 }
121
122 Iterator<JsonNode> neighborInfoIter = neighborInfo.elements();
123
124 while (neighborInfoIter.hasNext()) {
125 JsonNode info = neighborInfoIter.next();
126 String chassisIdType = info.get(CHASSIS_ID_TYPE).asText("");
127
128 if (chassisIdType == null) {
129 log.warn("{} is null", CHASSIS_ID_TYPE);
130 continue;
131 }
132
133 if (!chassisIdType.equals(CHASSIS_ID_TYPE_MAC)) {
134 log.warn("{} is not mac: {}", CHASSIS_ID_TYPE_MAC, chassisIdType);
135 continue;
136 }
137
138 JsonNode remotePortNameNode = info.findValue(PORT_ID);
139
140 if (remotePortNameNode == null) {
141 continue;
142 }
143
144 String remoteChassisId = info.get(CHASSIS_ID).asText("");
145 String remotePortName = remotePortNameNode.asText("");
146
147 log.debug("{}: {}, {}: {}", CHASSIS_ID, remoteChassisId, PORT_ID, remotePortName);
148
149 Optional<Port> localPort = findLocalPortByName(ports, lldpLocalPort);
150
151 if (!localPort.isPresent()) {
152 log.warn("local port not found. lldpLocalPort value: {}", lldpLocalPort);
153 continue;
154 }
155
156 Optional<Device> remoteDevice = findRemoteDeviceByChassisId(deviceService, remoteChassisId);
157
158 if (!remoteDevice.isPresent()) {
159 log.warn("remote device not found. remoteChassisId value: {}", remoteChassisId);
160 continue;
161 }
162
163 Optional<Port> remotePort = findDestinationPortByName(
164 remotePortName,
165 deviceService,
166 remoteDevice.get());
167
168 if (!remotePort.isPresent()) {
169 log.warn("remote port not found. remotePortName value: {}", remotePortName);
170 continue;
171 }
172
DongRyeol Cha81935a62018-11-30 16:51:21 +0900173 if (!localPort.get().isEnabled() || !remotePort.get().isEnabled()) {
174 log.debug("Ports are disabled. Cannot create a link between {}/{} and {}/{}",
175 localDeviceId, localPort.get(), remoteDevice.get().id(), remotePort.get());
176 continue;
177 }
178
DongRyeol Cha06041fa2018-06-07 10:23:32 +0900179 linkDescriptions
180 .addAll(buildLinkPair(localDeviceId, localPort.get(),
181 remoteDevice.get().id(), remotePort.get()));
182 }
183 }
184
185 log.debug("returning linkDescriptions: {}", linkDescriptions);
186
187 return linkDescriptions;
188 }
189
190 private Optional<Port> findLocalPortByName(List<Port> ports, String lldpLocalPort) {
191 Optional<Port> localPort = ports.stream()
192 .filter(port -> lldpLocalPort.equalsIgnoreCase(port.annotations().value(AnnotationKeys.PORT_NAME)))
193 .findAny();
194
195 if (!localPort.isPresent()) {
196 localPort = ports.stream()
197 .filter(port -> lldpLocalPort.equalsIgnoreCase(port.annotations().value(AnnotationKeys.NAME)))
198 .findAny();
199
200 if (!localPort.isPresent()) {
201 return Optional.empty();
202 }
203 }
204
205 return localPort;
206 }
207
208 private Optional<Device> findRemoteDeviceByChassisId(DeviceService deviceService, String remoteChassisIdString) {
209 String forMacTmp = remoteChassisIdString
210 .replace(".", "")
211 .replaceAll("(.{2})", "$1:")
212 .trim()
213 .substring(0, 17);
214 MacAddress mac = MacAddress.valueOf(forMacTmp);
215 Supplier<Stream<Device>> deviceStream = () ->
216 StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false);
217 Optional<Device> remoteDeviceOptional = deviceStream.get()
218 .filter(device -> device.chassisId() != null
219 && MacAddress.valueOf(device.chassisId().value()).equals(mac))
220 .findAny();
221
222 if (remoteDeviceOptional.isPresent()) {
223 log.debug("remoteDevice found by chassis id: {}", forMacTmp);
224 return remoteDeviceOptional;
225 } else {
226 remoteDeviceOptional = deviceStream.get().filter(device ->
227 Tools.stream(deviceService.getPorts(device.id()))
228 .anyMatch(port -> port.annotations().keys().contains(AnnotationKeys.PORT_MAC)
229 && MacAddress.valueOf(port.annotations().value(AnnotationKeys.PORT_MAC))
230 .equals(mac)))
231 .findAny();
232 if (remoteDeviceOptional.isPresent()) {
233 log.debug("remoteDevice found by port mac: {}", forMacTmp);
234 return remoteDeviceOptional;
235 } else {
236 return Optional.empty();
237 }
238 }
239 }
240
241 private Optional<Port> findDestinationPortByName(String remotePortName,
242 DeviceService deviceService,
243 Device remoteDevice) {
244 Optional<Port> remotePort = deviceService.getPorts(remoteDevice.id())
245 .stream().filter(port -> remotePortName.equals(port.annotations().value(AnnotationKeys.PORT_NAME)))
246 .findAny();
247
248 if (remotePort.isPresent()) {
249 return remotePort;
250 } else {
251 int portNumber = Integer.valueOf(remotePortName.replaceAll("\\D+", ""));
252 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
253 .set(AnnotationKeys.PORT_NAME, remotePortName);
254
255 return Optional.of(new DefaultPort(remoteDevice, PortNumber.portNumber(portNumber, remotePortName),
256 true,
257 annotations.build()));
258 }
259 }
260
261 private static Set<LinkDescription> buildLinkPair(DeviceId localDevId,
262 Port localPort,
263 DeviceId remoteDevId,
264 Port remotePort) {
265
266 Set<LinkDescription> linkDescriptions = Sets.newHashSet();
267 ConnectPoint local = new ConnectPoint(localDevId, localPort.number());
268 ConnectPoint remote = new ConnectPoint(remoteDevId, remotePort.number());
269 DefaultAnnotations annotations = DefaultAnnotations.builder()
DongRyeol Chace65cc02018-07-23 15:02:28 +0900270 .set(AnnotationKeys.LAYER, "ETHERNET")
DongRyeol Cha06041fa2018-06-07 10:23:32 +0900271 .build();
272
273 linkDescriptions.add(new DefaultLinkDescription(
274 remote, local, Link.Type.DIRECT, true, annotations));
275
276 return linkDescriptions;
277 }
278}