I am willing to determine the current CPU usage of a bunch of Linux computers, all available via SSH connexion. As for now I have managed something via the psutil and subprocess modules, but it could be much more straightforward if I could get a list out of subprocess.check_output.
So I defined a cpu_script.py :
import psutil
print(psutil.cpu_percent(interval=1,percpu=True))
to be called in my main script, which is in the same folder, with:
import subprocess
import os
import numpy as np
import psutil
usr = "AA"
computer = "BB"
cpu_script = os.path.join(os.getcwd(),"cpu_script.py")
test = subprocess.check_output("ssh -X " + usr + "@" + computer +
"python3 -u - < " + cpu_script,shell=True).strip()
As cpu_percent's outputs are typically in a list, I would expect my "test" variable to be a list, but what I really get is a byte:
input(test)
>> b'[4.0, 5.0, 7.0, 10.9, 18.4, 1.0, 4.0, 3.0]'
input(type(test))
>> <class 'bytes'>
Here I have managed to bend everything to finally get an array (with decode + replace + np.fromstring), but it is far from satisfying:
tmp = test.decode("utf-8")
input(tmp)
>> [4.0, 5.0, 7.0, 10.9, 18.4, 1.0, 4.0, 3.0]
input(type(tmp))
>> <class 'str'>
tmp = (tmp.replace('[','')).replace(']','')
result = np.fromstring(tmp, sep=',')
input(result)
>> [ 4. 5. 7. 10.9 18.4 1. 4. 3. ]
input(type(result))
>> <class 'numpy.ndarray'>
Could'nt I have straightaway a subprocess.check_output variable that is a list or an array ? What am I missing ?
Also if there is a way to avoid defining a cpu_script, hence to pass directly the python command in the subprocess, I would be interested ! I have failed at all my attempts to do so for now, the tricky part here being that there is both a ssh connexion and a python3 console command followed by python commands.
Thanks for your help!
The underlying python program "serialized" the output as text.
check_output
returns a bytes
object that you need to decode, then "unserialize".
I suggest:
import ast
result = ast.literal_eval(test.decode())
The best way would be to call this sub-python script as a module. You wouldn't have to run a subprocess/serialize/unserialize in that case. Just import the sub-module, call a function and get a real list
as a result. Of course, in the case of ssh
subprocess call, you cannot.
However you could improve this ugly and code-injection prone/unsafe check_output
call needing shell=True
by passing the filehandle of the opened python file to execute, feeding the output to the input of check_output
, and using a list of strings instead of a string as argument:
with open(cpu_script,"rb") as f:
test = subprocess.check_output(["ssh","-X",usr + "@" + computer,"python3","-u","-"],stdin=f)
followed by the decode+evaluation code.