blob: 2aeea4fdc8d0a8f673f54ebc516e1f428652d577 [file] [log] [blame]
Michele Santuari21c14012016-11-14 13:31:33 +01001/*
2 * Copyright 2016-present Open Networking Laboratory
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.juniper;
18
Michele Santuari21c14012016-11-14 13:31:33 +010019import org.apache.commons.configuration.HierarchicalConfiguration;
20import org.onlab.packet.ChassisId;
21import org.onlab.packet.MacAddress;
22import org.onosproject.net.AnnotationKeys;
23import org.onosproject.net.ConnectPoint;
24import org.onosproject.net.DefaultAnnotations;
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -070025import org.onosproject.net.DefaultAnnotations.Builder;
Michele Santuari21c14012016-11-14 13:31:33 +010026import org.onosproject.net.DeviceId;
27import org.onosproject.net.Link;
28import org.onosproject.net.Port;
29import org.onosproject.net.PortNumber;
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -070030import org.onosproject.net.Port.Type;
Michele Santuari21c14012016-11-14 13:31:33 +010031import org.onosproject.net.device.DefaultDeviceDescription;
32import org.onosproject.net.device.DefaultPortDescription;
33import org.onosproject.net.device.DeviceDescription;
34import org.onosproject.net.device.PortDescription;
35import org.onosproject.net.link.DefaultLinkDescription;
36import org.onosproject.net.link.LinkDescription;
Yuta HIGUCHI0184a7b2017-03-31 13:13:58 -070037import org.slf4j.Logger;
Michele Santuari21c14012016-11-14 13:31:33 +010038
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -070039import com.google.common.base.Strings;
40
41import java.util.ArrayList;
Michele Santuari21c14012016-11-14 13:31:33 +010042import java.util.HashSet;
43import java.util.List;
44import java.util.Set;
45import java.util.regex.Matcher;
46import java.util.regex.Pattern;
47
Michele Santuari21c14012016-11-14 13:31:33 +010048import static org.onosproject.net.Device.Type.ROUTER;
Michele Santuari21c14012016-11-14 13:31:33 +010049import static org.onosproject.net.PortNumber.portNumber;
Yuta HIGUCHI0184a7b2017-03-31 13:13:58 -070050import static org.slf4j.LoggerFactory.getLogger;
Michele Santuari21c14012016-11-14 13:31:33 +010051
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -070052// Ref: Junos YANG:
53// https://github.com/Juniper/yang
Michele Santuari21c14012016-11-14 13:31:33 +010054/**
55 * Utility class for Netconf XML for Juniper.
56 * Tested with MX240 junos 14.2
57 */
58public final class JuniperUtils {
59
Yuta HIGUCHI0184a7b2017-03-31 13:13:58 -070060 private static final Logger log = getLogger(JuniperUtils.class);
61
Michele Santuari21c14012016-11-14 13:31:33 +010062 public static final String FAILED_CFG = "Failed to retrieve configuration.";
63
64 private static final String RPC_TAG_NETCONF_BASE = "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
65 private static final String RPC_CLOSE_TAG = "</rpc>";
66
67 //requests
68 public static final String REQ_LLDP_NBR_INFO = "<get-lldp-neighbors-information/>";
69 public static final String REQ_SYS_INFO = "<get-system-information/>";
70 public static final String REQ_MAC_ADD_INFO = "<get-chassis-mac-addresses/>";
71 public static final String REQ_IF_INFO = "<get-interface-information/>";
72
73 //helper strings for parsing
Michele Santuarif5945372017-01-19 16:39:21 +010074 private static final String LLDP_LIST_NBR_INFO = "lldp-neighbors-information";
75 private static final String LLDP_NBR_INFO = "lldp-neighbor-information";
Michele Santuari21c14012016-11-14 13:31:33 +010076 private static final String SYS_INFO = "system-information";
77 private static final String HW_MODEL = "hardware-model";
78 private static final String OS_NAME = "os-name";
79 private static final String OS_VER = "os-version";
80 private static final String SER_NUM = "serial-number";
81 private static final String IF_INFO = "interface-information";
82 private static final String IF_PHY = "physical-interface";
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -070083
Michele Santuari21c14012016-11-14 13:31:33 +010084 private static final String IF_TYPE = "if-type";
85 private static final String SPEED = "speed";
Michele Santuari21c14012016-11-14 13:31:33 +010086 private static final String NAME = "name";
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -070087
88 // seems to be unique index within device
Michele Santuari21c14012016-11-14 13:31:33 +010089 private static final String SNMP_INDEX = "snmp-index";
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -070090
Michele Santuari21c14012016-11-14 13:31:33 +010091 private static final String LLDP_LO_PORT = "lldp-local-port-id";
92 private static final String LLDP_REM_CHASS = "lldp-remote-chassis-id";
93 private static final String LLDP_REM_PORT = "lldp-remote-port-id";
94 private static final String REGEX_ADD =
95 ".*Private base address\\s*([:,0-9,a-f,A-F]*).*";
96 private static final Pattern ADD_PATTERN =
97 Pattern.compile(REGEX_ADD, Pattern.DOTALL);
98
99 private static final String JUNIPER = "JUNIPER";
100 private static final String UNKNOWN = "UNKNOWN";
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700101
102 /**
103 * Annotation key for interface type.
104 */
105 static final String AK_IF_TYPE = "ifType";
106
107 /**
108 * Annotation key for Logical link-layer encapsulation.
109 */
110 static final String AK_ENCAPSULATION = "encapsulation";
111
112 /**
113 * Annotation key for interface description.
114 */
115 static final String AK_DESCRIPTION = "description";
116
117 /**
118 * Annotation key for interface admin status. "up"/"down"
119 */
120 static final String AK_ADMIN_STATUS = "adminStatus";
121
122 /**
123 * Annotation key for interface operational status. "up"/"down"
124 */
125 static final String AK_OPER_STATUS = "operStatus";
126
127 /**
128 * Annotation key for logical-interfaces parent physical interface name.
129 */
130 static final String AK_PHYSICAL_PORT_NAME = "physicalPortName";
131
132
133 private static final String NUMERIC_SPEED_REGEXP = "(\\d+)([GM])bps";
134
135 /**
136 * {@value #NUMERIC_SPEED_REGEXP} as {@link Pattern}.
137 * Case insensitive
138 */
139 private static final Pattern SPEED_PATTERN =
140 Pattern.compile(NUMERIC_SPEED_REGEXP, Pattern.CASE_INSENSITIVE);
141
142 /**
143 * Default port speed {@value} Mbps.
144 */
Michele Santuari21c14012016-11-14 13:31:33 +0100145 private static final long DEFAULT_PORT_SPEED = 1000;
146
147
148 private JuniperUtils() {
149 //not called, preventing any allocation
150 }
151
152 /**
153 * Helper method to build a XML schema given a request.
154 *
155 * @param request a tag element of the XML schema
156 * @return string containing the XML schema
157 */
158 public static String requestBuilder(String request) {
159 return RPC_TAG_NETCONF_BASE +
160 request + RPC_CLOSE_TAG;
161 }
162
163 /**
164 * Parses device configuration and returns the device description.
165 *
166 * @param deviceId the id of the device
167 * @param sysInfoCfg system configuration
168 * @param chassisText chassis string
169 * @return device description
170 */
171 public static DeviceDescription parseJuniperDescription(DeviceId deviceId,
172 HierarchicalConfiguration sysInfoCfg,
173 String chassisText) {
174 HierarchicalConfiguration info = sysInfoCfg.configurationAt(SYS_INFO);
175
176 String hw = info.getString(HW_MODEL) == null ? UNKNOWN : info.getString(HW_MODEL);
177 String sw = UNKNOWN;
178 if (info.getString(OS_NAME) != null || info.getString(OS_VER) != null) {
179 sw = info.getString(OS_NAME) + " " + info.getString(OS_VER);
180 }
181 String serial = info.getString(SER_NUM) == null ? UNKNOWN : info.getString(SER_NUM);
182
183 Matcher matcher = ADD_PATTERN.matcher(chassisText);
184 if (matcher.lookingAt()) {
185 String chassis = matcher.group(1);
186 MacAddress chassisMac = MacAddress.valueOf(chassis);
187 return new DefaultDeviceDescription(deviceId.uri(), ROUTER,
188 JUNIPER, hw, sw, serial,
189 new ChassisId(chassisMac.toLong()),
190 DefaultAnnotations.EMPTY);
191 }
192 return new DefaultDeviceDescription(deviceId.uri(), ROUTER,
193 JUNIPER, hw, sw, serial,
194 null, DefaultAnnotations.EMPTY);
195 }
196
197 /**
198 * Parses device ports configuration and returns a list of
199 * port description.
200 *
201 * @param cfg interface configuration
202 * @return list of interface descriptions of the device
203 */
204 public static List<PortDescription> parseJuniperPorts(HierarchicalConfiguration cfg) {
205 //This methods ignores some internal ports
206
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700207 List<PortDescription> portDescriptions = new ArrayList<>();
Michele Santuari21c14012016-11-14 13:31:33 +0100208 List<HierarchicalConfiguration> subtrees =
209 cfg.configurationsAt(IF_INFO);
210 for (HierarchicalConfiguration interfInfo : subtrees) {
211 List<HierarchicalConfiguration> interfaceTree =
212 interfInfo.configurationsAt(IF_PHY);
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700213 for (HierarchicalConfiguration phyIntf : interfaceTree) {
214 if (phyIntf == null) {
215 continue;
Michele Santuari21c14012016-11-14 13:31:33 +0100216 }
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700217 // parse physical Interface
218 parsePhysicalInterface(portDescriptions, phyIntf);
Michele Santuari21c14012016-11-14 13:31:33 +0100219 }
220 }
221 return portDescriptions;
222 }
223
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700224 /**
225 * Parses {@literal physical-interface} tree.
226 *
227 * @param portDescriptions list to populate Ports found parsing configuration
228 * @param phyIntf physical-interface
229 */
230 private static void parsePhysicalInterface(List<PortDescription> portDescriptions,
231 HierarchicalConfiguration phyIntf) {
232 Builder annotations = DefaultAnnotations.builder();
233 PortNumber portNumber = portNumber(phyIntf.getString(SNMP_INDEX));
234 String phyPortName = phyIntf.getString(NAME);
235 if (portNumber == null) {
236 log.debug("Skipping physical-interface {}, no PortNumer",
237 phyPortName);
238 log.trace(" {}", phyIntf);
239 return;
Michele Santuari21c14012016-11-14 13:31:33 +0100240 }
241
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700242 setIfNonNull(annotations,
243 AnnotationKeys.PORT_NAME,
244 phyPortName);
Michele Santuari21c14012016-11-14 13:31:33 +0100245
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700246 setIfNonNull(annotations,
247 AnnotationKeys.PORT_MAC,
248 phyIntf.getString("current-physical-address"));
Michele Santuari21c14012016-11-14 13:31:33 +0100249
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700250 setIfNonNull(annotations,
251 AK_IF_TYPE,
252 phyIntf.getString(IF_TYPE));
Michele Santuari21c14012016-11-14 13:31:33 +0100253
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700254 setIfNonNull(annotations,
255 AK_DESCRIPTION,
256 phyIntf.getString("description"));
Michele Santuari21c14012016-11-14 13:31:33 +0100257
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700258 boolean opUp = phyIntf.getString(AK_OPER_STATUS, "down").equals("up");
259 annotations.set("oper-status", toUpDown(opUp));
Michele Santuari21c14012016-11-14 13:31:33 +0100260
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700261 boolean admUp = phyIntf.getString(AK_ADMIN_STATUS, "down").equals("up");
262 annotations.set("admin-status", toUpDown(admUp));
Michele Santuari21c14012016-11-14 13:31:33 +0100263
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700264 long portSpeed = toMbps(phyIntf.getString(SPEED));
Michele Santuari21c14012016-11-14 13:31:33 +0100265
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700266 portDescriptions.add(new DefaultPortDescription(portNumber,
267 admUp & opUp,
268 Type.COPPER,
269 portSpeed,
270 annotations.build()));
Michele Santuari21c14012016-11-14 13:31:33 +0100271
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700272 // parse each logical Interface
273 for (HierarchicalConfiguration logIntf : phyIntf.configurationsAt("logical-interface")) {
274 if (logIntf == null) {
275 continue;
276 }
277 PortNumber lPortNumber = safePortNumber(logIntf.getString(SNMP_INDEX));
278 if (lPortNumber == null) {
279 log.debug("Skipping logical-interface {} under {}, no PortNumer",
280 logIntf.getString(NAME), phyPortName);
281 log.trace(" {}", logIntf);
282 continue;
283 }
284
285 Builder lannotations = DefaultAnnotations.builder();
286 setIfNonNull(lannotations,
287 AnnotationKeys.PORT_NAME,
288 logIntf.getString(NAME));
289 setIfNonNull(lannotations,
290 AK_PHYSICAL_PORT_NAME,
291 phyPortName);
292
293 String afName = logIntf.getString("address-family.address-family-name");
294 String address = logIntf.getString("address-family.interface-address.ifa-local");
295 if (afName != null && address != null) {
296 // e.g., inet : IPV4, inet6 : IPV6
297 setIfNonNull(lannotations, afName, address);
298 }
299
300 // preserving former behavior
301 setIfNonNull(lannotations,
302 "ip",
303 logIntf.getString("address-family.interface-address.ifa-local"));
304
305 setIfNonNull(lannotations,
306 AK_ENCAPSULATION, logIntf.getString("encapsulation"));
307
308 // TODO confirm if this is correct.
309 // Looking at sample data,
310 // it seemed all logical loop-back interfaces were down
311 boolean lEnabled = logIntf.getString("if-config-flags.iff-up") != null;
312
313 portDescriptions.add(new DefaultPortDescription(lPortNumber,
314 admUp & opUp & lEnabled,
315 Type.COPPER,
316 portSpeed,
317 lannotations.build()));
Michele Santuari21c14012016-11-14 13:31:33 +0100318 }
Michele Santuari21c14012016-11-14 13:31:33 +0100319 }
320
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700321 /**
322 * Port status as "up"/"down".
323 *
324 * @param portStatus port status
325 * @return "up" if {@code portStats} is {@literal true}, "down" otherwise
326 */
327 static String toUpDown(boolean portStatus) {
328 return portStatus ? "up" : "down";
329 }
330
331 /**
332 * Translate interface {@literal speed} value as Mbps value.
333 *
334 * Note: {@literal Unlimited} and unrecognizable string will be treated as
335 * {@value #DEFAULT_PORT_SPEED} Mbps.
336 *
337 * @param speed in String
338 * @return Mbps
339 */
340 static long toMbps(String speed) {
341 String s = Strings.nullToEmpty(speed).trim().toLowerCase();
342 Matcher matcher = SPEED_PATTERN.matcher(s);
343 if (matcher.matches()) {
344 // numeric
345 int n = Integer.parseInt(matcher.group(1));
346 String unit = matcher.group(2);
347 if ("m".equalsIgnoreCase(unit)) {
348 // Mbps
349 return n;
350 } else {
351 // assume Gbps
352 return 1000 * n;
353 }
Michele Santuari21c14012016-11-14 13:31:33 +0100354 }
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700355 log.trace("Treating unknown speed value {} as default", speed);
356 // Unlimited or unrecognizable
357 return DEFAULT_PORT_SPEED;
358 }
359
360 /**
361 * Sets annotation entry if {@literal value} was not {@literal null}.
362 *
363 * @param builder Annotation Builder
364 * @param key Annotation key
365 * @param value Annotation value (can be {@literal null})
366 */
367 static void setIfNonNull(Builder builder, String key, String value) {
368 if (value != null) {
369 builder.set(key, value.trim());
370 }
371 }
372
373 /**
374 * Creates PortNumber instance from String.
375 *
376 * Instead for throwing Exception, it will return null on format error.
377 *
378 * @param s port number as string
379 * @return PortNumber instance or null on error
380 */
381 static PortNumber safePortNumber(String s) {
382 try {
383 return portNumber(s);
384 } catch (RuntimeException e) {
385 log.trace("Failed parsing PortNumber {}", s, e);
386 }
387 return null;
Michele Santuari21c14012016-11-14 13:31:33 +0100388 }
389
390 /**
391 * Create two LinkDescriptions corresponding to the bidirectional links.
392 *
393 * @param localDevId the identity of the local device
394 * @param localPort the port of the local device
395 * @param remoteDevId the identity of the remote device
396 * @param remotePort the port of the remote device
397 * @param descs the collection to which the link descriptions
398 * should be added
399 */
400 public static void createBiDirLinkDescription(DeviceId localDevId,
401 Port localPort,
402 DeviceId remoteDevId,
403 Port remotePort,
404 Set<LinkDescription> descs) {
405
406 ConnectPoint local = new ConnectPoint(localDevId, localPort.number());
407 ConnectPoint remote = new ConnectPoint(remoteDevId, remotePort.number());
408 DefaultAnnotations annotations = DefaultAnnotations.builder()
409 .set("layer", "IP")
410 .build();
411 descs.add(new DefaultLinkDescription(
412 local, remote, Link.Type.INDIRECT, false, annotations));
413 descs.add(new DefaultLinkDescription(
414 remote, local, Link.Type.INDIRECT, false, annotations));
415 }
416
417 /**
418 * Parses neighbours discovery information and returns a list of
419 * link abstractions.
420 *
421 * @param info interface configuration
422 * @return set of link abstractions
423 */
424 public static Set<LinkAbstraction> parseJuniperLldp(HierarchicalConfiguration info) {
425 Set<LinkAbstraction> neighbour = new HashSet<>();
426 List<HierarchicalConfiguration> subtrees =
Michele Santuarif5945372017-01-19 16:39:21 +0100427 info.configurationsAt(LLDP_LIST_NBR_INFO);
Michele Santuari21c14012016-11-14 13:31:33 +0100428 for (HierarchicalConfiguration neighborsInfo : subtrees) {
429 List<HierarchicalConfiguration> neighbors =
430 neighborsInfo.configurationsAt(LLDP_NBR_INFO);
431 for (HierarchicalConfiguration neighbor : neighbors) {
432 String localPortName = neighbor.getString(LLDP_LO_PORT);
433 MacAddress mac = MacAddress.valueOf(
434 neighbor.getString(LLDP_REM_CHASS));
435 int remotePortIndex =
436 neighbor.getInt(LLDP_REM_PORT);
437 LinkAbstraction link = new LinkAbstraction(
438 localPortName,
439 mac.toLong(),
440 remotePortIndex);
441 neighbour.add(link);
442 }
443 }
444 return neighbour;
445 }
446
447 /**
448 * Device representation of the adjacency at the IP Layer.
449 */
450 protected static final class LinkAbstraction {
451 protected String localPortName;
452 protected ChassisId remoteChassisId;
453 protected int remotePortIndex;
454
455 protected LinkAbstraction(String pName, long chassisId, int pIndex) {
456 this.localPortName = pName;
457 this.remoteChassisId = new ChassisId(chassisId);
458 this.remotePortIndex = pIndex;
459 }
460 }
461}