Merge lp:~louis/ubuntu/precise/vm-builder/lp531599 into lp:ubuntu/precise/vm-builder

Proposed by Louis Bouchard
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
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.

To post a comment you must log in.
Revision history for this message
Serge Hallyn (serge-hallyn) wrote :

Hi,

the merge submission was targeted at the wrong tree, however I've incorporated it into lp:vmbuilder.

review: Needs Resubmitting
Revision history for this message
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

Merge lp:~barry/vmbuilder/bug-612082

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

merged lp:~smoser/vmbuilder/lp795358

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches