# # Miscellaneous routines to manipulate things in A3COM-SWITCHING-SYSTEMS-MIB # # Jim Trocki # # $Id: Sys.pm,v 1.8 2000/08/18 21:40:57 trockij Exp $ # # Copyright (C) 1998 Jim Trocki # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package A3Com::Sys; use strict; use vars qw($VERSION); use SNMP; use Socket; use Expect; sub filetransfer; sub reset; sub console_password; sub system_info; sub get_vlaninfo; sub _delete_row; sub _handle_bad_set; $VERSION = "0.05"; # # %var = ( # host => "hostname of switch", # community => "snmp write community name", # owner => "mgmt owner", # operation => "save_nvdata|restore_nvdata|softwareupdate", # tftphost => "hostname", # file => "filename", # status => \&status_callback, # force => "true|false", # ) # sub filetransfer { my %var = @_; my ($r, $errstr); $ENV{"MIBS"} = "A3COM-SWITCHING-SYSTEMS-MIB"; my $delete_row = \&_delete_row; if ($var{"operation"} ne "save_nvdata" && $var{"operation"} ne "restore_nvdata" && $var{"operation"} ne "softwareupdate") { return (undef, "unknown operation"); } if ($var{"host"} eq "") { return (undef, "no host specified"); } elsif ($var{"community"} eq "") { return (undef, "no community specified"); } elsif ($var{"owner"} eq "") { return (undef, "no owner specified"); } elsif ($var{"tftphost"} eq "") { return (undef, "no TFTP host specified"); } elsif ($var{"file"} eq "") { return (undef, "no file specified"); } elsif ($var{"force"} !~ /^true|false$/) { return (undef, "force not set to true of false"); } my $dir; if ($var{"operation"} eq "save_nvdata") { $dir = "localToRemote"; } else { $dir = "remoteToLocal"; } my $src; if ($var{"operation"} eq "softwareupdate") { $src = "storageFlashMemory"; } else { $src = "storageNonVolatileMemory"; } my $rsrcattr; if ($var{"operation"} eq "softwareupdate") { $rsrcattr = SNMP::translateObj ("a3ComSysFtSystemOperationalCode"); } else { $rsrcattr = SNMP::translateObj ("a3ComSysFtSystemConfiguration"); } my $s = new SNMP::Session ( DestHost => $var{"host"}, Timeout => 10_000_000, Retries => 3, Community => $var{"community"}, UseEnums => 1, ); return (undef, "could not create session") if (!defined $s); # # start from scratch # &{$delete_row}($s); # # begin row creation # $s->set ([ ["a3ComSysFtRowStatus", 1, "createAndWait", "INTEGER"], ["a3ComSysFtOwnerString", 1, $var{"owner"}, "OCTETSTR"], ]); ($r, $errstr) = _handle_bad_set ($s, "could not create row", $delete_row); return (undef, $errstr) if (!defined $r); $s->set ([ ["a3ComSysFtDirection", 1, $dir, "INTEGER"], ["a3ComSysFtLocalStorageType", 1, $src, "INTEGER"], ["a3ComSysFtLocalResourceType", 1, "resourceSystem", "INTEGER"], ["a3ComSysFtLocalResourceMask", 1, "\x00\x00\x00\x80", "OCTETSTR"], ]); ($r, $errstr) = _handle_bad_set ($s, "could not set resource", $delete_row); return (undef, $errstr) if (!defined $r); $s->set ([ ["a3ComSysFtLocalResourceAttribute", 1, $rsrcattr, "OBJECTID"], ["a3ComSysFtRemoteAddressType", 1, "addrIp", "INTEGER"], ["a3ComSysFtRemoteAddress", 1, inet_aton($var{"tftphost"}), "OCTETSTR"], ["a3ComSysFtRemoteFileName", 1, $var{"file"}, "OCTETSTR"], ]); ($r, $errstr) = _handle_bad_set ($s, "could not set data source", $delete_row); return (undef, $errstr) if (!defined $r); # # clear username and stuff # $s->set ([ ["a3ComSysFtRemoteUserName", 1, "", "OCTETSTR"], ["a3ComSysFtRemoteUserPassword", 1, "", "OCTETSTR"], ["a3ComSysFtForceTransfer", 1, $var{"force"}, "INTEGER"], ]); ($r, $errstr) = _handle_bad_set ($s, "could not set username", $delete_row); return (undef, $errstr) if (!defined $r); # # start the transfer # $s->set ([ ["a3ComSysFtRowStatus", 1, "active", "INTEGER"], ]); ($r, $errstr) = _handle_bad_set ($s, "could not start transfer", $delete_row); return (undef, $errstr) if (!defined $r); # # monitor the transfer # my @a; while ((@a = $s->get ([ ["a3ComSysFtStatus", 1], ["a3ComSysFtBytesTransferred", 1], ]))[0] eq "statusInProgress") { &{$var{"status"}}(@a) if (ref($var{"status"}) eq "CODE"); sleep 2; } if ($a[0] eq "statusSuccessfulCompletion" || $s->{"ErrorStr"} eq "Timeout") { # all is OK } else { &{$delete_row}($s); my $e = $s->{"ErrorStr"}; return (undef, "file transfer not completed: status[$a[0]] error[$e]"); } # # delete row # &{$delete_row}($s); return 1; } sub _delete_row { my $s = shift; $s->set ([ ["a3ComSysFtRowStatus", 1, "destroy", "INTEGER"], ]); } sub _handle_bad_set { my ($s, $msg, $del) = @_; if ($s->{"ErrorStr"}) { my $e = $s->{"ErrorStr"}; my $ind = $s->{"ErrorInd"}; &{$del}; return (undef, "$msg: $e item $ind"); } return 1; } # # Resets the system # # reset ( # host => "hostname", # community => "community", # ) # sub reset { my %var = @_; my ($r, $errstr); $ENV{"MIBS"} = "A3COM-SWITCHING-SYSTEMS-MIB"; my $s = new SNMP::Session ( DestHost => $var{"host"}, Timeout => 5_000_000, Retries => 1, Community => $var{"community"}, UseEnums => 1, ); return (undef, "could not create session") if (!defined $s); if ($var{"host"} eq "") { return (undef, "no host specified"); } elsif ($var{"community"} eq "") { return (undef, "no community specified"); } $s->set (["a3ComSysSystemAction", 0, "reset", "INTEGER"]); if ($s->{"ErrorStr"} || $s->{"ErrorStr"} ne "Timeout") { return (undef, "reset failed: " . $s->{"ErrorStr"}); } return 1; } sub system_info { my %var = @_; $ENV{"MIBS"} = "A3COM-SWITCHING-SYSTEMS-MIB"; my $s = new SNMP::Session ( DestHost => $var{"host"}, Timeout => 5_000_000, Retries => 1, Community => $var{"community"}, UseEnums => 1, ); return (undef, "could not create session") if (!defined $s); if ($var{"host"} eq "") { return (undef, "no host specified"); } elsif ($var{"community"} eq "") { return (undef, "no community specified"); } my $vb = new SNMP::VarList ( ["a3ComSysSystemType", 0], ["a3ComSysSystemHardwareRevision", 0], ["a3ComSysSystemSoftwareRevision", 0], ["a3ComSysSystemChassisSerialNumber", 0], ["a3ComSysSystemName", 0], ["a3ComSysSystemId", 0], ["a3ComSysModuleCardInfoModuleSerialNumber", 1], ); $s->get ($vb); if ($s->{"ErrorStr"}) { return (undef, "could not get system info: " . $s->{"ErrorStr"}); } my $v = { type => $vb->[0]->val, hardware_revision => join (".", unpack ("C C", $vb->[1]->val)), software_revision => join (".", unpack ("C C C", $vb->[2]->val)), part_number => $vb->[3]->val, name => $vb->[4]->val, id => sprintf ("%x", $vb->[5]->val), serial_number => $vb->[6]->val, }; return (1, $v); } sub console_password { my %var = @_; my ($r, $errstr); $ENV{"MIBS"} = "A3COM-SWITCHING-SYSTEMS-MIB"; if ($var{"host"} eq "") { return (undef, "no host specified"); } elsif ($var{"community"} eq "") { return (undef, "no community specified"); } my $s = new SNMP::Session ( DestHost => $var{"host"}, Timeout => 5_000_000, Retries => 1, Community => $var{"community"}, UseEnums => 1, ); return (undef, "could not create session") if (!defined $s); my @p; if (defined $var{"read"}) { push @p, ["a3ComSysSystemConsoleReadPwd", 0, $var{"read"}, "OCTETSTR"]; } if (defined $var{"write"}) { push @p, ["a3ComSysSystemConsoleWritePwd", 0, $var{"write"}, "OCTETSTR"]; } if (defined $var{"adm"}) { push @p, ["a3ComSysSystemConsoleAdminPwd", 0, $var{"adm"}, "OCTETSTR"]; } if (@p == 0) { return (undef, "must set read, write, or adm"); } my $v = new SNMP::VarList (@p); $s->set ($v); if ($s->{"ErrorStr"}) { return (undef, "set failed: error[" . $s->{"ErrorStr"} . "] ind[" . $s->{"ErrorInd"} . "]"); } return 1; } sub get_password { my $prompt = shift; my $pass; if (-t STDIN) { my $sigint = $SIG{"INT"}; $SIG{"INT"} = \&_restore_echo_and_exit; system "stty -echo"; print STDERR $prompt; chop ($pass = ); print STDERR "\n"; system "stty echo"; $SIG{"INT"} = $sigint; } else { chomp ($pass = ); } return $pass; } sub _restore_echo_and_exit { system "stty echo"; exit 1; } # # retrieve VLAN info # sub get_vlaninfo { my %var = @_; my $login = $var{"login"} || "read"; my $password = $var{"password"} || ""; my $host = $var{"host"}; return (undef, "no host supplied") if ($host eq ""); $Expect::Log_Stdout = 0; my $s = Expect->spawn ("telnet $host"); return (undef, "could not telnet to $host") if (!$s); if (!$s->expect (15, ("Select access level (read, write, administer): "))) { $s->hard_close(); return (undef, "did not get login prompt in time"); } $s->clear_accum; print $s "$login\r"; if (!$s->expect (15, ("Password:"))) { $s->hard_close(); return (undef, "did not get password prompt in time"); } $s->clear_accum; print $s "$password\r"; my @n = $s->expect (8, "Incorrect password", "Select menu option: "); if ($n[0] == 1) { $s->hard_close(); return (undef, "incorrect password"); } elsif (!defined $n[0] && $n[1] eq "1:TIMEOUT") { $s->hard_close(); return (undef, "timeout waiting for prompt"); } $s->clear_accum; print $s "brid vlan detail all\r"; my $vlaninfo; my ($pos, $err, $string, $before, $after) = $s->expect (30, ("Select menu option:")); if ($err eq "1:TIMEOUT") { $s->hard_close(); return (undef, "timeout waiting for vlan info"); } $vlaninfo = $before; print $s "logout\r"; ($pos, $err, $string, $before, $after) = $s->expect (30, ("Exiting")); if ($err eq "1:TIMEOUT") { $s->hard_close(); return (undef, "timeout after issuing 'logout'"); } $s->hard_close(); # # decode junk # my $tbl = ""; my %idx_vid = (); my %vlan = (); for (split (/\n\r/, $vlaninfo)) { next if (/^\s*$/); s/^\s*//; last if (/^Menu options/); if (/index \s+ vid \s+ type \s+ origin \s*$/ix) { $tbl = "vlan"; next; } elsif (/index \s+ vid \s+ type \s+ origin \s+ name/ix) { $tbl = "vlanCB"; next; } elsif (/index \s+ name \s+ ports/ix) { $tbl = "port"; next; } elsif (/index \s+ port \s+ tag/ix) { $tbl = "tag"; next; } elsif (/index \s+ protocol/ix) { $tbl = "protCB"; next; } elsif (/index \s+ layer \s+ 3/ix) { $tbl = "l3infoCB"; next; } elsif (/^index/ix) { $tbl = "unknown"; next; } s/[\r\n]+//g; s/\s*$//; if ($tbl eq "vlan") { my ($idx, $vid, $type, $origin) = split (/\s+/, $_); $idx_vid{$idx} = $vid; $vlan{$vid}->{"type"} = $type; $vlan{$vid}->{"origin"} = $origin; } elsif ($tbl eq "vlanCB") { my ($idx, $vid, $type, $origin, $name, $ports) = split (/\s+/, $_); $idx_vid{$idx} = $vid; $vlan{$vid}->{"type"} = $type; $vlan{$vid}->{"origin"} = $origin; $vlan{$vid}->{"name"} = $name; $vlan{$vid}->{"ports"} = "$ports"; } elsif ($tbl eq "port") { my ($idx, $name, $ports) = split (/\s+/, $_); $vlan{$idx_vid{$idx}}->{"name"} = $name; $vlan{$idx_vid{$idx}}->{"ports"} = "$ports"; } elsif ($tbl eq "tag") { my ($idx, $port, $tag) = split (/\s+/, $_); my $vid = $idx_vid{$idx}; $vlan{$vid}->{"porttags"}->{$port} = $tag; } elsif ($tbl eq "protCB") { my ($idx, $prot) = split (/\s+/, $_); $vlan{$idx_vid{$idx}}->{"protocol"} = $prot; } elsif ($tbl eq "l3infoCB") { my ($idx, $addr) = split (/\s+/, $_, 2); $vlan{$idx_vid{$idx}}->{"l3addr"} = $addr; } else { # unknown } } return (1, {%vlan}); } sub set_screenheight { my %var = @_; my $SWITCH = $var{"host"}; my $LOGIN = $var{"login"}; my $PASSWORD = $var{"password"}; my $s = Expect->spawn ("telnet $SWITCH") || return (undef, "could not telnet to $SWITCH"); $s->log_stdout(0) unless $var{"verbose"}; # # log in # $s->expect (15, ("Select access level (read, write, administer): ")) || return (undef, "did not get login prompt in time"); $s->clear_accum; print $s "$LOGIN\r"; $s->expect (8, ("Password:")) || return (undef, "did not get password prompt"); $s->clear_accum; print $s "$PASSWORD\r"; my @n = $s->expect (8, "Incorrect password", "Select menu option: "); if ($n[0] == 1) { $s->hard_close(); return (undef, "incorrect password for $SWITCH"); } elsif (!defined $n[0] && $n[1] eq "1:TIMEOUT") { $s->hard_close(); return (undef, "timeout logging in to $SWITCH, it says {$n[3]}"); } $s->clear_accum; print $s "system screenHeight 0\r"; @n = $s->expect (45, "the new default screen height"); $s->clear_accum; if (!defined ($n[0])) { $s->hard_close(); return (undef, "error ($n[1]) while setting screen height $SWITCH\n$n[3]"); } print $s "y\r"; @n = $s->expect (45, "Select menu option:"); $s->clear_accum; if (!defined ($n[0])) { $s->hard_close(); return (undef, "error ($n[1]) no menu prompt after setting scrn height $SWITCH\n$n[3]"); } # # log out politely # print $s "logout\r"; if ($s->expect (8, "Exiting") != 1) { $s->hard_close(); return (undef, "did not get logout confirmation"); } $s->hard_close(); return (1, 1); }