File Coverage

lib/App/RouterColorizer.pm
Criterion Covered Total %
statement 429 453 94.7
branch 19 20 95.0
condition 4 4 100.0
subroutine 25 25 100.0
pod 1 1 100.0
total 478 503 95.0


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2              
3             #
4             # Copyright (C) 2021-2024 Joelle Maslak
5             # All Rights Reserved - See License
6             #
7              
8             # ABSTRACT: Colorize router CLI output
9              
10 11     11   2669467 use v5.22;
  11         47  
11 11     11   74 use strict;
  11         29  
  11         323  
12 11     11   50 use warnings;
  11         20  
  11         1018  
13              
14             package App::RouterColorizer;
15             $App::RouterColorizer::VERSION = '1.242880';
16 11     11   6941 use Moose;
  11         6231281  
  11         100  
17              
18 11     11   102060 use feature 'signatures';
  11         27  
  11         2599  
19 11     11   125 no warnings 'experimental::signatures';
  11         32  
  11         737  
20              
21 11     11   8849 use English;
  11         37894  
  11         115  
22 11     11   12344 use Import::Into;
  11         8592  
  11         491  
23 11     11   85 use List::Util qw(any);
  11         26  
  11         986  
24 11     11   7387 use Regexp::Common qw/net number/;
  11         39281  
  11         50  
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 11 50   11   89654 if ( $PERL_VERSION gt v5.33.0 ) {
31 11         144 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[35m\e[47m", # magenta on white
44             "\e[90m\e[47m", # gray on white
45             "\e[30m\e[41m", # black on red
46             "\e[90m\e[41m", # gray on red
47             "\e[37m\e[41m", # white on red
48             "\e[30m\e[42m", # black on green
49             "\e[30m\e[43m", # black on yellow (orange)
50             "\e[31m\e[43m", # red on yellow (orange)
51             "\e[37m\e[44m", # white on blue
52             "\e[30m\e[45m", # black on magenta
53             "\e[37m\e[45m", # white on magenta
54             "\e[30m\e[46m", # black on cyan
55             "\e[30m\e[100m", # black on gray
56             "\e[97m\e[100m", # white on gray
57             );
58              
59             our $NUM = qr/$RE{num}{real}/;
60             our $INT = qr/$RE{num}{int}{-sign => ''}/;
61             our $POSINT = qr/(?!0)$INT/;
62             our $LOWLIGHT = qr/ (?: -[3-9][0-9]\. [0-9]{1,2} )
63             | (?: - \s Inf)
64             | (?: -2 [5-9] \. [0-9]{1,2} )/xx;
65             our $VERYLL = qr/ (?: -[4-9][0-9]\. [0-9]{1,2} )/xx;
66             our $LIGHT = qr/ (?: $NUM ) | (?: N\/A ) /xx;
67              
68             our $GOODRETURNLOSS = qr/ (?: 29\.[0-9]+ )
69             | (?: [3-9][0-9]+\.[0-9]+ )/xx;
70             our $WARNRETURNLOSS = qr/ (?: 2[0-8]\.[0-9]+ )
71             | (?: 1[6-9]\.[0-9]+ )/xx;
72             our $BADRETURNLOSS = qr/ (?: 1[0-5]\.[0-9]+ )
73             | (?: [0-9]\.[0-9]+ )/xx;
74              
75             our $IPV4CIDR = qr/ $RE{net}{IPv4}
76             (?: \/
77             (?:
78             (?:[12][0-9])
79             | (?:3[0-2])
80             | (?:[0-9])
81             )
82             )?
83             (?![0-9])
84             /xx;
85              
86             our $IPV6CIDR = qr/ $RE{net}{IPv6}
87             (?: \/
88             (?:
89             (?:1[01][0-9])
90             | (?:12[0-8])
91             | (?:[1-9][0-9])
92             | (?:[0-9])
93             )
94             )?
95             (?![0-9])
96             /xx;
97              
98             our $BIGALARMS = qr/critical|major|minor|warning/;
99             our $LITTLEALARMS = qr/info/;
100             our $BIGOVERRIDES = qr/ (?:\QRx Remote Fault\E)
101             | (?:\QFar End Client Signal Fail\E)
102             /xx;
103              
104             our @INTERFACE_IGNORES = ( "bytes", "packets input", "packets output", "multicast" );
105             our @INTERFACE_INFOS = ( "PAUSE input", "PAUSE output", "pause input" );
106              
107             our $STP_GOOD = qr/forwarding|FWD/;
108             our $STP_WARN = qr/learning|LRN/;
109             our $STP_BAD = qr/discarding|BLK/;
110             our $STP_TYPES = qr/designated|root|alternate|Desg|Root|Altn/;
111              
112             our $TTY_TYPES = qr/AUX|CTY|LPR|TTY|VTY/;
113              
114             our @bgcolors = (
115             "\e[30m\e[47m", # black on white
116             "\e[30m\e[41m", # black on red
117             "\e[30m\e[42m", # black on green
118             "\e[30m\e[43m", # black on yellow (orange)
119             "\e[37m\e[44m", # white on blue
120             "\e[30m\e[45m", # black on magenta
121             "\e[30m\e[46m", # black on cyan
122             );
123              
124 5091     5091 1 3530604 sub format_text ( $self, $text ) {
  5091         8368  
  5091         8990  
  5091         7397  
125             # Remove Arista "more" prompt
126 5091         11664 $text =~ s/ \x1b \[ \Q3m --More-- \E \x1b \[ 23m \x1b \[ K \r \x1b \[ K //gmsxx;
127              
128             # Break into lines
129 5091         8641 my $output = '';
130 5091         24901 while ( $text =~ m/([^\n\r]*[\r]?[\n]?)/g ) {
131 10237         28739 $output .= $self->_parse_line($1);
132             }
133 5091         26103 return $output;
134             }
135              
136 10237     10237   16819 sub _parse_line ( $self, $text ) {
  10237         17114  
  10237         28967  
  10237         15853  
137 10237         46810 my ( $line, $eol ) = $text =~ m/([^\n]*)(\n?)/;
138              
139             # We want to strip out the control characters at the start of the
140             # line. This is kind of black magic...
141 10237         19684 my $preamble = '';
142 10237 100       28935 if ( $line =~ s/^ ( .* (?<! \x1b \[23m) \x1b (?: \[K | M)) //sxx ) {
143 9         28 $preamble = $1;
144             }
145              
146             # Arista "More" prompt (should this be in the Arista parse-line?)
147 10237         17870 $line =~ s/ ^ ( \x1b \[ \Q3m --More-- \E .* \x0d \x1b \[K )//sxx;
148              
149             # A space that is backspaced over
150 10237         17680 $line =~ s/ \Q \E \x08 //gxx;
151              
152 10237         17201 my $trailer = "";
153 10237 100       38320 if ( $line =~ s/(\x0d) $//sxx ) {
154 4929         11947 $trailer = $1;
155             }
156              
157 10237         24750 $line = $self->_parse_line_arista($line);
158 10237         30480 $line = $self->_parse_line_vyos($line);
159 10237         25494 $line = $self->_parse_line_junos($line);
160 10237         29795 $line = $self->_parse_line_ciena($line);
161              
162             # IPv4
163 10237         49848 $line =~ s/($IPV4CIDR)/$self->_ipv4ify($1)/egxx;
  81         320  
164              
165             # IPv6
166 10237         719963 $line =~ s/ ( (?<! [a-fA-F0-9:\-]) $IPV6CIDR (?! [\w:\.\/]) ) /$self->_ipv6ify($1)/egxx;
  17         92  
167              
168             # Numbers
169             # We need to make sure we don't highlight undesirably, such as in an
170             # escape sequence.
171 10237         53154 $line =~ s/ (
172             (?<! [:\.0-9]) (?<! \e \[) (?<! \e \[\?)
173             [0-9]+ (?! [:0-9])
174 8331         22250 ) /$self->_numerify($1)/egxx;
175              
176 10237         92506 return "$preamble$line$trailer$eol";
177             }
178              
179 10237     10237   16367 sub _parse_line_arista ( $self, $line ) {
  10237         15471  
  10237         18161  
  10237         16378  
180             #
181             # Arista & Cisco
182             #
183              
184             # BGP
185 10237         17102 $line =~ s/^ ( \Q BGP state is Established\E \N* ) $/$self->_colorize($1, $GREEN)/exx;
  2         12  
186 10237         16618 $line =~ s/^ ( \Q BGP state is \E \N* ) $/$self->_colorize($1, $RED)/exx;
  2         9  
187              
188 10237         16164 $line =~
189 6         31 s/^ ( \Q Last \E (?: sent || rcvd) \ (?: socket-error || notification) : \N+ ) $/$self->_colorize($1, $INFO)/exx;
190              
191 10237         18405 $line =~
192 2         12 s/^ ( \ \ (?: Inbound || Outbound) \Q route map is \E \N* )$/$self->_colorize($1, $INFO)/exx;
193 10237         15579 $line =~
194 0         0 s/^ ( \Q Inherits configuration from and member of peer-group \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
195              
196 10237         16403 $line =~
197 8         35 s/^ ( \Q \E (?: IPv4 | IPv6) \Q Unicast: \E \N* ) $/$self->_colorize($1, $INFO)/exx;
198 10237         16131 $line =~
199 5         20 s/^ ( \Q Configured maximum total number of routes is \E \d+ (?: \Q, warning limit is \E \d+ )? ) $/$self->_colorize($1, $INFO)/exx;
200              
201             # BGP Errors
202 10237         158803 my $errors = qr/
203             ^
204             \Q \E
205             (?:
206             \QAS path loop detection\E
207             | \QEnforced First AS\E
208             | \QMalformed MPBGP routes\E
209             | \QAS path loop detection\E
210             | \QOriginator ID matches local router ID\E
211             | \QNexthop matches local IP address\E
212             | \QResulting in removal of all paths in update (treat as withdraw)\E
213             | \QResulting in AFI\E \/ \QSAFI disable\E
214             | \QResulting in attribute ignore\E
215             | \QDisabled AFI\E \/ \QSAFIs\E
216             | \QIPv4 labeled-unicast NLRIs dropped due to excessive labels\E
217             | \QIPv6 labeled-unicast NLRIs dropped due to excessive labels\E
218             | \QIPv4 local address not available\E
219             | \QIPv6 local address not available\E
220             | \QUnexpected IPv6 nexthop for IPv4 routes\E
221             )
222             \Q: \E
223             $POSINT
224             $
225             /xx;
226 10237         47868 $line =~ s/($errors)/$self->_colorize($1, $RED)/e;
  0         0  
227              
228 10237         16862 $line =~ s/^ ( \QBGP neighbor is \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  5         27  
229 10237         17468 $line =~ s/^ ( (?: Local | Remote) \Q TCP address is \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  7         31  
230              
231             #
232             # Terminals/Serial
233             #
234 10237         17185 $line =~ s/^ ( \QBaud rate (TX\/RX) is \E .* ) $/$self->_colorize($1, $INFO)/exx;
  1         6  
235 10237         16766 $line =~ s/^ ( \QStatus: Ready\E ) $/$self->_colorize($1, $GREEN)/exx;
  1         4  
236 10237         15801 $line =~ s/^ ( \QStatus: \E .* ) $/$self->_colorize($1, $INFO)/exx;
  1         6  
237 10237         15846 $line =~ s/^ ( \QModem state: Idle\E ) $/$self->_colorize($1, $GREEN)/exx;
  1         11  
238 10237         15869 $line =~ s/^ ( \QModem state: Ready\E ) $/$self->_colorize($1, $GREEN)/exx;
  1         6  
239 10237         16690 $line =~ s/^ ( \QModem state: \E .* ) $/$self->_colorize($1, $INFO)/exx;
  1         5  
240 10237         43212 $line =~ s/^ ( Line \s $INT \Q, Location: \E .* ) $/$self->_colorize($1, $INFO)/exx;
  1         6  
241              
242             #
243             # Interfaces
244             #
245              
246             # We look for information lines, but ignore VTY/CTY/etc lines
247 10237 100       93619 if ( $line =~ m/^\s+$INT $TTY_TYPES/ ) {
    100          
248             # Ignore this line because it's a TTY
249             } elsif ( $line =~ m/^ ((?:$INT [^, ][^,]+, )*$INT [^, ][^,]+)$/ ) {
250             my (%values) =
251 690         3290 map { reverse split / /, $_, 2 } split ', ', $1;
  1617         6667  
252              
253 690 100   2250   6030 if ( any { exists( $values{$_} ) } @INTERFACE_IGNORES ) {
  2250 100       5919  
    100          
254             # Do nothing.
255 1329     1329   3676 } elsif ( any { exists( $values{$_} ) } @INTERFACE_INFOS ) {
256 128         509 $line = $self->_colorize( $line, $INFO );
257 1068     1068   1911 } elsif ( any { $values{$_} } keys %values ) {
258 16         94 $line = $self->_colorize( $line, $RED );
259             } else {
260 363         1240 $line = $self->_colorize( $line, $GREEN );
261             }
262             }
263              
264 10237         30076 my $INTERFACE = qr/ [A-Z] \S+ /xx;
265 10237         23494 my $INTSHORT = qr/ [A-Z][a-z][0-9] \S* /xx;
266              
267             # "show int" up/down
268 10237         45435 $line =~
269 43         157 s/^ ( $INTERFACE \Q is up, line protocol is up\E (:? \Q (connected)\E )? \s? ) $/$self->_colorize($1, $GREEN)/exx;
270 10237         37302 $line =~
271 34         129 s/^ ( $INTERFACE \Q is administratively down,\E \N+ ) $/$self->_colorize($1, $ORANGE)/exx;
272 10237         44449 $line =~
273 11         51 s/^ ( $INTERFACE \Q is \E \N+ \Q, line protocol is \E \N+ ) $/$self->_colorize($1, $RED)/exx;
274              
275 10237         17206 $line =~ s/^ ( \Q Up \E \N+ ) $/$self->_colorize($1, $GREEN)/exx;
  36         141  
276 10237         16900 $line =~ s/^ ( \Q Down \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  38         193  
277              
278             # "show int" description lines
279 10237         16914 $line =~ s/^ ( (?: \Q \E|\Q \E)? \Q Description: \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  72         284  
280              
281             # "show int" rates
282 10237         51223 $line =~
283 170         666 s/^ ( \Q \E $NUM \s \w+ \s (?: input | output) \s rate \s $NUM \s \N+ ) $/$self->_colorize($1, $INFO)/exx;
284              
285             # "show int status"
286 10237         37239 $line =~ s/^ ( $INTSHORT \N+ \Q connected \E \N+ ) $/$self->_colorize($1, $GREEN)/exx;
  38         126  
287 10237         32220 $line =~ s/^ ( $INTSHORT \N+ \Q disabled \E \N+ ) $/$self->_colorize($1, $ORANGE)/exx;
  34         149  
288 10237         31043 $line =~ s/^ ( $INTSHORT \N+ \Q errdisabled \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  0         0  
289 10237         31276 $line =~ s/^ ( $INTSHORT \N+ \Q notconnect \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  10         105  
290              
291             # "show int description"
292 10237         30586 $line =~
293 36         102 s/^ ( $INTSHORT \s+ up \s+ up (?: \s+ \N+)? ) $/$self->_colorize($1, $GREEN)/exx;
294 10237         37706 $line =~
295 33         119 s/^ ( $INTSHORT \s+ \Qadmin down\E \s+ \S+ (?: \s+ \N+)? ) $/$self->_colorize($1, $ORANGE)/exx;
296 10237         30054 $line =~
297 5         92 s/^ ( $INTSHORT \s+ down \s+ \S+ (?: \s+ \N+)? ) $/$self->_colorize($1, $RED)/exx;
298              
299             # "show int transceiver" (Arista)
300 10237         49513 $line =~
301 16         30 s/^ ( $INTSHORT (?: \s+ $LIGHT){4} \s+ $LOWLIGHT \s+ \S+ \s ago ) $/$self->_colorize($1, $RED)/exx;
302 10237         38668 $line =~
303 8         18 s/^ ( $INTSHORT (?: \s+ $LIGHT){5} \s+ \S+ \s ago ) $/$self->_colorize($1, $INFO)/exx;
304 10237         29628 $line =~
305 0         0 s/^ ( $INTSHORT (?: \s+ N\/A ){6} \s* ) $/$self->_colorize($1, $ORANGE)/exx;
306              
307             # "show int transceiver" (Cisco)
308 10237         48900 $line =~
309 1         7 s/^ ( $INTSHORT (?: \s+ $LIGHT){3} \s+ $LOWLIGHT ) \s+ $/$self->_colorize($1, $RED)/exx;
310 10237         40482 $line =~
311 3         15 s/^ ( $INTSHORT (?: \s+ $LIGHT){4} ) \s+ $/$self->_colorize($1, $INFO)/exx;
312              
313             #
314             # LLDP Neighbors Detail
315             #
316 10237         39943 $line =~
317 0         0 s/^ ( \QInterface\E \s \S+ \s detected \s $POSINT \Q LLDP neighbors:\E ) $/$self->_colorize($1, $INFO)/exx;
318 10237         37301 $line =~
319 0         0 s/^ ( \Q Neighbor \E \S+ \s age \s $POSINT \s seconds ) $/$self->_colorize($1, $INFO)/exx;
320 10237         17263 $line =~
321 0         0 s/^ ( \Q Discovered \E \N+ \Q; Last changed \E \N+ \s ago ) $/$self->_colorize($1, $INFO)/exx;
322 10237         15926 $line =~ s/^ ( \Q - System Name: \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  0         0  
323 10237         16062 $line =~ s/^ ( \Q Port ID :\E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  0         0  
324 10237         16679 $line =~ s/^ ( \Q Management Address : \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  0         0  
325              
326             #
327             # Show Spanning-Tree
328             #
329 10237         47730 $line =~
330 13         51 s/^ ( $INTSHORT \s+ $STP_TYPES\s+ $STP_GOOD \s+ [0-9]+ \s+ [0-9]+\.[0-9]+ \s+ P2p .* ) $/$self->_colorize($1, $GREEN)/exx;
331 10237         41500 $line =~
332 1         21 s/^ ( $INTSHORT \s+ $STP_TYPES\s+ $STP_WARN \s+ [0-9]+ \s+ [0-9]+\.[0-9]+ \s+ P2p .* ) $/$self->_colorize($1, $ORANGE)/exx;
333 10237         40787 $line =~
334 2         11 s/^ ( $INTSHORT \s+ $STP_TYPES\s+ $STP_BAD \s+ [0-9]+ \s+ [0-9]+\.[0-9]+ \s+ P2p .* ) $/$self->_colorize($1, $RED)/exx;
335              
336              
337             # show bgp rpki cache (Arista)
338 10237         17350 $line =~ s/^ (State: \s synced) $/$self->_colorize($1, $GREEN)/exx;
  1         5  
339 10237         16815 $line =~ s/^ (State: \s .* ) $/$self->_colorize($1, $RED)/exx;
  1         5  
340              
341 10237         16385 $line =~ s/^ (Connection: \s Active \s .*) $/$self->_colorize($1, $GREEN)/exx;
  1         1109  
342 10237         16047 $line =~ s/^ (Connection: \s .* ) $/$self->_colorize($1, $RED)/exx;
  0         0  
343              
344             # Error in authentication
345 10237         16023 $line =~ s/^(Error in authentication)$/$self->_colorize($1, $RED)/e;
  1         22  
346              
347 10237         45343 return $line;
348             }
349              
350 10237     10237   15544 sub _parse_line_junos ( $self, $line ) {
  10237         15317  
  10237         16730  
  10237         14397  
351              
352             #
353             # JunOS
354             #
355              
356             # Show Interfaces
357 10237         16258 $line =~
358 53         225 s/^ ( \QPhysical interface: \E \S+ \Q Enabled, Physical link is Up\E ) $/$self->_colorize($1, $GREEN)/exx;
359 10237         16328 $line =~
360 36         174 s/^ ( \QPhysical interface: \E \S+ \Q Enabled, Physical link is Down\E ) $/$self->_colorize($1, $RED)/exx;
361 10237         16972 $line =~
362 0         0 s/^ ( \QPhysical interface: \E \S+ \s \S+ \Q Physical link is Down\E ) $/$self->_colorize($1, $ORANGE)/exx;
363 10237         16177 $line =~
364 2         12 s/^ ( \QPhysical interface: \E \S+ ) $/$self->_colorize($1, $INFO)/exx;
365              
366 10237         16134 $line =~ s/^ ( \Q Logical interface \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  72         268  
367              
368 10237         17746 $line =~ s/^ ( \Q Last flapped : \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  40         197  
369              
370 10237         44526 $line =~ s/^ ( \Q Input rate : \E $NUM \N+ ) $/$self->_colorize($1, $INFO)/exx;
  29         143  
371 10237         36999 $line =~ s/^ ( \Q Output rate : \E $NUM \N+ ) $/$self->_colorize($1, $INFO)/exx;
  29         118  
372              
373 10237         36221 $line =~ s/^ ( \Q Input packets : \E $NUM \N+ ) $/$self->_colorize($1, $INFO)/exx;
  44         192  
374 10237         33549 $line =~ s/^ ( \Q Output packets: \E $NUM \N+ ) $/$self->_colorize($1, $INFO)/exx;
  44         161  
375              
376 10237         17011 $line =~ s/^ ( \Q Active alarms : None\E ) $/$self->_colorize($1, $GREEN)/exx;
  12         67  
377 10237         16459 $line =~ s/^ ( \Q Active alarms : \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  13         55  
378 10237         17042 $line =~ s/^ ( \Q Active defects : None\E ) $/$self->_colorize($1, $GREEN)/exx;
  12         79  
379 10237         15326 $line =~ s/^ ( \Q Active defects : \E \N+ ) $/$self->_colorize($1, $RED)/exx;
  13         51  
380              
381 10237         29477 my $AE = qr/ (?: ae [0-9\.]+ ) /xx;
382 10237         23680 my $BME = qr/ (?: bme [0-9\.]+ ) /xx;
383 10237         22484 my $CBP = qr/ (?: cbp [0-9\.]+ ) /xx;
384 10237         22290 my $ETH = qr/ (?: [gx] e- [0-9\/\.]+ ) /xx;
385 10237         23608 my $IRB = qr/ (?: irb [0-9\/\.]* ) /xx;
386 10237         21802 my $JSRV = qr/ (?: jsrv [0-9\.]* ) /xx;
387 10237         23352 my $LO = qr/ (?: lo [0-9\.]+ ) /xx;
388 10237         25493 my $ME = qr/ (?: me [0-9\.]+ ) /xx;
389 10237         23009 my $PFE = qr/ (?: pf [eh] - [0-9\/\.]+ ) /xx;
390 10237         25915 my $PIP = qr/ (?: pip [0-9\/\.]+ ) /xx;
391 10237         23985 my $VCP = qr/ (?: vcp- [0-9\/\.]+ ) /xx;
392 10237         23623 my $VLAN = qr/ (?: vlan\. [0-9]+ ) /xx;
393 10237         23579 my $OTHER = qr/
394             (:? fti|fxp|gr|ip|lsq|lt|mt|sp|pp|ppd|ppe|st ) (:?-)? [0-9\/\.]+
395             | gr-\S+
396             | dsc | esi | gre | ipip | jsrv | lsi
397             | mtun | pimd | pime | rbeb | tap | vlan | vme | vtep
398             /xx;
399              
400 10237         84308 my $IFACES = qr/$AE|$BME|$CBP|$ETH|$IRB|$LO|$ME|$JSRV|$PFE|$PIP|$VCP|$VLAN|$OTHER/xx;
401              
402 10237         45088 $line =~ s/^ ( (?: $IFACES) \s+ up \s+ up \N* ) $/$self->_colorize($1, $GREEN)/exx;
  116         395  
403 10237         34594 $line =~ s/^ ( $ETH \s+ VCP ) $/$self->_colorize($1, $GREEN)/exx;
  0         0  
404 10237         35835 $line =~ s/^ ( (?: $IFACES) \s+ up \s+ down \N* ) $/$self->_colorize($1, $RED)/exx;
  64         205  
405 10237         48257 $line =~ s/^ ( (?: $IFACES) \s+ down \s+ \N* ) $/$self->_colorize($1, $ORANGE)/exx;
  1         4  
406              
407             # Errors
408 10237         17692 $line =~ s/^ ( \Q Bit errors \E \s+ 0 ) $/$self->_colorize($1, $GREEN)/exx;
  1         6  
409 10237         15941 $line =~ s/^ ( \Q Bit errors \E \s+ 0 ) $/$self->_colorize($1, $GREEN)/exx;
  0         0  
410 10237         15686 $line =~ s/^ ( \Q Errored blocks \E \s+ [1-9][0-9]+ ) $/$self->_colorize($1, $RED)/exx;
  0         0  
411 10237         16405 $line =~ s/^ ( \Q Errored blocks \E \s+ 0 ) $/$self->_colorize($1, $GREEN)/exx;
  1         23  
412 10237         15973 $line =~
413 4         17 s/^ ( \Q FEC \E \S+ \s Errors (?: \s Rate)? \s+ 0 ) $/$self->_colorize($1, $GREEN)/exx;
414 10237         15221 $line =~
415 0         0 s/^ ( \Q FEC \E \S+ \s Errors (?: \s Rate)? \s+ \N+ ) $/$self->_colorize($1, $RED)/exx;
416              
417             # show interfaces diagnostics optics
418 10237         40617 $line =~ s/^ ( \Q Laser output power \E \s+ : \s $NUM \N+ ) $/$self->_colorize($1, $RED)/exx;
  0         0  
419 10237         47649 $line =~
420 0         0 s/^ ( \Q Laser output power \E \s+ : \s+ $NUM \Q mW \/ \E $LOWLIGHT \s dBm ) $/$self->_colorize($1, $RED)/exx;
421 10237         45008 $line =~
422 2         23 s/^ ( \Q Laser output power \E \s+ : \s+ $NUM \Q mW \/ \E $LIGHT \s dBm ) $/$self->_colorize($1, $INFO)/exx;
423 10237         42729 $line =~
424 1         6 s/^ ( \Q Laser receiver power \E \s+ : \s+ $NUM \Q mW \/ \E $LOWLIGHT \s dBm ) $/$self->_colorize($1, $RED)/exx;
425 10237         41348 $line =~
426 1         5 s/^ ( \Q Laser receiver power \E \s+ : \s+ $NUM \Q mW \/ \E $LIGHT \s dBm ) $/$self->_colorize($1, $INFO)/exx;
427 10237         43625 $line =~
428 1         13 s/^ ( \Q Receiver signal average optical power \E \s+ : \s+ $NUM \Q mW \/ \E $LOWLIGHT \s dBm ) $/$self->_colorize($1, $RED)/exx;
429 10237         40275 $line =~
430 1         5 s/^ ( \Q Receiver signal average optical power \E \s+ : \s+ $NUM \Q mW \/ \E $LIGHT \s dBm ) $/$self->_colorize($1, $INFO)/exx;
431              
432 10237         68836 return $line;
433             }
434              
435 10237     10237   15763 sub _parse_line_vyos ( $self, $line ) {
  10237         16002  
  10237         16662  
  10237         14825  
436              
437             #
438             # VyOS (Stuff the Arista/Cisco commands did not do)
439             #
440              
441             # BGP
442 10237         17531 $line =~ s/^ ( \Q BGP state = \E (?!Established) \N* ) $/$self->_colorize($1, $RED)/exx;
  0         0  
443 10237         15961 $line =~
444 1         6 s/^ ( \Q BGP state = Established\E \N* ) $/$self->_colorize($1, $GREEN)/exx;
445              
446 10237         15697 $line =~
447 2         15 s/^ ( \Q Route map for \E (?: incoming|outgoing ) \Q advertisements is \E \N* ) $/$self->_colorize($1, $INFO)/exx;
448 10237         16452 $line =~ s/^ ( \Q \E \S+ \Q peer-group member\E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  1         6  
449              
450 10237         44161 $line =~ s/^ ( \Q \E $INT \Q accepted prefixes\E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  1         6  
451              
452 10237         17896 $line =~ s/^ ( \QLocal host: \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  1         5  
453 10237         15881 $line =~ s/^ ( \QForeign host: \E \N+ ) $/$self->_colorize($1, $INFO)/exx;
  1         5  
454              
455 10237         22915 return $line;
456             }
457              
458 10237     10237   15559 sub _parse_line_ciena ( $self, $line ) {
  10237         16340  
  10237         16791  
  10237         14399  
459              
460             #
461             # Ciena
462             #
463              
464             # Admin/Operational state for multiple commands
465 10237         18075 $line =~
466 8         39 s/^ ( \| \s? \QAdmin State \E \s+ \| ) ( \s? \QEnabled \E ) ( \N+ ) $/$1.$self->_colorize($2, $GREEN).$3/exx;
467 10237         16651 $line =~
468 1         5 s/^ ( \| \s? \QAdmin State \E \s+ \| ) ( \s? \QDisabled \E ) ( \N+ ) $/$1.$self->_colorize($2, $ORANGE).$3/exx;
469 10237         15486 $line =~
470 10         32 s/^ ( \| \s? \QAdmin State \E \s+ \| ) ( [^|]+ ) ( \N+ ) $/$1.$self->_colorize($2, $RED).$3/exx;
471              
472 10237         16898 $line =~
473 7         29 s/^ ( \| \s? \QOperational State \E \s+ \| ) ( \s? \QUp \E ) ( \N+ ) $/$1.$self->_colorize($2, $GREEN).$3/exx;
474 10237         16552 $line =~
475 1         4 s/^ ( \| \s? \QOperational State \E \s+ \| ) ( \s? \QInitializing \E ) ( \N+ ) $/$1.$self->_colorize($2, $ORANGE).$3/exx;
476 10237         19201 $line =~
477 10         28 s/^ ( \| \s? \QOperational State \E \s+ \| ) ( [^|]+ ) ( \N+ ) $/$1.$self->_colorize($2, $RED).$3/exx;
478              
479             # xcvr show xcvr N/N
480 10237         47886 $line =~ s/ ^ ( \| ) ( \Q Tx Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ ) $/
481 1         5 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5/exx;
482 10237         42133 $line =~ s/ ^ ( \| ) ( \Q Tx Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ ) $/
483 1         7 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5/exx;
484              
485 10237         36485 $line =~ s/ ^ ( \| ) ( \Q Rx Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ ) $/
486 1         6 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5/exx;
487 10237         36066 $line =~ s/ ^ ( \| ) ( \Q Rx Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ ) $/
488 1         6 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5/exx;
489              
490             # xcvr show xcvr status
491 10237         40459 $line =~
492             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Tx Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ ) $/
493 1         5 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5/exx;
494 10237         41103 $line =~
495             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Tx Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ ) $/
496 1         7 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5/exx;
497 10237         38643 $line =~
498             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Rx Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ ) $/
499 1         6 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5/exx;
500 10237         37914 $line =~
501             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Rx Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ ) $/
502 1         6 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5/exx;
503              
504             # ptp show ptp x/x [status]
505 10237         18366 $line =~
506             s/^ ( \| (?: \s Transmitter)? \Q State \E \s+ \| ) ( \Q Enabled \E \s+ ) ( \| ) ( \Q Up \E|\Q Enabled \E )( \N+ ) $/
507 6         34 $1.$self->_colorize($2, $GREEN).$3.$self->_colorize($4, $GREEN).$5/exx;
508 10237         16737 $line =~
509             s/^ ( \| (?: \s Transmitter)? \Q State \E \s+ \| ) ( \Q Enabled \E \s+ ) ( \| ) ( [^|]+ )( \N+ ) $/
510 3         62 $1.$self->_colorize($2, $GREEN).$3.$self->_colorize($4, $RED).$5/exx;
511 10237         17342 $line =~
512             s/^ ( \| (?: \s Transmitter)? \Q State \E \s+ \| ) ( \Q Disabled \E \s+ ) ( \| ) ( [^|]+ )( \N+ ) $/
513 2         10 $1.$self->_colorize($2, $ORANGE).$3.$self->_colorize($4, $ORANGE).$5/exx;
514              
515 10237         47510 $line =~
516             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ )$/
517 1         8 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5.$self->_colorize($6, $RED).$7/exx;
518 10237         45876 $line =~
519             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ )$/
520 1         6 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5.$self->_colorize($6, $INFO).$7/exx;
521 10237         42978 $line =~
522             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ )$/
523 1         5 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $RED).$7/exx;
524 10237         46847 $line =~
525             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ )$/
526 4         24 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $INFO).$7/exx;
527              
528 10237         46310 $line =~
529             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Aggregate Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ )$/
530 0         0 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5.$self->_colorize($6, $RED).$7/exx;
531 10237         47456 $line =~
532             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Aggregate Power (dBm)\E \s* ) ( \| ) ( \s+ $VERYLL \s+ ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ )$/
533 0         0 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $RED).$5.$self->_colorize($6, $INFO).$7/exx;
534 10237         44089 $line =~
535             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Aggregate Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \| ) ( \s+ $VERYLL \s+ ) ( \N+ )$/
536 0         0 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $RED).$7/exx;
537 10237         44636 $line =~
538             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Actual Aggregate Power (dBm)\E \s* ) ( \| ) ( \s+ $LIGHT \s+ ) ( \| ) ( \s+ $LIGHT \s+ ) ( \N+ )$/
539 1         5 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $INFO).$7/exx;
540              
541 10237         47171 $line =~
542             s/ ^ ( \| \s+ [0-9]* \s* \| ) ( \Q Span Loss (dB)\E \s* ) ( \| ) ( \s+ $NUM \s+ ) ( \| ) ( \s+ $NUM \s+ ) ( \| )$/
543 1         7 $1.$self->_colorize($2, $INFO).$3.$self->_colorize($4, $INFO).$5.$self->_colorize($6, $INFO).$7/exx;
544              
545 10237         44506 $line =~
546             s/^ ( \| \s+ \| ) ( \Q Optical Return Loss \E \( \QdB\E \) ) ( \s+ \| \s+ ) ( $GOODRETURNLOSS ) (\s+ \| ) $/
547 1         8 $1.$self->_colorize($2, $GREEN).$3.$self->_colorize($4, $GREEN).$5/exx;
548 10237         41951 $line =~
549             s/^ ( \| \s+ \| ) ( \Q Optical Return Loss \E \( \QdB\E \) ) ( \s+ \| \s+ ) ( $WARNRETURNLOSS ) (\s+ \| ) $/
550 1         7 $1.$self->_colorize($2, $ORANGE).$3.$self->_colorize($4, $ORANGE).$5/exx;
551 10237         41425 $line =~
552             s/^ ( \| \s+ \| ) ( \Q Optical Return Loss \E \( \QdB\E \) ) ( \s+ \| \s+ ) ( $BADRETURNLOSS ) (\s+ \| ) $/
553 1         4 $1.$self->_colorize($2, $RED).$3.$self->_colorize($4, $RED).$5/exx;
554              
555             # alarm show
556 10237         50697 $line =~ s/ ^ ( \| ) ( \s* [0-9]* ) ( \| ) ( \s+ ) ( \| ) ( [^|]+ ) ( \| ) ( \s+ [0-9]+ )
557             ( \| ) ( \s* $BIGALARMS|$LITTLEALARMS \s* ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
558             ( \| ) ( \s+ $BIGOVERRIDES \s+ ) ( \| ) $/
559 3         9 $1.$self->_colorize($2, $ORANGE).
560             $3.$self->_colorize($4, $ORANGE).
561             $5.$self->_colorize($6, $ORANGE).
562             $7.$self->_colorize($8, $ORANGE).
563             $9.$self->_colorize($10, $ORANGE).
564             $11.$self->_colorize($12, $ORANGE).
565             $13.$self->_colorize($14, $ORANGE).
566             $15.$self->_colorize($16, $ORANGE).
567             $17/exx;
568 10237         42739 $line =~ s/ ^ ( \| ) ( \s* [0-9]* ) ( \| ) ( \s+ ) ( \| ) ( [^|]+ ) ( \| ) ( \s+ [0-9]+ )
569             ( \| ) ( \s* $BIGALARMS|$LITTLEALARMS \s* ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $/
570 1         5 $1.$self->_colorize($2, $RED).
571             $3.$self->_colorize($4, $RED).
572             $5.$self->_colorize($6, $RED).
573             $7.$self->_colorize($8, $RED).
574             $9.$self->_colorize($10, $RED).
575             $11.$self->_colorize($12, $RED).
576             $13.$self->_colorize($14, $RED).
577             $15.$self->_colorize($16, $RED).
578             $17/exx;
579 10237         40233 $line =~
580             s/ ^ ( \| ) ( \s+ [0-9]* ) ( \| ) ( \s+ \QY\E \s* ) ( \| ) ( [^|]+ ) ( \| ) ( \s+ [0-9]+ )
581             ( \| ) ( \s* $BIGALARMS \s* ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $/
582 8         23 $1.$self->_colorize($2, $ORANGE).
583             $3.$self->_colorize($4, $ORANGE).
584             $5.$self->_colorize($6, $ORANGE).
585             $7.$self->_colorize($8, $ORANGE).
586             $9.$self->_colorize($10, $ORANGE).
587             $11.$self->_colorize($12, $ORANGE).
588             $13.$self->_colorize($14, $ORANGE).
589             $15.$self->_colorize($16, $ORANGE).
590             $17/exx;
591 10237         41856 $line =~
592             s/ ^ ( \| ) ( \s+ [0-9]* ) ( \| ) ( \s+ \QY\E \s* ) ( \| ) ( [^|]+ ) ( \| ) ( \s+ [0-9]+ )
593             ( \| ) ( \s* $LITTLEALARMS \s* ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $/
594 1         5 $1.$self->_colorize($2, $INFO).
595             $3.$self->_colorize($4, $INFO).
596             $5.$self->_colorize($6, $INFO).
597             $7.$self->_colorize($8, $INFO).
598             $9.$self->_colorize($10, $INFO).
599             $11.$self->_colorize($12, $INFO).
600             $13.$self->_colorize($14, $INFO).
601             $15.$self->_colorize($16, $INFO).
602             $17/exx;
603              
604             # Errors
605 10237         18193 $line =~ s/ ^ ( \QSHELL \E \S+ \Q FAILURE\E \N+ ) $/$self->_colorize($1, $RED)/exx;
  2         12  
606              
607             # ptp show
608 10237         16964 $line =~
609             s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( \Q Ena \E ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
610             ( \| ) ( [^|]+ ) ( \| ) ( \QUp\E \s+ ) ( \| ) ( [^|]+ )
611             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) /
612 15         40 $1.$self->_colorize($2, $GREEN).
613             $3.$self->_colorize($4, $GREEN).
614             $5.$self->_colorize($6, $GREEN).
615             $7.$self->_colorize($8, $GREEN).
616             $9.$self->_colorize($10, $GREEN).
617             $11.$self->_colorize($12, $GREEN).
618             $13.$self->_colorize($14, $GREEN).
619             $15.$self->_colorize($16, $GREEN).
620             $17.$self->_colorize($18, $GREEN).
621             $19.$self->_colorize($20, $GREEN).
622             $21.$self->_colorize($22, $GREEN).
623             $23/exx;
624 10237         18237 $line =~
625             s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( \Q Ena \E ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
626             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
627             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) /
628 8         21 $1.$self->_colorize($2, $RED).
629             $3.$self->_colorize($4, $RED).
630             $5.$self->_colorize($6, $RED).
631             $7.$self->_colorize($8, $RED).
632             $9.$self->_colorize($10, $RED).
633             $11.$self->_colorize($12, $RED).
634             $13.$self->_colorize($14, $RED).
635             $15.$self->_colorize($16, $RED).
636             $17.$self->_colorize($18, $RED).
637             $19.$self->_colorize($20, $RED).
638             $21.$self->_colorize($22, $RED).
639             $23/exx;
640 10237         20915 $line =~
641             s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( \Q Dis \E ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
642             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
643             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) /
644 0         0 $1.$self->_colorize($2, $ORANGE).
645             $3.$self->_colorize($4, $ORANGE).
646             $5.$self->_colorize($6, $ORANGE).
647             $7.$self->_colorize($8, $ORANGE).
648             $9.$self->_colorize($10, $ORANGE).
649             $11.$self->_colorize($12, $ORANGE).
650             $13.$self->_colorize($14, $ORANGE).
651             $15.$self->_colorize($16, $ORANGE).
652             $17.$self->_colorize($18, $ORANGE).
653             $19.$self->_colorize($20, $ORANGE).
654             $21.$self->_colorize($22, $ORANGE).
655             $23/exx;
656              
657             # module show
658 10237         17571 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( \Q Enabled \E \s+ )
659             ( \| ) ( [^|]+ ) ( \| ) ( \Q Up \E \s+ ) ( \| ) ( [^|]+ ) ( \| ) $ /
660 1         4 $1.$self->_colorize($2, $GREEN).
661             $3.$self->_colorize($4, $GREEN).
662             $5.$self->_colorize($6, $GREEN).
663             $7.$self->_colorize($8, $GREEN).
664             $9.$self->_colorize($10, $GREEN).
665             $11.$self->_colorize($12, $GREEN).
666             $13/exx;
667 10237         16734 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( \Q Enabled \E \s+ )
668             ( \| ) ( [^|]+ ) ( \| ) ( \Q Initializing \E \s+ ) ( \| ) ( [^|]+ ) ( \| ) $ /
669 1         5 $1.$self->_colorize($2, $ORANGE).
670             $3.$self->_colorize($4, $ORANGE).
671             $5.$self->_colorize($6, $ORANGE).
672             $7.$self->_colorize($8, $ORANGE).
673             $9.$self->_colorize($10, $ORANGE).
674             $11.$self->_colorize($12, $ORANGE).
675             $13/exx;
676 10237         16882 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( \Q Enabled \E \s+ )
677             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $ /
678 1         3 $1.$self->_colorize($2, $RED).
679             $3.$self->_colorize($4, $RED).
680             $5.$self->_colorize($6, $RED).
681             $7.$self->_colorize($8, $RED).
682             $9.$self->_colorize($10, $RED).
683             $11.$self->_colorize($12, $RED).
684             $13/exx;
685 10237         17683 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( \Q Disabled \E \s+ )
686             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) $ /
687 0         0 $1.$self->_colorize($2, $ORANGE).
688             $3.$self->_colorize($4, $ORANGE).
689             $5.$self->_colorize($6, $ORANGE).
690             $7.$self->_colorize($8, $ORANGE).
691             $9.$self->_colorize($10, $ORANGE).
692             $11.$self->_colorize($12, $ORANGE).
693             $13/exx;
694              
695             # chassis power show
696 10237         17723 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( \s+ \QDisabled\E \s+ ) ( \| ) ( [^|]+ )
697             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
698             ( \| ) ( [^|]+ ) ( \| ) $ /
699 1         6 $1.$self->_colorize($2, $ORANGE).
700             $3.$self->_colorize($4, $ORANGE).
701             $5.$self->_colorize($6, $ORANGE).
702             $7.$self->_colorize($8, $ORANGE).
703             $9.$self->_colorize($10, $ORANGE).
704             $11.$self->_colorize($12, $ORANGE).
705             $13.$self->_colorize($14, $ORANGE).
706             $15/exx;
707 10237         18536 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( \s+ \QEnabled\E \s+ ) ( \| ) ( \s+ \QUp\E \s+ )
708             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
709             ( \| ) ( [^|]+ ) ( \| ) $ /
710 1         7 $1.$self->_colorize($2, $GREEN).
711             $3.$self->_colorize($4, $GREEN).
712             $5.$self->_colorize($6, $GREEN).
713             $7.$self->_colorize($8, $GREEN).
714             $9.$self->_colorize($10, $GREEN).
715             $11.$self->_colorize($12, $GREEN).
716             $13.$self->_colorize($14, $GREEN).
717             $15/exx;
718 10237         16079 $line =~ s/ ^ ( \| ) ( [^|]+ ) ( \| ) ( \s+ \QEnabled\E \s+ ) ( \| ) ( \s+ \QFaulted\E \s+ )
719             ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ ) ( \| ) ( [^|]+ )
720             ( \| ) ( [^|]+ ) ( \| ) $ /
721 1         6 $1.$self->_colorize($2, $RED).
722             $3.$self->_colorize($4, $RED).
723             $5.$self->_colorize($6, $RED).
724             $7.$self->_colorize($8, $RED).
725             $9.$self->_colorize($10, $RED).
726             $11.$self->_colorize($12, $RED).
727             $13.$self->_colorize($14, $RED).
728             $15/exx;
729              
730             # Success for dumps
731 10237         15842 $line =~ s/^ ( \QSuccess! Preparing state dump...\E \s* ) $/ $self->_colorize($1, $GREEN)/exx;
  1         5  
732              
733             # Logout message
734 10237         15305 $line =~ s/^ ( \QTerminal will be disconnected in 1 minute if it remains inactive.\E )/
735 1         6 $self->_colorize($1, $RED)/exx;
736              
737             # Warning message
738 10237         15350 $line =~ s/^ (\QWARNING:\E) / $self->_colorize($1, $ORANGE)/exx;
  1         4  
739 10237         15505 $line =~ s/^ (\Q You cannot abort the restart operation once it has started.\E ) $/
740 1         4 $self->_colorize($1, $ORANGE)/exx;
741              
742             # Error messages when something isn't disabled first
743 10237         15432 $line =~ s/^ (\QERROR: \E .* disabled ) $/ $self->_colorize($1, $RED)/exx;
  1         6  
744              
745              
746 10237         27610 return $line;
747             }
748              
749 81     81   130 sub _ipv4ify ( $self, $ip ) {
  81         161  
  81         247  
  81         144  
750 81         336 my ( $subnet, $len ) = split "/", $ip;
751 81   100     463 $len //= 32;
752              
753 81         236 my (@oct) = split /\./, $subnet;
754 81         160 my $val = 0;
755 81         225 foreach my $i (@oct) {
756 324         481 $val *= 256;
757 324         772 $val += $i;
758             }
759 81         170 $val = $val * 256 + $len;
760 81         272 my $color = $BGCOLORS[ $val % scalar(@BGCOLORS) ];
761 81         244 return $self->_colorize( $ip, $color );
762             }
763              
764 17     17   36 sub _ipv6ify ( $self, $ip ) {
  17         34  
  17         49  
  17         27  
765 17         74 my ( $subnet, $len ) = split "/", $ip;
766 17   100     90 $len //= 128;
767              
768 17         57 my ( $first, $second ) = split "::", $subnet;
769              
770 17         51 my (@fparts) = split /:/, $first;
771 17         121 push( @fparts, ( '', '', '', '', '', '', '', '' ) );
772 17         119 @fparts = @fparts[ 0 .. 7 ];
773              
774 17         32 my (@sparts);
775 17 100       52 if ( defined($second) ) {
776 15         60 (@sparts) = reverse split /:/, $second;
777             }
778 17         70 push( @sparts, ( '', '', '', '', '', '', '', '' ) );
779 17         74 @sparts = reverse @sparts[ 0 .. 7 ];
780              
781 17         32 my $val = 0;
782 17         108 for my $i ( 0 .. 7 ) {
783 136         244 $val *= 16;
784 136         281 $val += hex( $fparts[$i] . $sparts[$i] );
785             }
786 17         27 $val *= 32;
787 17         41 $val += $len;
788              
789 17         55 my $color = $BGCOLORS[ $val % scalar(@BGCOLORS) ];
790 17         68 return $self->_colorize( $ip, $color );
791             }
792              
793 8331     8331   12406 sub _numerify ( $self, $num ) {
  8331         12530  
  8331         17827  
  8331         11609  
794 8331         12848 my $output = "";
795 8331         20787 while ( length($num) > 3 ) {
796 1116         2763 $output = substr( $num, -3 ) . $output;
797 1116         2483 $num = substr( $num, 0, length($num) - 3 );
798              
799 1116         1679 my $next3;
800 1116 100       2540 if ( length($num) > 3 ) {
801 420         792 $next3 = substr( $num, -3 );
802 420         764 $num = substr( $num, 0, length($num) - 3 );
803             } else {
804 696         1214 $next3 = $num;
805 696         1369 $num = '';
806             }
807 1116         2728 $output = $self->_highlight($next3) . $output;
808             }
809 8331         13790 $output = $num . $output;
810              
811 8331         37484 return $output;
812             }
813              
814 1116     1116   1688 sub _highlight ( $self, $str ) {
  1116         1621  
  1116         1756  
  1116         1598  
815 1116         4484 return "\e[4m$str\e[24m";
816             }
817              
818 2367     2367   3621 sub _colorize ( $self, $text, $color ) {
  2367         3564  
  2367         5101  
  2367         4001  
  2367         3171  
819 2367         5762 $text = $color . $text;
820 2367         10233 $text =~ s/ \Q$RESET\E / $color /;
821 2367         4385 $text = $text . $RESET;
822 2367         8601 return $text;
823             }
824              
825             __END__
826              
827             =pod
828              
829             =encoding UTF-8
830              
831             =head1 NAME
832              
833             App::RouterColorizer - Colorize router CLI output
834              
835             =head1 VERSION
836              
837             version 1.242880
838              
839             =head1 DESCRIPTION
840              
841             This module colorizes the output of router output, using.
842              
843             The output will be colorized based on detection of key strings as they
844             might be sent from Arista, Cisco, Juniper, and VyOS routers/switches and
845             Ciena WDM devices. It may also work on other router outputs, but these
846             have not been used for development.
847              
848             =head1 METHODS
849              
850             =head2 new
851              
852             my $colorizer = App::RouterColorizer->new()
853              
854             Instnatiates a new instance of C<App::RouterColorizer>.
855              
856             =head2 format_text
857              
858             $colorized_text = $colorizer->format_text($text)
859              
860             This method colorizes/formats the text, as provided in C<$text>.
861              
862             /usr/bin/ssh router.example.com | router-colorizer.pl
863              
864             =head1 COLOR CODING
865              
866             Color coding is used, which assumes a black background on your terminal.
867             The colors used indicate different kinds of output. Note that most lines
868             of output are not colorized, only "important" (as defined by me!) lines
869             are colorized.
870              
871             =over 4
872              
873             =item C<green>
874              
875             Green text is used to signify "good" values. For instance, in the output
876             from C<show interfaces> on an Arista router, seeing lines indicating the
877             circuit is "up" and not showing errors will show in green.
878              
879             =item C<orange>
880              
881             Orange text is used to show things that aren't "good" but also aren't "bad."
882             For instance, administratively down interfaces in C<show interfaces status>
883             will typically show as orange.
884              
885             =item C<red>
886              
887             Red text indicates error conditions, such as errors being seen on the
888             output of C<show interfaces>.
889              
890             =item C<cyan>
891              
892             Cyan text indicates potentially important text, seperated out from text
893             that is not-so-important. For instance, in C<show bgp neighbors>, cyan
894             is used to point out lines indicating which route map is being used.
895              
896             =back
897              
898             =head1 IP Address Colorization
899              
900             IP addresses are also colorized. These are colorized one of several colors,
901             all with a colorized background, based on the IP/CIDR address. Thus, an
902             IP address like C<1.2.3.4> will always be the same color, which should make
903             it easier to spot potential transposition or copy mistakes (if it shows up
904             sometimes as white-on-blue, but other times as black-on-red, it's not the
905             same address!).
906              
907             =head1 Number Grouping/Underlines
908              
909             The progarm also underlines alternating groups of 3 digits as they appear
910             in digit strings. This is used to assist people who might have dyslexia
911             or other visual processing differences, to allow them to quickly determine
912             if 1000000 is 1,000,000 or 10,000,000.
913              
914             =head1 Methods
915              
916             =head1 BUGS
917              
918             None known, however it is certainly possible that I am less than perfect. If
919             you find any bug you believe has security implications, I would greatly
920             appreciate being notified via email sent to C<jmaslak@antelope.net> prior
921             to public disclosure. In the event of such notification, I will attempt to
922             work with you to develop a plan for fixing the bug.
923              
924             All other bugs can be reported via email to C<jmaslak@antelope.net> or by
925             using the GitHub issue tracker
926             at L<https://github.com/jmaslak/App-RouterColorizer/issues>
927              
928             =head1 AUTHOR
929              
930             Joelle Maslak <jmaslak@antelope.net>
931              
932             =head1 COPYRIGHT AND LICENSE
933              
934             This software is copyright (c) 2021-2024 by Joelle Maslak.
935              
936             This is free software; you can redistribute it and/or modify it under
937             the same terms as the Perl 5 programming language system itself.
938              
939             =cut