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='Configuration file')
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 self.options.overwrite:
165 logging.debug('%s existed, but -o was specified. '
166 'Nuking it.' % destdir)
167 shutil.rmtree(destdir)
168 else:
169 raise VMBuilderUserError('%s already exists' % destdir)
170
171 if self.options.config:
172 config_files.append(self.options.config)
173 util.apply_config_files_to_context(config_files, distro)
174 util.apply_config_files_to_context(config_files, hypervisor)
175
176 if self.options.templates:
177 distro.template_dirs.insert(0, '%s/%%s'
178 % self.options.templates)
179 hypervisor.template_dirs.insert(0, '%s/%%s'
180 % self.options.templates)
181
182 for option in dir(self.options):
183 if option.startswith('_') or option in ['ensure_value',
184 'read_module',
185 'read_file']:
186 continue
187 val = getattr(self.options, option)
188 option = option.replace('_', '-')
189 if val:
190 if (distro.has_setting(option) and
191 distro.get_setting_default(option) != val):
192 distro.set_setting_fuzzy(option, val)
193 elif (hypervisor.has_setting(option) and
194 hypervisor.get_setting_default(option) != val):
195 hypervisor.set_setting_fuzzy(option, val)
196
197 chroot_dir = None
198 if self.options.existing_chroot:
199 distro.set_chroot_dir(self.options.existing_chroot)
200 distro.call_hooks('preflight_check')
201 else:
202 if self.options.tmpfs is not None:
203 if str(self.options.tmpfs) == '-':
204 tmpfs_size = 1024
205 else:
206 tmpfs_size = int(self.options.tmpfs)
207 tmpfs_mount_point = util.set_up_tmpfs(
208 tmp_root=self.options.tmp_root, size=tmpfs_size)
209 chroot_dir = tmpfs_mount_point
210 elif self.options.chroot_dir:
211 os.mkdir(self.options.chroot_dir)
212 chroot_dir = self.options.chroot_dir
213 else:
214 chroot_dir = util.tmpdir(tmp_root=self.options.tmp_root)
215 distro.set_chroot_dir(chroot_dir)
216 distro.build_chroot()
217
218 if self.options.only_chroot:
219 print 'Chroot can be found in %s' % distro.chroot_dir
220 sys.exit(0)
221
222 self.set_disk_layout(hypervisor)
223 hypervisor.install_os()
224
225 os.mkdir(destdir)
226 self.fix_ownership(destdir)
227 hypervisor.finalise(destdir)
228
229
230
231
232 if chroot_dir is not None and tmpfs_mount_point is None:
233 util.run_cmd('rm', '-rf', '--one-file-system', chroot_dir)
234 except VMBuilderException, e:
235 logging.error(e)
236 raise
237 finally:
238 if tmpfs_mount_point is not None:
239 util.clean_up_tmpfs(tmpfs_mount_point)
240 util.run_cmd('rmdir', tmpfs_mount_point)
241
243 """
244 Change ownership of file to $SUDO_USER.
245
246 @type path: string
247 @param path: file or directory to give to $SUDO_USER
248 """
249 if 'SUDO_USER' in os.environ:
250 logging.debug('Changing ownership of %s to %s' %
251 (filename, os.environ['SUDO_USER']))
252 (uid, gid) = pwd.getpwnam(os.environ['SUDO_USER'])[2:4]
253 os.chown(filename, uid, gid)
254
255 - def add_settings_from_context(self, optparser, context):
256 setting_groups = set([setting.setting_group for setting
257 in context._config.values()])
258 for setting_group in setting_groups:
259 optgroup = optparse.OptionGroup(optparser, setting_group.name)
260 for setting in setting_group._settings:
261 args = ['--%s' % setting.name]
262 args += setting.extra_args
263 kwargs = {}
264 if setting.help:
265 kwargs['help'] = setting.help
266 if len(setting.extra_args) > 0:
267 setting.help += " Config option: %s" % setting.name
268 if setting.metavar:
269 kwargs['metavar'] = setting.metavar
270 if setting.get_default():
271 kwargs['default'] = setting.get_default()
272 if type(setting) == VMBuilder.plugins.Plugin.BooleanSetting:
273 kwargs['action'] = 'store_true'
274 if type(setting) == VMBuilder.plugins.Plugin.ListSetting:
275 kwargs['action'] = 'append'
276 optgroup.add_option(*args, **kwargs)
277 optparser.add_option_group(optgroup)
278
283
285 optparser.set_usage('%prog hypervisor distro [options]')
286
287
295
303
305 default_filesystem = hypervisor.distro.preferred_filesystem()
306 if not self.options.part:
307 rootsize = parse_size(self.options.rootsize)
308 swapsize = parse_size(self.options.swapsize)
309 optsize = parse_size(self.options.optsize)
310 if hypervisor.preferred_storage == VMBuilder.hypervisor.STORAGE_FS_IMAGE:
311 tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
312 hypervisor.add_filesystem(filename=tmpfile,
313 size='%dM' % rootsize,
314 type='ext3',
315 mntpnt='/')
316 if swapsize > 0:
317 tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
318 hypervisor.add_filesystem(filename=tmpfile,
319 size='%dM' % swapsize,
320 type='swap',
321 mntpnt=None)
322 if optsize > 0:
323 tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
324 hypervisor.add_filesystem(filename=tmpfile,
325 size='%dM' % optsize,
326 type='ext3',
327 mntpnt='/opt')
328 else:
329 if self.options.raw:
330 for raw_disk in self.options.raw:
331 hypervisor.add_disk(filename=raw_disk)
332 disk = hypervisor.disks[0]
333 else:
334 size = rootsize + swapsize + optsize
335 tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
336 disk = hypervisor.add_disk(tmpfile, size='%dM' % size)
337 offset = 0
338 disk.add_part(offset, rootsize, default_filesystem, '/')
339 offset += rootsize
340 if swapsize > 0:
341 disk.add_part(offset, swapsize, 'swap', 'swap')
342 offset += swapsize
343 if optsize > 0:
344 disk.add_part(offset, optsize, default_filesystem, '/opt')
345 else:
346
347 if hypervisor.preferred_storage == VMBuilder.hypervisor.STORAGE_FS_IMAGE:
348 try:
349 for line in file(self.options.part):
350 elements = line.strip().split(' ')
351 if len(elements) < 4:
352 tmpfile = util.tmp_filename(tmp_root=self.options.tmp_root)
353 else:
354 tmpfile = elements[3]
355
356 if elements[0] == 'root':
357 hypervisor.add_filesystem(elements[1],
358 default_filesystem,
359 filename=tmpfile,
360 mntpnt='/')
361 elif elements[0] == 'swap':
362 hypervisor.add_filesystem(elements[1],
363 type='swap',
364 filename=tmpfile,
365 mntpnt=None)
366 elif elements[0] == '---':
367
368 pass
369 elif len(elements) == 3:
370 hypervisor.add_filesystem(elements[1],
371 type=default_filesystem,
372 filename=tmpfile,
373 mntpnt=elements[0],
374 devletter='',
375 device=elements[2],
376 dummy=(int(elements[1]) == 0))
377 else:
378 hypervisor.add_filesystem(elements[1],
379 type=default_filesystem,
380 filename=tmpfile,
381 mntpnt=elements[0])
382 except IOError, (errno, strerror):
383 self.optparser.error("%s parsing --part option: %s" %
384 (errno, strerror))
385 else:
386 try:
387 curdisk = list()
388 size = 0
389 disk_idx = 0
390 for line in file(self.options.part):
391 pair = line.strip().split(' ',1)
392 if pair[0] == '---':
393 self.do_disk(hypervisor, curdisk, size, disk_idx)
394 curdisk = list()
395 size = 0
396 disk_idx += 1
397 elif pair[0] != '':
398 logging.debug("part: %s, size: %d" % (pair[0],
399 int(pair[1])))
400 curdisk.append((pair[0], pair[1]))
401 size += int(pair[1])
402
403 self.do_disk(hypervisor, curdisk, size, disk_idx)
404
405 except IOError, (errno, strerror):
406 hypervisor.optparser.error("%s parsing --part option: %s" %
407 (errno, strerror))
408
409 - def do_disk(self, hypervisor, curdisk, size, disk_idx):
410 default_filesystem = hypervisor.distro.preferred_filesystem()
411
412 if self.options.raw:
413 disk = hypervisor.add_disk(filename=self.options.raw[disk_idx])
414 else:
415 disk = hypervisor.add_disk(
416 util.tmp_filename(tmp_root=self.options.tmp_root),
417 size+1)
418
419 logging.debug("do_disk #%i - size: %d" % (disk_idx, size))
420 offset = 0
421 for pair in curdisk:
422 logging.debug("do_disk #%i - part: %s, size: %s, offset: %d" %
423 (disk_idx, pair[0], pair[1], offset))
424 if pair[0] == 'root':
425 disk.add_part(offset, int(pair[1]), default_filesystem, '/')
426 elif pair[0] == 'swap':
427 disk.add_part(offset, int(pair[1]), pair[0], pair[0])
428 else:
429 disk.add_part(offset, int(pair[1]), default_filesystem, pair[0])
430 offset += int(pair[1])
431
433 arg = 'ubuntu-vm-builder'
434
436 optparser.set_usage('%prog hypervisor suite [options]')
437
438
442
451