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 22package fetchconfig::model::ProCurve; # fetchconfig/model/ProCurve.pm 23 24use strict; 25use warnings; 26use fetchconfig::model::Abstract; 27 28@fetchconfig::model::ProCurve::ISA = qw(fetchconfig::model::Abstract); 29 30#################################### 31# Implement model::Abstract - Begin 32# 33 34sub label { 35 'procurve'; 36} 37 38# "sub new" fully inherited from fetchconfig::model::Abstract 39 40sub 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 60sub 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 69sub 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 202sub 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 220sub 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 286sub 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 319sub 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 3481; 349