Home | History | Annotate | Download | only in controllers
      1 #!/usr/bin/env python3.4
      2 #
      3 #   Copyright 2016 - The Android Open Source Project
      4 #
      5 #   Licensed under the Apache License, Version 2.0 (the "License");
      6 #   you may not use this file except in compliance with the License.
      7 #   You may obtain a copy of the License at
      8 #
      9 #       http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 #   Unless required by applicable law or agreed to in writing, software
     12 #   distributed under the License is distributed on an "AS IS" BASIS,
     13 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 #   See the License for the specific language governing permissions and
     15 #   limitations under the License.
     16 
     17 import importlib
     18 import logging
     19 import os
     20 
     21 ACTS_CONTROLLER_CONFIG_NAME = "DiagLogger"
     22 ACTS_CONTROLLER_REFERENCE_NAME = "diag_logger"
     23 
     24 
     25 class DiagLoggerError(Exception):
     26     """This is the base exception class for errors generated by
     27     DiagLogger modules.
     28     """
     29     pass
     30 
     31 
     32 def create(configs, logger):
     33     """Initializes the Diagnotic Logger instances based on the
     34     provided JSON configuration(s). The expected keys are:
     35 
     36     Package: A package name containing the diagnostic logger
     37         module. It should be in python path of the environment.
     38     Type: A first-level type for the Logger, which should correspond
     39         the name of the module containing the Logger implementation.
     40     SubType: The exact implementation of the sniffer, which should
     41         correspond to the name of the class to be used.
     42     HostLogPath: This is the default directory used to dump any logs
     43         that are captured or any other files that are stored as part
     44         of the logging process. It's use is implementation specific,
     45         but it should be provided by all loggers for completeness.
     46     Configs: A dictionary specifying baseline configurations of the
     47         particular Logger. These configurations may be overridden at
     48         the start of a session.
     49     """
     50     objs = []
     51     for c in configs:
     52         diag_package_name = c["Package"]  # package containing module
     53         diag_logger_type = c["Type"]  # module name
     54         diag_logger_name = c["SubType"]  # class name
     55         host_log_path = c["HostLogPath"]
     56         base_configs = c["Configs"]
     57         module_name = "{}.{}".format(diag_package_name, diag_logger_type)
     58         module = importlib.import_module(module_name)
     59         logger = getattr(module, diag_logger_name)
     60 
     61         objs.append(logger(host_log_path,
     62                            logger,
     63                            config_container=base_configs))
     64     return objs
     65 
     66 
     67 def destroy(objs):
     68     """Stops all ongoing logger sessions, deletes any temporary files, and
     69     prepares logger objects for destruction.
     70     """
     71     for diag_logger in objs:
     72         try:
     73             diag_logger.reset()
     74         except DiagLoggerError:
     75             # TODO: log if things go badly here
     76             pass
     77 
     78 
     79 class DiagLoggerBase():
     80     """Base Class for Proprietary Diagnostic Log Collection
     81 
     82     The DiagLoggerBase is a simple interface for running on-device logging via
     83     a standard workflow that can be integrated without the caller actually
     84     needing to know the details of what logs are captured or how.
     85     The workflow is as follows:
     86 
     87     1) Create a DiagLoggerBase Object
     88     2) Call start() to begin an active logging session.
     89     3) Call stop() to end an active logging session.
     90     4) Call pull() to ensure all collected logs are stored at
     91        'host_log_path'
     92     5) Call reset() to stop all logging and clear any unretrieved logs.
     93     """
     94 
     95     def __init__(self, host_log_path, logger=None, config_container=None):
     96         """Create a Diagnostic Logging Proxy Object
     97 
     98         Args:
     99             host_log_path: File path where retrieved logs should be stored
    100             config_container: A transparent container used to pass config info
    101         """
    102         self.host_log_path = os.path.realpath(os.path.expanduser(
    103             host_log_path))
    104         self.config_container = config_container
    105         if not os.path.isdir(self.host_log_path):
    106             os.mkdir(self.host_log_path)
    107         self.logger = logger
    108         if not self.logger:
    109             self.logger = logging.getLogger(self.__class__.__name__)
    110 
    111     def start(self, config_container=None):
    112         """Start collecting Diagnostic Logs
    113 
    114         Args:
    115             config_container: A transparent container used to pass config info
    116 
    117         Returns:
    118             A logging session ID that can be later used to stop the session
    119             For Diag interfaces supporting only one session this is unneeded
    120         """
    121         raise NotImplementedError("Base class should not be invoked directly!")
    122 
    123     def stop(self, session_id=None):
    124         """Stop collecting Diagnostic Logs
    125 
    126         Args:
    127             session_id: an optional session id provided for multi session
    128                         logging support
    129 
    130         Returns:
    131         """
    132         raise NotImplementedError("Base class should not be invoked directly!")
    133 
    134     def pull(self, session_id=None, out_path=None):
    135         """Save all cached diagnostic logs collected to the host
    136 
    137         Args:
    138             session_id: an optional session id provided for multi session
    139                         logging support
    140 
    141             out_path: an optional override to host_log_path for a specific set
    142                       of logs
    143 
    144         Returns:
    145             An integer representing a port number on the host available for adb
    146             forward.
    147         """
    148         raise NotImplementedError("Base class should not be invoked directly!")
    149 
    150     def reset(self):
    151         """Stop any ongoing logging sessions and clear any cached logs that have
    152         not been retrieved with pull(). This must delete all session records and
    153         return the logging object to a state equal to when constructed.
    154         """
    155         raise NotImplementedError("Base class should not be invoked directly!")
    156 
    157     def get_log_path(self):
    158         """Return the log path for this object"""
    159         return self.host_log_path
    160