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