#!/s/std/bin/perl

# splat - a switch port configuration management tool 

package Splat;

use strict;
use warnings;

use DBI;
use FileHandle;
use Socket;
use XML::Simple;
use Getopt::Long; 
use CSL::Pg;
use CSL::DBs;
use Net::Syslog;
use File::Copy;
use Expect;

# ++++++++++++++++
#    $query->execute($old);
#
#    unless ($query->fetchrow) { 
#	    print "Can't find the part $old\n";
#		exit;
#        db_handler( 'disconnect');
#    }
# ++++++++++++++++


my $noswitchupdate;
my $full;
my $info;

=cut
# Parse Command Line
GetOptions ( 
    'a|attach'   => \my $attach,
    'c|commands' => \my $commands, 
    'd|detach'   => \my $detach,
    'e|ethset'   => \my $ethset, 
    'f|full'     => \my $full, 
    'h|help'     => \my $help,
    'i|info+'    => \my $info,
    'n|nolock'   => \my $nolock, 
    't'		 => \my $noswitchupdate,
    's|swap'     => \my $swap,
    'v|vlan=i'	 => \my $cmd_vlan );

$info = "brian.cs.wisc.edu";

if ( !$info ){

# Sanity check (AJK) Just make sure that we are on the 162 net
# Moved inside conditional by mblodget [CSL #275684]

my $myhost = `uname -n`;
chomp $myhost;
$myhost = gethostbyname($myhost); 
$myhost = inet_ntoa($myhost);   
	if(! ($myhost =~ /^128\.105\.162\./ ))
		{
    		die "You need to run splat from the 162 network! BLAAARGH\n";
		}
}

if ( $info && $full){
	my $myhost = `uname -n`;
	chomp $myhost;	
	$myhost = gethostbyname($myhost);
	$myhost = inet_ntoa($myhost);
	if(! ($myhost =~ /^128\.105\.162\./ )) {
			die "Full splat info not available from non-162 net for security!\n";
    }
}

if ( $attach ) {

    my ( $switch_command, $hostname, $jacknumber, $vlan );

    $vlan = $cmd_vlan;

    $switch_command = 'attach'; 

    check_args( 'attach', '2' );

    $hostname = shift @ARGV; 
    $jacknumber = shift @ARGV;

    unless (( check_exists( 'hostname', $hostname )) == 1 ) {
	$hostname = ensure_fqdn( $hostname );
	unless (( check_exists( 'hostname', $hostname )) == 1 ) {
            print "\nSPLAT ERROR:  Hostname $hostname is not recognized by the network database.\n";
            usage();
	}
    } 

    unless (( check_exists( 'jacknumber', $jacknumber )) == 1 ) {
        print "\nSPLAT ERROR:  Jacknumber $jacknumber is not recognized by the network database.\n";
        usage();
    }

    if ( $vlan ) {
        unless (( check_exists( 'vlan', $vlan )) == 1 ) {
            print "\nSPLAT ERROR:  VLAN $vlan is not recognized by the network database.\n";
            usage();
        }
    } 

    attach_host( $switch_command, $hostname, $jacknumber, $vlan, $nolock );
    db_handler( 'disconnect' );

} elsif ( $detach ) {
    check_args( 'detach', '1' );

    my $hostname = shift @ARGV; 
    unless (( check_exists( 'hostname', $hostname )) == 1 ) {
	$hostname = ensure_fqdn( $hostname );
	unless (( check_exists( 'hostname', $hostname )) == 1 ) {
           print "\nSPLAT ERROR:  Hostname $hostname is not recognized by the network database.\n";
           usage();
	}
    } 
    
    detach_host( $hostname );
    db_handler( 'disconnect' );

} elsif ( $ethset ) {
    #check args inside function, since it could be 2 for remove, or 4 for a change
    #check_args( 'ethset', '4' );
    my ($hostname, $new_part, $new_interface_number, $new_mac) = @ARGV; 
    ethset($hostname, $new_part, $new_interface_number, $new_mac);
    db_handler( 'disconnect' );

} elsif ( $help ) {
    usage();

} elsif ( $info ) {
    check_args( 'info', '1' );

    my $query = shift @ARGV;
    my $test;
    my $type;

    for ( 'hostname', 'jacknumber', 'idf', 'switch','room' ) {
        if ( $_ eq 'hostname' ) {

            unless(check_exists( $_, $query ) == 1){
		 $test = ensure_fqdn( $query );
                 $query = $test if ( $test );
	    }
        } 

        if ( check_exists( $_, $query ) >= 1 ) {
            $type = $_;	
        } 
    } 

    unless ( $type ) {
        print "\nSPLAT ERROR:  $query is not recognized by the network database.\n";
        usage();
    } 

    info( $type, $query );
    db_handler( 'disconnect' );

} elsif ( $commands  ) {
    check_args( 'commands', '1' );
    my $switch = shift @ARGV;

    $switch = ensure_fqdn( $switch );

    unless (( check_exists( 'switch', $switch )) >= 1 ) {
        print "\nSPLAT ERROR:  Switch $switch was not found in the splat database.\n";
        usage();
    } 

    unless ( -e "/p/lab/network/splat/ios_logs/$switch" ) { 
        print "\nSwitch $switch exists in the splat database.\n"; 
        print "But, there isn't a record on file of the commands commands run.\n";
        usage();
    }
    format_commands( $switch );

} elsif ( $swap ) {


    check_args( 'swap', '2' );

    my $hostname1 = shift @ARGV; 
    my $hostname2 = shift @ARGV; 

    for ( $hostname1, $hostname2 ) {
        $_ = ensure_fqdn( $_ );

        unless ( $_ ) {
            print "\nSPLAT ERROR: Invalild hostname.\n"; 
            usage();
        }

        unless (( check_exists( 'hostname', $_ )) == 1 ) {
            print "\nSPLAT ERROR:  Hostname $_ is not recognized by the network database.\n";
            usage();
        } 
    }

    swap_hosts( $hostname1, $hostname2 );
    db_handler( 'disconnect' );

} else {
    usage(); 
}
=cut


#--- Subroutines ---#

sub new {
  my ($class) = @_;
  my $bundle;
  my $ret;

# Database Variables
my $dbh = DBI->connect($CSL::DBs::csdb);
$dbh->{ RaiseError } = 1;
$dbh->{ AutoCommit } = 0;

# Syslog reporting
my $syslog_handle=new Net::Syslog(Facility=>'local3',SyslogHost=>'128.105.16.11');

# Get the username running splat
my $username = $ENV{USER};

  $bundle = {
    dbh => $dbh,
    syslog => $syslog_handle,
    username => $username,
  };

  $ret = bless $bundle, $class; 
  return $ret;
}

=head1 DESTRUCTOR

=item destroy

Disconnects from the databases provided there were no problems in establishing them.

=cut

sub destroy {
  my ($this) = @_;

  $this->{dbh}->disconnect();
}


