Merge lp:~louis/ubuntu/precise/vm-builder/lp531599 into lp:ubuntu/precise/vm-builder
- Precise (12.04)
- lp531599
- Merge into precise
Status: | Needs review |
---|---|
Proposed branch: | lp:~louis/ubuntu/precise/vm-builder/lp531599 |
Merge into: | lp:ubuntu/precise/vm-builder |
Diff against target: |
6519 lines (+6029/-0) (has conflicts) 91 files modified
.bzrignore (+11/-0) AUTHORS (+29/-0) MANIFEST.in (+7/-0) Makefile (+19/-0) NEWS (+58/-0) VMBuilder/__init__.py (+135/-0) VMBuilder/contrib/cli.py (+450/-0) VMBuilder/disk.py (+549/-0) VMBuilder/distro.py (+99/-0) VMBuilder/exception.py (+27/-0) VMBuilder/frontend.py (+53/-0) VMBuilder/hypervisor.py (+121/-0) VMBuilder/log.py (+36/-0) VMBuilder/plugins/__init__.py (+329/-0) VMBuilder/plugins/ec2/__init__.py (+150/-0) VMBuilder/plugins/ec2/templates/ec2_version.tmpl (+1/-0) VMBuilder/plugins/ec2/templates/landscape_client.tmpl (+1/-0) VMBuilder/plugins/ec2/templates/sshd_config.tmpl (+77/-0) VMBuilder/plugins/ec2/templates/sudoers.tmpl (+29/-0) VMBuilder/plugins/firstscripts/__init__.py (+65/-0) VMBuilder/plugins/firstscripts/templates/firstbootrc.tmpl (+10/-0) VMBuilder/plugins/firstscripts/templates/firstloginrc.tmpl (+10/-0) VMBuilder/plugins/kvm/__init__.py (+19/-0) VMBuilder/plugins/kvm/vm.py (+71/-0) VMBuilder/plugins/libvirt/__init__.py (+87/-0) VMBuilder/plugins/libvirt/templates/libvirtxml.tmpl (+46/-0) VMBuilder/plugins/libvirt/templates/libvirtxml_fsimage.tmpl (+33/-0) VMBuilder/plugins/network/__init__.py (+158/-0) VMBuilder/plugins/postinst/__init__.py (+77/-0) VMBuilder/plugins/ubuntu/__init__.py (+19/-0) VMBuilder/plugins/ubuntu/dapper.py (+375/-0) VMBuilder/plugins/ubuntu/distro.py (+294/-0) VMBuilder/plugins/ubuntu/edgy.py (+84/-0) VMBuilder/plugins/ubuntu/feisty.py (+26/-0) VMBuilder/plugins/ubuntu/gutsy.py (+26/-0) VMBuilder/plugins/ubuntu/hardy.py (+58/-0) VMBuilder/plugins/ubuntu/intrepid.py (+45/-0) VMBuilder/plugins/ubuntu/jaunty.py (+40/-0) VMBuilder/plugins/ubuntu/karmic.py (+33/-0) VMBuilder/plugins/ubuntu/lucid.py (+23/-0) VMBuilder/plugins/ubuntu/maverick.py (+23/-0) VMBuilder/plugins/ubuntu/natty.py (+22/-0) VMBuilder/plugins/ubuntu/oneiric.py (+28/-0) VMBuilder/plugins/ubuntu/precise.py (+23/-0) VMBuilder/plugins/ubuntu/suite.py (+28/-0) VMBuilder/plugins/ubuntu/templates/51_update-motd-hardy.tmpl (+10/-0) VMBuilder/plugins/ubuntu/templates/51_update-motd.tmpl (+10/-0) VMBuilder/plugins/ubuntu/templates/dapper_fstab.tmpl (+10/-0) VMBuilder/plugins/ubuntu/templates/dapper_fstab_fsimage.tmpl (+10/-0) VMBuilder/plugins/ubuntu/templates/devicemap.tmpl (+3/-0) VMBuilder/plugins/ubuntu/templates/etc_hosts.tmpl (+10/-0) VMBuilder/plugins/ubuntu/templates/initctl-stub.tmpl (+3/-0) VMBuilder/plugins/ubuntu/templates/interfaces.tmpl (+22/-0) VMBuilder/plugins/ubuntu/templates/is-compat-env.tmpl (+3/-0) VMBuilder/plugins/ubuntu/templates/kernelimg.tmpl (+8/-0) VMBuilder/plugins/ubuntu/templates/locale.tmpl (+1/-0) VMBuilder/plugins/ubuntu/templates/nostart-policy-rc.d.tmpl (+18/-0) VMBuilder/plugins/ubuntu/templates/sources.list.tmpl (+15/-0) VMBuilder/plugins/ubuntu/templates/sudoers.tmpl (+23/-0) VMBuilder/plugins/ubuntu/templates/timezone.tmpl (+1/-0) VMBuilder/plugins/ubuntu/templates/upstart.tmpl (+16/-0) VMBuilder/plugins/ubuntu/templates/xen-ld-so-conf.tmpl (+1/-0) VMBuilder/plugins/ubuntu/tests/test_distro.py (+14/-0) VMBuilder/plugins/virtualbox/__init__.py (+19/-0) VMBuilder/plugins/virtualbox/templates/vm_deploy_script.tmpl (+65/-0) VMBuilder/plugins/virtualbox/vm.py (+59/-0) VMBuilder/plugins/vmware/__init__.py (+19/-0) VMBuilder/plugins/vmware/templates/esxi.vmx.tmpl (+57/-0) VMBuilder/plugins/vmware/templates/flat.vmdk.tmpl (+19/-0) VMBuilder/plugins/vmware/templates/vmware.tmpl (+30/-0) VMBuilder/plugins/vmware/vm.py (+128/-0) VMBuilder/plugins/xen/__init__.py (+20/-0) VMBuilder/plugins/xen/vm.py (+89/-0) VMBuilder/tests/__init__.py (+17/-0) VMBuilder/tests/base_test.py (+67/-0) VMBuilder/tests/disk_tests.py (+289/-0) VMBuilder/tests/network_tests.py (+95/-0) VMBuilder/tests/plugin_tests.py (+119/-0) VMBuilder/tests/ubuntu_tests.py (+39/-0) VMBuilder/tests/util_tests.py (+10/-0) VMBuilder/util.py (+224/-0) VMBuilder/vm.py (+257/-0) examples/ec2-amd64-part-file.txt (+2/-0) examples/ec2-firstboot.sh (+20/-0) examples/ec2-firstlogin.sh (+49/-0) examples/ec2-i386-part-file.txt (+3/-0) setup.py (+26/-0) ubuntu-vm-builder (+24/-0) ubuntu-vm-builder.1 (+7/-0) vmbuilder (+24/-0) vmbuilder.1 (+170/-0) Conflict adding file AUTHORS. Moved existing file to AUTHORS.moved. Conflict adding file MANIFEST.in. Moved existing file to MANIFEST.in.moved. Conflict adding file Makefile. Moved existing file to Makefile.moved. Conflict adding file NEWS. Moved existing file to NEWS.moved. Conflict adding file VMBuilder. Moved existing file to VMBuilder.moved. Conflict adding file examples. Moved existing file to examples.moved. Conflict adding file setup.py. Moved existing file to setup.py.moved. Conflict adding file ubuntu-vm-builder.1. Moved existing file to ubuntu-vm-builder.1.moved. Conflict adding file ubuntu-vm-builder. Moved existing file to ubuntu-vm-builder.moved. Conflict adding file vmbuilder.1. Moved existing file to vmbuilder.1.moved. Conflict adding file vmbuilder. Moved existing file to vmbuilder.moved. |
To merge this branch: | bzr merge lp:~louis/ubuntu/precise/vm-builder/lp531599 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Serge Hallyn | Needs Resubmitting | ||
Scott Moser | Pending | ||
Review via email: mp+86539@code.launchpad.net |
Commit message
As discussed on IRC. Feel free to forward to another reviewer if needed.
Description of the change
device maps are created by 'parted' as well as 'kpartx' for the created partition. Those created by parted are not removed which triggers FS corruption.
The unmap() function is responsible for tearing apart the device maps created by the kpartx commands. Those are created by the map_partitions() function.
While tearing down the device maps created by the parted command, which is invoked in the partition() function might be better done by a callback in the partition() function, the patch is simpler by adding to the existing unmap() which is a callback from map_partition, which is invoked sequentially after partition() in hypervisor.py.
Serge Hallyn (serge-hallyn) wrote : | # |
Sorry, that shouldn't have been resubmit. It's been merged, please don't resubmit :)
Unmerged revisions
- 473. By Louis Bouchard
-
Fix for LP: #531599: dev maps created by parted caused fs corruption
- 472. By Serge Hallyn
- 471. By Michael Vogt
-
add precise
- 470. By Michael Vogt
-
VMBuilder/
plugins/ ubuntu/ oneiric. py: add small sleep to ensure /dev in the chroot is no longer busy after debootstrap finished - 469. By Michael Vogt
-
merge fix from tanktarta (tanktarta) for bug #677378
- 468. By Michael Vogt
- 467. By Michael Vogt
-
add oneiric
- 466. By Serge Hallyn
-
A fix suggested by garo for LP: #784888. If len(elements) < 4,
then checking for 'if not elements[3]' actually throws an error. - 465. By Serge Hallyn
-
Merge from Chris Jones to move execscripts later to make it more useful
- 464. By Serge Hallyn
-
Merge from Neil Wilson to fix builds from ppa
Fixes LP: #765648.
Preview Diff
1 | === added file '.bzrignore' |
2 | --- .bzrignore 1970-01-01 00:00:00 +0000 |
3 | +++ .bzrignore 2011-12-21 11:17:34 +0000 |
4 | @@ -0,0 +1,11 @@ |
5 | +Doxyfile |
6 | +doc |
7 | +.bzrignore |
8 | +.coverage |
9 | +coverage-report |
10 | +VMBuilder.egg-info |
11 | +build |
12 | +stamp |
13 | +VMBuilder/vcsversion.py |
14 | +dist |
15 | +MANIFEST |
16 | |
17 | === added file 'AUTHORS' |
18 | --- AUTHORS 1970-01-01 00:00:00 +0000 |
19 | +++ AUTHORS 2011-12-21 11:17:34 +0000 |
20 | @@ -0,0 +1,29 @@ |
21 | +Main author and original conceptor: Soren Hansen <soren@linux2go.dk> |
22 | + |
23 | +These people have also helped make VMBuilder what it is today: |
24 | + * Andreas Heck <aheck@gmx.de> |
25 | + * Andreas Rütten <AndreasRuetten@gmx.de> |
26 | + * Bart Trojanowski <bart@jukie.net> |
27 | + * Bitti |
28 | + * Bryan McLellan <btm@loftninjas.org> |
29 | + * Charles Hooper <chooper@plumata.com> |
30 | + * Chuck Short <zulcss@ubuntu.com> |
31 | + * Colin Watson <cjwatson@canonical.com> |
32 | + * Duncan McGreggor <duncan@canonical.com> |
33 | + * Eric Hammond <ehammond@thinksome.com> |
34 | + * Iain Lowe <me@ilowe.net> |
35 | + * Jamie Strandboge <jamie@canonical.com> |
36 | + * Kees Cook <kees@outflux.net> |
37 | + * Kevin McDermott <kevin@canonical.com> |
38 | + * Loïc Minier <lool@dooz.org> |
39 | + * Marcelo Boveto Shima <marceloshima@gmail.com> |
40 | + * Markus Korn <thekorn@gmx.de> |
41 | + * Mathias Gug <mathias.gug@canonical.com> |
42 | + * Michael Vogt <michael.vogt@ubuntu.com> |
43 | + * Mike Frisch <mfrisch@rogers.com> |
44 | + * Neal McBurnett <http://mcburnett.org/neal/> |
45 | + * Nick Barcet <nick.barcet@canonical.com> |
46 | + * Scott Moser <smoser@canonical.com> |
47 | + * Simon Andreas Frimann Lund <safl@safl.dk> |
48 | + * Tim Bielawa |
49 | + * Todd Deshane |
50 | |
51 | === renamed file 'AUTHORS' => 'AUTHORS.moved' |
52 | === added file 'MANIFEST.in' |
53 | --- MANIFEST.in 1970-01-01 00:00:00 +0000 |
54 | +++ MANIFEST.in 2011-12-21 11:17:34 +0000 |
55 | @@ -0,0 +1,7 @@ |
56 | +include VMBuilder/plugins/*/templates/*.tmpl |
57 | +include AUTHORS |
58 | +include NEWS |
59 | +include examples/* |
60 | +include ubuntu-vm-builder |
61 | +include ubuntu-vm-builder.1 |
62 | +include vmbuilder.1 |
63 | |
64 | === renamed file 'MANIFEST.in' => 'MANIFEST.in.moved' |
65 | === added file 'Makefile' |
66 | --- Makefile 1970-01-01 00:00:00 +0000 |
67 | +++ Makefile 2011-12-21 11:17:34 +0000 |
68 | @@ -0,0 +1,19 @@ |
69 | +#!/usr/bin/make -f |
70 | + |
71 | +test: clean stamp |
72 | + @nosetests --verbose |
73 | + |
74 | +stamp: |
75 | + @touch $@ |
76 | + |
77 | +build: |
78 | + @python setup.py bdist_egg sdist |
79 | + |
80 | +coverage: |
81 | + @nosetests --quiet --with-coverage --cover-package VMBuilder |
82 | + |
83 | +report: |
84 | + @nosetests --quiet --with-coverage --cover-package VMBuilder --cover-html --cover-html-dir coverage-report |
85 | + |
86 | +clean: |
87 | + @rm -rf stamp .coverage coverage-report |
88 | |
89 | === renamed file 'Makefile' => 'Makefile.moved' |
90 | === added file 'NEWS' |
91 | --- NEWS 1970-01-01 00:00:00 +0000 |
92 | +++ NEWS 2011-12-21 11:17:34 +0000 |
93 | @@ -0,0 +1,58 @@ |
94 | +0.12.4: |
95 | + |
96 | + * Revive firstscripts plugins. This fixes the --firstboot and --firstlogin |
97 | + options, both of which went missing with 0.12. |
98 | + * Make Plugin.install_file work in non-context plugins (plugins that are not |
99 | + Distros or Hypervisors). |
100 | + * Do a slightly better job at cleaning up after ourselves by removing the |
101 | + distro chroot by default and removing the temporary root mount point. |
102 | + * Revive QEMu hypervisor. |
103 | + |
104 | +0.12.3: |
105 | + |
106 | + * Reworked the way timezones and locales are handled. This fixes an |
107 | + actual problem with /etc/localtime not being set correctly, and fixes |
108 | + a theoretical problem with people not running VMBuilder on Ubuntu. |
109 | + * Resurrect -o (--overwrite) which went missing with 0.12.0. |
110 | + * Resurrect Xen support which went missing with 0.12.0. |
111 | + |
112 | +0.12.2: |
113 | + |
114 | + * Moved test directory under VMBuilder/ and include it in the tarball. |
115 | + * Revive ubuntu-vm-builder (which rotted during the 0.11 -> 0.12 transition) |
116 | + * Skip tests that need root, if not run as root. |
117 | + * Fix installing onto logical volumes. |
118 | + * Fix --raw handling. |
119 | + * Refresh documenation in disk.py |
120 | + * Improve handling of non-string settings from command line and config files. |
121 | + * A big bunch of cleanups, most thanks to pyflakes. |
122 | + |
123 | +0.12.1: |
124 | + |
125 | + * Added NEWS file |
126 | + * IntegerSetting.set_value now attempts to convert the given value |
127 | + to an int instead of rejecting it outright. |
128 | + * Bypass blkid cache. |
129 | + * Move initctl out of the way during package installs in Ubuntu plugin. |
130 | + * Update VMWare driver to 0.12 API |
131 | + * Support separate /boot partitions. |
132 | + * Update --part handling to 0.12 API. |
133 | + * Make sure the given MAC is used in the libvirt domain template. |
134 | + * Update libvirt plugin to 0.12 API. |
135 | + * Check for hvm capabilities during preflight check. |
136 | + * Add option to specify a seedfile which will be fed into the debconf |
137 | + database. This enables installing packages like sun-java6-jre which |
138 | + requires the user to accept an EULA. |
139 | + * Correctly handle config keys with dashes in them. |
140 | + * Update postinst plugin to 0.12 API. |
141 | + * If trying to install a file to a nonexisting directory, the directory |
142 | + will be created first. |
143 | + * Refresh the valid_flavours for jaunty, karmic, and Lucid. |
144 | + * Remove lpia from Lucid. |
145 | + * Rewrite $LANG (s/utf8$/UTF-8/) so that locale-gen is happy again. |
146 | + * Bail out early if destdir already exists. |
147 | + * Re-add the option to specify a custom template directory. |
148 | + * Re-add the option to specify a config file. |
149 | + * Default to ext3 for Dapper through Jaunty |
150 | + * Support non-dhcp network configurations. |
151 | + |
152 | |
153 | === renamed file 'NEWS' => 'NEWS.moved' |
154 | === added directory 'VMBuilder' |
155 | === renamed directory 'VMBuilder' => 'VMBuilder.moved' |
156 | === added file 'VMBuilder/__init__.py' |
157 | --- VMBuilder/__init__.py 1970-01-01 00:00:00 +0000 |
158 | +++ VMBuilder/__init__.py 2011-12-21 11:17:34 +0000 |
159 | @@ -0,0 +1,135 @@ |
160 | +#!/usr/bin/python |
161 | +# |
162 | +# Uncomplicated VM Builder |
163 | +# Copyright (C) 2007-2009 Canonical Ltd. |
164 | +# |
165 | +# See AUTHORS for list of contributors |
166 | +# |
167 | +# This program is free software: you can redistribute it and/or modify |
168 | +# it under the terms of the GNU General Public License version 3, as |
169 | +# published by the Free Software Foundation. |
170 | +# |
171 | +# This program is distributed in the hope that it will be useful, |
172 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
173 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
174 | +# GNU General Public License for more details. |
175 | +# |
176 | +# You should have received a copy of the GNU General Public License |
177 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
178 | +# |
179 | +# The publically exposed bits of VMBuilder |
180 | +# |
181 | +import logging |
182 | +import VMBuilder.log |
183 | +import VMBuilder.plugins |
184 | +from VMBuilder.distro import Distro |
185 | +from VMBuilder.hypervisor import Hypervisor |
186 | +from VMBuilder.plugins import Plugin |
187 | +from VMBuilder.exception import VMBuilderException, VMBuilderUserError |
188 | + |
189 | +# Internal bookkeeping |
190 | +distros = {} |
191 | +hypervisors = {} |
192 | +_distro_plugins = [] |
193 | +_hypervisor_plugins = [] |
194 | + |
195 | +# This is meant to be populated by plugins. It should contain a list of the files that we give back to the user. |
196 | + |
197 | +def register_hypervisor(cls): |
198 | + """ |
199 | + Register a hypervisor class with VMBuilder |
200 | + |
201 | + @type cls: Hypervisor |
202 | + @param cls: The new Hypervisor subclass to be registered with VMBuilder |
203 | + """ |
204 | + hypervisors[cls.arg] = cls |
205 | + |
206 | +def get_hypervisor(name): |
207 | + """ |
208 | + Get Hypervisor subclass by name |
209 | + |
210 | + @type name: string |
211 | + @param name: Name of the Hypervisor subclass (defined by its .arg attribute) |
212 | + """ |
213 | + if name in hypervisors: |
214 | + return hypervisors[name] |
215 | + else: |
216 | + raise VMBuilderUserError('No such hypervisor. Available hypervisors: %s' % (' '.join(hypervisors.keys()))) |
217 | + |
218 | +def register_distro(cls): |
219 | + """ |
220 | + Register a distro class with VMBuilder |
221 | + |
222 | + @type cls: Distro |
223 | + @param cls: The new Distro subclass to be registered with VMBuilder |
224 | + """ |
225 | + distros[cls.arg] = cls |
226 | + |
227 | +def get_distro(name): |
228 | + """ |
229 | + Get Distro subclass by name |
230 | + |
231 | + @type name: string |
232 | + @param name: Name of the Distro subclass (defined by its .arg attribute) |
233 | + """ |
234 | + if name in distros: |
235 | + return distros[name] |
236 | + else: |
237 | + raise VMBuilderUserError('No such distro. Available distros: %s' % (' '.join(distros.keys()))) |
238 | + |
239 | +def register_distro_plugin(cls): |
240 | + """ |
241 | + Register a distro plugin with VMBuilder |
242 | + |
243 | + B{Note}: A "distro plugin" is not a plugin that implements a new |
244 | + Distro. It's a plugin that pertains to Distro's. If you want to |
245 | + register a new Distro, use register_distro. |
246 | + |
247 | + @type cls: Plugin |
248 | + @param cls: The Plugin class to registered as a distro plugin |
249 | + """ |
250 | + _distro_plugins.append(cls) |
251 | + _distro_plugins.sort(key=lambda x: x.priority) |
252 | + |
253 | +def register_hypervisor_plugin(cls): |
254 | + """ |
255 | + Register a hypervisor plugin with VMBuilder |
256 | + |
257 | + B{Note}: A "hypervisor plugin" is not a plugin that implements a new |
258 | + Hypervisor. It's a plugin that pertains to Hypervisor's. If you |
259 | + want to register a new Hypervisor, use register_hypervisor. |
260 | + |
261 | + @type cls: Plugin |
262 | + @param cls: The Plugin class to registered as a hypervisor plugin |
263 | + """ |
264 | + _hypervisor_plugins.append(cls) |
265 | + _hypervisor_plugins.sort(key=lambda x: x.priority) |
266 | + |
267 | +def set_console_loglevel(level): |
268 | + """ |
269 | + Adjust the loglevel that will be sent to the console. |
270 | + |
271 | + @type level: number |
272 | + @param level: See the standard logging module |
273 | + """ |
274 | + VMBuilder.log.console.setLevel(level) |
275 | + |
276 | +def get_version_info(): |
277 | + """ |
278 | + Return a dict containing version information for VMBuilder. |
279 | + |
280 | + @return: A dict with (at least) the following keys: |
281 | + - major: Major version number. |
282 | + - minor: Minor version number. |
283 | + - micro: Micro version number. |
284 | + - revno: The revision number of the current branch or the branch from which the tarball was created. |
285 | + """ |
286 | + import vcsversion |
287 | + info = vcsversion.version_info |
288 | + info['major'] = 0 |
289 | + info['minor'] = 12 |
290 | + info['micro'] = 4 |
291 | + return info |
292 | + |
293 | +logging.debug('Loading plugins') |
294 | +VMBuilder.plugins.load_plugins() |
295 | |
296 | === added directory 'VMBuilder/contrib' |
297 | === added file 'VMBuilder/contrib/__init__.py' |
298 | === added file 'VMBuilder/contrib/cli.py' |
299 | --- VMBuilder/contrib/cli.py 1970-01-01 00:00:00 +0000 |
300 | +++ VMBuilder/contrib/cli.py 2011-12-21 11:17:34 +0000 |
301 | @@ -0,0 +1,450 @@ |
302 | +# Uncomplicated VM Builder |
303 | +# Copyright (C) 2007-2009 Canonical Ltd. |
304 | +# |
305 | +# See AUTHORS for list of contributors |
306 | +# |
307 | +# This program is free software: you can redistribute it and/or modify |
308 | +# it under the terms of the GNU General Public License version 3, as |
309 | +# published by the Free Software Foundation. |
310 | +# |
311 | +# This program is distributed in the hope that it will be useful, |
312 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
313 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
314 | +# GNU General Public License for more details. |
315 | +# |
316 | +# You should have received a copy of the GNU General Public License |
317 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
318 | +# |
319 | +# CLI plugin |
320 | +import logging |
321 | +import optparse |
322 | +import os |
323 | +import pwd |
324 | +import shutil |
325 | +import sys |
326 | +import tempfile |
327 | +import VMBuilder |
328 | +import VMBuilder.util as util |
329 | +from VMBuilder.disk import parse_size |
330 | +import VMBuilder.hypervisor |
331 | +from VMBuilder.exception import VMBuilderUserError, VMBuilderException |
332 | + |
333 | +class CLI(object): |
334 | + arg = 'cli' |
335 | + |
336 | + def main(self): |
337 | + tmpfs_mount_point = None |
338 | + try: |
339 | + optparser = optparse.OptionParser() |
340 | + |
341 | + self.set_usage(optparser) |
342 | + |
343 | + optparser.add_option('--version', |
344 | + action='callback', |
345 | + callback=self.versioninfo, |
346 | + help='Show version information') |
347 | + |
348 | + group = optparse.OptionGroup(optparser, 'Build options') |
349 | + group.add_option('--debug', |
350 | + action='callback', |
351 | + callback=self.set_verbosity, |
352 | + help='Show debug information') |
353 | + group.add_option('--verbose', |
354 | + '-v', |
355 | + action='callback', |
356 | + callback=self.set_verbosity, |
357 | + help='Show progress information') |
358 | + group.add_option('--quiet', |
359 | + '-q', |
360 | + action='callback', |
361 | + callback=self.set_verbosity, |
362 | + help='Silent operation') |
363 | + group.add_option('--overwrite', |
364 | + '-o', |
365 | + action='store_true', |
366 | + help='Configuration file') |
367 | + group.add_option('--config', |
368 | + '-c', |
369 | + type='str', |
370 | + help='Configuration file') |
371 | + group.add_option('--templates', |
372 | + metavar='DIR', |
373 | + help='Prepend DIR to template search path.') |
374 | + group.add_option('--destdir', |
375 | + '-d', |
376 | + type='str', |
377 | + help='Destination directory') |
378 | + group.add_option('--only-chroot', |
379 | + action='store_true', |
380 | + help=("Only build the chroot. Don't install it " |
381 | + "on disk images or anything.")) |
382 | + group.add_option('--chroot-dir', |
383 | + help="Build the chroot in directory.") |
384 | + group.add_option('--existing-chroot', |
385 | + help="Use existing chroot.") |
386 | + group.add_option('--tmp', |
387 | + '-t', |
388 | + metavar='DIR', |
389 | + dest='tmp_root', |
390 | + default=tempfile.gettempdir(), |
391 | + help=('Use TMP as temporary working space for ' |
392 | + 'image generation. Defaults to $TMPDIR if ' |
393 | + 'it is defined or /tmp otherwise. ' |
394 | + '[default: %default]')) |
395 | + group.add_option('--tmpfs', |
396 | + metavar="SIZE", |
397 | + help=('Use a tmpfs as the working directory, ' |
398 | + 'specifying its size or "-" to use tmpfs ' |
399 | + 'default (suid,dev,size=1G).')) |
400 | + optparser.add_option_group(group) |
401 | + |
402 | + group = optparse.OptionGroup(optparser, 'Disk') |
403 | + group.add_option('--rootsize', |
404 | + metavar='SIZE', |
405 | + default=4096, |
406 | + help=('Size (in MB) of the root filesystem ' |
407 | + '[default: %default]')) |
408 | + group.add_option('--optsize', |
409 | + metavar='SIZE', |
410 | + default=0, |
411 | + help=('Size (in MB) of the /opt filesystem. If not' |
412 | + ' set, no /opt filesystem will be added.')) |
413 | + group.add_option('--swapsize', |
414 | + metavar='SIZE', |
415 | + default=1024, |
416 | + help=('Size (in MB) of the swap partition ' |
417 | + '[default: %default]')) |
418 | + group.add_option('--raw', |
419 | + metavar='PATH', |
420 | + type='str', |
421 | + action='append', |
422 | + help=("Specify a file (or block device) to use as " |
423 | + "first disk image (can be specified multiple" |
424 | + " times).")) |
425 | + group.add_option('--part', |
426 | + metavar='PATH', |
427 | + type='str', |
428 | + help=("Specify a partition table in PATH. Each " |
429 | + "line of partfile should specify (root " |
430 | + "first): \n mountpoint size \none per " |
431 | + "line, separated by space, where size is " |
432 | + "in megabytes. You can have up to 4 " |
433 | + "virtual disks, a new disk starts on a " |
434 | + "line containing only '---'. ie: \n root " |
435 | + "2000 \n /boot 512 \n swap 1000 \n " |
436 | + "--- \n /var 8000 \n /var/log 2000")) |
437 | + optparser.add_option_group(group) |
438 | + |
439 | + optparser.disable_interspersed_args() |
440 | + (dummy, args) = optparser.parse_args(sys.argv[1:]) |
441 | + optparser.enable_interspersed_args() |
442 | + |
443 | + hypervisor, distro = self.handle_args(optparser, args) |
444 | + |
445 | + self.add_settings_from_context(optparser, distro) |
446 | + self.add_settings_from_context(optparser, hypervisor) |
447 | + |
448 | + hypervisor.register_hook('fix_ownership', self.fix_ownership) |
449 | + |
450 | + config_files = ['/etc/vmbuilder.cfg', |
451 | + os.path.expanduser('~/.vmbuilder.cfg')] |
452 | + (self.options, args) = optparser.parse_args(sys.argv[2:]) |
453 | + |
454 | + if os.geteuid() != 0: |
455 | + raise VMBuilderUserError('Must run as root') |
456 | + |
457 | + distro.overwrite = hypervisor.overwrite = self.options.overwrite |
458 | + destdir = self.options.destdir or ('%s-%s' % (distro.arg, |
459 | + hypervisor.arg)) |
460 | + |
461 | + if self.options.tmpfs and self.options.chroot_dir: |
462 | + raise VMBuilderUserError('--chroot-dir and --tmpfs can not be used together.') |
463 | + |
464 | + if os.path.exists(destdir): |
465 | + if self.options.overwrite: |
466 | + logging.debug('%s existed, but -o was specified. ' |
467 | + 'Nuking it.' % destdir) |
468 | + shutil.rmtree(destdir) |
469 | + else: |
470 | + raise VMBuilderUserError('%s already exists' % destdir) |
471 | + |
472 | + if self.options.config: |
473 | + config_files.append(self.options.config) |
474 | + util.apply_config_files_to_context(config_files, distro) |
475 | + util.apply_config_files_to_context(config_files, hypervisor) |
476 | + |
477 | + if self.options.templates: |
478 | + distro.template_dirs.insert(0, '%s/%%s' |
479 | + % self.options.templates) |
480 | + hypervisor.template_dirs.insert(0, '%s/%%s' |
481 | + % self.options.templates) |
482 | + |
483 | + for option in dir(self.options): |
484 | + if option.startswith('_') or option in ['ensure_value', |
485 | + 'read_module', |
486 | + 'read_file']: |
487 | + continue |
488 | + val = getattr(self.options, option) |
489 | + option = option.replace('_', '-') |
490 | + if val: |
491 | + if (distro.has_setting(option) and |
492 | + distro.get_setting_default(option) != val): |
493 | + distro.set_setting_fuzzy(option, val) |
494 | + elif (hypervisor.has_setting(option) and |
495 | + hypervisor.get_setting_default(option) != val): |
496 | + hypervisor.set_setting_fuzzy(option, val) |
497 | + |
498 | + chroot_dir = None |
499 | + if self.options.existing_chroot: |
500 | + distro.set_chroot_dir(self.options.existing_chroot) |
501 | + distro.call_hooks('preflight_check') |
502 | + else: |
503 | + if self.options.tmpfs is not None: |
504 | + if str(self.options.tmpfs) == '-': |
505 | + tmpfs_size = 1024 |
506 | + else: |
507 | + tmpfs_size = int(self.options.tmpfs) |
508 | + tmpfs_mount_point = util.set_up_tmpfs( |
509 | + tmp_root=self.options.tmp_root, size=tmpfs_size) |
510 | + chroot_dir = tmpfs_mount_point |
511 | + elif self.options.chroot_dir: |
512 | + os.mkdir(self.options.chroot_dir) |
513 | + chroot_dir = self.options.chroot_dir |
514 | + else: |
515 | + chroot_dir = util.tmpdir(tmp_root=self.options.tmp_root) |
516 | + distro.set_chroot_dir(chroot_dir) |
517 | + distro.build_chroot() |
518 | + |
519 | + if self.options.only_chroot: |
520 | + print 'Chroot can be found in %s' % distro.chroot_dir |
521 | + sys.exit(0) |
522 | + |
523 | + self.set_disk_layout(optparser, hypervisor) |
524 | + hypervisor.install_os() |
525 | + |
526 | + os.mkdir(destdir) |
527 | + self.fix_ownership(destdir) |
528 | + hypervisor.finalise(destdir) |
529 | + # If chroot_dir is not None, it means we created it, |
530 | + # and if we reach here, it means the user didn't pass |
531 | + # --only-chroot. Hence, we need to remove it to clean |
532 | + # up after ourselves. |
533 | + if chroot_dir is not None and tmpfs_mount_point is None: |
534 | + util.run_cmd('rm', '-rf', '--one-file-system', chroot_dir) |
535 | + except VMBuilderException, e: |
536 | + logging.error(e) |
537 | + raise |
538 | + finally: |
539 | + if tmpfs_mount_point is not None: |
540 | + util.clean_up_tmpfs(tmpfs_mount_point) |
541 | + util.run_cmd('rmdir', tmpfs_mount_point) |
542 | + |
543 | + def fix_ownership(self, filename): |
544 | + """ |
545 | + Change ownership of file to $SUDO_USER. |
546 | + |
547 | + @type path: string |
548 | + @param path: file or directory to give to $SUDO_USER |
549 | + """ |
550 | + if 'SUDO_USER' in os.environ: |
551 | + logging.debug('Changing ownership of %s to %s' % |
552 | + (filename, os.environ['SUDO_USER'])) |
553 | + (uid, gid) = pwd.getpwnam(os.environ['SUDO_USER'])[2:4] |
554 | + os.chown(filename, uid, gid) |
555 | + |
556 | + def add_settings_from_context(self, optparser, context): |
557 | + setting_groups = set([setting.setting_group for setting |
558 | + in context._config.values()]) |
559 | + for setting_group in setting_groups: |
560 | + optgroup = optparse.OptionGroup(optparser, setting_group.name) |
561 | + for setting in setting_group._settings: |
562 | + args = ['--%s' % setting.name] |
563 | + args += setting.extra_args |
564 | + kwargs = {} |
565 | + if setting.help: |
566 | + kwargs['help'] = setting.help |
567 | + if len(setting.extra_args) > 0: |
568 | + setting.help += " Config option: %s" % setting.name |
569 | + if setting.metavar: |
570 | + kwargs['metavar'] = setting.metavar |
571 | + if setting.get_default(): |
572 | + kwargs['default'] = setting.get_default() |
573 | + if type(setting) == VMBuilder.plugins.Plugin.BooleanSetting: |
574 | + kwargs['action'] = 'store_true' |
575 | + if type(setting) == VMBuilder.plugins.Plugin.ListSetting: |
576 | + kwargs['action'] = 'append' |
577 | + optgroup.add_option(*args, **kwargs) |
578 | + optparser.add_option_group(optgroup) |
579 | + |
580 | + def versioninfo(self, option, opt, value, parser): |
581 | + print ('%(major)d.%(minor)d.%(micro)s.r%(revno)d' % |
582 | + VMBuilder.get_version_info()) |
583 | + sys.exit(0) |
584 | + |
585 | + def set_usage(self, optparser): |
586 | + optparser.set_usage('%prog hypervisor distro [options]') |
587 | +# optparser.arg_help = (('hypervisor', vm.hypervisor_help), ('distro', vm.distro_help)) |
588 | + |
589 | + def handle_args(self, optparser, args): |
590 | + if len(args) < 2: |
591 | + optparser.error("You need to specify at least the hypervisor type " |
592 | + "and the distro") |
593 | + distro = VMBuilder.get_distro(args[1])() |
594 | + hypervisor = VMBuilder.get_hypervisor(args[0])(distro) |
595 | + return hypervisor, distro |
596 | + |
597 | + def set_verbosity(self, option, opt_str, value, parser): |
598 | + if opt_str == '--debug': |
599 | + VMBuilder.set_console_loglevel(logging.DEBUG) |
600 | + elif opt_str == '--verbose': |
601 | + VMBuilder.set_console_loglevel(logging.INFO) |
602 | + elif opt_str == '--quiet': |
603 | + VMBuilder.set_console_loglevel(logging.CRITICAL) |
604 | + |
605 | + def set_disk_layout(self, optparser, hypervisor): |
606 | + default_filesystem = hypervisor.distro.preferred_filesystem() |
607 | + if not self.options.part: |
608 | + rootsize = parse_size(self.options.rootsize) |
609 | + swapsize = parse_size(self.options.swapsize) |
610 | + optsize = parse_size(self.options.optsize) |
611 | + if hypervisor.preferred_storage == VMBuilder.hypervisor.STORAGE_FS_IMAGE: |
612 | + tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root) |
613 | + hypervisor.add_filesystem(filename=tmpfile, |
614 | + size='%dM' % rootsize, |
615 | + type='ext3', |
616 | + mntpnt='/') |
617 | + if swapsize > 0: |
618 | + tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root) |
619 | + hypervisor.add_filesystem(filename=tmpfile, |
620 | + size='%dM' % swapsize, |
621 | + type='swap', |
622 | + mntpnt=None) |
623 | + if optsize > 0: |
624 | + tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root) |
625 | + hypervisor.add_filesystem(filename=tmpfile, |
626 | + size='%dM' % optsize, |
627 | + type='ext3', |
628 | + mntpnt='/opt') |
629 | + else: |
630 | + if self.options.raw: |
631 | + for raw_disk in self.options.raw: |
632 | + hypervisor.add_disk(filename=raw_disk) |
633 | + disk = hypervisor.disks[0] |
634 | + else: |
635 | + size = rootsize + swapsize + optsize |
636 | + tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root) |
637 | + disk = hypervisor.add_disk(tmpfile, size='%dM' % size) |
638 | + offset = 0 |
639 | + disk.add_part(offset, rootsize, default_filesystem, '/') |
640 | + offset += rootsize |
641 | + if swapsize > 0: |
642 | + disk.add_part(offset, swapsize, 'swap', 'swap') |
643 | + offset += swapsize |
644 | + if optsize > 0: |
645 | + disk.add_part(offset, optsize, default_filesystem, '/opt') |
646 | + else: |
647 | + # We need to parse the file specified |
648 | + if hypervisor.preferred_storage == VMBuilder.hypervisor.STORAGE_FS_IMAGE: |
649 | + try: |
650 | + for line in file(self.options.part): |
651 | + elements = line.strip().split(' ') |
652 | + if len(elements) < 4: |
653 | + tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root) |
654 | + else: |
655 | + tmpfile = elements[3] |
656 | + |
657 | + if elements[0] == 'root': |
658 | + hypervisor.add_filesystem(elements[1], |
659 | + default_filesystem, |
660 | + filename=tmpfile, |
661 | + mntpnt='/') |
662 | + elif elements[0] == 'swap': |
663 | + hypervisor.add_filesystem(elements[1], |
664 | + type='swap', |
665 | + filename=tmpfile, |
666 | + mntpnt=None) |
667 | + elif elements[0] == '---': |
668 | + # We just ignore the user's attempt to specify multiple disks |
669 | + pass |
670 | + elif len(elements) == 3: |
671 | + hypervisor.add_filesystem(elements[1], |
672 | + type=default_filesystem, |
673 | + filename=tmpfile, |
674 | + mntpnt=elements[0], |
675 | + devletter='', |
676 | + device=elements[2], |
677 | + dummy=(int(elements[1]) == 0)) |
678 | + else: |
679 | + hypervisor.add_filesystem(elements[1], |
680 | + type=default_filesystem, |
681 | + filename=tmpfile, |
682 | + mntpnt=elements[0]) |
683 | + except IOError, (errno, strerror): |
684 | + optparser.error("%s parsing --part option: %s" % |
685 | + (errno, strerror)) |
686 | + else: |
687 | + try: |
688 | + curdisk = list() |
689 | + size = 0 |
690 | + disk_idx = 0 |
691 | + for line in file(self.options.part): |
692 | + pair = line.strip().split(' ',1) |
693 | + if pair[0] == '---': |
694 | + self.do_disk(hypervisor, curdisk, size, disk_idx) |
695 | + curdisk = list() |
696 | + size = 0 |
697 | + disk_idx += 1 |
698 | + elif pair[0] != '': |
699 | + logging.debug("part: %s, size: %d" % (pair[0], |
700 | + int(pair[1]))) |
701 | + curdisk.append((pair[0], pair[1])) |
702 | + size += int(pair[1]) |
703 | + |
704 | + self.do_disk(hypervisor, curdisk, size, disk_idx) |
705 | + |
706 | + except IOError, (errno, strerror): |
707 | + optparser.error("%s parsing --part option: %s" % |
708 | + (errno, strerror)) |
709 | + |
710 | + def do_disk(self, hypervisor, curdisk, size, disk_idx): |
711 | + default_filesystem = hypervisor.distro.preferred_filesystem() |
712 | + |
713 | + if self.options.raw: |
714 | + disk = hypervisor.add_disk(filename=self.options.raw[disk_idx]) |
715 | + else: |
716 | + disk = hypervisor.add_disk( |
717 | + util.tmp_filename(tmp_root=self.options.tmp_root), |
718 | + size+1) |
719 | + |
720 | + logging.debug("do_disk #%i - size: %d" % (disk_idx, size)) |
721 | + offset = 0 |
722 | + for pair in curdisk: |
723 | + logging.debug("do_disk #%i - part: %s, size: %s, offset: %d" % |
724 | + (disk_idx, pair[0], pair[1], offset)) |
725 | + if pair[0] == 'root': |
726 | + disk.add_part(offset, int(pair[1]), default_filesystem, '/') |
727 | + elif pair[0] == 'swap': |
728 | + disk.add_part(offset, int(pair[1]), pair[0], pair[0]) |
729 | + else: |
730 | + disk.add_part(offset, int(pair[1]), default_filesystem, pair[0]) |
731 | + offset += int(pair[1]) |
732 | + |
733 | +class UVB(CLI): |
734 | + arg = 'ubuntu-vm-builder' |
735 | + |
736 | + def set_usage(self, optparser): |
737 | + optparser.set_usage('%prog hypervisor suite [options]') |
738 | +# optparser.arg_help = (('hypervisor', vm.hypervisor_help), ('suite', self.suite_help)) |
739 | + |
740 | + def suite_help(self): |
741 | + return ('Suite. Valid options: %s' % |
742 | + " ".join(VMBuilder.plugins.ubuntu.distro.Ubuntu.suites)) |
743 | + |
744 | + def handle_args(self, optparser, args): |
745 | + if len(args) < 2: |
746 | + optparser.error("You need to specify at least the hypervisor type " |
747 | + "and the series") |
748 | + distro = VMBuilder.get_distro('ubuntu')() |
749 | + hypervisor = VMBuilder.get_hypervisor(args[0])(distro) |
750 | + distro.set_setting('suite', args[1]) |
751 | + return hypervisor, distro |
752 | |
753 | === added file 'VMBuilder/disk.py' |
754 | --- VMBuilder/disk.py 1970-01-01 00:00:00 +0000 |
755 | +++ VMBuilder/disk.py 2011-12-21 11:17:34 +0000 |
756 | @@ -0,0 +1,549 @@ |
757 | +# |
758 | +# Uncomplicated VM Builder |
759 | +# Copyright (C) 2007-2010 Canonical Ltd. |
760 | +# |
761 | +# See AUTHORS for list of contributors |
762 | +# |
763 | +# This program is free software: you can redistribute it and/or modify |
764 | +# it under the terms of the GNU General Public License version 3, as |
765 | +# published by the Free Software Foundation. |
766 | +# |
767 | +# This program is distributed in the hope that it will be useful, |
768 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
769 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
770 | +# GNU General Public License for more details. |
771 | +# |
772 | +# You should have received a copy of the GNU General Public License |
773 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
774 | +# |
775 | +# Virtual disk management |
776 | + |
777 | +import fcntl |
778 | +import logging |
779 | +import os |
780 | +import os.path |
781 | +import re |
782 | +import stat |
783 | +import string |
784 | +import time |
785 | +from VMBuilder.util import run_cmd |
786 | +from VMBuilder.exception import VMBuilderUserError, VMBuilderException |
787 | +from struct import unpack |
788 | + |
789 | +TYPE_EXT2 = 0 |
790 | +TYPE_EXT3 = 1 |
791 | +TYPE_XFS = 2 |
792 | +TYPE_SWAP = 3 |
793 | +TYPE_EXT4 = 4 |
794 | + |
795 | +class Disk(object): |
796 | + """ |
797 | + Virtual disk. |
798 | + |
799 | + @type vm: Hypervisor |
800 | + @param vm: The Hypervisor to which the disk belongs |
801 | + @type filename: string |
802 | + @param filename: filename of the disk image |
803 | + @type size: string or number |
804 | + @param size: The size of the disk image to create (passed to |
805 | + L{parse_size}). If specified and filename already exists, |
806 | + L{VMBuilderUserError} will be raised. Otherwise, a disk image of |
807 | + this size will be created once L{create}() is called. |
808 | + """ |
809 | + |
810 | + def __init__(self, vm, filename, size=None): |
811 | + self.vm = vm |
812 | + "The hypervisor to which the disk belongs." |
813 | + |
814 | + self.filename = filename |
815 | + "The filename of the disk image." |
816 | + |
817 | + self.partitions = [] |
818 | + "The list of partitions on the disk. Is kept in order by L{add_part}." |
819 | + |
820 | + self.preallocated = False |
821 | + "Whether the file existed already (True if it did, False if we had to create it)." |
822 | + |
823 | + self.size = 0 |
824 | + "The size of the disk. For preallocated disks, this is detected." |
825 | + |
826 | + if not os.path.exists(self.filename): |
827 | + if not size: |
828 | + raise VMBuilderUserError('%s does not exist, but no size was given.' % (self.filename)) |
829 | + self.size = parse_size(size) |
830 | + else: |
831 | + if size: |
832 | + raise VMBuilderUserError('%s exists, but size was given.' % (self.filename)) |
833 | + self.preallocated = True |
834 | + self.size = detect_size(self.filename) |
835 | + |
836 | + self.format_type = None |
837 | + "The format type of the disks. Only used for converted disks." |
838 | + |
839 | + def devletters(self): |
840 | + """ |
841 | + @rtype: string |
842 | + @return: the series of letters that ought to correspond to the device inside |
843 | + the VM. E.g. the first disk of a VM would return 'a', while the 702nd would return 'zz' |
844 | + """ |
845 | + |
846 | + return index_to_devname(self.vm.disks.index(self)) |
847 | + |
848 | + def create(self): |
849 | + """ |
850 | + Creates the disk image (if it doesn't already exist). |
851 | + |
852 | + Once this method returns succesfully, L{filename} can be |
853 | + expected to points to point to whatever holds the virtual disk |
854 | + (be it a file, partition, logical volume, etc.). |
855 | + """ |
856 | + if not os.path.exists(self.filename): |
857 | + logging.info('Creating disk image: "%s" of size: %dMB' % (self.filename, self.size)) |
858 | + run_cmd(qemu_img_path(), 'create', '-f', 'raw', self.filename, '%dM' % self.size) |
859 | + |
860 | + def partition(self): |
861 | + """ |
862 | + Partitions the disk image. First adds a partition table and then |
863 | + adds the individual partitions. |
864 | + |
865 | + Should only be called once and only after you've added all partitions. |
866 | + """ |
867 | + |
868 | + logging.info('Adding partition table to disk image: %s' % self.filename) |
869 | + run_cmd('parted', '--script', self.filename, 'mklabel', 'msdos') |
870 | + |
871 | + # Partition the disk |
872 | + for part in self.partitions: |
873 | + part.create(self) |
874 | + |
875 | + def map_partitions(self): |
876 | + """ |
877 | + Create loop devices corresponding to the partitions. |
878 | + |
879 | + Once this has returned succesfully, each partition's map device |
880 | + is set as its L{filename<Disk.Partition.filename>} attribute. |
881 | + |
882 | + Call this after L{partition}. |
883 | + """ |
884 | + logging.info('Creating loop devices corresponding to the created partitions') |
885 | + self.vm.add_clean_cb(lambda : self.unmap(ignore_fail=True)) |
886 | + kpartx_output = run_cmd('kpartx', '-av', self.filename) |
887 | + parts = [] |
888 | + for line in kpartx_output.split('\n'): |
889 | + if line == "" or line.startswith("gpt:") or line.startswith("dos:"): |
890 | + continue |
891 | + if line.startswith("add"): |
892 | + parts.append(line) |
893 | + continue |
894 | + logging.error('Skipping unknown line in kpartx output (%s)' % line) |
895 | + mapdevs = [] |
896 | + for line in parts: |
897 | + mapdevs.append(line.split(' ')[2]) |
898 | + for (part, mapdev) in zip(self.partitions, mapdevs): |
899 | + part.set_filename('/dev/mapper/%s' % mapdev) |
900 | + |
901 | + def mkfs(self): |
902 | + """ |
903 | + Creates the partitions' filesystems |
904 | + """ |
905 | + logging.info("Creating file systems") |
906 | + for part in self.partitions: |
907 | + part.mkfs() |
908 | + |
909 | + def get_grub_id(self): |
910 | + """ |
911 | + @rtype: string |
912 | + @return: name of the disk as known by grub |
913 | + """ |
914 | + return '(hd%d)' % self.get_index() |
915 | + |
916 | + def get_index(self): |
917 | + """ |
918 | + @rtype: number |
919 | + @return: index of the disk (starting from 0 for the hypervisor's first disk) |
920 | + """ |
921 | + return self.vm.disks.index(self) |
922 | + |
923 | + def unmap(self, ignore_fail=False): |
924 | + """ |
925 | + Destroy all mapping devices |
926 | + |
927 | + Unsets L{Partition}s' and L{Filesystem}s' filename attribute |
928 | + """ |
929 | + # first sleep to give the loopback devices a chance to settle down |
930 | + time.sleep(3) |
931 | + |
932 | + tries = 0 |
933 | + max_tries = 3 |
934 | + while tries < max_tries: |
935 | + try: |
936 | + run_cmd('kpartx', '-d', self.filename, ignore_fail=False) |
937 | + break |
938 | + except: |
939 | + pass |
940 | + tries += 1 |
941 | + time.sleep(3) |
942 | + |
943 | + if tries >= max_tries: |
944 | + # try it one last time |
945 | + logging.info("Could not unmap '%s' after '%d' attempts. Final attempt" % (self.filename, tries)) |
946 | + run_cmd('kpartx', '-d', self.filename, ignore_fail=ignore_fail) |
947 | + |
948 | + for part in self.partitions: |
949 | + parted_oldmap=part.filename[len("/dev/mapper/"):-1]+"p"+part.filename[-1] |
950 | + logging.debug("Removing parted old map with 'dmsetup remove %s'" % parted_oldmap) |
951 | + run_cmd('dmsetup', 'remove', parted_oldmap, ignore_fail=ignore_fail) |
952 | + part.set_filename(None) |
953 | + |
954 | + def add_part(self, begin, length, type, mntpnt): |
955 | + """ |
956 | + Add a partition to the disk |
957 | + |
958 | + @type begin: number |
959 | + @param begin: Start offset of the new partition (in megabytes) |
960 | + @type length: |
961 | + @param length: Size of the new partition (in megabytes) |
962 | + @type type: string |
963 | + @param type: Type of the new partition. Valid options are: ext2 ext3 xfs swap linux-swap |
964 | + @type mntpnt: string |
965 | + @param mntpnt: Intended mountpoint inside the guest of the new partition |
966 | + """ |
967 | + length = parse_size(length) |
968 | + end = begin+length-1 |
969 | + logging.debug("add_part - begin %d, length %d, end %d, type %s, mntpnt %s" % (begin, length, end, type, mntpnt)) |
970 | + for part in self.partitions: |
971 | + if (begin >= part.begin and begin <= part.end) or \ |
972 | + (end >= part.begin and end <= part.end): |
973 | + raise VMBuilderUserError('Partitions are overlapping') |
974 | + if begin < 0 or end > self.size: |
975 | + raise VMBuilderUserError('Partition is out of bounds. start=%d, end=%d, disksize=%d' % (begin,end,self.size)) |
976 | + part = self.Partition(disk=self, begin=begin, end=end, type=str_to_type(type), mntpnt=mntpnt) |
977 | + self.partitions.append(part) |
978 | + |
979 | + # We always keep the partitions in order, so that the output from kpartx matches our understanding |
980 | + self.partitions.sort(cmp=lambda x,y: x.begin - y.begin) |
981 | + |
982 | + def convert(self, destdir, format): |
983 | + """ |
984 | + Convert the disk image |
985 | + |
986 | + @type destdir: string |
987 | + @param destdir: Target location of converted disk image |
988 | + @type format: string |
989 | + @param format: The target format (as understood by qemu-img or vdi) |
990 | + @rtype: string |
991 | + @return: the name of the converted image |
992 | + """ |
993 | + if self.preallocated: |
994 | + # We don't convert preallocated disk images. That would be silly. |
995 | + return self.filename |
996 | + |
997 | + filename = os.path.basename(self.filename) |
998 | + if '.' in filename: |
999 | + filename = filename[:filename.rindex('.')] |
1000 | + destfile = '%s/%s.%s' % (destdir, filename, format) |
1001 | + |
1002 | + logging.info('Converting %s to %s, format %s' % (self.filename, format, destfile)) |
1003 | + if format == 'vdi': |
1004 | + run_cmd(vbox_manager_path(), 'convertfromraw', '-format', 'VDI', self.filename, destfile) |
1005 | + else: |
1006 | + run_cmd(qemu_img_path(), 'convert', '-O', format, self.filename, destfile) |
1007 | + os.unlink(self.filename) |
1008 | + self.filename = os.path.abspath(destfile) |
1009 | + self.format_type = format |
1010 | + return destfile |
1011 | + |
1012 | + class Partition(object): |
1013 | + def __init__(self, disk, begin, end, type, mntpnt): |
1014 | + self.disk = disk |
1015 | + "The disk on which this Partition resides." |
1016 | + |
1017 | + self.begin = begin |
1018 | + "The start of the partition" |
1019 | + |
1020 | + self.end = end |
1021 | + "The end of the partition" |
1022 | + |
1023 | + self.type = type |
1024 | + "The partition type" |
1025 | + |
1026 | + self.mntpnt = mntpnt |
1027 | + "The destined mount point" |
1028 | + |
1029 | + self.filename = None |
1030 | + "The filename of this partition (the map device)" |
1031 | + |
1032 | + self.fs = Filesystem(vm=self.disk.vm, type=self.type, mntpnt=self.mntpnt) |
1033 | + "The enclosed filesystem" |
1034 | + |
1035 | + def set_filename(self, filename): |
1036 | + self.filename = filename |
1037 | + self.fs.filename = filename |
1038 | + |
1039 | + def parted_fstype(self): |
1040 | + """ |
1041 | + @rtype: string |
1042 | + @return: the filesystem type of the partition suitable for passing to parted |
1043 | + """ |
1044 | + return { TYPE_EXT2: 'ext2', TYPE_EXT3: 'ext2', TYPE_EXT4: 'ext2', TYPE_XFS: 'ext2', TYPE_SWAP: 'linux-swap(new)' }[self.type] |
1045 | + |
1046 | + def create(self, disk): |
1047 | + """Adds partition to the disk image (does not mkfs or anything like that)""" |
1048 | + logging.info('Adding type %d partition to disk image: %s' % (self.type, disk.filename)) |
1049 | + if self.begin == 0: |
1050 | + logging.info('Partition at beginning of disk - reserving first cylinder') |
1051 | + partition_start = "63s" |
1052 | + else: |
1053 | + partition_start = self.begin |
1054 | + run_cmd('parted', '--script', '--', disk.filename, 'mkpart', 'primary', self.parted_fstype(), partition_start, self.end) |
1055 | + |
1056 | + def mkfs(self): |
1057 | + """Adds Filesystem object""" |
1058 | + self.fs.mkfs() |
1059 | + |
1060 | + def get_grub_id(self): |
1061 | + """The name of the partition as known by grub""" |
1062 | + return '(hd%d,%d)' % (self.disk.get_index(), self.get_index()) |
1063 | + |
1064 | + def get_suffix(self): |
1065 | + """Returns 'a4' for a device that would be called /dev/sda4 in the guest. |
1066 | + This allows other parts of VMBuilder to set the prefix to something suitable.""" |
1067 | + return '%s%d' % (self.disk.devletters(), self.get_index() + 1) |
1068 | + |
1069 | + def get_index(self): |
1070 | + """Index of the disk (starting from 0)""" |
1071 | + return self.disk.partitions.index(self) |
1072 | + |
1073 | + def set_type(self, type): |
1074 | + try: |
1075 | + if int(type) == type: |
1076 | + self.type = type |
1077 | + else: |
1078 | + self.type = str_to_type(type) |
1079 | + except ValueError: |
1080 | + self.type = str_to_type(type) |
1081 | + |
1082 | +class Filesystem(object): |
1083 | + def __init__(self, vm=None, size=0, type=None, mntpnt=None, filename=None, devletter='a', device='', dummy=False): |
1084 | + self.vm = vm |
1085 | + self.filename = filename |
1086 | + self.size = parse_size(size) |
1087 | + self.devletter = devletter |
1088 | + self.device = device |
1089 | + self.dummy = dummy |
1090 | + |
1091 | + self.set_type(type) |
1092 | + |
1093 | + self.mntpnt = mntpnt |
1094 | + |
1095 | + self.preallocated = False |
1096 | + "Whether the file existed already (True if it did, False if we had to create it)." |
1097 | + |
1098 | + def create(self): |
1099 | + logging.info('Creating filesystem: %s, size: %d, dummy: %s' % (self.mntpnt, self.size, repr(self.dummy))) |
1100 | + if not os.path.exists(self.filename): |
1101 | + logging.info('Not preallocated, so we create it.') |
1102 | + if not self.filename: |
1103 | + if self.mntpnt: |
1104 | + self.filename = re.sub('[^\w\s/]', '', self.mntpnt).strip().lower() |
1105 | + self.filename = re.sub('[\w/]', '_', self.filename) |
1106 | + if self.filename == '_': |
1107 | + self.filename = 'root' |
1108 | + elif self.type == TYPE_SWAP: |
1109 | + self.filename = 'swap' |
1110 | + else: |
1111 | + raise VMBuilderException('mntpnt not set') |
1112 | + |
1113 | + self.filename = '%s/%s' % (self.vm.workdir, self.filename) |
1114 | + while os.path.exists('%s.img' % self.filename): |
1115 | + self.filename += '_' |
1116 | + self.filename += '.img' |
1117 | + logging.info('A name wasn\'t specified either, so we make one up: %s' % self.filename) |
1118 | + run_cmd(qemu_img_path(), 'create', '-f', 'raw', self.filename, '%dM' % self.size) |
1119 | + self.mkfs() |
1120 | + |
1121 | + def mkfs(self): |
1122 | + if not self.filename: |
1123 | + raise VMBuilderException('We can\'t mkfs if filename is not set. Did you forget to call .create()?') |
1124 | + if not self.dummy: |
1125 | + cmd = self.mkfs_fstype() + [self.filename] |
1126 | + run_cmd(*cmd) |
1127 | + # Let udev have a chance to extract the UUID for us |
1128 | + run_cmd('udevadm', 'settle') |
1129 | + if os.path.exists("/sbin/vol_id"): |
1130 | + self.uuid = run_cmd('vol_id', '--uuid', self.filename).rstrip() |
1131 | + elif os.path.exists("/sbin/blkid"): |
1132 | + self.uuid = run_cmd('blkid', '-c', '/dev/null', '-sUUID', '-ovalue', self.filename).rstrip() |
1133 | + |
1134 | + def mkfs_fstype(self): |
1135 | + map = { TYPE_EXT2: ['mkfs.ext2', '-F'], TYPE_EXT3: ['mkfs.ext3', '-F'], TYPE_EXT4: ['mkfs.ext4', '-F'], TYPE_XFS: ['mkfs.xfs'], TYPE_SWAP: ['mkswap'] } |
1136 | + |
1137 | + if not self.vm.distro.has_256_bit_inode_ext3_support(): |
1138 | + map[TYPE_EXT3] = ['mkfs.ext3', '-I 128', '-F'] |
1139 | + |
1140 | + return map[self.type] |
1141 | + |
1142 | + def fstab_fstype(self): |
1143 | + return { TYPE_EXT2: 'ext2', TYPE_EXT3: 'ext3', TYPE_EXT4: 'ext4', TYPE_XFS: 'xfs', TYPE_SWAP: 'swap' }[self.type] |
1144 | + |
1145 | + def fstab_options(self): |
1146 | + return 'defaults' |
1147 | + |
1148 | + def mount(self, rootmnt): |
1149 | + if (self.type != TYPE_SWAP) and not self.dummy: |
1150 | + logging.debug('Mounting %s', self.mntpnt) |
1151 | + self.mntpath = '%s%s' % (rootmnt, self.mntpnt) |
1152 | + if not os.path.exists(self.mntpath): |
1153 | + os.makedirs(self.mntpath) |
1154 | + run_cmd('mount', '-o', 'loop', self.filename, self.mntpath) |
1155 | + self.vm.add_clean_cb(self.umount) |
1156 | + |
1157 | + def umount(self): |
1158 | + self.vm.cancel_cleanup(self.umount) |
1159 | + if (self.type != TYPE_SWAP) and not self.dummy: |
1160 | + logging.debug('Unmounting %s', self.mntpath) |
1161 | + run_cmd('umount', self.mntpath) |
1162 | + |
1163 | + def get_suffix(self): |
1164 | + """Returns 'a4' for a device that would be called /dev/sda4 in the guest.. |
1165 | + This allows other parts of VMBuilder to set the prefix to something suitable.""" |
1166 | + if self.device: |
1167 | + return self.device |
1168 | + else: |
1169 | + return '%s%d' % (self.devletters(), self.get_index() + 1) |
1170 | + |
1171 | + def devletters(self): |
1172 | + """ |
1173 | + @rtype: string |
1174 | + @return: the series of letters that ought to correspond to the device inside |
1175 | + the VM. E.g. the first filesystem of a VM would return 'a', while the 702nd would return 'zz' |
1176 | + """ |
1177 | + return self.devletter |
1178 | + |
1179 | + def get_index(self): |
1180 | + """Index of the disk (starting from 0)""" |
1181 | + return self.vm.filesystems.index(self) |
1182 | + |
1183 | + def set_type(self, type): |
1184 | + try: |
1185 | + if int(type) == type: |
1186 | + self.type = type |
1187 | + else: |
1188 | + self.type = str_to_type(type) |
1189 | + except ValueError: |
1190 | + self.type = str_to_type(type) |
1191 | + |
1192 | +def parse_size(size_str): |
1193 | + """Takes a size like qemu-img would accept it and returns the size in MB""" |
1194 | + try: |
1195 | + return int(size_str) |
1196 | + except ValueError: |
1197 | + pass |
1198 | + |
1199 | + try: |
1200 | + num = int(size_str[:-1]) |
1201 | + except ValueError: |
1202 | + raise VMBuilderUserError("Invalid size: %s" % size_str) |
1203 | + |
1204 | + if size_str[-1:] == 'g' or size_str[-1:] == 'G': |
1205 | + return num * 1024 |
1206 | + if size_str[-1:] == 'm' or size_str[-1:] == 'M': |
1207 | + return num |
1208 | + if size_str[-1:] == 'k' or size_str[-1:] == 'K': |
1209 | + return num / 1024 |
1210 | + |
1211 | +str_to_type_map = { 'ext2': TYPE_EXT2, |
1212 | + 'ext3': TYPE_EXT3, |
1213 | + 'ext4': TYPE_EXT4, |
1214 | + 'xfs': TYPE_XFS, |
1215 | + 'swap': TYPE_SWAP, |
1216 | + 'linux-swap': TYPE_SWAP } |
1217 | + |
1218 | +def str_to_type(type): |
1219 | + try: |
1220 | + return str_to_type_map[type] |
1221 | + except KeyError: |
1222 | + raise Exception('Unknown partition type: %s' % type) |
1223 | + |
1224 | +def rootpart(disks): |
1225 | + """Returns the partition which contains the root dir""" |
1226 | + return path_to_partition(disks, '/') |
1227 | + |
1228 | +def bootpart(disks): |
1229 | + """Returns the partition which contains /boot""" |
1230 | + return path_to_partition(disks, '/boot/foo') |
1231 | + |
1232 | +def path_to_partition(disks, path): |
1233 | + parts = get_ordered_partitions(disks) |
1234 | + parts.reverse() |
1235 | + for part in parts: |
1236 | + if path.startswith(part.mntpnt): |
1237 | + return part |
1238 | + raise VMBuilderException("Couldn't find partition path %s belongs to" % path) |
1239 | + |
1240 | +def create_filesystems(vm): |
1241 | + for filesystem in vm.filesystems: |
1242 | + filesystem.create() |
1243 | + |
1244 | +def create_partitions(vm): |
1245 | + for disk in vm.disks: |
1246 | + disk.create(vm.workdir) |
1247 | + |
1248 | +def get_ordered_filesystems(vm): |
1249 | + """Returns filesystems (self hosted as well as contained in partitions |
1250 | + in an order suitable for mounting them""" |
1251 | + fss = list(vm.filesystems) |
1252 | + for disk in vm.disks: |
1253 | + fss += [part.fs for part in disk.partitions] |
1254 | + fss.sort(lambda x,y: len(x.mntpnt or '')-len(y.mntpnt or '')) |
1255 | + return fss |
1256 | + |
1257 | +def get_ordered_partitions(disks): |
1258 | + """Returns partitions from disks in an order suitable for mounting them""" |
1259 | + parts = [] |
1260 | + for disk in disks: |
1261 | + parts += disk.partitions |
1262 | + parts.sort(lambda x,y: len(x.mntpnt or '')-len(y.mntpnt or '')) |
1263 | + return parts |
1264 | + |
1265 | +def devname_to_index(devname): |
1266 | + return devname_to_index_rec(devname) - 1 |
1267 | + |
1268 | +def devname_to_index_rec(devname): |
1269 | + if not devname: |
1270 | + return 0 |
1271 | + return 26 * devname_to_index_rec(devname[:-1]) + (string.ascii_lowercase.index(devname[-1]) + 1) |
1272 | + |
1273 | +def index_to_devname(index, suffix=''): |
1274 | + if index < 0: |
1275 | + return suffix |
1276 | + return index_to_devname(index / 26 -1, string.ascii_lowercase[index % 26]) + suffix |
1277 | + |
1278 | +def detect_size(filename): |
1279 | + st = os.stat(filename) |
1280 | + if stat.S_ISREG(st.st_mode): |
1281 | + return st.st_size / 1024*1024 |
1282 | + elif stat.S_ISBLK(st.st_mode): |
1283 | + # I really wish someone would make these available in Python |
1284 | + BLKGETSIZE64 = 2148012658 |
1285 | + fp = open(filename, 'r') |
1286 | + fd = fp.fileno() |
1287 | + s = fcntl.ioctl(fd, BLKGETSIZE64, ' '*8) |
1288 | + return unpack('L', s)[0] / 1024*1024 |
1289 | + |
1290 | + raise VMBuilderException('No idea how to find the size of %s' % filename) |
1291 | + |
1292 | +def qemu_img_path(): |
1293 | + exes = ['kvm-img', 'qemu-img'] |
1294 | + for dir in os.environ['PATH'].split(os.path.pathsep): |
1295 | + for exe in exes: |
1296 | + path = '%s%s%s' % (dir, os.path.sep, exe) |
1297 | + if os.access(path, os.X_OK): |
1298 | + return path |
1299 | + |
1300 | +def vbox_manager_path(): |
1301 | + exe = 'VBoxManage' |
1302 | + for dir in os.environ['PATH'].split(os.path.pathsep): |
1303 | + path = '%s%s%s' % (dir, os.path.sep, exe) |
1304 | + if os.access(path, os.X_OK): |
1305 | + return path |
1306 | |
1307 | === added file 'VMBuilder/distro.py' |
1308 | --- VMBuilder/distro.py 1970-01-01 00:00:00 +0000 |
1309 | +++ VMBuilder/distro.py 2011-12-21 11:17:34 +0000 |
1310 | @@ -0,0 +1,99 @@ |
1311 | +# |
1312 | +# Uncomplicated VM Builder |
1313 | +# Copyright (C) 2007-2009 Canonical Ltd. |
1314 | +# |
1315 | +# See AUTHORS for list of contributors |
1316 | +# |
1317 | +# This program is free software: you can redistribute it and/or modify |
1318 | +# it under the terms of the GNU General Public License version 3, as |
1319 | +# published by the Free Software Foundation. |
1320 | +# |
1321 | +# This program is distributed in the hope that it will be useful, |
1322 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1323 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1324 | +# GNU General Public License for more details. |
1325 | +# |
1326 | +# You should have received a copy of the GNU General Public License |
1327 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1328 | +# |
1329 | +# Distro super class |
1330 | + |
1331 | +import logging |
1332 | +import os |
1333 | + |
1334 | +from VMBuilder.util import run_cmd, call_hooks |
1335 | +import VMBuilder.plugins |
1336 | + |
1337 | +class Context(VMBuilder.plugins.Plugin): |
1338 | + def __init__(self): |
1339 | + self._config = {} |
1340 | + super(Context, self).__init__(self) |
1341 | + self.plugins = [plugin_class(self) for plugin_class in self.plugin_classes] |
1342 | + self.plugins.sort(key=lambda x:x.priority) |
1343 | + self._cleanup_cbs = [] |
1344 | + self.hooks = {} |
1345 | + self.template_dirs = [os.path.expanduser('~/.vmbuilder/%s'), |
1346 | + os.path.dirname(__file__) + '/plugins/%s/templates', |
1347 | + '/etc/vmbuilder/%s'] |
1348 | + self.overwrite = False |
1349 | + |
1350 | + # Cleanup |
1351 | + def cleanup(self): |
1352 | + logging.info("Cleaning up") |
1353 | + while len(self._cleanup_cbs) > 0: |
1354 | + self._cleanup_cbs.pop(0)() |
1355 | + |
1356 | + def add_clean_cb(self, cb): |
1357 | + self._cleanup_cbs.insert(0, cb) |
1358 | + |
1359 | + def add_clean_cmd(self, *argv, **kwargs): |
1360 | + cb = lambda : run_cmd(*argv, **kwargs) |
1361 | + self.add_clean_cb(cb) |
1362 | + return cb |
1363 | + |
1364 | + def cancel_cleanup(self, cb): |
1365 | + try: |
1366 | + self._cleanup_cbs.remove(cb) |
1367 | + except ValueError: |
1368 | + # Wasn't in there. No worries. |
1369 | + pass |
1370 | + |
1371 | + # Hooks |
1372 | + def register_hook(self, hook_name, func): |
1373 | + self.hooks[hook_name] = self.hooks.get(hook_name, []) + [func] |
1374 | + |
1375 | + def call_hooks(self, *args, **kwargs): |
1376 | + try: |
1377 | + call_hooks(self, *args, **kwargs) |
1378 | + except Exception: |
1379 | + self.cleanup() |
1380 | + raise |
1381 | + |
1382 | +class Distro(Context): |
1383 | + def __init__(self): |
1384 | + self.plugin_classes = VMBuilder._distro_plugins |
1385 | + super(Distro, self).__init__() |
1386 | + |
1387 | + def set_chroot_dir(self, chroot_dir): |
1388 | + self.chroot_dir = chroot_dir |
1389 | + |
1390 | + def build_chroot(self): |
1391 | + self.call_hooks('preflight_check') |
1392 | + self.call_hooks('set_defaults') |
1393 | + self.call_hooks('bootstrap') |
1394 | + self.call_hooks('configure_os') |
1395 | + self.cleanup() |
1396 | + |
1397 | + def has_xen_support(self): |
1398 | + """Install the distro into destdir""" |
1399 | + raise NotImplemented('Distro subclasses need to implement the has_xen_support method') |
1400 | + |
1401 | + def install(self, destdir): |
1402 | + """Install the distro into destdir""" |
1403 | + raise NotImplemented('Distro subclasses need to implement the install method') |
1404 | + |
1405 | + def post_mount(self, fs): |
1406 | + """Called each time a filesystem is mounted to let the distro add things to the filesystem""" |
1407 | + |
1408 | + def install_vmbuilder_log(self, logfile): |
1409 | + """Let the distro copy the install logfile to the guest""" |
1410 | |
1411 | === added file 'VMBuilder/exception.py' |
1412 | --- VMBuilder/exception.py 1970-01-01 00:00:00 +0000 |
1413 | +++ VMBuilder/exception.py 2011-12-21 11:17:34 +0000 |
1414 | @@ -0,0 +1,27 @@ |
1415 | +# |
1416 | +# Uncomplicated VM Builder |
1417 | +# Copyright (C) 2007-2009 Canonical Ltd. |
1418 | +# |
1419 | +# See AUTHORS for list of contributors |
1420 | +# |
1421 | +# This program is free software: you can redistribute it and/or modify |
1422 | +# it under the terms of the GNU General Public License version 3, as |
1423 | +# published by the Free Software Foundation. |
1424 | +# |
1425 | +# This program is distributed in the hope that it will be useful, |
1426 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1427 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1428 | +# GNU General Public License for more details. |
1429 | +# |
1430 | +# You should have received a copy of the GNU General Public License |
1431 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1432 | +# |
1433 | +# Our very own exception |
1434 | + |
1435 | +class VMBuilderException(Exception): |
1436 | + """Something failed inside VMBuilder""" |
1437 | + pass |
1438 | + |
1439 | +class VMBuilderUserError(VMBuilderException): |
1440 | + """The user did something silly.""" |
1441 | + pass |
1442 | |
1443 | === added file 'VMBuilder/frontend.py' |
1444 | --- VMBuilder/frontend.py 1970-01-01 00:00:00 +0000 |
1445 | +++ VMBuilder/frontend.py 2011-12-21 11:17:34 +0000 |
1446 | @@ -0,0 +1,53 @@ |
1447 | +# |
1448 | +# Uncomplicated VM Builder |
1449 | +# Copyright (C) 2007-2009 Canonical Ltd. |
1450 | +# |
1451 | +# See AUTHORS for list of contributors |
1452 | +# |
1453 | +# This program is free software: you can redistribute it and/or modify |
1454 | +# it under the terms of the GNU General Public License version 3, as |
1455 | +# published by the Free Software Foundation. |
1456 | +# |
1457 | +# This program is distributed in the hope that it will be useful, |
1458 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1459 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1460 | +# GNU General Public License for more details. |
1461 | +# |
1462 | +# You should have received a copy of the GNU General Public License |
1463 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1464 | +# |
1465 | +# Frontend interface and classes |
1466 | + |
1467 | +import VMBuilder.plugins |
1468 | + |
1469 | +class Frontend(VMBuilder.plugins.Plugin): |
1470 | + def __init__(self): |
1471 | + self._setting_groups = [] |
1472 | + self._config = {} |
1473 | + self.context = self |
1474 | + |
1475 | +# self.settings = [] |
1476 | + |
1477 | +# def setting_group(self, help=None): |
1478 | +# return self.SettingsGroup(help) |
1479 | +# |
1480 | +# def add_setting_group(self, group): |
1481 | +# self.settings.append(group) |
1482 | +# |
1483 | +# def add_setting(self, **kwargs): |
1484 | +# self.settings.append(Setting(**kwargs)) |
1485 | +# |
1486 | +# setting_types = ['store', 'store'] |
1487 | +# class Setting(object): |
1488 | +# def __init__(self, **kwargs): |
1489 | +# self.shortarg = kwargs.get('shortarg', None) |
1490 | +# self.longarg = kwargs.get('shortarg', None) |
1491 | +# self.default = kwargs.get('default', None) |
1492 | +# self.help = kwargs.get('help', None) |
1493 | +# type = kwargs.get('type', 'store') |
1494 | +# if type not in setting_types: |
1495 | +# raise VMBuilderException("Invalid option type: %s" % type) |
1496 | +# |
1497 | +# class SettingsGroup(Setting): |
1498 | +# pass |
1499 | + |
1500 | |
1501 | === added file 'VMBuilder/hypervisor.py' |
1502 | --- VMBuilder/hypervisor.py 1970-01-01 00:00:00 +0000 |
1503 | +++ VMBuilder/hypervisor.py 2011-12-21 11:17:34 +0000 |
1504 | @@ -0,0 +1,121 @@ |
1505 | +# |
1506 | +# Uncomplicated VM Builder |
1507 | +# Copyright (C) 2007-2009 Canonical Ltd. |
1508 | +# |
1509 | +# See AUTHORS for list of contributors |
1510 | +# |
1511 | +# This program is free software: you can redistribute it and/or modify |
1512 | +# it under the terms of the GNU General Public License version 3, as |
1513 | +# published by the Free Software Foundation. |
1514 | +# |
1515 | +# This program is distributed in the hope that it will be useful, |
1516 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1517 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1518 | +# GNU General Public License for more details. |
1519 | +# |
1520 | +# You should have received a copy of the GNU General Public License |
1521 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1522 | +# |
1523 | +# Hypervisor super class |
1524 | + |
1525 | +import logging |
1526 | +import os |
1527 | +import VMBuilder.distro |
1528 | +import VMBuilder.disk |
1529 | +from VMBuilder.util import run_cmd, tmpdir |
1530 | + |
1531 | +STORAGE_DISK_IMAGE = 0 |
1532 | +STORAGE_FS_IMAGE = 1 |
1533 | + |
1534 | +class Hypervisor(VMBuilder.distro.Context): |
1535 | + preferred_storage = STORAGE_DISK_IMAGE |
1536 | + |
1537 | + def __init__(self, distro): |
1538 | + self.plugin_classes = VMBuilder._hypervisor_plugins |
1539 | + super(Hypervisor, self).__init__() |
1540 | + self.plugins += [distro] |
1541 | + self.distro = distro |
1542 | + self.filesystems = [] |
1543 | + self.disks = [] |
1544 | + self.nics = [] |
1545 | + |
1546 | + def add_filesystem(self, *args, **kwargs): |
1547 | + """Adds a filesystem to the virtual machine""" |
1548 | + from VMBuilder.disk import Filesystem |
1549 | + |
1550 | + fs = Filesystem(self, *args, **kwargs) |
1551 | + self.filesystems.append(fs) |
1552 | + return fs |
1553 | + |
1554 | + def add_disk(self, *args, **kwargs): |
1555 | + """Adds a disk image to the virtual machine""" |
1556 | + from VMBuilder.disk import Disk |
1557 | + |
1558 | + disk = Disk(self, *args, **kwargs) |
1559 | + self.disks.append(disk) |
1560 | + return disk |
1561 | + |
1562 | + def install_os(self): |
1563 | + self.nics = [self.NIC()] |
1564 | + self.call_hooks('preflight_check') |
1565 | + self.call_hooks('configure_networking', self.nics) |
1566 | + self.call_hooks('configure_mounting', self.disks, self.filesystems) |
1567 | + |
1568 | + self.chroot_dir = tmpdir() |
1569 | + self.call_hooks('mount_partitions', self.chroot_dir) |
1570 | + run_cmd('rsync', '-aHA', '%s/' % self.distro.chroot_dir, self.chroot_dir) |
1571 | + self.distro.set_chroot_dir(self.chroot_dir) |
1572 | + if self.needs_bootloader: |
1573 | + self.call_hooks('install_bootloader', self.chroot_dir, self.disks) |
1574 | + self.call_hooks('install_kernel', self.chroot_dir) |
1575 | + self.distro.call_hooks('post_install') |
1576 | + self.call_hooks('unmount_partitions') |
1577 | + os.rmdir(self.chroot_dir) |
1578 | + |
1579 | + def finalise(self, destdir): |
1580 | + self.call_hooks('convert', |
1581 | + self.preferred_storage == STORAGE_DISK_IMAGE and self.disks or self.filesystems, |
1582 | + destdir) |
1583 | + self.call_hooks('deploy', destdir) |
1584 | + |
1585 | + def mount_partitions(self, mntdir): |
1586 | + """Mounts all the vm's partitions and filesystems below .rootmnt""" |
1587 | + logging.info('Mounting target filesystems') |
1588 | + for fs in self.filesystems: |
1589 | + fs.create() |
1590 | + fs.mkfs() |
1591 | + for disk in self.disks: |
1592 | + disk.create() |
1593 | + disk.partition() |
1594 | + disk.map_partitions() |
1595 | + disk.mkfs() |
1596 | + fss = VMBuilder.disk.get_ordered_filesystems(self) |
1597 | + for fs in fss: |
1598 | + fs.mount(mntdir) |
1599 | + self.distro.post_mount(fs) |
1600 | + |
1601 | + def unmount_partitions(self): |
1602 | + """Unmounts all the vm's partitions and filesystems""" |
1603 | + logging.info('Unmounting target filesystem') |
1604 | + fss = VMBuilder.disk.get_ordered_filesystems(self) |
1605 | + fss.reverse() |
1606 | + for fs in fss: |
1607 | + fs.umount() |
1608 | + for disk in self.disks: |
1609 | + disk.unmap() |
1610 | + |
1611 | + def convert_disks(self, disks, destdir): |
1612 | + for disk in disks: |
1613 | + disk.convert(destdir, self.filetype) |
1614 | + |
1615 | + class NIC(object): |
1616 | + def __init__(self, type='dhcp', ip=None, network=None, netmask=None, |
1617 | + broadcast=None, dns=None, gateway=None): |
1618 | + self.type = type |
1619 | + self.ip = ip |
1620 | + self.network = network |
1621 | + self.netmask = netmask |
1622 | + self.broadcast = broadcast |
1623 | + self.dns = dns |
1624 | + self.gateway = gateway |
1625 | + |
1626 | |
1627 | === added file 'VMBuilder/log.py' |
1628 | --- VMBuilder/log.py 1970-01-01 00:00:00 +0000 |
1629 | +++ VMBuilder/log.py 2011-12-21 11:17:34 +0000 |
1630 | @@ -0,0 +1,36 @@ |
1631 | +# |
1632 | +# Uncomplicated VM Builder |
1633 | +# Copyright (C) 2007-2009 Canonical Ltd. |
1634 | +# |
1635 | +# See AUTHORS for list of contributors |
1636 | +# |
1637 | +# This program is free software: you can redistribute it and/or modify |
1638 | +# it under the terms of the GNU General Public License version 3, as |
1639 | +# published by the Free Software Foundation. |
1640 | +# |
1641 | +# This program is distributed in the hope that it will be useful, |
1642 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1643 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1644 | +# GNU General Public License for more details. |
1645 | +# |
1646 | +# You should have received a copy of the GNU General Public License |
1647 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1648 | +# |
1649 | +# Logging |
1650 | + |
1651 | +import logging |
1652 | +import os |
1653 | +import tempfile |
1654 | + |
1655 | +format = '%(asctime)s %(levelname)-8s: %(message)s' |
1656 | + |
1657 | +fd, logfile = tempfile.mkstemp() |
1658 | + |
1659 | +# Log everything to the logfile |
1660 | +logging.basicConfig(format=format, level=logging.DEBUG, datefmt='%Y-%m-%d %H:%M', stream=os.fdopen(fd, 'a+'), filemode='w') |
1661 | + |
1662 | +console = logging.StreamHandler() |
1663 | +console.setLevel(logging.INFO) |
1664 | +console.setFormatter(logging.Formatter(format)) |
1665 | +logging.getLogger('').addHandler(console) |
1666 | + |
1667 | |
1668 | === added directory 'VMBuilder/plugins' |
1669 | === added file 'VMBuilder/plugins/__init__.py' |
1670 | --- VMBuilder/plugins/__init__.py 1970-01-01 00:00:00 +0000 |
1671 | +++ VMBuilder/plugins/__init__.py 2011-12-21 11:17:34 +0000 |
1672 | @@ -0,0 +1,329 @@ |
1673 | +# |
1674 | +# Uncomplicated VM Builder |
1675 | +# Copyright (C) 2007-2009 Canonical Ltd. |
1676 | +# |
1677 | +# See AUTHORS for list of contributors |
1678 | +# |
1679 | +# This program is free software: you can redistribute it and/or modify |
1680 | +# it under the terms of the GNU General Public License version 3, as |
1681 | +# published by the Free Software Foundation. |
1682 | +# |
1683 | +# This program is distributed in the hope that it will be useful, |
1684 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1685 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1686 | +# GNU General Public License for more details. |
1687 | +# |
1688 | +# You should have received a copy of the GNU General Public License |
1689 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1690 | +# |
1691 | +import os |
1692 | +import re |
1693 | +import shutil |
1694 | + |
1695 | +import VMBuilder |
1696 | +import VMBuilder.util as util |
1697 | +from VMBuilder.exception import VMBuilderException |
1698 | + |
1699 | +def load_plugins(): |
1700 | + for plugin in find_plugins(): |
1701 | + exec "import %s" % plugin |
1702 | + |
1703 | +def find_plugins(): |
1704 | + retval = [] |
1705 | + for plugin_dir in __path__: |
1706 | + for p in os.listdir(plugin_dir): |
1707 | + path = '%s/%s' % (plugin_dir, p) |
1708 | + if os.path.isdir(path) and os.path.isfile('%s/__init__.py' % path): |
1709 | + retval.append("VMBuilder.plugins.%s" % p) |
1710 | + return retval |
1711 | + |
1712 | +class Plugin(object): |
1713 | + priority = 10 |
1714 | + |
1715 | + def __init__(self, context): |
1716 | + self.context = context |
1717 | + self._setting_groups = [] |
1718 | + self.register_options() |
1719 | + |
1720 | + def register_options(self): |
1721 | + pass |
1722 | + |
1723 | + def set_defaults(self): |
1724 | + pass |
1725 | + |
1726 | + def preflight_check(self): |
1727 | + """ |
1728 | + Override this method with checks for anything that might cause the VM creation to fail |
1729 | + |
1730 | + raise an exception if you can see already that this won't work |
1731 | + """ |
1732 | + pass |
1733 | + |
1734 | + def post_install(self): |
1735 | + """ |
1736 | + This is called just after the distro is installed, before it gets copied to the fs images. |
1737 | + """ |
1738 | + pass |
1739 | + |
1740 | + def install_file(self, path, contents=None, source=None, mode=None): |
1741 | + fullpath = '%s%s' % (self.context.chroot_dir, path) |
1742 | + if not os.path.isdir(os.path.dirname(fullpath)): |
1743 | + os.makedirs(os.path.dirname(fullpath)) |
1744 | + if source and not contents: |
1745 | + shutil.copy(source, fullpath) |
1746 | + else: |
1747 | + fp = open(fullpath, 'w') |
1748 | + fp.write(contents) |
1749 | + fp.close() |
1750 | + if mode: |
1751 | + os.chmod(fullpath, mode) |
1752 | + return fullpath |
1753 | + |
1754 | + def install_from_template(self, path, tmplname, context=None, mode=None): |
1755 | + return self.install_file(path, VMBuilder.util.render_template(self.__module__.split('.')[2], self.context, tmplname, context), mode=mode) |
1756 | + |
1757 | + def run_in_target(self, *args, **kwargs): |
1758 | + return util.run_cmd('chroot', self.chroot_dir, *args, **kwargs) |
1759 | + |
1760 | + def call_hooks(self, *args, **kwargs): |
1761 | + return util.call_hooks(self.context, *args, **kwargs) |
1762 | + |
1763 | + # Settings |
1764 | + class SettingGroup(object): |
1765 | + def __init__(self, plugin, context, name): |
1766 | + # The plugin that owns this setting |
1767 | + self.plugin = plugin |
1768 | + # The VM object |
1769 | + self.context = context |
1770 | + # Name of the Setting Group |
1771 | + self.name = name |
1772 | + # A list of Setting objects |
1773 | + self._settings = [] |
1774 | + |
1775 | + def add_setting(self, *args, **kwargs): |
1776 | + # kwarg['type'] is used to determine which type of Setting object to create |
1777 | + # but we don't want to pass it on to its __init__. |
1778 | + if 'type' in kwargs: |
1779 | + type = kwargs['type'] |
1780 | + del kwargs['type'] |
1781 | + else: |
1782 | + type = 'str' |
1783 | + |
1784 | + if type == 'str': |
1785 | + setting = self.plugin.StringSetting(self, *args, **kwargs) |
1786 | + elif type == 'bool': |
1787 | + setting = self.plugin.BooleanSetting(self, *args, **kwargs) |
1788 | + elif type == 'list': |
1789 | + setting = self.plugin.ListSetting(self, *args, **kwargs) |
1790 | + elif type == 'int': |
1791 | + setting = self.plugin.IntSetting(self, *args, **kwargs) |
1792 | + else: |
1793 | + raise VMBuilderException("Unknown setting type: '%s' (Plugin: '%s', Setting group: '%s', Setting: '%s')" % |
1794 | + (type, |
1795 | + self.plugin.__module__, |
1796 | + self.name, |
1797 | + args[0])) |
1798 | + self._settings.append(setting) |
1799 | + |
1800 | + class Setting(object): |
1801 | + default = None |
1802 | + |
1803 | + def __init__(self, setting_group, name, metavar=None, help=None, extra_args=None, valid_options=None, action=None, **kwargs): |
1804 | + # The Setting Group object that owns this Setting |
1805 | + self.setting_group = setting_group |
1806 | + # The name if the setting |
1807 | + name_regex = '[a-z0-9-]+$' |
1808 | + if not re.match(name_regex, name): |
1809 | + raise VMBuilderException('Invalid name for Setting: %s. Must match regex: %s' % (name, name_regex)) |
1810 | + else: |
1811 | + self.name = name |
1812 | + |
1813 | + self.default = kwargs.get('default', self.default) |
1814 | + self.help = help |
1815 | + # Alternate names (for the CLI) |
1816 | + self.extra_args = extra_args or [] |
1817 | + self.metavar = metavar |
1818 | + self.value = None |
1819 | + self.value_set = False |
1820 | + self.valid_options = valid_options |
1821 | + |
1822 | + if self.name in self.setting_group.context._config: |
1823 | + raise VMBuilderException("Setting named %s already exists. Previous definition in %s/%s/%s." % |
1824 | + (self.name, |
1825 | + self.setting_group.plugin.__name__, |
1826 | + self.setting_group.plugin._config[self.name].setting_group.name, |
1827 | + self.setting_group.plugin._config[self.name].name)) |
1828 | + |
1829 | + self.setting_group.context._config[self.name] = self |
1830 | + |
1831 | + def get_value(self): |
1832 | + """ |
1833 | + If a value has previously been set, return it. |
1834 | + If not, return the default value. |
1835 | + """ |
1836 | + |
1837 | + if self.value_set: |
1838 | + return self.value |
1839 | + else: |
1840 | + return self.default |
1841 | + |
1842 | + def do_check_value(self, value): |
1843 | + """ |
1844 | + Checks the value's validity. |
1845 | + """ |
1846 | + if self.valid_options is not None: |
1847 | + if value not in self.valid_options: |
1848 | + raise VMBuilderException('%r is not a valid option for %s. Valid options are: %s' % (value, self.name, ' '.join(self.valid_options))) |
1849 | + else: |
1850 | + return self.check_value(value) |
1851 | + |
1852 | + def get_valid_options(self): |
1853 | + return self.valid_options |
1854 | + |
1855 | + def set_valid_options(self, valid_options): |
1856 | + """ |
1857 | + Set the list of valid options for this setting. |
1858 | + """ |
1859 | + if not type(valid_options) == list and valid_options is not None: |
1860 | + raise VMBuilderException('set_valid_options only accepts lists or None') |
1861 | + if valid_options: |
1862 | + for option in valid_options: |
1863 | + self.check_value(option) |
1864 | + self.valid_options = valid_options |
1865 | + |
1866 | + def get_default(self): |
1867 | + """ |
1868 | + Return the default value. |
1869 | + """ |
1870 | + return self.default |
1871 | + |
1872 | + def set_default(self, value): |
1873 | + """ |
1874 | + Set a new default value. |
1875 | + """ |
1876 | + value = self.do_check_value(value) |
1877 | + self.default = value |
1878 | + |
1879 | + def set_value_fuzzy(self, value): |
1880 | + """ |
1881 | + Set new value. |
1882 | + |
1883 | + Contrary to L{set_value}, L{set_value_fuzzy} will attempt |
1884 | + to turn L{value} into the target type. E.g. turning '10' |
1885 | + into 10, "main,universe,multiverse" into ['main', |
1886 | + 'universe', 'multiverse'] |
1887 | + """ |
1888 | + return self.set_value(value) |
1889 | + |
1890 | + def set_value(self, value): |
1891 | + """ |
1892 | + Set a new value. |
1893 | + """ |
1894 | + value = self.do_check_value(value) |
1895 | + self.value = value |
1896 | + self.value_set = True |
1897 | + |
1898 | + class ListSetting(Setting): |
1899 | + def __init__(self, *args, **kwargs): |
1900 | + self.default = [] |
1901 | + super(Plugin.ListSetting, self).__init__(*args, **kwargs) |
1902 | + |
1903 | + def set_value_fuzzy(self, value): |
1904 | + if len(value) == 1 and type(value[0]) == str: |
1905 | + value = value[0] |
1906 | + if type(value) == str: |
1907 | + if value == '': |
1908 | + return self.set_value([]) |
1909 | + for sep in [':', ',']: |
1910 | + if sep in value: |
1911 | + split_regex = re.compile("\s*%s\s*" % sep) |
1912 | + return self.set_value(split_regex.split(value)) |
1913 | + value = [value] |
1914 | + self.set_value(value) |
1915 | + return self.set_value(value) |
1916 | + |
1917 | + def check_value(self, value): |
1918 | + if not type(value) == list: |
1919 | + raise VMBuilderException('%r is type %s, expected list.' % (value, type(value))) |
1920 | + return value |
1921 | + |
1922 | + class IntSetting(Setting): |
1923 | + def set_value_fuzzy(self, value): |
1924 | + if type(value) != int: |
1925 | + try: |
1926 | + value = int(value) |
1927 | + except ValueError: |
1928 | + raise VMBuilderException('Could not interpret %r as an int.' % (value,)) |
1929 | + return self.set_value(value) |
1930 | + |
1931 | + def check_value(self, value): |
1932 | + if not type(value) == int: |
1933 | + raise VMBuilderException('%r is type %s, expected int.' % (value, type(value))) |
1934 | + return value |
1935 | + |
1936 | + class BooleanSetting(Setting): |
1937 | + def set_value_fuzzy(self, value): |
1938 | + if type(value) == str: |
1939 | + if value.lower() in ['no', 'false', 'off', '0']: |
1940 | + value = False |
1941 | + elif value.lower() in ['yes', 'true', 'on', '1']: |
1942 | + value = True |
1943 | + else: |
1944 | + raise VMBuilderException('Could not interpret %r as a boolean value.' % (value,)) |
1945 | + return self.set_value(value) |
1946 | + |
1947 | + def check_value(self, value): |
1948 | + if not type(value) == bool: |
1949 | + raise VMBuilderException('%r is type %s, expected bool.' % (value, type(value))) |
1950 | + return value |
1951 | + |
1952 | + class StringSetting(Setting): |
1953 | + def check_value(self, value): |
1954 | + if not type(value) == str: |
1955 | + raise VMBuilderException('%r is type %s, expected str.' % (value, type(value))) |
1956 | + return value |
1957 | + |
1958 | + def setting_group(self, name): |
1959 | + setting_group = self.SettingGroup(self, self.context, name) |
1960 | + self._setting_groups.append(setting_group) |
1961 | + return setting_group |
1962 | + |
1963 | + def has_setting(self, name): |
1964 | + return name in self.context._config |
1965 | + |
1966 | + def get_setting(self, name): |
1967 | + if not name in self.context._config: |
1968 | + raise VMBuilderException('Unknown config key: %s' % name) |
1969 | + return self.context._config[name].get_value() |
1970 | + |
1971 | + def set_setting_fuzzy(self, name, value): |
1972 | + if not name in self.context._config: |
1973 | + raise VMBuilderException('Unknown config key: %s' % name) |
1974 | +# print 'fuzzy setting of %s: %r' % (name, value) |
1975 | + self.context._config[name].set_value_fuzzy(value) |
1976 | + |
1977 | + def set_setting(self, name, value): |
1978 | + if not name in self.context._config: |
1979 | + raise VMBuilderException('Unknown config key: %s' % name) |
1980 | + self.context._config[name].set_value(value) |
1981 | + |
1982 | + def set_setting_default(self, name, value): |
1983 | + if not name in self.context._config: |
1984 | + raise VMBuilderException('Unknown config key: %s' % name) |
1985 | + self.context._config[name].set_default(value) |
1986 | + |
1987 | + def get_setting_default(self, name): |
1988 | + if not name in self.context._config: |
1989 | + raise VMBuilderException('Unknown config key: %s' % name) |
1990 | + return self.context._config[name].get_default() |
1991 | + |
1992 | + def get_setting_valid_options(self, name): |
1993 | + if not name in self.context._config: |
1994 | + raise VMBuilderException('Unknown config key: %s' % name) |
1995 | + return self.context._config[name].get_valid_options() |
1996 | + |
1997 | + def set_setting_valid_options(self, name, valid_options): |
1998 | + if not name in self.context._config: |
1999 | + raise VMBuilderException('Unknown config key: %s' % name) |
2000 | + self.context._config[name].set_valid_options(valid_options) |
2001 | + |
2002 | |
2003 | === added directory 'VMBuilder/plugins/ec2' |
2004 | === added file 'VMBuilder/plugins/ec2/__init__.py' |
2005 | --- VMBuilder/plugins/ec2/__init__.py 1970-01-01 00:00:00 +0000 |
2006 | +++ VMBuilder/plugins/ec2/__init__.py 2011-12-21 11:17:34 +0000 |
2007 | @@ -0,0 +1,150 @@ |
2008 | +# |
2009 | +# Uncomplicated VM Builder |
2010 | +# Copyright (C) 2007-2009 Canonical Ltd. |
2011 | +# |
2012 | +# See AUTHORS for list of contributors |
2013 | +# |
2014 | +# This program is free software: you can redistribute it and/or modify |
2015 | +# it under the terms of the GNU General Public License version 3, as |
2016 | +# published by the Free Software Foundation. |
2017 | +# |
2018 | +# This program is distributed in the hope that it will be useful, |
2019 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2020 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2021 | +# GNU General Public License for more details. |
2022 | +# |
2023 | +# You should have received a copy of the GNU General Public License |
2024 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2025 | +# |
2026 | +import VMBuilder |
2027 | +from VMBuilder import register_distro_plugin, register_hypervisor_plugin, Plugin, VMBuilderUserError, VMBuilderException |
2028 | +from VMBuilder.util import run_cmd |
2029 | +import logging |
2030 | +import os |
2031 | + |
2032 | +class EC2(Plugin): |
2033 | + name = 'EC2 integration' |
2034 | + |
2035 | + def register_options(self): |
2036 | + # Don't pretend like we can do EC2 |
2037 | + if not isinstance(self.context.hypervisor, VMBuilder.plugins.xen.Xen): |
2038 | + return |
2039 | + group = self.context.setting_group('EC2 integation') |
2040 | + group.add_option('--ec2', action='store_true', help='Build for EC2') |
2041 | + group.add_option('--ec2-name','--ec2-prefix', metavar='EC2_NAME', help='Name for the EC2 image.') |
2042 | + group.add_option('--ec2-cert', metavar='CERTFILE', help='PEM encoded public certificate for EC2.') |
2043 | + group.add_option('--ec2-key', metavar='KEYFILE', help='PEM encoded private key for EC2.') |
2044 | + group.add_option('--ec2-user', metavar='AWS_ACCOUNT', help='EC2 user ID (a.k.a. AWS account number, not AWS access key ID).') |
2045 | + group.add_option('--ec2-bucket', metavar='BUCKET', help='S3 bucket to hold the AMI.') |
2046 | + group.add_option('--ec2-access-key', metavar='ACCESS_ID', help='AWS access key ID.') |
2047 | + group.add_option('--ec2-secret-key', metavar='SECRET_ID', help='AWS secret access key.') |
2048 | + group.add_option('--ec2-kernel','--ec2-aki', metavar='AKI', help='EC2 AKI (kernel) to use.') |
2049 | + group.add_option('--ec2-ramdisk','--ec2-ari', metavar='ARI', help='EC2 ARI (ramdisk) to use.') |
2050 | + group.add_option('--ec2-version', metavar='EC2_VER', help='Specify the EC2 image version.') |
2051 | + group.add_option('--ec2-landscape', action='store_true', help='Install landscape client support') |
2052 | + group.add_option('--ec2-bundle', action='store_true', help='Bundle the instance') |
2053 | + group.add_option('--ec2-upload', action='store_true', help='Upload the instance') |
2054 | + group.add_option('--ec2-register', action='store_true', help='Register the instance') |
2055 | + self.context.register_setting_group(group) |
2056 | + |
2057 | + def preflight_check(self): |
2058 | + if not getattr(self.vm, 'ec2', False): |
2059 | + return True |
2060 | + |
2061 | + if not self.context.hypervisor.name == 'Xen': |
2062 | + raise VMBuilderUserError('When building for EC2 you must use the xen hypervisor.') |
2063 | + |
2064 | + if self.context.ec2_bundle: |
2065 | + try: |
2066 | + run_cmd('ec2-ami-tools-version') |
2067 | + except VMBuilderException, e: |
2068 | + raise VMBuilderUserError('You need to have the Amazon EC2 AMI tools installed') |
2069 | + |
2070 | + if not self.context.ec2_name: |
2071 | + raise VMBuilderUserError('When building for EC2 you must supply the name for the image.') |
2072 | + |
2073 | + if not self.context.ec2_cert: |
2074 | + if "EC2_CERT" in os.environ: |
2075 | + self.context.ec2_cert = os.environ["EC2_CERT"] |
2076 | + else: |
2077 | + raise VMBuilderUserError('When building for EC2 you must provide your PEM encoded public key certificate') |
2078 | + |
2079 | + if not self.context.ec2_key: |
2080 | + if "EC2_PRIVATE_KEY" in os.environ: |
2081 | + self.context.ec2_key = os.environ["EC2_PRIVATE_KEY"] |
2082 | + else: |
2083 | + raise VMBuilderUserError('When building for EC2 you must provide your PEM encoded private key file') |
2084 | + |
2085 | + if not self.context.ec2_user: |
2086 | + raise VMBuilderUserError('When building for EC2 you must provide your EC2 user ID (your AWS account number, not your AWS access key ID)') |
2087 | + |
2088 | + if not self.context.ec2_kernel: |
2089 | + self.context.ec2_kernel = self.vm.distro.get_ec2_kernel() |
2090 | + logging.debug('%s - to be used for AKI.' %(self.context.ec2_kernel)) |
2091 | + |
2092 | + if not self.context.ec2_ramdisk: |
2093 | + self.context.ec2_ramdisk = self.vm.distro.ec2_ramdisk_id() |
2094 | + logging.debug('%s - to be use for the ARI.' %(self.context.ec2_ramdisk)) |
2095 | + |
2096 | + if self.context.ec2_upload: |
2097 | + if not self.context.ec2_bucket: |
2098 | + raise VMBuilderUserError('When building for EC2 you must provide an S3 bucket to hold the AMI') |
2099 | + |
2100 | + if not self.context.ec2_access_key: |
2101 | + raise VMBuilderUserError('When building for EC2 you must provide your AWS access key ID.') |
2102 | + |
2103 | + if not self.context.ec2_secret_key: |
2104 | + raise VMBuilderUserError('When building for EC2 you must provide your AWS secret access key.') |
2105 | + |
2106 | + if not self.context.ec2_version: |
2107 | + raise VMBuilderUserError('When building for EC2 you must provide version info.') |
2108 | + |
2109 | + if not self.context.addpkg: |
2110 | + self.context.addpkg = [] |
2111 | + |
2112 | + if self.context.ec2_landscape: |
2113 | + logging.info('Installing landscape support') |
2114 | + self.context.addpkg += ['landscape-client'] |
2115 | + |
2116 | + def post_install(self): |
2117 | + if not getattr(self.vm, 'ec2', False): |
2118 | + return |
2119 | + |
2120 | + logging.info("Running ec2 postinstall") |
2121 | + self.install_from_template('/etc/ec2_version', 'ec2_version', { 'version' : self.context.ec2_version } ) |
2122 | + self.install_from_template('/etc/ssh/sshd_config', 'sshd_config') |
2123 | + self.install_from_template('/etc/sudoers', 'sudoers') |
2124 | + |
2125 | + if self.context.ec2_landscape: |
2126 | + self.install_from_template('/etc/default/landscape-client', 'landscape_client') |
2127 | + |
2128 | + self.context.distro.disable_hwclock_access() |
2129 | + |
2130 | + def deploy(self): |
2131 | + if not getattr(self.vm, 'ec2', False): |
2132 | + return False |
2133 | + |
2134 | + if self.context.ec2_bundle: |
2135 | + logging.info("Building EC2 bundle") |
2136 | + bundle_cmdline = ['ec2-bundle-image', '--image', self.context.filesystems[0].filename, '--cert', self.vm.ec2_cert, '--privatekey', self.vm.ec2_key, '--user', self.vm.ec2_user, '--prefix', self.vm.ec2_name, '-r', ['i386', 'x86_64'][self.vm.arch == 'amd64'], '-d', self.vm.workdir, '--kernel', self.vm.ec2_kernel, '--ramdisk', self.vm.ec2_ramdisk] |
2137 | + run_cmd(*bundle_cmdline) |
2138 | + |
2139 | + manifest = '%s/%s.manifest.xml' % (self.context.workdir, self.vm.ec2_name) |
2140 | + if self.context.ec2_upload: |
2141 | + logging.info("Uploading EC2 bundle") |
2142 | + upload_cmdline = ['ec2-upload-bundle', '--retry', '--manifest', manifest, '--bucket', self.context.ec2_bucket, '--access-key', self.vm.ec2_access_key, '--secret-key', self.vm.ec2_secret_key] |
2143 | + run_cmd(*upload_cmdline) |
2144 | + |
2145 | + if self.context.ec2_register: |
2146 | + from boto.ec2.connection import EC2Connection |
2147 | + conn = EC2Connection(self.context.ec2_access_key, self.vm.ec2_secret_key) |
2148 | + amiid = conn.register_image('%s/%s.manifest.xml' % (self.context.ec2_bucket, self.vm.ec2_name)) |
2149 | + print 'Image registered as %s' % amiid |
2150 | + else: |
2151 | + self.context.result_files.append(manifest) |
2152 | + else: |
2153 | + self.context.result_files.append(self.vm.filesystems[0].filename) |
2154 | + |
2155 | + return True |
2156 | + |
2157 | +#register_plugin(EC2) |
2158 | |
2159 | === added directory 'VMBuilder/plugins/ec2/templates' |
2160 | === added file 'VMBuilder/plugins/ec2/templates/ec2_version.tmpl' |
2161 | --- VMBuilder/plugins/ec2/templates/ec2_version.tmpl 1970-01-01 00:00:00 +0000 |
2162 | +++ VMBuilder/plugins/ec2/templates/ec2_version.tmpl 2011-12-21 11:17:34 +0000 |
2163 | @@ -0,0 +1,1 @@ |
2164 | +$version |
2165 | |
2166 | === added file 'VMBuilder/plugins/ec2/templates/landscape_client.tmpl' |
2167 | --- VMBuilder/plugins/ec2/templates/landscape_client.tmpl 1970-01-01 00:00:00 +0000 |
2168 | +++ VMBuilder/plugins/ec2/templates/landscape_client.tmpl 2011-12-21 11:17:34 +0000 |
2169 | @@ -0,0 +1,1 @@ |
2170 | +CLOUD=1 |
2171 | |
2172 | === added file 'VMBuilder/plugins/ec2/templates/sshd_config.tmpl' |
2173 | --- VMBuilder/plugins/ec2/templates/sshd_config.tmpl 1970-01-01 00:00:00 +0000 |
2174 | +++ VMBuilder/plugins/ec2/templates/sshd_config.tmpl 2011-12-21 11:17:34 +0000 |
2175 | @@ -0,0 +1,77 @@ |
2176 | +# Package generated configuration file |
2177 | +# See the sshd(8) manpage for details |
2178 | + |
2179 | +# What ports, IPs and protocols we listen for |
2180 | +Port 22 |
2181 | +# Use these options to restrict which interfaces/protocols sshd will bind to |
2182 | +#ListenAddress :: |
2183 | +#ListenAddress 0.0.0.0 |
2184 | +Protocol 2 |
2185 | +# HostKeys for protocol version 2 |
2186 | +HostKey /etc/ssh/ssh_host_rsa_key |
2187 | +HostKey /etc/ssh/ssh_host_dsa_key |
2188 | +#Privilege Separation is turned on for security |
2189 | +UsePrivilegeSeparation yes |
2190 | + |
2191 | +# Lifetime and size of ephemeral version 1 server key |
2192 | +KeyRegenerationInterval 3600 |
2193 | +ServerKeyBits 768 |
2194 | + |
2195 | +# Logging |
2196 | +SyslogFacility AUTH |
2197 | +LogLevel INFO |
2198 | + |
2199 | +# Authentication: |
2200 | +LoginGraceTime 120 |
2201 | +PermitRootLogin yes |
2202 | +StrictModes yes |
2203 | + |
2204 | +RSAAuthentication yes |
2205 | +PubkeyAuthentication yes |
2206 | +#AuthorizedKeysFile %h/.ssh/authorized_keys |
2207 | + |
2208 | +# Don't read the user's ~/.rhosts and ~/.shosts files |
2209 | +IgnoreRhosts yes |
2210 | +# For this to work you will also need host keys in /etc/ssh_known_hosts |
2211 | +RhostsRSAAuthentication no |
2212 | +# similar for protocol version 2 |
2213 | +HostbasedAuthentication no |
2214 | +# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication |
2215 | +#IgnoreUserKnownHosts yes |
2216 | + |
2217 | +# To enable empty passwords, change to yes (NOT RECOMMENDED) |
2218 | +PermitEmptyPasswords no |
2219 | + |
2220 | +# Change to yes to enable challenge-response passwords (beware issues with |
2221 | +# some PAM modules and threads) |
2222 | +ChallengeResponseAuthentication no |
2223 | + |
2224 | +# Change to no to disable tunnelled clear text passwords |
2225 | +PasswordAuthentication no |
2226 | + |
2227 | +# Kerberos options |
2228 | +#KerberosAuthentication no |
2229 | +#KerberosGetAFSToken no |
2230 | +#KerberosOrLocalPasswd yes |
2231 | +#KerberosTicketCleanup yes |
2232 | + |
2233 | +# GSSAPI options |
2234 | +#GSSAPIAuthentication no |
2235 | +#GSSAPICleanupCredentials yes |
2236 | + |
2237 | +X11Forwarding yes |
2238 | +X11DisplayOffset 10 |
2239 | +PrintMotd no |
2240 | +PrintLastLog yes |
2241 | +TCPKeepAlive yes |
2242 | +#UseLogin no |
2243 | + |
2244 | +#MaxStartups 10:30:60 |
2245 | +#Banner /etc/issue.net |
2246 | + |
2247 | +# Allow client to pass locale environment variables |
2248 | +AcceptEnv LANG LC_* |
2249 | + |
2250 | +Subsystem sftp /usr/lib/openssh/sftp-server |
2251 | + |
2252 | +UsePAM yes |
2253 | |
2254 | === added file 'VMBuilder/plugins/ec2/templates/sudoers.tmpl' |
2255 | --- VMBuilder/plugins/ec2/templates/sudoers.tmpl 1970-01-01 00:00:00 +0000 |
2256 | +++ VMBuilder/plugins/ec2/templates/sudoers.tmpl 2011-12-21 11:17:34 +0000 |
2257 | @@ -0,0 +1,29 @@ |
2258 | +# /etc/sudoers |
2259 | +# |
2260 | +# This file MUST be edited with the 'visudo' command as root. |
2261 | +# |
2262 | +# See the man page for details on how to write a sudoers file. |
2263 | +# |
2264 | + |
2265 | +Defaults env_reset |
2266 | + |
2267 | +# Host alias specification |
2268 | + |
2269 | +# User alias specification |
2270 | + |
2271 | +# Cmnd alias specification |
2272 | + |
2273 | +# User privilege specification |
2274 | +root ALL=(ALL) ALL |
2275 | + |
2276 | +# Uncomment to allow members of group sudo to not need a password |
2277 | +# (Note that later entries override this, so you might need to move |
2278 | +# it further down) |
2279 | +# %sudo ALL=NOPASSWD: ALL |
2280 | + |
2281 | +# Members of the admin group may gain root privileges |
2282 | +%admin ALL=(ALL) ALL |
2283 | + |
2284 | +# ubuntu user is default user in ec2-images. |
2285 | +# It needs passwordless sudo functionality. |
2286 | +ubuntu ALL=(ALL) NOPASSWD:ALL |
2287 | |
2288 | === added directory 'VMBuilder/plugins/firstscripts' |
2289 | === added file 'VMBuilder/plugins/firstscripts/__init__.py' |
2290 | --- VMBuilder/plugins/firstscripts/__init__.py 1970-01-01 00:00:00 +0000 |
2291 | +++ VMBuilder/plugins/firstscripts/__init__.py 2011-12-21 11:17:34 +0000 |
2292 | @@ -0,0 +1,65 @@ |
2293 | +# |
2294 | +# Uncomplicated VM Builder |
2295 | +# Copyright (C) 2007-2010 Canonical Ltd. |
2296 | +# |
2297 | +# See AUTHORS for list of contributors |
2298 | +# |
2299 | +# This program is free software: you can redistribute it and/or modify |
2300 | +# it under the terms of the GNU General Public License version 3, as |
2301 | +# published by the Free Software Foundation. |
2302 | +# |
2303 | +# This program is distributed in the hope that it will be useful, |
2304 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2305 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2306 | +# GNU General Public License for more details. |
2307 | +# |
2308 | +# You should have received a copy of the GNU General Public License |
2309 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2310 | +# |
2311 | +from VMBuilder import register_distro_plugin, Plugin, VMBuilderUserError |
2312 | + |
2313 | +import logging |
2314 | +import os |
2315 | + |
2316 | +class Firstscripts(Plugin): |
2317 | + """ |
2318 | + Plugin to provide --firstboot and --firstlogin scripts capabilities |
2319 | + """ |
2320 | + name = 'First-Scripts plugin' |
2321 | + |
2322 | + def register_options(self): |
2323 | + group = self.setting_group('Scripts') |
2324 | + group.add_setting('firstboot', metavar='PATH', help='Specify a script that will be copied into the guest and executed the first time the machine boots. This script must not be interactive.') |
2325 | + group.add_setting('firstlogin', metavar='PATH', help='Specify a script that will be copied into the guest and will be executed the first time the user logs in. This script can be interactive.') |
2326 | + |
2327 | + def preflight_check(self): |
2328 | + firstboot = self.context.get_setting('firstboot') |
2329 | + if firstboot: |
2330 | + logging.debug("Checking if firstboot script %s exists" % (firstboot,)) |
2331 | + if not(os.path.isfile(firstboot) and firstboot.startswith('/')): |
2332 | + raise VMBuilderUserError('The path to the first-boot script is invalid: %s. Make sure you are providing a full path.' % firstboot) |
2333 | + |
2334 | + firstlogin = self.context.get_setting('firstlogin') |
2335 | + if firstlogin: |
2336 | + logging.debug("Checking if first login script %s exists" % (firstlogin,)) |
2337 | + if not(os.path.isfile(firstlogin) and firstlogin.startswith('/')): |
2338 | + raise VMBuilderUserError('The path to the first-login script is invalid: %s. Make sure you are providing a full path.' % firstlogin) |
2339 | + |
2340 | + def post_install(self): |
2341 | + firstboot = self.context.get_setting('firstboot') |
2342 | + if firstboot: |
2343 | + logging.debug("Installing firstboot script %s" % (firstboot,)) |
2344 | + self.context.install_file('/root/firstboot.sh', source=firstboot, mode=0700) |
2345 | + os.rename('%s/etc/rc.local' % self.context.chroot_dir, '%s/etc/rc.local.orig' % self.context.chroot_dir) |
2346 | + self.install_from_template('/etc/rc.local', 'firstbootrc', mode=0755) |
2347 | + |
2348 | + firstlogin = self.context.get_setting('firstlogin') |
2349 | + if firstlogin: |
2350 | + logging.debug("Installing first login script %s" % (firstlogin,)) |
2351 | + self.context.install_file('/root/firstlogin.sh', source=firstlogin, mode=0755) |
2352 | + os.rename('%s/etc/bash.bashrc' % self.context.chroot_dir, '%s/etc/bash.bashrc.orig' % self.context.chroot_dir) |
2353 | + self.install_from_template('/etc/bash.bashrc', 'firstloginrc') |
2354 | + |
2355 | + return True |
2356 | + |
2357 | +register_distro_plugin(Firstscripts) |
2358 | |
2359 | === added directory 'VMBuilder/plugins/firstscripts/templates' |
2360 | === added file 'VMBuilder/plugins/firstscripts/templates/firstbootrc.tmpl' |
2361 | --- VMBuilder/plugins/firstscripts/templates/firstbootrc.tmpl 1970-01-01 00:00:00 +0000 |
2362 | +++ VMBuilder/plugins/firstscripts/templates/firstbootrc.tmpl 2011-12-21 11:17:34 +0000 |
2363 | @@ -0,0 +1,10 @@ |
2364 | +#!/bin/sh -e |
2365 | +#execute firstboot.sh only once |
2366 | +if [ ! -e /root/firstboot_done ]; then |
2367 | + if [ -e /root/firstboot.sh ]; then |
2368 | + /root/firstboot.sh |
2369 | + fi |
2370 | + touch /root/firstboot_done |
2371 | +fi |
2372 | +exit 0 |
2373 | + |
2374 | |
2375 | === added file 'VMBuilder/plugins/firstscripts/templates/firstloginrc.tmpl' |
2376 | --- VMBuilder/plugins/firstscripts/templates/firstloginrc.tmpl 1970-01-01 00:00:00 +0000 |
2377 | +++ VMBuilder/plugins/firstscripts/templates/firstloginrc.tmpl 2011-12-21 11:17:34 +0000 |
2378 | @@ -0,0 +1,10 @@ |
2379 | +#!/bin/sh -e |
2380 | +#execute firstlogin.sh only once |
2381 | +if [ ! -e /root/firstlogin_done ]; then |
2382 | + if [ -e /root/firstlogin.sh ]; then |
2383 | + /root/firstlogin.sh |
2384 | + fi |
2385 | + # This part should not be necessary any more |
2386 | + # sudo dpkg-reconfigure -p critical console-setup &> /dev/null |
2387 | + sudo touch /root/firstlogin_done |
2388 | +fi |
2389 | |
2390 | === added directory 'VMBuilder/plugins/kvm' |
2391 | === added file 'VMBuilder/plugins/kvm/__init__.py' |
2392 | --- VMBuilder/plugins/kvm/__init__.py 1970-01-01 00:00:00 +0000 |
2393 | +++ VMBuilder/plugins/kvm/__init__.py 2011-12-21 11:17:34 +0000 |
2394 | @@ -0,0 +1,19 @@ |
2395 | +# |
2396 | +# Uncomplicated VM Builder |
2397 | +# Copyright (C) 2007-2009 Canonical Ltd. |
2398 | +# |
2399 | +# See AUTHORS for list of contributors |
2400 | +# |
2401 | +# This program is free software: you can redistribute it and/or modify |
2402 | +# it under the terms of the GNU General Public License version 3, as |
2403 | +# published by the Free Software Foundation. |
2404 | +# |
2405 | +# This program is distributed in the hope that it will be useful, |
2406 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2407 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2408 | +# GNU General Public License for more details. |
2409 | +# |
2410 | +# You should have received a copy of the GNU General Public License |
2411 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2412 | +# |
2413 | +import vm |
2414 | |
2415 | === added file 'VMBuilder/plugins/kvm/vm.py' |
2416 | --- VMBuilder/plugins/kvm/vm.py 1970-01-01 00:00:00 +0000 |
2417 | +++ VMBuilder/plugins/kvm/vm.py 2011-12-21 11:17:34 +0000 |
2418 | @@ -0,0 +1,71 @@ |
2419 | +# |
2420 | +# Uncomplicated VM Builder |
2421 | +# Copyright (C) 2007-2009 Canonical Ltd. |
2422 | +# |
2423 | +# See AUTHORS for list of contributors |
2424 | +# |
2425 | +# This program is free software: you can redistribute it and/or modify |
2426 | +# it under the terms of the GNU General Public License version 3, as |
2427 | +# published by the Free Software Foundation. |
2428 | +# |
2429 | +# This program is distributed in the hope that it will be useful, |
2430 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2431 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2432 | +# GNU General Public License for more details. |
2433 | +# |
2434 | +# You should have received a copy of the GNU General Public License |
2435 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2436 | +# |
2437 | +from VMBuilder import register_hypervisor, Hypervisor |
2438 | +import VMBuilder |
2439 | +import os |
2440 | +import stat |
2441 | + |
2442 | +class KVM(Hypervisor): |
2443 | + name = 'KVM' |
2444 | + arg = 'kvm' |
2445 | + filetype = 'qcow2' |
2446 | + preferred_storage = VMBuilder.hypervisor.STORAGE_DISK_IMAGE |
2447 | + needs_bootloader = True |
2448 | + |
2449 | + def register_options(self): |
2450 | + group = self.setting_group('VM settings') |
2451 | + group.add_setting('mem', extra_args=['-m'], type='int', default=128, help='Assign MEM megabytes of memory to the guest vm. [default: %default]') |
2452 | + group.add_setting('cpus', type='int', default=1, help='Assign NUM cpus to the guest vm. [default: %default]') |
2453 | + |
2454 | + def convert(self, disks, destdir): |
2455 | + self.imgs = [] |
2456 | + self.cmdline = ['kvm', '-m', str(self.context.get_setting('mem'))] |
2457 | + self.cmdline += ['-smp', str(self.context.get_setting('cpus'))] |
2458 | + for disk in disks: |
2459 | + img_path = disk.convert(destdir, self.filetype) |
2460 | + self.imgs.append(img_path) |
2461 | + self.call_hooks('fix_ownership', img_path) |
2462 | + self.cmdline += ['-drive', 'file=%s' % os.path.basename(img_path)] |
2463 | + |
2464 | + self.cmdline += ['"$@"'] |
2465 | + |
2466 | + def deploy(self, destdir): |
2467 | + # No need create run script if vm is registered with libvirt |
2468 | + if self.context.get_setting('libvirt'): |
2469 | + return |
2470 | + |
2471 | + script = '%s/run.sh' % destdir |
2472 | + fp = open(script, 'w') |
2473 | + fp.write("#!/bin/sh\n\nexec %s\n" % ' '.join(self.cmdline)) |
2474 | + fp.close() |
2475 | + os.chmod(script, stat.S_IRWXU | stat.S_IRWXG | stat.S_IROTH | stat.S_IXOTH) |
2476 | + self.call_hooks('fix_ownership', script) |
2477 | + |
2478 | + def libvirt_domain_type_name(self): |
2479 | + return 'kvm' |
2480 | + |
2481 | +class QEMu(KVM): |
2482 | + name = 'QEMu' |
2483 | + arg = 'qemu' |
2484 | + |
2485 | + def libvirt_domain_type_name(self): |
2486 | + return 'qemu' |
2487 | + |
2488 | +register_hypervisor(KVM) |
2489 | +register_hypervisor(QEMu) |
2490 | |
2491 | === added directory 'VMBuilder/plugins/libvirt' |
2492 | === added file 'VMBuilder/plugins/libvirt/__init__.py' |
2493 | --- VMBuilder/plugins/libvirt/__init__.py 1970-01-01 00:00:00 +0000 |
2494 | +++ VMBuilder/plugins/libvirt/__init__.py 2011-12-21 11:17:34 +0000 |
2495 | @@ -0,0 +1,87 @@ |
2496 | +# |
2497 | +# Uncomplicated VM Builder |
2498 | +# Copyright (C) 2007-2010 Canonical Ltd. |
2499 | +# |
2500 | +# See AUTHORS for list of contributors |
2501 | +# |
2502 | +# This program is free software: you can redistribute it and/or modify |
2503 | +# it under the terms of the GNU General Public License version 3, as |
2504 | +# published by the Free Software Foundation. |
2505 | +# |
2506 | +# This program is distributed in the hope that it will be useful, |
2507 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2508 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2509 | +# GNU General Public License for more details. |
2510 | +# |
2511 | +# You should have received a copy of the GNU General Public License |
2512 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2513 | +# |
2514 | +from VMBuilder import register_hypervisor_plugin, Plugin, VMBuilderUserError |
2515 | +import VMBuilder.util |
2516 | + |
2517 | +class Libvirt(Plugin): |
2518 | + name = 'libvirt integration' |
2519 | + |
2520 | + def register_options(self): |
2521 | + group = self.setting_group('libvirt integration') |
2522 | + group.add_setting('libvirt', metavar='URI', help='Add VM to given URI') |
2523 | + group.add_setting('bridge', metavar="BRIDGE", help='Set up bridged network connected to BRIDGE.') |
2524 | + group.add_setting('network', metavar='NETWORK', default='default', help='Set up a network connection to virtual network NETWORK.') |
2525 | + |
2526 | + def all_domains(self): |
2527 | + # This does not seem to work when any domain is already running |
2528 | + return self.conn.listDefinedDomains() + [self.conn.lookupByID(id).name() for id in self.conn.listDomainsID()] |
2529 | + |
2530 | + def preflight_check(self): |
2531 | + libvirt_uri = self.get_setting('libvirt') |
2532 | + if not libvirt_uri: |
2533 | + return True |
2534 | + |
2535 | + if not self.context.name == 'KVM' and not self.context.name == 'QEMu': |
2536 | + raise VMBuilderUserError('The libvirt plugin is only equiped to work with KVM and QEMu at the moment.') |
2537 | + |
2538 | + import libvirt |
2539 | + import xml.etree.ElementTree |
2540 | + |
2541 | + self.conn = libvirt.open(libvirt_uri) |
2542 | + |
2543 | + e = xml.etree.ElementTree.fromstring(self.conn.getCapabilities()) |
2544 | + |
2545 | + if not 'hvm' in [x.text for x in e.getiterator('os_type')]: |
2546 | + raise VMBuilderUserError('libvirt does not seem to want to accept hvm domains') |
2547 | + |
2548 | + hostname = self.context.distro.get_setting('hostname') |
2549 | + if hostname in self.all_domains() and not self.context.overwrite: |
2550 | + raise VMBuilderUserError('Domain %s already exists at %s' % (hostname, libvirt_uri)) |
2551 | + |
2552 | + def deploy(self, destdir): |
2553 | + libvirt_uri = self.get_setting('libvirt') |
2554 | + if not libvirt_uri: |
2555 | + # Not for us |
2556 | + return False |
2557 | + |
2558 | + hostname = self.context.distro.get_setting('hostname') |
2559 | + tmpl_ctxt = { 'mem': self.context.get_setting('mem'), |
2560 | + 'cpus': self.context.get_setting('cpus'), |
2561 | + 'bridge' : self.context.get_setting('bridge'), |
2562 | + 'mac' : self.context.get_setting('mac'), |
2563 | + 'network' : self.context.get_setting('network'), |
2564 | + 'mac' : self.context.get_setting('mac'), |
2565 | + 'virtio_net' : self.context.distro.use_virtio_net(), |
2566 | + 'disks' : self.context.disks, |
2567 | + 'filesystems' : self.context.filesystems, |
2568 | + 'hostname' : hostname, |
2569 | + 'domain_type' : self.context.libvirt_domain_type_name() } |
2570 | + if self.context.preferred_storage == VMBuilder.hypervisor.STORAGE_FS_IMAGE: |
2571 | + vmxml = VMBuilder.util.render_template('libvirt', self.context, 'libvirtxml_fsimage', tmpl_ctxt) |
2572 | + else: |
2573 | + vmxml = VMBuilder.util.render_template('libvirt', self.context, 'libvirtxml', tmpl_ctxt) |
2574 | + |
2575 | + if hostname in self.all_domains() and not self.context.overwrite: |
2576 | + raise VMBuilderUserError('Domain %s already exists at %s' % (hostname, libvirt_uri)) |
2577 | + else: |
2578 | + self.conn.defineXML(vmxml) |
2579 | + |
2580 | + return True |
2581 | + |
2582 | +register_hypervisor_plugin(Libvirt) |
2583 | |
2584 | === added directory 'VMBuilder/plugins/libvirt/templates' |
2585 | === added file 'VMBuilder/plugins/libvirt/templates/libvirtxml.tmpl' |
2586 | --- VMBuilder/plugins/libvirt/templates/libvirtxml.tmpl 1970-01-01 00:00:00 +0000 |
2587 | +++ VMBuilder/plugins/libvirt/templates/libvirtxml.tmpl 2011-12-21 11:17:34 +0000 |
2588 | @@ -0,0 +1,46 @@ |
2589 | +<domain type='$domain_type'> |
2590 | + <name>$hostname</name> |
2591 | + <memory>#echo int($mem) * 1024 #</memory> |
2592 | + <vcpu>$cpus</vcpu> |
2593 | + <os> |
2594 | + <type>hvm</type> |
2595 | + <boot dev='hd'/> |
2596 | + </os> |
2597 | + <features> |
2598 | + <acpi/> |
2599 | + </features> |
2600 | + <clock offset='utc'/> |
2601 | + <on_poweroff>destroy</on_poweroff> |
2602 | + <on_reboot>restart</on_reboot> |
2603 | + <on_crash>destroy</on_crash> |
2604 | + <devices> |
2605 | + <emulator>/usr/bin/kvm</emulator> |
2606 | +#if $bridge |
2607 | + <interface type='bridge'> |
2608 | + <source bridge='$bridge'/> |
2609 | +#else |
2610 | + <interface type='network'> |
2611 | +#if $mac |
2612 | + <mac address='$mac'/> |
2613 | +#end if |
2614 | +#if $network |
2615 | + <source network='$network'/> |
2616 | +#end if |
2617 | +#end if |
2618 | +#if $virtio_net |
2619 | + <model type='virtio'/> |
2620 | +#end if |
2621 | + </interface> |
2622 | + <input type='mouse' bus='ps2'/> |
2623 | + <graphics type='vnc' port='-1' listen='127.0.0.1'/> |
2624 | +#for $disk in $disks |
2625 | + <disk type='file' device='disk'> |
2626 | +#if $disk.format_type != None |
2627 | + <driver name='qemu' type='$disk.format_type' /> |
2628 | +#end if |
2629 | + <source file='$disk.filename' /> |
2630 | + <target dev='hd$disk.devletters()' /> |
2631 | + </disk> |
2632 | +#end for |
2633 | + </devices> |
2634 | +</domain> |
2635 | |
2636 | === added file 'VMBuilder/plugins/libvirt/templates/libvirtxml_fsimage.tmpl' |
2637 | --- VMBuilder/plugins/libvirt/templates/libvirtxml_fsimage.tmpl 1970-01-01 00:00:00 +0000 |
2638 | +++ VMBuilder/plugins/libvirt/templates/libvirtxml_fsimage.tmpl 2011-12-21 11:17:34 +0000 |
2639 | @@ -0,0 +1,33 @@ |
2640 | +<domain type='kvm'> |
2641 | + <name>$hostname</name> |
2642 | + <memory>#echo $mem * 1024 #</memory> |
2643 | + <vcpu>$cpus</vcpu> |
2644 | + <os> |
2645 | + <type>hvm</type> |
2646 | + <boot dev='hd'/> |
2647 | + </os> |
2648 | + <features> |
2649 | + <acpi/> |
2650 | + </features> |
2651 | + <clock offset='utc'/> |
2652 | + <on_poweroff>destroy</on_poweroff> |
2653 | + <on_reboot>restart</on_reboot> |
2654 | + <on_crash>destroy</on_crash> |
2655 | + <devices> |
2656 | + <emulator>/usr/bin/kvm</emulator> |
2657 | + <interface type='network'> |
2658 | + <source network='default'/> |
2659 | + </interface> |
2660 | + <input type='mouse' bus='ps2'/> |
2661 | + <graphics type='vnc' port='-1' listen='127.0.0.1'/> |
2662 | +#for $fs in $filesystems |
2663 | + <disk type='file' device='disk'> |
2664 | +#if $fs.format_type != None |
2665 | + <driver name='qemu' type='$fs.format_type' /> |
2666 | +#end if |
2667 | + <source file='$fs.filename' /> |
2668 | + <target dev='hd$fs.devletters()' /> |
2669 | + </disk> |
2670 | +#end for |
2671 | + </devices> |
2672 | +</domain> |
2673 | |
2674 | === added directory 'VMBuilder/plugins/network' |
2675 | === added file 'VMBuilder/plugins/network/__init__.py' |
2676 | --- VMBuilder/plugins/network/__init__.py 1970-01-01 00:00:00 +0000 |
2677 | +++ VMBuilder/plugins/network/__init__.py 2011-12-21 11:17:34 +0000 |
2678 | @@ -0,0 +1,158 @@ |
2679 | +# |
2680 | +# Uncomplicated VM Builder |
2681 | +# Copyright (C) 2007-2009 Canonical Ltd. |
2682 | +# |
2683 | +# See AUTHORS for list of contributors |
2684 | +# |
2685 | +# This program is free software: you can redistribute it and/or modify |
2686 | +# it under the terms of the GNU General Public License version 3, as |
2687 | +# published by the Free Software Foundation. |
2688 | +# |
2689 | +# This program is distributed in the hope that it will be useful, |
2690 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2691 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2692 | +# GNU General Public License for more details. |
2693 | +# |
2694 | +# You should have received a copy of the GNU General Public License |
2695 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2696 | +# |
2697 | +# Virtual network management |
2698 | + |
2699 | +import logging |
2700 | +import re |
2701 | +import struct |
2702 | +import socket |
2703 | + |
2704 | +from VMBuilder import register_hypervisor_plugin, register_distro_plugin |
2705 | +from VMBuilder.plugins import Plugin |
2706 | +from VMBuilder.exception import VMBuilderUserError |
2707 | + |
2708 | +def validate_mac(mac): |
2709 | + valid_mac_address = re.compile("^([0-9a-f]{2}:){5}([0-9a-f]{2})$", re.IGNORECASE) |
2710 | + if not valid_mac_address.match(mac): |
2711 | + return False |
2712 | + else: |
2713 | + return True |
2714 | + |
2715 | +def numeric_to_dotted_ip(numeric_ip): |
2716 | + return socket.inet_ntoa(struct.pack('I', numeric_ip)) |
2717 | + |
2718 | +def dotted_to_numeric_ip(dotted_ip): |
2719 | + try: |
2720 | + return struct.unpack('I', socket.inet_aton(dotted_ip))[0] |
2721 | + except socket.error: |
2722 | + raise VMBuilderUserError('%s is not a valid ip address' % dotted_ip) |
2723 | + |
2724 | +def guess_mask_from_ip(numip): |
2725 | + first_octet = numip & 0xFF |
2726 | + |
2727 | + if (first_octet > 0) and (first_octet <= 127): |
2728 | + return 0xFF |
2729 | + elif (first_octet > 128) and (first_octet < 192): |
2730 | + return 0xFFFF |
2731 | + elif (first_octet < 224): |
2732 | + return 0xFFFFFF |
2733 | + else: |
2734 | + raise VMBuilderUserError('Could not guess network class of: %s' % numeric_to_dotted_ip(numip)) |
2735 | + |
2736 | +def calculate_net_address_from_ip_and_netmask(ip, netmask): |
2737 | + return ip & netmask |
2738 | + |
2739 | +def calculate_broadcast_address_from_ip_and_netmask(net, mask): |
2740 | + return net + (mask ^ 0xFFFFFFFF) |
2741 | + |
2742 | +def guess_gw_from_ip(ip): |
2743 | + return ip + 0x01000000 |
2744 | + |
2745 | +class NetworkDistroPlugin(Plugin): |
2746 | + def register_options(self): |
2747 | + group = self.setting_group('Network') |
2748 | + domainname = '.'.join(socket.gethostbyname_ex(socket.gethostname())[0].split('.')[1:]) or "defaultdomain" |
2749 | + group.add_setting('domain', metavar='DOMAIN', default=domainname, help='Set DOMAIN as the domain name of the guest [default: %default].') |
2750 | + |
2751 | + def preflight_check(self): |
2752 | + domain = self.context.get_setting('domain') |
2753 | + if domain == '': |
2754 | + raise VMBuilderUserError('Domain is undefined and host has no domain set.') |
2755 | + |
2756 | +class NetworkHypervisorPlugin(Plugin): |
2757 | + def register_options(self): |
2758 | + group = self.setting_group('Network') |
2759 | + group.add_setting('ip', metavar='ADDRESS', default='dhcp', help='IP address in dotted form [default: %default].') |
2760 | + group.add_setting('mac', metavar='MAC', help='MAC address of the guest [default: random].') |
2761 | + group.add_setting('mask', metavar='VALUE', help='IP mask in dotted form [default: based on ip setting]. Ignored if ip is not specified.') |
2762 | + group.add_setting('net', metavar='ADDRESS', help='IP net address in dotted form [default: based on ip setting]. Ignored if ip is not specified.') |
2763 | + group.add_setting('bcast', metavar='VALUE', help='IP broadcast in dotted form [default: based on ip setting]. Ignored if ip is not specified.') |
2764 | + group.add_setting('gw', metavar='ADDRESS', help='Gateway (router) address in dotted form [default: based on ip setting (first valid address in the network)]. Ignored if ip is not specified.') |
2765 | + group.add_setting('dns', metavar='ADDRESS', help='DNS address in dotted form [default: based on ip setting (first valid address in the network)] Ignored if ip is not specified.') |
2766 | + |
2767 | + |
2768 | + def preflight_check(self): |
2769 | + """ |
2770 | + Validate the ip configuration given and set defaults |
2771 | + """ |
2772 | + |
2773 | + ip = self.context.get_setting('ip') |
2774 | + logging.debug("ip: %s" % ip) |
2775 | + |
2776 | + mac = self.context.get_setting('mac') |
2777 | + if mac: |
2778 | + if not validate_mac(mac): |
2779 | + raise VMBuilderUserError("Malformed MAC address entered: %s" % mac) |
2780 | + |
2781 | + if ip != 'dhcp': |
2782 | + # num* are numeric representations |
2783 | + numip = dotted_to_numeric_ip(ip) |
2784 | + |
2785 | + mask = self.context.get_setting('mask') |
2786 | + if not mask: |
2787 | + nummask = guess_mask_from_ip(numip) |
2788 | + else: |
2789 | + nummask = dotted_to_numeric_ip(mask) |
2790 | + |
2791 | + numnet = calculate_net_address_from_ip_and_netmask(numip, nummask) |
2792 | + |
2793 | + net = self.context.get_setting('net') |
2794 | + if not net: |
2795 | + self.context.set_setting_default('net', numeric_to_dotted_ip(numnet)) |
2796 | + |
2797 | + bcast = self.context.get_setting('bcast') |
2798 | + if not bcast: |
2799 | + numbcast = calculate_broadcast_address_from_ip_and_netmask(numnet, nummask) |
2800 | + self.context.set_setting_default('bcast', numeric_to_dotted_ip(numbcast)) |
2801 | + |
2802 | + gw = self.context.get_setting('gw') |
2803 | + if not gw: |
2804 | + numgw = guess_gw_from_ip(numip) |
2805 | + self.context.set_setting_default('gw', numeric_to_dotted_ip(numgw)) |
2806 | + |
2807 | + dns = self.context.get_setting('dns') |
2808 | + if not dns: |
2809 | + self.context.set_setting_default('dns', self.context.get_setting('gw')) |
2810 | + |
2811 | + self.context.set_setting_default('mask', numeric_to_dotted_ip(nummask)) |
2812 | + |
2813 | + logging.debug("net: %s" % self.context.get_setting('net')) |
2814 | + logging.debug("netmask: %s" % self.context.get_setting('mask')) |
2815 | + logging.debug("broadcast: %s" % self.context.get_setting('bcast')) |
2816 | + logging.debug("gateway: %s" % self.context.get_setting('gw')) |
2817 | + logging.debug("dns: %s" % self.context.get_setting('dns')) |
2818 | + |
2819 | + def configure_networking(self, nics): |
2820 | + if len(nics) > 0: |
2821 | + nic = nics[0] |
2822 | + |
2823 | + ip = self.get_setting('ip') |
2824 | + if ip == 'dhcp': |
2825 | + nic.type = 'dhcp' |
2826 | + else: |
2827 | + nic.type = 'static' |
2828 | + nic.ip = ip |
2829 | + nic.network = self.context.get_setting('net') |
2830 | + nic.netmask = self.context.get_setting('mask') |
2831 | + nic.broadcast = self.context.get_setting('bcast') |
2832 | + nic.gateway = self.context.get_setting('gw') |
2833 | + nic.dns = self.context.get_setting('dns') |
2834 | + |
2835 | +register_distro_plugin(NetworkDistroPlugin) |
2836 | +register_hypervisor_plugin(NetworkHypervisorPlugin) |
2837 | |
2838 | === added directory 'VMBuilder/plugins/postinst' |
2839 | === added file 'VMBuilder/plugins/postinst/__init__.py' |
2840 | --- VMBuilder/plugins/postinst/__init__.py 1970-01-01 00:00:00 +0000 |
2841 | +++ VMBuilder/plugins/postinst/__init__.py 2011-12-21 11:17:34 +0000 |
2842 | @@ -0,0 +1,77 @@ |
2843 | +# |
2844 | +# Uncomplicated VM Builder |
2845 | +# Copyright (C) 2007-2009 Canonical Ltd. |
2846 | +# |
2847 | +# See AUTHORS for list of contributors |
2848 | +# |
2849 | +# This program is free software: you can redistribute it and/or modify |
2850 | +# it under the terms of the GNU General Public License version 3, as |
2851 | +# published by the Free Software Foundation. |
2852 | +# |
2853 | +# This program is distributed in the hope that it will be useful, |
2854 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2855 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2856 | +# GNU General Public License for more details. |
2857 | +# |
2858 | +# You should have received a copy of the GNU General Public License |
2859 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2860 | +# |
2861 | +from VMBuilder import register_distro_plugin, Plugin, VMBuilderUserError |
2862 | + |
2863 | +import logging |
2864 | +import os |
2865 | +import VMBuilder.util as util |
2866 | + |
2867 | +class postinst(Plugin): |
2868 | + """ |
2869 | + Plugin to provide --exec and --copy post install capabilities |
2870 | + """ |
2871 | + name ='Post install plugin' |
2872 | + |
2873 | + def register_options(self): |
2874 | + group = self.setting_group('Post install actions') |
2875 | + group.add_setting('copy', metavar='FILE', help="Read 'source dest' lines from FILE, copying source files from host to dest in the guest's file system.") |
2876 | + group.add_setting('execscript', extra_args=['--exec'], metavar='SCRIPT', help="Run SCRIPT after distro installation finishes. Script will be called with the guest's chroot as first argument, so you can use 'chroot $1 <cmd>' to run code in the virtual machine.") |
2877 | + |
2878 | + def preflight_check(self): |
2879 | + copy = self.context.get_setting('copy') |
2880 | + if copy: |
2881 | + logging.debug("Checking if copy PATH exists: %s" % copy) |
2882 | + if not(os.path.isfile(copy)): |
2883 | + raise VMBuilderUserError('The path to the copy directive is invalid: %s. Make sure you are providing a full path.' % copy) |
2884 | + |
2885 | + execscript = self.context.get_setting('execscript') |
2886 | + if execscript: |
2887 | + logging.debug("Checking if exec PATH exists: %s" % execscript) |
2888 | + if not(os.path.isfile(execscript)): |
2889 | + raise VMBuilderUserError('The path to the execscript file is invalid: %s. Make sure you are providing a full path.' % execscript) |
2890 | + |
2891 | + logging.debug("Checking permissions of exec PATH: %s" % execscript) |
2892 | + if not os.access(execscript, os.X_OK|os.R_OK): |
2893 | + raise VMBuilderUserError('The path to the execscript file has invalid permissions: %s. Make sure the path is readable and executable.' % execscript) |
2894 | + |
2895 | + def post_install(self): |
2896 | + copy = self.context.get_setting('copy') |
2897 | + execscript = self.context.get_setting('execscript') |
2898 | + if copy: |
2899 | + logging.info("Copying files specified by copy in: %s" % copy) |
2900 | + try: |
2901 | + for line in file(copy): |
2902 | + pair = line.strip().split(' ') |
2903 | + if len(pair) < 2: # skip blank and incomplete lines |
2904 | + continue |
2905 | + directory = '%s%s' % (self.context.chroot_dir, os.path.dirname(pair[1])) |
2906 | + if not os.path.exists(directory): |
2907 | + os.makedirs(directory) |
2908 | + util.run_cmd('cp', '-LpR', pair[0], '%s%s' % (self.context.chroot_dir, pair[1])) |
2909 | + |
2910 | + except IOError, (errno, strerror): |
2911 | + raise VMBuilderUserError("%s executing copy directives: %s" % (errno, strerror)) |
2912 | + |
2913 | + if execscript: |
2914 | + logging.info("Executing script: %s" % execscript) |
2915 | + util.run_cmd(execscript, self.context.chroot_dir) |
2916 | + |
2917 | + return True |
2918 | + |
2919 | +register_distro_plugin(postinst) |
2920 | |
2921 | === added directory 'VMBuilder/plugins/ubuntu' |
2922 | === added file 'VMBuilder/plugins/ubuntu/__init__.py' |
2923 | --- VMBuilder/plugins/ubuntu/__init__.py 1970-01-01 00:00:00 +0000 |
2924 | +++ VMBuilder/plugins/ubuntu/__init__.py 2011-12-21 11:17:34 +0000 |
2925 | @@ -0,0 +1,19 @@ |
2926 | +# |
2927 | +# Uncomplicated VM Builder |
2928 | +# Copyright (C) 2007-2009 Canonical Ltd. |
2929 | +# |
2930 | +# See AUTHORS for list of contributors |
2931 | +# |
2932 | +# This program is free software: you can redistribute it and/or modify |
2933 | +# it under the terms of the GNU General Public License version 3, as |
2934 | +# published by the Free Software Foundation. |
2935 | +# |
2936 | +# This program is distributed in the hope that it will be useful, |
2937 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2938 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2939 | +# GNU General Public License for more details. |
2940 | +# |
2941 | +# You should have received a copy of the GNU General Public License |
2942 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2943 | +# |
2944 | +import distro |
2945 | |
2946 | === added file 'VMBuilder/plugins/ubuntu/dapper.py' |
2947 | --- VMBuilder/plugins/ubuntu/dapper.py 1970-01-01 00:00:00 +0000 |
2948 | +++ VMBuilder/plugins/ubuntu/dapper.py 2011-12-21 11:17:34 +0000 |
2949 | @@ -0,0 +1,375 @@ |
2950 | +# |
2951 | +# Uncomplicated VM Builder |
2952 | +# Copyright (C) 2007-2010 Canonical Ltd. |
2953 | +# |
2954 | +# See AUTHORS for list of contributors |
2955 | +# |
2956 | +# This program is free software: you can redistribute it and/or modify |
2957 | +# it under the terms of the GNU General Public License version 3, as |
2958 | +# published by the Free Software Foundation. |
2959 | +# |
2960 | +# This program is distributed in the hope that it will be useful, |
2961 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2962 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2963 | +# GNU General Public License for more details. |
2964 | +# |
2965 | +# You should have received a copy of the GNU General Public License |
2966 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2967 | +# |
2968 | +import glob |
2969 | +import logging |
2970 | +import os |
2971 | +import suite |
2972 | +import shutil |
2973 | +import tempfile |
2974 | +import VMBuilder.disk as disk |
2975 | +from VMBuilder.util import run_cmd |
2976 | +from VMBuilder.exception import VMBuilderException |
2977 | + |
2978 | +class Dapper(suite.Suite): |
2979 | + updategrub = "/sbin/update-grub" |
2980 | + grubroot = "/lib/grub" |
2981 | + valid_flavours = { 'i386' : ['386', '686', '686-smp', 'k7', 'k7-smp', 'server', 'server-bigiron'], |
2982 | + 'amd64' : ['amd64-generic', 'amd64-k8', 'amd64-k8-smp', 'amd64-server', 'amd64-xeon']} |
2983 | + default_flavour = { 'i386' : 'server', 'amd64' : 'amd64-server' } |
2984 | + disk_prefix = 'hd' |
2985 | + xen_kernel_flavour = None |
2986 | + virtio_net = False |
2987 | + chpasswd_cmd = [ 'chpasswd', '--md5' ] |
2988 | + preferred_filesystem = 'ext3' |
2989 | + |
2990 | + def pre_install(self): |
2991 | + pass |
2992 | + |
2993 | + def check_kernel_flavour(self, arch, flavour): |
2994 | + return flavour in self.valid_flavours[arch] |
2995 | + |
2996 | + def check_arch_validity(self, arch): |
2997 | + return arch in self.valid_flavours.keys() |
2998 | + |
2999 | + def install(self, destdir): |
3000 | + raise VMBuilderException('Do not call this method!') |
3001 | + |
3002 | + # These are still missing after the refactoring. |
3003 | + logging.debug("Creating device.map") |
3004 | + self.install_device_map() |
3005 | + |
3006 | + logging.debug("Copy host settings") |
3007 | + self.copy_settings() |
3008 | + |
3009 | + if hasattr(self.context, 'ec2') and self.context.ec2: |
3010 | + logging.debug("Configuring for ec2") |
3011 | + self.install_ec2() |
3012 | + |
3013 | + def create_manifest(self): |
3014 | + manifest = self.context.get_setting('manifest') |
3015 | + if manifest: |
3016 | + logging.debug("Creating manifest") |
3017 | + manifest_contents = self.run_in_target('dpkg-query', '-W', '--showformat=${Package} ${Version}\n') |
3018 | + fp = open(manifest, 'w') |
3019 | + fp.write(manifest_contents) |
3020 | + fp.close |
3021 | + self.call_hook('fix_ownership', manifest) |
3022 | + |
3023 | + def update(self): |
3024 | + self.run_in_target('apt-get', '-y', '--force-yes', 'dist-upgrade', |
3025 | + env={ 'DEBIAN_FRONTEND' : 'noninteractive' }) |
3026 | + |
3027 | + def install_authorized_keys(self): |
3028 | + ssh_key = self.context.get_setting('ssh-key') |
3029 | + if ssh_key: |
3030 | + os.mkdir('%s/root/.ssh' % self.context.chroot_dir, 0700) |
3031 | + shutil.copy(ssh_key, '%s/root/.ssh/authorized_keys' % self.context.chroot_dir) |
3032 | + os.chmod('%s/root/.ssh/authorized_keys' % self.context.chroot_dir, 0644) |
3033 | + |
3034 | + user = self.context.get_setting('user') |
3035 | + ssh_user_key = self.context.get_setting('ssh-user-key') |
3036 | + if ssh_user_key: |
3037 | + os.mkdir('%s/home/%s/.ssh' % (self.context.chroot_dir, user), 0700) |
3038 | + shutil.copy(ssh_user_key, '%s/home/%s/.ssh/authorized_keys' % (self.context.chroot_dir, user)) |
3039 | + os.chmod('%s/home/%s/.ssh/authorized_keys' % (self.context.chroot_dir, user), 0644) |
3040 | + self.run_in_target('chown', '-R', '%s:%s' % ((user,)*2), '/home/%s/.ssh/' % (user)) |
3041 | + |
3042 | + if ssh_user_key or ssh_key: |
3043 | + addpkg = self.context.get_setting('addpkg') |
3044 | + addpkg += ['openssh-server'] |
3045 | + self.context.set_setting('addpkg', addpkg) |
3046 | + |
3047 | + def mount_dev_proc(self): |
3048 | + run_cmd('mount', '--bind', '/dev', '%s/dev' % self.context.chroot_dir) |
3049 | + self.context.add_clean_cb(self.unmount_dev) |
3050 | + |
3051 | + run_cmd('mount', '--bind', '/dev/pts', '%s/dev/pts' % self.context.chroot_dir) |
3052 | + self.context.add_clean_cb(self.unmount_dev_pts) |
3053 | + |
3054 | + self.run_in_target('mount', '-t', 'proc', 'proc', '/proc') |
3055 | + self.context.add_clean_cb(self.unmount_proc) |
3056 | + |
3057 | + def unmount_proc(self): |
3058 | + self.context.cancel_cleanup(self.unmount_proc) |
3059 | + run_cmd('umount', '%s/proc' % self.context.chroot_dir) |
3060 | + |
3061 | + def unmount_dev_pts(self): |
3062 | + self.context.cancel_cleanup(self.unmount_dev_pts) |
3063 | + run_cmd('umount', '%s/dev/pts' % self.context.chroot_dir) |
3064 | + |
3065 | + def unmount_dev(self): |
3066 | + self.context.cancel_cleanup(self.unmount_dev) |
3067 | + run_cmd('umount', '%s/dev' % self.context.chroot_dir) |
3068 | + |
3069 | + def update_passwords(self): |
3070 | + # Set the user password, using md5 |
3071 | + user = self.context.get_setting('user') |
3072 | + passwd = self.context.get_setting('pass') |
3073 | + self.run_in_target(stdin=('%s:%s\n' % (user, passwd)), *self.chpasswd_cmd) |
3074 | + |
3075 | + # Lock root account only if we didn't set the root password |
3076 | + rootpass = self.context.get_setting('rootpass') |
3077 | + if rootpass: |
3078 | + self.run_in_target(stdin=('%s:%s\n' % ('root', rootpass)), *self.chpasswd_cmd) |
3079 | + else: |
3080 | + self.run_in_target('usermod', '-L', 'root') |
3081 | + |
3082 | + lock_user = self.context.get_setting('lock-user') |
3083 | + if lock_user: |
3084 | + logging.info('Locking %s' % (user, )) |
3085 | + self.run_in_target('usermod', '-L', user) |
3086 | + |
3087 | + def create_initial_user(self): |
3088 | + uid = self.context.get_setting('uid') |
3089 | + name = self.context.get_setting('name') |
3090 | + user = self.context.get_setting('user') |
3091 | + if uid: |
3092 | + self.run_in_target('adduser', '--disabled-password', '--uid', uid, '--gecos', name, user) |
3093 | + else: |
3094 | + self.run_in_target('adduser', '--disabled-password', '--gecos', name, user) |
3095 | + |
3096 | + self.run_in_target('addgroup', '--system', 'admin') |
3097 | + self.run_in_target('adduser', user, 'admin') |
3098 | + |
3099 | + self.install_from_template('/etc/sudoers', 'sudoers') |
3100 | + for group in ['adm', 'audio', 'cdrom', 'dialout', 'floppy', 'video', 'plugdev', 'dip', 'netdev', 'powerdev', 'lpadmin', 'scanner']: |
3101 | + self.run_in_target('adduser', user, group, ignore_fail=True) |
3102 | + |
3103 | + self.update_passwords() |
3104 | + |
3105 | + def kernel_name(self): |
3106 | + flavour = self.context.get_setting('flavour') |
3107 | + arch = self.context.get_setting('arch') |
3108 | + return 'linux-image-%s' % (flavour or self.default_flavour[arch],) |
3109 | + |
3110 | + def config_host_and_domainname(self): |
3111 | + hostname = self.context.get_setting('hostname') |
3112 | + domain = self.context.get_setting('domain') |
3113 | + self.context.install_file('/etc/hostname', hostname) |
3114 | + self.install_from_template('/etc/hosts', 'etc_hosts', { 'hostname' : hostname, 'domain' : domain }) |
3115 | + |
3116 | + def config_interfaces(self, nics): |
3117 | + self.install_from_template('/etc/network/interfaces', 'interfaces', |
3118 | + { 'ip' : nics[0].type == 'dhcp' and 'dhcp' or nics[0].ip, |
3119 | + 'mask' : nics[0].netmask, |
3120 | + 'net' : nics[0].network, |
3121 | + 'bcast' : nics[0].broadcast, |
3122 | + 'gw' : nics[0].gateway, |
3123 | + 'dns' : nics[0].dns, |
3124 | + 'domain' : self.context.get_setting('domain') }) |
3125 | + |
3126 | + def unprevent_daemons_starting(self): |
3127 | + os.unlink('%s/usr/sbin/policy-rc.d' % self.context.chroot_dir) |
3128 | + |
3129 | + def prevent_daemons_starting(self): |
3130 | + os.chmod(self.install_from_template('/usr/sbin/policy-rc.d', 'nostart-policy-rc.d'), 0755) |
3131 | + |
3132 | + def seed(self, seedfile): |
3133 | + """Seed debconf with the contents of a seedfile""" |
3134 | + logging.info('Seeding with "%s"' % seedfile) |
3135 | + |
3136 | + self.run_in_target('debconf-set-selections', stdin=open(seedfile, 'r').read()) |
3137 | + |
3138 | + def install_extras(self): |
3139 | + seedfile = self.context.get_setting('seedfile') |
3140 | + if seedfile: |
3141 | + self.seed(seedfile) |
3142 | + |
3143 | + addpkg = self.context.get_setting('addpkg') |
3144 | + removepkg = self.context.get_setting('removepkg') |
3145 | + if not addpkg and not removepkg: |
3146 | + return |
3147 | + |
3148 | + cmd = ['apt-get', 'install', '-y', '--force-yes'] |
3149 | + cmd += addpkg or [] |
3150 | + cmd += ['%s-' % pkg for pkg in removepkg or []] |
3151 | + self.run_in_target(env={ 'DEBIAN_FRONTEND' : 'noninteractive' }, *cmd) |
3152 | + |
3153 | + def unmount_volatile(self): |
3154 | + for mntpnt in glob.glob('%s/lib/modules/*/volatile' % self.context.chroot_dir): |
3155 | + logging.debug("Unmounting %s" % mntpnt) |
3156 | + run_cmd('umount', mntpnt) |
3157 | + |
3158 | + def install_menu_lst(self, disks): |
3159 | + self.run_in_target(self.updategrub, '-y') |
3160 | + self.mangle_grub_menu_lst(disks) |
3161 | + self.run_in_target(self.updategrub) |
3162 | + self.run_in_target('grub-set-default', '0') |
3163 | + |
3164 | + def mangle_grub_menu_lst(self, disks): |
3165 | + bootdev = disk.bootpart(disks) |
3166 | + run_cmd('sed', '-ie', 's/^# kopt=root=\([^ ]*\)\(.*\)/# kopt=root=\/dev\/hd%s%d\\2/g' % (bootdev.disk.devletters(), bootdev.get_index()+1), '%s/boot/grub/menu.lst' % self.context.chroot_dir) |
3167 | + run_cmd('sed', '-ie', 's/^# groot.*/# groot %s/g' % bootdev.get_grub_id(), '%s/boot/grub/menu.lst' % self.context.chroot_dir) |
3168 | + run_cmd('sed', '-ie', '/^# kopt_2_6/ d', '%s/boot/grub/menu.lst' % self.context.chroot_dir) |
3169 | + |
3170 | + def install_sources_list(self, final=False): |
3171 | + if final: |
3172 | + mirror = updates_mirror = self.context.get_setting('mirror') |
3173 | + security_mirror = self.context.get_setting('security-mirror') |
3174 | + else: |
3175 | + mirror, updates_mirror, security_mirror = self.install_mirrors() |
3176 | + |
3177 | + components = self.context.get_setting('components') |
3178 | + ppa = self.context.get_setting('ppa') |
3179 | + suite = self.context.get_setting('suite') |
3180 | + self.install_from_template('/etc/apt/sources.list', 'sources.list', { 'mirror' : mirror, |
3181 | + 'security_mirror' : security_mirror, |
3182 | + 'updates_mirror' : updates_mirror, |
3183 | + 'components' : components, |
3184 | + 'ppa' : ppa, |
3185 | + 'suite' : suite }) |
3186 | + |
3187 | + # If setting up the final mirror, allow apt-get update to fail |
3188 | + # (since we might be on a complete different network than the |
3189 | + # final vm is going to be on). |
3190 | + self.run_in_target('apt-get', 'update', ignore_fail=final) |
3191 | + |
3192 | + def install_apt_proxy(self): |
3193 | + proxy = self.context.get_setting('proxy') |
3194 | + if proxy is not None: |
3195 | + self.context.install_file('/etc/apt/apt.conf', '// Proxy added by vmbuilder\nAcquire::http { Proxy "%s"; };' % proxy) |
3196 | + |
3197 | + def install_fstab(self, disks, filesystems): |
3198 | + self.install_from_template('/etc/fstab', 'dapper_fstab', { 'parts' : disk.get_ordered_partitions(disks), 'prefix' : self.disk_prefix }) |
3199 | + |
3200 | + def install_device_map(self): |
3201 | + self.install_from_template('/boot/grub/device.map', 'devicemap', { 'prefix' : self.disk_prefix }) |
3202 | + |
3203 | + def debootstrap(self): |
3204 | + arch = self.context.get_setting('arch') |
3205 | + cmd = ['/usr/sbin/debootstrap', '--arch=%s' % arch] |
3206 | + |
3207 | + variant = self.context.get_setting('variant') |
3208 | + if variant: |
3209 | + cmd += ['--variant=%s' % variant] |
3210 | + |
3211 | + suite = self.context.get_setting('suite') |
3212 | + cmd += [suite, self.context.chroot_dir, self.debootstrap_mirror()] |
3213 | + kwargs = { 'env' : { 'DEBIAN_FRONTEND' : 'noninteractive' } } |
3214 | + |
3215 | + proxy = self.context.get_setting('proxy') |
3216 | + if proxy: |
3217 | + kwargs['env']['http_proxy'] = proxy |
3218 | + run_cmd(*cmd, **kwargs) |
3219 | + |
3220 | + def debootstrap_mirror(self): |
3221 | + iso = self.context.get_setting('iso') |
3222 | + if iso: |
3223 | + isodir = tempfile.mkdtemp() |
3224 | + self.context.add_clean_cb(lambda:os.rmdir(isodir)) |
3225 | + run_cmd('mount', '-o', 'loop', '-t', 'iso9660', iso, isodir) |
3226 | + self.context.add_clean_cmd('umount', isodir) |
3227 | + self.iso_mounted = True |
3228 | + |
3229 | + return 'file://%s' % isodir |
3230 | + else: |
3231 | + return self.install_mirrors()[0] |
3232 | + |
3233 | + |
3234 | + def install_mirrors(self): |
3235 | + install_mirror = self.context.get_setting('install-mirror') |
3236 | + if install_mirror: |
3237 | + mirror = install_mirror |
3238 | + else: |
3239 | + mirror = self.context.get_setting('mirror') |
3240 | + |
3241 | + updates_mirror = mirror |
3242 | + |
3243 | + install_security_mirror = self.context.get_setting('install-security-mirror') |
3244 | + if install_security_mirror: |
3245 | + security_mirror = install_security_mirror |
3246 | + else: |
3247 | + security_mirror = self.context.get_setting('security-mirror') |
3248 | + |
3249 | + return (mirror, updates_mirror, security_mirror) |
3250 | + |
3251 | + def install_kernel(self, destdir): |
3252 | + run_cmd('chroot', destdir, 'apt-get', '--force-yes', '-y', 'install', self.kernel_name(), env={ 'DEBIAN_FRONTEND' : 'noninteractive' }) |
3253 | + |
3254 | + def install_grub(self, chroot_dir): |
3255 | + self.install_from_template('/etc/kernel-img.conf', 'kernelimg', { 'updategrub' : self.updategrub }) |
3256 | + arch = self.context.get_setting('arch') |
3257 | + self.run_in_target('apt-get', '--force-yes', '-y', 'install', 'grub', env={ 'DEBIAN_FRONTEND' : 'noninteractive' }) |
3258 | + run_cmd('rsync', '-a', '%s%s/%s/' % (chroot_dir, self.grubroot, arch == 'amd64' and 'x86_64-pc' or 'i386-pc'), '%s/boot/grub/' % chroot_dir) |
3259 | + |
3260 | + def create_devices(self): |
3261 | + pass |
3262 | +# FIXME |
3263 | +# import VMBuilder.plugins.xen |
3264 | + |
3265 | +# if isinstance(self.context.hypervisor, VMBuilder.plugins.xen.Xen): |
3266 | +# self.run_in_target('mknod', '/dev/xvda', 'b', '202', '0') |
3267 | +# self.run_in_target('mknod', '/dev/xvda1', 'b', '202', '1') |
3268 | +# self.run_in_target('mknod', '/dev/xvda2', 'b', '202', '2') |
3269 | +# self.run_in_target('mknod', '/dev/xvda3', 'b', '202', '3') |
3270 | +# self.run_in_target('mknod', '/dev/xvc0', 'c', '204', '191') |
3271 | + |
3272 | + def install_from_template(self, *args, **kwargs): |
3273 | + return self.context.install_from_template(*args, **kwargs) |
3274 | + |
3275 | + def run_in_target(self, *args, **kwargs): |
3276 | + return self.context.run_in_target(*args, **kwargs) |
3277 | + |
3278 | + def copy_to_target(self, infile, destpath): |
3279 | + logging.debug("Copying %s on host to %s in guest" % (infile, destpath)) |
3280 | + dir = '%s/%s' % (self.destdir, os.path.dirname(destpath)) |
3281 | + if not os.path.isdir(dir): |
3282 | + os.makedirs(dir) |
3283 | + if os.path.isdir(infile): |
3284 | + shutil.copytree(infile, '%s/%s' % (self.destdir, destpath)) |
3285 | + else: |
3286 | + shutil.copy(infile, '%s/%s' % (self.destdir, destpath)) |
3287 | + |
3288 | + def post_mount(self, fs): |
3289 | + if fs.mntpnt == '/': |
3290 | + logging.debug("Creating /var/run in root filesystem") |
3291 | + os.makedirs('%s/var/run' % fs.mntpath) |
3292 | + logging.debug("Creating /var/lock in root filesystem") |
3293 | + os.makedirs('%s/var/lock' % fs.mntpath) |
3294 | + |
3295 | + def set_locale(self): |
3296 | + lang = self.context.get_setting('lang') |
3297 | + if lang: |
3298 | + self.install_from_template('/etc/default/locale', 'locale', { 'lang' : lang }) |
3299 | + if lang != "C": |
3300 | + self.run_in_target('locale-gen', lang) |
3301 | + self.run_in_target('dpkg-reconfigure', '-fnoninteractive', '-pcritical', 'libc6') |
3302 | + self.run_in_target('dpkg-reconfigure', '-fnoninteractive', '-pcritical', 'locales') |
3303 | + |
3304 | + def install_vmbuilder_log(self, logfile, rootdir): |
3305 | + shutil.copy(logfile, '%s/var/log/vmbuilder-install.log' % (rootdir,)) |
3306 | + |
3307 | + def set_timezone(self): |
3308 | + timezone = self.context.get_setting('timezone') |
3309 | + if timezone: |
3310 | + self.install_from_template('/etc/timezone', 'timezone', { 'timezone' : timezone }) |
3311 | + self.run_in_target('dpkg-reconfigure', '-fnoninteractive', '-pcritical', 'locales') |
3312 | + |
3313 | + def install_ec2(self): |
3314 | + if self.context.ec2: |
3315 | + logging.debug('This suite does not support ec2') |
3316 | + |
3317 | + def disable_hwclock_access(self): |
3318 | + fp = open('%s/etc/default/rcS' % self.destdir, 'a') |
3319 | + fp.write('HWCLOCKACCESS=no') |
3320 | + fp.close() |
3321 | + |
3322 | + def has_256_bit_inode_ext3_support(self): |
3323 | + return False |
3324 | + |
3325 | |
3326 | === added file 'VMBuilder/plugins/ubuntu/distro.py' |
3327 | --- VMBuilder/plugins/ubuntu/distro.py 1970-01-01 00:00:00 +0000 |
3328 | +++ VMBuilder/plugins/ubuntu/distro.py 2011-12-21 11:17:34 +0000 |
3329 | @@ -0,0 +1,294 @@ |
3330 | +# |
3331 | +# Uncomplicated VM Builder |
3332 | +# Copyright (C) 2007-2010 Canonical Ltd. |
3333 | +# |
3334 | +# See AUTHORS for list of contributors |
3335 | +# |
3336 | +# This program is free software: you can redistribute it and/or modify |
3337 | +# it under the terms of the GNU General Public License version 3, as |
3338 | +# published by the Free Software Foundation. |
3339 | +# |
3340 | +# This program is distributed in the hope that it will be useful, |
3341 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3342 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3343 | +# GNU General Public License for more details. |
3344 | +# |
3345 | +# You should have received a copy of the GNU General Public License |
3346 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3347 | +# |
3348 | +import logging |
3349 | +import os |
3350 | +import shutil |
3351 | +import stat |
3352 | +import VMBuilder |
3353 | +from VMBuilder import register_distro, Distro |
3354 | +from VMBuilder.util import run_cmd |
3355 | +from VMBuilder.exception import VMBuilderUserError, VMBuilderException |
3356 | + |
3357 | +class Ubuntu(Distro): |
3358 | + name = 'Ubuntu' |
3359 | + arg = 'ubuntu' |
3360 | + suites = ['dapper', 'gutsy', 'hardy', 'intrepid', 'jaunty', |
3361 | + 'karmic', 'lucid', 'maverick', 'natty', 'oneiric', |
3362 | + 'precise' ] |
3363 | + |
3364 | + # Maps host arch to valid guest archs |
3365 | + valid_archs = { 'amd64' : ['amd64', 'i386', 'lpia' ], |
3366 | + 'i386' : [ 'i386', 'lpia' ], |
3367 | + 'lpia' : [ 'i386', 'lpia' ] } |
3368 | + |
3369 | + xen_kernel = '' |
3370 | + |
3371 | + def register_options(self): |
3372 | + group = self.setting_group('Package options') |
3373 | + group.add_setting('addpkg', type='list', metavar='PKG', help='Install PKG into the guest (can be specified multiple times).') |
3374 | + group.add_setting('removepkg', type='list', metavar='PKG', help='Remove PKG from the guest (can be specified multiple times)') |
3375 | + group.add_setting('seedfile', metavar="SEEDFILE", help='Seed the debconf database with the contents of this seed file before installing packages') |
3376 | + |
3377 | + group = self.setting_group('General OS options') |
3378 | + self.host_arch = run_cmd('dpkg', '--print-architecture').rstrip() |
3379 | + group.add_setting('arch', extra_args=['-a'], default=self.host_arch, help='Specify the target architecture. Valid options: amd64 i386 lpia (defaults to host arch)') |
3380 | + group.add_setting('hostname', default='ubuntu', help='Set NAME as the hostname of the guest. Default: ubuntu. Also uses this name as the VM name.') |
3381 | + |
3382 | + group = self.setting_group('Installation options') |
3383 | + group.add_setting('suite', default='lucid', help='Suite to install. Valid options: %s [default: %%default]' % ' '.join(self.suites)) |
3384 | + group.add_setting('flavour', extra_args=['--kernel-flavour'], help='Kernel flavour to use. Default and valid options depend on architecture and suite') |
3385 | + group.add_setting('variant', metavar='VARIANT', help='Passed to debootstrap --variant flag; use minbase, buildd, or fakechroot.') |
3386 | + group.add_setting('iso', metavar='PATH', help='Use an iso image as the source for installation of file. Full path to the iso must be provided. If --mirror is also provided, it will be used in the final sources.list of the vm. This requires suite and kernel parameter to match what is available on the iso, obviously.') |
3387 | + group.add_setting('mirror', metavar='URL', help='Use Ubuntu mirror at URL instead of the default, which is http://archive.ubuntu.com/ubuntu for official arches and http://ports.ubuntu.com/ubuntu-ports otherwise') |
3388 | + group.add_setting('proxy', metavar='URL', help='Use proxy at URL for cached packages') |
3389 | + group.add_setting('install-mirror', metavar='URL', help='Use Ubuntu mirror at URL for the installation only. Apt\'s sources.list will still use default or URL set by --mirror') |
3390 | + group.add_setting('security-mirror', metavar='URL', help='Use Ubuntu security mirror at URL instead of the default, which is http://security.ubuntu.com/ubuntu for official arches and http://ports.ubuntu.com/ubuntu-ports otherwise.') |
3391 | + group.add_setting('install-security-mirror', metavar='URL', help='Use the security mirror at URL for installation only. Apt\'s sources.list will still use default or URL set by --security-mirror') |
3392 | + group.add_setting('components', type='list', metavar='COMPS', help='A comma seperated list of distro components to include (e.g. main,universe).') |
3393 | + group.add_setting('ppa', metavar='PPA', type='list', help='Add ppa belonging to PPA to the vm\'s sources.list.') |
3394 | + group.add_setting('lang', metavar='LANG', default=get_locale(), help='Set the locale to LANG [default: %default]') |
3395 | + group.add_setting('timezone', metavar='TZ', default='UTC', help='Set the timezone to TZ in the vm. [default: %default]') |
3396 | + |
3397 | + group = self.setting_group('Settings for the initial user') |
3398 | + group.add_setting('user', default='ubuntu', help='Username of initial user [default: %default]') |
3399 | + group.add_setting('name', default='Ubuntu', help='Full name of initial user [default: %default]') |
3400 | + group.add_setting('pass', default='ubuntu', help='Password of initial user [default: %default]') |
3401 | + group.add_setting('rootpass', help='Initial root password (WARNING: this has strong security implications).') |
3402 | + group.add_setting('uid', type='int', help='Initial UID value.') |
3403 | + group.add_setting('gid', help='Initial GID value.') |
3404 | + group.add_setting('lock-user', type='bool', default=False, help='Lock the initial user [default: %default]') |
3405 | + |
3406 | + group = self.setting_group('Other options') |
3407 | + group.add_setting('ssh-key', metavar='PATH', help='Add PATH to root\'s ~/.ssh/authorized_keys (WARNING: this has strong security implications).') |
3408 | + group.add_setting('ssh-user-key', help='Add PATH to the user\'s ~/.ssh/authorized_keys.') |
3409 | + group.add_setting('manifest', metavar='PATH', help='If passed, a manifest will be written to PATH') |
3410 | + |
3411 | + def set_defaults(self): |
3412 | + arch = self.get_setting('arch') |
3413 | + |
3414 | + if arch == 'lpia': |
3415 | + self.set_setting_default('mirror', 'http://ports.ubuntu.com/ubuntu-ports') |
3416 | + self.set_setting_default('security-mirror', 'http://ports.ubuntu.com/ubuntu-ports') |
3417 | + else: |
3418 | + self.set_setting_default('mirror', 'http://archive.ubuntu.com/ubuntu') |
3419 | + self.set_setting_default('security-mirror', 'http://security.ubuntu.com/ubuntu') |
3420 | + |
3421 | + self.set_setting_default('components', ['main', 'restricted', 'universe']) |
3422 | + |
3423 | + def preflight_check(self): |
3424 | + """While not all of these are strictly checks, their failure would inevitably |
3425 | + lead to failure, and since we can check them before we start setting up disk |
3426 | + and whatnot, we might as well go ahead an do this now.""" |
3427 | + |
3428 | + suite = self.get_setting('suite') |
3429 | + if not suite in self.suites: |
3430 | + raise VMBuilderUserError('Invalid suite: "%s". Valid suites are: %s' % (suite, ' '.join(self.suites))) |
3431 | + |
3432 | + modname = 'VMBuilder.plugins.ubuntu.%s' % (suite, ) |
3433 | + mod = __import__(modname, fromlist=[suite]) |
3434 | + self.suite = getattr(mod, suite.capitalize())(self) |
3435 | + |
3436 | + arch = self.get_setting('arch') |
3437 | + if arch not in self.valid_archs[self.host_arch] or \ |
3438 | + not self.suite.check_arch_validity(arch): |
3439 | + raise VMBuilderUserError('%s is not a valid architecture. Valid architectures are: %s' % (arch, |
3440 | + ' '.join(self.valid_archs[self.host_arch]))) |
3441 | + |
3442 | + components = self.get_setting('components') |
3443 | + if not components: |
3444 | + self.set_config_value_list = ['main', 'restricted', 'universe'] |
3445 | + else: |
3446 | + if type(components) is str: |
3447 | + self.vm.components = self.vm.components.split(',') |
3448 | + |
3449 | + self.context.virtio_net = self.use_virtio_net() |
3450 | + |
3451 | + # check if the seedfile exists if one is to be used |
3452 | + seedfile = self.context.get_setting('seedfile') |
3453 | + if seedfile and not os.path.exists(seedfile): |
3454 | + raise VMBuilderUserError("Seedfile '%s' does not exist" % seedfile) |
3455 | + |
3456 | + lang = self.get_setting('lang') |
3457 | + |
3458 | +# FIXME |
3459 | +# if getattr(self.vm, 'ec2', False): |
3460 | +# self.get_ec2_kernel() |
3461 | +# self.get_ec2_ramdisk() |
3462 | +# self.apply_ec2_settings() |
3463 | + |
3464 | + def bootstrap(self): |
3465 | + self.suite.debootstrap() |
3466 | + self.suite.pre_install() |
3467 | + |
3468 | + def configure_os(self): |
3469 | + self.suite.install_sources_list() |
3470 | + self.suite.install_apt_proxy() |
3471 | + self.suite.create_devices() |
3472 | + self.suite.prevent_daemons_starting() |
3473 | + self.suite.mount_dev_proc() |
3474 | + self.suite.install_extras() |
3475 | + self.suite.create_initial_user() |
3476 | + self.suite.install_authorized_keys() |
3477 | + self.suite.set_timezone() |
3478 | + self.suite.set_locale() |
3479 | + self.suite.update() |
3480 | + self.suite.install_sources_list(final=True) |
3481 | + self.suite.run_in_target('apt-get', 'clean'); |
3482 | + self.suite.unmount_volatile() |
3483 | + self.suite.unmount_proc() |
3484 | + self.suite.unmount_dev_pts() |
3485 | + self.suite.unmount_dev() |
3486 | + self.suite.unprevent_daemons_starting() |
3487 | + self.suite.create_manifest() |
3488 | + |
3489 | + def configure_networking(self, nics): |
3490 | + self.suite.config_host_and_domainname() |
3491 | + self.suite.config_interfaces(nics) |
3492 | + |
3493 | + def configure_mounting(self, disks, filesystems): |
3494 | + self.suite.install_fstab(disks, filesystems) |
3495 | + |
3496 | + def install(self, destdir): |
3497 | + self.destdir = destdir |
3498 | + self.suite.install(destdir) |
3499 | + |
3500 | + def install_vmbuilder_log(self, logfile, rootdir): |
3501 | + self.suite.install_vmbuilder_log(logfile, rootdir) |
3502 | + |
3503 | + def post_mount(self, fs): |
3504 | + self.suite.post_mount(fs) |
3505 | + |
3506 | + def use_virtio_net(self): |
3507 | + return self.suite.virtio_net |
3508 | + |
3509 | + def install_bootloader_cleanup(self, chroot_dir): |
3510 | + self.context.cancel_cleanup(self.install_bootloader_cleanup) |
3511 | + tmpdir = '%s/tmp/vmbuilder-grub' % chroot_dir |
3512 | + for disk in os.listdir(tmpdir): |
3513 | + if disk != 'device.map': |
3514 | + run_cmd('umount', os.path.join(tmpdir, disk)) |
3515 | + shutil.rmtree(tmpdir) |
3516 | + |
3517 | + def install_kernel(self, destdir): |
3518 | + self.suite.install_kernel(destdir) |
3519 | + |
3520 | + def install_bootloader(self, chroot_dir, disks): |
3521 | + root_dev = VMBuilder.disk.bootpart(disks).get_grub_id() |
3522 | + |
3523 | + tmpdir = '/tmp/vmbuilder-grub' |
3524 | + os.makedirs('%s%s' % (chroot_dir, tmpdir)) |
3525 | + self.context.add_clean_cb(self.install_bootloader_cleanup) |
3526 | + devmapfile = os.path.join(tmpdir, 'device.map') |
3527 | + devmap = open('%s%s' % (chroot_dir, devmapfile), 'w') |
3528 | + for (disk, id) in zip(disks, range(len(disks))): |
3529 | + new_filename = os.path.join(tmpdir, os.path.basename(disk.filename)) |
3530 | + open('%s%s' % (chroot_dir, new_filename), 'w').close() |
3531 | + run_cmd('mount', '--bind', disk.filename, '%s%s' % (chroot_dir, new_filename)) |
3532 | + st = os.stat(disk.filename) |
3533 | + if stat.S_ISBLK(st.st_mode): |
3534 | + for (part, part_id) in zip(disk.partitions, range(len(disk.partitions))): |
3535 | + part_mountpnt = '%s%s%d' % (chroot_dir, new_filename, part_id+1) |
3536 | + open(part_mountpnt, 'w').close() |
3537 | + run_cmd('mount', '--bind', part.filename, part_mountpnt) |
3538 | + devmap.write("(hd%d) %s\n" % (id, new_filename)) |
3539 | + devmap.close() |
3540 | + run_cmd('cat', '%s%s' % (chroot_dir, devmapfile)) |
3541 | + self.suite.install_grub(chroot_dir) |
3542 | + self.run_in_target('grub', '--device-map=%s' % devmapfile, '--batch', stdin='''root %s |
3543 | +setup (hd0) |
3544 | +EOT''' % root_dev) |
3545 | + self.suite.install_menu_lst(disks) |
3546 | + self.install_bootloader_cleanup(chroot_dir) |
3547 | + |
3548 | + def xen_kernel_version(self): |
3549 | + if self.suite.xen_kernel_flavour: |
3550 | + # if this is ec2, do not call rmadison. |
3551 | + # this could be replaced with a method to get most recent |
3552 | + # stable kernel, but really, this is not used at all for ec2 |
3553 | + if hasattr(self.context, 'ec2') and self.context.ec2: |
3554 | + logging.debug("selecting ec2 kernel") |
3555 | + self.xen_kernel = "2.6.ec2-kernel" |
3556 | + return self.xen_kernel |
3557 | + if not self.xen_kernel: |
3558 | + rmad = run_cmd('rmadison', 'linux-image-%s' % self.suite.xen_kernel_flavour) |
3559 | + version = ['0', '0','0', '0'] |
3560 | + |
3561 | + for line in rmad.splitlines(): |
3562 | + sline = line.split('|') |
3563 | + |
3564 | + if sline[2].strip().startswith(self.context.get_setting('suite')): |
3565 | + vt = sline[1].strip().split('.') |
3566 | + for i in range(4): |
3567 | + if int(vt[i]) > int(version[i]): |
3568 | + version = vt |
3569 | + break |
3570 | + |
3571 | + if version[0] == '0': |
3572 | + raise VMBuilderException('Something is wrong, no valid xen kernel for the suite %s found by rmadison' % self.context.suite) |
3573 | + |
3574 | + self.xen_kernel = '%s.%s.%s-%s' % (version[0],version[1],version[2],version[3]) |
3575 | + return self.xen_kernel |
3576 | + else: |
3577 | + raise VMBuilderUserError('There is no valid xen kernel for the suite selected.') |
3578 | + |
3579 | + def xen_kernel_initrd_path(self, which): |
3580 | + path = '/boot/%s-%s-%s' % (which, self.xen_kernel_version(), self.suite.xen_kernel_flavour) |
3581 | + return path |
3582 | + |
3583 | + def xen_kernel_path(self): |
3584 | + return self.xen_kernel_initrd_path('kernel') |
3585 | + |
3586 | + def xen_ramdisk_path(self): |
3587 | + return self.xen_kernel_initrd_path('ramdisk') |
3588 | + |
3589 | + def get_ec2_kernel(self): |
3590 | + if self.suite.ec2_kernel_info: |
3591 | + return self.suite.ec2_kernel_info[self.context.arch] |
3592 | + else: |
3593 | + raise VMBuilderUserError('EC2 is not supported for the suite selected') |
3594 | + |
3595 | + def get_ec2_ramdisk(self): |
3596 | + if self.suite.ec2_ramdisk_info: |
3597 | + return self.suite.ec2_ramdisk_info[self.context.arch] |
3598 | + else: |
3599 | + raise VMBuilderUserError('EC2 is not supported for the suite selected') |
3600 | + |
3601 | + def disable_hwclock_access(self): |
3602 | + return self.suite.disable_hwclock_access() |
3603 | + |
3604 | + def apply_ec2_settings(self): |
3605 | + return self.suite.apply_ec2_settings() |
3606 | + |
3607 | + def has_256_bit_inode_ext3_support(self): |
3608 | + return self.suite.has_256_bit_inode_ext3_support() |
3609 | + |
3610 | + def preferred_filesystem(self): |
3611 | + return self.suite.preferred_filesystem |
3612 | + |
3613 | +def get_locale(): |
3614 | + lang = os.getenv('LANG') |
3615 | + if lang is None: |
3616 | + return 'C' |
3617 | + # People's $LANG looks different since lucid, but locale-gen still |
3618 | + # wants the old format. |
3619 | + if lang.endswith('utf8'): |
3620 | + return lang[:-4] + 'UTF-8' |
3621 | + return lang |
3622 | + |
3623 | +register_distro(Ubuntu) |
3624 | |
3625 | === added file 'VMBuilder/plugins/ubuntu/edgy.py' |
3626 | --- VMBuilder/plugins/ubuntu/edgy.py 1970-01-01 00:00:00 +0000 |
3627 | +++ VMBuilder/plugins/ubuntu/edgy.py 2011-12-21 11:17:34 +0000 |
3628 | @@ -0,0 +1,84 @@ |
3629 | +# |
3630 | +# Uncomplicated VM Builder |
3631 | +# Copyright (C) 2007-2009 Canonical Ltd. |
3632 | +# |
3633 | +# See AUTHORS for list of contributors |
3634 | +# |
3635 | +# This program is free software: you can redistribute it and/or modify |
3636 | +# it under the terms of the GNU General Public License version 3, as |
3637 | +# published by the Free Software Foundation. |
3638 | +# |
3639 | +# This program is distributed in the hope that it will be useful, |
3640 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3641 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3642 | +# GNU General Public License for more details. |
3643 | +# |
3644 | +# You should have received a copy of the GNU General Public License |
3645 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3646 | +# |
3647 | +import shutil |
3648 | +import os |
3649 | +import VMBuilder.disk as disk |
3650 | +from VMBuilder.util import run_cmd |
3651 | +from VMBuilder.plugins.ubuntu.dapper import Dapper |
3652 | + |
3653 | +class Edgy(Dapper): |
3654 | + valid_flavours = { 'i386' : ['386', '686', '686-smp', 'generic', 'k7', 'k7-smp', 'server', 'server-bigiron'], |
3655 | + 'amd64' : ['amd64-generic', 'amd64-k8', 'amd64-k8-smp', 'amd64-server', 'amd64-xeon', 'server']} |
3656 | + default_flavour = { 'i386' : 'server', 'amd64' : 'server' } |
3657 | + disk_prefix = 'sd' |
3658 | + |
3659 | + def mangle_grub_menu_lst(self, disks): |
3660 | + bootdev = disk.bootpart(disks) |
3661 | + run_cmd('sed', '-ie', 's/^# kopt=root=\([^ ]*\)\(.*\)/# kopt=root=UUID=%s\\2/g' % bootdev.fs.uuid, |
3662 | + '%s/boot/grub/menu.lst' % self.context.chroot_dir) |
3663 | + run_cmd('sed', '-ie', 's/^# groot.*/# groot %s/g' % bootdev.get_grub_id(), |
3664 | + '%s/boot/grub/menu.lst' % self.context.chroot_dir) |
3665 | + run_cmd('sed', '-ie', '/^# kopt_2_6/ d', '%s/boot/grub/menu.lst' % |
3666 | + self.context.chroot_dir) |
3667 | + |
3668 | + def fstab(self): |
3669 | + retval = '''# /etc/fstab: static file system information. |
3670 | +# |
3671 | +# <file system> <mount point> <type> <options> <dump> <pass> |
3672 | +proc /proc proc defaults 0 0 |
3673 | +''' |
3674 | + parts = disk.get_ordered_partitions(self.context.disks) |
3675 | + for part in parts: |
3676 | + retval += "UUID=%-40s %15s %7s %15s %d %d\n" % (part.fs.uuid, part.fs.mntpnt, part.fs.fstab_fstype(), part.fs.fstab_options(), 0, 0) |
3677 | + return retval |
3678 | + |
3679 | + def copy_settings(self): |
3680 | + if os.path.exists('/etc/default/locale'): |
3681 | + self.copy_to_target('/etc/default/locale', '/etc/default/locale') |
3682 | + csdir = '%s/etc/console-setup' % self.destdir |
3683 | + have_cs = os.path.isdir(csdir) |
3684 | + if have_cs: |
3685 | + shutil.rmtree(csdir) |
3686 | + self.copy_to_target('/etc/console-setup', '/etc/console-setup') |
3687 | + self.copy_to_target('/etc/default/console-setup', '/etc/default/console-setup') |
3688 | + self.run_in_target('dpkg-reconfigure', '-fnoninteractive', '-pcritical', 'tzdata') |
3689 | + self.run_in_target('locale-gen', 'en_US') |
3690 | + if self.context.lang: |
3691 | + self.run_in_target('locale-gen', self.context.lang) |
3692 | + self.install_from_template('/etc/default/locale', 'locale', { 'lang' : self.context.lang }) |
3693 | + self.run_in_target('dpkg-reconfigure', '-fnoninteractive', '-pcritical', 'locales') |
3694 | + if have_cs: |
3695 | + self.run_in_target('dpkg-reconfigure', '-fnoninteractive', '-pcritical', 'console-setup') |
3696 | + |
3697 | + def set_timezone(self): |
3698 | + timezone = self.context.get_setting('timezone') |
3699 | + if timezone: |
3700 | + self.install_from_template('/etc/timezone', 'timezone', { 'timezone' : timezone }) |
3701 | + self.run_in_target('dpkg-reconfigure', '-fnoninteractive', '-pcritical', 'tzdata') |
3702 | + |
3703 | + def prevent_daemons_starting(self): |
3704 | + super(Edgy, self).prevent_daemons_starting() |
3705 | + initctl = '%s/sbin/initctl' % (self.context.chroot_dir,) |
3706 | + os.rename(initctl, '%s.REAL' % (initctl,)) |
3707 | + self.install_from_template('/sbin/initctl', 'initctl-stub', mode=0755) |
3708 | + |
3709 | + def unprevent_daemons_starting(self): |
3710 | + super(Edgy, self).unprevent_daemons_starting() |
3711 | + initctl = '%s/sbin/initctl' % (self.context.chroot_dir,) |
3712 | + os.rename('%s.REAL' % (initctl,), initctl) |
3713 | |
3714 | === added file 'VMBuilder/plugins/ubuntu/feisty.py' |
3715 | --- VMBuilder/plugins/ubuntu/feisty.py 1970-01-01 00:00:00 +0000 |
3716 | +++ VMBuilder/plugins/ubuntu/feisty.py 2011-12-21 11:17:34 +0000 |
3717 | @@ -0,0 +1,26 @@ |
3718 | +# |
3719 | +# Uncomplicated VM Builder |
3720 | +# Copyright (C) 2007-2009 Canonical Ltd. |
3721 | +# |
3722 | +# See AUTHORS for list of contributors |
3723 | +# |
3724 | +# This program is free software: you can redistribute it and/or modify |
3725 | +# it under the terms of the GNU General Public License version 3, as |
3726 | +# published by the Free Software Foundation. |
3727 | +# |
3728 | +# This program is distributed in the hope that it will be useful, |
3729 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3730 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3731 | +# GNU General Public License for more details. |
3732 | +# |
3733 | +# You should have received a copy of the GNU General Public License |
3734 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3735 | +# |
3736 | +from VMBuilder.plugins.ubuntu.edgy import Edgy |
3737 | + |
3738 | +class Feisty(Edgy): |
3739 | + updategrub = "/usr/sbin/update-grub" |
3740 | + grubroot = "/usr/lib/grub" |
3741 | + valid_flavours = { 'i386' : ['386', '686', '686-smp', 'generic', 'k7', 'k7-smp', 'server', 'server-bigiron', 'lowlatency'], |
3742 | + 'amd64' : ['amd64-generic', 'amd64-k8', 'amd64-k8-smp', 'amd64-server', 'amd64-xeon', 'server']} |
3743 | + disk_prefix = 'sd' |
3744 | |
3745 | === added file 'VMBuilder/plugins/ubuntu/gutsy.py' |
3746 | --- VMBuilder/plugins/ubuntu/gutsy.py 1970-01-01 00:00:00 +0000 |
3747 | +++ VMBuilder/plugins/ubuntu/gutsy.py 2011-12-21 11:17:34 +0000 |
3748 | @@ -0,0 +1,26 @@ |
3749 | +# |
3750 | +# Uncomplicated VM Builder |
3751 | +# Copyright (C) 2007-2009 Canonical Ltd. |
3752 | +# |
3753 | +# See AUTHORS for list of contributors |
3754 | +# |
3755 | +# This program is free software: you can redistribute it and/or modify |
3756 | +# it under the terms of the GNU General Public License version 3, as |
3757 | +# published by the Free Software Foundation. |
3758 | +# |
3759 | +# This program is distributed in the hope that it will be useful, |
3760 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3761 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3762 | +# GNU General Public License for more details. |
3763 | +# |
3764 | +# You should have received a copy of the GNU General Public License |
3765 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3766 | +# |
3767 | +from VMBuilder.plugins.ubuntu.feisty import Feisty |
3768 | + |
3769 | +class Gutsy(Feisty): |
3770 | + valid_flavours = { 'i386' : ['386', 'generic', 'rt', 'server', 'virtual'], |
3771 | + 'amd64' : ['generic', 'rt', 'server'], |
3772 | + 'lpia' : ['lpia', 'lpiacompat'] } |
3773 | + default_flavour = { 'i386' : 'virtual', 'amd64' : 'server', 'lpia' : 'lpia' } |
3774 | + xen_kernel_flavour = 'xen' |
3775 | |
3776 | === added file 'VMBuilder/plugins/ubuntu/hardy.py' |
3777 | --- VMBuilder/plugins/ubuntu/hardy.py 1970-01-01 00:00:00 +0000 |
3778 | +++ VMBuilder/plugins/ubuntu/hardy.py 2011-12-21 11:17:34 +0000 |
3779 | @@ -0,0 +1,58 @@ |
3780 | +# |
3781 | +# Uncomplicated VM Builder |
3782 | +# Copyright (C) 2007-2009 Canonical Ltd. |
3783 | +# |
3784 | +# See AUTHORS for list of contributors |
3785 | +# |
3786 | +# This program is free software: you can redistribute it and/or modify |
3787 | +# it under the terms of the GNU General Public License version 3, as |
3788 | +# published by the Free Software Foundation. |
3789 | +# |
3790 | +# This program is distributed in the hope that it will be useful, |
3791 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3792 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3793 | +# GNU General Public License for more details. |
3794 | +# |
3795 | +# You should have received a copy of the GNU General Public License |
3796 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3797 | +# |
3798 | +from VMBuilder.plugins.ubuntu.gutsy import Gutsy |
3799 | + |
3800 | +class Hardy(Gutsy): |
3801 | + virtio_net = True |
3802 | + ec2_kernel_info = { 'i386' : 'aki-6e709707', 'amd64' : 'aki-6f709706' } |
3803 | + ec2_ramdisk_info = { 'i386' : 'ari-6c709705', 'amd64' : 'ari-61709708' } |
3804 | + |
3805 | + def apply_ec2_settings(self): |
3806 | + self.context.addpkg += ['ec2-init', |
3807 | + 'openssh-server', |
3808 | + 'ec2-modules', |
3809 | + 'standard^', |
3810 | + 'ec2-ami-tools', |
3811 | + 'update-motd'] |
3812 | + |
3813 | + if not self.context.ppa: |
3814 | + self.context.ppa = [] |
3815 | + |
3816 | + self.context.ppa += ['ubuntu-on-ec2/ppa'] |
3817 | + |
3818 | + def install_ec2(self): |
3819 | + |
3820 | + if self.context.arch == 'i386': |
3821 | + self.run_in_target('apt-get' ,'--force-yes', '-y', 'install', 'libc6-xen') |
3822 | + self.run_in_target('apt-get','--purge','--force-yes', '-y', 'remove', 'libc6-i686') |
3823 | + self.install_from_template('/etc/ld.so.conf.d/libc6-xen.conf', 'xen-ld-so-conf') |
3824 | + self.install_from_template('/etc/event.d/xvc0', 'upstart', { 'console' : 'xvc0' }) |
3825 | + self.run_in_target('update-rc.d', '-f', 'hwclockfirst.sh', 'remove') |
3826 | + self.install_from_template('/etc/update-motd.d/51_update-motd', '51_update-motd-hardy') |
3827 | + self.run_in_target('chmod', '755', '/etc/update-motd.d/51_update-motd') |
3828 | + self.install_from_template('/etc/ec2-init/is-compat-env', 'is-compat-env') |
3829 | + |
3830 | + def xen_kernel_path(self): |
3831 | + return '/boot/vmlinuz-2.6.24-19-xen' |
3832 | + |
3833 | + def xen_ramdisk_path(self): |
3834 | + return '/boot/initrd.img-2.6.24-19-xen' |
3835 | + |
3836 | + def has_256_bit_inode_ext3_support(self): |
3837 | + return True |
3838 | |
3839 | === added file 'VMBuilder/plugins/ubuntu/intrepid.py' |
3840 | --- VMBuilder/plugins/ubuntu/intrepid.py 1970-01-01 00:00:00 +0000 |
3841 | +++ VMBuilder/plugins/ubuntu/intrepid.py 2011-12-21 11:17:34 +0000 |
3842 | @@ -0,0 +1,45 @@ |
3843 | +# |
3844 | +# Uncomplicated VM Builder |
3845 | +# Copyright (C) 2007-2009 Canonical Ltd. |
3846 | +# |
3847 | +# See AUTHORS for list of contributors |
3848 | +# |
3849 | +# This program is free software: you can redistribute it and/or modify |
3850 | +# it under the terms of the GNU General Public License version 3, as |
3851 | +# published by the Free Software Foundation. |
3852 | +# |
3853 | +# This program is distributed in the hope that it will be useful, |
3854 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3855 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3856 | +# GNU General Public License for more details. |
3857 | +# |
3858 | +# You should have received a copy of the GNU General Public License |
3859 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3860 | +# |
3861 | +import VMBuilder.disk as disk |
3862 | +from VMBuilder.util import run_cmd |
3863 | +from VMBuilder.plugins.ubuntu.hardy import Hardy |
3864 | + |
3865 | +class Intrepid(Hardy): |
3866 | + valid_flavours = { 'i386' : ['386', 'generic', 'server', 'virtual'], |
3867 | + 'amd64' : ['generic', 'server', 'virtual'], |
3868 | + 'lpia' : ['lpia', 'lpiacompat'] } |
3869 | + default_flavour = { 'i386' : 'virtual', 'amd64' : 'virtual', 'lpia' : 'lpia' } |
3870 | + xen_kernel_flavour = 'virtual' |
3871 | + ec2_kernel_info = { 'i386' : 'aki-714daa18', 'amd64' : 'aki-4f4daa26' } |
3872 | + ec2_ramdisk_info = { 'i386': 'ari-7e4daa17', 'amd64' : 'ari-4c4daa25' } |
3873 | + |
3874 | + def install_ec2(self): |
3875 | +# workaround for policy bug on ubuntu-server. (see bug #275432) |
3876 | + self.run_in_target('apt-get', '--force-yes', '-y', 'install', 'policykit') |
3877 | + self.run_in_target('apt-get', '--force-yes', '-y', 'install', 'server^') |
3878 | + self.install_from_template('/etc/update-motd.d/51_update-motd', '51_update-motd') |
3879 | + self.run_in_target('chmod', '755', '/etc/update-motd.d/51_update-motd') |
3880 | + self.install_from_template('/etc/ec2-init/is-compat-env', 'is-compat-env') |
3881 | + |
3882 | + def mangle_grub_menu_lst(self, disks): |
3883 | + rootdev = disk.rootpart(disks) |
3884 | + bootdev = disk.bootpart(disks) |
3885 | + run_cmd('sed', '-ie', 's/^# kopt=root=\([^ ]*\)\(.*\)/# kopt=root=UUID=%s\\2/g' % rootdev.fs.uuid, '%s/boot/grub/menu.lst' % self.context.chroot_dir) |
3886 | + run_cmd('sed', '-ie', 's/^# groot.*/# groot=%s/g' % bootdev.fs.uuid, '%s/boot/grub/menu.lst' % self.context.chroot_dir) |
3887 | + run_cmd('sed', '-ie', '/^# kopt_2_6/ d', '%s/boot/grub/menu.lst' % self.context.chroot_dir) |
3888 | |
3889 | === added file 'VMBuilder/plugins/ubuntu/jaunty.py' |
3890 | --- VMBuilder/plugins/ubuntu/jaunty.py 1970-01-01 00:00:00 +0000 |
3891 | +++ VMBuilder/plugins/ubuntu/jaunty.py 2011-12-21 11:17:34 +0000 |
3892 | @@ -0,0 +1,40 @@ |
3893 | +# |
3894 | +# Uncomplicated VM Builder |
3895 | +# Copyright (C) 2007-2009 Canonical Ltd. |
3896 | +# |
3897 | +# See AUTHORS for list of contributors |
3898 | +# |
3899 | +# This program is free software: you can redistribute it and/or modify |
3900 | +# it under the terms of the GNU General Public License version 3, as |
3901 | +# published by the Free Software Foundation. |
3902 | +# |
3903 | +# This program is distributed in the hope that it will be useful, |
3904 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3905 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3906 | +# GNU General Public License for more details. |
3907 | +# |
3908 | +# You should have received a copy of the GNU General Public License |
3909 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3910 | +# |
3911 | +import os |
3912 | +import VMBuilder.disk as disk |
3913 | +from VMBuilder.util import run_cmd |
3914 | +from VMBuilder.plugins.ubuntu.intrepid import Intrepid |
3915 | + |
3916 | +class Jaunty(Intrepid): |
3917 | + valid_flavours = { 'i386' : ['generic', 'server', 'virtual'], |
3918 | + 'amd64' : ['generic', 'server', 'virtual'], |
3919 | + 'lpia' : ['lpia', 'lpiacompat'] } |
3920 | + xen_kernel_flavour = 'server' |
3921 | + ec2_kernel_info = { 'i386' : 'aki-c553b4ac', 'amd64' : 'aki-d653b4bf' } |
3922 | + ec2_ramdisk_info = { 'i386' : 'ari-c253b4ab', 'amd64' : 'ari-d753b4be' } |
3923 | + chpasswd_cmd= [ 'chpasswd' ] |
3924 | + |
3925 | + def install_ec2(self): |
3926 | + self.run_in_target('apt-get', '--force-yes', '-y', 'install', 'server^') |
3927 | + self.install_from_template('/etc/update-motd.d/51_update-motd', '51_update-motd') |
3928 | + # lucid and later wont have an /etc/ec2-init, so only write |
3929 | + # that file if the dir exists |
3930 | + if os.path.isdir("/etc/ec2-init"): |
3931 | + self.install_from_template('/etc/ec2-init/is-compat-env', 'is-compat-env') |
3932 | + self.run_in_target('chmod', '755', '/etc/update-motd.d/51_update-motd') |
3933 | |
3934 | === added file 'VMBuilder/plugins/ubuntu/karmic.py' |
3935 | --- VMBuilder/plugins/ubuntu/karmic.py 1970-01-01 00:00:00 +0000 |
3936 | +++ VMBuilder/plugins/ubuntu/karmic.py 2011-12-21 11:17:34 +0000 |
3937 | @@ -0,0 +1,33 @@ |
3938 | +# |
3939 | +# Uncomplicated VM Builder |
3940 | +# Copyright (C) 2007-2010 Canonical Ltd. |
3941 | +# |
3942 | +# See AUTHORS for list of contributors |
3943 | +# |
3944 | +# This program is free software: you can redistribute it and/or modify |
3945 | +# it under the terms of the GNU General Public License version 3, as |
3946 | +# published by the Free Software Foundation. |
3947 | +# |
3948 | +# This program is distributed in the hope that it will be useful, |
3949 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3950 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3951 | +# GNU General Public License for more details. |
3952 | +# |
3953 | +# You should have received a copy of the GNU General Public License |
3954 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3955 | +# |
3956 | +from VMBuilder.plugins.ubuntu.jaunty import Jaunty |
3957 | + |
3958 | +class Karmic(Jaunty): |
3959 | + valid_flavours = { 'i386' : ['386', 'generic', 'generic-pae', 'virtual'], |
3960 | + 'amd64' : ['generic', 'server', 'virtual'], |
3961 | + 'lpia' : ['lpia'] } |
3962 | + |
3963 | + preferred_filesystem = 'ext4' |
3964 | + |
3965 | + def apply_ec2_settings(self): |
3966 | + self.context.addpkg += ['standard^', |
3967 | + 'uec^'] |
3968 | + |
3969 | + def pre_install(self): |
3970 | + self.context.install_file('/etc/hosts', contents='') |
3971 | |
3972 | === added file 'VMBuilder/plugins/ubuntu/lucid.py' |
3973 | --- VMBuilder/plugins/ubuntu/lucid.py 1970-01-01 00:00:00 +0000 |
3974 | +++ VMBuilder/plugins/ubuntu/lucid.py 2011-12-21 11:17:34 +0000 |
3975 | @@ -0,0 +1,23 @@ |
3976 | +# |
3977 | +# Uncomplicated VM Builder |
3978 | +# Copyright (C) 2010 Canonical Ltd. |
3979 | +# |
3980 | +# See AUTHORS for list of contributors |
3981 | +# |
3982 | +# This program is free software: you can redistribute it and/or modify |
3983 | +# it under the terms of the GNU General Public License version 3, as |
3984 | +# published by the Free Software Foundation. |
3985 | +# |
3986 | +# This program is distributed in the hope that it will be useful, |
3987 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3988 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3989 | +# GNU General Public License for more details. |
3990 | +# |
3991 | +# You should have received a copy of the GNU General Public License |
3992 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3993 | +# |
3994 | +from VMBuilder.plugins.ubuntu.karmic import Karmic |
3995 | + |
3996 | +class Lucid(Karmic): |
3997 | + valid_flavours = { 'i386' : ['386', 'generic', 'generic-pae', 'virtual'], |
3998 | + 'amd64' : ['generic', 'preempt', 'server', 'virtual'] } |
3999 | |
4000 | === added file 'VMBuilder/plugins/ubuntu/maverick.py' |
4001 | --- VMBuilder/plugins/ubuntu/maverick.py 1970-01-01 00:00:00 +0000 |
4002 | +++ VMBuilder/plugins/ubuntu/maverick.py 2011-12-21 11:17:34 +0000 |
4003 | @@ -0,0 +1,23 @@ |
4004 | +# |
4005 | +# Uncomplicated VM Builder |
4006 | +# Copyright (C) 2010 Canonical Ltd. |
4007 | +# |
4008 | +# See AUTHORS for list of contributors |
4009 | +# |
4010 | +# This program is free software: you can redistribute it and/or modify |
4011 | +# it under the terms of the GNU General Public License version 3, as |
4012 | +# published by the Free Software Foundation. |
4013 | +# |
4014 | +# This program is distributed in the hope that it will be useful, |
4015 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4016 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4017 | +# GNU General Public License for more details. |
4018 | +# |
4019 | +# You should have received a copy of the GNU General Public License |
4020 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4021 | +# |
4022 | +from VMBuilder.plugins.ubuntu.lucid import Lucid |
4023 | + |
4024 | +class Maverick(Lucid): |
4025 | + valid_flavours = { 'i386' : ['generic', 'generic-pae', 'virtual'], |
4026 | + 'amd64' : ['generic', 'server', 'virtual'] } |
4027 | |
4028 | === added file 'VMBuilder/plugins/ubuntu/natty.py' |
4029 | --- VMBuilder/plugins/ubuntu/natty.py 1970-01-01 00:00:00 +0000 |
4030 | +++ VMBuilder/plugins/ubuntu/natty.py 2011-12-21 11:17:34 +0000 |
4031 | @@ -0,0 +1,22 @@ |
4032 | +# |
4033 | +# Uncomplicated VM Builder |
4034 | +# Copyright (C) 2010 Canonical Ltd. |
4035 | +# |
4036 | +# See AUTHORS for list of contributors |
4037 | +# |
4038 | +# This program is free software: you can redistribute it and/or modify |
4039 | +# it under the terms of the GNU General Public License version 3, as |
4040 | +# published by the Free Software Foundation. |
4041 | +# |
4042 | +# This program is distributed in the hope that it will be useful, |
4043 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4044 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4045 | +# GNU General Public License for more details. |
4046 | +# |
4047 | +# You should have received a copy of the GNU General Public License |
4048 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4049 | +# |
4050 | +from VMBuilder.plugins.ubuntu.maverick import Maverick |
4051 | + |
4052 | +class Natty(Maverick): |
4053 | + pass |
4054 | |
4055 | === added file 'VMBuilder/plugins/ubuntu/oneiric.py' |
4056 | --- VMBuilder/plugins/ubuntu/oneiric.py 1970-01-01 00:00:00 +0000 |
4057 | +++ VMBuilder/plugins/ubuntu/oneiric.py 2011-12-21 11:17:34 +0000 |
4058 | @@ -0,0 +1,28 @@ |
4059 | +# |
4060 | +# Uncomplicated VM Builder |
4061 | +# Copyright (C) 2010 Canonical Ltd. |
4062 | +# |
4063 | +# See AUTHORS for list of contributors |
4064 | +# |
4065 | +# This program is free software: you can redistribute it and/or modify |
4066 | +# it under the terms of the GNU General Public License version 3, as |
4067 | +# published by the Free Software Foundation. |
4068 | +# |
4069 | +# This program is distributed in the hope that it will be useful, |
4070 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4071 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4072 | +# GNU General Public License for more details. |
4073 | +# |
4074 | +# You should have received a copy of the GNU General Public License |
4075 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4076 | +# |
4077 | +import time |
4078 | +from VMBuilder.plugins.ubuntu.natty import Natty |
4079 | + |
4080 | +class Oneiric(Natty): |
4081 | + |
4082 | + def unmount_dev(self): |
4083 | + # no idea why, but something keep /dev busy briefly during the |
4084 | + # bootstrap |
4085 | + time.sleep(1) |
4086 | + super(Oneiric, self).unmount_dev() |
4087 | |
4088 | === added file 'VMBuilder/plugins/ubuntu/precise.py' |
4089 | --- VMBuilder/plugins/ubuntu/precise.py 1970-01-01 00:00:00 +0000 |
4090 | +++ VMBuilder/plugins/ubuntu/precise.py 2011-12-21 11:17:34 +0000 |
4091 | @@ -0,0 +1,23 @@ |
4092 | +# |
4093 | +# Uncomplicated VM Builder |
4094 | +# Copyright (C) 2010 Canonical Ltd. |
4095 | +# |
4096 | +# See AUTHORS for list of contributors |
4097 | +# |
4098 | +# This program is free software: you can redistribute it and/or modify |
4099 | +# it under the terms of the GNU General Public License version 3, as |
4100 | +# published by the Free Software Foundation. |
4101 | +# |
4102 | +# This program is distributed in the hope that it will be useful, |
4103 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4104 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4105 | +# GNU General Public License for more details. |
4106 | +# |
4107 | +# You should have received a copy of the GNU General Public License |
4108 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4109 | +# |
4110 | +import time |
4111 | +from VMBuilder.plugins.ubuntu.oneiric import Oneiric |
4112 | + |
4113 | +class Precise(Oneiric): |
4114 | + pass |
4115 | |
4116 | === added file 'VMBuilder/plugins/ubuntu/suite.py' |
4117 | --- VMBuilder/plugins/ubuntu/suite.py 1970-01-01 00:00:00 +0000 |
4118 | +++ VMBuilder/plugins/ubuntu/suite.py 2011-12-21 11:17:34 +0000 |
4119 | @@ -0,0 +1,28 @@ |
4120 | +# |
4121 | +# Uncomplicated VM Builder |
4122 | +# Copyright (C) 2007-2009 Canonical Ltd. |
4123 | +# |
4124 | +# See AUTHORS for list of contributors |
4125 | +# |
4126 | +# This program is free software: you can redistribute it and/or modify |
4127 | +# it under the terms of the GNU General Public License version 3, as |
4128 | +# published by the Free Software Foundation. |
4129 | +# |
4130 | +# This program is distributed in the hope that it will be useful, |
4131 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4132 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4133 | +# GNU General Public License for more details. |
4134 | +# |
4135 | +# You should have received a copy of the GNU General Public License |
4136 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4137 | +# |
4138 | + |
4139 | +class Suite(object): |
4140 | + def __init__(self, context): |
4141 | + self.context = context |
4142 | + self.isodir = '/media/vmbuilder_inst_image' |
4143 | + self.iso_mounted = False |
4144 | + |
4145 | + def check_arch_validity(self, arch): |
4146 | + """Checks whether the given arch is valid for this suite""" |
4147 | + raise NotImplemented('Suite subclasses need to implement the check_arch_validity method') |
4148 | |
4149 | === added directory 'VMBuilder/plugins/ubuntu/templates' |
4150 | === added file 'VMBuilder/plugins/ubuntu/templates/51_update-motd-hardy.tmpl' |
4151 | --- VMBuilder/plugins/ubuntu/templates/51_update-motd-hardy.tmpl 1970-01-01 00:00:00 +0000 |
4152 | +++ VMBuilder/plugins/ubuntu/templates/51_update-motd-hardy.tmpl 2011-12-21 11:17:34 +0000 |
4153 | @@ -0,0 +1,10 @@ |
4154 | +#!/bin/sh |
4155 | + |
4156 | +echo "---------------------------------------------------------------------" |
4157 | +echo "At the moment, only the core of the system is installed. To tune the " |
4158 | +echo "system to your needs, you can choose to install one or more " |
4159 | +echo "predefined collections of software by running the following " |
4160 | +echo "command: " |
4161 | +echo " " |
4162 | +echo " sudo tasksel " |
4163 | +echo "---------------------------------------------------------------------" |
4164 | |
4165 | === added file 'VMBuilder/plugins/ubuntu/templates/51_update-motd.tmpl' |
4166 | --- VMBuilder/plugins/ubuntu/templates/51_update-motd.tmpl 1970-01-01 00:00:00 +0000 |
4167 | +++ VMBuilder/plugins/ubuntu/templates/51_update-motd.tmpl 2011-12-21 11:17:34 +0000 |
4168 | @@ -0,0 +1,10 @@ |
4169 | +#!/bin/sh |
4170 | + |
4171 | +echo "---------------------------------------------------------------------" |
4172 | +echo "At the moment, only the core of the system is installed. To tune the " |
4173 | +echo "system to your needs, you can choose to install one or more " |
4174 | +echo "predefined collections of software by running the following " |
4175 | +echo "command: " |
4176 | +echo " " |
4177 | +echo " sudo tasksel --section server " |
4178 | +echo "---------------------------------------------------------------------" |
4179 | |
4180 | === added file 'VMBuilder/plugins/ubuntu/templates/dapper_fstab.tmpl' |
4181 | --- VMBuilder/plugins/ubuntu/templates/dapper_fstab.tmpl 1970-01-01 00:00:00 +0000 |
4182 | +++ VMBuilder/plugins/ubuntu/templates/dapper_fstab.tmpl 2011-12-21 11:17:34 +0000 |
4183 | @@ -0,0 +1,10 @@ |
4184 | +# /etc/fstab: static file system information. |
4185 | +# |
4186 | +# <file system> <mount point> <type> <options> <dump> <pass> |
4187 | +proc /proc proc defaults 0 0 |
4188 | +#for $part in $parts |
4189 | +#echo '/dev/%s%-40s %-15s %-7s %-15s %d %d\n' % ($prefix, part.get_suffix(), part.mntpnt, part.fs.fstab_fstype(), part.fs.fstab_options(), 0, 0) |
4190 | +#* |
4191 | +echo "/dev/$prefix$part.get_suffix() $part.mntpnt $part.fs.fstab_fstype() $part.fs.fstab_options() 0 0 |
4192 | +*# |
4193 | +#end for |
4194 | |
4195 | === added file 'VMBuilder/plugins/ubuntu/templates/dapper_fstab_fsimage.tmpl' |
4196 | --- VMBuilder/plugins/ubuntu/templates/dapper_fstab_fsimage.tmpl 1970-01-01 00:00:00 +0000 |
4197 | +++ VMBuilder/plugins/ubuntu/templates/dapper_fstab_fsimage.tmpl 2011-12-21 11:17:34 +0000 |
4198 | @@ -0,0 +1,10 @@ |
4199 | +# /etc/fstab: static file system information. |
4200 | +# |
4201 | +# <file system> <mount point> <type> <options> <dump> <pass> |
4202 | +proc /proc proc defaults 0 0 |
4203 | +#for $fs in $fss |
4204 | +#echo '/dev/%s%-40s %-15s %-7s %-15s %d %d\n' % ($prefix, fs.get_suffix(), fs.mntpnt, fs.fstab_fstype(), fs.fstab_options(), 0, 0) |
4205 | +#* |
4206 | +echo "/dev/$prefix$part.get_suffix() $part.mntpnt $part.fs.fstab_fstype() $part.fs.fstab_options() 0 0 |
4207 | +*# |
4208 | +#end for |
4209 | |
4210 | === added file 'VMBuilder/plugins/ubuntu/templates/devicemap.tmpl' |
4211 | --- VMBuilder/plugins/ubuntu/templates/devicemap.tmpl 1970-01-01 00:00:00 +0000 |
4212 | +++ VMBuilder/plugins/ubuntu/templates/devicemap.tmpl 2011-12-21 11:17:34 +0000 |
4213 | @@ -0,0 +1,3 @@ |
4214 | +#for $disk in $disks |
4215 | +$disk.get_grub_id() /dev/$prefix$disk.devletters() |
4216 | +#end for |
4217 | |
4218 | === added file 'VMBuilder/plugins/ubuntu/templates/etc_hosts.tmpl' |
4219 | --- VMBuilder/plugins/ubuntu/templates/etc_hosts.tmpl 1970-01-01 00:00:00 +0000 |
4220 | +++ VMBuilder/plugins/ubuntu/templates/etc_hosts.tmpl 2011-12-21 11:17:34 +0000 |
4221 | @@ -0,0 +1,10 @@ |
4222 | +127.0.0.1 localhost |
4223 | +127.0.1.1 $hostname.$domain $hostname |
4224 | + |
4225 | +# The following lines are desirable for IPv6 capable hosts |
4226 | +::1 ip6-localhost ip6-loopback |
4227 | +fe00::0 ip6-localnet |
4228 | +ff00::0 ip6-mcastprefix |
4229 | +ff02::1 ip6-allnodes |
4230 | +ff02::2 ip6-allrouters |
4231 | +ff02::3 ip6-allhosts |
4232 | |
4233 | === added file 'VMBuilder/plugins/ubuntu/templates/initctl-stub.tmpl' |
4234 | --- VMBuilder/plugins/ubuntu/templates/initctl-stub.tmpl 1970-01-01 00:00:00 +0000 |
4235 | +++ VMBuilder/plugins/ubuntu/templates/initctl-stub.tmpl 2011-12-21 11:17:34 +0000 |
4236 | @@ -0,0 +1,3 @@ |
4237 | +#!/bin/sh |
4238 | +echo |
4239 | +echo "Warning: Fake initctl called, doing nothing" |
4240 | |
4241 | === added file 'VMBuilder/plugins/ubuntu/templates/interfaces.tmpl' |
4242 | --- VMBuilder/plugins/ubuntu/templates/interfaces.tmpl 1970-01-01 00:00:00 +0000 |
4243 | +++ VMBuilder/plugins/ubuntu/templates/interfaces.tmpl 2011-12-21 11:17:34 +0000 |
4244 | @@ -0,0 +1,22 @@ |
4245 | +# This file describes the network interfaces available on your system |
4246 | +# and how to activate them. For more information, see interfaces(5). |
4247 | + |
4248 | +# The loopback network interface |
4249 | +auto lo |
4250 | +iface lo inet loopback |
4251 | + |
4252 | +# The primary network interface |
4253 | +auto eth0 |
4254 | +#if $ip == 'dhcp' |
4255 | +iface eth0 inet dhcp |
4256 | +#else |
4257 | +iface eth0 inet static |
4258 | + address $ip |
4259 | + netmask $mask |
4260 | + network $net |
4261 | + broadcast $bcast |
4262 | + gateway $gw |
4263 | + # dns-* options are implemented by the resolvconf package, if installed |
4264 | + dns-nameservers $dns |
4265 | + dns-search $domain |
4266 | +#end if |
4267 | |
4268 | === added file 'VMBuilder/plugins/ubuntu/templates/is-compat-env.tmpl' |
4269 | --- VMBuilder/plugins/ubuntu/templates/is-compat-env.tmpl 1970-01-01 00:00:00 +0000 |
4270 | +++ VMBuilder/plugins/ubuntu/templates/is-compat-env.tmpl 2011-12-21 11:17:34 +0000 |
4271 | @@ -0,0 +1,3 @@ |
4272 | +# this is read by ec2-is-compat-env to determine if this is an |
4273 | +# ec2 compatible environment |
4274 | +compat=1 |
4275 | |
4276 | === added file 'VMBuilder/plugins/ubuntu/templates/kernelimg.tmpl' |
4277 | --- VMBuilder/plugins/ubuntu/templates/kernelimg.tmpl 1970-01-01 00:00:00 +0000 |
4278 | +++ VMBuilder/plugins/ubuntu/templates/kernelimg.tmpl 2011-12-21 11:17:34 +0000 |
4279 | @@ -0,0 +1,8 @@ |
4280 | +do_symlinks = yes |
4281 | +relative_links = yes |
4282 | +do_bootfloppy = no |
4283 | +do_initrd = yes |
4284 | +link_in_boot = no |
4285 | +postinst_hook = $updategrub |
4286 | +postrm_hook = $updategrub |
4287 | +do_bootloader = no |
4288 | |
4289 | === added file 'VMBuilder/plugins/ubuntu/templates/locale.tmpl' |
4290 | --- VMBuilder/plugins/ubuntu/templates/locale.tmpl 1970-01-01 00:00:00 +0000 |
4291 | +++ VMBuilder/plugins/ubuntu/templates/locale.tmpl 2011-12-21 11:17:34 +0000 |
4292 | @@ -0,0 +1,1 @@ |
4293 | +LANG="$lang" |
4294 | |
4295 | === added file 'VMBuilder/plugins/ubuntu/templates/nostart-policy-rc.d.tmpl' |
4296 | --- VMBuilder/plugins/ubuntu/templates/nostart-policy-rc.d.tmpl 1970-01-01 00:00:00 +0000 |
4297 | +++ VMBuilder/plugins/ubuntu/templates/nostart-policy-rc.d.tmpl 2011-12-21 11:17:34 +0000 |
4298 | @@ -0,0 +1,18 @@ |
4299 | +#!/bin/sh |
4300 | + |
4301 | +while true; do |
4302 | + case "$1" in |
4303 | + -*) |
4304 | + shift |
4305 | + ;; |
4306 | + makedev) |
4307 | + exit 0 |
4308 | + ;; |
4309 | + x11-common) |
4310 | + exit 0 |
4311 | + ;; |
4312 | + *) |
4313 | + exit 101 |
4314 | + ;; |
4315 | + esac |
4316 | +done |
4317 | |
4318 | === added file 'VMBuilder/plugins/ubuntu/templates/sources.list.tmpl' |
4319 | --- VMBuilder/plugins/ubuntu/templates/sources.list.tmpl 1970-01-01 00:00:00 +0000 |
4320 | +++ VMBuilder/plugins/ubuntu/templates/sources.list.tmpl 2011-12-21 11:17:34 +0000 |
4321 | @@ -0,0 +1,15 @@ |
4322 | +deb $mirror $suite #slurp |
4323 | +#echo ' '.join($components) |
4324 | + |
4325 | +deb $updates_mirror $suite-updates #slurp |
4326 | +#echo ' '.join($components) |
4327 | + |
4328 | +deb $security_mirror $suite-security #slurp |
4329 | +#echo ' '.join($components) |
4330 | + |
4331 | +#if $ppa |
4332 | +#for $p in $ppa |
4333 | +deb http://ppa.launchpad.net/$p/ubuntu $suite main |
4334 | + |
4335 | +#end for |
4336 | +#end if |
4337 | |
4338 | === added file 'VMBuilder/plugins/ubuntu/templates/sudoers.tmpl' |
4339 | --- VMBuilder/plugins/ubuntu/templates/sudoers.tmpl 1970-01-01 00:00:00 +0000 |
4340 | +++ VMBuilder/plugins/ubuntu/templates/sudoers.tmpl 2011-12-21 11:17:34 +0000 |
4341 | @@ -0,0 +1,23 @@ |
4342 | +# /etc/sudoers |
4343 | +# |
4344 | +# This file MUST be edited with the 'visudo' command as root. |
4345 | +# |
4346 | +# See the man page for details on how to write a sudoers file. |
4347 | +# Defaults |
4348 | + |
4349 | +Defaults !lecture,tty_tickets,!fqdn |
4350 | + |
4351 | +# Uncomment to allow members of group sudo to not need a password |
4352 | +# %sudo ALL=NOPASSWD: ALL |
4353 | + |
4354 | +# Host alias specification |
4355 | + |
4356 | +# User alias specification |
4357 | + |
4358 | +# Cmnd alias specification |
4359 | + |
4360 | +# User privilege specification |
4361 | +root ALL=(ALL) ALL |
4362 | + |
4363 | +# Members of the admin group may gain root privileges |
4364 | +%admin ALL=(ALL) ALL |
4365 | |
4366 | === added file 'VMBuilder/plugins/ubuntu/templates/timezone.tmpl' |
4367 | --- VMBuilder/plugins/ubuntu/templates/timezone.tmpl 1970-01-01 00:00:00 +0000 |
4368 | +++ VMBuilder/plugins/ubuntu/templates/timezone.tmpl 2011-12-21 11:17:34 +0000 |
4369 | @@ -0,0 +1,1 @@ |
4370 | +$timezone |
4371 | |
4372 | === added file 'VMBuilder/plugins/ubuntu/templates/upstart.tmpl' |
4373 | --- VMBuilder/plugins/ubuntu/templates/upstart.tmpl 1970-01-01 00:00:00 +0000 |
4374 | +++ VMBuilder/plugins/ubuntu/templates/upstart.tmpl 2011-12-21 11:17:34 +0000 |
4375 | @@ -0,0 +1,16 @@ |
4376 | +# $console - getty |
4377 | +# |
4378 | +# This service maintains a getty on $console from the point the system is |
4379 | +# started until it is shut down again. |
4380 | + |
4381 | +start on stopped rc2 |
4382 | +start on stopped rc3 |
4383 | +start on stopped rc4 |
4384 | +start on stopped rc5 |
4385 | + |
4386 | +stop on runlevel 0 |
4387 | +stop on runlevel 1 |
4388 | +stop on runlevel 6 |
4389 | + |
4390 | +respawn |
4391 | +exec /sbin/getty 38400 $console |
4392 | |
4393 | === added file 'VMBuilder/plugins/ubuntu/templates/xen-ld-so-conf.tmpl' |
4394 | --- VMBuilder/plugins/ubuntu/templates/xen-ld-so-conf.tmpl 1970-01-01 00:00:00 +0000 |
4395 | +++ VMBuilder/plugins/ubuntu/templates/xen-ld-so-conf.tmpl 2011-12-21 11:17:34 +0000 |
4396 | @@ -0,0 +1,1 @@ |
4397 | +hwcap 0 nosegneg |
4398 | |
4399 | === added directory 'VMBuilder/plugins/ubuntu/tests' |
4400 | === added file 'VMBuilder/plugins/ubuntu/tests/test_distro.py' |
4401 | --- VMBuilder/plugins/ubuntu/tests/test_distro.py 1970-01-01 00:00:00 +0000 |
4402 | +++ VMBuilder/plugins/ubuntu/tests/test_distro.py 2011-12-21 11:17:34 +0000 |
4403 | @@ -0,0 +1,14 @@ |
4404 | +import os |
4405 | +import unittest |
4406 | + |
4407 | +from VMBuilder.plugins.ubuntu import distro |
4408 | + |
4409 | +class TestUbuntuDistro(unittest.TestCase): |
4410 | + def test_get_locale(self): |
4411 | + os.environ['LANG'] = 'foo' |
4412 | + self.assertEqual(distro.get_locale(), 'foo') |
4413 | + os.environ['LANG'] = 'foo.utf8' |
4414 | + self.assertEqual(distro.get_locale(), 'foo.UTF-8') |
4415 | + del os.environ['LANG'] |
4416 | + self.assertEqual(distro.get_locale(), 'C') |
4417 | + |
4418 | |
4419 | === added directory 'VMBuilder/plugins/virtualbox' |
4420 | === added file 'VMBuilder/plugins/virtualbox/__init__.py' |
4421 | --- VMBuilder/plugins/virtualbox/__init__.py 1970-01-01 00:00:00 +0000 |
4422 | +++ VMBuilder/plugins/virtualbox/__init__.py 2011-12-21 11:17:34 +0000 |
4423 | @@ -0,0 +1,19 @@ |
4424 | +# |
4425 | +# Uncomplicated VM Builder |
4426 | +# Copyright (C) 2007-2009 Canonical Ltd. |
4427 | +# |
4428 | +# See AUTHORS for list of contributors |
4429 | +# |
4430 | +# This program is free software: you can redistribute it and/or modify |
4431 | +# it under the terms of the GNU General Public License version 3, as |
4432 | +# published by the Free Software Foundation. |
4433 | +# |
4434 | +# This program is distributed in the hope that it will be useful, |
4435 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4436 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4437 | +# GNU General Public License for more details. |
4438 | +# |
4439 | +# You should have received a copy of the GNU General Public License |
4440 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4441 | +# |
4442 | +import vm |
4443 | |
4444 | === added directory 'VMBuilder/plugins/virtualbox/templates' |
4445 | === added file 'VMBuilder/plugins/virtualbox/templates/vm_deploy_script.tmpl' |
4446 | --- VMBuilder/plugins/virtualbox/templates/vm_deploy_script.tmpl 1970-01-01 00:00:00 +0000 |
4447 | +++ VMBuilder/plugins/virtualbox/templates/vm_deploy_script.tmpl 2011-12-21 11:17:34 +0000 |
4448 | @@ -0,0 +1,65 @@ |
4449 | +#raw |
4450 | +#! /usr/bin/env bash |
4451 | +############################################################################### |
4452 | +# |
4453 | +# This script is used to create und register a new VM |
4454 | +# in VirtualBox |
4455 | +# |
4456 | +############################################################################### |
4457 | +#end raw |
4458 | +#import os |
4459 | +#import os.path |
4460 | + |
4461 | + |
4462 | +#if $os_type == "Ubuntu" |
4463 | +os_type="Ubuntu" |
4464 | +#else |
4465 | +os_type="Other" |
4466 | +#end if |
4467 | + |
4468 | +disk_path="#echo os.path.abspath(os.path.dirname($vm_disks[0]))#" |
4469 | + |
4470 | + |
4471 | +VBoxManage createvm -name $vm_name -ostype \$os_type -register |
4472 | + |
4473 | +VBoxManage openmedium #slurp |
4474 | +#set $i = 0 |
4475 | +#for $disk in $vm_disks |
4476 | + #set $i = $i + 1 |
4477 | + #set $disk = os.path.basename(disk) |
4478 | +disk \${disk_path}/$disk -type normal #slurp |
4479 | +#end for |
4480 | + |
4481 | +VBoxManage modifyvm $vm_name -memory $memory -cpus $cpus -sata on #slurp |
4482 | +#set $i = 0 |
4483 | +#for $disk in $vm_disks |
4484 | + #set $i = $i + 1 |
4485 | + #set $disk = os.path.basename(disk) |
4486 | + #if $i >= 31 |
4487 | + #break |
4488 | + #end if |
4489 | + #if $i == 1 |
4490 | + -hda \${disk_path}/$disk #slurp |
4491 | + #else if $i == 2 |
4492 | + -hdb \${disk_path}/$disk #slurp |
4493 | + #else if $i == 3 |
4494 | + -hdd \${disk_path}/$disk #slurp |
4495 | + #else |
4496 | + -sataport${i} \${disk_path}/$disk #slurp |
4497 | + #end if |
4498 | +#end for |
4499 | + |
4500 | +#if $mac |
4501 | +VBoxManage modifyvm $vm_name -macaddress1 $mac |
4502 | +#end if |
4503 | + |
4504 | +#if $ip |
4505 | +#if $ip == "dhcp" |
4506 | +VBoxManage modifyvm $vm_name -nic1 nat |
4507 | +#else |
4508 | +VBoxManage modifyvm $vm_name -nic1 intnet |
4509 | +#end if |
4510 | +#end if |
4511 | + |
4512 | +#activating PAE support for the CPU because some OS (e.g. ubuntu server ) won't boot in a virtual machine without it |
4513 | +VBoxManage modifyvm $vm_name -pae on |
4514 | |
4515 | === added file 'VMBuilder/plugins/virtualbox/vm.py' |
4516 | --- VMBuilder/plugins/virtualbox/vm.py 1970-01-01 00:00:00 +0000 |
4517 | +++ VMBuilder/plugins/virtualbox/vm.py 2011-12-21 11:17:34 +0000 |
4518 | @@ -0,0 +1,59 @@ |
4519 | +# |
4520 | +# Uncomplicated VM Builder |
4521 | +# Copyright (C) 2007-2009 Canonical Ltd. |
4522 | +# |
4523 | +# See AUTHORS for list of contributors |
4524 | +# |
4525 | +# This program is free software: you can redistribute it and/or modify |
4526 | +# it under the terms of the GNU General Public License version 3, as |
4527 | +# published by the Free Software Foundation. |
4528 | +# |
4529 | +# This program is distributed in the hope that it will be useful, |
4530 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4531 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4532 | +# GNU General Public License for more details. |
4533 | +# |
4534 | +# You should have received a copy of the GNU General Public License |
4535 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4536 | +# |
4537 | + |
4538 | +import os |
4539 | +import os.path |
4540 | +import stat |
4541 | +import VMBuilder |
4542 | +from VMBuilder import register_hypervisor, Hypervisor |
4543 | +from VMBuilder.disk import vbox_manager_path |
4544 | +import VMBuilder.hypervisor |
4545 | + |
4546 | +class VirtualBox(Hypervisor): |
4547 | + preferred_storage = VMBuilder.hypervisor.STORAGE_DISK_IMAGE |
4548 | + needs_bootloader = True |
4549 | + name = 'VirtualBox' |
4550 | + arg = 'vbox' |
4551 | + |
4552 | + def register_options(self): |
4553 | + group = self.setting_group('VirtualBox options') |
4554 | + group.add_setting('mem', extra_args=['-m'], type='int', default=128, help='Assign MEM megabytes of memory to the guest vm. [default: %default]') |
4555 | + group.add_setting('cpus', type='int', default=1, help='Assign NUM cpus to the guest vm. [default: %default]') |
4556 | + group.add_setting('vbox-disk-format', metavar='FORMAT', default='vdi', help='Desired disk format. Valid options are: vdi vmdk. [default: %default]') |
4557 | + |
4558 | + def convert(self, disks, destdir): |
4559 | + self.imgs = [] |
4560 | + for disk in disks: |
4561 | + img_path = disk.convert(destdir, self.context.get_setting('vbox-disk-format')) |
4562 | + self.imgs.append(img_path) |
4563 | + |
4564 | + def deploy(self,destdir): |
4565 | + hostname = self.context.distro.get_setting('hostname') |
4566 | + mac = self.context.get_setting('mac') |
4567 | + ip = self.context.get_setting('ip') |
4568 | + vm_deploy_script = VMBuilder.util.render_template('virtualbox', self.context, 'vm_deploy_script', { 'os_type' : self.context.distro.__class__.__name__, 'vm_name' : hostname, 'vm_disks' : self.imgs, 'memory' : self.context.get_setting('mem'), 'cpus' : self.context.get_setting('cpus') }) |
4569 | + |
4570 | + script_file = '%s/deploy_%s.sh' % (destdir, hostname) |
4571 | + fp = open(script_file, 'w') |
4572 | + fp.write(vm_deploy_script) |
4573 | + fp.close() |
4574 | + os.chmod(script_file, stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH) |
4575 | + self.context.result_files.append(script_file) |
4576 | + |
4577 | +register_hypervisor(VirtualBox) |
4578 | |
4579 | === added directory 'VMBuilder/plugins/vmware' |
4580 | === added file 'VMBuilder/plugins/vmware/__init__.py' |
4581 | --- VMBuilder/plugins/vmware/__init__.py 1970-01-01 00:00:00 +0000 |
4582 | +++ VMBuilder/plugins/vmware/__init__.py 2011-12-21 11:17:34 +0000 |
4583 | @@ -0,0 +1,19 @@ |
4584 | +# |
4585 | +# Uncomplicated VM Builder |
4586 | +# Copyright (C) 2007-2009 Canonical Ltd. |
4587 | +# |
4588 | +# See AUTHORS for list of contributors |
4589 | +# |
4590 | +# This program is free software: you can redistribute it and/or modify |
4591 | +# it under the terms of the GNU General Public License version 3, as |
4592 | +# published by the Free Software Foundation. |
4593 | +# |
4594 | +# This program is distributed in the hope that it will be useful, |
4595 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4596 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4597 | +# GNU General Public License for more details. |
4598 | +# |
4599 | +# You should have received a copy of the GNU General Public License |
4600 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4601 | +# |
4602 | +import vm |
4603 | |
4604 | === added directory 'VMBuilder/plugins/vmware/templates' |
4605 | === added file 'VMBuilder/plugins/vmware/templates/esxi.vmx.tmpl' |
4606 | --- VMBuilder/plugins/vmware/templates/esxi.vmx.tmpl 1970-01-01 00:00:00 +0000 |
4607 | +++ VMBuilder/plugins/vmware/templates/esxi.vmx.tmpl 2011-12-21 11:17:34 +0000 |
4608 | @@ -0,0 +1,57 @@ |
4609 | +config.version = "8" |
4610 | +virtualHW.version = "$vmhwversion" |
4611 | +floppy0.present = "true" |
4612 | +nvram = "nvram" |
4613 | +deploymentPlatform = "windows" |
4614 | +virtualHW.productCompatibility = "hosted" |
4615 | +tools.upgrade.policy = "useGlobal" |
4616 | +powerType.powerOff = "default" |
4617 | +powerType.powerOn = "default" |
4618 | +powerType.suspend = "default" |
4619 | +powerType.reset = "default" |
4620 | + |
4621 | +displayName = "$hostname $arch" |
4622 | + |
4623 | +numvcpus = "$numvcpus" |
4624 | +scsi0.present = "true" |
4625 | +scsi0.sharedBus = "none" |
4626 | +scsi0.virtualDev = "lsilogic" |
4627 | +memsize = "$mem" |
4628 | + |
4629 | +#set $i = -1 |
4630 | +#for $disk in $disks |
4631 | + #set $i = $i + 1 |
4632 | + #set $bus = $i / 2 |
4633 | + #set $id = $i % 2 |
4634 | +scsi${bus}:${id}.present = "true" |
4635 | +scsi${bus}:${id}.fileName = "${disk}.vmdk" |
4636 | +scsi${bus}:${id}.deviceType = "scsi-hardDisk" |
4637 | +#end for |
4638 | + |
4639 | +ide0:0.present = "true" |
4640 | +ide0:0.clientDevice = "TRUE" |
4641 | +ide0:0.deviceType = "cdrom-raw" |
4642 | +ide0:0.startConnected = "FALSE" |
4643 | +floppy0.startConnected = "false" |
4644 | +floppy0.clientDevice = "true" |
4645 | + |
4646 | +ethernet0.present = "true" |
4647 | +ethernet0.virtualDev = "e1000" |
4648 | +ethernet0.networkName = "VM Network" |
4649 | +ethernet0.addressType = "generated" |
4650 | + |
4651 | +guestOSAltName = "$guestos ($arch)" |
4652 | +guestOS = "$guestos" |
4653 | + |
4654 | +toolScripts.afterPowerOn = "true" |
4655 | +toolScripts.afterResume = "true" |
4656 | +toolScripts.beforeSuspend = "true" |
4657 | +toolScripts.beforePowerOff = "true" |
4658 | + |
4659 | +scsi0:0.redo = "" |
4660 | + |
4661 | +tools.syncTime = "FALSE" |
4662 | +ethernet0.generatedAddressOffset = "0" |
4663 | +tools.remindInstall = "TRUE" |
4664 | + |
4665 | +evcCompatibilityMode = "FALSE" |
4666 | |
4667 | === added file 'VMBuilder/plugins/vmware/templates/flat.vmdk.tmpl' |
4668 | --- VMBuilder/plugins/vmware/templates/flat.vmdk.tmpl 1970-01-01 00:00:00 +0000 |
4669 | +++ VMBuilder/plugins/vmware/templates/flat.vmdk.tmpl 2011-12-21 11:17:34 +0000 |
4670 | @@ -0,0 +1,19 @@ |
4671 | +# Disk DescriptorFile for Virtual Disk: $diskname |
4672 | +version=1 |
4673 | +CID=ffffffff |
4674 | +parentCID=ffffffff |
4675 | +createType="vmfs" |
4676 | + |
4677 | +# Extent description |
4678 | +RW $disksize FLAT "$diskname" 0 |
4679 | +# The Disk Data Base |
4680 | +#DDB |
4681 | +ddb.toolsVersion = "0" |
4682 | +ddb.adapterType = "$adaptertype" |
4683 | +ddb.geometry.biosSectors = "$sectors" |
4684 | +ddb.geometry.biosHeads = "255" |
4685 | +ddb.geometry.biosCylinders = "63" |
4686 | +ddb.geometry.sectors = "$sectors" |
4687 | +ddb.geometry.heads = "255" |
4688 | +ddb.geometry.cylinders = "63" |
4689 | +ddb.virtualHWVersion = "4" |
4690 | \ No newline at end of file |
4691 | |
4692 | === added file 'VMBuilder/plugins/vmware/templates/vmware.tmpl' |
4693 | --- VMBuilder/plugins/vmware/templates/vmware.tmpl 1970-01-01 00:00:00 +0000 |
4694 | +++ VMBuilder/plugins/vmware/templates/vmware.tmpl 2011-12-21 11:17:34 +0000 |
4695 | @@ -0,0 +1,30 @@ |
4696 | +config.version = "8" |
4697 | +virtualHW.version = "$vmhwversion" |
4698 | +scsi0.present = "FALSE" |
4699 | +scsi0.virtualDev = "lsilogic" |
4700 | +memsize = "$mem" |
4701 | +numvcpus = "$numvcpus" |
4702 | +Ethernet0.virtualDev = "vlance" |
4703 | +Ethernet0.present = "TRUE" |
4704 | +Ethernet0.connectionType = "bridged" |
4705 | +#if $mac |
4706 | +Ethernet0.addressType = "static" |
4707 | +Ethernet0.address = "$mac" |
4708 | +#end if |
4709 | +displayName = "$hostname $arch" |
4710 | +guestOS = "$guestos" |
4711 | +priority.grabbed = "normal" |
4712 | +priority.ungrabbed = "normal" |
4713 | +powerType.powerOff = "hard" |
4714 | +powerType.powerOn = "hard" |
4715 | +powerType.suspend = "hard" |
4716 | +powerType.reset = "hard" |
4717 | +floppy0.present = "FALSE" |
4718 | +#set $i = -1 |
4719 | +#for $disk in $disks |
4720 | + #set $i = $i + 1 |
4721 | + #set $bus = $i / 2 |
4722 | + #set $id = $i % 2 |
4723 | +ide${bus}:${id}.present = "TRUE" |
4724 | +ide${bus}:${id}.fileName = "${disk.filename}" |
4725 | +#end for |
4726 | |
4727 | === added file 'VMBuilder/plugins/vmware/vm.py' |
4728 | --- VMBuilder/plugins/vmware/vm.py 1970-01-01 00:00:00 +0000 |
4729 | +++ VMBuilder/plugins/vmware/vm.py 2011-12-21 11:17:34 +0000 |
4730 | @@ -0,0 +1,128 @@ |
4731 | +# |
4732 | +# Uncomplicated VM Builder |
4733 | +# Copyright (C) 2007-2009 Canonical Ltd. |
4734 | +# |
4735 | +# See AUTHORS for list of contributors |
4736 | +# |
4737 | +# This program is free software: you can redistribute it and/or modify |
4738 | +# it under the terms of the GNU General Public License version 3, as |
4739 | +# published by the Free Software Foundation. |
4740 | +# |
4741 | +# This program is distributed in the hope that it will be useful, |
4742 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4743 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4744 | +# GNU General Public License for more details. |
4745 | +# |
4746 | +# You should have received a copy of the GNU General Public License |
4747 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4748 | +# |
4749 | +from VMBuilder import register_hypervisor, Hypervisor |
4750 | +import VMBuilder |
4751 | +import VMBuilder.hypervisor |
4752 | +import os |
4753 | +import os.path |
4754 | +import stat |
4755 | +from shutil import move |
4756 | +from math import floor |
4757 | + |
4758 | +class VMWare(Hypervisor): |
4759 | + filetype = 'vmdk' |
4760 | + preferred_storage = VMBuilder.hypervisor.STORAGE_DISK_IMAGE |
4761 | + needs_bootloader = True |
4762 | + vmxtemplate = 'vmware' |
4763 | + |
4764 | + def register_options(self): |
4765 | + group = self.setting_group('VM settings') |
4766 | + group.add_setting('mem', extra_args=['-m'], default='128', help='Assign MEM megabytes of memory to the guest vm. [default: %default]') |
4767 | + group.add_setting('cpus', type='int', default=1, help='Assign NUM cpus to the guest vm. [default: %default]') |
4768 | + |
4769 | + def convert(self, disks, destdir): |
4770 | + self.imgs = [] |
4771 | + for disk in self.get_disks(): |
4772 | + img_path = disk.convert(destdir, self.filetype) |
4773 | + self.imgs.append(img_path) |
4774 | + self.call_hooks('fix_ownership', img_path) |
4775 | + |
4776 | + def get_disks(self): |
4777 | + return self.disks |
4778 | + |
4779 | + def deploy(self, destdir): |
4780 | + mem = self.context.get_setting('mem') |
4781 | + cpus = self.context.get_setting('cpus') |
4782 | + hostname = self.context.distro.get_setting('hostname') |
4783 | + arch = self.context.distro.get_setting('arch') |
4784 | + mac = self.context.get_setting('mac') |
4785 | + vmdesc = VMBuilder.util.render_template('vmware', |
4786 | + self.context, |
4787 | + self.vmxtemplate, |
4788 | + { 'disks' : self.get_disks(), |
4789 | + 'vmhwversion' : self.vmhwversion, |
4790 | + 'mem' : mem, |
4791 | + 'numvcpus' : cpus, |
4792 | + 'hostname' : hostname, |
4793 | + 'arch' : arch, |
4794 | + 'mac' : mac, |
4795 | + 'guestos' : (arch == 'amd64' and 'ubuntu-64' or 'ubuntu') }) |
4796 | + |
4797 | + vmx = '%s/%s.vmx' % (destdir, hostname) |
4798 | + fp = open(vmx, 'w') |
4799 | + fp.write(vmdesc) |
4800 | + fp.close() |
4801 | + os.chmod(vmx, stat.S_IRWXU | stat.S_IRWXU | stat.S_IROTH | stat.S_IXOTH) |
4802 | + self.call_hooks('fix_ownership', vmx) |
4803 | + |
4804 | +class VMWareWorkstation6(VMWare): |
4805 | + name = 'VMWare Workstation 6' |
4806 | + arg = 'vmw6' |
4807 | + vmhwversion = 6 |
4808 | + |
4809 | +class VMWareServer(VMWare): |
4810 | + name = 'VMWare Server' |
4811 | + arg = 'vmserver' |
4812 | + vmhwversion = 4 |
4813 | + |
4814 | +class VMWareEsxi(VMWare): |
4815 | + name = 'VMWare ESXi' |
4816 | + arg = 'esxi' |
4817 | + vmhwversion = 4 |
4818 | + adaptertype = 'lsilogic' # lsilogic | buslogic, ide is not supported by ESXi |
4819 | + vmxtemplate = 'esxi.vmx' |
4820 | + |
4821 | + vmdks = [] # vmdk filenames used when deploying vmx file |
4822 | + |
4823 | + def convert(self, disks, destdir): |
4824 | + self.imgs = [] |
4825 | + for disk in disks: |
4826 | + |
4827 | + # Move raw image to <imagename>-flat.vmdk |
4828 | + diskfilename = os.path.basename(disk.filename) |
4829 | + if '.' in diskfilename: |
4830 | + diskfilename = diskfilename[:diskfilename.rindex('.')] |
4831 | + |
4832 | + flat = '%s/%s-flat.vmdk' % (destdir, diskfilename) |
4833 | + self.vmdks.append(diskfilename) |
4834 | + |
4835 | + move(disk.filename, flat) |
4836 | + |
4837 | + self.call_hooks('fix_ownership', flat) |
4838 | + |
4839 | + # Create disk descriptor file |
4840 | + sectorTotal = disk.size * 2048 |
4841 | + sector = int(floor(sectorTotal / 16065)) # pseudo geometry |
4842 | + |
4843 | + diskdescriptor = VMBuilder.util.render_template('vmware', self.context, 'flat.vmdk', { 'adaptertype' : self.adaptertype, 'sectors' : sector, 'diskname' : os.path.basename(flat), 'disksize' : sectorTotal }) |
4844 | + vmdk = '%s/%s.vmdk' % (destdir, diskfilename) |
4845 | + |
4846 | + fp = open(vmdk, 'w') |
4847 | + fp.write(diskdescriptor) |
4848 | + fp.close() |
4849 | + os.chmod(vmdk, stat.S_IRWXU | stat.S_IRWXU | stat.S_IROTH | stat.S_IXOTH) |
4850 | + |
4851 | + self.call_hooks('fix_ownership', vmdk) |
4852 | + |
4853 | + def get_disks(self): |
4854 | + return self.vmdks |
4855 | + |
4856 | +register_hypervisor(VMWareServer) |
4857 | +register_hypervisor(VMWareWorkstation6) |
4858 | +register_hypervisor(VMWareEsxi) |
4859 | |
4860 | === added directory 'VMBuilder/plugins/xen' |
4861 | === added file 'VMBuilder/plugins/xen/__init__.py' |
4862 | --- VMBuilder/plugins/xen/__init__.py 1970-01-01 00:00:00 +0000 |
4863 | +++ VMBuilder/plugins/xen/__init__.py 2011-12-21 11:17:34 +0000 |
4864 | @@ -0,0 +1,20 @@ |
4865 | +# |
4866 | +# Uncomplicated VM Builder |
4867 | +# Copyright (C) 2007-2009 Canonical Ltd. |
4868 | +# |
4869 | +# See AUTHORS for list of contributors |
4870 | +# |
4871 | +# This program is free software: you can redistribute it and/or modify |
4872 | +# it under the terms of the GNU General Public License version 3, as |
4873 | +# published by the Free Software Foundation. |
4874 | +# |
4875 | +# This program is distributed in the hope that it will be useful, |
4876 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4877 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4878 | +# GNU General Public License for more details. |
4879 | +# |
4880 | +# You should have received a copy of the GNU General Public License |
4881 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4882 | +# |
4883 | +import vm |
4884 | +from vm import Xen |
4885 | |
4886 | === added file 'VMBuilder/plugins/xen/vm.py' |
4887 | --- VMBuilder/plugins/xen/vm.py 1970-01-01 00:00:00 +0000 |
4888 | +++ VMBuilder/plugins/xen/vm.py 2011-12-21 11:17:34 +0000 |
4889 | @@ -0,0 +1,89 @@ |
4890 | +# |
4891 | +# Uncomplicated VM Builder |
4892 | +# Copyright (C) 2007-2009 Canonical Ltd. |
4893 | +# |
4894 | +# See AUTHORS for list of contributors |
4895 | +# |
4896 | +# This program is free software: you can redistribute it and/or modify |
4897 | +# it under the terms of the GNU General Public License version 3, as |
4898 | +# published by the Free Software Foundation. |
4899 | +# |
4900 | +# This program is distributed in the hope that it will be useful, |
4901 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4902 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4903 | +# GNU General Public License for more details. |
4904 | +# |
4905 | +# You should have received a copy of the GNU General Public License |
4906 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4907 | +# |
4908 | +from VMBuilder import register_hypervisor, Hypervisor |
4909 | +from VMBuilder.util import run_cmd |
4910 | +import VMBuilder |
4911 | +import VMBuilder.hypervisor |
4912 | +import logging |
4913 | +import os.path |
4914 | + |
4915 | +class Xen(Hypervisor): |
4916 | + name = 'Xen' |
4917 | + arg = 'xen' |
4918 | + preferred_storage = VMBuilder.hypervisor.STORAGE_FS_IMAGE |
4919 | + needs_bootloader = False |
4920 | + |
4921 | + def register_options(self): |
4922 | + group = self.setting_group('Xen options') |
4923 | + group.add_setting('xen-kernel', metavar='PATH', help='Path to the kernel to use (e.g.: /boot/vmlinux-2.6.27-7-server). Default depends on distribution and suite') |
4924 | + group.add_setting('xen-ramdisk', metavar='PATH', help='Path to the ramdisk to use (e.g.: /boot/initrd.img-2.6.27-7-server). Default depends on distribution and suite.') |
4925 | + group.add_setting('mem', extra_args=['-m'], type='int', default=128, help='Assign MEM megabytes of memory to the guest vm. [default: %default]') |
4926 | + |
4927 | + def convert(self, filesystems, destdir): |
4928 | + destimages = [] |
4929 | + for filesystem in filesystems: |
4930 | + if not filesystem.preallocated: |
4931 | + destfile = '%s/%s' % (destdir, os.path.basename(filesystem.filename)) |
4932 | + logging.info('Moving %s to %s' % (filesystem.filename, destfile)) |
4933 | + run_cmd('cp', '--sparse=always', filesystem.filename, destfile) |
4934 | + self.call_hooks('fix_ownership', destfile) |
4935 | + os.unlink(filesystem.filename) |
4936 | + filesystem.filename = os.path.abspath(destfile) |
4937 | + destimages.append(destfile) |
4938 | + |
4939 | + if not self.context.get_setting('xen-kernel'): |
4940 | + self.context.xen_kernel = self.context.distro.xen_kernel_path() |
4941 | + if not self.context.get_setting('xen-ramdisk'): |
4942 | + self.context.xen_ramdisk = self.context.distro.xen_ramdisk_path() |
4943 | + |
4944 | + xenconf = '%s/xen.conf' % destdir |
4945 | + fp = open(xenconf, 'w') |
4946 | + fp.write(""" |
4947 | +# Configuration file for the Xen instance %s, created |
4948 | +# by VMBuilder |
4949 | +kernel = '%s' |
4950 | +ramdisk = '%s' |
4951 | +memory = %d |
4952 | + |
4953 | +root = '/dev/xvda1 ro' |
4954 | +disk = [ |
4955 | +%s |
4956 | +] |
4957 | + |
4958 | +name = '%s' |
4959 | + |
4960 | +dhcp = 'dhcp' |
4961 | +vif = [''] |
4962 | + |
4963 | +on_poweroff = 'destroy' |
4964 | +on_reboot = 'restart' |
4965 | +on_crash = 'restart' |
4966 | + |
4967 | +extra = 'xencons=tty console=tty1 console=hvc0' |
4968 | + |
4969 | +""" % (self.context.distro.get_setting('hostname'), |
4970 | + self.context.get_setting('xen-kernel'), |
4971 | + self.context.get_setting('xen-ramdisk'), |
4972 | + self.context.get_setting('mem'), |
4973 | + ',\n'.join(["'tap:aio:%s,xvda%d,w'" % (os.path.abspath(img), id+1) for (img, id) in zip(destimages, range(len(destimages)))]), |
4974 | + self.context.distro.get_setting('hostname'))) |
4975 | + fp.close() |
4976 | + self.call_hooks('fix_ownership', xenconf) |
4977 | + |
4978 | +register_hypervisor(Xen) |
4979 | |
4980 | === added directory 'VMBuilder/tests' |
4981 | === added file 'VMBuilder/tests/__init__.py' |
4982 | --- VMBuilder/tests/__init__.py 1970-01-01 00:00:00 +0000 |
4983 | +++ VMBuilder/tests/__init__.py 2011-12-21 11:17:34 +0000 |
4984 | @@ -0,0 +1,17 @@ |
4985 | +# |
4986 | +# Uncomplicated VM Builder |
4987 | +# Copyright (C) 2007-2009 Canonical Ltd. |
4988 | +# |
4989 | +# See AUTHORS for list of contributors |
4990 | +# |
4991 | +# This program is free software: you can redistribute it and/or modify |
4992 | +# it under the terms of the GNU General Public License version 3, as |
4993 | +# published by the Free Software Foundation. |
4994 | +# |
4995 | +# This program is distributed in the hope that it will be useful, |
4996 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4997 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4998 | +# GNU General Public License for more details. |
4999 | +# |
5000 | +# You should have received a copy of the GNU General Public License |
Hi,
the merge submission was targeted at the wrong tree, however I've incorporated it into lp:vmbuilder.