Turn to Python from Perl: example of SSH in multi-threads

本文介绍了一种使用Python的Paramiko库来并发地在多个远程服务器上执行SSH命令的方法。作者尝试了Shell脚本、Perl及Python的不同库如PySSH等方案,最终成功实现了需求。

I need to execute commands on several remote servers at the same time via ssh. Writing shell script to send commands to servers with code below is quite simple but has trouble to know when all the tasks are finished on all servers.

            ssh $ip “nohup sh go.sh > log 2>&1 &”

Then I turn to perl, write following perl code:

#!/usr/bin/perl


use threads;
use Net::SSH::Perl;
use strict;

my @thread_array;
system( "touch $result_file" );
open( ResultHandle, "> $result_file" ) || die "Open result file error...\n";

sub Test
{
        my $host = shift;
        chomp( $host );
        $current_host = $host;
        print "\nDebug:Try to connect $host for sequence write test...\n"; 


        eval
        {
                alarm $time_out;
                my $ssh = Net::SSH::Perl->new( $host );
                $ssh->login($user, $pass);
                my($out,$err,$exit ) = $ssh->cmd($myCmd);


                alarm 0;
               print ResultHandle " Test\n $host \n $out\n \n $err";
                print "$host\n$out\n$err\n$exit\n";
        };
}


my @clients = ("10.0.0.4","10.0.0.6"); 


for(my $i = 0; $i <= $#clients; $i++){
        print "thread $i\n";
        $thread_array[$i] = threads->new(\&Test, $clients[$i]);
}


foreach my $thread( @thread_array )
{
        $thread -> join( );
}

 

However, when I run this script, it says “Segment fault” when running more than threads, I do not know why and it stops me moving ahead. Quite disappointed!!!
Then I turn to python…first try the library pyssh:
With following code:

import os
import re
import time
import sys
import pyssh
from threading import Thread 


class SSHController(Thread): 


    """Connect to remote host with SSH and issue commands.
    This is a facade/wrapper that uses PySSH to spawn and control an SSH client.
    You must have OpenSSH installed. 


    @ivar host_name: Host name or IP address
    @ivar user_name: User name
    @ivar password: Password
    @ivar prompt: Command prompt (or partial string matching the end of the prompt)
    @ivar ssh: Instance of a pyssh.Ssh object
    """
    def __init__(self, host_name, user_name, password, cmd):
        """
        @param host_name: Host name or IP address
        @param user_name: User name
        @param password: Password
        @param prompt: Command prompt (or partial string matching the end of the prompt)
        """
        Thread.__init__(self)
        self.host_name = host_name
        self.user_name = user_name
        self.password = password
        self.port = '22'  #default SSH port
        self.ssh = None
        self.cmd = cmd 


    def login(self):

        """Connect to a remote host and login.
        """
        self.ssh = pyssh.Ssh(self.user_name, self.host_name, self.port)
        self.ssh.login(self.password)


    def run_command(self, command):
        """Run a command on the remote host.
        @param command: Unix command
        @return: Command output
        @rtype: String
        """
        response = self.ssh.sendcmd(command)
        return self.__strip_output(command, response) 


    def logout(self):
        """Close the connection to the remote host.
        """
        self.ssh.logout()


    def run_atomic_command(self, command):
        """Connect to a remote host, login, run a command, and close the connection.
        @param command: Unix command
        @return: Command output
        @rtype: String
        """
        self.login()
        command_output = self.run_command(command)
        self.logout()
        return command_output


    def __strip_output(self, command, response):
        """Strip everything from the response except the actual command output.
        @param command: Unix command
        @param response: Command output
        @return: Stripped output
        @rtype: String
        """
        lines = response.splitlines()
        # if our command was echoed back, remove it from the output
        if command in lines[0]:
            lines.pop(0)
        # remove the last element, which is the prompt being displayed again
        lines.pop()
        # append a newline to each line of output
        lines = [item + '\n' for item in lines]
        # join the list back into a string and return it
        return ''.join(lines)


    def run(self):        
        self.run_atomic_command(self.cmd)


print time.ctime()
pinglist = []
for host in range(1,2):
   ip = "10.0.0."+str(host)
   print ip
   current = SSHController(ip,"tao","123456","ls")
   pinglist.append(current)
   current.start()


