blob: b8a2016e6f0f81b7df165b00cc95e251afcf4cd7 [file] [log] [blame]
gyewan.aneeb4cf52018-12-24 11:47:57 +09001/*
sdn5d65d112019-01-11 18:45:26 +09002 * Copyright 2019-present Open Networking Foundation
gyewan.aneeb4cf52018-12-24 11:47:57 +09003 *
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.cisco.rest;
17
18
19import com.fasterxml.jackson.databind.JsonNode;
20import com.fasterxml.jackson.databind.ObjectMapper;
21import com.google.common.base.Supplier;
22import com.google.common.collect.Lists;
23import com.google.common.collect.Sets;
24import org.onlab.packet.ChassisId;
25import org.onlab.packet.MacAddress;
26import org.onlab.util.Tools;
27import org.onosproject.net.AnnotationKeys;
28import org.onosproject.net.ConnectPoint;
29import org.onosproject.net.DefaultAnnotations;
30import org.onosproject.net.DefaultDevice;
31import org.onosproject.net.DefaultPort;
32import org.onosproject.net.Device;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.Link;
35import org.onosproject.net.Port;
36import org.onosproject.net.PortNumber;
37import org.onosproject.net.behaviour.LinkDiscovery;
38import org.onosproject.net.device.DeviceService;
39import org.onosproject.net.driver.AbstractHandlerBehaviour;
40import org.onosproject.net.driver.DriverHandler;
41import org.onosproject.net.link.DefaultLinkDescription;
42import org.onosproject.net.link.LinkDescription;
43import org.onosproject.net.provider.ProviderId;
44import org.onosproject.protocol.rest.RestSBController;
45import org.slf4j.Logger;
46
47import javax.ws.rs.core.MediaType;
48import java.io.ByteArrayInputStream;
49import java.io.IOException;
50import java.io.InputStream;
51import java.nio.charset.StandardCharsets;
52import java.util.Iterator;
53import java.util.List;
54import java.util.Objects;
55import java.util.Optional;
56import java.util.Set;
57import java.util.stream.Stream;
58import java.util.stream.StreamSupport;
59
60import static com.google.common.base.Preconditions.checkNotNull;
61import static org.onosproject.net.AnnotationKeys.PORT_NAME;
62import static org.slf4j.LoggerFactory.getLogger;
63
64
65/**
66 * Discovers the links from a Cisco Nexus 9K REST device.
67 */
68public class LinkDiscoveryCisco9kImpl extends AbstractHandlerBehaviour implements LinkDiscovery {
69
70 private static final String SHOW_LLDP_NEIGHBOR_DETAIL_CMD = "show lldp neighbor detail";
71 private static final String UNKNOWN = "unknown";
72 private static final String JSON_RESULT = "result";
73 private static final String TABLE_NBOR_DETAIL = "TABLE_nbor_detail";
74 private static final String ROW_NBOR_DETAIL = "ROW_nbor_detail";
75 private static final String CHASSIS_ID = "chassis_id";
76 private static final String PORT_ID = "port_id";
77 private static final String PORT_DESC = "port_desc";
78 private static final String SYS_NAME = "sys_name";
79 private static final String LOCAL_PORT_ID = "l_port_id";
80 private static final String LLDP = "lldp:";
81
82 private final Logger log = getLogger(getClass());
83
84 @Override
85 public Set<LinkDescription> getLinks() {
86 String response = retrieveResponse(SHOW_LLDP_NEIGHBOR_DETAIL_CMD);
87 DeviceId localDeviceId = this.handler().data().deviceId();
88 DeviceService deviceService = this.handler().get(DeviceService.class);
89 Set<LinkDescription> linkDescriptions = Sets.newHashSet();
90 List<Port> ports = deviceService.getPorts(localDeviceId);
91
92 if (ports.size() == 0 || Objects.isNull(response)) {
93 return linkDescriptions;
94 }
95 try {
96 ObjectMapper om = new ObjectMapper();
97 JsonNode json = om.readTree(response);
98 if (json == null) {
99 return linkDescriptions;
100 }
101
102 JsonNode res = json.at("/" + JSON_RESULT);
103 if (res.isMissingNode()) {
104 return linkDescriptions;
105 }
106
107 JsonNode lldpNeighborsRow = res.at("/" + TABLE_NBOR_DETAIL);
108 if (lldpNeighborsRow.isMissingNode()) {
109 return linkDescriptions;
110 }
111
112 JsonNode lldpNeighbors = lldpNeighborsRow.at("/" + ROW_NBOR_DETAIL);
113 if (lldpNeighbors.isMissingNode()) {
114 return linkDescriptions;
115 }
116
117 Iterator<JsonNode> iterator = lldpNeighbors.elements();
118
119 while (iterator.hasNext()) {
120 JsonNode neighbors = iterator.next();
121 String remoteChassisId = neighbors.get(CHASSIS_ID).asText();
122 String remotePortName = neighbors.get(PORT_ID).asText();
123 String remotePortDesc = neighbors.get(PORT_DESC).asText();
124 String lldpLocalPort = neighbors.get(LOCAL_PORT_ID).asText()
125 .replaceAll("(Eth.{0,5})(.\\d{0,5}/\\d{0,5})", "Ethernet$2");
126
127 Port localPort = findLocalPortByName(ports, lldpLocalPort);
128 if (localPort == null) {
129 log.warn("local port not found. LldpLocalPort value: {}", lldpLocalPort);
130 continue;
131 }
132
133 Device remoteDevice = findRemoteDeviceByChassisId(deviceService, remoteChassisId);
134 Port remotePort = findDestinationPortByName(remotePortName,
135 remotePortDesc,
136 deviceService,
137 remoteDevice);
138
139 if (!localPort.isEnabled() || !remotePort.isEnabled()) {
140 log.debug("Ports are disabled. Cannot create a link between {}/{} and {}/{}",
141 localDeviceId, localPort, remoteDevice.id(), remotePort);
142 continue;
143 }
144
145 linkDescriptions.addAll(buildLinkPair(localDeviceId, localPort, remoteDevice.id(), remotePort));
146 }
147 } catch (IOException e) {
148 log.error("Failed to get links ", e);
149 }
150
151 log.debug("Returning linkDescriptions: {}", linkDescriptions);
152 return linkDescriptions;
153
154 }
155
156 private Port findLocalPortByName(List<Port> ports, String lldpLocalPort) {
157 Optional<Port> localPort = ports.stream()
158 .filter(port -> lldpLocalPort.equalsIgnoreCase(port.annotations().value(PORT_NAME))).findAny();
159 if (!localPort.isPresent()) {
160 return null;
161 }
162 return localPort.get();
163 }
164
165 private Device findRemoteDeviceByChassisId(DeviceService deviceService, String remoteChassisIdString) {
166 String forMacTmp = remoteChassisIdString.replace(".", "").replaceAll("(.{2})", "$1:").trim().substring(0, 17);
167 MacAddress mac = MacAddress.valueOf(forMacTmp);
168 ChassisId remoteChassisId = new ChassisId(mac.toLong());
169 Optional<Device> remoteDeviceOptional;
170 Supplier<Stream<Device>> deviceStream = () ->
171 StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false);
172 remoteDeviceOptional = deviceStream.get()
173 .filter(device -> device.chassisId() != null
174 && MacAddress.valueOf(device.chassisId().value()).equals(mac))
175 .findAny();
176
177 if (remoteDeviceOptional.isPresent()) {
178 return remoteDeviceOptional.get();
179 } else {
180 remoteDeviceOptional = deviceStream.get().filter(device ->
181 Tools.stream(deviceService.getPorts(device.id())).anyMatch(port ->
182 port.annotations().keys().contains(AnnotationKeys.PORT_MAC)
183 && MacAddress.valueOf(port.annotations().value(AnnotationKeys.PORT_MAC))
184 .equals(mac))).findAny();
185 if (remoteDeviceOptional.isPresent()) {
186 return remoteDeviceOptional.get();
187 } else {
188 log.debug("remote device not found. remoteChassisId value: {}", remoteChassisId);
189 return new DefaultDevice(ProviderId.NONE,
190 DeviceId.deviceId(LLDP + mac.toString()),
191 Device.Type.SWITCH,
192 UNKNOWN,
193 UNKNOWN,
194 UNKNOWN,
195 UNKNOWN,
196 remoteChassisId,
197 DefaultAnnotations.EMPTY);
198 }
199 }
200 }
201
202 private Port findDestinationPortByName(String remotePortName,
203 String remotePortDesc,
204 DeviceService deviceService,
205 Device remoteDevice) {
206 Optional<Port> remotePort = deviceService.getPorts(remoteDevice.id())
207 .stream().filter(port -> remotePortName.equals(port.annotations().value(PORT_NAME))).findAny();
208 if (remotePort.isPresent()) {
209 return remotePort.get();
210 } else {
211 Optional<Port> remotePortByDesc = deviceService.getPorts(remoteDevice.id())
212 .stream().filter(port -> remotePortDesc.equals(port.annotations().value(PORT_NAME))).findAny();
213 if (remotePortByDesc.isPresent()) {
214 return remotePortByDesc.get();
215 } else {
216 int portNumber = Integer.valueOf(remotePortName.replaceAll("\\D+", ""));
217 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
218 .set(AnnotationKeys.PORT_NAME, remotePortName);
219 return new DefaultPort(remoteDevice, PortNumber.portNumber(portNumber),
220 true,
221 annotations.build());
222 }
223 }
224 }
225
226 private String retrieveResponse(String command) {
227 DriverHandler handler = handler();
228 RestSBController controller = checkNotNull(handler.get(RestSBController.class));
229 DeviceId deviceId = handler.data().deviceId();
230
231 String req = NxApiRequest.generate(Lists.newArrayList(command), NxApiRequest.CommandType.CLI);
232 log.debug("request :" + req);
233
234 InputStream stream = new ByteArrayInputStream(req.getBytes(StandardCharsets.UTF_8));
235 return controller.post(deviceId, "/ins", stream, MediaType.valueOf("application/json-rpc"), String.class);
236 }
237
238 private static Set<LinkDescription> buildLinkPair(DeviceId localDevId,
239 Port localPort,
240 DeviceId remoteDevId,
241 Port remotePort) {
242
243 Set<LinkDescription> linkDescriptions = Sets.newHashSet();
244 ConnectPoint local = new ConnectPoint(localDevId, localPort.number());
245 ConnectPoint remote = new ConnectPoint(remoteDevId, remotePort.number());
246 DefaultAnnotations annotations = DefaultAnnotations.builder()
247 .set("layer", "ETHERNET")
248 .build();
249 linkDescriptions.add(new DefaultLinkDescription(
250 remote, local, Link.Type.DIRECT, true, annotations));
251
252 return linkDescriptions;
253 }
254}