Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / chromite / lib / dev_server_wrapper.py
index e52359b..a6bb454 100644 (file)
@@ -5,20 +5,26 @@
 """Module containing methods and classes to interact with a devserver instance.
 """
 
+from __future__ import print_function
+
 import logging
 import multiprocessing
 import os
 import socket
 import tempfile
+import httplib
 import urllib2
 
-from chromite.buildbot import constants
+from chromite.cbuildbot import constants
 from chromite.lib import cros_build_lib
 from chromite.lib import osutils
 from chromite.lib import timeout_util
 from chromite.lib import remote_access
 
 
+DEFAULT_PORT = 8080
+
+
 def GenerateUpdateId(target, src, key, for_vm):
   """Returns a simple representation id of |target| and |src| paths.
 
@@ -49,6 +55,10 @@ class DevServerStartupError(DevServerException):
   """Thrown when the devserver fails to start up."""
 
 
+class DevServerStopError(DevServerException):
+  """Thrown when the devserver fails to stop."""
+
+
 class DevServerResponseError(DevServerException):
   """Thrown when the devserver responds with an error."""
 
@@ -79,7 +89,9 @@ class DevServerWrapper(multiprocessing.Process):
     """
     super(DevServerWrapper, self).__init__()
     self.devserver_bin = 'start_devserver'
-    self.port = 8080 if not port else port
+    # Set port if it is given. Otherwise, devserver will start at any
+    # available port.
+    self.port = None if not port else port
     self.src_image = src_image
     self.board = board
     self.tempdir = None
@@ -91,7 +103,8 @@ class DevServerWrapper(multiprocessing.Process):
           sudo_rm=True)
       self.log_dir = self.tempdir.tempdir
     self.static_dir = static_dir
-    self.log_filename = os.path.join(self.log_dir, 'dev_server.log')
+    self.log_file = os.path.join(self.log_dir, 'dev_server.log')
+    self.port_file = os.path.join(self.log_dir, 'dev_server.port')
     self._pid_file = self._GetPIDFilePath()
     self._pid = None
 
@@ -104,6 +117,10 @@ class DevServerWrapper(multiprocessing.Process):
     logging.info('Downloading %s to %s', url, dest)
     osutils.WriteFile(dest, DevServerWrapper.OpenURL(url), mode='wb')
 
+  def GetURL(self, sub_dir=None):
+    """Returns the URL of this devserver instance."""
+    return self.GetDevServerURL(port=self.port, sub_dir=sub_dir)
+
   @classmethod
   def GetDevServerURL(cls, ip=None, port=None, sub_dir=None):
     """Returns the dev server url.
@@ -115,7 +132,9 @@ class DevServerWrapper(multiprocessing.Process):
       sub_dir: The subdirectory of the devserver url.
     """
     ip = cros_build_lib.GetIPv4Address() if not ip else ip
-    port = 8080 if not port else port
+    # If port number is not given, assume 8080 for backward
+    # compatibility.
+    port = DEFAULT_PORT if not port else port
     url = 'http://%(ip)s:%(port)s' % {'ip': ip, 'port': str(port)}
     if sub_dir:
       url += '/' + sub_dir
@@ -128,8 +147,8 @@ class DevServerWrapper(multiprocessing.Process):
     logging.debug('Retrieving %s', url)
     try:
       res = urllib2.urlopen(url, timeout=timeout)
-    except urllib2.HTTPError as e:
-      logging.error('Devserver responsded with an error!')
+    except (urllib2.HTTPError, httplib.HTTPException) as e:
+      logging.error('Devserver responded with an error!')
       raise DevServerResponseError(e)
     except (urllib2.URLError, socket.timeout) as e:
       if not ignore_url_error:
@@ -167,6 +186,22 @@ class DevServerWrapper(multiprocessing.Process):
         cmd, enter_chroot=True, print_cmd=False, combine_stdout_stderr=True,
         redirect_stdout=True, redirect_stderr=True, cwd=constants.SOURCE_ROOT)
 
+  def _ReadPortNumber(self):
+    """Read port number from file."""
+    if not self.is_alive():
+      raise DevServerStartupError('Devserver terminated unexpectedly!')
+
+    try:
+      timeout_util.WaitForReturnTrue(os.path.exists,
+                                     func_args=[self.port_file],
+                                     timeout=self.DEV_SERVER_TIMEOUT,
+                                     period=5)
+    except timeout_util.TimeoutError:
+      self.terminate()
+      raise DevServerStartupError('Devserver portfile does not exist!')
+
+    self.port = int(osutils.ReadFile(self.port_file).strip())
+
   def IsReady(self):
     """Check if devserver is up and running."""
     if not self.is_alive():
@@ -192,10 +227,13 @@ class DevServerWrapper(multiprocessing.Process):
 
   def _WaitUntilStarted(self):
     """Wait until the devserver has started."""
+    if not self.port:
+      self._ReadPortNumber()
+
     try:
-      timeout_util.WaitForReturnValue([True], self.IsReady,
-                                      timeout=self.DEV_SERVER_TIMEOUT,
-                                      period=5)
+      timeout_util.WaitForReturnTrue(self.IsReady,
+                                     timeout=self.DEV_SERVER_TIMEOUT,
+                                     period=5)
     except timeout_util.TimeoutError:
       self.terminate()
       raise DevServerStartupError('Devserver did not start')
@@ -203,13 +241,17 @@ class DevServerWrapper(multiprocessing.Process):
   def run(self):
     """Kicks off devserver in a separate process and waits for it to finish."""
     # Truncate the log file if it already exists.
-    if os.path.exists(self.log_filename):
-      osutils.SafeUnlink(self.log_filename, sudo=True)
+    if os.path.exists(self.log_file):
+      osutils.SafeUnlink(self.log_file, sudo=True)
 
