blob: 14fb38a64b20edc2fd552ba5542a1b48ebc1d978 [file] [log] [blame]
Michele Santuari21c14012016-11-14 13:31:33 +01001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Michele Santuari21c14012016-11-14 13:31:33 +01003 *
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;
Michele Santuarice256492017-06-23 14:44:01 +020020import org.apache.commons.lang.StringUtils;
Michele Santuari21c14012016-11-14 13:31:33 +010021import org.onlab.packet.ChassisId;
Michele Santuarice256492017-06-23 14:44:01 +020022import org.onlab.packet.Ip4Address;
23import org.onlab.packet.Ip4Prefix;
DongRyeol Cha5f870032018-07-12 18:36:20 +090024import org.onlab.packet.IpAddress;
Michele Santuari21c14012016-11-14 13:31:33 +010025import org.onlab.packet.MacAddress;
26import org.onosproject.net.AnnotationKeys;
27import org.onosproject.net.ConnectPoint;
28import org.onosproject.net.DefaultAnnotations;
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -070029import org.onosproject.net.DefaultAnnotations.Builder;
Michele Santuari21c14012016-11-14 13:31:33 +010030import org.onosproject.net.DeviceId;
31import org.onosproject.net.Link;
32import org.onosproject.net.Port;
33import org.onosproject.net.PortNumber;
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -070034import org.onosproject.net.Port.Type;
DongRyeol Cha5f870032018-07-12 18:36:20 +090035import org.onosproject.net.behaviour.ControllerInfo;
Michele Santuari21c14012016-11-14 13:31:33 +010036import org.onosproject.net.device.DefaultDeviceDescription;
37import org.onosproject.net.device.DefaultPortDescription;
38import org.onosproject.net.device.DeviceDescription;
39import org.onosproject.net.device.PortDescription;
40import org.onosproject.net.link.DefaultLinkDescription;
41import org.onosproject.net.link.LinkDescription;
Yuta HIGUCHI0184a7b2017-03-31 13:13:58 -070042import org.slf4j.Logger;
Michele Santuari21c14012016-11-14 13:31:33 +010043
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -070044import com.google.common.base.Strings;
45
46import java.util.ArrayList;
Michele Santuarice256492017-06-23 14:44:01 +020047import java.util.Collection;
Michele Santuari21c14012016-11-14 13:31:33 +010048import java.util.HashSet;
49import java.util.List;
Michele Santuarice256492017-06-23 14:44:01 +020050import java.util.Optional;
Michele Santuari21c14012016-11-14 13:31:33 +010051import java.util.Set;
52import java.util.regex.Matcher;
53import java.util.regex.Pattern;
54
Michele Santuarice256492017-06-23 14:44:01 +020055import static org.onosproject.drivers.juniper.StaticRoute.DEFAULT_METRIC_STATIC_ROUTE;
56import static org.onosproject.drivers.juniper.StaticRoute.toFlowRulePriority;
Michele Santuari21c14012016-11-14 13:31:33 +010057import static org.onosproject.net.Device.Type.ROUTER;
Michele Santuari21c14012016-11-14 13:31:33 +010058import static org.onosproject.net.PortNumber.portNumber;
Yuta HIGUCHI0184a7b2017-03-31 13:13:58 -070059import static org.slf4j.LoggerFactory.getLogger;
Michele Santuari21c14012016-11-14 13:31:33 +010060
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -070061// Ref: Junos YANG:
62// https://github.com/Juniper/yang
Michele Santuarice256492017-06-23 14:44:01 +020063
Michele Santuari21c14012016-11-14 13:31:33 +010064/**
65 * Utility class for Netconf XML for Juniper.
66 * Tested with MX240 junos 14.2
67 */
68public final class JuniperUtils {
69
Yuta HIGUCHI0184a7b2017-03-31 13:13:58 -070070 private static final Logger log = getLogger(JuniperUtils.class);
71
Michele Santuari21c14012016-11-14 13:31:33 +010072 public static final String FAILED_CFG = "Failed to retrieve configuration.";
73
74 private static final String RPC_TAG_NETCONF_BASE = "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
75 private static final String RPC_CLOSE_TAG = "</rpc>";
76
77 //requests
78 public static final String REQ_LLDP_NBR_INFO = "<get-lldp-neighbors-information/>";
79 public static final String REQ_SYS_INFO = "<get-system-information/>";
80 public static final String REQ_MAC_ADD_INFO = "<get-chassis-mac-addresses/>";
81 public static final String REQ_IF_INFO = "<get-interface-information/>";
82
83 //helper strings for parsing
Michele Santuarif5945372017-01-19 16:39:21 +010084 private static final String LLDP_LIST_NBR_INFO = "lldp-neighbors-information";
85 private static final String LLDP_NBR_INFO = "lldp-neighbor-information";
Michele Santuari21c14012016-11-14 13:31:33 +010086 private static final String SYS_INFO = "system-information";
87 private static final String HW_MODEL = "hardware-model";
88 private static final String OS_NAME = "os-name";
89 private static final String OS_VER = "os-version";
90 private static final String SER_NUM = "serial-number";
91 private static final String IF_INFO = "interface-information";
92 private static final String IF_PHY = "physical-interface";
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -070093
Michele Santuari21c14012016-11-14 13:31:33 +010094 private static final String IF_TYPE = "if-type";
DongRyeol Chae23b18b2018-11-20 15:09:50 +090095 private static final String IF_MEDIA_TYPE = "if-media-type";
Michele Santuari21c14012016-11-14 13:31:33 +010096 private static final String SPEED = "speed";
Michele Santuari21c14012016-11-14 13:31:33 +010097 private static final String NAME = "name";
DongRyeol Cha5f870032018-07-12 18:36:20 +090098 private static final String PORT = "port";
99 private static final String PROTOCOL = "protocol";
100
DongRyeol Chae23b18b2018-11-20 15:09:50 +0900101 private static final String FIBER = "fiber";
102 private static final String COPPER = "copper";
103
DongRyeol Cha5f870032018-07-12 18:36:20 +0900104 private static final String TCP = "tcp";
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700105
106 // seems to be unique index within device
Michele Santuari21c14012016-11-14 13:31:33 +0100107 private static final String SNMP_INDEX = "snmp-index";
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700108
Michele Santuari21c14012016-11-14 13:31:33 +0100109 private static final String LLDP_LO_PORT = "lldp-local-port-id";
110 private static final String LLDP_REM_CHASS = "lldp-remote-chassis-id";
donghyeok.hoe6d71872018-07-25 18:57:44 +0900111 private static final String LLDP_REM_PORT_SUBTYPE = "lldp-remote-port-id-subtype";
Michele Santuari21c14012016-11-14 13:31:33 +0100112 private static final String LLDP_REM_PORT = "lldp-remote-port-id";
donghyeok.ho82749aa2018-07-16 19:57:03 +0900113 private static final String LLDP_REM_PORT_DES = "lldp-remote-port-description";
donghyeok.hoe6d71872018-07-25 18:57:44 +0900114 private static final String LLDP_SUBTYPE_MAC = "Mac address";
115 private static final String LLDP_SUBTYPE_INTERFACE_NAME = "Interface name";
Michele Santuari21c14012016-11-14 13:31:33 +0100116 private static final String REGEX_ADD =
117 ".*Private base address\\s*([:,0-9,a-f,A-F]*).*";
118 private static final Pattern ADD_PATTERN =
119 Pattern.compile(REGEX_ADD, Pattern.DOTALL);
120
Michele Santuarice256492017-06-23 14:44:01 +0200121 public static final String PROTOCOL_NAME = "protocol-name";
122
Michele Santuari21c14012016-11-14 13:31:33 +0100123 private static final String JUNIPER = "JUNIPER";
124 private static final String UNKNOWN = "UNKNOWN";
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700125
126 /**
127 * Annotation key for interface type.
128 */
129 static final String AK_IF_TYPE = "ifType";
130
131 /**
132 * Annotation key for Logical link-layer encapsulation.
133 */
134 static final String AK_ENCAPSULATION = "encapsulation";
135
136 /**
137 * Annotation key for interface description.
138 */
139 static final String AK_DESCRIPTION = "description";
140
141 /**
142 * Annotation key for interface admin status. "up"/"down"
143 */
144 static final String AK_ADMIN_STATUS = "adminStatus";
145
146 /**
147 * Annotation key for interface operational status. "up"/"down"
148 */
149 static final String AK_OPER_STATUS = "operStatus";
150
151 /**
152 * Annotation key for logical-interfaces parent physical interface name.
153 */
154 static final String AK_PHYSICAL_PORT_NAME = "physicalPortName";
155
156
157 private static final String NUMERIC_SPEED_REGEXP = "(\\d+)([GM])bps";
158
159 /**
160 * {@value #NUMERIC_SPEED_REGEXP} as {@link Pattern}.
161 * Case insensitive
162 */
163 private static final Pattern SPEED_PATTERN =
164 Pattern.compile(NUMERIC_SPEED_REGEXP, Pattern.CASE_INSENSITIVE);
165
166 /**
167 * Default port speed {@value} Mbps.
168 */
Michele Santuari21c14012016-11-14 13:31:33 +0100169 private static final long DEFAULT_PORT_SPEED = 1000;
170
171
172 private JuniperUtils() {
173 //not called, preventing any allocation
174 }
175
176 /**
177 * Helper method to build a XML schema given a request.
178 *
179 * @param request a tag element of the XML schema
180 * @return string containing the XML schema
181 */
182 public static String requestBuilder(String request) {
183 return RPC_TAG_NETCONF_BASE +
184 request + RPC_CLOSE_TAG;
185 }
186
187 /**
Michele Santuarice256492017-06-23 14:44:01 +0200188 * Helper method to commit a config.
189 *
190 * @return string contains the result of the commit
191 */
192 public static String commitBuilder() {
193 return RPC_TAG_NETCONF_BASE +
194 "<commit/>" + RPC_CLOSE_TAG;
195 }
196
197 /**
198 * Helper method to build the schema for returning to a previously
199 * committed configuration.
200 *
201 * @param versionToReturn Configuration to return to. The range of values is from 0 through 49.
202 * The most recently saved configuration is number 0,
203 * and the oldest saved configuration is number 49.
204 * @return string containing the XML schema
205 */
206 public static String rollbackBuilder(int versionToReturn) {
207 return RPC_TAG_NETCONF_BASE +
208 "<get-rollback-information>" +
209 "<rollback>" + versionToReturn + "</rollback>" +
210 "</get-rollback-information>" +
211 RPC_CLOSE_TAG;
212 }
213
214
215 /**
216 * Helper method to build an XML schema to configure a static route
217 * given a {@link StaticRoute}.
218 *
219 * @param staticRoute the static route to be configured
220 * @return string contains the result of the configuration
221 */
222 public static String routeAddBuilder(StaticRoute staticRoute) {
223 StringBuilder rpc = new StringBuilder("<configuration>\n");
224 rpc.append("<routing-options>\n");
225 rpc.append("<static>\n");
226 rpc.append("<route>\n");
227 rpc.append("<destination>" + staticRoute.ipv4Dst().toString() + "</destination>\n");
228 rpc.append("<next-hop>" + staticRoute.nextHop() + "</next-hop>\n");
229
230 if (staticRoute.getMetric() != DEFAULT_METRIC_STATIC_ROUTE) {
231 rpc.append("<metric>" + staticRoute.getMetric() + "</metric>");
232 }
233
234 rpc.append("</route>\n");
235 rpc.append("</static>\n");
236 rpc.append("</routing-options>\n");
237 rpc.append("</configuration>\n");
238
239 return rpc.toString();
240 }
241
242 /**
243 * Helper method to build a XML schema to delete a static route
244 * given a {@link StaticRoute}.
245 * @param staticRoute the static route to be deleted
246 * @return string contains the result of the configuratio
247 */
248 public static String routeDeleteBuilder(StaticRoute staticRoute) {
249 return "<configuration>\n" +
250 "<routing-options>\n" +
251 "<static>\n" +
252 "<route operation=\"delete\">\n" +
253 "<name>" + staticRoute.ipv4Dst().toString() + "</name>\n" +
254 "</route>\n" +
255 "</static>\n" +
256 "</routing-options>\n" +
257 "</configuration>\n";
258 }
259
260
261 /**
Michele Santuari21c14012016-11-14 13:31:33 +0100262 * Parses device configuration and returns the device description.
263 *
264 * @param deviceId the id of the device
265 * @param sysInfoCfg system configuration
266 * @param chassisText chassis string
267 * @return device description
268 */
269 public static DeviceDescription parseJuniperDescription(DeviceId deviceId,
270 HierarchicalConfiguration sysInfoCfg,
271 String chassisText) {
272 HierarchicalConfiguration info = sysInfoCfg.configurationAt(SYS_INFO);
273
274 String hw = info.getString(HW_MODEL) == null ? UNKNOWN : info.getString(HW_MODEL);
275 String sw = UNKNOWN;
276 if (info.getString(OS_NAME) != null || info.getString(OS_VER) != null) {
277 sw = info.getString(OS_NAME) + " " + info.getString(OS_VER);
278 }
279 String serial = info.getString(SER_NUM) == null ? UNKNOWN : info.getString(SER_NUM);
280
281 Matcher matcher = ADD_PATTERN.matcher(chassisText);
282 if (matcher.lookingAt()) {
283 String chassis = matcher.group(1);
284 MacAddress chassisMac = MacAddress.valueOf(chassis);
285 return new DefaultDeviceDescription(deviceId.uri(), ROUTER,
286 JUNIPER, hw, sw, serial,
287 new ChassisId(chassisMac.toLong()),
288 DefaultAnnotations.EMPTY);
289 }
290 return new DefaultDeviceDescription(deviceId.uri(), ROUTER,
291 JUNIPER, hw, sw, serial,
292 null, DefaultAnnotations.EMPTY);
293 }
294
295 /**
296 * Parses device ports configuration and returns a list of
297 * port description.
298 *
299 * @param cfg interface configuration
300 * @return list of interface descriptions of the device
301 */
302 public static List<PortDescription> parseJuniperPorts(HierarchicalConfiguration cfg) {
303 //This methods ignores some internal ports
304
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700305 List<PortDescription> portDescriptions = new ArrayList<>();
Michele Santuari21c14012016-11-14 13:31:33 +0100306 List<HierarchicalConfiguration> subtrees =
307 cfg.configurationsAt(IF_INFO);
308 for (HierarchicalConfiguration interfInfo : subtrees) {
309 List<HierarchicalConfiguration> interfaceTree =
310 interfInfo.configurationsAt(IF_PHY);
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700311 for (HierarchicalConfiguration phyIntf : interfaceTree) {
312 if (phyIntf == null) {
313 continue;
Michele Santuari21c14012016-11-14 13:31:33 +0100314 }
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700315 // parse physical Interface
316 parsePhysicalInterface(portDescriptions, phyIntf);
Michele Santuari21c14012016-11-14 13:31:33 +0100317 }
318 }
319 return portDescriptions;
320 }
321
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700322 /**
323 * Parses {@literal physical-interface} tree.
324 *
325 * @param portDescriptions list to populate Ports found parsing configuration
326 * @param phyIntf physical-interface
327 */
328 private static void parsePhysicalInterface(List<PortDescription> portDescriptions,
329 HierarchicalConfiguration phyIntf) {
330 Builder annotations = DefaultAnnotations.builder();
331 PortNumber portNumber = portNumber(phyIntf.getString(SNMP_INDEX));
332 String phyPortName = phyIntf.getString(NAME);
333 if (portNumber == null) {
334 log.debug("Skipping physical-interface {}, no PortNumer",
335 phyPortName);
336 log.trace(" {}", phyIntf);
337 return;
Michele Santuari21c14012016-11-14 13:31:33 +0100338 }
339
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700340 setIfNonNull(annotations,
341 AnnotationKeys.PORT_NAME,
342 phyPortName);
Michele Santuari21c14012016-11-14 13:31:33 +0100343
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700344 setIfNonNull(annotations,
345 AnnotationKeys.PORT_MAC,
346 phyIntf.getString("current-physical-address"));
Michele Santuari21c14012016-11-14 13:31:33 +0100347
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700348 setIfNonNull(annotations,
349 AK_IF_TYPE,
350 phyIntf.getString(IF_TYPE));
Michele Santuari21c14012016-11-14 13:31:33 +0100351
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700352 setIfNonNull(annotations,
353 AK_DESCRIPTION,
354 phyIntf.getString("description"));
Michele Santuari21c14012016-11-14 13:31:33 +0100355
Michele Santuari0b832652017-06-05 16:59:11 +0200356 boolean opUp = phyIntf.getString("oper-status", "down").equals("up");
357 annotations.set(AK_OPER_STATUS, toUpDown(opUp));
Michele Santuari21c14012016-11-14 13:31:33 +0100358
Michele Santuari0b832652017-06-05 16:59:11 +0200359 boolean admUp = phyIntf.getString("admin-status", "down").equals("up");
360 annotations.set(AK_ADMIN_STATUS, toUpDown(admUp));
Michele Santuari21c14012016-11-14 13:31:33 +0100361
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700362 long portSpeed = toMbps(phyIntf.getString(SPEED));
DongRyeol Chae23b18b2018-11-20 15:09:50 +0900363 Type portType = phyIntf.getString(IF_MEDIA_TYPE, COPPER).equalsIgnoreCase(FIBER) ? Type.FIBER : Type.COPPER;
Michele Santuari21c14012016-11-14 13:31:33 +0100364
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800365 portDescriptions.add(DefaultPortDescription.builder()
366 .withPortNumber(portNumber)
367 .isEnabled(admUp && opUp)
DongRyeol Chae23b18b2018-11-20 15:09:50 +0900368 .type(portType)
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800369 .portSpeed(portSpeed)
370 .annotations(annotations.build()).build());
Michele Santuari21c14012016-11-14 13:31:33 +0100371
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700372 // parse each logical Interface
373 for (HierarchicalConfiguration logIntf : phyIntf.configurationsAt("logical-interface")) {
374 if (logIntf == null) {
375 continue;
376 }
377 PortNumber lPortNumber = safePortNumber(logIntf.getString(SNMP_INDEX));
378 if (lPortNumber == null) {
379 log.debug("Skipping logical-interface {} under {}, no PortNumer",
380 logIntf.getString(NAME), phyPortName);
381 log.trace(" {}", logIntf);
382 continue;
383 }
384
385 Builder lannotations = DefaultAnnotations.builder();
386 setIfNonNull(lannotations,
387 AnnotationKeys.PORT_NAME,
388 logIntf.getString(NAME));
389 setIfNonNull(lannotations,
390 AK_PHYSICAL_PORT_NAME,
391 phyPortName);
392
393 String afName = logIntf.getString("address-family.address-family-name");
394 String address = logIntf.getString("address-family.interface-address.ifa-local");
395 if (afName != null && address != null) {
396 // e.g., inet : IPV4, inet6 : IPV6
397 setIfNonNull(lannotations, afName, address);
398 }
399
400 // preserving former behavior
401 setIfNonNull(lannotations,
402 "ip",
403 logIntf.getString("address-family.interface-address.ifa-local"));
404
405 setIfNonNull(lannotations,
406 AK_ENCAPSULATION, logIntf.getString("encapsulation"));
407
408 // TODO confirm if this is correct.
409 // Looking at sample data,
410 // it seemed all logical loop-back interfaces were down
411 boolean lEnabled = logIntf.getString("if-config-flags.iff-up") != null;
412
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800413 portDescriptions.add(DefaultPortDescription.builder()
414 .withPortNumber(lPortNumber)
415 .isEnabled(admUp && opUp && lEnabled)
DongRyeol Chae23b18b2018-11-20 15:09:50 +0900416 .type(portType)
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800417 .portSpeed(portSpeed).annotations(lannotations.build())
418 .build());
Michele Santuari21c14012016-11-14 13:31:33 +0100419 }
Michele Santuari21c14012016-11-14 13:31:33 +0100420 }
421
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700422 /**
423 * Port status as "up"/"down".
424 *
425 * @param portStatus port status
426 * @return "up" if {@code portStats} is {@literal true}, "down" otherwise
427 */
428 static String toUpDown(boolean portStatus) {
429 return portStatus ? "up" : "down";
430 }
431
432 /**
433 * Translate interface {@literal speed} value as Mbps value.
434 *
435 * Note: {@literal Unlimited} and unrecognizable string will be treated as
436 * {@value #DEFAULT_PORT_SPEED} Mbps.
437 *
438 * @param speed in String
439 * @return Mbps
440 */
441 static long toMbps(String speed) {
442 String s = Strings.nullToEmpty(speed).trim().toLowerCase();
443 Matcher matcher = SPEED_PATTERN.matcher(s);
444 if (matcher.matches()) {
445 // numeric
Ray Milkey3717e602018-02-01 13:49:47 -0800446 long n = Long.parseLong(matcher.group(1));
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700447 String unit = matcher.group(2);
448 if ("m".equalsIgnoreCase(unit)) {
449 // Mbps
450 return n;
451 } else {
452 // assume Gbps
453 return 1000 * n;
454 }
Michele Santuari21c14012016-11-14 13:31:33 +0100455 }
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700456 log.trace("Treating unknown speed value {} as default", speed);
457 // Unlimited or unrecognizable
458 return DEFAULT_PORT_SPEED;
459 }
460
461 /**
462 * Sets annotation entry if {@literal value} was not {@literal null}.
463 *
464 * @param builder Annotation Builder
465 * @param key Annotation key
466 * @param value Annotation value (can be {@literal null})
467 */
468 static void setIfNonNull(Builder builder, String key, String value) {
469 if (value != null) {
470 builder.set(key, value.trim());
471 }
472 }
473
474 /**
475 * Creates PortNumber instance from String.
476 *
477 * Instead for throwing Exception, it will return null on format error.
478 *
479 * @param s port number as string
480 * @return PortNumber instance or null on error
481 */
482 static PortNumber safePortNumber(String s) {
483 try {
484 return portNumber(s);
485 } catch (RuntimeException e) {
486 log.trace("Failed parsing PortNumber {}", s, e);
487 }
488 return null;
Michele Santuari21c14012016-11-14 13:31:33 +0100489 }
490
491 /**
492 * Create two LinkDescriptions corresponding to the bidirectional links.
493 *
494 * @param localDevId the identity of the local device
495 * @param localPort the port of the local device
496 * @param remoteDevId the identity of the remote device
497 * @param remotePort the port of the remote device
498 * @param descs the collection to which the link descriptions
499 * should be added
500 */
501 public static void createBiDirLinkDescription(DeviceId localDevId,
502 Port localPort,
503 DeviceId remoteDevId,
504 Port remotePort,
505 Set<LinkDescription> descs) {
506
507 ConnectPoint local = new ConnectPoint(localDevId, localPort.number());
508 ConnectPoint remote = new ConnectPoint(remoteDevId, remotePort.number());
509 DefaultAnnotations annotations = DefaultAnnotations.builder()
DongRyeol Chace65cc02018-07-23 15:02:28 +0900510 .set(AnnotationKeys.LAYER, "ETHERNET")
Michele Santuari21c14012016-11-14 13:31:33 +0100511 .build();
512 descs.add(new DefaultLinkDescription(
DongRyeol Chace65cc02018-07-23 15:02:28 +0900513 local, remote, Link.Type.DIRECT, true, annotations));
Michele Santuari21c14012016-11-14 13:31:33 +0100514 descs.add(new DefaultLinkDescription(
DongRyeol Chace65cc02018-07-23 15:02:28 +0900515 remote, local, Link.Type.DIRECT, true, annotations));
516 }
517
518 /**
519 * Create one way LinkDescriptions.
520 *
521 * @param localDevId the identity of the local device
522 * @param localPort the port of the local device
523 * @param remoteDevId the identity of the remote device
524 * @param remotePort the port of the remote device
525 * @param descs the collection to which the link descriptions
526 * should be added
527 */
528 public static void createOneWayLinkDescription(DeviceId localDevId,
529 Port localPort,
530 DeviceId remoteDevId,
531 Port remotePort,
532 Set<LinkDescription> descs) {
533
534 ConnectPoint local = new ConnectPoint(localDevId, localPort.number());
535 ConnectPoint remote = new ConnectPoint(remoteDevId, remotePort.number());
536 DefaultAnnotations annotations = DefaultAnnotations.builder()
537 .set(AnnotationKeys.LAYER, "ETHERNET")
538 .build();
539 descs.add(new DefaultLinkDescription(
540 remote, local, Link.Type.DIRECT, true, annotations));
Michele Santuari21c14012016-11-14 13:31:33 +0100541 }
542
543 /**
544 * Parses neighbours discovery information and returns a list of
545 * link abstractions.
546 *
547 * @param info interface configuration
548 * @return set of link abstractions
549 */
550 public static Set<LinkAbstraction> parseJuniperLldp(HierarchicalConfiguration info) {
551 Set<LinkAbstraction> neighbour = new HashSet<>();
552 List<HierarchicalConfiguration> subtrees =
Michele Santuarif5945372017-01-19 16:39:21 +0100553 info.configurationsAt(LLDP_LIST_NBR_INFO);
Michele Santuari21c14012016-11-14 13:31:33 +0100554 for (HierarchicalConfiguration neighborsInfo : subtrees) {
555 List<HierarchicalConfiguration> neighbors =
556 neighborsInfo.configurationsAt(LLDP_NBR_INFO);
557 for (HierarchicalConfiguration neighbor : neighbors) {
558 String localPortName = neighbor.getString(LLDP_LO_PORT);
donghyeok.hoe6d71872018-07-25 18:57:44 +0900559 MacAddress mac = MacAddress.valueOf(neighbor.getString(LLDP_REM_CHASS));
560 String remotePortId = null;
561 long remotePortIndex = -1;
562 String remotePortIdSubtype = neighbor.getString(LLDP_REM_PORT_SUBTYPE, null);
563 if (remotePortIdSubtype != null) {
564 if (remotePortIdSubtype.equals(LLDP_SUBTYPE_MAC)
565 || remotePortIdSubtype.equals(LLDP_SUBTYPE_INTERFACE_NAME)) {
566 remotePortId = neighbor.getString(LLDP_REM_PORT, null);
567 } else {
568 remotePortIndex = neighbor.getLong(LLDP_REM_PORT, -1);
569 }
570 }
donghyeok.ho82749aa2018-07-16 19:57:03 +0900571 String remotePortDescription = neighbor.getString(LLDP_REM_PORT_DES, null);
Michele Santuari21c14012016-11-14 13:31:33 +0100572 LinkAbstraction link = new LinkAbstraction(
573 localPortName,
574 mac.toLong(),
donghyeok.ho82749aa2018-07-16 19:57:03 +0900575 remotePortIndex,
donghyeok.hoe6d71872018-07-25 18:57:44 +0900576 remotePortId,
donghyeok.ho82749aa2018-07-16 19:57:03 +0900577 remotePortDescription);
Michele Santuari21c14012016-11-14 13:31:33 +0100578 neighbour.add(link);
579 }
580 }
581 return neighbour;
582 }
583
584 /**
585 * Device representation of the adjacency at the IP Layer.
586 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800587 static final class LinkAbstraction {
Michele Santuari21c14012016-11-14 13:31:33 +0100588 protected String localPortName;
589 protected ChassisId remoteChassisId;
Michele Santuaria85bd3b2017-05-05 12:57:40 +0200590 protected long remotePortIndex;
donghyeok.hoe6d71872018-07-25 18:57:44 +0900591 protected String remotePortId;
donghyeok.ho82749aa2018-07-16 19:57:03 +0900592 protected String remotePortDescription;
Michele Santuari21c14012016-11-14 13:31:33 +0100593
donghyeok.hoe6d71872018-07-25 18:57:44 +0900594 protected LinkAbstraction(String pName, long chassisId, long pIndex, String pPortId, String pDescription) {
Michele Santuari21c14012016-11-14 13:31:33 +0100595 this.localPortName = pName;
596 this.remoteChassisId = new ChassisId(chassisId);
597 this.remotePortIndex = pIndex;
donghyeok.hoe6d71872018-07-25 18:57:44 +0900598 this.remotePortId = pPortId;
donghyeok.ho82749aa2018-07-16 19:57:03 +0900599 this.remotePortDescription = pDescription;
Michele Santuari21c14012016-11-14 13:31:33 +0100600 }
601 }
Michele Santuarice256492017-06-23 14:44:01 +0200602
Ray Milkey9c9cde42018-01-12 14:22:06 -0800603 enum OperationType {
Michele Santuarice256492017-06-23 14:44:01 +0200604 ADD,
605 REMOVE,
606 }
607
608 /**
609 * Parses {@literal route-information} tree.
610 * This implementation supports only static routes.
611 *
612 * @param cfg route-information
613 * @return a collection of static routes
614 */
615 public static Collection<StaticRoute> parseRoutingTable(HierarchicalConfiguration cfg) {
616
617 Collection<StaticRoute> staticRoutes = new HashSet<>();
618 HierarchicalConfiguration routeInfo =
619 cfg.configurationAt("route-information");
620 List<HierarchicalConfiguration> routeTables = routeInfo.configurationsAt("route-table");
621 for (HierarchicalConfiguration routeTable : routeTables) {
622 List<HierarchicalConfiguration> routes = routeTable.configurationsAt("rt");
623 for (HierarchicalConfiguration route : routes) {
624 if (route != null) {
Andrea Campanella248cc242018-09-21 13:58:38 +0200625 List<HierarchicalConfiguration> rtEntries = route.configurationsAt("rt-entry");
626 rtEntries.forEach(rtEntry -> {
627 if (rtEntry.getString(PROTOCOL_NAME) != null &&
628 rtEntry.getString(PROTOCOL_NAME).contains("Static")) {
629 parseStaticRoute(rtEntry,
630 route.getString("rt-destination"),
631 rtEntry.getString("metric"))
632 .ifPresent(staticRoutes::add);
Michele Santuarice256492017-06-23 14:44:01 +0200633
Andrea Campanella248cc242018-09-21 13:58:38 +0200634 }
635 });
Michele Santuarice256492017-06-23 14:44:01 +0200636 }
637 }
638 }
639 return staticRoutes;
640 }
641
642 /**
643 * Parse the {@literal rt-entry} for static routes.
644 *
645 * @param rtEntry rt-entry filtered by {@literal protocol-name} equals to Static
646 * @param destination rt-destination
647 * @return optional of static route
648 */
649 private static Optional<StaticRoute> parseStaticRoute(HierarchicalConfiguration rtEntry,
650 String destination, String metric) {
651
652 Ip4Prefix ipDst = Ip4Prefix.valueOf(destination);
653
654 HierarchicalConfiguration nextHop = rtEntry.configurationAt("nh");
655 String to = nextHop.getString("to");
656 if (StringUtils.isEmpty(to)) {
657 return Optional.empty();
658 }
659 Ip4Address nextHopIp = Ip4Address.valueOf(to);
660
661 if (metric == null) {
662 return Optional.of(new StaticRoute(ipDst, nextHopIp, false));
663 } else {
664 return Optional.of(new StaticRoute(ipDst, nextHopIp, false,
665 toFlowRulePriority(Integer.parseInt(metric))));
666 }
667 }
DongRyeol Cha5f870032018-07-12 18:36:20 +0900668
669 /**
670 * Helper method to build a XML schema for a given "set/merge" Op inJunOS XML Format.
671 *
672 * @param request a CLI command
673 * @return string containing the XML schema
674 */
675 public static String cliSetRequestBuilder(StringBuilder request) {
676 StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
677 rpc.append("<edit-config>");
678 rpc.append("<target><candidate/></target><config>");
679 rpc.append("<configuration>");
680 rpc.append(request);
681 rpc.append("</configuration>");
682 rpc.append("</config></edit-config>");
683 rpc.append(RPC_CLOSE_TAG);
684 rpc.append("]]>]]>");
685 return rpc.toString();
686 }
687
688 /**
689 * Helper method to build a XML schema for a given "delete Op" in JunOS XML Format.
690 *
691 * @param request a CLI command
692 * @return string containing the XML schema
693 */
694 public static String cliDeleteRequestBuilder(StringBuilder request) {
695 StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
696 rpc.append("<edit-config>");
697 rpc.append("<target><candidate/></target>");
698 rpc.append("<default-operation>none</default-operation>");
699 rpc.append("<config>");
700 rpc.append("<configuration>");
701 rpc.append(request);
702 rpc.append("</configuration>");
703 rpc.append("</config></edit-config>");
704 rpc.append(RPC_CLOSE_TAG);
705 rpc.append("]]>]]>");
706 return rpc.toString();
707 }
708
709 public static List<ControllerInfo> getOpenFlowControllersFromConfig(HierarchicalConfiguration cfg) {
710 List<ControllerInfo> controllers = new ArrayList<ControllerInfo>();
711 String ipKey = "configuration.protocols.openflow.mode.ofagent-mode.controller.ip";
712
713 if (!cfg.configurationsAt(ipKey).isEmpty()) {
714 List<HierarchicalConfiguration> ipNodes = cfg.configurationsAt(ipKey);
715
716 ipNodes.forEach(ipNode -> {
717 int port = 0;
718 String proto = UNKNOWN;
719 HierarchicalConfiguration protocolNode = ipNode.configurationAt(PROTOCOL);
720 HierarchicalConfiguration tcpNode = protocolNode.configurationAt(TCP);
721
722 if (!tcpNode.isEmpty()) {
723 String portString = tcpNode.getString(PORT);
724
725 if (portString != null && !portString.isEmpty()) {
726 port = Integer.parseInt(portString);
727 }
728
729 proto = TCP;
730 }
731
732 String ipaddress = ipNode.getString(NAME);
733
734 if (ipaddress == null) {
735 ipaddress = UNKNOWN;
736 }
737
738 if (ipaddress.equals(UNKNOWN) || proto.equals(UNKNOWN) || port == 0) {
739 log.error("Controller infomation is invalid. Skip this controller node." +
740 " ipaddress: {}, proto: {}, port: {}", ipaddress, proto, port);
741 return;
742 }
743
744 controllers.add(new ControllerInfo(IpAddress.valueOf(ipaddress), port, proto));
745 });
746 } else {
747 log.error("Controller not present");
748 }
749
750 return controllers;
751 }
Michele Santuari21c14012016-11-14 13:31:33 +0100752}