I'm working on using subprocess.check_output to call ping command and get some output. The script will catch the wrong domain name. However, the output of the ping command will always print out on its own. When there's no exception, the output will not print out on its own.
hostname = "somewrongdomain.com" # This domain will not connect
try:
response = (subprocess.check_output(["ping", "-c 1", self.hostname]).decode("utf-8")
print(response)
except subprocess.TimeoutExpired as e:
ping_rating = 0
except subprocess.CalledProcessError as e:
ping_rating = 0
#do something else
The output I'm getting:
$ python3 test.py
ping: sendto: No route to host
Is there any way to NOT print the output(ping: sendto: No route to host) when the exception is caught?
As written in the documentation subprocess.check_output only captures STDOUT.
To also capture standard error in the result, use stderr=subprocess.STDOUT:
>>> subprocess.check_output(
... "ls non_existent_file; exit 0",
... stderr=subprocess.STDOUT,
... shell=True)
'ls: non_existent_file: No such file or directory\n'
Related
I am writing a python script to calculate packet loss through ping an IP address using subprocess module in linux. More than one IP address kept in CSV file. It is running fine when the pingable destination are only given.
But throwing an error when the non-pingable IP given in the CSV file and then the script is exiting without checking the other IP address in that CSV file. So I am not able to capture the packet loss for the non-pingable destination which is the main purpose the script.
Please suggest a way forward.
subprocess.check_output(['ping','-c 4',hostname], shell=False,
universal_newlines=True).splitlines()
subprocess.CalledProcessError: Command '['ping', '-c 4', '192.168.134.100']' returned non-zero exit status 1
It is just that subprocess returns an error if your ping has 100% packet loss, destination unreachable or any other problem. What you could do is:
try:
# subprocess code here
except:
# some code here if the destination is not pingable, e.g. print("Destination unreachable..") or something else
pass # You need pass so the script will continue on even after the error
Try this Code:
import subprocess
def systemCommand(Command):
Output = ""
Error = ""
try:
Output = subprocess.check_output(Command,stderr = subprocess.STDOUT,shell='True')
except subprocess.CalledProcessError as e:
#Invalid command raises this exception
Error = e.output
if Output:
Stdout = Output.split("\n")
else:
Stdout = []
if Error:
Stderr = Error.split("\n")
else:
Stderr = []
return (Stdout,Stderr)
#in main
Host = "ip to ping"
NoOfPackets = 2
Timeout = 5000 #in milliseconds
#Command for windows
Command = 'ping -n {0} -w {1} {2}'.format(NoOfPackets,Timeout,Host)
#Command for linux
#Command = 'ping -c {0} -w {1} {2}'.format(NoOfPackets,Timeout,Host)
Stdout,Stderr = systemCommand(Command)
if Stdout:
print("Host [{}] is reachable.".format(Host))
else:
print("Host [{}] is unreachable.".format(Host))
I'm looking for a solution to check whether or not a specific interface (eth0, wlan0, etc) has internet or not.
My situation is as follows; I have 2 active connections. One ethernet connection that has no internet (eth0) and one Wireless connection (wlan0) that has internet. Both have recieved an LAN IP from their respective DHCP servers.
I have come to the conclusion, but I am very open to suggestions, that the best solution would:
Have a ping command:
ping -I wlan0 -c 3 www.google.com
And have Python pick up or not I am able to reach the destination (check for: "Destination Host Unreachable")
import subprocess
command = ["ping", "-I", "wlan0", "-c", "3", "www.google.com"]
find = "Destination Host Unreachable"
p = subprocess.Popen(command, stdout=subprocess.PIPE)
text = p.stdout.read()
retcode = p.wait()
if find in command:
print "text found"
else:
print "not found"
This however does not yield the best result, and I could really use some help.
Thanks!
The text variable prints out the pint command output, it's just the finding the text inside the print command output that's not working.
Yes because you don't capture stderr, you can redirect stderr to stdout, you can also just call communicate to wait for the process to finish and get the output:
p = subprocess.Popen(command, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
out,_ = p.communicate()
You can also just use check_call which will raise an error for any non-zero exit status:
from subprocess import check_call, CalledProcessError, PIPE
def is_reachable(inter, i, add):
command = ["ping", "-I", inter, "-c", i, add]
try:
check_call(command, stdout=PIPE)
return True
except CalledProcessError as e:
print e.message
return False
If you want to only catch a certain error, you can check the return code.
def is_reachable(inter, i, add):
command = ["ping", "-I", inter, "-c", i, add]
try:
check_call(command, stdout=PIPE)
return True
except CalledProcessError as e:
if e.returncode == 1:
return False
raise
I am able to give the following command in the command-line
C:\>cd "C:\Program Files\ExtraPuTTY\Bin"
C:\Program Files\ExtraPuTTY\Bin>putty.exe -ssh root#172.20.0.102 22
This helps me open the SSH session through PuTTY.
Whereas I am not able to reproduce them in the Python script.
cwd="C://Program Files//ExtraPuTTY//Bin"
COMMAND="ls"
ssh = Popen(['putty.exe -ssh','%s'%HOST, COMMAND,cwd],shell=True,stdout=f,stderr=f)
The error that I see is
"putty.exe -ssh"' is not recognized as an internal or external command,operable program or batch file
In the putty download page, download and install plink, and make sure its in the windows path ($PATH variable)
Then, this python snippet should work:
import subprocess
cmd='plink -ssh {}#{} -pw {}'.format(user,server,password)
sp = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE, shell=True)
sp.stdin.write(stdin)
sp.stdin.close()
stdout= sp.stdout.read()
stderr=sp.stderr.read()
sp.wait()
stdin is the commands typed by the user in the terminal, stdout and stderr are the server output.
Fill in your credentials in the user="root", server="172.20.0.102 22" and maybe password for the ssh connection
You have to pass the cwd as the cwd parameter of the Popen:
Popen(['putty.exe -ssh'...], shell=True, stdout=f, stderr=f, cwd=cwd)
And you should use Plink, not PuTTY, for automating the remote command execution. The Plink accepts the command on its command-line (PuTTY does not):
Popen(['plink.exe -ssh root#172.20.0.102 ls'], shell=True, stdout=f, stderr=f, cwd=cwd)
Even better, use a native Python SSH library, like Paramiko:
Python Paramiko - Run command
I know it' a bit beside the question, but that the most closed topic I found (I'd like to found that code a week ago on that post)
I was looking for a code to massively check if a password is active, and change it if possible
Putty have several cli tool like plink and pscp whch are useful for a lot of stuff.
Here is python 3 function to connect to a ssh server and accept ssh key.
using pscp allow a auto accept key... can be useful for first time
def TestSSHConnection(IP_Addr,User,Password,verbosity=0, force_plink=False):
#Some infos about returned code
# 0 = Error Or crash
# 1 = Connection ok
# 2 = No connect Password Error
# 3 = SSH key trouble (shit append)
# 4 = Timeout
# 5 = Host Unreachable
# 6 = Connection Crash
out=""
err=""
try:
if force_plink:
print("echo y | plink -l "+str(User)+" -pw "+str(Password)+" -batch "+str(IP_Addr)+" exit",)
ssh=subprocess.Popen("echo y | plink -l "+str(User)+" -pw "+str(Password)+" -batch "+str(IP_Addr)+" exit",shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,encoding='utf8')#,stdin=subprocess.PIPE)
else:
print("echo y | pscp -l "+str(User)+" -pw "+str(Password)+" -ls "+str(IP_Addr)+":/",)
ssh=subprocess.Popen("echo y | pscp -l "+str(User)+" -pw "+str(Password)+" -ls "+str(IP_Addr)+":/",shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out,err = ssh.communicate()
try:
out = out.decode('utf-8')
except AttributeError as inst:
pass
except Exception as inst:
if verbosity>1:
print("While decoding stdout: "+str(type(inst)))
try:
err = err.decode('utf-8')
except AttributeError as inst:
pass
except Exception as inst:
print("While decoding stderr: "+str(type(inst)))
ssh.kill()
del ssh
except Exception as inst:
print("Crash"+str(inst))
return 0
if len(err)>0:
if 'Unable to open connection' in err or 'Host does not exist' in err:
if verbosity>0: print("Unreachable")
result = 5
if verbosity>1:
print()
print("-"*30)
print(err)
print("-"*30)
elif 'Connection timed out' in err:
result = 4
elif 'The server\'s host key is not cached in the registry' in err:
result = 3
if verbosity>1:
print()
print("SSH key Err".center(30,"-"))
print(err)
print("-"*30)
elif 'Access denied' in err:
result = 2
if verbosity>2:
print()
print("Denied".center(30,"-"))
print(err)
print("-"*30)
else:
result = 6
if verbosity>0: print("ConnCrash")
print("Oups".center(30,"-"))
print(err)
print("-"*30)
else:
if verbosity>0: print("Conn ok")
result = 1
del out,err
return result
Of cource, this juste Check connection (and accept ssh key)
So here is a code to run a script on the host (precisely a password change).
To do so, you can't use one line syntax (even it must work, it won't, i tried) You have to pass through a script file and push it with plink.
def ChangeMyPassword(IP_Addr,User,Old_Password,New_Password,verbosity=0):
#Some infos about returned code
# 0 = Error Or crash
# 1 = Change Ok
# 2 = Old Password Error
# 3 = SSH key trouble (shit append)
# 4 = Timeout
# 5 = Host Unreachable
# 6 = Connection Crash
out=""
err=""
try:
path_script = "."+os.sep+"Script_chmdp_"+str(IP_Addr)+".sh"
if os.path.exists(path_script):os.remove(path_script)
fic_script = codecs.open(path_script,'w', encoding='utf8')
fic_script.write('echo -e \"'+str(Old_Password)+'\\n'+str(New_Password)+'\\n'+str(New_Password)+'\" | passwd')
fic_script.flush()
fic_script.close()
cmd = "plink -l "+str(User)+" -pw "+str(Old_Password)+" -batch "+str(IP_Addr)+ " "
cmd += "-m "+path_script
print(str(cmd))
ssh=subprocess.Popen(cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,encoding='utf8')#,stdin=subprocess.PIPE)
out,err = ssh.communicate()
try:
out = out.decode('utf-8')
except AttributeError as inst:
pass
except Exception as inst:
if verbosity>1:
print("While decoding stdout: "+str(type(inst)))
try:
err = err.decode('utf-8')
except AttributeError as inst:
pass
except Exception as inst:
print("While decoding stderr: "+str(type(inst)))
ssh.kill()
del ssh
except Exception as inst:
print("Crash"+str(inst))
return 0
if 'all authentication tokens updated successfully' in out:
if verbosity>0: print("Conn ok")
result = 1
else:
if verbosity>0: print("Something goes wrong, hope we do not crash your server :)")
result = 0
del out,err
return result
So now you have two function to massively change password on your systems.
Bonus: a function to get /etc/passwd and /etc/shadow. Why? for educationnal use on your IT admin like 'hey you f*** up and use the same password everywhere, and now all this account can be Bruteforced. So clean up your mess
def GetPass(IP_Addr,User,Password,verbosity=0, force_plink=False):
#Some infos about returned code
# 0 = Error Or crash
# 1 = Connection ok
# 2 = No connect Password Error
# 3 = SSH key trouble (shit append)
# 4 = Timeout
# 5 = Host Unreachable
# 6 = Connection Crash
out=""
err=""
try:
ssh=subprocess.Popen("echo "+str(Password)+" | plink -l "+str(User)+" -pw "+str(Password)+" -batch "+str(IP_Addr)+" sudo cat /etc/passwd;sudo cat /etc/shadow",shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,encoding='utf8')#,stdin=subprocess.PIPE)
out,err = ssh.communicate()
try:
out = out.decode('utf-8')
except AttributeError as inst:
pass
except Exception as inst:
if verbosity>1:
print("While decoding stdout: "+str(type(inst)))
try:
err = err.decode('utf-8')
except AttributeError as inst:
pass
except Exception as inst:
print("While decoding stderr: "+str(type(inst)))
ssh.kill()
del ssh
except Exception as inst:
print("Crash"+str(inst))
return 0
if len(err)>0:
if 'Unable to open connection' in err or 'Host does not exist' in err:
if verbosity>0: print("Unreachable")
result = 5
if verbosity>1:
print()
print("-"*30)
print(err)
print("-"*30)
elif 'Connection timed out' in err:
result = 4
elif 'The server\'s host key is not cached in the registry' in err:
result = 3
if verbosity>1:
print()
print("SSH key Err".center(30,"-"))
print(err)
print("-"*30)
elif 'Access denied' in err:
result = 2
if verbosity>2:
print()
print("Denied".center(30,"-"))
print(err)
print("-"*30)
else:
result = 6
if verbosity>0: print("ConnCrash")
print("Oups".center(30,"-"))
print(err)
print("-"*30)
else:
if verbosity>0: print("Conn ok")
result = out
del out,err
return result
Some more notes:
if you don't use shell=True, you don't get the output, and it does not work, I don't know why.
I also tried a asynchronous communication to send comand line by line, it does not work.
I also tried the ssh command (yes it now exist on windows \o/) but it does not work for my purpose.
Hope this help someone one day, it would have helped me a lot a week ago :)
I am developing a small tool in Python 2.7 and using subprocess module. I am using this module to run commands on remote devices using its check_output function. There might be a situation in which the remote device is not functioning and therefore I am getting the following response:
Timeout: No Response from 10.xxx.xxx.xxx
Following is my code:
try:
x=subprocess.check_output(command, shell=True)
except Exception:
print ("Some issues in fetching details")
exit()
else:
print (x)
I want to put timeout into this function so that if after a certain amount of time, no response is recieved, my code goes in the Exception part and prints the given message. I tried using timeout argument in the check_output command but after running my script with timeout argument, it is immediately printing the message given in the Exception part.
What I tried:
try:
x=subprocess.check_output(command, shell=True, timeout=5)
except Exception:
print ("Some issues in fetching details")
exit()
else:
print (x)
My guess is that you are running your code in Python 2.
If that is the case, subprocess.check_output() does not accept a timeout parameter, and the function will fail immediately with:
TypeError: __init__() got an unexpected keyword argument 'timeout'
But, because you are catching all exceptions and printing a generic message, you don't see the actual exception, and you assume that the command is timing out immediately.
One way to fix this problem is to run your code in Python 3.
Whether you are running Python 2 or 3, I recommend that you do not catch all exceptions, or that you at least print the value of the exception so that you can see the actual cause, e.g.
try:
x=subprocess.check_output(command, shell=True, timeout=5)
except subprocess.TimeoutExpired as exc:
print("Command timed out: {}".format(exc))
exit()
else:
print (x)
which explicitly checks for a timeout exception. All other exceptions are propagated as usual and so are not masked by your "catch all" code. Or,
try:
x=subprocess.check_output(command, shell=True, timeout=5)
except Exception as exc:
print("Command failed: {}".format(exc))
exit()
else:
print (x)
but the former is preferred.
Edit
OP can't use Python 3. If you are using Linux then you could use the timeout command, e.g.
x = subprocess.check_output('timeout 5 {}'.format(command), shell=True)
On timeout this will raise an exception with a particular exit status value of 124:
subprocess.CalledProcessError: Command 'timeout 5 sleep 10' returned non-zero exit status 124
BTW you shouldn't use the shell=True option as there are security implications as mentioned in the documentation. Instead you should pass a list of strings to check_output() like this:
from shlex import shlex
command = shlex('timeout 5 {}'.format(command))
try:
x = subprocess.check_output(command)
except subprocess.CalledProcessError as exc:
if exc.returncode == 124:
print "Command timed out"
else:
raise
If you are using another OS (or you don't want to use timeout) then you can run your subprocess in a separate thread and have your main thread time it out if required. See this other question, Using module 'subprocess' with timeout, for details about how to do that.
Python 2.7 does not support timeout parameter. You can instead use EasyProcess. This is a layer on top of subprocess module and pretty easy to use.
I've got a function def tldomaint that executes the Tasklist command via a subprocess call_checkout. All is working as expected but I'm getting odd output from TaskList. I'm not sure if it's due to my error capturing or if its just an oddity of Tasklist. I'm hoping someone can help pin-point the issue.
Output example:
Attempting to make remote connections and gather data:
Targeted User: xpuser
ERROR: The RPC server is unavailable.
1
WARNING: User credentials cannot be used for local connections
ERROR: The RPC server is unavailable.
1
The 1 in the output is the oddity I'm referring to.
Below is the function.
def tldomaint(serverlist, domain, username, password, targetuser):
nlist = serverlist
print "\nAttempting to make remote connections and gather data:\n"
print "Targeted User: {0}\n" .format(targetuser)
for serverl in nlist:
try:
out = subprocess.check_output(["tasklist", "/V", "/S", serverl, "/U", domain + "\\" + username, "/P", password, "/FO", "List", "/FI", "USERNAME eq %s\\%s" % (domain, targetuser)])
users = [item for item in out.split() if domain in item and targetuser in item]
sortedl = set(users)
for name in sortedl:
if name in sortedl != '':
print "Targeted User Found On {0}\n" .format(serverl)
print name
else:
print "User Not Found"
except CalledProcessError as e:
print(e.returncode)
return sortedl
You are printing the process return code:
except CalledProcessError as e:
print(e.returncode)
From the subprocess.check_output() documentation:
If the return code was non-zero it raises a CalledProcessError.
When an error occured, the tasklist writes an error message to stderr, and sets the exit code to 1. subprocess.check_output() then raises the CalledProcessError exception (as documented) and you catch that exception and then print the return code.
Remove the print() statement and your mysterious 1s will go away.
If you wanted to handle the problem in Python, redirect stderr to stdout; the exception will still be raised but you can read the output still:
out = subprocess.check_output(["tasklist", "/V", "/S", serverl, "/U",
domain + "\\" + username, "/P", password, "/FO", "List",
"/FI", "USERNAME eq %s\\%s" % (domain, targetuser)],
stderr=subprocess.STDOUT)
and in your exception handler:
except CalledProcessError as e:
errormessage = e.output
# do something with the error message