thiezn / iperf3-python

Python wrapper around iperf3
https://iperf3-python.readthedocs.org/
MIT License
110 stars 51 forks source link

Cannot run from Cron #13

Closed cvicente closed 6 years ago

cvicente commented 7 years ago

I just discovered that a script using this library cannot run under Cron. I get:

File "/usr/local/lib/python2.7/dist-packages/iperf_graphite/iperf_graphite.py", line 58, in run_iperf
    r = client.run()
  File "/usr/local/lib/python2.7/dist-packages/iperf3/iperf3.py", line 465, in run
    return TestResult(data)
  File "/usr/local/lib/python2.7/dist-packages/iperf3/iperf3.py", line 590, in __init__
    self.json = json.loads(result)
  File "/usr/lib/python2.7/json/__init__.py", line 338, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python2.7/json/decoder.py", line 366, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python2.7/json/decoder.py", line 384, in raw_decode
    raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded

It seems to be related to how iperf3-python handles stdout/stderr via pipes. I'm not sure how to fix this. Any pointers appreciated.

thiezn commented 7 years ago

The libiperf C library doesn't seem to allow you to retrieve the result through an API call. This meant I had to resort to capturing the output by temporarily redirecting stdout to the script so I can parse the response.

I suspect cron doesn't like this very much but you might be able to get around this by setting json_output = False in the latest (0.1.3) version. When this is enabled the client no longer returns a TestResult but just prints the normal iperf3 text output to stdout.

Perhaps this would help you to use the library in crontab?

jaxzin commented 7 years ago

I can reproduce this ValueError outside of cron by redirecting stdout to a file (ex. myscript.py > output.log). The result is the same error as above. Without the redirection the script runs fine.

bmah888 commented 7 years ago

iperf3 maintainer here. The reason you can't get the JSON programmatically is because libiperf gets the JSON rendering from cJSON_Print() in the cJSON library. cJSON owns the memory, so we want to ship this off before we have cJSON_Delete() clean up. We do this all in iperf_json_finish(). It doesn't necessarily have to be this way. We could in theory (as in "I haven't tried this") make a copy of the string before calling cJSON_Delete(), so you could access it after the test is done. We'd need to make sure to get rid of the string when we delete the iperf_test object.

I guess the other issue to be handled is you'd need to find out whether you can actually use this or not (assume we implement the correct setters and getters for json_output_string if we don't have them already). I haven't thought of a good way to do that with the API as it currently stands.

If this is of interest, you can try putting an issue (or even better a pull request!) in the iperf3 issue tracker at https://github.com/esnet/iperf.

(I guess I should also mention that I am not sure if this would solve the OP's problem.)

thiezn commented 7 years ago

Thanks @bmah888 that pointed me in the right direction. I noticed you already implemented your suggestion in https://github.com/esnet/iperf/commit/51a275de9463febc440d41cee9d971fcd381e01c. The json_output_string feature was added in release 3.1 so I've updated the python wrapper to use this when available.

At the moment I've only been able to get this working using the iperf3 client session, the server instance throws a segmentation fault. This might be related to my module or perhaps something in the iperf3 C library itself, but will investigate futher, when I can find some time.

@cvicente @jaxzin python-iperf3 has been updated to v0.1.5 and uploaded to pypi. Please let me know if you still encounter issues.

bmah888 commented 7 years ago

@thiezn: Glad that helped! Clearly I was confused (or at least forgetful), although I guess consistent in that I recreated the solution I implemented three years ago. iperf_json_finish(), which I mentioned earlier, is one layer above where I had in mind when I wrote my comment yesterday. It's something that a libiperf-using application might do, but that particular function isn't something that an application would be calling directly.

cvicente commented 7 years ago

@thiezn Unfortunately I'm still seeing the same problem after upgrading to 0.1.7

Relevant section of the stack trace:

  File "/usr/local/lib/python2.7/dist-packages/iperf3/iperf3.py", line 512, in run
    return TestResult(data)
  File "/usr/local/lib/python2.7/dist-packages/iperf3/iperf3.py", line 659, in __init__
    self.json = json.loads(result)
  File "/usr/lib/python2.7/json/__init__.py", line 338, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python2.7/json/decoder.py", line 366, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python2.7/json/decoder.py", line 384, in raw_decode
    raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded
cvicente commented 7 years ago

Never mind. I just payed more attention to the thread above. Seems to work after upgrading to iperf3-3.1.3

Thanks!!