sub db_handler {
    my ($this, $handle) = @_;

    for ( $handle ) {
        if ( /connect/ ) {
            unless ( $this->{dbh} ) {
                die "Can't contact DBI proxy at $CSL::DBs::csdb: ", $DBI::errstr, "\n";
             }
        } elsif ( /disconnect/ ) {
            unless ( $this->{dbh}->disconnect() ) {
                die "Can't contact DBI proxy at $CSL::DBs::csdb: ", $DBI::errstr, "\n";
             }
        } else {
            die "Must specify a database connection or disconnecton : $!\n";
        }
    } 
}


sub attach_host {

    my ($this, $switch_command, $hostname, $jacknumber, $vlan, $nolock) = @_;

    my $current_jack;
    my $other_host;
    my $jack_note;
    my $interface_note;
    my $answer;
    my ( $switch, $portnumber );

    db_handler($this, 'connect' );

    print "\nUpdating the network database...\n";

    my ( $idf, $patchnumber ) = split_jacknumber( $jacknumber );
    $jack_note = check_jack_note($idf, $patchnumber);
    ( $switch, $portnumber ) = get_switch_portnumber( $idf, $patchnumber );

    ( $idf, $patchnumber ) = sql_quotes( $idf, $patchnumber );

    if (defined $jack_note) {

	print STDERR "-" x 30 . "JACK NOTE" . "-" x 30 . "\n";
	print STDERR $jack_note . "\n";
        print STDERR "-" x 28 . "JACK NOTE END" . "-" x 28 . "\n";

        print STDERR "Is it allright to continue?  [Y]/n : ";
                chomp ($answer = <STDIN>);
                my $length = length( $answer );

                unless (( $answer =~ /^y/i ) || ( $length == 0 )) {
                        die "Exiting...\n";
	        } 
    }

    if( (!defined $switch) | (!defined $portnumber) ){
	print STDERR "\nJack is not properly port mapped, contact network administrator.\n";
        die "Exiting...\n";
    }    

    $interface_note = check_switch_interface_note($switch, $portnumber);
    if(defined $interface_note){

        print STDERR "-" x 24 . "SWITCH INTERFACE  NOTE" . "-" x 24 . "\n";
        print STDERR $jack_note . "\n";
        print STDERR "-" x 22 . "SWITCH INTERFACE NOTE END" . "-" x 22 . "\n";

        print STDERR "Is it allright to continue?  [Y]/n : ";
                chomp ($answer = <STDIN>);
                my $length = length( $answer );

                unless (( $answer =~ /^y/i ) || ( $length == 0 )) {
                        die "Exiting...\n";
                }
    }

    my ( $part, $interface_number ) = get_part_interface( $hostname );
    my $host_mac = get_mac($part,$interface_number);
   ( $part, $interface_number ) = sql_quotes( $part, $interface_number );

   if( !$nolock && $host_mac eq '00:00:00:00:00:00'){
	print "\nSPLAT ERROR: Host has MAC 00:00:00:00:00:00 use non mac lock attach.\n";
        usage();
   }

   if(get_trunk_mode($hostname)){
	printf("Detected AP, disabling port-security.\n");
	$nolock = 1;
   }

    # Figure out mac lock status and vlan
    my $locked = !$nolock ? "'t'" : "'f'";

    my $network;
    if ($vlan)  #forced attach
    {
	my $data = $this->{dbh}->selectall_arrayref("select network from splat.vlans where vlan = '$vlan'");
	if($data->[0][0]){
	    $network = "'" . $data->[0][0] . "'";
        }else{
	    $network = "null";
        }
    }
    else
    {
	$network = get_network($hostname);
	if(!defined $network){
	        print "\nSPLAT ERROR: Network does not exist in the network database.\n";
        	usage();
	}
	($network) = sql_quotes($network);

	 $vlan =
            $this->{dbh}->selectrow_array ( "
                    SELECT vlan
                    FROM splat.vlans
                    WHERE network = $network
            ");

    }

    my $sth_insert = $this->{dbh}->prepare ( "
        INSERT INTO splat.hosts
        VALUES ($part, $interface_number, $idf, $patchnumber, $locked,
        $network, $vlan)
    " );
   
    # Strip quotes for remainder of subroutine
    for ( $idf, $patchnumber, $part, $interface_number ) {
        $_ =~ tr/'//d; #'
    } 

    # Are any effected hosts or jacknumbers currently in use?
    $current_jack = check_if_host_attached( $hostname );

    unless ( ( $current_jack ) && $jacknumber eq $current_jack ) {
        $other_host = check_if_jack_attached( $jacknumber );
    }

    if (  $current_jack ) {
        detach( $hostname );

        print "\nUpdating the switch...\n";
        ( switch_update( 'detach', $hostname, $current_jack, $vlan ) )
            or die "Switch update failed : $!\n";
    }

    if ( $other_host ) {
        detach( $other_host );

        print "\nUpdating the switch...\n";
        ( switch_update( 'detach', $other_host, $jacknumber ) )
            or die "Switch update failed : $!\n";
    }

    $sth_insert->execute();
    print "Host $hostname attached to jacknumber $jacknumber.\n"; 

    ( switch_update( $switch_command, $hostname, $jacknumber, $vlan, $nolock ) )
        or die "Configuration change failed : $!";

    $this->{dbh}->commit;
}


sub detach_host {
    my ($this, $hostname ) = @_;
    my $jacknumber = get_jacknumber1( $hostname );
    my ( $idf, $patchnumber ) = split_jacknumber( $jacknumber );
    my $switch_command = 'detach';
    my ( $switch, $portnumber );

    db_handler( 'connect');

    check_if_host_attached( $hostname );

    # Detach host from current jacknumber
    ( detach( $hostname ) ) 
        or die "Can't detach $hostname from its current jacknumber : $!";

    print "\nUpdating the switch...\n";

    ( switch_update( $switch_command, $hostname, $jacknumber ) )
        or die "Switch update failed : $!\n";

}


sub swap_hosts {
    my ( $hostname1, $hostname2 ) = @_;
    my ( $part, $interface_number );

    db_handler( 'connect' );

    # Save current hostname to jacknumber mapping
    my ( $jacknumber1 ) = get_jacknumber1( $hostname1 );
    my ( $jacknumber2 ) = get_jacknumber1( $hostname2 );

    eval {
        for ( $hostname1, $hostname2 ) {
            check_if_host_attached( $_ );
        }
             
        for ( $hostname1, $hostname2 ) {
            detach( $_ );
        }

        print "\nUpdating the switch...\n";
            switch_update( 'detach', $hostname1, $jacknumber1 )
                or die "Switch update failed : $!\n";
    
        print "\nUpdating the switch...\n";
            switch_update( 'detach', $hostname2, $jacknumber2 )
                or die "Switch update failed : $!\n";
    
           # Switch host / jacknumber combinations
           attach_host( 'attach', $hostname1, $jacknumber2 ) 
               or die "Can't attach host $hostname1 to jacknumber $jacknumber2 : $!";
    
             attach_host( 'attach', $hostname2, $jacknumber1 )
                or die "Can't attach host $hostname2 to jacknumber $jacknumber1 : $!";
    };    

    if ( $@ ne '' ) {
        die "\nSPLAT ERROR - Unable to swap hosts: $@\n\n";
    } else { 
        print "Swap of hosts $hostname1 and $hostname2 was successful.\n";
    } 
}


sub ethset {
	my ($hostname, $new_part, $new_interface_number, $new_mac) = @_;

    # splat -e takes either 1 or 4 arguments on the command line.
    # This accounts for the odd parameter checking
    if (!defined $hostname) {
        check_args('ethset', '1');
    }

	if (defined $new_mac) {
	    if ($new_mac =~ /(\w{2})(\w{2})\.(\w{2})(\w{2})\.(\w{2})(\w{2})/) {
            print "Found cisco style MAC address converting!\n";
        	$new_mac = "$1:$2:$3:$4:$5:$6";
	    }
	}

	# Check if host has a current ethernet card and if it is presently attached
    my ($old_part, $old_interface_number) = get_part_interface($hostname);
    my ($old_mac) = get_mac( $old_part, $old_interface_number);

	if ($old_part) {
		unless(defined $old_mac) {
		    $old_mac = "UNASSIGNED";
		}

        print "$hostname currently using part $old_part with mac address $old_mac.\n";
        print "Checking to see if $hostname is currently attached to a jack...\n";

        if (get_idf_patchnumber($old_part, $old_interface_number )) {
            detach_host($hostname);
        }
        else {
            print "$hostname not currently attached...\n";
        }
    }

    if (!defined $new_part) {
        print "You didn't enter a new part number did you want to remove the host [y/n] : ";
        unless ( <STDIN> =~ /^y/i ) {
            die "Exiting - no changes made.\n";
        }
        delete_mac_by_part_int($old_part,$old_interface_number);
        return 0;
    }
    elsif (defined $new_part && $new_mac) {
        print "Are you sure you want to update it to have part $new_part and mac address $new_mac? [y/n] : ";
        unless (<STDIN> =~ /^y/i) {
            die "Exiting - no changes made.\n";
        }
    }
    else {
        print "\nSPLAT ERROR: Incorrect number of arguments for ethset.\n";
        usage();
    }


	# Check whether new ethernet card (part,interface) is in database
	# and whether it is currently attached.
    my $existing_hostname = get_hostname($new_part, $new_interface_number);
    if ($existing_hostname) {
		print "This card is currently being used by $existing_hostname.\n";
		print "Checking to see if it is attached...\n";

        if (get_idf_patchnumber($new_part, $new_interface_number)) {
            detach_host($existing_hostname);
        }
        else {
            print "$hostname not currently attached...\n";
        }
    }

	# check if new card has an actual MAC registerd,
	my ($existing_mac) = get_mac($new_part,$new_interface_number); 

	# if there is a $existing_hostname there has to be a exisiting_mac by db constraints but not the reverse
	if ($existing_mac) {
		if (!defined $existing_hostname) {
			$existing_hostname = "none";
		}

		print "Part $new_part currently  has the following associations:\n"; 
        print "HOST [$existing_hostname] PART [$new_part]  INTERFACE NUMBER [$new_interface_number]  MAC ADDRESS [$existing_mac]\n";
		print "Updating $new_part to have these associations:\n";
        print "HOST [$hostname]  PART [$new_part]  INTERFACE NUMBER [$new_interface_number]  MAC ADDRESS [$new_mac]\n";
		print "Ok to continue? [y/n] : ";
        unless ( <STDIN> =~ /^y/i ) {
            die "Exiting - no changes made.\n";
        }

		delete_mac_by_part_int($old_part,$old_interface_number);
		delete_mac_by_part_int($new_part,$new_interface_number);
		add_mac($new_part, $new_interface_number, $new_mac);
    gen_dhcp();
		add_part_hostnames($new_part, $new_interface_number, $hostname);
		print "Done.\n";
		exit 0;
	}

	# If you got to this point the new card had no associated hostname or mac
	print "Adding new card for $hostname with:\n"; 
	print "PART [$new_part]  INTERFACE NUMBER [$new_interface_number]  MAC ADDRESS [$new_mac]\n";
	add_mac($new_part, $new_interface_number, $new_mac);
  gen_dhcp();

	if (( check_exists( 'hostname', $hostname )) == 1 ) {
		update_part_hostnames($old_part, $new_part);
	}
    else {
		add_part_hostnames($new_part, $new_interface_number, $hostname);
	}
	print "Done.\n";
}

sub check_jack_note {
    my ($this, $idf, $patchnumber ) = @_;
    ($idf) = sql_quotes($idf);
    ($patchnumber) = sql_quotes($patchnumber);

    my $note =
    $this->{dbh}->selectrow_array ("
	SELECT note
	FROM splat.jacks
	WHERE idf = $idf and jacknum = $patchnumber
    " );

    return ($note);
    
}

sub check_switch_interface_note {
    my ( $this, $device, $interface ) = @_;
    ($device) = sql_quotes($device);
    ($interface) = sql_quotes($interface);

    my $note =
    $this->{dbh}->selectrow_array ("
        SELECT note
        FROM splat.switch_interfaces 
        WHERE device = $device and interface = $interface
    " );

    return ($note);

}

sub check_part_mac { 
    my ( $this, $part ) = @_;
	($part) =  sql_quotes($part);

    my ( $interface_number, $mac_address ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT interface_number, mac_address
        FROM splat.mac_addresses
        WHERE part = $part
    " );

    return ($interface_number, $mac_address);
}


sub add_mac {
    my ($this, $new_part, $new_interface_number, $new_mac) = @_;
	($new_part, $new_interface_number, $new_mac) = sql_quotes($new_part, $new_interface_number, $new_mac);

    my $sth_insert = $this->{dbh}->prepare ( "
        INSERT INTO splat.mac_addresses
		VALUES ($new_part, $new_interface_number, $new_mac)
    " );

    $sth_insert->execute();
    $this->{dbh}->commit;
}

sub gen_dhcp {
  my $thisp = DBI->connect($CSL::DBs::csdb);
  my $key = "/p/lab/network/etc/.ssh/dhcp_id_rsa";
  my $basedir = "/s/dhcp/conf/";
  my $hdr = "dhcpd.header";
  my $mutable = "dhcpd.edit";
#  my $filename = "dhcpd.conf";
  my $filename = "dummy.conf";
  my $dhcpd_host = "dhcp.cs.wisc.edu";
  my $dhcpd_bin = "/var/adm/dhcpd/dhcpd";

  my $splat = $thisp->selectall_arrayref("select h.hostname, m.mac_address from splat.hostnames h, splat.mac_addresses m where h.part = m.part and h.interface_number = m.interface_number order by hostname asc");

  copy("$basedir/$hdr", "$basedir/$filename") or die("$!\n");

  open(my $fh, ">>$basedir/$filename") or die("Could not open the specified file: $!\n");
  open(my $mh, "$basedir/$mutable");

  my @mutable = <$mh>;
  print $fh $_ foreach (@mutable);

  close($mh);

  foreach (@$splat) {
    my $hostname = $_->[0];
    my $macaddr = $_->[1] || next;

    next if ($macaddr eq '00:00:00:00:00:00');
    next if $hostname !~ /^[\w.-]+$/;

    my $frag = <<EOF;
host $hostname {
  hardware ethernet $macaddr;
  fixed-address $hostname;
}
EOF

    print $fh $frag;
  }

  close($fh);

  my $cmd = qq{ssh -i $key -l root $dhcpd_host <<EOF
$dhcpd_bin -t -cf $basedir/$filename && echo SUCCESS
EOF};
#$dhcpd_bin -t -cf $basedir/$filename && /etc/rc.d/init.d/dhcpd restart && echo SUCCESS

=cut
#  $Expect::Log_Stdout = 0;
  my $timeout = 15;
  my $exp = Expect->spawn($cmd);

  $exp->expect($timeout,
      [ qr/Password/,
        sub {
              my $efh = shift;
              print $efh "$passwd\n";
              exp_continue;
            }
      ],
        [ timeout =>
        sub {
              die "Timeout connecting to dhcp server!\n";
            }
        ],
  );
=cut
  my $exitcode = system($cmd);
}

sub add_part_hostnames {
    my ($this, $new_part, $new_interface_number, $hostname) = @_;
	($new_part, $new_interface_number, $hostname) = sql_quotes($new_part, $new_interface_number, $hostname);

    my $sth_insert = $this->{dbh}->prepare ( "
        INSERT INTO splat.hostnames
		VALUES ($new_part, $new_interface_number, $hostname)
    " );

    $sth_insert->execute();
    $this->{dbh}->commit;
}


sub update_part_hostnames {
    my ($this, $old_part, $new_part) = @_;
	($old_part, $new_part) = sql_quotes($old_part, $new_part);

    my $sth_update = $this->{dbh}->prepare ( "
        UPDATE splat.hostnames
		SET part = $new_part
		WHERE part = $old_part
    " );

    $sth_update->execute();
    $this->{dbh}->commit;
}


sub delete_mac {
    my ($this, $mac_address) = @_;
	($mac_address) = sql_quotes($mac_address);

    my $sth_delete = $this->{dbh}->prepare ( "
        DELETE FROM splat.mac_addresses
        WHERE mac_address = $mac_address 
    " );

    $sth_delete->execute();
    $this->{dbh}->commit;
}

sub delete_mac_by_part_int {
    my ($this, $part,$interface_number) = @_;
    ($part) = sql_quotes($part);
    ($interface_number) = sql_quotes($interface_number);

    my $sth_delete = $this->{dbh}->prepare ( "
        DELETE FROM splat.mac_addresses
        WHERE part = $part and
        interface_number = $interface_number
    " );

    $sth_delete->execute();
    $this->{dbh}->commit;
}

sub delete_hostname {
    my ($this, $hostname) = @_;
	($hostname) = sql_quotes($hostname);

    my $sth_delete = $this->{dbh}->prepare ( "
        DELETE FROM splat.hostnames
        WHERE hostname = $hostname 
    " );

    $sth_delete->execute();
    $this->{dbh}->commit;
}


sub switch_update {
    my ( $this, $switch_command, $hostname, $jacknumber, $vlan, $nolock ) = @_;

    my ( $idf, $patchnumber ) = split_jacknumber( $jacknumber );
    my ( $switch, $portnumber ) = get_switch_portnumber( $idf, $patchnumber );
    my $network;
    my ( $part, $interface_number );
    my $mac;
    my @ios_commands;
    my $ios_config;
    my $ios_logs;
    my $fh_config;
    my @configblocks;
    my $trunk;

    eval {
        my $ios_config = XMLin('/p/lab/network/splat/ios_config.xml');
        my $ios_logs = "/p/lab/network/splat/ios_logs";

        unless ( ($switch_command =~ /attach/ && $nolock ) || $switch_command =~ /detach/ ) {

            # Get mac address for this host
            ( $part, $interface_number ) = get_part_interface( $hostname );
            $mac  = get_mac( $part, $interface_number );
        
            # Put mac address in cisco friendly format
            $mac =~ s/://g;
            $mac  =~ s/\S{4}/$&\./g;
            chop $mac;
        } 

	# Get media type for jack
	my $media_type = get_media_type( $idf, $patchnumber );
	my $trunk = get_trunk_mode( $hostname );    

	push @configblocks,"start";

	if($switch_command =~ /attach/){
                push @configblocks,"clear-port";
                
		if($trunk){
			push @configblocks,"trunk-attach";
		}else{
  			push @configblocks,"host-attach";
			push @configblocks,"portfast";
                }

		if(!$nolock){
                	push @configblocks,"port-security";
                }

		if($media_type =~ /cat3/){
	                push @configblocks,"speed-10";
		}
                push @configblocks,"enable-port";
	}

	if($switch_command =~ /detach/){
                 push @configblocks,"clear-port";
	}

        push @configblocks,"end";

        # Create an IOS command file to run on the switch
        my $fh_out = new FileHandle "> $ios_logs/$switch"  
            or die "Can't open switch output config file : $!\n";

        if ( defined $fh_out ) {
	    foreach my $block(@configblocks){
		#printf("Running block %s\n",$block);
		for ( $ios_config->{configblock}->{$block}->{cmd} ) {
		   chomp($_);
                   $_ =~ s/(\$\w+)/$1/gee;
                   print $fh_out "$_";
                }

	    }

        } 
    
        $fh_out->close;

    $this->{syslog}->send('SPLAT-CHANGE ' . $switch_command . ' ' . $switch . ' ' . $portnumber . ' ' . $hostname . ' ' . $jacknumber . ' ' . $this->{username},Priority=>'info');

    # Run command file on switch via RANCID's clogin
    unless($noswitchupdate){
    	$fh_config = new FileHandle "/s/rancid/bin/clogin -x $ios_logs/$switch $switch |" 
           or die "Can't push config to switch $switch : $!";
	}
    };

    unless($noswitchupdate){
        if ( $@ ne '' ) {
            die "\nSPLAT ERROR - Can't get info for switch update: $@\n\n";
        }

        # Get output from the switch
    while( <$fh_config> ) {
        print;
    }

        $fh_config->close;
    }
    switch_differ( $switch );

}  


sub switch_differ {
    my ($this, $switch ) = @_;

    if($noswitchupdate){
	print "\nNot running RANCID to collect config -t passed...\n";
	return 1;
    }
	

    print "\nRunning RANCID to collect switch config...\n";

    eval {
        my $fh_diff = new FileHandle "do-diffs -r $switch |"; 
            $fh_diff->close;
    };

    if ( $@ ne '' ) {
        die "\nSPLAT ERROR - Can't run RANCID's do-diffs: $@\n\n";
    }

    print "CVS commit for $switch config successful.\n\n";
}


sub info { 

    my ( $this, $query ) = @_;
    my $test;
    my $type;

    for ( 'hostname', 'jacknumber', 'idf', 'switch','room' ) {
        if ( $_ eq 'hostname' ) {

            unless(check_exists( $_, $query ) == 1){
		 $test = ensure_fqdn( $query );
                 $query = $test if ( $test );
	    }
        } 

        if ( check_exists( $_, $query ) >= 1 ) {
            $type = $_;	
        } 
    } 

    unless ( $type ) {
        print "\nSPLAT ERROR:  $query is not recognized by the network database.\n";
        usage();
    } 

    my ( $hostname, $jacknumber, $part, $interface_number, $mac, $idf, $patchnumber, $jack_room, $device, $portnumber, $idf_room, $building, $note, $network_at_attach,$maclock_status_at_attach, $jack_note );
 
    db_handler( 'connect' );
	
    push my @output, format_header();
    print join( "\n", @output ) ."\n";

    for ( $type ) {
        if ( /hostname/ ) { 
            $hostname = $query;
            my @output;
    
            ( $part, $interface_number ) = get_part_interface( $hostname );
            ( $mac ) = get_mac( $part, $interface_number );
            ( $idf, $patchnumber ) = get_idf_patchnumber( $part, $interface_number );
	    ( $jack_note ) = check_jack_note ($idf, $patchnumber);
            ( $device, $portnumber ) = get_device_portnumber( $idf, $patchnumber );
	    ( $jack_room ) = get_jack_room( $idf, $patchnumber );
            ( $idf_room, $building, $note ) = get_room_building( $idf );
            ( $network_at_attach ) = get_network_at_attach( $part, $interface_number );
            ( $maclock_status_at_attach ) = get_maclock_status_at_attach( $part, $interface_number );

            push @output, format_hostname_jacknumber( $hostname, $part, $interface_number, $mac, $idf, $patchnumber, $jack_room,  $device, $portnumber, $idf_room, $building, $note, $network_at_attach, $maclock_status_at_attach, $jack_note ); 

            if ( $full ) {
                if ( $portnumber && $device ) {
                    push @output, switch_stats( $portnumber, $device );
                    push @output, switch_log( $device );
			        push @output,  switch_portconfig ( $device, $portnumber );
                }
            }

            print join( "\n", @output ) ."\n";

        } elsif (/jacknumber/ ) { 
            $jacknumber = $query;
            my @output;

            ( $idf, $patchnumber ) = split_jacknumber( $jacknumber );
	    ( $jack_note ) = check_jack_note($idf, $patchnumber);
            ( $part, $interface_number ) = get_part_interface2( $idf, $patchnumber ); 
            ( $device, $portnumber ) = get_device_portnumber( $idf, $patchnumber); 
            ( $hostname ) = get_hostname( $part, $interface_number );
            ( $mac ) = get_mac( $part, $interface_number );
            ( $jack_room ) = get_jack_room( $idf, $patchnumber );
            ( $idf_room, $building, $note ) = get_room_building( $idf );
            ( $network_at_attach ) = get_network_at_attach( $part, $interface_number );
            ( $maclock_status_at_attach ) = get_maclock_status_at_attach( $part, $interface_number );

            push @output, format_hostname_jacknumber( $hostname, $part, $interface_number, $mac, $idf, $patchnumber, $jack_room, $device, $portnumber, $idf_room, $building, $note, $network_at_attach, $maclock_status_at_attach, $jack_note);

            if ( $full ) {
                if ( $portnumber && $device ) {
                    push @output, switch_stats( $portnumber, $device );
                    push @output, switch_log( $device );

                    if($info >= 2){
                        push @output,  switch_portconfig ( $device, $portnumber );
                    }


                }
            }
            print join( "\n", @output ) ."\n";

        } elsif ( /switch/ ) {
            my $switch = $query;
            format_switches( $switch );

        } elsif ( /idf/ ) {
            my $idf = $query;
            my @switches;
            my @header;

            push @header, sprintf( "%-40s", "Switches In IDF $idf" );
            push @header, "-" x 70;

            @switches = get_switches( $idf );
            for ( @switches ) {
                push @header, sprintf("%-20s", "$_" );
            } 

            push @header, " " x 70;
            print join( "\n", @header ) ."\n";

            if ( $full ) {  
                for ( @switches ) {
                    format_switches( $_ );
                } 
            } 
        } elsif ( /room/ ) {
	    my $room_query = $this->{dbh}->prepare("select * from splat.jacks where room = '$query' order by idf,jacknum");
	    $room_query->execute();

            while(my $queryhash_ref=$room_query->fetchrow_hashref){
 
            ( $device, $portnumber ) = get_device_portnumber( $queryhash_ref->{idf},$queryhash_ref->{jacknum});
            ( $part, $interface_number ) = get_part_interface2( $queryhash_ref->{idf},$queryhash_ref->{jacknum} );
            ( $hostname ) = get_hostname( $part, $interface_number );

            printf("%s-%s ",$queryhash_ref->{idf},$queryhash_ref->{jacknum});
	
	    if($device){
                printf("%s %s ",$device,$portnumber); 
            }else{
		print("Device not mapped to switch interface");
	    }
	    if($part){print "$hostname"}
            print "\n";
	    
	    }
            $room_query->finish;
	}
    }
  db_handler('disconnect');
}

sub detach {
    my $hostname = shift;
    my $jacknumber = get_jacknumber1( $hostname );
    my ( $this, $part, $interface ) =  get_part_interface( $hostname );
    ( $part, $interface ) = sql_quotes( $part, $interface );

    my $sth = $this->{dbh}->prepare ( "
        DELETE FROM splat.hosts
        WHERE (part, interface_number) = ($part, $interface)
    " );

    unless ( $sth->execute()) {
        die "SPLAT ERROR - Can't detach $hostname from $jacknumber in the splat database: ", $DBI::errstr, "\n";
    } 

    eval { $this->{dbh}->commit };
    if ( $@ ne '' ) {
        die "SPLAT ERROR - Unable to commit change to database: $@\n";
    }

        print "Host $hostname detached from jacknumber $jacknumber in the splat database.\n"
}


sub split_jacknumber {
    my $jacknumber = shift;
    my ( $this, $idf, $patchnumber ) ;
    if($jacknumber =~ /^(\w+)-(\w+)$/ ){
        ($idf,$patchnumber) = ($1,$2);
    }
    return ( $idf, $patchnumber );
}


sub get_jacknumber1 {
    my $hostname = shift;
    my ( $this, $idf, $patchnumber );

    # First get part and interface_number for this host
    my ( $part, $interface_number ) = get_part_interface( $hostname );
    ( $part, $interface_number ) = sql_quotes( $part, $interface_number );

    my $sth = $this->{dbh}->prepare ( "
        SELECT idf, jacknum
        FROM splat.hosts
        WHERE (part, interface_number) = ($part, $interface_number) 
    " );

    my $rc = $sth->execute();
    die "\nSPLAT ERROR:  Hostname $hostname isn't currently attached to a jacknumber.\n\n" unless ( $rc == 1 );

    $sth->bind_columns( \$idf, \$patchnumber );
    $sth->fetch;

    return  "$idf-$patchnumber";
}


sub get_jacknumber2 {
    my ( $this, $switch, $device ) = @_;

    my ( $idf, $patchnumber ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT idf, jacknum
        FROM splat.port_mapping
        WHERE ( switch, device ) = ( $switch, $device ) 
    " );

    return "$idf-$patchnumber";
}


sub get_hostname {
    my ( $this, $part, $interface_number ) = @_;
    ( $part, $interface_number ) = sql_quotes( $part, $interface_number );

    my ( $hostname ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT hostname
        FROM splat.hostnames 
        WHERE ( part, interface_number ) = ( $part, $interface_number ) 
    " );

    return $hostname;
}


sub get_part_interface {
    my ($this, $hostname) = @_;
    ( $hostname ) = sql_quotes( $hostname );

    my ( $part, $interface_number ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT part, interface_number
        FROM splat.hostnames 
        WHERE hostname = $hostname
    " );

    return ( $part, $interface_number );
}


sub get_part_interface2 {
    my ( $this, $idf, $patchnumber ) = @_;
    ( $idf, $patchnumber ) = sql_quotes( $idf, $patchnumber ); 

    my ( $part, $interface_number ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT part, interface_number
        FROM splat.hosts
        WHERE (idf, jacknum) = ($idf, $patchnumber)
    " );

    return ( $part, $interface_number );
}


sub get_switches { 
    my ($this, $idf)  = @_;
    ( $idf ) = sql_quotes( $idf );

    my $switch;
    my @switches;
    my @final;
    my %seen;

    my $sth = $this->{dbh}->prepare( "
        SELECT device
        FROM splat.switch_interfaces
        WHERE idf  =  $idf
    " );

    $sth->execute();
    $sth->bind_columns( \$switch );
    while ( $sth->fetch ) {
        push ( @switches, "$switch" ) unless $seen{ $switch }++;
    }

    return @switches;
}

sub get_switch_portnumber {
    my ( $this, $idf, $patchnumber ) = @_;
    ( $idf, $patchnumber ) = sql_quotes( $idf, $patchnumber ); 

    # Identify switch and portnumber
    my ( $switch, $portnumber ) =
    $this->{dbh}->selectrow_array ( "
        SELECT device, interface
        FROM splat.port_mapping
        WHERE idf = $idf
        AND jacknum = $patchnumber 
    " );

    return ( $switch, $portnumber );
}

sub get_port_jacknumbers { 
    my ($this, $switch ) = @_;
    ( $switch ) = sql_quotes( $switch );
    my ( $interface, $idf, $jacknum );
    my @portnumbers;

    my $sth = $this->{dbh}->prepare( "
        SELECT interface, idf, jacknum
        FROM splat.port_mapping
        WHERE device  =  $switch
	ORDER by substring(interface from '[0-9]')::int,substring(interface from '[0-9]*\$')::int
    " );
    $sth->execute();

   $sth->bind_col( 1, \$interface );
   $sth->bind_col( 2, \$idf );
   $sth->bind_col( 3, \$jacknum );

    while ( $sth->fetch ) {
       push @portnumbers, "$interface $idf-$jacknum";
    }

    return @portnumbers;
}


sub get_idf_patchnumber {
    my ( $this, $part, $interface_number ) = @_;
    ( $part, $interface_number ) = sql_quotes( $part, $interface_number );

    my ( $idf, $patchnumber ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT idf, jacknum
        FROM splat.hosts
        WHERE (part, interface_number) = ($part, $interface_number) 
    " );

    return ( $idf, $patchnumber );
}


sub get_idf_patchnumber2 {
    my ($this, $room) = @_;
    ( $room ) = sql_quotes( $room );

    my ( $idf, $patchnumber ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT idf, jacknum
        FROM splat.jacks
        WHERE room = $room
    " );

    return ( $idf, $patchnumber );
}


sub get_device_portnumber {
    my ( $this, $idf, $patchnumber ) = @_;
    ( $idf, $patchnumber ) = sql_quotes( $idf, $patchnumber );

    my ( $device, $portnumber ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT device, interface
        FROM splat.port_mapping
        WHERE ( idf, jacknum ) = ( $idf, $patchnumber ) 
    " );

    return ( $device, $portnumber );
}


sub get_device_portnumber2 {
    my ($this, $idf) = @_;
    ( $idf ) = sql_quotes( $idf );

    my ( $device, $portnumber ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT device, interface
        FROM splat.switch_interfaces
        WHERE idf = $idf
    " );

    return ( $device, $portnumber );
}


sub get_mac {
    my ( $this, $part, $interface_number ) = @_;
    ( $part, $interface_number ) = sql_quotes( $part, $interface_number );

    my ( $mac ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT mac_address
        FROM splat.mac_addresses
        WHERE (part, interface_number) = ($part, $interface_number) 
    " );

    return $mac;
}


sub get_patchnumber {
    my ( $this, $device, $portnumber, $idf ) = @_;
    ( $device, $portnumber, $idf ) = sql_quotes( $device, $portnumber, $idf );

    my ( $patchnumber ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT jacknum
        FROM splat.port_mapping
        WHERE (device, interface, idf) = ($device, $portnumber, $idf)
    " );
    
    return $patchnumber;
}

sub get_vlan_at_attach {
   my ( $this, $part, $interface_number ) = @_;
   ( $part, $interface_number ) = sql_quotes( $part, $interface_number );

   my ( $attach_vlan ) = 
   $this->{dbh}->selectrow_array ( "
        SELECT vlan
        FROM splat.hosts
        WHERE (part, interface_number) = ($part, $interface_number)
   " );
   
   return $attach_vlan;
}

sub get_network_at_attach {
   my ( $this, $part, $interface_number ) = @_;
   ( $part, $interface_number ) = sql_quotes( $part, $interface_number );

   my ( $attach_network ) =
   $this->{dbh}->selectrow_array ( "
	SELECT network
	FROM splat.hosts
	WHERE (part, interface_number) = ($part, $interface_number)
   " );

   return $attach_network;
}

sub get_maclock_status_at_attach {
   my ( $this, $part, $interface_number ) = @_;
   ( $part, $interface_number ) = sql_quotes( $part, $interface_number );

   my ( $maclock_status ) =
   $this->{dbh}->selectrow_array ( "
        SELECT mac_locked
        FROM splat.hosts 
        WHERE (part, interface_number) = ($part, $interface_number)
   " );

  return $maclock_status;
}

sub get_room_building {
    my ($this, $idf ) = @_;
    ( $idf ) = sql_quotes( $idf );

    my ( $room, $building, $note ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT room, building, note
        FROM splat.idfs
        WHERE name = $idf
    " );

    return ( $room, $building, $note );
}


sub get_building_note {
    my ($this, $room ) = @_;
    ( $room ) = sql_quotes( $room );

    my ( $building, $note ) =
    $this->{dbh}->selectrow_array ( "
        SELECT  building, note
        FROM splat.idfs
        WHERE room = $room
    " );

    return ( $building, $note );
}


sub get_network {
    my ($this, $hostname ) = @_;

    my $packedip = inet_aton( $hostname ) or die "Can't find valid IP address for $hostname in DNS : $! \n";
    my $ip = inet_ntoa( $packedip);

    ( $ip ) = sql_quotes( $ip );

    my $query = $this->{dbh}->prepare("SELECT network from splat.vlans WHERE $ip <<= network");
    $query->execute();

    if($query->rows!=1){
        printf("\nHost IP is contained in %d network segments, contact administrator\n",$query->rows);
        usage();
    }

    my $network=$query->fetchrow_array;

    $query->finish;
    return $network;
}


sub get_media_type {
    my ( $this, $idf, $jacknum ) = @_;
    ( $idf, $jacknum ) = sql_quotes( $idf, $jacknum );

    my ( $media_type ) = 
    $this->{dbh}->selectrow_array ( "
        SELECT media
        FROM splat.jacks
        WHERE ( idf, jacknum ) = ( $idf, $jacknum )
    " );
 
	# Use "default" if undefined
    $media_type = $media_type || "default";

    return $media_type;
}

sub get_trunk_mode {
    my ($this, $hostname ) = @_;
    if ( $hostname =~ /aironet/ ){
        return 1;
    }
    return 0;
}

sub get_jack_room {
	my ( $this, $idf, $jacknum ) = @_;
	( $idf, $jacknum ) = sql_quotes( $idf, $jacknum );
	
	my ( $jack_room ) =
	$this->{dbh}->selectrow_array ( "
        	SELECT room
        	FROM splat.jacks
        	WHERE ( idf, jacknum ) = ( $idf, $jacknum )
	" );

	# Use "default" if undefined
	$jack_room = $jack_room || "NONE";
    
	return $jack_room;	
}


sub ensure_fqdn {
    my ($this, $hostname) = @_;
    my $fqdn;
    my $answer;

    return $hostname
        if ( $hostname =~ /cs.wisc.edu|stat.wisc.edu/ );

    if ( $fqdn = ( gethostbyname( $hostname ))[0]) {
        $hostname = $fqdn; 
        print STDERR "Use fully qualified domain name $hostname?  [Y]/n : ";
		chomp ($answer = <STDIN>);
		my $length = length( $answer );
		
		unless (( $answer =~ /^y/i ) || ( $length == 0 )) {
			die "Exiting.  Please ensure you have the correct fully qualified domain name.\n";
        } else {
            return $hostname;
        } 
    } else {

    return;
    } 
}


sub check_exists {
    my ( $this, $type, $query ) = @_;
    my $rc;

    for ( $type ) {
        if ( /hostname/ ) { 
            ( $query ) = sql_quotes( $query );

            my $sth = $this->{dbh}->prepare ( "
                SELECT *
                FROM splat.hostnames
                WHERE hostname = $query
            " );
            $rc = $sth->execute();

        } elsif ( /jacknumber/ ) {
            my ( $idf, $patchnumber ) = split_jacknumber( $query );
            ( $idf, $patchnumber ) = sql_quotes( $idf, $patchnumber );

            my $sth = $this->{dbh}->prepare ( "
                SELECT *
                FROM splat.jacks
                WHERE ( idf, jacknum ) = ( $idf, $patchnumber ) 
            " );
            $rc = $sth->execute();

        } elsif ( /idf/ ) {
            ( $query ) = sql_quotes( $query );

            my $sth = $this->{dbh}->prepare ( "
                SELECT * 
                FROM splat.idfs
                WHERE name = $query
            " );
            $rc = $sth->execute();

        } elsif ( /switch/ ) {
            ( $query ) = sql_quotes( $query );

            my $sth = $this->{dbh}->prepare ( "
                SELECT * 
                FROM splat.switch_interfaces
                WHERE device = $query
            " );
            $rc = $sth->execute();

        } elsif ( /vlan/ ) {
            ( $query ) = sql_quotes( $query );

            my $sth = $this->{dbh}->prepare ( "
                SELECT * 
                FROM splat.vlans
                WHERE vlan = $query
            " );
            $rc = $sth->execute();

	} elsif ( /room/ ) {
            ( $query ) = sql_quotes( $query );

	    my $sth = $this->{dbh}->prepare ( "
	        SELECT *
		FROM splat.jacks
		WHERE room = $query
	    " );
	    $rc = $sth->execute();
												
	}
	else {
        
        return 0;
        } 
    } 

    return $rc;
}


sub check_args {
    my ( $this, $action, $number ) = @_;

    if ( scalar @ARGV != "$number" ) {
        print "\nSPLAT ERROR: Incorrect number of arguments for $action.\n";
        usage();
    }
}


sub check_if_host_attached {
    my ($this, $hostname ) = @_;
    my ( $part, $interface_number ) = get_part_interface( $hostname );
    my $check_host = get_idf_patchnumber( $part, $interface_number );
    my $current_jack;

    # Is this host currently attached to a jack?
    if ( defined( $check_host ) ) {
        my ( $idf, $patchnumber ) = get_idf_patchnumber( $part, $interface_number );
        my $current_jack = "$idf-$patchnumber";

        print "WARNING - Hostname $hostname is currently attached to jack $current_jack.\n"; 
        print "OK to detach this connection?  [n]/y : ";
        unless ( <STDIN> =~ /^y/i ) {
            print "Exiting - no changes made.\n";
            exit( 0 );
        } else {
            return( $current_jack );
        } 
    } else {
        return( $current_jack );
   } 
}


sub check_if_jack_attached { 
    my ($this, $jacknumber ) = @_;
    my ( $idf, $patchnumber ) = split_jacknumber( $jacknumber );
    my $check_jack = get_part_interface2( $idf, $patchnumber );

    # Is this jacknumber currently attached to another host?
    if ( defined( $check_jack ) ) {
        my ( $part, $interface_number ) = get_part_interface2( $idf, $patchnumber );
        my $other_host = get_hostname( $part, $interface_number );
        print "WARNING - Jack $jacknumber is currently attached to host $other_host.\n"; 
        print "OK to detach this connection?  [n]/y : ";

        unless ( <STDIN> =~ /^y/i ) {
            print "Exiting with no changes made.\n";
            exit( 0 );
        } else {
            return( $other_host );
        }
    } 
}


sub sql_quotes {
    my ($this, @quote_list ) = @_;
    my @quoted;

    for ( @quote_list ) {
        push @quoted, $this->{dbh}->quote( $_ );
    }

    return @quoted;
}


sub switch_stats {
    my ( $this, $portnumber, $device ) = @_;
    my @output;

	push @output, sprintf( "%-32s", "Available Switch Statistics" );
	push @output, "-" x 70;

    # Get portnumber stats from switch
    my $fh = new FileHandle "/s/rancid/bin/clogin -c 'show interfaces $portnumber' $device |";
    while( <$fh> ) {
        if ( /^$portnumber|  \w/ ) {
            chomp;
            push @output, $_;
        }
    }
    $fh->close;

    push @output, sprintf( " " x 70 );

    return @output;
}

sub switch_log {
    my ($this, $device ) = @_;
    my @output;

    push @output, sprintf( "%-32s", "Last 10 Lines of Switch Log" );
    push @output, "-" x 70;

    my $fh = new FileHandle "/s/rancid/bin/clogin -c 'show log' $device |"
      or die "Can't get log from switch $device : $!";

    my @log;
    while( <$fh> ) {
        chomp;
        push @log, $_;
    }
    $fh->close;

    push @output, @log[-12..-3];   # Just get last 10 important lines

    push @output, sprintf(" " x 70);

    return @output;
}

sub switch_portconfig {   
    my ($this, $device, $interface ) = @_;
    my @output;
    my $show = 0;

    push @output, sprintf( "%-32s", "Interface configuration" );
    push @output, "-" x 70;

    my $fh = new FileHandle "/s/rancid/bin/clogin -c 'show run int $interface' $device |"
      or die "Can't get log from switch $device : $!";
        
    my @log;
 
   while( <$fh> ) {
        chomp;

	if($_ =~ /interface $interface/){
		$show = 1;
	}
	if($show){
        	push @output, $_;
	}
        if($_ =~ /end/){  
                $show = 0;
        }
    }
    $fh->close;   

    push @output, sprintf( " " x 70 );

    return @output;
}

sub format_header {
    my $time = localtime;
    my @header;

    push @header, " " x 70;
    push @header, "-" x 70;
    push @header, sprintf( "%-40s%28s", "SPLAT NETWORK DATABASE", $time );
    push @header, "-" x 70;

    return @header;
}


sub format_hostname_jacknumber {
    my ( $hostname, $part, $interface_number, $mac, $idf, $patchnumber, $jack_room, $device, $portnumber, $idf_room, $building, $note, $network_at_attach, $maclock_status, $jack_note )  = @_;
    my @results;
    my @output;

    # Place NONE in any undef fields
    for ( $hostname, $part, $interface_number, $mac, $idf, $patchnumber, $jack_room, $device, $portnumber, $idf_room, $building, $note, $network_at_attach ) {
        $_ = defined($_) ? $_ : 'NONE';
    }; 

    if($maclock_status){
	$maclock_status = "YES";
    }else{
	$maclock_status = "NO";
    }

    push @results, sprintf( " Hostname: %-48s", $hostname);
    push @results, sprintf( "     Part: %-25s        Bldg: %-10s", $part, $building );
    push @results, sprintf( "Interface: %-25s        IDF Room: %-10s", $interface_number, $idf_room );
    push @results, sprintf( "      MAC: %-25s        Note: %-20s", $mac, $note );
    push @results, sprintf( "   Locked: %-25s        Jack Room: %-20s",$maclock_status, $jack_room);
    push @results, sprintf( "  Network: %s at attach time",$network_at_attach);
    push @results, " " x 70;
    push @results, sprintf( "%-5s  %-4s  %-28s   %-20s", 'IDF', 'Jack', 'Switch', 'Portnumber' );
    push @results, "-" x 70;
    push @results, sprintf( "%-5s  %-4s  %-28s   %-20s", $idf, $patchnumber, $device, $portnumber );

    if(defined $jack_note){
       push @results, "-" x 30 . "JACK NOTE" . "-" x 30 . "\n";
       push @results, sprintf("%s\n", $jack_note);
       push @results, "-" x 28 . "JACK NOTE END" . "-" x 28 . "\n";
    }
    push @results, "-" x 70;
    push @results, " " x 70;

    push @output, join( "\n", @results );

    return @output;
}


sub format_switches {
    my $switch = shift;
    my @switch_results;
    my @output;
    my ( $hostname, $jacknumber, $part, $interface_number, $idf, $patchnumber, $device, $portnumber );

    my ( @portnumbers ) = get_port_jacknumbers( $switch );

    for ( @portnumbers ) {
        my ( $portnumber, $jacknumber ) = split(/ /, $_ );
        my ( $idf, $patchnumber ) = split_jacknumber( $jacknumber );
        my ( $part, $interface_number ) = get_part_interface2( $idf, $patchnumber );
        my ( $hostname ) = get_hostname( $part, $interface_number );

        # Place NONE in any undef fields
        for ( $portnumber, $jacknumber, $hostname ) { 
            $_ = defined ($_) ? $_ : 'NONE';
        }; 

        push @switch_results, "$portnumber $jacknumber $hostname";
    }



    push @output, " " x 70;
    push @output, "-" x 70;
    push @output, sprintf("Switch: %-40s", $switch );
    push @output, sprintf( "%-16s  %-9s  %-s", 'Interface', 'Jacknum', 'Hostname' );
    push @output, "-" x 70;

    for ( @switch_results ) {
        my ( $interface, $jack, $hostname ) = split(/ /, $_);
        push @output, sprintf("%-16s  %-9s  %-36s", $interface, $jack, $hostname);
    } 
        push @output, " " x 70;

    print STDOUT join( "\n", @output ) ."\n"; 
}


sub format_commands { 
    my $switch = shift;
    my @output;
    push @output,  format_header();
    push @output, " " x 70;
    push @output, sprintf("Last command set run on switch: %-40s", $switch );
    push @output, "-" x 70;

    my $fh = new FileHandle "/p/lab/network/splat/ios_logs/$switch";

    while( <$fh> ) {
        chomp;
        push @output, $_;
    }

    push @output, " " x 70;

    print join( "\n", @output ) ."\n";
}


sub usage {
    print <<USAGE;

USAGE: 
Either POSIX or GNU styled options

splat [ -a|--attach [ -v|--vlan vlan ] [ -n|--nolock ] hostname jacknumber ]
      [ -c|--commands switch ]
      [ -d|--detach hostname ]
      [ -e|--ethset <hostname> <C number> <interface number> <mac address>]
      [ -h|--help ]
      [ -s|--swap hostname1 hostname2 ]
      [ -i|--info [ -f|--full ]  [ hostname | idf | jacknumber |  switch ]]

USAGE

exit 0;
}

1;
