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