[autotest] Refactor lab_inventory main() routine.

This changes the lab_inventory main() routine to extract various
chunks into separate functions, so as to shorten main(), and give
names to some of the constituent operations.

BUG=None
TEST=Run locally with --debug

Change-Id: If000304deca0e9a99178777fd60b668a64b74620
Reviewed-on: https://chromium-review.googlesource.com/734144
Commit-Ready: Richard Barnette <jrbarnette@chromium.org>
Tested-by: Richard Barnette <jrbarnette@chromium.org>
Reviewed-by: Prathmesh Prabhu <pprabhu@chromium.org>
diff --git a/site_utils/lab_inventory.py b/site_utils/lab_inventory.py
index 2fc0b44..a5d9c40 100755
--- a/site_utils/lab_inventory.py
+++ b/site_utils/lab_inventory.py
@@ -912,7 +912,7 @@
     The message is logged in the selected log directory using `tag`
     for the file name.
 
-    If the --print option was requested, the message is neither
+    If the --debug option was requested, the message is neither
     logged nor sent, but merely printed on stdout.
 
     @param arguments   Parsed command-line options.
@@ -946,6 +946,137 @@
                           all_recipients, e)
 
 
+def _populate_board_counts(inventory):
+    """Gather board counts while providing interactive feedback.
+
+    Gathering the status of all individual DUTs in the lab can take
+    considerable time (~30 minutes at the time of this writing).
+
+    Normally, we pay that cost by querying as we go.  However, with
+    the `--debug` option, we expect a human being to be watching the
+    progress in real time.  So, we force the first (expensive)
+    queries to happen up front, and provide simple ASCII output
+    (without using logging) to show a progress bar and results.
+
+    @param inventory  _LabInventory object with the inventory to
+                      be gathered.
+
+    """
+    n = 0
+    total_broken = 0
+    for counts in inventory.by_board.itervalues():
+        n += 1
+        if n % 10 == 5:
+            c = '+'
+        elif n % 10 == 0:
+            c = '%d' % ((n / 10) % 10)
+        else:
+            c = '.'
+        sys.stdout.write(c)
+        sys.stdout.flush()
+        # This next call is where all the time goes - it forces all
+        # of a board's HostJobHistory objects to query the database
+        # and cache their results.
+        total_broken += counts.get_broken()
+    sys.stdout.write('\n')
+    sys.stdout.write('Found %d broken DUTs\n' % total_broken)
+
+
+def _perform_board_inventory(arguments, inventory, timestamp):
+    """Perform the board inventory report.
+
+    The board inventory report consists of the following:
+      * A list of DUTs that are recommended to be repaired.
+        This list is optional, and only appears if the `--recommend`
+        option is present.
+      * A list of all boards that have failed DUTs, with counts
+        of working, broken, and spare DUTs, among others.
+
+    @param arguments  Command-line arguments as returned by
+                      `ArgumentParser`
+    @param inventory  _LabInventory object with the inventory to
+                      be reported.
+    @param timestamp  A string used to identify this run's timestamp
+                      in logs and email output.
+    """
+    if arguments.recommend:
+        recommend_message = _generate_repair_recommendation(
+                inventory, arguments.recommend) + '\n\n\n'
+    else:
+        recommend_message = ''
+    board_message = _generate_board_inventory_message(inventory)
+    _send_email(arguments,
+                'boards-%s.txt' % timestamp,
+                'DUT board inventory %s' % timestamp,
+                arguments.board_notify,
+                recommend_message + board_message)
+
+
+def _perform_pool_inventory(arguments, inventory, timestamp):
+    """Perform the pool inventory report.
+
+    The pool inventory report consists of the following:
+      * A list of all critical pools that have failed DUTs, with counts
+        of working, broken, and idle DUTs.
+      * A list of all idle DUTs by hostname including the board and
+        pool.
+
+    @param arguments  Command-line arguments as returned by
+                      `ArgumentParser`
+    @param inventory  _LabInventory object with the inventory to
+                      be reported.
+    @param timestamp  A string used to identify this run's timestamp
+                      in logs and email output.
+    """
+    pool_message = _generate_pool_inventory_message(inventory)
+    idle_message = _generate_idle_inventory_message(inventory)
+    _send_email(arguments,
+                'pools-%s.txt' % timestamp,
+                'DUT pool inventory %s' % timestamp,
+                arguments.pool_notify,
+                pool_message + '\n\n\n' + idle_message)
+
+
+def _log_startup(arguments, startup_time):
+    """Log the start of this inventory run.
+
+    Print various log messages indicating the start of the run.  Return
+    a string based on `startup_time` that will be used to identify this
+    run in log files and e-mail messages.
+
+    @param startup_time   A UNIX timestamp marking the moment when
+                          this inventory run began.
+    @returns  A timestamp string that will be used to identify this run
+              in logs and email output.
+    """
+    timestamp = time.strftime('%Y-%m-%d.%H',
+                              time.localtime(startup_time))
+    logging.debug('Starting lab inventory for %s', timestamp)
+    if arguments.board_notify:
+        if arguments.recommend:
+            logging.debug('Will include repair recommendations')
+        logging.debug('Will include board inventory')
+    if arguments.pool_notify:
+        logging.debug('Will include pool inventory')
+    return timestamp
+
+
+def _create_inventory(arguments, end_time):
+    """Create the `_LabInventory` instance to use for reporting.
+
+    @param end_time   A UNIX timestamp for the end of the time range
+                      to be searched in this inventory run.
+    """
+    start_time = end_time - arguments.duration * 60 * 60
+    afe = frontend_wrappers.RetryingAFE(server=None)
+    inventory = _LabInventory.create_inventory(
+            afe, start_time, end_time, arguments.boardnames)
+    logging.info('Found %d hosts across %d boards',
+                     inventory.get_num_duts(),
+                     inventory.get_num_boards())
+    return inventory
+
+
 def _separate_email_addresses(address_list):
     """Parse a list of comma-separated lists of e-mail addresses.
 
@@ -997,7 +1128,7 @@
     return True
 
 
-def _get_logdir(script):
+def _get_default_logdir(script):
     """Get the default directory for the `--logdir` option.
 
     The default log directory is based on the parent directory
