Merge lp:~andreserl/maas/consolidate_maas_ipmi_autodetect into lp:~maas-committers/maas/trunk

Proposed by Andres Rodriguez
Status: Merged
Approved by: Andres Rodriguez
Approved revision: no longer in the source branch.
Merged at revision: 1522
Proposed branch: lp:~andreserl/maas/consolidate_maas_ipmi_autodetect
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 651 lines (+223/-285)
9 files modified
contrib/preseeds_v2/enlist_userdata (+1/-141)
etc/maas/templates/commissioning-user-data/snippets/maas_ipmi_autodetect.py (+161/-0)
etc/maas/templates/commissioning-user-data/user_data.template (+1/-131)
src/maasserver/preseed.py (+6/-1)
src/maasserver/tests/test_preseed.py (+13/-4)
src/metadataserver/commissioning/snippets.py (+22/-0)
src/metadataserver/commissioning/tests/test_snippets.py (+12/-0)
src/metadataserver/commissioning/tests/test_user_data.py (+1/-0)
src/metadataserver/commissioning/user_data.py (+6/-8)
To merge this branch: bzr merge lp:~andreserl/maas/consolidate_maas_ipmi_autodetect
Reviewer Review Type Date Requested Status
Andres Rodriguez (community) Approve
Jeroen T. Vermeulen (community) Approve
Review via email: mp+167806@code.launchpad.net

Commit message

Consolidate the maas-ipmi-autodetect script by separating it from the preseeds (user_data.template, enlist_userdata) into its own snippet, and allowing the enlistment context to be able to access the commissioning snippets.

Additionally fix test_render_preseed and test_render_enlistment_preseed to reflect testing
the correct templates.

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Thanks for doing this. It's great to see so much duplication disappear! It may even make it easier, if we ever get to that, to write unit-tests for the code inside the user data.

I always try to come up with a few remarks. The test for get_snippet_context is the right thing to do; it's short but probably only because it's well-written. But it'd be nice to have a negative test as well, to ensure that the function doesn't return things that it shouldn't. But that's no reason to stop this branch from landing. If you like I could write one for you afterwards.

Another note is that Python docstrings normally start with a description in the imperative. So where your docstrings start with "Returns [something]" the preferred form is "Return [something]." Not urgent.

One other small thing that I would prefer you to change: with values that aren't booleans, explicit comparisons are clearer and more robust than evaluating them directly as Booleans. So in get_snippet_context, it's better to write "if snippets_dir is None:" than to write "if not snippets_dir:". It produces more easily predictable behaviour in edge cases or failure scenarios, such as empty strings.

Jeroen

review: Approve
Revision history for this message
Andres Rodriguez (andreserl) wrote :

Addressed comments and approving this branch for it to land.

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

The Jenkins job https://jenkins.qa.ubuntu.com/job/maas-merger-trunk/319/console reported an error when processing this lp:~andreserl/maas/consolidate_maas_ipmi_autodetect branch.
Not merging it.

