Merge lp:~ltrager/maas-image-builder/update_ks into lp:maas-image-builder/0.9
- update_ks
- Merge into 0.9
Proposed by
Lee Trager
Status: | Rejected |
---|---|
Rejected by: | Blake Rouse |
Proposed branch: | lp:~ltrager/maas-image-builder/update_ks |
Merge into: | lp:maas-image-builder/0.9 |
Diff against target: |
6442 lines (+3545/-2237) 49 files modified
.bzrignore (+2/-16) LICENSE (+1/-1) Makefile (+10/-110) README (+9/-2) buildout.cfg (+0/-49) contrib/centos/centos6/centos6-amd64.ks (+75/-123) contrib/centos/centos6/curtin/curtin-hooks (+0/-342) contrib/centos/centos6/curtin/curtin-hooks.py (+342/-0) contrib/centos/centos6/curtin/finalize (+0/-100) contrib/centos/centos6/curtin/finalize.py (+99/-0) contrib/centos/centos6/curtin/python_wrapper (+11/-0) contrib/centos/centos7/centos7-amd64.ks (+93/-70) contrib/centos/centos7/curtin/curtin-hooks (+0/-333) contrib/centos/centos7/curtin/curtin-hooks.py (+338/-0) contrib/centos/centos7/curtin/finalize (+0/-100) contrib/centos/centos7/curtin/finalize.py (+100/-0) contrib/centos/centos7/curtin/python_wrapper (+11/-0) contrib/rhel/rhel7-amd64.ks (+128/-0) contrib/windows/Autounattend.xml (+148/-0) contrib/windows/curtin/curtin-hooks (+1/-0) contrib/windows/curtin/finalize.py (+165/-0) contrib/windows/curtin/python_wrapper (+11/-0) contrib/windows/scripts/firstlogon.ps1 (+104/-0) contrib/windows/scripts/logon.ps1 (+124/-0) debian/changelog (+49/-0) debian/control (+18/-11) debian/python3-mib.install (+1/-1) debian/rules (+4/-4) pylintrc (+425/-0) required-packages/base (+6/-1) required-packages/dev (+1/-16) requirements.txt (+2/-0) scripts/maas-image-builder (+42/-3) setup.py (+64/-35) src/mib/__init__.py (+41/-8) src/mib/builders/__init__.py (+64/-42) src/mib/builders/centos.py (+94/-27) src/mib/builders/rhel.py (+187/-0) src/mib/builders/windows.py (+497/-0) src/mib/core.py (+45/-9) src/mib/net.py (+42/-8) src/mib/parser.py (+42/-7) src/mib/utils.py (+86/-32) src/mib/virt.py (+48/-12) test_requirements.txt (+2/-0) tox.ini (+13/-0) utilities/format-imports (+0/-421) utilities/python_standard_libs.py (+0/-321) versions.cfg (+0/-33) |
To merge this branch: | bzr merge lp:~ltrager/maas-image-builder/update_ks |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Disapprove | ||
Review via email: mp+325576@code.launchpad.net |
Commit message
Cleanup and standardize all kickstart config files.
Description of the change
To post a comment you must log in.
Unmerged revisions
- 55. By Lee Trager
-
Clean up kickstart files
- 54. By Blake Rouse
-
Fix release 1.0.5 revision number.
- 53. By Blake Rouse
-
Release 1.0.5.
- 52. By Blake Rouse
-
Only allow VNC to windows build locally.
- 51. By Blake Rouse
-
Make kpartx_del handle failure and retry for up to 10 seconds.
- 50. By Blake Rouse
-
Fix --windows-drivers to work.
- 49. By Blake Rouse
-
Fix PEP8 errors in CentOS curtin hooks.
- 48. By Blake Rouse
-
Update changelog for 1.0.4 release.
- 47. By Blake Rouse
-
Modify RHEL to use the CentOS Curtin hooks.
- 46. By Blake Rouse
-
Allow customization of CentOS and RHEL images
Adds the --custom-kickstart flag to the centos and rhel commands. This command allows a user to specify a custom kickstart file which is run after the builtin one runs.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2015-03-14 19:29:17 +0000 |
3 | +++ .bzrignore 2017-06-13 16:13:40 +0000 |
4 | @@ -1,18 +1,4 @@ |
5 | -*.egg |
6 | -*.egg-info |
7 | -./.installed.cfg |
8 | -./bin |
9 | +./.tox |
10 | ./build |
11 | ./debian/changelog |
12 | -./develop-eggs |
13 | -./dist |
14 | -./eggs |
15 | -./include |
16 | -./lib |
17 | -./local |
18 | -./parts |
19 | -./run/* |
20 | -./TAGS |
21 | -./tags |
22 | -dropin.cache |
23 | -.idea |
24 | +*.egg-info |
25 | |
26 | === modified file 'LICENSE' |
27 | --- LICENSE 2015-03-10 20:42:06 +0000 |
28 | +++ LICENSE 2017-06-13 16:13:40 +0000 |
29 | @@ -1,4 +1,4 @@ |
30 | -MAAS Image Builder is Copyright 2015 Canonical Ltd. |
31 | +MAAS Image Builder is Copyright 2017 Canonical Ltd. |
32 | |
33 | Canonical Ltd ("Canonical") distributes the MAAS Image Builder source code |
34 | under the GNU Affero General Public License, version 3 ("AGPLv3"). |
35 | |
36 | === modified file 'Makefile' |
37 | --- Makefile 2015-03-15 13:36:38 +0000 |
38 | +++ Makefile 2017-06-13 16:13:40 +0000 |
39 | @@ -1,21 +1,3 @@ |
40 | -python := python2.7 |
41 | - |
42 | -# Network activity can be suppressed by setting offline=true (or any |
43 | -# non-empty string) at the command-line. |
44 | -ifeq ($(offline),) |
45 | -buildout := bin/buildout |
46 | -virtualenv := virtualenv |
47 | -else |
48 | -buildout := bin/buildout buildout:offline=true |
49 | -virtualenv := virtualenv --never-download |
50 | -endif |
51 | - |
52 | -build: \ |
53 | - bin/buildout \ |
54 | - bin/maas-image-builder |
55 | - |
56 | -all: build |
57 | - |
58 | # Install all packages required for MAAS image builder development & operation |
59 | # on the system. This may prompt for a password. |
60 | install-dependencies: |
61 | @@ -23,107 +5,25 @@ |
62 | --no-install-recommends install $(shell sort -u \ |
63 | $(addprefix required-packages/,base build dev)) |
64 | |
65 | -bin/python: |
66 | - $(virtualenv) --python=$(python) --system-site-packages $(CURDIR) |
67 | - |
68 | -bin/buildout: bin/python bootstrap/zc.buildout-1.5.2.tar.gz |
69 | - bin/python -m pip --quiet install --ignore-installed \ |
70 | - --no-dependencies bootstrap/zc.buildout-1.5.2.tar.gz |
71 | - $(RM) -f README.txt # zc.buildout installs an annoying README.txt. |
72 | - @touch --no-create $@ # Ensure it's newer than its dependencies. |
73 | - |
74 | -bin/maas-image-builder: \ |
75 | - bin/buildout buildout.cfg setup.py |
76 | - # Install the egg-info so stevedore can find the entry points. |
77 | - bin/python -m pip --quiet install --ignore-installed \ |
78 | - --no-dependencies -e . |
79 | - $(buildout) install mib |
80 | - @touch --no-create $@ |
81 | - |
82 | -bin/flake8: bin/buildout buildout.cfg versions.cfg setup.py |
83 | - $(buildout) install flake8 |
84 | - @touch --no-create $@ |
85 | - |
86 | -lint: lint-py |
87 | - |
88 | -pocketlint = $(call available,pocketlint,python-pocket-lint) |
89 | - |
90 | -# Python lint checks are time-intensive, so we run them in parallel. It may |
91 | -# make things matters worse if the files need to be read from disk, though, so |
92 | -# this may need more tuning. |
93 | -# The -n50 -P4 setting roughly doubled speed on a high-end system with SSD and |
94 | -# all the files in cache. |
95 | -lint-py: sources = $(wildcard *.py contrib/*.py) src contrib |
96 | -lint-py: bin/flake8 |
97 | - @find $(sources) -name '*.py' -print0 \ |
98 | - | xargs -r0 -n50 -P4 bin/flake8 --ignore=E123 --config=/dev/null |
99 | - |
100 | -lint-doc: |
101 | - @./utilities/doc-lint |
102 | - |
103 | -# Apply automated formatting to all Python files. |
104 | -format: sources = $(wildcard *.py contrib/*.py) src contrib |
105 | -format: |
106 | - @find $(sources) -name '*.py' -print0 | xargs -r0 ./utilities/format-imports |
107 | - |
108 | -check: clean |
109 | - |
110 | -clean: |
111 | - find . -type f -name '*.py[co]' -print0 | xargs -r0 $(RM) |
112 | - find . -type f -name '*~' -print0 | xargs -r0 $(RM) |
113 | - find . -type f -name dropin.cache -print0 | xargs -r0 $(RM) |
114 | - $(RM) coverage.data coverage.xml |
115 | - $(RM) -r coverage |
116 | - |
117 | -distclean: clean |
118 | - $(RM) -r bin include lib local |
119 | - $(RM) -r eggs develop-eggs |
120 | - $(RM) -r build dist logs/* parts |
121 | - $(RM) tags TAGS .installed.cfg |
122 | - $(RM) -r *.egg *.egg-info src/*.egg-info |
123 | +lint: |
124 | + @tox |
125 | |
126 | package_export: VER = $(shell dpkg-parsechangelog -ldebian/changelog | sed -rne 's,^Version: ([^-]+).*,\1,p') |
127 | package_export: TARBALL = maas-image-builder_$(VER).orig.tar.gz |
128 | package_export: |
129 | @$(RM) -f build/$(TARBALL) |
130 | @mkdir -p build |
131 | - @bzr export --root=maas-image-builder-$(VER).orig build/$(TARBALL) $(CURDIR) |
132 | + @bzr export $(packaging-export-extra) \ |
133 | + --root=maas-image-builder-$(VER).orig build/$(TARBALL) $(CURDIR) |
134 | |
135 | package: package_export |
136 | - bzr bd --result-dir=build --build-dir=build |
137 | + bzr bd --result-dir=build --build-dir=build -- $(packaging-build-extra) |
138 | + |
139 | +package-dev: packaging-export-extra = --uncommitted |
140 | +package-dev: packaging-build-extra = -uc -us |
141 | +package-dev: package |
142 | |
143 | source_package: package_export |
144 | bzr bd --result-dir=build --build-dir=build -S |
145 | |
146 | -# |
147 | -# Phony stuff. |
148 | -# |
149 | - |
150 | -define phony |
151 | - build |
152 | - check |
153 | - clean |
154 | - distclean |
155 | - format |
156 | - install-dependencies |
157 | - lint |
158 | - lint-py |
159 | - package |
160 | - source_package |
161 | -endef |
162 | - |
163 | -phony := $(sort $(strip $(phony))) |
164 | - |
165 | -.PHONY: $(phony) |
166 | - |
167 | -# |
168 | -# Functions. |
169 | -# |
170 | - |
171 | -# Check if a command is found on PATH. Raise an error if not, citing |
172 | -# the package to install. Return the command otherwise. |
173 | -# Usage: $(call available,<command>,<package>) |
174 | -define available |
175 | - $(if $(shell which $(1)),$(1),$(error $(1) not found; \ |
176 | - install it with 'sudo apt-get install $(2)')) |
177 | -endef |
178 | +.PHONY: check install-dependencies |
179 | |
180 | === modified file 'README' |
181 | --- README 2015-07-31 18:46:41 +0000 |
182 | +++ README 2017-06-13 16:13:40 +0000 |
183 | @@ -1,8 +1,8 @@ |
184 | .. -*- mode: rst -*- |
185 | |
186 | -************************ |
187 | +****************** |
188 | MAAS Image Builder |
189 | -************************ |
190 | +****************** |
191 | |
192 | Automated builder for creating images that can be deployed by MAAS and |
193 | installed by the curtin installer. For information on MAAS visit |
194 | @@ -12,3 +12,10 @@ |
195 | Supported Operating Systems: |
196 | - CentOS 6 (i386, amd64) |
197 | - CentOS 7 (amd64) |
198 | + - RedHat Enterprise Linux 7 (amd64) |
199 | + - Windows Server 2008 (i386, amd64) |
200 | + - Windows Server 2008 R2 (i386, amd64) |
201 | + - Windows Server 2012 (i386, amd64) |
202 | + - Windows Server 2012 R2 (i386, amd64) |
203 | + - Windows Hyper-V Server 2012 (i386, amd64) |
204 | + - Windows Hyper-V Server 2012 R2 (i386, amd64) |
205 | |
206 | === removed directory 'bootstrap' |
207 | === removed file 'bootstrap/zc.buildout-1.5.2.tar.gz' |
208 | Binary files bootstrap/zc.buildout-1.5.2.tar.gz 2015-03-10 20:42:06 +0000 and bootstrap/zc.buildout-1.5.2.tar.gz 1970-01-01 00:00:00 +0000 differ |
209 | === removed file 'buildout.cfg' |
210 | --- buildout.cfg 2015-03-11 15:52:11 +0000 |
211 | +++ buildout.cfg 1970-01-01 00:00:00 +0000 |
212 | @@ -1,49 +0,0 @@ |
213 | -[buildout] |
214 | -parts = |
215 | - mib |
216 | -extensions = buildout-versions |
217 | -buildout_versions_file = versions.cfg |
218 | -versions = versions |
219 | -extends = versions.cfg |
220 | -offline = false |
221 | -newest = false |
222 | -include-site-packages = true |
223 | -prefer-final = true |
224 | -allow-picked-versions = true |
225 | - |
226 | -[common] |
227 | -extra-paths = |
228 | - ${buildout:directory}/src |
229 | -test-eggs = |
230 | - coverage |
231 | - fixtures |
232 | - mock |
233 | - nose |
234 | - python-subunit |
235 | - sst |
236 | - testresources |
237 | - testscenarios |
238 | - testtools |
239 | -initialization = |
240 | - from os import environ |
241 | - environ.setdefault("MIB_CONTRIB_DIR", "${buildout:directory}/contrib") |
242 | - |
243 | -[mib] |
244 | -recipe = zc.recipe.egg |
245 | -eggs = |
246 | - ${common:test-eggs} |
247 | -entry-points = |
248 | - maas-image-builder=mib.core:execute |
249 | -initialization = |
250 | - ${common:initialization} |
251 | -scripts = |
252 | - maas-image-builder |
253 | -extra-paths = |
254 | - ${common:extra-paths} |
255 | - |
256 | -[flake8] |
257 | -recipe = zc.recipe.egg |
258 | -eggs = |
259 | - flake8 |
260 | -entry-points = |
261 | - flake8=flake8.run:main |
262 | |
263 | === modified file 'contrib/centos/centos6/centos6-amd64.ks' |
264 | --- contrib/centos/centos6/centos6-amd64.ks 2015-03-11 18:57:11 +0000 |
265 | +++ contrib/centos/centos6/centos6-amd64.ks 2017-06-13 16:13:40 +0000 |
266 | @@ -1,159 +1,111 @@ |
267 | -#version=DEVEL |
268 | +#version=CentOS6 |
269 | +install |
270 | +# Poweroff after installation |
271 | +poweroff |
272 | +# System authorization information |
273 | +auth --enableshadow --passalgo=sha512 |
274 | # Firewall configuration |
275 | firewall --enabled --service=ssh |
276 | -repo --name="repo0" --baseurl=http://mirror.centos.org/centos/6/os/x86_64/ |
277 | -repo --name="repo1" --baseurl=http://mirror.centos.org/centos/6/updates/x86_64/ |
278 | -repo --name="repo2" --baseurl=http://dl.fedoraproject.org/pub/epel/6/x86_64/ |
279 | -# Root password |
280 | -rootpw --iscrypted --lock $1$2e74e5$wMj25e4rEb4rJxqm7BAnk0 |
281 | -# System authorization information |
282 | -auth --useshadow --enablemd5 |
283 | -# System keyboard |
284 | +firstboot --disable |
285 | +ignoredisk --only-use=vda |
286 | +# Keyboard layouts |
287 | keyboard us |
288 | # System language |
289 | lang en_US.UTF-8 |
290 | -# SELinux configuration |
291 | -selinux --enforcing |
292 | -# Installation logging level |
293 | -logging --level=info |
294 | -# Reboot after installation |
295 | -reboot |
296 | -# System services |
297 | -services --disabled="avahi-daemon,iscsi,iscsid,firstboot,kdump" --enabled="network,sshd,rsyslog,tuned" |
298 | -# System timezone |
299 | -timezone --isUtc America/New_York |
300 | +repo --name "os" --baseurl="http://mirror.centos.org/centos/6/os/x86_64" |
301 | +repo --name "updates" --baseurl="http://mirror.centos.org/centos/6/updates/x86_64" |
302 | +repo --name="epel" --baseurl=http://dl.fedoraproject.org/pub/epel/6/x86_64/ |
303 | # Network information |
304 | network --bootproto=dhcp --device=eth0 --onboot=on |
305 | # System bootloader configuration |
306 | -bootloader --append="console=ttyS0,115200n8 console=tty0" --location=mbr --driveorder="sda" --timeout=1 |
307 | -# Clear the Master Boot Record |
308 | +bootloader --append="console=ttyS0,115200n8 console=tty0" --location=mbr --driveorder="vda" --timeout=1 |
309 | +# Root password |
310 | +rootpw --iscrypted nothing |
311 | +selinux --enforcing |
312 | +services --disabled="kdump" --enabled="network,sshd,rsyslog,chronyd" |
313 | +timezone UTC --isUtc |
314 | +# Disk |
315 | zerombr |
316 | -# Partition clearing information |
317 | -clearpart --all |
318 | -# Disk partitioning information |
319 | +clearpart --all --initlabel |
320 | part / --fstype="ext4" --size=3072 |
321 | |
322 | -%post |
323 | +%post --erroronfail |
324 | + |
325 | +# workaround anaconda requirements |
326 | +passwd -d root |
327 | +passwd -l root |
328 | + |
329 | +# remove avahi and networkmanager |
330 | +echo "Removing avahi/zeroconf and NetworkManager" |
331 | +yum -C -y remove avahi\* Network\* |
332 | |
333 | # make sure firstboot doesn't start |
334 | echo "RUN_FIRSTBOOT=NO" > /etc/sysconfig/firstboot |
335 | |
336 | -cat <<EOL >> /etc/rc.local |
337 | -if [ ! -d /root/.ssh ] ; then |
338 | - mkdir -p /root/.ssh |
339 | - chmod 0700 /root/.ssh |
340 | - restorecon /root/.ssh |
341 | -fi |
342 | -EOL |
343 | - |
344 | -cat <<EOL >> /etc/ssh/sshd_config |
345 | -UseDNS no |
346 | -PermitRootLogin without-password |
347 | -EOL |
348 | - |
349 | -# bz705572 |
350 | -ln -s /boot/grub/grub.conf /etc/grub.conf |
351 | - |
352 | -# bz688608 |
353 | -sed -i 's|\(^PasswordAuthentication \)yes|\1no|' /etc/ssh/sshd_config |
354 | - |
355 | -# allow sudo powers to cloud-user |
356 | -echo -e 'cloud-user\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers |
357 | - |
358 | -#bz912801 |
359 | -# prevent udev rules from remapping nics |
360 | -touch /etc/udev/rules.d/75-persistent-net-generator.rules |
361 | - |
362 | -#setup getty on ttyS0 |
363 | -echo "ttyS0" >> /etc/securetty |
364 | -cat <<EOF > /etc/init/ttyS0.conf |
365 | -start on stopped rc RUNLEVEL=[2345] |
366 | -stop on starting runlevel [016] |
367 | -respawn |
368 | -instance /dev/ttyS0 |
369 | -exec /sbin/agetty /dev/ttyS0 115200 vt100-nav |
370 | +echo "Cleaning old yum repodata." |
371 | +yum clean all |
372 | + |
373 | +# chance dhcp client retry/timeouts to resolve #6866 |
374 | +cat >> /etc/dhcp/dhclient.conf << EOF |
375 | + |
376 | +timeout 300; |
377 | +retry 60; |
378 | EOF |
379 | |
380 | -# lock root password |
381 | -passwd -d root |
382 | -passwd -l root |
383 | - |
384 | # clean up installation logs" |
385 | -yum clean all |
386 | rm -rf /var/log/yum.log |
387 | rm -rf /var/lib/yum/* |
388 | rm -rf /root/install.log |
389 | rm -rf /root/install.log.syslog |
390 | rm -rf /root/anaconda-ks.cfg |
391 | rm -rf /var/log/anaconda* |
392 | +rm -rf /root/anac* |
393 | + |
394 | %end |
395 | |
396 | -%packages --nobase |
397 | -acpid |
398 | -attr |
399 | -audit |
400 | -authconfig |
401 | -basesystem |
402 | -bash |
403 | +%packages |
404 | +@core |
405 | +chrony |
406 | cloud-init |
407 | -coreutils |
408 | -cpio |
409 | -cronie |
410 | -device-mapper |
411 | -dhclient |
412 | -dracut |
413 | -e2fsprogs |
414 | +cloud-utils-growpart |
415 | efibootmgr |
416 | -filesystem |
417 | -glibc |
418 | +epel-release |
419 | grub |
420 | -heat-cfntools |
421 | -initscripts |
422 | -iproute |
423 | -iptables |
424 | -iptables-ipv6 |
425 | -iputils |
426 | -kbd |
427 | kernel |
428 | -kpartx |
429 | -ncurses |
430 | -net-tools |
431 | -nfs-utils |
432 | -openssh-clients |
433 | -openssh-server |
434 | -parted |
435 | -passwd |
436 | -policycoreutils |
437 | -procps |
438 | -python-oauth |
439 | -rootfiles |
440 | -rpm |
441 | rsync |
442 | -rsyslog |
443 | -selinux-policy |
444 | -selinux-policy-targeted |
445 | -sendmail |
446 | -setup |
447 | -shadow-utils |
448 | -sudo |
449 | -syslinux |
450 | tar |
451 | -tuned |
452 | -util-linux-ng |
453 | -vim-minimal |
454 | -yum |
455 | -yum-metadata-parser |
456 | +yum-utils |
457 | +python-oauth |
458 | -NetworkManager |
459 | --b43-openfwwf |
460 | --biosdevname |
461 | --fprintd |
462 | --fprintd-pam |
463 | --gtk2 |
464 | --libfprint |
465 | --mcelog |
466 | +-aic94xx-firmware |
467 | +-alsa-firmware |
468 | +-alsa-lib |
469 | +-alsa-tools-firmware |
470 | +-iprutils |
471 | +-ivtv-firmware |
472 | +-iwl100-firmware |
473 | +-iwl1000-firmware |
474 | +-iwl105-firmware |
475 | +-iwl135-firmware |
476 | +-iwl2000-firmware |
477 | +-iwl2030-firmware |
478 | +-iwl3160-firmware |
479 | +-iwl3945-firmware |
480 | +-iwl4965-firmware |
481 | +-iwl5000-firmware |
482 | +-iwl5150-firmware |
483 | +-iwl6000-firmware |
484 | +-iwl6000g2a-firmware |
485 | +-iwl6000g2b-firmware |
486 | +-iwl6050-firmware |
487 | +-iwl7260-firmware |
488 | +-iwl7265-firmware |
489 | +-libertas-sd8686-firmware |
490 | +-libertas-sd8787-firmware |
491 | +-libertas-usb8388-firmware |
492 | -plymouth |
493 | --redhat-support-tool |
494 | --system-config-* |
495 | --wireless-tools |
496 | +-postfix |
497 | +-wpa_supplicant |
498 | |
499 | %end |
500 | + |
501 | |
502 | === modified file 'contrib/centos/centos6/curtin/curtin-hooks' |
503 | --- contrib/centos/centos6/curtin/curtin-hooks 2015-07-28 22:00:23 +0000 |
504 | +++ contrib/centos/centos6/curtin/curtin-hooks 1970-01-01 00:00:00 +0000 |
505 | @@ -1,342 +0,0 @@ |
506 | -#!/usr/bin/env python |
507 | - |
508 | -from __future__ import ( |
509 | - absolute_import, |
510 | - print_function, |
511 | - unicode_literals, |
512 | - ) |
513 | - |
514 | -import os |
515 | -import re |
516 | -import sys |
517 | - |
518 | -sys.path.append('/curtin') |
519 | -from curtin import ( |
520 | - block, |
521 | - net, |
522 | - util, |
523 | - ) |
524 | - |
525 | -""" |
526 | -CentOS 6 |
527 | - |
528 | -Currently Support: |
529 | - |
530 | -- Legacy boot |
531 | -- DHCP of BOOTIF |
532 | - |
533 | -Not Supported: |
534 | - |
535 | -- UEFI boot (*Bad support, most likely wont support) |
536 | -- Multiple network configration |
537 | -- IPv6 |
538 | -""" |
539 | - |
540 | -FSTAB_PREPEND = """\ |
541 | -# |
542 | -# /etc/fstab |
543 | -# Created by MAAS fast-path installer. |
544 | -# |
545 | -# Accessible filesystems, by reference, are maintained under '/dev/disk' |
546 | -# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info |
547 | -# |
548 | -""" |
549 | - |
550 | -FSTAB_APPEND = """\ |
551 | -tmpfs /dev/shm tmpfs defaults 0 0 |
552 | -devpts /dev/pts devpts gid=5,mode=620 0 0 |
553 | -sysfs /sys sysfs defaults 0 0 |
554 | -proc /proc proc defaults 0 0 |
555 | -""" |
556 | - |
557 | -GRUB_CONF = """\ |
558 | -# |
559 | -# /boot/grub/grub.conf |
560 | -# Created by MAAS fast-path installer. |
561 | -# |
562 | -default 0 |
563 | -timeout 0 |
564 | -title MAAS |
565 | - root {grub_root} |
566 | - kernel /boot/{vmlinuz} root=UUID={root_uuid} {extra_opts} |
567 | - initrd /boot/{initrd} |
568 | -""" |
569 | - |
570 | - |
571 | -def get_block_devices(target): |
572 | - """Returns list of block devices for the given target.""" |
573 | - devs = block.get_devices_for_mp(target) |
574 | - blockdevs = set() |
575 | - for maybepart in devs: |
576 | - (blockdev, part) = block.get_blockdev_for_partition(maybepart) |
577 | - blockdevs.add(blockdev) |
578 | - return list(blockdevs) |
579 | - |
580 | - |
581 | -def get_root_info(target): |
582 | - """Returns the root partitions information.""" |
583 | - rootpath = block.get_devices_for_mp(target)[0] |
584 | - rootdev = os.path.basename(rootpath) |
585 | - blocks = block._lsblock() |
586 | - return blocks[rootdev] |
587 | - |
588 | - |
589 | -def read_file(path): |
590 | - """Returns content of a file.""" |
591 | - with open(path, 'rb') as stream: |
592 | - return stream.read().encode('utf-8') |
593 | - |
594 | - |
595 | -def write_fstab(target, curtin_fstab): |
596 | - """Writes the new fstab, using the fstab provided |
597 | - from curtin.""" |
598 | - fstab_path = os.path.join(target, 'etc', 'fstab') |
599 | - fstab_data = read_file(curtin_fstab) |
600 | - with open(fstab_path, 'w') as stream: |
601 | - stream.write(FSTAB_PREPEND) |
602 | - stream.write(fstab_data) |
603 | - stream.write(FSTAB_APPEND) |
604 | - |
605 | - |
606 | -def extract_kernel_params(data): |
607 | - """Extracts the kernel parametes from the provided |
608 | - grub config data.""" |
609 | - match = re.search('^\s+kernel (.+?)$', data, re.MULTILINE) |
610 | - return match.group(0) |
611 | - |
612 | - |
613 | -def strip_kernel_params(params, strip_params=[]): |
614 | - """Removes un-needed kernel parameters.""" |
615 | - new_params = [] |
616 | - for param in params: |
617 | - remove = False |
618 | - for strip in strip_params: |
619 | - if param.startswith(strip): |
620 | - remove = True |
621 | - break |
622 | - if remove is False: |
623 | - new_params.append(param) |
624 | - return new_params |
625 | - |
626 | - |
627 | -def get_boot_file(target, filename): |
628 | - """Return the full filename of file in /boot on target.""" |
629 | - boot_dir = os.path.join(target, 'boot') |
630 | - files = [ |
631 | - fname |
632 | - for fname in os.listdir(boot_dir) |
633 | - if fname.startswith(filename) |
634 | - ] |
635 | - if not files: |
636 | - return None |
637 | - return files[0] |
638 | - |
639 | - |
640 | -def write_grub_conf(target, grub_root, extra=[]): |
641 | - """Writes a new /boot/grub/grub.conf with the correct |
642 | - boot arguments.""" |
643 | - root_info = get_root_info(target) |
644 | - grub_path = os.path.join(target, 'boot', 'grub', 'grub.conf') |
645 | - extra_opts = ' '.join(extra) |
646 | - vmlinuz = get_boot_file(target, 'vmlinuz') |
647 | - initrd = get_boot_file(target, 'initramfs') |
648 | - with open(grub_path, 'w') as stream: |
649 | - stream.write( |
650 | - GRUB_CONF.format( |
651 | - grub_root=grub_root, |
652 | - vmlinuz=vmlinuz, |
653 | - initrd=initrd, |
654 | - root_uuid=root_info['UUID'], |
655 | - extra_opts=extra_opts) + '\n') |
656 | - |
657 | - |
658 | -def get_extra_kernel_parameters(): |
659 | - """Extracts the extra kernel commands from /proc/cmdline |
660 | - that should be placed onto the host. |
661 | - |
662 | - Any command following the '--' entry should be placed |
663 | - onto the host. |
664 | - """ |
665 | - cmdline = read_file('/proc/cmdline') |
666 | - cmdline = cmdline.split() |
667 | - if '--' not in cmdline: |
668 | - return [] |
669 | - idx = cmdline.index('--') + 1 |
670 | - if idx >= len(cmdline) + 1: |
671 | - return [] |
672 | - return strip_kernel_params( |
673 | - cmdline[idx:], |
674 | - strip_params=['initrd=', 'BOOT_IMAGE=', 'BOOTIF=']) |
675 | - |
676 | - |
677 | -def get_grub_root(target): |
678 | - """Extracts the grub root (hdX,X) from the grub command. |
679 | - |
680 | - This is used so the correct root device is used to install |
681 | - stage1/stage2 boot loader. |
682 | - |
683 | - Note: grub-install normally does all of this for you, but |
684 | - since the grub is older, it has an issue with the ISCSI |
685 | - target as /dev/sda and cannot enumarate it with the BIOS. |
686 | - """ |
687 | - with util.RunInChroot(target) as in_chroot: |
688 | - data = '\n'.join([ |
689 | - 'find /boot/grub/stage1', |
690 | - 'quit', |
691 | - ]) |
692 | - out, err = in_chroot(['grub', '--batch'], |
693 | - data=data, capture=True) |
694 | - regex = re.search('^\s+(\(.+?\))$', out, re.MULTILINE) |
695 | - return regex.groups()[0] |
696 | - |
697 | - |
698 | -def grub_install(target, root): |
699 | - """Installs grub onto the root.""" |
700 | - root_dev = root.split(',')[0] + ')' |
701 | - with util.RunInChroot(target) as in_chroot: |
702 | - data = '\n'.join([ |
703 | - 'root %s' % root, |
704 | - 'setup %s' % root_dev, |
705 | - 'quit', |
706 | - ]) |
707 | - in_chroot(['grub', '--batch'], |
708 | - data=data) |
709 | - |
710 | - |
711 | -def set_autorelabel(target): |
712 | - """Creates file /.autorelabel. |
713 | - |
714 | - This is used by SELinux to relabel all of the |
715 | - files on the filesystem to have the correct |
716 | - security context. Without this SSH login will |
717 | - fail. |
718 | - """ |
719 | - path = os.path.join(target, '.autorelabel') |
720 | - open(path, 'a').close() |
721 | - |
722 | - |
723 | -def get_boot_mac(): |
724 | - """Return the mac address of the booting interface.""" |
725 | - cmdline = read_file('/proc/cmdline') |
726 | - cmdline = cmdline.split() |
727 | - try: |
728 | - bootif = [ |
729 | - option |
730 | - for option in cmdline |
731 | - if option.startswith('BOOTIF') |
732 | - ][0] |
733 | - except IndexError: |
734 | - return None |
735 | - _, mac = bootif.split('=') |
736 | - mac = mac.split('-')[1:] |
737 | - return ':'.join(mac) |
738 | - |
739 | - |
740 | -def get_interface_names(): |
741 | - """Return a dictionary mapping mac addresses to interface names.""" |
742 | - sys_path = "/sys/class/net" |
743 | - ifaces = {} |
744 | - for iname in os.listdir(sys_path): |
745 | - mac = read_file(os.path.join(sys_path, iname, "address")) |
746 | - mac = mac.strip().lower() |
747 | - ifaces[mac] = iname |
748 | - return ifaces |
749 | - |
750 | - |
751 | -def get_ipv4_config(iface, data): |
752 | - """Returns the contents of the interface file for ipv4.""" |
753 | - config = [ |
754 | - 'TYPE="Ethernet"', |
755 | - 'NM_CONTROLLED="no"', |
756 | - 'USERCTL="yes"', |
757 | - ] |
758 | - if 'hwaddress' in data: |
759 | - config.append('HWADDR="%s"' % data['hwaddress']) |
760 | - else: |
761 | - # Last ditch effort, use the device name, it probably won't match though! |
762 | - config.append('DEVICE="%s"' % iface) |
763 | - if data['auto']: |
764 | - config.append('ONBOOT="yes"') |
765 | - else: |
766 | - config.append('ONBOOT="no"') |
767 | - |
768 | - method = data['method'] |
769 | - if method == 'dhcp': |
770 | - config.append('BOOTPROTO="dhcp"') |
771 | - config.append('PEERDNS="yes"') |
772 | - config.append('PERSISTENT_DHCLIENT="1"') |
773 | - if 'hostname' in data: |
774 | - config.append('DHCP_HOSTNAME="%s"' % data['hostname']) |
775 | - elif method == 'static': |
776 | - config.append('BOOTPROTO="none"') |
777 | - config.append('IPADDR="%s"' % data['address']) |
778 | - config.append('NETMASK="%s"' % data['netmask']) |
779 | - if 'broadcast' in data: |
780 | - config.append('BROADCAST="%s"' % data['broadcast']) |
781 | - if 'gateway' in data: |
782 | - config.append('GATEWAY="%s"' % data['gateway']) |
783 | - elif method == 'manual': |
784 | - config.append('BOOTPROTO="none"') |
785 | - return '\n'.join(config) |
786 | - |
787 | - |
788 | -def write_interface_config(target, iface, data): |
789 | - """Writes config for interface.""" |
790 | - family = data['family'] |
791 | - if family != "inet": |
792 | - # Only supporting ipv4 currently |
793 | - print( |
794 | - "WARN: unsupported family %s, " |
795 | - "failed to configure interface: %s" (family, iface)) |
796 | - return |
797 | - config = get_ipv4_config(iface, data) |
798 | - path = os.path.join( |
799 | - target, 'etc', 'sysconfig', 'network-scripts', 'ifcfg-%s' % iface) |
800 | - with open(path, 'w') as stream: |
801 | - stream.write(config + '\n') |
802 | - |
803 | - |
804 | -def write_network_config(target, mac): |
805 | - """Write network configuration for the given MAC address.""" |
806 | - inames = get_interface_names() |
807 | - iname = inames[mac.lower()] |
808 | - write_interface_config( |
809 | - target, iname, { |
810 | - 'family': 'inet', |
811 | - 'hwaddress': mac.upper(), |
812 | - 'auto': True, |
813 | - 'method': 'dhcp' |
814 | - }) |
815 | - |
816 | - |
817 | -def main(): |
818 | - state = util.load_command_environment() |
819 | - target = state['target'] |
820 | - if target is None: |
821 | - print("Target was not provided in the environment.") |
822 | - sys.exit(1) |
823 | - fstab = state['fstab'] |
824 | - if fstab is None: |
825 | - print("/etc/fstab output was not provided in the environment.") |
826 | - sys.exit(1) |
827 | - bootmac = get_boot_mac() |
828 | - if bootmac is None: |
829 | - print("Unable to determine boot interface.") |
830 | - sys.exit(1) |
831 | - devices = get_block_devices(target) |
832 | - if not devices: |
833 | - print("Unable to find block device for: %s" % target) |
834 | - sys.exit(1) |
835 | - |
836 | - write_fstab(target, fstab) |
837 | - |
838 | - grub_root = get_grub_root(target) |
839 | - write_grub_conf(target, grub_root, extra=get_extra_kernel_parameters()) |
840 | - grub_install(target, grub_root) |
841 | - |
842 | - set_autorelabel(target) |
843 | - write_network_config(target, bootmac) |
844 | - |
845 | - |
846 | -if __name__ == "__main__": |
847 | - main() |
848 | |
849 | === target is u'python_wrapper' |
850 | === added file 'contrib/centos/centos6/curtin/curtin-hooks.py' |
851 | --- contrib/centos/centos6/curtin/curtin-hooks.py 1970-01-01 00:00:00 +0000 |
852 | +++ contrib/centos/centos6/curtin/curtin-hooks.py 2017-06-13 16:13:40 +0000 |
853 | @@ -0,0 +1,342 @@ |
854 | +#!/usr/bin/env python |
855 | + |
856 | +from __future__ import ( |
857 | + absolute_import, |
858 | + print_function, |
859 | + unicode_literals, |
860 | + ) |
861 | + |
862 | +import codecs |
863 | +import os |
864 | +import re |
865 | +import sys |
866 | + |
867 | +from curtin import ( |
868 | + block, |
869 | + util, |
870 | + ) |
871 | + |
872 | +""" |
873 | +CentOS 6 |
874 | + |
875 | +Currently Support: |
876 | + |
877 | +- Legacy boot |
878 | +- DHCP of BOOTIF |
879 | + |
880 | +Not Supported: |
881 | + |
882 | +- UEFI boot (*Bad support, most likely wont support) |
883 | +- Multiple network configration |
884 | +- IPv6 |
885 | +""" |
886 | + |
887 | +FSTAB_PREPEND = """\ |
888 | +# |
889 | +# /etc/fstab |
890 | +# Created by MAAS fast-path installer. |
891 | +# |
892 | +# Accessible filesystems, by reference, are maintained under '/dev/disk' |
893 | +# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info |
894 | +# |
895 | +""" |
896 | + |
897 | +FSTAB_APPEND = """\ |
898 | +tmpfs /dev/shm tmpfs defaults 0 0 |
899 | +devpts /dev/pts devpts gid=5,mode=620 0 0 |
900 | +sysfs /sys sysfs defaults 0 0 |
901 | +proc /proc proc defaults 0 0 |
902 | +""" |
903 | + |
904 | +GRUB_CONF = """\ |
905 | +# |
906 | +# /boot/grub/grub.conf |
907 | +# Created by MAAS fast-path installer. |
908 | +# |
909 | +default 0 |
910 | +timeout 0 |
911 | +title MAAS |
912 | + root {grub_root} |
913 | + kernel /boot/{vmlinuz} root=UUID={root_uuid} {extra_opts} |
914 | + initrd /boot/{initrd} |
915 | +""" |
916 | + |
917 | + |
918 | +def get_block_devices(target): |
919 | + """Returns list of block devices for the given target.""" |
920 | + devs = block.get_devices_for_mp(target) |
921 | + blockdevs = set() |
922 | + for maybepart in devs: |
923 | + (blockdev, part) = block.get_blockdev_for_partition(maybepart) |
924 | + blockdevs.add(blockdev) |
925 | + return list(blockdevs) |
926 | + |
927 | + |
928 | +def get_root_info(target): |
929 | + """Returns the root partitions information.""" |
930 | + rootpath = block.get_devices_for_mp(target)[0] |
931 | + rootdev = os.path.basename(rootpath) |
932 | + blocks = block._lsblock() |
933 | + return blocks[rootdev] |
934 | + |
935 | + |
936 | +def read_file(path): |
937 | + """Returns content of a file.""" |
938 | + with codecs.open(path, encoding='utf-8') as stream: |
939 | + return stream.read() |
940 | + |
941 | + |
942 | +def write_fstab(target, curtin_fstab): |
943 | + """Writes the new fstab, using the fstab provided |
944 | + from curtin.""" |
945 | + fstab_path = os.path.join(target, 'etc', 'fstab') |
946 | + fstab_data = read_file(curtin_fstab) |
947 | + with open(fstab_path, 'w') as stream: |
948 | + stream.write(FSTAB_PREPEND) |
949 | + stream.write(fstab_data) |
950 | + stream.write(FSTAB_APPEND) |
951 | + |
952 | + |
953 | +def extract_kernel_params(data): |
954 | + """Extracts the kernel parametes from the provided |
955 | + grub config data.""" |
956 | + match = re.search('^\s+kernel (.+?)$', data, re.MULTILINE) |
957 | + return match.group(0) |
958 | + |
959 | + |
960 | +def strip_kernel_params(params, strip_params=[]): |
961 | + """Removes un-needed kernel parameters.""" |
962 | + new_params = [] |
963 | + for param in params: |
964 | + remove = False |
965 | + for strip in strip_params: |
966 | + if param.startswith(strip): |
967 | + remove = True |
968 | + break |
969 | + if remove is False: |
970 | + new_params.append(param) |
971 | + return new_params |
972 | + |
973 | + |
974 | +def get_boot_file(target, filename): |
975 | + """Return the full filename of file in /boot on target.""" |
976 | + boot_dir = os.path.join(target, 'boot') |
977 | + files = [ |
978 | + fname |
979 | + for fname in os.listdir(boot_dir) |
980 | + if fname.startswith(filename) |
981 | + ] |
982 | + if not files: |
983 | + return None |
984 | + return files[0] |
985 | + |
986 | + |
987 | +def write_grub_conf(target, grub_root, extra=[]): |
988 | + """Writes a new /boot/grub/grub.conf with the correct |
989 | + boot arguments.""" |
990 | + root_info = get_root_info(target) |
991 | + grub_path = os.path.join(target, 'boot', 'grub', 'grub.conf') |
992 | + extra_opts = ' '.join(extra) |
993 | + vmlinuz = get_boot_file(target, 'vmlinuz') |
994 | + initrd = get_boot_file(target, 'initramfs') |
995 | + with open(grub_path, 'w') as stream: |
996 | + stream.write( |
997 | + GRUB_CONF.format( |
998 | + grub_root=grub_root, |
999 | + vmlinuz=vmlinuz, |
1000 | + initrd=initrd, |
1001 | + root_uuid=root_info['UUID'], |
1002 | + extra_opts=extra_opts) + '\n') |
1003 | + |
1004 | + |
1005 | +def get_extra_kernel_parameters(): |
1006 | + """Extracts the extra kernel commands from /proc/cmdline |
1007 | + that should be placed onto the host. |
1008 | + |
1009 | + Any command following the '--' entry should be placed |
1010 | + onto the host. |
1011 | + """ |
1012 | + cmdline = read_file('/proc/cmdline') |
1013 | + cmdline = cmdline.split() |
1014 | + if '--' not in cmdline: |
1015 | + return [] |
1016 | + idx = cmdline.index('--') + 1 |
1017 | + if idx >= len(cmdline) + 1: |
1018 | + return [] |
1019 | + return strip_kernel_params( |
1020 | + cmdline[idx:], |
1021 | + strip_params=['initrd=', 'BOOT_IMAGE=', 'BOOTIF=']) |
1022 | + |
1023 | + |
1024 | +def get_grub_root(target): |
1025 | + """Extracts the grub root (hdX,X) from the grub command. |
1026 | + |
1027 | + This is used so the correct root device is used to install |
1028 | + stage1/stage2 boot loader. |
1029 | + |
1030 | + Note: grub-install normally does all of this for you, but |
1031 | + since the grub is older, it has an issue with the ISCSI |
1032 | + target as /dev/sda and cannot enumarate it with the BIOS. |
1033 | + """ |
1034 | + with util.RunInChroot(target) as in_chroot: |
1035 | + data = '\n'.join([ |
1036 | + 'find /boot/grub/stage1', |
1037 | + 'quit', |
1038 | + ]).encode('utf-8') |
1039 | + out, err = in_chroot(['grub', '--batch'], |
1040 | + data=data, capture=True) |
1041 | + regex = re.search('^\s+(\(.+?\))$', out, re.MULTILINE) |
1042 | + return regex.groups()[0] |
1043 | + |
1044 | + |
1045 | +def grub_install(target, root): |
1046 | + """Installs grub onto the root.""" |
1047 | + root_dev = root.split(',')[0] + ')' |
1048 | + with util.RunInChroot(target) as in_chroot: |
1049 | + data = '\n'.join([ |
1050 | + 'root %s' % root, |
1051 | + 'setup %s' % root_dev, |
1052 | + 'quit', |
1053 | + ]).encode('utf-8') |
1054 | + in_chroot(['grub', '--batch'], |
1055 | + data=data) |
1056 | + |
1057 | + |
1058 | +def set_autorelabel(target): |
1059 | + """Creates file /.autorelabel. |
1060 | + |
1061 | + This is used by SELinux to relabel all of the |
1062 | + files on the filesystem to have the correct |
1063 | + security context. Without this SSH login will |
1064 | + fail. |
1065 | + """ |
1066 | + path = os.path.join(target, '.autorelabel') |
1067 | + open(path, 'a').close() |
1068 | + |
1069 | + |
1070 | +def get_boot_mac(): |
1071 | + """Return the mac address of the booting interface.""" |
1072 | + cmdline = read_file('/proc/cmdline') |
1073 | + cmdline = cmdline.split() |
1074 | + try: |
1075 | + bootif = [ |
1076 | + option |
1077 | + for option in cmdline |
1078 | + if option.startswith('BOOTIF') |
1079 | + ][0] |
1080 | + except IndexError: |
1081 | + return None |
1082 | + _, mac = bootif.split('=') |
1083 | + mac = mac.split('-')[1:] |
1084 | + return ':'.join(mac) |
1085 | + |
1086 | + |
1087 | +def get_interface_names(): |
1088 | + """Return a dictionary mapping mac addresses to interface names.""" |
1089 | + sys_path = "/sys/class/net" |
1090 | + ifaces = {} |
1091 | + for iname in os.listdir(sys_path): |
1092 | + mac = read_file(os.path.join(sys_path, iname, "address")) |
1093 | + mac = mac.strip().lower() |
1094 | + ifaces[mac] = iname |
1095 | + return ifaces |
1096 | + |
1097 | + |
1098 | +def get_ipv4_config(iface, data): |
1099 | + """Returns the contents of the interface file for ipv4.""" |
1100 | + config = [ |
1101 | + 'TYPE="Ethernet"', |
1102 | + 'NM_CONTROLLED="no"', |
1103 | + 'USERCTL="yes"', |
1104 | + ] |
1105 | + if 'hwaddress' in data: |
1106 | + config.append('HWADDR="%s"' % data['hwaddress']) |
1107 | + else: |
1108 | + # Last ditch effort, use the device name, it probably won't match |
1109 | + # though! |
1110 | + config.append('DEVICE="%s"' % iface) |
1111 | + if data['auto']: |
1112 | + config.append('ONBOOT="yes"') |
1113 | + else: |
1114 | + config.append('ONBOOT="no"') |
1115 | + |
1116 | + method = data['method'] |
1117 | + if method == 'dhcp': |
1118 | + config.append('BOOTPROTO="dhcp"') |
1119 | + config.append('PEERDNS="yes"') |
1120 | + config.append('PERSISTENT_DHCLIENT="1"') |
1121 | + if 'hostname' in data: |
1122 | + config.append('DHCP_HOSTNAME="%s"' % data['hostname']) |
1123 | + elif method == 'static': |
1124 | + config.append('BOOTPROTO="none"') |
1125 | + config.append('IPADDR="%s"' % data['address']) |
1126 | + config.append('NETMASK="%s"' % data['netmask']) |
1127 | + if 'broadcast' in data: |
1128 | + config.append('BROADCAST="%s"' % data['broadcast']) |
1129 | + if 'gateway' in data: |
1130 | + config.append('GATEWAY="%s"' % data['gateway']) |
1131 | + elif method == 'manual': |
1132 | + config.append('BOOTPROTO="none"') |
1133 | + return '\n'.join(config) |
1134 | + |
1135 | + |
1136 | +def write_interface_config(target, iface, data): |
1137 | + """Writes config for interface.""" |
1138 | + family = data['family'] |
1139 | + if family != "inet": |
1140 | + # Only supporting ipv4 currently |
1141 | + print( |
1142 | + "WARN: unsupported family %s, " |
1143 | + "failed to configure interface: %s" (family, iface)) |
1144 | + return |
1145 | + config = get_ipv4_config(iface, data) |
1146 | + path = os.path.join( |
1147 | + target, 'etc', 'sysconfig', 'network-scripts', 'ifcfg-%s' % iface) |
1148 | + with open(path, 'w') as stream: |
1149 | + stream.write(config + '\n') |
1150 | + |
1151 | + |
1152 | +def write_network_config(target, mac): |
1153 | + """Write network configuration for the given MAC address.""" |
1154 | + inames = get_interface_names() |
1155 | + iname = inames[mac.lower()] |
1156 | + write_interface_config( |
1157 | + target, iname, { |
1158 | + 'family': 'inet', |
1159 | + 'hwaddress': mac.upper(), |
1160 | + 'auto': True, |
1161 | + 'method': 'dhcp' |
1162 | + }) |
1163 | + |
1164 | + |
1165 | +def main(): |
1166 | + state = util.load_command_environment() |
1167 | + target = state['target'] |
1168 | + if target is None: |
1169 | + print("Target was not provided in the environment.") |
1170 | + sys.exit(1) |
1171 | + fstab = state['fstab'] |
1172 | + if fstab is None: |
1173 | + print("/etc/fstab output was not provided in the environment.") |
1174 | + sys.exit(1) |
1175 | + bootmac = get_boot_mac() |
1176 | + if bootmac is None: |
1177 | + print("Unable to determine boot interface.") |
1178 | + sys.exit(1) |
1179 | + devices = get_block_devices(target) |
1180 | + if not devices: |
1181 | + print("Unable to find block device for: %s" % target) |
1182 | + sys.exit(1) |
1183 | + |
1184 | + write_fstab(target, fstab) |
1185 | + |
1186 | + grub_root = get_grub_root(target) |
1187 | + write_grub_conf(target, grub_root, extra=get_extra_kernel_parameters()) |
1188 | + grub_install(target, grub_root) |
1189 | + |
1190 | + set_autorelabel(target) |
1191 | + write_network_config(target, bootmac) |
1192 | + |
1193 | + |
1194 | +if __name__ == "__main__": |
1195 | + main() |
1196 | |
1197 | === modified file 'contrib/centos/centos6/curtin/finalize' |
1198 | --- contrib/centos/centos6/curtin/finalize 2015-02-05 13:50:16 +0000 |
1199 | +++ contrib/centos/centos6/curtin/finalize 1970-01-01 00:00:00 +0000 |
1200 | @@ -1,100 +0,0 @@ |
1201 | -#!/usr/bin/env python |
1202 | - |
1203 | -from __future__ import ( |
1204 | - absolute_import, |
1205 | - print_function, |
1206 | - unicode_literals, |
1207 | - ) |
1208 | - |
1209 | -import json |
1210 | -import os |
1211 | -import sys |
1212 | - |
1213 | -sys.path.append('/curtin') |
1214 | -from curtin import util |
1215 | - |
1216 | - |
1217 | -DATASOURCE_LIST = """\ |
1218 | -datasource_list: [ MAAS ] |
1219 | -""" |
1220 | - |
1221 | -DATASOURCE = """\ |
1222 | -datasource: |
1223 | - MAAS: {{consumer_key: {consumer_key}, metadata_url: '{url}', |
1224 | - token_key: {token_key}, token_secret: {token_secret}}} |
1225 | -""" |
1226 | - |
1227 | - |
1228 | -def get_datasource(**kwargs): |
1229 | - """Returns the format cloud-init datasource.""" |
1230 | - return DATASOURCE_LIST + DATASOURCE.format(**kwargs) |
1231 | - |
1232 | - |
1233 | -def load_config(path): |
1234 | - """Loads the curtin config.""" |
1235 | - with open(path, 'r') as stream: |
1236 | - return json.load(stream) |
1237 | - |
1238 | - |
1239 | -def extract_maas_parameters(config): |
1240 | - """Extracts the needed values from the debconf |
1241 | - entry.""" |
1242 | - params = {} |
1243 | - for line in config.splitlines(): |
1244 | - cloud, key, type, value = line.split()[:4] |
1245 | - if key == "cloud-init/maas-metadata-url": |
1246 | - params['url'] = value |
1247 | - elif key == "cloud-init/maas-metadata-credentials": |
1248 | - values = value.split("&") |
1249 | - for oauth in values: |
1250 | - key, value = oauth.split('=') |
1251 | - if key == 'oauth_token_key': |
1252 | - params['token_key'] = value |
1253 | - elif key == 'oauth_token_secret': |
1254 | - params['token_secret'] = value |
1255 | - elif key == 'oauth_consumer_key': |
1256 | - params['consumer_key'] = value |
1257 | - return params |
1258 | - |
1259 | - |
1260 | -def get_maas_debconf_selections(config): |
1261 | - """Gets the debconf selections from the curtin config.""" |
1262 | - try: |
1263 | - return config['debconf_selections']['maas'] |
1264 | - except KeyError: |
1265 | - return None |
1266 | - |
1267 | - |
1268 | -def write_datasource(target, data): |
1269 | - """Writes the cloudinit config into |
1270 | - /etc/cloud/cloud.cfg.d/90_datasource.cfg.""" |
1271 | - path = os.path.join( |
1272 | - target, 'etc', 'cloud', 'cloud.cfg.d', '90_datasource.cfg') |
1273 | - with open(path, 'w') as stream: |
1274 | - stream.write(data + '\n') |
1275 | - |
1276 | - |
1277 | -def main(): |
1278 | - state = util.load_command_environment() |
1279 | - target = state['target'] |
1280 | - if target is None: |
1281 | - print("Target was not provided in the environment.") |
1282 | - sys.exit(1) |
1283 | - config_f = state['config'] |
1284 | - if config_f is None: |
1285 | - print("Config was not provided in the environment.") |
1286 | - sys.exit(1) |
1287 | - config = load_config(config_f) |
1288 | - |
1289 | - debconf = get_maas_debconf_selections(config) |
1290 | - if debconf is None: |
1291 | - print("Failed to get the debconf_selections.") |
1292 | - sys.exit(1) |
1293 | - |
1294 | - params = extract_maas_parameters(debconf) |
1295 | - datasource = get_datasource(**params) |
1296 | - write_datasource(target, datasource) |
1297 | - |
1298 | - |
1299 | -if __name__ == "__main__": |
1300 | - main() |
1301 | |
1302 | === target is u'python_wrapper' |
1303 | === added file 'contrib/centos/centos6/curtin/finalize.py' |
1304 | --- contrib/centos/centos6/curtin/finalize.py 1970-01-01 00:00:00 +0000 |
1305 | +++ contrib/centos/centos6/curtin/finalize.py 2017-06-13 16:13:40 +0000 |
1306 | @@ -0,0 +1,99 @@ |
1307 | +#!/usr/bin/env python |
1308 | + |
1309 | +from __future__ import ( |
1310 | + absolute_import, |
1311 | + print_function, |
1312 | + unicode_literals, |
1313 | + ) |
1314 | + |
1315 | +import json |
1316 | +import os |
1317 | +import sys |
1318 | + |
1319 | +from curtin import util |
1320 | + |
1321 | + |
1322 | +DATASOURCE_LIST = """\ |
1323 | +datasource_list: [ MAAS ] |
1324 | +""" |
1325 | + |
1326 | +DATASOURCE = """\ |
1327 | +datasource: |
1328 | + MAAS: {{consumer_key: {consumer_key}, metadata_url: '{url}', |
1329 | + token_key: {token_key}, token_secret: {token_secret}}} |
1330 | +""" |
1331 | + |
1332 | + |
1333 | +def get_datasource(**kwargs): |
1334 | + """Returns the format cloud-init datasource.""" |
1335 | + return DATASOURCE_LIST + DATASOURCE.format(**kwargs) |
1336 | + |
1337 | + |
1338 | +def load_config(path): |
1339 | + """Loads the curtin config.""" |
1340 | + with open(path, 'r') as stream: |
1341 | + return json.load(stream) |
1342 | + |
1343 | + |
1344 | +def extract_maas_parameters(config): |
1345 | + """Extracts the needed values from the debconf |
1346 | + entry.""" |
1347 | + params = {} |
1348 | + for line in config.splitlines(): |
1349 | + cloud, key, type, value = line.split()[:4] |
1350 | + if key == "cloud-init/maas-metadata-url": |
1351 | + params['url'] = value |
1352 | + elif key == "cloud-init/maas-metadata-credentials": |
1353 | + values = value.split("&") |
1354 | + for oauth in values: |
1355 | + key, value = oauth.split('=') |
1356 | + if key == 'oauth_token_key': |
1357 | + params['token_key'] = value |
1358 | + elif key == 'oauth_token_secret': |
1359 | + params['token_secret'] = value |
1360 | + elif key == 'oauth_consumer_key': |
1361 | + params['consumer_key'] = value |
1362 | + return params |
1363 | + |
1364 | + |
1365 | +def get_maas_debconf_selections(config): |
1366 | + """Gets the debconf selections from the curtin config.""" |
1367 | + try: |
1368 | + return config['debconf_selections']['maas'] |
1369 | + except KeyError: |
1370 | + return None |
1371 | + |
1372 | + |
1373 | +def write_datasource(target, data): |
1374 | + """Writes the cloudinit config into |
1375 | + /etc/cloud/cloud.cfg.d/90_datasource.cfg.""" |
1376 | + path = os.path.join( |
1377 | + target, 'etc', 'cloud', 'cloud.cfg.d', '90_datasource.cfg') |
1378 | + with open(path, 'w') as stream: |
1379 | + stream.write(data + '\n') |
1380 | + |
1381 | + |
1382 | +def main(): |
1383 | + state = util.load_command_environment() |
1384 | + target = state['target'] |
1385 | + if target is None: |
1386 | + print("Target was not provided in the environment.") |
1387 | + sys.exit(1) |
1388 | + config_f = state['config'] |
1389 | + if config_f is None: |
1390 | + print("Config was not provided in the environment.") |
1391 | + sys.exit(1) |
1392 | + config = load_config(config_f) |
1393 | + |
1394 | + debconf = get_maas_debconf_selections(config) |
1395 | + if debconf is None: |
1396 | + print("Failed to get the debconf_selections.") |
1397 | + sys.exit(1) |
1398 | + |
1399 | + params = extract_maas_parameters(debconf) |
1400 | + datasource = get_datasource(**params) |
1401 | + write_datasource(target, datasource) |
1402 | + |
1403 | + |
1404 | +if __name__ == "__main__": |
1405 | + main() |
1406 | |
1407 | === added file 'contrib/centos/centos6/curtin/python_wrapper' |
1408 | --- contrib/centos/centos6/curtin/python_wrapper 1970-01-01 00:00:00 +0000 |
1409 | +++ contrib/centos/centos6/curtin/python_wrapper 2017-06-13 16:13:40 +0000 |
1410 | @@ -0,0 +1,11 @@ |
1411 | +#!/bin/bash |
1412 | + |
1413 | +export PYTHONPATH='/curtin' |
1414 | + |
1415 | +# Ubuntu 16.04 only ships with Python 3 while previous versions only ship |
1416 | +# with Python 2. |
1417 | +if type -p python > /dev/null; then |
1418 | + exec python "$0.py" "$@" |
1419 | +else |
1420 | + exec python3 "$0.py" "$@" |
1421 | +fi |
1422 | |
1423 | === modified file 'contrib/centos/centos7/centos7-amd64.ks' |
1424 | --- contrib/centos/centos7/centos7-amd64.ks 2015-07-23 20:03:39 +0000 |
1425 | +++ contrib/centos/centos7/centos7-amd64.ks 2017-06-13 16:13:40 +0000 |
1426 | @@ -1,103 +1,126 @@ |
1427 | -#version=RHEL7 |
1428 | +#version=CentOS7 |
1429 | install |
1430 | +# Poweroff after installation |
1431 | +poweroff |
1432 | +# System authorization information |
1433 | +auth --enableshadow --passalgo=sha512 |
1434 | # Firewall configuration |
1435 | firewall --enabled --service=ssh |
1436 | -repo --name="repo0" --baseurl=http://mirror.centos.org/centos/7/os/x86_64 |
1437 | -repo --name="repo1" --baseurl=http://mirror.centos.org/centos/7/updates/x86_64 |
1438 | -repo --name="repo2" --baseurl=http://dl.fedoraproject.org/pub/epel/7/x86_64/ |
1439 | -repo --name="repo3" --baseurl=http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/20/Everything/x86_64/os/ --includepkgs=python-oauth |
1440 | -# Root password |
1441 | -rootpw --iscrypted $6$c78cFcbEdD2FcfE1$W0v5nUb1j1T8E3szv01CoBWFnl1TEWpt43WSZqtVP5kNih6zLiixQWS1umh1bDGnzWIqkwCwjIR8lHr.W0ua21 |
1442 | -# System authorization information |
1443 | -auth --useshadow --enablemd5 |
1444 | -# System keyboard |
1445 | -keyboard us |
1446 | +firstboot --disable |
1447 | +ignoredisk --only-use=vda |
1448 | +# Keyboard layouts |
1449 | +# old format: keyboard us |
1450 | +# new format: |
1451 | +keyboard --vckeymap=us --xlayouts='us' |
1452 | # System language |
1453 | lang en_US.UTF-8 |
1454 | -# SELinux configuration |
1455 | -selinux --enforcing |
1456 | -# Installation logging level |
1457 | -logging --level=info |
1458 | -# Poweroff after installation |
1459 | -poweroff |
1460 | -# System services |
1461 | -services --disabled="avahi-daemon,iscsi,iscsid,firstboot,kdump" --enabled="network,sshd,rsyslog,tuned,chronyd" |
1462 | -# System timezone |
1463 | -timezone --isUtc America/New_York |
1464 | +repo --name "os" --baseurl="http://mirror.centos.org/centos/7/os/x86_64" |
1465 | +repo --name "updates" --baseurl="http://mirror.centos.org/centos/7/updates/x86_64" |
1466 | +repo --name "extras" --baseurl="http://mirror.centos.org/centos/7/extras/x86_64" |
1467 | +repo --name="python-oauth" --baseurl="http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/20/Everything/x86_64/os/" --includepkgs=python-oauth |
1468 | # Network information |
1469 | network --bootproto=dhcp --device=eth0 --onboot=on |
1470 | # System bootloader configuration |
1471 | -bootloader --append="console=ttyS0,115200n8 console=tty0" --location=mbr --driveorder="sda" --timeout=1 |
1472 | -# Clear the Master Boot Record |
1473 | +bootloader --append="console=ttyS0,115200n8 console=tty0" --location=mbr --driveorder="vda" --timeout=1 |
1474 | +# Root password |
1475 | +rootpw --iscrypted nothing |
1476 | +selinux --enforcing |
1477 | +services --disabled="kdump" --enabled="network,sshd,rsyslog,chronyd" |
1478 | +timezone UTC --isUtc |
1479 | +# Disk |
1480 | zerombr |
1481 | -# Partition clearing information |
1482 | -clearpart --all |
1483 | -# Disk partitioning information |
1484 | +clearpart --all --initlabel |
1485 | part / --fstype="ext4" --size=3072 |
1486 | |
1487 | -%post |
1488 | +%post --erroronfail |
1489 | + |
1490 | +# workaround anaconda requirements |
1491 | +passwd -d root |
1492 | +passwd -l root |
1493 | + |
1494 | +# remove avahi and networkmanager |
1495 | +echo "Removing avahi/zeroconf and NetworkManager" |
1496 | +yum -C -y remove avahi\* Network\* |
1497 | |
1498 | # make sure firstboot doesn't start |
1499 | echo "RUN_FIRSTBOOT=NO" > /etc/sysconfig/firstboot |
1500 | |
1501 | -cat <<EOL >> /etc/rc.local |
1502 | -if [ ! -d /root/.ssh ] ; then |
1503 | - mkdir -p /root/.ssh |
1504 | - chmod 0700 /root/.ssh |
1505 | - restorecon /root/.ssh |
1506 | -fi |
1507 | -EOL |
1508 | - |
1509 | -cat <<EOL >> /etc/ssh/sshd_config |
1510 | -UseDNS no |
1511 | -PermitRootLogin without-password |
1512 | -EOL |
1513 | - |
1514 | -# bz705572 |
1515 | -ln -s /boot/grub/grub.conf /etc/grub.conf |
1516 | - |
1517 | -# bz688608 |
1518 | -sed -i 's|\(^PasswordAuthentication \)yes|\1no|' /etc/ssh/sshd_config |
1519 | - |
1520 | -# allow sudo powers to cloud-user |
1521 | -echo -e 'cloud-user\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers |
1522 | - |
1523 | -#setup getty on ttyS0 |
1524 | -echo "ttyS0" >> /etc/securetty |
1525 | -cat <<EOF > /etc/init/ttyS0.conf |
1526 | -start on stopped rc RUNLEVEL=[2345] |
1527 | -stop on starting runlevel [016] |
1528 | -respawn |
1529 | -instance /dev/ttyS0 |
1530 | -exec /sbin/agetty /dev/ttyS0 115200 vt100-nav |
1531 | +echo "Cleaning old yum repodata." |
1532 | +yum clean all |
1533 | + |
1534 | +# chance dhcp client retry/timeouts to resolve #6866 |
1535 | +cat >> /etc/dhcp/dhclient.conf << EOF |
1536 | + |
1537 | +timeout 300; |
1538 | +retry 60; |
1539 | EOF |
1540 | |
1541 | -# lock root password |
1542 | -passwd -d root |
1543 | -passwd -l root |
1544 | - |
1545 | -# fix the cloud-init config, so its for rhel and not for fedora |
1546 | -sed -i 's/name: fedora/name: cloud-user/g' /etc/cloud/cloud.cfg |
1547 | -sed -i 's/gecos: Fedora Cloud User/gecos: CentOS Cloud User/g' /etc/cloud/cloud.cfg |
1548 | - |
1549 | -# delete the eth0 config |
1550 | -rm -rf /etc/sysconfig/network-scripts/ifcfg-eth0 |
1551 | - |
1552 | # clean up installation logs" |
1553 | -yum clean all |
1554 | rm -rf /var/log/yum.log |
1555 | rm -rf /var/lib/yum/* |
1556 | rm -rf /root/install.log |
1557 | rm -rf /root/install.log.syslog |
1558 | rm -rf /root/anaconda-ks.cfg |
1559 | rm -rf /var/log/anaconda* |
1560 | +rm -rf /root/anac* |
1561 | + |
1562 | +echo "Fixing SELinux contexts." |
1563 | +touch /var/log/cron |
1564 | +touch /var/log/boot.log |
1565 | +mkdir -p /var/cache/yum |
1566 | +/usr/sbin/fixfiles -R -a restore |
1567 | + |
1568 | +# reorder console entries |
1569 | +sed -i 's/console=tty0/console=tty0 console=ttyS0,115200n8/' /boot/grub2/grub.cfg |
1570 | + |
1571 | %end |
1572 | |
1573 | %packages |
1574 | @core |
1575 | +chrony |
1576 | cloud-init |
1577 | +cloud-utils-growpart |
1578 | +dracut-config-generic |
1579 | +dracut-norescue |
1580 | +efibootmgr |
1581 | +firewalld |
1582 | +grub2 |
1583 | +grub2-efi-modules |
1584 | +kernel |
1585 | +rsync |
1586 | +tar |
1587 | +yum-utils |
1588 | python-oauth |
1589 | -# Don't install NetworkManager |
1590 | -NetworkManager |
1591 | +-aic94xx-firmware |
1592 | +-alsa-firmware |
1593 | +-alsa-lib |
1594 | +-alsa-tools-firmware |
1595 | +-iprutils |
1596 | +-ivtv-firmware |
1597 | +-iwl100-firmware |
1598 | +-iwl1000-firmware |
1599 | +-iwl105-firmware |
1600 | +-iwl135-firmware |
1601 | +-iwl2000-firmware |
1602 | +-iwl2030-firmware |
1603 | +-iwl3160-firmware |
1604 | +-iwl3945-firmware |
1605 | +-iwl4965-firmware |
1606 | +-iwl5000-firmware |
1607 | +-iwl5150-firmware |
1608 | +-iwl6000-firmware |
1609 | +-iwl6000g2a-firmware |
1610 | +-iwl6000g2b-firmware |
1611 | +-iwl6050-firmware |
1612 | +-iwl7260-firmware |
1613 | +-iwl7265-firmware |
1614 | +-libertas-sd8686-firmware |
1615 | +-libertas-sd8787-firmware |
1616 | +-libertas-usb8388-firmware |
1617 | +-plymouth |
1618 | +-postfix |
1619 | +-wpa_supplicant |
1620 | |
1621 | %end |
1622 | + |
1623 | |
1624 | === modified file 'contrib/centos/centos7/curtin/curtin-hooks' |
1625 | --- contrib/centos/centos7/curtin/curtin-hooks 2015-07-23 20:03:39 +0000 |
1626 | +++ contrib/centos/centos7/curtin/curtin-hooks 1970-01-01 00:00:00 +0000 |
1627 | @@ -1,333 +0,0 @@ |
1628 | -#!/usr/bin/env python |
1629 | - |
1630 | -from __future__ import ( |
1631 | - absolute_import, |
1632 | - print_function, |
1633 | - unicode_literals, |
1634 | - ) |
1635 | - |
1636 | -import os |
1637 | -import re |
1638 | -import sys |
1639 | -import shutil |
1640 | - |
1641 | -sys.path.append('/curtin') |
1642 | -from curtin import ( |
1643 | - block, |
1644 | - net, |
1645 | - util, |
1646 | - ) |
1647 | - |
1648 | -""" |
1649 | -CentOS 7 |
1650 | - |
1651 | -Currently Support: |
1652 | - |
1653 | -- Legacy boot |
1654 | -- UEFI boot |
1655 | -- DHCP of BOOTIF |
1656 | - |
1657 | -Not Supported: |
1658 | - |
1659 | -- Multiple network configration |
1660 | -- IPv6 |
1661 | -""" |
1662 | - |
1663 | -FSTAB_PREPEND = """\ |
1664 | -# |
1665 | -# /etc/fstab |
1666 | -# Created by MAAS fast-path installer. |
1667 | -# |
1668 | -# Accessible filesystems, by reference, are maintained under '/dev/disk' |
1669 | -# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info |
1670 | -# |
1671 | -""" |
1672 | - |
1673 | -FSTAB_UEFI = """\ |
1674 | -LABEL=uefi-boot /boot/efi vfat defaults 0 0 |
1675 | -""" |
1676 | - |
1677 | -GRUB_PREPEND = """\ |
1678 | -# Set by MAAS fast-path installer. |
1679 | -GRUB_TIMEOUT=0 |
1680 | -GRUB_TERMINAL_OUTPUT=console |
1681 | -GRUB_DISABLE_OS_PROBER=true |
1682 | -""" |
1683 | - |
1684 | - |
1685 | -def get_block_devices(target): |
1686 | - """Returns list of block devices for the given target.""" |
1687 | - devs = block.get_devices_for_mp(target) |
1688 | - blockdevs = set() |
1689 | - for maybepart in devs: |
1690 | - (blockdev, part) = block.get_blockdev_for_partition(maybepart) |
1691 | - blockdevs.add(blockdev) |
1692 | - return list(blockdevs) |
1693 | - |
1694 | - |
1695 | -def get_root_info(target): |
1696 | - """Returns the root partitions information.""" |
1697 | - rootpath = block.get_devices_for_mp(target)[0] |
1698 | - rootdev = os.path.basename(rootpath) |
1699 | - blocks = block._lsblock() |
1700 | - return blocks[rootdev] |
1701 | - |
1702 | - |
1703 | -def get_uefi_partition(): |
1704 | - """Return the UEFI partition.""" |
1705 | - for _, value in block._lsblock().items(): |
1706 | - if value['LABEL'] == 'uefi-boot': |
1707 | - return value |
1708 | - return None |
1709 | - |
1710 | - |
1711 | -def read_file(path): |
1712 | - """Returns content of a file.""" |
1713 | - with open(path, 'rb') as stream: |
1714 | - return stream.read().encode('utf-8') |
1715 | - |
1716 | - |
1717 | -def write_fstab(target, curtin_fstab): |
1718 | - """Writes the new fstab, using the fstab provided |
1719 | - from curtin.""" |
1720 | - fstab_path = os.path.join(target, 'etc', 'fstab') |
1721 | - fstab_data = read_file(curtin_fstab) |
1722 | - with open(fstab_path, 'w') as stream: |
1723 | - stream.write(FSTAB_PREPEND) |
1724 | - stream.write(fstab_data) |
1725 | - if util.is_uefi_bootable(): |
1726 | - stream.write(FSTAB_UEFI) |
1727 | - |
1728 | - |
1729 | -def strip_kernel_params(params, strip_params=[]): |
1730 | - """Removes un-needed kernel parameters.""" |
1731 | - new_params = [] |
1732 | - for param in params: |
1733 | - remove = False |
1734 | - for strip in strip_params: |
1735 | - if param.startswith(strip): |
1736 | - remove = True |
1737 | - break |
1738 | - if remove is False: |
1739 | - new_params.append(param) |
1740 | - return new_params |
1741 | - |
1742 | - |
1743 | -def get_extra_kernel_parameters(): |
1744 | - """Extracts the extra kernel commands from /proc/cmdline |
1745 | - that should be placed onto the host. |
1746 | - |
1747 | - Any command following the '--' entry should be placed |
1748 | - onto the host. |
1749 | - """ |
1750 | - cmdline = read_file('/proc/cmdline') |
1751 | - cmdline = cmdline.split() |
1752 | - if '--' not in cmdline: |
1753 | - return [] |
1754 | - idx = cmdline.index('--') + 1 |
1755 | - if idx >= len(cmdline) + 1: |
1756 | - return [] |
1757 | - return strip_kernel_params( |
1758 | - cmdline[idx:], |
1759 | - strip_params=['initrd=', 'BOOT_IMAGE=', 'BOOTIF=']) |
1760 | - |
1761 | - |
1762 | -def update_grub_default(target, extra=[]): |
1763 | - """Updates /etc/default/grub with the correct options.""" |
1764 | - grub_default_path = os.path.join(target, 'etc', 'default', 'grub') |
1765 | - kernel_cmdline = ' '.join(extra) |
1766 | - with open(grub_default_path, 'a') as stream: |
1767 | - stream.write(GRUB_PREPEND) |
1768 | - stream.write('GRUB_CMDLINE_LINUX=\"%s\"\n' % kernel_cmdline) |
1769 | - |
1770 | - |
1771 | -def grub2_install(target, root): |
1772 | - """Installs grub2 to the root.""" |
1773 | - with util.RunInChroot(target) as in_chroot: |
1774 | - in_chroot(['grub2-install', '--recheck', root]) |
1775 | - |
1776 | - |
1777 | -def grub2_mkconfig(target): |
1778 | - """Writes the new grub2 config.""" |
1779 | - with util.RunInChroot(target) as in_chroot: |
1780 | - in_chroot(['grub2-mkconfig', '-o', '/boot/grub2/grub.cfg']) |
1781 | - |
1782 | - |
1783 | -def install_efi(target, uefi_path): |
1784 | - """Install the EFI data from /boot into efi partition.""" |
1785 | - # Create temp mount point for uefi partition. |
1786 | - tmp_efi = os.path.join(target, 'boot', 'efi_part') |
1787 | - os.mkdir(tmp_efi) |
1788 | - util.subp(['mount', uefi_path, tmp_efi]) |
1789 | - |
1790 | - # Copy the data over. |
1791 | - try: |
1792 | - efi_path = os.path.join(target, 'boot', 'efi') |
1793 | - if os.path.exists(os.path.join(tmp_efi, 'EFI')): |
1794 | - shutil.rmtree(os.path.join(tmp_efi, 'EFI')) |
1795 | - shutil.copytree( |
1796 | - os.path.join(efi_path, 'EFI'), |
1797 | - os.path.join(tmp_efi, 'EFI')) |
1798 | - finally: |
1799 | - # Clean up tmp mount |
1800 | - util.subp(['umount', tmp_efi]) |
1801 | - os.rmdir(tmp_efi) |
1802 | - |
1803 | - # Mount and do grub install |
1804 | - util.subp(['mount', uefi_path, efi_path]) |
1805 | - try: |
1806 | - with util.RunInChroot(target) as in_chroot: |
1807 | - in_chroot([ |
1808 | - 'grub2-install', '--target=x86_64-efi', |
1809 | - '--efi-directory', '/boot/efi', |
1810 | - '--recheck']) |
1811 | - finally: |
1812 | - util.subp(['umount', efi_path]) |
1813 | - |
1814 | - |
1815 | -def set_autorelabel(target): |
1816 | - """Creates file /.autorelabel. |
1817 | - |
1818 | - This is used by SELinux to relabel all of the |
1819 | - files on the filesystem to have the correct |
1820 | - security context. Without this SSH login will |
1821 | - fail. |
1822 | - """ |
1823 | - path = os.path.join(target, '.autorelabel') |
1824 | - open(path, 'a').close() |
1825 | - |
1826 | - |
1827 | -def get_boot_mac(): |
1828 | - """Return the mac address of the booting interface.""" |
1829 | - cmdline = read_file('/proc/cmdline') |
1830 | - cmdline = cmdline.split() |
1831 | - try: |
1832 | - bootif = [ |
1833 | - option |
1834 | - for option in cmdline |
1835 | - if option.startswith('BOOTIF') |
1836 | - ][0] |
1837 | - except IndexError: |
1838 | - return None |
1839 | - _, mac = bootif.split('=') |
1840 | - mac = mac.split('-')[1:] |
1841 | - return ':'.join(mac) |
1842 | - |
1843 | - |
1844 | -def get_interface_names(): |
1845 | - """Return a dictionary mapping mac addresses to interface names.""" |
1846 | - sys_path = "/sys/class/net" |
1847 | - ifaces = {} |
1848 | - for iname in os.listdir(sys_path): |
1849 | - mac = read_file(os.path.join(sys_path, iname, "address")) |
1850 | - mac = mac.strip().lower() |
1851 | - ifaces[mac] = iname |
1852 | - return ifaces |
1853 | - |
1854 | - |
1855 | -def get_ipv4_config(iface, data): |
1856 | - """Returns the contents of the interface file for ipv4.""" |
1857 | - config = [ |
1858 | - 'TYPE="Ethernet"', |
1859 | - 'NM_CONTROLLED="no"', |
1860 | - 'USERCTL="yes"', |
1861 | - ] |
1862 | - if 'hwaddress' in data: |
1863 | - config.append('HWADDR="%s"' % data['hwaddress']) |
1864 | - # Fallback to using device name |
1865 | - else: |
1866 | - config.append('DEVICE="%"' % iface) |
1867 | - if data['auto']: |
1868 | - config.append('ONBOOT="yes"') |
1869 | - else: |
1870 | - config.append('ONBOOT="no"') |
1871 | - |
1872 | - method = data['method'] |
1873 | - if method == 'dhcp': |
1874 | - config.append('BOOTPROTO="dhcp"') |
1875 | - config.append('PEERDNS="yes"') |
1876 | - config.append('PERSISTENT_DHCLIENT="1"') |
1877 | - if 'hostname' in data: |
1878 | - config.append('DHCP_HOSTNAME="%s"' % data['hostname']) |
1879 | - elif method == 'static': |
1880 | - config.append('BOOTPROTO="none"') |
1881 | - config.append('IPADDR="%s"' % data['address']) |
1882 | - config.append('NETMASK="%s"' % data['netmask']) |
1883 | - if 'broadcast' in data: |
1884 | - config.append('BROADCAST="%s"' % data['broadcast']) |
1885 | - if 'gateway' in data: |
1886 | - config.append('GATEWAY="%s"' % data['gateway']) |
1887 | - elif method == 'manual': |
1888 | - config.append('BOOTPROTO="none"') |
1889 | - return '\n'.join(config) |
1890 | - |
1891 | - |
1892 | -def write_interface_config(target, iface, data): |
1893 | - """Writes config for interface.""" |
1894 | - family = data['family'] |
1895 | - if family != "inet": |
1896 | - # Only supporting ipv4 currently |
1897 | - print( |
1898 | - "WARN: unsupported family %s, " |
1899 | - "failed to configure interface: %s" (family, iface)) |
1900 | - return |
1901 | - config = get_ipv4_config(iface, data) |
1902 | - path = os.path.join( |
1903 | - target, 'etc', 'sysconfig', 'network-scripts', 'ifcfg-%s' % iface) |
1904 | - with open(path, 'w') as stream: |
1905 | - stream.write(config + '\n') |
1906 | - |
1907 | - |
1908 | -def write_network_config(target, mac): |
1909 | - """Write network configuration for the given MAC address.""" |
1910 | - inames = get_interface_names() |
1911 | - iname = inames[mac.lower()] |
1912 | - write_interface_config( |
1913 | - target, iname, { |
1914 | - 'family': 'inet', |
1915 | - 'hwaddress': mac.upper(), |
1916 | - 'auto': True, |
1917 | - 'method': 'dhcp' |
1918 | - }) |
1919 | - |
1920 | - |
1921 | -def main(): |
1922 | - state = util.load_command_environment() |
1923 | - target = state['target'] |
1924 | - if target is None: |
1925 | - print("Target was not provided in the environment.") |
1926 | - sys.exit(1) |
1927 | - fstab = state['fstab'] |
1928 | - if fstab is None: |
1929 | - print("/etc/fstab output was not provided in the environment.") |
1930 | - sys.exit(1) |
1931 | - bootmac = get_boot_mac() |
1932 | - if bootmac is None: |
1933 | - print("Unable to determine boot interface.") |
1934 | - sys.exit(1) |
1935 | - devices = get_block_devices(target) |
1936 | - if not devices: |
1937 | - print("Unable to find block device for: %s" % target) |
1938 | - sys.exit(1) |
1939 | - |
1940 | - write_fstab(target, fstab) |
1941 | - |
1942 | - update_grub_default( |
1943 | - target, extra=get_extra_kernel_parameters()) |
1944 | - grub2_mkconfig(target) |
1945 | - if util.is_uefi_bootable(): |
1946 | - uefi_part = get_uefi_partition() |
1947 | - if uefi_part is None: |
1948 | - print('Unable to determine UEFI parition.') |
1949 | - sys.exit(1) |
1950 | - install_efi(target, uefi_part['device_path']) |
1951 | - else: |
1952 | - for dev in devices: |
1953 | - grub2_install(target, dev) |
1954 | - |
1955 | - set_autorelabel(target) |
1956 | - write_network_config(target, bootmac) |
1957 | - |
1958 | - |
1959 | -if __name__ == "__main__": |
1960 | - main() |
1961 | |
1962 | === target is u'python_wrapper' |
1963 | === added file 'contrib/centos/centos7/curtin/curtin-hooks.py' |
1964 | --- contrib/centos/centos7/curtin/curtin-hooks.py 1970-01-01 00:00:00 +0000 |
1965 | +++ contrib/centos/centos7/curtin/curtin-hooks.py 2017-06-13 16:13:40 +0000 |
1966 | @@ -0,0 +1,338 @@ |
1967 | +#!/usr/bin/env python |
1968 | + |
1969 | +from __future__ import ( |
1970 | + absolute_import, |
1971 | + print_function, |
1972 | + unicode_literals, |
1973 | + ) |
1974 | + |
1975 | +import os |
1976 | +import re |
1977 | +import sys |
1978 | +import shutil |
1979 | + |
1980 | +sys.path.append('/curtin') |
1981 | +from curtin import ( |
1982 | + block, |
1983 | + net, |
1984 | + util, |
1985 | + ) |
1986 | + |
1987 | +""" |
1988 | +CentOS/RHEL 7 |
1989 | + |
1990 | +Currently Support: |
1991 | + |
1992 | +- Legacy boot |
1993 | +- UEFI boot |
1994 | +- DHCP of BOOTIF |
1995 | + |
1996 | +Not Supported: |
1997 | + |
1998 | +- Multiple network configration |
1999 | +- IPv6 |
2000 | +""" |
2001 | + |
2002 | +FSTAB_PREPEND = """\ |
2003 | +# |
2004 | +# /etc/fstab |
2005 | +# Created by MAAS fast-path installer. |
2006 | +# |
2007 | +# Accessible filesystems, by reference, are maintained under '/dev/disk' |
2008 | +# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info |
2009 | +# |
2010 | +""" |
2011 | + |
2012 | +FSTAB_UEFI = """\ |
2013 | +LABEL=uefi-boot /boot/efi vfat defaults 0 0 |
2014 | +""" |
2015 | + |
2016 | +GRUB_PREPEND = """\ |
2017 | +# Set by MAAS fast-path installer. |
2018 | +GRUB_TIMEOUT=0 |
2019 | +GRUB_TERMINAL_OUTPUT=console |
2020 | +GRUB_DISABLE_OS_PROBER=true |
2021 | +""" |
2022 | + |
2023 | + |
2024 | +def get_block_devices(target): |
2025 | + """Returns list of block devices for the given target.""" |
2026 | + devs = block.get_devices_for_mp(target) |
2027 | + blockdevs = set() |
2028 | + for maybepart in devs: |
2029 | + (blockdev, part) = block.get_blockdev_for_partition(maybepart) |
2030 | + blockdevs.add(blockdev) |
2031 | + return list(blockdevs) |
2032 | + |
2033 | + |
2034 | +def get_root_info(target): |
2035 | + """Returns the root partitions information.""" |
2036 | + rootpath = block.get_devices_for_mp(target)[0] |
2037 | + rootdev = os.path.basename(rootpath) |
2038 | + blocks = block._lsblock() |
2039 | + return blocks[rootdev] |
2040 | + |
2041 | + |
2042 | +def read_file(path): |
2043 | + """Returns content of a file.""" |
2044 | + with open(path, encoding="utf-8") as stream: |
2045 | + return stream.read() |
2046 | + |
2047 | + |
2048 | +def write_fstab(target, curtin_fstab): |
2049 | + """Writes the new fstab, using the fstab provided |
2050 | + from curtin.""" |
2051 | + fstab_path = os.path.join(target, 'etc', 'fstab') |
2052 | + fstab_data = read_file(curtin_fstab) |
2053 | + with open(fstab_path, 'w') as stream: |
2054 | + stream.write(FSTAB_PREPEND) |
2055 | + stream.write(fstab_data) |
2056 | + if util.is_uefi_bootable(): |
2057 | + stream.write(FSTAB_UEFI) |
2058 | + |
2059 | + |
2060 | +def strip_kernel_params(params, strip_params=[]): |
2061 | + """Removes un-needed kernel parameters.""" |
2062 | + new_params = [] |
2063 | + for param in params: |
2064 | + remove = False |
2065 | + for strip in strip_params: |
2066 | + if param.startswith(strip): |
2067 | + remove = True |
2068 | + break |
2069 | + if remove is False: |
2070 | + new_params.append(param) |
2071 | + return new_params |
2072 | + |
2073 | + |
2074 | +def get_extra_kernel_parameters(): |
2075 | + """Extracts the extra kernel commands from /proc/cmdline |
2076 | + that should be placed onto the host. |
2077 | + |
2078 | + Any command following the '--' entry should be placed |
2079 | + onto the host. |
2080 | + """ |
2081 | + cmdline = read_file('/proc/cmdline') |
2082 | + cmdline = cmdline.split() |
2083 | + if '--' not in cmdline: |
2084 | + return [] |
2085 | + idx = cmdline.index('--') + 1 |
2086 | + if idx >= len(cmdline) + 1: |
2087 | + return [] |
2088 | + return strip_kernel_params( |
2089 | + cmdline[idx:], |
2090 | + strip_params=['initrd=', 'BOOT_IMAGE=', 'BOOTIF=']) |
2091 | + |
2092 | + |
2093 | +def update_grub_default(target, extra=[]): |
2094 | + """Updates /etc/default/grub with the correct options.""" |
2095 | + grub_default_path = os.path.join(target, 'etc', 'default', 'grub') |
2096 | + kernel_cmdline = ' '.join(extra) |
2097 | + with open(grub_default_path, 'a') as stream: |
2098 | + stream.write(GRUB_PREPEND) |
2099 | + stream.write('GRUB_CMDLINE_LINUX=\"%s\"\n' % kernel_cmdline) |
2100 | + |
2101 | + |
2102 | +def grub2_install(target, root): |
2103 | + """Installs grub2 to the root.""" |
2104 | + with util.RunInChroot(target) as in_chroot: |
2105 | + in_chroot(['grub2-install', '--recheck', root]) |
2106 | + |
2107 | + |
2108 | +def grub2_mkconfig(target): |
2109 | + """Writes the new grub2 config.""" |
2110 | + with util.RunInChroot(target) as in_chroot: |
2111 | + in_chroot(['grub2-mkconfig', '-o', '/boot/grub2/grub.cfg']) |
2112 | + |
2113 | + |
2114 | +def get_efibootmgr_value(output, key): |
2115 | + """Parses the `output` from 'efibootmgr' to return value for `key`.""" |
2116 | + for line in output.splitlines(): |
2117 | + split = line.split(':') |
2118 | + if len(split) == 2: |
2119 | + curr_key = split[0].strip() |
2120 | + value = split[1].strip() |
2121 | + if curr_key == key: |
2122 | + return value |
2123 | + |
2124 | + |
2125 | +def get_file_efi_loaders(output): |
2126 | + """Parses the `output` from 'efibootmgr' to return all loaders that exist |
2127 | + in '\EFI' path.""" |
2128 | + return re.findall( |
2129 | + r"^Boot(?P<hex>[0-9a-fA-F]{4})\*?\s*\S+\s+.*File\(\\EFI.*$", |
2130 | + output, re.MULTILINE) |
2131 | + |
2132 | + |
2133 | +def grub2_install_efi(target): |
2134 | + """Configure for EFI. |
2135 | + |
2136 | + First capture the currently booted loader (normally a network device), |
2137 | + then perform grub installation (adds a new bootloader and adjusts the |
2138 | + boot order), finally re-adjust the boot order so that the currently booted |
2139 | + loader is set to boot first in the new order. |
2140 | + """ |
2141 | + with util.RunInChroot(target) as in_chroot: |
2142 | + stdout, _ = in_chroot(['efibootmgr', '-v'], capture=True) |
2143 | + currently_booted = get_efibootmgr_value(stdout, 'BootCurrent') |
2144 | + loaders = get_file_efi_loaders(stdout) |
2145 | + if currently_booted in loaders: |
2146 | + loaders.remove(currently_booted) |
2147 | + for loader in loaders: |
2148 | + in_chroot(['efibootmgr', '-B', '-b', loader], capture=True) |
2149 | + in_chroot([ |
2150 | + 'grub2-install', '--target=x86_64-efi', |
2151 | + '--efi-directory', '/boot/efi', |
2152 | + '--recheck']) |
2153 | + stdout, _ = in_chroot(['efibootmgr'], capture=True) |
2154 | + currently_booted = get_efibootmgr_value(stdout, 'BootCurrent') |
2155 | + boot_order = get_efibootmgr_value(stdout, 'BootOrder').split(',') |
2156 | + if currently_booted in boot_order: |
2157 | + boot_order.remove(currently_booted) |
2158 | + boot_order = [currently_booted] + boot_order |
2159 | + new_boot_order = ','.join(boot_order) |
2160 | + in_chroot(['efibootmgr', '-o', new_boot_order]) |
2161 | + |
2162 | + |
2163 | +def set_autorelabel(target): |
2164 | + """Creates file /.autorelabel. |
2165 | + |
2166 | + This is used by SELinux to relabel all of the |
2167 | + files on the filesystem to have the correct |
2168 | + security context. Without this SSH login will |
2169 | + fail. |
2170 | + """ |
2171 | + path = os.path.join(target, '.autorelabel') |
2172 | + open(path, 'a').close() |
2173 | + |
2174 | + |
2175 | +def get_boot_mac(): |
2176 | + """Return the mac address of the booting interface.""" |
2177 | + cmdline = read_file('/proc/cmdline') |
2178 | + cmdline = cmdline.split() |
2179 | + try: |
2180 | + bootif = [ |
2181 | + option |
2182 | + for option in cmdline |
2183 | + if option.startswith('BOOTIF') |
2184 | + ][0] |
2185 | + except IndexError: |
2186 | + return None |
2187 | + _, mac = bootif.split('=') |
2188 | + mac = mac.split('-')[1:] |
2189 | + return ':'.join(mac) |
2190 | + |
2191 | + |
2192 | +def get_interface_names(): |
2193 | + """Return a dictionary mapping mac addresses to interface names.""" |
2194 | + sys_path = "/sys/class/net" |
2195 | + ifaces = {} |
2196 | + for iname in os.listdir(sys_path): |
2197 | + mac = read_file(os.path.join(sys_path, iname, "address")) |
2198 | + mac = mac.strip().lower() |
2199 | + ifaces[mac] = iname |
2200 | + return ifaces |
2201 | + |
2202 | + |
2203 | +def get_ipv4_config(iface, data): |
2204 | + """Returns the contents of the interface file for ipv4.""" |
2205 | + config = [ |
2206 | + 'TYPE="Ethernet"', |
2207 | + 'NM_CONTROLLED="no"', |
2208 | + 'USERCTL="yes"', |
2209 | + ] |
2210 | + if 'hwaddress' in data: |
2211 | + config.append('HWADDR="%s"' % data['hwaddress']) |
2212 | + # Fallback to using device name |
2213 | + else: |
2214 | + config.append('DEVICE="%"' % iface) |
2215 | + if data['auto']: |
2216 | + config.append('ONBOOT="yes"') |
2217 | + else: |
2218 | + config.append('ONBOOT="no"') |
2219 | + |
2220 | + method = data['method'] |
2221 | + if method == 'dhcp': |
2222 | + config.append('BOOTPROTO="dhcp"') |
2223 | + config.append('PEERDNS="yes"') |
2224 | + config.append('PERSISTENT_DHCLIENT="1"') |
2225 | + if 'hostname' in data: |
2226 | + config.append('DHCP_HOSTNAME="%s"' % data['hostname']) |
2227 | + elif method == 'static': |
2228 | + config.append('BOOTPROTO="none"') |
2229 | + config.append('IPADDR="%s"' % data['address']) |
2230 | + config.append('NETMASK="%s"' % data['netmask']) |
2231 | + if 'broadcast' in data: |
2232 | + config.append('BROADCAST="%s"' % data['broadcast']) |
2233 | + if 'gateway' in data: |
2234 | + config.append('GATEWAY="%s"' % data['gateway']) |
2235 | + elif method == 'manual': |
2236 | + config.append('BOOTPROTO="none"') |
2237 | + return '\n'.join(config) |
2238 | + |
2239 | + |
2240 | +def write_interface_config(target, iface, data): |
2241 | + """Writes config for interface.""" |
2242 | + family = data['family'] |
2243 | + if family != "inet": |
2244 | + # Only supporting ipv4 currently |
2245 | + print( |
2246 | + "WARN: unsupported family %s, " |
2247 | + "failed to configure interface: %s" (family, iface)) |
2248 | + return |
2249 | + config = get_ipv4_config(iface, data) |
2250 | + path = os.path.join( |
2251 | + target, 'etc', 'sysconfig', 'network-scripts', 'ifcfg-%s' % iface) |
2252 | + with open(path, 'w') as stream: |
2253 | + stream.write(config + '\n') |
2254 | + |
2255 | + |
2256 | +def write_network_config(target, mac): |
2257 | + """Write network configuration for the given MAC address.""" |
2258 | + inames = get_interface_names() |
2259 | + iname = inames[mac.lower()] |
2260 | + write_interface_config( |
2261 | + target, iname, { |
2262 | + 'family': 'inet', |
2263 | + 'hwaddress': mac.upper(), |
2264 | + 'auto': True, |
2265 | + 'method': 'dhcp' |
2266 | + }) |
2267 | + |
2268 | + |
2269 | +def main(): |
2270 | + state = util.load_command_environment() |
2271 | + target = state['target'] |
2272 | + if target is None: |
2273 | + print("Target was not provided in the environment.") |
2274 | + sys.exit(1) |
2275 | + fstab = state['fstab'] |
2276 | + if fstab is None: |
2277 | + print("/etc/fstab output was not provided in the environment.") |
2278 | + sys.exit(1) |
2279 | + bootmac = get_boot_mac() |
2280 | + if bootmac is None: |
2281 | + print("Unable to determine boot interface.") |
2282 | + sys.exit(1) |
2283 | + devices = get_block_devices(target) |
2284 | + if not devices: |
2285 | + print("Unable to find block device for: %s" % target) |
2286 | + sys.exit(1) |
2287 | + |
2288 | + write_fstab(target, fstab) |
2289 | + |
2290 | + update_grub_default( |
2291 | + target, extra=get_extra_kernel_parameters()) |
2292 | + grub2_mkconfig(target) |
2293 | + if util.is_uefi_bootable(): |
2294 | + grub2_install_efi(target) |
2295 | + else: |
2296 | + for dev in devices: |
2297 | + grub2_install(target, dev) |
2298 | + |
2299 | + set_autorelabel(target) |
2300 | + write_network_config(target, bootmac) |
2301 | + |
2302 | + |
2303 | +if __name__ == "__main__": |
2304 | + main() |
2305 | |
2306 | === modified file 'contrib/centos/centos7/curtin/finalize' |
2307 | --- contrib/centos/centos7/curtin/finalize 2015-02-05 13:50:16 +0000 |
2308 | +++ contrib/centos/centos7/curtin/finalize 1970-01-01 00:00:00 +0000 |
2309 | @@ -1,100 +0,0 @@ |
2310 | -#!/usr/bin/env python |
2311 | - |
2312 | -from __future__ import ( |
2313 | - absolute_import, |
2314 | - print_function, |
2315 | - unicode_literals, |
2316 | - ) |
2317 | - |
2318 | -import json |
2319 | -import os |
2320 | -import sys |
2321 | - |
2322 | -sys.path.append('/curtin') |
2323 | -from curtin import util |
2324 | - |
2325 | - |
2326 | -DATASOURCE_LIST = """\ |
2327 | -datasource_list: [ MAAS ] |
2328 | -""" |
2329 | - |
2330 | -DATASOURCE = """\ |
2331 | -datasource: |
2332 | - MAAS: {{consumer_key: {consumer_key}, metadata_url: '{url}', |
2333 | - token_key: {token_key}, token_secret: {token_secret}}} |
2334 | -""" |
2335 | - |
2336 | - |
2337 | -def get_datasource(**kwargs): |
2338 | - """Returns the format cloud-init datasource.""" |
2339 | - return DATASOURCE_LIST + DATASOURCE.format(**kwargs) |
2340 | - |
2341 | - |
2342 | -def load_config(path): |
2343 | - """Loads the curtin config.""" |
2344 | - with open(path, 'r') as stream: |
2345 | - return json.load(stream) |
2346 | - |
2347 | - |
2348 | -def extract_maas_parameters(config): |
2349 | - """Extracts the needed values from the debconf |
2350 | - entry.""" |
2351 | - params = {} |
2352 | - for line in config.splitlines(): |
2353 | - cloud, key, type, value = line.split()[:4] |
2354 | - if key == "cloud-init/maas-metadata-url": |
2355 | - params['url'] = value |
2356 | - elif key == "cloud-init/maas-metadata-credentials": |
2357 | - values = value.split("&") |
2358 | - for oauth in values: |
2359 | - key, value = oauth.split('=') |
2360 | - if key == 'oauth_token_key': |
2361 | - params['token_key'] = value |
2362 | - elif key == 'oauth_token_secret': |
2363 | - params['token_secret'] = value |
2364 | - elif key == 'oauth_consumer_key': |
2365 | - params['consumer_key'] = value |
2366 | - return params |
2367 | - |
2368 | - |
2369 | -def get_maas_debconf_selections(config): |
2370 | - """Gets the debconf selections from the curtin config.""" |
2371 | - try: |
2372 | - return config['debconf_selections']['maas'] |
2373 | - except KeyError: |
2374 | - return None |
2375 | - |
2376 | - |
2377 | -def write_datasource(target, data): |
2378 | - """Writes the cloudinit config into |
2379 | - /etc/cloud/cloud.cfg.d/90_datasource.cfg.""" |
2380 | - path = os.path.join( |
2381 | - target, 'etc', 'cloud', 'cloud.cfg.d', '90_datasource.cfg') |
2382 | - with open(path, 'w') as stream: |
2383 | - stream.write(data + '\n') |
2384 | - |
2385 | - |
2386 | -def main(): |
2387 | - state = util.load_command_environment() |
2388 | - target = state['target'] |
2389 | - if target is None: |
2390 | - print("Target was not provided in the environment.") |
2391 | - sys.exit(1) |
2392 | - config_f = state['config'] |
2393 | - if config_f is None: |
2394 | - print("Config was not provided in the environment.") |
2395 | - sys.exit(1) |
2396 | - config = load_config(config_f) |
2397 | - |
2398 | - debconf = get_maas_debconf_selections(config) |
2399 | - if debconf is None: |
2400 | - print("Failed to get the debconf_selections.") |
2401 | - sys.exit(1) |
2402 | - |
2403 | - params = extract_maas_parameters(debconf) |
2404 | - datasource = get_datasource(**params) |
2405 | - write_datasource(target, datasource) |
2406 | - |
2407 | - |
2408 | -if __name__ == "__main__": |
2409 | - main() |
2410 | |
2411 | === target is u'python_wrapper' |
2412 | === added file 'contrib/centos/centos7/curtin/finalize.py' |
2413 | --- contrib/centos/centos7/curtin/finalize.py 1970-01-01 00:00:00 +0000 |
2414 | +++ contrib/centos/centos7/curtin/finalize.py 2017-06-13 16:13:40 +0000 |
2415 | @@ -0,0 +1,100 @@ |
2416 | +#!/usr/bin/env python |
2417 | + |
2418 | +from __future__ import ( |
2419 | + absolute_import, |
2420 | + print_function, |
2421 | + unicode_literals, |
2422 | + ) |
2423 | + |
2424 | +import json |
2425 | +import os |
2426 | +import sys |
2427 | + |
2428 | +sys.path.append('/curtin') |
2429 | +from curtin import util |
2430 | + |
2431 | + |
2432 | +DATASOURCE_LIST = """\ |
2433 | +datasource_list: [ MAAS ] |
2434 | +""" |
2435 | + |
2436 | +DATASOURCE = """\ |
2437 | +datasource: |
2438 | + MAAS: {{consumer_key: {consumer_key}, metadata_url: '{url}', |
2439 | + token_key: {token_key}, token_secret: {token_secret}}} |
2440 | +""" |
2441 | + |
2442 | + |
2443 | +def get_datasource(**kwargs): |
2444 | + """Returns the format cloud-init datasource.""" |
2445 | + return DATASOURCE_LIST + DATASOURCE.format(**kwargs) |
2446 | + |
2447 | + |
2448 | +def load_config(path): |
2449 | + """Loads the curtin config.""" |
2450 | + with open(path, 'r') as stream: |
2451 | + return json.load(stream) |
2452 | + |
2453 | + |
2454 | +def extract_maas_parameters(config): |
2455 | + """Extracts the needed values from the debconf |
2456 | + entry.""" |
2457 | + params = {} |
2458 | + for line in config.splitlines(): |
2459 | + cloud, key, type, value = line.split()[:4] |
2460 | + if key == "cloud-init/maas-metadata-url": |
2461 | + params['url'] = value |
2462 | + elif key == "cloud-init/maas-metadata-credentials": |
2463 | + values = value.split("&") |
2464 | + for oauth in values: |
2465 | + key, value = oauth.split('=') |
2466 | + if key == 'oauth_token_key': |
2467 | + params['token_key'] = value |
2468 | + elif key == 'oauth_token_secret': |
2469 | + params['token_secret'] = value |
2470 | + elif key == 'oauth_consumer_key': |
2471 | + params['consumer_key'] = value |
2472 | + return params |
2473 | + |
2474 | + |
2475 | +def get_maas_debconf_selections(config): |
2476 | + """Gets the debconf selections from the curtin config.""" |
2477 | + try: |
2478 | + return config['debconf_selections']['maas'] |
2479 | + except KeyError: |
2480 | + return None |
2481 | + |
2482 | + |
2483 | +def write_datasource(target, data): |
2484 | + """Writes the cloudinit config into |
2485 | + /etc/cloud/cloud.cfg.d/90_datasource.cfg.""" |
2486 | + path = os.path.join( |
2487 | + target, 'etc', 'cloud', 'cloud.cfg.d', '90_datasource.cfg') |
2488 | + with open(path, 'w') as stream: |
2489 | + stream.write(data + '\n') |
2490 | + |
2491 | + |
2492 | +def main(): |
2493 | + state = util.load_command_environment() |
2494 | + target = state['target'] |
2495 | + if target is None: |
2496 | + print("Target was not provided in the environment.") |
2497 | + sys.exit(1) |
2498 | + config_f = state['config'] |
2499 | + if config_f is None: |
2500 | + print("Config was not provided in the environment.") |
2501 | + sys.exit(1) |
2502 | + config = load_config(config_f) |
2503 | + |
2504 | + debconf = get_maas_debconf_selections(config) |
2505 | + if debconf is None: |
2506 | + print("Failed to get the debconf_selections.") |
2507 | + sys.exit(1) |
2508 | + |
2509 | + params = extract_maas_parameters(debconf) |
2510 | + datasource = get_datasource(**params) |
2511 | + write_datasource(target, datasource) |
2512 | + |
2513 | + |
2514 | +if __name__ == "__main__": |
2515 | + main() |
2516 | |
2517 | === added file 'contrib/centos/centos7/curtin/python_wrapper' |
2518 | --- contrib/centos/centos7/curtin/python_wrapper 1970-01-01 00:00:00 +0000 |
2519 | +++ contrib/centos/centos7/curtin/python_wrapper 2017-06-13 16:13:40 +0000 |
2520 | @@ -0,0 +1,11 @@ |
2521 | +#!/bin/bash |
2522 | + |
2523 | +export PYTHONPATH='/curtin' |
2524 | + |
2525 | +# Ubuntu 16.04 only ships with Python 3 while previous versions only ship |
2526 | +# with Python 2. |
2527 | +if type -p python > /dev/null; then |
2528 | + exec python "$0.py" "$@" |
2529 | +else |
2530 | + exec python3 "$0.py" "$@" |
2531 | +fi |
2532 | |
2533 | === added directory 'contrib/rhel' |
2534 | === added symlink 'contrib/rhel/curtin' |
2535 | === target is u'../centos/centos7/curtin' |
2536 | === added file 'contrib/rhel/rhel7-amd64.ks' |
2537 | --- contrib/rhel/rhel7-amd64.ks 1970-01-01 00:00:00 +0000 |
2538 | +++ contrib/rhel/rhel7-amd64.ks 2017-06-13 16:13:40 +0000 |
2539 | @@ -0,0 +1,128 @@ |
2540 | +#version=RHEL7 |
2541 | +install |
2542 | +# Poweroff after installation |
2543 | +poweroff |
2544 | +# System authorization information |
2545 | +auth --enableshadow --passalgo=sha512 |
2546 | +# Firewall configuration |
2547 | +firewall --enabled --service=ssh |
2548 | +firstboot --disable |
2549 | +ignoredisk --only-use=vda |
2550 | +# Keyboard layouts |
2551 | +# old format: keyboard us |
2552 | +# new format: |
2553 | +keyboard --vckeymap=us --xlayouts='us' |
2554 | +# System language |
2555 | +lang en_US.UTF-8 |
2556 | +# Use CDROM installation media |
2557 | +cdrom |
2558 | +repo --name="repo0" --baseurl=http://mirrors.kernel.org/centos/7/os/x86_64 --includepkgs=python-pygments |
2559 | +repo --name="repo1" --baseurl=http://mirrors.kernel.org/centos/7/updates/x86_64 --includepkgs=python-pygments |
2560 | +repo --name="repo2" --baseurl=http://mirrors.kernel.org/centos/7/extras/x86_64 |
2561 | +repo --name="repo3" --baseurl=http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/20/Everything/x86_64/os/ --includepkgs=python-oauth |
2562 | +# Network information |
2563 | +network --bootproto=dhcp --device=eth0 --onboot=on |
2564 | +# System bootloader configuration |
2565 | +bootloader --append="console=ttyS0,115200n8 console=tty0" --location=mbr --driveorder="vda" --timeout=1 |
2566 | +# Root password |
2567 | +rootpw --iscrypted nothing |
2568 | +selinux --enforcing |
2569 | +services --disabled="kdump" --enabled="network,sshd,rsyslog,chronyd" |
2570 | +timezone UTC --isUtc |
2571 | +# Disk |
2572 | +zerombr |
2573 | +clearpart --all --initlabel |
2574 | +part / --fstype="ext4" --size=3072 |
2575 | + |
2576 | +%post --erroronfail |
2577 | + |
2578 | +# workaround anaconda requirements |
2579 | +passwd -d root |
2580 | +passwd -l root |
2581 | + |
2582 | +# remove avahi and networkmanager |
2583 | +echo "Removing avahi/zeroconf and NetworkManager" |
2584 | +yum -C -y remove avahi\* Network\* |
2585 | + |
2586 | +# make sure firstboot doesn't start |
2587 | +echo "RUN_FIRSTBOOT=NO" > /etc/sysconfig/firstboot |
2588 | + |
2589 | +echo "Cleaning old yum repodata." |
2590 | +yum clean all |
2591 | + |
2592 | +# chance dhcp client retry/timeouts to resolve #6866 |
2593 | +cat >> /etc/dhcp/dhclient.conf << EOF |
2594 | + |
2595 | +timeout 300; |
2596 | +retry 60; |
2597 | +EOF |
2598 | + |
2599 | +# clean up installation logs" |
2600 | +rm -rf /var/log/yum.log |
2601 | +rm -rf /var/lib/yum/* |
2602 | +rm -rf /root/install.log |
2603 | +rm -rf /root/install.log.syslog |
2604 | +rm -rf /root/anaconda-ks.cfg |
2605 | +rm -rf /var/log/anaconda* |
2606 | +rm -rf /root/anac* |
2607 | + |
2608 | +echo "Fixing SELinux contexts." |
2609 | +touch /var/log/cron |
2610 | +touch /var/log/boot.log |
2611 | +mkdir -p /var/cache/yum |
2612 | +/usr/sbin/fixfiles -R -a restore |
2613 | + |
2614 | +# reorder console entries |
2615 | +sed -i 's/console=tty0/console=tty0 console=ttyS0,115200n8/' /boot/grub2/grub.cfg |
2616 | + |
2617 | +%end |
2618 | + |
2619 | +%packages |
2620 | +@core |
2621 | +chrony |
2622 | +cloud-init |
2623 | +cloud-utils-growpart |
2624 | +dracut-config-generic |
2625 | +dracut-norescue |
2626 | +efibootmgr |
2627 | +firewalld |
2628 | +grub2 |
2629 | +grub2-efi-modules |
2630 | +kernel |
2631 | +rsync |
2632 | +tar |
2633 | +yum-utils |
2634 | +python-oauth |
2635 | +-NetworkManager |
2636 | +-aic94xx-firmware |
2637 | +-alsa-firmware |
2638 | +-alsa-lib |
2639 | +-alsa-tools-firmware |
2640 | +-iprutils |
2641 | +-ivtv-firmware |
2642 | +-iwl100-firmware |
2643 | +-iwl1000-firmware |
2644 | +-iwl105-firmware |
2645 | +-iwl135-firmware |
2646 | +-iwl2000-firmware |
2647 | +-iwl2030-firmware |
2648 | +-iwl3160-firmware |
2649 | +-iwl3945-firmware |
2650 | +-iwl4965-firmware |
2651 | +-iwl5000-firmware |
2652 | +-iwl5150-firmware |
2653 | +-iwl6000-firmware |
2654 | +-iwl6000g2a-firmware |
2655 | +-iwl6000g2b-firmware |
2656 | +-iwl6050-firmware |
2657 | +-iwl7260-firmware |
2658 | +-iwl7265-firmware |
2659 | +-libertas-sd8686-firmware |
2660 | +-libertas-sd8787-firmware |
2661 | +-libertas-usb8388-firmware |
2662 | +-plymouth |
2663 | +-postfix |
2664 | +-wpa_supplicant |
2665 | + |
2666 | +%end |
2667 | + |
2668 | |
2669 | === added directory 'contrib/windows' |
2670 | === added file 'contrib/windows/Autounattend.xml' |
2671 | --- contrib/windows/Autounattend.xml 1970-01-01 00:00:00 +0000 |
2672 | +++ contrib/windows/Autounattend.xml 2017-06-13 16:13:40 +0000 |
2673 | @@ -0,0 +1,148 @@ |
2674 | +<?xml version="1.0" encoding="utf-8"?> |
2675 | +<unattend xmlns="urn:schemas-microsoft-com:unattend"> |
2676 | + <settings pass="windowsPE"> |
2677 | + <component name="Microsoft-Windows-Setup" processorArchitecture="{{arch}}" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
2678 | + <DiskConfiguration> |
2679 | + <WillShowUI>OnError</WillShowUI> |
2680 | + <Disk wcm:action="add"> |
2681 | + <CreatePartitions> |
2682 | + <CreatePartition wcm:action="add"> |
2683 | + <Order>1</Order> |
2684 | + <Size>100</Size> |
2685 | + <Type>Primary</Type> |
2686 | + </CreatePartition> |
2687 | + <CreatePartition wcm:action="add"> |
2688 | + <Order>2</Order> |
2689 | + <Extend>true</Extend> |
2690 | + <Type>Primary</Type> |
2691 | + </CreatePartition> |
2692 | + </CreatePartitions> |
2693 | + <ModifyPartitions> |
2694 | + <ModifyPartition wcm:action="add"> |
2695 | + <Active>true</Active> |
2696 | + <Label>Boot</Label> |
2697 | + <Format>NTFS</Format> |
2698 | + <Order>1</Order> |
2699 | + <PartitionID>1</PartitionID> |
2700 | + </ModifyPartition> |
2701 | + <ModifyPartition wcm:action="add"> |
2702 | + <Format>NTFS</Format> |
2703 | + <Order>2</Order> |
2704 | + <PartitionID>2</PartitionID> |
2705 | + <Label>System</Label> |
2706 | + </ModifyPartition> |
2707 | + </ModifyPartitions> |
2708 | + <DiskID>0</DiskID> |
2709 | + <WillWipeDisk>true</WillWipeDisk> |
2710 | + </Disk> |
2711 | + </DiskConfiguration> |
2712 | + <ImageInstall> |
2713 | + <OSImage> |
2714 | + <InstallTo> |
2715 | + <PartitionID>2</PartitionID> |
2716 | + <DiskID>0</DiskID> |
2717 | + </InstallTo> |
2718 | + <InstallToAvailablePartition>false</InstallToAvailablePartition> |
2719 | + <WillShowUI>OnError</WillShowUI> |
2720 | + <InstallFrom> |
2721 | + <MetaData wcm:action="add"> |
2722 | + <Key>/IMAGE/NAME</Key> |
2723 | + <Value>{{image_name}}</Value> |
2724 | + </MetaData> |
2725 | + </InstallFrom> |
2726 | + </OSImage> |
2727 | + </ImageInstall> |
2728 | + <UserData> |
2729 | + <AcceptEula>true</AcceptEula> |
2730 | + {{if license_key}} |
2731 | + <ProductKey> |
2732 | + <Key>{{license_key}}</Key> |
2733 | + <WillShowUI>OnError</WillShowUI> |
2734 | + </ProductKey> |
2735 | + {{endif}} |
2736 | + </UserData> |
2737 | + </component> |
2738 | + <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="{{arch}}" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
2739 | + <SetupUILanguage> |
2740 | + <UILanguage>{{language}}</UILanguage> |
2741 | + </SetupUILanguage> |
2742 | + <InputLocale>{{language}}</InputLocale> |
2743 | + <UILanguage>{{language}}</UILanguage> |
2744 | + <SystemLocale>{{language}}</SystemLocale> |
2745 | + <UserLocale>{{language}}</UserLocale> |
2746 | + </component> |
2747 | + </settings> |
2748 | + <settings pass="oobeSystem"> |
2749 | + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="{{arch}}" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
2750 | + <VisualEffects> |
2751 | + <FontSmoothing>ClearType</FontSmoothing> |
2752 | + </VisualEffects> |
2753 | + <OOBE> |
2754 | + <HideEULAPage>true</HideEULAPage> |
2755 | + <ProtectYourPC>3</ProtectYourPC> |
2756 | + <NetworkLocation>Work</NetworkLocation> |
2757 | + <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> |
2758 | + <!-- Comment the following two options on Windows Vista / 7 and Windows Server 2008 / 2008 R2 --> |
2759 | + <!-- |
2760 | + <HideOnlineAccountScreens>true</HideOnlineAccountScreens> |
2761 | + <HideLocalAccountScreen>true</HideLocalAccountScreen> |
2762 | + --> |
2763 | + </OOBE> |
2764 | + {{if enable_updates}} |
2765 | + <LogonCommands> |
2766 | + <AsynchronousCommand wcm:action="add"> |
2767 | + <CommandLine>%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell -NoLogo -NonInteractive -ExecutionPolicy RemoteSigned -File E:\scripts\logon.ps1</CommandLine> |
2768 | + <Order>1</Order> |
2769 | + </AsynchronousCommand> |
2770 | + </LogonCommands> |
2771 | + {{else}} |
2772 | + <FirstLogonCommands> |
2773 | + <SynchronousCommand wcm:action="add"> |
2774 | + <CommandLine>%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell -NoLogo -NonInteractive -ExecutionPolicy RemoteSigned -File E:\scripts\firstlogon.ps1</CommandLine> |
2775 | + <Order>1</Order> |
2776 | + </SynchronousCommand> |
2777 | + </FirstLogonCommands> |
2778 | + {{endif}} |
2779 | + <UserAccounts> |
2780 | + <AdministratorPassword> |
2781 | + <Value>Passw0rd</Value> |
2782 | + <PlainText>true</PlainText> |
2783 | + </AdministratorPassword> |
2784 | + </UserAccounts> |
2785 | + <AutoLogon> |
2786 | + <Password> |
2787 | + <Value>Passw0rd</Value> |
2788 | + <PlainText>true</PlainText> |
2789 | + </Password> |
2790 | + <Enabled>true</Enabled> |
2791 | + <LogonCount>50</LogonCount> |
2792 | + <Username>Administrator</Username> |
2793 | + </AutoLogon> |
2794 | + <ComputerName>*</ComputerName> |
2795 | + </component> |
2796 | + </settings> |
2797 | + <settings pass="specialize"> |
2798 | + <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="{{arch}}" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
2799 | + <fDenyTSConnections>false</fDenyTSConnections> |
2800 | + </component> |
2801 | + <component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" processorArchitecture="{{arch}}" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
2802 | + <UserAuthentication>0</UserAuthentication> |
2803 | + </component> |
2804 | + <component name="Networking-MPSSVC-Svc" processorArchitecture="{{arch}}" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
2805 | + <FirewallGroups> |
2806 | + <FirewallGroup wcm:action="add" wcm:keyValue="RemoteDesktop"> |
2807 | + <Active>true</Active> |
2808 | + <Profile>all</Profile> |
2809 | + <Group>@FirewallAPI.dll,-28752</Group> |
2810 | + </FirewallGroup> |
2811 | + </FirewallGroups> |
2812 | + </component> |
2813 | + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="{{arch}}" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="NonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
2814 | + <TimeZone>UTC</TimeZone> |
2815 | + <ComputerName>*</ComputerName> |
2816 | + </component> |
2817 | + <component name="Microsoft-Windows-SQMApi" processorArchitecture="{{arch}}" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="NonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
2818 | + <CEIPEnabled>0</CEIPEnabled> |
2819 | + </component> |
2820 | + </settings> |
2821 | +</unattend> |
2822 | |
2823 | === added directory 'contrib/windows/curtin' |
2824 | === added file 'contrib/windows/curtin/curtin-hooks' |
2825 | --- contrib/windows/curtin/curtin-hooks 1970-01-01 00:00:00 +0000 |
2826 | +++ contrib/windows/curtin/curtin-hooks 2017-06-13 16:13:40 +0000 |
2827 | @@ -0,0 +1,1 @@ |
2828 | +#!/bin/bash |
2829 | |
2830 | === added symlink 'contrib/windows/curtin/finalize' |
2831 | === target is u'python_wrapper' |
2832 | === added file 'contrib/windows/curtin/finalize.py' |
2833 | --- contrib/windows/curtin/finalize.py 1970-01-01 00:00:00 +0000 |
2834 | +++ contrib/windows/curtin/finalize.py 2017-06-13 16:13:40 +0000 |
2835 | @@ -0,0 +1,165 @@ |
2836 | +#!/usr/bin/env python |
2837 | + |
2838 | +from __future__ import ( |
2839 | + absolute_import, |
2840 | + print_function, |
2841 | + unicode_literals, |
2842 | + ) |
2843 | + |
2844 | +import os |
2845 | +import sys |
2846 | +import json |
2847 | + |
2848 | +sys.path.append('/curtin') |
2849 | +from curtin import util |
2850 | + |
2851 | +CLOUDBASE_INIT_CONFIG = """\ |
2852 | +metadata_services=cloudbaseinit.metadata.services.maasservice.MaaSHttpService |
2853 | +maas_metadata_url={url} |
2854 | +maas_oauth_consumer_key={consumer_key} |
2855 | +maas_oauth_consumer_secret='' |
2856 | +maas_oauth_token_key={token_key} |
2857 | +maas_oauth_token_secret={token_secret} |
2858 | +""" |
2859 | + |
2860 | + |
2861 | +LICENSE_KEY_SCRIPT = """\ |
2862 | +slmgr /ipk {license_key} |
2863 | +Remove-Item $MyInvocation.InvocationName |
2864 | +""" |
2865 | + |
2866 | + |
2867 | +def load_config(path): |
2868 | + """Loads the curtin config.""" |
2869 | + with open(path, 'r') as stream: |
2870 | + return json.load(stream) |
2871 | + |
2872 | + |
2873 | +def get_maas_debconf_selections(config): |
2874 | + """Gets the debconf selections from the curtin config.""" |
2875 | + try: |
2876 | + return config['debconf_selections']['maas'] |
2877 | + except KeyError: |
2878 | + return None |
2879 | + |
2880 | + |
2881 | +def extract_maas_parameters(config): |
2882 | + """Extracts the needed values from the debconf |
2883 | + entry.""" |
2884 | + params = {} |
2885 | + for line in config.splitlines(): |
2886 | + cloud, key, type, value = line.split()[:4] |
2887 | + if key == "cloud-init/maas-metadata-url": |
2888 | + params['url'] = value |
2889 | + elif key == "cloud-init/maas-metadata-credentials": |
2890 | + values = value.split("&") |
2891 | + for oauth in values: |
2892 | + key, value = oauth.split('=') |
2893 | + if key == 'oauth_token_key': |
2894 | + params['token_key'] = value |
2895 | + elif key == 'oauth_token_secret': |
2896 | + params['token_secret'] = value |
2897 | + elif key == 'oauth_consumer_key': |
2898 | + params['consumer_key'] = value |
2899 | + return params |
2900 | + |
2901 | + |
2902 | +def get_cloudbase_init_config(params): |
2903 | + """Returns the cloudbase-init config file.""" |
2904 | + config = CLOUDBASE_INIT_CONFIG.format(**params) |
2905 | + output = "" |
2906 | + for line in config.splitlines(): |
2907 | + output += "%s\r\n" % line |
2908 | + return output |
2909 | + |
2910 | + |
2911 | +def write_cloudbase_init(target, params): |
2912 | + """Writes the configuration files for cloudbase-init.""" |
2913 | + cloudbase_init_cfg = os.path.join( |
2914 | + target, |
2915 | + "Program Files", |
2916 | + "Cloudbase Solutions", |
2917 | + "Cloudbase-Init", |
2918 | + "conf", |
2919 | + "cloudbase-init.conf") |
2920 | + cloudbase_init_unattended_cfg = os.path.join( |
2921 | + target, |
2922 | + "Program Files", |
2923 | + "Cloudbase Solutions", |
2924 | + "Cloudbase-Init", |
2925 | + "conf", |
2926 | + "cloudbase-init-unattend.conf") |
2927 | + |
2928 | + config = get_cloudbase_init_config(params) |
2929 | + with open(cloudbase_init_cfg, 'a') as stream: |
2930 | + stream.write(config) |
2931 | + with open(cloudbase_init_unattended_cfg, 'a') as stream: |
2932 | + stream.write(config) |
2933 | + |
2934 | + |
2935 | +def get_license_key(config): |
2936 | + """Return license_key from the curtin config.""" |
2937 | + try: |
2938 | + license_key = config['license_key'] |
2939 | + except KeyError: |
2940 | + return None |
2941 | + if license_key is None: |
2942 | + return None |
2943 | + license_key = license_key.strip() |
2944 | + if license_key == '': |
2945 | + return None |
2946 | + return license_key |
2947 | + |
2948 | + |
2949 | +def write_license_key_script(target, license_key): |
2950 | + local_scripts_path = os.path.join( |
2951 | + target, |
2952 | + "Program Files", |
2953 | + "Cloudbase Solutions", |
2954 | + "Cloudbase-Init", |
2955 | + "LocalScripts") |
2956 | + script_path = os.path.join(local_scripts_path, 'set_license_key.ps1') |
2957 | + set_key_script = LICENSE_KEY_SCRIPT.format(license_key=license_key) |
2958 | + with open(script_path, 'w') as stream: |
2959 | + for line in set_key_script.splitlines(): |
2960 | + stream.write("%s\r\n" % line) |
2961 | + |
2962 | + |
2963 | +def write_network_config(target, config): |
2964 | + network_config_path = os.path.join(target, 'network.json') |
2965 | + config_json = config.get('network', None) |
2966 | + if config_json is not None: |
2967 | + config_json = json.dumps(config_json) |
2968 | + with open(network_config_path, 'w') as stream: |
2969 | + for line in config_json.splitlines(): |
2970 | + stream.write("%s\r\n" % line) |
2971 | + |
2972 | + |
2973 | +def main(): |
2974 | + state = util.load_command_environment() |
2975 | + target = state['target'] |
2976 | + if target is None: |
2977 | + print("Target was not provided in the environment.") |
2978 | + sys.exit(1) |
2979 | + config_f = state['config'] |
2980 | + if config_f is None: |
2981 | + print("Config was not provided in the environment.") |
2982 | + sys.exit(1) |
2983 | + config = load_config(config_f) |
2984 | + |
2985 | + debconf = get_maas_debconf_selections(config) |
2986 | + if debconf is None: |
2987 | + print("Failed to get the debconf_selections.") |
2988 | + sys.exit(1) |
2989 | + |
2990 | + params = extract_maas_parameters(debconf) |
2991 | + write_cloudbase_init(target, params) |
2992 | + write_network_config(target, config) |
2993 | + |
2994 | + license_key = get_license_key(config) |
2995 | + if license_key is not None: |
2996 | + write_license_key_script(target, license_key) |
2997 | + |
2998 | + |
2999 | +if __name__ == "__main__": |
3000 | + main() |
3001 | |
3002 | === added file 'contrib/windows/curtin/python_wrapper' |
3003 | --- contrib/windows/curtin/python_wrapper 1970-01-01 00:00:00 +0000 |
3004 | +++ contrib/windows/curtin/python_wrapper 2017-06-13 16:13:40 +0000 |
3005 | @@ -0,0 +1,11 @@ |
3006 | +#!/bin/bash |
3007 | + |
3008 | +export PYTHONPATH='/curtin' |
3009 | + |
3010 | +# Ubuntu 16.04 only ships with Python 3 while previous versions only ship |
3011 | +# with Python 2. |
3012 | +if type -p python > /dev/null; then |
3013 | + exec python "$0.py" "$@" |
3014 | +else |
3015 | + exec python3 "$0.py" "$@" |
3016 | +fi |
3017 | |
3018 | === added directory 'contrib/windows/scripts' |
3019 | === added file 'contrib/windows/scripts/firstlogon.ps1' |
3020 | --- contrib/windows/scripts/firstlogon.ps1 1970-01-01 00:00:00 +0000 |
3021 | +++ contrib/windows/scripts/firstlogon.ps1 2017-06-13 16:13:40 +0000 |
3022 | @@ -0,0 +1,104 @@ |
3023 | +$ErrorActionPreference = "Stop" |
3024 | + |
3025 | +function WaitForNetwork ($seconds) { |
3026 | + while (1) { |
3027 | + # Get a list of DHCP-enabled interfaces that have a |
3028 | + # non-$null DefaultIPGateway property. |
3029 | + $x = gwmi -class Win32_NetworkAdapterConfiguration ` |
3030 | + -filter DHCPEnabled=TRUE | |
3031 | + where { $_.DefaultIPGateway -ne $null } |
3032 | + |
3033 | + # If there is (at least) one available, exit the loop. |
3034 | + if ( ($x | measure).count -gt 0 ) { |
3035 | + break |
3036 | + } |
3037 | + |
3038 | + # If $seconds > 0 and we have tried $seconds times without |
3039 | + # success, throw an exception. |
3040 | + if ( $seconds -gt 0 -and $try++ -ge $seconds ) { |
3041 | + throw "Network unavaiable after $try seconds." |
3042 | + } |
3043 | + |
3044 | + # Wait one second. |
3045 | + start-sleep -s 1 |
3046 | + } |
3047 | +} |
3048 | + |
3049 | +try |
3050 | +{ |
3051 | + $osArch = (Get-WmiObject Win32_OperatingSystem).OSArchitecture |
3052 | + if($osArch.StartsWith("64")) |
3053 | + { |
3054 | + $archDir = "x64" |
3055 | + $programFilesDir = ${ENV:ProgramFiles(x86)} |
3056 | + } |
3057 | + else |
3058 | + { |
3059 | + $archDir = "x86" |
3060 | + $programFilesDir = $ENV:ProgramFiles |
3061 | + } |
3062 | + |
3063 | + # Inject extra drivers if the infs directory is present on the attached iso |
3064 | + if (Test-Path -Path "E:\infs") |
3065 | + { |
3066 | + # To install extra drivers the Windows Driver Kit is needed for dpinst.exe. |
3067 | + # Sadly you cannot just download dpinst.exe. The whole driver kit must be |
3068 | + # installed. |
3069 | + |
3070 | + # Need to have network connection to continue, wait a maximum of 60 |
3071 | + # seconds for the network to be active. |
3072 | + WaitForNetwork 60 |
3073 | + |
3074 | + # Download the WDK installer. |
3075 | + $Host.UI.RawUI.WindowTitle = "Downloading Windows Driver Kit..." |
3076 | + $webclient = New-Object System.Net.WebClient |
3077 | + $wdksetup = [IO.Path]::GetFullPath("$ENV:TEMP\wdksetup.exe") |
3078 | + $wdkurl = "http://download.microsoft.com/download/0/8/C/08C7497F-8551-4054-97DE-60C0E510D97A/wdk/wdksetup.exe" |
3079 | + $webclient.DownloadFile($wdkurl, $wdksetup) |
3080 | + |
3081 | + # Run the installer. |
3082 | + $Host.UI.RawUI.WindowTitle = "Installing Windows Driver Kit..." |
3083 | + $p = Start-Process -PassThru -Wait -FilePath "$wdksetup" -ArgumentList "/features OptionId.WindowsDriverKitComplete /q /ceip off /norestart" |
3084 | + if ($p.ExitCode -ne 0) |
3085 | + { |
3086 | + throw "Installing $wdksetup failed." |
3087 | + } |
3088 | + |
3089 | + # Run dpinst.exe with the path to the drivers. |
3090 | + $Host.UI.RawUI.WindowTitle = "Injecting Windows drivers..." |
3091 | + $dpinst = "$programFilesDir\Windows Kits\8.1\redist\DIFx\dpinst\EngMui\$archDir\dpinst.exe" |
3092 | + Start-Process -Wait -FilePath "$dpinst" -ArgumentList "/S /C /F /SA /Path E:\infs" |
3093 | + |
3094 | + # Uninstall the WDK |
3095 | + $Host.UI.RawUI.WindowTitle = "Uninstalling Windows Driver Kit..." |
3096 | + Start-Process -Wait -FilePath "$wdksetup" -ArgumentList "/features + /q /uninstall /norestart" |
3097 | + } |
3098 | + |
3099 | + $Host.UI.RawUI.WindowTitle = "Installing Cloudbase-Init..." |
3100 | + $cloudbaseInitPath = "E:\cloudbase\cloudbase_init.msi" |
3101 | + $cloudbaseInitLog = "$ENV:Temp\cloudbase_init.log" |
3102 | + $serialPortName = @(Get-WmiObject Win32_SerialPort)[0].DeviceId |
3103 | + $p = Start-Process -Wait -PassThru -FilePath msiexec -ArgumentList "/i $cloudbaseInitPath /qn /l*v $cloudbaseInitLog LOGGINGSERIALPORTNAME=$serialPortName" |
3104 | + if ($p.ExitCode -ne 0) |
3105 | + { |
3106 | + throw "Installing $cloudbaseInitPath failed. Log: $cloudbaseInitLog" |
3107 | + } |
3108 | + |
3109 | + # We're done, disable AutoLogon |
3110 | + Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoLogonCount |
3111 | + |
3112 | + $Host.UI.RawUI.WindowTitle = "Running Cloudbase-Init SetSetupComplete..." |
3113 | + & "$ENV:ProgramFiles\Cloudbase Solutions\Cloudbase-Init\bin\SetSetupComplete.cmd" |
3114 | + |
3115 | + # Write success, this is used to check that this process made it this far |
3116 | + New-Item -Path C:\success.tch -Type file -Force |
3117 | + |
3118 | + $Host.UI.RawUI.WindowTitle = "Running Cloudbase-Init Sysprep..." |
3119 | + $unattendedXmlPath = "$ENV:ProgramFiles\Cloudbase Solutions\Cloudbase-Init\conf\Unattend.xml" |
3120 | + & "$ENV:SystemRoot\System32\Sysprep\Sysprep.exe" `/generalize `/oobe `/shutdown `/unattend:"$unattendedXmlPath" |
3121 | +} |
3122 | +catch |
3123 | +{ |
3124 | + $_ | Out-File C:\error_log.txt |
3125 | + shutdown /s /f /t 0 |
3126 | +} |
3127 | |
3128 | === added file 'contrib/windows/scripts/logon.ps1' |
3129 | --- contrib/windows/scripts/logon.ps1 1970-01-01 00:00:00 +0000 |
3130 | +++ contrib/windows/scripts/logon.ps1 2017-06-13 16:13:40 +0000 |
3131 | @@ -0,0 +1,124 @@ |
3132 | +$ErrorActionPreference = "Stop" |
3133 | + |
3134 | +function WaitForNetwork ($seconds) { |
3135 | + while (1) { |
3136 | + # Get a list of DHCP-enabled interfaces that have a |
3137 | + # non-$null DefaultIPGateway property. |
3138 | + $x = gwmi -class Win32_NetworkAdapterConfiguration ` |
3139 | + -filter DHCPEnabled=TRUE | |
3140 | + where { $_.DefaultIPGateway -ne $null } |
3141 | + |
3142 | + # If there is (at least) one available, exit the loop. |
3143 | + if ( ($x | measure).count -gt 0 ) { |
3144 | + break |
3145 | + } |
3146 | + |
3147 | + # If $seconds > 0 and we have tried $seconds times without |
3148 | + # success, throw an exception. |
3149 | + if ( $seconds -gt 0 -and $try++ -ge $seconds ) { |
3150 | + throw "Network unavaiable after $try seconds." |
3151 | + } |
3152 | + |
3153 | + # Wait one second. |
3154 | + start-sleep -s 1 |
3155 | + } |
3156 | +} |
3157 | + |
3158 | +try |
3159 | +{ |
3160 | + # Need to have network connection to continue, wait a maximum of 60 |
3161 | + # seconds for the network to be active. |
3162 | + WaitForNetwork 60 |
3163 | + |
3164 | + # Install PSWindowsUpdate modules for PowerShell |
3165 | + if (!(Test-Path -Path "$ENV:SystemRoot\System32\WindowsPowerShell\v1.0\Modules\PSWindowsUpdate")) |
3166 | + { |
3167 | + $Host.UI.RawUI.WindowTitle = "Installing PSWindowsUpdate..." |
3168 | + Copy-Item E:\PSWindowsUpdate $ENV:SystemRoot\System32\WindowsPowerShell\v1.0\Modules -recurse |
3169 | + } |
3170 | + |
3171 | + # Start the Update process. |
3172 | + Import-Module PSWindowsUpdate |
3173 | + $Host.UI.RawUI.WindowTitle = "Installing updates..." |
3174 | + Get-WUInstall -AcceptAll -IgnoreReboot -IgnoreUserInput -NotCategory "Language packs" |
3175 | + if (Get-WURebootStatus -Silent) |
3176 | + { |
3177 | + $Host.UI.RawUI.WindowTitle = "Updates installation finished. Rebooting." |
3178 | + shutdown /r /t 0 |
3179 | + } |
3180 | + else |
3181 | + { |
3182 | + $osArch = (Get-WmiObject Win32_OperatingSystem).OSArchitecture |
3183 | + if($osArch.StartsWith("64")) |
3184 | + { |
3185 | + $archDir = "x64" |
3186 | + $programFilesDir = ${ENV:ProgramFiles(x86)} |
3187 | + } |
3188 | + else |
3189 | + { |
3190 | + $archDir = "x86" |
3191 | + $programFilesDir = $ENV:ProgramFiles |
3192 | + } |
3193 | + |
3194 | + # Inject extra drivers if the infs directory is present on the attached iso |
3195 | + if (Test-Path -Path "E:\infs") |
3196 | + { |
3197 | + # To install extra drivers the Windows Driver Kit is needed for dpinst.exe. |
3198 | + # Sadly you cannot just download dpinst.exe. The whole driver kit must be |
3199 | + # installed. |
3200 | + |
3201 | + # Download the WDK installer. |
3202 | + $Host.UI.RawUI.WindowTitle = "Downloading Windows Driver Kit..." |
3203 | + $webclient = New-Object System.Net.WebClient |
3204 | + $wdksetup = [IO.Path]::GetFullPath("$ENV:TEMP\wdksetup.exe") |
3205 | + $wdkurl = "http://download.microsoft.com/download/0/8/C/08C7497F-8551-4054-97DE-60C0E510D97A/wdk/wdksetup.exe" |
3206 | + $webclient.DownloadFile($wdkurl, $wdksetup) |
3207 | + |
3208 | + # Run the installer. |
3209 | + $Host.UI.RawUI.WindowTitle = "Installing Windows Driver Kit..." |
3210 | + $p = Start-Process -PassThru -Wait -FilePath "$wdksetup" -ArgumentList "/features OptionId.WindowsDriverKitComplete /q /ceip off /norestart" |
3211 | + if ($p.ExitCode -ne 0) |
3212 | + { |
3213 | + throw "Installing $wdksetup failed." |
3214 | + } |
3215 | + |
3216 | + # Run dpinst.exe with the path to the drivers. |
3217 | + $Host.UI.RawUI.WindowTitle = "Injecting Windows drivers..." |
3218 | + $dpinst = "$programFilesDir\Windows Kits\8.1\redist\DIFx\dpinst\EngMui\$archDir\dpinst.exe" |
3219 | + Start-Process -Wait -FilePath "$dpinst" -ArgumentList "/S /C /F /SA /Path E:\infs" |
3220 | + |
3221 | + # Uninstall the WDK |
3222 | + $Host.UI.RawUI.WindowTitle = "Uninstalling Windows Driver Kit..." |
3223 | + Start-Process -Wait -FilePath "$wdksetup" -ArgumentList "/features + /q /uninstall /norestart" |
3224 | + } |
3225 | + |
3226 | + $Host.UI.RawUI.WindowTitle = "Installing Cloudbase-Init..." |
3227 | + $cloudbaseInitPath = "E:\cloudbase\cloudbase_init.msi" |
3228 | + $cloudbaseInitLog = "$ENV:Temp\cloudbase_init.log" |
3229 | + $serialPortName = @(Get-WmiObject Win32_SerialPort)[0].DeviceId |
3230 | + $p = Start-Process -Wait -PassThru -FilePath msiexec -ArgumentList "/i $cloudbaseInitPath /qn /l*v $cloudbaseInitLog LOGGINGSERIALPORTNAME=$serialPortName" |
3231 | + if ($p.ExitCode -ne 0) |
3232 | + { |
3233 | + throw "Installing $cloudbaseInitPath failed. Log: $cloudbaseInitLog" |
3234 | + } |
3235 | + |
3236 | + # We're done, remove LogonScript and disable AutoLogon |
3237 | + Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name Unattend* |
3238 | + Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoLogonCount |
3239 | + |
3240 | + $Host.UI.RawUI.WindowTitle = "Running SetSetupComplete..." |
3241 | + & "$ENV:ProgramFiles\Cloudbase Solutions\Cloudbase-Init\bin\SetSetupComplete.cmd" |
3242 | + |
3243 | + # Write success, this is used to check that this process made it this far |
3244 | + New-Item -Path C:\success.tch -Type file -Force |
3245 | + |
3246 | + $Host.UI.RawUI.WindowTitle = "Running Sysprep..." |
3247 | + $unattendedXmlPath = "$ENV:ProgramFiles\Cloudbase Solutions\Cloudbase-Init\conf\Unattend.xml" |
3248 | + & "$ENV:SystemRoot\System32\Sysprep\Sysprep.exe" `/generalize `/oobe `/shutdown `/unattend:"$unattendedXmlPath" |
3249 | + } |
3250 | +} |
3251 | +catch |
3252 | +{ |
3253 | + $_ | Out-File C:\error_log.txt |
3254 | + shutdown /s /f /t 0 |
3255 | +} |
3256 | |
3257 | === modified file 'debian/changelog' |
3258 | --- debian/changelog 2015-07-31 18:56:35 +0000 |
3259 | +++ debian/changelog 2017-06-13 16:13:40 +0000 |
3260 | @@ -1,3 +1,52 @@ |
3261 | +maas-image-builder (1.0.5+bzr54-0ubuntu1) UNRELEASED; urgency=low |
3262 | + |
3263 | + * Fix --windows-drivers to not fail during logon and firstlogon. |
3264 | + * Fix kpartx_del to handle failures and retry up to 10 seconds. |
3265 | + * Prevent windows qemu VNC from listening on all addresses. |
3266 | + |
3267 | + -- Blake Rouse <blake.rouse@canonical.com> Fri, 9 Jun 2017 10:51:15 -0400 |
3268 | + |
3269 | +maas-image-builder (1.0.4+bzr48-0ubuntu1) UNRELEASED; urgency=low |
3270 | + |
3271 | + * Add --cloudbase-init option to Windows driver. |
3272 | + * Support appending custom kickstarter option to CentOS and RHEL drivers. |
3273 | + * RHEL driver use CentOS hook files. |
3274 | + |
3275 | + -- Blake Rouse <blake.rouse@canonical.com> Wed, 31 May 2017 13:50:18 -0400 |
3276 | + |
3277 | +maas-image-builder (1.0.3+bzr44-0ubuntu1) UNRELEASED; urgency=low |
3278 | + |
3279 | + * Fix Cent OS 7 UEFI to remove old EFI entries and place current boot |
3280 | + in-front of centos EFI loader. |
3281 | + |
3282 | + -- Blake Rouse <blake.rouse@canonical.com> Fri, 3 May 2017 20:08:32 -0400 |
3283 | + |
3284 | +maas-image-builder (1.0.2+bzr42-0ubuntu1) UNRELEASED; urgency=low |
3285 | + |
3286 | + * Fix Windows image builder to work with python3. |
3287 | + |
3288 | + -- Blake Rouse <blake.rouse@canonical.com> Fri, 21 Apr 2017 11:42:32 -0400 |
3289 | + |
3290 | +maas-image-builder (1.0.1+bzr40-0ubuntu1) UNRELEASED; urgency=low |
3291 | + |
3292 | + * Fix UEFI installation with CentOS 7 |
3293 | + |
3294 | + -- Blake Rouse <blake.rouse@canonical.com> Fri, 21 Apr 2017 09:14:08 -0400 |
3295 | + |
3296 | +maas-image-builder (1.0.0+bzr36-0ubuntu1) UNRELEASED; urgency=low |
3297 | + |
3298 | + * Fix virt-install to work on systems with apparmor. |
3299 | + * Change from python2.7 to python3. |
3300 | + |
3301 | + -- Blake Rouse <blake.rouse@canonical.com> Wed, 19 Apr 2017 16:54:04 -0400 |
3302 | + |
3303 | +maas-image-builder (0.9.0+bzr27-0ubuntu1) UNRELEASED; urgency=low |
3304 | + |
3305 | + * Merge Windows and RHEL into the same code base. Make all of |
3306 | + maas-image-builder private and update the copyright.. |
3307 | + |
3308 | + -- Blake Rouse <blake.rouse@canonical.com> Wed, 6 Jul 2016 11:14:18 -0400 |
3309 | + |
3310 | maas-image-builder (0.9.0+bzr24-0ubuntu1) UNRELEASED; urgency=low |
3311 | |
3312 | * Fix CentOS 6 & 7 to not use udev rules. |
3313 | |
3314 | === modified file 'debian/control' |
3315 | --- debian/control 2015-03-11 19:32:46 +0000 |
3316 | +++ debian/control 2017-06-13 16:13:40 +0000 |
3317 | @@ -5,39 +5,46 @@ |
3318 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
3319 | Build-Depends: debhelper (>= 7), |
3320 | dh-python, |
3321 | - python (>= 2.7), |
3322 | - python-setuptools |
3323 | + python3, |
3324 | + python3-setuptools |
3325 | Homepage: http://launchpad.net/maas-image-builder |
3326 | -X-Python-Version: >= 2.7 |
3327 | +X-Python3-Version: >= 3.5 |
3328 | |
3329 | Package: maas-image-builder |
3330 | Architecture: all |
3331 | -Depends: python, |
3332 | - python-mib (= ${binary:Version}), |
3333 | +Depends: python3, |
3334 | + python3-mib (= ${binary:Version}), |
3335 | ${misc:Depends}, |
3336 | - ${python:Depends} |
3337 | + ${python3:Depends} |
3338 | Description: Library and tools for the MAAS Image Builder |
3339 | This package provides the MAAS Image Builder. |
3340 | |
3341 | Package: mib-common |
3342 | Architecture: all |
3343 | -Depends: util-linux (>= 2.20.1-1ubuntu3), ${misc:Depends}, ${python:Depends} |
3344 | +Depends: util-linux (>= 2.20.1-1ubuntu3), ${misc:Depends}, ${python3:Depends} |
3345 | Description: Library and tools for the MAAS Image Builder |
3346 | This package provides the MAAS Image Builder. |
3347 | |
3348 | -Package: python-mib |
3349 | +Package: python3-mib |
3350 | Section: python |
3351 | Architecture: all |
3352 | -Depends: genisoimage, |
3353 | +Depends: dos2unix, |
3354 | + dosfstools, |
3355 | + genisoimage, |
3356 | kpartx, |
3357 | kvm, |
3358 | libvirt-bin, |
3359 | mib-common (= ${binary:Version}), |
3360 | - python-stevedore, |
3361 | + ntfs-3g, |
3362 | + python3-stevedore, |
3363 | + python3-tempita, |
3364 | + qemu-kvm-spice, |
3365 | qemu-utils, |
3366 | + unzip, |
3367 | util-linux (>= 2.20.1-1ubuntu3), |
3368 | virtinst, |
3369 | + wget, |
3370 | ${misc:Depends}, |
3371 | - ${python:Depends} |
3372 | + ${python3:Depends} |
3373 | Description: Library and tools for the MAAS Image Builder |
3374 | This package provides the MAAS Image Builder. |
3375 | |
3376 | === renamed file 'debian/python-mib.install' => 'debian/python3-mib.install' |
3377 | --- debian/python-mib.install 2015-03-11 19:32:46 +0000 |
3378 | +++ debian/python3-mib.install 2017-06-13 16:13:40 +0000 |
3379 | @@ -1,1 +1,1 @@ |
3380 | -usr/lib/python2*/dist-packages/* |
3381 | +usr/lib/python3*/dist-packages/* |
3382 | |
3383 | === modified file 'debian/rules' |
3384 | --- debian/rules 2015-03-14 19:32:31 +0000 |
3385 | +++ debian/rules 2017-06-13 16:13:40 +0000 |
3386 | @@ -1,16 +1,16 @@ |
3387 | #!/usr/bin/make -f |
3388 | |
3389 | -PYTHON = $(shell pyversions -d) |
3390 | +PYTHON3 = $(shell py3versions -d) |
3391 | |
3392 | %: |
3393 | - dh $@ --with=python2 |
3394 | + dh $@ --with=python3 |
3395 | |
3396 | override_dh_auto_build: |
3397 | @echo "Auto build does nothing." |
3398 | |
3399 | override_dh_auto_install: |
3400 | - $(PYTHON) setup.py build --executable=/usr/bin/python |
3401 | - $(PYTHON) setup.py install --root=$(CURDIR)/debian/tmp --install-layout=deb |
3402 | + $(PYTHON3) setup.py build --executable=/usr/bin/python3 |
3403 | + $(PYTHON3) setup.py install --root=$(CURDIR)/debian/tmp --install-layout=deb |
3404 | |
3405 | dh_install --list-missing |
3406 | |
3407 | |
3408 | === added file 'pylintrc' |
3409 | --- pylintrc 1970-01-01 00:00:00 +0000 |
3410 | +++ pylintrc 2017-06-13 16:13:40 +0000 |
3411 | @@ -0,0 +1,425 @@ |
3412 | +[MASTER] |
3413 | + |
3414 | +# A comma-separated list of package or module names from where C extensions may |
3415 | +# be loaded. Extensions are loading into the active Python interpreter and may |
3416 | +# run arbitrary code |
3417 | +extension-pkg-whitelist= |
3418 | + |
3419 | +# Add files or directories to the blacklist. They should be base names, not |
3420 | +# paths. |
3421 | +ignore=CVS |
3422 | + |
3423 | +# Add files or directories matching the regex patterns to the blacklist. The |
3424 | +# regex matches against base names, not paths. |
3425 | +ignore-patterns= |
3426 | + |
3427 | +# Python code to execute, usually for sys.path manipulation such as |
3428 | +# pygtk.require(). |
3429 | +#init-hook= |
3430 | + |
3431 | +# Use multiple processes to speed up Pylint. |
3432 | +jobs=1 |
3433 | + |
3434 | +# List of plugins (as comma separated values of python modules names) to load, |
3435 | +# usually to register additional checkers. |
3436 | +load-plugins= |
3437 | + |
3438 | +# Pickle collected data for later comparisons. |
3439 | +persistent=yes |
3440 | + |
3441 | +# Specify a configuration file. |
3442 | +#rcfile= |
3443 | + |
3444 | +# Allow loading of arbitrary C extensions. Extensions are imported into the |
3445 | +# active Python interpreter and may run arbitrary code. |
3446 | +unsafe-load-any-extension=no |
3447 | + |
3448 | + |
3449 | +[MESSAGES CONTROL] |
3450 | + |
3451 | +# Only show warnings with the listed confidence levels. Leave empty to show |
3452 | +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED |
3453 | +confidence= |
3454 | + |
3455 | +# Disable the message, report, category or checker with the given id(s). You |
3456 | +# can either give multiple identifiers separated by comma (,) or put this |
3457 | +# option multiple times (only on the command line, not in the configuration |
3458 | +# file where it should appear only once).You can also use "--disable=all" to |
3459 | +# disable everything first and then reenable specific checks. For example, if |
3460 | +# you want to run only the similarities checker, you can use "--disable=all |
3461 | +# --enable=similarities". If you want to run only the classes checker, but have |
3462 | +# no Warning level messages displayed, use"--disable=all --enable=classes |
3463 | +# --disable=W" |
3464 | +disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,similarities |
3465 | + |
3466 | +# Enable the message, report, category or checker with the given id(s). You can |
3467 | +# either give multiple identifier separated by comma (,) or put this option |
3468 | +# multiple time (only on the command line, not in the configuration file where |
3469 | +# it should appear only once). See also the "--disable" option for examples. |
3470 | +enable= |
3471 | + |
3472 | + |
3473 | +[REPORTS] |
3474 | + |
3475 | +# Python expression which should return a note less than 10 (10 is the highest |
3476 | +# note). You have access to the variables errors warning, statement which |
3477 | +# respectively contain the number of errors / warnings messages and the total |
3478 | +# number of statements analyzed. This is used by the global evaluation report |
3479 | +# (RP0004). |
3480 | +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) |
3481 | + |
3482 | +# Template used to display messages. This is a python new-style format string |
3483 | +# used to format the message information. See doc for all details |
3484 | +#msg-template= |
3485 | + |
3486 | +# Set the output format. Available formats are text, parseable, colorized, json |
3487 | +# and msvs (visual studio).You can also give a reporter class, eg |
3488 | +# mypackage.mymodule.MyReporterClass. |
3489 | +output-format=text |
3490 | + |
3491 | +# Tells whether to display a full report or only the messages |
3492 | +reports=no |
3493 | + |
3494 | +# Activate the evaluation score. |
3495 | +score=yes |
3496 | + |
3497 | + |
3498 | +[REFACTORING] |
3499 | + |
3500 | +# Maximum number of nested blocks for function / method body |
3501 | +max-nested-blocks=5 |
3502 | + |
3503 | + |
3504 | +[TYPECHECK] |
3505 | + |
3506 | +# List of decorators that produce context managers, such as |
3507 | +# contextlib.contextmanager. Add to this list to register other decorators that |
3508 | +# produce valid context managers. |
3509 | +contextmanager-decorators=contextlib.contextmanager |
3510 | + |
3511 | +# List of members which are set dynamically and missed by pylint inference |
3512 | +# system, and so shouldn't trigger E1101 when accessed. Python regular |
3513 | +# expressions are accepted. |
3514 | +generated-members= |
3515 | + |
3516 | +# Tells whether missing members accessed in mixin class should be ignored. A |
3517 | +# mixin class is detected if its name ends with "mixin" (case insensitive). |
3518 | +ignore-mixin-members=yes |
3519 | + |
3520 | +# This flag controls whether pylint should warn about no-member and similar |
3521 | +# checks whenever an opaque object is returned when inferring. The inference |
3522 | +# can return multiple potential results while evaluating a Python object, but |
3523 | +# some branches might not be evaluated, which results in partial inference. In |
3524 | +# that case, it might be useful to still emit no-member and other checks for |
3525 | +# the rest of the inferred objects. |
3526 | +ignore-on-opaque-inference=yes |
3527 | + |
3528 | +# List of class names for which member attributes should not be checked (useful |
3529 | +# for classes with dynamically set attributes). This supports the use of |
3530 | +# qualified names. |
3531 | +ignored-classes=optparse.Values,thread._local,_thread._local |
3532 | + |
3533 | +# List of module names for which member attributes should not be checked |
3534 | +# (useful for modules/projects where namespaces are manipulated during runtime |
3535 | +# and thus existing member attributes cannot be deduced by static analysis. It |
3536 | +# supports qualified module names, as well as Unix pattern matching. |
3537 | +ignored-modules= |
3538 | + |
3539 | +# Show a hint with possible names when a member name was not found. The aspect |
3540 | +# of finding the hint is based on edit distance. |
3541 | +missing-member-hint=yes |
3542 | + |
3543 | +# The minimum edit distance a name should have in order to be considered a |
3544 | +# similar match for a missing member name. |
3545 | +missing-member-hint-distance=1 |
3546 | + |
3547 | +# The total number of similar names that should be taken in consideration when |
3548 | +# showing a hint for a missing member. |
3549 | +missing-member-max-choices=1 |
3550 | + |
3551 | + |
3552 | +[VARIABLES] |
3553 | + |
3554 | +# List of additional names supposed to be defined in builtins. Remember that |
3555 | +# you should avoid to define new builtins when possible. |
3556 | +additional-builtins= |
3557 | + |
3558 | +# Tells whether unused global variables should be treated as a violation. |
3559 | +allow-global-unused-variables=yes |
3560 | + |
3561 | +# List of strings which can identify a callback function by name. A callback |
3562 | +# name must start or end with one of those strings. |
3563 | +callbacks=cb_,_cb |
3564 | + |
3565 | +# A regular expression matching the name of dummy variables (i.e. expectedly |
3566 | +# not used). |
3567 | +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ |
3568 | + |
3569 | +# Argument names that match this expression will be ignored. Default to name |
3570 | +# with leading underscore |
3571 | +ignored-argument-names=_.*|^ignored_|^unused_ |
3572 | + |
3573 | +# Tells whether we should check for unused import in __init__ files. |
3574 | +init-import=no |
3575 | + |
3576 | +# List of qualified module names which can have objects that can redefine |
3577 | +# builtins. |
3578 | +redefining-builtins-modules=six.moves,future.builtins |
3579 | + |
3580 | + |
3581 | +[BASIC] |
3582 | + |
3583 | +# Naming hint for argument names |
3584 | +argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ |
3585 | + |
3586 | +# Regular expression matching correct argument names |
3587 | +argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ |
3588 | + |
3589 | +# Naming hint for attribute names |
3590 | +attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ |
3591 | + |
3592 | +# Regular expression matching correct attribute names |
3593 | +attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ |
3594 | + |
3595 | +# Bad variable names which should always be refused, separated by a comma |
3596 | +bad-names=foo,bar,baz,toto,tutu,tata |
3597 | + |
3598 | +# Naming hint for class attribute names |
3599 | +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ |
3600 | + |
3601 | +# Regular expression matching correct class attribute names |
3602 | +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ |
3603 | + |
3604 | +# Naming hint for class names |
3605 | +class-name-hint=[A-Z_][a-zA-Z0-9]+$ |
3606 | + |
3607 | +# Regular expression matching correct class names |
3608 | +class-rgx=[A-Z_][a-zA-Z0-9]+$ |
3609 | + |
3610 | +# Naming hint for constant names |
3611 | +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ |
3612 | + |
3613 | +# Regular expression matching correct constant names |
3614 | +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ |
3615 | + |
3616 | +# Minimum line length for functions/classes that require docstrings, shorter |
3617 | +# ones are exempt. |
3618 | +docstring-min-length=-1 |
3619 | + |
3620 | +# Naming hint for function names |
3621 | +function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ |
3622 | + |
3623 | +# Regular expression matching correct function names |
3624 | +function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ |
3625 | + |
3626 | +# Good variable names which should always be accepted, separated by a comma |
3627 | +good-names=i,j,k,ex,Run,_ |
3628 | + |
3629 | +# Include a hint for the correct naming format with invalid-name |
3630 | +include-naming-hint=no |
3631 | + |
3632 | +# Naming hint for inline iteration names |
3633 | +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ |
3634 | + |
3635 | +# Regular expression matching correct inline iteration names |
3636 | +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ |
3637 | + |
3638 | +# Naming hint for method names |
3639 | +method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ |
3640 | + |
3641 | +# Regular expression matching correct method names |
3642 | +method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ |
3643 | + |
3644 | +# Naming hint for module names |
3645 | +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ |
3646 | + |
3647 | +# Regular expression matching correct module names |
3648 | +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ |
3649 | + |
3650 | +# Colon-delimited sets of names that determine each other's naming style when |
3651 | +# the name regexes allow several styles. |
3652 | +name-group= |
3653 | + |
3654 | +# Regular expression which should only match function or class names that do |
3655 | +# not require a docstring. |
3656 | +no-docstring-rgx=^_ |
3657 | + |
3658 | +# List of decorators that produce properties, such as abc.abstractproperty. Add |
3659 | +# to this list to register other decorators that produce valid properties. |
3660 | +property-classes=abc.abstractproperty |
3661 | + |
3662 | +# Naming hint for variable names |
3663 | +variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ |
3664 | + |
3665 | +# Regular expression matching correct variable names |
3666 | +variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ |
3667 | + |
3668 | + |
3669 | +[FORMAT] |
3670 | + |
3671 | +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. |
3672 | +expected-line-ending-format= |
3673 | + |
3674 | +# Regexp for a line that is allowed to be longer than the limit. |
3675 | +ignore-long-lines=^\s*(# )?<?https?://\S+>?$ |
3676 | + |
3677 | +# Number of spaces of indent required inside a hanging or continued line. |
3678 | +indent-after-paren=4 |
3679 | + |
3680 | +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 |
3681 | +# tab). |
3682 | +indent-string=' ' |
3683 | + |
3684 | +# Maximum number of characters on a single line. |
3685 | +max-line-length=100 |
3686 | + |
3687 | +# Maximum number of lines in a module |
3688 | +max-module-lines=1000 |
3689 | + |
3690 | +# List of optional constructs for which whitespace checking is disabled. `dict- |
3691 | +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. |
3692 | +# `trailing-comma` allows a space between comma and closing bracket: (a, ). |
3693 | +# `empty-line` allows space-only lines. |
3694 | +no-space-check=trailing-comma,dict-separator |
3695 | + |
3696 | +# Allow the body of a class to be on the same line as the declaration if body |
3697 | +# contains single statement. |
3698 | +single-line-class-stmt=no |
3699 | + |
3700 | +# Allow the body of an if to be on the same line as the test if there is no |
3701 | +# else. |
3702 | +single-line-if-stmt=no |
3703 | + |
3704 | + |
3705 | +[SIMILARITIES] |
3706 | + |
3707 | +# Ignore comments when computing similarities. |
3708 | +ignore-comments=yes |
3709 | + |
3710 | +# Ignore docstrings when computing similarities. |
3711 | +ignore-docstrings=yes |
3712 | + |
3713 | +# Ignore imports when computing similarities. |
3714 | +ignore-imports=no |
3715 | + |
3716 | +# Minimum lines number of a similarity. |
3717 | +min-similarity-lines=4 |
3718 | + |
3719 | + |
3720 | +[SPELLING] |
3721 | + |
3722 | +# Spelling dictionary name. Available dictionaries: none. To make it working |
3723 | +# install python-enchant package. |
3724 | +spelling-dict= |
3725 | + |
3726 | +# List of comma separated words that should not be checked. |
3727 | +spelling-ignore-words= |
3728 | + |
3729 | +# A path to a file that contains private dictionary; one word per line. |
3730 | +spelling-private-dict-file= |
3731 | + |
3732 | +# Tells whether to store unknown words to indicated private dictionary in |
3733 | +# --spelling-private-dict-file option instead of raising a message. |
3734 | +spelling-store-unknown-words=no |
3735 | + |
3736 | + |
3737 | +[MISCELLANEOUS] |
3738 | + |
3739 | +# List of note tags to take in consideration, separated by a comma. |
3740 | +notes=FIXME,XXX,TODO |
3741 | + |
3742 | + |
3743 | +[LOGGING] |
3744 | + |
3745 | +# Logging modules to check that the string format arguments are in logging |
3746 | +# function parameter format |
3747 | +logging-modules=logging |
3748 | + |
3749 | + |
3750 | +[IMPORTS] |
3751 | + |
3752 | +# Allow wildcard imports from modules that define __all__. |
3753 | +allow-wildcard-with-all=no |
3754 | + |
3755 | +# Analyse import fallback blocks. This can be used to support both Python 2 and |
3756 | +# 3 compatible code, which means that the block might have code that exists |
3757 | +# only in one or another interpreter, leading to false positives when analysed. |
3758 | +analyse-fallback-blocks=no |
3759 | + |
3760 | +# Deprecated modules which should not be used, separated by a comma |
3761 | +deprecated-modules=optparse,tkinter.tix |
3762 | + |
3763 | +# Create a graph of external dependencies in the given file (report RP0402 must |
3764 | +# not be disabled) |
3765 | +ext-import-graph= |
3766 | + |
3767 | +# Create a graph of every (i.e. internal and external) dependencies in the |
3768 | +# given file (report RP0402 must not be disabled) |
3769 | +import-graph= |
3770 | + |
3771 | +# Create a graph of internal dependencies in the given file (report RP0402 must |
3772 | +# not be disabled) |
3773 | +int-import-graph= |
3774 | + |
3775 | +# Force import order to recognize a module as part of the standard |
3776 | +# compatibility libraries. |
3777 | +known-standard-library= |
3778 | + |
3779 | +# Force import order to recognize a module as part of a third party library. |
3780 | +known-third-party=enchant |
3781 | + |
3782 | + |
3783 | +[CLASSES] |
3784 | + |
3785 | +# List of method names used to declare (i.e. assign) instance attributes. |
3786 | +defining-attr-methods=__init__,__new__,setUp |
3787 | + |
3788 | +# List of member names, which should be excluded from the protected access |
3789 | +# warning. |
3790 | +exclude-protected=_asdict,_fields,_replace,_source,_make |
3791 | + |
3792 | +# List of valid names for the first argument in a class method. |
3793 | +valid-classmethod-first-arg=cls |
3794 | + |
3795 | +# List of valid names for the first argument in a metaclass class method. |
3796 | +valid-metaclass-classmethod-first-arg=mcs |
3797 | + |
3798 | + |
3799 | +[DESIGN] |
3800 | + |
3801 | +# Maximum number of arguments for function / method |
3802 | +max-args=14 |
3803 | + |
3804 | +# Maximum number of attributes for a class (see R0902). |
3805 | +max-attributes=7 |
3806 | + |
3807 | +# Maximum number of boolean expressions in a if statement |
3808 | +max-bool-expr=5 |
3809 | + |
3810 | +# Maximum number of branch for function / method body |
3811 | +max-branches=12 |
3812 | + |
3813 | +# Maximum number of locals for function / method body |
3814 | +max-locals=15 |
3815 | + |
3816 | +# Maximum number of parents for a class (see R0901). |
3817 | +max-parents=7 |
3818 | + |
3819 | +# Maximum number of public methods for a class (see R0904). |
3820 | +max-public-methods=25 |
3821 | + |
3822 | +# Maximum number of return / yield for function / method body |
3823 | +max-returns=6 |
3824 | + |
3825 | +# Maximum number of statements in function / method body |
3826 | +max-statements=50 |
3827 | + |
3828 | +# Minimum number of public methods for a class (see R0903). |
3829 | +min-public-methods=2 |
3830 | + |
3831 | + |
3832 | +[EXCEPTIONS] |
3833 | + |
3834 | +# Exceptions that will emit a warning when being caught. Defaults to |
3835 | +# "Exception" |
3836 | +overgeneral-exceptions=Exception |
3837 | |
3838 | === modified file 'required-packages/base' |
3839 | --- required-packages/base 2015-03-11 15:52:11 +0000 |
3840 | +++ required-packages/base 2017-06-13 16:13:40 +0000 |
3841 | @@ -1,7 +1,12 @@ |
3842 | +dos2unix |
3843 | +dosfstools |
3844 | genisoimage |
3845 | kpartx |
3846 | kvm |
3847 | libvirt-bin |
3848 | -python-stevedore |
3849 | +ntfs-3g |
3850 | +qemu-kvm-spice |
3851 | qemu-utils |
3852 | +unzip |
3853 | virtinst |
3854 | +wget |
3855 | |
3856 | === modified file 'required-packages/dev' |
3857 | --- required-packages/dev 2015-03-10 20:42:06 +0000 |
3858 | +++ required-packages/dev 2017-06-13 16:13:40 +0000 |
3859 | @@ -1,18 +1,3 @@ |
3860 | build-essential |
3861 | +tox |
3862 | make |
3863 | -pep8 |
3864 | -pyflakes |
3865 | -python-coverage |
3866 | -python-extras |
3867 | -python-fixtures |
3868 | -python-flake8 |
3869 | -python-mock |
3870 | -python-nose |
3871 | -python-pip |
3872 | -python-pocket-lint |
3873 | -python-subunit |
3874 | -python-testresources |
3875 | -python-testscenarios |
3876 | -python-testtools |
3877 | -python-unittest2 |
3878 | -python-virtualenv |
3879 | |
3880 | === added file 'requirements.txt' |
3881 | --- requirements.txt 1970-01-01 00:00:00 +0000 |
3882 | +++ requirements.txt 2017-06-13 16:13:40 +0000 |
3883 | @@ -0,0 +1,2 @@ |
3884 | +stevedore |
3885 | +tempita |
3886 | |
3887 | === modified file 'scripts/maas-image-builder' |
3888 | --- scripts/maas-image-builder 2015-03-11 13:40:58 +0000 |
3889 | +++ scripts/maas-image-builder 2017-06-13 16:13:40 +0000 |
3890 | @@ -1,6 +1,45 @@ |
3891 | -#!/usr/bin/env python2.7 |
3892 | -# Copyright 2014-2015 Canonical Ltd. This software is licensed under the |
3893 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
3894 | +#!/usr/bin/env python3 |
3895 | +# vi: ts=4 expandtab |
3896 | +# Upstream Author: |
3897 | +# |
3898 | +# Canonical Ltd. |
3899 | +# |
3900 | +# Copyright: |
3901 | +# |
3902 | +# (c) 2014-2016 Canonical Ltd. |
3903 | +# |
3904 | +# Licence: |
3905 | +# |
3906 | +# If you have an executed agreement with a Canonical group company which |
3907 | +# includes a licence to this software, your use of this software is governed |
3908 | +# by that agreement. Otherwise, the following applies: |
3909 | +# |
3910 | +# Canonical Ltd. hereby grants to you a world-wide, non-exclusive, |
3911 | +# non-transferable, revocable, perpetual (unless revoked) licence, to (i) use |
3912 | +# this software in connection with Canonical's MAAS software to install Windows |
3913 | +# in non-production environments and (ii) to make a reasonable number of copies |
3914 | +# of this software for backup and installation purposes. You may not: use, |
3915 | +# copy, modify, disassemble, decompile, reverse engineer, or distribute the |
3916 | +# software except as expressly permitted in this licence; permit access to the |
3917 | +# software to any third party other than those acting on your behalf; or use |
3918 | +# this software in connection with a production environment. |
3919 | +# |
3920 | +# CANONICAL LTD. MAKES THIS SOFTWARE AVAILABLE "AS-IS". CANONICAL LTD. MAKES |
3921 | +# NO REPRESENTATIONS OR WARRANTIES OF ANY KIND, WHETHER ORAL OR WRITTEN, |
3922 | +# WHETHER EXPRESS, IMPLIED, OR ARISING BY STATUTE, CUSTOM, COURSE OF DEALING |
3923 | +# OR TRADE USAGE, WITH RESPECT TO THIS SOFTWARE. CANONICAL LTD. SPECIFICALLY |
3924 | +# DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OR CONDITIONS OF TITLE, SATISFACTORY |
3925 | +# QUALITY, MERCHANTABILITY, SATISFACTORINESS, FITNESS FOR A PARTICULAR PURPOSE |
3926 | +# AND NON-INFRINGEMENT. |
3927 | +# |
3928 | +# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL |
3929 | +# CANONICAL LTD. OR ANY OF ITS AFFILIATES, BE LIABLE TO YOU FOR DAMAGES, |
3930 | +# INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
3931 | +# OUT OF THE USE OR INABILITY TO USE THIS SOFTWARE (INCLUDING BUT NOT LIMITED |
3932 | +# TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU |
3933 | +# OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
3934 | +# PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
3935 | +# POSSIBILITY OF SUCH DAMAGES. |
3936 | |
3937 | from __future__ import ( |
3938 | absolute_import, |
3939 | |
3940 | === modified file 'setup.py' |
3941 | --- setup.py 2015-07-31 18:46:41 +0000 |
3942 | +++ setup.py 2017-06-13 16:13:40 +0000 |
3943 | @@ -1,17 +1,47 @@ |
3944 | -#!/usr/bin/env python2.7 |
3945 | -# Copyright 2015 Canonical Ltd. This software is licensed under the |
3946 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
3947 | +#!/usr/bin/env python3 |
3948 | +# Upstream Author: |
3949 | +# |
3950 | +# Canonical Ltd. |
3951 | +# |
3952 | +# Copyright: |
3953 | +# |
3954 | +# (c) 2014-2017 Canonical Ltd. |
3955 | +# |
3956 | +# Licence: |
3957 | +# |
3958 | +# If you have an executed agreement with a Canonical group company which |
3959 | +# includes a licence to this software, your use of this software is governed |
3960 | +# by that agreement. Otherwise, the following applies: |
3961 | +# |
3962 | +# Canonical Ltd. hereby grants to you a world-wide, non-exclusive, |
3963 | +# non-transferable, revocable, perpetual (unless revoked) licence, to (i) use |
3964 | +# this software in connection with Canonical's MAAS software to install Windows |
3965 | +# in non-production environments and (ii) to make a reasonable number of copies |
3966 | +# of this software for backup and installation purposes. You may not: use, |
3967 | +# copy, modify, disassemble, decompile, reverse engineer, or distribute the |
3968 | +# software except as expressly permitted in this licence; permit access to the |
3969 | +# software to any third party other than those acting on your behalf; or use |
3970 | +# this software in connection with a production environment. |
3971 | +# |
3972 | +# CANONICAL LTD. MAKES THIS SOFTWARE AVAILABLE "AS-IS". CANONICAL LTD. MAKES |
3973 | +# NO REPRESENTATIONS OR WARRANTIES OF ANY KIND, WHETHER ORAL OR WRITTEN, |
3974 | +# WHETHER EXPRESS, IMPLIED, OR ARISING BY STATUTE, CUSTOM, COURSE OF DEALING |
3975 | +# OR TRADE USAGE, WITH RESPECT TO THIS SOFTWARE. CANONICAL LTD. SPECIFICALLY |
3976 | +# DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OR CONDITIONS OF TITLE, SATISFACTORY |
3977 | +# QUALITY, MERCHANTABILITY, SATISFACTORINESS, FITNESS FOR A PARTICULAR PURPOSE |
3978 | +# AND NON-INFRINGEMENT. |
3979 | +# |
3980 | +# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL |
3981 | +# CANONICAL LTD. OR ANY OF ITS AFFILIATES, BE LIABLE TO YOU FOR DAMAGES, |
3982 | +# INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
3983 | +# OUT OF THE USE OR INABILITY TO USE THIS SOFTWARE (INCLUDING BUT NOT LIMITED |
3984 | +# TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU |
3985 | +# OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
3986 | +# PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
3987 | +# POSSIBILITY OF SUCH DAMAGES. |
3988 | |
3989 | """Distribute/Setuptools installer for MAAS Image Builder.""" |
3990 | |
3991 | -from __future__ import ( |
3992 | - absolute_import, |
3993 | - print_function, |
3994 | - unicode_literals, |
3995 | - ) |
3996 | - |
3997 | -str = None |
3998 | - |
3999 | from glob import glob |
4000 | from os.path import ( |
4001 | dirname, |
4002 | @@ -35,32 +65,37 @@ |
4003 | return fin.read().strip() |
4004 | |
4005 | |
4006 | -__version__ = "0.9" |
4007 | +__version__ = "1.0.2" |
4008 | |
4009 | setup( |
4010 | name="maas-image-builder", |
4011 | version=__version__, |
4012 | url="https://launchpad.net/maas-image-builder", |
4013 | - license="AGPLv3", |
4014 | + license="Proprietary", |
4015 | description="MAAS Image Builder", |
4016 | - long_description=read('README'), |
4017 | + long_description="", |
4018 | |
4019 | author="Blake Rouse", |
4020 | author_email="blake.rouse@canonical.com", |
4021 | |
4022 | packages=find_packages( |
4023 | - where=b'src', |
4024 | + where='src', |
4025 | exclude=[ |
4026 | - b"*.testing", |
4027 | - b"*.tests", |
4028 | + "*.testing", |
4029 | + "*.tests", |
4030 | ], |
4031 | ), |
4032 | - package_dir={'': b'src'}, |
4033 | + package_dir={'': 'src'}, |
4034 | include_package_data=True, |
4035 | |
4036 | entry_points={ |
4037 | + 'console_scripts': [ |
4038 | + 'maas-image-builder = mib.core:execute', |
4039 | + ], |
4040 | 'mib.builder': [ |
4041 | 'centos = mib.builders.centos:CentOSBuilder', |
4042 | + 'rhel = mib.builders.rhel:RHELBuilder', |
4043 | + 'windows = mib.builders.windows:WindowsOSBuilder', |
4044 | ], |
4045 | }, |
4046 | |
4047 | @@ -75,29 +110,23 @@ |
4048 | [f for f in glob('contrib/centos/centos7/*') if isfile(f)]), |
4049 | ('/usr/lib/maas-image-builder/contrib/centos/centos7/curtin', |
4050 | [f for f in glob('contrib/centos/centos7/curtin/*') if isfile(f)]), |
4051 | + ('/usr/lib/maas-image-builder/contrib/rhel', |
4052 | + [f for f in glob('contrib/rhel/*') if isfile(f)]), |
4053 | + ('/usr/lib/maas-image-builder/contrib/rhel/curtin', |
4054 | + [f for f in glob('contrib/rhel/curtin/*') if isfile(f)]), |
4055 | + ('/usr/lib/maas-image-builder/contrib/windows', |
4056 | + [f for f in glob('contrib/windows/*') if isfile(f)]), |
4057 | + ('/usr/lib/maas-image-builder/contrib/windows/curtin', |
4058 | + [f for f in glob('contrib/windows/curtin/*') if isfile(f)]), |
4059 | + ('/usr/lib/maas-image-builder/contrib/windows/scripts', |
4060 | + [f for f in glob('contrib/windows/scripts/*') if isfile(f)]), |
4061 | ], |
4062 | |
4063 | - install_requires=[ |
4064 | - 'python-stevedore' |
4065 | - ], |
4066 | classifiers=[ |
4067 | 'Development Status :: 4 - Beta', |
4068 | 'Intended Audience :: Developers', |
4069 | "Intended Audience :: System Administrators", |
4070 | - 'License :: OSI Approved :: GPL License', |
4071 | 'Operating System :: OS Independent', |
4072 | 'Programming Language :: Python', |
4073 | - ], |
4074 | - extras_require=dict( |
4075 | - tests=[ |
4076 | - 'coverage', |
4077 | - 'fixtures', |
4078 | - 'mock', |
4079 | - 'nose', |
4080 | - 'python-subunit', |
4081 | - 'testresources', |
4082 | - 'testscenarios', |
4083 | - 'testtools', |
4084 | - ], |
4085 | - ) |
4086 | + ], |
4087 | ) |
4088 | |
4089 | === modified file 'src/mib/__init__.py' |
4090 | --- src/mib/__init__.py 2015-03-11 13:40:58 +0000 |
4091 | +++ src/mib/__init__.py 2017-06-13 16:13:40 +0000 |
4092 | @@ -1,8 +1,41 @@ |
4093 | -# Copyright 2014-2015 Canonical Ltd. This software is licensed under the |
4094 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
4095 | - |
4096 | -from __future__ import ( |
4097 | - absolute_import, |
4098 | - print_function, |
4099 | - unicode_literals, |
4100 | - ) |
4101 | +# vi: ts=4 expandtab |
4102 | +# Upstream Author: |
4103 | +# |
4104 | +# Canonical Ltd. |
4105 | +# |
4106 | +# Copyright: |
4107 | +# |
4108 | +# (c) 2014-2017 Canonical Ltd. |
4109 | +# |
4110 | +# Licence: |
4111 | +# |
4112 | +# If you have an executed agreement with a Canonical group company which |
4113 | +# includes a licence to this software, your use of this software is governed |
4114 | +# by that agreement. Otherwise, the following applies: |
4115 | +# |
4116 | +# Canonical Ltd. hereby grants to you a world-wide, non-exclusive, |
4117 | +# non-transferable, revocable, perpetual (unless revoked) licence, to (i) use |
4118 | +# this software in connection with Canonical's MAAS software to install Windows |
4119 | +# in non-production environments and (ii) to make a reasonable number of copies |
4120 | +# of this software for backup and installation purposes. You may not: use, |
4121 | +# copy, modify, disassemble, decompile, reverse engineer, or distribute the |
4122 | +# software except as expressly permitted in this licence; permit access to the |
4123 | +# software to any third party other than those acting on your behalf; or use |
4124 | +# this software in connection with a production environment. |
4125 | +# |
4126 | +# CANONICAL LTD. MAKES THIS SOFTWARE AVAILABLE "AS-IS". CANONICAL LTD. MAKES |
4127 | +# NO REPRESENTATIONS OR WARRANTIES OF ANY KIND, WHETHER ORAL OR WRITTEN, |
4128 | +# WHETHER EXPRESS, IMPLIED, OR ARISING BY STATUTE, CUSTOM, COURSE OF DEALING |
4129 | +# OR TRADE USAGE, WITH RESPECT TO THIS SOFTWARE. CANONICAL LTD. SPECIFICALLY |
4130 | +# DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OR CONDITIONS OF TITLE, SATISFACTORY |
4131 | +# QUALITY, MERCHANTABILITY, SATISFACTORINESS, FITNESS FOR A PARTICULAR PURPOSE |
4132 | +# AND NON-INFRINGEMENT. |
4133 | +# |
4134 | +# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL |
4135 | +# CANONICAL LTD. OR ANY OF ITS AFFILIATES, BE LIABLE TO YOU FOR DAMAGES, |
4136 | +# INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
4137 | +# OUT OF THE USE OR INABILITY TO USE THIS SOFTWARE (INCLUDING BUT NOT LIMITED |
4138 | +# TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU |
4139 | +# OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
4140 | +# PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
4141 | +# POSSIBILITY OF SUCH DAMAGES. |
4142 | |
4143 | === modified file 'src/mib/builders/__init__.py' |
4144 | --- src/mib/builders/__init__.py 2015-03-11 18:57:11 +0000 |
4145 | +++ src/mib/builders/__init__.py 2017-06-13 16:13:40 +0000 |
4146 | @@ -1,11 +1,46 @@ |
4147 | -# Copyright 2014-2015 Canonical Ltd. This software is licensed under the |
4148 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
4149 | +# vi: ts=4 expandtab |
4150 | +# Upstream Author: |
4151 | +# |
4152 | +# Canonical Ltd. |
4153 | +# |
4154 | +# Copyright: |
4155 | +# |
4156 | +# (c) 2014-2017 Canonical Ltd. |
4157 | +# |
4158 | +# Licence: |
4159 | +# |
4160 | +# If you have an executed agreement with a Canonical group company which |
4161 | +# includes a licence to this software, your use of this software is governed |
4162 | +# by that agreement. Otherwise, the following applies: |
4163 | +# |
4164 | +# Canonical Ltd. hereby grants to you a world-wide, non-exclusive, |
4165 | +# non-transferable, revocable, perpetual (unless revoked) licence, to (i) use |
4166 | +# this software in connection with Canonical's MAAS software to install Windows |
4167 | +# in non-production environments and (ii) to make a reasonable number of copies |
4168 | +# of this software for backup and installation purposes. You may not: use, |
4169 | +# copy, modify, disassemble, decompile, reverse engineer, or distribute the |
4170 | +# software except as expressly permitted in this licence; permit access to the |
4171 | +# software to any third party other than those acting on your behalf; or use |
4172 | +# this software in connection with a production environment. |
4173 | +# |
4174 | +# CANONICAL LTD. MAKES THIS SOFTWARE AVAILABLE "AS-IS". CANONICAL LTD. MAKES |
4175 | +# NO REPRESENTATIONS OR WARRANTIES OF ANY KIND, WHETHER ORAL OR WRITTEN, |
4176 | +# WHETHER EXPRESS, IMPLIED, OR ARISING BY STATUTE, CUSTOM, COURSE OF DEALING |
4177 | +# OR TRADE USAGE, WITH RESPECT TO THIS SOFTWARE. CANONICAL LTD. SPECIFICALLY |
4178 | +# DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OR CONDITIONS OF TITLE, SATISFACTORY |
4179 | +# QUALITY, MERCHANTABILITY, SATISFACTORINESS, FITNESS FOR A PARTICULAR PURPOSE |
4180 | +# AND NON-INFRINGEMENT. |
4181 | +# |
4182 | +# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL |
4183 | +# CANONICAL LTD. OR ANY OF ITS AFFILIATES, BE LIABLE TO YOU FOR DAMAGES, |
4184 | +# INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
4185 | +# OUT OF THE USE OR INABILITY TO USE THIS SOFTWARE (INCLUDING BUT NOT LIMITED |
4186 | +# TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU |
4187 | +# OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
4188 | +# PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
4189 | +# POSSIBILITY OF SUCH DAMAGES. |
4190 | |
4191 | -from __future__ import ( |
4192 | - absolute_import, |
4193 | - print_function, |
4194 | - unicode_literals, |
4195 | - ) |
4196 | +"""Built-in builders.""" |
4197 | |
4198 | from abc import ( |
4199 | ABCMeta, |
4200 | @@ -82,22 +117,8 @@ |
4201 | |
4202 | def build_image(self, params): |
4203 | """Builds the image with virt-install.""" |
4204 | - output = params.output |
4205 | - interface = params.interface |
4206 | - ram = params.ram |
4207 | - vcpus = params.vcpus |
4208 | - arch = params.arch |
4209 | - os_type = self.os_type |
4210 | - os_variant = self.os_variant |
4211 | - disk_size = self.disk_size |
4212 | - interface_model = self.nic_model |
4213 | - install_location = self.install_location |
4214 | - install_cdrom = self.install_cdrom |
4215 | - extra_arguments = self.extra_arguments |
4216 | - initrd_inject = self.initrd_inject |
4217 | - |
4218 | # Check for valid location |
4219 | - if install_location is None and install_cdrom is None: |
4220 | + if self.install_location is None and self.install_cdrom is None: |
4221 | raise BuildError( |
4222 | "Missing install_location or install_cdrom for virt-install.") |
4223 | |
4224 | @@ -113,39 +134,40 @@ |
4225 | # Create the disk, and set the permissions |
4226 | # that will allow virt-install to access it |
4227 | disk_path = os.path.join(workdir, 'disk.img') |
4228 | - virt.create_disk(disk_path, disk_size, format='raw') |
4229 | + virt.create_disk(disk_path, self.disk_size, disk_format='raw') |
4230 | utils.subp(['chmod', '777', disk_path]) |
4231 | disk_str = "path=%s,format=raw" % disk_path |
4232 | |
4233 | # Start the installation |
4234 | vm_name = 'img-build-%s' % full_name |
4235 | - network_str = 'bridge=%s' % interface |
4236 | - if interface_model is not None: |
4237 | - network_str = '%s,model=%s' % (network_str, interface_model) |
4238 | - if install_location: |
4239 | + network_str = 'bridge=%s' % params.interface |
4240 | + if self.nic_model is not None: |
4241 | + network_str = '%s,model=%s' % ( |
4242 | + network_str, self.nic_model) |
4243 | + if self.install_location: |
4244 | virt.install_location( |
4245 | vm_name, |
4246 | - ram, |
4247 | - arch, |
4248 | - vcpus, |
4249 | - os_type, |
4250 | - os_variant, |
4251 | + params.ram, |
4252 | + params.arch, |
4253 | + params.vcpus, |
4254 | + self.os_type, |
4255 | + self.os_variant, |
4256 | disk_str, |
4257 | network_str, |
4258 | - install_location, |
4259 | - initrd_inject=initrd_inject, |
4260 | - extra_args=extra_arguments) |
4261 | + self.install_location, |
4262 | + initrd_inject=self.initrd_inject, |
4263 | + extra_args=self.extra_arguments) |
4264 | else: |
4265 | virt.install_cdrom( |
4266 | vm_name, |
4267 | - ram, |
4268 | - arch, |
4269 | - vcpus, |
4270 | - os_type, |
4271 | - os_variant, |
4272 | + params.ram, |
4273 | + params.arch, |
4274 | + params.vcpus, |
4275 | + self.os_type, |
4276 | + self.os_variant, |
4277 | disk_str, |
4278 | network_str, |
4279 | - install_cdrom) |
4280 | + self.install_cdrom) |
4281 | |
4282 | # Remove the finished installation from virsh |
4283 | virt.undefine(vm_name) |
4284 | @@ -167,4 +189,4 @@ |
4285 | utils.umount_loop(disk_path, mount_path) |
4286 | |
4287 | # Place in output |
4288 | - shutil.move(output_path, output) |
4289 | + shutil.move(output_path, params.output) |
4290 | |
4291 | === modified file 'src/mib/builders/centos.py' |
4292 | --- src/mib/builders/centos.py 2015-03-11 18:57:11 +0000 |
4293 | +++ src/mib/builders/centos.py 2017-06-13 16:13:40 +0000 |
4294 | @@ -1,19 +1,52 @@ |
4295 | -# Copyright 2014-2015 Canonical Ltd. This software is licensed under the |
4296 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
4297 | +# vi: ts=4 expandtab |
4298 | +# Upstream Author: |
4299 | +# |
4300 | +# Canonical Ltd. |
4301 | +# |
4302 | +# Copyright: |
4303 | +# |
4304 | +# (c) 2014-2017 Canonical Ltd. |
4305 | +# |
4306 | +# Licence: |
4307 | +# |
4308 | +# If you have an executed agreement with a Canonical group company which |
4309 | +# includes a licence to this software, your use of this software is governed |
4310 | +# by that agreement. Otherwise, the following applies: |
4311 | +# |
4312 | +# Canonical Ltd. hereby grants to you a world-wide, non-exclusive, |
4313 | +# non-transferable, revocable, perpetual (unless revoked) licence, to (i) use |
4314 | +# this software in connection with Canonical's MAAS software to install Windows |
4315 | +# in non-production environments and (ii) to make a reasonable number of copies |
4316 | +# of this software for backup and installation purposes. You may not: use, |
4317 | +# copy, modify, disassemble, decompile, reverse engineer, or distribute the |
4318 | +# software except as expressly permitted in this licence; permit access to the |
4319 | +# software to any third party other than those acting on your behalf; or use |
4320 | +# this software in connection with a production environment. |
4321 | +# |
4322 | +# CANONICAL LTD. MAKES THIS SOFTWARE AVAILABLE "AS-IS". CANONICAL LTD. MAKES |
4323 | +# NO REPRESENTATIONS OR WARRANTIES OF ANY KIND, WHETHER ORAL OR WRITTEN, |
4324 | +# WHETHER EXPRESS, IMPLIED, OR ARISING BY STATUTE, CUSTOM, COURSE OF DEALING |
4325 | +# OR TRADE USAGE, WITH RESPECT TO THIS SOFTWARE. CANONICAL LTD. SPECIFICALLY |
4326 | +# DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OR CONDITIONS OF TITLE, SATISFACTORY |
4327 | +# QUALITY, MERCHANTABILITY, SATISFACTORINESS, FITNESS FOR A PARTICULAR PURPOSE |
4328 | +# AND NON-INFRINGEMENT. |
4329 | +# |
4330 | +# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL |
4331 | +# CANONICAL LTD. OR ANY OF ITS AFFILIATES, BE LIABLE TO YOU FOR DAMAGES, |
4332 | +# INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
4333 | +# OUT OF THE USE OR INABILITY TO USE THIS SOFTWARE (INCLUDING BUT NOT LIMITED |
4334 | +# TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU |
4335 | +# OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
4336 | +# PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
4337 | +# POSSIBILITY OF SUCH DAMAGES. |
4338 | |
4339 | -from __future__ import ( |
4340 | - absolute_import, |
4341 | - print_function, |
4342 | - unicode_literals, |
4343 | - ) |
4344 | +"""Builder for CentOS.""" |
4345 | |
4346 | import os |
4347 | import shutil |
4348 | +import tempfile |
4349 | |
4350 | -from mib.builders import ( |
4351 | - BuildError, |
4352 | - VirtInstallBuilder, |
4353 | - ) |
4354 | +from mib.builders import BuildError, VirtInstallBuilder |
4355 | |
4356 | |
4357 | class CentOSBuilder(VirtInstallBuilder): |
4358 | @@ -23,21 +56,29 @@ |
4359 | name = "centos" |
4360 | arches = ["i386", "amd64"] |
4361 | os_type = "linux" |
4362 | - os_variant = "rhel5" |
4363 | disk_size = 5 |
4364 | nic_model = "virtio" |
4365 | install_location = "" |
4366 | |
4367 | + @property |
4368 | + def os_variant(self): |
4369 | + if self.edition == '6': |
4370 | + return 'centos6.5' |
4371 | + return 'centos7.0' |
4372 | + |
4373 | def full_name(self, params): |
4374 | return 'centos%s-%s' % (params.edition, params.arch) |
4375 | |
4376 | - def populate_parser(self, parser): |
4377 | + def populate_parser(self, parser): # pylint: disable=no-self-use |
4378 | """Add parser options.""" |
4379 | parser.add_argument( |
4380 | '--edition', default='7', |
4381 | help="CentOS edition to generate. (Default: 7)") |
4382 | + parser.add_argument( |
4383 | + '--custom-kickstart', default=None, |
4384 | + help="Path to a custom kickstart file used to customize the image") |
4385 | |
4386 | - def validate_params(self, params): |
4387 | + def validate_params(self, params): # pylint: disable=no-self-use |
4388 | """Validates the command line parameters.""" |
4389 | if params.edition not in ['6', '7']: |
4390 | raise BuildError( |
4391 | @@ -46,6 +87,11 @@ |
4392 | raise BuildError( |
4393 | "Cannot generate CentOS 7 for i386, as only amd64 is " |
4394 | "supported.") |
4395 | + if (params.custom_kickstart is not None and |
4396 | + not os.path.exists(params.custom_kickstart)): |
4397 | + raise BuildError( |
4398 | + "Custom kickstart file '%s' does not exist!" % |
4399 | + params.custom_kickstart) |
4400 | |
4401 | def modify_mount(self, mount_path): |
4402 | """Install the curtin directory into mount point.""" |
4403 | @@ -61,29 +107,50 @@ |
4404 | |
4405 | def build_image(self, params): |
4406 | self.validate_params(params) |
4407 | + # pylint: disable=attribute-defined-outside-init |
4408 | self.edition = params.edition |
4409 | + |
4410 | if self.edition == '6': |
4411 | - arch = params.arch |
4412 | - if arch == 'i386': |
4413 | + if params.arch == 'i386': |
4414 | self.install_location = ( |
4415 | "http://mirror.centos.org/centos/6/os/i386") |
4416 | - self.extra_arguments = ( |
4417 | - "console=ttyS0 ks=file:/centos6-i386.ks text utf8") |
4418 | - self.initrd_inject = self.get_contrib_path( |
4419 | + base_kickstart_file = self.get_contrib_path( |
4420 | "centos6/centos6-i386.ks") |
4421 | - elif arch == 'amd64': |
4422 | + elif params.arch == 'amd64': |
4423 | self.install_location = ( |
4424 | "http://mirror.centos.org/centos/6/os/x86_64") |
4425 | - self.extra_arguments = ( |
4426 | - "console=ttyS0 ks=file:/centos6-amd64.ks text utf8") |
4427 | - self.initrd_inject = self.get_contrib_path( |
4428 | + base_kickstart_file = self.get_contrib_path( |
4429 | "centos6/centos6-amd64.ks") |
4430 | + extra_arguments_template = "console=ttyS0 ks=file:/%s text utf8" |
4431 | elif self.edition == '7': |
4432 | self.install_location = ( |
4433 | "http://mirror.centos.org/centos/7/os/x86_64") |
4434 | - self.extra_arguments = ( |
4435 | - "console=ttyS0 inst.ks=file:/centos7-amd64.ks text " |
4436 | + base_kickstart_file = self.get_contrib_path( |
4437 | + "centos7/centos7-amd64.ks") |
4438 | + extra_arguments_template = ( |
4439 | + "console=ttyS0 inst.ks=file:/%s text " |
4440 | "inst.cmdline inst.headless") |
4441 | - self.initrd_inject = self.get_contrib_path( |
4442 | - "centos7/centos7-amd64.ks") |
4443 | + |
4444 | + if params.custom_kickstart is None: |
4445 | + self.extra_arguments = extra_arguments_template % os.path.basename( |
4446 | + base_kickstart_file) |
4447 | + self.initrd_inject = base_kickstart_file |
4448 | + else: |
4449 | + # If a custom kickstart file was given create a new file which |
4450 | + # concatenates the custom kickstart file to the end of ours. |
4451 | + tmp_file_path = tempfile.mktemp(prefix='maas-image-builder-') |
4452 | + with open(tmp_file_path, 'w') as tmp_file: |
4453 | + for ks_file_path in ( |
4454 | + base_kickstart_file, params.custom_kickstart): |
4455 | + tmp_file.write('#\n# From %s\n#\n\n' % ks_file_path) |
4456 | + with open(ks_file_path, 'r') as ks_file: |
4457 | + for line in ks_file: |
4458 | + tmp_file.write(line) |
4459 | + self.extra_arguments = extra_arguments_template % os.path.basename( |
4460 | + tmp_file_path) |
4461 | + self.initrd_inject = tmp_file_path |
4462 | + |
4463 | super(CentOSBuilder, self).build_image(params) |
4464 | + |
4465 | + if params.custom_kickstart is not None: |
4466 | + os.remove(self.initrd_inject) |
4467 | |
4468 | === added file 'src/mib/builders/rhel.py' |
4469 | --- src/mib/builders/rhel.py 1970-01-01 00:00:00 +0000 |
4470 | +++ src/mib/builders/rhel.py 2017-06-13 16:13:40 +0000 |
4471 | @@ -0,0 +1,187 @@ |
4472 | +# vi: ts=4 expandtab |
4473 | +# Upstream Author: |
4474 | +# |
4475 | +# Canonical Ltd. |
4476 | +# |
4477 | +# Copyright: |
4478 | +# |
4479 | +# (c) 2014-2017 Canonical Ltd. |
4480 | +# |
4481 | +# Licence: |
4482 | +# |
4483 | +# If you have an executed agreement with a Canonical group company which |
4484 | +# includes a licence to this software, your use of this software is governed |
4485 | +# by that agreement. Otherwise, the following applies: |
4486 | +# |
4487 | +# Canonical Ltd. hereby grants to you a world-wide, non-exclusive, |
4488 | +# non-transferable, revocable, perpetual (unless revoked) licence, to (i) use |
4489 | +# this software in connection with Canonical's MAAS software to install Windows |
4490 | +# in non-production environments and (ii) to make a reasonable number of copies |
4491 | +# of this software for backup and installation purposes. You may not: use, |
4492 | +# copy, modify, disassemble, decompile, reverse engineer, or distribute the |
4493 | +# software except as expressly permitted in this licence; permit access to the |
4494 | +# software to any third party other than those acting on your behalf; or use |
4495 | +# this software in connection with a production environment. |
4496 | +# |
4497 | +# CANONICAL LTD. MAKES THIS SOFTWARE AVAILABLE "AS-IS". CANONICAL LTD. MAKES |
4498 | +# NO REPRESENTATIONS OR WARRANTIES OF ANY KIND, WHETHER ORAL OR WRITTEN, |
4499 | +# WHETHER EXPRESS, IMPLIED, OR ARISING BY STATUTE, CUSTOM, COURSE OF DEALING |
4500 | +# OR TRADE USAGE, WITH RESPECT TO THIS SOFTWARE. CANONICAL LTD. SPECIFICALLY |
4501 | +# DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OR CONDITIONS OF TITLE, SATISFACTORY |
4502 | +# QUALITY, MERCHANTABILITY, SATISFACTORINESS, FITNESS FOR A PARTICULAR PURPOSE |
4503 | +# AND NON-INFRINGEMENT. |
4504 | +# |
4505 | +# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL |
4506 | +# CANONICAL LTD. OR ANY OF ITS AFFILIATES, BE LIABLE TO YOU FOR DAMAGES, |
4507 | +# INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
4508 | +# OUT OF THE USE OR INABILITY TO USE THIS SOFTWARE (INCLUDING BUT NOT LIMITED |
4509 | +# TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU |
4510 | +# OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
4511 | +# PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
4512 | +# POSSIBILITY OF SUCH DAMAGES. |
4513 | + |
4514 | +"""Builder for RHEL.""" |
4515 | + |
4516 | +import os |
4517 | +import shutil |
4518 | + |
4519 | +from mib import utils |
4520 | +from mib.builders import BuildError, VirtInstallBuilder |
4521 | + |
4522 | +ISOLINUX_CFG = ( |
4523 | + "default text\n" |
4524 | + "timeout 0\n" |
4525 | + "\n" |
4526 | + "label text\n" |
4527 | + " kernel vmlinuz\n" |
4528 | + " append initrd=initrd.img linux text console=ttyS0 inst.repo=cdrom " |
4529 | + "inst.ks=cdrom:/ks.cfg inst.cmdline inst.headless\n") |
4530 | + |
4531 | + |
4532 | +class RHELBuilder(VirtInstallBuilder): |
4533 | + """Builds the RHEL image for amd64. Uses virt-install |
4534 | + to perform this installation process.""" |
4535 | + |
4536 | + name = "rhel" |
4537 | + arches = ["amd64"] |
4538 | + os_type = "linux" |
4539 | + os_variant = "rhel7.0" |
4540 | + disk_size = 5 |
4541 | + nic_model = "virtio" |
4542 | + |
4543 | + def populate_parser(self, parser): |
4544 | + """Add parser arguments.""" |
4545 | + parser.add_argument( |
4546 | + '--rhel-iso', required=True, |
4547 | + help="Path to RHEL installation ISO.") |
4548 | + parser.add_argument( |
4549 | + '--custom-kickstart', default=None, |
4550 | + help="Path to a custom kickstart file used to customize the image") |
4551 | + |
4552 | + def validate_params(self, params): |
4553 | + """Validates the command line parameters.""" |
4554 | + self.install_cdrom = params.rhel_iso |
4555 | + if self.install_cdrom is None: |
4556 | + raise BuildError( |
4557 | + "RHEL requires the --rhel-iso option.") |
4558 | + if not os.path.exists(self.install_cdrom): |
4559 | + raise BuildError( |
4560 | + "Invalid RHEL iso. File does not exist.") |
4561 | + if (params.custom_kickstart is not None and |
4562 | + not os.path.exists(params.custom_kickstart)): |
4563 | + raise BuildError( |
4564 | + "Custom kickstart file '%s' does not exist!" % |
4565 | + params.custom_kickstart) |
4566 | + |
4567 | + def mount_iso(self, workdir, source): # pylint: disable=no-self-use |
4568 | + """Mounts iso in 'iso' directory under workdir.""" |
4569 | + iso_dir = os.path.join(workdir, 'iso') |
4570 | + os.mkdir(iso_dir) |
4571 | + utils.subp([ |
4572 | + 'mount', |
4573 | + source, |
4574 | + iso_dir, |
4575 | + ]) |
4576 | + return iso_dir |
4577 | + |
4578 | + def umount_iso(self, iso_dir): # pylint: disable=no-self-use |
4579 | + """Unmounts iso at path.""" |
4580 | + utils.subp(['umount', iso_dir]) |
4581 | + |
4582 | + def copy_iso(self, workdir, iso_dir): # pylint: disable=no-self-use |
4583 | + """Copies the contents of the iso_dir, into output dir.""" |
4584 | + output = os.path.join(workdir, 'output') |
4585 | + shutil.copytree(iso_dir, output) |
4586 | + return output |
4587 | + |
4588 | + def write_ks(self, output_dir, custom_kickstart=None): |
4589 | + """Writes the kickstarter config into the output_dir at 'ks.cfg'.""" |
4590 | + base_kickstart_file = self.get_contrib_path('rhel7-amd64.ks') |
4591 | + output_file = os.path.join(output_dir, 'ks.cfg') |
4592 | + if custom_kickstart is None: |
4593 | + shutil.copyfile(base_kickstart_file, output_file) |
4594 | + else: |
4595 | + with open(output_file, 'w') as output: |
4596 | + for ks_file_path in (base_kickstart_file, custom_kickstart): |
4597 | + output.write('#\n# From %s\n#\n\n' % ks_file_path) |
4598 | + with open(ks_file_path, 'r') as ks_file: |
4599 | + for line in ks_file: |
4600 | + output.write(line) |
4601 | + |
4602 | + def set_timeout_zero(self, output_dir): # pylint: disable=no-self-use |
4603 | + """Sets the isolinux.cfg timeout to zero.""" |
4604 | + isolinux_cfg = os.path.join(output_dir, 'isolinux', 'isolinux.cfg') |
4605 | + with open(isolinux_cfg, 'w') as stream: |
4606 | + stream.write(ISOLINUX_CFG + '\n') |
4607 | + |
4608 | + def create_iso(self, workdir, source): # pylint: disable=no-self-use |
4609 | + """Creates iso at output, containing files at source.""" |
4610 | + output = os.path.join(workdir, 'output.iso') |
4611 | + utils.subp([ |
4612 | + 'mkisofs', |
4613 | + '-o', output, |
4614 | + '-b', 'isolinux/isolinux.bin', |
4615 | + '-c', 'isolinux/boot.cat', |
4616 | + '-no-emul-boot', |
4617 | + '-boot-load-size', '4', |
4618 | + '-boot-info-table', '-R', '-J', '-v', |
4619 | + '-T', source |
4620 | + ]) |
4621 | + utils.subp(['chmod', '777', workdir]) |
4622 | + utils.subp(['chmod', '777', output]) |
4623 | + return output |
4624 | + |
4625 | + def modify_mount(self, mount_path): |
4626 | + """Install the curtin directory into mount point.""" |
4627 | + path = self.get_contrib_path('curtin') |
4628 | + if not os.path.exists(path): |
4629 | + return |
4630 | + opt_path = os.path.join(mount_path, 'curtin') |
4631 | + shutil.copytree(path, opt_path) |
4632 | + |
4633 | + def build_image(self, params): |
4634 | + self.validate_params(params) |
4635 | + |
4636 | + # Create work space |
4637 | + with utils.tempdir() as workdir: |
4638 | + # Copy out the contents of the ISO file. |
4639 | + iso_dir = self.mount_iso(workdir, self.install_cdrom) |
4640 | + try: |
4641 | + output_dir = self.copy_iso(workdir, iso_dir) |
4642 | + finally: |
4643 | + self.umount_iso(iso_dir) |
4644 | + shutil.rmtree(iso_dir) |
4645 | + |
4646 | + # Write the kickstarter config. |
4647 | + self.write_ks(output_dir, params.custom_kickstart) |
4648 | + |
4649 | + # Update isolinux to not have a timeout. |
4650 | + self.set_timeout_zero(output_dir) |
4651 | + |
4652 | + # Create the final ISO for installation. |
4653 | + try: |
4654 | + self.install_cdrom = self.create_iso(workdir, output_dir) |
4655 | + finally: |
4656 | + shutil.rmtree(output_dir) |
4657 | + |
4658 | + super(RHELBuilder, self).build_image(params) |
4659 | |
4660 | === added file 'src/mib/builders/windows.py' |
4661 | --- src/mib/builders/windows.py 1970-01-01 00:00:00 +0000 |
4662 | +++ src/mib/builders/windows.py 2017-06-13 16:13:40 +0000 |
4663 | @@ -0,0 +1,497 @@ |
4664 | +# vi: ts=4 expandtab |
4665 | +# Upstream Author: |
4666 | +# |
4667 | +# Canonical Ltd. |
4668 | +# |
4669 | +# Copyright: |
4670 | +# |
4671 | +# (c) 2014-2017 Canonical Ltd. |
4672 | +# |
4673 | +# Licence: |
4674 | +# |
4675 | +# If you have an executed agreement with a Canonical group company which |
4676 | +# includes a licence to this software, your use of this software is governed |
4677 | +# by that agreement. Otherwise, the following applies: |
4678 | +# |
4679 | +# Canonical Ltd. hereby grants to you a world-wide, non-exclusive, |
4680 | +# non-transferable, revocable, perpetual (unless revoked) licence, to (i) use |
4681 | +# this software in connection with Canonical's MAAS software to install Windows |
4682 | +# in non-production environments and (ii) to make a reasonable number of copies |
4683 | +# of this software for backup and installation purposes. You may not: use, |
4684 | +# copy, modify, disassemble, decompile, reverse engineer, or distribute the |
4685 | +# software except as expressly permitted in this licence; permit access to the |
4686 | +# software to any third party other than those acting on your behalf; or use |
4687 | +# this software in connection with a production environment. |
4688 | +# |
4689 | +# CANONICAL LTD. MAKES THIS SOFTWARE AVAILABLE "AS-IS". CANONICAL LTD. MAKES |
4690 | +# NO REPRESENTATIONS OR WARRANTIES OF ANY KIND, WHETHER ORAL OR WRITTEN, |
4691 | +# WHETHER EXPRESS, IMPLIED, OR ARISING BY STATUTE, CUSTOM, COURSE OF DEALING |
4692 | +# OR TRADE USAGE, WITH RESPECT TO THIS SOFTWARE. CANONICAL LTD. SPECIFICALLY |
4693 | +# DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OR CONDITIONS OF TITLE, SATISFACTORY |
4694 | +# QUALITY, MERCHANTABILITY, SATISFACTORINESS, FITNESS FOR A PARTICULAR PURPOSE |
4695 | +# AND NON-INFRINGEMENT. |
4696 | +# |
4697 | +# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL |
4698 | +# CANONICAL LTD. OR ANY OF ITS AFFILIATES, BE LIABLE TO YOU FOR DAMAGES, |
4699 | +# INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
4700 | +# OUT OF THE USE OR INABILITY TO USE THIS SOFTWARE (INCLUDING BUT NOT LIMITED |
4701 | +# TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU |
4702 | +# OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
4703 | +# PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
4704 | +# POSSIBILITY OF SUCH DAMAGES. |
4705 | + |
4706 | +"""Builder for Windows.""" |
4707 | + |
4708 | +import os |
4709 | +import re |
4710 | +import shutil |
4711 | +import tempfile |
4712 | + |
4713 | +from tempita import Template |
4714 | + |
4715 | +from mib import net, utils |
4716 | +from mib.builders import Builder, BuildError |
4717 | + |
4718 | +EDITIONS = { |
4719 | + 'win2008r2': "Windows Server 2008 R2 SERVERSTANDARD", |
4720 | + 'win2008hvr2': "Windows Server 2008 R2 SERVERHYPERCORE", |
4721 | + 'win2012': "Windows Server 2012 SERVERSTANDARD", |
4722 | + 'win2012hv': "Hyper-V Server 2012 SERVERHYPERCORE", |
4723 | + 'win2012r2': "Windows Server 2012 R2 SERVERSTANDARD", |
4724 | + 'win2012hvr2': "Hyper-V Server 2012 R2 SERVERHYPERCORE", |
4725 | + 'win2016': "Windows Server 2016 SERVERSTANDARD", |
4726 | + 'win2016hv': "Hyper-V Server 2016 SERVERHYPERCORE", |
4727 | + } |
4728 | + |
4729 | + |
4730 | +class WindowsOSBuilder(Builder): |
4731 | + """Builds the Windows image using kvm-spice.""" |
4732 | + |
4733 | + name = "windows" |
4734 | + arches = ["i386", "amd64"] |
4735 | + |
4736 | + def populate_parser(self, parser): |
4737 | + """Add parser options.""" |
4738 | + parser.add_argument( |
4739 | + '--windows-iso', |
4740 | + help="Path to Windows installation ISO.") |
4741 | + parser.add_argument( |
4742 | + '--windows-edition', |
4743 | + help="Windows edition to install from the ISO.") |
4744 | + parser.add_argument( |
4745 | + '--windows-license-key', |
4746 | + help="Windows license key to embed into generated image.") |
4747 | + parser.add_argument( |
4748 | + '--windows-updates', action='store_true', |
4749 | + help=( |
4750 | + "Install all Windows updates into generated image. " |
4751 | + "(Requires access to microsoft.com)")) |
4752 | + parser.add_argument( |
4753 | + '--windows-drivers', |
4754 | + help=( |
4755 | + "Folder containing drivers to be injected into the Windows " |
4756 | + "installation before the image is generated.")) |
4757 | + parser.add_argument( |
4758 | + '--windows-language', |
4759 | + default='en-US', |
4760 | + help="Windows installation language. Default: en-US") |
4761 | + parser.add_argument( |
4762 | + '--cloudbase-init', |
4763 | + help=( |
4764 | + "Path to the cloudbase-init installer to use. By default it " |
4765 | + "will be pulled from cloudbase.it")) |
4766 | + |
4767 | + def validate_params(self, params): |
4768 | + """Validates the command line parameters.""" |
4769 | + iso = params.windows_iso |
4770 | + if iso is None: |
4771 | + raise BuildError( |
4772 | + "Windows requires the --windows-iso option.") |
4773 | + if not os.path.exists(iso): |
4774 | + raise BuildError( |
4775 | + "Failed to access Windows ISO at: %s" % iso) |
4776 | + edition = params.windows_edition |
4777 | + if edition is None or edition == '': |
4778 | + raise BuildError( |
4779 | + "Windows requires the --windows-edition option.") |
4780 | + if edition not in EDITIONS.keys(): |
4781 | + raise BuildError( |
4782 | + "Invalid Windows edition, should be one of %s." % ( |
4783 | + EDITIONS.keys())) |
4784 | + license_key = params.windows_license_key |
4785 | + if license_key is not None and license_key != '': |
4786 | + if not self.validate_license_key(license_key): |
4787 | + raise BuildError( |
4788 | + "Invalid Windows license key.") |
4789 | + drivers = params.windows_drivers |
4790 | + if drivers is not None and not os.path.isdir(drivers): |
4791 | + raise BuildError( |
4792 | + "Invalid driver path: %s" % drivers) |
4793 | + |
4794 | + def validate_license_key(self, license_key): # pylint: disable=no-self-use |
4795 | + """Validates that license key is in the correct format. It does not |
4796 | + validate, if that license key will work with the selected edition of |
4797 | + Windows.""" |
4798 | + regex = re.compile('^([A-Za-z0-9]{5}-){4}[A-Za-z0-9]{5}$') |
4799 | + return regex.match(license_key) |
4800 | + |
4801 | + def load_unattended_template(self): |
4802 | + """Loads the unattended template that is used for installation.""" |
4803 | + path = self.get_contrib_path('Autounattend.xml') |
4804 | + with open(path, "rb") as stream: |
4805 | + return Template(stream.read().decode('utf-8')) |
4806 | + |
4807 | + def write_unattended(self, output_path, arch, edition, language, |
4808 | + license_key=None, enable_updates=False): |
4809 | + """Outputs the effective unattended.xml file that will be used by |
4810 | + Windows during the installation.""" |
4811 | + template = self.load_unattended_template() |
4812 | + image_name = EDITIONS[edition] |
4813 | + # Windows doesn't accept i386, instead that maps to x86. |
4814 | + if arch == 'i386': |
4815 | + arch = 'x86' |
4816 | + output = template.substitute( |
4817 | + arch=arch, image_name=image_name, language=language, |
4818 | + license_key=license_key, enable_updates=enable_updates) |
4819 | + with open(output_path, 'w') as stream: |
4820 | + for line in output.splitlines(): |
4821 | + stream.write("%s\r\n" % line) |
4822 | + |
4823 | + def create_floppy_disk(self, output_path): # pylint: disable=no-self-use |
4824 | + """Creates an empty floppy disk, formatted with vfat.""" |
4825 | + utils.subp([ |
4826 | + 'dd', 'if=/dev/zero', |
4827 | + 'of=%s' % output_path, |
4828 | + 'bs=1024', 'count=1440', |
4829 | + ]) |
4830 | + utils.subp([ |
4831 | + 'mkfs.vfat', |
4832 | + output_path |
4833 | + ]) |
4834 | + |
4835 | + def prepare_floppy_disk(self, workdir, arch, edition, language, |
4836 | + license_key=None, enable_updates=False): |
4837 | + """Prepares the working directory with Autounattend.vfd.""" |
4838 | + # Create the disk |
4839 | + vfd_path = os.path.join(workdir, 'Autounattend.vfd') |
4840 | + self.create_floppy_disk(vfd_path) |
4841 | + |
4842 | + # Mount the disk |
4843 | + mount_path = os.path.join(workdir, 'vfd_mount') |
4844 | + os.mkdir(mount_path) |
4845 | + utils.subp([ |
4846 | + 'mount', |
4847 | + '-t', 'vfat', |
4848 | + '-o', 'loop', |
4849 | + vfd_path, mount_path, |
4850 | + ]) |
4851 | + |
4852 | + # Place the generated Autounattend.xml file |
4853 | + xml_path = os.path.join(mount_path, 'Autounattend.xml') |
4854 | + try: |
4855 | + self.write_unattended( |
4856 | + xml_path, arch, edition, language, |
4857 | + license_key=license_key, enable_updates=enable_updates) |
4858 | + finally: |
4859 | + utils.subp(['umount', mount_path]) |
4860 | + os.rmdir(mount_path) |
4861 | + return vfd_path |
4862 | + |
4863 | + def download_cloudbase_init( # pylint: disable=no-self-use |
4864 | + self, workdir, arch, cloudbase_init=None): |
4865 | + """Downloads cloudbase init.""" |
4866 | + output_path = os.path.join(workdir, 'cloudbase_init.msi') |
4867 | + if arch == 'amd64': |
4868 | + msi_file = "CloudbaseInitSetup_x64.msi" |
4869 | + elif arch == 'i386': |
4870 | + msi_file = "CloudbaseInitSetup_x86.msi" |
4871 | + download_path = "http://www.cloudbase.it/downloads/" + msi_file |
4872 | + |
4873 | + # --cloudbase-init passed in, don't download. |
4874 | + if cloudbase_init: |
4875 | + shutil.copyfile(cloudbase_init, output_path) |
4876 | + return output_path |
4877 | + |
4878 | + # Remove me, testing only |
4879 | + tmp_path = os.path.join('/tmp', msi_file) |
4880 | + if os.path.exists(tmp_path): |
4881 | + shutil.copyfile(tmp_path, output_path) |
4882 | + return output_path |
4883 | + |
4884 | + utils.subp([ |
4885 | + 'wget', |
4886 | + '-O', output_path, |
4887 | + download_path |
4888 | + ]) |
4889 | + return output_path |
4890 | + |
4891 | + def download_ps_windows_update( # pylint: disable=no-self-use |
4892 | + self, workdir): |
4893 | + """Downloads the PSWindowsUpdate package.""" |
4894 | + output_path = os.path.join(workdir, 'pswindowsupdate.zip') |
4895 | + download_path = ( |
4896 | + "http://gallery.technet.microsoft.com/scriptcenter/" |
4897 | + "2d191bcd-3308-4edd-9de2-88dff796b0bc/file/41459/43/" |
4898 | + "PSWindowsUpdate.zip") |
4899 | + utils.subp([ |
4900 | + 'wget', |
4901 | + '-O', output_path, |
4902 | + download_path |
4903 | + ]) |
4904 | + return output_path |
4905 | + |
4906 | + def unzip_archive(self, src, dest): # pylint: disable=no-self-use |
4907 | + """Un-zips an archive into destination.""" |
4908 | + utils.subp([ |
4909 | + 'unzip', '-q', |
4910 | + src, |
4911 | + '-d', dest, |
4912 | + ]) |
4913 | + |
4914 | + def create_iso(self, output, source): # pylint: disable=no-self-use |
4915 | + """Creates iso at output, containing files at source.""" |
4916 | + utils.subp([ |
4917 | + 'genisoimage', |
4918 | + '-o', output, |
4919 | + '-V', 'SCRIPTS', |
4920 | + '-J', source |
4921 | + ]) |
4922 | + |
4923 | + def build_install_iso(self, workdir, arch, with_updates=False, |
4924 | + drivers_path=None, cloudbase_init=None): |
4925 | + """Builds the iso that is mounted to Windows, to complete the |
4926 | + installation process.""" |
4927 | + install_path = os.path.join(workdir, 'install') |
4928 | + os.mkdir(install_path) |
4929 | + |
4930 | + # Download cloudbase-init into install/cloudbase |
4931 | + cloudbase_dir = os.path.join(install_path, 'cloudbase') |
4932 | + os.mkdir(cloudbase_dir) |
4933 | + self.download_cloudbase_init( |
4934 | + cloudbase_dir, arch, cloudbase_init=cloudbase_init) |
4935 | + |
4936 | + # Copy contrib scripts into install/scripts |
4937 | + contrib_path = self.get_contrib_path('scripts') |
4938 | + scripts_path = os.path.join(install_path, 'scripts') |
4939 | + shutil.copytree(contrib_path, scripts_path) |
4940 | + |
4941 | + # Copy the drivers if provided |
4942 | + if drivers_path is not None: |
4943 | + shutil.copytree( |
4944 | + drivers_path, |
4945 | + os.path.join(install_path, 'infs')) |
4946 | + |
4947 | + # Place PSWindowsUpdate modules if using with_updates |
4948 | + if with_updates: |
4949 | + zip_path = self.download_ps_windows_update(workdir) |
4950 | + self.unzip_archive(zip_path, install_path) |
4951 | + |
4952 | + # Create the iso |
4953 | + output_iso = os.path.join(workdir, 'install.iso') |
4954 | + self.create_iso(output_iso, install_path) |
4955 | + shutil.rmtree(install_path) |
4956 | + return output_iso |
4957 | + |
4958 | + def create_disk_image( # pylint: disable=no-self-use |
4959 | + self, output_path, size): |
4960 | + """Creates the disk image that Windows will install to.""" |
4961 | + utils.subp([ |
4962 | + 'qemu-img', 'create', |
4963 | + '-f', 'raw', |
4964 | + output_path, size |
4965 | + ]) |
4966 | + |
4967 | + def spawn_vm( # pylint: disable=no-self-use |
4968 | + self, ram, vcpus, cdrom, floppy, install_iso, disk, |
4969 | + tap=None): |
4970 | + """Spawns the qemu vm for Windows to install.""" |
4971 | + args = [ |
4972 | + 'kvm-spice', |
4973 | + '-m', '%s' % ram, '-smp', vcpus, |
4974 | + '-cdrom', cdrom, |
4975 | + '-drive', 'file=%s,index=0,format=raw,if=ide,media=disk' % disk, |
4976 | + '-drive', 'file=%s,index=1,format=raw,if=floppy' % floppy, |
4977 | + '-drive', 'file=%s,index=3,format=raw,if=ide,media=cdrom' % install_iso, |
4978 | + ] |
4979 | + if tap is not None: |
4980 | + mac = net.get_random_qemu_mac() |
4981 | + args.extend([ |
4982 | + '-device', 'rtl8139,netdev=net00,mac=%s' % mac, |
4983 | + '-netdev', |
4984 | + 'type=tap,id=net00,script=no,downscript=no,ifname=%s' % tap, |
4985 | + ]) |
4986 | + args.extend([ |
4987 | + '-boot', 'd', '-vga', 'std', |
4988 | + '-k', 'en-us', |
4989 | + # Debug *Remove* |
4990 | + '-vnc', 'localhost:1', |
4991 | + ]) |
4992 | + utils.subp(args) |
4993 | + |
4994 | + def mount_partition( # pylint: disable=no-self-use |
4995 | + self, workdir, disk_path, partition): |
4996 | + """Mounts the parition from the disk.""" |
4997 | + mount_path = os.path.join(workdir, 'disk_mount') |
4998 | + os.mkdir(mount_path) |
4999 | + utils.mount_loop(disk_path, mount_path, partition) |
5000 | + return mount_path |
The diff has been truncated for viewing.
This is into the wrong code repository. This project has also switched to git, please update this branch to merge into the git repo.