@@ -1046,7 +1177,7 @@
     parser.add_argument('--debug', action='store_true',
                         help='Print e-mail messages on stdout '
                              'without sending them.')
-    parser.add_argument('--logdir', default=_get_logdir(argv[0]),
+    parser.add_argument('--logdir', default=_get_default_logdir(argv[0]),
                         help='Directory where logs will be written.')
     parser.add_argument('boardnames', nargs='*',
                         metavar='BOARD',
@@ -1061,15 +1192,15 @@
 def _configure_logging(arguments):
     """Configure the `logging` module for our needs.
 
-    How we log depends on whether the `--print` option was
-    provided on the command line.  Without the option, we log all
-    messages at DEBUG level or above, and write them to a file in
-    the directory specified by the `--logdir` option.  With the
-    option, we write log messages to stdout; messages below INFO
-    level are discarded.
-
-    The log file is configured to rotate once a week on Friday
-    evening, preserving ~3 months worth of history.
+    How we log depends on whether the `--debug` option was provided on
+    the command line.
+      * Without the option, we configure the logging to capture all
+        potentially relevant events in a log file.  The log file is
+        configured to rotate once a week on Friday evening, preserving
+        ~3 months worth of history.
+      * With the option, we expect stdout to contain other
+        human-readable output (including the contents of the e-mail
+        messages), so we restrict the output.
 
     @param arguments  Command-line arguments as returned by
                       `ArgumentParser`
@@ -1100,42 +1231,6 @@
     root_logger.addHandler(handler)
 
 
-def _populate_board_counts(inventory):
-    """Gather board counts while providing interactive feedback.
-
-    Gathering the status of all individual DUTs in the lab can take
-    considerable time (~30 minutes at the time of this writing).
-
-    Normally, we pay that cost by querying as we go.  However, with
-    the `--print` option, a human being may be watching the
-    progress.  So, we force the first (expensive) queries to happen
-    up front, and provide a small ASCII progress bar to give an
-    indicator of how many boards have been processed.
-
-    @param inventory  _LabInventory object with the inventory to
-                      be gathered.
-
-    """
-    n = 0
-    total_broken = 0
-    for counts in inventory.values():
-        n += 1
-        if n % 10 == 5:
-            c = '+'
-        elif n % 10 == 0:
-            c = '%d' % ((n / 10) % 10)
-        else:
-            c = '.'
-        sys.stdout.write(c)
-        sys.stdout.flush()
-        # This next call is where all the time goes - it forces all
-        # of a board's HostJobHistory objects to query the database
-        # and cache their results.
-        total_broken += counts.get_broken()
-    sys.stdout.write('\n')
-    sys.stdout.write('Found %d broken DUTs\n' % total_broken)
-
-
 def main(argv):
     """Standard main routine.
     @param argv  Command line arguments including `sys.argv[0]`.
@@ -1145,49 +1240,15 @@
         sys.exit(1)
     _configure_logging(arguments)
     try:
-        end_time = int(time.time())
-        start_time = end_time - arguments.duration * 60 * 60
-        timestamp = time.strftime('%Y-%m-%d.%H',
-                                  time.localtime(end_time))
-        logging.debug('Starting lab inventory for %s', timestamp)
-        if arguments.board_notify:
-            if arguments.recommend:
-                logging.debug('Will include repair recommendations')
-            logging.debug('Will include board inventory')
-        if arguments.pool_notify:
-            logging.debug('Will include pool inventory')
-
-        afe = frontend_wrappers.RetryingAFE(server=None)
-        inventory = _LabInventory.create_inventory(
-                afe, start_time, end_time, arguments.boardnames)
-        logging.info('Found %d hosts across %d boards',
-                         inventory.get_num_duts(),
-                         inventory.get_num_boards())
-
+        startup_time = time.time()
+        timestamp = _log_startup(arguments, startup_time)
+        inventory = _create_inventory(arguments, startup_time)
         if arguments.debug:
             _populate_board_counts(inventory)
-
         if arguments.board_notify:
-            if arguments.recommend:
-                recommend_message = _generate_repair_recommendation(
-                        inventory, arguments.recommend) + '\n\n\n'
-            else:
-                recommend_message = ''
-            board_message = _generate_board_inventory_message(inventory)
-            _send_email(arguments,
-                        'boards-%s.txt' % timestamp,
-                        'DUT board inventory %s' % timestamp,
-                        arguments.board_notify,
-                        recommend_message + board_message)
-
+            _perform_board_inventory(arguments, inventory, timestamp)
         if arguments.pool_notify:
-            pool_message = _generate_pool_inventory_message(inventory)
-            idle_message = _generate_idle_inventory_message(inventory)
-            _send_email(arguments,
-                        'pools-%s.txt' % timestamp,
-                        'DUT pool inventory %s' % timestamp,
-                        arguments.pool_notify,
-                        pool_message + '\n\n\n' + idle_message)
+            _perform_pool_inventory(arguments, inventory, timestamp)
     except KeyboardInterrupt:
         pass
     except EnvironmentError as e: