Devserver: Disabling RPCs that are not being used.

This CL disables the following RPCs: controlfiles, list_suite_controls,
hostlog, fileinfo, build, locate_file, symbolicate_dump, latest_build,
xbuddy_translate, xbuddy, xbuddy_list, xbuddy_capacity, index, and doc
by raising a DevServerDeprecationError whenever those RPCs are called
but only on Infra managed devservers. The RPCs will work as they have
been on non-Infra managed devservers.

BUG=1030890
TEST=Manual Testing

Change-Id: I1da7a004acdebb6066d25254cd94c04307003109
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/2038060
Commit-Queue: Sanika Kulkarni <sanikak@chromium.org>
Tested-by: Sanika Kulkarni <sanikak@chromium.org>
Reviewed-by: Allen Li <ayatane@chromium.org>
Reviewed-by: Congbin Guo <guocb@chromium.org>
Auto-Submit: Sanika Kulkarni <sanikak@chromium.org>
diff --git a/devserver.py b/devserver.py
index a0919a9..0bdc787 100755
--- a/devserver.py
+++ b/devserver.py
@@ -96,11 +96,28 @@
 # Error msg for missing key in CrOS auto-update.
 KEY_ERROR_MSG = 'Key Error in RPC: %s= is required'
 
+# Error msg for deprecated RPC usage.
+DEPRECATED_RPC_ERROR_MSG = ('The %s RPC has been deprecated. Usage of this '
+                            'RPC is discouraged. Please go to '
+                            'go/devserver-deprecation for more information.')
+
 
 class DevServerError(Exception):
   """Exception class used by DevServer."""
 
 
+class DeprecatedRPCError(DevServerError):
+  """Exception class used when an RPC is deprecated but is still being used."""
+
+  def __init__(self, rpc_name):
+    """Constructor for DeprecatedRPCError class.
+
+    :param rpc_name: (str) name of the RPC that has been deprecated.
+    """
+    super(DeprecatedRPCError, self).__init__(DEPRECATED_RPC_ERROR_MSG % rpc_name)
+    self.rpc_name = rpc_name
+
+
 class DevServerHTTPError(cherrypy.HTTPError):
   """Exception class to log the HTTPResponse before routing it to cherrypy."""
   def __init__(self, status, message):
@@ -527,6 +544,11 @@
   cros_update_progress.DelExecuteLogFile(host_name, pid)
 
 
+def is_deprecated_server():
+  """Gets whether the devserver has deprecated RPCs."""
+  return cherrypy.config.get('infra_removal', False)
+
+
 class ApiRoot(object):
   """RESTful API for Dev Server information."""
   exposed = True
@@ -554,6 +576,8 @@
     Example URL:
       http://myhost/api/hostlog?ip=192.168.1.5
     """
+    if is_deprecated_server():
+      raise DeprecatedRPCError('hostlog')
     return updater.HandleHostLogPing(ip)
 
   @cherrypy.expose
@@ -572,6 +596,9 @@
     Example URL:
       http://myhost/api/fileinfo/some/path/to/file
     """
+    if is_deprecated_server():
+      raise DeprecatedRPCError('fileinfo')
+
     # TODO(ahassani): A better way of doing this is to just return the the
     # content of the payloads' property file instead. That has all this info
     # except that the key for sha256 is 'sha256_hex', but still base64 encdoed.
@@ -626,6 +653,9 @@
   @cherrypy.expose
   def build(self, board, pkg, **kwargs):
     """Builds the package specified."""
+    if is_deprecated_server():
+      raise DeprecatedRPCError('build')
+
     import builder
     if self._builder is None:
       self._builder = builder.Builder()
@@ -1040,6 +1070,9 @@
       Path to the file with the given name. It's relative to the folder for the
       build, e.g., DATA/priv-app/sl4a/sl4a.apk
     """
+    if is_deprecated_server():
+      raise DeprecatedRPCError('locate_file')
+
     dl, _ = _get_downloader_and_factory(kwargs)
     try:
       file_name = kwargs['file_name']
@@ -1127,6 +1160,9 @@
       archive_url: Google Storage URL for the build.
       minidump: The binary minidump file to symbolicate.
     """
