blob: 60baded1a92942143667e02f3cdee86d9948dbe4 [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";
95 private static final String SPEED = "speed";
Michele Santuari21c14012016-11-14 13:31:33 +010096 private static final String NAME = "name";
DongRyeol Cha5f870032018-07-12 18:36:20 +090097 private static final String PORT = "port";
98 private static final String PROTOCOL = "protocol";
99
100 private static final String TCP = "tcp";
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700101
102 // seems to be unique index within device
Michele Santuari21c14012016-11-14 13:31:33 +0100103 private static final String SNMP_INDEX = "snmp-index";
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700104
Michele Santuari21c14012016-11-14 13:31:33 +0100105 private static final String LLDP_LO_PORT = "lldp-local-port-id";
106 private static final String LLDP_REM_CHASS = "lldp-remote-chassis-id";
107 private static final String LLDP_REM_PORT = "lldp-remote-port-id";
donghyeok.ho82749aa2018-07-16 19:57:03 +0900108 private static final String LLDP_REM_PORT_DES = "lldp-remote-port-description";
Michele Santuari21c14012016-11-14 13:31:33 +0100109 private static final String REGEX_ADD =
110 ".*Private base address\\s*([:,0-9,a-f,A-F]*).*";
111 private static final Pattern ADD_PATTERN =
112 Pattern.compile(REGEX_ADD, Pattern.DOTALL);
113
Michele Santuarice256492017-06-23 14:44:01 +0200114 public static final String PROTOCOL_NAME = "protocol-name";
115
Michele Santuari21c14012016-11-14 13:31:33 +0100116 private static final String JUNIPER = "JUNIPER";
117 private static final String UNKNOWN = "UNKNOWN";
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700118
119 /**
120 * Annotation key for interface type.
121 */
122 static final String AK_IF_TYPE = "ifType";
123
124 /**
125 * Annotation key for Logical link-layer encapsulation.
126 */
127 static final String AK_ENCAPSULATION = "encapsulation";
128
129 /**
130 * Annotation key for interface description.
131 */
132 static final String AK_DESCRIPTION = "description";
133
134 /**
135 * Annotation key for interface admin status. "up"/"down"
136 */
137 static final String AK_ADMIN_STATUS = "adminStatus";
138
139 /**
140 * Annotation key for interface operational status. "up"/"down"
141 */
142 static final String AK_OPER_STATUS = "operStatus";
143
144 /**
145 * Annotation key for logical-interfaces parent physical interface name.
146 */
147 static final String AK_PHYSICAL_PORT_NAME = "physicalPortName";
148
149
150 private static final String NUMERIC_SPEED_REGEXP = "(\\d+)([GM])bps";
151
152 /**
153 * {@value #NUMERIC_SPEED_REGEXP} as {@link Pattern}.
154 * Case insensitive
155 */
156 private static final Pattern SPEED_PATTERN =
157 Pattern.compile(NUMERIC_SPEED_REGEXP, Pattern.CASE_INSENSITIVE);
158
159 /**
160 * Default port speed {@value} Mbps.
161 */
Michele Santuari21c14012016-11-14 13:31:33 +0100162 private static final long DEFAULT_PORT_SPEED = 1000;
163
164
165 private JuniperUtils() {
166 //not called, preventing any allocation
167 }
168
169 /**
170 * Helper method to build a XML schema given a request.
171 *
172 * @param request a tag element of the XML schema
173 * @return string containing the XML schema
174 */
175 public static String requestBuilder(String request) {
176 return RPC_TAG_NETCONF_BASE +
177 request + RPC_CLOSE_TAG;
178 }
179
180 /**
Michele Santuarice256492017-06-23 14:44:01 +0200181 * Helper method to commit a config.
182 *
183 * @return string contains the result of the commit
184 */
185 public static String commitBuilder() {
186 return RPC_TAG_NETCONF_BASE +
187 "<commit/>" + RPC_CLOSE_TAG;
188 }
189
190 /**
191 * Helper method to build the schema for returning to a previously
192 * committed configuration.
193 *
194 * @param versionToReturn Configuration to return to. The range of values is from 0 through 49.
195 * The most recently saved configuration is number 0,
196 * and the oldest saved configuration is number 49.
197 * @return string containing the XML schema
198 */
199 public static String rollbackBuilder(int versionToReturn) {
200 return RPC_TAG_NETCONF_BASE +
201 "<get-rollback-information>" +
202 "<rollback>" + versionToReturn + "</rollback>" +
203 "</get-rollback-information>" +
204 RPC_CLOSE_TAG;
205 }
206
207
208 /**
209 * Helper method to build an XML schema to configure a static route
210 * given a {@link StaticRoute}.
211 *
212 * @param staticRoute the static route to be configured
213 * @return string contains the result of the configuration
214 */
215 public static String routeAddBuilder(StaticRoute staticRoute) {
216 StringBuilder rpc = new StringBuilder("<configuration>\n");
217 rpc.append("<routing-options>\n");
218 rpc.append("<static>\n");
219 rpc.append("<route>\n");
220 rpc.append("<destination>" + staticRoute.ipv4Dst().toString() + "</destination>\n");
221 rpc.append("<next-hop>" + staticRoute.nextHop() + "</next-hop>\n");
222
223 if (staticRoute.getMetric() != DEFAULT_METRIC_STATIC_ROUTE) {
224 rpc.append("<metric>" + staticRoute.getMetric() + "</metric>");
225 }
226
227 rpc.append("</route>\n");
228 rpc.append("</static>\n");
229 rpc.append("</routing-options>\n");
230 rpc.append("</configuration>\n");
231
232 return rpc.toString();
233 }
234
235 /**
236 * Helper method to build a XML schema to delete a static route
237 * given a {@link StaticRoute}.
238 * @param staticRoute the static route to be deleted
239 * @return string contains the result of the configuratio
240 */
241 public static String routeDeleteBuilder(StaticRoute staticRoute) {
242 return "<configuration>\n" +
243 "<routing-options>\n" +
244 "<static>\n" +
245 "<route operation=\"delete\">\n" +
246 "<name>" + staticRoute.ipv4Dst().toString() + "</name>\n" +
247 "</route>\n" +
248 "</static>\n" +
249 "</routing-options>\n" +
250 "</configuration>\n";
251 }
252
253
254 /**
Michele Santuari21c14012016-11-14 13:31:33 +0100255 * Parses device configuration and returns the device description.
256 *
257 * @param deviceId the id of the device
258 * @param sysInfoCfg system configuration
259 * @param chassisText chassis string
260 * @return device description
261 */
262 public static DeviceDescription parseJuniperDescription(DeviceId deviceId,
263 HierarchicalConfiguration sysInfoCfg,
264 String chassisText) {
265 HierarchicalConfiguration info = sysInfoCfg.configurationAt(SYS_INFO);
266
267 String hw = info.getString(HW_MODEL) == null ? UNKNOWN : info.getString(HW_MODEL);
268 String sw = UNKNOWN;
269 if (info.getString(OS_NAME) != null || info.getString(OS_VER) != null) {
270 sw = info.getString(OS_NAME) + " " + info.getString(OS_VER);
271 }
272 String serial = info.getString(SER_NUM) == null ? UNKNOWN : info.getString(SER_NUM);
273
274 Matcher matcher = ADD_PATTERN.matcher(chassisText);
275 if (matcher.lookingAt()) {
276 String chassis = matcher.group(1);
277 MacAddress chassisMac = MacAddress.valueOf(chassis);
278 return new DefaultDeviceDescription(deviceId.uri(), ROUTER,
279 JUNIPER, hw, sw, serial,
280 new ChassisId(chassisMac.toLong()),
281 DefaultAnnotations.EMPTY);
282 }
283 return new DefaultDeviceDescription(deviceId.uri(), ROUTER,
284 JUNIPER, hw, sw, serial,
285 null, DefaultAnnotations.EMPTY);
286 }
287
288 /**
289 * Parses device ports configuration and returns a list of
290 * port description.
291 *
292 * @param cfg interface configuration
293 * @return list of interface descriptions of the device
294 */
295 public static List<PortDescription> parseJuniperPorts(HierarchicalConfiguration cfg) {
296 //This methods ignores some internal ports
297
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700298 List<PortDescription> portDescriptions = new ArrayList<>();
Michele Santuari21c14012016-11-14 13:31:33 +0100299 List<HierarchicalConfiguration> subtrees =
300 cfg.configurationsAt(IF_INFO);
301 for (HierarchicalConfiguration interfInfo : subtrees) {
302 List<HierarchicalConfiguration> interfaceTree =
303 interfInfo.configurationsAt(IF_PHY);
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700304 for (HierarchicalConfiguration phyIntf : interfaceTree) {
305 if (phyIntf == null) {
306 continue;
Michele Santuari21c14012016-11-14 13:31:33 +0100307 }
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700308 // parse physical Interface
309 parsePhysicalInterface(portDescriptions, phyIntf);
Michele Santuari21c14012016-11-14 13:31:33 +0100310 }
311 }
312 return portDescriptions;
313 }
314
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700315 /**
316 * Parses {@literal physical-interface} tree.
317 *
318 * @param portDescriptions list to populate Ports found parsing configuration
319 * @param phyIntf physical-interface
320 */
321 private static void parsePhysicalInterface(List<PortDescription> portDescriptions,
322 HierarchicalConfiguration phyIntf) {
323 Builder annotations = DefaultAnnotations.builder();
324 PortNumber portNumber = portNumber(phyIntf.getString(SNMP_INDEX));
325 String phyPortName = phyIntf.getString(NAME);
326 if (portNumber == null) {
327 log.debug("Skipping physical-interface {}, no PortNumer",
328 phyPortName);
329 log.trace(" {}", phyIntf);
330 return;
Michele Santuari21c14012016-11-14 13:31:33 +0100331 }
332
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700333 setIfNonNull(annotations,
334 AnnotationKeys.PORT_NAME,
335 phyPortName);
Michele Santuari21c14012016-11-14 13:31:33 +0100336
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700337 setIfNonNull(annotations,
338 AnnotationKeys.PORT_MAC,
339 phyIntf.getString("current-physical-address"));
Michele Santuari21c14012016-11-14 13:31:33 +0100340
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700341 setIfNonNull(annotations,
342 AK_IF_TYPE,
343 phyIntf.getString(IF_TYPE));
Michele Santuari21c14012016-11-14 13:31:33 +0100344
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700345 setIfNonNull(annotations,
346 AK_DESCRIPTION,
347 phyIntf.getString("description"));
Michele Santuari21c14012016-11-14 13:31:33 +0100348
Michele Santuari0b832652017-06-05 16:59:11 +0200349 boolean opUp = phyIntf.getString("oper-status", "down").equals("up");
350 annotations.set(AK_OPER_STATUS, toUpDown(opUp));
Michele Santuari21c14012016-11-14 13:31:33 +0100351
Michele Santuari0b832652017-06-05 16:59:11 +0200352 boolean admUp = phyIntf.getString("admin-status", "down").equals("up");
353 annotations.set(AK_ADMIN_STATUS, toUpDown(admUp));
Michele Santuari21c14012016-11-14 13:31:33 +0100354
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700355 long portSpeed = toMbps(phyIntf.getString(SPEED));
Michele Santuari21c14012016-11-14 13:31:33 +0100356
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800357 portDescriptions.add(DefaultPortDescription.builder()
358 .withPortNumber(portNumber)
359 .isEnabled(admUp && opUp)
360 .type(Type.COPPER)
361 .portSpeed(portSpeed)
362 .annotations(annotations.build()).build());
Michele Santuari21c14012016-11-14 13:31:33 +0100363
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700364 // parse each logical Interface
365 for (HierarchicalConfiguration logIntf : phyIntf.configurationsAt("logical-interface")) {
366 if (logIntf == null) {
367 continue;
368 }
369 PortNumber lPortNumber = safePortNumber(logIntf.getString(SNMP_INDEX));
370 if (lPortNumber == null) {
371 log.debug("Skipping logical-interface {} under {}, no PortNumer",
372 logIntf.getString(NAME), phyPortName);
373 log.trace(" {}", logIntf);
374 continue;
375 }
376
377 Builder lannotations = DefaultAnnotations.builder();
378 setIfNonNull(lannotations,
379 AnnotationKeys.PORT_NAME,
380 logIntf.getString(NAME));
381 setIfNonNull(lannotations,
382 AK_PHYSICAL_PORT_NAME,
383 phyPortName);
384
385 String afName = logIntf.getString("address-family.address-family-name");
386 String address = logIntf.getString("address-family.interface-address.ifa-local");
387 if (afName != null && address != null) {
388 // e.g., inet : IPV4, inet6 : IPV6
389 setIfNonNull(lannotations, afName, address);
390 }
391
392 // preserving former behavior
393 setIfNonNull(lannotations,
394 "ip",
395 logIntf.getString("address-family.interface-address.ifa-local"));
396
397 setIfNonNull(lannotations,
398 AK_ENCAPSULATION, logIntf.getString("encapsulation"));
399
400 // TODO confirm if this is correct.
401 // Looking at sample data,
402 // it seemed all logical loop-back interfaces were down
403 boolean lEnabled = logIntf.getString("if-config-flags.iff-up") != null;
404
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800405 portDescriptions.add(DefaultPortDescription.builder()
406 .withPortNumber(lPortNumber)
407 .isEnabled(admUp && opUp && lEnabled)
408 .type(Type.COPPER)
409 .portSpeed(portSpeed).annotations(lannotations.build())
410 .build());
Michele Santuari21c14012016-11-14 13:31:33 +0100411 }
Michele Santuari21c14012016-11-14 13:31:33 +0100412 }
413
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700414 /**
415 * Port status as "up"/"down".
416 *
417 * @param portStatus port status
418 * @return "up" if {@code portStats} is {@literal true}, "down" otherwise
419 */
420 static String toUpDown(boolean portStatus) {
421 return portStatus ? "up" : "down";
422 }
423
424 /**
425 * Translate interface {@literal speed} value as Mbps value.
426 *
427 * Note: {@literal Unlimited} and unrecognizable string will be treated as
428 * {@value #DEFAULT_PORT_SPEED} Mbps.
429 *
430 * @param speed in String
431 * @return Mbps
432 */
433 static long toMbps(String speed) {
434 String s = Strings.nullToEmpty(speed).trim().toLowerCase();
435 Matcher matcher = SPEED_PATTERN.matcher(s);
436 if (matcher.matches()) {
437 // numeric
Ray Milkey3717e602018-02-01 13:49:47 -0800438 long n = Long.parseLong(matcher.group(1));
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700439 String unit = matcher.group(2);
440 if ("m".equalsIgnoreCase(unit)) {
441 // Mbps
442 return n;
443 } else {
444 // assume Gbps
445 return 1000 * n;
446 }
Michele Santuari21c14012016-11-14 13:31:33 +0100447 }
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700448 log.trace("Treating unknown speed value {} as default", speed);
449 // Unlimited or unrecognizable
450 return DEFAULT_PORT_SPEED;
451 }
452
453 /**
454 * Sets annotation entry if {@literal value} was not {@literal null}.
455 *
456 * @param builder Annotation Builder
457 * @param key Annotation key
458 * @param value Annotation value (can be {@literal null})
459 */
460 static void setIfNonNull(Builder builder, String key, String value) {
461 if (value != null) {
462 builder.set(key, value.trim());
463 }
464 }
465
466 /**
467 * Creates PortNumber instance from String.
468 *
469 * Instead for throwing Exception, it will return null on format error.
470 *
471 * @param s port number as string
472 * @return PortNumber instance or null on error
473 */
474 static PortNumber safePortNumber(String s) {
475 try {
476 return portNumber(s);
477 } catch (RuntimeException e) {
478 log.trace("Failed parsing PortNumber {}", s, e);
479 }
480 return null;
Michele Santuari21c14012016-11-14 13:31:33 +0100481 }
482
483 /**
484 * Create two LinkDescriptions corresponding to the bidirectional links.
485 *
486 * @param localDevId the identity of the local device
487 * @param localPort the port of the local device
488 * @param remoteDevId the identity of the remote device
489 * @param remotePort the port of the remote device
490 * @param descs the collection to which the link descriptions
491 * should be added
492 */
493 public static void createBiDirLinkDescription(DeviceId localDevId,
494 Port localPort,
495 DeviceId remoteDevId,
496 Port remotePort,
497 Set<LinkDescription> descs) {
498
499 ConnectPoint local = new ConnectPoint(localDevId, localPort.number());
500 ConnectPoint remote = new ConnectPoint(remoteDevId, remotePort.number());
501 DefaultAnnotations annotations = DefaultAnnotations.builder()
502 .set("layer", "IP")
503 .build();
504 descs.add(new DefaultLinkDescription(
505 local, remote, Link.Type.INDIRECT, false, annotations));
506 descs.add(new DefaultLinkDescription(
507 remote, local, Link.Type.INDIRECT, false, annotations));
508 }
509
510 /**
511 * Parses neighbours discovery information and returns a list of
512 * link abstractions.
513 *
514 * @param info interface configuration
515 * @return set of link abstractions
516 */
517 public static Set<LinkAbstraction> parseJuniperLldp(HierarchicalConfiguration info) {
518 Set<LinkAbstraction> neighbour = new HashSet<>();
519 List<HierarchicalConfiguration> subtrees =
Michele Santuarif5945372017-01-19 16:39:21 +0100520 info.configurationsAt(LLDP_LIST_NBR_INFO);
Michele Santuari21c14012016-11-14 13:31:33 +0100521 for (HierarchicalConfiguration neighborsInfo : subtrees) {
522 List<HierarchicalConfiguration> neighbors =
523 neighborsInfo.configurationsAt(LLDP_NBR_INFO);
524 for (HierarchicalConfiguration neighbor : neighbors) {
525 String localPortName = neighbor.getString(LLDP_LO_PORT);
526 MacAddress mac = MacAddress.valueOf(
527 neighbor.getString(LLDP_REM_CHASS));
Michele Santuaria85bd3b2017-05-05 12:57:40 +0200528 long remotePortIndex =
donghyeok.ho82749aa2018-07-16 19:57:03 +0900529 neighbor.getInt(LLDP_REM_PORT, -1);
530 String remotePortDescription = neighbor.getString(LLDP_REM_PORT_DES, null);
Michele Santuari21c14012016-11-14 13:31:33 +0100531 LinkAbstraction link = new LinkAbstraction(
532 localPortName,
533 mac.toLong(),
donghyeok.ho82749aa2018-07-16 19:57:03 +0900534 remotePortIndex,
535 remotePortDescription);
Michele Santuari21c14012016-11-14 13:31:33 +0100536 neighbour.add(link);
537 }
538 }
539 return neighbour;
540 }
541
542 /**
543 * Device representation of the adjacency at the IP Layer.
544 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800545 static final class LinkAbstraction {
Michele Santuari21c14012016-11-14 13:31:33 +0100546 protected String localPortName;
547 protected ChassisId remoteChassisId;
Michele Santuaria85bd3b2017-05-05 12:57:40 +0200548 protected long remotePortIndex;
donghyeok.ho82749aa2018-07-16 19:57:03 +0900549 protected String remotePortDescription;
Michele Santuari21c14012016-11-14 13:31:33 +0100550
donghyeok.ho82749aa2018-07-16 19:57:03 +0900551 protected LinkAbstraction(String pName, long chassisId, long pIndex, String pDescription) {
Michele Santuari21c14012016-11-14 13:31:33 +0100552 this.localPortName = pName;
553 this.remoteChassisId = new ChassisId(chassisId);
554 this.remotePortIndex = pIndex;
donghyeok.ho82749aa2018-07-16 19:57:03 +0900555 this.remotePortDescription = pDescription;
Michele Santuari21c14012016-11-14 13:31:33 +0100556 }
557 }
Michele Santuarice256492017-06-23 14:44:01 +0200558
Ray Milkey9c9cde42018-01-12 14:22:06 -0800559 enum OperationType {
Michele Santuarice256492017-06-23 14:44:01 +0200560 ADD,
561 REMOVE,
562 }
563
564 /**
565 * Parses {@literal route-information} tree.
566 * This implementation supports only static routes.
567 *
568 * @param cfg route-information
569 * @return a collection of static routes
570 */
571 public static Collection<StaticRoute> parseRoutingTable(HierarchicalConfiguration cfg) {
572
573 Collection<StaticRoute> staticRoutes = new HashSet<>();
574 HierarchicalConfiguration routeInfo =
575 cfg.configurationAt("route-information");
576 List<HierarchicalConfiguration> routeTables = routeInfo.configurationsAt("route-table");
577 for (HierarchicalConfiguration routeTable : routeTables) {
578 List<HierarchicalConfiguration> routes = routeTable.configurationsAt("rt");
579 for (HierarchicalConfiguration route : routes) {
580 if (route != null) {
581 HierarchicalConfiguration rtEntry = route.configurationAt("rt-entry");
582 if (rtEntry.getString(PROTOCOL_NAME) != null &&
583 rtEntry.getString(PROTOCOL_NAME).contains("Static")) {
584 parseStaticRoute(rtEntry,
585 route.getString("rt-destination"),
586 rtEntry.getString("metric"))
587 .ifPresent(x -> staticRoutes.add(x));
588
589 }
590 }
591 }
592 }
593 return staticRoutes;
594 }
595
596 /**
597 * Parse the {@literal rt-entry} for static routes.
598 *
599 * @param rtEntry rt-entry filtered by {@literal protocol-name} equals to Static
600 * @param destination rt-destination
601 * @return optional of static route
602 */
603 private static Optional<StaticRoute> parseStaticRoute(HierarchicalConfiguration rtEntry,
604 String destination, String metric) {
605
606 Ip4Prefix ipDst = Ip4Prefix.valueOf(destination);
607
608 HierarchicalConfiguration nextHop = rtEntry.configurationAt("nh");
609 String to = nextHop.getString("to");
610 if (StringUtils.isEmpty(to)) {
611 return Optional.empty();
612 }
613 Ip4Address nextHopIp = Ip4Address.valueOf(to);
614
615 if (metric == null) {
616 return Optional.of(new StaticRoute(ipDst, nextHopIp, false));
617 } else {
618 return Optional.of(new StaticRoute(ipDst, nextHopIp, false,
619 toFlowRulePriority(Integer.parseInt(metric))));
620 }
621 }
DongRyeol Cha5f870032018-07-12 18:36:20 +0900622
623 /**
624 * Helper method to build a XML schema for a given "set/merge" Op inJunOS XML Format.
625 *
626 * @param request a CLI command
627 * @return string containing the XML schema
628 */
629 public static String cliSetRequestBuilder(StringBuilder request) {
630 StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
631 rpc.append("<edit-config>");
632 rpc.append("<target><candidate/></target><config>");
633 rpc.append("<configuration>");
634 rpc.append(request);
635 rpc.append("</configuration>");
636 rpc.append("</config></edit-config>");
637 rpc.append(RPC_CLOSE_TAG);
638 rpc.append("]]>]]>");
639 return rpc.toString();
640 }
641
642 /**
643 * Helper method to build a XML schema for a given "delete Op" in JunOS XML Format.
644 *
645 * @param request a CLI command
646 * @return string containing the XML schema
647 */
648 public static String cliDeleteRequestBuilder(StringBuilder request) {
649 StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
650 rpc.append("<edit-config>");
651 rpc.append("<target><candidate/></target>");
652 rpc.append("<default-operation>none</default-operation>");
653 rpc.append("<config>");
654 rpc.append("<configuration>");
655 rpc.append(request);
656 rpc.append("</configuration>");
657 rpc.append("</config></edit-config>");
658 rpc.append(RPC_CLOSE_TAG);
659 rpc.append("]]>]]>");
660 return rpc.toString();
661 }
662
663 public static List<ControllerInfo> getOpenFlowControllersFromConfig(HierarchicalConfiguration cfg) {
664 List<ControllerInfo> controllers = new ArrayList<ControllerInfo>();
665 String ipKey = "configuration.protocols.openflow.mode.ofagent-mode.controller.ip";
666
667 if (!cfg.configurationsAt(ipKey).isEmpty()) {
668 List<HierarchicalConfiguration> ipNodes = cfg.configurationsAt(ipKey);
669
670 ipNodes.forEach(ipNode -> {
671 int port = 0;
672 String proto = UNKNOWN;
673 HierarchicalConfiguration protocolNode = ipNode.configurationAt(PROTOCOL);
674 HierarchicalConfiguration tcpNode = protocolNode.configurationAt(TCP);
675
676 if (!tcpNode.isEmpty()) {
677 String portString = tcpNode.getString(PORT);
678
679 if (portString != null && !portString.isEmpty()) {
680 port = Integer.parseInt(portString);
681 }
682
683 proto = TCP;
684 }
685
686 String ipaddress = ipNode.getString(NAME);
687
688 if (ipaddress == null) {
689 ipaddress = UNKNOWN;
690 }
691
692 if (ipaddress.equals(UNKNOWN) || proto.equals(UNKNOWN) || port == 0) {
693 log.error("Controller infomation is invalid. Skip this controller node." +
694 " ipaddress: {}, proto: {}, port: {}", ipaddress, proto, port);
695 return;
696 }
697
698 controllers.add(new ControllerInfo(IpAddress.valueOf(ipaddress), port, proto));
699 });
700 } else {
701 log.error("Controller not present");
702 }
703
704 return controllers;
705 }
Michele Santuari21c14012016-11-14 13:31:33 +0100706}