thombashi / pingparsing

pingparsing is a CLI-tool/Python-library parser and transmitter for ping command :arrow_right_hook:
https://pingparsing.rtfd.io/
MIT License
78 stars 10 forks source link

Error when parsing ping results with `pipe` field #45

Closed docweirdo closed 3 years ago

docweirdo commented 3 years ago

I noticed that when parsing a ping result which includes a pipe field, no error is thrown but the PingStats object is still empty.

import pingparsing
import json

ping_result_string = '''
PING 119.28.156.219 (119.28.156.219) 64(92) bytes of data.
[1622139975.732085] no answer yet for icmp_seq=1
[1622139975.817765] 72 bytes from 119.28.156.219: icmp_seq=1 ttl=48 time=283 ms
[1622139975.932917] no answer yet for icmp_seq=2
[1622139976.015482] 72 bytes from 119.28.156.219: icmp_seq=2 ttl=48 time=283 ms
[1622139976.133145] no answer yet for icmp_seq=3
[1622139976.215812] 72 bytes from 119.28.156.219: icmp_seq=3 ttl=48 time=283 ms
[1622139976.334197] no answer yet for icmp_seq=4
[1622139976.416428] 72 bytes from 119.28.156.219: icmp_seq=4 ttl=48 time=283 ms
[1622139976.625736] 72 bytes from 119.28.156.219: icmp_seq=5 ttl=48 time=284 ms

--- 119.28.156.219 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 803ms
rtt min/avg/max/mdev = 282.854/283.307/284.019/0.701 ms, pipe 2
'''

parser = pingparsing.PingParsing()
parsed_result = parser.parse(ping_result_string)
print(json.dumps(parsed_result.as_dict(), indent=4))

Output:

{
    "destination": null,
    "packet_transmit": null,
    "packet_receive": null,
    "packet_loss_count": null,
    "packet_loss_rate": null,
    "rtt_min": null,
    "rtt_avg": null,
    "rtt_max": null,
    "rtt_mdev": null,
    "packet_duplicate_count": null,
    "packet_duplicate_rate": null
}

This is both the case when the last line is blank except for the pipe field as well as when the pipe field sits at the end. In this case, the display of the field is owed to a reduced interval of 0.2 seconds. But if I'm not mistaken, it will turn up with RTTs higher than 1 second without setting the interval.

docweirdo commented 3 years ago

In the meantime: a simple solution is to just partition the string and take the first half before passing it to the parser.

ping_result_string = ping_result_string.partition("pipe")[0]

This works without regard to the presence of the pipe field in ping_result_string.

thombashi commented 3 years ago

@docweirdo Thank you for your report.

In my environment, your code looks work well:

>>> import pingparsing
>>> importt json
>>> ping_result_string = '''
... PING 119.28.156.219 (119.28.156.219) 64(92) bytes of data.
... [1622139975.732085] no answer yet for icmp_seq=1
... [1622139975.817765] 72 bytes from 119.28.156.219: icmp_seq=1 ttl=48 time=283 ms
... [1622139975.932917] no answer yet for icmp_seq=2
... [1622139976.015482] 72 bytes from 119.28.156.219: icmp_seq=2 ttl=48 time=283 ms
... [1622139976.133145] no answer yet for icmp_seq=3
... [1622139976.215812] 72 bytes from 119.28.156.219: icmp_seq=3 ttl=48 time=283 ms
... [1622139976.334197] no answer yet for icmp_seq=4
... [1622139976.416428] 72 bytes from 119.28.156.219: icmp_seq=4 ttl=48 time=283 ms
... [1622139976.625736] 72 bytes from 119.28.156.219: icmp_seq=5 ttl=48 time=284 ms
...
... --- 119.28.156.219 ping statistics ---
... 5 packets transmitted, 5 received, 0% packet loss, time 803ms
... rtt min/avg/max/mdev = 282.854/283.307/284.019/0.701 ms, pipe 2
... '''
>>>
>>> parser = pingparsing.PingParsing()
>>> parsed_result = parser.parse(ping_result_string)
>>> print(json.dumps(parsed_result.as_dict(), indent=4))
{
    "destination": "119.28.156.219",
    "packet_transmit": 5,
    "packet_receive": 5,
    "packet_loss_count": 0,
    "packet_loss_rate": 0.0,
    "rtt_min": 282.854,
    "rtt_avg": 283.307,
    "rtt_max": 284.019,
    "rtt_mdev": 0.701,
    "packet_duplicate_count": 0,
    "packet_duplicate_rate": 0.0
}

