Merge ~smoser/cloud-init:azure-dhcp into cloud-init:master
- Git
- lp:~smoser/cloud-init
- azure-dhcp
- Merge into master
Proposed by
Scott Moser
Status: | Merged |
---|---|
Approved by: | Scott Moser |
Approved revision: | d010217d6961756e8c3fe95c274c2b59765c8cf9 |
Merged at revision: | 648dbbf6b090c81e989f1ab70bf99f4de16a6a70 |
Proposed branch: | ~smoser/cloud-init:azure-dhcp |
Merge into: | cloud-init:master |
Diff against target: |
552 lines (+277/-42) 12 files modified
cloudinit/atomic_helper.py (+25/-0) cloudinit/cmd/main.py (+27/-18) cloudinit/dhclient_hook.py (+50/-0) cloudinit/sources/DataSourceAzure.py (+10/-5) cloudinit/sources/helpers/azure.py (+87/-12) config/cloud.cfg (+6/-0) doc/sources/azure/README.rst (+28/-4) setup.py (+2/-0) tests/unittests/test_datasource/test_azure_helper.py (+12/-3) tools/hook-dhclient (+9/-0) tools/hook-network-manager (+9/-0) tools/hook-rhel.sh (+12/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brent Baude | Pending | ||
cloud-init Commiters | Pending | ||
Review via email: mp+302604@code.launchpad.net |
Commit message
Description of the change
A few small set of changes from
https:/
To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote : | # |
Revision history for this message
Brent Baude (bbaude) wrote : | # |
Scott,
Looks good. Sniff tested on RHEL and Ubuntu on Azure.
There was an error fetching revisions from git servers. Please try again in a few minutes. If the problem persists, contact Launchpad support.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/cloudinit/atomic_helper.py b/cloudinit/atomic_helper.py |
2 | new file mode 100644 |
3 | index 0000000..15319f7 |
4 | --- /dev/null |
5 | +++ b/cloudinit/atomic_helper.py |
6 | @@ -0,0 +1,25 @@ |
7 | +#!/usr/bin/python |
8 | +# vi: ts=4 expandtab |
9 | + |
10 | +import json |
11 | +import os |
12 | +import tempfile |
13 | + |
14 | + |
15 | +def atomic_write_file(path, content, mode='w'): |
16 | + tf = None |
17 | + try: |
18 | + tf = tempfile.NamedTemporaryFile(dir=os.path.dirname(path), |
19 | + delete=False, mode=mode) |
20 | + tf.write(content) |
21 | + tf.close() |
22 | + os.rename(tf.name, path) |
23 | + except Exception as e: |
24 | + if tf is not None: |
25 | + os.unlink(tf.name) |
26 | + raise e |
27 | + |
28 | + |
29 | +def atomic_write_json(path, data): |
30 | + return atomic_write_file(path, json.dumps(data, indent=1, |
31 | + sort_keys=True) + "\n") |
32 | diff --git a/cloudinit/cmd/main.py b/cloudinit/cmd/main.py |
33 | index 63621c1..ba22b16 100644 |
34 | --- a/cloudinit/cmd/main.py |
35 | +++ b/cloudinit/cmd/main.py |
36 | @@ -25,7 +25,6 @@ import argparse |
37 | import json |
38 | import os |
39 | import sys |
40 | -import tempfile |
41 | import time |
42 | import traceback |
43 | |
44 | @@ -47,6 +46,10 @@ from cloudinit.reporting import events |
45 | from cloudinit.settings import (PER_INSTANCE, PER_ALWAYS, PER_ONCE, |
46 | CLOUD_CONFIG) |
47 | |
48 | +from cloudinit.atomic_helper import atomic_write_json |
49 | + |
50 | +from cloudinit.dhclient_hook import LogDhclient |
51 | + |
52 | |
53 | # Pretty little cheetah formatted welcome message template |
54 | WELCOME_MSG_TPL = ("Cloud-init v. ${version} running '${action}' at " |
55 | @@ -452,22 +455,10 @@ def main_single(name, args): |
56 | return 0 |
57 | |
58 | |
59 | -def atomic_write_file(path, content, mode='w'): |
60 | - tf = None |
61 | - try: |
62 | - tf = tempfile.NamedTemporaryFile(dir=os.path.dirname(path), |
63 | - delete=False, mode=mode) |
64 | - tf.write(content) |
65 | - tf.close() |
66 | - os.rename(tf.name, path) |
67 | - except Exception as e: |
68 | - if tf is not None: |
69 | - os.unlink(tf.name) |
70 | - raise e |
71 | - |
72 | - |
73 | -def atomic_write_json(path, data): |
74 | - return atomic_write_file(path, json.dumps(data, indent=1) + "\n") |
75 | +def dhclient_hook(name, args): |
76 | + record = LogDhclient(args) |
77 | + record.check_hooks_dir() |
78 | + record.record() |
79 | |
80 | |
81 | def status_wrapper(name, args, data_d=None, link_d=None): |
82 | @@ -627,7 +618,6 @@ def main(sysv_args=None): |
83 | # This subcommand allows you to run a single module |
84 | parser_single = subparsers.add_parser('single', |
85 | help=('run a single module ')) |
86 | - parser_single.set_defaults(action=('single', main_single)) |
87 | parser_single.add_argument("--name", '-n', action="store", |
88 | help="module name to run", |
89 | required=True) |
90 | @@ -644,6 +634,16 @@ def main(sysv_args=None): |
91 | ' pass to this module')) |
92 | parser_single.set_defaults(action=('single', main_single)) |
93 | |
94 | + parser_dhclient = subparsers.add_parser('dhclient-hook', |
95 | + help=('run the dhclient hook' |
96 | + 'to record network info')) |
97 | + parser_dhclient.add_argument("net_action", |
98 | + help=('action taken on the interface')) |
99 | + parser_dhclient.add_argument("net_interface", |
100 | + help=('the network interface being acted' |
101 | + ' upon')) |
102 | + parser_dhclient.set_defaults(action=('dhclient_hook', dhclient_hook)) |
103 | + |
104 | args = parser.parse_args(args=sysv_args) |
105 | |
106 | try: |
107 | @@ -677,9 +677,18 @@ def main(sysv_args=None): |
108 | "running single module %s" % args.name) |
109 | report_on = args.report |
110 | |
111 | + elif name == 'dhclient_hook': |
112 | + rname, rdesc = ("dhclient-hook", |
113 | + "running dhclient-hook module") |
114 | + |
115 | args.reporter = events.ReportEventStack( |
116 | rname, rdesc, reporting_enabled=report_on) |
117 | + |
118 | with args.reporter: |
119 | return util.log_time( |
120 | logfunc=LOG.debug, msg="cloud-init mode '%s'" % name, |
121 | get_uptime=True, func=functor, args=(name, args)) |
122 | + |
123 | + |
124 | +if __name__ == '__main__': |
125 | + main(sys.argv) |
126 | diff --git a/cloudinit/dhclient_hook.py b/cloudinit/dhclient_hook.py |
127 | new file mode 100644 |
128 | index 0000000..9dcbe39 |
129 | --- /dev/null |
130 | +++ b/cloudinit/dhclient_hook.py |
131 | @@ -0,0 +1,50 @@ |
132 | +#!/usr/bin/python |
133 | +# vi: ts=4 expandtab |
134 | + |
135 | +import os |
136 | + |
137 | +from cloudinit.atomic_helper import atomic_write_json |
138 | +from cloudinit import log as logging |
139 | +from cloudinit import stages |
140 | + |
141 | +LOG = logging.getLogger(__name__) |
142 | + |
143 | + |
144 | +class LogDhclient(object): |
145 | + |
146 | + def __init__(self, cli_args): |
147 | + self.hooks_dir = self._get_hooks_dir() |
148 | + self.net_interface = cli_args.net_interface |
149 | + self.net_action = cli_args.net_action |
150 | + self.hook_file = os.path.join(self.hooks_dir, |
151 | + self.net_interface + ".json") |
152 | + |
153 | + @staticmethod |
154 | + def _get_hooks_dir(): |
155 | + i = stages.Init() |
156 | + return os.path.join(i.paths.get_runpath(), 'dhclient.hooks') |
157 | + |
158 | + def check_hooks_dir(self): |
159 | + if not os.path.exists(self.hooks_dir): |
160 | + os.makedirs(self.hooks_dir) |
161 | + else: |
162 | + # If the action is down and the json file exists, we need to |
163 | + # delete the file |
164 | + if self.net_action is 'down' and os.path.exists(self.hook_file): |
165 | + os.remove(self.hook_file) |
166 | + |
167 | + @staticmethod |
168 | + def get_vals(info): |
169 | + new_info = {} |
170 | + for k, v in info.items(): |
171 | + if k.startswith("DHCP4_") or k.startswith("new_"): |
172 | + key = (k.replace('DHCP4_', '').replace('new_', '')).lower() |
173 | + new_info[key] = v |
174 | + return new_info |
175 | + |
176 | + def record(self): |
177 | + envs = os.environ |
178 | + if self.hook_file is None: |
179 | + return |
180 | + atomic_write_json(self.hook_file, self.get_vals(envs)) |
181 | + LOG.debug("Wrote dhclient options in %s", self.hook_file) |
182 | diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py |
183 | index 8c7e867..a251fe0 100644 |
184 | --- a/cloudinit/sources/DataSourceAzure.py |
185 | +++ b/cloudinit/sources/DataSourceAzure.py |
186 | @@ -20,18 +20,17 @@ import base64 |
187 | import contextlib |
188 | import crypt |
189 | import fnmatch |
190 | +from functools import partial |
191 | import os |
192 | import os.path |
193 | import time |
194 | -import xml.etree.ElementTree as ET |
195 | - |
196 | from xml.dom import minidom |
197 | - |
198 | -from cloudinit.sources.helpers.azure import get_metadata_from_fabric |
199 | +import xml.etree.ElementTree as ET |
200 | |
201 | from cloudinit import log as logging |
202 | from cloudinit.settings import PER_ALWAYS |
203 | from cloudinit import sources |
204 | +from cloudinit.sources.helpers.azure import get_metadata_from_fabric |
205 | from cloudinit import util |
206 | |
207 | LOG = logging.getLogger(__name__) |
208 | @@ -107,6 +106,8 @@ def temporary_hostname(temp_hostname, cfg, hostname_command='hostname'): |
209 | |
210 | |
211 | class DataSourceAzureNet(sources.DataSource): |
212 | + FALLBACK_LEASE = '/var/lib/dhcp/dhclient.eth0.leases' |
213 | + |
214 | def __init__(self, sys_cfg, distro, paths): |
215 | sources.DataSource.__init__(self, sys_cfg, distro, paths) |
216 | self.seed_dir = os.path.join(paths.seed_dir, 'azure') |
217 | @@ -115,6 +116,8 @@ class DataSourceAzureNet(sources.DataSource): |
218 | self.ds_cfg = util.mergemanydict([ |
219 | util.get_cfg_by_path(sys_cfg, DS_CFG_PATH, {}), |
220 | BUILTIN_DS_CONFIG]) |
221 | + self.dhclient_lease_file = self.paths.cfgs.get('dhclient_lease', |
222 | + self.FALLBACK_LEASE) |
223 | |
224 | def __str__(self): |
225 | root = sources.DataSource.__str__(self) |
226 | @@ -226,7 +229,9 @@ class DataSourceAzureNet(sources.DataSource): |
227 | write_files(ddir, files, dirmode=0o700) |
228 | |
229 | if self.ds_cfg['agent_command'] == '__builtin__': |
230 | - metadata_func = get_metadata_from_fabric |
231 | + metadata_func = partial(get_metadata_from_fabric, |
232 | + fallback_lease_file=self. |
233 | + dhclient_lease_file) |
234 | else: |
235 | metadata_func = self.get_metadata_from_agent |
236 | try: |
237 | diff --git a/cloudinit/sources/helpers/azure.py b/cloudinit/sources/helpers/azure.py |
238 | index 63ccf10..6e43440 100644 |
239 | --- a/cloudinit/sources/helpers/azure.py |
240 | +++ b/cloudinit/sources/helpers/azure.py |
241 | @@ -1,3 +1,4 @@ |
242 | +import json |
243 | import logging |
244 | import os |
245 | import re |
246 | @@ -6,6 +7,7 @@ import struct |
247 | import tempfile |
248 | import time |
249 | |
250 | +from cloudinit import stages |
251 | from contextlib import contextmanager |
252 | from xml.etree import ElementTree |
253 | |
254 | @@ -187,19 +189,32 @@ class WALinuxAgentShim(object): |
255 | ' </Container>', |
256 | '</Health>']) |
257 | |
258 | - def __init__(self): |
259 | + def __init__(self, fallback_lease_file=None): |
260 | LOG.debug('WALinuxAgentShim instantiated...') |
261 | - self.endpoint = self.find_endpoint() |
262 | + self.dhcpoptions = None |
263 | + self._endpoint = None |
264 | self.openssl_manager = None |
265 | self.values = {} |
266 | + self.lease_file = fallback_lease_file |
267 | |
268 | def clean_up(self): |
269 | if self.openssl_manager is not None: |
270 | self.openssl_manager.clean_up() |
271 | |
272 | @staticmethod |
273 | - def get_ip_from_lease_value(lease_value): |
274 | - unescaped_value = lease_value.replace('\\', '') |
275 | + def _get_hooks_dir(): |
276 | + _paths = stages.Init() |
277 | + return os.path.join(_paths.paths.get_runpath(), "dhclient.hooks") |
278 | + |
279 | + @property |
280 | + def endpoint(self): |
281 | + if self._endpoint is None: |
282 | + self._endpoint = self.find_endpoint(self.lease_file) |
283 | + return self._endpoint |
284 | + |
285 | + @staticmethod |
286 | + def get_ip_from_lease_value(fallback_lease_value): |
287 | + unescaped_value = fallback_lease_value.replace('\\', '') |
288 | if len(unescaped_value) > 4: |
289 | hex_string = '' |
290 | for hex_pair in unescaped_value.split(':'): |
291 | @@ -213,15 +228,75 @@ class WALinuxAgentShim(object): |
292 | return socket.inet_ntoa(packed_bytes) |
293 | |
294 | @staticmethod |
295 | - def find_endpoint(): |
296 | - LOG.debug('Finding Azure endpoint...') |
297 | - content = util.load_file('/var/lib/dhcp/dhclient.eth0.leases') |
298 | - value = None |
299 | + def _get_value_from_leases_file(fallback_lease_file): |
300 | + leases = [] |
301 | + content = util.load_file(fallback_lease_file) |
302 | + LOG.debug("content is {}".format(content)) |
303 | for line in content.splitlines(): |
304 | if 'unknown-245' in line: |
305 | - value = line.strip(' ').split(' ', 2)[-1].strip(';\n"') |
306 | + # Example line from Ubuntu |
307 | + # option unknown-245 a8:3f:81:10; |
308 | + leases.append(line.strip(' ').split(' ', 2)[-1].strip(';\n"')) |
309 | + # Return the "most recent" one in the list |
310 | + if len(leases) < 1: |
311 | + return None |
312 | + else: |
313 | + return leases[-1] |
314 | + |
315 | + @staticmethod |
316 | + def _load_dhclient_json(): |
317 | + dhcp_options = {} |
318 | + hooks_dir = WALinuxAgentShim._get_hooks_dir() |
319 | + if not os.path.exists(hooks_dir): |
320 | + LOG.debug("%s not found.", hooks_dir) |
321 | + return None |
322 | + hook_files = [os.path.join(hooks_dir, x) |
323 | + for x in os.listdir(hooks_dir)] |
324 | + for hook_file in hook_files: |
325 | + try: |
326 | + name = os.path.basename(hook_file).replace('.json', '') |
327 | + dhcp_options[name] = json.loads(util.load_file((hook_file))) |
328 | + except ValueError: |
329 | + raise ValueError("%s is not valid JSON data", hook_file) |
330 | + return dhcp_options |
331 | + |
332 | + @staticmethod |
333 | + def _get_value_from_dhcpoptions(dhcp_options): |
334 | + if dhcp_options is None: |
335 | + return None |
336 | + # the MS endpoint server is given to us as DHPC option 245 |
337 | + _value = None |
338 | + for interface in dhcp_options: |
339 | + _value = dhcp_options[interface].get('unknown_245', None) |
340 | + if _value is not None: |
341 | + LOG.debug("Endpoint server found in dhclient options") |
342 | + break |
343 | + return _value |
344 | + |
345 | + @staticmethod |
346 | + def find_endpoint(fallback_lease_file=None): |
347 | + LOG.debug('Finding Azure endpoint...') |
348 | + value = None |
349 | + # Option-245 stored in /run/cloud-init/dhclient.hooks/<ifc>.json |
350 | + # a dhclient exit hook that calls cloud-init-dhclient-hook |
351 | + dhcp_options = WALinuxAgentShim._load_dhclient_json() |
352 | + value = WALinuxAgentShim._get_value_from_dhcpoptions(dhcp_options) |
353 | if value is None: |
354 | - raise ValueError('No endpoint found in DHCP config.') |
355 | + # Fallback and check the leases file if unsuccessful |
356 | + LOG.debug("Unable to find endpoint in dhclient logs. " |
357 | + " Falling back to check lease files") |
358 | + if fallback_lease_file is None: |
359 | + LOG.warn("No fallback lease file was specified.") |
360 | + value = None |
361 | + else: |
362 | + LOG.debug("Looking for endpoint in lease file %s", |
363 | + fallback_lease_file) |
364 | + value = WALinuxAgentShim._get_value_from_leases_file( |
365 | + fallback_lease_file) |
366 | + |
367 | + if value is None: |
368 | + raise ValueError('No endpoint found.') |
369 | + |
370 | endpoint_ip_address = WALinuxAgentShim.get_ip_from_lease_value(value) |
371 | LOG.debug('Azure endpoint found at %s', endpoint_ip_address) |
372 | return endpoint_ip_address |
373 | @@ -271,8 +346,8 @@ class WALinuxAgentShim(object): |
374 | LOG.info('Reported ready to Azure fabric.') |
375 | |
376 | |
377 | -def get_metadata_from_fabric(): |
378 | - shim = WALinuxAgentShim() |
379 | +def get_metadata_from_fabric(fallback_lease_file=None): |
380 | + shim = WALinuxAgentShim(fallback_lease_file=fallback_lease_file) |
381 | try: |
382 | return shim.register_with_azure_and_fetch_data() |
383 | finally: |
384 | diff --git a/config/cloud.cfg b/config/cloud.cfg |
385 | index 2d7fb47..93ef342 100644 |
386 | --- a/config/cloud.cfg |
387 | +++ b/config/cloud.cfg |
388 | @@ -98,6 +98,7 @@ system_info: |
389 | cloud_dir: /var/lib/cloud/ |
390 | templates_dir: /etc/cloud/templates/ |
391 | upstart_dir: /etc/init/ |
392 | + dhclient_lease: |
393 | package_mirrors: |
394 | - arches: [i386, amd64] |
395 | failsafe: |
396 | @@ -114,3 +115,8 @@ system_info: |
397 | primary: http://ports.ubuntu.com/ubuntu-ports |
398 | security: http://ports.ubuntu.com/ubuntu-ports |
399 | ssh_svcname: ssh |
400 | +datasource: |
401 | + Azure: |
402 | + set_hostname: False |
403 | + agent_command: __builtin__ |
404 | + |
405 | diff --git a/doc/sources/azure/README.rst b/doc/sources/azure/README.rst |
406 | index 8239d1f..48f3cc7 100644 |
407 | --- a/doc/sources/azure/README.rst |
408 | +++ b/doc/sources/azure/README.rst |
409 | @@ -9,10 +9,34 @@ Azure Platform |
410 | The azure cloud-platform provides initial data to an instance via an attached |
411 | CD formated in UDF. That CD contains a 'ovf-env.xml' file that provides some |
412 | information. Additional information is obtained via interaction with the |
413 | -"endpoint". The ip address of the endpoint is advertised to the instance |
414 | -inside of dhcp option 245. On ubuntu, that can be seen in |
415 | -/var/lib/dhcp/dhclient.eth0.leases as a colon delimited hex value (example: |
416 | -``option unknown-245 64:41:60:82;`` is 100.65.96.130) |
417 | +"endpoint". |
418 | + |
419 | +To find the endpoint, we now leverage the dhcp client's ability to log its |
420 | +known values on exit. The endpoint server is special DHCP option 245. |
421 | +Depending on your networking stack, this can be done |
422 | +by calling a script in /etc/dhcp/dhclient-exit-hooks or a file in |
423 | +/etc/NetworkManager/dispatcher.d. Both of these call a sub-command |
424 | +'dhclient_hook' of cloud-init itself. This sub-command will write the client |
425 | +information in json format to /run/cloud-init/dhclient.hook/<interface>.json. |
426 | + |
427 | +In order for cloud-init to leverage this method to find the endpoint, the |
428 | +cloud.cfg file must contain: |
429 | + |
430 | +datasource: |
431 | + Azure: |
432 | + set_hostname: False |
433 | + agent_command: __builtin__ |
434 | + |
435 | +If those files are not available, the fallback is to check the leases file |
436 | +for the endpoint server (again option 245). |
437 | + |
438 | +You can define the path to the lease file with the 'dhclient_lease' configuration |
439 | +value under system_info: and paths:. For example: |
440 | + |
441 | + dhclient_lease: /var/lib/dhcp/dhclient.eth0.leases |
442 | + |
443 | +If no configuration value is provided, the dhclient_lease value will fallback to |
444 | +/var/lib/dhcp/dhclient.eth0.leases. |
445 | |
446 | walinuxagent |
447 | ------------ |
448 | diff --git a/setup.py b/setup.py |
449 | index 4abbb67..bbadd7b 100755 |
450 | --- a/setup.py |
451 | +++ b/setup.py |
452 | @@ -176,6 +176,8 @@ else: |
453 | (ETC + '/cloud', glob('config/*.cfg')), |
454 | (ETC + '/cloud/cloud.cfg.d', glob('config/cloud.cfg.d/*')), |
455 | (ETC + '/cloud/templates', glob('templates/*')), |
456 | + (ETC + '/NetworkManager/dispatcher.d/', ['tools/hook-network-manager']), |
457 | + (ETC + '/dhcp/dhclient-exit-hooks.d/', ['tools/hook-dhclient']), |
458 | (USR_LIB_EXEC + '/cloud-init', ['tools/uncloud-init', |
459 | 'tools/write-ssh-key-fingerprints']), |
460 | (USR + '/share/doc/cloud-init', [f for f in glob('doc/*') if is_f(f)]), |
461 | diff --git a/tests/unittests/test_datasource/test_azure_helper.py b/tests/unittests/test_datasource/test_azure_helper.py |
462 | index 65202ff..64523e1 100644 |
463 | --- a/tests/unittests/test_datasource/test_azure_helper.py |
464 | +++ b/tests/unittests/test_datasource/test_azure_helper.py |
465 | @@ -54,13 +54,17 @@ class TestFindEndpoint(TestCase): |
466 | self.load_file = patches.enter_context( |
467 | mock.patch.object(azure_helper.util, 'load_file')) |
468 | |
469 | + self.dhcp_options = patches.enter_context( |
470 | + mock.patch.object(azure_helper.WALinuxAgentShim, |
471 | + '_load_dhclient_json')) |
472 | + |
473 | def test_missing_file(self): |
474 | - self.load_file.side_effect = IOError |
475 | - self.assertRaises(IOError, |
476 | + self.assertRaises(ValueError, |
477 | azure_helper.WALinuxAgentShim.find_endpoint) |
478 | |
479 | def test_missing_special_azure_line(self): |
480 | self.load_file.return_value = '' |
481 | + self.dhcp_options.return_value = {'eth0': {'key': 'value'}} |
482 | self.assertRaises(ValueError, |
483 | azure_helper.WALinuxAgentShim.find_endpoint) |
484 | |
485 | @@ -72,13 +76,18 @@ class TestFindEndpoint(TestCase): |
486 | ' option unknown-245 {0};'.format(encoded_address), |
487 | '}']) |
488 | |
489 | + def test_from_dhcp_client(self): |
490 | + self.dhcp_options.return_value = {"eth0": {"unknown_245": "5:4:3:2"}} |
491 | + self.assertEqual('5.4.3.2', |
492 | + azure_helper.WALinuxAgentShim.find_endpoint(None)) |
493 | + |
494 | def test_latest_lease_used(self): |
495 | encoded_addresses = ['5:4:3:2', '4:3:2:1'] |
496 | file_content = '\n'.join([self._build_lease_content(encoded_address) |
497 | for encoded_address in encoded_addresses]) |
498 | self.load_file.return_value = file_content |
499 | self.assertEqual(encoded_addresses[-1].replace(':', '.'), |
500 | - azure_helper.WALinuxAgentShim.find_endpoint()) |
501 | + azure_helper.WALinuxAgentShim.find_endpoint("foobar")) |
502 | |
503 | |
504 | class TestExtractIpAddressFromLeaseValue(TestCase): |
505 | diff --git a/tools/hook-dhclient b/tools/hook-dhclient |
506 | new file mode 100755 |
507 | index 0000000..d099979 |
508 | --- /dev/null |
509 | +++ b/tools/hook-dhclient |
510 | @@ -0,0 +1,9 @@ |
511 | +#!/bin/sh |
512 | +# This script writes DHCP lease information into the cloud-init run directory |
513 | +# It is sourced, not executed. For more information see dhclient-script(8). |
514 | + |
515 | +case "$reason" in |
516 | + BOUND) cloud-init dhclient-hook up "$interface";; |
517 | + DOWN|RELEASE|REBOOT|STOP|EXPIRE) |
518 | + cloud-init dhclient-hook down "$interface";; |
519 | +esac |
520 | diff --git a/tools/hook-network-manager b/tools/hook-network-manager |
521 | new file mode 100755 |
522 | index 0000000..447b134 |
523 | --- /dev/null |
524 | +++ b/tools/hook-network-manager |
525 | @@ -0,0 +1,9 @@ |
526 | +#!/bin/sh |
527 | +# This script hooks into NetworkManager(8) via its scripts |
528 | +# arguments are 'interface-name' and 'action' |
529 | +# |
530 | + |
531 | +case "$1:$2" in |
532 | + *:up) exec cloud-init dhclient-hook up "$1";; |
533 | + *:down) exec cloud-init dhclient-hook down "$1";; |
534 | +esac |
535 | diff --git a/tools/hook-rhel.sh b/tools/hook-rhel.sh |
536 | new file mode 100755 |
537 | index 0000000..5e963a8 |
538 | --- /dev/null |
539 | +++ b/tools/hook-rhel.sh |
540 | @@ -0,0 +1,12 @@ |
541 | +#!/bin/sh |
542 | +# Current versions of RHEL and CentOS do not honor the directory |
543 | +# /etc/dhcp/dhclient-exit-hooks.d so this file can be placed in |
544 | +# /etc/dhcp/dhclient.d instead |
545 | + |
546 | +hook-rhel_config(){ |
547 | + cloud-init dhclient-hook up "$interface" |
548 | +} |
549 | + |
550 | +hook-rhel_restore(){ |
551 | + cloud-init dhclient-hook down "$interface" |
552 | +} |
Brent,
I've made a few small changes to what we had discussed in the bzr merge proposal.
I think this is good to go, but your test would be wonderful.