6e86259a6eb8a46b60a19468166139686c68a742
[netvirt.git] /
1 /*
2  * Copyright (c) 2015 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.netvirt.bgpmanager.oam;
10
11 import java.io.BufferedReader;
12 import java.io.BufferedWriter;
13 import java.io.File;
14 import java.io.FileNotFoundException;
15 import java.io.FileWriter;
16 import java.io.IOException;
17 import java.io.InputStreamReader;
18 import java.io.PrintWriter;
19 import java.lang.management.ManagementFactory;
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.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Scanner;
31 import java.util.TimerTask;
32
33 import javax.annotation.Nonnull;
34 import javax.management.JMException;
35 import javax.management.MBeanServer;
36 import javax.management.ObjectName;
37
38 import org.opendaylight.netvirt.bgpmanager.thrift.gen.af_afi;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42
43 public class BgpCounters extends TimerTask {
44
45     private static final Logger LOG = LoggerFactory.getLogger(BgpCounters.class);
46     private static BgpCountersBroadcaster bgpStatsBroadcaster = null;
47     private MBeanServer bgpStatsServer = null;
48     private Map<String, String> countersMap = new HashMap<>();
49     private String bgpSdncMip = "127.0.0.1";
50     public static final String BGP_VPNV6_FILE = "cmd_ip_bgp_vpnv6_all.txt";
51     public static final String BGP_VPNV4_FILE = "cmd_ip_bgp_vpnv4_all.txt";
52     public static final String BGP_VPNV6_SUMMARY_FILE = "cmd_ip_bgp_vpnv6_all_summary.txt";
53     public static final String BGP_VPNV4_SUMMARY_FILE = "cmd_ip_bgp_vpnv4_all_summary.txt";
54
55     public BgpCounters(String mipAddress) {
56         bgpSdncMip = mipAddress;
57     }
58
59     @Override
60     public void run() {
61         try {
62             LOG.debug("Fetching counters from BGP");
63             resetCounters();
64             fetchCmdOutputs("cmd_ip_bgp_summary.txt", "show ip bgp summary");
65             fetchCmdOutputs("cmd_bgp_ipv4_unicast_statistics.txt", "show bgp ipv4 unicast statistics");
66             fetchCmdOutputs(BGP_VPNV4_FILE, "show ip bgp vpnv4 all");
67             fetchCmdOutputs(BGP_VPNV6_FILE, "show ip bgp vpnv6 all");
68             parseIpBgpSummary();
69             parseIpBgpVpnv4All();
70             parseIpBgpVpnv6All();
71             if (LOG.isDebugEnabled()) {
72                 dumpCounters();
73             }
74             if (bgpStatsBroadcaster == null) {
75                 //First time execution
76                 try {
77                     bgpStatsBroadcaster = new BgpCountersBroadcaster();
78                     bgpStatsServer = ManagementFactory.getPlatformMBeanServer();
79                     ObjectName bgpStatsObj = new ObjectName("SDNC.PM:type=BgpCountersBroadcaster");
80                     bgpStatsServer.registerMBean(bgpStatsBroadcaster, bgpStatsObj);
81                     LOG.info("BGP Counters MBean Registered :::");
82                 } catch (JMException e) {
83                     LOG.error("Adding a NotificationBroadcaster failed.", e);
84                     return;
85                 }
86             }
87             bgpStatsBroadcaster.setBgpCountersMap(countersMap);
88             LOG.debug("Finished updating the counters from BGP");
89         } catch (IOException e) {
90             LOG.error("Failed to publish bgp counters ", e);
91         }
92     }
93
94     private void dumpCounters() {
95         for (Map.Entry<String, String> entry : countersMap.entrySet()) {
96             LOG.debug("{}, Value = {}", entry.getKey(), entry.getValue());
97         }
98     }
99
100     void fetchCmdOutputs(String filename, String cmdName) throws IOException {
101         try (Socket socket = new Socket(bgpSdncMip, 2605);
102              PrintWriter toRouter = new PrintWriter(socket.getOutputStream(), true);
103              BufferedReader fromRouter = new BufferedReader(new InputStreamReader(socket.getInputStream()));
104              BufferedWriter toFile = new BufferedWriter(new FileWriter(filename, true))) {
105             socket.setSoTimeout(2 * 1000);
106
107             // Wait for the password prompt
108             StringBuilder sb = new StringBuilder();
109             int read;
110             char[] cbuf = new char[10];
111             while (!sb.toString().contains("Password:")) {
112                 if ((read = fromRouter.read(cbuf)) == -1) {
113                     LOG.error("Connection closed by BGPd.");
114                     return;
115                 }
116                 sb.append(cbuf, 0, read);
117             }
118
119             // Send the password
120             toRouter.println(BgpConstants.QBGP_VTY_PASSWORD);
121
122             // Wait for the prompt (ending with '>' or '#')
123             sb = new StringBuilder();
124             String prompt = null;
125             while (prompt == null) {
126                 switch (read = fromRouter.read()) {
127                     case -1:
128                         LOG.error("Connection closed by BGPd, read {}", sb.toString());
129                         return;
130                     case '>':
131                         // Fall through
132                     case '#':
133                         sb.append((char) read);
134                         prompt = sb.toString().trim();
135                         break;
136                     default:
137                         sb.append((char) read);
138                         break;
139                 }
140             }
141
142             // Enable
143             toRouter.println("en");
144
145             // Wait for '#'
146             while ((read = fromRouter.read()) != '#') {
147                 if (read == -1) {
148                     LOG.error("Connection closed by BGPd, read {}", sb.toString());
149                     return;
150                 }
151             }
152
153             // Send the command
154             toRouter.println(cmdName);
155
156             // Read all the router's output
157             sb = new StringBuilder();
158             cbuf = new char[1024];
159             while ((read = fromRouter.read(cbuf)) != -1) {
160                 sb.append(cbuf, 0, read);
161                 if (sb.toString().trim().endsWith(prompt)) {
162                     break;
163                 }
164             }
165
166             // Only keep output up to the last prompt
167             int lastPromptIndex = sb.lastIndexOf(prompt);
168             if (lastPromptIndex >= 0) {
169                 sb.delete(lastPromptIndex, sb.length());
170             }
171
172             // Store in the file
173             toFile.write(sb.toString().trim());
174         } catch (UnknownHostException e) {
175             LOG.error("Unknown host {}", bgpSdncMip, e);
176         } catch (SocketTimeoutException e) {
177             LOG.error("Socket timeout", e);
178         } catch (IOException e) {
179             LOG.error("I/O error", e);
180         }
181     }
182
183     private static boolean validate(@Nonnull final String ip, af_afi afi) {
184         if (ip.equals("")) {
185             return false;
186         }
187         int identifiedAFI = 0;
188         try {
189             InetAddress address = InetAddress.getByName(ip);
190             if (address instanceof Inet6Address) {
191                 identifiedAFI = af_afi.AFI_IPV6.getValue();
192             } else if (address instanceof Inet4Address) {
193                 identifiedAFI = af_afi.AFI_IP.getValue();
194             }
195         } catch (java.net.UnknownHostException e) {
196             /*if exception is catched then the prefix is not an IPv6 and IPv4*/
197             LOG.error("Unrecognized ip address ipAddress: {}", ip);
198         }
199         return (identifiedAFI == afi.getValue() ? true : false);
200     }
201
202     /*
203      * The below function parses the output of "show ip bgp summary" saved in a file.
204      * Below is the snippet for the same :-
205         <output>
206         BGP router identifier 10.183.254.53, local AS number 101
207         .....
208         Neighbor        V    AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd
209         10.183.254.76   4   100       3       4        0    0    0 00:01:27        0
210         .........
211         Total number of neighbors 1
212         </output>
213      */
214
215     private void parseIpBgpSummary() {
216         File file = new File("cmd_ip_bgp_summary.txt");
217
218         try (Scanner scanner = new Scanner(file)) {
219             boolean startEntries = false;
220             while (scanner.hasNextLine()) {
221                 String str = scanner.nextLine();
222                 if (str.contains("State/PfxRcd")) {
223                     startEntries = true;
224                 } else if (startEntries) {
225                     String[] result = str.split("\\s+");
226                     if (result.length < 5) {
227                         return;
228                     }
229                     String strIp = result[0].trim();
230                     if (!validate(strIp, af_afi.AFI_IP)) {
231                         return;
232                     }
233                     final String as = result[2];
234                     final String rx = result[3];
235                     final String tx = result[4];
236
237                     countersMap.put(
238                             BgpConstants.BGP_COUNTER_NBR_PKTS_RX + ":BGP_Nbr_IP_" + strIp + "_AS_" + as
239                                     + "_PktsReceived",
240                             rx);
241                     countersMap.put(
242                             BgpConstants.BGP_COUNTER_NBR_PKTS_TX + ":BGP_Nbr_IP_" + strIp + "_AS_" + as + "_PktsSent",
243                             tx);
244                 }
245             }
246         } catch (IOException e) {
247             LOG.error("Could not process the file {}", file.getAbsolutePath());
248         }
249     }
250     /*
251      * The below function parses the output of "show bgp ipv4 unicast statistics" saved in a file.
252      * Below is the sample output for the same :-
253         <output>
254         BGP IPv4 Unicast RIB statistics
255         ...
256         Total Prefixes                :            8
257         ......
258         </output>
259      */
260
261     private void parseBgpIpv4UnicastStatistics() {
262         File file = new File("cmd_bgp_ipv4_unicast_statistics.txt");
263         String totPfx = "";
264         try (Scanner scanner = new Scanner(file)) {
265             while (scanner.hasNextLine()) {
266                 String instr = scanner.nextLine();
267                 if (instr.contains("Total Prefixes")) {
268                     String[] result = instr.split(":");
269                     if (result.length > 1) {
270                         totPfx = result[1].trim();
271                     } else {
272                         totPfx = "0";
273                     }
274                     break;
275                 }
276             }
277         } catch (IOException e) {
278             LOG.error("Could not process the file {}", file.getAbsolutePath());
279             return;
280         }
281         countersMap.put(BgpConstants.BGP_COUNTER_TOTAL_PFX, totPfx);
282     }
283
284     /*
285      *  The below function parses the output of "show ip bgp vpnv4 all" saved in a file.
286      *  Below is the sample output for the same :-
287      *  show ip bgp vpnv4 all
288         <output>
289         BGP table version is 0, local router ID is 10.183.181.21
290         ......
291         Route Distinguisher: 100:1
292         *>i15.15.15.15/32   10.183.181.25            0    100      0 ?
293         *>i17.18.17.17/32   10.183.181.25            0    100      0 ?
294         *>i17.18.17.17/32   10.183.181.25            0    100      0 ?
295         *>i17.18.17.17/32   10.183.181.25            0    100      0 ?
296         Route Distinguisher: 100:2
297         *>i16.16.16.16/32   10.183.181.25            0    100      0 ?
298         *>i18.18.18.18/32   10.183.181.25            0    100      0 ?
299         *>i17.18.17.17/32   10.183.181.25            0    100      0 ?
300         </output>
301      */
302     private void parseIpBgpVpnv4All() {
303         File file = new File(BGP_VPNV4_FILE);
304         List<String> inputStrs = new ArrayList<>();
305
306         try (Scanner scanner = new Scanner(file)) {
307             while (scanner.hasNextLine()) {
308                 inputStrs.add(scanner.nextLine());
309             }
310         } catch (IOException e) {
311             LOG.error("Could not process the file {}", file.getAbsolutePath());
312             return;
313         }
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, i + 1, inputStrs);
320             }
321         }
322         /*populate the "BgpTotalPrefixes" counter by combining
323         the prefixes that are calculated per RD basis*/
324         int bgpTotalPfxs = calculateBgpTotalPrefixes();
325         LOG.trace("BGP Total Prefixes:{}",bgpTotalPfxs);
326         countersMap.put(BgpConstants.BGP_COUNTER_TOTAL_PFX,String.valueOf(bgpTotalPfxs));
327     }
328
329     /*
330      *  The below function parses the output of "show ip bgp vpnv6 all" saved in a file.
331      *  Below is the sample output for the same :-
332      *  show ip bgp vpnv6 all
333         <output>
334         BGP table version is 0, local router ID is 10.183.181.21
335         ......
336         Route Distinguisher: 100:1
337         *>i2001:db8:0:2::/128   10.183.181.25            0    100      0 ?
338         *>i2001:db8:0:2::/128   10.183.181.25            0    100      0 ?
339         *>i2001:db8:0:2::/128   10.183.181.25            0    100      0 ?
340         *>i2001:db8:0:2::/128   10.183.181.25            0    100      0 ?
341         Route Distinguisher: 100:2
342         *>i2001:db9:0:3::/128   10.183.181.25            0    100      0 ?
343         *>i2001:db9:0:3::/128   10.183.181.25            0    100      0 ?
344         *>i2001:db9:0:3::/128   10.183.181.25            0    100      0 ?
345         </output>
346      */
347     private void parseIpBgpVpnv6All() {
348         File file = new File(BGP_VPNV6_FILE);
349         List<String> inputStrs = new ArrayList<>();
350
351         try (Scanner scanner = new Scanner(file)) {
352             while (scanner.hasNextLine()) {
353                 inputStrs.add(scanner.nextLine());
354             }
355         } catch (IOException e) {
356             LOG.error("Could not process the file {}", file.getAbsolutePath());
357             return;
358         }
359         for (int i = 0; i < inputStrs.size(); i++) {
360             String instr = inputStrs.get(i);
361             if (instr.contains("Route Distinguisher")) {
362                 String[] result = instr.split(":");
363                 String rd = result[1].trim() + "_" + result[2].trim();
364                 i = processRouteCount(rd, i + 1, inputStrs);
365             }
366         }
367     }
368
369     private int processRouteCount(String rd, int startIndex, List<String> inputStrs) {
370         int num = startIndex;
371         int routeCount = 0;
372         String key = BgpConstants.BGP_COUNTER_RD_ROUTE_COUNT + ":BGP_RD_" + rd + "_route_count";
373
374         for (String str = inputStrs.get(num); str != null && !str.trim().equals("") && num < inputStrs.size();
375                 str = inputStrs.get(num)) {
376             if (str.contains("Route Distinguisher")) {
377                 countersMap.put(key, Integer.toString(routeCount));
378                 return num - 1;
379             }
380             routeCount++;
381             num++;
382             if (num == inputStrs.size()) {
383                 break;
384             }
385         }
386         if (routeCount == 0) {
387             // Erroneous condition, should never happen.
388             // Implies that the file contains marker for RD  without routes.
389             // will form an infinite loop if not broken
390             // by sending a big number back.
391             return Integer.MAX_VALUE;
392         }
393         countersMap.put(key, Integer.toString(routeCount));
394         return num - 1;
395     }
396
397     private int calculateBgpTotalPrefixes() {
398         return countersMap.entrySet().stream().filter(entry -> entry.getKey().contains(BgpConstants
399                 .BGP_COUNTER_RD_ROUTE_COUNT)).map(Map.Entry::getValue).mapToInt(Integer::parseInt).sum();
400     }
401
402     private void resetCounters() {
403         countersMap.clear();
404         resetFile("cmd_ip_bgp_summary.txt");
405         resetFile("cmd_bgp_ipv4_unicast_statistics.txt");
406         resetFile(BGP_VPNV4_FILE);
407         resetFile(BGP_VPNV6_FILE);
408     }
409
410     static void resetFile(String fileName) {
411         File file = (new File(fileName));
412         if (!file.delete()) {
413             try (PrintWriter pw = new PrintWriter(file)) {
414                 pw.print("");
415             } catch (FileNotFoundException e) {
416                 // Ignored
417             }
418         }
419     }
420
421     static Map<String, String> parseIpBgpVpnAllSummary(Map<String, String> countMap,
422                                                        String cmdFile,
423                                                        af_afi afi) {
424         File file = new File(cmdFile);
425
426         try (Scanner scanner = new Scanner(file)) {
427             boolean startEntries = false;
428             while (scanner.hasNextLine()) {
429                 String str = scanner.nextLine();
430                 LOG.trace("str is:: {}", str);
431                 if (str.contains("State/PfxRcd")) {
432                     startEntries = true;
433                 } else if (startEntries) {
434                     String[] result = str.split("\\s+");
435                     if (result.length > 9) {
436                         String strIp = result[0].trim();
437                         LOG.trace("strIp " + strIp);
438
439                         if (!validate(strIp, afi)) {
440                             break;
441                         }
442                         String statePfxRcvd = result[9];
443                         countMap.put(strIp, statePfxRcvd);
444                     }
445                 }
446             }
447         } catch (IOException e) {
448             LOG.trace("Could not process the file {}", file.getAbsolutePath());
449             return null;
450         }
451
452         return countMap;
453     }
454
455     static Map<String, String> parseIpBgpVpnv4AllSummary(Map<String, String> countMap) {
456         return BgpCounters.parseIpBgpVpnAllSummary(countMap,
457                                                    BGP_VPNV4_SUMMARY_FILE,
458                                                    af_afi.AFI_IP);
459     }
460
461     static Map<String, String> parseIpBgpVpnv6AllSummary(Map<String, String> countMap) {
462         return BgpCounters.parseIpBgpVpnAllSummary(countMap,
463                                                    BGP_VPNV6_SUMMARY_FILE,
464                                                    af_afi.AFI_IP);
465     }
466 }