geekdoc-python-zh/docs/pythonlibrary/python-running-ping-tracero...

18 KiB
Raw Permalink Blame History

Python:运行 Ping、Traceroute 等等

原文:https://www.blog.pythonlibrary.org/2010/06/05/python-running-ping-traceroute-and-more/

去年,我需要想出一种方法来用 Python 获取以下信息:获取路由表,从 ping 一系列 IP 中捕获数据,运行 tracert 并获取有关安装的 NIC 的信息。这些都需要在 Windows 机器上完成,因为它是诊断脚本的一部分,试图找出为什么机器(通常是笔记本电脑)无法连接到我们的 VPN。我最终创建了一个 wxPython GUI让用户可以轻松运行但是这些脚本没有 wx 也可以很好地工作。让我们看看他们长什么样!

主脚本

首先,我们将看一下整个剧本,然后检查每一个重要的部分。如果你想使用下面的代码,你需要 wxPythonPyWin32 包。


import os
import subprocess
import sys
import time
import win32com.client
import win32net
import wx

filename = r"C:\logs\nic-diag.log"

class RedirectText:
    def __init__(self,aWxTextCtrl):
        self.out=aWxTextCtrl

        if not os.path.exists(r"C:\logs"):
            os.mkdir(r"C:\logs")
        self.filename = open(filename, "w")

    def write(self,string):
        self.out.WriteText(string)
        if self.filename.closed:
            pass
        else:
            self.filename.write(string)

