You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by jp...@apache.org on 2015/01/28 05:36:58 UTC

[20/50] trafficserver-qa git commit: Replaced sleeps with port polling.

Replaced sleeps with port polling.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver-qa/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver-qa/commit/a502d378
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver-qa/tree/a502d378
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver-qa/diff/a502d378

Branch: refs/heads/master
Commit: a502d378bd9eca7df0c9b1107d5d789bdbd5da81
Parents: 32fc37c
Author: Joshua Blatt <bl...@yahoo-inc.com>
Authored: Mon Dec 29 11:04:51 2014 -0800
Committer: Joshua Blatt <bl...@yahoo-inc.com>
Committed: Mon Dec 29 11:04:51 2014 -0800

----------------------------------------------------------------------
 tests/hello_world_test.py |  10 ++--
 tsqa/environment.py       |  79 +++++++++++++++------------
 tsqa/test_cases.py        |   8 ++-
 tsqa/utils.py             | 118 ++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 175 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver-qa/blob/a502d378/tests/hello_world_test.py
----------------------------------------------------------------------
diff --git a/tests/hello_world_test.py b/tests/hello_world_test.py
index 2d918ea..302e4b2 100644
--- a/tests/hello_world_test.py
+++ b/tests/hello_world_test.py
@@ -22,22 +22,24 @@ class TestEnvironmentCase(tsqa.test_cases.EnvironmentCase):
 
     # TODO: actually test this, this is currently terrible ;)
     def test_daemon(self):
-        self.environment.start()  # start ATS
-        time.sleep(2)
+        self.log.info('Begin test_daemon')
         assert self.environment.cop.pid > 0
         assert self.environment.cop.returncode is None
-        self.environment.stop()
+        self.log.info('End test_daemon')
 
 class TestDynamicHTTPEndpointCase(tsqa.test_cases.DynamicHTTPEndpointCase):
     def test_base(self):
+        self.log.info('Begin test_base')
         ret = requests.get(self.endpoint_url('/footest'))
         self.assertEqual(ret.status_code, 404)
+        self.log.info('End test_base')
 
     def test_endpoint_url(self):
+        self.log.info('Begin test_endpoint_url')
         assert self.endpoint_url() == 'http://127.0.0.1:{0}'.format(self.http_endpoint.address[1])
 
         assert self.endpoint_url('/foo') == 'http://127.0.0.1:{0}/foo'.format(self.http_endpoint.address[1])
-
+        self.log.info('End test_endpoint_url')
 
 if __name__ == "__main__":
     unittest.main()

http://git-wip-us.apache.org/repos/asf/trafficserver-qa/blob/a502d378/tsqa/environment.py
----------------------------------------------------------------------
diff --git a/tsqa/environment.py b/tsqa/environment.py
index cd5b747..03c1396 100644
--- a/tsqa/environment.py
+++ b/tsqa/environment.py
@@ -3,8 +3,9 @@ import tempfile
 import os
 import copy
 import shutil
-import json
-
+import tsqa.utils
+import logging
+import sys
 
 import tsqa.configs
 import tsqa.utils
@@ -28,7 +29,7 @@ class EnvironmentFactory(object):
 
         # TODO: ensure this directory exists? (and is git?)
         self.source_dir = source_dir
-
+        self.log = tsqa.utils.get_logger()
         self.env_cache_dir = env_cache_dir  # base directory for environment caching
 
         if default_configure is not None:
@@ -45,13 +46,20 @@ class EnvironmentFactory(object):
         '''
         Autoreconf to make the configure script
         '''
+
+        kwargs = {
+            'cwd': self.source_dir,
+            'env': self.default_env,
+            'stdout': subprocess.PIPE,
+            'stderr': subprocess.PIPE
+        }
+
+        if self.log.isEnabledFor(logging.DEBUG):
+            kwargs['stdout'] = sys.stdout.fileno()
+            kwargs['stderr'] = sys.stderr.fileno()
+
         # run autoreconf in source tree
-        tsqa.utils.run_sync_command(['autoreconf', '-if'],
-                                    cwd=self.source_dir,
-                                    env=self.default_env,
-                                    stdout=subprocess.PIPE,
-                                    stderr=subprocess.PIPE,
-                                    )
+        tsqa.utils.run_sync_command(['autoreconf', '-if'], **kwargs)
 
     @property
     def source_hash(self):
@@ -114,39 +122,34 @@ class EnvironmentFactory(object):
                 del env[blacklisted_key]
 
         key = self._get_key(configure, env)
-        # TODO: remove
-        print 'Key is:', key, 'args are:', configure, env
+        self.log.debug('Key is: %s, args are: %s %s' % (key, configure, env))
 
         # if we don't have it built already, lets build it
         if key not in self.environment_stash:
             self.autoreconf()
             builddir = tempfile.mkdtemp()
 
+            kwargs = {
+                'cwd': builddir,
+                'env': env,
+                'stdout': subprocess.PIPE,
+                'stderr': subprocess.PIPE
+            }
+
+            if self.log.isEnabledFor(logging.DEBUG):
+                kwargs['stdout'] = sys.stdout.fileno()
+                kwargs['stderr'] = sys.stderr.fileno()
+
             # configure
             args = [os.path.join(self.source_dir, 'configure'), '--prefix=/'] + tsqa.utils.configure_list(configure)
