Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import json | 5 import json |
| 6 import logging | 6 import logging |
| 7 import os | 7 import os |
| 8 import re | 8 import re |
| 9 import shutil | 9 import shutil |
| 10 import tempfile | 10 import tempfile |
| 11 | 11 |
| 12 from telemetry import page as page_module | 12 from telemetry import page as page_module |
| 13 from telemetry.util import cloud_storage | 13 from telemetry.util import cloud_storage |
| 14 | 14 |
| 15 | 15 |
| 16 def AssertValidCloudStorageBucket(bucket): | 16 def AssertValidCloudStorageBucket(bucket): |
| 17 is_valid = bucket in (None, | 17 is_valid = bucket in (None, |
| 18 cloud_storage.PUBLIC_BUCKET, | 18 cloud_storage.PUBLIC_BUCKET, |
| 19 cloud_storage.PARTNER_BUCKET, | 19 cloud_storage.PARTNER_BUCKET, |
| 20 cloud_storage.INTERNAL_BUCKET) | 20 cloud_storage.INTERNAL_BUCKET) |
| 21 if not is_valid: | 21 if not is_valid: |
| 22 raise ValueError("Cloud storage privacy bucket %s is invalid" % bucket) | 22 raise ValueError("Cloud storage privacy bucket %s is invalid" % bucket) |
| 23 | 23 |
| 24 | 24 |
| 25 # TODO(chrishenry): Rename this (and module) to wpr_archive_info.WprArchiveInfo | 25 class WprArchiveInfo(object): |
| 26 # and move to telemetry.user_story or telemetry.wpr or telemetry.core. | |
| 27 class PageSetArchiveInfo(object): | |
| 28 def __init__(self, file_path, data, bucket, ignore_archive=False): | 26 def __init__(self, file_path, data, bucket, ignore_archive=False): |
| 29 AssertValidCloudStorageBucket(bucket) | 27 AssertValidCloudStorageBucket(bucket) |
| 30 self._file_path = file_path | 28 self._file_path = file_path |
| 31 self._base_dir = os.path.dirname(file_path) | 29 self._base_dir = os.path.dirname(file_path) |
| 32 self._bucket = bucket | 30 self._bucket = bucket |
| 33 | 31 |
| 34 # Ensure directory exists. | 32 # Ensure directory exists. |
| 35 if not os.path.exists(self._base_dir): | 33 if not os.path.exists(self._base_dir): |
| 36 os.makedirs(self._base_dir) | 34 os.makedirs(self._base_dir) |
| 37 | 35 |
| 38 # Download all .wpr files. | 36 # Download all .wpr files. |
| 39 if not ignore_archive: | 37 if not ignore_archive: |
| 40 if not self._bucket: | 38 if not self._bucket: |
| 41 logging.warning('page_set in %s has no bucket specified, and cannot be' | 39 logging.warning('User story set in %s has no bucket specified, and ' |
| 42 'downloaded from cloud_storage.', file_path) | 40 'cannot be downloaded from cloud_storage.', file_path) |
| 43 else: | 41 else: |
| 44 for archive_path in data['archives']: | 42 for archive_path in data['archives']: |
| 45 archive_path = self._WprFileNameToPath(archive_path) | 43 archive_path = self._WprFileNameToPath(archive_path) |
| 46 try: | 44 try: |
| 47 cloud_storage.GetIfChanged(archive_path, bucket) | 45 cloud_storage.GetIfChanged(archive_path, bucket) |
| 48 except (cloud_storage.CredentialsError, | 46 except (cloud_storage.CredentialsError, |
| 49 cloud_storage.PermissionError): | 47 cloud_storage.PermissionError): |
| 50 if os.path.exists(archive_path): | 48 if os.path.exists(archive_path): |
| 51 # If the archive exists, assume the user recorded their own and | 49 # If the archive exists, assume the user recorded their own and |
| 52 # simply warn. | 50 # simply warn. |
| 53 logging.warning('Need credentials to update WPR archive: %s', | 51 logging.warning('Need credentials to update WPR archive: %s', |
| 54 archive_path) | 52 archive_path) |
| 55 | 53 |
| 56 # Map from the relative path (as it appears in the metadata file) of the | 54 # Map from the relative path (as it appears in the metadata file) of the |
| 57 # .wpr file to a list of page names it supports. | 55 # .wpr file to a list of user story names it supports. |
| 58 self._wpr_file_to_page_names = data['archives'] | 56 self._wpr_file_to_user_story_names = data['archives'] |
| 59 | 57 |
| 60 # Map from the page name to a relative path (as it appears in the metadata | 58 # Map from the user_story name to a relative path (as it appears |
| 61 # file) of the .wpr file. | 59 # in the metadata file) of the .wpr file. |
| 62 self._page_name_to_wpr_file = dict() | 60 self._user_story_name_to_wpr_file = dict() |
| 63 # Find out the wpr file names for each page. | 61 # Find out the wpr file names for each user_story. |
| 64 for wpr_file in data['archives']: | 62 for wpr_file in data['archives']: |
| 65 page_names = data['archives'][wpr_file] | 63 user_story_names = data['archives'][wpr_file] |
| 66 for page_name in page_names: | 64 for user_story_name in user_story_names: |
| 67 self._page_name_to_wpr_file[page_name] = wpr_file | 65 self._user_story_name_to_wpr_file[user_story_name] = wpr_file |
| 68 self.temp_target_wpr_file_path = None | 66 self.temp_target_wpr_file_path = None |
| 69 | 67 |
| 70 @classmethod | 68 @classmethod |
| 71 def FromFile(cls, file_path, bucket, ignore_archive=False): | 69 def FromFile(cls, file_path, bucket, ignore_archive=False): |
| 72 if os.path.exists(file_path): | 70 if os.path.exists(file_path): |
| 73 with open(file_path, 'r') as f: | 71 with open(file_path, 'r') as f: |
| 74 data = json.load(f) | 72 data = json.load(f) |
| 75 return cls(file_path, data, bucket, ignore_archive=ignore_archive) | 73 return cls(file_path, data, bucket, ignore_archive=ignore_archive) |
| 76 return cls(file_path, {'archives': {}}, bucket, | 74 return cls(file_path, {'archives': {}}, bucket, |
| 77 ignore_archive=ignore_archive) | 75 ignore_archive=ignore_archive) |
| 78 | 76 |
| 79 def WprFilePathForUserStory(self, story): | 77 def WprFilePathForUserStory(self, story): |
| 80 if self.temp_target_wpr_file_path: | 78 if self.temp_target_wpr_file_path: |
| 81 return self.temp_target_wpr_file_path | 79 return self.temp_target_wpr_file_path |
| 82 wpr_file = self._page_name_to_wpr_file.get(story.display_name, None) | 80 wpr_file = self._user_story_name_to_wpr_file.get(story.display_name, None) |
| 83 if wpr_file is None and isinstance(story, page_module.Page): | 81 if wpr_file is None and isinstance(story, page_module.Page): |
| 84 # Some old page sets always use the URL to identify a page rather than the | 82 # Some old pages always use the URL to identify a page rather than the |
| 85 # display_name, so try to look for that. | 83 # display_name, so try to look for that. |
| 86 wpr_file = self._page_name_to_wpr_file.get(story.url, None) | 84 wpr_file = self._user_story_name_to_wpr_file.get(story.url, None) |
| 87 if wpr_file: | 85 if wpr_file: |
| 88 return self._WprFileNameToPath(wpr_file) | 86 return self._WprFileNameToPath(wpr_file) |
| 89 return None | 87 return None |
| 90 | 88 |
| 91 def AddNewTemporaryRecording(self, temp_wpr_file_path=None): | 89 def AddNewTemporaryRecording(self, temp_wpr_file_path=None): |
| 92 if temp_wpr_file_path is None: | 90 if temp_wpr_file_path is None: |
| 93 temp_wpr_file_handle, temp_wpr_file_path = tempfile.mkstemp() | 91 temp_wpr_file_handle, temp_wpr_file_path = tempfile.mkstemp() |
| 94 os.close(temp_wpr_file_handle) | 92 os.close(temp_wpr_file_handle) |
| 95 self.temp_target_wpr_file_path = temp_wpr_file_path | 93 self.temp_target_wpr_file_path = temp_wpr_file_path |
| 96 | 94 |
| 97 def AddRecordedPages(self, pages, upload_to_cloud_storage=False): | 95 def AddRecordedUserStories(self, user_stories, upload_to_cloud_storage=False): |
| 98 if not pages: | 96 if not user_stories: |
| 99 os.remove(self.temp_target_wpr_file_path) | 97 os.remove(self.temp_target_wpr_file_path) |
| 100 return | 98 return |
| 101 | 99 |
| 102 (target_wpr_file, target_wpr_file_path) = self._NextWprFileName() | 100 (target_wpr_file, target_wpr_file_path) = self._NextWprFileName() |
| 103 for page in pages: | 101 for user_story in user_stories: |
| 104 self._SetWprFileForPage(page.display_name, target_wpr_file) | 102 self._SetWprFileForUserStory(user_story.display_name, target_wpr_file) |
|
nednguyen
2014/12/08 00:53:16
Do we have a check to make sure that all user stor
| |
| 105 shutil.move(self.temp_target_wpr_file_path, target_wpr_file_path) | 103 shutil.move(self.temp_target_wpr_file_path, target_wpr_file_path) |
| 106 | 104 |
| 107 # Update the hash file. | 105 # Update the hash file. |
| 108 with open(target_wpr_file_path + '.sha1', 'wb') as f: | 106 with open(target_wpr_file_path + '.sha1', 'wb') as f: |
| 109 f.write(cloud_storage.CalculateHash(target_wpr_file_path)) | 107 f.write(cloud_storage.CalculateHash(target_wpr_file_path)) |
| 110 f.flush() | 108 f.flush() |
| 111 | 109 |
| 112 self._WriteToFile() | 110 self._WriteToFile() |
| 113 self._DeleteAbandonedWprFiles() | 111 self._DeleteAbandonedWprFiles() |
| 114 | 112 |
| 115 # Upload to cloud storage | 113 # Upload to cloud storage |
| 116 if upload_to_cloud_storage: | 114 if upload_to_cloud_storage: |
| 117 if not self._bucket: | 115 if not self._bucket: |
| 118 logging.warning('PageSet must have bucket specified to upload pages to' | 116 logging.warning('UserStorySet must have bucket specified to upload ' |
| 119 ' cloud storage.') | 117 'user stories to cloud storage.') |
| 120 return | 118 return |
| 121 try: | 119 try: |
| 122 cloud_storage.Insert(self._bucket, target_wpr_file, | 120 cloud_storage.Insert(self._bucket, target_wpr_file, |
| 123 target_wpr_file_path) | 121 target_wpr_file_path) |
| 124 except cloud_storage.CloudStorageError, e: | 122 except cloud_storage.CloudStorageError, e: |
| 125 logging.warning('Failed to upload wpr file %s to cloud storage. ' | 123 logging.warning('Failed to upload wpr file %s to cloud storage. ' |
| 126 'Error:%s' % target_wpr_file_path, e) | 124 'Error:%s' % target_wpr_file_path, e) |
| 127 | 125 |
| 128 def _DeleteAbandonedWprFiles(self): | 126 def _DeleteAbandonedWprFiles(self): |
| 129 # Update the metadata so that the abandoned wpr files don't have empty page | 127 # Update the metadata so that the abandoned wpr files don't have |
| 130 # name arrays. | 128 # empty user story name arrays. |
| 131 abandoned_wpr_files = self._AbandonedWprFiles() | 129 abandoned_wpr_files = self._AbandonedWprFiles() |
| 132 for wpr_file in abandoned_wpr_files: | 130 for wpr_file in abandoned_wpr_files: |
| 133 del self._wpr_file_to_page_names[wpr_file] | 131 del self._wpr_file_to_user_story_names[wpr_file] |
| 134 # Don't fail if we're unable to delete some of the files. | 132 # Don't fail if we're unable to delete some of the files. |
| 135 wpr_file_path = self._WprFileNameToPath(wpr_file) | 133 wpr_file_path = self._WprFileNameToPath(wpr_file) |
| 136 try: | 134 try: |
| 137 os.remove(wpr_file_path) | 135 os.remove(wpr_file_path) |
| 138 except Exception: | 136 except Exception: |
| 139 logging.warning('Failed to delete file: %s' % wpr_file_path) | 137 logging.warning('Failed to delete file: %s' % wpr_file_path) |
| 140 | 138 |
| 141 def _AbandonedWprFiles(self): | 139 def _AbandonedWprFiles(self): |
| 142 abandoned_wpr_files = [] | 140 abandoned_wpr_files = [] |
| 143 for wpr_file, page_names in self._wpr_file_to_page_names.iteritems(): | 141 for wpr_file, user_story_names in ( |
| 144 if not page_names: | 142 self._wpr_file_to_user_story_names.iteritems()): |
| 143 if not user_story_names: | |
| 145 abandoned_wpr_files.append(wpr_file) | 144 abandoned_wpr_files.append(wpr_file) |
| 146 return abandoned_wpr_files | 145 return abandoned_wpr_files |
| 147 | 146 |
| 148 def _WriteToFile(self): | 147 def _WriteToFile(self): |
| 149 """Writes the metadata into the file passed as constructor parameter.""" | 148 """Writes the metadata into the file passed as constructor parameter.""" |
| 150 metadata = dict() | 149 metadata = dict() |
| 151 metadata['description'] = ( | 150 metadata['description'] = ( |
| 152 'Describes the Web Page Replay archives for a page set. Don\'t edit by ' | 151 'Describes the Web Page Replay archives for a user story set. ' |
| 153 'hand! Use record_wpr for updating.') | 152 'Don\'t edit by hand! Use record_wpr for updating.') |
| 154 metadata['archives'] = self._wpr_file_to_page_names.copy() | 153 metadata['archives'] = self._wpr_file_to_user_story_names.copy() |
| 155 # Don't write data for abandoned archives. | 154 # Don't write data for abandoned archives. |
| 156 abandoned_wpr_files = self._AbandonedWprFiles() | 155 abandoned_wpr_files = self._AbandonedWprFiles() |
| 157 for wpr_file in abandoned_wpr_files: | 156 for wpr_file in abandoned_wpr_files: |
| 158 del metadata['archives'][wpr_file] | 157 del metadata['archives'][wpr_file] |
| 159 | 158 |
| 160 with open(self._file_path, 'w') as f: | 159 with open(self._file_path, 'w') as f: |
| 161 json.dump(metadata, f, indent=4) | 160 json.dump(metadata, f, indent=4) |
| 162 f.flush() | 161 f.flush() |
| 163 | 162 |
| 164 def _WprFileNameToPath(self, wpr_file): | 163 def _WprFileNameToPath(self, wpr_file): |
| 165 return os.path.abspath(os.path.join(self._base_dir, wpr_file)) | 164 return os.path.abspath(os.path.join(self._base_dir, wpr_file)) |
| 166 | 165 |
| 167 def _NextWprFileName(self): | 166 def _NextWprFileName(self): |
| 168 """Creates a new file name for a wpr archive file.""" | 167 """Creates a new file name for a wpr archive file.""" |
| 169 # The names are of the format "some_thing_number.wpr". Read the numbers. | 168 # The names are of the format "some_thing_number.wpr". Read the numbers. |
| 170 highest_number = -1 | 169 highest_number = -1 |
| 171 base = None | 170 base = None |
| 172 for wpr_file in self._wpr_file_to_page_names: | 171 for wpr_file in self._wpr_file_to_user_story_names: |
| 173 match = re.match(r'(?P<BASE>.*)_(?P<NUMBER>[0-9]+)\.wpr', wpr_file) | 172 match = re.match(r'(?P<BASE>.*)_(?P<NUMBER>[0-9]+)\.wpr', wpr_file) |
| 174 if not match: | 173 if not match: |
| 175 raise Exception('Illegal wpr file name ' + wpr_file) | 174 raise Exception('Illegal wpr file name ' + wpr_file) |
| 176 highest_number = max(int(match.groupdict()['NUMBER']), highest_number) | 175 highest_number = max(int(match.groupdict()['NUMBER']), highest_number) |
| 177 if base and match.groupdict()['BASE'] != base: | 176 if base and match.groupdict()['BASE'] != base: |
| 178 raise Exception('Illegal wpr file name ' + wpr_file + | 177 raise Exception('Illegal wpr file name ' + wpr_file + |
| 179 ', doesn\'t begin with ' + base) | 178 ', doesn\'t begin with ' + base) |
| 180 base = match.groupdict()['BASE'] | 179 base = match.groupdict()['BASE'] |
| 181 if not base: | 180 if not base: |
| 182 # If we're creating a completely new info file, use the base name of the | 181 # If we're creating a completely new info file, use the base name of the |
| 183 # page set file. | 182 # user story set file. |
| 184 base = os.path.splitext(os.path.basename(self._file_path))[0] | 183 base = os.path.splitext(os.path.basename(self._file_path))[0] |
| 185 new_filename = '%s_%03d.wpr' % (base, highest_number + 1) | 184 new_filename = '%s_%03d.wpr' % (base, highest_number + 1) |
| 186 return new_filename, self._WprFileNameToPath(new_filename) | 185 return new_filename, self._WprFileNameToPath(new_filename) |
| 187 | 186 |
| 188 def _SetWprFileForPage(self, page_name, wpr_file): | 187 def _SetWprFileForUserStory(self, user_story_name, wpr_file): |
| 189 """For modifying the metadata when we're going to record a new archive.""" | 188 """For modifying the metadata when we're going to record a new archive.""" |
| 190 old_wpr_file = self._page_name_to_wpr_file.get(page_name, None) | 189 old_wpr_file = self._user_story_name_to_wpr_file.get(user_story_name, None) |
| 191 if old_wpr_file: | 190 if old_wpr_file: |
| 192 self._wpr_file_to_page_names[old_wpr_file].remove(page_name) | 191 self._wpr_file_to_user_story_names[old_wpr_file].remove(user_story_name) |
| 193 self._page_name_to_wpr_file[page_name] = wpr_file | 192 self._user_story_name_to_wpr_file[user_story_name] = wpr_file |
| 194 if wpr_file not in self._wpr_file_to_page_names: | 193 if wpr_file not in self._wpr_file_to_user_story_names: |
| 195 self._wpr_file_to_page_names[wpr_file] = [] | 194 self._wpr_file_to_user_story_names[wpr_file] = [] |
| 196 self._wpr_file_to_page_names[wpr_file].append(page_name) | 195 self._wpr_file_to_user_story_names[wpr_file].append(user_story_name) |
| OLD | NEW |