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   2000200 use v5.22;
  10         96  
11 10     10   58 use strict;
  10         33  
  10         223  
12 10     10   49 use warnings;
  10         21  
  10         416  
13              
14             package App::RouterColorizer;
15             $App::RouterColorizer::VERSION = '1.231460';
16 10     10   5849 use Moose;
  10         4710856  
  10         67  
17              
18 10     10   80196 use feature 'signatures';
  10         26  
  10         1778  
19 10     10   86 no warnings 'experimental::signatures';
  10         49  
  10         532  
20              
21 10     10   7108 use English;
  10         37987  
  10         68  
22 10     10   10093 use Import::Into;
  10         5539  
  10         352  
23 10     10   78 use List::Util qw(any);
  10         28  
  10         773  
24 10     10   5604 use Regexp::Common qw/net number/;
  10         28555  
  10         82  
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   176043 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 5003     5003 1 103878 sub format_text ( $self, $text ) {
  5003         7451  
  5003         8034  
  5003         6633  
113             # Remove Arista "more" prompt
114 5003         9769 $text =~ s/ \x1b \[ \Q3m --More-- \E \x1b \[ 23m \x1b \[ K \r \x1b \[ K //gmsxx;
115              
116             # Break into lines
117 5003         8063 my $output = '';
118 5003         19756 while ( $text =~ m/([^\n\r]*[\r]?[\n]?)/g ) {
119 10070         23163 $output .= $self->_parse_line($1);
120             }
121 5003         19617 return $output;
122             }
123              
124 10070     10070   14497 sub _parse_line ( $self, $text ) {
  10070         15165  
  10070         22762  
  10070         15163  
125 10070         35340 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 10070         18477 my $preamble = '';
130 10070 100       23823 if ( $line =~ s/^ ( .* (?<! \x1b \[23m) \x1b (?: \[K | M)) //sxx ) {
131 9         26 $preamble = $1;
132             }
133              
134             # Arista "More" prompt (should this be in the Arista parse-line?)
135 10070         14890 $line =~ s/ ^ ( \x1b \[ \Q3m --More-- \E .* \x0d \x1b \[K )//sxx;
136              
137             # A space that is backspaced over
138 10070         14685 $line =~ s/ \Q \E \x08 //gxx;
139              
140 10070         15080 my $trailer = "";
141 10070 100       30864 if ( $line =~ s/(\x0d) $//sxx ) {
142 4929         10785 $trailer = $1;
143             }
144              
145 10070         20271 $line = $self->_parse_line_arista($line);
146 10070         24021 $line = $self->_parse_line_vyos($line);
147 10070         19988 $line = $self->_parse_line_junos($line);
148 10070         24024 $line = $self->_parse_line_ciena($line);
149              
150             # IPv4
151 10070         35113 $line =~ s/($IPV4CIDR)/$self->_ipv4ify($1)/egxx;
  73         250  
152              
153             # IPv6
154 10070         309726 $line =~ s/ ( (?<! [a-fA-F0-9:\-]) $IPV6CIDR (?! [\w:\.\/]) ) /$self->_ipv6ify($1)/egxx;
  17         78  
155              
156             # Numbers
157             # We need to make sure we don't highlight undesirably, such as in an
158             # escape sequence.
159 10070         29070 $line =~ s/ (
160             (?<! [:\.0-9]) (?<! \e \[) (?<! \e \[\?)
161             [0-9]+ (?! [:0-9])
162 8158         18200 ) /$self->_numerify($1)/egxx;
163              
164 10070         64374 return "$preamble$line$trailer$eol";
165             }
166              
167 10070     10070   13953 sub _parse_line_arista ( $self, $line ) {
  10070         14322  
  10070         14848  
  10070         14107  
168             #
169             # Arista & Cisco
170             #
171              
172             # BGP
173 10070         15024 $line =~ s/^ ( \Q BGP state is Established\E \N* ) $/$self->_colorize($1, $GREEN)/exx;
  2         8  
174 10070         14594 $line =~ s/^ ( \Q BGP state is \E \N* ) $/$self->_colorize($1, $RED)/exx;
  2         9  
175              
176 10070         14671 $line =~
177 6         19 s/^ ( \Q Last \E (?: sent || rcvd) \ (?: socket-error || notification) : \N+ ) $/$self->_colorize($1, $INFO)/exx;
178              
179 10070         15379 $line =~
180 2         8 s/^ ( \ \ (?: Inbound || Outbound) \Q route map is \E \N* )$/$self->_colorize($1, $INFO)/exx;
181 10070         14736 $line =~
182 0         0 s/^ ( \Q Inherits configuration from and member of peer-group \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
183              
184 10070         14290 $line =~
185 8         24 s/^ ( \Q \E (?: IPv4 | IPv6) \Q Unicast: \E \N* ) $/$self->_colorize($1, $INFO)/exx;
186 10070         14289 $line =~
187 5         13 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 10070         95097 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 10070         37094 $line =~ s/($errors)/$self->_colorize($1, $RED)/e;
  0         0  
215              
216 10070         16499 $line =~ s/^ ( \QBGP neighbor is \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  5         18  
217 10070         14854 $line =~ s/^ ( (?: Local | Remote) \Q TCP address is \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  7         22  
218              
219             #
220             # Interfaces
221             #
222              
223             # We look for information lines
224 10070 100       41153 if ( $line =~ m/^ ((?:$INT [^, ][^,]+, )*$INT [^, ][^,]+)$/ ) {
225             my (%values) =
226 690         2749 map { reverse split / /, $_, 2 } split ', ', $1;
  1617         5386  
227              
228 690 100   2250   4118 if ( any { exists( $values{$_} ) } @INTERFACE_IGNORES ) {
  2250 100       5220  
    100          
229             # Do nothing.
230 1329     1329   3143 } elsif ( any { exists( $values{$_} ) } @INTERFACE_INFOS ) {
231 128         343 $line = $self->_colorize( $line, $INFO );
232 1069     1069   1818 } elsif ( any { $values{$_} } keys %values ) {
233 16         54 $line = $self->_colorize( $line, $RED );
234             } else {
235 363         920 $line = $self->_colorize( $line, $GREEN );
236             }
237             }
238              
239 10070         27701 my $INTERFACE = qr/ [A-Z] \S+ /xx;
240 10070         23019 my $INTSHORT = qr/ [A-Z][a-z][0-9] \S* /xx;
241              
242             # "show int" up/down
243 10070         33421 $line =~
244 43         137 s/^ ( $INTERFACE \Q is up, line protocol is up\E (:? \Q (connected)\E )? \s? ) $/$self->_colorize($1, $GREEN)/exx;
245 10070         30291 $line =~
246 34         117 s/^ ( $INTERFACE \Q is administratively down,\E \N+ ) $/$self->_colorize($1, $ORANGE)/exx;
247 10070         28735 $line =~
248 11         33 s/^ ( $INTERFACE \Q is \E \N+ \Q, line protocol is \E \N+ ) $/$self->_colorize($1, $RED)/exx;
249              
250 10070         16346 $line =~ s/^ ( \Q Up \E \N+ ) $/$self->_colorize($1, $GREEN)/exx;
  36         115  
251 10070         14672 $line =~ s/^ ( \Q Down \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  38         152  
252              
253             # "show int" description lines
254 10070         14761 $line =~ s/^ ( (?: \Q \E|\Q \E)? \Q Description: \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  72         202  
255              
256             # "show int" rates
257 10070         34340 $line =~
258 170         491 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 10070         28592 $line =~ s/^ ( $INTSHORT \N+ \Q connected \E \N+ ) $/$self->_colorize($1, $GREEN)/exx;
  38         125  
262 10070         26255 $line =~ s/^ ( $INTSHORT \N+ \Q disabled \E \N+ ) $/$self->_colorize($1, $ORANGE)/exx;
  34         118  
263 10070         26042 $line =~ s/^ ( $INTSHORT \N+ \Q errdisabled \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  0         0  
264 10070         26241 $line =~ s/^ ( $INTSHORT \N+ \Q notconnect \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  10         36  
265              
266             # "show int description"
267 10070         24075 $line =~
268 36         108 s/^ ( $INTSHORT \s+ up \s+ up (?: \s+ \N+)? ) $/$self->_colorize($1, $GREEN)/exx;
269 10070         26129 $line =~
270 33         97 s/^ ( $INTSHORT \s+ \Qadmin down\E \s+ \S+ (?: \s+ \N+)? ) $/$self->_colorize($1, $ORANGE)/exx;
271 10070         23742 $line =~
272 5         16 s/^ ( $INTSHORT \s+ down \s+ \S+ (?: \s+ \N+)? ) $/$self->_colorize($1, $RED)/exx;
273              
274             # "show int transceiver" (Arista)
275 10070         33872 $line =~
276 16         53 s/^ ( $INTSHORT (?: \s+ $LIGHT){4} \s+ $LOWLIGHT \s+ \S+ \s ago ) $/$self->_colorize($1, $RED)/exx;
277 10070         28158 $line =~
278 8         23 s/^ ( $INTSHORT (?: \s+ $LIGHT){5} \s+ \S+ \s ago ) $/$self->_colorize($1, $INFO)/exx;
279 10070         24635 $line =~
280 0         0 s/^ ( $INTSHORT (?: \s+ N\/A ){6} \s* ) $/$self->_colorize($1, $ORANGE)/exx;
281              
282             # "show int transceiver" (Cisco)
283 10070         35530 $line =~
284 1         5 s/^ ( $INTSHORT (?: \s+ $LIGHT){3} \s+ $LOWLIGHT ) \s+ $/$self->_colorize($1, $RED)/exx;
285 10070         30302 $line =~
286 3         11 s/^ ( $INTSHORT (?: \s+ $LIGHT){4} ) \s+ $/$self->_colorize($1, $INFO)/exx;
287              
288             #
289             # LLDP Neighbors Detail
290             #
291 10070         30481 $line =~
292 0         0 s/^ ( \QInterface\E \s \S+ \s detected \s $POSINT \Q LLDP neighbors:\E ) $/$self->_colorize($1, $INFO)/exx;
293 10070         27851 $line =~
294 0         0 s/^ ( \Q Neighbor \E \S+ \s age \s $POSINT \s seconds ) $/$self->_colorize($1, $INFO)/exx;
295 10070         15634 $line =~
296 0         0 s/^ ( \Q Discovered \E \N+ \Q; Last changed \E \N+ \s ago ) $/$self->_colorize($1, $INFO)/exx;
297 10070         14163 $line =~ s/^ ( \Q - System Name: \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  0         0  
298 10070         14104 $line =~ s/^ ( \Q Port ID :\E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  0         0  
299 10070         14784 $line =~ s/^ ( \Q Management Address : \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  0         0  
300              
301             #
302             # Show Spanning-Tree
303             #
304 10070         30364 $line =~
305 13         38 s/^ ( $INTSHORT \s+ $STP_TYPES\s+ $STP_GOOD \s+ [0-9]+ \s+ [0-9]+\.[0-9]+ \s+ P2p .* ) $/$self->_colorize($1, $GREEN)/exx;
306 10070         30303 $line =~
307 1         4 s/^ ( $INTSHORT \s+ $STP_TYPES\s+ $STP_WARN \s+ [0-9]+ \s+ [0-9]+\.[0-9]+ \s+ P2p .* ) $/$self->_colorize($1, $ORANGE)/exx;
308 10070         29348 $line =~
309 2         9 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 10070         15411 $line =~ s/^ (State: \s synced) $/$self->_colorize($1, $GREEN)/exx;
  1         6  
314 10070         13949 $line =~ s/^ (State: \s .* ) $/$self->_colorize($1, $RED)/exx;
  1         12  
315              
316 10070         13713 $line =~ s/^ (Connection: \s Active \s .*) $/$self->_colorize($1, $GREEN)/exx;
  1         5  
317 10070         14487 $line =~ s/^ (Connection: \s .* ) $/$self->_colorize($1, $RED)/exx;
  0         0  
318              
319 10070         49697 return $line;
320             }
321              
322 10070     10070   14330 sub _parse_line_junos ( $self, $line ) {
  10070         13710  
  10070         14757  
  10070         13538  
323              
324             #
325             # JunOS
326             #
327              
328             # Show Interfaces
329 10070         15273 $line =~
330 53         170 s/^ ( \QPhysical interface: \E \S+ \Q Enabled, Physical link is Up\E ) $/$self->_colorize($1, $GREEN)/exx;
331 10070         13937 $line =~
332 36         118 s/^ ( \QPhysical interface: \E \S+ \Q Enabled, Physical link is Down\E ) $/$self->_colorize($1, $RED)/exx;
333 10070         14801 $line =~
334 0         0 s/^ ( \QPhysical interface: \E \S+ \s \S+ \Q Physical link is Down\E ) $/$self->_colorize($1, $ORANGE)/exx;
335 10070         13973 $line =~
336 2         27 s/^ ( \QPhysical interface: \E \S+ ) $/$self->_colorize($1, $INFO)/exx;
337              
338 10070         14222 $line =~ s/^ ( \Q Logical interface \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  72         196  
339              
340 10070         14379 $line =~ s/^ ( \Q Last flapped : \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  40         113  
341              
342 10070         31782 $line =~ s/^ ( \Q Input rate : \E $NUM \N+ ) $/$self->_colorize($1, $INFO)/exx;
  29         82  
343 10070         28741 $line =~ s/^ ( \Q Output rate : \E $NUM \N+ ) $/$self->_colorize($1, $INFO)/exx;
  29         87  
344              
345 10070         29349 $line =~ s/^ ( \Q Input packets : \E $NUM \N+ ) $/$self->_colorize($1, $INFO)/exx;
  44         116  
346 10070         28363 $line =~ s/^ ( \Q Output packets: \E $NUM \N+ ) $/$self->_colorize($1, $INFO)/exx;
  44         117  
347              
348 10070         16073 $line =~ s/^ ( \Q Active alarms : None\E ) $/$self->_colorize($1, $GREEN)/exx;
  12         40  
349 10070         14039 $line =~ s/^ ( \Q Active alarms : \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  13         43  
350 10070         13850 $line =~ s/^ ( \Q Active defects : None\E ) $/$self->_colorize($1, $GREEN)/exx;
  12         37  
351 10070         14216 $line =~ s/^ ( \Q Active defects : \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  13         38  
352              
353 10070         24407 my $AE = qr/ (?: ae [0-9\.]+ ) /xx;
354 10070         22983 my $BME = qr/ (?: bme [0-9\.]+ ) /xx;
355 10070         21398 my $CBP = qr/ (?: cbp [0-9\.]+ ) /xx;
356 10070         22213 my $ETH = qr/ (?: [gx] e- [0-9\/\.]+ ) /xx;
357 10070         20897 my $IRB = qr/ (?: irb [0-9\/\.]* ) /xx;
358 10070         20844 my $JSRV = qr/ (?: jsrv [0-9\.]* ) /xx;
359 10070         20984 my $LO = qr/ (?: lo [0-9\.]+ ) /xx;
360 10070         20846 my $ME = qr/ (?: me [0-9\.]+ ) /xx;
361 10070         20552 my $PFE = qr/ (?: pf [eh] - [0-9\/\.]+ ) /xx;
362 10070         20687 my $PIP = qr/ (?: pip [0-9\/\.]+ ) /xx;
363 10070         20352 my $VCP = qr/ (?: vcp- [0-9\/\.]+ ) /xx;
364 10070         20871 my $VLAN = qr/ (?: vlan\. [0-9]+ ) /xx;
365 10070         21320 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 10070         61833 my $IFACES = qr/$AE|$BME|$CBP|$ETH|$IRB|$LO|$ME|$JSRV|$PFE|$PIP|$VCP|$VLAN|$OTHER/xx;
373              
374 10070         33219 $line =~ s/^ ( (?: $IFACES) \s+ up \s+ up \N* ) $/$self->_colorize($1, $GREEN)/exx;
  116         337  
375 10070         26105 $line =~ s/^ ( $ETH \s+ VCP ) $/$self->_colorize($1, $GREEN)/exx;
  0         0  
376 10070         27388 $line =~ s/^ ( (?: $IFACES) \s+ up \s+ down \N* ) $/$self->_colorize($1, $RED)/exx;
  64         230  
377 10070         26403 $line =~ s/^ ( (?: $IFACES) \s+ down \s+ \N* ) $/$self->_colorize($1, $ORANGE)/exx;
  1         4  
378              
379             # Errors
380 10070         15703 $line =~ s/^ ( \Q Bit errors \E \s+ 0 ) $/$self->_colorize($1, $GREEN)/exx;
  1         17  
381 10070         14093 $line =~ s/^ ( \Q Bit errors \E \s+ 0 ) $/$self->_colorize($1, $GREEN)/exx;
  0         0  
382 10070         13583 $line =~ s/^ ( \Q Errored blocks \E \s+ [1-9][0-9]+ ) $/$self->_colorize($1, $RED)/exx;
  0         0  
383 10070         14361 $line =~ s/^ ( \Q Errored blocks \E \s+ 0 ) $/$self->_colorize($1, $GREEN)/exx;
  1         5  
384 10070         13668 $line =~
385 4         13 s/^ ( \Q FEC \E \S+ \s Errors (?: \s Rate)? \s+ 0 ) $/$self->_colorize($1, $GREEN)/exx;
386 10070         13437 $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 10070         28567 $line =~ s/^ ( \Q Laser output power \E \s+ : \s $NUM \N+ ) $/$self->_colorize($1, $RED)/exx;
  0         0  
391 10070         33637 $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 10070         33897 $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 10070         31691 $line =~
396 1         5 s/^ ( \Q Laser receiver power \E \s+ : \s+ $NUM \Q mW \/ \E $LOWLIGHT \s dBm ) $/$self->_colorize($1, $RED)/exx;
397 10070         31337 $line =~
398 1         7 s/^ ( \Q Laser receiver power \E \s+ : \s+ $NUM \Q mW \/ \E $LIGHT \s dBm ) $/$self->_colorize($1, $INFO)/exx;
399 10070         32753 $line =~
400 1         12 s/^ ( \Q Receiver signal average optical power \E \s+ : \s+ $NUM \Q mW \/ \E $LOWLIGHT \s dBm ) $/$self->_colorize($1, $RED)/exx;
401 10070         34125 $line =~
402 1         4 s/^ ( \Q Receiver signal average optical power \E \s+ : \s+ $NUM \Q mW \/ \E $LIGHT \s dBm ) $/$self->_colorize($1, $INFO)/exx;
403              
404 10070         62384 return $line;
405             }
406              
407 10070     10070   14191 sub _parse_line_vyos ( $self, $line ) {
  10070         14971  
  10070         14744  
  10070         13106  
408              
409             #
410             # VyOS (Stuff the Arista/Cisco commands did not do)
411             #
412              
413             # BGP
414 10070         16186 $line =~ s/^ ( \Q BGP state = \E (?!Established) \N* ) $/$self->_colorize($1, $RED)/exx;
  0         0  
415 10070         15353 $line =~
416 1         8 s/^ ( \Q BGP state = Established\E \N* ) $/$self->_colorize($1, $GREEN)/exx;
417              
418 10070         14373 $line =~
419 2         15 s/^ ( \Q Route map for \E (?: incoming|outgoing ) \Q advertisements is \E \N* ) $/$self->_colorize($1, $INFO)/exx;
420 10070         15009 $line =~ s/^ ( \Q \E \S+ \Q peer-group member\E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  1         4  
421              
422 10070         31720 $line =~ s/^ ( \Q \E $INT \Q accepted prefixes\E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  1         13  
423              
424 10070         16586 $line =~ s/^ ( \QLocal host: \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  1         16  
425 10070         14816 $line =~ s/^ ( \QForeign host: \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  1         7  
426              
427 10070         19437 return $line;
428             }
429              
430 10070     10070   13840 sub _parse_line_ciena ( $self, $line ) {
  10070         14137  
  10070         15056  
  10070         13113  
431              
432             #
433             # Ciena
434             #
435              
436             # Admin/Operational state for multiple commands
437 10070         15526 $line =~
438 8         35 s/^ ( \| \s? \QAdmin State \E \s+ \| ) ( \s? \QEnabled \E ) ( \N+ ) $/$1.$self->_colorize($2, $GREEN).$3/exx;
439 10070         14340 $line =~
440 1         4 s/^ ( \| \s? \QAdmin State \E \s+ \| ) ( \s? \QDisabled \E ) ( \N+ ) $/$1.$self->_colorize($2, $ORANGE).$3/exx;
441 10070         13865 $line =~
442 10         33 s/^ ( \| \s? \QAdmin State \E \s+ \| ) ( [^|]+ ) ( \N+ ) $/$1.$self->_colorize($2, $RED).$3/exx;
443              
444 10070         14050 $line =~
445 7         21 s/^ ( \| \s? \QOperational State \E \s+ \| ) ( \s? \QUp \E ) ( \N+ ) $/$1.$self->_colorize($2, $GREEN).$3/exx;
446 10070         14144 $line =~
447 1         7 s/^ ( \| \s? \QOperational State \E \s+ \| ) ( \s? \QInitializing \E ) ( \N+ ) $/$1.$self->_colorize($2, $ORANGE).$3/exx;
448 10070         14781 $line =~
449 10         28 s/^ ( \| \s? \QOperational State \E \s+ \| ) ( [^|]+ ) ( \N+ ) $/$1.$self->_colorize($2, $RED).$3/exx;
450              
451             # xcvr show xcvr N/N
452 10070         33906 $line =~ s/ ^ ( \| ) ( \Q Tx Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ ) $/
453 1         15 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5/exx;
454 10070         32639 $line =~ s/ ^ ( \| ) ( \Q Tx Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ ) $/
455 1         6 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5/exx;
456              
457 10070         30439 $line =~ s/ ^ ( \| ) ( \Q Rx Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ ) $/
458 1         18 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5/exx;
459 10070         29194 $line =~ s/ ^ ( \| ) ( \Q Rx Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ ) $/
460 1         6 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5/exx;
461              
462             # xcvr show xcvr status
463 10070         30625 $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 10070         30760 $line =~
467             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Tx Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ ) $/
468 1         7 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5/exx;
469 10070         29209 $line =~
470             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Rx Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ ) $/
471 1         12 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5/exx;
472 10070         29979 $line =~
473             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Rx Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ ) $/
474 1         15 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5/exx;
475              
476             # ptp show ptp x/x [status]
477 10070         17059 $line =~
478             s/^ ( \| (?: \s Transmitter)? \Q State \E \s+ \| ) ( \Q Enabled \E \s+ ) ( \| ) ( \Q Up \E|\Q Enabled \E )( \N+ ) $/
479 6         35 $1.$self->_colorize($2, $GREEN).$3.$self->_colorize($4, $GREEN).$5/exx;
480 10070         14694 $line =~
481             s/^ ( \| (?: \s Transmitter)? \Q State \E \s+ \| ) ( \Q Enabled \E \s+ ) ( \| ) ( [^|]+ )( \N+ ) $/
482 3         17 $1.$self->_colorize($2, $GREEN).$3.$self->_colorize($4, $RED).$5/exx;
483 10070         14785 $line =~
484             s/^ ( \| (?: \s Transmitter)? \Q State \E \s+ \| ) ( \Q Disabled \E \s+ ) ( \| ) ( [^|]+ )( \N+ ) $/
485 2         16 $1.$self->_colorize($2, $ORANGE).$3.$self->_colorize($4, $ORANGE).$5/exx;
486              
487 10070         33798 $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 10070         34032 $line =~
491             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ )$/
492 1         8 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5.$self->_colorize($6, $INFO).$7/exx;
493 10070         33454 $line =~
494             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ )$/
495 1         10 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $RED).$7/exx;
496 10070         33681 $line =~
497             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ )$/
498 4         21 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $INFO).$7/exx;
499              
500 10070         33889 $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 10070         33234 $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 10070         31559 $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 10070         32737 $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 10070         33801 $line =~
514             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Span Loss (dB)\E \s* ) ( \| ) ( \s+ $NUM \s+ ) ( \| ) ( \s+ $NUM \s+ ) ( \| )$/
515 1         6 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $INFO).$7/exx;
516              
517 10070         31211 $line =~
518             s/^ ( \| \s+ \| ) ( \Q Optical Return Loss \E \( \QdB\E \) ) ( \s+ \| \s+ ) ( $GOODRETURNLOSS ) (\s+ \| ) $/
519 1         6 $1.$self->_colorize($2, $GREEN).$3.$self->_colorize($4, $GREEN).$5/exx;
520 10070         31406 $line =~
521             s/^ ( \| \s+ \| ) ( \Q Optical Return Loss \E \( \QdB\E \) ) ( \s+ \| \s+ ) ( $WARNRETURNLOSS ) (\s+ \| ) $/
522 1         5 $1.$self->_colorize($2, $ORANGE).$3.$self->_colorize($4, $ORANGE).$5/exx;
523 10070         29465 $line =~
524             s/^ ( \| \s+ \| ) ( \Q Optical Return Loss \E \( \QdB\E \) ) ( \s+ \| \s+ ) ( $BADRETURNLOSS ) (\s+ \| ) $/
525 1         5 $1.$self->_colorize($2, $RED).$3.$self->_colorize($4, $RED).$5/exx;
526              
527             # alarm show
528 10070         33596 $line =~ s/ ^ ( \| ) ( \s* [0-9]* ) ( \| ) ( \s+ ) ( \| ) ( [^|]+ ) ( \| ) ( \s+ [0-9]+ )
529             ( \| ) ( \s* $BIGALARMS|$LITTLEALARMS \s* ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
530             ( \| ) ( \s+ $BIGOVERRIDES \s+ ) ( \| ) $/
531 3         20 $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 10070         28724 $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 10070         29706 $line =~
552             s/ ^ ( \| ) ( \s+ [0-9]* ) ( \| ) ( \s+ \QY\E \s* ) ( \| ) ( [^|]+ ) ( \| ) ( \s+ [0-9]+ )
553             ( \| ) ( \s* $BIGALARMS \s* ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $/
554 8         30 $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 10070         28060 $line =~
564             s/ ^ ( \| ) ( \s+ [0-9]* ) ( \| ) ( \s+ \QY\E \s* ) ( \| ) ( [^|]+ ) ( \| ) ( \s+ [0-9]+ )
565             ( \| ) ( \s* $LITTLEALARMS \s* ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $/
566 1         20 $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 10070         15851 $line =~ s/ ^ ( \QSHELL \E \S+ \Q FAILURE\E \N+ ) $/$self->_colorize($1, $RED)/exx;
  2         7  
578              
579             # ptp show
580 10070         14413 $line =~
581             s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( \Q Ena \E ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
582             ( \| ) ( [^|]+ ) ( \| ) ( \QUp\E \s+ ) ( \| ) ( [^|]+ )
583             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) /
584 15         40 $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 10070         14051 $line =~
597             s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( \Q Ena \E ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
598             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
599             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) /
600 8         25 $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 10070         15194 $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 10070         15668 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( \Q Enabled \E \s+ )
631             ( \| ) ( [^|]+ ) ( \| ) ( \Q Up \E \s+ ) ( \| ) ( [^|]+ ) ( \| ) $ /
632 1         6 $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 10070         14092 $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 10070         14559 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( \Q Enabled \E \s+ )
649             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $ /
650 1         6 $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 10070         14790 $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 10070         13579 $line =~ s/^ ( \QSuccess! Preparing state dump...\E \s* ) $/ $self->_colorize($1, $GREEN)/exx;
  1         5  
669              
670             # Logout message
671 10070         14313 $line =~ s/^ ( \QTerminal will be disconnected in 1 minute if it remains inactive.\E )/
672 1         7 $self->_colorize($1, $RED)/exx;
673              
674             # Warning message
675 10070         13834 $line =~ s/^ (\QWARNING:\E) / $self->_colorize($1, $ORANGE)/exx;
  1         5  
676 10070         14312 $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 10070         13658 $line =~ s/^ (\QERROR: \E .* disabled ) $/ $self->_colorize($1, $RED)/exx;
  1         5  
681              
682              
683 10070         40158 return $line;
684             }
685              
686 73     73   111 sub _ipv4ify ( $self, $ip ) {
  73         120  
  73         176  
  73         101  
687 73         265 my ( $subnet, $len ) = split "/", $ip;
688 73   100     399 $len //= 32;
689              
690 73         209 my (@oct) = split /\./, $subnet;
691 73         133 my $val = 0;
692 73         173 foreach my $i (@oct) {
693 292         403 $val *= 256;
694 292         543 $val += $i;
695             }
696 73         132 $val = $val * 256 + $len;
697 73         219 my $color = $BGCOLORS[ $val % scalar(@BGCOLORS) ];
698 73         193 return $self->_colorize( $ip, $color );
699             }
700              
701 17     17   28 sub _ipv6ify ( $self, $ip ) {
  17         32  
  17         42  
  17         27  
702 17         69 my ( $subnet, $len ) = split "/", $ip;
703 17   100     77 $len //= 128;
704              
705 17         58 my ( $first, $second ) = split "::", $subnet;
706              
707 17         48 my (@fparts) = split /:/, $first;
708 17         70 push( @fparts, ( '', '', '', '', '', '', '', '' ) );
709 17         69 @fparts = @fparts[ 0 .. 7 ];
710              
711 17         31 my (@sparts);
712 17 100       74 if ( defined($second) ) {
713 15         49 (@sparts) = reverse split /:/, $second;
714             }
715 17         63 push( @sparts, ( '', '', '', '', '', '', '', '' ) );
716 17         83 @sparts = reverse @sparts[ 0 .. 7 ];
717              
718 17         33 my $val = 0;
719 17         58 for my $i ( 0 .. 7 ) {
720 136         181 $val *= 16;
721 136         262 $val += hex( $fparts[$i] . $sparts[$i] );
722             }
723 17         51 $val *= 32;
724 17         44 $val += $len;
725              
726 17         53 my $color = $BGCOLORS[ $val % scalar(@BGCOLORS) ];
727 17         57 return $self->_colorize( $ip, $color );
728             }
729              
730 8158     8158   11050 sub _numerify ( $self, $num ) {
  8158         11546  
  8158         15057  
  8158         10407  
731 8158         11378 my $output = "";
732 8158         17554 while ( length($num) > 3 ) {
733 1096         2479 $output = substr( $num, -3 ) . $output;
734 1096         2242 $num = substr( $num, 0, length($num) - 3 );
735              
736 1096         1483 my $next3;
737 1096 100       2185 if ( length($num) > 3 ) {
738 418         659 $next3 = substr( $num, -3 );
739 418         706 $num = substr( $num, 0, length($num) - 3 );
740             } else {
741 678         1064 $next3 = $num;
742 678         1019 $num = '';
743             }
744 1096         2060 $output = $self->_highlight($next3) . $output;
745             }
746 8158         14545 $output = $num . $output;
747              
748 8158         31265 return $output;
749             }
750              
751 1096     1096   1488 sub _highlight ( $self, $str ) {
  1096         1551  
  1096         1540  
  1096         1389  
752 1096         4068 return "\e[4m$str\e[24m";
753             }
754              
755 2330     2330   3360 sub _colorize ( $self, $text, $color ) {
  2330         3348  
  2330         4498  
  2330         3455  
  2330         3046  
756 2330         5182 $text = $color . $text;
757 2330         7737 $text =~ s/ \Q$RESET\E / $color /;
758 2330         3994 $text = $text . $RESET;
759 2330         7484 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.231460
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