summaryrefslogtreecommitdiff
path: root/pygerrit/ssh.py
diff options
context:
space:
mode:
authorDavid Pursehouse <david.pursehouse@sonymobile.com>2013-09-13 19:03:24 +0900
committerDavid Pursehouse <david.pursehouse@sonymobile.com>2013-09-13 19:03:37 +0900
commit87b68fc2db307fe4e294abd913bcee80ecb9cb7f (patch)
tree70f1f534633cb128c780cc1b29ef685fe255798e /pygerrit/ssh.py
parent8571778ed4ebd1ebacba2334d8b6b19fbcd5a2b9 (diff)
parentae41ccac316c6bd45ec4af0146219212bbb08c87 (diff)
downloadpygerrit-87b68fc2db307fe4e294abd913bcee80ecb9cb7f.tar.gz
Merge branch 'master' into internal
* master: Bump version to 0.1.1 Add changelog Fix #10: Allow to manually specify ssh username and port Completely refactor the stream event handling Add missing __repr__ methods on ErrorEvent and UnhandledEvent Fix initialisation of error event Fix #11: correct handling of `identityfile` in the ssh config Allow example script to continue if errors are received Fix #9: Add a bit more detail in the documentation Fix #8: Support the "topic-changed" stream event Fix #7: Support the "reviewer-added" stream event Fix #6: Support the "merge-failed" stream event Fix #5: Move json parsing and error handling into the event factory Improved logging in the example script Fix #3: Don't treat unhandled event types as errors Fix #4: Keep event's raw json data in the event object Add __repr__ methods on event and model classes Remove redundant `exec_command` method Fix #2: Establish SSH connection in a thread-safe way Fix #1: Use select.select() instead of select.poll() Change-Id: Ib91384b16acca30bfc5aadcdc1131c8bdecef583
Diffstat (limited to 'pygerrit/ssh.py')
-rw-r--r--pygerrit/ssh.py79
1 files changed, 49 insertions, 30 deletions
diff --git a/pygerrit/ssh.py b/pygerrit/ssh.py
index 3e2add1..e779715 100644
--- a/pygerrit/ssh.py
+++ b/pygerrit/ssh.py
@@ -25,6 +25,7 @@
from os.path import abspath, expanduser, isfile
import re
import socket
+from threading import Event, Lock
from .error import GerritError
@@ -64,18 +65,19 @@ class GerritSSHClient(SSHClient):
""" Gerrit SSH Client, wrapping the paramiko SSH Client. """
- def __init__(self, hostname):
+ def __init__(self, hostname, username=None, port=None):
""" Initialise and connect to SSH. """
super(GerritSSHClient, self).__init__()
self.remote_version = None
self.hostname = hostname
- self.connected = False
-
- def _connect(self):
- """ Connect to the remote if not already connected. """
- if self.connected:
- return
- self.load_system_host_keys()
+ self.username = username
+ self.key_filename = None
+ self.port = port
+ self.connected = Event()
+ self.lock = Lock()
+
+ def _configure(self):
+ """ Configure the ssh parameters from the config file. """
configfile = expanduser("~/.ssh/config")
if not isfile(configfile):
raise GerritError("ssh config file '%s' does not exist" %
@@ -88,22 +90,29 @@ class GerritSSHClient(SSHClient):
raise GerritError("No ssh config for host %s" % self.hostname)
if not 'hostname' in data or not 'port' in data or not 'user' in data:
raise GerritError("Missing configuration data in %s" % configfile)
- key_filename = None
+ self.hostname = data['hostname']
+ self.username = data['user']
if 'identityfile' in data:
- key_filename = abspath(expanduser(data['identityfile']))
+ key_filename = abspath(expanduser(data['identityfile'][0]))
if not isfile(key_filename):
raise GerritError("Identity file '%s' does not exist" %
key_filename)
+ self.key_filename = key_filename
try:
- port = int(data['port'])
+ self.port = int(data['port'])
except ValueError:
raise GerritError("Invalid port: %s" % data['port'])
+
+ def _do_connect(self):
+ """ Connect to the remote. """
+ self.load_system_host_keys()
+ if self.username is None or self.port is None:
+ self._configure()
try:
- self.connect(hostname=data['hostname'],
- port=port,
- username=data['user'],
- key_filename=key_filename)
- self.connected = True
+ self.connect(hostname=self.hostname,
+ port=self.port,
+ username=self.username,
+ key_filename=self.key_filename)
except socket.error as e:
raise GerritError("Failed to connect to server: %s" % e)
@@ -114,17 +123,20 @@ class GerritSSHClient(SSHClient):
except AttributeError:
self.remote_version = None
- def exec_command(self, command, bufsize=1, timeout=None, get_pty=False):
- """ Execute the command.
-
- Make sure we're connected and then execute the command.
-
- Return a tuple of stdin, stdout, stderr.
-
- """
- self._connect()
- return super(GerritSSHClient, self).\
- exec_command(command, bufsize, timeout, get_pty)
+ def _connect(self):
+ """ Connect to the remote if not already connected. """
+ if not self.connected.is_set():
+ try:
+ self.lock.acquire()
+ # Another thread may have connected while we were
+ # waiting to acquire the lock
+ if not self.connected.is_set():
+ self._do_connect()
+ self.connected.set()
+ except GerritError:
+ raise
+ finally:
+ self.lock.release()
def get_remote_version(self):
""" Return the version of the remote Gerrit server. """
@@ -138,17 +150,24 @@ class GerritSSHClient(SSHClient):
def run_gerrit_command(self, command):
""" Run the given command.
- Run `command` and return a `GerritSSHCommandResult`.
+ Make sure we're connected to the remote server, and run `command`.
+
+ Return the results as a `GerritSSHCommandResult`.
- Raise `ValueError` if `command` is not a string.
+ Raise `ValueError` if `command` is not a string, or `GerritError` if
+ command execution fails.
"""
if not isinstance(command, basestring):
raise ValueError("command must be a string")
gerrit_command = "gerrit " + command
+ self._connect()
try:
- stdin, stdout, stderr = self.exec_command(gerrit_command)
+ stdin, stdout, stderr = self.exec_command(gerrit_command,
+ bufsize=1,
+ timeout=None,
+ get_pty=False)
except SSHException as err:
raise GerritError("Command execution error: %s" % err)
return GerritSSHCommandResult(command, stdin, stdout, stderr)