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 # TODO(aiolos): We should take this out of init if we reduce the number of | 36 # TODO(aiolos): We should take this out of init if we reduce the number of |
39 # supported code paths/configs using archive_info when switching over to | 37 # supported code paths/configs using archive_info when switching over to |
40 # user_stories. | 38 # user_stories. |
41 # Download all .wpr files. | 39 # Download all .wpr files. |
42 if not ignore_archive: | 40 if not ignore_archive: |
43 if not self._bucket: | 41 if not self._bucket: |
44 logging.warning('page_set in %s has no bucket specified, and cannot be' | 42 logging.warning('User story set in %s has no bucket specified, and ' |
45 'downloaded from cloud_storage.', file_path) | 43 'cannot be downloaded from cloud_storage.', file_path) |
46 else: | 44 else: |
47 for archive_path in data['archives']: | 45 for archive_path in data['archives']: |
48 archive_path = self._WprFileNameToPath(archive_path) | 46 archive_path = self._WprFileNameToPath(archive_path) |
49 try: | 47 try: |
50 cloud_storage.GetIfChanged(archive_path, bucket) | 48 cloud_storage.GetIfChanged(archive_path, bucket) |
51 except (cloud_storage.CredentialsError, | 49 except (cloud_storage.CredentialsError, |
52 cloud_storage.PermissionError): | 50 cloud_storage.PermissionError): |
53 if os.path.exists(archive_path): | 51 if os.path.exists(archive_path): |
54 # If the archive exists, assume the user recorded their own and | 52 # If the archive exists, assume the user recorded their own and |
55 # simply warn. | 53 # simply warn. |
56 logging.warning('Need credentials to update WPR archive: %s', | 54 logging.warning('Need credentials to update WPR archive: %s', |
57 archive_path) | 55 archive_path) |
58 else: | 56 else: |
59 logging.error("You either aren't authenticated or don't have " | 57 logging.error("You either aren't authenticated or don't have " |
60 "permission to use the archives for this page set." | 58 "permission to use the archives for this page set." |
61 "\nYou may need to run gsutil config") | 59 "\nYou may need to run gsutil config") |
62 raise | 60 raise |
63 | 61 |
64 # Map from the relative path (as it appears in the metadata file) of the | 62 # Map from the relative path (as it appears in the metadata file) of the |
65 # .wpr file to a list of page names it supports. | 63 # .wpr file to a list of user story names it supports. |
66 self._wpr_file_to_page_names = data['archives'] | 64 self._wpr_file_to_user_story_names = data['archives'] |
67 | 65 |
68 # Map from the page name to a relative path (as it appears in the metadata | 66 # Map from the user_story name to a relative path (as it appears |
69 # file) of the .wpr file. | 67 # in the metadata file) of the .wpr file. |
70 self._page_name_to_wpr_file = dict() | 68 self._user_story_name_to_wpr_file = dict() |
71 # Find out the wpr file names for each page. | 69 # Find out the wpr file names for each user_story. |
72 for wpr_file in data['archives']: | 70 for wpr_file in data['archives']: |
73 page_names = data['archives'][wpr_file] | 71 user_story_names = data['archives'][wpr_file] |
74 for page_name in page_names: | 72 for user_story_name in user_story_names: |
75 self._page_name_to_wpr_file[page_name] = wpr_file | 73 self._user_story_name_to_wpr_file[user_story_name] = wpr_file |
76 self.temp_target_wpr_file_path = None | 74 self.temp_target_wpr_file_path = None |
77 | 75 |
78 @classmethod | 76 @classmethod |
79 def FromFile(cls, file_path, bucket, ignore_archive=False): | 77 def FromFile(cls, file_path, bucket, ignore_archive=False): |
80 if os.path.exists(file_path): | 78 if os.path.exists(file_path): |
81 with open(file_path, 'r') as f: | 79 with open(file_path, 'r') as f: |
82 data = json.load(f) | 80 data = json.load(f) |
83 return cls(file_path, data, bucket, ignore_archive=ignore_archive) | 81 return cls(file_path, data, bucket, ignore_archive=ignore_archive) |
84 return cls(file_path, {'archives': {}}, bucket, | 82 return cls(file_path, {'archives': {}}, bucket, |
85 ignore_archive=ignore_archive) | 83 ignore_archive=ignore_archive) |
86 | 84 |
87 def WprFilePathForUserStory(self, story): | 85 def WprFilePathForUserStory(self, story): |
88 if self.temp_target_wpr_file_path: | 86 if self.temp_target_wpr_file_path: |
89 return self.temp_target_wpr_file_path | 87 return self.temp_target_wpr_file_path |
90 wpr_file = self._page_name_to_wpr_file.get(story.display_name, None) | 88 wpr_file = self._user_story_name_to_wpr_file.get(story.display_name, None) |
91 if wpr_file is None and isinstance(story, page_module.Page): | 89 if wpr_file is None and isinstance(story, page_module.Page): |
92 # Some old page sets always use the URL to identify a page rather than the | 90 # Some old pages always use the URL to identify a page rather than the |
93 # display_name, so try to look for that. | 91 # display_name, so try to look for that. |
94 wpr_file = self._page_name_to_wpr_file.get(story.url, None) | 92 wpr_file = self._user_story_name_to_wpr_file.get(story.url, None) |
95 if wpr_file: | 93 if wpr_file: |
96 return self._WprFileNameToPath(wpr_file) | 94 return self._WprFileNameToPath(wpr_file) |
97 return None | 95 return None |
98 | 96 |
99 def AddNewTemporaryRecording(self, temp_wpr_file_path=None): | 97 def AddNewTemporaryRecording(self, temp_wpr_file_path=None): |
100 if temp_wpr_file_path is None: | 98 if temp_wpr_file_path is None: |
101 temp_wpr_file_handle, temp_wpr_file_path = tempfile.mkstemp() | 99 temp_wpr_file_handle, temp_wpr_file_path = tempfile.mkstemp() |
102 os.close(temp_wpr_file_handle) | 100 os.close(temp_wpr_file_handle) |
103 self.temp_target_wpr_file_path = temp_wpr_file_path | 101 self.temp_target_wpr_file_path = temp_wpr_file_path |
104 | 102 |
105 def AddRecordedPages(self, pages, upload_to_cloud_storage=False): | 103 def AddRecordedUserStories(self, user_stories, upload_to_cloud_storage=False): |
106 if not pages: | 104 if not user_stories: |
107 os.remove(self.temp_target_wpr_file_path) | 105 os.remove(self.temp_target_wpr_file_path) |
108 return | 106 return |
109 | 107 |
110 (target_wpr_file, target_wpr_file_path) = self._NextWprFileName() | 108 (target_wpr_file, target_wpr_file_path) = self._NextWprFileName() |
111 for page in pages: | 109 for user_story in user_stories: |
112 self._SetWprFileForPage(page.display_name, target_wpr_file) | 110 self._SetWprFileForUserStory(user_story.display_name, target_wpr_file) |
113 shutil.move(self.temp_target_wpr_file_path, target_wpr_file_path) | 111 shutil.move(self.temp_target_wpr_file_path, target_wpr_file_path) |
114 | 112 |
115 # Update the hash file. | 113 # Update the hash file. |
116 with open(target_wpr_file_path + '.sha1', 'wb') as f: | 114 with open(target_wpr_file_path + '.sha1', 'wb') as f: |
117 f.write(cloud_storage.CalculateHash(target_wpr_file_path)) | 115 f.write(cloud_storage.CalculateHash(target_wpr_file_path)) |
118 f.flush() | 116 f.flush() |
119 | 117 |
120 self._WriteToFile() | 118 self._WriteToFile() |
121 self._DeleteAbandonedWprFiles() | 119 self._DeleteAbandonedWprFiles() |
122 | 120 |
123 # Upload to cloud storage | 121 # Upload to cloud storage |
124 if upload_to_cloud_storage: | 122 if upload_to_cloud_storage: |
125 if not self._bucket: | 123 if not self._bucket: |
126 logging.warning('PageSet must have bucket specified to upload pages to' | 124 logging.warning('UserStorySet must have bucket specified to upload ' |
127 ' cloud storage.') | 125 'user stories to cloud storage.') |
128 return | 126 return |
129 try: | 127 try: |
130 cloud_storage.Insert(self._bucket, target_wpr_file, | 128 cloud_storage.Insert(self._bucket, target_wpr_file, |
131 target_wpr_file_path) | 129 target_wpr_file_path) |
132 except cloud_storage.CloudStorageError, e: | 130 except cloud_storage.CloudStorageError, e: |
133 logging.warning('Failed to upload wpr file %s to cloud storage. ' | 131 logging.warning('Failed to upload wpr file %s to cloud storage. ' |
134 'Error:%s' % target_wpr_file_path, e) | 132 'Error:%s' % target_wpr_file_path, e) |
135 | 133 |
136 def _DeleteAbandonedWprFiles(self): | 134 def _DeleteAbandonedWprFiles(self): |
137 # Update the metadata so that the abandoned wpr files don't have empty page | 135 # Update the metadata so that the abandoned wpr files don't have |
138 # name arrays. | 136 # empty user story name arrays. |
139 abandoned_wpr_files = self._AbandonedWprFiles() | 137 abandoned_wpr_files = self._AbandonedWprFiles() |
140 for wpr_file in abandoned_wpr_files: | 138 for wpr_file in abandoned_wpr_files: |
141 del self._wpr_file_to_page_names[wpr_file] | 139 del self._wpr_file_to_user_story_names[wpr_file] |
142 # Don't fail if we're unable to delete some of the files. | 140 # Don't fail if we're unable to delete some of the files. |
143 wpr_file_path = self._WprFileNameToPath(wpr_file) | 141 wpr_file_path = self._WprFileNameToPath(wpr_file) |
144 try: | 142 try: |
145 os.remove(wpr_file_path) | 143 os.remove(wpr_file_path) |
146 except Exception: | 144 except Exception: |
147 logging.warning('Failed to delete file: %s' % wpr_file_path) | 145 logging.warning('Failed to delete file: %s' % wpr_file_path) |
148 | 146 |
149 def _AbandonedWprFiles(self): | 147 def _AbandonedWprFiles(self): |
150 abandoned_wpr_files = [] | 148 abandoned_wpr_files = [] |
151 for wpr_file, page_names in self._wpr_file_to_page_names.iteritems(): | 149 for wpr_file, user_story_names in ( |
152 if not page_names: | 150 self._wpr_file_to_user_story_names.iteritems()): |
| 151 if not user_story_names: |
153 abandoned_wpr_files.append(wpr_file) | 152 abandoned_wpr_files.append(wpr_file) |
154 return abandoned_wpr_files | 153 return abandoned_wpr_files |
155 | 154 |
156 def _WriteToFile(self): | 155 def _WriteToFile(self): |
157 """Writes the metadata into the file passed as constructor parameter.""" | 156 """Writes the metadata into the file passed as constructor parameter.""" |
158 metadata = dict() | 157 metadata = dict() |
159 metadata['description'] = ( | 158 metadata['description'] = ( |
160 'Describes the Web Page Replay archives for a page set. Don\'t edit by ' | 159 'Describes the Web Page Replay archives for a user story set. ' |
161 'hand! Use record_wpr for updating.') | 160 'Don\'t edit by hand! Use record_wpr for updating.') |
162 metadata['archives'] = self._wpr_file_to_page_names.copy() | 161 metadata['archives'] = self._wpr_file_to_user_story_names.copy() |
163 # Don't write data for abandoned archives. | 162 # Don't write data for abandoned archives. |
164 abandoned_wpr_files = self._AbandonedWprFiles() | 163 abandoned_wpr_files = self._AbandonedWprFiles() |
165 for wpr_file in abandoned_wpr_files: | 164 for wpr_file in abandoned_wpr_files: |
166 del metadata['archives'][wpr_file] | 165 del metadata['archives'][wpr_file] |
167 | 166 |
168 with open(self._file_path, 'w') as f: | 167 with open(self._file_path, 'w') as f: |
169 json.dump(metadata, f, indent=4) | 168 json.dump(metadata, f, indent=4) |
170 f.flush() | 169 f.flush() |
171 | 170 |
172 def _WprFileNameToPath(self, wpr_file): | 171 def _WprFileNameToPath(self, wpr_file): |
173 return os.path.abspath(os.path.join(self._base_dir, wpr_file)) | 172 return os.path.abspath(os.path.join(self._base_dir, wpr_file)) |
174 | 173 |
175 def _NextWprFileName(self): | 174 def _NextWprFileName(self): |
176 """Creates a new file name for a wpr archive file.""" | 175 """Creates a new file name for a wpr archive file.""" |
177 # The names are of the format "some_thing_number.wpr". Read the numbers. | 176 # The names are of the format "some_thing_number.wpr". Read the numbers. |
178 highest_number = -1 | 177 highest_number = -1 |
179 base = None | 178 base = None |
180 for wpr_file in self._wpr_file_to_page_names: | 179 for wpr_file in self._wpr_file_to_user_story_names: |
181 match = re.match(r'(?P<BASE>.*)_(?P<NUMBER>[0-9]+)\.wpr', wpr_file) | 180 match = re.match(r'(?P<BASE>.*)_(?P<NUMBER>[0-9]+)\.wpr', wpr_file) |
182 if not match: | 181 if not match: |
183 raise Exception('Illegal wpr file name ' + wpr_file) | 182 raise Exception('Illegal wpr file name ' + wpr_file) |
184 highest_number = max(int(match.groupdict()['NUMBER']), highest_number) | 183 highest_number = max(int(match.groupdict()['NUMBER']), highest_number) |
185 if base and match.groupdict()['BASE'] != base: | 184 if base and match.groupdict()['BASE'] != base: |
186 raise Exception('Illegal wpr file name ' + wpr_file + | 185 raise Exception('Illegal wpr file name ' + wpr_file + |
187 ', doesn\'t begin with ' + base) | 186 ', doesn\'t begin with ' + base) |
188 base = match.groupdict()['BASE'] | 187 base = match.groupdict()['BASE'] |
189 if not base: | 188 if not base: |
190 # If we're creating a completely new info file, use the base name of the | 189 # If we're creating a completely new info file, use the base name of the |
191 # page set file. | 190 # user story set file. |
192 base = os.path.splitext(os.path.basename(self._file_path))[0] | 191 base = os.path.splitext(os.path.basename(self._file_path))[0] |
193 new_filename = '%s_%03d.wpr' % (base, highest_number + 1) | 192 new_filename = '%s_%03d.wpr' % (base, highest_number + 1) |
194 return new_filename, self._WprFileNameToPath(new_filename) | 193 return new_filename, self._WprFileNameToPath(new_filename) |
195 | 194 |
196 def _SetWprFileForPage(self, page_name, wpr_file): | 195 def _SetWprFileForUserStory(self, user_story_name, wpr_file): |
197 """For modifying the metadata when we're going to record a new archive.""" | 196 """For modifying the metadata when we're going to record a new archive.""" |
198 old_wpr_file = self._page_name_to_wpr_file.get(page_name, None) | 197 old_wpr_file = self._user_story_name_to_wpr_file.get(user_story_name, None) |
199 if old_wpr_file: | 198 if old_wpr_file: |
200 self._wpr_file_to_page_names[old_wpr_file].remove(page_name) | 199 self._wpr_file_to_user_story_names[old_wpr_file].remove(user_story_name) |
201 self._page_name_to_wpr_file[page_name] = wpr_file | 200 self._user_story_name_to_wpr_file[user_story_name] = wpr_file |
202 if wpr_file not in self._wpr_file_to_page_names: | 201 if wpr_file not in self._wpr_file_to_user_story_names: |
203 self._wpr_file_to_page_names[wpr_file] = [] | 202 self._wpr_file_to_user_story_names[wpr_file] = [] |
204 self._wpr_file_to_page_names[wpr_file].append(page_name) | 203 self._wpr_file_to_user_story_names[wpr_file].append(user_story_name) |
OLD | NEW |