-            tsqa.utils.run_sync_command(args,
-                                        cwd=builddir,
-                                        env=env,
-                                        stdout=subprocess.PIPE,
-                                        stderr=subprocess.PIPE,
-                                        )
+            tsqa.utils.run_sync_command(args, **kwargs)
 
             # make
-            tsqa.utils.run_sync_command(['make', '-j'],
-                                        cwd=builddir,
-                                        env=env,
-                                        stdout=subprocess.PIPE,
-                                        stderr=subprocess.PIPE,
-                                        )
+            tsqa.utils.run_sync_command(['make', '-j'], **kwargs)
             installdir = tempfile.mkdtemp(dir=self.env_cache_dir)
 
             # make install
-            tsqa.utils.run_sync_command(['make', 'install', 'DESTDIR={0}'.format(installdir)],
-                                        cwd=builddir,
-                                        env=env,
-                                        stdout=subprocess.PIPE,
-                                        stderr=subprocess.PIPE,
-                                        )
+            tsqa.utils.run_sync_command(['make', 'install', 'DESTDIR={0}'.format(installdir)], **kwargs)
 
             shutil.rmtree(builddir)  # delete builddir, not useful after install
             # stash the env
@@ -184,6 +187,7 @@ class Layout:
 
     def __init__(self, prefix):
         self.prefix = prefix
+        self.log = tsqa.utils.get_logger()
 
     def __getattr__(self, name):
         # Raise an error for suffixes we don't know about
@@ -237,8 +241,8 @@ class Environment:
                                         stdout=logfile,
                                         stderr=logfile,
                                         )
-            import time
-            time.sleep(3)  # TODO: wait or the process to listen?
+            tsqa.utils.poll_interfaces(self.hostports)
+
             # TODO: better checking...
             self.cop.poll()
             if self.cop.returncode is not None:
@@ -248,7 +252,9 @@ class Environment:
         """
         Initialize a new Environment.
         """
+        self.log = tsqa.utils.get_logger()
         self.cop = None
+        self.hostports = []
         if layout:
             self.layout = layout
         else:
@@ -292,6 +298,10 @@ class Environment:
             else:
                 os.chmod(dirname, 0777)
 
+        http_server_port = tsqa.utils.bind_unused_port()[1]
+        manager_mgmt_port = tsqa.utils.bind_unused_port()[1]
+        self.hostports = [('127.0.0.1', http_server_port), ('127.0.0.1', manager_mgmt_port)]
+
         # overwrite a few things that need to be changed to have a unique env
         records = tsqa.configs.RecordsConfig(os.path.join(self.layout.sysconfdir, 'records.config'))
         records['CONFIG'].update({
@@ -301,8 +311,8 @@ class Environment:
             'proxy.config.bin_path': self.layout.bindir,
             'proxy.config.log.logfile_dir': self.layout.logdir,
             'proxy.config.local_state_dir': self.layout.runtimedir,
-            'proxy.config.http.server_ports': str(tsqa.utils.bind_unused_port()[1]),  # your own listen port
-            'proxy.config.process_manager.mgmt_port': tsqa.utils.bind_unused_port()[1],  # your own listen port
+            'proxy.config.http.server_ports': str(http_server_port),  # your own listen port
+            'proxy.config.process_manager.mgmt_port': manager_mgmt_port,  # your own listen port
         })
         records.write()
 
@@ -319,12 +329,15 @@ class Environment:
         self.layout = Layout(None)
 
     def start(self):
+        self.log.debug("Starting traffic cop")
         assert(os.path.isfile(os.path.join(self.layout.sysconfdir, 'records.config')))
         self.__exec_cop()
+        self.log.debug("Started traffic cop: %s", self.cop)
 
     # TODO: more graceful stop?
     # TODO: raise exception when you call stop when its not started?
     def stop(self):
+        self.log.debug("Killing traffic cop: %s", self.cop)
         if self.cop is not None:
             self.cop.kill()
             self.cop.terminate()  # TODO: remove?? or wait...

http://git-wip-us.apache.org/repos/asf/trafficserver-qa/blob/a502d378/tsqa/test_cases.py
----------------------------------------------------------------------
diff --git a/tsqa/test_cases.py b/tsqa/test_cases.py
index d7ba75a..cb1ef55 100644
--- a/tsqa/test_cases.py
+++ b/tsqa/test_cases.py
@@ -10,7 +10,6 @@ unittest = tsqa.utils.import_unittest()
 
 import os
 
