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;
13 import java.io.BufferedReader;
14 import java.io.BufferedWriter;
16 import java.io.FileNotFoundException;
17 import java.io.FileWriter;
18 import java.io.IOException;
19 import java.io.InputStreamReader;
20 import java.io.PrintWriter;
21 import java.net.Inet4Address;
22 import java.net.Inet6Address;
23 import java.net.InetAddress;
24 import java.net.Socket;
25 import java.net.SocketTimeoutException;
26 import java.net.UnknownHostException;
27 import java.util.ArrayList;
28 import java.util.List;
30 import java.util.Scanner;
31 import java.util.concurrent.ConcurrentHashMap;
33 import javax.annotation.Nonnull;
34 import javax.inject.Inject;
36 import org.opendaylight.infrautils.metrics.Counter;
37 import org.opendaylight.infrautils.metrics.Labeled;
38 import org.opendaylight.infrautils.metrics.MetricDescriptor;
39 import org.opendaylight.infrautils.metrics.MetricProvider;
40 import org.opendaylight.netvirt.bgpmanager.thrift.gen.af_afi;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
44 @SuppressFBWarnings("DM_DEFAULT_ENCODING")
45 public class BgpCounters implements Runnable, AutoCloseable {
46 public static final String BGP_VPNV6_FILE = "cmd_ip_bgp_vpnv6_all.txt";
47 public static final String BGP_VPNV4_FILE = "cmd_ip_bgp_vpnv4_all.txt";
48 public static final String BGP_EVPN_FILE = "cmd_bgp_l2vpn_evpn_all.txt";
49 public static final String BGP_VPNV6_SUMMARY_FILE = "cmd_ip_bgp_vpnv6_all_summary.txt";
50 public static final String BGP_VPNV4_SUMMARY_FILE = "cmd_ip_bgp_vpnv4_all_summary.txt";
51 public static final String BGP_EVPN_SUMMARY_FILE = "cmd_bgp_evpn_all_summary.txt";
52 public static final String BFD_NBR_DETAIL_FILE = "cmd_bfd_neighbors_details.txt";
54 // BFD related constants
55 public static final int LINE = 1; // line where the ip address of neigbor present after "NeighbroAddr"
56 public static final int NBR_IP_WORD_INDEX = 1; // word where the ip address is present (count start from 0)
57 public static final int RX_COUNT_WORD_INDEX = 1; // word where the Rx Count is present after split :
58 public static final int TX_COUNT_WORD_INDEX = 1; // word where the Tx Count is present after split :
60 private static final Logger LOG = LoggerFactory.getLogger(BgpCounters.class);
62 private final Map<String, String> totalPfxMap = new ConcurrentHashMap<>();
64 private final String bgpSdncMip;
65 private final MetricProvider metricProvider;
68 public BgpCounters(String mipAddress, final MetricProvider metricProvider) {
69 this.metricProvider = metricProvider;
70 this.bgpSdncMip = mipAddress;
79 LOG.debug("Fetching counters from BGP");
81 fetchCmdOutputs("cmd_ip_bgp_summary.txt", "show ip bgp summary");
82 fetchCmdOutputs("cmd_bgp_ipv4_unicast_statistics.txt", "show bgp ipv4 unicast statistics");
83 fetchCmdOutputs(BGP_VPNV4_FILE, "show ip bgp vpnv4 all");
84 fetchCmdOutputs(BGP_VPNV6_FILE, "show ip bgp vpnv6 all");
85 fetchCmdOutputs(BGP_EVPN_FILE, "show bgp l2vpn evpn all");
86 fetchCmdOutputs(BFD_NBR_DETAIL_FILE, "show bgp bfd neighbors details");
91 parseBgpL2vpnEvpnAll();
92 parseBfdNbrsDetails();
93 LOG.debug("Finished updating the counters from BGP");
96 void fetchCmdOutputs(String filename, String cmdName) {
97 try (Socket socket = new Socket(bgpSdncMip, 2605);
98 PrintWriter toRouter = new PrintWriter(socket.getOutputStream(), true);
99 BufferedReader fromRouter = new BufferedReader(new InputStreamReader(socket.getInputStream()));
100 BufferedWriter toFile = new BufferedWriter(new FileWriter(filename, true))) {
101 socket.setSoTimeout(2 * 1000);
103 // Wait for the password prompt
104 StringBuilder sb = new StringBuilder();
106 char[] cbuf = new char[10];
107 while (!sb.toString().contains("Password:")) {
108 if ((read = fromRouter.read(cbuf)) == -1) {
109 LOG.error("Connection closed by BGPd.");
112 sb.append(cbuf, 0, read);
116 toRouter.println(BgpConstants.QBGP_VTY_PASSWORD);
118 // Wait for the prompt (ending with '>' or '#')
119 sb = new StringBuilder();
120 String prompt = null;
121 while (prompt == null) {
122 switch (read = fromRouter.read()) {
124 LOG.error("Connection closed by BGPd, read {}", sb.toString());
129 sb.append((char) read);
130 prompt = sb.toString().trim();
133 sb.append((char) read);
139 toRouter.println("en");
142 while ((read = fromRouter.read()) != '#') {
144 LOG.error("Connection closed by BGPd, read {}", sb.toString());
150 toRouter.println(cmdName);
152 // Read all the router's output
153 sb = new StringBuilder();
154 cbuf = new char[1024];
155 while ((read = fromRouter.read(cbuf)) != -1) {
156 sb.append(cbuf, 0, read);
157 if (sb.toString().trim().endsWith(prompt)) {
162 // Only keep output up to the last prompt
163 int lastPromptIndex = sb.lastIndexOf(prompt);
164 if (lastPromptIndex >= 0) {
165 sb.delete(lastPromptIndex, sb.length());
169 toFile.write(sb.toString().trim());
170 } catch (UnknownHostException e) {
171 LOG.error("Unknown host {}", bgpSdncMip, e);
172 } catch (SocketTimeoutException e) {
173 LOG.error("Socket timeout", e);
174 } catch (IOException e) {
175 LOG.error("I/O error", e);
179 private static boolean validate(@Nonnull final String ip, af_afi afi) {
183 int identifiedAFI = 0;
185 InetAddress address = InetAddress.getByName(ip);
186 if (address instanceof Inet6Address) {
187 identifiedAFI = af_afi.AFI_IPV6.getValue();
188 } else if (address instanceof Inet4Address) {
189 identifiedAFI = af_afi.AFI_IP.getValue();
191 } catch (java.net.UnknownHostException e) {
192 /*if exception is catched then the prefix is not an IPv6 and IPv4*/
193 LOG.error("Unrecognized ip address ipAddress: {}", ip);
195 return identifiedAFI == afi.getValue() ? true : false;
199 * The below function parses the output of "show ip bgp summary" saved in a file.
200 * Below is the snippet for the same :-
202 BGP router identifier 10.183.254.53, local AS number 101
204 Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
205 10.183.254.76 4 100 3 4 0 0 0 00:01:27 0
207 Total number of neighbors 1
211 private void parseIpBgpSummary() {
212 File file = new File("cmd_ip_bgp_summary.txt");
214 try (Scanner scanner = new Scanner(file)) {
215 boolean startEntries = false;
216 while (scanner.hasNextLine()) {
217 String str = scanner.nextLine();
218 if (str.contains("State/PfxRcd")) {
220 } else if (startEntries) {
221 String[] result = str.split("\\s+");
222 if (result.length < 5) {
225 String strIp = result[0].trim();
226 if (!validate(strIp, af_afi.AFI_IP)) {
229 final String as = result[2];
230 final String rx = result[3];
231 final String tx = result[4];
233 Counter counter = getCounter(BgpConstants.BGP_COUNTER_NBR_PKTS_RX, as,
234 rx, null, strIp, null, "bgp-peer");
235 updateCounter(counter, Long.parseLong(rx));
237 counter = getCounter(BgpConstants.BGP_COUNTER_NBR_PKTS_TX, as,
238 null, tx, strIp, null, "bgp-peer");
239 updateCounter(counter, Long.parseLong(tx));
242 } catch (IOException e) {
243 LOG.error("Could not process the file {}", file.getAbsolutePath());
248 * The below function parses the output of "show ip bgp vpnv4 all" saved in a file.
249 * Below is the sample output for the same :-
250 * show ip bgp vpnv4 all
252 BGP table version is 0, local router ID is 10.183.181.21
254 Route Distinguisher: 100:1
255 *>i15.15.15.15/32 10.183.181.25 0 100 0 ?
256 *>i17.18.17.17/32 10.183.181.25 0 100 0 ?
257 *>i17.18.17.17/32 10.183.181.25 0 100 0 ?
258 *>i17.18.17.17/32 10.183.181.25 0 100 0 ?
259 Route Distinguisher: 100:2
260 *>i16.16.16.16/32 10.183.181.25 0 100 0 ?
261 *>i18.18.18.18/32 10.183.181.25 0 100 0 ?
262 *>i17.18.17.17/32 10.183.181.25 0 100 0 ?
265 private void parseIpBgpVpnv4All() {
266 File file = new File(BGP_VPNV4_FILE);
267 List<String> inputStrs = new ArrayList<>();
269 try (Scanner scanner = new Scanner(file)) {
270 while (scanner.hasNextLine()) {
271 inputStrs.add(scanner.nextLine());
273 } catch (IOException e) {
274 LOG.error("Could not process the file {}", file.getAbsolutePath());
277 for (int i = 0; i < inputStrs.size(); i++) {
278 String instr = inputStrs.get(i);
279 if (instr.contains("Route Distinguisher")) {
280 String[] result = instr.split(":");
281 String rd = result[1].trim() + "_" + result[2].trim();
282 i = processRouteCount(rd + "_VPNV4", i + 1, inputStrs);
288 * The below function parses the output of "show ip bgp vpnv6 all" saved in a file.
289 * Below is the sample output for the same :-
290 * show ip bgp vpnv6 all
292 BGP table version is 0, local router ID is 10.183.181.21
294 Route Distinguisher: 100:1
295 *>i2001:db8:0:2::/128 10.183.181.25 0 100 0 ?
296 *>i2001:db8:0:2::/128 10.183.181.25 0 100 0 ?
297 *>i2001:db8:0:2::/128 10.183.181.25 0 100 0 ?
298 *>i2001:db8:0:2::/128 10.183.181.25 0 100 0 ?
299 Route Distinguisher: 100:2
300 *>i2001:db9:0:3::/128 10.183.181.25 0 100 0 ?
301 *>i2001:db9:0:3::/128 10.183.181.25 0 100 0 ?
302 *>i2001:db9:0:3::/128 10.183.181.25 0 100 0 ?
305 private void parseIpBgpVpnv6All() {
306 File file = new File(BGP_VPNV6_FILE);
307 List<String> inputStrs = new ArrayList<>();
309 try (Scanner scanner = new Scanner(file)) {
310 while (scanner.hasNextLine()) {
311 inputStrs.add(scanner.nextLine());
313 } catch (IOException e) {
314 LOG.error("Could not process the file {}", file.getAbsolutePath());
317 for (int i = 0; i < inputStrs.size(); i++) {
318 String instr = inputStrs.get(i);
319 if (instr.contains("Route Distinguisher")) {
320 String[] result = instr.split(":");
321 String rd = result[1].trim() + "_" + result[2].trim();
322 i = processRouteCount(rd + "_VPNV6", i + 1, inputStrs);
327 private void parseBgpL2vpnEvpnAll() {
328 File file = new File(BGP_EVPN_FILE);
329 List<String> inputStrs = new ArrayList<>();
331 try (Scanner scanner = new Scanner(file)) {
332 while (scanner.hasNextLine()) {
333 inputStrs.add(scanner.nextLine());
335 } catch (IOException e) {
336 LOG.error("Could not process the file {}", file.getAbsolutePath());
339 for (int i = 0; i < inputStrs.size(); i++) {
340 String instr = inputStrs.get(i);
341 if (instr.contains("Route Distinguisher")) {
342 String[] result = instr.split(":");
343 String rd = result[1].trim() + "_" + result[2].trim();
344 i = processRouteCount(rd + "_EVPN", i + 1, inputStrs);
347 /*populate the "BgpTotalPrefixes" counter by combining
348 the prefixes that are calculated per RD basis*/
349 long bgpTotalPfxs = calculateBgpTotalPrefixes();
350 LOG.trace("BGP Total Prefixes:{}",bgpTotalPfxs);
351 Counter counter = getCounter(BgpConstants.BGP_COUNTER_TOTAL_PFX, null, null, null,
352 null, null, "bgp-peer");
353 updateCounter(counter, bgpTotalPfxs);
356 private void parseBfdNbrsDetails() {
357 File file = new File(BFD_NBR_DETAIL_FILE);
358 List<String> inputStrs = new ArrayList<>();
360 try (Scanner scanner = new Scanner(file)) {
361 while (scanner.hasNextLine()) {
362 inputStrs.add(scanner.nextLine());
364 } catch (IOException e) {
365 LOG.error("Could not process the file {}", file.getAbsolutePath());
368 String neighborIPstr = null;
369 for (int i = 0; i < inputStrs.size(); i++) {
370 String instr = inputStrs.get(i);
371 if (instr.contains("NeighAddr") && instr.contains("State")) {
372 neighborIPstr = inputStrs.get(i + LINE).split("\\s+")[NBR_IP_WORD_INDEX];
373 if (!validate(neighborIPstr, af_afi.AFI_IP)) {
374 LOG.error("Invalid neighbor IP {}", neighborIPstr);
378 if ((neighborIPstr != null) && inputStrs.get(i).contains("Rx Count:")
379 && inputStrs.get(i + 1).contains("Tx Count:")) {
383 rxCount = Long.parseLong(inputStrs.get(i).split(":")[RX_COUNT_WORD_INDEX].trim());
385 catch (NumberFormatException e) {
386 LOG.error("Rx count Number format exception: {}",
387 inputStrs.get(i + 1).split(":")[RX_COUNT_WORD_INDEX].trim());
394 txCount = Long.parseLong(inputStrs.get(i + 1).split(":")
395 [TX_COUNT_WORD_INDEX].trim());
396 } catch (NumberFormatException e) {
397 LOG.error("Tx count Number format exception: {}",
398 inputStrs.get(i + 1).split(":")[TX_COUNT_WORD_INDEX].trim());
401 Counter counter = getCounter(BgpConstants.BFD_COUNTER_NBR_PKTS_RX, null,
402 Long.toString(rxCount), null, neighborIPstr, null, "bfd-peer");
403 updateCounter(counter, rxCount);
405 counter = getCounter(BgpConstants.BFD_COUNTER_NBR_PKTS_TX, null,
406 null, Long.toString(txCount), neighborIPstr, null, "bfd-peer");
407 updateCounter(counter, txCount);
409 //Counter fetching is done, search for next BFD Neighbor IP
410 neighborIPstr = null;
415 private int processRouteCount(String rd, int startIndex, List<String> inputStrs) {
416 int num = startIndex;
419 String bgpRdRouteCountKey = BgpConstants.BGP_COUNTER_RD_ROUTE_COUNT + rd;
420 Counter counter = getCounter(BgpConstants.BGP_COUNTER_RD_ROUTE_COUNT, null, null, null,
421 null, rd, "bgp-peer");
423 for (String str = inputStrs.get(num); str != null && !str.trim().equals("")
424 && num < inputStrs.size();
425 str = inputStrs.get(num)) {
426 if (str.contains("Route Distinguisher")) {
427 totalPfxMap.put(bgpRdRouteCountKey, Long.toString(routeCount));
428 updateCounter(counter, routeCount);
433 if (num == inputStrs.size()) {
437 if (routeCount == 0) {
438 // Erroneous condition, should never happen.
439 // Implies that the file contains marker for RD without routes.
440 // will form an infinite loop if not broken
441 // by sending a big number back.
442 return Integer.MAX_VALUE;
444 updateCounter(counter, routeCount);
448 private Long calculateBgpTotalPrefixes() {
449 return totalPfxMap.entrySet().stream()
450 .map(Map.Entry::getValue).mapToLong(Long::parseLong).sum();
453 private void resetCounters() {
455 resetFile("cmd_ip_bgp_summary.txt");
456 resetFile("cmd_bgp_ipv4_unicast_statistics.txt");
457 resetFile(BGP_VPNV4_FILE);
458 resetFile(BGP_VPNV6_FILE);
459 resetFile(BGP_EVPN_FILE);
460 resetFile(BFD_NBR_DETAIL_FILE);
463 static void resetFile(String fileName) {
464 File file = new File(fileName);
465 if (!file.delete()) {
466 try (PrintWriter pw = new PrintWriter(file)) {
468 } catch (FileNotFoundException e) {
474 static Map<String, String> parseIpBgpVpnAllSummary(Map<String, String> countMap,
477 File file = new File(cmdFile);
479 try (Scanner scanner = new Scanner(file)) {
480 boolean startEntries = false;
481 while (scanner.hasNextLine()) {
482 String str = scanner.nextLine();
483 LOG.trace("str is:: {}", str);
484 if (str.contains("State/PfxRcd")) {
486 } else if (startEntries) {
487 String[] result = str.split("\\s+");
488 if (result.length > 9) {
489 String strIp = result[0].trim();
490 LOG.trace("strIp {} ", strIp);
492 if (!validate(strIp, afi)) {
495 String statePfxRcvd = result[9];
496 countMap.put(strIp, statePfxRcvd);
500 } catch (IOException e) {
501 LOG.trace("Could not process the file {}", file.getAbsolutePath());
508 static Map<String, String> parseIpBgpVpnv4AllSummary(Map<String, String> countMap) {
509 return BgpCounters.parseIpBgpVpnAllSummary(countMap,
510 BGP_VPNV4_SUMMARY_FILE,
514 static Map<String, String> parseIpBgpVpnv6AllSummary(Map<String, String> countMap) {
515 return BgpCounters.parseIpBgpVpnAllSummary(countMap,
516 BGP_VPNV6_SUMMARY_FILE,
520 static Map<String, String> parseBgpL2vpnEvpnAllSummary(Map<String, String> countMap) {
521 return BgpCounters.parseIpBgpVpnAllSummary(countMap,
522 BGP_EVPN_SUMMARY_FILE,
527 * This method updates Counter values.
528 * @param counter object of the Counter
529 * @param counterValue value of Counter
531 private void updateCounter(Counter counter, long counterValue) {
533 /*Reset counter to zero*/
534 counter.decrement(counter.get());
535 /*Set counter to specified value*/
536 counter.increment(counterValue);
537 } catch (IllegalStateException e) {
538 LOG.error("Exception occured during updating the Counter {}", counter, e);
543 * Returns the counter.
544 * This method returns counter and also creates counter if does not exist.
546 * @param counterName name of the counter.
547 * @param asValue as value.
548 * @param rxValue rx value.
549 * @param txValue tx value.
550 * @param neighborIp neighbor Ipaddress.
551 * @param rdValue rd value.
552 * @return counter object.
554 private Counter getCounter(String counterName, String asValue,
555 String rxValue, String txValue, String neighborIp, String rdValue, String peerType) {
556 String counterTypeEntityCounter = "entitycounter";
557 String labelKeyEntityType = "entitytype";
559 String labelValEntityTypeBgpPeer = null;
560 String labelKeyAsId = "asid";
561 String labelKeyNeighborIp = "neighborip";
563 String labelValEntityTypeBgpRd = "bgp-rd";
564 String labelKeyRd = "rd";
566 String counterTypeAggregateCounter = "aggregatecounter";
567 String labelKeyCounterName = "name";
569 Counter counter = null;
571 if (peerType.equals("bgp-peer")) {
572 labelValEntityTypeBgpPeer = "bgp-peer";
573 } else if (peerType.equals("bfd-peer")) {
574 labelValEntityTypeBgpPeer = "bfd-peer";
576 //nothing defined, default to "bgp-peer"
577 labelValEntityTypeBgpPeer = "bgp-peer";
580 if (rxValue != null) {
582 * Following is the key pattern for Counter BgpNeighborPacketsReceived
583 * netvirt.bgpmanager.entitycounter{entitytype=bgp-peer, asid=value, neighborip=value, name=countername}
585 Labeled<Labeled<Labeled<Labeled<Counter>>>> labeledCounter =
586 metricProvider.newCounter(MetricDescriptor.builder().anchor(this).project("netvirt")
587 .module("bgpmanager").id(counterTypeEntityCounter).build(),
588 labelKeyEntityType, labelKeyAsId,
589 labelKeyNeighborIp, labelKeyCounterName);
590 counter = labeledCounter.label(labelValEntityTypeBgpPeer).label(asValue)
591 .label(neighborIp).label(counterName);
592 } else if (txValue != null) {
594 * Following is the key pattern for Counter BgpNeighborPacketsSent
595 * netvirt.bgpmanager.entitycounter{entitytype=bgp-peer, asid=value, neighborip=value, name=countername}
597 Labeled<Labeled<Labeled<Labeled<Counter>>>> labeledCounter =
598 metricProvider.newCounter(MetricDescriptor.builder().anchor(this).project("netvirt")
599 .module("bgpmanager").id(counterTypeEntityCounter).build(),
600 labelKeyEntityType, labelKeyAsId,
601 labelKeyNeighborIp, labelKeyCounterName);
602 counter = labeledCounter.label(labelValEntityTypeBgpPeer).label(asValue)
603 .label(neighborIp).label(counterName);
604 } else if (rdValue != null) {
606 * Following is the key pattern for Counter BgpRdRouteCount
607 * netvirt.bgpmanager.entitycounter{entitytype=bgp-rd, rd=value, name=countername}
609 Labeled<Labeled<Labeled<Counter>>> labeledCounter =
610 metricProvider.newCounter(MetricDescriptor.builder().anchor(this).project("netvirt")
611 .module("bgpmanager").id(counterTypeEntityCounter).build(),
612 labelKeyEntityType, labelKeyRd,
613 labelKeyCounterName);
614 counter = labeledCounter.label(labelValEntityTypeBgpRd).label(rdValue)
618 * Following is the key pattern for Counter BgpTotalPrefixes:Bgp_Total_Prefixes
619 * netvirt.bgpmanager.aggregatecounter{name=countername}
621 Labeled<Counter> labeledCounter =
622 metricProvider.newCounter(MetricDescriptor.builder().anchor(this).project("netvirt")
623 .module("bgpmanager").id(counterTypeAggregateCounter).build(),
624 labelKeyCounterName);
625 counter = labeledCounter.label(counterName);
630 public void clearBfdNbrCounters(String neighborIPstr) {
631 Counter bfdRxCounter = getCounter(BgpConstants.BFD_COUNTER_NBR_PKTS_RX, null,
632 Long.toString(0), null, neighborIPstr, null, "bfd-peer");
633 updateCounter(bfdRxCounter, 0);
635 Counter bfdTxCounter = getCounter(BgpConstants.BFD_COUNTER_NBR_PKTS_TX, null,
636 null, Long.toString(0), neighborIPstr, null, "bfd-peer");
637 updateCounter(bfdTxCounter, 0);