diff options
author | Raman Tenneti <rtenneti@google.com> | 2021-07-28 14:36:49 -0700 |
---|---|---|
committer | Raman Tenneti <rtenneti@google.com> | 2021-07-29 19:20:57 +0000 |
commit | 7954de13b79182e299163307c430ad96f9cdbd38 (patch) | |
tree | e9b8cf74daa7c4827cb55d63a90dfd040492c150 /git_config.py | |
parent | ae86a460222c34b2f9cd600e6d17f8fd4f467fae (diff) | |
download | git-repo-7954de13b79182e299163307c430ad96f9cdbd38.tar.gz |
sync: Added logging of repo sync state and config options for analysis.
git_config.py:
+ Added SyncAnalysisState class, which saves the following data
into the config object.
++ sys.argv, options, superproject's logging data.
++ repo.*, branch.* and remote.* parameters from config object.
++ current time as synctime.
++ Version number of the object.
+ All the keys for the above data are prepended with 'repo.syncstate.'
+ Added GetSyncAnalysisStateData and UpdateSyncAnalysisState methods
to GitConfig object to save/get the above data.
git_trace2_event_log.py:
+ Added LogConfigEvents method with code from DefParamRepoEvents
to log events.
sync.py:
+ superproject_logging_data is a dictionary that collects all the
superproject data that is to be logged as trace2 event.
+ Sync at the end logs the previously saved syncstate.* parameters
as previous_sync_state. Then it calls config's UpdateSyncAnalysisState
to save and log all the current options, superproject logged data.
docs/internal-fs-layout.md:
+ Added doc string explaining [repo.syncstate ...] sections of
.repo/manifests.git/config file.
test_git_config.py:
+ Added unit test for the new methods of GitConfig object.
Tested:
$ ./run_tests
$ repo_dev init --use-superproject -u https://android.googlesource.com/platform/manifest
Tested it by running the following command multiple times.
$ repo_dev sync -j 20
repo sync has finished successfully
Verified config file has [syncstate ...] data saved.
Bug: [google internal] b/188573450
Change-Id: I1f914ce50f3382111b72940ca56de7c41b53d460
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/313123
Tested-by: Raman Tenneti <rtenneti@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
Reviewed-by: Xin Li <delphij@google.com>
Diffstat (limited to 'git_config.py')
-rw-r--r-- | git_config.py | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/git_config.py b/git_config.py index 3eaf201c..05d824cb 100644 --- a/git_config.py +++ b/git_config.py | |||
@@ -13,6 +13,7 @@ | |||
13 | # limitations under the License. | 13 | # limitations under the License. |
14 | 14 | ||
15 | import contextlib | 15 | import contextlib |
16 | import datetime | ||
16 | import errno | 17 | import errno |
17 | from http.client import HTTPException | 18 | from http.client import HTTPException |
18 | import json | 19 | import json |
@@ -30,6 +31,10 @@ from repo_trace import Trace | |||
30 | from git_command import GitCommand | 31 | from git_command import GitCommand |
31 | from git_refs import R_CHANGES, R_HEADS, R_TAGS | 32 | from git_refs import R_CHANGES, R_HEADS, R_TAGS |
32 | 33 | ||
34 | # Prefix that is prepended to all the keys of SyncAnalysisState's data | ||
35 | # that is saved in the config. | ||
36 | SYNC_STATE_PREFIX = 'repo.syncstate.' | ||
37 | |||
33 | ID_RE = re.compile(r'^[0-9a-f]{40}$') | 38 | ID_RE = re.compile(r'^[0-9a-f]{40}$') |
34 | 39 | ||
35 | REVIEW_CACHE = dict() | 40 | REVIEW_CACHE = dict() |
@@ -262,6 +267,22 @@ class GitConfig(object): | |||
262 | self._branches[b.name] = b | 267 | self._branches[b.name] = b |
263 | return b | 268 | return b |
264 | 269 | ||
270 | def GetSyncAnalysisStateData(self): | ||
271 | """Returns data to be logged for the analysis of sync performance.""" | ||
272 | return {k: v for k, v in self.DumpConfigDict().items() if k.startswith(SYNC_STATE_PREFIX)} | ||
273 | |||
274 | def UpdateSyncAnalysisState(self, options, superproject_logging_data): | ||
275 | """Update Config's SYNC_STATE_PREFIX* data with the latest sync data. | ||
276 | |||
277 | Args: | ||
278 | options: Options passed to sync returned from optparse. See _Options(). | ||
279 | superproject_logging_data: A dictionary of superproject data that is to be logged. | ||
280 | |||
281 | Returns: | ||
282 | SyncAnalysisState object. | ||
283 | """ | ||
284 | return SyncAnalysisState(self, options, superproject_logging_data) | ||
285 | |||
265 | def GetSubSections(self, section): | 286 | def GetSubSections(self, section): |
266 | """List all subsection names matching $section.*.* | 287 | """List all subsection names matching $section.*.* |
267 | """ | 288 | """ |
@@ -717,3 +738,69 @@ class Branch(object): | |||
717 | def _Get(self, key, all_keys=False): | 738 | def _Get(self, key, all_keys=False): |
718 | key = 'branch.%s.%s' % (self.name, key) | 739 | key = 'branch.%s.%s' % (self.name, key) |
719 | return self._config.GetString(key, all_keys=all_keys) | 740 | return self._config.GetString(key, all_keys=all_keys) |
741 | |||
742 | |||
743 | class SyncAnalysisState: | ||
744 | """Configuration options related to logging of sync state for analysis. | ||
745 | |||
746 | This object is versioned. | ||
747 | """ | ||
748 | def __init__(self, config, options, superproject_logging_data): | ||
749 | """Initializes SyncAnalysisState. | ||
750 | |||
751 | Saves the following data into the |config| object. | ||
752 | - sys.argv, options, superproject's logging data. | ||
753 | - repo.*, branch.* and remote.* parameters from config object. | ||
754 | - Current time as synctime. | ||
755 | - Version number of the object. | ||
756 | |||
757 | All the keys saved by this object are prepended with SYNC_STATE_PREFIX. | ||
758 | |||
759 | Args: | ||
760 | config: GitConfig object to store all options. | ||
761 | options: Options passed to sync returned from optparse. See _Options(). | ||
762 | superproject_logging_data: A dictionary of superproject data that is to be logged. | ||
763 | """ | ||
764 | self._config = config | ||
765 | now = datetime.datetime.utcnow() | ||
766 | self._Set('main.synctime', now.isoformat() + 'Z') | ||
767 | self._Set('main.version', '1') | ||
768 | self._Set('sys.argv', sys.argv) | ||
769 | for key, value in superproject_logging_data.items(): | ||
770 | self._Set(f'superproject.{key}', value) | ||
771 | for key, value in options.__dict__.items(): | ||
772 | self._Set(f'options.{key}', value) | ||
773 | config_items = config.DumpConfigDict().items() | ||
774 | EXTRACT_NAMESPACES = {'repo', 'branch', 'remote'} | ||
775 | self._SetDictionary({k: v for k, v in config_items | ||
776 | if not k.startswith(SYNC_STATE_PREFIX) and | ||
777 | k.split('.', 1)[0] in EXTRACT_NAMESPACES}) | ||
778 | |||
779 | def _SetDictionary(self, data): | ||
780 | """Save all key/value pairs of |data| dictionary. | ||
781 | |||
782 | Args: | ||
783 | data: A dictionary whose key/value are to be saved. | ||
784 | """ | ||
785 | for key, value in data.items(): | ||
786 | self._Set(key, value) | ||
787 | |||
788 | def _Set(self, key, value): | ||
789 | """Set the |value| for a |key| in the |_config| member. | ||
790 | |||
791 | |key| is prepended with the value of SYNC_STATE_PREFIX constant. | ||
792 | |||
793 | Args: | ||
794 | key: Name of the key. | ||
795 | value: |value| could be of any type. If it is 'bool', it will be saved | ||
796 | as a Boolean and for all other types, it will be saved as a String. | ||
797 | """ | ||
798 | if value is None: | ||
799 | return | ||
800 | sync_key = f'{SYNC_STATE_PREFIX}{key}' | ||
801 | if isinstance(value, str): | ||
802 | self._config.SetString(sync_key, value) | ||
803 | elif isinstance(value, bool): | ||
804 | self._config.SetBoolean(sync_key, value) | ||
805 | else: | ||
806 | self._config.SetString(sync_key, str(value)) | ||