Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
  • 4 commits
  • 3 files changed
  • 0 commit comments
  • 2 contributors
Showing with 58 additions and 12 deletions.
  1. +2 −0  AUTHORS
  2. +55 −11 openssh_wrapper.py
  3. +1 −1  setup.py
View
2  AUTHORS
@@ -0,0 +1,2 @@
+Roman Imankulov <roman@netangels.ru>
+Jan-Oliver Kaiser <mail@janno-kaiser.de>
View
66 openssh_wrapper.py
@@ -1,12 +1,19 @@
# -*- coding: utf-8 -*-
+"""
+This is a wrapper around the openssh binaries ssh and scp.
+"""
import re, os, subprocess, signal
__all__ = 'SSHConnection SSHResult SSHError'.split()
class SSHConnection(object):
+ """
+ This class holds all values needed to connect to a host via ssh.
+ It provides methods for command execution and file transfer via scp.
+ """
- def __init__(self, server, login=None, configfile=None, identity_file=None,
- ssh_agent_socket=None, timeout=60):
+ def __init__(self, server, port=None, login=None, configfile=None,
+ identity_file=None, ssh_agent_socket=None, timeout=60):
"""
Create new object to establish SSH connection to remote
servers.
@@ -28,6 +35,7 @@ def __init__(self, server, login=None, configfile=None, identity_file=None,
command.
"""
self.server = str(server)
+ self.port = port
self.timeout = timeout
self.check_server(server)
if login:
@@ -50,11 +58,25 @@ def __init__(self, server, login=None, configfile=None, identity_file=None,
self.ssh_agent_socket = ssh_agent_socket
def check_server(self, server):
- if not re.compile(r'^[a-zA-Z0-9.\-]+$').match(server):
+ """
+ Check the server string for illegal characters.
+ Returns:
+ Nothing
+ Raises:
+ SSHError
+ """
+ if not re.compile(r'^[a-zA-Z0-9.\-_]+$').match(server):
raise SSHError('Server name contains illegal symbols')
def check_login(self, login):
- if not re.compile(r'^[a-zA-Z0-9.\-]+$').match(login):
+ """
+ Check the login string for illegal characters.
+ Returns:
+ Nothing
+ Raises:
+ SSHError
+ """
+ if not re.compile(r'^[a-zA-Z0-9.\-_]+$').match(login):
raise SSHError('User login contains illegal symbols')
def run(self, command, interpreter='/bin/bash', forward_ssh_agent=False):
@@ -75,16 +97,17 @@ def run(self, command, interpreter='/bin/bash', forward_ssh_agent=False):
stderr=subprocess.PIPE, env=self.get_env())
try:
signal.signal(signal.SIGALRM, _timeout_handler)
- except ValueError, e: #signal only works in main thread
+ except ValueError, _: #signal only works in main thread
pass
signal.alarm(self.timeout)
+ out = err = ""
try:
out, err = pipe.communicate(command)
- except IOError, e:
+ except IOError, exc:
# pipe.terminate() # only in python 2.6 allowed
os.kill(pipe.pid, signal.SIGTERM)
signal.alarm(0) # disable alarm
- raise SSHError(str(e))
+ raise SSHError(str(exc))
signal.alarm(0) # disable alarm
returncode = pipe.returncode
@@ -103,19 +126,22 @@ def scp(self, *filenames, **kwargs):
stderr=subprocess.PIPE, env=self.get_env())
signal.signal(signal.SIGALRM, _timeout_handler)
signal.alarm(self.timeout)
+ err = ""
try:
- out, err = pipe.communicate()
- except IOError, e:
+ _, err = pipe.communicate()
+ except IOError, exc:
# pipe.terminate() # only in python 2.6 allowed
os.kill(pipe.pid, signal.SIGTERM)
signal.alarm(0) # disable alarm
- raise SSHError(stderr=str(e))
+ raise SSHError(stderr=str(exc))
signal.alarm(0) # disable alarm
returncode = pipe.returncode
if returncode != 0: # ssh client error
raise SSHError(err.strip())
def ssh_command(self, interpreter, forward_ssh_agent):
+ """ Build the command string to connect to the server
+ and start the given interpreter. """
interpreter = str(interpreter)
cmd = ['/usr/bin/ssh', ]
if self.login:
@@ -126,11 +152,16 @@ def ssh_command(self, interpreter, forward_ssh_agent):
cmd += [ '-i', self.identity_file ]
if forward_ssh_agent:
cmd.append('-A')
+ if self.port:
+ cmd += [ '-p', str(self.port) ]
cmd.append(self.server)
cmd.append(interpreter)
return cmd
def scp_command(self, *filenames, **kwargs):
+ """ Build the command string to transfer the files
+ identifiend by the given filenames.
+ Include target(s) if specified. """
cmd = ['/usr/bin/scp', '-q', '-r']
filenames = map(str, filenames)
if self.login:
@@ -141,6 +172,8 @@ def scp_command(self, *filenames, **kwargs):
cmd += [ '-F', self.configfile ]
if self.identity_file:
cmd += [ '-i', self.identity_file ]
+ if self.port:
+ cmd += [ '-P', self.port ]
if len(filenames) < 1:
raise ValueError('You should name at least one file to copy')
@@ -159,6 +192,8 @@ def scp_command(self, *filenames, **kwargs):
return cmd
def get_env(self):
+ """ Retrieve environment variables and replace SSH_AUTH_SOCK
+ if ssh_agent_socket was specified on object creation. """
env = os.environ.copy()
if self.ssh_agent_socket:
env['SSH_AUTH_SOCK'] = self.ssh_agent_socket
@@ -166,6 +201,7 @@ def get_env(self):
def _timeout_handler(signum, frame):
+ """ This function is called when ssh takes too long to connect. """
raise IOError, 'SSH connect timeout'
@@ -174,15 +210,19 @@ class SSHResult(object):
``returncode`` fields """
def __init__(self, command, stdout, stderr, returncode):
+ """ Create a new object to hold output and a return code
+ to the given command. """
self.command = command
self.stdout = stdout
self.stderr = stderr
self.returncode = returncode
def __str__(self):
+ """ Get acscii representation from unicode representation. """
return unicode(self).decode('utf-8', 'ignore')
def __unicode__(self):
+ """ Build simple unicode representation from all member values. """
ret = []
ret.append(u'command: %s' % unicode(self.command))
ret.append(u'stdout: %s' % unicode(self.stdout))
@@ -190,4 +230,8 @@ def __unicode__(self):
ret.append(u'returncode: %s' % unicode(self.returncode))
return u'\n'.join(ret)
-class SSHError(Exception): pass
+class SSHError(Exception):
+ """
+ This exception is used for all errors raised by this module.
+ """
+ pass
View
2  setup.py
@@ -10,7 +10,7 @@ def read(fname):
setup(
name='openssh-wrapper',
- version='0.2',
+ version='0.2.1',
description='OpenSSH python wrapper',
author='NetAngels team',
author_email='info@netangels.ru',

No commit comments for this range

Something went wrong with that request. Please try again.