+    if is_deprecated_server():
+      raise DeprecatedRPCError('symbolicate_dump')
+
     # Ensure the symbols have been staged.
     # Try debug.tar.xz first, then debug.tgz
     for artifact in (artifact_info.SYMBOLS_ONLY, artifact_info.SYMBOLS):
@@ -1182,6 +1218,9 @@
           R19-1993.0.0-a1-b1480.
       An empty string if no latest could be found.
     """
+    if is_deprecated_server():
+      raise DeprecatedRPCError('latestbuild')
+
     if not kwargs:
       return _PrintDocStringAsHTML(self.latestbuild)
 
@@ -1221,6 +1260,9 @@
     Returns:
       A dictionary of all control files's path to its content for given suite.
     """
+    if is_deprecated_server():
+      raise DeprecatedRPCError('list_suite_controls')
+
     if not kwargs:
       return _PrintDocStringAsHTML(self.controlfiles)
 
@@ -1270,6 +1312,9 @@
       Contents of a control file if control_path is provided.
       A list of control files if no control_path is provided.
     """
+    if is_deprecated_server():
+      raise DeprecatedRPCError('controlfiles')
+
     if not kwargs:
       return _PrintDocStringAsHTML(self.controlfiles)
 
@@ -1305,6 +1350,9 @@
       String in the format of build_id/artifact as stored on the local server
       or in Google Storage.
     """
+    if is_deprecated_server():
+      raise DeprecatedRPCError('xbuddy_translate')
+
     build_id, filename = self._xbuddy.Translate(
         args, image_dir=kwargs.get('image_dir'))
     response = os.path.join(build_id, filename)
@@ -1360,6 +1408,9 @@
       payloads are. E.g.,
         archive/x86-generic-release/R26-4000.0.0
     """
+    if is_deprecated_server():
+      raise DeprecatedRPCError('xbuddy')
+
     boolean_string = kwargs.get('for_update')
     for_update = xbuddy.XBuddy.ParseBoolean(boolean_string)
     boolean_string = kwargs.get('return_dir')
@@ -1415,16 +1466,25 @@
       A string representation of a list of tuples [(build_id, time since last
       access),...]
     """
+    if is_deprecated_server():
+      raise DeprecatedRPCError('xbuddy')
+
     return self._xbuddy.List()
 
   @cherrypy.expose
   def xbuddy_capacity(self):
     """Returns the number of images cached by xBuddy."""
+    if is_deprecated_server():
+      raise DeprecatedRPCError('xbuddy_capacity')
+
     return self._xbuddy.Capacity()
 
   @cherrypy.expose
   def index(self):
     """Presents a welcome message and documentation links."""
+    if is_deprecated_server():
+      raise DeprecatedRPCError('index')
+
     html_template = (
         'Welcome to the Dev Server!<br>\n'
         '<br>\n'
@@ -1449,6 +1509,9 @@
     Examples:
       http://myhost/doc/update
     """
+    if is_deprecated_server():
+      raise DeprecatedRPCError('doc')
+
     name = '/'.join(args)
     method = _GetExposedMethod(name)
     if not method:
@@ -1651,6 +1714,11 @@
                     default=None,
                     help='Path to a json file which contains the credential '
                     'needed to access Android builds.')
+  parser.add_option('--infra_removal',
+                    action='store_true', default=False,
+                    help='If option is present, some RPCs will be disabled to '
+                         'help with infra removal efforts. See '
+                         'go/devserver-deprecation')
   _AddProductionOptions(parser)
   _AddUpdateOptions(parser)
   _AddTestingOptions(parser)
@@ -1661,6 +1729,7 @@
   # initialization.
   if options.production:
     cherrypy.config.update({'environment': 'production'})
+  cherrypy.config.update({'infra_removal': options.infra_removal})
   if not options.logfile:
     cherrypy.config.update({'log.screen': True})
   else: