media/webrtc/trunk/build/android/pylib/ports.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/media/webrtc/trunk/build/android/pylib/ports.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,167 @@
     1.4 +# Copyright (c) 2012 The Chromium Authors. All rights reserved.
     1.5 +# Use of this source code is governed by a BSD-style license that can be
     1.6 +# found in the LICENSE file.
     1.7 +
     1.8 +"""Functions that deals with local and device ports."""
     1.9 +
    1.10 +import contextlib
    1.11 +import fcntl
    1.12 +import httplib
    1.13 +import logging
    1.14 +import os
    1.15 +import re
    1.16 +import socket
    1.17 +import traceback
    1.18 +
    1.19 +import cmd_helper
    1.20 +import constants
    1.21 +
    1.22 +
    1.23 +#The following two methods are used to allocate the port source for various
    1.24 +# types of test servers. Because some net relates tests can be run on shards
    1.25 +# at same time, it's important to have a mechanism to allocate the port process
    1.26 +# safe. In here, we implement the safe port allocation by leveraging flock.
    1.27 +def ResetTestServerPortAllocation():
    1.28 +  """Reset the port allocation to start from TEST_SERVER_PORT_FIRST.
    1.29 +
    1.30 +  Returns:
    1.31 +    Returns True if reset successes. Otherwise returns False.
    1.32 +  """
    1.33 +  try:
    1.34 +    with open(constants.TEST_SERVER_PORT_FILE, 'w') as fp:
    1.35 +      fp.write('%d' % constants.TEST_SERVER_PORT_FIRST)
    1.36 +    if os.path.exists(constants.TEST_SERVER_PORT_LOCKFILE):
    1.37 +      os.unlink(constants.TEST_SERVER_PORT_LOCKFILE)
    1.38 +    return True
    1.39 +  except Exception as e:
    1.40 +    logging.error(e)
    1.41 +  return False
    1.42 +
    1.43 +
    1.44 +def AllocateTestServerPort():
    1.45 +  """Allocate a port incrementally.
    1.46 +
    1.47 +  Returns:
    1.48 +    Returns a valid port which should be in between TEST_SERVER_PORT_FIRST and
    1.49 +    TEST_SERVER_PORT_LAST. Returning 0 means no more valid port can be used.
    1.50 +  """
    1.51 +  port = 0
    1.52 +  ports_tried = []
    1.53 +  try:
    1.54 +    fp_lock = open(constants.TEST_SERVER_PORT_LOCKFILE, 'w')
    1.55 +    fcntl.flock(fp_lock, fcntl.LOCK_EX)
    1.56 +    # Get current valid port and calculate next valid port.
    1.57 +    assert os.path.exists(constants.TEST_SERVER_PORT_FILE)
    1.58 +    with open(constants.TEST_SERVER_PORT_FILE, 'r+') as fp:
    1.59 +      port = int(fp.read())
    1.60 +      ports_tried.append(port)
    1.61 +      while IsHostPortUsed(port):
    1.62 +        port += 1
    1.63 +        ports_tried.append(port)
    1.64 +      if (port > constants.TEST_SERVER_PORT_LAST or
    1.65 +          port < constants.TEST_SERVER_PORT_FIRST):
    1.66 +        port = 0
    1.67 +      else:
    1.68 +        fp.seek(0, os.SEEK_SET)
    1.69 +        fp.write('%d' % (port + 1))
    1.70 +  except Exception as e:
    1.71 +    logging.info(e)
    1.72 +  finally:
    1.73 +    if fp_lock:
    1.74 +      fcntl.flock(fp_lock, fcntl.LOCK_UN)
    1.75 +      fp_lock.close()
    1.76 +  if port:
    1.77 +    logging.info('Allocate port %d for test server.', port)
    1.78 +  else:
    1.79 +    logging.error('Could not allocate port for test server. '
    1.80 +                  'List of ports tried: %s', str(ports_tried))
    1.81 +  return port
    1.82 +
    1.83 +
    1.84 +def IsHostPortUsed(host_port):
    1.85 +  """Checks whether the specified host port is used or not.
    1.86 +
    1.87 +  Uses -n -P to inhibit the conversion of host/port numbers to host/port names.
    1.88 +
    1.89 +  Args:
    1.90 +    host_port: Port on host we want to check.
    1.91 +
    1.92 +  Returns:
    1.93 +    True if the port on host is already used, otherwise returns False.
    1.94 +  """
    1.95 +  port_info = '(127\.0\.0\.1)|(localhost)\:%d' % host_port
    1.96 +  # TODO(jnd): Find a better way to filter the port.
    1.97 +  re_port = re.compile(port_info, re.MULTILINE)
    1.98 +  if re_port.findall(cmd_helper.GetCmdOutput(['lsof', '-nPi:%d' % host_port])):
    1.99 +    return True
   1.100 +  return False
   1.101 +
   1.102 +
   1.103 +def IsDevicePortUsed(adb, device_port, state=''):
   1.104 +  """Checks whether the specified device port is used or not.
   1.105 +
   1.106 +  Args:
   1.107 +    adb: Instance of AndroidCommands for talking to the device.
   1.108 +    device_port: Port on device we want to check.
   1.109 +    state: String of the specified state. Default is empty string, which
   1.110 +           means any state.
   1.111 +
   1.112 +  Returns:
   1.113 +    True if the port on device is already used, otherwise returns False.
   1.114 +  """
   1.115 +  base_url = '127.0.0.1:%d' % device_port
   1.116 +  netstat_results = adb.RunShellCommand('netstat', log_result=False)
   1.117 +  for single_connect in netstat_results:
   1.118 +    # Column 3 is the local address which we want to check with.
   1.119 +    connect_results = single_connect.split()
   1.120 +    is_state_match = connect_results[5] == state if state else True
   1.121 +    if connect_results[3] == base_url and is_state_match:
   1.122 +      return True
   1.123 +  return False
   1.124 +
   1.125 +
   1.126 +def IsHttpServerConnectable(host, port, tries=3, command='GET', path='/',
   1.127 +                            expected_read='', timeout=2):
   1.128 +  """Checks whether the specified http server is ready to serve request or not.
   1.129 +
   1.130 +  Args:
   1.131 +    host: Host name of the HTTP server.
   1.132 +    port: Port number of the HTTP server.
   1.133 +    tries: How many times we want to test the connection. The default value is
   1.134 +           3.
   1.135 +    command: The http command we use to connect to HTTP server. The default
   1.136 +             command is 'GET'.
   1.137 +    path: The path we use when connecting to HTTP server. The default path is
   1.138 +          '/'.
   1.139 +    expected_read: The content we expect to read from the response. The default
   1.140 +                   value is ''.
   1.141 +    timeout: Timeout (in seconds) for each http connection. The default is 2s.
   1.142 +
   1.143 +  Returns:
   1.144 +    Tuple of (connect status, client error). connect status is a boolean value
   1.145 +    to indicate whether the server is connectable. client_error is the error
   1.146 +    message the server returns when connect status is false.
   1.147 +  """
   1.148 +  assert tries >= 1
   1.149 +  for i in xrange(0, tries):
   1.150 +    client_error = None
   1.151 +    try:
   1.152 +      with contextlib.closing(httplib.HTTPConnection(
   1.153 +          host, port, timeout=timeout)) as http:
   1.154 +        # Output some debug information when we have tried more than 2 times.
   1.155 +        http.set_debuglevel(i >= 2)
   1.156 +        http.request(command, path)
   1.157 +        r = http.getresponse()
   1.158 +        content = r.read()
   1.159 +        if r.status == 200 and r.reason == 'OK' and content == expected_read:
   1.160 +          return (True, '')
   1.161 +        client_error = ('Bad response: %s %s version %s\n  ' %
   1.162 +                        (r.status, r.reason, r.version) +
   1.163 +                        '\n  '.join([': '.join(h) for h in r.getheaders()]))
   1.164 +    except (httplib.HTTPException, socket.error) as e:
   1.165 +      # Probably too quick connecting: try again.
   1.166 +      exception_error_msgs = traceback.format_exception_only(type(e), e)
   1.167 +      if exception_error_msgs:
   1.168 +        client_error = ''.join(exception_error_msgs)
   1.169 +  # Only returns last client_error.
   1.170 +  return (False, client_error or 'Timeout')

mercurial