Import all the stacktester stuff as-is (s/stacktester/kong/, though).
diff --git a/kong/common/__init__.py b/kong/common/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/kong/common/__init__.py
diff --git a/kong/common/http.py b/kong/common/http.py
new file mode 100644
index 0000000..47ac058
--- /dev/null
+++ b/kong/common/http.py
@@ -0,0 +1,57 @@
+from kong import exceptions
+
+import httplib2
+import os
+import time
+
+
+class Client(object):
+
+ USER_AGENT = 'python-nova_test_client'
+
+ def __init__(self, host='localhost', port=80, base_url=''):
+ #TODO: join these more robustly
+ self.base_url = "http://%s:%s/%s" % (host, port, base_url)
+
+ def poll_request(self, method, url, check_response, **kwargs):
+
+ timeout = kwargs.pop('timeout', 180)
+ interval = kwargs.pop('interval', 2)
+ # Start timestamp
+ start_ts = int(time.time())
+
+ while True:
+ resp, body = self.request(method, url, **kwargs)
+ if (check_response(resp, body)):
+ break
+ if (int(time.time()) - start_ts >= timeout):
+ raise exceptions.TimeoutException
+ time.sleep(interval)
+
+ def poll_request_status(self, method, url, status=200, **kwargs):
+
+ def check_response(resp, body):
+ return resp['status'] == str(status)
+
+ self.poll_request(method, url, check_response, **kwargs)
+
+
+ def request(self, method, url, **kwargs):
+ # Default to management_url, but can be overridden here
+ # (for auth requests)
+ base_url = kwargs.get('base_url', self.management_url)
+
+ self.http_obj = httplib2.Http()
+
+ params = {}
+ params['headers'] = {'User-Agent': self.USER_AGENT}
+ params['headers'].update(kwargs.get('headers', {}))
+ if 'Content-Type' not in params.get('headers',{}):
+ params['headers']['Content-Type'] = 'application/json'
+
+ if 'body' in kwargs:
+ params['body'] = kwargs.get('body')
+
+ req_url = os.path.join(base_url, url.strip('/'))
+ resp, body = self.http_obj.request(req_url, method, **params)
+ return resp, body
diff --git a/kong/common/ssh.py b/kong/common/ssh.py
new file mode 100644
index 0000000..f650e64
--- /dev/null
+++ b/kong/common/ssh.py
@@ -0,0 +1,79 @@
+import time
+import socket
+import warnings
+
+with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ import paramiko
+
+
+class Client(object):
+
+ def __init__(self, host, username, password, timeout=300):
+ self.host = host
+ self.username = username
+ self.password = password
+ self.timeout = timeout
+
+ def _get_ssh_connection(self):
+ """Returns an ssh connection to the specified host"""
+ _timeout = True
+ ssh = paramiko.SSHClient()
+ ssh.set_missing_host_key_policy(
+ paramiko.AutoAddPolicy())
+ _start_time = time.time()
+
+ while not self._is_timed_out(self.timeout, _start_time):
+ try:
+ ssh.connect(self.host, username=self.username,
+ password=self.password, look_for_keys=False,
+ timeout=self.timeout)
+ _timeout = False
+ break
+ except socket.error:
+ continue
+ except paramiko.AuthenticationException:
+ time.sleep(15)
+ continue
+ if _timeout:
+ raise socket.error("SSH connect timed out")
+ return ssh
+
+ def _is_timed_out(self, timeout, start_time):
+ return (time.time() - timeout) > start_time
+
+ def connect_until_closed(self):
+ """Connect to the server and wait until connection is lost"""
+ try:
+ ssh = self._get_ssh_connection()
+ _transport = ssh.get_transport()
+ _start_time = time.time()
+ _timed_out = self._is_timed_out(self.timeout, _start_time)
+ while _transport.is_active() and not _timed_out:
+ time.sleep(5)
+ _timed_out = self._is_timed_out(self.timeout, _start_time)
+ ssh.close()
+ except (EOFError, paramiko.AuthenticationException, socket.error):
+ return
+
+ def exec_command(self, cmd):
+ """Execute the specified command on the server.
+
+ :returns: data read from standard output of the command
+
+ """
+ ssh = self._get_ssh_connection()
+ stdin, stdout, stderr = ssh.exec_command(cmd)
+ output = stdout.read()
+ ssh.close()
+ return output
+
+ def test_connection_auth(self):
+ """ Returns true if ssh can connect to server"""
+ try:
+ connection = self._get_ssh_connection()
+ connection.close()
+ except paramiko.AuthenticationException:
+ return False
+
+ return True