1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import logging
20 import optparse
21 import os
22 import pwd
23 import shutil
24 import sys
25 import tempfile
26 import VMBuilder
27 import VMBuilder.util as util
28 from VMBuilder.disk import parse_size
29 import VMBuilder.hypervisor
30 from VMBuilder.exception import VMBuilderUserError, VMBuilderException
31
33 arg = 'cli'
34
36 tmpfs_mount_point = None
37 try:
38 optparser = optparse.OptionParser()
39
40 self.set_usage(optparser)
41
42 optparser.add_option('--version',
43 action='callback',
44 callback=self.versioninfo,
45 help='Show version information')
46
47 group = optparse.OptionGroup(optparser, 'Build options')
48 group.add_option('--debug',
49 action='callback',
50 callback=self.set_verbosity,
51 help='Show debug information')
52 group.add_option('--verbose',
53 '-v',
54 action='callback',
55 callback=self.set_verbosity,
56 help='Show progress information')
57 group.add_option('--quiet',
58 '-q',
59 action='callback',
60 callback=self.set_verbosity,
61 help='Silent operation')
62 group.add_option('--overwrite',
63 '-o',
64 action='store_true',
65 help='Remove destination directory before starting build')
66 group.add_option('--config',
67 '-c',
68 type='str',
69 help='Configuration file')
70 group.add_option('--templates',
71 metavar='DIR',
72 help='Prepend DIR to template search path.')
73 group.add_option('--destdir',
74 '-d',
75 type='str',
76 help='Destination directory')
77 group.add_option('--only-chroot',
78 action='store_true',
79 help=("Only build the chroot. Don't install it "
80 "on disk images or anything."))
81 group.add_option('--chroot-dir',
82 help="Build the chroot in directory.")
83 group.add_option('--existing-chroot',
84 help="Use existing chroot.")
85 group.add_option('--tmp',
86 '-t',
87 metavar='DIR',
88 dest='tmp_root',
89 default=tempfile.gettempdir(),
90 help=('Use TMP as temporary working space for '
91 'image generation. Defaults to $TMPDIR if '
92 'it is defined or /tmp otherwise. '
93 '[default: %default]'))
94 group.add_option('--tmpfs',
95 metavar="SIZE",
96 help=('Use a tmpfs as the working directory, '
97 'specifying its size or "-" to use tmpfs '
98 'default (suid,dev,size=1G).'))
99 optparser.add_option_group(group)
100
101 group = optparse.OptionGroup(optparser, 'Disk')
102 group.add_option('--rootsize',
103 metavar='SIZE',
104 default=4096,
105 help=('Size (in MB) of the root filesystem '
106 '[default: %default]'))
107 group.add_option('--optsize',
108 metavar='SIZE',
109 default=0,
110 help=('Size (in MB) of the /opt filesystem. If not'
111 ' set, no /opt filesystem will be added.'))
112 group.add_option('--swapsize',
113 metavar='SIZE',
114 default=1024,
115 help=('Size (in MB) of the swap partition '
116 '[default: %default]'))
117 group.add_option('--raw',
118 metavar='PATH',
119 type='str',
120 action='append',
121 help=("Specify a file (or block device) to use as "
122 "first disk image (can be specified multiple"
123 " times)."))
124 group.add_option('--part',
125 metavar='PATH',
126 type='str',
127 help=("Specify a partition table in PATH. Each "
128 "line of partfile should specify (root "
129 "first): \n mountpoint size \none per "
130 "line, separated by space, where size is "
131 "in megabytes. You can have up to 4 "
132 "virtual disks, a new disk starts on a "
133 "line containing only '---'. ie: \n root "
134 "2000 \n /boot 512 \n swap 1000 \n "
135 "--- \n /var 8000 \n /var/log 2000"))
136 optparser.add_option_group(group)
137
138 optparser.disable_interspersed_args()
139 (dummy, args) = optparser.parse_args(sys.argv[1:])
140 optparser.enable_interspersed_args()
141
142 hypervisor, distro = self.handle_args(optparser, args)
143
144 self.add_settings_from_context(optparser, distro)
145 self.add_settings_from_context(optparser, hypervisor)
146
147 hypervisor.register_hook('fix_ownership', self.fix_ownership)
148
149 config_files = ['/etc/vmbuilder.cfg',
150 os.path.expanduser('~/.vmbuilder.cfg')]
151 (self.options, args) = optparser.parse_args(sys.argv[2:])
152
153 if os.geteuid() != 0:
154 raise VMBuilderUserError('Must run as root')
155
156 distro.overwrite = hypervisor.overwrite = self.options.overwrite
157 destdir = self.options.destdir or ('%s-%s' % (distro.arg,
158 hypervisor.arg))
159
160 if self.options.tmpfs and self.options.chroot_dir:
161 raise VMBuilderUserError('--chroot-dir and --tmpfs can not be used together.')
162
163 if os.path.exists(destdir):
164 if os.path.realpath(destdir) == os.getcwd():
165 raise VMBuilderUserError('Current working directory cannot be used as a destination directory')
166 if self.options.overwrite:
167 logging.debug('%s existed, but -o was specified. '
168 'Nuking it.' % destdir)
169 shutil.rmtree(destdir)
170 else:
171 raise VMBuilderUserError('%s already exists' % destdir)
172
173 if self.options.config:
174 config_files.append(self.options.config)
175 util.apply_config_files_to_context(config_files, distro)
176 util.apply_config_files_to_context(config_files, hypervisor)
177
178 if self.options.templates:
179 distro.template_dirs.insert(0, '%s/%%s'
180 % self.options.templates)
181 hypervisor.template_dirs.insert(0, '%s/%%s'
182 % self.options.templates)
183
184 for option in dir(self.options):
185 if option.startswith('_') or option in ['ensure_value',
186 'read_module',
187 'read_file']:
188 continue
189 val = getattr(self.options, option)
190 option = option.replace('_', '-')
191 if val:
192 if (distro.has_setting(option) and
193 distro.get_setting_default(option) != val):
194 distro.set_setting_fuzzy(option, val)
195 elif (hypervisor.has_setting(option) and
196 hypervisor.get_setting_default(option) != val):
197 hypervisor.set_setting_fuzzy(option, val)
198
199 chroot_dir = None
200 if self.options.existing_chroot:
201 distro.set_chroot_dir(self.options.existing_chroot)
202 distro.call_hooks('preflight_check')
203 else:
204 if self.options.tmpfs is not None:
205 if str(self.options.tmpfs) == '-':
206 tmpfs_size = 1024
207 else:
208 tmpfs_size = int(self.options.tmpfs)
209 tmpfs_mount_point = util.set_up_tmpfs(
210 tmp_root=self.options.tmp_root, size=tmpfs_size)
211 chroot_dir = tmpfs_mount_point
212 elif self.options.chroot_dir:
213 os.mkdir(self.options.chroot_dir)
214 chroot_dir = self.options.chroot_dir
215 else:
216 chroot_dir = util.tmpdir(tmp_root=self.options.tmp_root)
217 distro.set_chroot_dir(chroot_dir)
218 distro.build_chroot()
219
220 if self.options.only_chroot:
221 print 'Chroot can be found in %s' % distro.chroot_dir
222 sys.exit(0)
223
224 self.set_disk_layout(optparser, hypervisor)
225 hypervisor.install_os()
226
227 os.mkdir(destdir)
228 self.fix_ownership(destdir)
229 hypervisor.finalise(destdir)
230
231
232
233
234 if chroot_dir is not None and tmpfs_mount_point is None:
235 util.run_cmd('rm', '-rf', '--one-file-system', chroot_dir)
236 except VMBuilderException, e:
237 logging.error(e)
238 raise
239 finally:
240 if tmpfs_mount_point is not None:
241 util.clean_up_tmpfs(tmpfs_mount_point)
242 util.run_cmd('rmdir', tmpfs_mount_point)
243
245 """
246 Change ownership of file to $SUDO_USER.
247
248 @type path: string
249 @param path: file or directory to give to $SUDO_USER
250 """
251 if 'SUDO_USER' in os.environ:
252 logging.debug('Changing ownership of %s to %s' %
253 (filename, os.environ['SUDO_USER']))
254 (uid, gid) = pwd.getpwnam(os.environ['SUDO_USER'])[2:4]
255 os.chown(filename, uid, gid)
256
257 - def add_settings_from_context(self, optparser, context):
258 setting_groups = set([setting.setting_group for setting
259 in context._config.values()])
260 for setting_group in setting_groups:
261 optgroup = optparse.OptionGroup(optparser, setting_group.name)
262 for setting in setting_group._settings:
263 args = ['--%s' % setting.name]
264 args += setting.extra_args
265 kwargs = {}
266 if setting.help:
267 kwargs['help'] = setting.help
268 if len(setting.extra_args) > 0:
269 setting.help += " Config option: %s" % setting.name
270 if setting.metavar:
271 kwargs['metavar'] = setting.metavar
272 if setting.get_default():
273 kwargs['default'] = setting.get_default()
274 if type(setting) == VMBuilder.plugins.Plugin.BooleanSetting:
275 kwargs['action'] = 'store_true'
276 if type(setting) == VMBuilder.plugins.Plugin.ListSetting:
277 kwargs['action'] = 'append'
278 optgroup.add_option(*args, **kwargs)
279 optparser.add_option_group(optgroup)
280
285
287 optparser.set_usage('%prog hypervisor distro [options]')
288
289
297
305
307 default_filesystem = hypervisor.distro.preferred_filesystem()
308 if not self.options.part:
309 rootsize = parse_size(self.options.rootsize)
310 swapsize = parse_size(self.options.swapsize)
311 optsize = parse_size(self.options.optsize)
312 if hypervisor.preferred_storage == VMBuilder.hypervisor.STORAGE_FS_IMAGE:
313 tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
314 hypervisor.add_filesystem(filename=tmpfile,
315 size='%dM' % rootsize,
316 type='ext3',
317 mntpnt='/')
318 if swapsize > 0:
319 tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
320 hypervisor.add_filesystem(filename=tmpfile,
321 size='%dM' % swapsize,
322 type='swap',
323 mntpnt=None)
324 if optsize > 0:
325 tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
326 hypervisor.add_filesystem(filename=tmpfile,
327 size='%dM' % optsize,
328 type='ext3',
329 mntpnt='/opt')
330 else:
331 if self.options.raw:
332 for raw_disk in self.options.raw:
333 hypervisor.add_disk(filename=raw_disk)
334 disk = hypervisor.disks[0]
335 else:
336 size = rootsize + swapsize + optsize
337 tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
338 disk = hypervisor.add_disk(tmpfile, size='%dM' % size)
339 offset = 0
340 disk.add_part(offset, rootsize, default_filesystem, '/')
341 offset += rootsize
342 if swapsize > 0:
343 disk.add_part(offset, swapsize, 'swap', 'swap')
344 offset += swapsize
345 if optsize > 0:
346 disk.add_part(offset, optsize, default_filesystem, '/opt')
347 else:
348
349 if hypervisor.preferred_storage == VMBuilder.hypervisor.STORAGE_FS_IMAGE:
350 try:
351 for line in file(self.options.part):
352 elements = line.strip().split(' ')
353 if len(elements) < 4:
354 tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
355 else:
356 tmpfile = elements[3]
357
358 if elements[0] == 'root':
359 hypervisor.add_filesystem(elements[1],
360 default_filesystem,
361 filename=tmpfile,
362 mntpnt='/')
363 elif elements[0] == 'swap':
364 hypervisor.add_filesystem(elements[1],
365 type='swap',
366 filename=tmpfile,
367 mntpnt=None)
368 elif elements[0] == '---':
369
370 pass
371 elif len(elements) == 3:
372 hypervisor.add_filesystem(elements[1],
373 type=default_filesystem,
374 filename=tmpfile,
375 mntpnt=elements[0],
376 devletter='',
377 device=elements[2],
378 dummy=(int(elements[1]) == 0))
379 else:
380 hypervisor.add_filesystem(elements[1],
381 type=default_filesystem,
382 filename=tmpfile,
383 mntpnt=elements[0])
384 except IOError, (errno, strerror):
385 optparser.error("%s parsing --part option: %s" %
386 (errno, strerror))
387 else:
388 try:
389 curdisk = list()
390 size = 0
391 disk_idx = 0
392 for line in file(self.options.part):
393 pair = line.strip().split(' ',1)
394 if pair[0] == '---':
395 self.do_disk(hypervisor, curdisk, size, disk_idx)
396 curdisk = list()
397 size = 0
398 disk_idx += 1
399 elif pair[0] != '':
400 logging.debug("part: %s, size: %d" % (pair[0],
401 int(pair[1])))
402 curdisk.append((pair[0], pair[1]))
403 size += int(pair[1])
404
405 self.do_disk(hypervisor, curdisk, size, disk_idx)
406
407 except IOError, (errno, strerror):
408 optparser.error("%s parsing --part option: %s" %
409 (errno, strerror))
410
411 - def do_disk(self, hypervisor, curdisk, size, disk_idx):
412 default_filesystem = hypervisor.distro.preferred_filesystem()
413
414 if self.options.raw:
415 disk = hypervisor.add_disk(filename=self.options.raw[disk_idx])
416 else:
417 disk = hypervisor.add_disk(
418 util.tmp_filename(tmp_root=self.options.tmp_root),
419 size+1)
420
421 logging.debug("do_disk #%i - size: %d" % (disk_idx, size))
422 offset = 0
423 for pair in curdisk:
424 logging.debug("do_disk #%i - part: %s, size: %s, offset: %d" %
425 (disk_idx, pair[0], pair[1], offset))
426 if pair[0] == 'root':
427 disk.add_part(offset, int(pair[1]), default_filesystem, '/')
428 elif pair[0] == 'swap':
429 disk.add_part(offset, int(pair[1]), pair[0], pair[0])
430 else:
431 disk.add_part(offset, int(pair[1]), default_filesystem, pair[0])
432 offset += int(pair[1])
433
435 arg = 'ubuntu-vm-builder'
436
438 optparser.set_usage('%prog hypervisor suite [options]')
439
440
444
453