我认为在Perl中的Telnet输入缓冲区问题

时间:2012-04-30 19:09:27

标签: perl stdin telnet

所以在过去的一个月里,我一直在编写一个脚本来在HP procurve交换机上进行主清除。我和内部的惠普人谈过,他们使用的是一个完全不同的系统,所以他们没有帮助。我相信我已经弄清楚了。这就是我的脚本我应该做的:

  1. 建立到交换机的telnet连接
  2. 重新启动通过SNMP陷阱切换到连接的APC
  3. 等待来自交换机的输入以确定它已准备好访问扩展引导ROM
  4. 访问扩展bootrom。
  5. 将目录更改为小型闪存卡
  6. 在交换机上运行list命令并将其转储到数组
  7. 对阵列中的每个项目运行foreach以识别固件,文件和文件夹 A)忽略固件 B)将每个文件添加到主删除数组 C)对文件夹中的文件运行类似的foreach并将它们添加到主删除数组然后添加到文件夹中(用于删除目的)
  8. 运行foreach以删除主删除阵列中的所有项目以清除开关
  9. 重新启动并设置开关的基本信息。
  10. 更新

    好的,所以我无法让$telnet->buffer_empty; 100%的时间工作,所以我决定做的是调用第二个list命令并将其转储到垃圾文件中。下面是我的脚本,到目前为止它正在测试我测试的10个开关。我的夜班将打破开关并测试这个重置脚本。

    对于任何有兴趣的人来说,这个脚本正在HP Procurve交换机上使用,从我的研究中它可以用于大多数,但不是全部。

    #!/usr/bin/perl
    $| = 1;
    
    
    use Net::Telnet;
    use Net::SNMP;
    use strict;
    use CGI;
    
    #variabls for power, connections and systemname
    my $power_host     = ('Power-APC');
    my $power_port     = ('Power-APC-Port');
    my $system_name    = ('system-name');
    my $console_server = ('Console-Server');
    my $console_mgmtA  = ('Console-ManagementA');
    my $match;
    my ($garbage, $trash);
    my $master_value;
    my $telnet;
    my @array;
    my @master_array;
    
    
    checkForNull("Console-Server",$console_server,1);
    checkForNull("Console-ManagementA",$console_mgmtA,1);
    checkForNull("Power_Host",$power_host,1);
    checkForNull("Power_Port",$power_port,1);
    checkForNull("System_Name", $system_name,1);
    
    
    #Starting reset script
    print("Please wait while $system_name resets.\n");
    print("Please do not interact with any consoles open to this device\n");
    print("The reset will take approximitly 5 minutes\n");
    
    #establishing Telnet connection
    $telnet = new Net::Telnet (Timeout=>120, Errmode=>'return', Port => $console_mgmtA);
    
    $telnet->open($console_server);
    sleep(1);
    
    #executing reboot via SNMP
    print("Deleting configuration files.\n");
    
       my ($snmp, $error) = Net::SNMP->session(
          -hostname  => shift || "$power_host",
          -community => shift || 'private',
          -port      => shift || 161
       );
       if (!defined($snmp)) {
          printf("ERROR: %s.\n", $error);
          exit 1;
       }
       my $sysx = ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.$power_port";
       my $result = $snmp->set_request(
          -varbindlist => [$sysx, INTEGER, '3']
       );
       $snmp->close;
    
    
    
    
    $telnet->waitfor(match=> '/Profiles/');
    $telnet->cmd(string => '0', prompt => '/=>/');
    #shift to compact flash card
    $telnet->cmd (string=> 'cd cfa0', prompt => '/=>/');
    my $garbage_output = $telnet -> cmd (string => 'ls', prompt => '/=>/');
    $telnet->buffer_empty;
    
    
    #propigates primary array with file list
    undef @array;
    @array = $telnet->cmd(string => 'ls', prompt => '/=>/');
    
    
    #determines what should be deleted
    foreach my $item (@array) {
        #cleans up the files for use in the if statement
       chomp ($item);
       #determines if it's a folder, passes folder name to sub
       if ($item=~ /\//) {
           subfolders ($item);
           #adds folder AFTER files in dir to ensure deletion
           push(@master_array, "rmdir $item");
       }
       #finds and ignores the primary and secondary firmware
    
       elsif ($item =~ /btm.swi|secondary.swi/) {
       }
       else {
        #adds all files to the master deletion list
        push(@master_array,"rm $item");
       }
    
    
    
    #deletes all files listed in the master array
    foreach $master_value (@master_array){
    $telnet -> print ("$master_value");
    }
    
    
    #reboot switch
    $telnet->print('boot');
    $telnet->put(chr(13));
    sleep(120);
    
    
    #looking for ready screen
    $telnet->waitfor(match=> '/continue/');
    sleep (1);
    $telnet->put(chr(13));
    sleep (1);
    $telnet->put(chr(13));
    $telnet->waitfor(match=> '/continue/');
    $telnet->put(chr(13));
    $telnet->print('config');
    $telnet->put(chr(13));
    $telnet->print("hostname $system_name\n");
    $telnet->print('lldp run');
    $telnet->put(chr(13));
    $telnet->print('wr mem');
    $telnet->put(chr(13));
    $telnet->print('boot');
    $telnet->put(chr(13));
    $telnet->print('y');
    
    
    print("$system_name has been reset to factory settings.\n");
    }
    
    
    #add all files in folders to master deletion list
    sub subfolders {
     my @subfolder_contents;
     my $subfolder = $_[0];
     my $file;
    
     @subfolder_contents = $telnet ->cmd(string => "ls $subfolder", prompt => '/=>/');
     foreach $file (@subfolder_contents){
      chomp ($file);
        push(@master_array, "rm $subfolder$file");
     }
    };
    
    
    sub checkForNull {
        my $name = $_[0];
        my $param = $_[1];
        my $required = $_[2];
    
        if($param eq "") {
            if ($required == 1) {
                print("\nSorry, no '$name' specified for this device, cannot continue.\n");
                exit 1;
            }
        }
    # else {
     #       print("\n$name: $param\n");
      #  }
    }
    
    exit;
    

1 个答案:

答案 0 :(得分:2)

有一个$telnet->buffer_empty可以满足您的要求,但我不认为这是您的问题(尽管如此)。

你确定你的提示不太匹配吗?换句话说,是否有一些命令输出恰好也包含'=>'?调试的最佳方法是通过$telnet->dump_log('/tmp/some_file'),它为您提供进出连接的所有内容的十六进制转储(更重要的是,读取和写入Net :: Telnet的确切顺序,以便您可以判断和太早匹配的地方)。

如果您过早地使用'=>'进行匹配提示,只是尝试使正则表达式更严格。添加'\ z'来检查字符串的结尾将是一个好的开始,但是如果'=>'后面有空格,请小心(所以类似'/ =>?\ z /')。 (但是,当然,'\ z'并不是真正的“结束”,而是Net :: Telnet到目前为止收集的内容。所以如果输出中间的一个不幸的数据包恰好以'=>'结尾那么它也将打破更严格的正则表达式)

此外,由于打印在返回之前不等待提示,因此您的长序列“打印”看起来很可疑。最好始终设置Net :: Telnet对象的“提示”字段,然后使用 - > cmd,它将在返回之前等待提示。