1
# Uncomplicated VM Builder
2
# Copyright (C) 2007-2009 Canonical Ltd.
4
# See AUTHORS for list of contributors
6
# This program is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License version 3, as
8
# published by the Free Software Foundation.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program. If not, see <http://www.gnu.org/licenses/>.
25
import VMBuilder.util as util
26
from VMBuilder.disk import parse_size
27
import VMBuilder.hypervisor
33
optparser = optparse.OptionParser()
35
self.set_usage(optparser)
37
optparser.add_option('--version', action='callback', callback=self.versioninfo, help='Show version information')
39
group = optparse.OptionGroup(optparser, 'Build options')
40
group.add_option('--config', '-c', type='str', help='Configuration file')
41
group.add_option('--destdir', '-d', type='str', help='Destination directory')
42
group.add_option('--only-chroot', action='store_true', help="Only build the chroot. Don't install it on disk images or anything.")
43
group.add_option('--existing-chroot', help="Use existing chroot.")
44
optparser.add_option_group(group)
46
group = optparse.OptionGroup(optparser, 'Disk')
47
group.add_option('--rootsize', metavar='SIZE', default=4096, help='Size (in MB) of the root filesystem [default: %default]')
48
group.add_option('--optsize', metavar='SIZE', default=0, help='Size (in MB) of the /opt filesystem. If not set, no /opt filesystem will be added.')
49
group.add_option('--swapsize', metavar='SIZE', default=1024, help='Size (in MB) of the swap partition [default: %default]')
50
group.add_option('--raw', metavar='PATH', type='str', help="Specify a file (or block device) to as first disk image.")
51
group.add_option('--part', metavar='PATH', type='str', help="Allows to specify a partition table in PATH each line of partfile should specify (root first): \n mountpoint size \none per line, separated by space, where size is in megabytes. You can have up to 4 virtual disks, a new disk starts on a line containing only '---'. ie: \n root 2000 \n /boot 512 \n swap 1000 \n --- \n /var 8000 \n /var/log 2000")
52
optparser.add_option_group(group)
54
distro_name = sys.argv[2]
55
distro_class = VMBuilder.get_distro(distro_name)
56
distro = distro_class()
57
self.add_settings_from_context(optparser, distro)
59
hypervisor_name = sys.argv[1]
60
hypervisor_class = VMBuilder.get_hypervisor(hypervisor_name)
61
hypervisor = hypervisor_class(distro)
62
hypervisor.register_hook('fix_ownership', self.fix_ownership)
63
self.add_settings_from_context(optparser, hypervisor)
65
(self.options, args) = optparser.parse_args(sys.argv[2:])
66
for option in dir(self.options):
67
if option.startswith('_') or option in ['ensure_value', 'read_module', 'read_file']:
69
val = getattr(self.options, option)
71
if distro.has_setting(option):
72
distro.set_setting(option, val)
73
elif hypervisor.has_setting(option):
74
hypervisor.set_setting(option, val)
76
if self.options.existing_chroot:
77
distro.set_chroot_dir(self.options.existing_chroot)
78
distro.call_hooks('preflight_check')
80
chroot_dir = util.tmpdir()
81
distro.set_chroot_dir(chroot_dir)
84
if self.options.only_chroot:
85
print 'Chroot can be found in %s' % distro.chroot_dir
88
self.set_disk_layout(hypervisor)
89
hypervisor.install_os()
91
destdir = self.options.destdir or ('%s-%s' % (distro_name, hypervisor_name))
93
self.fix_ownership(destdir)
94
hypervisor.finalise(destdir)
97
def fix_ownership(self, filename):
99
Change ownership of file to $SUDO_USER.
102
@param path: file or directory to give to $SUDO_USER
104
if 'SUDO_USER' in os.environ:
105
logging.debug('Changing ownership of %s to %s' % (filename, os.environ['SUDO_USER']))
106
(uid, gid) = pwd.getpwnam(os.environ['SUDO_USER'])[2:4]
107
os.chown(filename, uid, gid)
109
def add_settings_from_context(self, optparser, context):
110
setting_groups = set([setting.setting_group for setting in context._config.values()])
111
for setting_group in setting_groups:
112
optgroup = optparse.OptionGroup(optparser, setting_group.name)
113
for setting in setting_group._settings:
114
args = ['--%s' % setting.name]
115
args += setting.extra_args
118
kwargs['help'] = setting.help
119
if len(setting.extra_args) > 0:
120
setting.help += " Config option: %s" % setting.name
122
kwargs['metavar'] = setting.metavar
123
if setting.get_default():
124
kwargs['default'] = setting.get_default()
125
if type(setting) == VMBuilder.plugins.Plugin.BooleanSetting:
126
kwargs['action'] = 'store_true'
127
if type(setting) == VMBuilder.plugins.Plugin.ListSetting:
128
kwargs['action'] = 'append'
129
optgroup.add_option(*args, **kwargs)
130
optparser.add_option_group(optgroup)
132
def versioninfo(self, option, opt, value, parser):
133
print '%(major)d.%(minor)d.%(micro)s.r%(revno)d' % VMBuilder.get_version_info()
136
def set_usage(self, optparser):
137
optparser.set_usage('%prog hypervisor distro [options]')
138
# optparser.arg_help = (('hypervisor', vm.hypervisor_help), ('distro', vm.distro_help))
140
def handle_args(self, vm, args):
142
vm.optparser.error("You need to specify at least the hypervisor type and the distro")
143
self.hypervisor = vm.get_hypervisor(args[0])
144
self.distro = distro.vm.get_distro(args[1])
146
def set_disk_layout(self, hypervisor):
147
default_filesystem = hypervisor.distro.preferred_filesystem()
148
if not self.options.part:
149
rootsize = parse_size(self.options.rootsize)
150
swapsize = parse_size(self.options.swapsize)
151
optsize = parse_size(self.options.optsize)
152
if hypervisor.preferred_storage == VMBuilder.hypervisor.STORAGE_FS_IMAGE:
153
hypervisor.add_filesystem(size='%dM' % rootsize, type='ext3', mntpnt='/')
154
hypervisor.add_filesystem(size='%dM' % swapsize, type='swap', mntpnt=None)
156
hypervisor.add_filesystem(size='%dM' % optsize, type='ext3', mntpnt='/opt')
159
disk = hypervisor.add_disk(filename=self.options.raw, preallocated=True)
161
size = rootsize + swapsize + optsize
162
tmpfile = util.tmpfile(keep=False)
163
disk = hypervisor.add_disk(tmpfile, size='%dM' % size)
165
disk.add_part(offset, rootsize, default_filesystem, '/')
167
disk.add_part(offset, swapsize, 'swap', 'swap')
170
disk.add_part(offset, optsize, default_filesystem, '/opt')
172
# We need to parse the file specified
173
if vm.hypervisor.preferred_storage == VMBuilder.hypervisor.STORAGE_FS_IMAGE:
175
for line in file(self.options.part):
176
elements = line.strip().split(' ')
177
if elements[0] == 'root':
178
vm.add_filesystem(elements[1], default_filesystem, mntpnt='/')
179
elif elements[0] == 'swap':
180
vm.add_filesystem(elements[1], type='swap', mntpnt=None)
181
elif elements[0] == '---':
182
# We just ignore the user's attempt to specify multiple disks
184
elif len(elements) == 3:
185
vm.add_filesystem(elements[1], type=default_filesystem, mntpnt=elements[0], devletter='', device=elements[2], dummy=(int(elements[1]) == 0))
187
vm.add_filesystem(elements[1], type=default_filesystem, mntpnt=elements[0])
189
except IOError, (errno, strerror):
190
vm.optparser.error("%s parsing --part option: %s" % (errno, strerror))
195
for line in file(part):
196
pair = line.strip().split(' ',1)
198
self.do_disk(vm, curdisk, size)
202
logging.debug("part: %s, size: %d" % (pair[0], int(pair[1])))
203
curdisk.append((pair[0], pair[1]))
206
self.do_disk(vm, curdisk, size)
208
except IOError, (errno, strerror):
209
vm.optparser.error("%s parsing --part option: %s" % (errno, strerror))
211
def do_disk(self, hypervisor, curdisk, size):
212
default_filesystem = hypervisor.distro.preferred_filesystem()
213
disk = hypervisor.add_disk(size+1)
214
logging.debug("do_disk - size: %d" % size)
217
logging.debug("do_disk - part: %s, size: %s, offset: %d" % (pair[0], pair[1], offset))
218
if pair[0] == 'root':
219
disk.add_part(offset, int(pair[1]), default_filesystem, '/')
220
elif pair[0] == 'swap':
221
disk.add_part(offset, int(pair[1]), pair[0], pair[0])
223
disk.add_part(offset, int(pair[1]), default_filesystem, pair[0])
224
offset += int(pair[1])
227
arg = 'ubuntu-vm-builder'
229
def set_usage(self, vm):
230
optparser.set_usage('%prog hypervisor suite [options]')
231
optparser.arg_help = (('hypervisor', vm.hypervisor_help), ('suite', self.suite_help))
233
def suite_help(self):
234
return 'Suite. Valid options: %s' % " ".join(VMBuilder.plugins.ubuntu.distro.Ubuntu.suites)
236
def handle_args(self, vm, args):
238
vm.optparser.error("You need to specify at least the hypervisor type and the suite")
239
vm.set_hypervisor(args[0])
240
vm.set_distro('ubuntu')