blob: 3ceffd4c03230ee21f7b943f60d9d8db55a2578f [file] [log] [blame]
# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Common code for servo parsing support."""
import argparse
import os
import textwrap
if os.getuid():
DEFAULT_RC_FILE = '/home/%s/.servodrc' % os.getenv('USER', '')
else:
DEFAULT_RC_FILE = '/home/%s/.servodrc' % os.getenv('SUDO_USER', '')
class ServodParserHelpFormatter(argparse.RawDescriptionHelpFormatter,
argparse.ArgumentDefaultsHelpFormatter):
"""Servod help formatter.
Combines ability for raw description printing (needed to have control over
how to print examples) and default argument printing, printing the default
which each argument.
"""
pass
class ServodParserError(Exception):
"""Error class for Servod parsing errors."""
pass
class _BaseServodParser(argparse.ArgumentParser):
"""Extension to ArgumentParser that allows for examples in the description.
_BaseServodParser allows for a list of example tuples, where
element[0]: is the cmdline invocation
element[1]: is a comment to explain what the invocation does.
For example (loosely based on servod.)
('-b board', 'Start servod with the configuation for board |board|')
would print the following help message:
...
Examples:
> servod -b board
Start servod with the configuration for board |board|
Optional Arguments...
see servod, or dut_control for more examples.
"""
def __init__(self, description='', examples=None, **kwargs):
"""Initialize _BaseServodParser by setting description and formatter.
Args:
description: description of the program
examples: list of tuples where the first element is the cmdline example,
and the second element is a comment explaining the example.
%(prog)s will be prepended to each example if it does not
start with %(prog)s.
**kwargs: keyword arguments forwarded to ArgumentParser
"""
# Generate description.
description_lines = textwrap.wrap(description)
# Setting it into the kwargs here ensures that we overwrite an potentially
# passed in and undesired formatter class.
kwargs['formatter_class'] = ServodParserHelpFormatter
if examples:
# Extra newline to separate description from examples.
description_lines.append('\n')
description_lines.append('Examples:')
for example, comment in examples:
if not example.startswith('%(prog)s'):
example = '%(prog)s ' + example
example_lines = [' > ' + example]
example_lines.extend(textwrap.wrap(comment))
description_lines.append('\n\t'.join(example_lines))
description = '\n'.join(description_lines)
kwargs['description'] = description
super(_BaseServodParser, self).__init__(**kwargs)
class BaseServodParser(_BaseServodParser):
"""BaseServodParser handling common arguments in the servod cmdline tools."""
def __init__(self, **kwargs):
"""Initialize by adding common arguments.
Adds:
- host/port arguments to find/initialize a servod instance
- debug argument to toggle debug message printing
- name/rcfile arguments to handle servodrc configurations
Args:
**kwargs: keyword arguments forwarded to _BaseServodParser
"""
super(BaseServodParser, self).__init__(**kwargs)
self.add_argument('-d', '--debug', action='store_true', default=False,
help='enable debug messages')
self.add_argument('--host', default='localhost', type=str,
help='hostname of the servod server.')
self.add_argument('-p', '--port', default=None, type=int,
help='port of the servod server.')
def add_servo_parsing_rc_options(parser):
"""Add common options descriptors to the parser object.
Args:
parser: Argumentparser to append arguments to
Both servod and dut-control accept command line options for configuring
servo_parsing operation. This function configures the command line parser
object to accept those options.
"""
parser.add_argument('--rcfile', type=str, default=DEFAULT_RC_FILE,
help='servo description file for multi-servo operation.')
parser.add_argument('-n', '--name', type=str,
help='symbolic name of the servo board, '
'used as a config shortcut, could also be supplied '
'through environment variable SERVOD_NAME')
def parse_rc(logger, rc_file):
"""Parse servodrc configuration file.
The format of the configuration file is described above in comments to
DEFAULT_RC_FILE. I the file is not found or is mis-formatted, a warning is
printed but the program tries to continue.
Args:
logger: a logging instance used by this servod driver
rc_file: a string, name of the file storing the configuration
Returns:
a dictionary, where keys are symbolic servo names, and values are
dictionaries representing servo parameters read from the config file,
keyed by strings 'sn' (for serial number), 'port', 'board', and 'model'.
"""
rcd = {} # Dictionary representing the rc file contents.
if os.path.isfile(rc_file):
for rc_line in open(rc_file, 'r'):
line = rc_line.split('#')[0].strip()
if not line:
continue
elts = [x.strip() for x in line.split(',')]
name = elts[0]
if not name or len(elts) < 2 or [x for x in elts if ' ' in x]:
logger.info('ignoring rc line "%s"', rc_line.rstrip())
continue
rcd[name] = {'sn': elts[1], 'port': None, 'board': None, 'model': None}
if (len(elts) > 2):
rcd[name]['port'] = int(elts[2])
if len(elts) > 3:
rcd[name]['board'] = elts[3]
if len(elts) > 4:
rcd[name]['model'] = elts[4]
if len(elts) > 5:
logger.info('discarding %s for for %s', ' '.join(elts[5:]), name)
return rcd
def get_env_options(logger, options):
"""Look for non-defined options in the environment.
SERVOD_PORT and SERVOD_NAME environment variables can be used if --port
and --name command line switches are not set. Set the options values as
necessary.
Args:
logger: a logging instance used by this servod driver
options: the options object returned by opt_parse
"""
if not options.port:
env_port = os.getenv('SERVOD_PORT')
if env_port:
try:
options.port = int(env_port)
except ValueError:
logger.warning('Ignoring environment port definition "%s"', env_port)
if hasattr(options, 'name') and not options.name:
options.name = os.getenv('SERVOD_NAME')