for pingle in pinglist:
   pingle.join() 


print time.ctime()

 

But again, this script failed.  It says:  ValueError: signal only works in main threadI do not know if it’s pyssh library’s problem.
Then I turn to a library named paramiko, and finally I succeed!
First you should install this library; possibly you may need another library pycrypto-2.0.1.
With all things ready, and the following script:

#!/usr/bin/env python
import os, sys, socket
import paramiko
from threading import Thread 


class myThread(Thread): 


    def __init__ (self, ip, usr, pwd, command):
        Thread.__init__(self)
        self.ip = ip
        self.usr= usr
        self.pwd= pwd
        self.command = command


    def run (self):
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        client.connect(self.ip, username=self.usr, password=self.pwd)
        stdin, stdout, stderr = client.exec_command(self.command)
        if self.command.startswith("sudo")
                  stdin.write(“123456\n”)
                  stdin.flush()
        for line in stdout.read().splitlines():
                print 'host: %s: %s' % (self.ip, line)
        for line in stderr.read().splitlines():
                print 'host: %s: %s' % (self.ip, line)


    def test (self):
        pass # some code to test this class

 mythreads = []
hosts = []
cmd = ""
if __name__ == "__main__":
    num = int(sys.argv[1])
    print "total %d nodes\n" % num
    for i in range(2,2+num):
        hosts.append("10.0.0." + str(sys.argv[i]))
    for i in range(2+num,len(sys.argv)):
        cmd += " " + sys.argv[i]
    print "cmd to execute: %s\n" % cmd


    for ip in hosts:
        t = myThread(ip, "tao","123456",cmd)
        mythreads.append(t);
        t.start()


    for thread in mythreads:
        thread.join()
        print "execution on %s completes!\n" % thread.ip
print "test done!"

 

 

This script accepts arguments like this: 2 1 4 du –h
    First argument 2 means I want to execute this command on two hosts, and followed with the last part of ip. ( ip of my machines all have the pattern of 10.0.0.*).
    Then the command follows the hosts. If you need to execute a sudo command, remember to modify /etc/sudoers to allow you send ssh commands without a real tty.
    You can add a line like this to walk around this problem.

Defaults:tao !requiretty