+    port = self.port if self.port else 0
     cmd = [self.devserver_bin,
-           '--port', str(self.port),
            '--pidfile', cros_build_lib.ToChrootPath(self._pid_file),
-           '--logfile', cros_build_lib.ToChrootPath(self.log_filename)]
+           '--logfile', cros_build_lib.ToChrootPath(self.log_file),
+           '--port=%d' % port]
+
+    if not self.port:
+      cmd.append('--portfile=%s' % cros_build_lib.ToChrootPath(self.port_file))
 
     if self.static_dir:
       cmd.append(
@@ -222,7 +264,7 @@ class DevServerWrapper(multiprocessing.Process):
       cmd.append('--board=%s' % self.board)
 
     result = self._RunCommand(
-        cmd, enter_chroot=True,
+        cmd, enter_chroot=True, chroot_args=['--no-ns-pid'],
         cwd=constants.SOURCE_ROOT, error_code_ok=True,
         redirect_stdout=True, combine_stdout_stderr=True)
     if result.returncode != 0:
@@ -251,7 +293,7 @@ class DevServerWrapper(multiprocessing.Process):
 
     logging.debug('Stopping devserver instance with pid %s', self._pid)
     if self.is_alive():
-      self._RunCommand(['kill', self._pid])
+      self._RunCommand(['kill', self._pid], error_code_ok=True)
     else:
       logging.debug('Devserver not running!')
       return
@@ -259,16 +301,22 @@ class DevServerWrapper(multiprocessing.Process):
     self.join(self.KILL_TIMEOUT)
     if self.is_alive():
       logging.warning('Devserver is unstoppable. Killing with SIGKILL')
-      self._RunCommand(['kill', '-9', self._pid])
+      try:
+        self._RunCommand(['kill', '-9', self._pid])
+      except cros_build_lib.RunCommandError as e:
+        raise DevServerStopError('Unable to stop devserver: %s' % e)
 
   def PrintLog(self):
     """Print devserver output to stdout."""
-    print self.TailLog(num_lines='+1')
+    print(self.TailLog(num_lines='+1'))
 
   def TailLog(self, num_lines=50):
     """Returns the most recent |num_lines| lines of the devserver log file."""
-    fname = self.log_filename
-    if os.path.exists(fname):
+    fname = self.log_file
+    # We use self._RunCommand here to check the existence of the log
+    # file, so it works for RemoteDevserverWrapper as well.
+    if self._RunCommand(
+        ['test', '-f', fname], error_code_ok=True).returncode == 0:
       result = self._RunCommand(['tail', '-n', str(num_lines), fname],
                                 capture_output=True)
       output = '--- Start output from %s ---' % fname
@@ -345,6 +393,27 @@ You can fix this with one of the following three options:
     kwargs.setdefault('debug_level', logging.DEBUG)
     return self.device.RunCommand(*args, **kwargs)
 
+  def _ReadPortNumber(self):
+    """Read port number from file."""
+    if not self.is_alive():
+      raise DevServerStartupError('Devserver terminated unexpectedly!')
+
+    def PortFileExists():
+      result = self._RunCommand(['test', '-f', self.port_file],
+                                error_code_ok=True)
+      return result.returncode == 0
+
+    try:
+      timeout_util.WaitForReturnTrue(PortFileExists,
+                                     timeout=self.DEV_SERVER_TIMEOUT,
+                                     period=5)
+    except timeout_util.TimeoutError:
+      self.terminate()
+      raise DevServerStartupError('Devserver portfile does not exist!')
+
+    self.port = int(self._RunCommand(
+        ['cat', self.port_file], capture_output=True).output.strip())
+
   def IsReady(self):
     """Returns True if devserver is ready to accept requests."""
     if not self.is_alive():
@@ -359,19 +428,21 @@ You can fix this with one of the following three options:
 
   def run(self):
     """Launches a devserver process on the device."""
-    self._RunCommand(['cat', '/dev/null', '>|', self.log_filename])
-    self._RunCommand(['pkill', os.path.basename(self.devserver_bin)],
-                     error_code_ok=True)
+    self._RunCommand(['cat', '/dev/null', '>|', self.log_file])
+
+    port = self.port if self.port else 0
     cmd = ['python', self.devserver_bin,
-           '--port=%s' % str(self.port),
-           '--logfile=%s' % self.log_filename,
-           '--pidfile', self._pid_file]
+           '--logfile=%s' % self.log_file,
+           '--pidfile', self._pid_file,
+           '--port=%d' % port,]
+
+    if not self.port:
+      cmd.append('--portfile=%s' % self.port_file)
 
     if self.static_dir:
       cmd.append('--static_dir=%s' % self.static_dir)
 
-    logging.info('Starting devserver %s', self.GetDevServerURL(ip=self.hostname,
-                                                               port=self.port))
+    logging.info('Starting devserver on %s', self.hostname)
     result = self._RunCommand(cmd, error_code_ok=True, redirect_stdout=True,
                               combine_stdout_stderr=True)
     if result.returncode != 0:
@@ -384,6 +455,11 @@ You can fix this with one of the following three options:
       if 'ImportError: No module named cherrypy' in result.output:
         logging.error(self.CHERRYPY_ERROR_MSG)
 
+  def GetURL(self, sub_dir=None):
+    """Returns the URL of this devserver instance."""
+    return self.GetDevServerURL(ip=self.hostname, port=self.port,
+                                sub_dir=sub_dir)
+
   @classmethod
   def WipePayloadCache(cls, devserver_bin='start_devserver', static_dir=None):
     """Cleans up devserver cache of payloads."""