├── .gitignore ├── CHANGES ├── COPYING ├── CREDITS ├── README ├── configure ├── device_table.example ├── fetchconfig.pl ├── fetchconfig ├── Constants.pm ├── Logger.pm └── model │ ├── 3ComMSR.pm │ ├── Abstract.pm │ ├── Acme.pm │ ├── CiscoASA.pm │ ├── CiscoCAT.pm │ ├── CiscoIOS.pm │ ├── CiscoIOSXR.pm │ ├── CiscoPIX.pm │ ├── CiscoSG300.pm │ ├── Coriant8600.pm │ ├── Dell.pm │ ├── Detector.pm │ ├── DmSwitch.pm │ ├── FortiGate.pm │ ├── JunOS.pm │ ├── Mediant.pm │ ├── MikroTik.pm │ ├── NECUnivergeIX.pm │ ├── Parks.pm │ ├── ProCurve.pm │ ├── Riverstone.pm │ ├── TellabsMSR.pm │ └── Terayon.pm └── rpm ├── fetchconfig ├── fetchconfig-cvs.spec ├── fetchconfig-daily ├── fetchconfig-sysconfig └── fetchconfigtab /.gitignore: -------------------------------------------------------------------------------- 1 | /blib/ 2 | /.build/ 3 | _build/ 4 | cover_db/ 5 | inc/ 6 | Build 7 | !Build/ 8 | Build.bat 9 | .last_cover_stats 10 | /Makefile 11 | /Makefile.old 12 | /MANIFEST.bak 13 | /META.yml 14 | /META.json 15 | /MYMETA.* 16 | nytprof.out 17 | /pm_to_blib 18 | *.o 19 | *.bs 20 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | # 2 | # $Id: CHANGES,v 1.53 2013/10/16 03:29:10 evertonm Exp $ 3 | # 4 | 5 | 0.26 6 | ==== 7 | 8 | 0.25 9 | ==== 10 | + CiscoSG300.pm: support for Cisco SG300. 11 | 12 | 0.24 13 | ==== 14 | + Coriant8600.pm: support for Coriant 8600. 15 | + CiscoIOSXR.pm: support for Cisco IOS XR. 16 | + NECUnivergeIX.pm: support for NEC Univerge IX series. 17 | 18 | 0.23 19 | ==== 20 | + support for -line=string switch for passing device table lines 21 | directly into the command-line. 22 | + option on_fetch_cat=1 issues current configuration to stdout. 23 | + Acme.pm: support for Oracle Acme Packet. 24 | + Mediant.pm: support for Audiocodes Mediant. 25 | 26 | 0.22 27 | ==== 28 | + JunOS.pm: support for Juniper JunOS. 29 | 30 | 0.21 31 | ==== 32 | + TellabsMSR.pm: support for Tellabs MSR Routers. 33 | 34 | 0.20 35 | ==== 36 | + CiscoPIX.pm: support for Cisco PIX. 37 | 38 | 0.19 39 | ==== 40 | + suport for Mikrotik RB800. 41 | 42 | 0.18 43 | ==== 44 | + support for 3Com MSR devices. 45 | 46 | 0.17 47 | ==== 48 | + bug #25475: Stuart Schneis added compatibility for Dell 6224P, 5324, 5424 49 | 50 | 0.16 51 | ==== 52 | + rip@devco.net added CiscoASA.pm sr #106606: Cisco ASA Support 53 | 54 | 0.15 55 | ==== 56 | + support equal '=' character in values for options. 57 | 58 | 0.14 59 | ==== 60 | + option timezone=hide omits timezone name from file name. 61 | + added DmSwitch.pm for Datacom DmSwitch devices. 62 | 63 | 0.13 64 | ==== 65 | + resort to strftime -%Z for tz name in systems which 66 | lack support for strftime %z for tz offset (Solaris 7). 67 | + added FETCHCONFIG_DEV_ID and FETCHCONFIG_DEV_HOST 68 | environmental variables to external on_fetch_run program. 69 | 70 | 0.12 71 | ==== 72 | + option on_fetch_run to call an external program whenever 73 | a config backup is fetched. 74 | + append timezone offset to backup filename. 75 | + Sergey Alexanov added Terayon.pm for Old Terayon 3200/3500 CMTS. 76 | 77 | 0.11 78 | ==== 79 | + add retrieval timing info into log. 80 | 81 | 0.10 82 | ==== 83 | + bug #21501: Problem if device-specific-options are not specified 84 | 85 | 0.9 86 | === 87 | + Slightly better device table parser in order to support 88 | options with blank spaces. Example: show_cmd=show run 89 | 90 | 0.8 91 | === 92 | + Julien Louis added Dell.pm for Dell PowerConnect switches. 93 | 94 | 0.7 95 | === 96 | + added Riverstone.pm for Alcatel Ethernet Routers. 97 | 98 | 0.6 99 | === 100 | + added Parks.pm. 101 | + log program version. (cmd line option: -v) 102 | 103 | 0.5 104 | === 105 | + added show_cmd option for older routers in CiscoIOS.pm. (Task 6204) 106 | + added FortiGate.pm. (Doug Schaapveld / Task 6205) 107 | + modified CiscoIOS.pm, FortiGate.pm to strip leading "show" commands 108 | and other info. This results in clean configs that can be used 109 | as-is to restore a device. (Doug Schaapveld) 110 | + added ProCurve.pm for HP Procurve switches. (Doug Schaapveld) 111 | 112 | 0.4 113 | === 114 | + added CiscoCAT.pm contributed by Marcos Sandez. 115 | 116 | 0.3 117 | === 118 | + ported to Windows. 119 | 120 | 0.2 121 | === 122 | + new filesystem layout. 123 | + FSF address updated. 124 | 125 | 0.1 126 | === 127 | + notes about the device id. 128 | + copyright notice at beginning of source files. 129 | + added this CHANGES file. 130 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | # 2 | # $Id: CREDITS,v 1.6 2009/02/03 11:07:55 evertonm Exp $ 3 | # 4 | 5 | Marcos Sandez contributed CiscoCAT.pm 6 | Doug Schaapveld added FortiGate.pm, old IOS support 7 | Julien Louis added Dell.pm 8 | Sergey Alexanov added Terayon.pm for Old Terayon 3200/3500 CMTS 9 | rip@devco.net added CiscoASA.pm 10 | Stuart Schneis added support for Dell switches 6224P, 5324, 5424 11 | Sohgo Takeuchi added NECUnivergeIX.pm 12 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | true 4 | 5 | -------------------------------------------------------------------------------- /device_table.example: -------------------------------------------------------------------------------- 1 | # 2 | # $Id: device_table.example,v 1.19 2013/10/16 03:29:11 evertonm Exp $ 3 | # 4 | 5 | # DEFAULT OPTIONS SECTION 6 | # 7 | # model options 8 | # 9 | default: cisco-ios user=backup,pass=fran,enable=jose 10 | default: cisco-ios timeout=10,keep=5,changes_only=0 11 | default: cisco-ios repository=/var/fetchconfig/save 12 | 13 | default: cisco-cat user=backup,pass=fran,enable=jose 14 | default: cisco-cat timeout=10,keep=5 15 | default: cisco-cat changes_only=0,fetch_timeout=30 16 | default: cisco-cat repository=/var/fetchconfig/save 17 | 18 | default: parks user=admin,pass=parks 19 | default: parks timeout=2,keep=5 20 | default: parks fetch_timeout=30,banner_timeout=60 21 | default: parks repository=/var/fetchconfig/save 22 | 23 | default: riverstone user=rs3,pass=100,enable=adm 24 | default: riverstone timeout=3,keep=3 25 | default: riverstone fetch_timeout=30 26 | default: riverstone repository=/var/fetchconfig/save 27 | 28 | default: terayon-os user=testuser,pass=testpassword,enable=testenable 29 | default: terayon-os timeout=60,keep=50,changes_only=0 30 | default: terayon-os repository=/var/fetchconfig/save 31 | 32 | default: dmswitch user=dm,pass=dmpass 33 | default: dmswitch timeout=10,keep=5,changes_only=0 34 | default: dmswitch repository=/var/fetchconfig/save 35 | 36 | default: cisco-asa timeout=10,keep=5,changes_only=1 37 | default: cisco-asa repository=/srv/fetchconfig 38 | 39 | default: 3com-msr user=admin,pass=lab,enable=lab 40 | default: 3com-msr timeout=10,keep=5,changes_only=0 41 | default: 3com-msr repository=/var/fetchconfig/save 42 | 43 | default: mikrotik user=admin,pass= 44 | default: mikrotik timeout=10,keep=5,changes_only=0 45 | default: mikrotik repository=/var/fetchconfig/save 46 | 47 | default: cisco-pix timeout=10,keep=5,changes_only=1 48 | default: cisco-pix repository=/var/fetchconfig/save 49 | 50 | default: tellabs-msr user=usr,pass=pwd,enable=jose 51 | default: tellabs-msr timeout=10,keep=5,changes_only=0 52 | default: tellabs-msr fetch_timeout=1200 53 | default: tellabs-msr repository=/var/fetchconfig/save 54 | default: tellabs-msr filename_append_suffix=.txt 55 | 56 | default: junos user=juniper,pass=jun@321 57 | default: junos timeout=10,keep=5,changes_only=0 58 | default: junos filename_append_suffix=.txt 59 | default: junos repository=/var/fetchconfig/save 60 | 61 | default: acme user=admin,pass=acme 62 | default: acme timeout=10,keep=5,changes_only=0 63 | default: acme filename_append_suffix=.txt 64 | default: acme repository=/var/fetchconfig/save 65 | 66 | default: mediant user=Admin,pass=Admin 67 | default: mediant timeout=10,keep=5,changes_only=0 68 | default: mediant filename_append_suffix=.txt 69 | default: mediant repository=/var/fetchconfig/save 70 | 71 | default: cisco-iosxr user=backup,pass=fran 72 | default: cisco-iosxr timeout=10,keep=5,changes_only=0 73 | default: cisco-iosxr repository=/var/fetchconfig/save 74 | 75 | default: nec-univerge-ix user=admin,pass=admin 76 | default: nec-univerge-ix timeout=10,keep=5,changes_only=0 77 | default: nec-univerge-ix filename_append_suffix=.txt 78 | default: nec-univerge-ix repository=/var/fetchconfig/save 79 | 80 | default: coriant-8600 user=backup,pass=fran,enable=jose 81 | default: coriant-8600 timeout=10,keep=5,changes_only=0 82 | default: coriant-8600 repository=/var/fetchconfig/save 83 | 84 | 85 | # DEVICES SECTION 86 | # 87 | # model dev-unique-id hostname device-specific-options 88 | # 89 | cisco-ios inet-gw 10.0.0.1 90 | cisco-ios vpn-gw 192.168.0.1 keep=10,changes_only=1 91 | cisco-ios ancient-ios 192.168.0.3 show_cmd=wrterm 92 | 93 | cisco-cat sales-sw 172.16.0.10 94 | cisco-cat eng-sw 172.16.0.11 keep=10,changes_only=1 95 | 96 | parks lab209r 10.30.0.6 97 | 98 | riverstone lab3100 10.1.30.2 99 | 100 | terayon-os cmts-1 10.10.1.1 101 | terayon-os cmts-2 10.10.1.2 102 | 103 | dmswitch lab3224F2 10.1.59.130 104 | 105 | cisco-asa fw1-1 10.1.1.1 user=fwadmin,pass=fwpass,enable=fwpass 106 | 107 | 3com-msr lab20-11 10.0.0.147 108 | 109 | mikrotik lab-rb800 10.0.0.147 110 | 111 | cisco-pix lab-pix 10.0.0.1 user=pixadmin,pass=pixpass,enable=pixenable 112 | 113 | tellabs-msr 8800-lab 1.1.1.1 114 | 115 | junos ex4200-1 10.1.0.100 116 | 117 | acme sbc1 10.2.2.2 118 | 119 | mediant gateway1 11.0.0.2 120 | 121 | cisco-iosxr asr9010-edge 127.0.0.1 122 | 123 | nec-univerge-ix ix-01 10.22.1.5 124 | 125 | coriant-8600 8605 10.0.0.1 126 | -------------------------------------------------------------------------------- /fetchconfig.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl -w 2 | # 3 | # fetchconfig - Retrieving configuration for multiple devices 4 | # Copyright (C) 2006 Everton da Silva Marques 5 | # 6 | # fetchconfig is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2, or (at your option) 9 | # any later version. 10 | # 11 | # fetchconfig is distributed in the hope that it will be useful, but 12 | # WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | # General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with fetchconfig; see the file COPYING. If not, write to the 18 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 19 | # MA 02110-1301 USA. 20 | # 21 | # $Id: fetchconfig.pl,v 1.8 2012/11/27 21:42:28 evertonm Exp $ 22 | 23 | use strict; 24 | use fetchconfig::Logger; 25 | use fetchconfig::Constants; 26 | use fetchconfig::model::Detector; 27 | 28 | sub basename { 29 | my ($path) = @_; 30 | 31 | my @list; 32 | 33 | if ($^O eq 'MSWin32') { 34 | @list = split /\\/, $path; 35 | } 36 | else { 37 | @list = split /\//, $path; 38 | } 39 | 40 | pop @list; 41 | } 42 | 43 | my $me = basename($0); 44 | 45 | my $log = fetchconfig::Logger->new({ prefix => $me }); 46 | 47 | $log->info('version ' . fetchconfig::Constants::version); 48 | 49 | my @device_file_list; 50 | my @line_list; 51 | 52 | foreach my $opt (@ARGV) { 53 | if ($opt eq '-v') { 54 | exit; # only show version 55 | } 56 | if ($opt =~ /^-devices=(.+)$/) { 57 | push @device_file_list, $1; 58 | next; 59 | } 60 | if ($opt =~ /^-line=(.+)$/) { 61 | push @line_list, $1; 62 | next; 63 | } 64 | $log->error("unexpected argument: $opt"); 65 | &usage; 66 | die; 67 | } 68 | 69 | if ((@device_file_list < 1) && (@line_list < 1)){ 70 | $log->error("at least one -devices=filename or one -line=string is required"); 71 | &usage; 72 | die; 73 | } 74 | 75 | fetchconfig::model::Detector->init($log); 76 | 77 | foreach my $dev_file (@device_file_list) { 78 | &load_device_list($dev_file); 79 | } 80 | 81 | my $line_num = 0; 82 | foreach my $line (@line_list) { 83 | ++$line_num; 84 | &load_line($line, $line_num); 85 | } 86 | 87 | $log->info("done"); 88 | 89 | exit; 90 | 91 | sub usage { 92 | warn "usage: $me [-v] [-devices=file] [-line=string]\n"; 93 | } 94 | 95 | sub load_device_list { 96 | my ($filename) = @_; 97 | 98 | local *IN; 99 | 100 | if (!open(IN, "<$filename")) { 101 | $log->error("could not read device list: $filename: $!"); 102 | return; 103 | } 104 | 105 | $log->debug("loading device list: $filename"); 106 | 107 | my $line_num = 0; 108 | 109 | while () { 110 | chomp; 111 | 112 | ++$line_num; 113 | 114 | #$log->debug("[$line_num] $_"); 115 | 116 | next if (/^\s*(#|$)/); 117 | 118 | fetchconfig::model::Detector->parse($filename, $line_num, $_); 119 | } 120 | 121 | close IN; 122 | } 123 | 124 | sub load_line { 125 | my ($line, $num) = @_; 126 | 127 | $log->debug("loading line: line=$num [$line]"); 128 | 129 | return if ($line =~ /^\s*(#|$)/); 130 | 131 | fetchconfig::model::Detector->parse('', $num, $line); 132 | } 133 | -------------------------------------------------------------------------------- /fetchconfig/Constants.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2007 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: Constants.pm,v 1.17 2012/11/28 14:17:28 evertonm Exp $ 20 | 21 | package fetchconfig::Constants; # fetchconfig/Constants.pm 22 | 23 | use strict; 24 | use warnings; 25 | 26 | my $version = '0.26'; 27 | 28 | sub version { 29 | $version; 30 | } 31 | 32 | 1; 33 | -------------------------------------------------------------------------------- /fetchconfig/Logger.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2006 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: Logger.pm,v 1.1 2006/06/08 20:28:39 evertonm Exp $ 20 | 21 | package fetchconfig::Logger; # fetchconfig/Logger.pm 22 | 23 | use strict; 24 | use warnings; 25 | 26 | sub new { 27 | my ($proto, $options) = @_; 28 | my $class = ref($proto) || $proto; 29 | 30 | # defaults 31 | my $self = { 32 | prefix => '?' 33 | }; 34 | 35 | # user options 36 | if (@_ > 1) { 37 | foreach (keys %$options) { 38 | $self->{$_} = $options->{$_}; 39 | } 40 | } 41 | 42 | bless $self, $class; 43 | } 44 | 45 | sub prefix { 46 | my ($self, $prefix) = @_; 47 | 48 | my $old = $self->{prefix}; 49 | 50 | if (defined($prefix)) { 51 | $self->{prefix} = $prefix; 52 | } 53 | 54 | $old; 55 | } 56 | 57 | sub info { 58 | my ($self, $msg) = @_; 59 | 60 | warn $self->{prefix}, ": info: ", $msg, "\n"; 61 | } 62 | 63 | sub debug { 64 | my ($self, $msg) = @_; 65 | 66 | warn $self->{prefix}, ": debug: ", $msg, "\n"; 67 | } 68 | 69 | sub error { 70 | my ($self, $msg) = @_; 71 | 72 | warn $self->{prefix}, ": error: ", $msg, "\n"; 73 | } 74 | 75 | 1; 76 | -------------------------------------------------------------------------------- /fetchconfig/model/3ComMSR.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2006 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: 3ComMSR.pm,v 1.1 2010/10/07 16:10:25 evertonm Exp $ 20 | 21 | package fetchconfig::model::3ComMSR; # fetchconfig/model/3ComMSR.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::3ComMSR::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | '3com-msr'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my $login_prompt = '/(Username:|Password:)$/'; 63 | 64 | # chat_banner is used to allow temporary modification 65 | # of timeout throught the 'banner_timeout' option 66 | 67 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 68 | if (!defined($prematch)) { 69 | $self->log_error("could not find login prompt: $login_prompt"); 70 | return undef; 71 | } 72 | 73 | $self->log_debug("found login prompt: [$match]"); 74 | 75 | if ($match =~ /^Username/) { 76 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 77 | if (!defined($dev_user)) { 78 | $self->log_error("login username needed but not provided"); 79 | return undef; 80 | } 81 | 82 | $ok = $t->print($dev_user); 83 | if (!$ok) { 84 | $self->log_error("could not send login username"); 85 | return undef; 86 | } 87 | 88 | ($prematch, $match) = $t->waitfor(Match => '/Password:$/'); 89 | if (!defined($prematch)) { 90 | $self->log_error("could not find password prompt"); 91 | return undef; 92 | } 93 | 94 | $self->log_debug("found password prompt: [$match]"); 95 | } 96 | 97 | if ($match =~ /^Password/) { 98 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 99 | if (!defined($dev_pass)) { 100 | $self->log_error("login password needed but not provided"); 101 | return undef; 102 | } 103 | 104 | $ok = $t->print($dev_pass); 105 | if (!$ok) { 106 | $self->log_error("could not send login password"); 107 | return undef; 108 | } 109 | 110 | ($prematch, $match) = $t->waitfor(Match => '/<([^<>]+)>$/'); 111 | if (!defined($prematch)) { 112 | $self->log_error("could not find command prompt"); 113 | return undef; 114 | } 115 | 116 | $self->log_debug("found command prompt: [$match]"); 117 | } 118 | 119 | if ($match =~ /^<([^<>]+)>$/) { 120 | $ok = $t->print('super'); 121 | if (!$ok) { 122 | $self->log_error("could not send enable command (super)"); 123 | return undef; 124 | } 125 | 126 | ($prematch, $match) = $t->waitfor(Match => '/(Password:|<([^<>]+)>)$/'); 127 | if (!defined($prematch)) { 128 | $self->log_error("could not find enable password prompt"); 129 | return undef; 130 | } 131 | 132 | if ($match =~ /^Password/) { 133 | my $dev_enable = $self->dev_option($dev_opt_tab, "enable"); 134 | if (!defined($dev_enable)) { 135 | $self->log_error("enable password needed but not provided"); 136 | return undef; 137 | } 138 | 139 | $ok = $t->print($dev_enable); 140 | if (!$ok) { 141 | $self->log_error("could not send enable password"); 142 | return undef; 143 | } 144 | 145 | ($prematch, $match) = $t->waitfor(Match => '/<([^<>]+)>$/'); 146 | if (!defined($prematch)) { 147 | $self->log_error("could not find enable command prompt"); 148 | return undef; 149 | } 150 | } 151 | 152 | $self->log_debug("found enable prompt: '$match'"); 153 | } 154 | 155 | if ($match =~ /^<([^<>]+)>$/) { 156 | $ok = $t->print('sys'); 157 | if (!$ok) { 158 | $self->log_error("could not send system-view command"); 159 | return undef; 160 | } 161 | 162 | ($prematch, $match) = $t->waitfor(Match => '/\[([^\[\]]+)\]$/'); 163 | if (!defined($prematch)) { 164 | $self->log_error("could not find system-view prompt"); 165 | return undef; 166 | } 167 | 168 | $self->log_debug("found system-view prompt: '$match'"); 169 | } 170 | 171 | if ($match !~ /^\[([^\[\]]+)\]$/) { 172 | $self->log_error("could not match system-view prompt"); 173 | return undef; 174 | } 175 | 176 | my $prompt = $1; 177 | 178 | $self->{prompt} = $prompt; # save prompt 179 | 180 | $self->log_debug("logged in prompt='$prompt'"); 181 | 182 | $prompt; 183 | } 184 | 185 | sub expect_enable_prompt { 186 | my ($self, $t, $prompt) = @_; 187 | 188 | if (!defined($prompt)) { 189 | $self->log_error("internal failure: undefined command prompt"); 190 | return undef; 191 | } 192 | 193 | my $enable_prompt_regexp = '/\[' . $prompt . '[^\[\]]*\]$/'; 194 | 195 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 196 | if (!defined($prematch)) { 197 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 198 | } 199 | 200 | ($prematch, $match); 201 | } 202 | 203 | sub pager_off { 204 | my ($self, $t, $dev_id, $dev_host, $prompt) = @_; 205 | my $ok; 206 | 207 | $ok = $t->print('user-interface vty 0 4'); 208 | if (!$ok) { 209 | $self->log_error("could not send pager disabling command: user-interface vty 0 4"); 210 | return 1; 211 | } 212 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 213 | return 1 unless defined($prematch); 214 | 215 | $ok = $t->print('screen-length 0'); 216 | if (!$ok) { 217 | $self->log_error("could not send pager disabling command: screen-length 0"); 218 | return 1; 219 | } 220 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 221 | return 1 unless defined($prematch); 222 | 223 | $ok = $t->print('quit'); 224 | if (!$ok) { 225 | $self->log_error("could not send pager disabling command: quit"); 226 | return 1; 227 | } 228 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 229 | return 1 unless defined($prematch); 230 | 231 | undef; 232 | } 233 | 234 | sub pager_on { 235 | my ($self, $t, $dev_id, $dev_host, $prompt) = @_; 236 | my $ok; 237 | 238 | $ok = $t->print('user-interface vty 0 4'); 239 | if (!$ok) { 240 | $self->log_error("could not send pager enabling command: user-interface vty 0 4"); 241 | return 1; 242 | } 243 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 244 | return 1 unless defined($prematch); 245 | 246 | $ok = $t->print('undo screen-length'); 247 | if (!$ok) { 248 | $self->log_error("could not send pager enabling command: undo screen-length"); 249 | return 1; 250 | } 251 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 252 | return 1 unless defined($prematch); 253 | 254 | $ok = $t->print('quit'); 255 | if (!$ok) { 256 | $self->log_error("could not send pager enabling command: quit"); 257 | return 1; 258 | } 259 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 260 | return 1 unless defined($prematch); 261 | 262 | undef; 263 | } 264 | 265 | sub chat_fetch { 266 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 267 | my $ok; 268 | 269 | # disable pager 270 | if ($self->pager_off($t, $dev_id, $dev_host, $prompt)) { 271 | return 1; 272 | } 273 | 274 | # issue show run cmd 275 | if ($self->chat_show_conf($t, 'disp curr', $show_cmd)) { 276 | return 1; 277 | } 278 | 279 | # override default timeout with fetch_timeout, if defined 280 | my $save_timeout; 281 | if (defined($fetch_timeout)) { 282 | $save_timeout = $t->timeout; 283 | $t->timeout($fetch_timeout); 284 | } 285 | 286 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 287 | my $config_result = !defined($prematch); 288 | if ($config_result) { 289 | $self->log_error("could not find end of configuration"); 290 | } 291 | 292 | # restore overriden timeout 293 | if (defined($fetch_timeout)) { 294 | $t->timeout($save_timeout); 295 | } 296 | 297 | # restore pager 298 | $self->pager_on($t, $dev_id, $dev_host, $prompt); 299 | 300 | return $config_result if $config_result; 301 | 302 | $self->log_debug("found end of configuration: '$match'"); 303 | 304 | @$conf_ref = split /\n/, $prematch; 305 | 306 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 307 | 308 | undef; 309 | } 310 | 311 | sub do_fetch { 312 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 313 | 314 | $self->log_debug("trying"); 315 | 316 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 317 | if (!defined($dev_repository)) { 318 | $self->log_error("undefined repository"); 319 | return; 320 | } 321 | 322 | if (! -d $dev_repository) { 323 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 324 | return; 325 | } 326 | 327 | if (! -w $dev_repository) { 328 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 329 | return; 330 | } 331 | 332 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 333 | 334 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 335 | 336 | my $ok = $t->open($dev_host); 337 | if (!$ok) { 338 | $self->log_error("could not connect: $!"); 339 | return; 340 | } 341 | 342 | $self->log_debug("connected"); 343 | 344 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 345 | 346 | return unless defined($prompt); 347 | 348 | my @config; 349 | 350 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 351 | 352 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 353 | 354 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 355 | 356 | $ok = $t->close; 357 | if (!$ok) { 358 | $self->log_error("disconnecting: $!"); 359 | } 360 | 361 | $self->log_debug("disconnected"); 362 | 363 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 364 | } 365 | 366 | 1; 367 | -------------------------------------------------------------------------------- /fetchconfig/model/Acme.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2013 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: Acme.pm,v 1.1 2013/10/16 03:29:11 evertonm Exp $ 20 | 21 | package fetchconfig::model::Acme; # fetchconfig/model/Acme.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::Acme::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'acme'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my $login_prompt = '/(Username:|Password:) $/'; 63 | 64 | # chat_banner is used to allow temporary modification 65 | # of timeout throught the 'banner_timeout' option 66 | 67 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 68 | if (!defined($prematch)) { 69 | $self->log_error("could not find login prompt: $login_prompt"); 70 | return undef; 71 | } 72 | 73 | $self->log_debug("found login prompt: [$match]"); 74 | 75 | if ($match =~ /^Username/) { 76 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 77 | if (!defined($dev_user)) { 78 | $self->log_error("login username needed but not provided"); 79 | return undef; 80 | } 81 | 82 | $ok = $t->print($dev_user); 83 | if (!$ok) { 84 | $self->log_error("could not send login username"); 85 | return undef; 86 | } 87 | 88 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 89 | if (!defined($prematch)) { 90 | $self->log_error("could not find password prompt"); 91 | return undef; 92 | } 93 | 94 | $self->log_debug("found password prompt: [$match]"); 95 | } 96 | 97 | if ($match =~ /^Password/) { 98 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 99 | if (!defined($dev_pass)) { 100 | $self->log_error("login password needed but not provided"); 101 | return undef; 102 | } 103 | 104 | $ok = $t->print($dev_pass); 105 | if (!$ok) { 106 | $self->log_error("could not send login password"); 107 | return undef; 108 | } 109 | 110 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)[>#] $/'); 111 | if (!defined($prematch)) { 112 | $self->log_error("could not find command prompt"); 113 | return undef; 114 | } 115 | 116 | $self->log_debug("found command prompt: [$match]"); 117 | } 118 | 119 | if ($match !~ /^(\S+)[>#] $/) { 120 | $self->log_error("could not match command prompt"); 121 | return undef; 122 | } 123 | 124 | my $prompt = $1; 125 | 126 | $self->{prompt} = $prompt; # save prompt 127 | 128 | $self->log_debug("logged in prompt=[$prompt]"); 129 | 130 | $prompt; 131 | } 132 | 133 | sub expect_enable_prompt { 134 | my ($self, $t, $prompt, $delim) = @_; 135 | 136 | if (!defined($prompt)) { 137 | $self->log_error("internal failure: undefined command prompt"); 138 | return undef; 139 | } 140 | 141 | my $enable_prompt_regexp = '/' . $prompt . $delim . '/'; 142 | 143 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 144 | if (!defined($prematch)) { 145 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 146 | } 147 | 148 | ($prematch, $match); 149 | } 150 | 151 | sub chat_fetch { 152 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 153 | my $ok; 154 | 155 | my ($prematch, $match); 156 | 157 | if ($self->chat_show_conf($t, 'show conf', $show_cmd)) { 158 | return 1; 159 | } 160 | 161 | my $save_timeout; 162 | if (defined($fetch_timeout)) { 163 | $save_timeout = $t->timeout; 164 | $t->timeout($fetch_timeout); 165 | } 166 | 167 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt, '[#>]'); 168 | if (!defined($prematch)) { 169 | $self->log_error("could not find end of configuration"); 170 | return 1; 171 | } 172 | 173 | if (defined($fetch_timeout)) { 174 | $t->timeout($save_timeout); 175 | } 176 | 177 | $self->log_debug("found end of configuration: [$match]"); 178 | 179 | @$conf_ref = split /\n/, $prematch; 180 | 181 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 182 | 183 | undef; 184 | } 185 | 186 | sub do_fetch { 187 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 188 | 189 | $self->log_debug("trying"); 190 | 191 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 192 | if (!defined($dev_repository)) { 193 | $self->log_error("undefined repository"); 194 | return; 195 | } 196 | 197 | if (! -d $dev_repository) { 198 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 199 | return; 200 | } 201 | 202 | if (! -w $dev_repository) { 203 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 204 | return; 205 | } 206 | 207 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 208 | 209 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 210 | 211 | my $ok = $t->open($dev_host); 212 | if (!$ok) { 213 | $self->log_error("could not connect: $!"); 214 | return; 215 | } 216 | 217 | $self->log_debug("connected"); 218 | 219 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 220 | 221 | return unless defined($prompt); 222 | 223 | my @config; 224 | 225 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 226 | 227 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 228 | 229 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 230 | 231 | $ok = $t->close; 232 | if (!$ok) { 233 | $self->log_error("disconnecting: $!"); 234 | } 235 | 236 | $self->log_debug("disconnected"); 237 | 238 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 239 | } 240 | 241 | 1; 242 | -------------------------------------------------------------------------------- /fetchconfig/model/CiscoASA.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2009 rip@devco.net 3 | # Copyright (C) 2006 Everton da Silva Marques 4 | # 5 | # fetchconfig is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2, or (at your option) 8 | # any later version. 9 | # 10 | # fetchconfig is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with fetchconfig; see the file COPYING. If not, write to the 17 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 18 | # MA 02110-1301 USA. 19 | # 20 | # $Id: CiscoASA.pm,v 1.1 2009/01/21 13:22:58 evertonm Exp $ 21 | 22 | package fetchconfig::model::CiscoASA; # fetchconfig/model/CiscoASA.pm 23 | 24 | use strict; 25 | use warnings; 26 | use Net::Telnet; 27 | use fetchconfig::model::Abstract; 28 | 29 | @fetchconfig::model::CiscoASA::ISA = qw(fetchconfig::model::Abstract); 30 | 31 | #################################### 32 | # Implement model::Abstract - Begin 33 | # 34 | 35 | sub label { 36 | 'cisco-asa'; 37 | } 38 | 39 | # "sub new" fully inherited from fetchconfig::model::Abstract 40 | 41 | sub fetch { 42 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 43 | 44 | my $saved_prefix = $self->{log}->prefix; # save log prefix 45 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 46 | 47 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 48 | 49 | # restore log prefix 50 | $self->{log}->prefix($saved_prefix); 51 | 52 | @conf; 53 | } 54 | 55 | # 56 | # Implement model::Abstract - End 57 | ################################## 58 | 59 | sub chat_login { 60 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 61 | my $ok; 62 | 63 | my $login_prompt = '/(Username:|Password:) $/'; 64 | 65 | # chat_banner is used to allow temporary modification 66 | # of timeout throught the 'banner_timeout' option 67 | 68 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 69 | if (!defined($prematch)) { 70 | $self->log_error("could not find login prompt: $login_prompt"); 71 | return undef; 72 | } 73 | 74 | $self->log_debug("found login prompt: [$match]"); 75 | 76 | if ($match =~ /^Username/) { 77 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 78 | if (!defined($dev_user)) { 79 | $self->log_error("login username needed but not provided"); 80 | return undef; 81 | } 82 | 83 | $ok = $t->print($dev_user); 84 | if (!$ok) { 85 | $self->log_error("could not send login username"); 86 | return undef; 87 | } 88 | 89 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 90 | if (!defined($prematch)) { 91 | $self->log_error("could not find password prompt"); 92 | return undef; 93 | } 94 | 95 | $self->log_debug("found password prompt: [$match]"); 96 | } 97 | 98 | if ($match =~ /^Password/) { 99 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 100 | if (!defined($dev_pass)) { 101 | $self->log_error("login password needed but not provided"); 102 | return undef; 103 | } 104 | 105 | $ok = $t->print($dev_pass); 106 | if (!$ok) { 107 | $self->log_error("could not send login password"); 108 | return undef; 109 | } 110 | 111 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)[>#] $/'); 112 | if (!defined($prematch)) { 113 | $self->log_error("could not find command prompt"); 114 | return undef; 115 | } 116 | 117 | $self->log_debug("found command prompt: [$match]"); 118 | } 119 | 120 | if ($match =~ /^\S+> $/) { 121 | $ok = $t->print('enable'); 122 | if (!$ok) { 123 | $self->log_error("could not send enable command"); 124 | return undef; 125 | } 126 | 127 | ($prematch, $match) = $t->waitfor(Match => '/(Password: |\S+# )$/'); 128 | if (!defined($prematch)) { 129 | $self->log_error("could not find enable password prompt"); 130 | return undef; 131 | } 132 | 133 | if ($match =~ /^Password/) { 134 | my $dev_enable = $self->dev_option($dev_opt_tab, "enable"); 135 | if (!defined($dev_enable)) { 136 | $self->log_error("enable password needed but not provided"); 137 | return undef; 138 | } 139 | 140 | $ok = $t->print($dev_enable); 141 | if (!$ok) { 142 | $self->log_error("could not send enable password"); 143 | return undef; 144 | } 145 | 146 | ($prematch, $match) = $t->waitfor(Match => '/\S+# $/'); 147 | if (!defined($prematch)) { 148 | $self->log_error("could not find enable command prompt"); 149 | return undef; 150 | } 151 | } 152 | 153 | $self->log_debug("found enable prompt: [$match]"); 154 | } 155 | 156 | if ($match !~ /^(\S+)\# $/) { 157 | $self->log_error("could not match enable command prompt ($match)"); 158 | return undef; 159 | } 160 | 161 | my $prompt = $1; 162 | 163 | $self->{prompt} = $prompt; # save prompt 164 | 165 | $self->log_debug("logged in prompt=[$prompt]"); 166 | 167 | $prompt; 168 | } 169 | 170 | sub expect_enable_prompt { 171 | my ($self, $t, $prompt) = @_; 172 | 173 | if (!defined($prompt)) { 174 | $self->log_error("internal failure: undefined command prompt"); 175 | return undef; 176 | } 177 | 178 | $prompt =~ s/\//\\\//g; 179 | 180 | my $enable_prompt_regexp = '/' . $prompt . '# $/'; 181 | 182 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 183 | if (!defined($prematch)) { 184 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 185 | } 186 | 187 | ($prematch, $match); 188 | } 189 | 190 | sub chat_fetch { 191 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 192 | my $ok; 193 | 194 | $ok = $t->print('term pager 0'); 195 | if (!$ok) { 196 | $self->log_error("could not send pager disabling command"); 197 | return 1; 198 | } 199 | 200 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 201 | return unless defined($prematch); 202 | 203 | # Backward compatibility support for option "show_cmd=wrterm" 204 | my $custom_cmd; 205 | if (defined($show_cmd)) { 206 | $custom_cmd = ($show_cmd eq 'wrterm') ? 'write term' : $show_cmd; 207 | } 208 | 209 | if ($self->chat_show_conf($t, 'show run', $custom_cmd)) { 210 | return 1; 211 | } 212 | 213 | # Prevent "show run" command from appearing in config dump 214 | $t->getline(); 215 | 216 | # Commment out garbage at top so config file can be restored 217 | # cleanly at a later date 218 | my($line,$top_info); 219 | while($line=$t->getline()) { 220 | if($line=~/^\!/) { 221 | $top_info.=$line; 222 | last; 223 | } else { 224 | $top_info.='!!' . $line; 225 | } 226 | } 227 | 228 | my $save_timeout; 229 | if (defined($fetch_timeout)) { 230 | $save_timeout = $t->timeout; 231 | $t->timeout($fetch_timeout); 232 | } 233 | 234 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 235 | if (!defined($prematch)) { 236 | $self->log_error("could not find end of configuration"); 237 | return 1; 238 | } 239 | 240 | if (defined($fetch_timeout)) { 241 | $t->timeout($save_timeout); 242 | } 243 | 244 | $self->log_debug("found end of configuration: [$match]"); 245 | 246 | @$conf_ref = split /\n/, $top_info . $prematch; 247 | 248 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 249 | 250 | return undef; 251 | } 252 | 253 | sub do_fetch { 254 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 255 | 256 | $self->log_debug("trying"); 257 | 258 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 259 | if (!defined($dev_repository)) { 260 | $self->log_error("undefined repository"); 261 | return; 262 | } 263 | 264 | if (! -d $dev_repository) { 265 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 266 | return; 267 | } 268 | 269 | if (! -w $dev_repository) { 270 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 271 | return; 272 | } 273 | 274 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 275 | 276 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 277 | 278 | my $ok = $t->open($dev_host); 279 | if (!$ok) { 280 | $self->log_error("could not connect: $!"); 281 | return; 282 | } 283 | 284 | $self->log_debug("connected"); 285 | 286 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 287 | 288 | return unless defined($prompt); 289 | 290 | my @config; 291 | 292 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 293 | 294 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 295 | 296 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 297 | 298 | $ok = $t->close; 299 | if (!$ok) { 300 | $self->log_error("disconnecting: $!"); 301 | } 302 | 303 | $self->log_debug("disconnected"); 304 | 305 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 306 | } 307 | 308 | 1; 309 | -------------------------------------------------------------------------------- /fetchconfig/model/CiscoCAT.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2006 Everton da Silva Marques 3 | # Copyright (C) 2006 Marcos Sandez 4 | # 5 | # fetchconfig is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2, or (at your option) 8 | # any later version. 9 | # 10 | # fetchconfig is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with fetchconfig; see the file COPYING. If not, write to the 17 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 18 | # MA 02110-1301 USA. 19 | # 20 | # $Id: CiscoCAT.pm,v 1.2 2006/08/29 19:27:53 evertonm Exp $ 21 | 22 | package fetchconfig::model::CiscoCAT; # fetchconfig/model/CiscoCAT.pm 23 | 24 | use strict; 25 | use warnings; 26 | use Net::Telnet; 27 | use fetchconfig::model::Abstract; 28 | 29 | @fetchconfig::model::CiscoCAT::ISA = qw(fetchconfig::model::Abstract); 30 | 31 | #################################### 32 | # Implement model::Abstract - Begin 33 | # 34 | 35 | sub label { 36 | 'cisco-cat'; 37 | } 38 | 39 | # "sub new" fully inherited from fetchconfig::model::Abstract 40 | 41 | sub fetch { 42 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 43 | 44 | my $saved_prefix = $self->{log}->prefix; # save log prefix 45 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 46 | 47 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 48 | 49 | # restore log prefix 50 | $self->{log}->prefix($saved_prefix); 51 | 52 | @conf; 53 | } 54 | 55 | # 56 | # Implement model::Abstract - End 57 | ################################## 58 | 59 | sub chat_login { 60 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 61 | my $ok; 62 | 63 | my ($prematch, $match) = $t->waitfor(Match => '/(Username:|Password:) $/'); 64 | if (!defined($prematch)) { 65 | $self->log_error("could not find login prompt"); 66 | return undef; 67 | } 68 | 69 | $self->log_debug("found login prompt: [$match]"); 70 | 71 | if ($match =~ /^Username/) { 72 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 73 | if (!defined($dev_user)) { 74 | $self->log_error("login username needed but not provided"); 75 | return undef; 76 | } 77 | 78 | $ok = $t->print($dev_user); 79 | if (!$ok) { 80 | $self->log_error("could not send login username"); 81 | return undef; 82 | } 83 | 84 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 85 | if (!defined($prematch)) { 86 | $self->log_error("could not find password prompt"); 87 | return undef; 88 | } 89 | 90 | $self->log_debug("found password prompt: [$match]"); 91 | } 92 | 93 | if ($match =~ /^Password/) { 94 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 95 | if (!defined($dev_pass)) { 96 | $self->log_error("login password needed but not provided"); 97 | return undef; 98 | } 99 | 100 | $ok = $t->print($dev_pass); 101 | if (!$ok) { 102 | $self->log_error("could not send login password"); 103 | return undef; 104 | } 105 | 106 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)> $/'); 107 | if (!defined($prematch)) { 108 | $self->log_error("could not find command prompt"); 109 | return undef; 110 | } 111 | 112 | $self->log_debug("found command prompt: [$match]"); 113 | } 114 | 115 | if ($match =~ /^\S+> $/) { 116 | $ok = $t->print('enable'); 117 | if (!$ok) { 118 | $self->log_error("could not send enable command"); 119 | return undef; 120 | } 121 | 122 | ($prematch, $match) = $t->waitfor(Match => '/(Password: |\S+> \(enable\) )$/'); 123 | if (!defined($prematch)) { 124 | $self->log_error("could not find enable password prompt"); 125 | return undef; 126 | } 127 | 128 | if ($match =~ /^Password/) { 129 | my $dev_enable = $self->dev_option($dev_opt_tab, "enable"); 130 | if (!defined($dev_enable)) { 131 | $self->log_error("enable password needed but not provided"); 132 | return undef; 133 | } 134 | 135 | $ok = $t->print($dev_enable); 136 | if (!$ok) { 137 | $self->log_error("could not send enable password"); 138 | return undef; 139 | } 140 | 141 | ($prematch, $match) = $t->waitfor(Match => '/\S+> \(enable\) $/'); 142 | if (!defined($prematch)) { 143 | $self->log_error("could not find enable command prompt"); 144 | return undef; 145 | } 146 | } 147 | 148 | $self->log_debug("found enable prompt: [$match]"); 149 | } 150 | 151 | if ($match !~ /^(\S+)> \(enable\) $/) { 152 | $self->log_error("could not match enable command prompt"); 153 | return undef; 154 | } 155 | 156 | my $prompt = $1; 157 | 158 | $self->{prompt} = $prompt; # save prompt 159 | 160 | $self->log_debug("logged in prompt=[$prompt]"); 161 | 162 | $prompt; 163 | } 164 | 165 | sub expect_enable_prompt { 166 | my ($self, $t, $prompt) = @_; 167 | 168 | if (!defined($prompt)) { 169 | $self->log_error("internal failure: undefined command prompt"); 170 | return undef; 171 | } 172 | 173 | my $enable_prompt_regexp = '/' . $prompt . '> \(enable\) $/'; 174 | 175 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 176 | if (!defined($prematch)) { 177 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 178 | } 179 | 180 | ($prematch, $match); 181 | } 182 | 183 | sub chat_fetch { 184 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $conf_ref) = @_; 185 | my $ok; 186 | 187 | $ok = $t->print('set len 0'); 188 | if (!$ok) { 189 | $self->log_error("could not send pager disabling command"); 190 | return 1; 191 | } 192 | 193 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 194 | return unless defined($prematch); 195 | 196 | $ok = $t->print('sh config'); 197 | if (!$ok) { 198 | $self->log_error("could not send show running command"); 199 | return 1; 200 | } 201 | 202 | my $save_timeout; 203 | if (defined($fetch_timeout)) { 204 | $save_timeout = $t->timeout; 205 | $t->timeout($fetch_timeout); 206 | } 207 | 208 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 209 | if (!defined($prematch)) { 210 | $self->log_error("could not find end of configuration"); 211 | return 1; 212 | } 213 | 214 | if (defined($fetch_timeout)) { 215 | $t->timeout($save_timeout); 216 | } 217 | 218 | $self->log_debug("found end of configuration: [$match]"); 219 | 220 | @$conf_ref = split /\n/, $prematch; 221 | 222 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 223 | 224 | return undef; 225 | } 226 | 227 | sub do_fetch { 228 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 229 | 230 | $self->log_debug("trying"); 231 | 232 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 233 | if (!defined($dev_repository)) { 234 | $self->log_error("undefined repository"); 235 | return; 236 | } 237 | 238 | if (! -d $dev_repository) { 239 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 240 | return; 241 | } 242 | 243 | if (! -w $dev_repository) { 244 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 245 | return; 246 | } 247 | 248 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 249 | 250 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 251 | 252 | my $ok = $t->open($dev_host); 253 | if (!$ok) { 254 | $self->log_error("could not connect: $!"); 255 | return; 256 | } 257 | 258 | $self->log_debug("connected"); 259 | 260 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 261 | 262 | return unless defined($prompt); 263 | 264 | my @config; 265 | 266 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 267 | 268 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, \@config); 269 | 270 | $ok = $t->close; 271 | if (!$ok) { 272 | $self->log_error("disconnecting: $!"); 273 | } 274 | 275 | $self->log_debug("disconnected"); 276 | 277 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 278 | } 279 | 280 | 1; 281 | -------------------------------------------------------------------------------- /fetchconfig/model/CiscoIOS.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2006 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: CiscoIOS.pm,v 1.8 2010/12/02 19:38:29 evertonm Exp $ 20 | 21 | package fetchconfig::model::CiscoIOS; # fetchconfig/model/CiscoIOS.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::CiscoIOS::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'cisco-ios'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my $login_prompt = '/(Username:|Password:) $/'; 63 | 64 | # chat_banner is used to allow temporary modification 65 | # of timeout throught the 'banner_timeout' option 66 | 67 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 68 | if (!defined($prematch)) { 69 | $self->log_error("could not find login prompt: $login_prompt"); 70 | return undef; 71 | } 72 | 73 | $self->log_debug("found login prompt: [$match]"); 74 | 75 | if ($match =~ /^Username/) { 76 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 77 | if (!defined($dev_user)) { 78 | $self->log_error("login username needed but not provided"); 79 | return undef; 80 | } 81 | 82 | $ok = $t->print($dev_user); 83 | if (!$ok) { 84 | $self->log_error("could not send login username"); 85 | return undef; 86 | } 87 | 88 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 89 | if (!defined($prematch)) { 90 | $self->log_error("could not find password prompt"); 91 | return undef; 92 | } 93 | 94 | $self->log_debug("found password prompt: [$match]"); 95 | } 96 | 97 | if ($match =~ /^Password/) { 98 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 99 | if (!defined($dev_pass)) { 100 | $self->log_error("login password needed but not provided"); 101 | return undef; 102 | } 103 | 104 | $ok = $t->print($dev_pass); 105 | if (!$ok) { 106 | $self->log_error("could not send login password"); 107 | return undef; 108 | } 109 | 110 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)[>#]$/'); 111 | if (!defined($prematch)) { 112 | $self->log_error("could not find command prompt"); 113 | return undef; 114 | } 115 | 116 | $self->log_debug("found command prompt: [$match]"); 117 | } 118 | 119 | if ($match =~ /^\S+>$/) { 120 | $ok = $t->print('enable'); 121 | if (!$ok) { 122 | $self->log_error("could not send enable command"); 123 | return undef; 124 | } 125 | 126 | ($prematch, $match) = $t->waitfor(Match => '/(Password: |\S+#)$/'); 127 | if (!defined($prematch)) { 128 | $self->log_error("could not find enable password prompt"); 129 | return undef; 130 | } 131 | 132 | if ($match =~ /^Password/) { 133 | my $dev_enable = $self->dev_option($dev_opt_tab, "enable"); 134 | if (!defined($dev_enable)) { 135 | $self->log_error("enable password needed but not provided"); 136 | return undef; 137 | } 138 | 139 | $ok = $t->print($dev_enable); 140 | if (!$ok) { 141 | $self->log_error("could not send enable password"); 142 | return undef; 143 | } 144 | 145 | ($prematch, $match) = $t->waitfor(Match => '/\S+#$/'); 146 | if (!defined($prematch)) { 147 | $self->log_error("could not find enable command prompt"); 148 | return undef; 149 | } 150 | } 151 | 152 | $self->log_debug("found enable prompt: [$match]"); 153 | } 154 | 155 | if ($match !~ /^(\S+)\#$/) { 156 | $self->log_error("could not match enable command prompt"); 157 | return undef; 158 | } 159 | 160 | my $prompt = $1; 161 | 162 | $self->{prompt} = $prompt; # save prompt 163 | 164 | $self->log_debug("logged in prompt=[$prompt]"); 165 | 166 | $prompt; 167 | } 168 | 169 | sub expect_enable_prompt { 170 | my ($self, $t, $prompt) = @_; 171 | 172 | if (!defined($prompt)) { 173 | $self->log_error("internal failure: undefined command prompt"); 174 | return undef; 175 | } 176 | 177 | my $enable_prompt_regexp = '/' . $prompt . '#$/'; 178 | 179 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 180 | if (!defined($prematch)) { 181 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 182 | } 183 | 184 | ($prematch, $match); 185 | } 186 | 187 | sub chat_fetch { 188 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 189 | my $ok; 190 | 191 | $ok = $t->print('term len 0'); 192 | if (!$ok) { 193 | $self->log_error("could not send pager disabling command"); 194 | return 1; 195 | } 196 | 197 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 198 | return unless defined($prematch); 199 | 200 | # Backward compatibility support for option "show_cmd=wrterm" 201 | my $custom_cmd; 202 | if (defined($show_cmd)) { 203 | $custom_cmd = ($show_cmd eq 'wrterm') ? 'write term' : $show_cmd; 204 | } 205 | 206 | if ($self->chat_show_conf($t, 'show run', $custom_cmd)) { 207 | return 1; 208 | } 209 | 210 | # Prevent "show run" command from appearing in config dump 211 | $t->getline(); 212 | 213 | # Commment out garbage at top so config file can be restored 214 | # cleanly at a later date 215 | my($line,$top_info); 216 | while($line=$t->getline()) { 217 | # Failsafe: Just in case 'Current configuration' 218 | # doesn't appear, assume config begins with 'version' 219 | # or first valid comment. 220 | if($line=~/^version / || $line=~/^\!/) { 221 | $top_info.=$line; 222 | last; 223 | } else { 224 | $top_info.='!!' . $line; 225 | } 226 | # Normally, finding the "Current configuration" line 227 | # will be enough to exit this loop. 228 | last if $line=~/^Current configuration/; 229 | } 230 | 231 | my $save_timeout; 232 | if (defined($fetch_timeout)) { 233 | $save_timeout = $t->timeout; 234 | $t->timeout($fetch_timeout); 235 | } 236 | 237 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 238 | if (!defined($prematch)) { 239 | $self->log_error("could not find end of configuration"); 240 | return 1; 241 | } 242 | 243 | if (defined($fetch_timeout)) { 244 | $t->timeout($save_timeout); 245 | } 246 | 247 | $self->log_debug("found end of configuration: [$match]"); 248 | 249 | @$conf_ref = split /\n/, $top_info . $prematch; 250 | 251 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 252 | 253 | undef; 254 | } 255 | 256 | sub do_fetch { 257 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 258 | 259 | $self->log_debug("trying"); 260 | 261 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 262 | if (!defined($dev_repository)) { 263 | $self->log_error("undefined repository"); 264 | return; 265 | } 266 | 267 | if (! -d $dev_repository) { 268 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 269 | return; 270 | } 271 | 272 | if (! -w $dev_repository) { 273 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 274 | return; 275 | } 276 | 277 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 278 | 279 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 280 | 281 | my $ok = $t->open($dev_host); 282 | if (!$ok) { 283 | $self->log_error("could not connect: $!"); 284 | return; 285 | } 286 | 287 | $self->log_debug("connected"); 288 | 289 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 290 | 291 | return unless defined($prompt); 292 | 293 | my @config; 294 | 295 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 296 | 297 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 298 | 299 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 300 | 301 | $ok = $t->close; 302 | if (!$ok) { 303 | $self->log_error("disconnecting: $!"); 304 | } 305 | 306 | $self->log_debug("disconnected"); 307 | 308 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 309 | } 310 | 311 | 1; 312 | -------------------------------------------------------------------------------- /fetchconfig/model/CiscoIOSXR.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2015 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id$ 20 | 21 | package fetchconfig::model::CiscoIOSXR; # fetchconfig/model/CiscoIOSXR.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::CiscoIOSXR::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'cisco-iosxr'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my $login_prompt = '/(Username:|Password:) $/'; 63 | 64 | # chat_banner is used to allow temporary modification 65 | # of timeout throught the 'banner_timeout' option 66 | 67 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 68 | if (!defined($prematch)) { 69 | $self->log_error("could not find login prompt: $login_prompt"); 70 | return undef; 71 | } 72 | 73 | $self->log_debug("found login prompt: [$match]"); 74 | 75 | if ($match =~ /^Username/) { 76 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 77 | if (!defined($dev_user)) { 78 | $self->log_error("login username needed but not provided"); 79 | return undef; 80 | } 81 | 82 | $ok = $t->print($dev_user); 83 | if (!$ok) { 84 | $self->log_error("could not send login username"); 85 | return undef; 86 | } 87 | 88 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 89 | if (!defined($prematch)) { 90 | $self->log_error("could not find password prompt"); 91 | return undef; 92 | } 93 | 94 | $self->log_debug("found password prompt: [$match]"); 95 | } 96 | 97 | if ($match =~ /^Password/) { 98 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 99 | if (!defined($dev_pass)) { 100 | $self->log_error("login password needed but not provided"); 101 | return undef; 102 | } 103 | 104 | $ok = $t->print($dev_pass); 105 | if (!$ok) { 106 | $self->log_error("could not send login password"); 107 | return undef; 108 | } 109 | 110 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)[#]$/'); 111 | if (!defined($prematch)) { 112 | $self->log_error("could not find command prompt"); 113 | return undef; 114 | } 115 | 116 | $self->log_debug("found command prompt: [$match]"); 117 | } 118 | 119 | if ($match !~ /^(\S+)\#$/) { 120 | $self->log_error("could not match command prompt"); 121 | return undef; 122 | } 123 | 124 | my $prompt = $1; 125 | 126 | $self->{prompt} = $prompt; # save prompt 127 | 128 | $self->log_debug("logged in prompt=[$prompt]"); 129 | 130 | $prompt; 131 | } 132 | 133 | sub expect_enable_prompt { 134 | my ($self, $t, $prompt) = @_; 135 | 136 | if (!defined($prompt)) { 137 | $self->log_error("internal failure: undefined command prompt"); 138 | return undef; 139 | } 140 | 141 | my $enable_prompt_regexp = $prompt; 142 | 143 | $enable_prompt_regexp =~ s{/}{\\/}g; # replace / with \/ 144 | 145 | $enable_prompt_regexp = '/' . $enable_prompt_regexp . '#$/'; 146 | 147 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 148 | if (!defined($prematch)) { 149 | $self->log_error("could not match enable command prompt: prompt='$prompt' regexp='$enable_prompt_regexp'"); 150 | } 151 | 152 | ($prematch, $match); 153 | } 154 | 155 | sub chat_fetch { 156 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 157 | my $ok; 158 | 159 | $ok = $t->print('term len 0'); 160 | if (!$ok) { 161 | $self->log_error("could not send pager disabling command"); 162 | return 1; 163 | } 164 | 165 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 166 | return unless defined($prematch); 167 | 168 | # Backward compatibility support for option "show_cmd=wrterm" 169 | my $custom_cmd; 170 | if (defined($show_cmd)) { 171 | $custom_cmd = ($show_cmd eq 'wrterm') ? 'write term' : $show_cmd; 172 | } 173 | 174 | if ($self->chat_show_conf($t, 'show run', $custom_cmd)) { 175 | return 1; 176 | } 177 | 178 | $self->log_debug("commenting out header of show run output"); 179 | 180 | # Commment out garbage at top so config file can be restored 181 | # cleanly at a later date 182 | my($line,$top_info); 183 | while($line=$t->getline()) { 184 | # Failsafe: Just in case 'Current configuration' 185 | # doesn't appear, assume config begins with 'version' 186 | # or first valid comment. 187 | if($line=~/^version / || $line=~/^\!/) { 188 | $top_info.=$line; 189 | last; 190 | } else { 191 | $top_info.='!!' . $line; 192 | } 193 | # Normally, finding the "Current configuration" line 194 | # will be enough to exit this loop. 195 | last if $line=~/^Current configuration/; 196 | } 197 | 198 | $self->log_debug("commenting out header of show run output: done"); 199 | 200 | my $save_timeout; 201 | if (defined($fetch_timeout)) { 202 | $save_timeout = $t->timeout; 203 | $t->timeout($fetch_timeout); 204 | } 205 | 206 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 207 | if (!defined($prematch)) { 208 | $self->log_error("could not find end of configuration"); 209 | return 1; 210 | } 211 | 212 | if (defined($fetch_timeout)) { 213 | $t->timeout($save_timeout); 214 | } 215 | 216 | $self->log_debug("found end of configuration: [$match]"); 217 | 218 | @$conf_ref = split /\n/, $top_info . $prematch; 219 | 220 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 221 | 222 | undef; 223 | } 224 | 225 | sub do_fetch { 226 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 227 | 228 | $self->log_debug("trying"); 229 | 230 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 231 | if (!defined($dev_repository)) { 232 | $self->log_error("undefined repository"); 233 | return; 234 | } 235 | 236 | if (! -d $dev_repository) { 237 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 238 | return; 239 | } 240 | 241 | if (! -w $dev_repository) { 242 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 243 | return; 244 | } 245 | 246 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 247 | 248 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 249 | 250 | my $ok = $t->open($dev_host); 251 | if (!$ok) { 252 | $self->log_error("could not connect: $!"); 253 | return; 254 | } 255 | 256 | $self->log_debug("connected"); 257 | 258 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 259 | 260 | return unless defined($prompt); 261 | 262 | my @config; 263 | 264 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 265 | 266 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 267 | 268 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 269 | 270 | $ok = $t->close; 271 | if (!$ok) { 272 | $self->log_error("disconnecting: $!"); 273 | } 274 | 275 | $self->log_debug("disconnected"); 276 | 277 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 278 | } 279 | 280 | 1; 281 | -------------------------------------------------------------------------------- /fetchconfig/model/CiscoPIX.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2009 rip@devco.net 3 | # Copyright (C) 2006 Everton da Silva Marques 4 | # 5 | # fetchconfig is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2, or (at your option) 8 | # any later version. 9 | # 10 | # fetchconfig is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with fetchconfig; see the file COPYING. If not, write to the 17 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 18 | # MA 02110-1301 USA. 19 | # 20 | # $Id: CiscoPIX.pm,v 1.2 2011/06/09 14:52:01 evertonm Exp $ 21 | 22 | package fetchconfig::model::CiscoPIX; # fetchconfig/model/CiscoPIX.pm 23 | 24 | use strict; 25 | use warnings; 26 | use Net::Telnet; 27 | use fetchconfig::model::Abstract; 28 | 29 | @fetchconfig::model::CiscoPIX::ISA = qw(fetchconfig::model::Abstract); 30 | 31 | #################################### 32 | # Implement model::Abstract - Begin 33 | # 34 | 35 | sub label { 36 | 'cisco-pix'; 37 | } 38 | 39 | # "sub new" fully inherited from fetchconfig::model::Abstract 40 | 41 | sub fetch { 42 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 43 | 44 | my $saved_prefix = $self->{log}->prefix; # save log prefix 45 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 46 | 47 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 48 | 49 | # restore log prefix 50 | $self->{log}->prefix($saved_prefix); 51 | 52 | @conf; 53 | } 54 | 55 | # 56 | # Implement model::Abstract - End 57 | ################################## 58 | 59 | sub chat_login { 60 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 61 | my $ok; 62 | 63 | my $login_prompt = '/(Username:|Password:) $/'; 64 | 65 | # chat_banner is used to allow temporary modification 66 | # of timeout throught the 'banner_timeout' option 67 | 68 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 69 | if (!defined($prematch)) { 70 | $self->log_error("could not find login prompt: $login_prompt"); 71 | return undef; 72 | } 73 | 74 | $self->log_debug("found login prompt: [$match]"); 75 | 76 | if ($match =~ /^Username/) { 77 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 78 | if (!defined($dev_user)) { 79 | $self->log_error("login username needed but not provided"); 80 | return undef; 81 | } 82 | 83 | $ok = $t->print($dev_user); 84 | if (!$ok) { 85 | $self->log_error("could not send login username"); 86 | return undef; 87 | } 88 | 89 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 90 | if (!defined($prematch)) { 91 | $self->log_error("could not find password prompt"); 92 | return undef; 93 | } 94 | 95 | $self->log_debug("found password prompt: [$match]"); 96 | } 97 | 98 | if ($match =~ /^Password/) { 99 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 100 | if (!defined($dev_pass)) { 101 | $self->log_error("login password needed but not provided"); 102 | return undef; 103 | } 104 | 105 | $ok = $t->print($dev_pass); 106 | if (!$ok) { 107 | $self->log_error("could not send login password"); 108 | return undef; 109 | } 110 | 111 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)[>#] $/'); 112 | if (!defined($prematch)) { 113 | $self->log_error("could not find command prompt"); 114 | return undef; 115 | } 116 | 117 | $self->log_debug("found command prompt: [$match]"); 118 | } 119 | 120 | if ($match =~ /^\S+> $/) { 121 | $ok = $t->print('enable'); 122 | if (!$ok) { 123 | $self->log_error("could not send enable command"); 124 | return undef; 125 | } 126 | 127 | ($prematch, $match) = $t->waitfor(Match => '/(Password: |\S+# )$/'); 128 | if (!defined($prematch)) { 129 | $self->log_error("could not find enable password prompt"); 130 | return undef; 131 | } 132 | 133 | if ($match =~ /^Password/) { 134 | my $dev_enable = $self->dev_option($dev_opt_tab, "enable"); 135 | if (!defined($dev_enable)) { 136 | $self->log_error("enable password needed but not provided"); 137 | return undef; 138 | } 139 | 140 | $ok = $t->print($dev_enable); 141 | if (!$ok) { 142 | $self->log_error("could not send enable password"); 143 | return undef; 144 | } 145 | 146 | ($prematch, $match) = $t->waitfor(Match => '/\S+# $/'); 147 | if (!defined($prematch)) { 148 | $self->log_error("could not find enable command prompt"); 149 | return undef; 150 | } 151 | } 152 | 153 | $self->log_debug("found enable prompt: [$match]"); 154 | } 155 | 156 | if ($match !~ /^(\S+)\# $/) { 157 | $self->log_error("could not match enable command prompt ($match)"); 158 | return undef; 159 | } 160 | 161 | my $prompt = $1; 162 | 163 | $self->{prompt} = $prompt; # save prompt 164 | 165 | $self->log_debug("logged in prompt=[$prompt]"); 166 | 167 | $prompt; 168 | } 169 | 170 | sub expect_enable_prompt { 171 | my ($self, $t, $prompt) = @_; 172 | 173 | if (!defined($prompt)) { 174 | $self->log_error("internal failure: undefined command prompt"); 175 | return undef; 176 | } 177 | 178 | $prompt =~ s/\//\\\//g; # quote / as \/ 179 | 180 | my $enable_prompt_regexp = '/' . $prompt . '# $/'; 181 | 182 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 183 | if (!defined($prematch)) { 184 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 185 | } 186 | 187 | ($prematch, $match); 188 | } 189 | 190 | sub chat_fetch { 191 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 192 | my $ok; 193 | 194 | $ok = $t->print('pager 0'); 195 | if (!$ok) { 196 | $self->log_error("could not send pager disabling command"); 197 | return 1; 198 | } 199 | 200 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 201 | return unless defined($prematch); 202 | 203 | # Backward compatibility support for option "show_cmd=wrterm" 204 | my $custom_cmd; 205 | if (defined($show_cmd)) { 206 | $custom_cmd = ($show_cmd eq 'wrterm') ? 'write term' : $show_cmd; 207 | } 208 | 209 | if ($self->chat_show_conf($t, 'show run', $custom_cmd)) { 210 | return 1; 211 | } 212 | 213 | # Prevent "show run" command from appearing in config dump 214 | $t->getline(); 215 | 216 | # Commment out garbage at top so config file can be restored 217 | # cleanly at a later date 218 | my($line,$top_info); 219 | while($line=$t->getline()) { 220 | if($line=~/^:/) { 221 | $top_info.=$line; 222 | last; 223 | } else { 224 | $top_info.='::' . $line; 225 | } 226 | } 227 | 228 | my $save_timeout; 229 | if (defined($fetch_timeout)) { 230 | $save_timeout = $t->timeout; 231 | $t->timeout($fetch_timeout); 232 | } 233 | 234 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 235 | if (!defined($prematch)) { 236 | $self->log_error("could not find end of configuration"); 237 | return 1; 238 | } 239 | 240 | if (defined($fetch_timeout)) { 241 | $t->timeout($save_timeout); 242 | } 243 | 244 | $self->log_debug("found end of configuration: [$match]"); 245 | 246 | @$conf_ref = split /\n/, $top_info . $prematch; 247 | 248 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 249 | 250 | return undef; 251 | } 252 | 253 | sub do_fetch { 254 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 255 | 256 | $self->log_debug("trying"); 257 | 258 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 259 | if (!defined($dev_repository)) { 260 | $self->log_error("undefined repository"); 261 | return; 262 | } 263 | 264 | if (! -d $dev_repository) { 265 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 266 | return; 267 | } 268 | 269 | if (! -w $dev_repository) { 270 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 271 | return; 272 | } 273 | 274 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 275 | 276 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 277 | 278 | my $ok = $t->open($dev_host); 279 | if (!$ok) { 280 | $self->log_error("could not connect: $!"); 281 | return; 282 | } 283 | 284 | $self->log_debug("connected"); 285 | 286 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 287 | 288 | return unless defined($prompt); 289 | 290 | my @config; 291 | 292 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 293 | 294 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 295 | 296 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 297 | 298 | $ok = $t->close; 299 | if (!$ok) { 300 | $self->log_error("disconnecting: $!"); 301 | } 302 | 303 | $self->log_debug("disconnected"); 304 | 305 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 306 | } 307 | 308 | 1; 309 | -------------------------------------------------------------------------------- /fetchconfig/model/CiscoSG300.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2006 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: CiscoSG300.pm,v 1.8 2010/12/02 19:38:29 evertonm Exp $ 20 | 21 | package fetchconfig::model::CiscoSG300; # fetchconfig/model/CiscoSG300.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::CiscoSG300::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'cisco-sg300'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my $login_prompt = '/(User Name:|Username: |Password: |Password:)$/'; 63 | 64 | # chat_banner is used to allow temporary modification 65 | # of timeout throught the 'banner_timeout' option 66 | 67 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 68 | if (!defined($prematch)) { 69 | $self->log_error("could not find login prompt: $login_prompt"); 70 | return undef; 71 | } 72 | 73 | $self->log_debug("found login prompt: [$match]"); 74 | 75 | if ($match =~ /^User\s*(n|N)ame/) { 76 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 77 | if (!defined($dev_user)) { 78 | $self->log_error("login username needed but not provided"); 79 | return undef; 80 | } 81 | 82 | $ok = $t->print($dev_user); 83 | if (!$ok) { 84 | $self->log_error("could not send login username"); 85 | return undef; 86 | } 87 | 88 | ($prematch, $match) = $t->waitfor(Match => '/Password:\s*$/'); 89 | if (!defined($prematch)) { 90 | $self->log_error("could not find password prompt"); 91 | return undef; 92 | } 93 | 94 | $self->log_debug("found password prompt: [$match]"); 95 | } 96 | 97 | if ($match =~ /^Password/) { 98 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 99 | if (!defined($dev_pass)) { 100 | $self->log_error("login password needed but not provided"); 101 | return undef; 102 | } 103 | 104 | $ok = $t->print($dev_pass); 105 | if (!$ok) { 106 | $self->log_error("could not send login password"); 107 | return undef; 108 | } 109 | 110 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)[>#]$/'); 111 | if (!defined($prematch)) { 112 | $self->log_error("could not find command prompt"); 113 | return undef; 114 | } 115 | 116 | $self->log_debug("found command prompt: [$match]"); 117 | } 118 | 119 | if ($match =~ /^\S+>$/) { 120 | $ok = $t->print('enable'); 121 | if (!$ok) { 122 | $self->log_error("could not send enable command"); 123 | return undef; 124 | } 125 | 126 | ($prematch, $match) = $t->waitfor(Match => '/(Password: |\S+#)$/'); 127 | if (!defined($prematch)) { 128 | $self->log_error("could not find enable password prompt"); 129 | return undef; 130 | } 131 | 132 | if ($match =~ /^Password/) { 133 | my $dev_enable = $self->dev_option($dev_opt_tab, "enable"); 134 | if (!defined($dev_enable)) { 135 | $self->log_error("enable password needed but not provided"); 136 | return undef; 137 | } 138 | 139 | $ok = $t->print($dev_enable); 140 | if (!$ok) { 141 | $self->log_error("could not send enable password"); 142 | return undef; 143 | } 144 | 145 | ($prematch, $match) = $t->waitfor(Match => '/\S+#$/'); 146 | if (!defined($prematch)) { 147 | $self->log_error("could not find enable command prompt"); 148 | return undef; 149 | } 150 | } 151 | 152 | $self->log_debug("found enable prompt: [$match]"); 153 | } 154 | 155 | if ($match !~ /^(\S+)\#$/) { 156 | $self->log_error("could not match enable command prompt"); 157 | return undef; 158 | } 159 | 160 | my $prompt = $1; 161 | 162 | $self->{prompt} = $prompt; # save prompt 163 | 164 | $self->log_debug("logged in prompt=[$prompt]"); 165 | 166 | $prompt; 167 | } 168 | 169 | sub expect_enable_prompt { 170 | my ($self, $t, $prompt) = @_; 171 | 172 | if (!defined($prompt)) { 173 | $self->log_error("internal failure: undefined command prompt"); 174 | return undef; 175 | } 176 | 177 | my ($prematch, $match) = $t->waitfor(Match => '/\S+#$/'); 178 | if (!defined($prematch)) { 179 | $self->log_error("could not match enable command prompt"); 180 | } 181 | 182 | ($prematch, $match); 183 | } 184 | 185 | sub chat_fetch { 186 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 187 | my $ok; 188 | 189 | $ok = $t->print('terminal datadump'); 190 | if (!$ok) { 191 | $self->log_error("could not send pager disabling command"); 192 | return 1; 193 | } 194 | 195 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 196 | return unless defined($prematch); 197 | 198 | # Backward compatibility support for option "show_cmd=wrterm" 199 | my $custom_cmd; 200 | if (defined($show_cmd)) { 201 | $custom_cmd = ($show_cmd eq 'wrterm') ? 'write term' : $show_cmd; 202 | } 203 | 204 | if ($self->chat_show_conf($t, 'show run', $custom_cmd)) { 205 | return 1; 206 | } 207 | 208 | # Prevent "show run" command from appearing in config dump 209 | $t->getline(); 210 | 211 | my $save_timeout; 212 | if (defined($fetch_timeout)) { 213 | $save_timeout = $t->timeout; 214 | $t->timeout($fetch_timeout); 215 | } 216 | 217 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 218 | if (!defined($prematch)) { 219 | $self->log_error("could not find end of configuration"); 220 | return 1; 221 | } 222 | 223 | if (defined($fetch_timeout)) { 224 | $t->timeout($save_timeout); 225 | } 226 | 227 | $self->log_debug("found end of configuration: [$match]"); 228 | 229 | @$conf_ref = split /\n/, $prematch; 230 | 231 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 232 | 233 | undef; 234 | } 235 | 236 | sub do_fetch { 237 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 238 | 239 | $self->log_debug("trying"); 240 | 241 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 242 | if (!defined($dev_repository)) { 243 | $self->log_error("undefined repository"); 244 | return; 245 | } 246 | 247 | if (! -d $dev_repository) { 248 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 249 | return; 250 | } 251 | 252 | if (! -w $dev_repository) { 253 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 254 | return; 255 | } 256 | 257 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 258 | 259 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout, Max_buffer_length => 10485760); 260 | 261 | my $ok = $t->open($dev_host); 262 | if (!$ok) { 263 | $self->log_error("could not connect: $!"); 264 | return; 265 | } 266 | 267 | $self->log_debug("connected"); 268 | 269 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 270 | 271 | return unless defined($prompt); 272 | 273 | my @config; 274 | 275 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 276 | 277 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 278 | 279 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 280 | 281 | $ok = $t->close; 282 | if (!$ok) { 283 | $self->log_error("disconnecting: $!"); 284 | } 285 | 286 | $self->log_debug("disconnected"); 287 | 288 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 289 | } 290 | 291 | 1; 292 | -------------------------------------------------------------------------------- /fetchconfig/model/Coriant8600.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2015 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id$ 20 | 21 | package fetchconfig::model::Coriant8600; # fetchconfig/model/Coriant8600.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::Coriant8600::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'coriant-8600'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my $login_prompt = '/(user name:|password:)$/'; 63 | 64 | # chat_banner is used to allow temporary modification 65 | # of timeout throught the 'banner_timeout' option 66 | 67 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 68 | if (!defined($prematch)) { 69 | $self->log_error("could not find login prompt: $login_prompt"); 70 | return undef; 71 | } 72 | 73 | $self->log_debug("found login prompt: [$match]"); 74 | 75 | if ($match =~ /^user/) { 76 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 77 | if (!defined($dev_user)) { 78 | $self->log_error("login username needed but not provided"); 79 | return undef; 80 | } 81 | 82 | $ok = $t->print($dev_user); 83 | if (!$ok) { 84 | $self->log_error("could not send login username"); 85 | return undef; 86 | } 87 | 88 | ($prematch, $match) = $t->waitfor(Match => '/password:$/'); 89 | if (!defined($prematch)) { 90 | $self->log_error("could not find password prompt"); 91 | return undef; 92 | } 93 | 94 | $self->log_debug("found password prompt: [$match]"); 95 | } 96 | 97 | if ($match =~ /^password/) { 98 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 99 | if (!defined($dev_pass)) { 100 | $self->log_error("login password needed but not provided"); 101 | return undef; 102 | } 103 | 104 | $ok = $t->print($dev_pass); 105 | if (!$ok) { 106 | $self->log_error("could not send login password"); 107 | return undef; 108 | } 109 | 110 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)[>#]$/'); 111 | if (!defined($prematch)) { 112 | $self->log_error("could not find command prompt"); 113 | return undef; 114 | } 115 | 116 | $self->log_debug("found command prompt: [$match]"); 117 | } 118 | 119 | if ($match =~ /^\S+>$/) { 120 | $ok = $t->print('enable'); 121 | if (!$ok) { 122 | $self->log_error("could not send enable command"); 123 | return undef; 124 | } 125 | 126 | ($prematch, $match) = $t->waitfor(Match => '/(Password: |\S+#)$/'); 127 | if (!defined($prematch)) { 128 | $self->log_error("could not find enable password prompt"); 129 | return undef; 130 | } 131 | 132 | if ($match =~ /^password/) { 133 | my $dev_enable = $self->dev_option($dev_opt_tab, "enable"); 134 | if (!defined($dev_enable)) { 135 | $self->log_error("enable password needed but not provided"); 136 | return undef; 137 | } 138 | 139 | $ok = $t->print($dev_enable); 140 | if (!$ok) { 141 | $self->log_error("could not send enable password"); 142 | return undef; 143 | } 144 | 145 | ($prematch, $match) = $t->waitfor(Match => '/\S+#$/'); 146 | if (!defined($prematch)) { 147 | $self->log_error("could not find enable command prompt"); 148 | return undef; 149 | } 150 | } 151 | 152 | $self->log_debug("found enable prompt: [$match]"); 153 | } 154 | 155 | if ($match !~ /^(\S+)\#$/) { 156 | $self->log_error("could not match enable command prompt"); 157 | return undef; 158 | } 159 | 160 | my $prompt = $1; 161 | 162 | $self->{prompt} = $prompt; # save prompt 163 | 164 | $self->log_debug("logged in prompt=[$prompt]"); 165 | 166 | $prompt; 167 | } 168 | 169 | sub expect_enable_prompt { 170 | my ($self, $t, $prompt) = @_; 171 | 172 | if (!defined($prompt)) { 173 | $self->log_error("internal failure: undefined command prompt"); 174 | return undef; 175 | } 176 | 177 | my $enable_prompt_regexp = '/' . $prompt . '#$/'; 178 | 179 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 180 | if (!defined($prematch)) { 181 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 182 | } 183 | 184 | ($prematch, $match); 185 | } 186 | 187 | sub chat_fetch { 188 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 189 | my ($ok, $prematch, $match); 190 | 191 | # 192 | # turn off paging 193 | # 194 | $ok = $t->print('terminal more off'); 195 | if (!$ok) { 196 | $self->log_error("could not send pager disabling command"); 197 | return 1; 198 | } 199 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 200 | return 1 unless defined($prematch); 201 | 202 | if ($self->chat_show_conf($t, 'show run', $show_cmd)) { 203 | return 1; 204 | } 205 | 206 | my $save_timeout; 207 | if (defined($fetch_timeout)) { 208 | $save_timeout = $t->timeout; 209 | $t->timeout($fetch_timeout); 210 | } 211 | 212 | ($prematch, $match) = $self->expect_enable_prompt_paging_auto($t, $prompt, '--More--'); 213 | if (!defined($prematch)) { 214 | $self->log_error("could not find end of configuration"); 215 | return 1; 216 | } 217 | 218 | if (defined($fetch_timeout)) { 219 | $t->timeout($save_timeout); 220 | } 221 | 222 | $self->log_debug("found end of configuration: [$match]"); 223 | 224 | @$conf_ref = split /\n/, $prematch; 225 | 226 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 227 | 228 | # 229 | # turn on paging 230 | # 231 | $ok = $t->print('terminal more on'); 232 | if (!$ok) { 233 | $self->log_error("could not send pager enabling command"); 234 | return 1; 235 | } 236 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 237 | return 1 unless defined($prematch); 238 | 239 | return undef; 240 | } 241 | 242 | sub do_fetch { 243 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 244 | 245 | $self->log_debug("trying"); 246 | 247 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 248 | if (!defined($dev_repository)) { 249 | $self->log_error("undefined repository"); 250 | return; 251 | } 252 | 253 | if (! -d $dev_repository) { 254 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 255 | return; 256 | } 257 | 258 | if (! -w $dev_repository) { 259 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 260 | return; 261 | } 262 | 263 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 264 | 265 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 266 | 267 | my $ok = $t->open($dev_host); 268 | if (!$ok) { 269 | $self->log_error("could not connect: $!"); 270 | return; 271 | } 272 | 273 | $self->log_debug("connected"); 274 | 275 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 276 | 277 | return unless defined($prompt); 278 | 279 | my @config; 280 | 281 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 282 | 283 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 284 | 285 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 286 | 287 | $ok = $t->close; 288 | if (!$ok) { 289 | $self->log_error("disconnecting: $!"); 290 | } 291 | 292 | $self->log_debug("disconnected"); 293 | 294 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 295 | } 296 | 297 | 1; 298 | -------------------------------------------------------------------------------- /fetchconfig/model/Dell.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2007 Julien Louis 3 | # Copyright (C) 2006 Everton da Silva Marques 4 | # 5 | # fetchconfig is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2, or (at your option) 8 | # any later version. 9 | # 10 | # fetchconfig is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with fetchconfig; see the file COPYING. If not, write to the 17 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 18 | # MA 02110-1301 USA. 19 | # 20 | # $Id: Dell.pm,v 1.2 2009/02/03 11:07:55 evertonm Exp $ 21 | 22 | package fetchconfig::model::Dell; # fetchconfig/model/Dell.pm 23 | 24 | use strict; 25 | use warnings; 26 | use Net::Telnet; 27 | use fetchconfig::model::Abstract; 28 | 29 | @fetchconfig::model::Dell::ISA = qw(fetchconfig::model::Abstract); 30 | 31 | #################################### 32 | # Implement model::Abstract - Begin 33 | # 34 | 35 | sub label { 36 | 'dell'; 37 | } 38 | 39 | # "sub new" fully inherited from fetchconfig::model::Abstract 40 | 41 | sub fetch { 42 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 43 | 44 | my $saved_prefix = $self->{log}->prefix; # save log prefix 45 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 46 | 47 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 48 | 49 | # restore log prefix 50 | $self->{log}->prefix($saved_prefix); 51 | 52 | @conf; 53 | } 54 | 55 | # 56 | # Implement model::Abstract - End 57 | ################################## 58 | 59 | sub chat_login { 60 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 61 | my $ok; 62 | 63 | my $login_prompt = '/(User Name:|User:|Password:) ?$/'; 64 | 65 | my ($prematch, $match) = $t->waitfor(Match => $login_prompt); 66 | if (!defined($prematch)) { 67 | $self->log_error("could not find login prompt: $login_prompt"); 68 | return undef; 69 | } 70 | 71 | $self->log_debug("found login prompt: [$match]"); 72 | 73 | if ($match =~ /^User/) { 74 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 75 | if (!defined($dev_user)) { 76 | $self->log_error("login username needed but not provided"); 77 | return undef; 78 | } 79 | 80 | $ok = $t->print($dev_user); 81 | if (!$ok) { 82 | $self->log_error("could not send login username"); 83 | return undef; 84 | } 85 | 86 | ($prematch, $match) = $t->waitfor(Match => '/Password: ?$/'); 87 | if (!defined($prematch)) { 88 | $self->log_error("could not find password prompt"); 89 | return undef; 90 | } 91 | 92 | $self->log_debug("found password prompt: [$match]"); 93 | } 94 | 95 | if ($match =~ /^Password/) { 96 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 97 | if (!defined($dev_pass)) { 98 | $self->log_error("login password needed but not provided"); 99 | return undef; 100 | } 101 | $ok = $t->print($dev_pass); 102 | if (!$ok) { 103 | $self->log_error("could not send login password"); 104 | return undef; 105 | } 106 | 107 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)[>#] ?$/'); 108 | if (!defined($prematch)) { 109 | $self->log_error("could not find command prompt"); 110 | return undef; 111 | } 112 | 113 | if ($match =~/>$/) { 114 | $ok = $t->print('enable'); 115 | if (!$ok) { 116 | $self->log_error("could not send enable command"); 117 | return undef; 118 | } 119 | 120 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)[#] ?$/'); 121 | if (!defined($prematch)) { 122 | $self->log_error("could not find command prompt"); 123 | return undef; 124 | } 125 | } 126 | 127 | $self->log_debug("found command prompt: [$match]"); 128 | } 129 | 130 | if ($match !~ /^(\S+)\# ?$/) { 131 | $self->log_error("could not match enable command prompt"); 132 | return undef; 133 | } 134 | 135 | my $prompt = $1; 136 | 137 | $self->{prompt} = $prompt; # save prompt 138 | 139 | $self->log_debug("logged in prompt=[$prompt]"); 140 | 141 | $prompt; 142 | } 143 | 144 | sub expect_enable_prompt { 145 | my ($self, $t, $prompt) = @_; 146 | 147 | if (!defined($prompt)) { 148 | $self->log_error("internal failure: undefined command prompt"); 149 | return undef; 150 | } 151 | 152 | my $enable_prompt_regexp = '/' . $prompt . '# ?$/'; 153 | 154 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 155 | if (!defined($prematch)) { 156 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 157 | } 158 | 159 | ($prematch, $match); 160 | } 161 | 162 | sub expect_enable_prompt_paging { 163 | my ($self, $t, $prompt, $paging_prompt) = @_; 164 | 165 | if (!defined($prompt)) { 166 | $self->log_error("internal failure: undefined command prompt"); 167 | return undef; 168 | } 169 | 170 | my $prompt_regexp = '/(' . $prompt . '#)|(\Q' . $paging_prompt . '\E)$/'; 171 | my $paging_prompt_regexp = '/' . $paging_prompt . '$/'; 172 | 173 | my ($prematch, $match, $full_prematch); 174 | 175 | for (;;) { 176 | ($prematch, $match) = $t->waitfor(Match => $prompt_regexp); 177 | if (!defined($prematch)) { 178 | $self->log_error("could not match enable/paging prompt: $prompt_regexp"); 179 | return; # signals error with undef 180 | } 181 | 182 | #$self->log_debug("paging match: [$match]"); 183 | 184 | $full_prematch .= $prematch; 185 | 186 | if ($match ne $paging_prompt) { 187 | #$self->log_debug("done paging match: [$match][$paging_prompt_regexp]"); 188 | last; 189 | } 190 | 191 | # Do paging 192 | my $ok = $t->put(' '); # SPACE 193 | if (!$ok) { 194 | $self->log_error("could not send paging SPACE command"); 195 | return; # signals error with undef 196 | } 197 | } 198 | 199 | ($full_prematch, $match); 200 | } 201 | 202 | sub chat_fetch { 203 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 204 | my $ok; 205 | 206 | $ok = $t->print('term datadump'); 207 | if (!$ok) { 208 | $self->log_error("could not send pager disabling command"); 209 | return 1; 210 | } 211 | 212 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 213 | return unless defined($prematch); 214 | 215 | my $full_show_cmd="show run"; 216 | if(defined($show_cmd)) { 217 | if($show_cmd eq "wrterm") { 218 | $full_show_cmd="write term"; 219 | } 220 | } 221 | 222 | $ok = $t->print($full_show_cmd); 223 | if (!$ok) { 224 | $self->log_error("could not send show run command: $full_show_cmd"); 225 | return 1; 226 | } 227 | 228 | # Prevent "show run" command from appearing in config dump 229 | $t->getline(); 230 | 231 | # Commment out garbage at top so config file can be restored 232 | # cleanly at a later date 233 | my($line,$top_info); 234 | while($line=$t->getline()) { 235 | # Failsafe: Just in case 'Current configuration' 236 | # doesn't appear, assume config begins with 'version' 237 | # or first valid comment. 238 | if($line=~/^version / || $line=~/^\!/) { 239 | $top_info.=$line; 240 | last; 241 | } else { 242 | $top_info .= $line; 243 | } 244 | # Normally, finding the "Current configuration" line 245 | # will be enough to exit this loop. 246 | last if $line=~/^Current configuration/; 247 | } 248 | 249 | my $save_timeout; 250 | if (defined($fetch_timeout)) { 251 | $save_timeout = $t->timeout; 252 | $t->timeout($fetch_timeout); 253 | } 254 | 255 | ($prematch, $match) = $self->expect_enable_prompt_paging($t, $prompt, '--More-- or (q)uit'); 256 | if (!defined($prematch)) { 257 | $self->log_error("could not find end of configuration"); 258 | return 1; 259 | } 260 | 261 | if (defined($fetch_timeout)) { 262 | $t->timeout($save_timeout); 263 | } 264 | 265 | $self->log_debug("found end of configuration: [$match]"); 266 | 267 | @$conf_ref = split /\n/, $top_info . $prematch; 268 | 269 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 270 | 271 | return undef; 272 | } 273 | 274 | sub do_fetch { 275 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 276 | 277 | $self->log_debug("trying"); 278 | 279 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 280 | if (!defined($dev_repository)) { 281 | $self->log_error("undefined repository"); 282 | return; 283 | } 284 | 285 | if (! -d $dev_repository) { 286 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 287 | return; 288 | } 289 | 290 | if (! -w $dev_repository) { 291 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 292 | return; 293 | } 294 | 295 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 296 | 297 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 298 | 299 | my $ok = $t->open($dev_host); 300 | if (!$ok) { 301 | $self->log_error("could not connect: $!"); 302 | return; 303 | } 304 | 305 | $self->log_debug("connected"); 306 | 307 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 308 | 309 | return unless defined($prompt); 310 | 311 | my @config; 312 | 313 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 314 | 315 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 316 | 317 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 318 | 319 | $ok = $t->close; 320 | if (!$ok) { 321 | $self->log_error("disconnecting: $!"); 322 | } 323 | 324 | $self->log_debug("disconnected"); 325 | 326 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 327 | } 328 | 329 | 1; 330 | 331 | -------------------------------------------------------------------------------- /fetchconfig/model/Detector.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2006 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: Detector.pm,v 1.28 2013/10/16 03:29:11 evertonm Exp $ 20 | 21 | package fetchconfig::model::Detector; # fetchconfig/model/Detector.pm 22 | 23 | use strict; 24 | use warnings; 25 | use fetchconfig::model::CiscoIOS; 26 | use fetchconfig::model::CiscoSG300; 27 | use fetchconfig::model::CiscoCAT; 28 | use fetchconfig::model::CiscoASA; 29 | use fetchconfig::model::FortiGate; 30 | use fetchconfig::model::ProCurve; 31 | use fetchconfig::model::Parks; 32 | use fetchconfig::model::Riverstone; 33 | use fetchconfig::model::Dell; 34 | use fetchconfig::model::Terayon; 35 | use fetchconfig::model::DmSwitch; 36 | use fetchconfig::model::3ComMSR; 37 | use fetchconfig::model::MikroTik; 38 | use fetchconfig::model::CiscoPIX; 39 | use fetchconfig::model::TellabsMSR; 40 | use fetchconfig::model::JunOS; 41 | use fetchconfig::model::Acme; 42 | use fetchconfig::model::Mediant; 43 | use fetchconfig::model::CiscoIOSXR; 44 | use fetchconfig::model::NECUnivergeIX; 45 | use fetchconfig::model::Coriant8600; 46 | 47 | my $logger; 48 | my %model_table; 49 | my %dev_id_table; 50 | 51 | sub parse { 52 | my ($class, $file, $num, $line) = @_; 53 | 54 | if (ref $class) { die "class method called as object method"; } 55 | unless (@_ == 4) { die "usage: $class->parse(\$logger, \$line_num, \$line)"; } 56 | 57 | #$logger->debug("Detector->parse: $line"); 58 | 59 | if ($line =~ /^\s*default:/) { 60 | # 61 | ## global model options 62 | # default: cisco-ios user=backup,pass=san,enable=san 63 | # 64 | if ($line !~ /^\s*(\S+)\s+(\S+)\s+(\S.*)$/) { 65 | $logger->error("unrecognized default at file=$file line=$num: $line"); 66 | return; 67 | } 68 | 69 | my @row = ($1, $2, $3); 70 | my $model_label = shift @row; 71 | 72 | $model_label = $row[0]; 73 | my $mod = $model_table{$model_label}; 74 | if (ref $mod) { 75 | shift @row; 76 | $mod->default_options($file, $num, $line, @row); 77 | return; 78 | } 79 | 80 | $logger->error("unknown model '$model_label' at file=$file line=$num: $line"); 81 | 82 | return; 83 | } 84 | 85 | # 86 | ## model dev-unique-id hostname device-specific-options 87 | #cisco-ios spo2 10.0.0.1 user=backup,pass=san,enable=fran 88 | # 89 | 90 | if ($line !~ /^\s*(\S+)\s+(\S+)\s+(\S+)\s*(.*)$/) { 91 | $logger->error("unrecognized device at file=$file line=$num: $line"); 92 | return; 93 | } 94 | 95 | my @row = ($1, $2, $3, $4); 96 | my $model_label = shift @row; 97 | 98 | my $mod = $model_table{$model_label}; 99 | if (! ref $mod) { 100 | $logger->error("unknown model '$model_label' at file=$file line=$num: $line"); 101 | return; 102 | } 103 | 104 | my $dev_id = shift @row; 105 | 106 | my $dev_id_linenum = $dev_id_table{$dev_id}; 107 | if (defined($dev_id_linenum)) { 108 | $logger->error("duplicated dev_id=$dev_id at file=$file line=$num: $line (previous at line $dev_id_linenum)"); 109 | return; 110 | } 111 | 112 | $dev_id_table{$dev_id} = $num; 113 | 114 | my $dev_host = shift @row; 115 | 116 | my $dev_opt_tab = {}; 117 | 118 | $mod->parse_options("dev=$dev_id", 119 | $file, $num, $line, 120 | $dev_opt_tab, 121 | @row); 122 | 123 | my ($latest_dir, $latest_file); 124 | 125 | # 126 | # "changes_only" is true: configuration is saved only when changed 127 | # "changes_only" is false: configuration is always saved 128 | # 129 | my $dev_changes_only = $mod->dev_option($dev_opt_tab, "changes_only"); 130 | 131 | my $dev_run = $mod->dev_option($dev_opt_tab, "on_fetch_run"); 132 | my $dev_cat = $mod->dev_option($dev_opt_tab, "on_fetch_cat"); 133 | 134 | # 135 | # Do we need to locate the latest backup? 136 | # - changes_only means we need to compare in order to detect change 137 | # - on_fetch_run means we need to pass it to the external program 138 | # - on_fetch_cat means we need to copy it to stdout 139 | # 140 | if ($dev_changes_only || $dev_run || $dev_cat) { 141 | ($latest_dir, $latest_file) = $mod->find_latest($dev_id, $dev_opt_tab); 142 | } 143 | 144 | my $fetch_ts_start = time; 145 | $logger->info("dev=$dev_id host=$dev_host: retrieving config at " . scalar(localtime($fetch_ts_start))); 146 | 147 | my ($config_dir, $config_file) = $mod->fetch($file, $num, $line, $dev_id, $dev_host, $dev_opt_tab); 148 | 149 | my $fetch_elap = time - $fetch_ts_start; 150 | $logger->info("dev=$dev_id host=$dev_host: config retrieval took $fetch_elap secs"); 151 | 152 | return unless defined($config_dir); 153 | 154 | my $cfg_equal = 0; # false 155 | 156 | if (defined($latest_dir)) { 157 | $cfg_equal = $mod->config_equal($latest_dir, $latest_file, $config_dir, $config_file); 158 | } 159 | 160 | my $curr = "$config_dir/$config_file"; 161 | 162 | if ($dev_run) { 163 | $ENV{FETCHCONFIG_DEV_ID} = $dev_id; 164 | $ENV{FETCHCONFIG_DEV_HOST} = $dev_host; 165 | if (defined($latest_dir)) { 166 | $ENV{FETCHCONFIG_PREV} = "$latest_dir/$latest_file" ; 167 | } 168 | else { 169 | delete $ENV{FETCHCONFIG_PREV}; 170 | } 171 | $ENV{FETCHCONFIG_CURR} = $curr; 172 | system($dev_run); 173 | delete $ENV{FETCHCONFIG_DEV_ID}; 174 | delete $ENV{FETCHCONFIG_DEV_HOST}; 175 | delete $ENV{FETCHCONFIG_PREV}; 176 | delete $ENV{FETCHCONFIG_CURR}; 177 | } 178 | 179 | if ($dev_cat) { 180 | local *IN; 181 | 182 | if (!open(IN, "<$curr")) { 183 | $logger->error("could not read current config: $curr: $!"); 184 | return; 185 | } 186 | 187 | my @cfg = ; 188 | chomp @cfg; 189 | 190 | print STDOUT @cfg; 191 | 192 | close IN; 193 | } 194 | 195 | if ($dev_changes_only && $cfg_equal) { 196 | $logger->debug("dev=$dev_id host=$dev_host: discarding config unchanged since last run"); 197 | $mod->config_discard($config_dir, $config_file); 198 | } 199 | 200 | $mod->purge_ancient($dev_id, $dev_opt_tab); 201 | } 202 | 203 | sub register { 204 | my ($class, $mod) = @_; 205 | 206 | $logger->debug("registering model: " . $mod->label); 207 | 208 | $model_table{$mod->label} = $mod; 209 | } 210 | 211 | sub init { 212 | my ($class, $log) = @_; 213 | 214 | $logger = $log; 215 | 216 | $class->register(fetchconfig::model::CiscoIOS->new($log)); 217 | $class->register(fetchconfig::model::CiscoSG300->new($log)); 218 | $class->register(fetchconfig::model::CiscoCAT->new($log)); 219 | $class->register(fetchconfig::model::CiscoASA->new($log)); 220 | $class->register(fetchconfig::model::FortiGate->new($log)); 221 | $class->register(fetchconfig::model::ProCurve->new($log)); 222 | $class->register(fetchconfig::model::Parks->new($log)); 223 | $class->register(fetchconfig::model::Riverstone->new($log)); 224 | $class->register(fetchconfig::model::Dell->new($log)); 225 | $class->register(fetchconfig::model::Terayon->new($log)); 226 | $class->register(fetchconfig::model::DmSwitch->new($log)); 227 | $class->register(fetchconfig::model::3ComMSR->new($log)); 228 | $class->register(fetchconfig::model::MikroTik->new($log)); 229 | $class->register(fetchconfig::model::CiscoPIX->new($log)); 230 | $class->register(fetchconfig::model::TellabsMSR->new($log)); 231 | $class->register(fetchconfig::model::JunOS->new($log)); 232 | $class->register(fetchconfig::model::Acme->new($log)); 233 | $class->register(fetchconfig::model::Mediant->new($log)); 234 | $class->register(fetchconfig::model::CiscoIOSXR->new($log)); 235 | $class->register(fetchconfig::model::NECUnivergeIX->new($log)); 236 | $class->register(fetchconfig::model::Coriant8600->new($log)); 237 | } 238 | 239 | 1; 240 | -------------------------------------------------------------------------------- /fetchconfig/model/DmSwitch.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2006 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: DmSwitch.pm,v 1.1 2008/04/24 21:59:19 evertonm Exp $ 20 | 21 | package fetchconfig::model::DmSwitch; # fetchconfig/model/DmSwitch.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::DmSwitch::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'dmswitch'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my $login_prompt = '/( login:|Password:) $/'; 63 | 64 | # chat_banner is used to allow temporary modification 65 | # of timeout throught the 'banner_timeout' option 66 | 67 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 68 | if (!defined($prematch)) { 69 | $self->log_error("could not find login prompt: $login_prompt"); 70 | return undef; 71 | } 72 | 73 | $self->log_debug("found login prompt: [$match]"); 74 | 75 | if ($match =~ /^ login/) { 76 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 77 | if (!defined($dev_user)) { 78 | $self->log_error("login username needed but not provided"); 79 | return undef; 80 | } 81 | 82 | $ok = $t->print($dev_user); 83 | if (!$ok) { 84 | $self->log_error("could not send login username"); 85 | return undef; 86 | } 87 | 88 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 89 | if (!defined($prematch)) { 90 | $self->log_error("could not find password prompt"); 91 | return undef; 92 | } 93 | 94 | $self->log_debug("found password prompt: [$match]"); 95 | } 96 | 97 | if ($match =~ /^Password/) { 98 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 99 | if (!defined($dev_pass)) { 100 | $self->log_error("login password needed but not provided"); 101 | return undef; 102 | } 103 | 104 | $ok = $t->print($dev_pass); 105 | if (!$ok) { 106 | $self->log_error("could not send login password"); 107 | return undef; 108 | } 109 | 110 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)[>#]$/'); 111 | if (!defined($prematch)) { 112 | $self->log_error("could not find command prompt"); 113 | return undef; 114 | } 115 | 116 | $self->log_debug("found command prompt: [$match]"); 117 | } 118 | 119 | if ($match =~ /^\S+>$/) { 120 | $ok = $t->print('enable'); 121 | if (!$ok) { 122 | $self->log_error("could not send enable command"); 123 | return undef; 124 | } 125 | 126 | ($prematch, $match) = $t->waitfor(Match => '/(Password: |\S+#)$/'); 127 | if (!defined($prematch)) { 128 | $self->log_error("could not find enable password prompt"); 129 | return undef; 130 | } 131 | 132 | if ($match =~ /^Password/) { 133 | my $dev_enable = $self->dev_option($dev_opt_tab, "enable"); 134 | if (!defined($dev_enable)) { 135 | $self->log_error("enable password needed but not provided"); 136 | return undef; 137 | } 138 | 139 | $ok = $t->print($dev_enable); 140 | if (!$ok) { 141 | $self->log_error("could not send enable password"); 142 | return undef; 143 | } 144 | 145 | ($prematch, $match) = $t->waitfor(Match => '/\S+#$/'); 146 | if (!defined($prematch)) { 147 | $self->log_error("could not find enable command prompt"); 148 | return undef; 149 | } 150 | } 151 | 152 | $self->log_debug("found enable prompt: [$match]"); 153 | } 154 | 155 | if ($match !~ /^(\S+)\#$/) { 156 | $self->log_error("could not match after-login enable command prompt"); 157 | return undef; 158 | } 159 | 160 | my $prompt = $1; 161 | 162 | $self->{prompt} = $prompt; # save prompt 163 | 164 | $self->log_debug("logged in prompt=[$prompt]"); 165 | 166 | $prompt; 167 | } 168 | 169 | sub expect_enable_prompt { 170 | my ($self, $t, $prompt) = @_; 171 | 172 | if (!defined($prompt)) { 173 | $self->log_error("internal failure: undefined command prompt"); 174 | return undef; 175 | } 176 | 177 | my $enable_prompt_regexp = '/' . $prompt . '#$/'; 178 | 179 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 180 | if (!defined($prematch)) { 181 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 182 | } 183 | 184 | ($prematch, $match); 185 | } 186 | 187 | sub expect_enable_prompt_paging { 188 | my ($self, $t, $prompt, $paging_prompt) = @_; 189 | 190 | if (!defined($prompt)) { 191 | $self->log_error("internal failure: undefined command prompt"); 192 | return undef; 193 | } 194 | 195 | my $prompt_regexp = '/(' . $prompt . '#)|(' . $paging_prompt . ')$/'; 196 | my $paging_prompt_regexp = '/' . $paging_prompt . '$/'; 197 | 198 | my ($prematch, $match, $full_prematch); 199 | 200 | for (;;) { 201 | ($prematch, $match) = $t->waitfor(Match => $prompt_regexp); 202 | if (!defined($prematch)) { 203 | $self->log_error("could not match enable/paging prompt: $prompt_regexp"); 204 | return; # signals error with undef 205 | } 206 | 207 | #$self->log_debug("paging match: [$match]"); 208 | 209 | $full_prematch .= $prematch; 210 | 211 | if ($match ne $paging_prompt) { 212 | #$self->log_debug("done paging match: [$match][$paging_prompt_regexp]"); 213 | last; 214 | } 215 | 216 | # Do paging 217 | my $ok = $t->put(' '); # SPACE 218 | if (!$ok) { 219 | $self->log_error("could not send paging SPACE command"); 220 | return; # signals error with undef 221 | } 222 | } 223 | 224 | ($full_prematch, $match); 225 | } 226 | 227 | sub chat_fetch { 228 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 229 | my ($ok, $prematch, $match); 230 | 231 | # $ok = $t->print('term len 0'); 232 | # if (!$ok) { 233 | # $self->log_error("could not send pager disabling command"); 234 | # return 1; 235 | # } 236 | # 237 | # ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 238 | # return unless defined($prematch); 239 | 240 | if ($self->chat_show_conf($t, 'show run', $show_cmd)) { 241 | return 1; 242 | } 243 | 244 | # Prevent "show run" command from appearing in config dump 245 | #$t->getline(); 246 | 247 | # Commment out garbage at top so config file can be restored 248 | # cleanly at a later date 249 | my $top_info; 250 | while (my $line = $t->getline()) { 251 | # assume config begins with first valid comment. 252 | if ($line =~ /^\!/) { 253 | $top_info .= $line; 254 | last; 255 | } else { 256 | $top_info .= '!!' . $line; 257 | } 258 | } 259 | 260 | my $save_timeout; 261 | if (defined($fetch_timeout)) { 262 | $save_timeout = $t->timeout; 263 | $t->timeout($fetch_timeout); 264 | } 265 | 266 | ($prematch, $match) = $self->expect_enable_prompt_paging($t, $prompt, '--More-- '); 267 | if (!defined($prematch)) { 268 | $self->log_error("could not find end of configuration"); 269 | return 1; 270 | } 271 | 272 | if (defined($fetch_timeout)) { 273 | $t->timeout($save_timeout); 274 | } 275 | 276 | $self->log_debug("found end of configuration: [$match]"); 277 | 278 | @$conf_ref = split /\n/, $top_info . $prematch; 279 | 280 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 281 | 282 | return undef; 283 | } 284 | 285 | sub do_fetch { 286 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 287 | 288 | $self->log_debug("trying"); 289 | 290 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 291 | if (!defined($dev_repository)) { 292 | $self->log_error("undefined repository"); 293 | return; 294 | } 295 | 296 | if (! -d $dev_repository) { 297 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 298 | return; 299 | } 300 | 301 | if (! -w $dev_repository) { 302 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 303 | return; 304 | } 305 | 306 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 307 | 308 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 309 | 310 | my $ok = $t->open($dev_host); 311 | if (!$ok) { 312 | $self->log_error("could not connect: $!"); 313 | return; 314 | } 315 | 316 | $self->log_debug("connected"); 317 | 318 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 319 | 320 | return unless defined($prompt); 321 | 322 | my @config; 323 | 324 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 325 | 326 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 327 | 328 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 329 | 330 | $ok = $t->close; 331 | if (!$ok) { 332 | $self->log_error("disconnecting: $!"); 333 | } 334 | 335 | $self->log_debug("disconnected"); 336 | 337 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 338 | } 339 | 340 | 1; 341 | -------------------------------------------------------------------------------- /fetchconfig/model/FortiGate.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2006 Doug Schaapveld 3 | # Copyright (C) 2006 Everton da Silva Marques 4 | # 5 | # fetchconfig is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2, or (at your option) 8 | # any later version. 9 | # 10 | # fetchconfig is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with fetchconfig; see the file COPYING. If not, write to the 17 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 18 | # MA 02110-1301 USA. 19 | # 20 | # $Id: FortiGate.pm,v 1.3 2007/01/17 23:23:35 djschaap Exp $ 21 | 22 | package fetchconfig::model::FortiGate; # fetchconfig/model/FortiGate.pm 23 | 24 | use strict; 25 | use warnings; 26 | use Net::Telnet; 27 | use fetchconfig::model::Abstract; 28 | 29 | @fetchconfig::model::FortiGate::ISA = qw(fetchconfig::model::Abstract); 30 | 31 | #################################### 32 | # Implement model::Abstract - Begin 33 | # 34 | 35 | sub label { 36 | 'fortigate'; 37 | } 38 | 39 | # "sub new" fully inherited from fetchconfig::model::Abstract 40 | 41 | sub fetch { 42 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 43 | 44 | my $saved_prefix = $self->{log}->prefix; # save log prefix 45 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 46 | 47 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 48 | 49 | # restore log prefix 50 | $self->{log}->prefix($saved_prefix); 51 | 52 | @conf; 53 | } 54 | 55 | # 56 | # Implement model::Abstract - End 57 | ################################## 58 | 59 | sub chat_login { 60 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 61 | my $ok; 62 | 63 | my ($prematch, $match) = $t->waitfor(Match => '/login: $/'); 64 | if (!defined($prematch)) { 65 | $self->log_error("could not find login prompt"); 66 | return undef; 67 | } 68 | 69 | $self->log_debug("found login prompt: [$match]"); 70 | 71 | if ($match =~ /login: $/) { 72 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 73 | if (!defined($dev_user)) { 74 | $self->log_error("login username needed but not provided"); 75 | return undef; 76 | } 77 | 78 | $ok = $t->print($dev_user); 79 | if (!$ok) { 80 | $self->log_error("could not send login username"); 81 | return undef; 82 | } 83 | 84 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 85 | if (!defined($prematch)) { 86 | $self->log_error("could not find password prompt"); 87 | return undef; 88 | } 89 | 90 | $self->log_debug("found password prompt: [$match]"); 91 | } 92 | 93 | if ($match =~ /^Password: $/) { 94 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 95 | if (!defined($dev_pass)) { 96 | $self->log_error("login password needed but not provided"); 97 | return undef; 98 | } 99 | 100 | $ok = $t->print($dev_pass); 101 | if (!$ok) { 102 | $self->log_error("could not send login password"); 103 | return undef; 104 | } 105 | 106 | ($prematch, $match) = $t->waitfor(Match => '/(\S+ ?)[#\$] $/'); 107 | if (!defined($prematch)) { 108 | $self->log_error("could not find command prompt"); 109 | return undef; 110 | } 111 | 112 | $self->log_debug("found command prompt: [$match]"); 113 | } 114 | 115 | if ($match !~ /^(\S+ ?)[#\$] $/) { 116 | $self->log_error("could not match command prompt in [$match]"); 117 | return undef; 118 | } 119 | 120 | my $prompt = $1; 121 | 122 | $self->{prompt} = $prompt; # save prompt 123 | 124 | $self->log_debug("logged in prompt=[$prompt]"); 125 | 126 | $prompt; 127 | } 128 | 129 | sub expect_command_prompt { 130 | my ($self, $t, $prompt) = @_; 131 | 132 | if (!defined($prompt)) { 133 | $self->log_error("internal failure: undefined command prompt"); 134 | return undef; 135 | } 136 | 137 | my $command_prompt_regexp = '/' . $prompt . '[#\$] $/'; 138 | 139 | my ($prematch, $match) = $t->waitfor(Match => $command_prompt_regexp); 140 | if (!defined($prematch)) { 141 | $self->log_error("could not match command prompt: $command_prompt_regexp"); 142 | } 143 | 144 | ($prematch, $match); 145 | } 146 | 147 | sub chat_fetch { 148 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $conf_ref) = @_; 149 | my $ok; 150 | 151 | $ok = $t->print('config system console'); 152 | if (!$ok) { 153 | $self->log_error("could not send config system console command"); 154 | return 1; 155 | } 156 | 157 | $ok = $t->print('set output standard'); 158 | if (!$ok) { 159 | $self->log_error("could not send pager disabling command"); 160 | return 1; 161 | } 162 | 163 | $ok = $t->print('end'); 164 | if (!$ok) { 165 | $self->log_error("could not send end config command"); 166 | return 1; 167 | } 168 | 169 | my ($prematch, $match) = $self->expect_command_prompt($t, $prompt); 170 | return unless defined($prematch); 171 | 172 | $ok = $t->print('show'); 173 | if (!$ok) { 174 | $self->log_error("could not send show command"); 175 | return 1; 176 | } 177 | 178 | # prevent 'show' command from appearing in config dump 179 | $t->getline(); 180 | 181 | my $save_timeout; 182 | if (defined($fetch_timeout)) { 183 | $save_timeout = $t->timeout; 184 | $t->timeout($fetch_timeout); 185 | } 186 | 187 | ($prematch, $match) = $self->expect_command_prompt($t, $prompt); 188 | if (!defined($prematch)) { 189 | $self->log_error("could not find end of configuration (unable to restore 'set output more')"); 190 | return 1; 191 | } 192 | 193 | if (defined($fetch_timeout)) { 194 | $t->timeout($save_timeout); 195 | } 196 | 197 | $self->log_debug("found end of configuration: [$match]"); 198 | 199 | @$conf_ref = split /\n/, $prematch; 200 | 201 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 202 | 203 | $ok = $t->print('config system console'); 204 | if (!$ok) { 205 | $self->log_error("could not send config system console command"); 206 | return 1; 207 | } 208 | 209 | $ok = $t->print('set output more'); 210 | if (!$ok) { 211 | $self->log_error("could not send pager enabling command"); 212 | return 1; 213 | } 214 | 215 | $ok = $t->print('end'); 216 | if (!$ok) { 217 | $self->log_error("could not send end config command"); 218 | return 1; 219 | } 220 | 221 | ($prematch, $match) = $self->expect_command_prompt($t, $prompt); 222 | return unless defined($prematch); 223 | 224 | return undef; 225 | } 226 | 227 | sub do_fetch { 228 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 229 | 230 | $self->log_debug("trying"); 231 | 232 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 233 | if (!defined($dev_repository)) { 234 | $self->log_error("undefined repository"); 235 | return; 236 | } 237 | 238 | if (! -d $dev_repository) { 239 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 240 | return; 241 | } 242 | 243 | if (! -w $dev_repository) { 244 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 245 | return; 246 | } 247 | 248 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 249 | 250 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 251 | 252 | my $ok = $t->open($dev_host); 253 | if (!$ok) { 254 | $self->log_error("could not connect: $!"); 255 | return; 256 | } 257 | 258 | $self->log_debug("connected"); 259 | 260 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 261 | 262 | return unless defined($prompt); 263 | 264 | my @config; 265 | 266 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 267 | 268 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, \@config); 269 | 270 | $ok = $t->close; 271 | if (!$ok) { 272 | $self->log_error("disconnecting: $!"); 273 | } 274 | 275 | $self->log_debug("disconnected"); 276 | 277 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 278 | } 279 | 280 | 1; 281 | -------------------------------------------------------------------------------- /fetchconfig/model/JunOS.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2006 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: JunOS.pm,v 1.1 2012/04/04 20:29:44 evertonm Exp $ 20 | 21 | package fetchconfig::model::JunOS; # fetchconfig/model/JunOS.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::JunOS::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'junos'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my $login_prompt = '/login: $/'; 63 | 64 | # chat_banner is used to allow temporary modification 65 | # of timeout throught the 'banner_timeout' option 66 | 67 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 68 | if (!defined($prematch)) { 69 | $self->log_error("could not find login prompt: $login_prompt"); 70 | return undef; 71 | } 72 | 73 | $self->log_debug("found login prompt: [$match]"); 74 | 75 | if ($match =~ /^login:/) { 76 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 77 | if (!defined($dev_user)) { 78 | $self->log_error("login username needed but not provided"); 79 | return undef; 80 | } 81 | 82 | $ok = $t->print($dev_user); 83 | if (!$ok) { 84 | $self->log_error("could not send login username"); 85 | return undef; 86 | } 87 | 88 | ($prematch, $match) = $t->waitfor(Match => '/Password:\s*$/'); 89 | if (!defined($prematch)) { 90 | $self->log_error("could not find password prompt"); 91 | return undef; 92 | } 93 | 94 | $self->log_debug("found password prompt: [$match]"); 95 | } 96 | 97 | if ($match =~ /^Password/) { 98 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 99 | if (!defined($dev_pass)) { 100 | $self->log_error("login password needed but not provided"); 101 | return undef; 102 | } 103 | 104 | $ok = $t->print($dev_pass); 105 | if (!$ok) { 106 | $self->log_error("could not send login password"); 107 | return undef; 108 | } 109 | 110 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)>\s$/'); 111 | if (!defined($prematch)) { 112 | $self->log_error("could not find command prompt"); 113 | return undef; 114 | } 115 | 116 | $self->log_debug("found command prompt: [$match]"); 117 | } 118 | 119 | my $pager_disable_cmd = 'set cli screen-length 0'; 120 | 121 | $ok = $t->print($pager_disable_cmd); 122 | if (!$ok) { 123 | $self->log_error("could not send pager disabling command: [$pager_disable_cmd]"); 124 | return 1; 125 | } 126 | 127 | $self->log_debug("sent pager disable command: [$pager_disable_cmd]"); 128 | 129 | if ($match =~ /^\S+>\s$/) { 130 | $ok = $t->print('configure'); 131 | if (!$ok) { 132 | $self->log_error("could not send configure command"); 133 | return undef; 134 | } 135 | 136 | ($prematch, $match) = $t->waitfor(Match => '/\S+\#\s$/'); 137 | if (!defined($prematch)) { 138 | $self->log_error("could not find configure prompt"); 139 | return undef; 140 | } 141 | 142 | $self->log_debug("found configure prompt: [$match]"); 143 | } 144 | 145 | if ($match !~ /^(\S+)\#\s$/) { 146 | $self->log_error("could not match configure command prompt"); 147 | return undef; 148 | } 149 | 150 | my $prompt = $1; 151 | 152 | $self->{prompt} = $prompt; # save prompt 153 | 154 | $self->log_debug("logged in prompt=[$prompt]"); 155 | 156 | $prompt; 157 | } 158 | 159 | sub expect_configure_prompt { 160 | my ($self, $t, $prompt) = @_; 161 | 162 | if (!defined($prompt)) { 163 | $self->log_error("expect_configure_prompt: internal failure: undefined command prompt"); 164 | return undef; 165 | } 166 | 167 | my $configure_prompt_regexp = '/' . $prompt . '# $/'; 168 | 169 | $configure_prompt_regexp =~ s/\@/\\\@/; # escape @ with \@ 170 | 171 | my ($prematch, $match) = $t->waitfor(Match => $configure_prompt_regexp); 172 | if (defined($prematch)) { 173 | $self->log_debug("expect_configure_prompt: found configure prompt: [$match]"); 174 | } else { 175 | $self->log_error("expect_configure_prompt: could not match configure command prompt: $configure_prompt_regexp"); 176 | } 177 | 178 | ($prematch, $match); 179 | } 180 | 181 | sub chat_fetch { 182 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 183 | my $ok; 184 | 185 | my ($prematch, $match); 186 | 187 | #($prematch, $match) = $self->expect_configure_prompt($t, $prompt); 188 | #return unless defined($prematch); 189 | 190 | if ($self->chat_show_conf($t, 'show', $show_cmd)) { 191 | return 1; 192 | } 193 | 194 | $self->log_debug("sent show command"); 195 | 196 | # Prevent "show" command from appearing in config dump 197 | #$t->getline(); 198 | 199 | # Commment out garbage at top so config file can be restored 200 | # cleanly at a later date 201 | #my($line,$top_info); 202 | #while($line=$t->getline()) { 203 | # Failsafe: Just in case 'Current configuration' 204 | # doesn't appear, assume config begins with 'version' 205 | # or first valid comment. 206 | #if($line=~/^version / || $line=~/^\!/) { 207 | # $top_info.=$line; 208 | # last; 209 | # } else { 210 | # $top_info.='!!' . $line; 211 | # } 212 | # Normally, finding the "Current configuration" line 213 | # will be enough to exit this loop. 214 | # last if $line=~/^Current configuration/; 215 | # } 216 | 217 | my $save_timeout; 218 | if (defined($fetch_timeout)) { 219 | $save_timeout = $t->timeout; 220 | $t->timeout($fetch_timeout); 221 | } 222 | 223 | ($prematch, $match) = $self->expect_configure_prompt($t, $prompt); 224 | if (!defined($prematch)) { 225 | $self->log_error("could not find end of configuration"); 226 | return 1; 227 | } 228 | 229 | if (defined($fetch_timeout)) { 230 | $t->timeout($save_timeout); 231 | } 232 | 233 | $self->log_debug("found end of configuration: [$match]"); 234 | 235 | # @$conf_ref = split /\n/, $top_info . $prematch; 236 | @$conf_ref = split /\n/, $prematch; 237 | 238 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 239 | 240 | undef; 241 | } 242 | 243 | sub do_fetch { 244 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 245 | 246 | $self->log_debug("trying"); 247 | 248 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 249 | if (!defined($dev_repository)) { 250 | $self->log_error("undefined repository"); 251 | return; 252 | } 253 | 254 | if (! -d $dev_repository) { 255 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 256 | return; 257 | } 258 | 259 | if (! -w $dev_repository) { 260 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 261 | return; 262 | } 263 | 264 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 265 | 266 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 267 | 268 | my $ok = $t->open($dev_host); 269 | if (!$ok) { 270 | $self->log_error("could not connect: $!"); 271 | return; 272 | } 273 | 274 | $self->log_debug("connected"); 275 | 276 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 277 | 278 | return unless defined($prompt); 279 | 280 | my @config; 281 | 282 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 283 | 284 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 285 | 286 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 287 | 288 | $ok = $t->close; 289 | if (!$ok) { 290 | $self->log_error("disconnecting: $!"); 291 | } 292 | 293 | $self->log_debug("disconnected"); 294 | 295 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 296 | } 297 | 298 | 1; 299 | -------------------------------------------------------------------------------- /fetchconfig/model/Mediant.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2013 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: Mediant.pm,v 1.1 2013/10/16 03:29:11 evertonm Exp $ 20 | 21 | package fetchconfig::model::Mediant; # fetchconfig/model/Mediant.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::Mediant::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'mediant'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my $login_prompt = '/(login:|password:) $/'; 63 | 64 | # chat_banner is used to allow temporary modification 65 | # of timeout throught the 'banner_timeout' option 66 | 67 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 68 | if (!defined($prematch)) { 69 | $self->log_error("could not find login prompt: $login_prompt"); 70 | return undef; 71 | } 72 | 73 | $self->log_debug("found login prompt: [$match]"); 74 | 75 | if ($match =~ /^login/) { 76 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 77 | if (!defined($dev_user)) { 78 | $self->log_error("login username needed but not provided"); 79 | return undef; 80 | } 81 | 82 | $ok = $t->print($dev_user); 83 | if (!$ok) { 84 | $self->log_error("could not send login username"); 85 | return undef; 86 | } 87 | 88 | ($prematch, $match) = $t->waitfor(Match => '/password: $/'); 89 | if (!defined($prematch)) { 90 | $self->log_error("could not find password prompt"); 91 | return undef; 92 | } 93 | 94 | $self->log_debug("found password prompt: [$match]"); 95 | } 96 | 97 | if ($match =~ /^password/) { 98 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 99 | if (!defined($dev_pass)) { 100 | $self->log_error("login password needed but not provided"); 101 | return undef; 102 | } 103 | 104 | $ok = $t->print($dev_pass); 105 | if (!$ok) { 106 | $self->log_error("could not send login password"); 107 | return undef; 108 | } 109 | 110 | ($prematch, $match) = $t->waitfor(Match => '/\/>$/'); 111 | if (!defined($prematch)) { 112 | $self->log_error("could not find command prompt"); 113 | return undef; 114 | } 115 | 116 | $self->log_debug("found command prompt: [$match]"); 117 | } 118 | 119 | if ($match =~ /^\/>$/) { 120 | $ok = $t->print('conf'); 121 | if (!$ok) { 122 | $self->log_error("could not send config command"); 123 | return undef; 124 | } 125 | 126 | ($prematch, $match) = $t->waitfor(Match => '/\/CONFiguration>$/'); 127 | if (!defined($prematch)) { 128 | $self->log_error("could not find config prompt"); 129 | return undef; 130 | } 131 | 132 | $self->log_debug("found config prompt: [$match]"); 133 | } 134 | 135 | if ($match !~ /(\/CONFiguration>)$/) { 136 | $self->log_error("could not match enable command prompt"); 137 | return undef; 138 | } 139 | 140 | my $prompt = $1; 141 | 142 | $self->{prompt} = $prompt; # save prompt 143 | 144 | $self->log_debug("logged in prompt=[$prompt]"); 145 | 146 | $prompt; 147 | } 148 | 149 | sub expect_enable_prompt { 150 | my ($self, $t, $prompt) = @_; 151 | 152 | if (!defined($prompt)) { 153 | $self->log_error("internal failure: undefined command prompt"); 154 | return undef; 155 | } 156 | 157 | my $enable_prompt_regexp = '/' . $prompt . '$/'; 158 | 159 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 160 | if (!defined($prematch)) { 161 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 162 | } 163 | 164 | ($prematch, $match); 165 | } 166 | 167 | sub chat_fetch { 168 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 169 | my $ok; 170 | 171 | if ($self->chat_show_conf($t, 'cf get', $show_cmd)) { 172 | return 1; 173 | } 174 | 175 | my $save_timeout; 176 | if (defined($fetch_timeout)) { 177 | $save_timeout = $t->timeout; 178 | $t->timeout($fetch_timeout); 179 | } 180 | 181 | my ($prematch, $match) = $self->expect_enable_prompt($t, '\\' . $prompt); 182 | if (!defined($prematch)) { 183 | $self->log_error("could not find end of configuration"); 184 | return 1; 185 | } 186 | 187 | if (defined($fetch_timeout)) { 188 | $t->timeout($save_timeout); 189 | } 190 | 191 | $self->log_debug("found end of configuration: [$match]"); 192 | 193 | @$conf_ref = split /\n/, $prematch; 194 | 195 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 196 | 197 | undef; 198 | } 199 | 200 | sub do_fetch { 201 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 202 | 203 | $self->log_debug("trying"); 204 | 205 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 206 | if (!defined($dev_repository)) { 207 | $self->log_error("undefined repository"); 208 | return; 209 | } 210 | 211 | if (! -d $dev_repository) { 212 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 213 | return; 214 | } 215 | 216 | if (! -w $dev_repository) { 217 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 218 | return; 219 | } 220 | 221 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 222 | 223 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 224 | 225 | my $ok = $t->open($dev_host); 226 | if (!$ok) { 227 | $self->log_error("could not connect: $!"); 228 | return; 229 | } 230 | 231 | $self->log_debug("connected"); 232 | 233 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 234 | 235 | return unless defined($prompt); 236 | 237 | my @config; 238 | 239 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 240 | 241 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 242 | 243 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 244 | 245 | $ok = $t->close; 246 | if (!$ok) { 247 | $self->log_error("disconnecting: $!"); 248 | } 249 | 250 | $self->log_debug("disconnected"); 251 | 252 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 253 | } 254 | 255 | 1; 256 | -------------------------------------------------------------------------------- /fetchconfig/model/MikroTik.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2010 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: MikroTik.pm,v 1.2 2010/12/02 19:50:37 evertonm Exp $ 20 | 21 | package fetchconfig::model::MikroTik; # fetchconfig/model/MikroTik.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::MikroTik::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'mikrotik'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my $login_prompt = '/Login: $/'; 63 | 64 | # chat_banner is used to allow temporary modification 65 | # of timeout throught the 'banner_timeout' option 66 | 67 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 68 | if (!defined($prematch)) { 69 | $self->log_error("could not find login prompt: $login_prompt"); 70 | return undef; 71 | } 72 | 73 | $self->log_debug("found login prompt: [$match]"); 74 | 75 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 76 | if (!defined($dev_user)) { 77 | $self->log_error("login username needed but not provided"); 78 | return undef; 79 | } 80 | 81 | # Append +ct console login options to username: 82 | # c: disable console colors 83 | # t: Do auto detection of terminal capabilities 84 | # 85 | # Source: 86 | # http://wiki.mikrotik.com/wiki/Console_login_process#Console_login_options 87 | # 88 | my $user = "$dev_user+ct"; 89 | $self->log_debug("sending user='$user'"); 90 | 91 | $ok = $t->print($user); 92 | if (!$ok) { 93 | $self->log_error("could not send login username: '$user'"); 94 | return undef; 95 | } 96 | 97 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 98 | if (!defined($prematch)) { 99 | $self->log_error("could not find password prompt"); 100 | return undef; 101 | } 102 | 103 | $self->log_debug("found password prompt: [$match]"); 104 | 105 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 106 | if (!defined($dev_pass)) { 107 | $self->log_error("login password needed but not provided"); 108 | return undef; 109 | } 110 | 111 | #$self->log_debug("sending password: '$dev_pass'"); 112 | 113 | $ok = $t->print($dev_pass); 114 | if (!$ok) { 115 | $self->log_error("could not send login password"); 116 | return undef; 117 | } 118 | 119 | ($prematch, $match) = $t->waitfor(Match => '/(\S+) > $/'); 120 | if (!defined($prematch)) { 121 | $self->log_error("could not find command prompt"); 122 | return undef; 123 | } 124 | 125 | my $prompt = $match; 126 | 127 | $self->log_debug("logged in prompt='$prompt'"); 128 | 129 | $self->{prompt} = $prompt; # save prompt 130 | 131 | $prompt; 132 | } 133 | 134 | sub expect_enable_prompt { 135 | my ($self, $t, $prompt) = @_; 136 | 137 | if (!defined($prompt)) { 138 | $self->log_error("internal failure: undefined command prompt"); 139 | return undef; 140 | } 141 | 142 | my $enable_prompt_regexp = '/' . $prompt . ' > $/'; 143 | 144 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 145 | if (!defined($prematch)) { 146 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 147 | } 148 | 149 | ($prematch, $match); 150 | } 151 | 152 | sub expect_enable_prompt_paging { 153 | my ($self, $t, $prompt, $paging_prompt) = @_; 154 | 155 | if (!defined($prompt)) { 156 | $self->log_error("internal failure: undefined command prompt"); 157 | return undef; 158 | } 159 | 160 | my $escaped_prompt = &escape_brackets($prompt); 161 | $self->log_debug("regexp='$prompt' escaped_brackets='$escaped_prompt'"); 162 | 163 | my $prompt_regexp = '/(' . $escaped_prompt . ')|(' . $paging_prompt . ')/'; 164 | my $paging_prompt_regexp = '/' . $paging_prompt . '/'; 165 | 166 | my ($prematch, $match, $full_prematch); 167 | 168 | for (;;) { 169 | ($prematch, $match) = $t->waitfor(Match => $prompt_regexp); 170 | if (!defined($prematch)) { 171 | $self->log_error("could not match enable/paging prompt: $prompt_regexp"); 172 | return; # signals error with undef 173 | } 174 | 175 | #$self->log_debug("paging match: [$match]"); 176 | 177 | $full_prematch .= $prematch; 178 | 179 | if ($match ne $paging_prompt) { 180 | #$self->log_debug("done paging match: [$match][$paging_prompt_regexp]"); 181 | last; 182 | } 183 | 184 | # Do paging 185 | my $ok = $t->put(' '); # SPACE 186 | if (!$ok) { 187 | $self->log_error("could not send paging SPACE command"); 188 | return; # signals error with undef 189 | } 190 | } 191 | 192 | ($full_prematch, $match); 193 | } 194 | 195 | sub chat_fetch { 196 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 197 | my ($ok, $prematch, $match); 198 | 199 | #$t->input_log(\*STDERR); 200 | 201 | if ($self->chat_show_conf($t, 'export', $show_cmd)) { 202 | return 1; 203 | } 204 | 205 | # Prevent "show run" command from appearing in config dump 206 | #$t->getline(); 207 | 208 | my $save_timeout; 209 | if (defined($fetch_timeout)) { 210 | $save_timeout = $t->timeout; 211 | $t->timeout($fetch_timeout); 212 | } 213 | 214 | ($prematch, $match) = $self->expect_enable_prompt_paging($t, $prompt, '--More-- '); 215 | if (!defined($prematch)) { 216 | $self->log_error("could not find end of configuration"); 217 | return 1; 218 | } 219 | 220 | if (defined($fetch_timeout)) { 221 | $t->timeout($save_timeout); 222 | } 223 | 224 | $self->log_debug("found end of configuration: '$match'"); 225 | 226 | @$conf_ref = split /\n/, $prematch; 227 | 228 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 229 | 230 | undef; 231 | } 232 | 233 | sub do_fetch { 234 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 235 | 236 | $self->log_debug("trying"); 237 | 238 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 239 | if (!defined($dev_repository)) { 240 | $self->log_error("undefined repository"); 241 | return; 242 | } 243 | 244 | if (! -d $dev_repository) { 245 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 246 | return; 247 | } 248 | 249 | if (! -w $dev_repository) { 250 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 251 | return; 252 | } 253 | 254 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 255 | 256 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 257 | 258 | my $tm = $t->telnetmode(1); 259 | $self->log_debug("telnet command interpretation was: " . ($tm ? "on" : "off")); 260 | 261 | my $ok = $t->open($dev_host); 262 | if (!$ok) { 263 | $self->log_error("could not connect: $!"); 264 | return; 265 | } 266 | 267 | $self->log_debug("connected"); 268 | 269 | $tm = $t->telnetmode(); 270 | $self->log_debug("telnet command interpretation is: " . ($tm ? "on" : "off")); 271 | 272 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 273 | 274 | return unless defined($prompt); 275 | 276 | my @config; 277 | 278 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 279 | 280 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 281 | 282 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 283 | 284 | $ok = $t->close; 285 | if (!$ok) { 286 | $self->log_error("disconnecting: $!"); 287 | } 288 | 289 | $self->log_debug("disconnected"); 290 | 291 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 292 | } 293 | 294 | 1; 295 | -------------------------------------------------------------------------------- /fetchconfig/model/NECUnivergeIX.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2006 Everton da Silva Marques 3 | # Copyright (C) 2015 Sohgo Takeuchi 4 | # 5 | # fetchconfig is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2, or (at your option) 8 | # any later version. 9 | # 10 | # fetchconfig is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with fetchconfig; see the file COPYING. If not, write to the 17 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 18 | # MA 02110-1301 USA. 19 | # 20 | # $Id$ 21 | 22 | package fetchconfig::model::NECUnivergeIX; # fetchconfig/model/NECUnivergeIX.pm 23 | 24 | use strict; 25 | use warnings; 26 | use Net::Telnet; 27 | use fetchconfig::model::Abstract; 28 | 29 | @fetchconfig::model::NECUnivergeIX::ISA = qw(fetchconfig::model::Abstract); 30 | 31 | #################################### 32 | # Implement model::Abstract - Begin 33 | # 34 | 35 | sub label { 36 | 'nec-univerge-ix'; 37 | } 38 | 39 | # "sub new" fully inherited from fetchconfig::model::Abstract 40 | 41 | sub fetch { 42 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 43 | 44 | my $saved_prefix = $self->{log}->prefix; # save log prefix 45 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 46 | 47 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 48 | 49 | # restore log prefix 50 | $self->{log}->prefix($saved_prefix); 51 | 52 | @conf; 53 | } 54 | 55 | # 56 | # Implement model::Abstract - End 57 | ################################## 58 | 59 | sub chat_login { 60 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 61 | my $ok; 62 | 63 | my $login_prompt = '/(((login|Password):)|(\S+[#\$])) $/'; 64 | 65 | # chat_banner is used to allow temporary modification 66 | # of timeout throught the 'banner_timeout' option 67 | 68 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 69 | if (!defined($prematch)) { 70 | $self->log_error("could not find login prompt: $login_prompt"); 71 | return undef; 72 | } 73 | 74 | $self->log_debug("found login prompt: [$match]"); 75 | 76 | if ($match =~ /^login/) { 77 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 78 | if (!defined($dev_user)) { 79 | $self->log_error("login username needed but not provided"); 80 | return undef; 81 | } 82 | 83 | $ok = $t->print($dev_user); 84 | if (!$ok) { 85 | $self->log_error("could not send login username"); 86 | return undef; 87 | } 88 | 89 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 90 | if (!defined($prematch)) { 91 | $self->log_error("could not find password prompt"); 92 | return undef; 93 | } 94 | 95 | $self->log_debug("found password prompt: [$match]"); 96 | } 97 | 98 | if ($match =~ /^Password/) { 99 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 100 | if (!defined($dev_pass)) { 101 | $self->log_error("login password needed but not provided"); 102 | return undef; 103 | } 104 | 105 | $ok = $t->print($dev_pass); 106 | if (!$ok) { 107 | $self->log_error("could not send login password"); 108 | return undef; 109 | } 110 | 111 | ($prematch, $match) = $t->waitfor(Match => '/\S+[#\$] $/'); 112 | if (!defined($prematch)) { 113 | $self->log_error("could not find command prompt"); 114 | return undef; 115 | } 116 | 117 | $self->log_debug("found command prompt: [$match]"); 118 | } 119 | 120 | if ($match =~ /^\S+[#\$] $/) { 121 | $ok = $t->print('enable-config'); 122 | if (!$ok) { 123 | $self->log_error("could not send enable-config command"); 124 | return undef; 125 | } 126 | 127 | ($prematch, $match) = $t->waitfor(Match => '/\S+\(config\)[#\$] $/'); 128 | if (!defined($prematch)) { 129 | $self->log_error("could not find configure command prompt"); 130 | return undef; 131 | } 132 | 133 | $self->log_debug("found configure prompt: [$match]"); 134 | } 135 | 136 | if ($match !~ /^(\S+\(config\))[#\$] $/) { 137 | $self->log_error("could not match configure command prompt"); 138 | return undef; 139 | } 140 | 141 | my $prompt = $1; 142 | 143 | $self->{prompt} = $prompt; # save prompt 144 | 145 | $self->log_debug("logged in prompt=[$prompt]"); 146 | 147 | $prompt; 148 | } 149 | 150 | sub expect_enable_prompt { 151 | my ($self, $t, $prompt) = @_; 152 | 153 | if (!defined($prompt)) { 154 | $self->log_error("internal failure: undefined command prompt"); 155 | return undef; 156 | } 157 | 158 | my $enable_prompt_regexp = '/' . quotemeta($prompt) . '[#\$] $/'; 159 | 160 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 161 | if (!defined($prematch)) { 162 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 163 | } 164 | 165 | ($prematch, $match); 166 | } 167 | 168 | sub chat_fetch { 169 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 170 | my $ok; 171 | 172 | $ok = $t->print('term len 0'); 173 | if (!$ok) { 174 | $self->log_error("could not send pager disabling command"); 175 | return 1; 176 | } 177 | 178 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 179 | return unless defined($prematch); 180 | 181 | if ($self->chat_show_conf($t, 'show run', $show_cmd)) { 182 | return 1; 183 | } 184 | 185 | # Prevent "show run" command from appearing in config dump 186 | $t->getline(); 187 | 188 | # Commment out garbage at top so config file can be restored 189 | # cleanly at a later date 190 | my($line,$top_info); 191 | while($line=$t->getline()) { 192 | # Failsafe: Just in case 'Current configuration' 193 | # doesn't appear, assume config begins with 'version' 194 | # or first valid comment. 195 | if($line=~/^version / || $line=~/^\!/) { 196 | $top_info.=$line; 197 | last; 198 | } else { 199 | $top_info.='!!' . $line; 200 | } 201 | # Normally, finding the "Current configuration" line 202 | # will be enough to exit this loop. 203 | last if $line=~/^Current configuration/; 204 | } 205 | 206 | my $save_timeout; 207 | if (defined($fetch_timeout)) { 208 | $save_timeout = $t->timeout; 209 | $t->timeout($fetch_timeout); 210 | } 211 | 212 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 213 | if (!defined($prematch)) { 214 | $self->log_error("could not find end of configuration"); 215 | return 1; 216 | } 217 | 218 | if (defined($fetch_timeout)) { 219 | $t->timeout($save_timeout); 220 | } 221 | 222 | $self->log_debug("found end of configuration: [$match]"); 223 | 224 | foreach my $line (split /\n/, $top_info . $prematch) { 225 | next if $line =~ /^\! Current time /; 226 | push(@$conf_ref, $line); 227 | } 228 | 229 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 230 | 231 | undef; 232 | } 233 | 234 | sub do_fetch { 235 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 236 | 237 | $self->log_debug("trying"); 238 | 239 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 240 | if (!defined($dev_repository)) { 241 | $self->log_error("undefined repository"); 242 | return; 243 | } 244 | 245 | if (! -d $dev_repository) { 246 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 247 | return; 248 | } 249 | 250 | if (! -w $dev_repository) { 251 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 252 | return; 253 | } 254 | 255 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 256 | 257 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 258 | 259 | my $ok = $t->open($dev_host); 260 | if (!$ok) { 261 | $self->log_error("could not connect: $!"); 262 | return; 263 | } 264 | 265 | $self->log_debug("connected"); 266 | 267 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 268 | 269 | return unless defined($prompt); 270 | 271 | my @config; 272 | 273 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 274 | 275 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 276 | 277 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 278 | 279 | $ok = $t->close; 280 | if (!$ok) { 281 | $self->log_error("disconnecting: $!"); 282 | } 283 | 284 | $self->log_debug("disconnected"); 285 | 286 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 287 | } 288 | 289 | 1; 290 | -------------------------------------------------------------------------------- /fetchconfig/model/Parks.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2007 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: Parks.pm,v 1.6 2007/07/13 16:19:10 evertonm Exp $ 20 | 21 | package fetchconfig::model::Parks; # fetchconfig/model/Parks.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::Parks::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'parks'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, '/login: $/'); 63 | if (!defined($prematch)) { 64 | $self->log_error("could not find login prompt"); 65 | return undef; 66 | } 67 | 68 | $self->log_debug("found login prompt: [$match]"); 69 | 70 | if ($match =~ /^login:/) { 71 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 72 | if (!defined($dev_user)) { 73 | $self->log_error("login username needed but not provided"); 74 | return undef; 75 | } 76 | 77 | $ok = $t->print($dev_user); 78 | if (!$ok) { 79 | $self->log_error("could not send login username"); 80 | return undef; 81 | } 82 | 83 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 84 | if (!defined($prematch)) { 85 | $self->log_error("could not find password prompt"); 86 | return undef; 87 | } 88 | 89 | $self->log_debug("found password prompt: [$match]"); 90 | } 91 | 92 | if ($match =~ /^Password/) { 93 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 94 | if (!defined($dev_pass)) { 95 | $self->log_error("login password needed but not provided"); 96 | return undef; 97 | } 98 | 99 | $ok = $t->print($dev_pass); 100 | if (!$ok) { 101 | $self->log_error("could not send login password"); 102 | return undef; 103 | } 104 | 105 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)# $/'); 106 | if (!defined($prematch)) { 107 | $self->log_error("could not find command prompt"); 108 | return undef; 109 | } 110 | 111 | $self->log_debug("found command prompt: [$match]"); 112 | } 113 | 114 | if ($match !~ /^(\S+)# $/) { 115 | $self->log_error("could not match enable command prompt"); 116 | return undef; 117 | } 118 | 119 | my $prompt = $1; 120 | 121 | $self->{prompt} = $prompt; # save prompt 122 | 123 | $self->log_debug("logged in prompt=[$prompt]"); 124 | 125 | $prompt; 126 | } 127 | 128 | sub expect_enable_prompt { 129 | my ($self, $t, $prompt, $label) = @_; 130 | 131 | if (!defined($prompt)) { 132 | $self->log_error("internal failure: undefined command prompt"); 133 | return undef; 134 | } 135 | 136 | my $enable_prompt_regexp = '/' . $prompt . '# $/'; 137 | 138 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 139 | if (!defined($prematch)) { 140 | $self->log_error("$label: could not match enable command prompt: $enable_prompt_regexp"); 141 | } 142 | 143 | ($prematch, $match); 144 | } 145 | 146 | sub chat_fetch { 147 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $conf_ref) = @_; 148 | my $ok; 149 | 150 | my $full_show_cmd="show running-config"; 151 | 152 | $ok = $t->print($full_show_cmd); 153 | if (!$ok) { 154 | $self->log_error("could not send show run command: $full_show_cmd"); 155 | return 1; 156 | } 157 | 158 | my $save_timeout; 159 | if (defined($fetch_timeout)) { 160 | $save_timeout = $t->timeout; 161 | $t->timeout($fetch_timeout); 162 | } 163 | 164 | my ($prematch, $match); 165 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt, 'fetching-config'); 166 | if (!defined($prematch)) { 167 | $self->log_error("could not find end of configuration"); 168 | return 1; 169 | } 170 | 171 | if (defined($fetch_timeout)) { 172 | $t->timeout($save_timeout); 173 | } 174 | 175 | $self->log_debug("found end of configuration: [$match]"); 176 | 177 | @$conf_ref = split /\n/, $prematch; 178 | 179 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 180 | 181 | return undef; 182 | } 183 | 184 | sub do_fetch { 185 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 186 | 187 | $self->log_debug("trying"); 188 | 189 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 190 | if (!defined($dev_repository)) { 191 | $self->log_error("undefined repository"); 192 | return; 193 | } 194 | 195 | if (! -d $dev_repository) { 196 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 197 | return; 198 | } 199 | 200 | if (! -w $dev_repository) { 201 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 202 | return; 203 | } 204 | 205 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 206 | 207 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 208 | 209 | my $ok = $t->open($dev_host); 210 | if (!$ok) { 211 | $self->log_error("could not connect: $!"); 212 | return; 213 | } 214 | 215 | $self->log_debug("connected"); 216 | 217 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 218 | 219 | return unless defined($prompt); 220 | 221 | my @config; 222 | 223 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 224 | 225 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, \@config); 226 | 227 | $ok = $t->close; 228 | if (!$ok) { 229 | $self->log_error("disconnecting: $!"); 230 | } 231 | 232 | $self->log_debug("disconnected"); 233 | 234 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 235 | } 236 | 237 | 1; 238 | 239 | -------------------------------------------------------------------------------- /fetchconfig/model/ProCurve.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2006 Doug Schaapveld 3 | # Copyright (C) 2006 Everton da Silva Marques 4 | # 5 | # fetchconfig is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2, or (at your option) 8 | # any later version. 9 | # 10 | # fetchconfig is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with fetchconfig; see the file COPYING. If not, write to the 17 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 18 | # MA 02110-1301 USA. 19 | # 20 | # $Id: ProCurve.pm,v 1.4 2007/01/12 20:11:08 djschaap Exp $ 21 | 22 | package fetchconfig::model::ProCurve; # fetchconfig/model/ProCurve.pm 23 | 24 | use strict; 25 | use warnings; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::ProCurve::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'procurve'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | # There's probably a better way to do this, but this works for now! 59 | # http://en.wikipedia.org/wiki/ANSI_escape_code 60 | sub stripansi ($) { 61 | my $str=shift; 62 | $str=~s/\x1b\[\d*;?\d*[A-Za-z]//g; 63 | $str=~s/\x1b\[\?\d+[h]//g; # What is this code? HP ProCurves use it 64 | $str=~s/\x1bE//g; # HP ProCurves use this, too 65 | $str=~s/\x0d//g; 66 | $str; 67 | } 68 | 69 | sub chat_login_telnet { 70 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 71 | my $ok; 72 | 73 | # my ($prematch, $match) = $t->waitfor(Match => '/(Username:|Password:) $/'); 74 | # my ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 75 | my ($prematch, $match) = $t->waitfor(Match => '/Password: |Press any key to continue/'); 76 | if (!defined($prematch)) { 77 | $self->log_error("could not find login prompt"); 78 | return undef; 79 | } 80 | 81 | $self->log_debug("found login prompt: [$match]"); 82 | 83 | if ($match =~ /Press any key to continue/) { 84 | $ok = $t->print("\n"); 85 | if (!$ok) { 86 | $self->log_error("could not send any key"); 87 | return undef; 88 | } 89 | 90 | ($prematch, $match) = $t->waitfor(Match => '/([A-Za-z0-9-]+ ?)[>#] /'); 91 | if (!defined($prematch)) { 92 | $self->log_error("could not find command prompt (nopw)"); 93 | return undef; 94 | } 95 | 96 | # Note that below line may not appear properly due to the 97 | # escape sequences from the switch 98 | $self->log_debug("found command prompt: [$match]"); 99 | } 100 | 101 | if ($match =~ /login: $/) { 102 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 103 | if (!defined($dev_user)) { 104 | $self->log_error("login username needed but not provided"); 105 | return undef; 106 | } 107 | 108 | $ok = $t->print($dev_user); 109 | if (!$ok) { 110 | $self->log_error("could not send login username"); 111 | return undef; 112 | } 113 | 114 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 115 | if (!defined($prematch)) { 116 | $self->log_error("could not find password prompt"); 117 | return undef; 118 | } 119 | 120 | $self->log_debug("found password prompt: [$match]"); 121 | } 122 | 123 | if ($match =~ /^Password: /) { 124 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 125 | if (!defined($dev_pass)) { 126 | $self->log_error("login password needed but not provided"); 127 | return undef; 128 | } 129 | 130 | $ok = $t->print($dev_pass); 131 | if (!$ok) { 132 | $self->log_error("could not send login password"); 133 | return undef; 134 | } 135 | 136 | ($prematch, $match) = $t->waitfor(Match => '/([A-Za-z0-9-]+ ?)[>#] /'); 137 | if (!defined($prematch)) { 138 | $self->log_error("could not find command prompt (pw)"); 139 | return undef; 140 | } 141 | 142 | # Note that below line may not appear properly due to the 143 | # escape sequences from the switch 144 | $self->log_debug("found command prompt: [$match]"); 145 | } 146 | 147 | if ($match !~ /^(\S+ ?)[>#] $/) { 148 | $self->log_error("could not match command prompt in [$match]"); 149 | return undef; 150 | } 151 | 152 | if ($match =~ /^(\S+ ?)> $/) { 153 | $ok = $t->print('enable'); 154 | if (!$ok) { 155 | $self->log_error("could not send enable command"); 156 | return undef; 157 | } 158 | 159 | ($prematch, $match) = $t->waitfor(Match => '/Password: /'); 160 | if (!defined($prematch)) { 161 | $self->log_error("could not find enable password prompt"); 162 | return undef; 163 | } 164 | 165 | if ($match =~ /^Password/) { 166 | my $dev_enable = $self->dev_option($dev_opt_tab, "enable"); 167 | if (!defined($dev_enable)) { 168 | $self->log_error("enable password needed but not provided"); 169 | return undef; 170 | } 171 | 172 | $ok = $t->print($dev_enable); 173 | if (!$ok) { 174 | $self->log_error("could not send enable password"); 175 | return undef; 176 | } 177 | 178 | ($prematch, $match) = $t->waitfor(Match => '/([A-Za-z0-9-]+ ?)# /'); 179 | if (!defined($prematch)) { 180 | $self->log_error("could not find enable command prompt"); 181 | return undef; 182 | } 183 | } 184 | 185 | $self->log_debug("found enable prompt: [$match]"); 186 | } 187 | 188 | if ($match !~ /([A-Za-z0-9-]+ ?)#/) { 189 | $self->log_error("could not match enable command prompt"); 190 | return undef; 191 | } 192 | 193 | my $prompt = $1; 194 | 195 | $self->{prompt} = $prompt; # save prompt 196 | 197 | $self->log_debug("logged in prompt: [$prompt]"); 198 | 199 | $prompt; 200 | } 201 | 202 | sub expect_enable_prompt { 203 | my ($self, $t, $prompt) = @_; 204 | 205 | if (!defined($prompt)) { 206 | $self->log_error("internal failure: undefined command prompt"); 207 | return undef; 208 | } 209 | 210 | my $enable_prompt_regexp = '/' . $prompt . '# /'; 211 | 212 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 213 | if (!defined($prematch)) { 214 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 215 | } 216 | 217 | ($prematch, $match); 218 | } 219 | 220 | sub chat_fetch { 221 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $conf_ref) = @_; 222 | my $ok; 223 | 224 | $ok = $t->print('no page'); 225 | if (!$ok) { 226 | $self->log_error("could not send pager disabling command"); 227 | return 1; 228 | } 229 | 230 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 231 | return unless defined($prematch); 232 | 233 | my $show_cmd="show run"; 234 | 235 | $ok = $t->print($show_cmd); 236 | if (!$ok) { 237 | $self->log_error("could not send show run command: $show_cmd"); 238 | return 1; 239 | } 240 | 241 | # Prevent "show run" command and "Running configuration" 242 | # from appearing in config dump 243 | $t->getline(); 244 | $t->getline(); 245 | $t->getline(); 246 | 247 | my $save_timeout; 248 | if (defined($fetch_timeout)) { 249 | $save_timeout = $t->timeout; 250 | $t->timeout($fetch_timeout); 251 | } 252 | 253 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 254 | if (!defined($prematch)) { 255 | $self->log_error("could not find end of configuration"); 256 | return 1; 257 | } 258 | 259 | if (defined($fetch_timeout)) { 260 | $t->timeout($save_timeout); 261 | } 262 | 263 | $self->log_debug("found end of configuration: [$match]"); 264 | 265 | foreach my $line (split /\n/, $prematch) { 266 | my $ascii_line=stripansi($line); 267 | chomp $ascii_line; 268 | push(@$conf_ref,$ascii_line ? $ascii_line : ""); 269 | } 270 | 271 | # Remove ANSI fragment from final line (if present) 272 | $conf_ref->[$#$conf_ref]=~s/\x1b\[24\;//; 273 | 274 | # Debugging code for line-by-line analysis 275 | # for(my $i=0;$i<(scalar @$conf_ref);$i++) { 276 | # if((my $line_len=length $conf_ref->[$i]) >1) { 277 | # $self->log_debug("[L " . $i . "-" . (length $conf_ref->[$i]) . "] " . $conf_ref->[$i]); 278 | # } 279 | # } 280 | 281 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 282 | 283 | return undef; 284 | } 285 | 286 | sub do_fetch_telnet { 287 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 288 | 289 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 290 | 291 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 292 | 293 | my $ok = $t->open($dev_host); 294 | if (!$ok) { 295 | $self->log_error("could not connect: $!"); 296 | return; 297 | } 298 | 299 | $self->log_debug("connected"); 300 | 301 | my $prompt = $self->chat_login_telnet($t, $dev_id, $dev_host, $dev_opt_tab); 302 | 303 | return unless defined($prompt); 304 | 305 | my $conf_ref=[]; 306 | 307 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 308 | 309 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $conf_ref); 310 | 311 | $ok = $t->close; 312 | if (!$ok) { 313 | $self->log_error("disconnecting: $!"); 314 | } 315 | 316 | return $conf_ref; 317 | } 318 | 319 | sub do_fetch { 320 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 321 | 322 | $self->log_debug("trying"); 323 | 324 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 325 | if (!defined($dev_repository)) { 326 | $self->log_error("undefined repository"); 327 | return; 328 | } 329 | 330 | if (! -d $dev_repository) { 331 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 332 | return; 333 | } 334 | 335 | if (! -w $dev_repository) { 336 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 337 | return; 338 | } 339 | 340 | my $conf_ref; 341 | $conf_ref=$self->do_fetch_telnet($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 342 | 343 | $self->log_debug("disconnected"); 344 | 345 | $self->dump_config($dev_id, $dev_opt_tab, $conf_ref); 346 | } 347 | 348 | 1; 349 | -------------------------------------------------------------------------------- /fetchconfig/model/Riverstone.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2007 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: Riverstone.pm,v 1.1 2007/07/17 15:05:50 evertonm Exp $ 20 | 21 | package fetchconfig::model::Riverstone; # fetchconfig/model/Riverstone.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::Riverstone::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'riverstone'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my $banner = '/Press RETURN to activate console \. \. \./'; 63 | 64 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $banner); 65 | if (!defined($prematch)) { 66 | $self->log_error("could not find banner: $banner"); 67 | return undef; 68 | } 69 | 70 | $self->log_debug("found banner: [$match]"); 71 | 72 | $ok = $t->print(''); 73 | if (!$ok) { 74 | $self->log_error("could not send ENTER"); 75 | return undef; 76 | } 77 | 78 | ($prematch, $match) = $t->waitfor(Match => '/(Username:|Password:|\S+>|\S+#) $/'); 79 | if (!defined($prematch)) { 80 | $self->log_error("could not find login prompt"); 81 | return undef; 82 | } 83 | 84 | if ($match =~ /^Username:/) { 85 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 86 | if (!defined($dev_user)) { 87 | $self->log_error("login username needed but not provided"); 88 | return undef; 89 | } 90 | 91 | $ok = $t->print($dev_user); 92 | if (!$ok) { 93 | $self->log_error("could not send login username"); 94 | return undef; 95 | } 96 | 97 | ($prematch, $match) = $t->waitfor(Match => '/(Password:|\S+>|\S+#) $/'); 98 | if (!defined($prematch)) { 99 | $self->log_error("could not find after-login prompt"); 100 | return undef; 101 | } 102 | 103 | $self->log_debug("found after-login prompt: [$match]"); 104 | } 105 | 106 | if ($match =~ /^Password:/) { 107 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 108 | if (!defined($dev_pass)) { 109 | $self->log_error("login password needed but not provided"); 110 | return undef; 111 | } 112 | 113 | $ok = $t->print($dev_pass); 114 | if (!$ok) { 115 | $self->log_error("could not send login password"); 116 | return undef; 117 | } 118 | 119 | ($prematch, $match) = $t->waitfor(Match => '/(\S+>|\S+#) $/'); 120 | if (!defined($prematch)) { 121 | $self->log_error("could not find command prompt"); 122 | return undef; 123 | } 124 | 125 | $self->log_debug("found command prompt: [$match]"); 126 | } 127 | 128 | if ($match =~ /^\S+> $/) { 129 | $ok = $t->print('enable'); 130 | if (!$ok) { 131 | $self->log_error("could not send enable command"); 132 | return undef; 133 | } 134 | 135 | ($prematch, $match) = $t->waitfor(Match => '/(Password:|\S+#) $/'); 136 | if (!defined($prematch)) { 137 | $self->log_error("could not find after-enable-command prompt"); 138 | return undef; 139 | } 140 | 141 | $self->log_debug("found after-enable-command prompt: [$match]"); 142 | 143 | if ($match eq 'Password: ') { 144 | $self->log_debug("found enable password prompt: [$match]"); 145 | 146 | my $dev_enable = $self->dev_option($dev_opt_tab, "enable"); 147 | if (!defined($dev_enable)) { 148 | $self->log_error("enable password needed but not provided"); 149 | return undef; 150 | } 151 | 152 | $ok = $t->print($dev_enable); 153 | if (!$ok) { 154 | $self->log_error("could not send enable password"); 155 | return undef; 156 | } 157 | 158 | ($prematch, $match) = $t->waitfor(Match => '/\S+# $/'); 159 | if (!defined($prematch)) { 160 | $self->log_error("could not find enable command prompt"); 161 | return undef; 162 | } 163 | 164 | $self->log_debug("found after-enable-password prompt: [$match]"); 165 | } 166 | } 167 | 168 | if ($match !~ /^(\S+)# $/) { 169 | $self->log_error("could not find enable command prompt"); 170 | return undef; 171 | } 172 | 173 | my $prompt = $1; 174 | 175 | $self->{prompt} = $prompt; # save prompt 176 | 177 | $self->log_debug("logged in prompt=[$prompt]"); 178 | 179 | $prompt; 180 | } 181 | 182 | sub expect_enable_prompt { 183 | my ($self, $t, $prompt, $label) = @_; 184 | 185 | if (!defined($prompt)) { 186 | $self->log_error("internal failure: undefined command prompt"); 187 | return undef; 188 | } 189 | 190 | my $enable_prompt_regexp = '/' . $prompt . '# $/'; 191 | 192 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 193 | if (!defined($prematch)) { 194 | $self->log_error("$label: could not match enable command prompt: $enable_prompt_regexp"); 195 | } 196 | 197 | ($prematch, $match); 198 | } 199 | 200 | sub expect_config_prompt { 201 | my ($self, $t, $prompt, $label) = @_; 202 | 203 | if (!defined($prompt)) { 204 | $self->log_error("internal failure: undefined command prompt"); 205 | return undef; 206 | } 207 | 208 | my $config_prompt_regexp = '/' . $prompt . '\(config\)# $/'; 209 | 210 | my ($prematch, $match) = $t->waitfor(Match => $config_prompt_regexp); 211 | if (!defined($prematch)) { 212 | $self->log_error("$label: could not match config prompt: $config_prompt_regexp"); 213 | } 214 | 215 | ($prematch, $match); 216 | } 217 | 218 | sub chat_fetch { 219 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $conf_ref) = @_; 220 | my $ok; 221 | 222 | my $show_cmd = 'system show active-config'; 223 | 224 | $ok = $t->print($show_cmd); 225 | if (!$ok) { 226 | $self->log_error("could not send show run command: $show_cmd"); 227 | return 1; 228 | } 229 | 230 | my $save_timeout; 231 | if (defined($fetch_timeout)) { 232 | $save_timeout = $t->timeout; 233 | $t->timeout($fetch_timeout); 234 | } 235 | 236 | my ($prematch, $match); 237 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt, 'fetching-config'); 238 | if (!defined($prematch)) { 239 | $self->log_error("could not find end of configuration"); 240 | return 1; 241 | } 242 | 243 | if (defined($fetch_timeout)) { 244 | $t->timeout($save_timeout); 245 | } 246 | 247 | $self->log_debug("found end of configuration: [$match]"); 248 | 249 | @$conf_ref = split /\n/, $prematch; 250 | 251 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 252 | 253 | undef; 254 | } 255 | 256 | sub chat_conf_mode_enter { 257 | my ($self, $t, $prompt) = @_; 258 | 259 | my $ok = $t->print('conf'); 260 | if (!$ok) { 261 | $self->log_error("could not send config mode command"); 262 | return 1; 263 | } 264 | 265 | my ($prematch, $match) = $self->expect_config_prompt($t, $prompt, 'entering-config-mode'); 266 | if (!defined($prematch)) { 267 | $self->log_error("could not find after-config-commmand prompt"); 268 | return 1; 269 | } 270 | 271 | $self->log_debug("entered config mode"); 272 | 273 | undef; 274 | } 275 | 276 | sub chat_conf_mode_exit { 277 | my ($self, $t, $prompt) = @_; 278 | 279 | my $ok; 280 | 281 | $ok = $t->print('exit'); 282 | if (!$ok) { 283 | $self->log_error("could not send config exit command"); 284 | return 1; 285 | } 286 | 287 | $ok = $t->print(''); 288 | if (!$ok) { 289 | $self->log_error("could not send ENTER"); 290 | return 1; 291 | } 292 | 293 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt, 'exiting-config-mode'); 294 | if (!defined($prematch)) { 295 | $self->log_error("could not find after-config-exit-commmand command prompt"); 296 | return 1; 297 | } 298 | 299 | $self->log_debug("exited config mode"); 300 | 301 | undef; 302 | } 303 | 304 | sub chat_pager_off { 305 | my ($self, $t, $prompt) = @_; 306 | 307 | if ($self->chat_conf_mode_enter($t, $prompt)) { 308 | return 1; 309 | } 310 | 311 | my $ok = $t->print('system set terminal rows 0'); 312 | my ($prematch, $match) = $self->expect_config_prompt($t, $prompt, 'disabling-pager'); 313 | if (!defined($prematch)) { 314 | $self->log_error("could not send disable pager command"); 315 | return 1; 316 | } 317 | 318 | $self->log_debug("pager disabled"); 319 | 320 | if ($self->chat_conf_mode_exit($t, $prompt)) { 321 | return 1; 322 | } 323 | 324 | undef; # success 325 | } 326 | 327 | sub chat_pager_on { 328 | my ($self, $t, $prompt) = @_; 329 | 330 | if ($self->chat_conf_mode_enter($t, $prompt)) { 331 | return 1; 332 | } 333 | 334 | my $ok = $t->print('no system set terminal rows 0'); 335 | my ($prematch, $match) = $self->expect_config_prompt($t, $prompt, 'enabling-pager'); 336 | if (!defined($prematch)) { 337 | $self->log_error("could not send disable pager command"); 338 | return 1; 339 | } 340 | 341 | $self->log_debug("pager enabled"); 342 | 343 | if ($self->chat_conf_mode_exit($t, $prompt)) { 344 | return 1; 345 | } 346 | 347 | undef; # success 348 | } 349 | 350 | sub do_fetch { 351 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 352 | 353 | $self->log_debug("trying"); 354 | 355 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 356 | if (!defined($dev_repository)) { 357 | $self->log_error("undefined repository"); 358 | return; 359 | } 360 | 361 | if (! -d $dev_repository) { 362 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 363 | return; 364 | } 365 | 366 | if (! -w $dev_repository) { 367 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 368 | return; 369 | } 370 | 371 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 372 | 373 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 374 | 375 | my $ok = $t->open($dev_host); 376 | if (!$ok) { 377 | $self->log_error("could not connect: $!"); 378 | return; 379 | } 380 | 381 | $self->log_debug("connected"); 382 | 383 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 384 | 385 | return unless defined($prompt); 386 | 387 | if ($self->chat_pager_off($t, $prompt)) { 388 | $self->log_error("could not disable pager"); 389 | return; 390 | } 391 | 392 | my @config; 393 | 394 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 395 | 396 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, \@config); 397 | 398 | if ($self->chat_pager_on($t, $prompt)) { 399 | $self->log_error("could not re-enable pager"); 400 | } 401 | 402 | $ok = $t->close; 403 | if (!$ok) { 404 | $self->log_error("disconnecting: $!"); 405 | } 406 | 407 | $self->log_debug("disconnected"); 408 | 409 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 410 | } 411 | 412 | 1; 413 | 414 | -------------------------------------------------------------------------------- /fetchconfig/model/TellabsMSR.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2011 Everton da Silva Marques 3 | # 4 | # fetchconfig is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2, or (at your option) 7 | # any later version. 8 | # 9 | # fetchconfig is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with fetchconfig; see the file COPYING. If not, write to the 16 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 17 | # MA 02110-1301 USA. 18 | # 19 | # $Id: TellabsMSR.pm,v 1.2 2011/06/16 19:34:40 evertonm Exp $ 20 | 21 | package fetchconfig::model::TellabsMSR; # fetchconfig/model/TellabsMSR.pm 22 | 23 | use strict; 24 | use warnings; 25 | use Net::Telnet; 26 | use fetchconfig::model::Abstract; 27 | 28 | @fetchconfig::model::TellabsMSR::ISA = qw(fetchconfig::model::Abstract); 29 | 30 | #################################### 31 | # Implement model::Abstract - Begin 32 | # 33 | 34 | sub label { 35 | 'tellabs-msr'; 36 | } 37 | 38 | # "sub new" fully inherited from fetchconfig::model::Abstract 39 | 40 | sub fetch { 41 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 42 | 43 | my $saved_prefix = $self->{log}->prefix; # save log prefix 44 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 45 | 46 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 47 | 48 | # restore log prefix 49 | $self->{log}->prefix($saved_prefix); 50 | 51 | @conf; 52 | } 53 | 54 | # 55 | # Implement model::Abstract - End 56 | ################################## 57 | 58 | sub chat_login { 59 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 60 | my $ok; 61 | 62 | my $login_prompt = '/(Login:|Password:) $/'; 63 | 64 | # chat_banner is used to allow temporary modification 65 | # of timeout throught the 'banner_timeout' option 66 | 67 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 68 | if (!defined($prematch)) { 69 | $self->log_error("could not find login prompt: $login_prompt"); 70 | return undef; 71 | } 72 | 73 | $self->log_debug("found login prompt: [$match]"); 74 | 75 | if ($match =~ /^Login/) { 76 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 77 | if (!defined($dev_user)) { 78 | $self->log_error("login username needed but not provided"); 79 | return undef; 80 | } 81 | 82 | $ok = $t->print($dev_user); 83 | if (!$ok) { 84 | $self->log_error("could not send login username"); 85 | return undef; 86 | } 87 | 88 | ($prematch, $match) = $t->waitfor(Match => '/Password: $/'); 89 | if (!defined($prematch)) { 90 | $self->log_error("could not find password prompt"); 91 | return undef; 92 | } 93 | 94 | $self->log_debug("found password prompt: [$match]"); 95 | } 96 | 97 | if ($match =~ /^Password/) { 98 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 99 | if (!defined($dev_pass)) { 100 | $self->log_error("login password needed but not provided"); 101 | return undef; 102 | } 103 | 104 | $ok = $t->print($dev_pass); 105 | if (!$ok) { 106 | $self->log_error("could not send login password"); 107 | return undef; 108 | } 109 | 110 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)[>#] $/'); 111 | if (!defined($prematch)) { 112 | $self->log_error("could not find command prompt"); 113 | return undef; 114 | } 115 | 116 | $self->log_debug("found command prompt: [$match]"); 117 | } 118 | 119 | if ($match =~ /^\S+> $/) { 120 | $ok = $t->print('enable config'); 121 | if (!$ok) { 122 | $self->log_error("could not send enable command"); 123 | return undef; 124 | } 125 | 126 | ($prematch, $match) = $t->waitfor(Match => '/(Password:|\S+#) $/'); 127 | if (!defined($prematch)) { 128 | $self->log_error("could not find enable password prompt"); 129 | return undef; 130 | } 131 | 132 | if ($match =~ /^Password/) { 133 | my $dev_enable = $self->dev_option($dev_opt_tab, "enable"); 134 | if (!defined($dev_enable)) { 135 | $self->log_error("enable password needed but not provided"); 136 | return undef; 137 | } 138 | 139 | $ok = $t->print($dev_enable); 140 | if (!$ok) { 141 | $self->log_error("could not send enable password"); 142 | return undef; 143 | } 144 | 145 | ($prematch, $match) = $t->waitfor(Match => '/\S+# $/'); 146 | if (!defined($prematch)) { 147 | $self->log_error("could not find enable command prompt"); 148 | return undef; 149 | } 150 | } 151 | 152 | $self->log_debug("found enable prompt: [$match]"); 153 | } 154 | 155 | if ($match !~ /^(\S+)\# $/) { 156 | $self->log_error("could not match enable command prompt"); 157 | return undef; 158 | } 159 | 160 | my $prompt = $1; 161 | 162 | $self->{prompt} = $prompt; # save prompt 163 | 164 | $self->log_debug("logged in prompt=[$prompt]"); 165 | 166 | $prompt; 167 | } 168 | 169 | sub expect_enable_prompt { 170 | my ($self, $t, $prompt) = @_; 171 | 172 | if (!defined($prompt)) { 173 | $self->log_error("internal failure: undefined command prompt"); 174 | return undef; 175 | } 176 | 177 | my $enable_prompt_regexp = '/' . $prompt . '# $/'; 178 | 179 | $self->log_debug("waiting enable command prompt: [$enable_prompt_regexp]"); 180 | 181 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 182 | if (!defined($prematch)) { 183 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 184 | } 185 | else { 186 | $self->log_debug("found enable command prompt: [$match]"); 187 | } 188 | 189 | ($prematch, $match); 190 | } 191 | 192 | sub chat_fetch { 193 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 194 | my $ok; 195 | 196 | $ok = $t->print('enable config terminal length 0'); 197 | if (!$ok) { 198 | $self->log_error("could not send pager disabling command"); 199 | return 1; 200 | } 201 | 202 | $self->log_debug('pager disabled'); 203 | 204 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 205 | return unless defined($prematch); 206 | 207 | # Backward compatibility support for option "show_cmd=wrterm" 208 | my $custom_cmd; 209 | if (defined($show_cmd)) { 210 | $custom_cmd = ($show_cmd eq 'wrterm') ? 'write term' : $show_cmd; 211 | } 212 | 213 | if ($self->chat_show_conf($t, 'show run', $custom_cmd)) { 214 | return 1; 215 | } 216 | 217 | $self->log_debug('config requested'); 218 | 219 | # Prevent "show run" command from appearing in config dump 220 | #$t->getline(); 221 | 222 | my $save_timeout; 223 | if (defined($fetch_timeout)) { 224 | $save_timeout = $t->timeout; 225 | $t->timeout($fetch_timeout); 226 | } 227 | 228 | my $conf_timeout = $t->timeout; 229 | 230 | $self->log_debug("waiting config ($conf_timeout seconds)"); 231 | 232 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 233 | if (!defined($prematch)) { 234 | $self->log_error("could not find end of configuration"); 235 | return 1; 236 | } 237 | 238 | if (defined($fetch_timeout)) { 239 | $t->timeout($save_timeout); 240 | } 241 | 242 | $self->log_debug("found end of configuration: [$match]"); 243 | 244 | @$conf_ref = split /\n/, $prematch; 245 | 246 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 247 | 248 | undef; 249 | } 250 | 251 | sub do_fetch { 252 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 253 | 254 | $self->log_debug("trying"); 255 | 256 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 257 | if (!defined($dev_repository)) { 258 | $self->log_error("undefined repository"); 259 | return; 260 | } 261 | 262 | if (! -d $dev_repository) { 263 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 264 | return; 265 | } 266 | 267 | if (! -w $dev_repository) { 268 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 269 | return; 270 | } 271 | 272 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 273 | 274 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 275 | 276 | my $ok = $t->open($dev_host); 277 | if (!$ok) { 278 | $self->log_error("could not connect: $!"); 279 | return; 280 | } 281 | 282 | $self->log_debug("connected"); 283 | 284 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 285 | 286 | return unless defined($prompt); 287 | 288 | my @config; 289 | 290 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 291 | 292 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 293 | 294 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 295 | 296 | $ok = $t->close; 297 | if (!$ok) { 298 | $self->log_error("disconnecting: $!"); 299 | } 300 | 301 | $self->log_debug("disconnected"); 302 | 303 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 304 | } 305 | 306 | 1; 307 | -------------------------------------------------------------------------------- /fetchconfig/model/Terayon.pm: -------------------------------------------------------------------------------- 1 | # fetchconfig - Retrieving configuration for multiple devices 2 | # Copyright (C) 2006 Everton da Silva Marques 3 | # Copyright (C) 2008 Sergey Alexanov 4 | # 5 | # fetchconfig is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2, or (at your option) 8 | # any later version. 9 | # 10 | # fetchconfig is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with fetchconfig; see the file COPYING. If not, write to the 17 | # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 18 | # MA 02110-1301 USA. 19 | # 20 | # $Id: Terayon.pm,v 1.1 2008/02/12 18:59:40 evertonm Exp $ 21 | 22 | package fetchconfig::model::Terayon; # fetchconfig/model/Terayon.pm 23 | 24 | use strict; 25 | use warnings; 26 | use Net::Telnet; 27 | use fetchconfig::model::Abstract; 28 | 29 | @fetchconfig::model::Terayon::ISA = qw(fetchconfig::model::Abstract); 30 | 31 | #################################### 32 | # Implement model::Abstract - Begin 33 | # 34 | 35 | sub label { 36 | 'terayon-os'; 37 | } 38 | 39 | # "sub new" fully inherited from fetchconfig::model::Abstract 40 | 41 | sub fetch { 42 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 43 | 44 | my $saved_prefix = $self->{log}->prefix; # save log prefix 45 | $self->{log}->prefix("$saved_prefix: dev=$dev_id host=$dev_host"); 46 | 47 | my @conf = $self->do_fetch($file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab); 48 | 49 | # restore log prefix 50 | $self->{log}->prefix($saved_prefix); 51 | 52 | @conf; 53 | } 54 | 55 | # 56 | # Implement model::Abstract - End 57 | ################################## 58 | 59 | sub chat_login { 60 | my ($self, $t, $dev_id, $dev_host, $dev_opt_tab) = @_; 61 | my $ok; 62 | 63 | my $login_prompt = '/(Login:|Password:)$/'; 64 | 65 | # chat_banner is used to allow temporary modification 66 | # of timeout throught the 'banner_timeout' option 67 | 68 | my ($prematch, $match) = $self->chat_banner($t, $dev_opt_tab, $login_prompt); 69 | if (!defined($prematch)) { 70 | $self->log_error("could not find login prompt: $login_prompt"); 71 | return undef; 72 | } 73 | 74 | $self->log_debug("found login prompt: [$match]"); 75 | 76 | if ($match =~ /^Login/) { 77 | my $dev_user = $self->dev_option($dev_opt_tab, "user"); 78 | if (!defined($dev_user)) { 79 | $self->log_error("login username needed but not provided"); 80 | return undef; 81 | } 82 | 83 | $ok = $t->print($dev_user); 84 | if (!$ok) { 85 | $self->log_error("could not send login username"); 86 | return undef; 87 | } 88 | 89 | ($prematch, $match) = $t->waitfor(Match => '/Password:$/'); 90 | if (!defined($prematch)) { 91 | $self->log_error("could not find password prompt"); 92 | return undef; 93 | } 94 | 95 | $self->log_debug("found password prompt: [$match]"); 96 | } 97 | 98 | if ($match =~ /^Password/) { 99 | my $dev_pass = $self->dev_option($dev_opt_tab, "pass"); 100 | if (!defined($dev_pass)) { 101 | $self->log_error("login password needed but not provided"); 102 | return undef; 103 | } 104 | 105 | $ok = $t->print($dev_pass); 106 | if (!$ok) { 107 | $self->log_error("could not send login password"); 108 | return undef; 109 | } 110 | 111 | ($prematch, $match) = $t->waitfor(Match => '/(\S+)[>#]$/'); 112 | if (!defined($prematch)) { 113 | $self->log_error("could not find command prompt"); 114 | return undef; 115 | } 116 | 117 | $self->log_debug("found command prompt: [$match]"); 118 | } 119 | 120 | if ($match =~ /^\S+>$/) { 121 | $ok = $t->print('enable'); 122 | if (!$ok) { 123 | $self->log_error("could not send enable command"); 124 | return undef; 125 | } 126 | 127 | ($prematch, $match) = $t->waitfor(Match => '/(Password:|\S+#)$/'); 128 | if (!defined($prematch)) { 129 | $self->log_error("could not find enable password prompt"); 130 | return undef; 131 | } 132 | 133 | if ($match =~ /^Password/) { 134 | my $dev_enable = $self->dev_option($dev_opt_tab, "enable"); 135 | if (!defined($dev_enable)) { 136 | $self->log_error("enable password needed but not provided"); 137 | return undef; 138 | } 139 | 140 | $ok = $t->print($dev_enable); 141 | if (!$ok) { 142 | $self->log_error("could not send enable password"); 143 | return undef; 144 | } 145 | 146 | ($prematch, $match) = $t->waitfor(Match => '/\S+#$/'); 147 | if (!defined($prematch)) { 148 | $self->log_error("could not find enable command prompt"); 149 | return undef; 150 | } 151 | } 152 | 153 | $self->log_debug("found enable prompt: [$match]"); 154 | } 155 | 156 | if ($match !~ /^(\S+)\#$/) { 157 | $self->log_error("could not match enable command prompt"); 158 | return undef; 159 | } 160 | 161 | my $prompt = $1; 162 | 163 | $self->{prompt} = $prompt; # save prompt 164 | 165 | $self->log_debug("logged in prompt=[$prompt]"); 166 | 167 | $prompt; 168 | } 169 | 170 | sub expect_enable_prompt { 171 | my ($self, $t, $prompt) = @_; 172 | 173 | if (!defined($prompt)) { 174 | $self->log_error("internal failure: undefined command prompt"); 175 | return undef; 176 | } 177 | 178 | my $enable_prompt_regexp = '/' . $prompt . '#/'; 179 | 180 | my ($prematch, $match) = $t->waitfor(Match => $enable_prompt_regexp); 181 | if (!defined($prematch)) { 182 | $self->log_error("could not match enable command prompt: $enable_prompt_regexp"); 183 | } 184 | 185 | ($prematch, $match); 186 | } 187 | 188 | sub chat_fetch { 189 | my ($self, $t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, $conf_ref) = @_; 190 | my $ok; 191 | 192 | $ok = $t->print('term len 0'); 193 | if (!$ok) { 194 | $self->log_error("could not send pager disabling command"); 195 | return 1; 196 | } 197 | 198 | my ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 199 | return unless defined($prematch); 200 | 201 | # Backward compatibility support for option "show_cmd=wrterm" 202 | my $custom_cmd; 203 | if (defined($show_cmd)) { 204 | $custom_cmd = ($show_cmd eq 'wrterm') ? 'write term' : $show_cmd; 205 | } 206 | 207 | if ($self->chat_show_conf($t, 'show run', $custom_cmd)) { 208 | return 1; 209 | } 210 | 211 | # Prevent "show run" command from appearing in config dump 212 | $t->getline(); 213 | 214 | # Commment out garbage at top so config file can be restored 215 | # cleanly at a later date 216 | my($line,$top_info); 217 | while($line=$t->getline()) { 218 | # Failsafe: Just in case 'Current configuration' 219 | # doesn't appear, assume config begins with 'version' 220 | # or first valid comment. 221 | if($line=~/^version / || $line=~/^\!/) { 222 | $top_info.=$line; 223 | last; 224 | } else { 225 | $top_info.='!!' . $line; 226 | } 227 | # Normally, finding the "Current configuration" line 228 | # will be enough to exit this loop. 229 | last if $line=~/^Current configuration/; 230 | } 231 | 232 | my $save_timeout; 233 | if (defined($fetch_timeout)) { 234 | $save_timeout = $t->timeout; 235 | $t->timeout($fetch_timeout); 236 | } 237 | 238 | ($prematch, $match) = $self->expect_enable_prompt($t, $prompt); 239 | if (!defined($prematch)) { 240 | $self->log_error("could not find end of configuration"); 241 | return 1; 242 | } 243 | 244 | if (defined($fetch_timeout)) { 245 | $t->timeout($save_timeout); 246 | } 247 | 248 | $self->log_debug("found end of configuration: [$match]"); 249 | 250 | @$conf_ref = split /\n/, $top_info . $prematch; 251 | 252 | $self->log_debug("fetched: " . scalar @$conf_ref . " lines"); 253 | 254 | return undef; 255 | } 256 | 257 | sub do_fetch { 258 | my ($self, $file, $line_num, $line, $dev_id, $dev_host, $dev_opt_tab) = @_; 259 | 260 | $self->log_debug("trying"); 261 | 262 | my $dev_repository = $self->dev_option($dev_opt_tab, "repository"); 263 | if (!defined($dev_repository)) { 264 | $self->log_error("undefined repository"); 265 | return; 266 | } 267 | 268 | if (! -d $dev_repository) { 269 | $self->log_error("not a directory repository=$dev_repository at file=$file line=$line_num: $line"); 270 | return; 271 | } 272 | 273 | if (! -w $dev_repository) { 274 | $self->log_error("unable to write to repository=$dev_repository at file=$file line=$line_num: $line"); 275 | return; 276 | } 277 | 278 | my $dev_timeout = $self->dev_option($dev_opt_tab, "timeout"); 279 | 280 | my $t = new Net::Telnet(Errmode => 'return', Timeout => $dev_timeout); 281 | 282 | my $ok = $t->open($dev_host); 283 | if (!$ok) { 284 | $self->log_error("could not connect: $!"); 285 | return; 286 | } 287 | 288 | $self->log_debug("connected"); 289 | 290 | my $prompt = $self->chat_login($t, $dev_id, $dev_host, $dev_opt_tab); 291 | 292 | return unless defined($prompt); 293 | 294 | my @config; 295 | 296 | my $fetch_timeout = $self->dev_option($dev_opt_tab, "fetch_timeout"); 297 | 298 | my $show_cmd = $self->dev_option($dev_opt_tab, "show_cmd"); 299 | 300 | return if $self->chat_fetch($t, $dev_id, $dev_host, $prompt, $fetch_timeout, $show_cmd, \@config); 301 | 302 | $ok = $t->close; 303 | if (!$ok) { 304 | $self->log_error("disconnecting: $!"); 305 | } 306 | 307 | $self->log_debug("disconnected"); 308 | 309 | $self->dump_config($dev_id, $dev_opt_tab, \@config); 310 | } 311 | 312 | 1; 313 | -------------------------------------------------------------------------------- /rpm/fetchconfig: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | perl -I/usr/lib/fetchconfig /usr/lib/fetchconfig/fetchconfig.pl "$@" 4 | 5 | -------------------------------------------------------------------------------- /rpm/fetchconfig-cvs.spec: -------------------------------------------------------------------------------- 1 | Summary: fetchconfig device configuration retrieval software 2 | Name: fetchconfig 3 | Version: 0.15 4 | Release: 1.rhfc5 5 | Group: Applications/System 6 | Url: http://savannah.nongnu.org/projects/fetchconfig 7 | #Url: http://www.nongnu.org/fetchconfig 8 | Source0: http://download.savannah.nongnu.org/releases/fetchconfig/fetchconfig-%{version}.tar.gz 9 | BuildArch: noarch 10 | BuildRoot: /var/tmp/%{name}-root 11 | License: GPL 12 | 13 | %description 14 | fetchconfig is a Perl script for retrieving the configuration of 15 | multiple devices. It has been tested under Linux and Windows, and 16 | currently supports a variety of devices. 17 | 18 | With some simple Perl programming, it is easily adaptable to any 19 | network devices which provides functionality similar to Cisco's 20 | "show running-config" command. 21 | 22 | This package was developed for and tested on Fedora Core 5. 23 | 24 | %prep 25 | %setup 26 | 27 | %build 28 | echo "Perl scripts do not need compilation." 29 | #%configure 30 | #make 31 | 32 | %install 33 | rm -rf $RPM_BUILD_ROOT 34 | 35 | mkdir -p $RPM_BUILD_ROOT/usr/lib/fetchconfig/fetchconfig/model 36 | cp fetchconfig.pl $RPM_BUILD_ROOT/usr/lib/fetchconfig 37 | cp fetchconfig/*.pm $RPM_BUILD_ROOT/usr/lib/fetchconfig/fetchconfig 38 | cp fetchconfig/model/*.pm $RPM_BUILD_ROOT/usr/lib/fetchconfig/fetchconfig/model 39 | 40 | mkdir -p $RPM_BUILD_ROOT/usr/bin 41 | #ln -sf ../lib/fetchconfig/fetchconfig.pl $RPM_BUILD_ROOT/usr/bin/fetchconfig 42 | cp rpm/fetchconfig $RPM_BUILD_ROOT/usr/bin 43 | 44 | mkdir -p $RPM_BUILD_ROOT/var/fetchconfig 45 | chmod 700 $RPM_BUILD_ROOT/var/fetchconfig 46 | 47 | mkdir -p $RPM_BUILD_ROOT/etc/cron.daily 48 | cp rpm/fetchconfig-daily $RPM_BUILD_ROOT/etc/cron.daily/fetchconfig 49 | 50 | mkdir -p $RPM_BUILD_ROOT/etc/sysconfig 51 | cp rpm/fetchconfigtab $RPM_BUILD_ROOT/etc/fetchconfigtab 52 | cp rpm/fetchconfig-sysconfig $RPM_BUILD_ROOT/etc/sysconfig/fetchconfig 53 | 54 | %clean 55 | rm -rf $RPM_BUILD_ROOT 56 | 57 | %files 58 | %defattr(-,root,root) 59 | 60 | %attr(755,root,root) /usr/bin/fetchconfig 61 | 62 | /usr/lib/fetchconfig 63 | 64 | %dir %attr(700,root,root) /var/fetchconfig 65 | 66 | %attr(755,root,root) /etc/cron.daily/fetchconfig 67 | 68 | %config(noreplace) %attr(600,root,root) /etc/fetchconfigtab 69 | %config(noreplace) %attr(600,root,root) /etc/sysconfig/fetchconfig 70 | 71 | %doc CHANGES COPYING CREDITS README 72 | 73 | %changelog 74 | * Wed Jan 10 2007 Doug Schaapveld 75 | - Initial release of fetchconfig SPEC file 76 | 77 | -------------------------------------------------------------------------------- /rpm/fetchconfig-daily: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -e /etc/sysconfig/fetchconfig ] ; then 4 | . /etc/sysconfig/fetchconfig 5 | fi 6 | 7 | if [ -z "$FETCHCONF" ] ; then 8 | /usr/bin/logger -p warning -t fetchconfig "no FETCHCONF specified; exiting" 9 | exit 1 10 | fi 11 | 12 | if [ ! -f "$FETCHCONF" ] ; then 13 | /usr/bin/logger -p warning -t fetchconfig "$FETCHCONF not found; exiting" 14 | exit 1 15 | fi 16 | 17 | if ! egrep -q -v '^(\W*$|#|default:)' $FETCHCONF ; then 18 | /usr/bin/logger -p warning -t fetchconfig "$FETCHCONF contains no devices; exiting" 19 | exit 1 20 | fi 21 | 22 | /usr/bin/fetchconfig -devices=$FETCHCONF 23 | 24 | EXITVALUE=$? 25 | if [ $EXITVALUE != 0 ]; then 26 | /usr/bin/logger -p warning -t fetchconfig "exited abnormally with [$EXITVALUE]" 27 | fi 28 | exit 0 29 | -------------------------------------------------------------------------------- /rpm/fetchconfig-sysconfig: -------------------------------------------------------------------------------- 1 | FETCHCONF=/etc/fetchconfigtab 2 | -------------------------------------------------------------------------------- /rpm/fetchconfigtab: -------------------------------------------------------------------------------- 1 | # 2 | # $Id: fetchconfigtab,v 1.1 2007/01/11 15:11:38 djschaap Exp $ 3 | # 4 | 5 | # 6 | # DEFAULT OPTIONS SECTION 7 | # 8 | # model options 9 | # 10 | default: cisco-cat user=backup,pass=fran,enable=jose 11 | default: cisco-cat timeout=10,keep=5 12 | default: cisco-cat changes_only=0,fetch_timeout=30 13 | default: cisco-cat repository=/var/fetchconfig 14 | 15 | default: cisco-ios user=backup,pass=fran,enable=jose 16 | default: cisco-ios timeout=10,keep=5,changes_only=0 17 | default: cisco-ios repository=/var/fetchconfig 18 | 19 | default: fortigate user=backup,pass=fran 20 | default: fortigate timeout=10,keep=5 21 | default: fortigate changes_only=0,fetch_timeout=30 22 | default: fortigate repository=/var/fetchconfig 23 | 24 | default: procurve user=backup,pass=fran,enable=jose 25 | default: procurve timeout=10,keep=5 26 | default: procurve changes_only=0,fetch_timeout=30 27 | default: procurve repository=/var/fetchconfig 28 | 29 | # 30 | # DEVICES SECTION 31 | # 32 | # model dev-unique-id hostname device-specific-options 33 | # 34 | #cisco-ios inet-gw 10.0.0.1 35 | #cisco-ios vpn-gw 192.168.0.1 keep=10,changes_only=1 36 | #cisco-ios ancient-ios 192.168.0.3 show_cmd=wrterm 37 | 38 | #cisco-cat sales-sw 172.16.0.10 39 | #cisco-cat eng-sw 172.16.0.11 keep=10,changes_only=1 40 | --------------------------------------------------------------------------------