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