blob: a7602c47fe9e7ad258ad5f32ea9a8c3e6572ce00 [file] [log] [blame]
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +08001# 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 Buchillon0902f092018-09-19 15:03:17 +08004"""Common code for servo parsing support."""
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +08005
6import argparse
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +08007import logging
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +08008import os
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +08009import textwrap
10
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +080011import client
12import servo_logging
Ruben Rodriguez Buchillon479bd242020-02-14 18:58:30 -080013import utils.scratch
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +080014
Ruben Rodriguez Buchillon014d8822018-09-20 10:12:13 +080015
16# A brief overview of the classes found here, and their abilities.
17# Indentation indicates inheritance.
18#
19# _BaseServodParser: ArgumentParser with pretty-formatting for example list
20#
21# BaseServodParser: adds common args: port, host, and debug
22#
23# ServodRCParser: adds -name/-rcfile & overwrites parsing logic so that
24# rc parsing & configuration is handled internally
25# i.e. the program does not need to know anything about
26# the servodrc system
27#
28# ServodClientParser: parser intended for servod clients (e.g. dut-control)
29# - has all the BaseServodParser and ServodRCParser args
30# - has the native rc configuration handling
31# - adds a serialname argument that gets mapped to a
32# port using ServoScratch
33
34
35# This text file holds servod configuration parameters. This is especially
36# handy for multi servo operation.
37#
38# The file format is pretty loose:
39# - text starting with # is ignored til the end of the line
40# - empty lines are ignored
41# - configuration lines consist of up to 5 comma separated fields (all
42# but the first field optional):
43# servo-name, serial-number, port-number, board-name, board-model
44#
45# where
46# . servo-name - a user defined symbolic name, just a reference
47# to a certain servo board
48# . serial-number - serial number of the servo board this line pertains to
49# . port-number - desired port number for servod for this board, can be
50# overridden by the command line switch --port or
51# environment variable setting SERVOD_PORT
52# NOTE: this is no longer in use, and will be ignored.
53# . board-name - board configuration file to use, can be
54# overridden by the command line switch --board
55# . model-name - model override to use, if applicable.
56# overridden by command line --model
57#
58# Example lines in the rc-file:
59# nocturne_micro, SNCQ00098, , nocturne # This is a nocturne without model
60# octopus_micro, SNCQ00098, , octopus, npcx # This an octopus that defines model
61#
62# As you can see, the port part is left out for now. This will be phased out
63# giving users time to adjust their rc files.
64#
65# Since the same parameters could be defined using different means, there is a
66# hierarchy of definitions (left being the highest priority):
67# command line <- environment definition <- rc config file
68
69
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +080070if os.getuid():
71 DEFAULT_RC_FILE = '/home/%s/.servodrc' % os.getenv('USER', '')
72else:
73 DEFAULT_RC_FILE = '/home/%s/.servodrc' % os.getenv('SUDO_USER', '')
74
75
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +080076PORT_ENV_VAR = 'SERVOD_PORT'
77NAME_ENV_VAR = 'SERVOD_NAME'
78
79
80ARG_BY_USER_MARKER = 'supplied_by_user'
81
82
83def ArgMarkedAsUserSupplied(namespace, arg_name):
84 """Query whether an argument that uses StoreAndMarkAction is user supplied."""
85 marker_name = '%s_%s' % (arg_name, ARG_BY_USER_MARKER)
86 return hasattr(namespace, marker_name)
87
88# pylint: disable=protected-access
89# Need to expand the StoreAction of the parser.
90class StoreAndMarkAction(argparse._StoreAction):
91 """Helper to mark arguments whether they were supplied by the user.
92
93 If an argument is supplied by the user instead of using defaults or RC,
94 add another option with the name |arg|_supplied_by_user.
95 """
96
97 def __call__(self, parser, namespace, values, option_string=None):
98 """Extend default __call__ implementation."""
99 # This sets the |values| to |self.dest|.
100 super(StoreAndMarkAction, self).__call__(parser=parser, namespace=namespace,
101 values=values,
102 option_string=option_string)
103 marker_name = '%s_%s' % (self.dest, ARG_BY_USER_MARKER)
104 setattr(namespace, marker_name, True)
105
106
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800107class ServodParserHelpFormatter(argparse.RawDescriptionHelpFormatter,
108 argparse.ArgumentDefaultsHelpFormatter):
109 """Servod help formatter.
110
111 Combines ability for raw description printing (needed to have control over
112 how to print examples) and default argument printing, printing the default
113 which each argument.
114 """
115 pass
116
117
118class ServodParserError(Exception):
119 """Error class for Servod parsing errors."""
120 pass
121
122
123class _BaseServodParser(argparse.ArgumentParser):
124 """Extension to ArgumentParser that allows for examples in the description.
125
126 _BaseServodParser allows for a list of example tuples, where
127 element[0]: is the cmdline invocation
128 element[1]: is a comment to explain what the invocation does.
129
130 For example (loosely based on servod.)
131 ('-b board', 'Start servod with the configuation for board |board|')
132 would print the following help message:
133 ...
134
135 Examples:
136 > servod -b board
137 Start servod with the configuration for board |board|
138
139 Optional Arguments...
140
141 see servod, or dut_control for more examples.
142 """
143
Ruben Rodriguez Buchillon0902f092018-09-19 15:03:17 +0800144 def __init__(self, description='', examples=None, **kwargs):
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800145 """Initialize _BaseServodParser by setting description and formatter.
146
147 Args:
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800148 description: description of the program
149 examples: list of tuples where the first element is the cmdline example,
150 and the second element is a comment explaining the example.
151 %(prog)s will be prepended to each example if it does not
152 start with %(prog)s.
Ruben Rodriguez Buchillon0902f092018-09-19 15:03:17 +0800153 **kwargs: keyword arguments forwarded to ArgumentParser
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800154 """
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800155 # Initialize logging up here first to ensure log messages from parsing
156 # can go through.
157 loglevel, fmt = servo_logging.LOGLEVEL_MAP[servo_logging.DEFAULT_LOGLEVEL]
158 logging.basicConfig(loglevel=loglevel, format=fmt)
159 self._logger = logging.getLogger(type(self).__name__)
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800160 # Generate description.
161 description_lines = textwrap.wrap(description)
162 # Setting it into the kwargs here ensures that we overwrite an potentially
163 # passed in and undesired formatter class.
164 kwargs['formatter_class'] = ServodParserHelpFormatter
165 if examples:
166 # Extra newline to separate description from examples.
167 description_lines.append('\n')
168 description_lines.append('Examples:')
169 for example, comment in examples:
170 if not example.startswith('%(prog)s'):
171 example = '%(prog)s ' + example
172 example_lines = [' > ' + example]
173 example_lines.extend(textwrap.wrap(comment))
174 description_lines.append('\n\t'.join(example_lines))
175 description = '\n'.join(description_lines)
176 kwargs['description'] = description
Ruben Rodriguez Buchillon0902f092018-09-19 15:03:17 +0800177 super(_BaseServodParser, self).__init__(**kwargs)
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800178
179
180class BaseServodParser(_BaseServodParser):
181 """BaseServodParser handling common arguments in the servod cmdline tools."""
182
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800183 def __init__(self, add_port=True, **kwargs):
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800184 """Initialize by adding common arguments.
185
186 Adds:
187 - host/port arguments to find/initialize a servod instance
188 - debug argument to toggle debug message printing
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800189
190 Args:
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800191 add_port: bool, whether to add --port to the parser. A caller might want
192 to add port themselves either to rename it (servod-port),
193 or to create mutual exclusion with serialname and name (clients)
Ruben Rodriguez Buchillon0902f092018-09-19 15:03:17 +0800194 **kwargs: keyword arguments forwarded to _BaseServodParser
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800195 """
Ruben Rodriguez Buchillon0902f092018-09-19 15:03:17 +0800196 super(BaseServodParser, self).__init__(**kwargs)
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800197 self.add_argument('-d', '--debug', action='store_true', default=False,
198 help='enable debug messages')
199 self.add_argument('--host', default='localhost', type=str,
200 help='hostname of the servod server.')
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800201 if add_port:
202 BaseServodParser.AddRCEnabledPortArg(self)
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800203
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800204 @staticmethod
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800205 def AddRCEnabledPortArg(parser, port_flags=['-p', '--port']):
206 """Add the port to the argparser.
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800207
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800208 Set the default to environment variable ENV_PORT_NAME if defined
209
210 Note: while this helper does allow for arbitrary flags for the port
211 variable, the destination is still set to 'port'. It's on the caller to
212 ensure that there is no conflict.
Ruben Rodriguez Buchillon0902f092018-09-19 15:03:17 +0800213
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800214 Args:
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800215 parser: parser or group to add argument to
216 port_flags: optional, list, if the flags for the port should be different
217 than the default ones.
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800218 """
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800219 # pylint: disable=dangerous-default-value
220 # Having the default flags here simplifies the code logic.
221 default = os.environ.get(PORT_ENV_VAR, client.DEFAULT_PORT)
222 parser.add_argument(*port_flags, default=default, type=int, dest='port',
223 action=StoreAndMarkAction,
224 help='port of the servod server. Can also be supplied '
225 'through environment variable ' + PORT_ENV_VAR)
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800226
227
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800228class ServodRCParser(_BaseServodParser):
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800229 """Base class to build Servod parsers to natively handle servorc.
230
231 This class overwrites parse_args & parse_known_args to:
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800232 - handle NAME_ENV_VAR environment variable
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800233 - parse & substitute in the servorc file on matches
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800234 """
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800235
236 def __init__(self, **kwargs):
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800237 super(ServodRCParser, self).__init__(**kwargs)
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800238 self.add_argument('--rcfile', type=str, default=DEFAULT_RC_FILE,
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800239 help='servo description file for multi-servo operation.')
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800240 # name and serialname are both ways to ID a servo device
241 self._id_group = self.add_mutually_exclusive_group()
242 self._id_group.add_argument('-s', '--serialname', default=None, type=str,
243 help='device serialname stored in eeprom.')
244 ServodRCParser.AddRCEnabledNameArg(self._id_group)
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800245
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800246 @staticmethod
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800247 def AddRCEnabledNameArg(parser, name_flags=['-n', '--name']):
248 """Add the name to the argparser.
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800249
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800250 Set the default to environment variable ENV_VAR_NAME if defined
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800251
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800252 Note: while this helper does allow for arbitrary flags for the name
253 variable, the destination is still set to 'name'. It's on the caller to
254 ensure that there is no conflict.
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800255
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800256 Args:
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800257 parser: parser or group to add argument to
258 name_flags: optional, list, if the flags for the name should be different
259 than the default ones.
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800260 """
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800261 # pylint: disable=dangerous-default-value
262 # Having the default flags here simplifies the code logic.
263 default = os.environ.get(NAME_ENV_VAR, '')
264 parser.add_argument(*name_flags, default=default, type=str, dest='name',
265 help='symbolic name of the servo board, '
266 'used as a config shortcut, could also be supplied '
267 'through environment variable ' + NAME_ENV_VAR)
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800268
269 @staticmethod
270 def PostProcessRCElements(options, rcpath=None, logger=logging):
271 """Handle 'name' in |options| by substituting it with the intended config.
272
273 This replaces the name option in the options with the intended serialname
274 for that name if one can be found. If a board file is also specified in the
275 rc file it appends that to the options too, which can be ignored if not
276 needed.
277
278 Note: this function changes the content of options.
279
280 Args:
281 options: argparse Namespace of options to process.
282 rcpath: optional rcfile path if it's not stored under options.rcfile
283 logger: logger instance to use
284
285 Returns:
286 Reference back to the same options passed in.
287
288 Raises:
289 ServodParserError: if -n/--name and -s/--serialname both defined
290 ServodParserError: if name in options doesn't show up in servodrc
291 """
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800292 if not rcpath:
293 rcpath = options.rcfile
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800294 rcd = ServodRCParser.ParseRC(rcpath, logger=logger)
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800295 rc = None
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800296 if not options.serialname and options.name:
297 # |name| can be set through the commandline or through an environment
298 # variable. If it's set through the commandline, serialname cannot have
299 # been set. However, if serialname is set and name is also set (through
300 # the environment variable) name gets ignored.
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800301 if options.name not in rcd:
302 raise ServodParserError('Name %r not in rc at %r' % (options.name,
303 rcpath))
304 rc = rcd[options.name]
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800305 # For an rc to exist, 'sn' has to be a part of it
306 setattr(options, 'serialname', rc['sn'])
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800307 elif options.serialname:
308 # srcs meaning serialname runtime configurations (rcs).
309 srcs = [(name, rc) for name, rc in rcd.iteritems() if
310 rc['sn'] == options.serialname]
311 if srcs:
312 logger.info('Found servodrc entry %r for serialname %r. Using it.',
313 srcs[0][0], options.serialname)
314 rc = srcs[0][1]
315 if rc:
316 for elem in ['board', 'model']:
317 # Unlike serialname explicit overwrites of board and model in the
318 # cmdline are fine as the name flag is still useful to refer to a
319 # serialname.
320 if elem in rc and hasattr(options, elem):
321 if not getattr(options, elem):
322 logger.info('Setting %r to %r in the options as indicated by '
323 'servodrc file.', elem, rc[elem])
324 setattr(options, elem, rc[elem])
325 else:
326 if getattr(options, elem) != rc[elem]:
327 logger.warning('Ignoring rc configured %r name %r for servo %r. '
328 'Option already defined on the command line as %r',
329 elem, rc[elem], rc['sn'], getattr(options, elem))
330 return options
331
332 def parse_known_args(self, args=None, namespace=None):
333 """Overwrite from Argumentparser to handle servo rc.
334
335 Note: this also overwrites parse_args as parse_args just calls
336 parse_known_args and throws an error if there's anything inside of
337 xtra.
338
339 Args:
340 args: list of cmdline elements
341 namespace: namespace to place the results into
342
343 Returns:
344 tuple (options, xtra) the result from parsing known args
345 """
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800346 opts, xtra = _BaseServodParser.parse_known_args(self, args=args,
347 namespace=namespace)
348 opts = ServodRCParser.PostProcessRCElements(opts, logger=self._logger)
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800349 return (opts, xtra)
350
351 @staticmethod
352 def ParseRC(rc_file, logger=logging):
353 """Parse servodrc configuration file.
354
355 The format of the configuration file is described above in comments to
356 DEFAULT_RC_FILE. If the file is not found or is mis-formatted, a warning is
357 printed but the program tries to continue.
358
359 Args:
360 rc_file: a string, name of the file storing the configuration
361 logger: logger instance to use
362
363 Returns:
364 a dictionary, where keys are symbolic servo names, and values are
365 dictionaries representing servo parameters read from the config file,
366 keyed by strings 'sn' (for serial number), 'port', 'board', and 'model'.
367 """
368
369 if not os.path.isfile(rc_file):
370 return {}
371 rcd = {} # Dictionary representing the rc file contents.
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800372 attributes = ['name', 'sn', 'port', 'board', 'model']
373 # These attributes have to be defined for a line to be valid.
374 required_attributes = ['name', 'sn']
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800375 with open(rc_file) as f:
376 for rc_line in f:
377 line = rc_line.split('#')[0].strip()
378 if not line:
379 continue
380 elts = [x.strip() for x in line.split(',')]
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800381 if len(elts) < len(required_attributes):
382 logger.warning('ignoring rc line %r. Not all required '
383 'attributes defined %r.', rc_line.rstrip(),
384 required_attributes)
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800385 continue
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800386 # Initialize all to None that are not in elts
387 line_content = dict(zip(attributes, elts + [None] * len(attributes)))
388 # All required attributes are defined. Store the entry.
389 name = line_content.pop('name')
390 if len(elts) > len(attributes):
391 extra_info = elts[len(attributes):]
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800392 logger.warning('discarding %r for for %r', ', '.join(extra_info),
393 name)
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800394 rcd[name] = line_content
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800395 return rcd
396
397
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800398class ServodClientParser(ServodRCParser):
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800399 """Parser to use for servod client cmdline tools.
400
401 This parser adds servoscratch serialname<>port conversion to allow
402 for servod client cmdline tools to address servod using a servo device's
403 serialname as well.
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800404 """
405
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800406 def __init__(self, scratch=None, **kwargs):
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800407 """Create a ServodRCParser that has the BaseServodParser args.
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800408
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800409 (for testing) pass a scratch directory instead of the global default.
410
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800411 Args:
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800412 scratch: scratch directory to use
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800413 **kwargs: keyword arguments forwarded to _BaseServodParser
414 """
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800415 # BaseServodParser is used here to get the common arguments. Later,
416 # the ServodClientParser adds port itself, because from a client perspective
417 # there is mutual exclusion between --port/--serialname/--name as they serve
418 # one purpose: to identify an instance.
419 self._scratchdir = scratch
420 base_parser = BaseServodParser(add_port=False, add_help=False)
421 if 'parents' not in kwargs:
422 kwargs['parents'] = []
423 kwargs['parents'].append(base_parser)
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800424 super(ServodClientParser, self).__init__(**kwargs)
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800425 # Add --port to the |_id_group| to ensure exclusion with name and
426 # serialname.
427 BaseServodParser.AddRCEnabledPortArg(self._id_group)
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800428
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800429 def _MapSNToPort(self, opts):
430 """Helper to map the serialname in opts to the port its running on.
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800431
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800432 Args:
433 opts: ArgumentParser Namespace after parsing.
Ruben Rodriguez Buchillon63e38602018-09-19 10:36:58 +0800434
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800435 Returns:
436 opts: reference back to passed in opts
437
438 Raises:
439 Forces a program exit if |opts.serialname| is not found in the servo
440 scratch
441 """
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800442 # Passing None here uses the default production logic while passing any
443 # other directory can be used for testing. No need to check whether
444 # |self._scratchdir| is None.
Ruben Rodriguez Buchillon479bd242020-02-14 18:58:30 -0800445 scratch = utils.scratch.Scratch(self._scratchdir)
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800446 try:
447 entry = scratch.FindById(opts.serialname)
Ruben Rodriguez Buchillon479bd242020-02-14 18:58:30 -0800448 except utils.scratch.ScratchError:
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800449 self.error('No servod instance running for device with serialname: %r' %
450 opts.serialname)
451 opts.port = int(entry['port'])
452 return opts
453
454 def parse_known_args(self, args=None, namespace=None):
455 """Overwrite from Argumentparser to handle servo scratch logic.
456
457 If port is not defined and serialname is defined, and serialname has
458 no scratch entry, this will raise an error & terminate the program.
459
460 If there was neither a serialname nor a port, set the port to the
461 default port.
462
463 Note: this also overwrites parse_args as parse_args just calls
464 parse_known_args and throws an error if there's anything inside of
465 xtra.
466
467 Args:
468 args: list of cmdline elements
469 namespace: namespace to place the results into
470
471 Returns:
472 tuple (opts, xtra) the result from parsing known args
473 """
Ruben Rodriguez Buchillon7be98d72018-09-19 18:03:36 +0800474 opts, xtra = _BaseServodParser.parse_known_args(self, args=args,
475 namespace=namespace)
476 opts = ServodRCParser.PostProcessRCElements(opts, logger=self._logger)
477 if opts.serialname:
478 # If serialname is set, this means that either serialname or name was used
479 # to find it, and therefore port cannot have been set by the user due to
480 # mutual exclusion.
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800481 opts = self._MapSNToPort(opts)
Ruben Rodriguez Buchillon90662392018-09-19 16:28:38 +0800482 return (opts, xtra)