Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 1 | # Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
Ruben Rodriguez Buchillon | 0902f09 | 2018-09-19 15:03:17 +0800 | [diff] [blame^] | 4 | """Common code for servo parsing support.""" |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 5 | |
| 6 | import argparse |
| 7 | import os |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 8 | import textwrap |
| 9 | |
| 10 | if os.getuid(): |
| 11 | DEFAULT_RC_FILE = '/home/%s/.servodrc' % os.getenv('USER', '') |
| 12 | else: |
| 13 | DEFAULT_RC_FILE = '/home/%s/.servodrc' % os.getenv('SUDO_USER', '') |
| 14 | |
| 15 | |
| 16 | class ServodParserHelpFormatter(argparse.RawDescriptionHelpFormatter, |
| 17 | argparse.ArgumentDefaultsHelpFormatter): |
| 18 | """Servod help formatter. |
| 19 | |
| 20 | Combines ability for raw description printing (needed to have control over |
| 21 | how to print examples) and default argument printing, printing the default |
| 22 | which each argument. |
| 23 | """ |
| 24 | pass |
| 25 | |
| 26 | |
| 27 | class ServodParserError(Exception): |
| 28 | """Error class for Servod parsing errors.""" |
| 29 | pass |
| 30 | |
| 31 | |
| 32 | class _BaseServodParser(argparse.ArgumentParser): |
| 33 | """Extension to ArgumentParser that allows for examples in the description. |
| 34 | |
| 35 | _BaseServodParser allows for a list of example tuples, where |
| 36 | element[0]: is the cmdline invocation |
| 37 | element[1]: is a comment to explain what the invocation does. |
| 38 | |
| 39 | For example (loosely based on servod.) |
| 40 | ('-b board', 'Start servod with the configuation for board |board|') |
| 41 | would print the following help message: |
| 42 | ... |
| 43 | |
| 44 | Examples: |
| 45 | > servod -b board |
| 46 | Start servod with the configuration for board |board| |
| 47 | |
| 48 | Optional Arguments... |
| 49 | |
| 50 | see servod, or dut_control for more examples. |
| 51 | """ |
| 52 | |
Ruben Rodriguez Buchillon | 0902f09 | 2018-09-19 15:03:17 +0800 | [diff] [blame^] | 53 | def __init__(self, description='', examples=None, **kwargs): |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 54 | """Initialize _BaseServodParser by setting description and formatter. |
| 55 | |
| 56 | Args: |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 57 | description: description of the program |
| 58 | examples: list of tuples where the first element is the cmdline example, |
| 59 | and the second element is a comment explaining the example. |
| 60 | %(prog)s will be prepended to each example if it does not |
| 61 | start with %(prog)s. |
Ruben Rodriguez Buchillon | 0902f09 | 2018-09-19 15:03:17 +0800 | [diff] [blame^] | 62 | **kwargs: keyword arguments forwarded to ArgumentParser |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 63 | """ |
| 64 | # Generate description. |
| 65 | description_lines = textwrap.wrap(description) |
| 66 | # Setting it into the kwargs here ensures that we overwrite an potentially |
| 67 | # passed in and undesired formatter class. |
| 68 | kwargs['formatter_class'] = ServodParserHelpFormatter |
| 69 | if examples: |
| 70 | # Extra newline to separate description from examples. |
| 71 | description_lines.append('\n') |
| 72 | description_lines.append('Examples:') |
| 73 | for example, comment in examples: |
| 74 | if not example.startswith('%(prog)s'): |
| 75 | example = '%(prog)s ' + example |
| 76 | example_lines = [' > ' + example] |
| 77 | example_lines.extend(textwrap.wrap(comment)) |
| 78 | description_lines.append('\n\t'.join(example_lines)) |
| 79 | description = '\n'.join(description_lines) |
| 80 | kwargs['description'] = description |
Ruben Rodriguez Buchillon | 0902f09 | 2018-09-19 15:03:17 +0800 | [diff] [blame^] | 81 | super(_BaseServodParser, self).__init__(**kwargs) |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 82 | |
| 83 | |
| 84 | class BaseServodParser(_BaseServodParser): |
| 85 | """BaseServodParser handling common arguments in the servod cmdline tools.""" |
| 86 | |
Ruben Rodriguez Buchillon | 0902f09 | 2018-09-19 15:03:17 +0800 | [diff] [blame^] | 87 | def __init__(self, **kwargs): |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 88 | """Initialize by adding common arguments. |
| 89 | |
| 90 | Adds: |
| 91 | - host/port arguments to find/initialize a servod instance |
| 92 | - debug argument to toggle debug message printing |
| 93 | - name/rcfile arguments to handle servodrc configurations |
| 94 | |
| 95 | Args: |
Ruben Rodriguez Buchillon | 0902f09 | 2018-09-19 15:03:17 +0800 | [diff] [blame^] | 96 | **kwargs: keyword arguments forwarded to _BaseServodParser |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 97 | """ |
Ruben Rodriguez Buchillon | 0902f09 | 2018-09-19 15:03:17 +0800 | [diff] [blame^] | 98 | super(BaseServodParser, self).__init__(**kwargs) |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 99 | self.add_argument('-d', '--debug', action='store_true', default=False, |
| 100 | help='enable debug messages') |
| 101 | self.add_argument('--host', default='localhost', type=str, |
| 102 | help='hostname of the servod server.') |
| 103 | self.add_argument('-p', '--port', default=None, type=int, |
| 104 | help='port of the servod server.') |
| 105 | |
| 106 | |
| 107 | def add_servo_parsing_rc_options(parser): |
Ruben Rodriguez Buchillon | 0902f09 | 2018-09-19 15:03:17 +0800 | [diff] [blame^] | 108 | """Add common options descriptors to the parser object. |
| 109 | |
| 110 | Args: |
| 111 | parser: Argumentparser to append arguments to |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 112 | |
| 113 | Both servod and dut-control accept command line options for configuring |
| 114 | servo_parsing operation. This function configures the command line parser |
| 115 | object to accept those options. |
| 116 | """ |
| 117 | parser.add_argument('--rcfile', type=str, default=DEFAULT_RC_FILE, |
| 118 | help='servo description file for multi-servo operation.') |
| 119 | parser.add_argument('-n', '--name', type=str, |
| 120 | help='symbolic name of the servo board, ' |
| 121 | 'used as a config shortcut, could also be supplied ' |
| 122 | 'through environment variable SERVOD_NAME') |
| 123 | |
| 124 | |
| 125 | def parse_rc(logger, rc_file): |
Ruben Rodriguez Buchillon | 0902f09 | 2018-09-19 15:03:17 +0800 | [diff] [blame^] | 126 | """Parse servodrc configuration file. |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 127 | |
| 128 | The format of the configuration file is described above in comments to |
| 129 | DEFAULT_RC_FILE. I the file is not found or is mis-formatted, a warning is |
| 130 | printed but the program tries to continue. |
| 131 | |
| 132 | Args: |
| 133 | logger: a logging instance used by this servod driver |
| 134 | rc_file: a string, name of the file storing the configuration |
| 135 | |
| 136 | Returns: |
| 137 | a dictionary, where keys are symbolic servo names, and values are |
| 138 | dictionaries representing servo parameters read from the config file, |
| 139 | keyed by strings 'sn' (for serial number), 'port', 'board', and 'model'. |
| 140 | """ |
| 141 | |
| 142 | rcd = {} # Dictionary representing the rc file contents. |
| 143 | if os.path.isfile(rc_file): |
Ruben Rodriguez Buchillon | 0902f09 | 2018-09-19 15:03:17 +0800 | [diff] [blame^] | 144 | for rc_line in open(rc_file, 'r'): |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 145 | line = rc_line.split('#')[0].strip() |
| 146 | if not line: |
| 147 | continue |
| 148 | elts = [x.strip() for x in line.split(',')] |
| 149 | name = elts[0] |
| 150 | if not name or len(elts) < 2 or [x for x in elts if ' ' in x]: |
| 151 | logger.info('ignoring rc line "%s"', rc_line.rstrip()) |
| 152 | continue |
| 153 | rcd[name] = {'sn': elts[1], 'port': None, 'board': None, 'model': None} |
| 154 | if (len(elts) > 2): |
| 155 | rcd[name]['port'] = int(elts[2]) |
| 156 | if len(elts) > 3: |
| 157 | rcd[name]['board'] = elts[3] |
| 158 | if len(elts) > 4: |
| 159 | rcd[name]['model'] = elts[4] |
| 160 | if len(elts) > 5: |
| 161 | logger.info('discarding %s for for %s', ' '.join(elts[5:]), name) |
| 162 | return rcd |
| 163 | |
| 164 | |
| 165 | def get_env_options(logger, options): |
Ruben Rodriguez Buchillon | 0902f09 | 2018-09-19 15:03:17 +0800 | [diff] [blame^] | 166 | """Look for non-defined options in the environment. |
Ruben Rodriguez Buchillon | 63e3860 | 2018-09-19 10:36:58 +0800 | [diff] [blame] | 167 | |
| 168 | SERVOD_PORT and SERVOD_NAME environment variables can be used if --port |
| 169 | and --name command line switches are not set. Set the options values as |
| 170 | necessary. |
| 171 | |
| 172 | Args: |
| 173 | logger: a logging instance used by this servod driver |
| 174 | options: the options object returned by opt_parse |
| 175 | """ |
| 176 | if not options.port: |
| 177 | env_port = os.getenv('SERVOD_PORT') |
| 178 | if env_port: |
| 179 | try: |
| 180 | options.port = int(env_port) |
| 181 | except ValueError: |
| 182 | logger.warning('Ignoring environment port definition "%s"', env_port) |
| 183 | if not options.name: |
| 184 | options.name = os.getenv('SERVOD_NAME') |