-
 # Example environment case
 class EnvironmentCase(unittest.TestCase):
     '''
@@ -25,6 +24,9 @@ class EnvironmentCase(unittest.TestCase):
         # call parent constructor
         super(EnvironmentCase, cls).setUpClass()
 
+        # get a logger
+        cls.log = tsqa.utils.get_logger()
+
         # get an environment
         cls.environment = cls.getEnv()
 
@@ -68,6 +70,7 @@ class EnvironmentCase(unittest.TestCase):
         if cls.environment.cop is not None and not cls.environment.running:
             raise Exception('ATS died during the test run')
         # stop ATS
+
         cls.environment.stop()
 
         # call parent destructor
@@ -89,6 +92,9 @@ class DynamicHTTPEndpointCase(unittest.TestCase):
     '''
     @classmethod
     def setUpClass(cls, port=0):
+        # get a logger
+        cls.log = tsqa.utils.get_logger()
+
         cls.http_endpoint = tsqa.endpoint.DynamicHTTPEndpoint(port=port)
         cls.http_endpoint.start()
 

http://git-wip-us.apache.org/repos/asf/trafficserver-qa/blob/a502d378/tsqa/utils.py
----------------------------------------------------------------------
diff --git a/tsqa/utils.py b/tsqa/utils.py
index f58a2bd..d522f66 100644
--- a/tsqa/utils.py
+++ b/tsqa/utils.py
@@ -4,6 +4,117 @@ import json
 import sys
 import subprocess
 import socket
+import logging
+import time
+
+tsqa_logger = None
+tsqa_log_level = logging.INFO
+tsqa_log_levels = {
+    'CRITICAL': logging.CRITICAL,
+    'ERROR': logging.ERROR,
+    'WARN': logging.WARNING,
+    'WARNING': logging.WARNING,
+    'INFO': logging.INFO,
+    'DEBUG': logging.DEBUG,
+    'NOTSET': logging.NOTSET
+}
+
+def set_log_level(log_level):
+    '''
+    Set the global log level (override with env var TSQA_LOG_LEVEL).  Must be called
+    before first get_logger()
+    '''
+
+    global tsqa_log_level
+    tsqa_log_level = log_level
+
+def get_log_level():
+    '''
+    Get the global log level (override with env var TSQA_LOG_LEVEL).
+    '''
+
+    if os.environ.has_key('TSQA_LOG_LEVEL'):
+        log_level = os.environ['TSQA_LOG_LEVEL'].upper()
+
+        if tsqa_log_levels.has_key(log_level):
+            return tsqa_log_levels[log_level]
+
+    return tsqa_log_level
+
+def set_logger(logger):
+    '''
+    Set/replace the global logger
+    '''
+
+    global tsqa_logger
+    tsqa_logger = logger
+
+def get_logger():
+    '''
+    Get the global logger
+    '''
+
+    global tsqa_logger
+
+    if tsqa_logger:
+        return tsqa_logger
+
+    tsqa_logger = logging.getLogger()
+    tsqa_logger.setLevel(get_log_level())
+    handler = logging.StreamHandler()
+    handler.setFormatter(logging.Formatter("%(levelname)s %(asctime)-15s - %(message)s"))
+    tsqa_logger.addHandler(handler)
+
+    return tsqa_logger
+
+def poll_interfaces(hostports, **kwargs):
+    '''  Block until we can successfully connect to all ports or timeout
+
+    :param hostports:
+    :param kwargs: optional timeout_sec
+    '''
+
+    connect_timeout_sec = 1
+    poll_sleep_sec = 1
+
+    if kwargs.has_key('timeout_sec'):
+        timeout = time.time() + kwargs['timeout_sec']
+    else:
+        timeout = time.time() + 5
+
+    hostports = hostports[:] # don't modify the caller's hostports
+
+    while timeout > time.time():
+        for hostport in hostports[:]: # don't modify our hostports copy during iteration
+            hostname = hostport[0]
+            port = hostport[1]
+
+            if get_logger().isEnabledFor(logging.DEBUG):
+                get_logger().debug("Checking interface '%s:%d'", hostname, port)
+
+            # This supports IPv6
+
+            try:
+                s = socket.create_connection((hostname, port), timeout=connect_timeout_sec)
+                s.close()
+                hostports.remove(hostport)
+
+                if get_logger().isEnabledFor(logging.DEBUG):
+                    get_logger().debug("Interface '%s:%d' is up", hostname, port)
+            except:
+                pass
+
+        if not hostports:
+            break
+
+        time.sleep(poll_sleep_sec)
+
+    if hostports:
+        raise Exception("Timeout waiting for interfaces: {0}".format(
+                        reduce(lambda x, y: str(x) + ',' + str(y), hostports)))
+
+    if get_logger().isEnabledFor(logging.DEBUG):
+        get_logger().debug("All interfaces are up")
 
 # TODO: test
 def import_unittest():
@@ -37,9 +148,12 @@ def run_sync_command(*args, **kwargs):
     p = subprocess.Popen(*args, **kwargs)
     stdout, stderr = p.communicate()
     if p.returncode != 0:
-        raise Exception('Error running: {0}\n{1}'.format(args[0], stderr))
-    return stdout, stderr
+        if stderr:
+            raise Exception('Error {0} running: {1}\n{2}'.format(p.returncode, args[0], stderr))
+        else:
+            raise Exception('Error {0} running: {1}'.format(p.returncode, args[0]))
 
+    return stdout, stderr
 
 def merge_dicts(*args):
     '''