2 * Copyright (c) 2015 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.netvirt.bgpmanager.oam;
11 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
12 import java.io.BufferedReader;
13 import java.io.BufferedWriter;
15 import java.io.FileNotFoundException;
16 import java.io.FileWriter;
17 import java.io.IOException;
18 import java.io.InputStreamReader;
19 import java.io.PrintWriter;
20 import java.net.Inet4Address;
21 import java.net.Inet6Address;
22 import java.net.InetAddress;
23 import java.net.Socket;
24 import java.net.SocketTimeoutException;
25 import java.net.UnknownHostException;
26 import java.util.ArrayList;
27 import java.util.List;
29 import java.util.Scanner;
30 import java.util.concurrent.ConcurrentHashMap;
31 import javax.inject.Inject;
32 import org.eclipse.jdt.annotation.NonNull;
33 import org.opendaylight.infrautils.metrics.Counter;
34 import org.opendaylight.infrautils.metrics.Labeled;
35 import org.opendaylight.infrautils.metrics.MetricDescriptor;
36 import org.opendaylight.infrautils.metrics.MetricProvider;
37 import org.opendaylight.netvirt.bgpmanager.thrift.gen.af_afi;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 @SuppressFBWarnings("DM_DEFAULT_ENCODING")
42 public class BgpCounters implements Runnable, AutoCloseable {
43 public static final String BGP_VPNV6_FILE = "cmd_ip_bgp_vpnv6_all.txt";
44 public static final String BGP_VPNV4_FILE = "cmd_ip_bgp_vpnv4_all.txt";
45 public static final String BGP_EVPN_FILE = "cmd_bgp_l2vpn_evpn_all.txt";
46 public static final String BGP_VPNV6_SUMMARY_FILE = "cmd_ip_bgp_vpnv6_all_summary.txt";
47 public static final String BGP_VPNV4_SUMMARY_FILE = "cmd_ip_bgp_vpnv4_all_summary.txt";
48 public static final String BGP_EVPN_SUMMARY_FILE = "cmd_bgp_evpn_all_summary.txt";
49 public static final String BFD_NBR_DETAIL_FILE = "cmd_bfd_neighbors_details.txt";
51 // BFD related constants
52 public static final int LINE = 1; // line where the ip address of neigbor present after "NeighbroAddr"
53 public static final int NBR_IP_WORD_INDEX = 1; // word where the ip address is present (count start from 0)
54 public static final int RX_COUNT_WORD_INDEX = 1; // word where the Rx Count is present after split :
55 public static final int TX_COUNT_WORD_INDEX = 1; // word where the Tx Count is present after split :
57 private static final Logger LOG = LoggerFactory.getLogger(BgpCounters.class);
59 private final Map<String, String> totalPfxMap = new ConcurrentHashMap<>();
61 private final String bgpSdncMip;
62 private final MetricProvider metricProvider;
65 public BgpCounters(String mipAddress, final MetricProvider metricProvider) {
66 this.metricProvider = metricProvider;
67 this.bgpSdncMip = mipAddress;
76 LOG.debug("Fetching counters from BGP");
78 fetchCmdOutputs("cmd_ip_bgp_summary.txt", "show ip bgp summary");
79 fetchCmdOutputs("cmd_bgp_ipv4_unicast_statistics.txt", "show bgp ipv4 unicast statistics");
80 fetchCmdOutputs(BGP_VPNV4_FILE, "show ip bgp vpnv4 all");
81 fetchCmdOutputs(BGP_VPNV6_FILE, "show ip bgp vpnv6 all");
82 fetchCmdOutputs(BGP_EVPN_FILE, "show bgp l2vpn evpn all");
83 fetchCmdOutputs(BFD_NBR_DETAIL_FILE, "show bgp bfd neighbors details");
88 parseBgpL2vpnEvpnAll();
89 parseBfdNbrsDetails();
90 LOG.debug("Finished updating the counters from BGP");
93 void fetchCmdOutputs(String filename, String cmdName) {
94 try (Socket socket = new Socket(bgpSdncMip, 2605);
95 PrintWriter toRouter = new PrintWriter(socket.getOutputStream(), true);
96 BufferedReader fromRouter = new BufferedReader(new InputStreamReader(socket.getInputStream()));
97 BufferedWriter toFile = new BufferedWriter(new FileWriter(filename, true))) {
98 socket.setSoTimeout(2 * 1000);
100 // Wait for the password prompt
101 StringBuilder sb = new StringBuilder();
103 char[] cbuf = new char[10];
104 while (!sb.toString().contains("Password:")) {
105 if ((read = fromRouter.read(cbuf)) == -1) {
106 LOG.error("Connection closed by BGPd.");
109 sb.append(cbuf, 0, read);
113 toRouter.println(BgpConstants.QBGP_VTY_PASSWORD);
115 // Wait for the prompt (ending with '>' or '#')
116 sb = new StringBuilder();
117 String prompt = null;
118 while (prompt == null) {
119 switch (read = fromRouter.read()) {
121 LOG.error("Connection closed by BGPd, read {}", sb.toString());
126 sb.append((char) read);
127 prompt = sb.toString().trim();
130 sb.append((char) read);
136 toRouter.println("en");
139 while ((read = fromRouter.read()) != '#') {
141 LOG.error("Connection closed by BGPd, read {}", sb.toString());
147 toRouter.println(cmdName);
149 // Read all the router's output
150 sb = new StringBuilder();
151 cbuf = new char[1024];
152 while ((read = fromRouter.read(cbuf)) != -1) {
153 sb.append(cbuf, 0, read);
154 if (sb.toString().trim().endsWith(prompt)) {
159 // Only keep output up to the last prompt
160 int lastPromptIndex = sb.lastIndexOf(prompt);
161 if (lastPromptIndex >= 0) {
162 sb.delete(lastPromptIndex, sb.length());
166 toFile.write(sb.toString().trim());
167 } catch (UnknownHostException e) {
168 LOG.error("Unknown host {}", bgpSdncMip, e);
169 } catch (SocketTimeoutException e) {
170 LOG.error("Socket timeout", e);
171 } catch (IOException e) {
172 LOG.error("I/O error", e);
176 private static boolean validate(@NonNull final String ip, af_afi afi) {
180 int identifiedAFI = 0;
182 InetAddress address = InetAddress.getByName(ip);
183 if (address instanceof Inet6Address) {
184 identifiedAFI = af_afi.AFI_IPV6.getValue();
185 } else if (address instanceof Inet4Address) {
186 identifiedAFI = af_afi.AFI_IP.getValue();
188 } catch (java.net.UnknownHostException e) {
189 /*if exception is catched then the prefix is not an IPv6 and IPv4*/
190 LOG.error("Unrecognized ip address ipAddress: {}", ip);
192 return identifiedAFI == afi.getValue() ? true : false;
196 * The below function parses the output of "show ip bgp summary" saved in a file.
197 * Below is the snippet for the same :-
199 BGP router identifier 10.183.254.53, local AS number 101
201 Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
202 10.183.254.76 4 100 3 4 0 0 0 00:01:27 0
204 Total number of neighbors 1
208 private void parseIpBgpSummary() {
209 File file = new File("cmd_ip_bgp_summary.txt");
211 try (Scanner scanner = new Scanner(file)) {
212 boolean startEntries = false;
213 while (scanner.hasNextLine()) {
214 String str = scanner.nextLine();
215 if (str.contains("State/PfxRcd")) {
217 } else if (startEntries) {
218 String[] result = str.split("\\s+");
219 if (result.length < 5) {
222 String strIp = result[0].trim();
223 if (!validate(strIp, af_afi.AFI_IP)) {
226 final String as = result[2];
227 final String rx = result[3];
228 final String tx = result[4];
230 Counter counter = getCounter(BgpConstants.BGP_COUNTER_NBR_PKTS_RX, as,
231 rx, null, strIp, null, "bgp-peer");
232 updateCounter(counter, Long.parseLong(rx));
234 counter = getCounter(BgpConstants.BGP_COUNTER_NBR_PKTS_TX, as,
235 null, tx, strIp, null, "bgp-peer");
236 updateCounter(counter, Long.parseLong(tx));
239 } catch (IOException e) {
240 LOG.error("Could not process the file {}", file.getAbsolutePath());
245 * The below function parses the output of "show ip bgp vpnv4 all" saved in a file.
246 * Below is the sample output for the same :-
247 * show ip bgp vpnv4 all
249 BGP table version is 0, local router ID is 10.183.181.21
251 Route Distinguisher: 100:1
252 *>i15.15.15.15/32 10.183.181.25 0 100 0 ?
253 *>i17.18.17.17/32 10.183.181.25 0 100 0 ?
254 *>i17.18.17.17/32 10.183.181.25 0 100 0 ?
255 *>i17.18.17.17/32 10.183.181.25 0 100 0 ?
256 Route Distinguisher: 100:2
257 *>i16.16.16.16/32 10.183.181.25 0 100 0 ?
258 *>i18.18.18.18/32 10.183.181.25 0 100 0 ?
259 *>i17.18.17.17/32 10.183.181.25 0 100 0 ?
262 private void parseIpBgpVpnv4All() {
263 File file = new File(BGP_VPNV4_FILE);
264 List<String> inputStrs = new ArrayList<>();
266 try (Scanner scanner = new Scanner(file)) {
267 while (scanner.hasNextLine()) {
268 inputStrs.add(scanner.nextLine());
270 } catch (IOException e) {
271 LOG.error("Could not process the file {}", file.getAbsolutePath());
274 for (int i = 0; i < inputStrs.size(); i++) {
275 String instr = inputStrs.get(i);
276 if (instr.contains("Route Distinguisher")) {
277 String[] result = instr.split(":");
278 String rd = result[1].trim() + "_" + result[2].trim();
279 i = processRouteCount(rd + "_VPNV4", i + 1, inputStrs);
285 * The below function parses the output of "show ip bgp vpnv6 all" saved in a file.
286 * Below is the sample output for the same :-
287 * show ip bgp vpnv6 all
289 BGP table version is 0, local router ID is 10.183.181.21
291 Route Distinguisher: 100:1
292 *>i2001:db8:0:2::/128 10.183.181.25 0 100 0 ?
293 *>i2001:db8:0:2::/128 10.183.181.25 0 100 0 ?
294 *>i2001:db8:0:2::/128 10.183.181.25 0 100 0 ?
295 *>i2001:db8:0:2::/128 10.183.181.25 0 100 0 ?
296 Route Distinguisher: 100:2
297 *>i2001:db9:0:3::/128 10.183.181.25 0 100 0 ?
298 *>i2001:db9:0:3::/128 10.183.181.25 0 100 0 ?
299 *>i2001:db9:0:3::/128 10.183.181.25 0 100 0 ?
302 private void parseIpBgpVpnv6All() {
303 File file = new File(BGP_VPNV6_FILE);
304 List<String> inputStrs = new ArrayList<>();
306 try (Scanner scanner = new Scanner(file)) {
307 while (scanner.hasNextLine()) {
308 inputStrs.add(scanner.nextLine());
310 } catch (IOException e) {
311 LOG.error("Could not process the file {}", file.getAbsolutePath());
314 for (int i = 0; i < inputStrs.size(); i++) {
315 String instr = inputStrs.get(i);
316 if (instr.contains("Route Distinguisher")) {
317 String[] result = instr.split(":");
318 String rd = result[1].trim() + "_" + result[2].trim();
319 i = processRouteCount(rd + "_VPNV6", i + 1, inputStrs);
324 private void parseBgpL2vpnEvpnAll() {
325 File file = new File(BGP_EVPN_FILE);
326 List<String> inputStrs = new ArrayList<>();
328 try (Scanner scanner = new Scanner(file)) {
329 while (scanner.hasNextLine()) {
330 inputStrs.add(scanner.nextLine());
332 } catch (IOException e) {
333 LOG.error("Could not process the file {}", file.getAbsolutePath());
336 for (int i = 0; i < inputStrs.size(); i++) {
337 String instr = inputStrs.get(i);
338 if (instr.contains("Route Distinguisher")) {
339 String[] result = instr.split(":");
340 String rd = result[1].trim() + "_" + result[2].trim();
341 i = processRouteCount(rd + "_EVPN", i + 1, inputStrs);
344 /*populate the "BgpTotalPrefixes" counter by combining
345 the prefixes that are calculated per RD basis*/
346 long bgpTotalPfxs = calculateBgpTotalPrefixes();
347 LOG.trace("BGP Total Prefixes:{}",bgpTotalPfxs);
348 Counter counter = getCounter(BgpConstants.BGP_COUNTER_TOTAL_PFX, null, null, null,
349 null, null, "bgp-peer");
350 updateCounter(counter, bgpTotalPfxs);
353 private void parseBfdNbrsDetails() {
354 File file = new File(BFD_NBR_DETAIL_FILE);
355 List<String> inputStrs = new ArrayList<>();
357 try (Scanner scanner = new Scanner(file)) {
358 while (scanner.hasNextLine()) {
359 inputStrs.add(scanner.nextLine());
361 } catch (IOException e) {
362 LOG.error("Could not process the file {}", file.getAbsolutePath());
365 String neighborIPstr = null;
366 for (int i = 0; i < inputStrs.size(); i++) {
367 String instr = inputStrs.get(i);
368 if (instr.contains("NeighAddr") && instr.contains("State")) {
369 neighborIPstr = inputStrs.get(i + LINE).split("\\s+")[NBR_IP_WORD_INDEX];
370 if (!validate(neighborIPstr, af_afi.AFI_IP)) {
371 LOG.error("Invalid neighbor IP {}", neighborIPstr);
375 if ((neighborIPstr != null) && inputStrs.get(i).contains("Rx Count:")
376 && inputStrs.get(i + 1).contains("Tx Count:")) {
380 rxCount = Long.parseLong(inputStrs.get(i).split(":")[RX_COUNT_WORD_INDEX].trim());
382 catch (NumberFormatException e) {
383 LOG.error("Rx count Number format exception: {}",
384 inputStrs.get(i + 1).split(":")[RX_COUNT_WORD_INDEX].trim());
391 txCount = Long.parseLong(inputStrs.get(i + 1).split(":")
392 [TX_COUNT_WORD_INDEX].trim());
393 } catch (NumberFormatException e) {
394 LOG.error("Tx count Number format exception: {}",
395 inputStrs.get(i + 1).split(":")[TX_COUNT_WORD_INDEX].trim());
398 Counter counter = getCounter(BgpConstants.BFD_COUNTER_NBR_PKTS_RX, null,
399 Long.toString(rxCount), null, neighborIPstr, null, "bfd-peer");
400 updateCounter(counter, rxCount);
402 counter = getCounter(BgpConstants.BFD_COUNTER_NBR_PKTS_TX, null,
403 null, Long.toString(txCount), neighborIPstr, null, "bfd-peer");
404 updateCounter(counter, txCount);
406 //Counter fetching is done, search for next BFD Neighbor IP
407 neighborIPstr = null;
412 private int processRouteCount(String rd, int startIndex, List<String> inputStrs) {
413 int num = startIndex;
416 String bgpRdRouteCountKey = BgpConstants.BGP_COUNTER_RD_ROUTE_COUNT + rd;
417 Counter counter = getCounter(BgpConstants.BGP_COUNTER_RD_ROUTE_COUNT, null, null, null,
418 null, rd, "bgp-peer");
420 for (String str = inputStrs.get(num); str != null && !str.trim().equals("")
421 && num < inputStrs.size();
422 str = inputStrs.get(num)) {
423 if (str.contains("Route Distinguisher")) {
424 totalPfxMap.put(bgpRdRouteCountKey, Long.toString(routeCount));
425 updateCounter(counter, routeCount);
430 if (num == inputStrs.size()) {
434 if (routeCount == 0) {
435 // Erroneous condition, should never happen.
436 // Implies that the file contains marker for RD without routes.
437 // will form an infinite loop if not broken
438 // by sending a big number back.
439 return Integer.MAX_VALUE;
441 updateCounter(counter, routeCount);
445 private Long calculateBgpTotalPrefixes() {
446 return totalPfxMap.entrySet().stream()
447 .map(Map.Entry::getValue).mapToLong(Long::parseLong).sum();
450 private void resetCounters() {
452 resetFile("cmd_ip_bgp_summary.txt");
453 resetFile("cmd_bgp_ipv4_unicast_statistics.txt");
454 resetFile(BGP_VPNV4_FILE);
455 resetFile(BGP_VPNV6_FILE);
456 resetFile(BGP_EVPN_FILE);
457 resetFile(BFD_NBR_DETAIL_FILE);
460 static void resetFile(String fileName) {
461 File file = new File(fileName);
462 if (!file.delete()) {
463 try (PrintWriter pw = new PrintWriter(file)) {
465 } catch (FileNotFoundException e) {
471 static Map<String, String> parseIpBgpVpnAllSummary(Map<String, String> countMap,
474 File file = new File(cmdFile);
476 try (Scanner scanner = new Scanner(file)) {
477 boolean startEntries = false;
478 while (scanner.hasNextLine()) {
479 String str = scanner.nextLine();
480 LOG.trace("str is:: {}", str);
481 if (str.contains("State/PfxRcd")) {
483 } else if (startEntries) {
484 String[] result = str.split("\\s+");
485 if (result.length > 9) {
486 String strIp = result[0].trim();
487 LOG.trace("strIp {} ", strIp);
489 if (!validate(strIp, afi)) {
492 String statePfxRcvd = result[9];
493 countMap.put(strIp, statePfxRcvd);
497 } catch (IOException e) {
498 LOG.trace("Could not process the file {}", file.getAbsolutePath());
505 static Map<String, String> parseIpBgpVpnv4AllSummary(Map<String, String> countMap) {
506 return BgpCounters.parseIpBgpVpnAllSummary(countMap,
507 BGP_VPNV4_SUMMARY_FILE,
511 static Map<String, String> parseIpBgpVpnv6AllSummary(Map<String, String> countMap) {
512 return BgpCounters.parseIpBgpVpnAllSummary(countMap,
513 BGP_VPNV6_SUMMARY_FILE,
517 static Map<String, String> parseBgpL2vpnEvpnAllSummary(Map<String, String> countMap) {
518 return BgpCounters.parseIpBgpVpnAllSummary(countMap,
519 BGP_EVPN_SUMMARY_FILE,
524 * This method updates Counter values.
525 * @param counter object of the Counter
526 * @param counterValue value of Counter
528 private void updateCounter(Counter counter, long counterValue) {
530 /*Reset counter to zero*/
531 counter.decrement(counter.get());
532 /*Set counter to specified value*/
533 counter.increment(counterValue);
534 } catch (IllegalStateException e) {
535 LOG.error("Exception occured during updating the Counter {}", counter, e);
540 * Returns the counter.
541 * This method returns counter and also creates counter if does not exist.
543 * @param counterName name of the counter.
544 * @param asValue as value.
545 * @param rxValue rx value.
546 * @param txValue tx value.
547 * @param neighborIp neighbor Ipaddress.
548 * @param rdValue rd value.
549 * @return counter object.
551 private Counter getCounter(String counterName, String asValue,
552 String rxValue, String txValue, String neighborIp, String rdValue, String peerType) {
553 String counterTypeEntityCounter = "entitycounter";
554 String labelKeyEntityType = "entitytype";
556 String labelValEntityTypeBgpPeer = null;
557 String labelKeyAsId = "asid";
558 String labelKeyNeighborIp = "neighborip";
560 String labelValEntityTypeBgpRd = "bgp-rd";
561 String labelKeyRd = "rd";
563 String counterTypeAggregateCounter = "aggregatecounter";
564 String labelKeyCounterName = "name";
566 Counter counter = null;
568 if (peerType.equals("bgp-peer")) {
569 labelValEntityTypeBgpPeer = "bgp-peer";
570 } else if (peerType.equals("bfd-peer")) {
571 labelValEntityTypeBgpPeer = "bfd-peer";
573 //nothing defined, default to "bgp-peer"
574 labelValEntityTypeBgpPeer = "bgp-peer";
577 if (rxValue != null) {
579 * Following is the key pattern for Counter BgpNeighborPacketsReceived
580 * netvirt.bgpmanager.entitycounter{entitytype=bgp-peer, asid=value, neighborip=value, name=countername}
582 Labeled<Labeled<Labeled<Labeled<Counter>>>> labeledCounter =
583 metricProvider.newCounter(MetricDescriptor.builder().anchor(this).project("netvirt")
584 .module("bgpmanager").id(counterTypeEntityCounter).build(),
585 labelKeyEntityType, labelKeyAsId,
586 labelKeyNeighborIp, labelKeyCounterName);
587 counter = labeledCounter.label(labelValEntityTypeBgpPeer).label(asValue)
588 .label(neighborIp).label(counterName);
589 } else if (txValue != null) {
591 * Following is the key pattern for Counter BgpNeighborPacketsSent
592 * netvirt.bgpmanager.entitycounter{entitytype=bgp-peer, asid=value, neighborip=value, name=countername}
594 Labeled<Labeled<Labeled<Labeled<Counter>>>> labeledCounter =
595 metricProvider.newCounter(MetricDescriptor.builder().anchor(this).project("netvirt")
596 .module("bgpmanager").id(counterTypeEntityCounter).build(),
597 labelKeyEntityType, labelKeyAsId,
598 labelKeyNeighborIp, labelKeyCounterName);
599 counter = labeledCounter.label(labelValEntityTypeBgpPeer).label(asValue)
600 .label(neighborIp).label(counterName);
601 } else if (rdValue != null) {
603 * Following is the key pattern for Counter BgpRdRouteCount
604 * netvirt.bgpmanager.entitycounter{entitytype=bgp-rd, rd=value, name=countername}
606 Labeled<Labeled<Labeled<Counter>>> labeledCounter =
607 metricProvider.newCounter(MetricDescriptor.builder().anchor(this).project("netvirt")
608 .module("bgpmanager").id(counterTypeEntityCounter).build(),
609 labelKeyEntityType, labelKeyRd,
610 labelKeyCounterName);
611 counter = labeledCounter.label(labelValEntityTypeBgpRd).label(rdValue)
615 * Following is the key pattern for Counter BgpTotalPrefixes:Bgp_Total_Prefixes
616 * netvirt.bgpmanager.aggregatecounter{name=countername}
618 Labeled<Counter> labeledCounter =
619 metricProvider.newCounter(MetricDescriptor.builder().anchor(this).project("netvirt")
620 .module("bgpmanager").id(counterTypeAggregateCounter).build(),
621 labelKeyCounterName);
622 counter = labeledCounter.label(counterName);
627 public void clearBfdNbrCounters(String neighborIPstr) {
628 Counter bfdRxCounter = getCounter(BgpConstants.BFD_COUNTER_NBR_PKTS_RX, null,
629 Long.toString(0), null, neighborIPstr, null, "bfd-peer");
630 updateCounter(bfdRxCounter, 0);
632 Counter bfdTxCounter = getCounter(BgpConstants.BFD_COUNTER_NBR_PKTS_TX, null,
633 null, Long.toString(0), neighborIPstr, null, "bfd-peer");
634 updateCounter(bfdTxCounter, 0);