blob: 52e8cefed02859f80e68d74b83927cbb54cbc4d0 [file] [log] [blame]
Tammo Spalink9a96b8a2012-04-03 11:10:41 +08001#!/usr/bin/python
2#
3# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Google Factory Tool.
8
9This tool is indended to be used on factory assembly lines. It
10provides all of the Google required test functionality and must be run
11on each device as part of the assembly process.
12"""
13
14
15import logging
16import os
17import re
18import sys
19
Tammo Spalink86a61c62012-05-25 15:10:35 +080020from tempfile import gettempdir
21
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080022import bmpblk
23import crosfw
24import hwid_tool
25import probe
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080026import report_upload
27import vpd_data
28
29from common import Error, ParseKeyValueData, SetupLogging, Shell, YamlWrite
30from hacked_argparse import CmdArg, Command, ParseCmdline, verbosity_cmd_arg
31from tempfile import NamedTemporaryFile
32
Tammo Spalink86a61c62012-05-25 15:10:35 +080033# TODO(tammo): Remove imp logic once the cros/factory code moves into this repo.
34import imp
35at_common = imp.find_module('common', ['/usr/local/autotest/client/bin'])
36imp.load_module('at_common', *at_common)
37from autotest_lib.client.cros.factory.event_log import EventLog, EVENT_LOG_DIR
38from autotest_lib.client.cros.factory.event_log import TimeString, TimedUuid
39from autotest_lib.client.cros.factory import FACTORY_LOG_PATH
40
41
42# Use a global event log, so that only a single log is created when
43# gooftool is called programmatically.
44_event_log = EventLog('gooftool')
45
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080046
47def GetPrimaryDevicePath(partition=None):
48 def IsFixed(dev):
49 sysfs_path = '/sys/block/%s/removable' % dev
50 return (os.path.exists(sysfs_path) and
51 open(sysfs_path).read().strip() == '0')
52 alpha_re = re.compile(r'^/dev/([a-zA-Z]+)[0-9]+$')
53 alnum_re = re.compile(r'^/dev/([a-zA-Z]+[0-9]+)p[0-9]+$')
cychiangde1dee22012-05-22 09:42:09 +080054 matched_alnum = False
55 dev_set = set()
56 for path in Shell('cgpt find -t rootfs').stdout.strip().split():
57 for dev in alpha_re.findall(path):
58 if IsFixed(dev):
59 dev_set.add(dev)
60 matched_alnum = False
61 for dev in alnum_re.findall(path):
62 if IsFixed(dev):
63 dev_set.add(dev)
64 matched_alnum = True
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080065 if len(dev_set) != 1:
66 raise Error('zero or multiple primary devs: %s' % dev_set)
67 dev_path = os.path.join('/dev', dev_set.pop())
68 if partition is None:
69 return dev_path
cychiangde1dee22012-05-22 09:42:09 +080070 fmt_str = '%sp%d' if matched_alnum else '%s%d'
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080071 return fmt_str % (dev_path, partition)
72
73
74def GetReleaseRootPartitionPath():
75 return GetPrimaryDevicePath(5)
76
77
78def GetReleaseKernelPartitionPath():
79 return GetPrimaryDevicePath(4)
80
81
82def FindScript(script_name):
Jon Salzd24269c2012-05-29 17:22:59 +080083 script_path = os.path.join(os.path.dirname(os.path.dirname(
84 os.path.realpath(__file__))), 'sh', script_name)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080085 if not os.path.exists(script_path):
86 raise Error('Needed script %s does not exist.' % script_path)
87 return script_path
88
89
Tammo Spalink86a61c62012-05-25 15:10:35 +080090def ReadVpd(fw_image_file, kind):
91 raw_vpd_data = Shell('vpd -i %s -l -f %s' % (kind, fw_image_file)).stdout
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080092 return ParseKeyValueData('"(.*)"="(.*)"$', raw_vpd_data)
93
94
Tammo Spalink86a61c62012-05-25 15:10:35 +080095def ReadRoVpd(fw_image_file):
96 return ReadVpd(fw_image_file, 'RO_VPD')
97
98
99def ReadRwVpd(fw_image_file):
100 return ReadVpd(fw_image_file, 'RW_VPD')
101
102
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800103@Command('write_hwid',
104 CmdArg('hwid', metavar='HWID', help='HWID string'))
105def WriteHwid(options):
106 """Write specified HWID value into the system BB."""
107 logging.debug('writing hwid string %r', options.hwid)
108 main_fw = crosfw.LoadMainFirmware()
109 Shell('gbb_utility --set --hwid="%s" "%s"' %
110 (options.hwid, main_fw.GetFileName()))
111 main_fw.Write(sections=['GBB'])
Tammo Spalink86a61c62012-05-25 15:10:35 +0800112 _event_log.Log('write_hwid', hwid=options.hwid)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800113
114
115@Command('probe',
116 CmdArg('--comps', nargs='*',
117 help='List of keys from the component_db registry.'),
118 CmdArg('--no_vol', action='store_true',
119 help='Do not probe volatile data.'),
120 CmdArg('--no_ic', action='store_true',
121 help='Do not probe initial_config data.'))
122def RunProbe(options):
123 """Print yaml-formatted breakdown of probed device properties."""
124 probe_results = probe.Probe(target_comp_classes=options.comps,
125 probe_volatile=not options.no_vol,
126 probe_initial_config=not options.no_ic)
127 print YamlWrite(probe_results.__dict__)
128
129
130_hwdb_path_cmd_arg = CmdArg(
131 '--hwdb_path', metavar='PATH',
132 default=hwid_tool.DEFAULT_HWID_DATA_PATH,
133 help='Path to the HWID database.')
134
135
Tammo Spalink214caf42012-05-28 10:45:00 +0800136@Command('verify_components',
137 _hwdb_path_cmd_arg,
138 CmdArg('comp_white_list', nargs='*'))
139def VerifyComponents(options):
140 """Verify that probable components all match entries in the component_db.
141
142 Probe for each component class in the comp_white_list and verify
143 that a corresponding match exists in the component_db -- make sure
144 that these components are present, that they have been approved, but
145 do not check against any specific BOM/HWID configurations.
146 """
147 hwdb = hwid_tool.ReadDatastore(options.hwdb_path)
148 if not options.comp_white_list:
149 sys.exit('ERROR: no component white list specified; possible choices:\n %s'
150 % '\n '.join(sorted(hwdb.comp_db.registry)))
151 for comp_class in options.comp_white_list:
152 if comp_class not in hwdb.comp_db.registry:
153 sys.exit('ERROR: specified white list component class %r does not exist'
154 ' in the component DB.' % comp_class)
155 probe_results = probe.Probe(target_comp_classes=options.comp_white_list,
156 probe_volatile=False, probe_initial_config=False)
157 probe_val_map = hwid_tool.CalcCompDbProbeValMap(hwdb.comp_db)
158 errors = []
159 matches = []
160 for comp_class in sorted(options.comp_white_list):
161 probe_val = probe_results.found_components.get(comp_class, None)
162 if probe_val is not None:
163 comp_name = probe_val_map.get(probe_val, None)
164 if comp_name is not None:
165 matches.append(comp_name)
166 else:
167 errors.append('unsupported %r component found with probe result'
168 ' %r (no matching name in the component DB)' %
169 (comp_class, probe_val))
170 else:
171 errors.append('missing %r component' % comp_class)
172 if errors:
173 print '\n'.join(errors)
174 sys.exit('component verification FAILURE')
175 else:
176 print 'component verification SUCCESS'
177 print 'found components:\n %s' % '\n '.join(matches)
178
179
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800180@Command('verify_hwid',
181 _hwdb_path_cmd_arg)
182def VerifyHwid(options):
183 """Verify system HWID properties match probed device properties.
184
185 First probe components, volatile and initial_config parameters for
186 the DUT. Then use the available device data to produce a list of
187 candidate HWIDs. Then verify the HWID from the DUT is present in
188 that list. Then verify that the DUT initial config values match
189 those specified for its HWID. Finally, verify that VPD contains all
190 the necessary fields as specified by the board data, and when
191 possible verify that values are legitimate.
192 """
193 hwdb = hwid_tool.ReadDatastore(options.hwdb_path)
194 main_fw_file = crosfw.LoadMainFirmware().GetFileName()
195 gbb_result = Shell('gbb_utility -g --hwid %s' % main_fw_file).stdout
196 hwid = re.findall(r'hardware_id:(.*)', gbb_result)[0].strip()
197 hwid_properties = hwid_tool.LookupHwidProperties(hwdb, hwid)
198 logging.info('Verifying system HWID: %r', hwid_properties.hwid)
199 logging.debug('expected system properties:\n%s',
200 YamlWrite(hwid_properties.__dict__))
201 probe_results = probe.Probe()
202 cooked_results = hwid_tool.CookProbeResults(
203 hwdb, probe_results, hwid_properties.board)
204 logging.debug('found system properties:\n%s',
205 YamlWrite(cooked_results.__dict__))
206 # TODO(tammo): Output a new-style log event with device details here.
207 match_errors = []
208 for comp_class, expected_name in hwid_properties.component_map.items():
209 if expected_name == 'ANY':
210 continue
211 if expected_name == cooked_results.matched_components.get(comp_class, None):
212 continue
213 if comp_class in probe_results.missing_components:
214 match_errors.append(' %s component mismatch, expected %s, found nothing'
215 % (comp_class, expected_name))
216 else:
217 probe_value = probe_results.found_components.get(comp_class, None)
218 match_errors.append(' %s component mismatch, expected %s, found %r' %
219 (comp_class, expected_name, probe_value))
220 if match_errors:
221 raise Error('HWID verification FAILED.\n%s' % '\n'.join(match_errors))
222 if hwid_properties.volatile not in cooked_results.matched_volatile_tags:
223 msg = (' HWID specified volatile %s, but found match only for %s' %
224 (hwid_properties.volatile,
225 ', '.join(cooked_results.matched_volatile_tags)))
226 raise Error('HWID verification FAILED.\n%s' % msg)
227 if (hwid_properties.initial_config is not None and
228 hwid_properties.initial_config not in
229 cooked_results.matched_initial_config_tags):
230 msg = (' HWID specified initial_config %s, but only found match for [%s]' %
231 (hwid_properties.initial_config,
232 ', '.join(cooked_results.matched_initial_config_tags)))
233 raise Error('HWID verification FAILED.\n%s' % msg)
234 # TODO(tammo): Verify HWID status is supported (or deprecated for RMA).
235 ro_vpd = ReadRoVpd(main_fw_file)
236 for field in hwid_properties.vpd_ro_field_list:
237 if field not in ro_vpd:
238 raise Error('Missing required VPD field: %s' % field)
239 known_valid_values = vpd_data.KNOWN_VPD_FIELD_DATA.get(field, None)
240 value = ro_vpd[field]
241 if known_valid_values is not None and value not in known_valid_values:
242 raise Error('Invalid VPD entry : field %r, value %r' % (field, value))
Tammo Spalink86a61c62012-05-25 15:10:35 +0800243 rw_vpd = ReadRwVpd(main_fw_file)
244 _event_log.Log(
245 'verify_hwid',
246 matched_components=cooked_results.matched_components,
247 initial_configs=cooked_results.matched_initial_config_tags,
248 volatiles=cooked_results.matched_volatile_tags,
249 ro_vpd=ro_vpd,
250 rw_vpd=rw_vpd)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800251
252
253@Command('verify_keys')
254def VerifyKeys(options):
255 """Verify keys in firmware and SSD match."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800256 script = FindScript('verify_keys.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800257 kernel_device = GetReleaseKernelPartitionPath()
258 main_fw_file = crosfw.LoadMainFirmware().GetFileName()
259 result = Shell('%s %s %s' % (script, kernel_device, main_fw_file))
260 if not result.success:
261 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
262
263
264@Command('set_fw_bitmap_locale')
265def SetFirmwareBitmapLocale(options):
266 """Use VPD locale value to set firmware bitmap default language."""
267 image_file = crosfw.LoadMainFirmware().GetFileName()
268 locale = ReadRoVpd(image_file).get('initial_locale', None)
269 if locale is None:
270 raise Error, 'Missing initial_locale VPD.'
271 bitmap_locales = []
272 with NamedTemporaryFile() as f:
273 Shell('gbb_utility -g --bmpfv=%s %s' % (f.name, image_file))
274 bmpblk_data = bmpblk.unpack_bmpblock(f.read())
275 bitmap_locales = bmpblk_data.get('locales', bitmap_locales)
276 # Some locale values are just a language code and others are a
277 # hyphen-separated language code and country code pair. We care
278 # only about the language code part.
279 language_code = locale.partition('-')[0]
280 if language_code not in bitmap_locales:
281 raise Error, ('Firmware bitmaps do not contain support for the specified '
282 'initial locale language %r' % language_code)
283 else:
284 locale_index = bitmap_locales.index(language_code)
285 logging.info('Firmware bitmap initial locale set to %d (%s).',
286 locale_index, bitmap_locales[locale_index])
287 Shell('crossystem loc_idx=%d' % locale_index)
288
289
290@Command('verify_system_time')
291def VerifySystemTime(options):
292 """Verify system time is later than release filesystem creation time."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800293 script = FindScript('verify_system_time.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800294 rootfs_device = GetReleaseRootPartitionPath()
295 result = Shell('%s %s' % (script, rootfs_device))
296 if not result.success:
297 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
298
299
300@Command('verify_rootfs')
301def VerifyRootFs(options):
302 """Verify rootfs on SSD is valid by checking hash."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800303 script = FindScript('verify_rootfs.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800304 rootfs_device = GetReleaseRootPartitionPath()
305 result = Shell('%s %s' % (script, rootfs_device))
306 if not result.success:
307 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
308
309
310@Command('verify_switch_wp')
311def VerifyWpSwitch(options):
312 """Verify hardware write protection switch is enabled."""
313 if Shell('crossystem wpsw_cur').stdout.strip() != '1':
314 raise Error, 'write protection is disabled'
315
316
317@Command('verify_switch_dev')
318def VerifyDevSwitch(options):
319 """Verify developer switch is disabled."""
Hung-Te Lin8f643e32012-05-23 18:40:47 +0800320 result = Shell('crossystem devsw_cur')
321 if result.success:
322 if result.stdout.strip() != '0':
323 raise Error, 'developer mode is enabled'
324 else:
325 return
326 # Try ChromeOS-EC. This may hang 15 seconds if the EC does not respond.
327 logging.warn('VerifyDevSwitch: Trying ChromeOS-EC...')
328 if not Shell('ectool vboot 0').success:
329 raise Error, 'failed to turn off developer mode.'
330 # TODO(hungte) Verify if the switch is turned off properly, using "ectoo
331 # vboot" and parse the key-value pairs, when the names are determined.
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800332
333
334@Command('write_protect')
335def EnableFwWp(options):
336 """Enable then verify firmware write protection."""
337
338 def WriteProtect(fw_file_path, fw_type, section):
339 """Calculate protection size, then invoke flashrom.
340
341 Our supported chips only allow write protecting half their total
342 size, so we parition the flash chipset space accordingly.
343 """
344 raw_image = open(fw_file_path, 'rb').read()
345 image = crosfw.FirmwareImage(raw_image)
346 if not image.has_section(section):
347 raise Error('could not find %s firmware section %s' % (fw_type, section))
348 section_data = image.get_section_area(section)
349 protectable_size = len(raw_image) / 2
350 ro_a = int(section_data[0] / protectable_size)
351 ro_b = int((section_data[0] + section_data[1] - 1) / protectable_size)
352 if ro_a != ro_b:
353 raise Error("%s firmware section %s has illegal size" %
354 (fw_type, section))
355 ro_offset = ro_a * protectable_size
356 logging.debug('write protecting %s', fw_type)
357 crosfw.Flashrom(fw_type).EnableWriteProtection(ro_offset, protectable_size)
358
359 WriteProtect(crosfw.LoadMainFirmware().GetFileName(), 'main', 'RO_SECTION')
Tammo Spalink86a61c62012-05-25 15:10:35 +0800360 _event_log.Log('wp', fw='main')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800361 ec_fw_file = crosfw.LoadEcFirmware().GetFileName()
362 if ec_fw_file is not None:
363 WriteProtect(ec_fw_file, 'ec', 'EC_RO')
Tammo Spalink86a61c62012-05-25 15:10:35 +0800364 _event_log.Log('wp', fw='ec')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800365 else:
366 logging.warning('EC not write protected (seems there is no EC flash).')
367
368
369@Command('clear_gbb_flags')
370def ClearGbbFlags(options):
371 """Zero out the GBB flags, in preparation for transition to release state.
372
373 No GBB flags are set in release/shipping state, but they are useful
374 for factory/development. See "gbb_utility --flags" for details.
375 """
Tammo Spalink461ddce2012-05-10 19:28:55 +0800376 script = FindScript('clear_gbb_flags.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800377 result = Shell(script)
378 if not result.success:
379 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
Tammo Spalink86a61c62012-05-25 15:10:35 +0800380 _event_log.Log('clear_gbb_flags')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800381
382
383@Command('prepare_wipe',
384 CmdArg('--fast', action='store_true',
385 help='use non-secure but faster wipe method.'))
386def PrepareWipe(options):
387 """Prepare system for transition to release state in next reboot."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800388 script = FindScript('prepare_wipe.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800389 tag = 'fast' if options.fast else ''
390 rootfs_device = GetReleaseRootPartitionPath()
391 result = Shell('FACTORY_WIPE_TAGS=%s %s %s' % (tag, script, rootfs_device))
392 if not result.success:
393 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
394
395
396@Command('verify',
397 CmdArg('--dev', action='store_true',
398 help='Do not verify switch state (dev mode and fw wp).'),
399 _hwdb_path_cmd_arg)
400def Verify(options):
401 """Verifies if whole factory process is ready for finalization.
402
403 This routine performs all the necessary checks to make sure the
404 device is ready to be finalized, but does not modify state. These
405 checks include dev switch, firmware write protection switch, hwid,
406 system time, keys, and root file system.
407 """
408 if not options.dev:
409 VerifyDevSwitch({})
410 VerifyWpSwitch({})
411 VerifyHwid(options)
412 VerifySystemTime({})
413 VerifyKeys({})
414 VerifyRootFs({})
415
416
Tammo Spalink86a61c62012-05-25 15:10:35 +0800417@Command('log_system_details')
418def LogSystemDetails(options):
419 """Write miscellaneous system details to the event log."""
420 raw_cs_data = Shell('crossystem').stdout.strip().splitlines()
421 # The crossytem output contains many lines like:
422 # 'key = value # description'
423 # Use regexps to pull out the key-value pairs and build a dict.
424 cs_data = dict((k, v.strip()) for k, v in
425 map(lambda x: re.findall(r'\A(\S+)\s+=\s+(.*)#.*\Z', x)[0],
426 raw_cs_data))
427 _event_log.Log(
428 'system_details',
429 platform_name=Shell('mosys platform name').stdout.strip(),
430 crossystem=cs_data,
431 modem_status=Shell('modem status').stdout.splitlines(),
432 ec_wp_status=Shell(
433 'flashrom -p internal:bus=lpc --get-size 2>/dev/null && '
434 'flashrom -p internal:bus=lpc --wp-status || '
435 'echo "EC is not available."').stdout,
436 bios_wp_status = Shell(
437 'flashrom -p internal:bus=spi --wp-status').stdout)
438
439
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800440_upload_method_cmd_arg = CmdArg(
441 '--upload_method', metavar='METHOD:PARAM',
442 help=('How to perform the upload. METHOD should be one of '
443 '{ftp, shopfloor, curl, cpfe, custom}.'))
444
445
446@Command('upload_report',
447 _upload_method_cmd_arg)
448def UploadReport(options):
449 """Create and a report containing key device details."""
Tammo Spalink86a61c62012-05-25 15:10:35 +0800450 ro_vpd = ReadRoVpd(crosfw.LoadMainFirmware().GetFileName())
451 device_sn = ro_vpd.get('serial_number', None)
452 if device_sn is None:
453 logging.warning('RO_VPD missing device serial number')
454 device_sn = 'MISSING_SN_' + TimedUuid()
455 target_name = '%s_%s.tbz2' % (TimeString(), device_sn)
456 target_path = os.path.join(gettempdir(), target_name)
457 # Intentionally ignoring dotfiles in EVENT_LOG_DIR.
458 tar_cmd = 'cd %s ; tar cjf %s *' % (EVENT_LOG_DIR, target_path)
459 tar_cmd += ' --add-file %s' % FACTORY_LOG_PATH
460 cmd_result = Shell(tar_cmd)
461 if not cmd_result.success:
462 raise Error('unable to tar event logs, cmd %r failed, stderr: %r' %
463 (tar_cmd, cmd_result.stderr))
464 if options.upload_method is None or options.upload_method == 'none':
465 logging.warning('REPORT UPLOAD SKIPPED (report left at %s)', target_path)
466 return
467 method, param = options.upload_method.split(':', 1)
468 if method == 'shopfloor':
469 report_upload.ShopFloorUpload(target_path, param)
470 elif method == 'ftp':
471 report_upload.FtpUpload(target_path, 'ftp://' + param)
472 elif method == 'ftps':
473 report_upload.CurlUrlUpload(target_path, '--ftp-ssl-reqd ftp:%s' % param)
474 elif method == 'cpfe':
475 report_upload.CpfeUpload(target_path, param)
476 else:
477 raise Error('unknown report upload method %r', method)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800478
479
480@Command('finalize',
481 CmdArg('--dev', action='store_true',
482 help='Do not verify or alter write protection or dev mode.'),
483 CmdArg('--fast', action='store_true',
484 help='use non-secure but faster wipe method.'),
485 _hwdb_path_cmd_arg,
486 _upload_method_cmd_arg)
487def Finalize(options):
488 """Verify system readiness and trigger transition into release state.
489
490 This routine first verifies system state (see verify command), then
491 clears all of the testing flags from the GBB, then modifies firmware
492 bitmaps to match locale. Then it enables firmware write protection
493 and sets the necessary boot flags to cause wipe of the factory image
494 on the next boot.
495 """
496 ClearGbbFlags({})
497 Verify(options)
498 SetFirmwareBitmapLocale({})
499 if not options.dev:
500 EnableFwWp({})
Jon Salza0f58e02012-05-29 19:33:39 +0800501 LogSystemDetails(options)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800502 UploadReport(options)
503 PrepareWipe(options)
504
505
506def Main():
507 """Run sub-command specified by the command line args."""
508 options = ParseCmdline(
509 'Perform Google required factory tests.',
510 CmdArg('-l', '--log', metavar='PATH',
511 help='Write logs to this file.'),
512 verbosity_cmd_arg
513 )
514 SetupLogging(options.verbosity, options.log)
515 logging.debug('gooftool options: %s', repr(options))
516 try:
517 logging.debug('GOOFTOOL command %r', options.command_name)
518 options.command(options)
519 logging.info('GOOFTOOL command %r SUCCESS', options.command_name)
520 except Error, e:
521 logging.exception(e)
522 sys.exit('GOOFTOOL command %r ERROR: %s' % (options.command_name, e))
523 except Exception, e:
524 logging.exception(e)
525 sys.exit('UNCAUGHT RUNTIME EXCEPTION %s' % e)
526
527
528if __name__ == '__main__':
529 Main()