Merge lp:~harlowja/cloud-init/smart-read into lp:~cloud-init-dev/cloud-init/trunk
- smart-read
- Merge into trunk
Proposed by
Joshua Harlow
Status: | Merged |
---|---|
Merged at revision: | 1002 |
Proposed branch: | lp:~harlowja/cloud-init/smart-read |
Merge into: | lp:~cloud-init-dev/cloud-init/trunk |
Diff against target: |
264 lines (+100/-57) 2 files modified
cloudinit/sources/helpers/openstack.py (+80/-52) tests/unittests/test_datasource/test_openstack.py (+20/-5) |
To merge this branch: | bzr merge lp:~harlowja/cloud-init/smart-read |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
cloud-init Commiters | Pending | ||
Review via email: mp+232316@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 1003. By Joshua Harlow
-
Add logging around failure to read optional/mandatory paths
- 1004. By Joshua Harlow
-
Just use os.path.exists directly
- 1005. By Joshua Harlow
-
Log a warning when unable to fetch the openstack versions
- 1006. By Joshua Harlow
-
Show the available versions in the debug log message
- 1007. By Joshua Harlow
-
Fixed more of the slowness around fetching and retrying
- 1008. By Joshua Harlow
-
Fix retry cb to reflect what used to exist
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'cloudinit/sources/helpers/openstack.py' |
2 | --- cloudinit/sources/helpers/openstack.py 2014-02-24 22:41:42 +0000 |
3 | +++ cloudinit/sources/helpers/openstack.py 2014-08-27 19:59:20 +0000 |
4 | @@ -150,17 +150,34 @@ |
5 | pass |
6 | |
7 | @abc.abstractmethod |
8 | - def _path_exists(self, path): |
9 | - pass |
10 | - |
11 | - @abc.abstractmethod |
12 | def _path_read(self, path): |
13 | pass |
14 | |
15 | @abc.abstractmethod |
16 | + def _fetch_available_versions(self): |
17 | + pass |
18 | + |
19 | + @abc.abstractmethod |
20 | def _read_ec2_metadata(self): |
21 | pass |
22 | |
23 | + def _find_working_version(self, version): |
24 | + search_versions = [version] + list(OS_VERSIONS) |
25 | + available_versions = self._fetch_available_versions() |
26 | + for potential_version in search_versions: |
27 | + if not potential_version: |
28 | + continue |
29 | + if potential_version not in available_versions: |
30 | + continue |
31 | + if potential_version != version: |
32 | + LOG.debug("Version '%s' not available, attempting to use" |
33 | + " version '%s' instead", version, |
34 | + potential_version) |
35 | + return potential_version |
36 | + LOG.debug("Version '%s' not available, attempting to use '%s'" |
37 | + " instead", version, OS_LATEST) |
38 | + return OS_LATEST |
39 | + |
40 | def _read_content_path(self, item): |
41 | path = item.get('content_path', '').lstrip("/") |
42 | path_pieces = path.split("/") |
43 | @@ -170,23 +187,6 @@ |
44 | path = self._path_join(self.base_path, "openstack", *path_pieces) |
45 | return self._path_read(path) |
46 | |
47 | - def _find_working_version(self, version): |
48 | - search_versions = [version] + list(OS_VERSIONS) |
49 | - for potential_version in search_versions: |
50 | - if not potential_version: |
51 | - continue |
52 | - path = self._path_join(self.base_path, "openstack", |
53 | - potential_version) |
54 | - if self._path_exists(path): |
55 | - if potential_version != version: |
56 | - LOG.debug("Version '%s' not available, attempting to use" |
57 | - " version '%s' instead", version, |
58 | - potential_version) |
59 | - return potential_version |
60 | - LOG.debug("Version '%s' not available, attempting to use '%s'" |
61 | - " instead", version, OS_LATEST) |
62 | - return OS_LATEST |
63 | - |
64 | def read_v2(self, version=None): |
65 | """Reads a version 2 formatted location. |
66 | |
67 | @@ -228,15 +228,18 @@ |
68 | path = self._path_join(self.base_path, path) |
69 | data = None |
70 | found = False |
71 | - if self._path_exists(path): |
72 | - try: |
73 | - data = self._path_read(path) |
74 | - except IOError: |
75 | - raise NonReadable("Failed to read: %s" % path) |
76 | + try: |
77 | + data = self._path_read(path) |
78 | + except IOError as e: |
79 | + if not required: |
80 | + LOG.debug("Failed reading optional path %s due" |
81 | + " to: %s", path, e) |
82 | + else: |
83 | + LOG.exception("Failed reading mandatory path %s", path) |
84 | + else: |
85 | found = True |
86 | - else: |
87 | - if required: |
88 | - raise NonReadable("Missing mandatory path: %s" % path) |
89 | + if required and not found: |
90 | + raise NonReadable("Missing mandatory path: %s" % path) |
91 | if found and translator: |
92 | try: |
93 | data = translator(data) |
94 | @@ -304,21 +307,35 @@ |
95 | class ConfigDriveReader(BaseReader): |
96 | def __init__(self, base_path): |
97 | super(ConfigDriveReader, self).__init__(base_path) |
98 | + self._versions = None |
99 | |
100 | def _path_join(self, base, *add_ons): |
101 | components = [base] + list(add_ons) |
102 | return os.path.join(*components) |
103 | |
104 | - def _path_exists(self, path): |
105 | - return os.path.exists(path) |
106 | - |
107 | def _path_read(self, path): |
108 | return util.load_file(path) |
109 | |
110 | + def _fetch_available_versions(self): |
111 | + if self._versions is not None: |
112 | + return self._versions |
113 | + else: |
114 | + versions_available = [] |
115 | + path = self._path_join(self.base_path, 'openstack') |
116 | + try: |
117 | + for child in os.listdir(path): |
118 | + child_path = os.path.join(path, child) |
119 | + if os.path.isdir(child_path): |
120 | + versions_available.append(child) |
121 | + except (OSError, IOError): |
122 | + pass |
123 | + self._versions = tuple(versions_available) |
124 | + return self._versions |
125 | + |
126 | def _read_ec2_metadata(self): |
127 | path = self._path_join(self.base_path, |
128 | 'ec2', 'latest', 'meta-data.json') |
129 | - if not self._path_exists(path): |
130 | + if not os.path.exists(path): |
131 | return {} |
132 | else: |
133 | try: |
134 | @@ -338,7 +355,7 @@ |
135 | found = {} |
136 | for name in FILES_V1.keys(): |
137 | path = self._path_join(self.base_path, name) |
138 | - if self._path_exists(path): |
139 | + if os.path.exists(path): |
140 | found[name] = path |
141 | if len(found) == 0: |
142 | raise NonReadable("%s: no files found" % (self.base_path)) |
143 | @@ -400,17 +417,31 @@ |
144 | self.ssl_details = ssl_details |
145 | self.timeout = float(timeout) |
146 | self.retries = int(retries) |
147 | + self._versions = None |
148 | + |
149 | + def _fetch_available_versions(self): |
150 | + if self._versions is not None: |
151 | + return self._versions |
152 | + else: |
153 | + path = self._path_join(self.base_path, "openstack") |
154 | + versions_available = [] |
155 | + try: |
156 | + versions = self._path_read(path) |
157 | + except IOError as e: |
158 | + LOG.warn("Unable to read openstack versions from %s due" |
159 | + " to: %s", path, e) |
160 | + else: |
161 | + for line in versions.splitlines(): |
162 | + line = line.strip() |
163 | + if not line: |
164 | + continue |
165 | + versions_available.append(line) |
166 | + self._versions = tuple(versions_available) |
167 | + return self._versions |
168 | |
169 | def _path_read(self, path): |
170 | - response = url_helper.readurl(path, |
171 | - retries=self.retries, |
172 | - ssl_details=self.ssl_details, |
173 | - timeout=self.timeout) |
174 | - return response.contents |
175 | - |
176 | - def _path_exists(self, path): |
177 | - |
178 | - def should_retry_cb(request, cause): |
179 | + |
180 | + def should_retry_cb(_request_args, cause): |
181 | try: |
182 | code = int(cause.code) |
183 | if code >= 400: |
184 | @@ -420,15 +451,12 @@ |
185 | pass |
186 | return True |
187 | |
188 | - try: |
189 | - response = url_helper.readurl(path, |
190 | - retries=self.retries, |
191 | - ssl_details=self.ssl_details, |
192 | - timeout=self.timeout, |
193 | - exception_cb=should_retry_cb) |
194 | - return response.ok() |
195 | - except IOError: |
196 | - return False |
197 | + response = url_helper.readurl(path, |
198 | + retries=self.retries, |
199 | + ssl_details=self.ssl_details, |
200 | + timeout=self.timeout, |
201 | + exception_cb=should_retry_cb) |
202 | + return response.contents |
203 | |
204 | def _path_join(self, base, *add_ons): |
205 | return url_helper.combine_url(base, *add_ons) |
206 | |
207 | === modified file 'tests/unittests/test_datasource/test_openstack.py' |
208 | --- tests/unittests/test_datasource/test_openstack.py 2014-08-22 15:40:25 +0000 |
209 | +++ tests/unittests/test_datasource/test_openstack.py 2014-08-27 19:59:20 +0000 |
210 | @@ -67,8 +67,8 @@ |
211 | CONTENT_0 = 'This is contents of /etc/foo.cfg\n' |
212 | CONTENT_1 = '# this is /etc/bar/bar.cfg\n' |
213 | OS_FILES = { |
214 | - 'openstack/2012-08-10/meta_data.json': json.dumps(OSTACK_META), |
215 | - 'openstack/2012-08-10/user_data': USER_DATA, |
216 | + 'openstack/latest/meta_data.json': json.dumps(OSTACK_META), |
217 | + 'openstack/latest/user_data': USER_DATA, |
218 | 'openstack/content/0000': CONTENT_0, |
219 | 'openstack/content/0001': CONTENT_1, |
220 | 'openstack/latest/meta_data.json': json.dumps(OSTACK_META), |
221 | @@ -78,6 +78,9 @@ |
222 | EC2_FILES = { |
223 | 'latest/user-data': USER_DATA, |
224 | } |
225 | +EC2_VERSIONS = [ |
226 | + 'lastest', |
227 | +] |
228 | |
229 | |
230 | def _register_uris(version, ec2_files, ec2_meta, os_files): |
231 | @@ -85,6 +88,9 @@ |
232 | same data returned by the openstack metadata service (and ec2 service).""" |
233 | |
234 | def match_ec2_url(uri, headers): |
235 | + path = uri.path.strip("/") |
236 | + if len(path) == 0: |
237 | + return (200, headers, "\n".join(EC2_VERSIONS)) |
238 | path = uri.path.lstrip("/") |
239 | if path in ec2_files: |
240 | return (200, headers, ec2_files.get(path)) |
241 | @@ -110,11 +116,20 @@ |
242 | return (200, headers, str(value)) |
243 | return (404, headers, '') |
244 | |
245 | + def match_os_uri(uri, headers): |
246 | + path = uri.path.strip("/") |
247 | + if path == 'openstack': |
248 | + return (200, headers, "\n".join([openstack.OS_LATEST])) |
249 | + path = uri.path.lstrip("/") |
250 | + if path in os_files: |
251 | + return (200, headers, os_files.get(path)) |
252 | + return (404, headers, '') |
253 | + |
254 | def get_request_callback(method, uri, headers): |
255 | uri = urlparse(uri) |
256 | - path = uri.path.lstrip("/") |
257 | - if path in os_files: |
258 | - return (200, headers, os_files.get(path)) |
259 | + path = uri.path.lstrip("/").split("/") |
260 | + if path[0] == 'openstack': |
261 | + return match_os_uri(uri, headers) |
262 | return match_ec2_url(uri, headers) |
263 | |
264 | hp.register_uri(hp.GET, re.compile(r'http://169.254.169.254/.*'), |