Revision history for this message
Andres Rodriguez (andreserl) :
review: Approve
Revision history for this message
Andres Rodriguez (andreserl) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'contrib/preseeds_v2/enlist_userdata'
2--- contrib/preseeds_v2/enlist_userdata 2013-05-22 17:10:37 +0000
3+++ contrib/preseeds_v2/enlist_userdata 2013-06-12 17:17:25 +0000
4@@ -55,147 +55,7 @@
5 END_IPMI_CONFIG
6
7 add_bin "maas-ipmi-autodetect" <<"END_MAAS_IPMI_AUTODETECT"
8- #!/usr/bin/python
9- import os
10- import commands
11- import glob
12- import re
13- import string
14- import random
15- import time
16- import json
17-
18- def detect_ipmi():
19- # XXX: andreserl 2013-04-09 bug=1064527: Try to detect if node
20- # is a Virtual Machine. If it is, do not try to detect IPMI.
21- with open('/proc/cpuinfo', 'r') as cpuinfo:
22- for line in cpuinfo:
23- if line.startswith('model name') and 'QEMU' in line:
24- return (False, None)
25-
26- (status, output) = commands.getstatusoutput('ipmi-locate')
27- show_re = re.compile('(IPMI\ Version:) (\d\.\d)')
28- res = show_re.search(output)
29- if res == None:
30- found = glob.glob("/dev/ipmi[0-9]")
31- if len(found):
32- return (True, "UNKNOWN: %s" % " ".join(found))
33- return (False, "")
34- return (True, res.group(2))
35-
36- def is_ipmi_dhcp():
37- (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="Lan_Conf:IP_Address_Source"')
38- show_re = re.compile('IP_Address_Source\s+Use_DHCP')
39- res = show_re.search(output)
40- if res == None:
41- return False
42- return True
43-
44- def set_ipmi_network_source(source):
45- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="Lan_Conf:IP_Address_Source=%s"' % source)
46-
47- def get_ipmi_ip_address():
48- (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="Lan_Conf:IP_Address"')
49- show_re = re.compile('([0-9]{1,3}[.]?){4}')
50- res = show_re.search(output)
51- return res.group()
52-
53- def get_ipmi_user_number(user):
54- for i in range(1, 17):
55- ipmi_user_number = "User%s" % i
56- (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="%s:Username"' % ipmi_user_number)
57- if user in output:
58- return ipmi_user_number
59- return None
60-
61- def commit_ipmi_user_settings(user, password):
62- ipmi_user_number = get_ipmi_user_number(user)
63- if ipmi_user_number is None:
64- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="User10:Username=%s"' % user)
65- ipmi_user_number = get_ipmi_user_number(user)
66- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Username=%s"' % (ipmi_user_number, user))
67- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Password=%s"' % (ipmi_user_number, password))
68- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Enable_User=Yes"' % ipmi_user_number)
69- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Lan_Enable_IPMI_Msgs=Yes"' % ipmi_user_number)
70- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Lan_Privilege_Limit=Administrator"' % ipmi_user_number)
71-
72- def commit_ipmi_settings(config):
73- (status, output) = commands.getstatusoutput('bmc-config --commit --filename %s' % config)
74-
75- def get_maas_power_settings(user, password, ipaddress):
76- return "%s,%s,%s" % (user, password, ipaddress)
77-
78- def get_maas_power_settings_json(user, password, ipaddress):
79- power_params = {"power_address": ipaddress, "power_pass": password, "power_user": user}
80- return json.dumps(power_params)
81-
82- def generate_random_password(min=8,max=15):
83- length=random.randint(min,max)
84- letters=string.ascii_letters+string.digits
85- return ''.join([random.choice(letters) for _ in range(length)])
86-
87- def main():
88-
89- import argparse
90-
91- parser = argparse.ArgumentParser(
92- description='send config file to modify IPMI settings with')
93- parser.add_argument("--configdir", metavar="folder",
94- help="specify config file directory", default=None)
95- parser.add_argument("--dhcp-if-static", action="store_true",
96- dest="dhcp", help="set network source to DHCP if Static", default=False)
97- parser.add_argument("--commission-creds", action="store_true",
98- dest="commission_creds", help="Create IPMI temporary credentials", default=False)
99-
100- args = parser.parse_args()
101-
102- # Check whether IPMI exists or not.
103- (status, ipmi_version) = detect_ipmi()
104- if status != True:
105- # if False, then failed to detect ipmi
106- exit(1)
107-
108- # Check whether IPMI is being set to DHCP. If it is not, and
109- # '--dhcp-if-static' has been passed, Set it to IPMI to DHCP.
110- if not is_ipmi_dhcp() and args.dhcp:
111- set_ipmi_network_source("Use_DHCP")
112- # allow IPMI 120 seconds to obtain an IP address
113- time.sleep(120)
114-
115- # create user/pass
116- IPMI_MAAS_USER="maas"
117- IPMI_MAAS_PASSWORD=generate_random_password()
118-
119- # Configure IPMI user/password
120- commit_ipmi_user_settings(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD)
121-
122- # Commit other IPMI settings
123- if args.configdir:
124- for file in os.listdir(args.configdir):
125- commit_ipmi_settings(os.path.join(args.configdir, file))
126-
127- # get the IP address
128- IPMI_IP_ADDRESS = get_ipmi_ip_address()
129- if IPMI_IP_ADDRESS == "0.0.0.0":
130- # if IPMI_IP_ADDRESS is 0.0.0.0, wait 60 seconds and retry.
131- set_ipmi_network_source("Static")
132- time.sleep(2)
133- set_ipmi_network_source("Use_DHCP")
134- time.sleep(60)
135- IPMI_IP_ADDRESS = get_ipmi_ip_address()
136-
137- if IPMI_IP_ADDRESS is None or IPMI_IP_ADDRESS == "0.0.0.0":
138- # Exit (to not set power params in MAAS) if no IPMI_IP_ADDRESS
139- # has been detected
140- exit(1)
141-
142- if args.commission_creds:
143- print get_maas_power_settings_json(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD, IPMI_IP_ADDRESS)
144- else:
145- print get_maas_power_settings(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD, IPMI_IP_ADDRESS)
146-
147- if __name__ == '__main__':
148- main()
149+ {{maas_ipmi_autodetect_py}}
150 END_MAAS_IPMI_AUTODETECT
151
152 # we could obtain the interface that booted from the kernel cmdline
153
154=== added file 'etc/maas/templates/commissioning-user-data/snippets/maas_ipmi_autodetect.py'
155--- etc/maas/templates/commissioning-user-data/snippets/maas_ipmi_autodetect.py 1970-01-01 00:00:00 +0000
156+++ etc/maas/templates/commissioning-user-data/snippets/maas_ipmi_autodetect.py 2013-06-12 17:17:25 +0000
157@@ -0,0 +1,161 @@
158+#!/usr/bin/python
159+#
160+# maas-ipmi-autodetect - autodetect and autoconfigure IPMI.
161+#
162+# Copyright (C) 2011-2013 Canonical
163+#
164+# Authors:
165+# Andres Rodriguez <andres.rodriguez@canonical.com>
166+#
167+# This program is free software: you can redistribute it and/or modify
168+# it under the terms of the GNU Affero General Public License as
169+# published by the Free Software Foundation, version 3 of the License.
170+#
171+# This program is distributed in the hope that it will be useful,
172+# but WITHOUT ANY WARRANTY; without even the implied warranty of
173+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
174+# GNU Affero General Public License for more details.
175+#
176+# You should have received a copy of the GNU Affero General Public License
177+# along with this program. If not, see <http://www.gnu.org/licenses/>.
178+
179+import os
180+import commands
181+import glob
182+import re
183+import string
184+import random
185+import time
186+import json
187+
188+def detect_ipmi():
189+ # XXX: andreserl 2013-04-09 bug=1064527: Try to detect if node
190+ # is a Virtual Machine. If it is, do not try to detect IPMI.
191+ with open('/proc/cpuinfo', 'r') as cpuinfo:
192+ for line in cpuinfo:
193+ if line.startswith('model name') and 'QEMU' in line:
194+ return (False, None)
195+
196+ (status, output) = commands.getstatusoutput('ipmi-locate')
197+ show_re = re.compile('(IPMI\ Version:) (\d\.\d)')
198+ res = show_re.search(output)
199+ if res == None:
200+ found = glob.glob("/dev/ipmi[0-9]")
201+ if len(found):
202+ return (True, "UNKNOWN: %s" % " ".join(found))
203+ return (False, "")
204+ return (True, res.group(2))
205+
206+def is_ipmi_dhcp():
207+ (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="Lan_Conf:IP_Address_Source"')
208+ show_re = re.compile('IP_Address_Source\s+Use_DHCP')
209+ res = show_re.search(output)
210+ if res == None:
211+ return False
212+ return True
213+
214+def set_ipmi_network_source(source):
215+ (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="Lan_Conf:IP_Address_Source=%s"' % source)
216+
217+def get_ipmi_ip_address():
218+ (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="Lan_Conf:IP_Address"')
219+ show_re = re.compile('([0-9]{1,3}[.]?){4}')
220+ res = show_re.search(output)
221+ return res.group()
222+
223+def get_ipmi_user_number(user):
224+ for i in range(1, 17):
225+ ipmi_user_number = "User%s" % i
226+ (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="%s:Username"' % ipmi_user_number)
227+ if user in output:
228+ return ipmi_user_number
229+ return None
230+
231+def commit_ipmi_user_settings(user, password):
232+ ipmi_user_number = get_ipmi_user_number(user)
233+ if ipmi_user_number is None:
234+ (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="User10:Username=%s"' % user)
235+ ipmi_user_number = get_ipmi_user_number(user)
236+ (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Username=%s"' % (ipmi_user_number, user))
237+ (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Password=%s"' % (ipmi_user_number, password))
238+ (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Enable_User=Yes"' % ipmi_user_number)
239+ (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Lan_Enable_IPMI_Msgs=Yes"' % ipmi_user_number)
240+ (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Lan_Privilege_Limit=Administrator"' % ipmi_user_number)
241+
242+def commit_ipmi_settings(config):
243+ (status, output) = commands.getstatusoutput('bmc-config --commit --filename %s' % config)
244+
245+def get_maas_power_settings(user, password, ipaddress):
246+ return "%s,%s,%s" % (user, password, ipaddress)
247+
248+def get_maas_power_settings_json(user, password, ipaddress):
249+ power_params = {"power_address": ipaddress, "power_pass": password, "power_user": user}
250+ return json.dumps(power_params)
251+
252+def generate_random_password(min=8,max=15):
253+ length=random.randint(min,max)
254+ letters=string.ascii_letters+string.digits
255+ return ''.join([random.choice(letters) for _ in range(length)])
256+
257+def main():
258+
259+ import argparse
260+
261+ parser = argparse.ArgumentParser(
262+ description='send config file to modify IPMI settings with')
263+ parser.add_argument("--configdir", metavar="folder",
264+ help="specify config file directory", default=None)
265+ parser.add_argument("--dhcp-if-static", action="store_true",
266+ dest="dhcp", help="set network source to DHCP if Static", default=False)
267+ parser.add_argument("--commission-creds", action="store_true",
268+ dest="commission_creds", help="Create IPMI temporary credentials", default=False)
269+
270+ args = parser.parse_args()
271+
272+ # Check whether IPMI exists or not.
273+ (status, ipmi_version) = detect_ipmi()
274+ if status != True:
275+ # if False, then failed to detect ipmi
276+ exit(1)
277+
278+ # Check whether IPMI is being set to DHCP. If it is not, and
279+ # '--dhcp-if-static' has been passed, Set it to IPMI to DHCP.
280+ if not is_ipmi_dhcp() and args.dhcp:
281+ set_ipmi_network_source("Use_DHCP")
282+ # allow IPMI 120 seconds to obtain an IP address
283+ time.sleep(120)
284+
285+ # create user/pass
286+ IPMI_MAAS_USER="maas"
287+ IPMI_MAAS_PASSWORD=generate_random_password()
288+
289+ # Configure IPMI user/password
290+ commit_ipmi_user_settings(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD)
291+
292+ # Commit other IPMI settings
293+ if args.configdir:
294+ for file in os.listdir(args.configdir):
295+ commit_ipmi_settings(os.path.join(args.configdir, file))
296+
297+ # get the IP address
298+ IPMI_IP_ADDRESS = get_ipmi_ip_address()
299+ if IPMI_IP_ADDRESS == "0.0.0.0":
300+ # if IPMI_IP_ADDRESS is 0.0.0.0, wait 60 seconds and retry.
301+ set_ipmi_network_source("Static")
302+ time.sleep(2)
303+ set_ipmi_network_source("Use_DHCP")
304+ time.sleep(60)
305+ IPMI_IP_ADDRESS = get_ipmi_ip_address()
306+
307+ if IPMI_IP_ADDRESS is None or IPMI_IP_ADDRESS == "0.0.0.0":
308+ # Exit (to not set power params in MAAS) if no IPMI_IP_ADDRESS
309+ # has been detected
310+ exit(1)
311+
312+ if args.commission_creds:
313+ print get_maas_power_settings_json(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD, IPMI_IP_ADDRESS)
314+ else:
315+ print get_maas_power_settings(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD, IPMI_IP_ADDRESS)
316+
317+if __name__ == '__main__':
318+ main()
319
320=== modified file 'etc/maas/templates/commissioning-user-data/user_data.template'
321--- etc/maas/templates/commissioning-user-data/user_data.template 2013-06-04 00:04:30 +0000
322+++ etc/maas/templates/commissioning-user-data/user_data.template 2013-06-12 17:17:25 +0000
323@@ -219,137 +219,7 @@
324 END_IPMI_CONFIG
325
326 add_bin "maas-ipmi-autodetect" <<"END_MAAS_IPMI_AUTODETECT"
327-#!/usr/bin/python
328-import os
329-import commands
330-import glob
331-import re
332-import string
333-import random
334-import time
335-
336-def detect_ipmi():
337- # XXX: andreserl 2013-04-09 bug=1064527: Try to detect if node
338- # is a Virtual Machine. If it is, do not try to detect IPMI.
339- with open('/proc/cpuinfo', 'r') as cpuinfo:
340- for line in cpuinfo:
341- if line.startswith('model name') and 'QEMU' in line:
342- return (False, None)
343-
344- (status, output) = commands.getstatusoutput('ipmi-locate')
345- show_re = re.compile('(IPMI\ Version:) (\d\.\d)')
346- res = show_re.search(output)
347- if res == None:
348- found = glob.glob("/dev/ipmi[0-9]")
349- if len(found):
350- return (True, "UNKNOWN: %s" % " ".join(found))
351- return (False, "")
352- return (True, res.group(2))
353-
354-def is_ipmi_dhcp():
355- (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="Lan_Conf:IP_Address_Source"')
356- show_re = re.compile('IP_Address_Source\s+Use_DHCP')
357- res = show_re.search(output)
358- if res == None:
359- return False
360- return True
361-
362-def set_ipmi_network_source(source):
363- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="Lan_Conf:IP_Address_Source=%s"' % source)
364-
365-def get_ipmi_ip_address():
366- (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="Lan_Conf:IP_Address"')
367- show_re = re.compile('([0-9]{1,3}[.]?){4}')
368- res = show_re.search(output)
369- return res.group()
370-
371-def get_ipmi_user_number(user):
372- for i in range(1, 17):
373- ipmi_user_number = "User%s" % i
374- (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="%s:Username"' % ipmi_user_number)
375- if user in output:
376- return ipmi_user_number
377- return None
378-
379-def commit_ipmi_user_settings(user, password):
380- ipmi_user_number = get_ipmi_user_number(user)
381- if ipmi_user_number is None:
382- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="User10:Username=%s"' % user)
383- ipmi_user_number = get_ipmi_user_number(user)
384- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Username=%s"' % (ipmi_user_number, user))
385- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Password=%s"' % (ipmi_user_number, password))
386- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Enable_User=Yes"' % ipmi_user_number)
387- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Lan_Enable_IPMI_Msgs=Yes"' % ipmi_user_number)
388- (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Lan_Privilege_Limit=Administrator"' % ipmi_user_number)
389-
390-def commit_ipmi_settings(config):
391- (status, output) = commands.getstatusoutput('bmc-config --commit --filename %s' % config)
392-
393-def get_maas_power_settings(user, password, ipaddress):
394- return "%s,%s,%s" % (user, password, ipaddress)
395-
396-def generate_random_password(min=8,max=15):
397- length=random.randint(min,max)
398- letters=string.ascii_letters+string.digits
399- return ''.join([random.choice(letters) for _ in range(length)])
400-
401-def main():
402-
403- import argparse
404-
405- parser = argparse.ArgumentParser(
406- description='send config file to modify IPMI settings with')
407- parser.add_argument("--configdir", metavar="folder",
408- help="specify config file", default=None)
409- parser.add_argument("--dhcp-if-static", action="store_true",
410- dest="dhcp", help="specify config file", default=False)
411-
412- args = parser.parse_args()
413-
414- # Check whether IPMI exists or not.
415- (status, ipmi_version) = detect_ipmi()
416- if status != True:
417- # if False, then failed to detect ipmi
418- exit(1)
419-
420- # Check whether IPMI is being set to DHCP. If it is not, and
421- # '--dhcp-if-static' has been passed, Set it to IPMI to DHCP.
422- if not is_ipmi_dhcp() and args.dhcp:
423- set_ipmi_network_source("Use_DHCP")
424- # allow IPMI 120 seconds to obtain an IP address
425- time.sleep(120)
426-
427- # create user/pass
428- IPMI_MAAS_USER="maas"
429- IPMI_MAAS_PASSWORD=generate_random_password()
430-
431- # Configure IPMI user/password
432- commit_ipmi_user_settings(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD)
433-
434- # Commit other IPMI settings
435- if args.configdir:
436- for file in os.listdir(args.configdir):
437- commit_ipmi_settings(os.path.join(args.configdir, file))
438-
439- # get the IP address
440- IPMI_IP_ADDRESS = get_ipmi_ip_address()
441- if IPMI_IP_ADDRESS == "0.0.0.0":
442- # if IPMI_IP_ADDRESS is 0.0.0.0, wait 60 seconds and retry.
443- set_ipmi_network_source("Static")
444- time.sleep(2)
445- set_ipmi_network_source("Use_DHCP")
446- time.sleep(60)
447- IPMI_IP_ADDRESS = get_ipmi_ip_address()
448-
449- if IPMI_IP_ADDRESS is None or IPMI_IP_ADDRESS == "0.0.0.0":
450- # Exit (to not set power params in MAAS) if no IPMI_IP_ADDRESS
451- # has been detected
452- exit(1)
453-
454- print get_maas_power_settings(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD, IPMI_IP_ADDRESS)
455-
456-if __name__ == '__main__':
457- main()
458+{{maas_ipmi_autodetect_py}}
459 END_MAAS_IPMI_AUTODETECT
460
461 add_bin "maas_api_helper.py" <<"END_MAAS_API_HELPER"
462
463=== modified file 'src/maasserver/preseed.py'
464--- src/maasserver/preseed.py 2013-05-11 18:38:34 +0000
465+++ src/maasserver/preseed.py 2013-06-12 17:17:25 +0000
466@@ -35,6 +35,8 @@
467 from maasserver.utils import absolute_reverse
468 import tempita
469
470+from metadataserver.commissioning.snippets import get_snippet_context
471+
472
473 GENERIC_FILENAME = 'generic'
474
475@@ -287,7 +289,10 @@
476 """
477 template = load_preseed_template(None, prefix, release)
478 context = get_preseed_context(release, nodegroup=nodegroup)
479- return template.substitute(**context)
480+ # Render the snippets in the main template.
481+ snippets = get_snippet_context()
482+ snippets.update(context)
483+ return template.substitute(**snippets)
484
485
486 def render_preseed(node, prefix, release=''):
487
488=== modified file 'src/maasserver/tests/test_preseed.py'
489--- src/maasserver/tests/test_preseed.py 2013-05-11 18:38:34 +0000
490+++ src/maasserver/tests/test_preseed.py 2013-06-12 17:17:25 +0000
491@@ -421,10 +421,12 @@
492 missing).
493 """
494
495- # Create a scenario for each possible value of PRESEED_TYPE.
496+ # Create a scenario for each possible value of PRESEED_TYPE except
497+ # enlistment. Those have their own test case.
498 scenarios = [
499 (name, {'preseed': value})
500- for name, value in map_enum(PRESEED_TYPE).items()]
501+ for name, value in map_enum(PRESEED_TYPE).items()
502+ if not value.startswith('enlist')]
503
504 def test_render_preseed(self):
505 node = factory.make_node()
506@@ -448,8 +450,15 @@
507 class TestRenderEnlistmentPreseed(TestCase):
508 """Tests for `render_enlistment_preseed`."""
509
510+ # Create a scenario for each possible value of PRESEED_TYPE for
511+ # enlistment. The rest have their own test case.
512+ scenarios = [
513+ (name, {'preseed': value})
514+ for name, value in map_enum(PRESEED_TYPE).items()
515+ if value.startswith('enlist')]
516+
517 def test_render_enlistment_preseed(self):
518- preseed = render_enlistment_preseed(PRESEED_TYPE.ENLIST, "precise")
519+ preseed = render_enlistment_preseed(self.preseed, "precise")
520 # The test really is that the preseed is rendered without an
521 # error.
522 self.assertIsInstance(preseed, str)
523@@ -460,7 +469,7 @@
524 self.patch(settings, 'DEFAULT_MAAS_URL', maas_url)
525 nodegroup = factory.make_node_group(maas_url=ng_url)
526 preseed = render_enlistment_preseed(
527- PRESEED_TYPE.ENLIST, "precise", nodegroup=nodegroup)
528+ self.preseed, "precise", nodegroup=nodegroup)
529 self.assertThat(
530 preseed, MatchesAll(*[Contains(ng_url), Not(Contains(maas_url))]))
531
532
533=== modified file 'src/metadataserver/commissioning/snippets.py'
534--- src/metadataserver/commissioning/snippets.py 2013-06-04 12:22:16 +0000
535+++ src/metadataserver/commissioning/snippets.py 2013-06-12 17:17:25 +0000
536@@ -18,9 +18,31 @@
537 'list_snippets',
538 'read_snippet',
539 'strip_name',
540+ 'get_snippet_context',
541+ 'get_userdata_template_dir',
542 ]
543
544 import os
545+from provisioningserver.utils import locate_config
546+
547+USERDATA_BASE_DIR = 'templates/commissioning-user-data'
548+
549+
550+def get_userdata_template_dir():
551+ """Return the absolute location of the userdata
552+ template directory."""
553+ return locate_config(USERDATA_BASE_DIR)
554+
555+
556+def get_snippet_context(snippets_dir=None, encoding='utf-8'):
557+ """Return the context of all of the snippets."""
558+ if snippets_dir is None:
559+ snippets_dir = os.path.join(get_userdata_template_dir(), 'snippets')
560+ snippets = {
561+ strip_name(name): read_snippet(snippets_dir, name, encoding=encoding)
562+ for name in list_snippets(snippets_dir)
563+ }
564+ return snippets
565
566
567 def read_snippet(snippets_dir, name, encoding='utf-8'):
568
569=== modified file 'src/metadataserver/commissioning/tests/test_snippets.py'
570--- src/metadataserver/commissioning/tests/test_snippets.py 2013-06-04 12:22:16 +0000
571+++ src/metadataserver/commissioning/tests/test_snippets.py 2013-06-12 17:17:25 +0000
572@@ -21,6 +21,7 @@
573 list_snippets,
574 read_snippet,
575 strip_name,
576+ get_snippet_context,
577 )
578
579
580@@ -59,3 +60,14 @@
581 factory.make_file(snippets_dir, 'snippet')
582 factory.make_file(snippets_dir, '.backup.pyc')
583 self.assertItemsEqual(['snippet'], list_snippets(snippets_dir))
584+
585+ def test_get_snippet_context(self):
586+ contents = factory.getRandomString()
587+ snippets_dir = self.make_dir()
588+ factory.make_file(snippets_dir, 'snippet.py', contents=contents)
589+ self.assertItemsEqual({'snippet_py': contents}, get_snippet_context(snippets_dir=snippets_dir))
590+
591+ def test_get_snippet_context_empty_if_no_snippets(self):
592+ snippets_dir = self.make_dir()
593+ context = {}
594+ self.assertEqual(context, get_snippet_context(snippets_dir=snippets_dir))
595
596=== modified file 'src/metadataserver/commissioning/tests/test_user_data.py'
597--- src/metadataserver/commissioning/tests/test_user_data.py 2013-06-04 12:22:16 +0000
598+++ src/metadataserver/commissioning/tests/test_user_data.py 2013-06-12 17:17:25 +0000
599@@ -32,6 +32,7 @@
600 generate_user_data(), ContainsAll({
601 'maas-get',
602 'maas-signal',
603+ 'maas-ipmi-autodetect',
604 'def authenticate_headers',
605 'def encode_multipart_data',
606 }))
607
608=== modified file 'src/metadataserver/commissioning/user_data.py'
609--- src/metadataserver/commissioning/user_data.py 2013-06-04 14:45:01 +0000
610+++ src/metadataserver/commissioning/user_data.py 2013-06-12 17:17:25 +0000
611@@ -30,10 +30,13 @@
612 list_snippets,
613 read_snippet,
614 strip_name,
615+ get_userdata_template_dir,
616+ get_snippet_context,
617 )
618-from provisioningserver.utils import locate_config
619 import tempita
620
621+ENCODING = 'utf-8'
622+
623
624 def generate_user_data(nodegroup=None):
625 """Produce the main commissioning script.
626@@ -50,13 +53,11 @@
627
628 :rtype: `bytes`
629 """
630- ENCODING = 'utf-8'
631- commissioning_dir = locate_config('templates/commissioning-user-data')
632+ commissioning_dir = get_userdata_template_dir()
633 userdata_template_file = os.path.join(
634 commissioning_dir, 'user_data.template')
635 config_template_file = os.path.join(
636 commissioning_dir, 'user_data_config.template')
637- snippets_dir = os.path.join(commissioning_dir, 'snippets')
638 userdata_template = tempita.Template.from_filename(
639 userdata_template_file, encoding=ENCODING)
640 config_template = tempita.Template.from_filename(
641@@ -66,10 +67,7 @@
642 preseed_context = get_preseed_context(nodegroup=nodegroup)
643
644 # Render the snippets in the main template.
645- snippets = {
646- strip_name(name): read_snippet(snippets_dir, name, encoding=ENCODING)
647- for name in list_snippets(snippets_dir)
648- }
649+ snippets = get_snippet_context(encoding=ENCODING)
650 snippets.update(preseed_context)
651 userdata = userdata_template.substitute(snippets).encode(ENCODING)
652