Стандартный поток ввода зависает, когда paramiko выдает исключение «Нет существующего сеанса»

avatar
David Pace
1 июля 2021 в 17:59
119
1
0

Я использую java для запуска скрипта Python, используя следующий метод:

private String[] runScript(String args) {
        ArrayList<String> lines = new ArrayList<String>();
        try {
            String cmd = "python py/grabber/backdoor.py " + args;
            System.out.println(cmd);
            Runtime run = Runtime.getRuntime();
            Process pr = run.exec(cmd);
            InputStream stdout = pr.getInputStream();
            BufferedReader stdInput = new BufferedReader(new InputStreamReader(stdout, StandardCharsets.UTF_8));
            String line;
            System.out.println("Starting to read output:");
            while ((line = stdInput.readLine()) != null) {
                if(!pr.isAlive()) {
                    break;
                }
                System.out.println("" + line);
                lines.add(line);
            }
            stdInput.close();
        } catch (Exception e) {
            System.out.println("Exception in reading output" + e.toString());
        }
        return lines.toArray(new String[lines.size()]);
    }

Скрипт python начинает выполнение приведенного ниже метода для списка устройств в новом дочернем потоке для каждого устройства:

def run_command(device, retry):
    try:
        if(retry != 0):
            save_log('Retrying '+device['ip']+'...')
        SSHClient = netmiko.ssh_dispatcher("cisco_ios")
        ssh_connection = SSHClient(**device)

        ruckus = ("ruckus" in str(device['device_type']))

        output = ssh_connection.send_command('show run | inc hostname')
        if(not ruckus):
            output = output.split('\n')[0]
            hostname = output.split(' ')[1] + '-' + today
        else:
            hostname = output.split(' ')[1] + '-' + today

        msg = (''+device['ip']+':Retry Success! Hostname:'+hostname+'\n',''+device['ip']+':hostname: ' + hostname + '\n')[retry == 0]
        save_log(msg)
        return True
    except Exception as e:
        if("Authentication" in str(e)):
            if(config.exe_i):
                save_log('\nAuthentication failed! Consider resetting credentials\n')
                if(not config.using_gui):
                    val = input("Continue with list? [y]/[n]: ")
                    if("n" in val):
                        return False
                else:
                    return True
            else:
                msg = '\nSwitch: ' + device['ip'] + '\nLogin Failure'
                save_error(msg)
                return True
        elif("SSH protocol banner" in str(e) and retry <= 3):
            retry += 1
            save_log('\nSwitch: ' + device['ip'] + ' - SSH Banner.. Attempt: '+str(retry))
            return run_command(device, retry=retry)
        elif("conn_timeout" in str(e) and retry <= 3):
            retry += 1
            save_log('\nSwitch: ' + device['ip'] + ' - Connection Timeout.. Attempt: '+str(retry))
            return run_command(device, retry=retry)
        error = 'error @ switch: ' + device['ip'] + ' with error: \n' + str(e)
        save_error(error)
        return True

Когда скрипт Python получает это конкретное исключение,

Paramiko: 'No existing session' error: try increasing 'conn_timeout' to 10 seconds or larger.

сценарий просто повторит попытку подключения и продолжит работу в обычном режиме, как и предполагалось, но программа Java начинает зависать в цикле while, несмотря на то, что сценарий python успешно завершает выполнение. Почему при возникновении этого исключения стандартный поток ввода в цикле while начинает зависать?

Дополнительная информация:

1.) При удалении потоков из сценария цикл while будет по-прежнему зависать, если выдается исключение «нет существующего сеанса».

2.) Другое распространенное исключение, вызывающее повторную попытку ssh, "баннер протокола SSH", не приводит к зависанию цикла while. Поэтому я делаю вывод, что проблема не в том, как само исключение обрабатывается в моем программном стеке.

Источник

Ответы (1)

avatar
David Pace
3 июля 2021 в 00:13
0

Проблема связана с сегментом Java, и решение состоит в том, чтобы читать не только поток ввода, но и поток вывода, поскольку процесс зависнет, если оба не будут очищены. Чтобы сделать это правильно, каждый поток должен быть прочитан в отдельном потоке. ProcessBuilder — лучшая альтернатива, поскольку он позволяет объединить поток ввода и вывода в один, и тогда необходим только основной поток.

Это сообщение стека является ссылкой на более канонический вопрос для этой концепции: Процесс Java с потоком ввода/вывода