My environment is as follows:

$ python3 -m envinfopy pingparsing humanreadable pyparsing --format markdown
|    Module     |              Version              |
| ------------- | --------------------------------- |
| uname         | Linux 4.19.104-microsoft-standard |
| Python        | CPython 3.8.5                     |
| platform      | Ubuntu 18.04                      |
| pingparsing   | 1.3.1                             |
| humanreadable | 0.1.0                             |
| pyparsing     | 2.4.7                             |

Could you tell me your environment information?

docweirdo commented 3 years ago

I just noticed that this behavior does not occur when the pipe field sits at the end but only when the last line is blank except for the pipe field.

My mistake for not properly providing a minimum working example.

try this string as input:

PING 91.221.122.179 (91.221.122.179) 64(92) bytes of data.
[1622145167.999326] no answer yet for icmp_seq=1
[1622145168.201746] no answer yet for icmp_seq=2
[1622145168.405761] no answer yet for icmp_seq=3
[1622145168.609750] no answer yet for icmp_seq=4
[1622145170.826836] From 91.221.122.179 icmp_seq=1 Destination Host Unreachable
[1622145170.826888] From 91.221.122.179 icmp_seq=2 Destination Host Unreachable
[1622145170.826896] From 91.221.122.179 icmp_seq=3 Destination Host Unreachable
[1622145170.826901] From 91.221.122.179 icmp_seq=4 Destination Host Unreachable
[1622145170.826907] From 91.221.122.179 icmp_seq=5 Destination Host Unreachable

--- 91.221.122.179 ping statistics ---
5 packets transmitted, 0 received, +5 errors, 100% packet loss, time 811ms
pipe 5

Environment information:

|    Module     |        Version         |
| ------------- | ---------------------- |
| uname         | Linux 5.4.0-73-generic |
| Python        | CPython 3.8.5          |
| platform      | Ubuntu 20.04           |
| pingparsing   | 1.3.1                  |
| humanreadable | 0.1.0                  |
| pyparsing     | 2.4.7                  |

Thanks for the great library!

thombashi commented 3 years ago

Thank you for your additional information.

I had fixed the problem at pngparsing 1.3.2. Please try to upgrade the package.

>>> import pingparsing
>>> importt json
>>> ping_result_string = '''
... PING 91.221.122.179 (91.221.122.179) 64(92) bytes of data.
... [1622145167.999326] no answer yet for icmp_seq=1
... [1622145168.201746] no answer yet for icmp_seq=2
... [1622145168.405761] no answer yet for icmp_seq=3
... [1622145168.609750] no answer yet for icmp_seq=4
... [1622145170.826836] From 91.221.122.179 icmp_seq=1 Destination Host Unreachable
... [1622145170.826888] From 91.221.122.179 icmp_seq=2 Destination Host Unreachable
... [1622145170.826896] From 91.221.122.179 icmp_seq=3 Destination Host Unreachable
... [1622145170.826901] From 91.221.122.179 icmp_seq=4 Destination Host Unreachable
... [1622145170.826907] From 91.221.122.179 icmp_seq=5 Destination Host Unreachable
...
... --- 91.221.122.179 ping statistics ---
... 5 packets transmitted, 0 received, +5 errors, 100% packet loss, time 811ms
... pipe 5
... '''
>>>
>>> parser = pingparsing.PingParsing()
>>> parsed_result = parser.parse(ping_result_string)
>>> print(json.dumps(parsed_result.as_dict(), indent=4))
{
    "destination": "91.221.122.179",
    "packet_transmit": 5,
    "packet_receive": 0,
    "packet_loss_count": 5,
    "packet_loss_rate": 100.0,
    "rtt_min": null,
    "rtt_avg": null,
    "rtt_max": null,
    "rtt_mdev": null,
    "packet_duplicate_count": 0,
    "packet_duplicate_rate": null
}
>>>
docweirdo commented 3 years ago

Works like a charm! Thank you