1package nxos;
2##
3## @PACKAGE@ @VERSION@
4@copyright@
5#
6#  RANCID - Really Awesome New Cisco confIg Differ
7#
8#  nxos.pm - Cisco NX-OS rancid procedures
9
10use 5.010;
11use strict 'vars';
12use warnings;
13no warnings 'uninitialized';
14require(Exporter);
15our @ISA = qw(Exporter);
16
17use rancid @VERSION@;
18
19our $proc;
20our $type;				# device model, from ShowVersion
21
22our $C0;				# output formatting control
23our $E0;
24our $H0;
25our $DO_SHOW_VLAN;
26
27@ISA = qw(Exporter rancid main);
28#XXX @Exporter::EXPORT = qw($VERSION @commandtable %commands @commands);
29
30# load-time initialization
31sub import {
32    0;
33}
34
35# post-open(collection file) initialization
36sub init {
37    $proc = "";
38    $type = undef;			# device model, from ShowVersion
39
40    $C0 = 0;				# output formatting control
41    $E0 = 0;
42    $H0 = 0;
43    $DO_SHOW_VLAN = 1;
44
45    # add content lines and separators
46    ProcessHistory("","","","!RANCID-CONTENT-TYPE: $devtype\n!\n");
47    ProcessHistory("COMMENTS","keysort","B0","!\n");
48    ProcessHistory("COMMENTS","keysort","D0","!\n");
49    ProcessHistory("COMMENTS","keysort","F0","!\n");
50    ProcessHistory("COMMENTS","keysort","G0","!\n");
51
52    0;
53}
54
55# main loop of input of device output
56sub inloop {
57    my($INPUT, $OUTPUT) = @_;
58    my($cmd, $rval);
59
60TOP: while(<$INPUT>) {
61	tr/\015//d;
62
63	if (/[>#]\s?exit$/) {
64	    print STDERR ("$host: found exit\n") if ($debug);
65	    $clean_run = 1;
66	    last;
67	}
68	if (/^Error:/) {
69	    print STDOUT ("$host clogin error: $_");
70	    print STDERR ("$host clogin error: $_") if ($debug);
71	    $clean_run = 0;
72	    last;
73	}
74	while (/#\s*($cmds_regexp)\s*$/) {
75	    $cmd = $1;
76	    if (!defined($prompt)) {
77		$prompt = ($_ =~ /^([^#]+#)/)[0];
78		$prompt =~ s/([][}{)(\\])/\\$1/g;
79		print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
80	    }
81	    print STDERR ("HIT COMMAND:$_") if ($debug);
82	    if (! defined($commands{$cmd})) {
83		print STDERR "$host: found unexpected command - \"$cmd\"\n";
84		$clean_run = 0;
85		last TOP;
86	    }
87	    if (! defined(&{$commands{$cmd}})) {
88		printf(STDERR "$host: undefined function - \"%s\"\n",
89		       $commands{$cmd});
90		$clean_run = 0;
91		last TOP;
92	    }
93	    $rval = &{$commands{$cmd}}($INPUT, $OUTPUT, $cmd);
94	    delete($commands{$cmd});
95	    if ($rval == -1) {
96		$clean_run = 0;
97		print STDERR ("$host: $cmd failed: $rval\n") if ($debug);
98		last TOP;
99	    }
100	    if (/[>#]\s?exit$/) {
101		print STDERR ("$host: found exit\n") if ($debug);
102		$clean_run = 1;
103		last TOP;
104	    }
105	}
106    }
107}
108
109# This routine parses "show version"
110sub ShowVersion {
111    my($INPUT, $OUTPUT, $cmd) = @_;
112    print STDERR "    In ShowVersion: $_" if ($debug);
113
114    while (<$INPUT>) {
115	tr/\015//d;
116	last if (/^$prompt/);
117	next if (/^(\s*|\s*$cmd\s*)$/);
118	return(1) if /Line has invalid autocommand /;
119	return(1) if /(Invalid input detected|Type help or )/;
120	return(-1) if (/\% Invalid command at /);
121	return(-1) if (/No token match at /);	# 1000v
122	return(-1) if (/\% Permission denied/);
123	return(-1) if (/command authorization failed/i);
124
125	if (/^Cisco (Nexus|Storage Area Networking) Operating System/) { $type = "NXOS";}
126	if (/^Software$/) {
127	    while (<$INPUT>) {
128		tr/\015//d;
129		last if (/^$prompt/);
130		next if (/^\s*$cmd\s*$/);
131
132		if (/^$/) {
133		    goto EndSoftware;
134		}
135		/\s*([^:]*:)\s*(.*)$/ &&
136		    ProcessHistory("COMMENTS","keysort","C1",
137				   "!Software: $1 $2\n") && next;
138	    }
139	}
140EndSoftware:
141	if (/^Hardware$/) {
142	    while (<$INPUT>) {
143		tr/\015//d;
144		last if (/^$prompt/);
145		next if (/^\s*$cmd\s*$/);
146
147		if (/^\s*cisco (Nexus[ \d]+) /) {
148		    # cisco Nexus7000 C7010 (10 Slot) Chassis ("Supervisor module-1X")
149		    # cisco Nexus7000 C7018 (18 Slot) Chassis ("Supervisor module-1X")
150		    # cisco Nexus5548 Chassis ("O2 32X10GE/Modular Supervisor")
151		    # cisco Nexus 4000 Chassis ("20x10GE/supervisor")
152		    $proc = $1;
153		}
154
155		if (/^$/) {
156		    goto EndHardware;
157		}
158		if (/^\s*(.*) CPU\s*with (\d*) kB(.*)$/) {
159		    my($tmp) = int($2 / 1024);
160		    ProcessHistory("COMMENTS","keysort","A2",
161				   "!Hardware: $1 CPU with $tmp MB$3\n");
162		    next;
163		}
164		if (/^\s*(.*)\s*with (\d*) kB(.*)$/) {
165		    my($tmp) = int($2 / 1024);
166		    ProcessHistory("COMMENTS","keysort","A2",
167				   "!Hardware: $1with $tmp MB$3\n");
168		    next;
169		}
170		/^\s*(.*)$/ &&
171		    ProcessHistory("COMMENTS","keysort","A2",
172				   "!Hardware: $1\n") && next;
173	    }
174	}
175EndHardware:
176	if (/^\s+(bootflash|slot0):\s+(\d+) kB(.*)$/) {
177	    my($tmp) = int($2 / 1024);
178	    ProcessHistory("COMMENTS","keysort","B1",
179			   "!Memory: $1: $tmp MB$3\n");
180	    next;
181	}
182
183    }
184
185    print STDERR "TYPE = $type\n" if ($debug);
186    # XXX $proc is never set
187    ProcessHistory("COMMENTS","keysort","A1",
188		   "!Chassis type: $proc - a $type router\n");
189    ProcessHistory("COMMENTS","keysort","B0", "!\n");
190    ProcessHistory("COMMENTS","keysort","C0", "!\n");
191    ProcessHistory("COMMENTS","","", "!\n");
192    return(0);
193}
194
195# This routine parses "show version build-info"
196sub ShowVersionBuild {
197    my($INPUT, $OUTPUT, $cmd) = @_;
198    print STDERR "    In ShowVersionBuild: $_" if ($debug);
199
200    while (<$INPUT>) {
201	tr/\015//d;
202	last if (/^$prompt/);
203	next if (/^(\s*|\s*$cmd\s*)$/);
204	return(1) if /Line has invalid autocommand /;
205	return(1) if /(Invalid input detected|Type help or )/;
206	return(1) if (/\% Invalid command at /);
207	return(1) if (/No token match at /);	# 1000v
208	return(-1) if (/\% Permission denied/);
209	return(-1) if (/command authorization failed/i);
210
211	/^Built By / && ProcessHistory("COMMENTS","","", "!Build: $_");
212	/^On Date / && ProcessHistory("COMMENTS","","", "!Build: $_");
213	/^From Tree / && ProcessHistory("COMMENTS","","", "!Build: $_");
214	/^Base Tag / && ProcessHistory("COMMENTS","","", "!Build: $_");
215	/^Release for / && ProcessHistory("COMMENTS","","", "!Build: $_");
216    }
217    ProcessHistory("COMMENTS","","","!\n");
218    return(0);
219}
220
221# This routine parses "show license *"
222sub ShowLicense {
223    my($INPUT, $OUTPUT, $cmd) = @_;
224    print STDERR "    In ShowLicense: $_" if ($debug);
225
226    while (<$INPUT>) {
227	tr/\015//d;
228	last if (/^$prompt/);
229	next if (/^(\s*|\s*$cmd\s*)$/);
230	return(1) if /(Invalid input detected|Type help or )/;
231	return(1) if (/\% Invalid command at /);
232	return(-1) if (/No token match at /);	# 1000v
233	return(-1) if (/\% Permission denied/);
234	return(-1) if (/command authorization failed/i);
235
236	/^-+$/ && next;		# Skip lines of all dashes.
237	s/ Grace .+$/ Grace/;	# Drop anything after Grace.
238	ProcessHistory("COMMENTS","","", "!LIC: $_");
239    }
240    ProcessHistory("COMMENTS","","","!\n");
241    return(0);
242}
243
244# This routine parses "show system redundancy status"
245sub ShowRedundancy {
246    my($INPUT, $OUTPUT, $cmd) = @_;
247    print STDERR "    In ShowRedundancy: $_" if ($debug);
248
249    while (<$INPUT>) {
250	tr/\015//d;
251	last if (/^$prompt/);
252	next if (/^(\s*|\s*$cmd\s*)$/);
253	return(1) if /Line has invalid autocommand /;
254	return(1) if /(Invalid input detected|Type help or )/;
255	return(1) if (/\% Invalid command at /);
256	return(1) if (/.* command is not support/i);
257	return(-1) if (/No token match at /);	# 1000v
258	return(-1) if (/\% Permission denied/);
259	return(-1) if (/command authorization failed/i);
260
261	s/ +$//;	# Drop trailing ' '
262	ProcessHistory("COMMENTS","","","!Red: $_");
263    }
264    ProcessHistory("COMMENTS","","","!\n");
265    return(0);
266}
267
268# This routine parses "show environment"
269sub ShowEnv {
270    my($INPUT, $OUTPUT, $cmd) = @_;
271    print STDERR "    In ShowEnv: $_" if ($debug);
272
273    while (<$INPUT>) {
274	tr/\015//d;
275	last if (/^$prompt/);
276	next if (/^(\s*|\s*$cmd\s*)$/);
277	next if (/^\s*\^\s*$/);
278	return(1) if /Line has invalid autocommand /;
279	return(1) if /(Invalid input detected|Type help or )/;
280	return(1) if (/\% Invalid command at /);
281	return(1) if (/ .* command is not support/i);
282	return(1) if (/No token match at /);	# 1000v
283	return(-1) if (/\% Permission denied/);
284	return(-1) if (/command authorization failed/i);
285
286	s/ +$//;	# Drop trailing ' '
287	next if (/Fan Zone Speed:/);
288	if (/(control temperature|monitor temperature)/i) {
289	    ProcessHistory("COMMENTS","","","!Env: $_");
290	    while (<$INPUT>) {
291		tr/\015//d;
292		goto EndShowEnv if (/^$prompt/);
293		if (/(.*\s+\d+\s+\d+\s+)(\d+)(.*$)/) {
294		    $_ = sprintf("%s%-". length($2)."s%s\n", $1, "", $3);
295		}
296		ProcessHistory("COMMENTS","","","!Env: $_");
297		last if (/^\s*$/);
298	    }
299	    next;
300	}
301	ProcessHistory("COMMENTS","","","!Env: $_");
302    }
303EndShowEnv:
304    ProcessHistory("COMMENTS","","","!\n");
305    return(0);
306}
307
308# This routine parses "show environment temperature"
309sub ShowEnvTemp {
310    my($INPUT, $OUTPUT, $cmd) = @_;
311    print STDERR "    In ShowEnvTemp: $_" if ($debug);
312
313    while (<$INPUT>) {
314	tr/\015//d;
315	last if (/^$prompt/);
316	next if (/^(\s*|\s*$cmd\s*)$/);
317	return(1) if /Line has invalid autocommand /;
318	return(1) if /(Invalid input detected|Type help or )/;
319	return(1) if (/No token match at /);	# 1000v
320	return(1) if (/\% Invalid command at /);# 1000v has no support
321	return(-1) if (/\% Permission denied/);
322	return(-1) if (/command authorization failed/i);
323
324	s/ +$//;	# Drop trailing spaces
325
326# Cut out CurTemp - drop the 2nd to last field.
327#--------------------------------------------------------------------
328#Module   Sensor        MajorThresh   MinorThres   CurTemp     Status
329#                       (Celsius)     (Celsius)    (Celsius)
330#5        Outlet1 (s1)    125             125         33         Ok
331#5        QEng1Sn1(s10)   115             105         39         Ok
332	if (/(control temperature|monitor temperature)/i) {
333	    ProcessHistory("COMMENTS","","","!Env: $_");
334	    while (<$INPUT>) {
335		tr/\015//d;
336		goto EndShowEnvTemp if (/^$prompt/);
337		if (/(.*\s+\d+\s+\d+\s+)(\d+)(.*$)/) {
338		    $_ = sprintf("%s%-". length($2)."s%s\n", $1, "", $3);
339		}
340		ProcessHistory("COMMENTS","","","!Env: $_");
341		last if (/^\s*$/);
342	    }
343	    next;
344	} else {
345	    next if (/INTAKE/);
346	    next if (/ASIC/);
347	    s/^(.+\s)(\S+\s+)(\S+\s*)$/$1$3/;
348	    s/ +$//;	# Drop trailing ' '
349	    ProcessHistory("COMMENTS","","","!Env: $_");
350	}
351    }
352EndShowEnvTemp:
353    ProcessHistory("COMMENTS","","","!\n");
354    return(0);
355}
356
357# This routine parses "show environment power"
358sub ShowEnvPower {
359    my($INPUT, $OUTPUT, $cmd) = @_;
360    print STDERR "    In ShowEnvPower: $_" if ($debug);
361
362    while (<$INPUT>) {
363	tr/\015//d;
364	last if (/^$prompt/);
365	next if (/^(\s*|\s*$cmd\s*)$/);
366	next if (/^\s*\^\s*$/);
367	return(1) if /Line has invalid autocommand /;
368	return(1) if /(Invalid input detected|Type help or )/;
369	return(1) if (/\% Invalid command at /);
370	return(1) if (/No token match at /);	# 1000v
371	return(-1) if (/\% Permission denied/);
372	return(-1) if (/command authorization failed/i);
373
374# Filter Actual Output/Draw.
375#Power                              Actual        Total
376#Supply    Model                    Output     Capacity    Status
377#                                 (Watts )     (Watts )
378#-------  -------------------  -----------  -----------  --------------
379#1        ------------                 0 W          0 W     Absent
380#3                                   749 W       5480 W     Ok
381#                                  Actual        Power
382#Module    Model                     Draw    Allocated    Status
383#                                 (Watts )     (Watts )
384#-------  -------------------  -----------  -----------  --------------
385#2        NURBURGRING                N/A          573 W    Powered-Up
386#fan1                                N/A          720 W    Powered-Up
387#
388# ELSE
389#
390# nexus# sh environment power
391# Power Supply:
392# Voltage: 12 Volts
393# Power                             Actual      Actual        Total
394# Supply    Model                   Output      Input      Capacity    Status
395#                                 (Watts )    (Watts )     (Watts )
396# -------  -------------------  ----------  ----------  ----------
397# --------------
398# 1        N9K-PAC-650W-B             65 W        76 W       649 W     Ok
399# 2        N9K-PAC-650W-B             70 W        84 W       649 W     Ok
400#
401#
402# Power Usage Summary:
403# --------------------
404# Power Supply redundancy mode (configured)                PS-Redundant
405# Power Supply redundancy mode (operational)               PS-Redundant
406#
407# Total Power Capacity (based on configured mode)             649.92 W
408# Total Grid-A (first half of PS slots) Power Capacity        649.92 W
409# Total Grid-B (second half of PS slots) Power Capacity       649.92 W
410# Total Power of all Inputs (cumulative)                     1299.84 W
411# Total Power Output (actual draw)                            136.00 W
412# Total Power Input (actual draw)                             161.00 W
413# Total Power Allocated (budget)                                N/A
414# Total Power Available for additional modules                  N/A
415#
416# nexus#
417	if (/(.* +)(\d+ W +\d+ W)( +\d+ W.+)/) {
418	    $_ = sprintf("%s%-". length($2)."s%s\n", $1, "", $3);
419	} elsif (/(.* +)(\d+(\.\d+)? W)( +\d+(\.\d+)? W.+)/) {
420	    $_ = sprintf("%s%-". length($2)."s%s\n", $1, "", $4);
421	}
422
423	/actual draw/ && next;	# Drop changing total power output.
424
425	s/ +$//;	# Drop trailing ' '
426	ProcessHistory("COMMENTS","","","!Env: $_");
427    }
428    ProcessHistory("COMMENTS","","","!\n");
429    return(0);
430}
431
432# This routine parses "show boot"
433sub ShowBoot {
434    my($INPUT, $OUTPUT, $cmd) = @_;
435    print STDERR "    In ShowBoot: $_" if ($debug);
436
437    while (<$INPUT>) {
438	tr/\015//d;
439	last if (/^$prompt/);
440	next if (/^(\s*|\s*$cmd\s*)$/);
441	return(1) if /^\s*\^\s*$/;
442	return(1) if /Line has invalid autocommand /;
443	return(1) if /(Invalid input detected|Type help or )/;
444	return(1) if /Ambiguous command/i;
445	return(-1) if (/\% Invalid command at /);
446	return(-1) if (/No token match at /);	# 1000v
447	return(-1) if (/\% Permission denied/);
448	return(-1) if (/command authorization failed/i);
449
450	s/ variable = / = /;
451	ProcessHistory("COMMENTS","","","!Variable: $_");
452    }
453    ProcessHistory("COMMENTS","","","!\n");
454    return(0);
455}
456
457# This routine parses "dir /all ((disk|slot)N|bootflash|nvram):"
458sub DirSlotN {
459    my($INPUT, $OUTPUT, $cmd) = @_;
460    print STDERR "    In DirSlotN: $_" if ($debug);
461    my($free);
462
463    my($dev) = (/\s([^\s]+):/);
464
465    while (<$INPUT>) {
466	tr/\015//d;
467	last if (/^$prompt/);
468	next if (/^(\s*|\s*$cmd\s*)$/);
469	return(1) if /^\s*\^\s*$/;
470	return(1) if /Line has invalid autocommand /;
471	return(1) if /(Invalid input detected|Type help or )/;
472	return(1) if (/\% Invalid command at /);
473	return(1) if /(No such device|Error Sending Request)/i;
474	return(1) if /No such file or directory/;
475	return(1) if /No space information available/;
476	return(1) if / is either not present or not formatted/;
477	return(-1) if /\%Error calling/;
478	return(-1) if /(: device being squeezed|ATA_Status time out)/i; # busy
479	return(-1) if (/\% Permission denied/);
480	return(-1) if (/command authorization failed/i);
481	return(1) if /(Open device \S+ failed|Error opening \S+:)/;
482
483	# Drop ee.log
484	/\s+ee\.log(?:\..*)?$/ && next;
485
486	# Drop bootvar_debug log files
487	/\s+bootvar_debug\./ && next;
488
489	# Drop accounting.log*
490	/\s+accounting.log.*/ && next;
491
492	# Drop vtp_debug.log and vtp_debug_old.log CDETS bug CSCuy87611
493	/\s+vtp_debug(_old)?\.log$/ && next;
494
495	# Drop bcm_mem_lock_trace.log
496	/\s+bcm_mem_lock_trace\.log$/ && next;
497
498	next if (/BufferMonitor-1HourData/);
499
500	if (/( debug_logs| log)\/$/) {
501	    # change
502	    #         8192    Jan 08 14:05:05 2015  log/
503	    # to
504	    #                                       log/
505	    if (/(\s*\d+\s+)(\S+ \d+\s+\d+:\d+:\d+ \d+)(.*)/) {
506		my($a, $dt, $rem) = ($1, $2, $3);
507		my($dtl) = length($dt);
508		my($fmt) = "%s%-". $dtl ."s%s\n";
509		$_ = sprintf($fmt, $a, "", $rem);
510	    }
511	}
512
513	if (/^\s*(\d+) bytes free/i) {
514	    $free = $1;
515	    next;
516	} elsif (/^\s*(\d+) bytes used/i) {
517	    next;
518	} elsif (/^\s*(\d+) bytes total/i) {
519	    $_ = diskszsummary($1, $free, undef) . "\n";
520	}
521
522	ProcessHistory("COMMENTS","","","!Flash: $dev: $_");
523    }
524    ProcessHistory("COMMENTS","","","!\n");
525    return(0);
526}
527
528# This routine parses "show module".
529sub ShowModule {
530    my($INPUT, $OUTPUT, $cmd) = @_;
531    print STDERR "    In ShowModule: $_" if ($debug);
532
533    while (<$INPUT>) {
534	tr/\015//d;
535	return if (/^\s*\^$/);
536	last if (/online diag status/i);
537	last if (/^$prompt/);
538	next if (/^\s*$cmd\s*$/);
539	return(1) if (/\% Invalid command at /);
540	return(1) if (/\% Invalid number at /);
541	return(-1) if (/\% Permission denied/);
542	return(-1) if (/command authorization failed/i);
543
544	s/(.*) \*$/$1/;	# Drop a trailing '*'
545	/^\* this terminal session/ && next;
546	s/ +$//;	# Drop trailing ' '
547
548	# filter fluctuating WWNs
549	if (/\s+World-Wide-Name.*/i) {
550	    ProcessHistory("COMMENTS","","","!Mod: $_");
551	    while (<$INPUT>) {
552		tr/\015//d;
553		goto EndShowModule if (/^$prompt/);
554		last if (/^\s*$/);
555		if (/^(\d+\s+\S+\s+\S+)\s+.*$/) {
556		    $_ = sprintf("%s\n", $1);
557		}
558		ProcessHistory("COMMENTS","","","!Mod: $_");
559	    }
560	}
561	ProcessHistory("COMMENTS","","","!Mod: $_");
562    }
563EndShowModule:
564    ProcessHistory("COMMENTS","","","!\n");
565
566    return(0);
567}
568
569# This routine parses "show interface transceiver".
570sub ShowIntTransceiver {
571    my($INPUT, $OUTPUT, $cmd) = @_;
572    print STDERR "    In ShowtTransceiver: $_" if ($debug);
573
574    while (<$INPUT>) {
575	tr/\015//d;
576	return if (/^\s*\^$/);
577	last if (/^$prompt/);
578	next if (/^(\s*|\s*$cmd\s*)$/);
579	return(1) if /Line has invalid autocommand /;
580	return(1) if /(Invalid input detected|Type help or )/;
581	return(1) if (/\% Invalid command at /);
582	return(-1) if (/No token match at /);	# 1000v
583	return(-1) if (/\% Permission denied/);
584	return(-1) if (/command authorization failed/i);
585
586	# filter out oscillating data from transceivers
587	next if (/(Temperature|Current|Power|Voltage)\s+:/);
588
589	ProcessHistory("COMMENTS","","","!$_");
590    }
591    ProcessHistory("COMMENTS","","","!\n");
592
593    return(0);
594}
595
596# This routine parses "show inventory".
597sub ShowInventory {
598    my($INPUT, $OUTPUT, $cmd) = @_;
599    print STDERR "    In ShowInventory: $_" if ($debug);
600
601    while (<$INPUT>) {
602	tr/\015//d;
603	return if (/^\s*\^$/);
604	last if (/^$prompt/);
605	next if (/^(\s*|\s*$cmd\s*)$/);
606	return(1) if /Line has invalid autocommand /;
607	return(1) if /(Invalid input detected|Type help or )/;
608	return(1) if (/\% Invalid command at /);
609	return(-1) if (/No token match at /);	# 1000v
610	return(-1) if (/\% Permission denied/);
611	return(-1) if (/command authorization failed/i);
612
613	if (/^(NAME: "[^"]*",)\s+(DESCR: "[^"]+")/) {
614	    ProcessHistory("COMMENTS","","", sprintf("!%-30s %s\n", $1, $2));
615	    next;
616	}
617	# split PID/VID/SN line
618	if (/^PID: (\S*)\s*,\s+VID: (\S*)\s*,\s+SN: (\S*)\s*$/) {
619	    my($entries) = "";
620	    $entries .= "!PID: $1\n" if ($1);
621	    $entries .= "!VID: $2\n" if ($2);
622	    $entries .= "!SN: $3\n" if ($3);
623	    ProcessHistory("COMMENTS","","", "$entries");
624	    next;
625	}
626	# split broken PID/VID/SN lines.
627	if (/^PID: (\S*)\s*,\s+VID: (\S*)\s*$/) {
628	    my($entries) = "";
629	    $entries .= "!PID: $1\n" if ($1);
630	    $entries .= "!VID: $2\n" if ($2);
631	    <$INPUT>;
632	    tr/\015//d;
633	    /^\s*,\s+SN: (\S*)\s*$/;
634	    $entries .= "!SN: $1\n" if ($1);
635	    ProcessHistory("COMMENTS","","", "$entries");
636	    next;
637	}
638	ProcessHistory("COMMENTS","","","!$_");
639    }
640    ProcessHistory("COMMENTS","","","!\n");
641
642    return(0);
643}
644
645# This routine parses "show vtp status"
646sub ShowVTP {
647    my($INPUT, $OUTPUT, $cmd) = @_;
648    print STDERR "    In ShowVTP: $_" if ($debug);
649
650    while (<$INPUT>) {
651	tr/\015//d;
652	last if (/^$prompt/);
653	next if (/^(\s*|\s*$cmd\s*)$/);
654	return(1) if /^\s*\^\s*$/;
655	return(1) if /Line has invalid autocommand /;
656	return(1) if /(Invalid input detected|Type help or )/;
657	return(1) if (/\% Invalid command at /);
658	return(1) if (/No token match at /);	# 1000v
659	return(-1) if (/\% Permission denied/);
660	return(-1) if (/command authorization failed/i);
661	next if (/^Configuration last modified by/);
662	# the pager can not be disabled per-session on the PIX
663	if (/^(<-+ More -+>)/) {
664	    my($len) = length($1);
665	    s/^$1\s{$len}//;
666	}
667
668	# Nexus 5k and 1000v do not support vtp
669	if (!/^VTP Operating Mode\s+:\s+(Transparent|Server)/) {
670	    $DO_SHOW_VLAN = 0;
671	}
672	ProcessHistory("COMMENTS","","","!VTP: $_");
673    }
674    ProcessHistory("COMMENTS","","","!\n");
675    return(0);
676}
677
678# This routine parses "show vlan"
679sub ShowVLAN {
680    my($INPUT, $OUTPUT, $cmd) = @_;
681    print STDERR "    In ShowVLAN: $_" if ($debug);
682
683    ($_ = <$INPUT>, return(1)) if (!$DO_SHOW_VLAN);
684
685    while (<$INPUT>) {
686	tr/\015//d;
687	last if (/^$prompt/);
688	next if (/^(\s*|\s*$cmd\s*)$/);
689	return(1) if /^\s*\^\s*$/;
690	return(1) if /Line has invalid autocommand /;
691	return(1) if /(Invalid input detected|Type help or )/;
692	return(-1) if (/\% Invalid command at /);
693	return(-1) if (/No token match at /);	# 1000v
694	return(1) if /Ambiguous command/i;
695	return(-1) if (/\% Permission denied/);
696	return(-1) if (/command authorization failed/i);
697	# the pager can not be disabled per-session on the PIX
698	if (/^(<-+ More -+>)/) {
699	    my($len) = length($1);
700	    s/^$1\s{$len}//;
701	}
702
703	ProcessHistory("COMMENTS","","","!VLAN: $_");
704    }
705    ProcessHistory("COMMENTS","","","!\n");
706    return(0);
707}
708
709# This routine parses "show debug"
710sub ShowDebug {
711    my($INPUT, $OUTPUT, $cmd) = @_;
712    print STDERR "    In ShowDebug: $_" if ($debug);
713    my($lines) = 0;
714
715    while (<$INPUT>) {
716	tr/\015//d;
717	last if (/^$prompt/);
718	next if (/^(\s*|\s*$cmd\s*)$/);
719	return(1) if /Line has invalid autocommand /;
720	return(1) if /(Invalid input detected|Type help or )/;
721	return(-1) if (/\% Invalid command at /);
722	return(-1) if (/No token match at /);	# 1000v
723	return(-1) if (/command authorization failed/i);
724	return(-1) if (/could not retrieve info/i);
725	# XXX return(-1) if (/\% Permission denied/);
726	# NX 5000 bug? "show debug" generates
727	# "Permission denied" when using command authorization. -Per-Olof Olsson
728	next if (/\% Permission denied/);
729
730	/^No matching debug flags set$/ && next;
731	/^No debug flags set$/ && next;
732	ProcessHistory("COMMENTS","","","!DEBUG: $_");
733	$lines++;
734    }
735    if ($lines) {
736	ProcessHistory("COMMENTS","","","!\n");
737    }
738    return(0);
739}
740
741# This routine parses "show cores"
742sub ShowCores {
743    my($INPUT, $OUTPUT, $cmd) = @_;
744    print STDERR "    In ShowCores: $_" if ($debug);
745
746    while (<$INPUT>) {
747	tr/\015//d;
748	last if (/^$prompt/);
749	next if (/^(\s*|\s*$cmd\s*)$/);
750	return(1) if /Line has invalid autocommand /;
751	return(1) if /(Invalid input detected|Type help or )/;
752	return(1) if (/\% Invalid command at /);
753	return(1) if (/No token match at /);	# 1000v
754	return(-1) if (/\% Permission denied/);
755	return(-1) if (/command authorization failed/i);
756
757	ProcessHistory("COMMENTS","","","!CORES: $_");
758    }
759    ProcessHistory("COMMENTS","","","!\n");
760    return(0);
761}
762
763# This routine parses "show fex" and "show module fex"
764sub ShowFex {
765    my($INPUT, $OUTPUT, $cmd) = @_;
766    print STDERR "    In ShowFex: $_" if ($debug);
767
768    while (<$INPUT>) {
769	tr/\015//d;
770	last if (/^$prompt/);
771	next if (/^(\s*|\s*$cmd\s*)$/);
772	return(1) if (/^\s*\^\s*$/);
773	return(1) if /Line has invalid autocommand /;
774	return(1) if /(Invalid input detected|Type help or )/;
775	return(1) if (/\% Invalid command at /);
776	return(1) if (/% Invalid number at /);	# 1000v
777	return(-1) if (/\% Permission denied/);
778	return(-1) if (/command authorization failed/i);
779
780	ProcessHistory("COMMENTS","","","!FEX: $_");
781    }
782    ProcessHistory("COMMENTS","","","!\n");
783    return(0);
784}
785
786# This routine parses "show processes log"
787sub ShowProcLog {
788    my($INPUT, $OUTPUT, $cmd) = @_;
789    print STDERR "    In ShowProcLog: $_" if ($debug);
790
791    while (<$INPUT>) {
792	tr/\015//d;
793	last if (/^$prompt/);
794	next if (/^(\s*|\s*$cmd\s*)$/);
795	return(1) if /Line has invalid autocommand /;
796	return(1) if /(Invalid input detected|Type help or )/;
797	return(1) if (/\% Invalid command at /);
798	return(-1) if (/No token match at /);	# 1000v
799	return(-1) if (/\% Permission denied/);
800	return(-1) if (/command authorization failed/i);
801
802	ProcessHistory("COMMENTS","","","!PROC_LOGS: $_");
803    }
804    ProcessHistory("COMMENTS","","","!\n");
805    return(0);
806}
807
808# This routine processes a "write term"
809sub WriteTerm {
810    my($INPUT, $OUTPUT, $cmd) = @_;
811    print STDERR "    In WriteTerm: $_" if ($debug);
812    my($lineauto,$comment,$linecnt) = (0,0,0);
813
814    while (<$INPUT>) {
815REPEAT:	tr/\015//d;
816	last if (/^$prompt/);
817	return(1) if /Line has invalid autocommand /;
818	return(1) if (/(Invalid input detected|Type help or )/i);
819	return(-1) if (/\% Invalid command at /);
820	return(-1) if (/No token match at /);	# 1000v
821	return(-1) if (/\% Permission denied/);
822	return(-1) if (/command authorization failed/i);
823	return(0) if ($found_end);		# Only do this routine once
824
825	$linecnt++;
826	$lineauto = 0 if (/^[^ ]/);
827
828	# Dog gone Cool matches to process the rest of the config
829	/^!Command: show running-config/ && next; # kill this junk
830	/^!Time: / && next; # kill this junk
831
832	# Sort username and delete passwords.
833	if (/^username (\S+) password (\d) (\S+)(\s.*)$/) {
834	    if ($filter_pwds >= 2) {
835		ProcessHistory("USER","keysort","$1",
836			       "!username $1 password <removed>$4\n");
837	    } elsif ($filter_pwds >= 1 && $2 ne "5") {
838		ProcessHistory("USER","keysort","$1",
839			       "!username $1 password <removed>$4\n");
840	    } else {
841		ProcessHistory("USER","keysort","$1","$_");
842	    }
843	    next;
844	}
845	# Sort any other username info.
846	/^username (\S+) .*$/ &&
847	    ProcessHistory("USER","keysort","$1","$_") && next;
848	if (/^\s*(.*?neighbor \S*) password / && $filter_pwds >= 1) {
849	    ProcessHistory("","","","! $1 password <removed>\n");
850	    next;
851	}
852	# sort ipv{4,6} access-lists
853	if ($aclfilterseq && /^ip(v4|v6)? access-list (\S+)\s*$/) {
854	    my($nlri, $key) = ($1, $2);
855	    my($seq, $cmd);
856	    if (length($nlri) eq 0) {
857		$nlri = "ipv4";	# assume ip will become ipv4 at some point
858	    }
859	    ProcessHistory("ACL $nlri $key","","","$_");
860	    while (<$INPUT>) {
861		tr/\015//d;
862		goto REPEAT if (/^$prompt/ || !/^\s/);
863		if (/^\s*!/) {
864		    ProcessHistory("ACL $nlri $key !", "", "", "$_");
865		    next;
866		}
867		# ip access-list copp-system-acl-bgp
868		#   10 permit tcp any gt 1024 any eq bgp
869		#   20 permit tcp any eq bgp any gt 1024
870		# ipv6 access-list copp-system-acl-bgp6
871		#   10 permit tcp any gt 1024 any eq bgp
872		#   20 permit tcp any eq bgp any gt 1024
873		# XXX this may need more format handling; going off email with
874		#     a user.
875		my($seq, $cmd, $resid) = ($_ =~ /^\s*(\d+) (\w+) (.+)/);
876		if ($cmd =~ /(permit|deny)/) {
877		    my($ip);
878		    my(@w) = ($resid =~ /(\S+) (\S+) (\S+\s)?(.+)/);
879		    for (my($i) = 0; $i < $#w; $i++) {
880			if ($w[$i] eq "any") {
881			    if ($nlri eq "ipv4") {
882				$ip = "255.255.255.255/32";
883			    } else {
884				$ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128";
885			    }
886			    last;
887			} elsif ($w[$i] =~ /^[:0-9]/ ||
888				 $2[$i] =~ /^[a-fA-F]{1,4}:/) {
889			    $ip = $w[$i];
890			    $ip =~ s/\s+$//;		# trim trailing WS
891			    last;
892			}
893		    }
894		    ProcessHistory("ACL $nlri $key $cmd", "$aclsort", "$ip",
895				   " $cmd $resid\n");
896		} else {
897		    ProcessHistory("ACL $nlri $key $cmd", "", "",
898				   " $cmd $resid\n");
899		}
900	    }
901	}
902
903	# Why was this commented out? It shows up in the raw text...
904	if (/^(snmp-server community) (\S+)/) {
905	    if ($filter_commstr) {
906		ProcessHistory("SNMPSERVERCOMM","keysort","$_",
907			       "!$1 <removed>$'") && next;
908	    } else {
909		ProcessHistory("SNMPSERVERCOMM","keysort","$_","$_") && next;
910	    }
911	}
912	# order/prune snmp-server host statements
913	# we only prune lines of the form
914	# snmp-server host a.b.c.d <community>
915	if (/^snmp-server host (\d+\.\d+\.\d+\.\d+) /) {
916	    if ($filter_commstr) {
917		my($ip) = $1;
918		my($line) = "snmp-server host $ip";
919		my(@tokens) = split(' ', $');
920		my($token);
921		while ($token = shift(@tokens)) {
922		    if ($token eq 'version') {
923			$line .= " " . join(' ', ($token, shift(@tokens)));
924			if ($token eq '3') {
925			    $line .= " " . join(' ', ($token, shift(@tokens)));
926			}
927		    } elsif ($token eq 'vrf') {
928			$line .= " " . join(' ', ($token, shift(@tokens)));
929		    } elsif ($token =~ /^(informs?|traps?|(no)?auth)$/) {
930			$line .= " " . $token;
931		    } else {
932			$line = "!$line " . join(' ', ("<removed>",
933						 join(' ',@tokens)));
934			last;
935		    }
936		}
937		ProcessHistory("SNMPSERVERHOST","ipsort","$ip","$line\n");
938	    } else {
939		ProcessHistory("SNMPSERVERHOST","ipsort","$1","$_");
940	    }
941	    next;
942	}
943	# Sort snmp user and delete passwords.
944	if (/^snmp-server user (\S+) (\S+) auth md5 (\S+) priv (\S+) localizedkey$/) {
945	    if ($filter_pwds >= 2) {
946		ProcessHistory("SNMP-USER","keysort","$1",
947			       "!snmp-server user $1 $2 auth md5 <removed> priv <removed> localizedkey\n");
948	    } else {
949		ProcessHistory("SNMP-USER","keysort","$1","$_");
950	    }
951	    next;
952	}
953	# Sort any other snmp user info.
954	/^snmp-server user (\S+) .*$/ &&
955	    ProcessHistory("SNMP-USER","keysort","$1","$_") && next;
956	# Delete bgp passwords.
957	if (/^(\s*)password (\d) (\S+)(\s.*)?$/) {
958	    if ($filter_pwds >= 2) {
959		ProcessHistory("","","","!$1password <removed>$4");
960	    } elsif ($filter_pwds >= 1 && $2 ne "5") {
961		ProcessHistory("","","","!$1password <removed>$4");
962	    } else {
963		ProcessHistory("","","","$_");
964	    }
965	    next;
966	}
967
968	# prune tacacs/radius server keys:
969	# tacacs-server host 196.23.0.13 key 7 "xxxxxxx" port 50 timeout 10
970	if (/^((tacacs|radius)-server.*?\bkey\b.*?) ".*?"(.*)/
971	    && $filter_pwds >= 1) {
972	    ProcessHistory("","","","!$1 <removed>$3\n"); next;
973	}
974
975	# order cli alias names
976	/^cli alias name (\S+) .*$/ &&
977	    ProcessHistory("CLI-ALIAS","keysort","$1","$_") && next;
978	# order snmp-server enable trap statements
979	/^snmp-server enable traps (.*)$/ &&
980	    ProcessHistory("SNMP-TRAPS","keysort","$1","$_") && next;
981
982	# catch anything that wasnt matched above.
983	ProcessHistory("","","","$_");
984	# end of config.  the ": " game is for the PIX
985	if (/^(: +)?end$/) {
986	    $found_end = 1;
987	    return(0);
988	}
989    }
990    # The ContentEngine lacks a definitive "end of config" marker.  If we
991    # know that it is NXOS and we have seen at least 5 lines
992    # of write term output, we can be reasonably sure that we got the config.
993    if (($type eq "NXOS") && $linecnt > 5) {
994	$found_end = 1;
995	return(0);
996    }
997
998    return(0);
999}
1000
10011;
1002