blob: d6a6f651052d1a5d654946ebd51a223eb0cbf6f9 [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";
donghyeok.hoe6d71872018-07-25 18:57:44 +0900107 private static final String LLDP_REM_PORT_SUBTYPE = "lldp-remote-port-id-subtype";
Michele Santuari21c14012016-11-14 13:31:33 +0100108 private static final String LLDP_REM_PORT = "lldp-remote-port-id";
donghyeok.ho82749aa2018-07-16 19:57:03 +0900109 private static final String LLDP_REM_PORT_DES = "lldp-remote-port-description";
donghyeok.hoe6d71872018-07-25 18:57:44 +0900110 private static final String LLDP_SUBTYPE_MAC = "Mac address";
111 private static final String LLDP_SUBTYPE_INTERFACE_NAME = "Interface name";
Michele Santuari21c14012016-11-14 13:31:33 +0100112 private static final String REGEX_ADD =
113 ".*Private base address\\s*([:,0-9,a-f,A-F]*).*";
114 private static final Pattern ADD_PATTERN =
115 Pattern.compile(REGEX_ADD, Pattern.DOTALL);
116
Michele Santuarice256492017-06-23 14:44:01 +0200117 public static final String PROTOCOL_NAME = "protocol-name";
118
Michele Santuari21c14012016-11-14 13:31:33 +0100119 private static final String JUNIPER = "JUNIPER";
120 private static final String UNKNOWN = "UNKNOWN";
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700121
122 /**
123 * Annotation key for interface type.
124 */
125 static final String AK_IF_TYPE = "ifType";
126
127 /**
128 * Annotation key for Logical link-layer encapsulation.
129 */
130 static final String AK_ENCAPSULATION = "encapsulation";
131
132 /**
133 * Annotation key for interface description.
134 */
135 static final String AK_DESCRIPTION = "description";
136
137 /**
138 * Annotation key for interface admin status. "up"/"down"
139 */
140 static final String AK_ADMIN_STATUS = "adminStatus";
141
142 /**
143 * Annotation key for interface operational status. "up"/"down"
144 */
145 static final String AK_OPER_STATUS = "operStatus";
146
147 /**
148 * Annotation key for logical-interfaces parent physical interface name.
149 */
150 static final String AK_PHYSICAL_PORT_NAME = "physicalPortName";
151
152
153 private static final String NUMERIC_SPEED_REGEXP = "(\\d+)([GM])bps";
154
155 /**
156 * {@value #NUMERIC_SPEED_REGEXP} as {@link Pattern}.
157 * Case insensitive
158 */
159 private static final Pattern SPEED_PATTERN =
160 Pattern.compile(NUMERIC_SPEED_REGEXP, Pattern.CASE_INSENSITIVE);
161
162 /**
163 * Default port speed {@value} Mbps.
164 */
Michele Santuari21c14012016-11-14 13:31:33 +0100165 private static final long DEFAULT_PORT_SPEED = 1000;
166
167
168 private JuniperUtils() {
169 //not called, preventing any allocation
170 }
171
172 /**
173 * Helper method to build a XML schema given a request.
174 *
175 * @param request a tag element of the XML schema
176 * @return string containing the XML schema
177 */
178 public static String requestBuilder(String request) {
179 return RPC_TAG_NETCONF_BASE +
180 request + RPC_CLOSE_TAG;
181 }
182
183 /**
Michele Santuarice256492017-06-23 14:44:01 +0200184 * Helper method to commit a config.
185 *
186 * @return string contains the result of the commit
187 */
188 public static String commitBuilder() {
189 return RPC_TAG_NETCONF_BASE +
190 "<commit/>" + RPC_CLOSE_TAG;
191 }
192
193 /**
194 * Helper method to build the schema for returning to a previously
195 * committed configuration.
196 *
197 * @param versionToReturn Configuration to return to. The range of values is from 0 through 49.
198 * The most recently saved configuration is number 0,
199 * and the oldest saved configuration is number 49.
200 * @return string containing the XML schema
201 */
202 public static String rollbackBuilder(int versionToReturn) {
203 return RPC_TAG_NETCONF_BASE +
204 "<get-rollback-information>" +
205 "<rollback>" + versionToReturn + "</rollback>" +
206 "</get-rollback-information>" +
207 RPC_CLOSE_TAG;
208 }
209
210
211 /**
212 * Helper method to build an XML schema to configure a static route
213 * given a {@link StaticRoute}.
214 *
215 * @param staticRoute the static route to be configured
216 * @return string contains the result of the configuration
217 */
218 public static String routeAddBuilder(StaticRoute staticRoute) {
219 StringBuilder rpc = new StringBuilder("<configuration>\n");
220 rpc.append("<routing-options>\n");
221 rpc.append("<static>\n");
222 rpc.append("<route>\n");
223 rpc.append("<destination>" + staticRoute.ipv4Dst().toString() + "</destination>\n");
224 rpc.append("<next-hop>" + staticRoute.nextHop() + "</next-hop>\n");
225
226 if (staticRoute.getMetric() != DEFAULT_METRIC_STATIC_ROUTE) {
227 rpc.append("<metric>" + staticRoute.getMetric() + "</metric>");
228 }
229
230 rpc.append("</route>\n");
231 rpc.append("</static>\n");
232 rpc.append("</routing-options>\n");
233 rpc.append("</configuration>\n");
234
235 return rpc.toString();
236 }
237
238 /**
239 * Helper method to build a XML schema to delete a static route
240 * given a {@link StaticRoute}.
241 * @param staticRoute the static route to be deleted
242 * @return string contains the result of the configuratio
243 */
244 public static String routeDeleteBuilder(StaticRoute staticRoute) {
245 return "<configuration>\n" +
246 "<routing-options>\n" +
247 "<static>\n" +
248 "<route operation=\"delete\">\n" +
249 "<name>" + staticRoute.ipv4Dst().toString() + "</name>\n" +
250 "</route>\n" +
251 "</static>\n" +
252 "</routing-options>\n" +
253 "</configuration>\n";
254 }
255
256
257 /**
Michele Santuari21c14012016-11-14 13:31:33 +0100258 * Parses device configuration and returns the device description.
259 *
260 * @param deviceId the id of the device
261 * @param sysInfoCfg system configuration
262 * @param chassisText chassis string
263 * @return device description
264 */
265 public static DeviceDescription parseJuniperDescription(DeviceId deviceId,
266 HierarchicalConfiguration sysInfoCfg,
267 String chassisText) {
268 HierarchicalConfiguration info = sysInfoCfg.configurationAt(SYS_INFO);
269
270 String hw = info.getString(HW_MODEL) == null ? UNKNOWN : info.getString(HW_MODEL);
271 String sw = UNKNOWN;
272 if (info.getString(OS_NAME) != null || info.getString(OS_VER) != null) {
273 sw = info.getString(OS_NAME) + " " + info.getString(OS_VER);
274 }
275 String serial = info.getString(SER_NUM) == null ? UNKNOWN : info.getString(SER_NUM);
276
277 Matcher matcher = ADD_PATTERN.matcher(chassisText);
278 if (matcher.lookingAt()) {
279 String chassis = matcher.group(1);
280 MacAddress chassisMac = MacAddress.valueOf(chassis);
281 return new DefaultDeviceDescription(deviceId.uri(), ROUTER,
282 JUNIPER, hw, sw, serial,
283 new ChassisId(chassisMac.toLong()),
284 DefaultAnnotations.EMPTY);
285 }
286 return new DefaultDeviceDescription(deviceId.uri(), ROUTER,
287 JUNIPER, hw, sw, serial,
288 null, DefaultAnnotations.EMPTY);
289 }
290
291 /**
292 * Parses device ports configuration and returns a list of
293 * port description.
294 *
295 * @param cfg interface configuration
296 * @return list of interface descriptions of the device
297 */
298 public static List<PortDescription> parseJuniperPorts(HierarchicalConfiguration cfg) {
299 //This methods ignores some internal ports
300
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700301 List<PortDescription> portDescriptions = new ArrayList<>();
Michele Santuari21c14012016-11-14 13:31:33 +0100302 List<HierarchicalConfiguration> subtrees =
303 cfg.configurationsAt(IF_INFO);
304 for (HierarchicalConfiguration interfInfo : subtrees) {
305 List<HierarchicalConfiguration> interfaceTree =
306 interfInfo.configurationsAt(IF_PHY);
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700307 for (HierarchicalConfiguration phyIntf : interfaceTree) {
308 if (phyIntf == null) {
309 continue;
Michele Santuari21c14012016-11-14 13:31:33 +0100310 }
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700311 // parse physical Interface
312 parsePhysicalInterface(portDescriptions, phyIntf);
Michele Santuari21c14012016-11-14 13:31:33 +0100313 }
314 }
315 return portDescriptions;
316 }
317
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700318 /**
319 * Parses {@literal physical-interface} tree.
320 *
321 * @param portDescriptions list to populate Ports found parsing configuration
322 * @param phyIntf physical-interface
323 */
324 private static void parsePhysicalInterface(List<PortDescription> portDescriptions,
325 HierarchicalConfiguration phyIntf) {
326 Builder annotations = DefaultAnnotations.builder();
327 PortNumber portNumber = portNumber(phyIntf.getString(SNMP_INDEX));
328 String phyPortName = phyIntf.getString(NAME);
329 if (portNumber == null) {
330 log.debug("Skipping physical-interface {}, no PortNumer",
331 phyPortName);
332 log.trace(" {}", phyIntf);
333 return;
Michele Santuari21c14012016-11-14 13:31:33 +0100334 }
335
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700336 setIfNonNull(annotations,
337 AnnotationKeys.PORT_NAME,
338 phyPortName);
Michele Santuari21c14012016-11-14 13:31:33 +0100339
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700340 setIfNonNull(annotations,
341 AnnotationKeys.PORT_MAC,
342 phyIntf.getString("current-physical-address"));
Michele Santuari21c14012016-11-14 13:31:33 +0100343
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700344 setIfNonNull(annotations,
345 AK_IF_TYPE,
346 phyIntf.getString(IF_TYPE));
Michele Santuari21c14012016-11-14 13:31:33 +0100347
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700348 setIfNonNull(annotations,
349 AK_DESCRIPTION,
350 phyIntf.getString("description"));
Michele Santuari21c14012016-11-14 13:31:33 +0100351
Michele Santuari0b832652017-06-05 16:59:11 +0200352 boolean opUp = phyIntf.getString("oper-status", "down").equals("up");
353 annotations.set(AK_OPER_STATUS, toUpDown(opUp));
Michele Santuari21c14012016-11-14 13:31:33 +0100354
Michele Santuari0b832652017-06-05 16:59:11 +0200355 boolean admUp = phyIntf.getString("admin-status", "down").equals("up");
356 annotations.set(AK_ADMIN_STATUS, toUpDown(admUp));
Michele Santuari21c14012016-11-14 13:31:33 +0100357
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700358 long portSpeed = toMbps(phyIntf.getString(SPEED));
Michele Santuari21c14012016-11-14 13:31:33 +0100359
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800360 portDescriptions.add(DefaultPortDescription.builder()
361 .withPortNumber(portNumber)
362 .isEnabled(admUp && opUp)
363 .type(Type.COPPER)
364 .portSpeed(portSpeed)
365 .annotations(annotations.build()).build());
Michele Santuari21c14012016-11-14 13:31:33 +0100366
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700367 // parse each logical Interface
368 for (HierarchicalConfiguration logIntf : phyIntf.configurationsAt("logical-interface")) {
369 if (logIntf == null) {
370 continue;
371 }
372 PortNumber lPortNumber = safePortNumber(logIntf.getString(SNMP_INDEX));
373 if (lPortNumber == null) {
374 log.debug("Skipping logical-interface {} under {}, no PortNumer",
375 logIntf.getString(NAME), phyPortName);
376 log.trace(" {}", logIntf);
377 continue;
378 }
379
380 Builder lannotations = DefaultAnnotations.builder();
381 setIfNonNull(lannotations,
382 AnnotationKeys.PORT_NAME,
383 logIntf.getString(NAME));
384 setIfNonNull(lannotations,
385 AK_PHYSICAL_PORT_NAME,
386 phyPortName);
387
388 String afName = logIntf.getString("address-family.address-family-name");
389 String address = logIntf.getString("address-family.interface-address.ifa-local");
390 if (afName != null && address != null) {
391 // e.g., inet : IPV4, inet6 : IPV6
392 setIfNonNull(lannotations, afName, address);
393 }
394
395 // preserving former behavior
396 setIfNonNull(lannotations,
397 "ip",
398 logIntf.getString("address-family.interface-address.ifa-local"));
399
400 setIfNonNull(lannotations,
401 AK_ENCAPSULATION, logIntf.getString("encapsulation"));
402
403 // TODO confirm if this is correct.
404 // Looking at sample data,
405 // it seemed all logical loop-back interfaces were down
406 boolean lEnabled = logIntf.getString("if-config-flags.iff-up") != null;
407
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800408 portDescriptions.add(DefaultPortDescription.builder()
409 .withPortNumber(lPortNumber)
410 .isEnabled(admUp && opUp && lEnabled)
411 .type(Type.COPPER)
412 .portSpeed(portSpeed).annotations(lannotations.build())
413 .build());
Michele Santuari21c14012016-11-14 13:31:33 +0100414 }
Michele Santuari21c14012016-11-14 13:31:33 +0100415 }
416
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700417 /**
418 * Port status as "up"/"down".
419 *
420 * @param portStatus port status
421 * @return "up" if {@code portStats} is {@literal true}, "down" otherwise
422 */
423 static String toUpDown(boolean portStatus) {
424 return portStatus ? "up" : "down";
425 }
426
427 /**
428 * Translate interface {@literal speed} value as Mbps value.
429 *
430 * Note: {@literal Unlimited} and unrecognizable string will be treated as
431 * {@value #DEFAULT_PORT_SPEED} Mbps.
432 *
433 * @param speed in String
434 * @return Mbps
435 */
436 static long toMbps(String speed) {
437 String s = Strings.nullToEmpty(speed).trim().toLowerCase();
438 Matcher matcher = SPEED_PATTERN.matcher(s);
439 if (matcher.matches()) {
440 // numeric
Ray Milkey3717e602018-02-01 13:49:47 -0800441 long n = Long.parseLong(matcher.group(1));
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700442 String unit = matcher.group(2);
443 if ("m".equalsIgnoreCase(unit)) {
444 // Mbps
445 return n;
446 } else {
447 // assume Gbps
448 return 1000 * n;
449 }
Michele Santuari21c14012016-11-14 13:31:33 +0100450 }
Yuta HIGUCHIebff1b52017-04-21 20:41:42 -0700451 log.trace("Treating unknown speed value {} as default", speed);
452 // Unlimited or unrecognizable
453 return DEFAULT_PORT_SPEED;
454 }
455
456 /**
457 * Sets annotation entry if {@literal value} was not {@literal null}.
458 *
459 * @param builder Annotation Builder
460 * @param key Annotation key
461 * @param value Annotation value (can be {@literal null})
462 */
463 static void setIfNonNull(Builder builder, String key, String value) {
464 if (value != null) {
465 builder.set(key, value.trim());
466 }
467 }
468
469 /**
470 * Creates PortNumber instance from String.
471 *
472 * Instead for throwing Exception, it will return null on format error.
473 *
474 * @param s port number as string
475 * @return PortNumber instance or null on error
476 */
477 static PortNumber safePortNumber(String s) {
478 try {
479 return portNumber(s);
480 } catch (RuntimeException e) {
481 log.trace("Failed parsing PortNumber {}", s, e);
482 }
483 return null;
Michele Santuari21c14012016-11-14 13:31:33 +0100484 }
485
486 /**
487 * Create two LinkDescriptions corresponding to the bidirectional links.
488 *
489 * @param localDevId the identity of the local device
490 * @param localPort the port of the local device
491 * @param remoteDevId the identity of the remote device
492 * @param remotePort the port of the remote device
493 * @param descs the collection to which the link descriptions
494 * should be added
495 */
496 public static void createBiDirLinkDescription(DeviceId localDevId,
497 Port localPort,
498 DeviceId remoteDevId,
499 Port remotePort,
500 Set<LinkDescription> descs) {
501
502 ConnectPoint local = new ConnectPoint(localDevId, localPort.number());
503 ConnectPoint remote = new ConnectPoint(remoteDevId, remotePort.number());
504 DefaultAnnotations annotations = DefaultAnnotations.builder()
DongRyeol Chace65cc02018-07-23 15:02:28 +0900505 .set(AnnotationKeys.LAYER, "ETHERNET")
Michele Santuari21c14012016-11-14 13:31:33 +0100506 .build();
507 descs.add(new DefaultLinkDescription(
DongRyeol Chace65cc02018-07-23 15:02:28 +0900508 local, remote, Link.Type.DIRECT, true, annotations));
Michele Santuari21c14012016-11-14 13:31:33 +0100509 descs.add(new DefaultLinkDescription(
DongRyeol Chace65cc02018-07-23 15:02:28 +0900510 remote, local, Link.Type.DIRECT, true, annotations));
511 }
512
513 /**
514 * Create one way LinkDescriptions.
515 *
516 * @param localDevId the identity of the local device
517 * @param localPort the port of the local device
518 * @param remoteDevId the identity of the remote device
519 * @param remotePort the port of the remote device
520 * @param descs the collection to which the link descriptions
521 * should be added
522 */
523 public static void createOneWayLinkDescription(DeviceId localDevId,
524 Port localPort,
525 DeviceId remoteDevId,
526 Port remotePort,
527 Set<LinkDescription> descs) {
528
529 ConnectPoint local = new ConnectPoint(localDevId, localPort.number());
530 ConnectPoint remote = new ConnectPoint(remoteDevId, remotePort.number());
531 DefaultAnnotations annotations = DefaultAnnotations.builder()
532 .set(AnnotationKeys.LAYER, "ETHERNET")
533 .build();
534 descs.add(new DefaultLinkDescription(
535 remote, local, Link.Type.DIRECT, true, annotations));
Michele Santuari21c14012016-11-14 13:31:33 +0100536 }
537
538 /**
539 * Parses neighbours discovery information and returns a list of
540 * link abstractions.
541 *
542 * @param info interface configuration
543 * @return set of link abstractions
544 */
545 public static Set<LinkAbstraction> parseJuniperLldp(HierarchicalConfiguration info) {
546 Set<LinkAbstraction> neighbour = new HashSet<>();
547 List<HierarchicalConfiguration> subtrees =
Michele Santuarif5945372017-01-19 16:39:21 +0100548 info.configurationsAt(LLDP_LIST_NBR_INFO);
Michele Santuari21c14012016-11-14 13:31:33 +0100549 for (HierarchicalConfiguration neighborsInfo : subtrees) {
550 List<HierarchicalConfiguration> neighbors =
551 neighborsInfo.configurationsAt(LLDP_NBR_INFO);
552 for (HierarchicalConfiguration neighbor : neighbors) {
553 String localPortName = neighbor.getString(LLDP_LO_PORT);
donghyeok.hoe6d71872018-07-25 18:57:44 +0900554 MacAddress mac = MacAddress.valueOf(neighbor.getString(LLDP_REM_CHASS));
555 String remotePortId = null;
556 long remotePortIndex = -1;
557 String remotePortIdSubtype = neighbor.getString(LLDP_REM_PORT_SUBTYPE, null);
558 if (remotePortIdSubtype != null) {
559 if (remotePortIdSubtype.equals(LLDP_SUBTYPE_MAC)
560 || remotePortIdSubtype.equals(LLDP_SUBTYPE_INTERFACE_NAME)) {
561 remotePortId = neighbor.getString(LLDP_REM_PORT, null);
562 } else {
563 remotePortIndex = neighbor.getLong(LLDP_REM_PORT, -1);
564 }
565 }
donghyeok.ho82749aa2018-07-16 19:57:03 +0900566 String remotePortDescription = neighbor.getString(LLDP_REM_PORT_DES, null);
Michele Santuari21c14012016-11-14 13:31:33 +0100567 LinkAbstraction link = new LinkAbstraction(
568 localPortName,
569 mac.toLong(),
donghyeok.ho82749aa2018-07-16 19:57:03 +0900570 remotePortIndex,
donghyeok.hoe6d71872018-07-25 18:57:44 +0900571 remotePortId,
donghyeok.ho82749aa2018-07-16 19:57:03 +0900572 remotePortDescription);
Michele Santuari21c14012016-11-14 13:31:33 +0100573 neighbour.add(link);
574 }
575 }
576 return neighbour;
577 }
578
579 /**
580 * Device representation of the adjacency at the IP Layer.
581 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800582 static final class LinkAbstraction {
Michele Santuari21c14012016-11-14 13:31:33 +0100583 protected String localPortName;
584 protected ChassisId remoteChassisId;
Michele Santuaria85bd3b2017-05-05 12:57:40 +0200585 protected long remotePortIndex;
donghyeok.hoe6d71872018-07-25 18:57:44 +0900586 protected String remotePortId;
donghyeok.ho82749aa2018-07-16 19:57:03 +0900587 protected String remotePortDescription;
Michele Santuari21c14012016-11-14 13:31:33 +0100588
donghyeok.hoe6d71872018-07-25 18:57:44 +0900589 protected LinkAbstraction(String pName, long chassisId, long pIndex, String pPortId, String pDescription) {
Michele Santuari21c14012016-11-14 13:31:33 +0100590 this.localPortName = pName;
591 this.remoteChassisId = new ChassisId(chassisId);
592 this.remotePortIndex = pIndex;
donghyeok.hoe6d71872018-07-25 18:57:44 +0900593 this.remotePortId = pPortId;
donghyeok.ho82749aa2018-07-16 19:57:03 +0900594 this.remotePortDescription = pDescription;
Michele Santuari21c14012016-11-14 13:31:33 +0100595 }
596 }
Michele Santuarice256492017-06-23 14:44:01 +0200597
Ray Milkey9c9cde42018-01-12 14:22:06 -0800598 enum OperationType {
Michele Santuarice256492017-06-23 14:44:01 +0200599 ADD,
600 REMOVE,
601 }
602
603 /**
604 * Parses {@literal route-information} tree.
605 * This implementation supports only static routes.
606 *
607 * @param cfg route-information
608 * @return a collection of static routes
609 */
610 public static Collection<StaticRoute> parseRoutingTable(HierarchicalConfiguration cfg) {
611
612 Collection<StaticRoute> staticRoutes = new HashSet<>();
613 HierarchicalConfiguration routeInfo =
614 cfg.configurationAt("route-information");
615 List<HierarchicalConfiguration> routeTables = routeInfo.configurationsAt("route-table");
616 for (HierarchicalConfiguration routeTable : routeTables) {
617 List<HierarchicalConfiguration> routes = routeTable.configurationsAt("rt");
618 for (HierarchicalConfiguration route : routes) {
619 if (route != null) {
Andrea Campanella248cc242018-09-21 13:58:38 +0200620 List<HierarchicalConfiguration> rtEntries = route.configurationsAt("rt-entry");
621 rtEntries.forEach(rtEntry -> {
622 if (rtEntry.getString(PROTOCOL_NAME) != null &&
623 rtEntry.getString(PROTOCOL_NAME).contains("Static")) {
624 parseStaticRoute(rtEntry,
625 route.getString("rt-destination"),
626 rtEntry.getString("metric"))
627 .ifPresent(staticRoutes::add);
Michele Santuarice256492017-06-23 14:44:01 +0200628
Andrea Campanella248cc242018-09-21 13:58:38 +0200629 }
630 });
Michele Santuarice256492017-06-23 14:44:01 +0200631 }
632 }
633 }
634 return staticRoutes;
635 }
636
637 /**
638 * Parse the {@literal rt-entry} for static routes.
639 *
640 * @param rtEntry rt-entry filtered by {@literal protocol-name} equals to Static
641 * @param destination rt-destination
642 * @return optional of static route
643 */
644 private static Optional<StaticRoute> parseStaticRoute(HierarchicalConfiguration rtEntry,
645 String destination, String metric) {
646
647 Ip4Prefix ipDst = Ip4Prefix.valueOf(destination);
648
649 HierarchicalConfiguration nextHop = rtEntry.configurationAt("nh");
650 String to = nextHop.getString("to");
651 if (StringUtils.isEmpty(to)) {
652 return Optional.empty();
653 }
654 Ip4Address nextHopIp = Ip4Address.valueOf(to);
655
656 if (metric == null) {
657 return Optional.of(new StaticRoute(ipDst, nextHopIp, false));
658 } else {
659 return Optional.of(new StaticRoute(ipDst, nextHopIp, false,
660 toFlowRulePriority(Integer.parseInt(metric))));
661 }
662 }
DongRyeol Cha5f870032018-07-12 18:36:20 +0900663
664 /**
665 * Helper method to build a XML schema for a given "set/merge" Op inJunOS XML Format.
666 *
667 * @param request a CLI command
668 * @return string containing the XML schema
669 */
670 public static String cliSetRequestBuilder(StringBuilder request) {
671 StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
672 rpc.append("<edit-config>");
673 rpc.append("<target><candidate/></target><config>");
674 rpc.append("<configuration>");
675 rpc.append(request);
676 rpc.append("</configuration>");
677 rpc.append("</config></edit-config>");
678 rpc.append(RPC_CLOSE_TAG);
679 rpc.append("]]>]]>");
680 return rpc.toString();
681 }
682
683 /**
684 * Helper method to build a XML schema for a given "delete Op" in JunOS XML Format.
685 *
686 * @param request a CLI command
687 * @return string containing the XML schema
688 */
689 public static String cliDeleteRequestBuilder(StringBuilder request) {
690 StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
691 rpc.append("<edit-config>");
692 rpc.append("<target><candidate/></target>");
693 rpc.append("<default-operation>none</default-operation>");
694 rpc.append("<config>");
695 rpc.append("<configuration>");
696 rpc.append(request);
697 rpc.append("</configuration>");
698 rpc.append("</config></edit-config>");
699 rpc.append(RPC_CLOSE_TAG);
700 rpc.append("]]>]]>");
701 return rpc.toString();
702 }
703
704 public static List<ControllerInfo> getOpenFlowControllersFromConfig(HierarchicalConfiguration cfg) {
705 List<ControllerInfo> controllers = new ArrayList<ControllerInfo>();
706 String ipKey = "configuration.protocols.openflow.mode.ofagent-mode.controller.ip";
707
708 if (!cfg.configurationsAt(ipKey).isEmpty()) {
709 List<HierarchicalConfiguration> ipNodes = cfg.configurationsAt(ipKey);
710
711 ipNodes.forEach(ipNode -> {
712 int port = 0;
713 String proto = UNKNOWN;
714 HierarchicalConfiguration protocolNode = ipNode.configurationAt(PROTOCOL);
715 HierarchicalConfiguration tcpNode = protocolNode.configurationAt(TCP);
716
717 if (!tcpNode.isEmpty()) {
718 String portString = tcpNode.getString(PORT);
719
720 if (portString != null && !portString.isEmpty()) {
721 port = Integer.parseInt(portString);
722 }
723
724 proto = TCP;
725 }
726
727 String ipaddress = ipNode.getString(NAME);
728
729 if (ipaddress == null) {
730 ipaddress = UNKNOWN;
731 }
732
733 if (ipaddress.equals(UNKNOWN) || proto.equals(UNKNOWN) || port == 0) {
734 log.error("Controller infomation is invalid. Skip this controller node." +
735 " ipaddress: {}, proto: {}, port: {}", ipaddress, proto, port);
736 return;
737 }
738
739 controllers.add(new ControllerInfo(IpAddress.valueOf(ipaddress), port, proto));
740 });
741 } else {
742 log.error("Controller not present");
743 }
744
745 return controllers;
746 }
Michele Santuari21c14012016-11-14 13:31:33 +0100747}