File Coverage

lib/App/RouterColorizer.pm
Criterion Covered Total %
statement 406 431 94.2
branch 17 18 94.4
condition 4 4 100.0
subroutine 25 25 100.0
pod 1 1 100.0
total 453 479 94.5


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2              
3             #
4             # Copyright (C) 2021-2023 Joelle Maslak
5             # All Rights Reserved - See License
6             #
7              
8             # ABSTRACT: Colorize router CLI output
9              
10 10     10   1962524 use v5.22;
  10         88  
11 10     10   66 use strict;
  10         33  
  10         244  
12 10     10   51 use warnings;
  10         25  
  10         530  
13              
14             package App::RouterColorizer;
15             $App::RouterColorizer::VERSION = '1.231100';
16 10     10   5736 use Moose;
  10         4713327  
  10         60  
17              
18 10     10   78760 use feature 'signatures';
  10         26  
  10         1822  
19 10     10   112 no warnings 'experimental::signatures';
  10         30  
  10         531  
20              
21 10     10   7062 use English;
  10         36824  
  10         62  
22 10     10   9559 use Import::Into;
  10         5343  
  10         353  
23 10     10   72 use List::Util qw(any);
  10         21  
  10         810  
24 10     10   5548 use Regexp::Common qw/net number/;
  10         27407  
  10         45  
