Merge lp:~harlowja/cloud-init/cloud-handler-finalizers into lp:~cloud-init-dev/cloud-init/trunk

Proposed by Joshua Harlow
Status: Merged
Merged at revision: 836
Proposed branch: lp:~harlowja/cloud-init/cloud-handler-finalizers
Merge into: lp:~cloud-init-dev/cloud-init/trunk
Diff against target: 203 lines (+87/-54)
3 files modified
cloudinit/handlers/__init__.py (+3/-4)
cloudinit/helpers.py (+4/-1)
cloudinit/stages.py (+80/-49)
To merge this branch: bzr merge lp:~harlowja/cloud-init/cloud-handler-finalizers
Reviewer Review Type Date Requested Status
cloud-init Commiters Pending
Review via email: mp+176024@code.launchpad.net
To post a comment you must log in.
835. By Joshua Harlow

Also handle custom handlers correctly.

836. By Joshua Harlow

Remove return not used.

837. By Joshua Harlow

Just use an initialized array.

838. By Joshua Harlow

Also make the dir handler registration a simple function.

839. By Joshua Harlow

Ensure what we are searching over is a directory.

840. By Joshua Harlow

Seperate fetching custom handlers from registering.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cloudinit/handlers/__init__.py'
2--- cloudinit/handlers/__init__.py 2013-06-19 06:44:00 +0000
3+++ cloudinit/handlers/__init__.py 2013-07-23 05:48:30 +0000
4@@ -151,10 +151,9 @@
5 try:
6 mod = fixup_handler(importer.import_module(modname))
7 call_begin(mod, pdata['data'], frequency)
8- # Only register and increment
9- # after the above have worked (so we don't if it
10- # fails)
11- handlers.register(mod)
12+ # Only register and increment after the above have worked, so we don't
13+ # register if it fails starting.
14+ handlers.register(mod, initialized=True)
15 pdata['handlercount'] = curcount + 1
16 except:
17 util.logexc(LOG, "Failed at registering python file: %s (part "
18
19=== modified file 'cloudinit/helpers.py'
20--- cloudinit/helpers.py 2013-06-19 06:44:00 +0000
21+++ cloudinit/helpers.py 2013-07-23 05:48:30 +0000
22@@ -281,6 +281,7 @@
23
24 def __init__(self):
25 self.registered = {}
26+ self.initialized = []
27
28 def __contains__(self, item):
29 return self.is_registered(item)
30@@ -291,11 +292,13 @@
31 def is_registered(self, content_type):
32 return content_type in self.registered
33
34- def register(self, mod):
35+ def register(self, mod, initialized=False):
36 types = set()
37 for t in mod.list_types():
38 self.registered[t] = mod
39 types.add(t)
40+ if initialized and mod not in self.initialized:
41+ self.initialized.append(mod)
42 return types
43
44 def _get_handler(self, content_type):
45
46=== modified file 'cloudinit/stages.py'
47--- cloudinit/stages.py 2013-06-19 06:44:00 +0000
48+++ cloudinit/stages.py 2013-07-23 05:48:30 +0000
49@@ -344,37 +344,53 @@
50 cdir = self.paths.get_cpath("handlers")
51 idir = self._get_ipath("handlers")
52
53- # Add the path to the plugins dir to the top of our list for import
54- # instance dir should be read before cloud-dir
55- if cdir and cdir not in sys.path:
56- sys.path.insert(0, cdir)
57- if idir and idir not in sys.path:
58- sys.path.insert(0, idir)
59+ # Add the path to the plugins dir to the top of our list for importing
60+ # new handlers.
61+ #
62+ # Note(harlowja): instance dir should be read before cloud-dir
63+ for d in [cdir, idir]:
64+ if d and d not in sys.path:
65+ sys.path.insert(0, d)
66
67 # Ensure datasource fetched before activation (just incase)
68 user_data_msg = self.datasource.get_userdata(True)
69
70+ def get_handlers_in_dir(path):
71+ # Attempts to register any handler modules under the given path.
72+ if not path or not os.path.isdir(path):
73+ return []
74+ potential_handlers = util.find_modules(path)
75+ handlers = []
76+ for (fname, mod_name) in potential_handlers.iteritems():
77+ try:
78+ mod_locs = importer.find_module(mod_name, [''],
79+ ['list_types',
80+ 'handle_part'])
81+ if not mod_locs:
82+ LOG.warn(("Could not find a valid user-data handler"
83+ " named %s in file %s"), mod_name, fname)
84+ continue
85+ mod = importer.import_module(mod_locs[0])
86+ mod = handlers.fixup_handler(mod)
87+ handlers.append((fname, mod))
88+ except Exception:
89+ util.logexc(LOG, "Failed to import handler from %s", fname)
90+ return handlers
91+
92 # This keeps track of all the active handlers
93 c_handlers = helpers.ContentHandlers()
94
95- # Add handlers in cdir
96- potential_handlers = util.find_modules(cdir)
97- for (fname, mod_name) in potential_handlers.iteritems():
98+ # Add any handlers in the cloud-dir
99+ for (fname, mod) in get_handlers_in_dir(cdir):
100 try:
101- mod_locs = importer.find_module(mod_name, [''],
102- ['list_types',
103- 'handle_part'])
104- if not mod_locs:
105- LOG.warn(("Could not find a valid user-data handler"
106- " named %s in file %s"), mod_name, fname)
107- continue
108- mod = importer.import_module(mod_locs[0])
109- mod = handlers.fixup_handler(mod)
110 types = c_handlers.register(mod)
111 LOG.debug("Added handler for %s from %s", types, fname)
112- except:
113+ except Exception:
114 util.logexc(LOG, "Failed to register handler from %s", fname)
115
116+ # Register any other handlers that come from the default set. This
117+ # is done after the cloud-dir handlers so that the cdir modules can
118+ # take over the default user-data handler content-types.
119 def_handlers = self._default_userdata_handlers()
120 applied_def_handlers = c_handlers.register_defaults(def_handlers)
121 if applied_def_handlers:
122@@ -383,36 +399,51 @@
123 # Form our cloud interface
124 data = self.cloudify()
125
126- # Init the handlers first
127- called = []
128- for (_ctype, mod) in c_handlers.iteritems():
129- if mod in called:
130- continue
131- handlers.call_begin(mod, data, frequency)
132- called.append(mod)
133-
134- # Walk the user data
135- part_data = {
136- 'handlers': c_handlers,
137- # Any new handlers that are encountered get writen here
138- 'handlerdir': idir,
139- 'data': data,
140- # The default frequency if handlers don't have one
141- 'frequency': frequency,
142- # This will be used when new handlers are found
143- # to help write there contents to files with numbered
144- # names...
145- 'handlercount': 0,
146- }
147- handlers.walk(user_data_msg, handlers.walker_callback, data=part_data)
148-
149- # Give callbacks opportunity to finalize
150- called = []
151- for (_ctype, mod) in c_handlers.iteritems():
152- if mod in called:
153- continue
154- handlers.call_end(mod, data, frequency)
155- called.append(mod)
156+ def init_handlers():
157+ # Init the handlers first
158+ for (_ctype, mod) in c_handlers.iteritems():
159+ if mod in c_handlers.initialized:
160+ # Avoid initing the same module twice (if said module
161+ # is registered to more than one content-type).
162+ continue
163+ handlers.call_begin(mod, data, frequency)
164+ c_handlers.initialized.append(mod)
165+
166+ def walk_handlers():
167+ # Walk the user data
168+ part_data = {
169+ 'handlers': c_handlers,
170+ # Any new handlers that are encountered get writen here
171+ 'handlerdir': idir,
172+ 'data': data,
173+ # The default frequency if handlers don't have one
174+ 'frequency': frequency,
175+ # This will be used when new handlers are found
176+ # to help write there contents to files with numbered
177+ # names...
178+ 'handlercount': 0,
179+ }
180+ handlers.walk(user_data_msg, handlers.walker_callback,
181+ data=part_data)
182+
183+ def finalize_handlers():
184+ # Give callbacks opportunity to finalize
185+ for (_ctype, mod) in c_handlers.iteritems():
186+ if mod not in c_handlers.initialized:
187+ # Said module was never inited in the first place, so lets
188+ # not attempt to finalize those that never got called.
189+ continue
190+ c_handlers.initialized.remove(mod)
191+ try:
192+ handlers.call_end(mod, data, frequency)
193+ except:
194+ util.logexc(LOG, "Failed to finalize handler: %s", mod)
195+
196+ try:
197+ init_handlers()
198+ walk_handlers()
199+ finally:
200+ finalize_handlers()
201
202 # Perform post-consumption adjustments so that
203 # modules that run during the init stage reflect