]>
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 'specify file system type for /',
79 'set up foreign debootstrap environment using provided program (ie binfmt handler)')
82 'select debootstrap variant it not using the default')
83 self
.settings
.boolean(
89 "tar up the disk's contents in FILE",
93 'configure apt to use MIRROR',
97 'use MIRROR as package source (%default)',
99 default
='http://http.debian.net/debian/')
100 self
.settings
.string(
102 'architecture to use (%default)',
104 default
=default_arch
)
105 self
.settings
.string(
107 'release to use (%default)',
110 self
.settings
.string_list(
112 'install PACKAGE onto system')
113 self
.settings
.string_list(
115 'install package in DEB file onto system (not from mirror)',
117 self
.settings
.boolean(
119 'do not install a linux package')
120 self
.settings
.boolean(
122 'enable DHCP on eth0')
123 self
.settings
.string(
127 self
.settings
.boolean(
128 ['lock-root-password'],
129 'lock root account so they cannot login?')
130 self
.settings
.string(
132 'run SCRIPT after setting up system',
134 self
.settings
.string(
136 'set name to HOSTNAME (%default)',
139 self
.settings
.string_list(
141 'create USER with PASSWORD',
142 metavar
='USER/PASSWORD')
143 self
.settings
.boolean(
145 'configure image to use a serial console')
146 self
.settings
.string(
147 ['serial-console-command'],
148 'command to manage the serial console, appended to /etc/inittab (%default)',
150 default
='/sbin/getty -L ttyS0 115200 vt100')
151 self
.settings
.boolean(
153 'install sudo, and if user is created, add them to sudo group')
154 self
.settings
.string(
156 'the user who will own the image when the build is complete.')
157 self
.settings
.boolean(
159 'use squashfs on the final image.')
160 self
.settings
.boolean(
162 'Create an apt source based on the distribution and mirror selected.')
163 self
.settings
.boolean(
165 'Run install-mbr (default if extlinux used)')
166 self
.settings
.boolean(
168 'Install and configure grub2 - disables extlinux.')
169 self
.settings
.boolean(
171 'Do not fill the image with zeros to keep a sparse disk image',
173 self
.settings
.boolean(
175 'Create a list of package names included in the image.')
177 def process_args(self
, args
): # pylint: disable=too-many-branches,too-many-statements
178 if not self
.settings
['image'] and not self
.settings
['tarball']:
179 raise cliapp
.AppException(
180 'You must give disk image filename, or tarball filename')
181 if self
.settings
['image'] and not self
.settings
['size']:
182 raise cliapp
.AppException(
183 'If disk image is specified, you must give image size.')
188 roottype
= self
.settings
['roottype']
191 if self
.settings
['image']:
192 self
.create_empty_image()
193 self
.partition_image()
194 if self
.settings
['mbr'] or self
.settings
['extlinux']:
196 (rootdev
, bootdev
) = self
.setup_kpartx()
197 self
.mkfs(rootdev
, fstype
=roottype
)
198 rootdir
= self
.mount(rootdev
)
200 if self
.settings
['boottype']:
201 boottype
= self
.settings
['boottype']
204 self
.mkfs(bootdev
, fstype
=boottype
)
205 bootdir
= '%s/%s' % (rootdir
, 'boot/')
207 self
.mount(bootdev
, bootdir
)
209 rootdir
= self
.mkdtemp()
210 self
.debootstrap(rootdir
)
211 self
.set_hostname(rootdir
)
212 self
.create_fstab(rootdir
, rootdev
, roottype
, bootdev
, boottype
)
213 self
.install_debs(rootdir
)
214 self
.cleanup_apt_cache(rootdir
)
215 self
.set_root_password(rootdir
)
216 self
.create_users(rootdir
)
217 self
.remove_udev_persistent_rules(rootdir
)
218 self
.setup_networking(rootdir
)
219 if self
.settings
['configure-apt'] or self
.settings
['apt-mirror']:
220 self
.configure_apt(rootdir
)
221 self
.customize(rootdir
)
222 self
.update_initramfs(rootdir
)
224 if self
.settings
['image']:
225 if self
.settings
['grub']:
226 self
.install_grub2(rootdev
, rootdir
)
227 elif self
.settings
['extlinux']:
228 self
.install_extlinux(rootdev
, rootdir
)
229 self
.append_serial_console(rootdir
)
230 self
.optimize_image(rootdir
)
231 if self
.settings
['squash']:
233 if self
.settings
['pkglist']:
234 self
.list_installed_pkgs(rootdir
)
236 if self
.settings
['foreign']:
237 os
.unlink('%s/usr/bin/%s' %
238 (rootdir
, os
.path
.basename(self
.settings
['foreign'])))
240 if self
.settings
['tarball']:
241 self
.create_tarball(rootdir
)
243 if self
.settings
['owner']:
245 except BaseException
, e
:
246 self
.message('EEEK! Something bad happened...')
248 db_log
= os
.path
.join(rootdir
, 'debootstrap', 'debootstrap.log')
249 if os
.path
.exists(db_log
):
250 shutil
.copy(db_log
, os
.getcwd())
252 self
.cleanup_system()
255 self
.cleanup_system()
257 def message(self
, msg
):
259 if self
.settings
['verbose']:
262 def runcmd(self
, argv
, stdin
='', ignore_fail
=False, env
=None, **kwargs
):
263 logging
.debug('runcmd: %s %s %s', argv
, env
, kwargs
)
264 p
= subprocess
.Popen(argv
, stdin
=subprocess
.PIPE
,
265 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
,
267 out
, err
= p
.communicate(stdin
)
268 if p
.returncode
!= 0:
269 msg
= 'command failed: %s\n%s\n%s' % (argv
, out
, err
)
272 raise cliapp
.AppException(msg
)
276 dirname
= tempfile
.mkdtemp()
277 self
.remove_dirs
.append(dirname
)
278 logging
.debug('mkdir %s', dirname
)
281 def mount(self
, device
, path
=None):
283 mount_point
= self
.mkdtemp()
286 self
.message('Mounting %s on %s' % (device
, mount_point
))
287 self
.runcmd(['mount', device
, mount_point
])
288 self
.mount_points
.append(mount_point
)
289 logging
.debug('mounted %s on %s', device
, mount_point
)
292 def create_empty_image(self
):
293 self
.message('Creating disk image')
294 self
.runcmd(['qemu-img', 'create', '-f', 'raw',
295 self
.settings
['image'],
296 str(self
.settings
['size'])])
298 def partition_image(self
):
300 Uses fat16 (msdos) partitioning by default, use part-type to change.
301 If bootoffset is specified, the first actual partition
302 starts at that offset to allow customisation scripts to
303 put bootloader images into the space, e.g. u-boot.
305 self
.message('Creating partitions')
306 self
.runcmd(['parted', '-s', self
.settings
['image'],
307 'mklabel', self
.settings
['part-type']])
310 if self
.settings
['bootoffset'] and self
.settings
['bootoffset'] is not '0':
311 # turn v.small offsets into something at least possible to create.
312 if self
.settings
['bootoffset'] < 1048576:
315 "Setting bootoffset %smib to allow for %s bytes",
316 partoffset
, self
.settings
['bootoffset'])
318 partoffset
= self
.settings
['bootoffset'] / (1024 * 1024)
319 self
.message("Using bootoffset: %smib %s bytes" % (partoffset
, self
.settings
['bootoffset']))
320 if self
.settings
['bootsize'] and self
.settings
['bootsize'] is not '0%':
321 bootsize
= self
.settings
['bootsize'] / (1024 * 1024)
322 bootsize
+= partoffset
323 self
.message("Using bootsize %smib: %s bytes" % (bootsize
, self
.settings
['bootsize']))
324 logging
.debug("Starting boot partition at %sMb", bootsize
)
325 self
.runcmd(['parted', '-s', self
.settings
['image'],
326 'mkpart', 'primary', 'fat16', str(partoffset
), str(bootsize
)])
328 self
.runcmd(['parted', '-s', self
.settings
['image'],
329 'mkpart', 'primary', '0%', '100%'])
331 logging
.debug("Starting root partition at %sMb", partoffset
)
332 self
.runcmd(['parted', '-s', self
.settings
['image'],
333 'mkpart', 'primary', str(bootsize
), '100%'])
334 self
.runcmd(['parted', '-s', self
.settings
['image'],
335 'set', '1', 'boot', 'on'])
337 def update_initramfs(self
, rootdir
):
338 cmd
= os
.path
.join('usr', 'sbin', 'update-initramfs')
339 if os
.path
.exists(os
.path
.join(rootdir
, cmd
)):
340 self
.message("Updating the initramfs")
341 self
.runcmd(['chroot', rootdir
, cmd
, '-u'])
343 def install_mbr(self
):
344 if os
.path
.exists("/sbin/install-mbr"):
345 self
.message('Installing MBR')
346 self
.runcmd(['install-mbr', self
.settings
['image']])
348 msg
= "mbr enabled but /sbin/install-mbr not found" \
349 " - please install the mbr package."
350 raise cliapp
.AppException(msg
)
352 def setup_kpartx(self
):
354 out
= self
.runcmd(['kpartx', '-avs', self
.settings
['image']])
355 if self
.settings
['bootsize']:
363 devices
= [line
.split()[2]
364 for line
in out
.splitlines()
365 if line
.startswith('add map ')]
366 if len(devices
) != parts
:
367 msg
= 'Surprising number of partitions'
368 logging
.debug("%s: devices=%s parts=%s", msg
, devices
, parts
)
369 raise cliapp
.AppException(msg
)
370 root
= '/dev/mapper/%s' % devices
[rootindex
]
371 if self
.settings
['bootsize']:
372 boot
= '/dev/mapper/%s' % devices
[bootindex
]
375 def mkfs(self
, device
, fstype
):
376 self
.message('Creating filesystem %s' % fstype
)
377 self
.runcmd(['mkfs', '-t', fstype
, device
])
379 def debootstrap(self
, rootdir
):
380 msg
= "(%s)" % self
.settings
['variant'] if self
.settings
['variant'] else ''
381 self
.message('Debootstrapping %s %s' % (self
.settings
['distribution'], msg
))
383 include
= self
.settings
['package']
385 if not self
.settings
['foreign']:
386 include
.append('acpid')
388 if self
.settings
['grub']:
389 include
.append('grub2')
391 if not self
.settings
['no-kernel']:
392 if self
.settings
['arch'] == 'i386':
395 kernel_arch
= self
.settings
['arch']
396 kernel_image
= 'linux-image-%s' % kernel_arch
397 include
.append(kernel_image
)
399 if self
.settings
['sudo'] and 'sudo' not in include
:
400 include
.append('sudo')
402 args
= ['debootstrap', '--arch=%s' % self
.settings
['arch']]
404 if self
.settings
['package']:
406 '--include=%s' % ','.join(include
))
407 if self
.settings
['foreign']:
408 args
.append('--foreign')
409 if self
.settings
['variant']:
410 args
.append('--variant')
411 args
.append(self
.settings
['variant'])
412 args
+= [self
.settings
['distribution'],
413 rootdir
, self
.settings
['mirror']]
414 logging
.debug(" ".join(args
))
416 if self
.settings
['foreign']:
417 # set a noninteractive debconf environment for secondstage
419 "DEBIAN_FRONTEND": "noninteractive",
420 "DEBCONF_NONINTERACTIVE_SEEN": "true",
423 # add the mapping to the complete environment.
424 env
.update(os
.environ
)
425 # First copy the binfmt handler over
426 self
.message('Setting up binfmt handler')
427 shutil
.copy(self
.settings
['foreign'], '%s/usr/bin/' % rootdir
)
428 # Next, run the package install scripts etc.
429 self
.message('Running debootstrap second stage')
430 self
.runcmd(['chroot', rootdir
,
431 '/debootstrap/debootstrap', '--second-stage'],
434 def set_hostname(self
, rootdir
):
435 hostname
= self
.settings
['hostname']
436 with
open(os
.path
.join(rootdir
, 'etc', 'hostname'), 'w') as f
:
437 f
.write('%s\n' % hostname
)
439 etc_hosts
= os
.path
.join(rootdir
, 'etc', 'hosts')
441 with
open(etc_hosts
, 'r') as f
:
443 with
open(etc_hosts
, 'w') as f
:
444 for line
in data
.splitlines():
445 if line
.startswith('127.0.0.1'):
446 line
+= ' %s' % hostname
447 f
.write('%s\n' % line
)
451 def create_fstab(self
, rootdir
, rootdev
, roottype
, bootdev
, boottype
): # pylint: disable=too-many-arguments
453 out
= self
.runcmd(['blkid', '-c', '/dev/null', '-o', 'value',
454 '-s', 'UUID', device
])
455 return out
.splitlines()[0].strip()
458 rootdevstr
= 'UUID=%s' % fsuuid(rootdev
)
460 rootdevstr
= '/dev/sda1'
463 bootdevstr
= 'UUID=%s' % fsuuid(bootdev
)
467 fstab
= os
.path
.join(rootdir
, 'etc', 'fstab')
468 with
open(fstab
, 'w') as f
:
469 f
.write('proc /proc proc defaults 0 0\n')
470 f
.write('%s / %s errors=remount-ro 0 1\n' % (rootdevstr
, roottype
))
472 f
.write('%s /boot %s errors=remount-ro 0 2\n' % (bootdevstr
, boottype
))
474 def install_debs(self
, rootdir
):
475 if not self
.settings
['custom-package']:
477 self
.message('Installing custom packages')
478 tmp
= os
.path
.join(rootdir
, 'tmp', 'install_debs')
480 for deb
in self
.settings
['custom-package']:
481 shutil
.copy(deb
, tmp
)
482 filenames
= [os
.path
.join('/tmp/install_debs', os
.path
.basename(deb
))
483 for deb
in self
.settings
['custom-package']]
485 self
.runcmd_unchecked(['chroot', rootdir
, 'dpkg', '-i'] + filenames
)
486 logging
.debug('stdout:\n%s', out
)
487 logging
.debug('stderr:\n%s', err
)
488 out
= self
.runcmd(['chroot', rootdir
,
489 'apt-get', '-f', '--no-remove', 'install'])
490 logging
.debug('stdout:\n%s', out
)
493 def cleanup_apt_cache(self
, rootdir
):
494 out
= self
.runcmd(['chroot', rootdir
, 'apt-get', 'clean'])
495 logging
.debug('stdout:\n%s', out
)
497 def set_root_password(self
, rootdir
):
498 if self
.settings
['root-password']:
499 self
.message('Setting root password')
500 self
.set_password(rootdir
, 'root', self
.settings
['root-password'])
501 elif self
.settings
['lock-root-password']:
502 self
.message('Locking root password')
503 self
.runcmd(['chroot', rootdir
, 'passwd', '-l', 'root'])
505 self
.message('Give root an empty password')
506 self
.delete_password(rootdir
, 'root')
508 def create_users(self
, rootdir
):
509 def create_user(user
):
510 self
.runcmd(['chroot', rootdir
, 'adduser', '--gecos', user
,
511 '--disabled-password', user
])
512 if self
.settings
['sudo']:
513 self
.runcmd(['chroot', rootdir
, 'adduser', user
, 'sudo'])
515 for userpass
in self
.settings
['user']:
517 user
, password
= userpass
.split('/', 1)
519 self
.set_password(rootdir
, user
, password
)
521 create_user(userpass
)
522 self
.delete_password(rootdir
, userpass
)
524 def set_password(self
, rootdir
, user
, password
):
525 encrypted
= crypt
.crypt(password
, '..')
526 self
.runcmd(['chroot', rootdir
, 'usermod', '-p', encrypted
, user
])
528 def delete_password(self
, rootdir
, user
):
529 self
.runcmd(['chroot', rootdir
, 'passwd', '-d', user
])
531 def remove_udev_persistent_rules(self
, rootdir
):
532 self
.message('Removing udev persistent cd and net rules')
533 for x
in ['70-persistent-cd.rules', '70-persistent-net.rules']:
534 pathname
= os
.path
.join(rootdir
, 'etc', 'udev', 'rules.d', x
)
535 if os
.path
.exists(pathname
):
536 logging
.debug('rm %s', pathname
)
539 logging
.debug('not removing non-existent %s', pathname
)
541 def setup_networking(self
, rootdir
):
542 self
.message('Setting up networking')
544 f
= open(os
.path
.join(rootdir
, 'etc', 'network', 'interfaces'), 'w')
546 f
.write('iface lo inet loopback\n')
548 if self
.settings
['enable-dhcp']:
550 f
.write('auto eth0\n')
551 f
.write('iface eth0 inet dhcp\n')
555 def append_serial_console(self
, rootdir
):
556 if self
.settings
['serial-console']:
557 serial_command
= self
.settings
['serial-console-command']
558 logging
.debug('adding getty to serial console')
559 inittab
= os
.path
.join(rootdir
, 'etc/inittab')
560 # to autologin, serial_command can contain '-a root'
561 with
open(inittab
, 'a') as f
:
562 f
.write('\nS0:23:respawn:%s\n' % serial_command
)
564 def install_grub2(self
, rootdev
, rootdir
):
565 self
.message("Configuring grub2")
566 # rely on kpartx using consistent naming to map loop0p1 to loop0
567 install_dev
= os
.path
.join('/dev', os
.path
.basename(rootdev
)[:-2])
568 self
.runcmd(['mount', '/dev', '-t', 'devfs', '-obind',
569 '%s' % os
.path
.join(rootdir
, 'dev')])
570 self
.runcmd(['mount', '/proc', '-t', 'proc', '-obind',
571 '%s' % os
.path
.join(rootdir
, 'proc')])
572 self
.runcmd(['mount', '/sys', '-t', 'sysfs', '-obind',
573 '%s' % os
.path
.join(rootdir
, 'sys')])
575 self
.runcmd(['chroot', rootdir
, 'update-grub'])
576 self
.runcmd(['chroot', rootdir
, 'grub-install', install_dev
])
577 except cliapp
.AppException
:
578 self
.message("Failed. Is grub2-common installed? Using extlinux.")
579 self
.runcmd(['umount', os
.path
.join(rootdir
, 'sys')])
580 self
.runcmd(['umount', os
.path
.join(rootdir
, 'proc')])
581 self
.runcmd(['umount', os
.path
.join(rootdir
, 'dev')])
582 self
.install_extlinux(rootdev
, rootdir
)
584 def install_extlinux(self
, rootdev
, rootdir
):
585 if not os
.path
.exists("/usr/bin/extlinux"):
586 self
.message("extlinux not installed, skipping.")
588 self
.message('Installing extlinux')
591 dirname
= os
.path
.join(rootdir
, 'boot')
592 basenames
= os
.listdir(dirname
)
593 logging
.debug('find: %s', basenames
)
594 for basename
in basenames
:
595 if re
.search(pattern
, basename
):
596 return os
.path
.join('boot', basename
)
597 raise cliapp
.AppException('Cannot find match: %s' % pattern
)
600 kernel_image
= find('vmlinuz-.*')
601 initrd_image
= find('initrd.img-.*')
602 except cliapp
.AppException
as e
:
603 self
.message("Unable to find kernel. Not installing extlinux.")
604 logging
.debug("No kernel found. %s. Skipping install of extlinux.", e
)
607 out
= self
.runcmd(['blkid', '-c', '/dev/null', '-o', 'value',
608 '-s', 'UUID', rootdev
])
609 uuid
= out
.splitlines()[0].strip()
611 conf
= os
.path
.join(rootdir
, 'extlinux.conf')
612 logging
.debug('configure extlinux %s', conf
)
613 kserial
= 'console=ttyS0,115200' if self
.settings
['serial-console'] else ''
614 extserial
= 'serial 0 115200' if self
.settings
['serial-console'] else ''
621 append initrd=%(initrd)s root=UUID=%(uuid)s ro %(kserial)s
624 'kernel': kernel_image
, # pylint: disable=bad-continuation
625 'initrd': initrd_image
, # pylint: disable=bad-continuation
626 'uuid': uuid
, # pylint: disable=bad-continuation
627 'kserial': kserial
, # pylint: disable=bad-continuation
628 'extserial': extserial
, # pylint: disable=bad-continuation
629 } # pylint: disable=bad-continuation
630 logging
.debug("extlinux config:\n%s", msg
)
632 # python multiline string substitution is just ugly.
633 # use an external file or live with the mangling, no point in
634 # mangling the string to remove spaces just to keep it pretty in source.
638 self
.runcmd(['extlinux', '--install', rootdir
])
639 self
.runcmd(['sync'])
642 def optimize_image(self
, rootdir
):
644 Filing up the image with zeros will increase its compression rate
646 if not self
.settings
['sparse']:
647 zeros
= os
.path
.join(rootdir
, 'ZEROS')
648 self
.runcmd_unchecked(['dd', 'if=/dev/zero', 'of=' + zeros
, 'bs=1M'])
649 self
.runcmd(['rm', '-f', zeros
])
653 Run squashfs on the image.
655 if not os
.path
.exists('/usr/bin/mksquashfs'):
656 logging
.warning("Squash selected but mksquashfs not found!")
658 self
.message("Running mksquashfs")
659 suffixed
= "%s.squashfs" % self
.settings
['image']
660 self
.runcmd(['mksquashfs', self
.settings
['image'],
662 '-no-progress', '-comp', 'xz'], ignore_fail
=False)
663 os
.unlink(self
.settings
['image'])
664 self
.settings
['image'] = suffixed
666 def cleanup_system(self
):
667 # Clean up after any errors.
669 self
.message('Cleaning up')
671 # Umount in the reverse mount order
672 if self
.settings
['image']:
673 for i
in range(len(self
.mount_points
) - 1, -1, -1):
674 mount_point
= self
.mount_points
[i
]
676 self
.runcmd(['umount', mount_point
], ignore_fail
=False)
677 except cliapp
.AppException
:
678 logging
.debug("umount failed, sleeping and trying again")
680 self
.runcmd(['umount', mount_point
], ignore_fail
=False)
682 self
.runcmd(['kpartx', '-d', self
.settings
['image']], ignore_fail
=True)
684 for dirname
in self
.remove_dirs
:
685 shutil
.rmtree(dirname
)
687 def customize(self
, rootdir
):
688 script
= self
.settings
['customize']
691 if not os
.path
.exists(script
):
692 example
= os
.path
.join("/usr/share/vmdebootstrap/examples/", script
)
693 if not os
.path
.exists(example
):
694 self
.message("Unable to find %s" % script
)
697 self
.message('Running customize script %s' % script
)
698 logging
.info("rootdir=%s image=%s", rootdir
, self
.settings
['image'])
699 with
open('/dev/tty', 'w') as tty
:
700 cliapp
.runcmd([script
, rootdir
, self
.settings
['image']], stdout
=tty
, stderr
=tty
)
702 def create_tarball(self
, rootdir
):
703 # Create a tarball of the disk's contents
704 # shell out to runcmd since it more easily handles rootdir
705 self
.message('Creating tarball of disk contents')
706 self
.runcmd(['tar', '-cf', self
.settings
['tarball'], '-C', rootdir
, '.'])
709 # Change image owner after completed build
710 if self
.settings
['image']:
711 filename
= self
.settings
['image']
712 elif self
.settings
['tarball']:
713 filename
= self
.settings
['tarball']
716 self
.message("Changing owner to %s" % self
.settings
["owner"])
717 subprocess
.call(["chown", self
.settings
["owner"], filename
])
719 def list_installed_pkgs(self
, rootdir
):
720 # output the list of installed packages for sources identification
721 self
.message("Creating a list of installed binary package names")
722 out
= self
.runcmd(['chroot', rootdir
,
723 'dpkg-query', '-W', "-f='${Package}.deb\n'"])
724 with
open('dpkg.list', 'w') as dpkg
:
727 def configure_apt(self
, rootdir
):
728 # use the distribution and mirror to create an apt source
729 self
.message("Configuring apt to use distribution and mirror")
730 conf
= os
.path
.join(rootdir
, 'etc', 'apt', 'sources.list.d', 'base.list')
731 logging
.debug('configure apt %s', conf
)
732 mirror
= self
.settings
['mirror']
733 if self
.settings
['apt-mirror']:
734 mirror
= self
.settings
['apt-mirror']
735 self
.message("Setting apt mirror to %s" % mirror
)
736 os
.unlink(os
.path
.join(rootdir
, 'etc', 'apt', 'sources.list'))
738 line
= 'deb %s %s main\n' % (mirror
, self
.settings
['distribution'])
740 line
= '#deb-src %s %s main\n' % (mirror
, self
.settings
['distribution'])
743 # ensure the apt sources have valid lists
744 self
.runcmd(['chroot', rootdir
, 'apt-get', '-qq', 'update'])
746 if __name__
== '__main__':
747 VmDebootstrap(version
=__version__
).run()