class MyForm(wx.Frame):

    #---------------------------------------------------------------------- 
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Diagnostic Tool")

        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)
        log = wx.TextCtrl(panel, wx.ID_ANY, size=(300,100),
                          style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
        # log.Disable()
        btn = wx.Button(panel, wx.ID_ANY, 'Run Diagnostics')
        self.Bind(wx.EVT_BUTTON, self.onRun, btn)

        # Add widgets to a sizer        
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(log, 1, wx.ALL|wx.EXPAND, 5)
        sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
        panel.SetSizer(sizer)

        # redirect text here
        self.redir=RedirectText(log)
        sys.stdout=self.redir

    #----------------------------------------------------------------------    
    def runDiagnostics(self):
        """
        Run some diagnostics to get the machine name, ip address, mac,
        gateway, DNS, route tables, etc
        """
        # create the route table:
        # based on the following list comp from http://win32com.goermezer.de/content/view/220/284/
        # route_table = [elem.strip().split() for elem in os.popen("route print").read().split("Metric\n")[1].split("\n") if re.match("^[0-9]", elem.strip())]
        route_table = []
        proc = subprocess.Popen("route print", shell=True,
                        stdout=subprocess.PIPE)
        while True:
            line = proc.stdout.readline()
            route_table.append(line.strip().split())
            if not line: break
        proc.wait()

        print "Log Created at %s" % time.ctime()
        print "----------------------------------------------------------------------------------------------"
        info = win32net.NetWkstaGetInfo(None, 102)
        self.compname = info["computername"]
        print "Computer name: %s\n" % self.compname

        print "----------------------------------------------------------------------------------------------"
        print "Route Table:"
        print "%20s\t %15s\t %15s\t %15s\t %s" % ("Network Destination", "Netmask",
                                          "Gateway", "Interface", "Metric")
        for route in route_table:
            if len(route) == 5:
                dst, mask, gateway, interface, metric = route
                print "%20s\t %15s\t %15s\t %15s\t %s" % (dst, mask, gateway, interface, metric)

        print "----------------------------------------------------------------------------------------------\n"
        ips = ["65.55.17.26", "67.205.46.185", "67.195.160.76"]
        for ip in ips:
            self.pingIP(ip)
            print
            self.tracertIP(ip)
            print "\n----------------------------------------------------------"
        self.getNICInfo()
        print "############ END OF LOG ############"

    #----------------------------------------------------------------------   
    def pingIP(self, ip):
        proc = subprocess.Popen("ping %s" % ip, shell=True, 
                                stdout=subprocess.PIPE) 
        print
        while True:
            line = proc.stdout.readline()                        
            wx.Yield()
            if line.strip() == "":
                pass
            else:
                print line.strip()
            if not line: break
        proc.wait()

    #----------------------------------------------------------------------
    def tracertIP(self, ip):
        proc = subprocess.Popen("tracert -d %s" % ip, shell=True, 
                                stdout=subprocess.PIPE)
        print 
        while True:
            line = proc.stdout.readline()
            wx.Yield()
            if line.strip() == "":
                pass
            else:
                print line.strip()
            if not line: break
        proc.wait()

    #----------------------------------------------------------------------            
    def getNICInfo(self):
        """
        http://www.microsoft.com/technet/scriptcenter/scripts/python/pyindex.mspx?mfr=true
        """
        print "\nInterface information:\n"
        strComputer = "."
        objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator")
        objSWbemServices = objWMIService.ConnectServer(strComputer,"root\cimv2")
        colItems = objSWbemServices.ExecQuery("Select * from Win32_NetworkAdapterConfiguration")
        numOfNics = len(colItems)
        count = 1
        for objItem in colItems:
            # if the IP interface is enabled, grab its info
            print "***Interface %s of %s***" % (count, numOfNics)
            if objItem.IPEnabled == True:                
                print "Arp Always Source Route: ", objItem.ArpAlwaysSourceRoute
                print "Arp Use EtherSNAP: ", objItem.ArpUseEtherSNAP
                print "Caption: ", objItem.Caption
                print "Database Path: ", objItem.DatabasePath
                print "Dead GW Detect Enabled: ", objItem.DeadGWDetectEnabled
                z = objItem.DefaultIPGateway
                if z is None:
                    a = 1
                else:
                    for x in z:
                        print "Default IP Gateway: ", x
                print "Default TOS: ", objItem.DefaultTOS
                print "Default TTL: ", objItem.DefaultTTL
                print "Description: ", objItem.Description
                print "DHCP Enabled: ", objItem.DHCPEnabled
                print "DHCP Lease Expires: ", objItem.DHCPLeaseExpires
                print "DHCP Lease Obtained: ", objItem.DHCPLeaseObtained
                print "DHCP Server: ", objItem.DHCPServer
                print "DNS Domain: ", objItem.DNSDomain
                z = objItem.DNSDomainSuffixSearchOrder
                if z is None:
                    a = 1
                else:
                    for x in z:
                        print "DNS Domain Suffix Search Order: ", x
                print "DNS Enabled For WINS Resolution: ", objItem.DNSEnabledForWINSResolution
                print "DNS Host Name: ", objItem.DNSHostName
                z = objItem.DNSServerSearchOrder
                if z is None:
                    a = 1
                else:
                    for x in z:
                        print "DNS Server Search Order: ", x
                print "Domain DNS Registration Enabled: ", objItem.DomainDNSRegistrationEnabled
                print "Forward Buffer Memory: ", objItem.ForwardBufferMemory
                print "Full DNS Registration Enabled: ", objItem.FullDNSRegistrationEnabled
                z = objItem.GatewayCostMetric
                if z is None:
                    a = 1
                else:
                    for x in z:
                        print "Gateway Cost Metric: ", x
                print "IGMP Level: ", objItem.IGMPLevel
                print "Index: ", objItem.Index
                z = objItem.IPAddress
                if z is None:
                    a = 1
                else:
                    for x in z:
                        print "IP Address: ", x
                print "IP Connection Metric: ", objItem.IPConnectionMetric
                print "IP Enabled: ", objItem.IPEnabled
                print "IP Filter Security Enabled: ", objItem.IPFilterSecurityEnabled
                print "IP Port Security Enabled: ", objItem.IPPortSecurityEnabled
                z = objItem.IPSecPermitIPProtocols
                if z is None:
                    a = 1
                else:
                    for x in z:
                        print "IP Sec Permit IP Protocols: ", x
                z = objItem.IPSecPermitTCPPorts
                if z is None:
                    a = 1
                else:
                    for x in z:
                        print "IP Sec Permit TCP Ports: ", x
                z = objItem.IPSecPermitUDPPorts
                if z is None:
                    a = 1
                else:
                    for x in z:
                        print "IPSec Permit UDP Ports: ", x
                z = objItem.IPSubnet
                if z is None:
                    a = 1
                else:
                    for x in z:
                        print "IP Subnet: ", x
                print "IP Use Zero Broadcast: ", objItem.IPUseZeroBroadcast
                print "IPX Address: ", objItem.IPXAddress
                print "IPX Enabled: ", objItem.IPXEnabled
                z = objItem.IPXFrameType
                if z is None:
                    a = 1
                else:
                    for x in z:
                        print "IPX Frame Type: ", x
                print "IPX Media Type: ", objItem.IPXMediaType
                z = objItem.IPXNetworkNumber
                if z is None:
                    a = 1
                else:
                    for x in z:
                        print "IPX Network Number: ", x
                print "IPX Virtual Net Number: ", objItem.IPXVirtualNetNumber
                print "Keep Alive Interval: ", objItem.KeepAliveInterval
                print "Keep Alive Time: ", objItem.KeepAliveTime
                print "MAC Address: ", objItem.MACAddress
                print "MTU: ", objItem.MTU
                print "Num Forward Packets: ", objItem.NumForwardPackets
                print "PMTUBH Detect Enabled: ", objItem.PMTUBHDetectEnabled
                print "PMTU Discovery Enabled: ", objItem.PMTUDiscoveryEnabled
                print "Service Name: ", objItem.ServiceName
                print "Setting ID: ", objItem.SettingID
                print "Tcpip Netbios Options: ", objItem.TcpipNetbiosOptions
                print "Tcp Max Connect Retransmissions: ", objItem.TcpMaxConnectRetransmissions
                print "Tcp Max Data Retransmissions: ", objItem.TcpMaxDataRetransmissions
                print "Tcp Num Connections: ", objItem.TcpNumConnections
                print "Tcp Use RFC1122 Urgent Pointer: ", objItem.TcpUseRFC1122UrgentPointer
                print "Tcp Window Size: ", objItem.TcpWindowSize
                print "WINS Enable LMHosts Lookup: ", objItem.WINSEnableLMHostsLookup
                print "WINS Host Lookup File: ", objItem.WINSHostLookupFile
                print "WINS Primary Server: ", objItem.WINSPrimaryServer
                print "WINS Scope ID: ", objItem.WINSScopeID
                print "WINS Secondary Server: ", objItem.WINSSecondaryServer
                print "-------------------------------------------------------\n"
            else:
                print "Interface is disabled!\n"
            count += 1

    #----------------------------------------------------------------------
    def onRun(self, event):
        self.runDiagnostics()
        self.redir.filename.close()
        # Restore stdout to normal
        sys.stdout = sys.__stdout__

#----------------------------------------------------------------------         
# Run the program
if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = MyForm().Show()
    app.MainLoop()

与大多数 Python 程序一样,这个程序从各种导入开始。接下来我们创建一个简单的类(RedirectText ),我们将使用它来帮助我们将 stdout 重定向到 wx。TextCtrl 和一个日志文件。这通过传入 wx 的实例来实现。TextCtrl然后设置“sys.stdout”以指向它(请参见 MyForm 类中的 init 方法)。在 RedirectText 类之后,我们有了 MyForm 类,在那里我们创建了 wxPython GUI。实际上 GUI 本身并没有太多的东西。只是一个多行文本控件和一个面板上的按钮,但这就是我们所需要的。这个类的其余部分由收集所有我们需要的信息并将其记录到屏幕和文件中的方法组成。

现在就来看看那些方法吧!注意,这些方法是从 runDiagnostics 方法调用的,该方法是从 onRun 按钮事件处理程序启动的。

获取路由表(又名:IP 路由)

当我研究如何做到这一点时,我在另一个博客上发现了以下脚本:


import os, re
route_table = [elem.strip().split() for elem in os.popen("route print").read().split("Metric\n")[1].split("\n") if re.match("^[0-9]", elem.strip())]

我发现这很难理解,所以我重新写了一遍(或者找到了另一个例子,但忘了记下来),如下所示:


route_table = []
proc = subprocess.Popen("route print", shell=True,
                stdout=subprocess.PIPE)
while True:
    line = proc.stdout.readline()
    route_table.append(line.strip().split())
    if not line: break
proc.wait()

print "----------------------------------------------------------------------------------------------"
print "Route Table:"
print "%20s\t %15s\t %15s\t %15s\t %s" % ("Network Destination", "Netmask",
                                  "Gateway", "Interface", "Metric")
for route in route_table:
    if len(route) == 5:
        dst, mask, gateway, interface, metric = route
        print "%20s\t %15s\t %15s\t %15s\t %s" % (dst, mask, gateway, interface, metric)

我发现上面的代码更容易阅读和理解。它所做的只是使用子流程模块运行“route print”并将结果写入 stdout。不要被上面的 proc.stdout 迷惑了。这是进程的标准输出,不是普通的标准输出。我们希望将数据重定向到普通的标准输出!为此,我们读取 proc 的 stdout(或者有人会说是管道),并将每一行数据追加到一个列表中。然后,我们使用 Python 的字符串格式创建一个很好的定制输出。现在我们来看看如何使用 Python 运行 Ping 和 Tracert。

使用 Python 运行 Ping / Tracert

使用 Python 执行 ping 操作非常简单。我们只需要子流程模块来完成这项工作,正如您在下面的代码片段中看到的:


def pingIP(self, ip):
    proc = subprocess.Popen("ping %s" % ip, shell=True, 
                            stdout=subprocess.PIPE) 
    print
    while True:
        line = proc.stdout.readline()                        
        wx.Yield()
        if line.strip() == "":
            pass
        else:
            print line.strip()
        if not line: break
    proc.wait()

在这段代码中,我们使用 wx。实时将 ping 结果发送到我们的文本控件。如果我们没有这样做,那么在 ping 完成运行之前,我们不会收到任何 ping 结果。注意,我们也使用无限循环来获取结果。一旦结果不再出现,我们就跳出这个循环。如果您查看 tracert 代码,您会发现唯一的区别在于 out 子流程。Popen 命令。这将是重构的一个很好的候选,但是我将把它作为一个练习留给读者。

使用 Python 获取网卡信息

微软在他们的 Technet 子网站上有一整套 Python 脚本,我最终使用它们来获取我们电脑中网络接口卡(NIC)的各种有用信息。我不会在这里重复代码,因为它很长,我们已经有了。然而,这很容易理解,我怀疑如果你知道自己在做什么,你可以通过 WMI 获得同样的信息。我们感兴趣的主要部分是 MAC 和 IP 地址。让我们从长代码中提取这些信息,看看它有多容易获得:


strComputer = "."
objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator")
objSWbemServices = objWMIService.ConnectServer(strComputer,"root\cimv2")
colItems = objSWbemServices.ExecQuery("Select * from Win32_NetworkAdapterConfiguration")
numOfNics = len(colItems)

for objItem in colItems:
    z = objItem.IPAddress
    if z is None:
        a = 1
    else:
        for x in z:
             print "IP Address: ", x
    print "MAC Address: ", objItem.MACAddress

很简单,是吧?快看。您使用类似 SQL 的语法来运行查询。这就是为什么我认为你可以使用 WMI(事实上,这可能是它在以一种迟钝的方式做)。不管怎样,这就是全部了。

包扎

现在,您知道了从 PC 获取各种网络信息的秘密,以及如何将子进程的管道重定向到日志文件和 wxPython 文本控件。你如何选择使用这些信息取决于你自己。