]>
git.siccegge.de Git - forks/vmdebootstrap.git/blob - vmdebootstrap
2 # Copyright 2011-2013 Lars Wirzenius
3 # Copyright 2012 Codethink Limited
4 # Copyright 2014 Neil Williams <codehelp@debian.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
32 # pylint: disable=invalid-name
35 class VmDebootstrap(cliapp
.Application
): # pylint: disable=too-many-public-methods
37 def __init__(self
, progname
=None, version
=__version__
, description
=None, epilog
=None):
38 super(VmDebootstrap
, self
).__init
__(progname
, version
, description
, epilog
)
40 self
.mount_points
= []
42 def add_settings(self
):
43 default_arch
= subprocess
.check_output(
44 ["dpkg", "--print-architecture"]).strip()
46 self
.settings
.boolean(
47 ['verbose'], 'report what is going on')
49 ['image'], 'put created disk image in FILE',
51 self
.settings
.bytesize(
53 'create a disk image of size SIZE (%default)',
56 self
.settings
.bytesize(
58 'create boot partition of size SIZE (%default)',
63 'specify file system type for /boot/',
65 self
.settings
.bytesize(
67 'Space to leave at start of the image for bootloader',
71 'Partition type to use for this image',
75 'set up foreign debootstrap environment using provided program (ie binfmt handler)')
78 'select debootstrap variant it not using the default')
79 self
.settings
.boolean(
85 "tar up the disk's contents in FILE",
89 'configure apt to use MIRROR',
93 'use MIRROR as package source (%default)',
95 default
='http://http.debian.net/debian/')
98 'architecture to use (%default)',
100 default
=default_arch
)
101 self
.settings
.string(
103 'release to use (%default)',
106 self
.settings
.string_list(
108 'install PACKAGE onto system')
109 self
.settings
.string_list(
111 'install package in DEB file onto system (not from mirror)',
113 self
.settings
.boolean(
115 'do not install a linux package')
116 self
.settings
.boolean(
118 'enable DHCP on eth0')
119 self
.settings
.string(
123 self
.settings
.boolean(
124 ['lock-root-password'],
125 'lock root account so they cannot login?')
126 self
.settings
.string(
128 'run SCRIPT after setting up system',
130 self
.settings
.string(
132 'set name to HOSTNAME (%default)',
135 self
.settings
.string_list(
137 'create USER with PASSWORD',
138 metavar
='USER/PASSWORD')
139 self
.settings
.boolean(
141 'configure image to use a serial console')
142 self
.settings
.string(
143 ['serial-console-command'],
144 'command to manage the serial console, appended to /etc/inittab (%default)',
146 default
='/sbin/getty -L ttyS0 115200 vt100')
147 self
.settings
.boolean(
149 'install sudo, and if user is created, add them to sudo group')
150 self
.settings
.string(
152 'the user who will own the image when the build is complete.')
153 self
.settings
.boolean(
155 'use squashfs on the final image.')
156 self
.settings
.boolean(
158 'Create an apt source based on the distribution and mirror selected.')
159 self
.settings
.boolean(
161 'Run install-mbr (no longer done by default)')
162 self
.settings
.boolean(
164 'Install and configure grub2 - disables extlinux.')
165 self
.settings
.boolean(
167 'Dont fill the image with zeros to keep a sparse disk image',
169 self
.settings
.boolean(
171 'Create a list of package names included in the image.')
173 def process_args(self
, args
): # pylint: disable=too-many-branches,too-many-statements
174 if not self
.settings
['image'] and not self
.settings
['tarball']:
175 raise cliapp
.AppException(
176 'You must give disk image filename, or tarball filename')
177 if self
.settings
['image'] and not self
.settings
['size']:
178 raise cliapp
.AppException(
179 'If disk image is specified, you must give image size.')
187 if self
.settings
['image']:
188 self
.create_empty_image()
189 self
.partition_image()
190 if self
.settings
['mbr']:
192 (rootdev
, bootdev
) = self
.setup_kpartx()
193 self
.mkfs(rootdev
, fstype
=roottype
)
194 rootdir
= self
.mount(rootdev
)
196 if self
.settings
['boottype']:
197 boottype
= self
.settings
['boottype']
200 self
.mkfs(bootdev
, fstype
=boottype
)
201 bootdir
= '%s/%s' % (rootdir
, 'boot/')
203 self
.mount(bootdev
, bootdir
)
205 rootdir
= self
.mkdtemp()
206 self
.debootstrap(rootdir
)
207 self
.set_hostname(rootdir
)
208 self
.create_fstab(rootdir
, rootdev
, roottype
, bootdev
, boottype
)
209 self
.install_debs(rootdir
)
210 self
.cleanup_apt_cache(rootdir
)
211 self
.set_root_password(rootdir
)
212 self
.create_users(rootdir
)
213 self
.remove_udev_persistent_rules(rootdir
)
214 self
.setup_networking(rootdir
)
215 if self
.settings
['configure-apt'] or self
.settings
['apt-mirror']:
216 self
.configure_apt(rootdir
)
217 self
.customize(rootdir
)
218 self
.update_initramfs(rootdir
)
220 if self
.settings
['image']:
221 if self
.settings
['grub']:
222 self
.install_grub2(rootdev
, rootdir
)
223 elif self
.settings
['extlinux']:
224 self
.install_extlinux(rootdev
, rootdir
)
225 self
.append_serial_console(rootdir
)
226 self
.optimize_image(rootdir
)
227 if self
.settings
['squash']:
229 if self
.settings
['pkglist']:
230 self
.list_installed_pkgs(rootdir
)
232 if self
.settings
['foreign']:
233 os
.unlink('%s/usr/bin/%s' %
234 (rootdir
, os
.path
.basename(self
.settings
['foreign'])))
236 if self
.settings
['tarball']:
237 self
.create_tarball(rootdir
)
239 if self
.settings
['owner']:
241 except BaseException
, e
:
242 self
.message('EEEK! Something bad happened...')
244 db_log
= os
.path
.join(rootdir
, 'debootstrap', 'debootstrap.log')
245 if os
.path
.exists(db_log
):
246 shutil
.copy(db_log
, os
.getcwd())
248 self
.cleanup_system()
251 self
.cleanup_system()
253 def message(self
, msg
):
255 if self
.settings
['verbose']:
258 def runcmd(self
, argv
, stdin
='', ignore_fail
=False, env
=None, **kwargs
):
259 logging
.debug('runcmd: %s %s %s', argv
, env
, kwargs
)
260 p
= subprocess
.Popen(argv
, stdin
=subprocess
.PIPE
,
261 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
,
263 out
, err
= p
.communicate(stdin
)
264 if p
.returncode
!= 0:
265 msg
= 'command failed: %s\n%s\n%s' % (argv
, out
, err
)
268 raise cliapp
.AppException(msg
)
272 dirname
= tempfile
.mkdtemp()
273 self
.remove_dirs
.append(dirname
)
274 logging
.debug('mkdir %s', dirname
)
277 def mount(self
, device
, path
=None):
279 mount_point
= self
.mkdtemp()
282 self
.message('Mounting %s on %s' % (device
, mount_point
))
283 self
.runcmd(['mount', device
, mount_point
])
284 self
.mount_points
.append(mount_point
)
285 logging
.debug('mounted %s on %s', device
, mount_point
)
288 def create_empty_image(self
):
289 self
.message('Creating disk image')
290 self
.runcmd(['qemu-img', 'create', '-f', 'raw',
291 self
.settings
['image'],
292 str(self
.settings
['size'])])
294 def partition_image(self
):
296 Uses fat16 (msdos) partitioning by default, use part-type to change.
297 If bootoffset is specified, the first actual partition
298 starts at that offset to allow customisation scripts to
299 put bootloader images into the space, e.g. u-boot.
301 self
.message('Creating partitions')
302 self
.runcmd(['parted', '-s', self
.settings
['image'],
303 'mklabel', self
.settings
['part-type']])
306 if self
.settings
['bootoffset'] and self
.settings
['bootoffset'] is not '0':
307 # turn v.small offsets into something at least possible to create.
308 if self
.settings
['bootoffset'] < 1048576:
311 "Setting bootoffset %smib to allow for %s bytes",
312 partoffset
, self
.settings
['bootoffset'])
314 partoffset
= self
.settings
['bootoffset'] / (1024 * 1024)
315 self
.message("Using bootoffset: %smib %s bytes" % (partoffset
, self
.settings
['bootoffset']))
316 if self
.settings
['bootsize'] and self
.settings
['bootsize'] is not '0%':
317 bootsize
= self
.settings
['bootsize'] / (1024 * 1024)
318 bootsize
+= partoffset
319 self
.message("Using bootsize %smib: %s bytes" % (bootsize
, self
.settings
['bootsize']))
320 logging
.debug("Starting boot partition at %sMb", bootsize
)
321 self
.runcmd(['parted', '-s', self
.settings
['image'],
322 'mkpart', 'primary', 'fat16', str(partoffset
), str(bootsize
)])
324 self
.runcmd(['parted', '-s', self
.settings
['image'],
325 'mkpart', 'primary', '0%', '100%'])
327 logging
.debug("Starting root partition at %sMb", partoffset
)
328 self
.runcmd(['parted', '-s', self
.settings
['image'],
329 'mkpart', 'primary', str(bootsize
), '100%'])
330 self
.runcmd(['parted', '-s', self
.settings
['image'],
331 'set', '1', 'boot', 'on'])
333 def update_initramfs(self
, rootdir
):
334 cmd
= os
.path
.join('usr', 'sbin', 'update-initramfs')
335 if os
.path
.exists(os
.path
.join(rootdir
, cmd
)):
336 self
.message("Updating the initramfs")
337 self
.runcmd(['chroot', rootdir
, cmd
, '-u'])
339 def install_mbr(self
):
340 if os
.path
.exists("/sbin/install-mbr"):
341 self
.message('Installing MBR')
342 self
.runcmd(['install-mbr', self
.settings
['image']])
344 def setup_kpartx(self
):
346 out
= self
.runcmd(['kpartx', '-avs', self
.settings
['image']])
347 if self
.settings
['bootsize']:
355 devices
= [line
.split()[2]
356 for line
in out
.splitlines()
357 if line
.startswith('add map ')]
358 if len(devices
) != parts
:
359 raise cliapp
.AppException('Surprising number of partitions')
360 root
= '/dev/mapper/%s' % devices
[rootindex
]
361 if self
.settings
['bootsize']:
362 boot
= '/dev/mapper/%s' % devices
[bootindex
]
365 def mkfs(self
, device
, fstype
):
366 self
.message('Creating filesystem %s' % fstype
)
367 self
.runcmd(['mkfs', '-t', fstype
, device
])
369 def debootstrap(self
, rootdir
):
370 msg
= "(%s)" % self
.settings
['variant'] if self
.settings
['variant'] else ''
371 self
.message('Debootstrapping %s %s' % (self
.settings
['distribution'], msg
))
373 include
= self
.settings
['package']
375 if not self
.settings
['foreign']:
376 include
.append('acpid')
378 if self
.settings
['grub']:
379 include
.append('grub2')
381 if not self
.settings
['no-kernel']:
382 if self
.settings
['arch'] == 'i386':
385 kernel_arch
= self
.settings
['arch']
386 kernel_image
= 'linux-image-%s' % kernel_arch
387 include
.append(kernel_image
)
389 if self
.settings
['sudo'] and 'sudo' not in include
:
390 include
.append('sudo')
392 args
= ['debootstrap', '--arch=%s' % self
.settings
['arch']]
394 if self
.settings
['package']:
396 '--include=%s' % ','.join(include
))
397 if self
.settings
['foreign']:
398 args
.append('--foreign')
399 if self
.settings
['variant']:
400 args
.append('--variant')
401 args
.append(self
.settings
['variant'])
402 args
+= [self
.settings
['distribution'],
403 rootdir
, self
.settings
['mirror']]
404 logging
.debug(" ".join(args
))
406 if self
.settings
['foreign']:
407 # set a noninteractive debconf environment for secondstage
409 "DEBIAN_FRONTEND": "noninteractive",
410 "DEBCONF_NONINTERACTIVE_SEEN": "true",
413 # add the mapping to the complete environment.
414 env
.update(os
.environ
)
415 # First copy the binfmt handler over
416 self
.message('Setting up binfmt handler')
417 shutil
.copy(self
.settings
['foreign'], '%s/usr/bin/' % rootdir
)
418 # Next, run the package install scripts etc.
419 self
.message('Running debootstrap second stage')
420 self
.runcmd(['chroot', rootdir
,
421 '/debootstrap/debootstrap', '--second-stage'],
424 def set_hostname(self
, rootdir
):
425 hostname
= self
.settings
['hostname']
426 with
open(os
.path
.join(rootdir
, 'etc', 'hostname'), 'w') as f
:
427 f
.write('%s\n' % hostname
)
429 etc_hosts
= os
.path
.join(rootdir
, 'etc', 'hosts')
431 with
open(etc_hosts
, 'r') as f
:
433 with
open(etc_hosts
, 'w') as f
:
434 for line
in data
.splitlines():
435 if line
.startswith('127.0.0.1'):
436 line
+= ' %s' % hostname
437 f
.write('%s\n' % line
)
441 def create_fstab(self
, rootdir
, rootdev
, roottype
, bootdev
, boottype
): # pylint: disable=too-many-arguments
443 out
= self
.runcmd(['blkid', '-c', '/dev/null', '-o', 'value',
444 '-s', 'UUID', device
])
445 return out
.splitlines()[0].strip()
448 rootdevstr
= 'UUID=%s' % fsuuid(rootdev
)
450 rootdevstr
= '/dev/sda1'
453 bootdevstr
= 'UUID=%s' % fsuuid(bootdev
)
457 fstab
= os
.path
.join(rootdir
, 'etc', 'fstab')
458 with
open(fstab
, 'w') as f
:
459 f
.write('proc /proc proc defaults 0 0\n')
460 f
.write('%s / %s errors=remount-ro 0 1\n' % (rootdevstr
, roottype
))
462 f
.write('%s /boot %s errors=remount-ro 0 2\n' % (bootdevstr
, boottype
))
464 def install_debs(self
, rootdir
):
465 if not self
.settings
['custom-package']:
467 self
.message('Installing custom packages')
468 tmp
= os
.path
.join(rootdir
, 'tmp', 'install_debs')
470 for deb
in self
.settings
['custom-package']:
471 shutil
.copy(deb
, tmp
)
472 filenames
= [os
.path
.join('/tmp/install_debs', os
.path
.basename(deb
))
473 for deb
in self
.settings
['custom-package']]
475 self
.runcmd_unchecked(['chroot', rootdir
, 'dpkg', '-i'] + filenames
)
476 logging
.debug('stdout:\n%s', out
)
477 logging
.debug('stderr:\n%s', err
)
478 out
= self
.runcmd(['chroot', rootdir
,
479 'apt-get', '-f', '--no-remove', 'install'])
480 logging
.debug('stdout:\n%s', out
)
483 def cleanup_apt_cache(self
, rootdir
):
484 out
= self
.runcmd(['chroot', rootdir
, 'apt-get', 'clean'])
485 logging
.debug('stdout:\n%s', out
)
487 def set_root_password(self
, rootdir
):
488 if self
.settings
['root-password']:
489 self
.message('Setting root password')
490 self
.set_password(rootdir
, 'root', self
.settings
['root-password'])
491 elif self
.settings
['lock-root-password']:
492 self
.message('Locking root password')
493 self
.runcmd(['chroot', rootdir
, 'passwd', '-l', 'root'])
495 self
.message('Give root an empty password')
496 self
.delete_password(rootdir
, 'root')
498 def create_users(self
, rootdir
):
499 def create_user(user
):
500 self
.runcmd(['chroot', rootdir
, 'adduser', '--gecos', user
,
501 '--disabled-password', user
])
502 if self
.settings
['sudo']:
503 self
.runcmd(['chroot', rootdir
, 'adduser', user
, 'sudo'])
505 for userpass
in self
.settings
['user']:
507 user
, password
= userpass
.split('/', 1)
509 self
.set_password(rootdir
, user
, password
)
511 create_user(userpass
)
512 self
.delete_password(rootdir
, userpass
)
514 def set_password(self
, rootdir
, user
, password
):
515 encrypted
= crypt
.crypt(password
, '..')
516 self
.runcmd(['chroot', rootdir
, 'usermod', '-p', encrypted
, user
])
518 def delete_password(self
, rootdir
, user
):
519 self
.runcmd(['chroot', rootdir
, 'passwd', '-d', user
])
521 def remove_udev_persistent_rules(self
, rootdir
):
522 self
.message('Removing udev persistent cd and net rules')
523 for x
in ['70-persistent-cd.rules', '70-persistent-net.rules']:
524 pathname
= os
.path
.join(rootdir
, 'etc', 'udev', 'rules.d', x
)
525 if os
.path
.exists(pathname
):
526 logging
.debug('rm %s', pathname
)
529 logging
.debug('not removing non-existent %s', pathname
)
531 def setup_networking(self
, rootdir
):
532 self
.message('Setting up networking')
534 f
= open(os
.path
.join(rootdir
, 'etc', 'network', 'interfaces'), 'w')
536 f
.write('iface lo inet loopback\n')
538 if self
.settings
['enable-dhcp']:
540 f
.write('auto eth0\n')
541 f
.write('iface eth0 inet dhcp\n')
545 def append_serial_console(self
, rootdir
):
546 if self
.settings
['serial-console']:
547 serial_command
= self
.settings
['serial-console-command']
548 logging
.debug('adding getty to serial console')
549 inittab
= os
.path
.join(rootdir
, 'etc/inittab')
550 # to autologin, serial_command can contain '-a root'
551 with
open(inittab
, 'a') as f
:
552 f
.write('\nS0:23:respawn:%s\n' % serial_command
)
554 def install_grub2(self
, rootdev
, rootdir
):
555 self
.message("Configuring grub2")
556 # rely on kpartx using consistent naming to map loop0p1 to loop0
557 install_dev
= os
.path
.join('/dev', os
.path
.basename(rootdev
)[:-2])
558 self
.runcmd(['mount', '/dev', '-t', 'devfs', '-obind',
559 '%s' % os
.path
.join(rootdir
, 'dev')])
560 self
.runcmd(['mount', '/proc', '-t', 'proc', '-obind',
561 '%s' % os
.path
.join(rootdir
, 'proc')])
562 self
.runcmd(['mount', '/sys', '-t', 'sysfs', '-obind',
563 '%s' % os
.path
.join(rootdir
, 'sys')])
565 self
.runcmd(['chroot', rootdir
, 'update-grub'])
566 self
.runcmd(['chroot', rootdir
, 'grub-install', install_dev
])
567 except cliapp
.AppException
:
568 self
.message("Failed. Is grub2-common installed? Using extlinux.")
569 self
.runcmd(['umount', os
.path
.join(rootdir
, 'sys')])
570 self
.runcmd(['umount', os
.path
.join(rootdir
, 'proc')])
571 self
.runcmd(['umount', os
.path
.join(rootdir
, 'dev')])
572 self
.install_extlinux(rootdev
, rootdir
)
574 def install_extlinux(self
, rootdev
, rootdir
):
575 if not os
.path
.exists("/usr/bin/extlinux"):
576 self
.message("extlinux not installed, skipping.")
578 self
.message('Installing extlinux')
581 dirname
= os
.path
.join(rootdir
, 'boot')
582 basenames
= os
.listdir(dirname
)
583 logging
.debug('find: %s', basenames
)
584 for basename
in basenames
:
585 if re
.search(pattern
, basename
):
586 return os
.path
.join('boot', basename
)
587 raise cliapp
.AppException('Cannot find match: %s' % pattern
)
590 kernel_image
= find('vmlinuz-.*')
591 initrd_image
= find('initrd.img-.*')
592 except cliapp
.AppException
as e
:
593 self
.message("Unable to find kernel. Not installing extlinux.")
594 logging
.debug("No kernel found. %s. Skipping install of extlinux.", e
)
597 out
= self
.runcmd(['blkid', '-c', '/dev/null', '-o', 'value',
598 '-s', 'UUID', rootdev
])
599 uuid
= out
.splitlines()[0].strip()
601 conf
= os
.path
.join(rootdir
, 'extlinux.conf')
602 logging
.debug('configure extlinux %s', conf
)
603 kserial
= 'console=ttyS0,115200' if self
.settings
['serial-console'] else ''
604 extserial
= 'serial 0 115200' if self
.settings
['serial-console'] else ''
611 append initrd=%(initrd)s root=UUID=%(uuid)s ro %(kserial)s
614 'kernel': kernel_image
, # pylint: disable=bad-continuation
615 'initrd': initrd_image
, # pylint: disable=bad-continuation
616 'uuid': uuid
, # pylint: disable=bad-continuation
617 'kserial': kserial
, # pylint: disable=bad-continuation
618 'extserial': extserial
, # pylint: disable=bad-continuation
619 } # pylint: disable=bad-continuation
620 logging
.debug("extlinux config:\n%s", msg
)
622 # python multiline string substitution is just ugly.
623 # use an external file or live with the mangling, no point in
624 # mangling the string to remove spaces just to keep it pretty in source.
628 self
.runcmd(['extlinux', '--install', rootdir
])
629 self
.runcmd(['sync'])
632 def optimize_image(self
, rootdir
):
634 Filing up the image with zeros will increase its compression rate
636 if not self
.settings
['sparse']:
637 zeros
= os
.path
.join(rootdir
, 'ZEROS')
638 self
.runcmd_unchecked(['dd', 'if=/dev/zero', 'of=' + zeros
, 'bs=1M'])
639 self
.runcmd(['rm', '-f', zeros
])
643 Run squashfs on the image.
645 if not os
.path
.exists('/usr/bin/mksquashfs'):
646 logging
.warning("Squash selected but mksquashfs not found!")
648 self
.message("Running mksquashfs")
649 suffixed
= "%s.squashfs" % self
.settings
['image']
650 self
.runcmd(['mksquashfs', self
.settings
['image'],
652 '-no-progress', '-comp', 'xz'], ignore_fail
=False)
653 os
.unlink(self
.settings
['image'])
654 self
.settings
['image'] = suffixed
656 def cleanup_system(self
):
657 # Clean up after any errors.
659 self
.message('Cleaning up')
661 # Umount in the reverse mount order
662 if self
.settings
['image']:
663 for i
in range(len(self
.mount_points
) - 1, -1, -1):
664 mount_point
= self
.mount_points
[i
]
666 self
.runcmd(['umount', mount_point
], ignore_fail
=False)
667 except cliapp
.AppException
:
668 logging
.debug("umount failed, sleeping and trying again")
670 self
.runcmd(['umount', mount_point
], ignore_fail
=False)
672 self
.runcmd(['kpartx', '-d', self
.settings
['image']], ignore_fail
=True)
674 for dirname
in self
.remove_dirs
:
675 shutil
.rmtree(dirname
)
677 def customize(self
, rootdir
):
678 script
= self
.settings
['customize']
681 if not os
.path
.exists(script
):
682 example
= os
.path
.join("/usr/share/vmdebootstrap/examples/", script
)
683 if not os
.path
.exists(example
):
684 self
.message("Unable to find %s" % script
)
687 self
.message('Running customize script %s' % script
)
688 logging
.info("rootdir=%s image=%s", rootdir
, self
.settings
['image'])
689 with
open('/dev/tty', 'w') as tty
:
690 cliapp
.runcmd([script
, rootdir
, self
.settings
['image']], stdout
=tty
, stderr
=tty
)
692 def create_tarball(self
, rootdir
):
693 # Create a tarball of the disk's contents
694 # shell out to runcmd since it more easily handles rootdir
695 self
.message('Creating tarball of disk contents')
696 self
.runcmd(['tar', '-cf', self
.settings
['tarball'], '-C', rootdir
, '.'])
699 # Change image owner after completed build
700 self
.message("Changing owner to %s" % self
.settings
["owner"])
701 subprocess
.call(["chown",
702 self
.settings
["owner"],
703 self
.settings
["image"]])
705 def list_installed_pkgs(self
, rootdir
):
706 # output the list of installed packages for sources identification
707 self
.message("Creating a list of installed binary package names")
708 out
= self
.runcmd(['chroot', rootdir
,
709 'dpkg-query', '-W' "-f='${Package}.deb\n'"])
710 with
open('dpkg.list', 'w') as dpkg
:
713 def configure_apt(self
, rootdir
):
714 # use the distribution and mirror to create an apt source
715 self
.message("Configuring apt to use distribution and mirror")
716 conf
= os
.path
.join(rootdir
, 'etc', 'apt', 'sources.list.d', 'base.list')
717 logging
.debug('configure apt %s', conf
)
718 mirror
= self
.settings
['mirror']
719 if self
.settings
['apt-mirror']:
720 mirror
= self
.settings
['apt-mirror']
721 self
.message("Setting apt mirror to %s" % mirror
)
722 os
.unlink(os
.path
.join(rootdir
, 'etc', 'apt', 'sources.list'))
724 line
= 'deb %s %s main\n' % (mirror
, self
.settings
['distribution'])
726 line
= '#deb-src %s %s main\n' % (mirror
, self
.settings
['distribution'])
730 if __name__
== '__main__':
731 VmDebootstrap(version
=__version__
).run()