Ayounsi has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/378708 )
Change subject: Add OpenGear support to Rancid ...................................................................... Add OpenGear support to Rancid From instructions on https://opengear.zendesk.com/hc/en-us/articles/216369543-RANCID-Support Next step is to add the rancid user to an OpenGear console server and test it. Bug: T175876 Change-Id: Ia75b7285562575e73bc25df19d18830c2fe6bf87 --- A modules/rancid/files/bin/oglogin A modules/rancid/files/bin/ograncid A modules/rancid/files/bin/ssh-serial-console-wrapper M modules/rancid/manifests/init.pp 4 files changed, 1,055 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/operations/puppet refs/changes/08/378708/1 diff --git a/modules/rancid/files/bin/oglogin b/modules/rancid/files/bin/oglogin new file mode 100755 index 0000000..5144fb5 --- /dev/null +++ b/modules/rancid/files/bin/oglogin @@ -0,0 +1,665 @@ +#! /usr/bin/expect -- +## +## $Id$ +## +## rancid 2.3.8 +## Copyright (c) 1997-2011 by Terrapin Communications, Inc. +## All rights reserved. +## +## This code is derived from software contributed to and maintained by +## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan, +## Pete Whiting, Austin Schutz, and Andrew Fort. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## 1. Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## 2. Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## 3. All advertising materials mentioning features or use of this software +## must display the following acknowledgement: +## This product includes software developed by Terrapin Communications, +## Inc. and its contributors for RANCID. +## 4. Neither the name of Terrapin Communications, Inc. nor the names of its +## contributors may be used to endorse or promote products derived from +## this software without specific prior written permission. +## 5. It is requested that non-binding fixes and modifications be contributed +## back to Terrapin Communications, Inc. +## +## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS +## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS +## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +## POSSIBILITY OF SUCH DAMAGE. +# +# The expect login scripts were based on Erik Sherk's gwtn, by permission. +# +# oglogin - Opengear login +# +# Modified by Toby Smith <[email protected]> for Opengear +# + +# Usage line +set usage "Usage: $argv0 \[-dSV\] \[-c command\] \ +\[-Evar=x\] \[-f cloginrc-file\] \[-p user-password\] \ +\[-r passphrase\] \[-s script-file\] \[-t timeout\] \[-u username\] \ +\[-v vty-password\] \[-x command-file\] \ +\[-y ssh_cypher_type\] router \[router...\]\n" + +# env(CLOGIN) may contain: +# x == do not set xterm banner or name + +# Password file +set password_file $env(HOME)/.cloginrc +# Default is to login to the router +set do_command 0 +set do_script 0 +# The default is to look in the password file to find the passwords. This +# tracks if we receive them on the command line. +set do_passwd 1 +# Save config, if prompted +set do_saveconfig 0 +# Sometimes routers take awhile to answer (the default is 10 sec) +set timeoutdflt 45 +# +set send_human {.4 .4 .7 .3 5} + +# Find the user in the ENV, or use the unix userid. +if {[info exists env(CISCO_USER)]} { + set default_user $env(CISCO_USER) +} elseif {[info exists env(USER)]} { + set default_user $env(USER) +} elseif {[info exists env(LOGNAME)]} { + set default_user $env(LOGNAME) +} else { + # This uses "id" which I think is portable. At least it has existed + # (without options) on all machines/OSes I've been on recently - + # unlike whoami or id -nu. + if [catch {exec id} reason] { + send_error "\nError: could not exec id: $reason\n" + exit 1 + } + regexp {\(([^)]*)} "$reason" junk default_user +} +if {[info exists env(CLOGINRC)]} { + set password_file $env(CLOGINRC) +} + +# Process the command line +for {set i 0} {$i < $argc} {incr i} { + set arg [lindex $argv $i] + + switch -glob -- $arg { + # Expect debug mode + -d* { + exp_internal 1 + # Username + } -u* { + if {! [regexp .\[uU\](.+) $arg ignore user]} { + incr i + set username [lindex $argv $i] + } + # VTY Password + } -p* { + if {! [regexp .\[pP\](.+) $arg ignore userpasswd]} { + incr i + set userpasswd [lindex $argv $i] + } + set do_passwd 0 + # ssh passphrase + } -r* { + if {! [regexp .\[rR\](.+) $arg ignore passphrase]} { + incr i + set vapassphrase [lindex $argv $i] + } + # VTY Password + } -v* { + if {! [regexp .\[vV\](.+) $arg ignore passwd]} { + incr i + set passwd [lindex $argv $i] + } + set do_passwd 0 + # Version string + } -V* { + send_user "rancid 2.3.8\n" + exit 0 + # Environment variable to pass to -s scripts + } -E* { + if {[regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} { + set E$varname $varvalue + } else { + send_user "\nError: invalid format for -E in $arg\n" + exit 1 + } + # Command to run. + } -c* { + if {! [regexp .\[cC\](.+) $arg ignore command]} { + incr i + set command [lindex $argv $i] + } + set do_command 1 + # Expect script to run. + } -s* { + if {! [regexp .\[sS\](.+) $arg ignore sfile]} { + incr i + set sfile [lindex $argv $i] + } + if { ! [file readable $sfile] } { + send_user "\nError: Can't read $sfile\n" + exit 1 + } + set do_script 1 + # save config on exit + } -S* { + set do_saveconfig 1 + # 'ssh -c' cypher type + } -y* { + if {! [regexp .\[eE\](.+) $arg ignore cypher]} { + incr i + set cypher [lindex $argv $i] + } + # alternate cloginrc file + } -f* { + if {! [regexp .\[fF\](.+) $arg ignore password_file]} { + incr i + set password_file [lindex $argv $i] + } + # Timeout + } -t* { + if {! [regexp .\[tT\](.+) $arg ignore timeout]} { + incr i + set timeoutdflt [lindex $argv $i] + } + # Command file + } -x* { + if {! [regexp .\[xX\](.+) $arg ignore cmd_file]} { + incr i + set cmd_file [lindex $argv $i] + } + if [catch {set cmd_fd [open $cmd_file r]} reason] { + send_user "\nError: $reason\n" + exit 1 + } + set cmd_text [read $cmd_fd] + close $cmd_fd + set command [join [split $cmd_text \n] \;] + set do_command 1 + } -* { + send_user "\nError: Unknown argument! $arg\n" + send_user $usage + exit 1 + } default { + break + } + } +} +# Process routers...no routers listed is an error. +if { $i == $argc } { + send_user "\nError: $usage" +} + +# Only be quiet if we are running a script (it can log its output +# on its own) +if { $do_script } { + log_user 0 +} else { + log_user 1 +} + +# +# Done configuration/variable setting. Now run with it... +# + +# Sets Xterm title if interactive...if its an xterm and the user cares +proc label { host } { + global env + # if CLOGIN has an 'x' in it, don't set the xterm name/banner + if [info exists env(CLOGIN)] { + if {[string first "x" $env(CLOGIN)] != -1} { return } + } + # take host from ENV(TERM) + if [info exists env(TERM)] { + if [regexp \^(xterm|vs) $env(TERM) ignore] { + send_user "\033]1;[lindex [split $host "."] 0]\a" + send_user "\033]2;$host\a" + } + } +} + +# This is a helper function to make the password file easier to +# maintain. Using this the password file has the form: +# add password sl* pete cow +# add password at* steve +# add password * hanky-pie +proc add {var args} { global int_$var ; lappend int_$var $args} +proc include {args} { + global env + regsub -all "(^{|}$)" $args {} args + if { [regexp "^/" $args ignore] == 0 } { + set args $env(HOME)/$args + } + source_password_file $args +} + +proc find {var router} { + upvar int_$var list + if { [info exists list] } { + foreach line $list { + if { [string match [lindex $line 0] $router] } { + return [lrange $line 1 end] + } + } + } + return {} +} + +# Loads the password file. Note that as this file is tcl, and that +# it is sourced, the user better know what to put in there, as it +# could install more than just password info... I will assume however, +# that a "bad guy" could just as easy put such code in the clogin +# script, so I will leave .cloginrc as just an extention of that script +proc source_password_file { password_file } { + global env + if { ! [file exists $password_file] } { + send_user "\nError: password file ($password_file) does not exist\n" + exit 1 + } + file stat $password_file fileinfo + if { [expr ($fileinfo(mode) & 007)] != 0000 } { + send_user "\nError: $password_file must not be world readable/writable\n" + exit 1 + } + if [catch {source $password_file} reason] { + send_user "\nError: $reason\n" + exit 1 + } +} + +# Log into the router. +# returns: 0 on success, 1 on failure, -1 if rsh was used successfully +proc login { router user userpswd passwd cmethod cyphertype identfile } { + global command spawn_id in_proc do_command do_script platform passphrase + global prompt prompt_match u_prompt p_prompt sshcmd + set in_proc 1 + set uprompt_seen 0 + + # try each of the connection methods in $cmethod until one is successful + set progs [llength $cmethod] + foreach prog [lrange $cmethod 0 end] { + incr progs -1 + if [string match "telnet*" $prog] { + regexp {telnet(:([^[:space:]]+))*} $prog methcmd suffix port + if {"$port" == ""} { + set retval [catch {spawn telnet $router} reason] + } else { + set retval [catch {spawn telnet $router $port} reason] + } + if { $retval } { + send_user "\nError: telnet failed: $reason\n" + return 1 + } + } elseif [string match "ssh*" $prog] { + # ssh to the router & try to login with or without an identfile. + regexp {ssh(:([^[:space:]]+))*} $prog methcmd suffix port + set cmd $sshcmd + if {"$port" != ""} { + set cmd "$cmd -p $port" + } + if {"$identfile" != ""} { + set cmd "$cmd -i $identfile" + } + set retval [catch {eval spawn [split "$cmd -c $cyphertype -x -l $user $router" { }]} reason] + if { $retval } { + send_user "\nError: $cmd failed: $reason\n" + return 1 + } + } elseif ![string compare $prog "rsh"] { + send_error "\nError: rsh is an invalid method\n" + if { $progs == 0 } { + return 1 + } + continue; + } else { + send_user "\nError: unknown connection method: $prog\n" + return 1 + } + sleep 0.3 + + # This helps cleanup each expect clause. + expect_after { + timeout { + send_user "\nError: TIMEOUT reached\n" + catch {close}; catch {wait}; + if { $in_proc} { + return 1 + } else { + continue + } + } eof { + send_user "\nError: EOF received\n" + catch {close}; catch {wait}; + if { $in_proc} { + return 1 + } else { + continue + } + } + } + + expect { + -re "(Connection refused|Secure connection \[^\n\r]+ refused)" { + catch {close}; catch {wait}; + if !$progs { + send_user "\nError: Connection Refused ($prog): $router\n" + return 1 + } + } + -re "(Connection closed by|Connection to \[^\n\r]+ closed)" { + catch {close}; catch {wait}; + if !$progs { + send_user "\nError: Connection closed ($prog): $router\n" + return 1 + } + } + eof { send_user "\nError: Couldn't login: $router\n"; wait; return 1 } + -nocase "unknown host\r" { + send_user "\nError: Unknown host $router\n"; + catch {close}; catch {wait}; + return 1 + } + "Host is unreachable" { + send_user "\nError: Host Unreachable: $router\n"; + catch {close}; catch {wait}; + return 1 + } + "No address associated with name" { + send_user "\nError: Unknown host $router\n"; + catch {close}; catch {wait}; + return 1 + } + -re "(Host key not found |The authenticity of host .* be established).* \\(yes/no\\)\\?" { + send "yes\r" + send_user "\nHost $router added to the list of known hosts.\n" + exp_continue + } + -re "HOST IDENTIFICATION HAS CHANGED.* \\(yes/no\\)\\?" { + send "no\r" + send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n" + catch {close}; catch {wait}; + return 1 + } + -re "HOST IDENTIFICATION HAS CHANGED\[^\n\r]+" { + send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n" + return 1 + } + -re "Offending key for .* \\(yes/no\\)\\?" { + send "no\r" + send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n" + catch {close}; catch {wait}; + return 1 + } + -re "(denied|Sorry)" { + send_user "\nError: Check your passwd for $router\n" + catch {close}; catch {wait}; return 1 + } + "Login failed" { + send_user "\nError: Check your passwd for $router\n" + catch {close}; catch {wait}; return 1 + } + -re "% (Bad passwords|Authentication failed)" { + send_user "\nError: Check your passwd for $router\n" + catch {close}; catch {wait}; return 1 + } + "Press any key to continue" { + # send_user "Pressing the ANY key\n" + send "\r" + exp_continue + } + -re "@\[^\r\n]+ $p_prompt" { + # ssh pwd prompt + sleep 1 + send -- "$userpswd\r" + exp_continue + } + -re "Enter passphrase.*: " { + # sleep briefly to allow time for stty -echo + sleep .3 + send -- "$passphrase\r" + exp_continue + } + -re "$u_prompt" { + send -- "$user\r" + set uprompt_seen 1 + exp_continue + } + -re "$p_prompt" { + sleep 1 + if {$uprompt_seen == 1} { + send -- "$userpswd\r" + } else { + send -- "$passwd\r" + } + exp_continue + } + -re "$prompt" { + set prompt_match $expect_out(0,string); + break; + } + "Login invalid" { + send_user "\nError: Invalid login: $router\n"; + catch {close}; catch {wait}; return 1 + } + } + } + + set in_proc 0 + return 0 +} + +# Run commands given on the command line. +proc run_commands { prompt command } { + global do_saveconfig in_proc platform + set in_proc 1 + + #set reprompt [join [split $prompt {}] "\\" ] + #set reprompt "\\$reprompt" + set reprompt [string map { + "$" "\\$" + "*" "\\*" + "+" "\\+" + "?" "\\?" + "{" "\\{" + "}" "\\}" + "^" "\\^" + } $prompt] + + # this is the only way i see to get rid of more prompts in o/p..grrrrr + log_user 0 + + set commands [split $command \;] + set num_commands [llength $commands] + + # Most of this came from clogin, matches fine against Opengear devices + for {set i 0} {$i < $num_commands} { incr i} { + send -- "[subst -nocommands [lindex $commands $i]]\r" + expect { + -re "\b+" { exp_continue } + -re "^\[^\n\r *]*$reprompt" { send_user -- "$expect_out(buffer)" + } + -re "^\[^\n\r]*$reprompt." { send_user -- "$expect_out(buffer)" + exp_continue + } + -re "^--More--\[\r\n]+" { # specific match c1900 pager + send " " + exp_continue + } + -re "\[^\r\n]*\[\n\r]+" { send_user -- "$expect_out(buffer)" + exp_continue + } + -re "\[^\r\n]*Press <SPACE> to cont\[^\r\n]*" { + send " " + # bloody ^[[2K after " " + expect { + -re "^\[^\r\n]*\r" {} + } + exp_continue + } + -re "^ *--More--\[^\n\r]*" { + send " " + exp_continue } + -re "^<-+ More -+>\[^\n\r]*" { + send_user -- "$expect_out(buffer)" + send " " + exp_continue } + } + } + log_user 1 + + send -h "exit\r" + expect { + -re "^\[^\n\r *]*$reprompt" { + send -h "exit\r" + exp_continue; + } + -re "\[\n\r]+" { exp_continue } + timeout { catch {close}; catch {wait}; + return 0 + } + eof { return 0 } + } + set in_proc 0 +} + +# +# For each router... (this is main loop) +# +source_password_file $password_file +set in_proc 0 +set exitval 0 +set prompt_match "" +foreach router [lrange $argv $i end] { + set router [string tolower $router] + # attempt at platform switching. + set platform "" + send_user -- "$router\n" + + # device timeout + set timeout [find timeout $router] + if { [llength $timeout] == 0 } { + set timeout $timeoutdflt + } + + # Default prompt. + set prompt "(#|\\$)" + + # Figure out passwords + if { $do_passwd || $do_enapasswd } { + set pswd [find password $router] + if { [llength $pswd] == 0 } { + send_user -- "\nError: no password for $router in $password_file.\n" + continue + } + set passwd [join [lindex $pswd 0] ""] + } else { + set passwd $userpasswd + } + + # Figure out username + if {[info exists username]} { + # command line username + set ruser $username + } else { + set ruser [join [find user $router] ""] + if { "$ruser" == "" } { set ruser $default_user } + } + + # Figure out username's password (if different from the vty password) + if {[info exists userpasswd]} { + # command line username + set userpswd $userpasswd + } else { + set userpswd [join [find userpassword $router] ""] + if { "$userpswd" == "" } { set userpswd $passwd } + } + + # Figure out prompts + set u_prompt [find userprompt $router] + if { "$u_prompt" == "" } { + set u_prompt "(Username|Login|login|user name|User):" + } else { + set u_prompt [join [lindex $u_prompt 0] ""] + } + set p_prompt [find passprompt $router] + if { "$p_prompt" == "" } { + set p_prompt "(\[Pp]assword|passwd|Enter password for \[^ :]+):" + } else { + set p_prompt [join [lindex $p_prompt 0] ""] + } + + # Figure out identity file to use + set identfile [join [lindex [find identity $router] 0] ""] + + set passphrase [join [lindex [find passphrase $router] 0] ""] + if { ! [string length "$passphrase"]} { + set passphrase $passwd + } + + # Figure out cypher type + if {[info exists cypher]} { + # command line cypher type + set cyphertype $cypher + } else { + set cyphertype [find cyphertype $router] + if { "$cyphertype" == "" } { set cyphertype "3des" } + } + + # Figure out connection method + set cmethod [find method $router] + if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} } + + # Figure out the SSH executable name + set sshcmd [join [lindex [find sshcmd $router] 0] ""] + if { "$sshcmd" == "" } { set sshcmd {ssh} } + + # Login to the router + if {[login $router $ruser $userpswd $passwd $cmethod $cyphertype $identfile]} { + incr exitval + # if login failed or rsh was unsuccessful, move on to the next device + continue + } + + # we are logged in, now figure out the full prompt + send "\r" + expect { + -re "\[\r\n]+" { exp_continue; } + -re "^.*$prompt" { set junk $expect_out(0,string); + regsub -all "\[\]\[\(\)]" $junk {\\&} prompt; + } + } + if { $do_command } { + if {[run_commands $prompt $command]} { + incr exitval + continue + } + } elseif { $do_script } { + source $sfile + catch {close}; + } else { + label $router + log_user 1 + interact + } + + # End of for each router + catch {wait}; + sleep 0.3 +} +exit $exitval diff --git a/modules/rancid/files/bin/ograncid b/modules/rancid/files/bin/ograncid new file mode 100755 index 0000000..5361153 --- /dev/null +++ b/modules/rancid/files/bin/ograncid @@ -0,0 +1,279 @@ +#! /usr/bin/perl +## +## $Id$ +## +## rancid 2.3.8 +## Copyright (C) 1997-2011 by Terrapin Communications, Inc. +## All rights reserved. +## +## This software may be freely copied, modified and redistributed +## without fee for non-commerical purposes provided that this license +## remains intact and unmodified with any RANCID distribution. +## +## There is no warranty or other guarantee of fitness of this software. +## It is provided solely "as is". The author(s) disclaim(s) all +## responsibility and liability with respect to this software's usage +## or its effect upon hardware, computer systems, other software, or +## anything else. +## +## Except where noted otherwise, rancid was written by and is maintained by +## Henry Kilmer, John Heasley, Andrew Partan, Pete Whiting, and Austin Schutz. +## +# +# RANCID - Really Awesome New Cisco confIg Differ +# +# usage: ograncid [-dV] [-l] [-f filename | $host] +# +# Modified by Toby Smith <[email protected]> for Opengear + +use Getopt::Std; +getopts('dflV'); +if ($opt_V) { + print "rancid 2.3.8\n"; + exit(0); +} +$log = $opt_l; +$debug = $opt_d; +$file = $opt_f; +$host = $ARGV[0]; +$clean_run = 0; +$found_end = 0; +$timeo = 90; # clogin timeout in seconds +$clogin_pgm= $ENV{'RANCID_CLOGIN'} || "oglogin"; + +my(@commandtable, %commands, @commands);# command lists +my(%filter_pwds); # password filtering mode + +# This routine is used to print out the router configuration +sub ProcessHistory { + my($new_hist_tag,$new_command,$command_string,@string) = (@_); + if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command)) + && scalar(%history)) { + print eval "$command \%history"; + undef %history; + } + if (($new_hist_tag) && ($new_command) && ($command_string)) { + if ($history{$command_string}) { + $history{$command_string} = "$history{$command_string}@string"; + } else { + $history{$command_string} = "@string"; + } + } elsif (($new_hist_tag) && ($new_command)) { + $history{++$#history} = "@string"; + } else { + print "@string"; + } + $hist_tag = $new_hist_tag; + $command = $new_command; + 1; +} + +sub numerically { $a <=> $b; } + +# This is a sort routine that will sort numerically on the +# keys of a hash as if it were a normal array. +sub keynsort { + local(%lines) = @_; + local($i) = 0; + local(@sorted_lines); + foreach $key (sort numerically keys(%lines)) { + $sorted_lines[$i] = $lines{$key}; + $i++; + } + @sorted_lines; +} + +# This is a sort routine that will sort on the +# keys of a hash as if it were a normal array. +sub keysort { + local(%lines) = @_; + local($i) = 0; + local(@sorted_lines); + foreach $key (sort keys(%lines)) { + $sorted_lines[$i] = $lines{$key}; + $i++; + } + @sorted_lines; +} + +# This is a sort routine that will sort on the +# values of a hash as if it were a normal array. +sub valsort{ + local(%lines) = @_; + local($i) = 0; + local(@sorted_lines); + foreach $key (sort values %lines) { + $sorted_lines[$i] = $key; + $i++; + } + @sorted_lines; +} + +# This is a numerical sort routine (ascending). +sub numsort { + local(%lines) = @_; + local($i) = 0; + local(@sorted_lines); + foreach $num (sort {$a <=> $b} keys %lines) { + $sorted_lines[$i] = $lines{$num}; + $i++; + } + @sorted_lines; +} + +# This is a sort routine that will sort on the +# ip address when the ip address is anywhere in +# the strings. +sub ipsort { + local(%lines) = @_; + local($i) = 0; + local(@sorted_lines); + foreach $addr (sort sortbyipaddr keys %lines) { + $sorted_lines[$i] = $lines{$addr}; + $i++; + } + @sorted_lines; +} + +# These two routines will sort based upon IP addresses +sub ipaddrval { + my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#); + $a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0])); +} +sub sortbyipaddr { + &ipaddrval($a) <=> &ipaddrval($b); +} + +sub File { + print STDERR " In File: $_" if ($debug); + my $buffer = ""; + #my $reprompt = "\\" . join("\\", split('', $prompt)); + my $reprompt = quotemeta($prompt); + + while (<INPUT>) { + tr/\015//d; + if (/^$reprompt/) { $found_end=1; $clean_run=1; return 0}; + next if(/^(\s*|\s*$cmd\s*)$/); + return(1) if /\(command not found\)/; + s/^\s+//g; + + # Splits long lines with backslashes - this joins them + $buffer .= $_; + + # Fix quoted strings + $buffer =~ s/(\S+)="(\S+)"/$1=$2/g; + + ProcessHistory("","","","$buffer"); + $buffer = ""; + } +} + +# Main +@commandtable=( + {'config -g config' => "File"}, + {'cat /proc/bus/usb/devices' => "File"}, + {'cat /etc/config/shadow' => "File"}, + {'cat /etc/version' => "File"}, +); + +# Use array to preserve order of commands, and hash for mapping to subroutine +my (%commands, @commands); +foreach (@commandtable) { + push @commands, (keys(%{$_}))[0]; + $commands{$commands[$#commands]}= (values(%{$_}))[0]; +}; + +$og_cmds=join(";",@commands); +$cmds_regexp=join("|",@commands); + +open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n"; +select(OUTPUT); +# make OUTPUT unbuffered if debugging +if ($debug) { $| = 1; } + +if ($file) { + print STDERR "opening file $host\n" if ($debug); + print STDOUT "opening file $host\n" if ($log); + open(INPUT,"<$host") || die "open failed for $host: $!\n"; +} else { + print STDERR "executing $clogin_pgm -t $timeo -c\"$og_cmds\" $host\n" if ($debug); + print STDOUT "executing $clogin_pgm -t $timeo -c\"$og_cmds\" $host\n" if ($log); + if (defined($ENV{NOPIPE})) { + system "$clogin_pgm -t $timeo -c \"$og_cmds\" $host </dev/null > $host.raw 2>&1" || die "$clogin_pgm failed for $host: $!\n"; + open(INPUT, "< $host.raw") || die "$clogin_pgm failed for $host: $!\n"; + } else { + open(INPUT,"$clogin_pgm -t $timeo -c \"$og_cmds\" $host </dev/null |") || die "$clogin_pgm failed for $host: $!\n"; + } +} + +# determine password filtering mode +if ($ENV{"FILTER_PWDS"} =~ /no/i) { + $filter_pwds = 0; +} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) { + $filter_pwds = 2; +} else { + $filter_pwds = 1; +} + +ProcessHistory("","","","#RANCID-CONTENT-TYPE: opengear\n#\n"); +ProcessHistory("COMMENTS","keysort","B0","#\n"); +ProcessHistory("COMMENTS","keysort","D0","#\n"); +ProcessHistory("COMMENTS","keysort","F0","#\n"); +ProcessHistory("COMMENTS","keysort","G0","#\n"); +TOP: while(<INPUT>) { + tr/\015//d; + if (/[\$#]\s*(quit|exit)$/) { + $clean_run=1; + last; + } + if (/^Error:/) { + print STDOUT ("$host $clogin_pgm error: $_"); + print STDERR ("$host $clogin_pgm error: $_") if ($debug); + $clean_run=0; + last; + } + while (/\s*($cmds_regexp)\s*$/) { + $cmd = $1; + if (!defined($prompt)) { + $prompt = ($_ =~ /^([^#\$]*[#\$])/)[0]; + $prompt =~ s/([][}{)(\\])/\\$1/g; + print STDERR ("PROMPT MATCH: $prompt\n") if ($debug); + } + print STDERR ("HIT COMMAND:$_") if ($debug); + if (! defined($commands{$cmd})) { + print STDERR "$host: found unexpected command - \"$cmd\"\n"; + $clean_run = 0; + last TOP; + } + $rval = &{$commands{$cmd}}; + delete($commands{$cmd}); + if ($rval == -1) { + $clean_run = 0; + last TOP; + } + } +} +print STDOUT "Done $logincmd: $_\n" if ($log); +# Flush History +ProcessHistory("","","",""); +# Cleanup +close(INPUT); +close(OUTPUT); + +if (defined($ENV{NOPIPE})) { + unlink("$host.raw") if (! $debug); +} + +# check for completeness +if (scalar(%commands) || !$clean_run || !$found_end) { + if (scalar(%commands)) { + printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands))); + printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug); + } + if (!$clean_run || !$found_end) { + print STDOUT "$host: End of run not found\n"; + print STDERR "$host: End of run not found\n" if ($debug); + system("/usr/bin/tail -1 $host.new"); + } + unlink "$host.new" if (! $debug); +} diff --git a/modules/rancid/files/bin/ssh-serial-console-wrapper b/modules/rancid/files/bin/ssh-serial-console-wrapper new file mode 100755 index 0000000..6b70d5f --- /dev/null +++ b/modules/rancid/files/bin/ssh-serial-console-wrapper @@ -0,0 +1,77 @@ +#! /usr/bin/expect -- +# +# ssh-serial-console-wrapper +# SSH wrapper for running RANCID login scripts to access devices via an +# Opengear serial console server +# +# Handles the different behaviour of a network-exposed serial console when +# compared to the network ssh or telnet console RANCID expects. +# +# Written by Toby Smith <[email protected]> + +# Usage line +set send_human {.4 .4 .7 .3 5} +set router {} +set otherargs {} +set exitval 0 + +# Process the command line +for {set i 0} {$i < $argc} {incr i} { + set arg [lindex $argv $i] + + switch -glob -- $arg { + -[bcDeFIiLlmOopRSWw]* { + #standard ssh args with arvuments + lappend otherargs $arg + if {! [regexp .\[bcDeFIiLlmOopRSWw\](.+) $arg ignore cmd_file]} { + #skip next argument + incr i + lappend otherargs [lindex $argv $i] + } + #standard ssh args without arguments + } -[a-zA-Z0-9]* { + lappend otherargs $arg + } default { + set router [lindex [split $arg %] 0] + break + } + } +} + +# Process routers...no routers listed is an error. +if { [string length $router] == 0 } { + send_user "\nError: unknown host" +} + +# ssh to the router & try to login with or without an identfile. +# need to pass through all the other arguments +set retval [catch {eval spawn [concat "ssh" $otherargs $router]} reason] +if { $retval } { + send_user "\nError: $cmd failed: $retval\n" + return 1 +} + +sleep 1 + +set sent_enter 0 +set login_timeout 30 +set logout_timeout 60 + +interact { + timeout $login_timeout { + if {$sent_enter} { + # do nothing + } else { + send "\r" + set sent_enter 1 + } + } + -o + timeout $logout_timeout { + return + } +} + +catch {close} +catch {wait} +exit $exitval diff --git a/modules/rancid/manifests/init.pp b/modules/rancid/manifests/init.pp index 2ccde54..9a869c0 100644 --- a/modules/rancid/manifests/init.pp +++ b/modules/rancid/manifests/init.pp @@ -34,6 +34,30 @@ source => 'puppet:///modules/rancid/rancid.conf', } + file { '/var/lib/rancid/bin/oglogin': + require => Package['rancid'], + owner => 'root', + group => 'root', + mode => '0755', + source => 'puppet:///modules/rancid/bin/oglogin', + } + + file { '/var/lib/rancid/bin/ograncid': + require => Package['rancid'], + owner => 'root', + group => 'root', + mode => '0755', + source => 'puppet:///modules/rancid/bin/ograncid', + } + + file { '/var/lib/rancid/bin/ssh-serial-console-wrapper': + require => Package['rancid'], + owner => 'root', + group => 'root', + mode => '0755', + source => 'puppet:///modules/rancid/bin/ssh-serial-console-wrapper', + } + file { '/var/lib/rancid/core': require => [ Package['rancid'], User['rancid'] ], owner => 'rancid', @@ -51,6 +75,16 @@ content => template('rancid/cloginrc.erb'), } + file_line { 'opengear_script': + path => '/etc/rancid/rancid.types.base', + line => 'opengear;script;ograncid', + } + + file_line { 'opengear_login': + path => '/etc/rancid/rancid.types.base', + line => 'opengear;login;oglogin', + } + if $active_server == $::fqdn { $cron_ensure = 'present' } else { -- To view, visit https://gerrit.wikimedia.org/r/378708 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ia75b7285562575e73bc25df19d18830c2fe6bf87 Gerrit-PatchSet: 1 Gerrit-Project: operations/puppet Gerrit-Branch: production Gerrit-Owner: Ayounsi <[email protected]> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