25              
26             # Ugly! But we need multidimensional arrays to work to get access to the
27             # syntactic sugar in Regexp::Common. Unfortunately, this feature didn't
28             # exist until recently (it was defaulting to being on).
29             BEGIN {
30 10 50   10   174665 if ( $PERL_VERSION gt v5.33.0 ) {
31 0         0 feature->import::into( __PACKAGE__, qw(multidimensional) );
32             }
33             }
34              
35             our $GREEN = "\e[32m"; # Green ANSI code
36             our $RED = "\e[1;31m"; # Bold + Red ANSI code
37             our $ORANGE = "\e[33m"; # Orange
38             our $INFO = "\e[36m"; # Cyan
39             our $RESET = "\e[0m";
40              
41             our @BGCOLORS = (
42             "\e[30m\e[47m", # black on white
43             "\e[30m\e[41m", # black on red
44             "\e[30m\e[42m", # black on green
45             "\e[30m\e[43m", # black on yellow (orange)
46             "\e[37m\e[44m", # white on blue
47             "\e[30m\e[45m", # black on magenta
48             "\e[30m\e[46m", # black on cyan
49             );
50              
51             our $NUM = qr/$RE{num}{real}/;
52             our $INT = qr/$RE{num}{int}{-sign => ''}/;
53             our $POSINT = qr/(?!0)$INT/;
54             our $LOWLIGHT = qr/ (?: -[3-9][0-9]\. [0-9]{1,2} )
55             | (?: - \s Inf)
56             | (?: -2 [5-9] \. [0-9]{1,2} )/xx;
57             our $VERYLL = qr/ (?: -[4-9][0-9]\. [0-9]{1,2} )/xx;
58             our $LIGHT = qr/ (?: $NUM ) | (?: N\/A ) /xx;
59              
60             our $GOODRETURNLOSS = qr/ (?: 29\.[0-9]+ )
61             | (?: [3-9][0-9]+\.[0-9]+ )/xx;
62             our $WARNRETURNLOSS = qr/ (?: 2[0-8]\.[0-9]+ )
63             | (?: 1[6-9]\.[0-9]+ )/xx;
64             our $BADRETURNLOSS = qr/ (?: 1[0-5]\.[0-9]+ )
65             | (?: [0-9]\.[0-9]+ )/xx;
66              
67             our $IPV4CIDR = qr/ $RE{net}{IPv4}
68             (?: \/
69             (?:
70             (?:[12][0-9])
71             | (?:3[0-2])
72             | (?:[0-9])
73             )
74             )?
75             /xx;
76              
77             our $IPV6CIDR = qr/ $RE{net}{IPv6}
78             (?: \/
79             (?:
80             (?:1[01][0-9])
81             | (?:12[0-8])
82             | (?:[1-9][0-9])
83             | (?:[0-9])
84             )
85             )?
86             /xx;
87              
88             our $BIGALARMS = qr/critical|major|minor|warning/;
89             our $LITTLEALARMS = qr/info/;
90             our $BIGOVERRIDES = qr/ (?:\QRx Remote Fault\E)
91             | (?:\QFar End Client Signal Fail\E)
92             /xx;
93              
94             our @INTERFACE_IGNORES = ( "bytes", "packets input", "packets output", "multicast" );
95             our @INTERFACE_INFOS = ( "PAUSE input", "PAUSE output", "pause input" );
96              
97             our $STP_GOOD = qr/forwarding|FWD/;
98             our $STP_WARN = qr/learning|LRN/;
99             our $STP_BAD = qr/discarding|BLK/;
100             our $STP_TYPES = qr/designated|root|alternate|Desg|Root|Altn/;
101              
102             our @bgcolors = (
103             "\e[30m\e[47m", # black on white
104             "\e[30m\e[41m", # black on red
105             "\e[30m\e[42m", # black on green
106             "\e[30m\e[43m", # black on yellow (orange)
107             "\e[37m\e[44m", # white on blue
108             "\e[30m\e[45m", # black on magenta
109             "\e[30m\e[46m", # black on cyan
110             );
111              
112 4994     4994 1 101401 sub format_text ( $self, $text ) {
  4994         7344  
  4994         7837  
  4994         6843  
113             # Remove Arista "more" prompt
114 4994         9800 $text =~ s/ \x1b \[ \Q3m --More-- \E \x1b \[ 23m \x1b \[ K \r \x1b \[ K //gmsxx;
115              
116             # Break into lines
117 4994         7789 my $output = '';
118 4994         19611 while ( $text =~ m/([^\n\r]*[\r]?[\n]?)/g ) {
119 10053         23284 $output .= $self->_parse_line($1);
120             }
121 4994         19091 return $output;
122             }
123              
124 10053     10053   14326 sub _parse_line ( $self, $text ) {
  10053         14811  
  10053         22051  
  10053         14679  
125 10053         35235 my ( $line, $eol ) = $text =~ m/([^\n]*)(\n?)/;
126              
127             # We want to strip out the control characters at the start of the
128             # line. This is kind of black magic...
129 10053         18235 my $preamble = '';
130 10053 100       23305 if ( $line =~ s/^ ( .* (?<! \x1b \[23m) \x1b (?: \[K | M)) //sxx ) {
131 9         28 $preamble = $1;
132             }
133              
134             # Arista "More" prompt (should this be in the Arista parse-line?)
135 10053         14891 $line =~ s/ ^ ( \x1b \[ \Q3m --More-- \E .* \x0d \x1b \[K )//sxx;
136              
137             # A space that is backspaced over
138 10053         15398 $line =~ s/ \Q \E \x08 //gxx;
139              
140 10053         14636 my $trailer = "";
141 10053 100       31449 if ( $line =~ s/(\x0d) $//sxx ) {
142 4929         10792 $trailer = $1;
143             }
144              
145 10053         20529 $line = $self->_parse_line_arista($line);
146 10053         23395 $line = $self->_parse_line_vyos($line);
147 10053         19759 $line = $self->_parse_line_junos($line);
148 10053         22796 $line = $self->_parse_line_ciena($line);
149              
150             # IPv4
151 10053         35279 $line =~ s/($IPV4CIDR)/$self->_ipv4ify($1)/egxx;
  73         236  
152              
153             # IPv6
154 10053         309159 $line =~ s/ ( (?<! [a-fA-F0-9:\-]) $IPV6CIDR (?! [\w:\.\/]) ) /$self->_ipv6ify($1)/egxx;
  17         65  
155              
156             # Numbers
157             # We need to make sure we don't highlight undesirably, such as in an
158             # escape sequence.
159 10053         28759 $line =~ s/ (
160             (?<! [:\.0-9]) (?<! \e \[) (?<! \e \[\?)
161             [0-9]+ (?! [:0-9])
162 8150         18837 ) /$self->_numerify($1)/egxx;
163              
164 10053         66799 return "$preamble$line$trailer$eol";
165             }
166              
167 10053     10053   13499 sub _parse_line_arista ( $self, $line ) {
  10053         14038  
  10053         14854  
  10053         13549  
168             #
169             # Arista & Cisco
170             #
171              
172             # BGP
173 10053         15237 $line =~ s/^ ( \Q BGP state is Established\E \N* ) $/$self->_colorize($1, $GREEN)/exx;
  2         12  
174 10053         15596 $line =~ s/^ ( \Q BGP state is \E \N* ) $/$self->_colorize($1, $RED)/exx;
  2         9  
175              
176 10053         14690 $line =~
177 6         18 s/^ ( \Q Last \E (?: sent || rcvd) \ (?: socket-error || notification) : \N+ ) $/$self->_colorize($1, $INFO)/exx;
178              
179 10053         15480 $line =~
180 2         15 s/^ ( \ \ (?: Inbound || Outbound) \Q route map is \E \N* )$/$self->_colorize($1, $INFO)/exx;
181 10053         14398 $line =~
182 0         0 s/^ ( \Q Inherits configuration from and member of peer-group \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
183              
184 10053         14557 $line =~
185 8         29 s/^ ( \Q \E (?: IPv4 | IPv6) \Q Unicast: \E \N* ) $/$self->_colorize($1, $INFO)/exx;
186 10053         14606 $line =~
187 5         16 s/^ ( \Q Configured maximum total number of routes is \E \d+ (?: \Q, warning limit is \E \d+ )? ) $/$self->_colorize($1, $INFO)/exx;
188              
189             # BGP Errors
190 10053         94084 my $errors = qr/
191             ^
192             \Q \E
193             (?:
194             \QAS path loop detection\E
195             | \QEnforced First AS\E
196             | \QMalformed MPBGP routes\E
197             | \QAS path loop detection\E
198             | \QOriginator ID matches local router ID\E
199             | \QNexthop matches local IP address\E
200             | \QResulting in removal of all paths in update (treat as withdraw)\E
201             | \QResulting in AFI\E \/ \QSAFI disable\E
202             | \QResulting in attribute ignore\E
203             | \QDisabled AFI\E \/ \QSAFIs\E
204             | \QIPv4 labeled-unicast NLRIs dropped due to excessive labels\E
205             | \QIPv6 labeled-unicast NLRIs dropped due to excessive labels\E
206             | \QIPv4 local address not available\E
207             | \QIPv6 local address not available\E
208             | \QUnexpected IPv6 nexthop for IPv4 routes\E
209             )
210             \Q: \E
211             $POSINT
212             $
213             /xx;
214 10053         36042 $line =~ s/($errors)/$self->_colorize($1, $RED)/e;
  0         0  
215              
216 10053         17237 $line =~ s/^ ( \QBGP neighbor is \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  5         30  
217 10053         15011 $line =~ s/^ ( (?: Local | Remote) \Q TCP address is \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  7         35  
218              
219             #
220             # Interfaces
221             #
222              
223             # We look for information lines
224 10053 100       39577 if ( $line =~ m/^ ((?:$INT [^,]+, )*$INT [^,]+)$/ ) {
225             my (%values) =
226 690         2658 map { reverse split / /, $_, 2 } split ', ', $1;
  1617         5628  
227              
228 690 100   2250   4056 if ( any { exists( $values{$_} ) } @INTERFACE_IGNORES ) {
  2250 100       5134  
    100          
229             # Do nothing.
230 1329     1329   3325 } elsif ( any { exists( $values{$_} ) } @INTERFACE_INFOS ) {
231 128         355 $line = $self->_colorize( $line, $INFO );
232 1062     1062   1783 } elsif ( any { $values{$_} } keys %values ) {
233 16         60 $line = $self->_colorize( $line, $RED );
234             } else {
235 363         917 $line = $self->_colorize( $line, $GREEN );
236             }
237             }
238              
239 10053         27931 my $INTERFACE = qr/ [A-Z] \S+ /xx;
240 10053         22246 my $INTSHORT = qr/ [A-Z][a-z][0-9] \S* /xx;
241              
242             # "show int" up/down
243 10053         33336 $line =~
244 43         133 s/^ ( $INTERFACE \Q is up, line protocol is up\E (:? \Q (connected)\E )? \s? ) $/$self->_colorize($1, $GREEN)/exx;
245 10053         27763 $line =~
246 34         126 s/^ ( $INTERFACE \Q is administratively down,\E \N+ ) $/$self->_colorize($1, $ORANGE)/exx;
247 10053         29690 $line =~
248 11         59 s/^ ( $INTERFACE \Q is \E \N+ \Q, line protocol is \E \N+ ) $/$self->_colorize($1, $RED)/exx;
249              
250 10053         16677 $line =~ s/^ ( \Q Up \E \N+ ) $/$self->_colorize($1, $GREEN)/exx;
  36         132  
251 10053         14975 $line =~ s/^ ( \Q Down \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  38         128  
252              
253             # "show int" description lines
254 10053         14999 $line =~ s/^ ( (?: \Q \E|\Q \E)? \Q Description: \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  72         215  
255              
256             # "show int" rates
257 10053         35945 $line =~
258 170         495 s/^ ( \Q \E $NUM \s \w+ \s (?: input | output) \s rate \s $NUM \s \N+ ) $/$self->_colorize($1, $INFO)/exx;
259              
260             # "show int status"
261 10053         27790 $line =~ s/^ ( $INTSHORT \N+ \Q connected \E \N+ ) $/$self->_colorize($1, $GREEN)/exx;
  38         129  
262 10053         26149 $line =~ s/^ ( $INTSHORT \N+ \Q disabled \E \N+ ) $/$self->_colorize($1, $ORANGE)/exx;
  34         107  
263 10053         25781 $line =~ s/^ ( $INTSHORT \N+ \Q errdisabled \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  0         0  
264 10053         25275 $line =~ s/^ ( $INTSHORT \N+ \Q notconnect \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  10         35  
265              
266             # "show int description"
267 10053         26129 $line =~
268 36         103 s/^ ( $INTSHORT \s+ up \s+ up (?: \s+ \N+)? ) $/$self->_colorize($1, $GREEN)/exx;
269 10053         27063 $line =~
270 33         109 s/^ ( $INTSHORT \s+ \Qadmin down\E \s+ \S+ (?: \s+ \N+)? ) $/$self->_colorize($1, $ORANGE)/exx;
271 10053         24129 $line =~
272 5         21 s/^ ( $INTSHORT \s+ down \s+ \S+ (?: \s+ \N+)? ) $/$self->_colorize($1, $RED)/exx;
273              
274             # "show int transceiver" (Arista)
275 10053         33981 $line =~
276 16         51 s/^ ( $INTSHORT (?: \s+ $LIGHT){4} \s+ $LOWLIGHT \s+ \S+ \s ago ) $/$self->_colorize($1, $RED)/exx;
277 10053         28310 $line =~
278 8         26 s/^ ( $INTSHORT (?: \s+ $LIGHT){5} \s+ \S+ \s ago ) $/$self->_colorize($1, $INFO)/exx;
279 10053         24153 $line =~
280 0         0 s/^ ( $INTSHORT (?: \s+ N\/A ){6} \s* ) $/$self->_colorize($1, $ORANGE)/exx;
281              
282             # "show int transceiver" (Cisco)
283 10053         34146 $line =~
284 1         5 s/^ ( $INTSHORT (?: \s+ $LIGHT){3} \s+ $LOWLIGHT ) \s+ $/$self->_colorize($1, $RED)/exx;
285 10053         31281 $line =~
286 3         35 s/^ ( $INTSHORT (?: \s+ $LIGHT){4} ) \s+ $/$self->_colorize($1, $INFO)/exx;
287              
288             #
289             # LLDP Neighbors Detail
290             #
291 10053         29563 $line =~
292 0         0 s/^ ( \QInterface\E \s \S+ \s detected \s $POSINT \Q LLDP neighbors:\E ) $/$self->_colorize($1, $INFO)/exx;
293 10053         27076 $line =~
294 0         0 s/^ ( \Q Neighbor \E \S+ \s age \s $POSINT \s seconds ) $/$self->_colorize($1, $INFO)/exx;
295 10053         15384 $line =~
296 0         0 s/^ ( \Q Discovered \E \N+ \Q; Last changed \E \N+ \s ago ) $/$self->_colorize($1, $INFO)/exx;
297 10053         14159 $line =~ s/^ ( \Q - System Name: \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  0         0  
298 10053         14549 $line =~ s/^ ( \Q Port ID :\E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  0         0  
299 10053         14016 $line =~ s/^ ( \Q Management Address : \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  0         0  
300              
301             #
302             # Show Spanning-Tree
303             #
304 10053         30304 $line =~
305 13         45 s/^ ( $INTSHORT \s+ $STP_TYPES\s+ $STP_GOOD \s+ [0-9]+ \s+ [0-9]+\.[0-9]+ \s+ P2p .* ) $/$self->_colorize($1, $GREEN)/exx;
306 10053         29695 $line =~
307 1         14 s/^ ( $INTSHORT \s+ $STP_TYPES\s+ $STP_WARN \s+ [0-9]+ \s+ [0-9]+\.[0-9]+ \s+ P2p .* ) $/$self->_colorize($1, $ORANGE)/exx;
308 10053         28636 $line =~
309 2         23 s/^ ( $INTSHORT \s+ $STP_TYPES\s+ $STP_BAD \s+ [0-9]+ \s+ [0-9]+\.[0-9]+ \s+ P2p .* ) $/$self->_colorize($1, $RED)/exx;
310              
311              
312             # show bgp rpki cache (Arista)
313 10053         15234 $line =~ s/^ (State: \s synced) $/$self->_colorize($1, $GREEN)/exx;
  1         13  
314 10053         13900 $line =~ s/^ (State: \s .* ) $/$self->_colorize($1, $RED)/exx;
  1         14  
315              
316 10053         13956 $line =~ s/^ (Connection: \s Active \s .*) $/$self->_colorize($1, $GREEN)/exx;
  1         9  
317 10053         14643 $line =~ s/^ (Connection: \s .* ) $/$self->_colorize($1, $RED)/exx;
  0         0  
318              
319 10053         49737 return $line;
320             }
321              
322 10053     10053   13454 sub _parse_line_junos ( $self, $line ) {
  10053         13655  
  10053         15229  
  10053         12956  
323              
324             #
325             # JunOS
326             #
327              
328             # Show Interfaces
329 10053         15187 $line =~
330 53         218 s/^ ( \QPhysical interface: \E \S+ \Q Enabled, Physical link is Up\E ) $/$self->_colorize($1, $GREEN)/exx;
331 10053         14391 $line =~
332 36         113 s/^ ( \QPhysical interface: \E \S+ \Q Enabled, Physical link is Down\E ) $/$self->_colorize($1, $RED)/exx;
333 10053         14524 $line =~
334 0         0 s/^ ( \QPhysical interface: \E \S+ \s \S+ \Q Physical link is Down\E ) $/$self->_colorize($1, $ORANGE)/exx;
335 10053         13944 $line =~
336 2         20 s/^ ( \QPhysical interface: \E \S+ ) $/$self->_colorize($1, $INFO)/exx;
337              
338 10053         13760 $line =~ s/^ ( \Q Logical interface \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  72         201  
339              
340 10053         14543 $line =~ s/^ ( \Q Last flapped : \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  40         115  
341              
342 10053         31692 $line =~ s/^ ( \Q Input rate : \E $NUM \N+ ) $/$self->_colorize($1, $INFO)/exx;
  29         87  
343 10053         28756 $line =~ s/^ ( \Q Output rate : \E $NUM \N+ ) $/$self->_colorize($1, $INFO)/exx;
  29         102  
344              
345 10053         30139 $line =~ s/^ ( \Q Input packets : \E $NUM \N+ ) $/$self->_colorize($1, $INFO)/exx;
  44         159  
346 10053         29062 $line =~ s/^ ( \Q Output packets: \E $NUM \N+ ) $/$self->_colorize($1, $INFO)/exx;
  44         136  
347              
348 10053         15609 $line =~ s/^ ( \Q Active alarms : None\E ) $/$self->_colorize($1, $GREEN)/exx;
  12         33  
349 10053         14411 $line =~ s/^ ( \Q Active alarms : \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  13         38  
350 10053         13881 $line =~ s/^ ( \Q Active defects : None\E ) $/$self->_colorize($1, $GREEN)/exx;
  12         37  
351 10053         13868 $line =~ s/^ ( \Q Active defects : \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  13         34  
352              
353 10053         23960 my $AE = qr/ (?: ae [0-9\.]+ ) /xx;
354 10053         21924 my $BME = qr/ (?: bme [0-9\.]+ ) /xx;
355 10053         21301 my $CBP = qr/ (?: cbp [0-9\.]+ ) /xx;
356 10053         21087 my $ETH = qr/ (?: [gx] e- [0-9\/\.]+ ) /xx;
357 10053         20044 my $IRB = qr/ (?: irb [0-9\/\.]* ) /xx;
358 10053         20551 my $JSRV = qr/ (?: jsrv [0-9\.]* ) /xx;
359 10053         20305 my $LO = qr/ (?: lo [0-9\.]+ ) /xx;
360 10053         20050 my $ME = qr/ (?: me [0-9\.]+ ) /xx;
361 10053         20467 my $PFE = qr/ (?: pf [eh] - [0-9\/\.]+ ) /xx;
362 10053         20210 my $PIP = qr/ (?: pip [0-9\/\.]+ ) /xx;
363 10053         19865 my $VCP = qr/ (?: vcp- [0-9\/\.]+ ) /xx;
364 10053         20438 my $VLAN = qr/ (?: vlan\. [0-9]+ ) /xx;
365 10053         21339 my $OTHER = qr/
366             (:? fti|fxp|gr|ip|lsq|lt|mt|sp|pp|ppd|ppe|st ) (:?-)? [0-9\/\.]+
367             | gr-\S+
368             | dsc | esi | gre | ipip | jsrv | lsi
369             | mtun | pimd | pime | rbeb | tap | vlan | vme | vtep
370             /xx;
371              
372 10053         61181 my $IFACES = qr/$AE|$BME|$CBP|$ETH|$IRB|$LO|$ME|$JSRV|$PFE|$PIP|$VCP|$VLAN|$OTHER/xx;
373              
374 10053         32948 $line =~ s/^ ( (?: $IFACES) \s+ up \s+ up \N* ) $/$self->_colorize($1, $GREEN)/exx;
  116         363  
375 10053         26700 $line =~ s/^ ( $ETH \s+ VCP ) $/$self->_colorize($1, $GREEN)/exx;
  0         0  
376 10053         27395 $line =~ s/^ ( (?: $IFACES) \s+ up \s+ down \N* ) $/$self->_colorize($1, $RED)/exx;
  64         192  
377 10053         26252 $line =~ s/^ ( (?: $IFACES) \s+ down \s+ \N* ) $/$self->_colorize($1, $ORANGE)/exx;
  1         13  
378              
379             # Errors
380 10053         15276 $line =~ s/^ ( \Q Bit errors \E \s+ 0 ) $/$self->_colorize($1, $GREEN)/exx;
  1         9  
381 10053         14285 $line =~ s/^ ( \Q Bit errors \E \s+ 0 ) $/$self->_colorize($1, $GREEN)/exx;
  0         0  
382 10053         13764 $line =~ s/^ ( \Q Errored blocks \E \s+ [1-9][0-9]+ ) $/$self->_colorize($1, $RED)/exx;
  0         0  
383 10053         13888 $line =~ s/^ ( \Q Errored blocks \E \s+ 0 ) $/$self->_colorize($1, $GREEN)/exx;
  1         6  
384 10053         14148 $line =~
385 4         15 s/^ ( \Q FEC \E \S+ \s Errors (?: \s Rate)? \s+ 0 ) $/$self->_colorize($1, $GREEN)/exx;
386 10053         14139 $line =~
387 0         0 s/^ ( \Q FEC \E \S+ \s Errors (?: \s Rate)? \s+ \N+ ) $/$self->_colorize($1, $RED)/exx;
388              
389             # show interfaces diagnostics optics
390 10053         28987 $line =~ s/^ ( \Q Laser output power \E \s+ : \s $NUM \N+ ) $/$self->_colorize($1, $RED)/exx;
  0         0  
391 10053         33662 $line =~
392 0         0 s/^ ( \Q Laser output power \E \s+ : \s+ $NUM \Q mW \/ \E $LOWLIGHT \s dBm ) $/$self->_colorize($1, $RED)/exx;
393 10053         32655 $line =~
394 2         11 s/^ ( \Q Laser output power \E \s+ : \s+ $NUM \Q mW \/ \E $LIGHT \s dBm ) $/$self->_colorize($1, $INFO)/exx;
395 10053         31768 $line =~
396 1         6 s/^ ( \Q Laser receiver power \E \s+ : \s+ $NUM \Q mW \/ \E $LOWLIGHT \s dBm ) $/$self->_colorize($1, $RED)/exx;
397 10053         31535 $line =~
398 1         5 s/^ ( \Q Laser receiver power \E \s+ : \s+ $NUM \Q mW \/ \E $LIGHT \s dBm ) $/$self->_colorize($1, $INFO)/exx;
399 10053         33845 $line =~
400 1         7 s/^ ( \Q Receiver signal average optical power \E \s+ : \s+ $NUM \Q mW \/ \E $LOWLIGHT \s dBm ) $/$self->_colorize($1, $RED)/exx;
401 10053         33598 $line =~
402 1         10 s/^ ( \Q Receiver signal average optical power \E \s+ : \s+ $NUM \Q mW \/ \E $LIGHT \s dBm ) $/$self->_colorize($1, $INFO)/exx;
403              
404 10053         62732 return $line;
405             }
406              
407 10053     10053   13640 sub _parse_line_vyos ( $self, $line ) {
  10053         14219  
  10053         15374  
  10053         13940  
408              
409             #
410             # VyOS (Stuff the Arista/Cisco commands did not do)
411             #
412              
413             # BGP
414 10053         15440 $line =~ s/^ ( \Q BGP state = \E (?!Established) \N* ) $/$self->_colorize($1, $RED)/exx;
  0         0  
415 10053         14668 $line =~
416 1         4 s/^ ( \Q BGP state = Established\E \N* ) $/$self->_colorize($1, $GREEN)/exx;
417              
418 10053         14200 $line =~
419 2         8 s/^ ( \Q Route map for \E (?: incoming|outgoing ) \Q advertisements is \E \N* ) $/$self->_colorize($1, $INFO)/exx;
420 10053         15297 $line =~ s/^ ( \Q \E \S+ \Q peer-group member\E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  1         5  
421              
422 10053         32273 $line =~ s/^ ( \Q \E $INT \Q accepted prefixes\E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  1         6  
423              
424 10053         16306 $line =~ s/^ ( \QLocal host: \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  1         8  
425 10053         14739 $line =~ s/^ ( \QForeign host: \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  1         6  
426              
427 10053         19896 return $line;
428             }
429              
430 10053     10053   14724 sub _parse_line_ciena ( $self, $line ) {
  10053         14482  
  10053         15087  
  10053         13699  
431              
432             #
433             # Ciena
434             #
435              
436             # Admin/Operational state for multiple commands
437 10053         15320 $line =~
438 8         46 s/^ ( \| \s? \QAdmin State \E \s+ \| ) ( \s? \QEnabled \E ) ( \N+ ) $/$1.$self->_colorize($2, $GREEN).$3/exx;
439 10053         14439 $line =~
440 1         15 s/^ ( \| \s? \QAdmin State \E \s+ \| ) ( \s? \QDisabled \E ) ( \N+ ) $/$1.$self->_colorize($2, $ORANGE).$3/exx;
441 10053         14414 $line =~
442 10         38 s/^ ( \| \s? \QAdmin State \E \s+ \| ) ( [^|]+ ) ( \N+ ) $/$1.$self->_colorize($2, $RED).$3/exx;
443              
444 10053         13838 $line =~
445 7         23 s/^ ( \| \s? \QOperational State \E \s+ \| ) ( \s? \QUp \E ) ( \N+ ) $/$1.$self->_colorize($2, $GREEN).$3/exx;
446 10053         14423 $line =~
447 1         6 s/^ ( \| \s? \QOperational State \E \s+ \| ) ( \s? \QInitializing \E ) ( \N+ ) $/$1.$self->_colorize($2, $ORANGE).$3/exx;
448 10053         14795 $line =~
449 10         29 s/^ ( \| \s? \QOperational State \E \s+ \| ) ( [^|]+ ) ( \N+ ) $/$1.$self->_colorize($2, $RED).$3/exx;
450              
451             # xcvr show xcvr N/N
452 10053         34400 $line =~ s/ ^ ( \| ) ( \Q Tx Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ ) $/
453 1         6 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5/exx;
454 10053         31271 $line =~ s/ ^ ( \| ) ( \Q Tx Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ ) $/
455 1         7 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5/exx;
456              
457 10053         29119 $line =~ s/ ^ ( \| ) ( \Q Rx Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ ) $/
458 1         5 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5/exx;
459 10053         28574 $line =~ s/ ^ ( \| ) ( \Q Rx Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ ) $/
460 1         5 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5/exx;
461              
462             # xcvr show xcvr status
463 10053         30398 $line =~
464             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Tx Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ ) $/
465 1         5 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5/exx;
466 10053         30084 $line =~
467             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Tx Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ ) $/
468 1         4 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5/exx;
469 10053         30868 $line =~
470             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Rx Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ ) $/
471 1         7 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5/exx;
472 10053         29692 $line =~
473             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Rx Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ ) $/
474 1         4 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5/exx;
475              
476             # ptp show ptp x/x [status]
477 10053         16221 $line =~
478             s/^ ( \| (?: \s Transmitter)? \Q State \E \s+ \| ) ( \Q Enabled \E \s+ ) ( \| ) ( \Q Up \E|\Q Enabled \E )( \N+ ) $/
479 6         21 $1.$self->_colorize($2, $GREEN).$3.$self->_colorize($4, $GREEN).$5/exx;
480 10053         14949 $line =~
481             s/^ ( \| (?: \s Transmitter)? \Q State \E \s+ \| ) ( \Q Enabled \E \s+ ) ( \| ) ( [^|]+ )( \N+ ) $/
482 3         12 $1.$self->_colorize($2, $GREEN).$3.$self->_colorize($4, $RED).$5/exx;
483 10053         14948 $line =~
484             s/^ ( \| (?: \s Transmitter)? \Q State \E \s+ \| ) ( \Q Disabled \E \s+ ) ( \| ) ( [^|]+ )( \N+ ) $/
485 2         15 $1.$self->_colorize($2, $ORANGE).$3.$self->_colorize($4, $ORANGE).$5/exx;
486              
487 10053         33570 $line =~
488             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ )$/
489 1         6 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5.$self->_colorize($6, $RED).$7/exx;
490 10053         33812 $line =~
491             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ )$/
492 1         5 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5.$self->_colorize($6, $INFO).$7/exx;
493 10053         33106 $line =~
494             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ )$/
495 1         5 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $RED).$7/exx;
496 10053         33533 $line =~
497             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ )$/
498 4         20 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $INFO).$7/exx;
499              
500 10053         33318 $line =~
501             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Aggregate Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ )$/
502 0         0 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5.$self->_colorize($6, $RED).$7/exx;
503 10053         32674 $line =~
504             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Aggregate Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ )$/
505 0         0 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5.$self->_colorize($6, $INFO).$7/exx;
506 10053         32064 $line =~
507             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Aggregate Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ )$/
508 0         0 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $RED).$7/exx;
509 10053         32634 $line =~
510             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Aggregate Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ )$/
511 1         5 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $INFO).$7/exx;
512              
513 10053         34027 $line =~
514             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Span Loss (dB)\E \s* ) ( \| ) ( \s+ $NUM \s+ ) ( \| ) ( \s+ $NUM \s+ ) ( \| )$/
515 1         8 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $INFO).$7/exx;
516              
517 10053         31417 $line =~
518             s/^ ( \| \s+ \| ) ( \Q Optical Return Loss \E \( \QdB\E \) ) ( \s+ \| \s+ ) ( $GOODRETURNLOSS ) (\s+ \| ) $/
519 1         5 $1.$self->_colorize($2, $GREEN).$3.$self->_colorize($4, $GREEN).$5/exx;
520 10053         30377 $line =~
521             s/^ ( \| \s+ \| ) ( \Q Optical Return Loss \E \( \QdB\E \) ) ( \s+ \| \s+ ) ( $WARNRETURNLOSS ) (\s+ \| ) $/
522 1         6 $1.$self->_colorize($2, $ORANGE).$3.$self->_colorize($4, $ORANGE).$5/exx;
523 10053         29779 $line =~
524             s/^ ( \| \s+ \| ) ( \Q Optical Return Loss \E \( \QdB\E \) ) ( \s+ \| \s+ ) ( $BADRETURNLOSS ) (\s+ \| ) $/
525 1         7 $1.$self->_colorize($2, $RED).$3.$self->_colorize($4, $RED).$5/exx;
526              
527             # alarm show
528 10053         34365 $line =~ s/ ^ ( \| ) ( \s* [0-9]* ) ( \| ) ( \s+ ) ( \| ) ( [^|]+ ) ( \| ) ( \s+ [0-9]+ )
529             ( \| ) ( \s* $BIGALARMS|$LITTLEALARMS \s* ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
530             ( \| ) ( \s+ $BIGOVERRIDES \s+ ) ( \| ) $/
531 3         15 $1.$self->_colorize($2, $ORANGE).
532             $3.$self->_colorize($4, $ORANGE).
533             $5.$self->_colorize($6, $ORANGE).
534             $7.$self->_colorize($8, $ORANGE).
535             $9.$self->_colorize($10, $ORANGE).
536             $11.$self->_colorize($12, $ORANGE).
537             $13.$self->_colorize($14, $ORANGE).
538             $15.$self->_colorize($16, $ORANGE).
539             $17/exx;
540 10053         28362 $line =~ s/ ^ ( \| ) ( \s* [0-9]* ) ( \| ) ( \s+ ) ( \| ) ( [^|]+ ) ( \| ) ( \s+ [0-9]+ )
541             ( \| ) ( \s* $BIGALARMS|$LITTLEALARMS \s* ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $/
542 1         7 $1.$self->_colorize($2, $RED).
543             $3.$self->_colorize($4, $RED).
544             $5.$self->_colorize($6, $RED).
545             $7.$self->_colorize($8, $RED).
546             $9.$self->_colorize($10, $RED).
547             $11.$self->_colorize($12, $RED).
548             $13.$self->_colorize($14, $RED).
549             $15.$self->_colorize($16, $RED).
550             $17/exx;
551 10053         29795 $line =~
552             s/ ^ ( \| ) ( \s+ [0-9]* ) ( \| ) ( \s+ \QY\E \s* ) ( \| ) ( [^|]+ ) ( \| ) ( \s+ [0-9]+ )
553             ( \| ) ( \s* $BIGALARMS \s* ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $/
554 8         26 $1.$self->_colorize($2, $ORANGE).
555             $3.$self->_colorize($4, $ORANGE).
556             $5.$self->_colorize($6, $ORANGE).
557             $7.$self->_colorize($8, $ORANGE).
558             $9.$self->_colorize($10, $ORANGE).
559             $11.$self->_colorize($12, $ORANGE).
560             $13.$self->_colorize($14, $ORANGE).
561             $15.$self->_colorize($16, $ORANGE).
562             $17/exx;
563 10053         27801 $line =~
564             s/ ^ ( \| ) ( \s+ [0-9]* ) ( \| ) ( \s+ \QY\E \s* ) ( \| ) ( [^|]+ ) ( \| ) ( \s+ [0-9]+ )
565             ( \| ) ( \s* $LITTLEALARMS \s* ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $/
566 1         8 $1.$self->_colorize($2, $INFO).
567             $3.$self->_colorize($4, $INFO).
568             $5.$self->_colorize($6, $INFO).
569             $7.$self->_colorize($8, $INFO).
570             $9.$self->_colorize($10, $INFO).
571             $11.$self->_colorize($12, $INFO).
572             $13.$self->_colorize($14, $INFO).
573             $15.$self->_colorize($16, $INFO).
574             $17/exx;
575              
576             # Errors
577 10053         16139 $line =~ s/ ^ ( \QSHELL \E \S+ \Q FAILURE\E \N+ ) $/$self->_colorize($1, $RED)/exx;
  2         9  
578              
579             # ptp show
580 10053         14977 $line =~
581             s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( \Q Ena \E ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
582             ( \| ) ( [^|]+ ) ( \| ) ( \QUp\E \s+ ) ( \| ) ( [^|]+ )
583             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) /
584 15         44 $1.$self->_colorize($2, $GREEN).
585             $3.$self->_colorize($4, $GREEN).
586             $5.$self->_colorize($6, $GREEN).
587             $7.$self->_colorize($8, $GREEN).
588             $9.$self->_colorize($10, $GREEN).
589             $11.$self->_colorize($12, $GREEN).
590             $13.$self->_colorize($14, $GREEN).
591             $15.$self->_colorize($16, $GREEN).
592             $17.$self->_colorize($18, $GREEN).
593             $19.$self->_colorize($20, $GREEN).
594             $21.$self->_colorize($22, $GREEN).
595             $23/exx;
596 10053         16446 $line =~
597             s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( \Q Ena \E ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
598             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
599             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) /
600 8         28 $1.$self->_colorize($2, $RED).
601             $3.$self->_colorize($4, $RED).
602             $5.$self->_colorize($6, $RED).
603             $7.$self->_colorize($8, $RED).
604             $9.$self->_colorize($10, $RED).
605             $11.$self->_colorize($12, $RED).
606             $13.$self->_colorize($14, $RED).
607             $15.$self->_colorize($16, $RED).
608             $17.$self->_colorize($18, $RED).
609             $19.$self->_colorize($20, $RED).
610             $21.$self->_colorize($22, $RED).
611             $23/exx;
612 10053         14796 $line =~
613             s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( \Q Dis \E ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
614             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
615             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) /
616 0         0 $1.$self->_colorize($2, $ORANGE).
617             $3.$self->_colorize($4, $ORANGE).
618             $5.$self->_colorize($6, $ORANGE).
619             $7.$self->_colorize($8, $ORANGE).
620             $9.$self->_colorize($10, $ORANGE).
621             $11.$self->_colorize($12, $ORANGE).
622             $13.$self->_colorize($14, $ORANGE).
623             $15.$self->_colorize($16, $ORANGE).
624             $17.$self->_colorize($18, $ORANGE).
625             $19.$self->_colorize($20, $ORANGE).
626             $21.$self->_colorize($22, $ORANGE).
627             $23/exx;
628              
629             # module show
630 10053         14692 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( \Q Enabled \E \s+ )
631             ( \| ) ( [^|]+ ) ( \| ) ( \Q Up \E \s+ ) ( \| ) ( [^|]+ ) ( \| ) $ /
632 1         7 $1.$self->_colorize($2, $GREEN).
633             $3.$self->_colorize($4, $GREEN).
634             $5.$self->_colorize($6, $GREEN).
635             $7.$self->_colorize($8, $GREEN).
636             $9.$self->_colorize($10, $GREEN).
637             $11.$self->_colorize($12, $GREEN).
638             $13/exx;
639 10053         14640 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( \Q Enabled \E \s+ )
640             ( \| ) ( [^|]+ ) ( \| ) ( \Q Initializing \E \s+ ) ( \| ) ( [^|]+ ) ( \| ) $ /
641 1         5 $1.$self->_colorize($2, $ORANGE).
642             $3.$self->_colorize($4, $ORANGE).
643             $5.$self->_colorize($6, $ORANGE).
644             $7.$self->_colorize($8, $ORANGE).
645             $9.$self->_colorize($10, $ORANGE).
646             $11.$self->_colorize($12, $ORANGE).
647             $13/exx;
648 10053         15310 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( \Q Enabled \E \s+ )
649             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $ /
650 1         5 $1.$self->_colorize($2, $RED).
651             $3.$self->_colorize($4, $RED).
652             $5.$self->_colorize($6, $RED).
653             $7.$self->_colorize($8, $RED).
654             $9.$self->_colorize($10, $RED).
655             $11.$self->_colorize($12, $RED).
656             $13/exx;
657 10053         14837 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( \Q Disabled \E \s+ )
658             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $ /
659 0         0 $1.$self->_colorize($2, $ORANGE).
660             $3.$self->_colorize($4, $ORANGE).
661             $5.$self->_colorize($6, $ORANGE).
662             $7.$self->_colorize($8, $ORANGE).
663             $9.$self->_colorize($10, $ORANGE).
664             $11.$self->_colorize($12, $ORANGE).
665             $13/exx;
666              
667             # Success for dumps
668 10053         14055 $line =~ s/^ ( \QSuccess! Preparing state dump...\E \s* ) $/ $self->_colorize($1, $GREEN)/exx;
  1         6  
669              
670             # Logout message
671 10053         14005 $line =~ s/^ ( \QTerminal will be disconnected in 1 minute if it remains inactive.\E )/
672 1         5 $self->_colorize($1, $RED)/exx;
673              
674             # Warning message
675 10053         14549 $line =~ s/^ (\QWARNING:\E) / $self->_colorize($1, $ORANGE)/exx;
  1         4  
676 10053         13954 $line =~ s/^ (\Q You cannot abort the restart operation once it has started.\E ) $/
677 1         5 $self->_colorize($1, $ORANGE)/exx;
678              
679             # Error messages when something isn't disabled first
680 10053         14321 $line =~ s/^ (\QERROR: \E .* disabled ) $/ $self->_colorize($1, $RED)/exx;
  1         4  
681              
682              
683 10053         40809 return $line;
684             }
685              
686 73     73   112 sub _ipv4ify ( $self, $ip ) {
  73         113  
  73         165  
  73         114  
687 73         293 my ( $subnet, $len ) = split "/", $ip;
688 73   100     384 $len //= 32;
689              
690 73         224 my (@oct) = split /\./, $subnet;
691 73         125 my $val = 0;
692 73         188 foreach my $i (@oct) {
693 292         406 $val *= 256;
694 292         556 $val += $i;
695             }
696 73         145 $val = $val * 256 + $len;
697 73         261 my $color = $BGCOLORS[ $val % scalar(@BGCOLORS) ];
698 73         223 return $self->_colorize( $ip, $color );
699             }
700              
701 17     17   31 sub _ipv6ify ( $self, $ip ) {
  17         33  
  17         43  
  17         27  
702 17         65 my ( $subnet, $len ) = split "/", $ip;
703 17   100     77 $len //= 128;
704              
705 17         51 my ( $first, $second ) = split "::", $subnet;
706              
707 17         50 my (@fparts) = split /:/, $first;
708 17         62 push( @fparts, ( '', '', '', '', '', '', '', '' ) );
709 17         70 @fparts = @fparts[ 0 .. 7 ];
710              
711 17         28 my (@sparts);
712 17 100       42 if ( defined($second) ) {
713 15         44 (@sparts) = reverse split /:/, $second;
714             }
715 17         59 push( @sparts, ( '', '', '', '', '', '', '', '' ) );
716 17         68 @sparts = reverse @sparts[ 0 .. 7 ];
717              
718 17         30 my $val = 0;
719 17         55 for my $i ( 0 .. 7 ) {
720 136         177 $val *= 16;
721 136         273 $val += hex( $fparts[$i] . $sparts[$i] );
722             }
723 17         34 $val *= 32;
724 17         35 $val += $len;
725              
726 17         51 my $color = $BGCOLORS[ $val % scalar(@BGCOLORS) ];
727 17         46 return $self->_colorize( $ip, $color );
728             }
729              
730 8150     8150   11277 sub _numerify ( $self, $num ) {
  8150         11053  
  8150         14574  
  8150         10851  
731 8150         11594 my $output = "";
732 8150         17622 while ( length($num) > 3 ) {
733 1096         2443 $output = substr( $num, -3 ) . $output;
734 1096         2243 $num = substr( $num, 0, length($num) - 3 );
735              
736 1096         1548 my $next3;
737 1096 100       2208 if ( length($num) > 3 ) {
738 418         728 $next3 = substr( $num, -3 );
739 418         707 $num = substr( $num, 0, length($num) - 3 );
740             } else {
741 678         1038 $next3 = $num;
742 678         1071 $num = '';
743             }
744 1096         2339 $output = $self->_highlight($next3) . $output;
745             }
746 8150         14951 $output = $num . $output;
747              
748 8150         31477 return $output;
749             }
750              
751 1096     1096   1508 sub _highlight ( $self, $str ) {
  1096         1459  
  1096         1542  
  1096         1426  
752 1096         3942 return "\e[4m$str\e[24m";
753             }
754              
755 2330     2330   3222 sub _colorize ( $self, $text, $color ) {
  2330         3344  
  2330         4740  
  2330         3585  
  2330         2986  
756 2330         5303 $text = $color . $text;
757 2330         7679 $text =~ s/ \Q$RESET\E / $color /;
758 2330         4074 $text = $text . $RESET;
759 2330         7537 return $text;
760             }
761              
762             __END__
763              
764             =pod
765              
766             =encoding UTF-8
767              
768             =head1 NAME
769              
770             App::RouterColorizer - Colorize router CLI output
771              
772             =head1 VERSION
773              
774             version 1.231100
775              
776             =head1 DESCRIPTION
777              
778             This module colorizes the output of router output, using.
779              
780             The output will be colorized based on detection of key strings as they
781             might be sent from Arista, Cisco, Juniper, and VyOS routers/switches and
782             Ciena WDM devices. It may also work on other router outputs, but these
783             have not been used for development.
784              
785             =head1 METHODS
786              
787             =head2 new
788              
789             my $colorizer = App::RouterColorizer->new()
790              
791             Instnatiates a new instance of C<App::RouterColorizer>.
792              
793             =head2 format_text
794              
795             $colorized_text = $colorizer->format_text($text)
796              
797             This method colorizes/formats the text, as provided in C<$text>.
798              
799             /usr/bin/ssh router.example.com | router-colorizer.pl
800              
801             =head1 COLOR CODING
802              
803             Color coding is used, which assumes a black background on your terminal.
804             The colors used indicate different kinds of output. Note that most lines
805             of output are not colorized, only "important" (as defined by me!) lines
806             are colorized.
807              
808             =over 4
809              
810             =item C<green>
811              
812             Green text is used to signify "good" values. For instance, in the output
813             from C<show interfaces> on an Arista router, seeing lines indicating the
814             circuit is "up" and not showing errors will show in green.
815              
816             =item C<orange>
817              
818             Orange text is used to show things that aren't "good" but also aren't "bad."
819             For instance, administratively down interfaces in C<show interfaces status>
820             will typically show as orange.
821              
822             =item C<red>
823              
824             Red text indicates error conditions, such as errors being seen on the
825             output of C<show interfaces>.
826              
827             =item C<cyan>
828              
829             Cyan text indicates potentially important text, seperated out from text
830             that is not-so-important. For instance, in C<show bgp neighbors>, cyan
831             is used to point out lines indicating which route map is being used.
832              
833             =back
834              
835             =head1 IP Address Colorization
836              
837             IP addresses are also colorized. These are colorized one of several colors,
838             all with a colorized background, based on the IP/CIDR address. Thus, an
839             IP address like C<1.2.3.4> will always be the same color, which should make
840             it easier to spot potential transposition or copy mistakes (if it shows up
841             sometimes as white-on-blue, but other times as black-on-red, it's not the
842             same address!).
843              
844             =head1 Number Grouping/Underlines
845              
846             The progarm also underlines alternating groups of 3 digits as they appear
847             in digit strings. This is used to assist people who might have dyslexia
848             or other visual processing differences, to allow them to quickly determine
849             if 1000000 is 1,000,000 or 10,000,000.
850              
851             =head1 Methods
852              
853             =head1 BUGS
854              
855             None known, however it is certainly possible that I am less than perfect. If
856             you find any bug you believe has security implications, I would greatly
857             appreciate being notified via email sent to C<jmaslak@antelope.net> prior
858             to public disclosure. In the event of such notification, I will attempt to
859             work with you to develop a plan for fixing the bug.
860              
861             All other bugs can be reported via email to C<jmaslak@antelope.net> or by
862             using the GitHub issue tracker
863             at L<https://github.com/jmaslak/App-RouterColorizer/issues>
864              
865             =head1 AUTHOR
866              
867             Joelle Maslak <jmaslak@antelope.net>
868              
869             =head1 COPYRIGHT AND LICENSE
870              
871             This software is copyright (c) 2021-2023 by Joelle Maslak.
872              
873             This is free software; you can redistribute it and/or modify it under
874             the same terms as the Perl 5 programming language system itself.
875              
876             =cut