Here is a simple dns auditor, maps name -> ip and should work for other things scotty3 supports, BTW does any one have a patch to get the full SOA record back? The theory is simple you have 2 config files, list of dns servers to check and a csv file with 4 entries: dept tag, item to check, action, result. Ex: server_grp,www.xyz.com,address, What happens is that the check is preformed against each of the listed dns servers and checked for accuracy against the result. This is done by generating a tcl testsuite for the config files that you can execute at will. There are some ugly bits in it but it has proved useful.

anyway here is the code:


package require cmdline
package require csv
package require fileutil

proc server_load { path file } {

    if {[catch {open $path/$file r} result]} {
        error "could not open file for reading : $result"
    return [split [read $result ] \n]

proc items_load {path file} {
    set result [list]
    foreach line [server_load $path $file] {
        lappend result [csv::split $line ]
    return $result

set options {
    {o.arg "out"      "Change the default out file location"}
    {d.arg "data"      "directory with data files"}
    {D.arg "servers"      "DNS server info file"}
    {C.arg "check.csv"      "Things to check file"}

set usage "{build|clean} \[options\] \n"

##::cmdline::getoptions argv $options $usage
## bug here with errors, probably my fault
array set params [::cmdline::getoptions argv $options $usage ]

#parray params
#puts "$argv\n"

set start "
    lappend auto_path /opt/local/lib
    package require Tnm
    package require tcltest

    namespace eval ::%s {
        namespace import ::tcltest::*
        namespace import ::Tnm::dns

   ## put dns server here format 'variable server ip'

set middle {
    test %s-%i.%i { %s: %s %s } -body {
        dns -server %s %s %s
    }  -result %s

set end "

set all.tcl {

package require tcltest

::tcltest::configure -testdir %s/%s
eval ::tcltest::configure $argv


set tmp [server_load $params(d) $params(D) ]
set servers [list]
foreach line $tmp {
    if {[string length $line] >3 } {
        lappend servers $line
set items [items_load $params(d) $params(C) ]

set i 1
array set j {}

foreach server $servers {
    #puts "server == $server\n"

    foreach row $items {
        if {[llength $row] < 2 } {
        #puts "row:: $row\n"
        set owner [lindex $row 0]
        set item [lindex $row 1]
        set action [lindex $row 2]
        set result [lindex $row 3]

        if {[info exists bob($owner/$item)] != 1 } {
            set bob($owner/$item) [list]
            lappend bob($owner/$item) [format $start $item]
            #puts "$owner/$item new \n"
            set j($owner/$item) 1

        lappend bob($owner/$item) [format "$middle" $item $i $j($owner/$item)\
                                   $server $action $item $server $action\
                                   $item $result  ]
        incr j($owner/$item)
    incr i
    foreach name [array names j] {
    set j($name) 1

array set group_dir {}

foreach row $items {
    if {[llength $row ] < 2 } {
    set group_dir([lindex $row 0]) 1

catch {exec rm -rf $params(o) } xxy
#puts "$xxy"

::fileutil::writeFile $params(o)/all.tcl "[format ${all.tcl} [file dirname [file normalize [info script]]] $params(o) ]"
foreach name [array names group_dir] {
    ::fileutil::writeFile $params(o)/$name/all.tcl "[format ${all.tcl} [file dirname [file normalize [info script]]] $params(o)/$name ]"

foreach name [array names bob] {

    lappend bob($name) [format $end]
    ::fileutil::writeFile $params(o)/$name.test  "[join $bob($name) \n ]\n"
   # gets stdin

Now if you have gotten here you can see there is room for improvement, comments welcome