Error downloading packages: perl-Storable-2.45-3.el7.x86_64: [Errno 256] No more mirrors to try. 4:perl-macros-5.16.3-297.el7.x86_64: [Errno 256] No more mirrors to try. perl-Scalar-List-Utils-1.27-248.el7.x86_64: [Errno 256] No more mirrors to try. 4:perl-libs-5.16.3-297.el7.x86_64: [Errno 256] No more mirrors to try. 2:vim-filesystem-7.4.629-7.el7.x86_64: [Errno 256] No more mirrors to try. 1:perl-Pod-Escapes-1.04-297.el7.noarch: [Errno 256] No more mirrors to try. 2:vim-enhanced-7.4.629-7.el7.x86_64: [Errno 256] No more mirrors to try. perl-Text-ParseWords-3.29-4.el7.noarch: [Errno 256] No more mirrors to try. perl-threads-1.87-4.el7.x86_64: [Errno 256] No more mirrors to try. perl-Encode-2.51-7.el7.x86_64: [Errno 256] No more mirrors to try. perl-threads-shared-1.43-6.el7.x86_64: [Errno 256] No more mirrors to try. 1:perl-Pod-Simple-3.28-4.el7.noarch: [Errno 256] No more mirrors to try. perl-Time-Local-1.2300-2.el7.noarch: [Errno 256] No more mirrors to try. perl-Socket-2.010-5.el7.x86_64: [Errno 256] No more mirrors to try. perl-podlators-2.5.1-3.el7.noarch: [Errno 256] No more mirrors to try. perl-File-Temp-0.23.01-3.el7.noarch: [Errno 256] No more mirrors to try. perl-PathTools-3.40-5.el7.x86_64: [Errno 256] No more mirrors to try. 4:perl-Time-HiRes-1.9725-3.el7.x86_64: [Errno 256] No more mirrors to try. perl-HTTP-Tiny-0.033-3.el7.noarch: [Errno 256] No more mirrors to try. perl-constant-1.27-2.el7.noarch: [Errno 256] No more mirrors to try. 4:perl-5.16.3-297.el7.x86_64: [Errno 256] No more mirrors to try. perl-Exporter-5.68-3.el7.noarch: [Errno 256] No more mirrors to try. perl-Getopt-Long-2.40-3.el7.noarch: [Errno 256] No more mirrors to try. 2:vim-common-7.4.629-7.el7.x86_64: [Errno 256] No more mirrors to try. 1:perl-parent-0.225-244.el7.noarch: [Errno 256] No more mirrors to try. perl-Carp-1.26-244.el7.noarch: [Errno 256] No more mirrors to try. perl-File-Path-2.09-2.el7.noarch: [Errno 256] No more mirrors to try. perl-Filter-1.49-3.el7.x86_64: [Errno 256] No more mirrors to try. perl-Pod-Perldoc-3.20-4.el7.noarch: [Errno 256] No more mirrors to try. perl-Pod-Usage-1.63-3.el7.noarch: [Errno 256] No more mirrors to try.
06-17
<think>我们正在解决yum或dnf在下载perl和vim相关包时出现的"[Errno256]Nomoremirrorstotry"错误。这个错误通常表示yum无法从配置的仓库中下载元数据或包,可能是由于网络问题、仓库配置错误、仓库不可用或缓存问题等。根据引用[1]和引用[2]提供的思路,我们可以尝试以下步骤:1.检查网络连接:确保服务器可以访问互联网,特别是能够访问配置的yum仓库地址。2.检查仓库配置:确保仓库配置文件(/etc/yum.repos.d/目录下的.repo文件)中的baseurl或mirrorlist指向可用的镜像。3.清理yum缓存:旧的缓存可能导致问题,清理缓存后重试。4.替换为国内镜像源:比如阿里云镜像,以解决国外镜像访问慢或不可用的问题。5.检查软件包名称:确保要安装的软件包名称正确。由于用户特别提到要下载perl和vim相关包,我们可以针对这两个软件包进行解决。步骤详细说明:步骤1:检查网络连接使用命令`ping`或`curl`测试网络连通性,例如:pingmirrors.aliyun.com或者curl-Ihttps://mirrors.aliyun.com步骤2:检查仓库配置查看/etc/yum.repos.d/目录下的repo文件,确认是否有可用的仓库配置。步骤3:清理yum缓存yumcleanallyummakecache如果使用dnf,则使用:dnfcleanalldnfmakecache步骤4:替换为国内镜像源(以阿里云为例,适用于CentOS/RHEL系统)参考引用[1]和[2]的做法:a.备份原有的仓库配置文件:mkdir/etc/yum.repos.d/backupmv/etc/yum.repos.d/*.repo/etc/yum.repos.d/backup/b.下载阿里云的仓库配置文件(这里以CentOS7为例,其他版本请替换版本号):注意:如果系统是RedHat,需要先卸载自带的yum(如引用[1]所述),然后安装阿里云的yum包,但通常我们直接替换repo文件即可。下载CentOS-Base.repo到/etc/yum.repos.d/目录:curl-o/etc/yum.repos.d/CentOS-Base.repohttps://mirrors.aliyun.com/repo/Centos-7.repo或者(对于RedHat系统,可能需要将`$releasever`替换为具体的版本号,比如7):sed-i's/\$releasever/7/g'/etc/yum.repos.d/CentOS-Base.repoc.运行清理缓存并重新生成缓存:yumcleanallyummakecache步骤5:安装软件包yuminstall-yperlvim如果问题仍然存在,可以尝试禁用其他仓库,只使用阿里云仓库:yum--disablerepo=\*--enablerepo=baseinstall-yperlvim另外,如果系统是RedHat,并且没有订阅,可能需要将系统转换为CentOS(不推荐)或者使用其他方法(如使用第三方仓库epel),但这里我们主要解决镜像源问题。如果上述步骤不能解决问题,可能需要查看具体的错误信息,调整镜像源地址。注意:引用[1]中提到卸载RedHat自带的yum源并安装阿里云的yum包,但这一步通常只在RedHat系统中需要,因为RedHat的yum需要订阅。如果用户系统是RedHat,可以参考以下步骤:1.卸载RedHat自带的yum:rpm-qa|grepyum|xargsrpm-e--nodepsrpm-qa|grepyum2.下载阿里云提供的yum包(以CentOS7为例):wgethttp://mirrors.aliyun.com/centos/7/os/x86_64/Packages/yum-3.4.3-168.el7.centos.noarch.rpmwgethttp://mirrors.aliyun.com/centos/7/os/x86_64/Packages/yum-metadata-parser-1.1.4-10.el7.x86_64.rpmwgethttp://mirrors.aliyun.com/centos/7/os/x86_64/Packages/yum-plugin-fastestmirror-1.1.31-54.el7_8.noarch.rpm如果wget不可用,可以使用curl-O下载。3.安装这些包:rpm-ivhyum-*rpm-ivhyum-metadata-parser-*.rpmrpm-ivhyum-plugin-fastestmirror-*.rpm4.然后按照步骤4配置阿里云镜像。但是,如果用户使用的是dnf(Fedora或CentOS8+),则步骤类似,但仓库配置文件会有所不同。根据用户的问题,我们主要针对perl和vim包,所以可以尝试直接使用阿里云镜像。下面给出一个针对RHEL/CentOS7系统的完整解决方案(假设用户系统为RHEL7,且没有有效的订阅):</think>### 解决yum/dnf下载perl和vim时出现"[Errno 256] No more mirrors to try"错误 此错误通常由以下原因导致: 1. **仓库配置失效**:默认仓库地址不可用 2. **缓存损坏**:本地yum元数据缓存异常 3. **网络限制**:无法访问官方仓库 4. **软件包冲突**:系统自带yum组件不兼容 --- ### 解决方案(以CentOS/RHEL为例): #### 📌 步骤1:清理yum缓存 ```bash sudo yum clean all # 清理所有缓存 sudo rm -rf /var/cache/yum/* # 强制删除残留缓存 sudo yum makecache # 重建元数据缓存 ``` #### 📌 步骤2:替换阿里云镜像源 ```bash # 备份原有仓库配置 sudo mkdir /etc/yum.repos.d/backup sudo mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/backup/ # 下载阿里云Base源 sudo curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo # 下载EPEL源(额外软件包) sudo curl -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo # 替换变量(针对RHEL系统) sudo sed -i 's/\$releasever/7/g' /etc/yum.repos.d/CentOS-Base.repo ``` #### 📌 步骤3:手动安装关键依赖(如系统yum损坏) ```bash # 下载必要组件(示例版本号,需匹配系统) curl -O https://mirrors.aliyun.com/centos/7/os/x86_64/Packages/yum-3.4.3-168.el7.centos.noarch.rpm curl -O https://mirrors.aliyun.com/centos/7/os/x86_64/Packages/yum-utils-1.1.31-54.el7_8.noarch.rpm # 强制安装 sudo rpm -ivh --nodeps yum-*.rpm ``` #### 📌 步骤4:安装目标软件包 ```bash sudo yum install -y vim-enhanced perl perl-devel ``` --- ### 验证安装 ```bash vim --version | head -n1 # 应显示VIM版本 perl -v | head -n2 # 应显示Perl版本 ``` --- ### 关键配置说明 1. **`$releasever`替换**: - RHEL/CentOS 7 → 替换为`7` - RHEL/CentOS 8 → 替换为`8` 2. **优先使用基础源**: ```bash sudo yum --disablerepo=* --enablerepo=base install vim ``` > 💡 **提示**:若使用dnf(Fedora/CentOS 8+),将上述命令中的`yum`替换为`dnf`即可,仓库文件路径相同[^1][^2]。 --- ### 常见问题排查 | 现象 | 解决方法 | |------|----------| | 证书错误 | `sudo sed -i 's/https/http/g' /etc/yum.repos.d/*.repo` | | GPG校验失败 | `sudo yum install --nogpgcheck vim` | | 特定镜像不可达 | 替换`mirrors.aliyun.com`为`mirrors.tuna.tsinghua.edu.cn` | > ⚠️ 操作前建议备份系统:`sudo tar -zcvf /backup/yum_repo_backup.tar.gz /etc/yum.repos.d/`
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值