MJ:
When monitoring customer systems or safeguarding system configurations before doing an upgrade, it is very useful to get the current config files and outputs of certain commands (for instance show running-config on routers). I previously used a script written in Ruby for this, but that isn't easily distributed. Hence, I have ported the automated config file download to Tcl now. The result of local commands will be ported using Expect.
The script is driven by an XML config file that defines a number of hosts and their types. And the actions that have to be performed for each type. For example:
<freezeconfig name="Platform A"> <host name="host"> <user name="user" pass="pass" /> <user name="mail" pass="pass" /> <type name="unix" /> <type name="mail" /> </host> <type name="unix"> <session user="user"> <protocol name="ftp"> <dir filter="hosts">/etc</dir> <dir filter="\.emacs">~</dir> </protocol> </session> </type> <type name="mail"> <session user="mail"> <protocol name="ftp"> <dir filter="sendmail\.cf">/etc</dir> </protocol> </session> </type> </freezeconfig>
The script
package require Tcl 8.5 package require tdom package require vfs::ftp variable xmlfile variable hostpattern variable errors if {$argc < 1 || $argc > 2} { puts stderr "usage: freezeconfig xml-file ?host-pattern?" exit 1 } proc set_params {file {pattern .*}} { variable xmlfile variable hostpattern set xmlfile $file set hostpattern $pattern } proc parse_file {xmlfile} { set f [open $xmlfile] set dom [dom parse -channel $f] set doc [$dom documentElement] close $f return $doc } proc do_type {hostnode hostname address type} { # get the type info puts "Executing type $type" set typeInfo [$hostnode selectNodes {../type[@name=$type]}] if {$typeInfo eq {}} { error "type not defined" } set sessionnodes [$typeInfo selectNodes session] foreach sessionnode $sessionnodes { set user [$sessionnode getAttribute user {}] set currentUserNode [$hostnode selectNodes {user[@name=$user]}] if {$currentUserNode eq {}} { error "user $user not defined for $hostname" } puts "Starting session for user: $user..." set pass [$currentUserNode getAttribute pass {}] foreach protocolnode [$sessionnode selectNodes protocol] { do_protocol $protocolnode $hostname $user $pass } } } proc do_protocol {node host user pass} { set protocol [$node getAttribute name] variable errors puts "Protocol $protocol" protocol::${protocol}::do $node $host $user $pass } namespace eval protocol { namespace eval ftp { } } # different protocol handlers proc ::protocol::ftp::do {node host user pass} { file mkdir $host foreach dirNode [$node selectNodes dir] { set dir [$dirNode asText] file mkdir ./$host/$dir set filter [$dirNode getAttribute filter {^.*$}] set fd [vfs::ftp::Mount $user:$pass@$host/$dir _ftp] puts "copying files from '$dir' matching '$filter'" foreach file [glob -tails -directory ./_ftp *] { if {[regexp -- $filter $file]} { puts "...$file" file copy ./_ftp/$file ./$host/$dir } } vfs::ftp::Unmount $fd _ftp } } # main program set_params {*}$argv if {[catch {parse_file $xmlfile} doc]} { puts stderr "parsing failed: $doc" exit 1 } set name [$doc getAttribute name unknown] set timestamp [clock format [clock seconds] -timezone UTC -format %Y%m%d-%H%M%S] set line [string repeat - 80] puts $line puts $line puts "Freezing $name ($timestamp)" puts $line file mkdir $name cd $name file mkdir $timestamp cd $timestamp set errors {} foreach hostnode [$doc selectNodes host] { set hostname [$hostnode getAttribute name] if {[regexp -- $hostpattern $hostname]} { set address [$hostnode getAttribute address $hostname] puts "freezing $hostname ($address)..." foreach typenode [$hostnode selectNodes type] { set type [$typenode getAttribute name] if {[catch {do_type $hostnode $hostname $address $type} res]} { lappend errors [list $hostname $type $res] } } } else { puts "skipping $host..." } } puts "Freezing failed for:" puts $line puts [format "| %-15s| %-10s| %s" Host Type Error] puts $line foreach error $errors { puts [format "| %-15s| %-10s| %s" {*}$error] } set timestamp [clock format [clock seconds] -timezone UTC -format %Y%m%d-%H%M%S] puts "Freezing $name finished ($timestamp)"
Design remarks