OLD | NEW |
1 import os, copy, logging, errno, fcntl, time, re, weakref, traceback | 1 import os, copy, logging, errno, fcntl, time, re, weakref, traceback |
2 import cPickle as pickle | 2 import cPickle as pickle |
3 | 3 |
4 from autotest_lib.client.common_lib import autotemp, error, log | 4 from autotest_lib.client.common_lib import autotemp, error, log |
5 | 5 |
6 | 6 |
7 class job_directory(object): | 7 class job_directory(object): |
8 """Represents a job.*dir directory.""" | 8 """Represents a job.*dir directory.""" |
9 | 9 |
10 | 10 |
(...skipping 404 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
415 return property(getter, setter) | 415 return property(getter, setter) |
416 | 416 |
417 | 417 |
418 class status_log_entry(object): | 418 class status_log_entry(object): |
419 """Represents a single status log entry.""" | 419 """Represents a single status log entry.""" |
420 | 420 |
421 RENDERED_NONE_VALUE = '----' | 421 RENDERED_NONE_VALUE = '----' |
422 TIMESTAMP_FIELD = 'timestamp' | 422 TIMESTAMP_FIELD = 'timestamp' |
423 LOCALTIME_FIELD = 'localtime' | 423 LOCALTIME_FIELD = 'localtime' |
424 | 424 |
425 # non-space whitespace is forbidden in any fields | |
426 BAD_CHAR_REGEX = re.compile(r'[\t\n\r\v\f]') | |
427 | |
428 def __init__(self, status_code, subdir, operation, message, fields, | 425 def __init__(self, status_code, subdir, operation, message, fields, |
429 timestamp=None): | 426 timestamp=None): |
430 """Construct a status.log entry. | 427 """Construct a status.log entry. |
431 | 428 |
432 @param status_code: A message status code. Must match the codes | 429 @param status_code: A message status code. Must match the codes |
433 accepted by autotest_lib.common_lib.log.is_valid_status. | 430 accepted by autotest_lib.common_lib.log.is_valid_status. |
434 @param subdir: A valid job subdirectory, or None. | 431 @param subdir: A valid job subdirectory, or None. |
435 @param operation: Description of the operation, or None. | 432 @param operation: Description of the operation, or None. |
436 @param message: A printable string describing event to be recorded. | 433 @param message: A printable string describing event to be recorded. |
437 @param fields: A dictionary of arbitrary alphanumeric key=value pairs | 434 @param fields: A dictionary of arbitrary alphanumeric key=value pairs |
438 to be included in the log, or None. | 435 to be included in the log, or None. |
439 @param timestamp: An optional integer timestamp, in the same format | 436 @param timestamp: An optional integer timestamp, in the same format |
440 as a time.time() timestamp. If unspecified, the current time is | 437 as a time.time() timestamp. If unspecified, the current time is |
441 used. | 438 used. |
442 | 439 |
443 @raise ValueError: if any of the parameters are invalid | 440 @raise ValueError: if any of the parameters are invalid |
444 """ | 441 """ |
| 442 # non-space whitespace is forbidden in any fields |
| 443 bad_char_regex = r'[\t\n\r\v\f]' |
445 | 444 |
446 if not log.is_valid_status(status_code): | 445 if not log.is_valid_status(status_code): |
447 raise ValueError('status code %r is not valid' % status_code) | 446 raise ValueError('status code %r is not valid' % status_code) |
448 self.status_code = status_code | 447 self.status_code = status_code |
449 | 448 |
450 if subdir and self.BAD_CHAR_REGEX.search(subdir): | 449 if subdir and re.search(bad_char_regex, subdir): |
451 raise ValueError('Invalid character in subdir string') | 450 raise ValueError('Invalid character in subdir string') |
452 self.subdir = subdir | 451 self.subdir = subdir |
453 | 452 |
454 if operation and self.BAD_CHAR_REGEX.search(operation): | 453 if operation and re.search(bad_char_regex, operation): |
455 raise ValueError('Invalid character in operation string') | 454 raise ValueError('Invalid character in operation string') |
456 self.operation = operation | 455 self.operation = operation |
457 | 456 |
458 # break the message line into a single-line message that goes into the | 457 # break the message line into a single-line message that goes into the |
459 # database, and a block of additional lines that goes into the status | 458 # database, and a block of additional lines that goes into the status |
460 # log but will never be parsed | 459 # log but will never be parsed |
461 message_lines = message.split('\n') | 460 message_lines = message.split('\n') |
462 self.message = message_lines[0].replace('\t', ' ' * 8) | 461 self.message = message_lines[0].replace('\t', ' ' * 8) |
463 self.extra_message_lines = message_lines[1:] | 462 self.extra_message_lines = message_lines[1:] |
464 if self.BAD_CHAR_REGEX.search(self.message): | 463 if re.search(bad_char_regex, self.message): |
465 raise ValueError('Invalid character in message %r' % self.message) | 464 raise ValueError('Invalid character in message %r' % self.message) |
466 | 465 |
467 if not fields: | 466 if not fields: |
468 self.fields = {} | 467 self.fields = {} |
469 else: | 468 else: |
470 self.fields = fields.copy() | 469 self.fields = fields.copy() |
471 for key, value in self.fields.iteritems(): | 470 for key, value in self.fields.iteritems(): |
472 if self.BAD_CHAR_REGEX.search(key + value): | 471 if re.search(bad_char_regex, key + value): |
473 raise ValueError('Invalid character in %r=%r field' | 472 raise ValueError('Invalid character in %r=%r field' |
474 % (key, value)) | 473 % (key, value)) |
475 | 474 |
476 # build up the timestamp | 475 # build up the timestamp |
477 if timestamp is None: | 476 if timestamp is None: |
478 timestamp = int(time.time()) | 477 timestamp = int(time.time()) |
479 self.fields[self.TIMESTAMP_FIELD] = str(timestamp) | 478 self.fields[self.TIMESTAMP_FIELD] = str(timestamp) |
480 self.fields[self.LOCALTIME_FIELD] = time.strftime( | 479 self.fields[self.LOCALTIME_FIELD] = time.strftime( |
481 '%b %d %H:%M:%S', time.localtime(timestamp)) | 480 '%b %d %H:%M:%S', time.localtime(timestamp)) |
482 | 481 |
(...skipping 508 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
991 """Record a job-level status event, using a status_log_entry. | 990 """Record a job-level status event, using a status_log_entry. |
992 | 991 |
993 This is the same as self.record but using an existing status log | 992 This is the same as self.record but using an existing status log |
994 entry object rather than constructing one for you. | 993 entry object rather than constructing one for you. |
995 | 994 |
996 @param entry: A status_log_entry object | 995 @param entry: A status_log_entry object |
997 @param log_in_subdir: A boolean that indicates (when true) that subdir | 996 @param log_in_subdir: A boolean that indicates (when true) that subdir |
998 logs should be written into the subdirectory status log file. | 997 logs should be written into the subdirectory status log file. |
999 """ | 998 """ |
1000 self._get_status_logger().record_entry(entry, log_in_subdir) | 999 self._get_status_logger().record_entry(entry, log_in_subdir) |
OLD | NEW |