Пинг-список компьютеров FAST с Python - PullRequest
0 голосов
/ 23 сентября 2019

Я проверял список компьютеров много раз, однако я пытаюсь использовать Python впервые со списком из примерно 4000 имен компьютеров, и мой сценарий очень медленный.Как мне сделать это намного быстрее и записать вывод в текстовый файл с разделителями-запятыми?

import pandas as pd
import os
import sys
import subprocess
import datetime

#Get current date and time
now = datetime.datetime.now()
dt = now.strftime("%Y-%m-%d")
dtnow = now.strftime("%Y-%m-%d %H:%M")

#Open the file and read into memory
fh = pd.read_csv('list.csv')

#Fix column headers by replacing the spaces with a underscore
fh.columns = fh.columns.str.strip().str.replace(' ', '_')

#Read the computer names into a variable called "computers"
computers = fh.Machine_Name
#Debug - Uncomment line below to see a list of computer names from csv file
#print(computers)

def ping(comp):
    args = ["ping", "-n", "2", comp]
    p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output, error = p.communicate()
    if 'bytes=32' in output:
        writetofile(comp, ',online')
    else:
        writetofile(comp, ',offline')
    #endIf
#endDef

def writetofile(compname, data):
    with open('DLPProv_' + dt + '.txt', 'a') as f:
        f.write(compname + data + '\n')
    #endWith
#endDef

for i in computers:
    ping(i)
#endFor

f.write('END: ' + dtnow)
f.close()

Я пытался использовать код, который разместил @rolandsmith, но получаю ошибки:


import concurrent.futures as cf
import os
import pandas as pd
from pythonping import ping

#Open the file and read into memory
fh = pd.read_csv('list.csv')

#Fix column headers by replacing the spaces with a underscore
fh.columns = fh.columns.str.strip().str.replace(' ', '_')

#Read the computer names into a variable called "computers"
computers = fh.Machine_Name

def pingworker(address):
    rv = ping(address, count=4)
    if rv.success():
        return address, True
    return address,False

with cf.ThreadPoolExecutor() as tp:
    res = tp.map(pingworker, computers)

1 Ответ

1 голос
/ 23 сентября 2019

Когда вы думаете, что скрипт медленный, вы должны измерить , что заставляет его работать медленно.Например, line-profiler .

Мой думаю, будет означать, что в этом случае это подпроцесс, который занимает большую часть времени.В идеале вы хотите снять накладные расходы на запуск процесса для каждого пинга.Поэтому вместо вызова программы ping установите модуль pythonping .Это позволяет вам выполнить эхо-запрос ICMP от Python.Обратите внимание, что для этого используются необработанные сокеты, поэтому в зависимости от операционной системы вам может потребоваться запустить сценарий от имени пользователя root или настроить его на использование необработанных сокетов.Использование этого модуля устраняет издержки использования подпроцесса.

Далее, когда ваш скрипт делает это, он в основном ожидает ответа от сети.Поэтому мы используем concurrent.futures.ThreadPoolExecutor для параллельного запуска более одного пинга

import concurrent.futures as cf
import os
from pythonping import ping

def pingworker(address):
    rv = ping(address)
    if rv.success():
        return address, True
    return address,False

with cf.ThreadPoolExecutor() as tp:
    res = tp.map(pingworker, list_of_addresses)

После этого res представляет собой список из двух кортежей, каждый из которых содержит адрес и логическое значение, если он завершился неудачно или успешно.

Обратите внимание, что начиная с Python 3.5 и далее ThreadPoolExecutor запускает 5 * N потоков, где N - количество ядер на вашем компьютере.Таким образом, для четырехъядерной машины одновременно будет выполняться 20 вызовов ping.Вы можете поэкспериментировать с параметром max_workers при создании ThreadPoolExecutor, но в определенный момент вы будете насыщать свое сетевое соединение с помощью пинг-вызовов.

Редактировать

Функция pythonping.ping требует IP-адрес , а не имя.Таким образом, вы должны сделать поиск имени в первую очередь.К счастью, это встроено в модуль socket.Вы можете использовать, например, socket.gethostbyname_ex для поиска IPv4-адреса.Или socket.getaddrinfo для получения адресов IPv4 и IPv6.

Если у вас есть список имен, предполагая, что вы используете IPv4, вы можете изменить работника следующим образом:

import concurrent.futures as cf
import socket
import os
from pythonping import ping

def pingworker(name):
    """
    Ping a hostname.

    Arguments:
        name (str): hostname:

    Returns:
        a 3-tuple (hostname, IP-address, ping-result)
        where hostname and IP-address are strings and
        ping-result is a bool.
    """
    try:
        _, _, IPs = socket.gethostbyname_ex(name)
        address = IPs[0]
    except socket.gaierror:
        return name, None, False  # Name lookup failed.
    rv = ping(address)
    if rv.success():
        return name, address, True
    return name, address, False   # Host doesn't respond.

with cf.ThreadPoolExecutor() as tp:
    res = tp.map(pingworker, list_of_names)

Я также изменил рабочую функцию, чтобы она также возвращала IP-адрес.Таким образом, вы можете отличить хост, который не возвращает эхо-запросы, от хоста, имя которого не может быть разрешено.

...