Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ The following people have contributed:

- Alexis Métaireau
- Chritophe Narbonne
- Alireza Kashani
=======

25 changes: 23 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Exemple in a python console::
>>> # funky prompt should reduce logs lisibility so you should use sh or bash
>>> s.send_commands('bash')
>>> s.enable_logs()
>>> s.send_commands("df") ## basically this function didn't work and I added a similar function
>>> s.send_commands("df")
>>> print next(s.logs)
df
Expand All @@ -45,6 +46,20 @@ Exemple in a python console::
[<Screen 'session2'>, <Screen 'session1'>]
>>>

>>> conn = tunel('yourserver', username='yourusername',password='passx')
>>> print conn.execute_command_screen_ssh(command='python /myremotedir/myfolder/script.py', name='python')
>>> print conn.execute_command_screen_ssh(command='perl /myremotedir/myfolder/script.pl', name='perl')
>>> print conn.execute_command_screen_ssh(command='ls -d > /myremotedir/tmp.log', name='log')
>>> print conn.execute_command_screen_ssh(command='bash')
>>> print conn.list_screens_ssh
['python', 'perl', 'log', 'bash']
>>> print list_screens_ssh(conn)
>>> print conn.kill_screen_ssh('python')
>>> print conn.list_screens_ssh
['perl', 'log' ,'bash']
>>> conn.download('tmp.log','pathto/myremotedir/', 'pathto/localdir/')
>>> conn.upload('newfile.log','pathto/myremotedir/', 'pathto/localdir/')
>>> conn.execute('ls /myremotedir/')

Installation
-------------
Expand Down Expand Up @@ -72,11 +87,17 @@ Features
+ ``sudo chmod +s /usr/bin/screen``
+ ``sudo chmod 755 /var/run/screen``

Known issues
-------------
* running command on a ssh tunel
* making screen session on ssh tunel
* terminating a screen session on ssh tunel
* screen listing on a ssh tunel
* download/upload on a ssh tunel


This may not work properly with bpython.

running short commands such as "ls" can not be run on a screen session via ssh; since the terminal ends much faster than listing the result, however it works for heavier commands; for just generating a session, "bash" command is recommended

Roadmap
--------

Expand Down
2 changes: 1 addition & 1 deletion TODO.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ TODO list
=========

* Add tests
* Add a wrapper to run screen commands

232 changes: 229 additions & 3 deletions screenutils/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
# and/or modify it under the terms of the GNU Public License 2 or upper.
# Please ask if you wish a more permissive license.

# here is a test
# think python chapter 5

from screenutils.errors import ScreenNotFoundError
import os
import commands as cox
from screenutils.errors import ScreenNotFoundError

try:
Expand Down Expand Up @@ -41,6 +47,13 @@ def list_screens():
if ".".join(l.split(".")[1:]).split("\t")[0]
]

def getfile(string, alternative):
""" return alternative if the string is empty
"""
if len(string) == 0:
return alternative
else:
return string

