船井総研デジタルのよもぎたです。
PythonのPing3パッケージのソースコードを調べたので、わかったこと、主に関数の使い方についてまとめておきます。
Ping3パッケージはVer.4.0.4を確認しています。Ping3パッケージのPyPIのページはこちら、ソースコードはGitHub上のこちらのリポジトリになります。
概要
- GitHubのREADMEのGet Startedにある
pingとverbose_pingを押さえておけばよい。 -
pingが1回Pingを実行する(ICMP Echo Requestを送信してICMP Echo Replyを受信する)。 -
verbose_pingはpingをラッピングしている。結果は標準出力に出力される。 - IPv6には対応していない。
ping
引数
必須のもの
-
dest_addr: Pingの宛先をIPアドレスまたはFQDNで指定します。
任意のもの
-
timeout:Pingのタイムアウト値を整数で指定します。単位は秒で、デフォルトは4秒です。 -
unit:戻り値の単位を文字列で指定します。指定できる値はsの秒と、msのミリ秒です。デフォルトはsです。 -
src_addr:Pingの送信元となるIPアドレスを文字列で指定します。デフォルトはNoneです。 -
interface:Pingの送信元となるインターフェース(NIC)を文字列で指定します。デフォルトはNoneです。この引数は、OSがLinuxの場合にだけ指定できます。 -
ttl: 送信するICMP Echo RequestのTTL(Time to Live)の値を整数で指定します。デフォルトはNoneで、その場合はOSのデフォルト値が使われます。Windowsの場合は128、Linuxの場合は64になります。 -
seq:送信するICMP Echo Requestのシーケンス番号を整数で指定します。デフォルトは0になります。 -
size:送信するICMP Echo Requestのペイロードの大きさを整数で指定します。単位はバイトで、デフォルトは56です。
戻り値
あて先からの応答時間がfloatで返されます。デフォルトはの単位は秒で、unit引数でmsを指定した場合はミリ秒になります。
タイムアウトになった場合はNoneが返されます。それ以外の何らかのエラーの場合には、Falseが返されます。
例外
Ping3モジュールはsocketモジュールを使用しています。SocketのタイプとしてまずSOCK_RAWでインスタンスの作成を試みて、失敗した場合はSOCK_DGRAMでインスタンスの作成を試みます。どちらも失敗した場合、PermissionError例外が投げられます。
verbose_ping
引数
必須のもの
-
dest_addr:Pingの宛先をIPアドレスまたはFQDNで指定します。
任意のもの
-
count:Pingを実行する回数を整数で指定します。デフォルト値は4です。0を指定すると無限に実行します。 -
interval:Pingを実行する間隔を整数で指定します。単位は秒で、デフォルト値は0です。0が指定されるとPingに応答があり次第即次のPingが実行されます。
その他、pingと同じ引数が指定できます。
戻り値
メソッドとしての戻り値は無くて、代わりにPingの実行結果(応答時間 or タイムアウト or エラー)が標準出力に出力されます。
余談
このパッケージを調べたキッカケはPythonでPing監視を実行したかったからです。監視要件に沿って複数回Pingを実行できるようにするつもりだったのですが、結果が標準出力に出力されてしまうとちょっと扱いにくいですね。
デバッグモード
ping3.DEBUG = Trueとすると、デバッグモードが有効になります。細かくデバッグメッセージの出力が埋め込まれていて、Trueにするとメッセージが標準出力に出力されます。
テストスクリプト
import ping3
ping3.DEBUG = True
ping3.ping('www.example.com')
実行例
$ python test_ping.py
[DEBUG] Ping3 Version: 4.0.4
[DEBUG] LOGGER: <Logger ping3 (DEBUG)>
[DEBUG] Function called: ping(www.example.com)
[DEBUG] `[Errno 1] Operation not permitted` when create socket.SOCK_RAW, using socket.SOCK_DGRAM instead.
[DEBUG] Function called: send_one_ping({'sock': <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=1, laddr=('0.0.0.0', 0)>, 'dest_addr': 'www.example.com', 'icmp_id': 51073, 'seq': 0, 'size': 56})
[DEBUG] Destination address: 'www.example.com'
[DEBUG] Destination IP address: 93.184.216.34
[DEBUG] Sent ICMP header: {'type': 8, 'code': 0, 'checksum': 8998, 'id': 51073, 'seq': 0}
[DEBUG] Sent ICMP payload: b'A\xd9C\xb3\xe7\xc9\x00bQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ'
[DEBUG] Function returned: send_one_ping -> None
[DEBUG] Function called: receive_one_ping({'sock': <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=1, laddr=('0.0.0.0', 37)>, 'icmp_id': 51073, 'seq': 0, 'timeout': 4})
[DEBUG] Unprivileged on Linux
[DEBUG] Timeout time: Sat Sep 23 20:44:35 2023 (1695469475.141067)
[DEBUG] Timeout left: 4.00s
[DEBUG] Received time: Sat Sep 23 20:44:31 2023 (1695469471.3031929))
[DEBUG] Received ICMP header: {'type': 0, 'code': 0, 'checksum': 62082, 'id': 37, 'seq': 0}
[DEBUG] Received ICMP payload: b'A\xd9C\xb3\xe7\xc9\x00bQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ'
[DEBUG] Received sent time: Sat Sep 23 20:44:31 2023 (1695469471.1406484)
[DEBUG] Function returned: receive_one_ping -> 0.16254448890686035
[DEBUG] Function returned: ping -> 0.16254448890686035
ちなみに上の例は一般ユーザーで実行していて、socket.SOCK_RAWが使えずにsocket.DGRAMで実行していることが分かります。同じスクリプトをrootで実行すると、次のようにsocket.RAWで実行していることが分かります。
# python test_ping.py
[DEBUG] Ping3 Version: 4.0.4
[DEBUG] LOGGER: <Logger ping3 (DEBUG)>
[DEBUG] Function called: ping(www.example.com)
[DEBUG] Function called: send_one_ping({'sock': <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_RAW, proto=1, laddr=('0.0.0.0', 1)>, 'dest_addr': 'www.example.com', 'icmp_id': 57200, 'seq': 0, 'size': 56})
[DEBUG] Destination address: 'www.example.com'
[DEBUG] Destination IP address: 93.184.216.34
[DEBUG] Sent ICMP header: {'type': 8, 'code': 0, 'checksum': 59052, 'id': 57200, 'seq': 0}
[DEBUG] Sent ICMP payload: b'A\xd9C\xb3\xf6W\x16^QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ'
[DEBUG] Function returned: send_one_ping -> None
[DEBUG] Function called: receive_one_ping({'sock': <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_RAW, proto=1, laddr=('0.0.0.0', 1)>, 'icmp_id': 57200, 'seq': 0, 'timeout': 4})
[DEBUG] Timeout time: Sat Sep 23 20:45:33 2023 (1695469533.3609657)
[DEBUG] Timeout left: 4.00s
[DEBUG] Received time: Sat Sep 23 20:45:29 2023 (1695469529.504645))
[DEBUG] Received IP header: {'version': 69, 'tos': 0, 'len': 84, 'id': 0, 'flags': 16384, 'ttl': 44, 'protocol': 1, 'checksum': 46826, 'src_addr': '93.184.216.34', 'dest_addr': '172.17.181.210'}
[DEBUG] Received ICMP header: {'type': 0, 'code': 0, 'checksum': 61100, 'id': 57200, 'seq': 0}
[DEBUG] Received ICMP payload: b'A\xd9C\xb3\xf6W\x16^QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ'
[DEBUG] Received sent time: Sat Sep 23 20:45:29 2023 (1695469529.3607402)
[DEBUG] Function returned: receive_one_ping -> 0.1439049243927002
[DEBUG] Function returned: ping -> 0.1439049243927002
EXCEPTIONSモード
Pingに応答がなかった時にpingメソッドがFaleseやNoneを返さずに例外を投げるようにするEXCEPTIONSモードがあります。
たとえば、存在しないFQDNに向かってPingを実行すると次のようになります。
テストスクリプト
import ping3
ping3.EXCEPTIONS = True
try:
ping3.ping('no.such.domain')
except Exception as e:
print(e)
実行例
$ python test2_ping.py
Cannot resolve: Unknown host. (Host='no.such.domain')
以上です、最後までお読みいただきありがとうございました。