class Screen(object):
"""Represents a gnu-screen object::
Expand All @@ -59,6 +72,8 @@ class Screen(object):

def __init__(self, name, initialize=False):
self.name = name
self.commanddict = dict() # for collecting the logs
self.flag = 'deactive' #do you need the status of a command ?
self._id = None
self._status = None
self.logs=None
Expand Down Expand Up @@ -89,6 +104,7 @@ def exists(self):
for l in lines]

def enable_logs(self):
self.flag = 'active'
self._screen_commands("logfile " + self.name, "log on")
system('touch '+self.name)
self.logs=tailf(self.name)
Expand All @@ -97,6 +113,7 @@ def enable_logs(self):
def disable_logs(self):
self._screen_commands("log off")
self.logs=None
self.flag = 'deactive'

def initialize(self):
"""initialize a screen, if does not exists yet"""
Expand All @@ -114,31 +131,99 @@ def interrupt(self):

def kill(self):
"""Kill the screen applications then close the screen"""
if self.flag == 'active': # remove the status files
if os.path.isfile(self.name+'.err'):
os.unlink(self.name+'.err')
if os.path.isfile(self.name+".log"):
os.unlink(self.name+'.err')
if os.path.isfile(self.name+".log"):
os.unlink(self.name+".log")
self._screen_commands('quit')

def detach(self):
"""detach the screen"""
self._check_exists()
system("screen -d " + self.name)

def send_commands(self, *commands):
def send_commands_original(self, *commands):
"""send commands to the active gnu-screen"""
self._check_exists()

for command in commands:
self._screen_commands( 'stuff "' + command + '" ' ,
'eval "stuff \\015"' )

def send_commands(self, *commands, **status):
"""send commands to the active gnu-screen"""

status_show = status.get('status', False)
for command in commands:
if self.flag == 'active':
cmd = command + '> ' + str(self.name)+'.out 2> ' + str(self.name)+'.err'
else:
cmd = command
self._screen_commands( 'stuff "' + cmd + '" ' ,
'eval "stuff \\015"' )

if self.flag == 'active':
f = open(self.name+'.out', 'r')
textout = f.read()
f.close

f = open(self.name+'.err', 'r')
texterr = f.read()
f.close

if status_show == True:
print(textout)
if getsize(self.name + '.err') != 0:
print 'ERROR:\n'
# below is the log dictionary ! contains, command, stdout, stderr
self.commanddict[len(self.commanddict)+1] = [commands[0], getfile(textout, 'ERROR'), getfile(texterr, 'Done')]

def add_user_access(self, unix_user_name):
"""allow to share your session with an other unix user"""
self._screen_commands('multiuser on', 'acladd ' + unix_user_name)

def report(self, n=1, entire=False):
""" return nth command status or write the entire log file
"""
L = range(1, len(self.commanddict)+1)
if entire==True:
f = open(str(self.name)+"_screen.log", "w")
for item in L:
f.writelines("# " + str(item) + "\n")
f.writelines(["%s\n" % item for item in self.commanddict.get(item)])
f.writelines("--------------------\n\n")
f.close()
f = open(str(self.name)+"_screen.log", "r")
mylog = f.read()
print mylog
print 'find the log file at \n' + str(os.getcwd()) + "/" + str(self.name)+"_screen.log"
else:
f = open(str(self.name)+".log", "w")
for item in L[-n:]:
f.writelines("# " + str(item) + "\n")
f.writelines(["%s\n" % item for item in self.commanddict.get(item)])
f.writelines("--------------------\n\n")
f.close()
f = open(str(self.name)+".log", "r")
mylog = f.read()
print mylog

def cmdcounter(self):
"""how many commands are executed in a given screen"""
print len(self.commanddict)

def _screen_commands(self, *commands):
"""allow to insert generic screen specific commands
a glossary of the existing screen command in `man screen`"""
self._check_exists()
for command in commands:
system('screen -x ' + self.name + ' -X ' + command)
sleep(0.02)

system('screen -x ' + self.name + ' -X ' + command)
#~ print(cox.getstatusoutput('screen -x ' + self.name + ' -X ' + command))
sleep(0.02)

def _check_exists(self, message="Error code: 404"):
"""check whereas the screen exist. if not, raise an exception"""
Expand All @@ -162,3 +247,144 @@ def _delayed_detach(self):

def __repr__(self):
return "<%s '%s'>" % (self.__class__.__name__, self.name)


import paramiko
import os, shutil


class tunel(object):
def __init__(self, server, username, password='empty', key='empty', rsa =False):
self.server = server
self.username = username
self.password = password
self.rsa = rsa
self.key = key

def execute(self,cmd):
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if self.password != 'empty' and self.rsa == True:
raise Exception("!! either password or rsa - not both")
if self.password == 'empty' and self.rsa == False and self.key == 'empty':
raise Exception("how should I access without rsa/password")
if self.password != 'empty':
self.ssh.connect(self.server, username=self.username, password=self.password)
if self.key != 'empty':
if os.path.isfile(self.key):
self.ssh.connect(self.server, username=self.username, key_filename=self.key)
else:
raise Exception('I cant see the key file at\n'+self.key)
if self.rsa == True:
rsafile = os.path.expanduser('~/.ssh/id_rsa')
print 'rsa file vojod dare'
if os.path.isfile(rsafile):
try:
mykey = paramiko.RSAKey.from_private_key_file(rsafile)
except:
raise Exception('If your rsa is encrypted - please make connection using the actual password')
self.ssh.connect(self.server, username=self.username, pkey=mykey)
else:
raise Exception('Cant see id_rsa at\n' + str(rsafile))

stdin, stdout, stderr = self.ssh.exec_command(cmd)
stderr.flush()
cmd_error = stderr.readlines()
cmd_out = stdout.readlines()
#~ cmd_in = stdin.readlines()
#~ if cmd_error:
#~ print 'Error :\n'
#~ print cmd_error[0]
#~ else:
#~ print 'the code is executed\n'
#~ print stdout.readlines()
self.ssh.close()
return cmd_out, cmd_error

def upload(self, filename, remotepath, localpath):
ssh = paramiko.SSHClient()
#~ ssh.load_host_keys(os.path.expanduser(os.path.join("~", ".ssh", "known_hosts")))
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # take care of unknown hosts

if self.password == 'empty' and self.rsa == False and self.key == 'empty':
raise Exception("how should I login ? \n provide me with password/key/rsa")

elif self.password != 'empty' and self.rsa == False and self.key == 'empty':
ssh.connect(self.server, username=self.username, password=self.password)

elif self.password == 'empty' and self.rsa == False and self.key != 'empty':
ssh.connect(self.server, username=self.username, key_filename=self.key)

elif self.rsa == True:
rsafile = os.path.expanduser('~/.ssh/id_rsa')
if os.path.isfile(rsafile):
try:
mykey = paramiko.RSAKey.from_private_key_file(rsafile)
except:
raise Exception('If your rsa is encrypted - please make connection using the actual password')
self.ssh.connect(self.server, username=self.username, pkey=mykey)
else:
raise Exception('Cant see id_rsa at\n' + str(rsafile))

else:
raise Exception('unknown error in ssh communication')

sftp = ssh.open_sftp()
try:
sftp.put(localpath+filename, remotepath+filename)
except OSError:
print 'Upload Error; possibilities\n file/localpath/remotepath does not exist\n'
sftp.close()
ssh.close()
tmp = tunel(self.server, self.username, self.password)
[cmdout,cmderror] = tmp.execute('ls '+ remotepath+filename)
if cmderror:
print 'ERORR in uploading : '+ str(cmderror[0])
if cmdout:
print 'the file is there ! : ' + str(cmdout[0])

def kill_screen_ssh(conn, name):
'''kill a screen session by its name'''
cmd = 'screen -S '+name+' -X quit'
cmd_out, cmd_err = conn.execute(cmd)
return cmd_out, cmd_err

def execute_command_screen_ssh(conn, command, name=''):
'''execute cmd in a server under a new screen session'''
if len(name) == 0:
name = command.split(' ')[0][0:5]

screensonssh = list_screens_ssh(conn)
if name in screensonssh:
name = name+"_"+str(screensonssh.count(name)+1)

cmd = 'screen -m -d -S '+name+' '+command
cmd_out, cmd_err = conn.execute(cmd)
return cmd_out, cmd_err


def list_screens_ssh(conn):
"""List all the existing screens and build a Screen instance for each
"""
a, b = conn.execute("screen -ls | grep -P '\t'")
return [".".join(l.split(".")[1:]).split("\t")[0] for l in a]

def kill_screen_ssh(conn, name):
'''kill a screen session by its name'''
cmd = 'screen -S '+name+' -X quit'
cmd_out, cmd_err = conn.execute(cmd)
return cmd_out, cmd_err

def execute_command_screen_ssh(conn, command, name=''):
'''execute cmd in a server under a new screen session'''
if len(name) == 0:
name = command.split(' ')[0][0:5]

screensonssh = list_screens_ssh(conn)
if name in screensonssh:
name = name+"_"+str(screensonssh.count(name)+1)

cmd = 'screen -m -d -S '+name+' '+command
cmd_out, cmd_err = conn.execute(cmd)
return cmd_out, cmd_err