]>
git.siccegge.de Git - forks/vmdebootstrap.git/blob - vmdebootstrap
b66a84ed182a87706ebd7fb786f130d4395f1815
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 (default if extlinux used)')
162 self
.settings
.boolean(
164 'Install and configure grub2 - disables extlinux.')
165 self
.settings
.boolean(
167 'Do not 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'] or self
.settings
['extlinux']:
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 msg
= "mbr enabled but /sbin/install-mbr not found" \
345 " - please install the mbr package."
346 raise cliapp
.AppException(msg
)
348 def setup_kpartx(self
):
350 out
= self
.runcmd(['kpartx', '-avs', self
.settings
['image']])
351 if self
.settings
['bootsize']:
359 devices
= [line
.split()[2]
360 for line
in out
.splitlines()
361 if line
.startswith('add map ')]
362 if len(devices
) != parts
:
363 msg
= 'Surprising number of partitions'
364 logging
.debug("%s: devices=%s parts=%s", msg
, devices
, parts
)
365 raise cliapp
.AppException(msg
)
366 root
= '/dev/mapper/%s' % devices
[rootindex
]
367 if self
.settings
['bootsize']:
368 boot
= '/dev/mapper/%s' % devices
[bootindex
]
371 def mkfs(self
, device
, fstype
):
372 self
.message('Creating filesystem %s' % fstype
)
373 self
.runcmd(['mkfs', '-t', fstype
, device
])
375 def debootstrap(self
, rootdir
):
376 msg
= "(%s)" % self
.settings
['variant'] if self
.settings
['variant'] else ''
377 self
.message('Debootstrapping %s %s' % (self
.settings
['distribution'], msg
))
379 include
= self
.settings
['package']
381 if not self
.settings
['foreign']:
382 include
.append('acpid')
384 if self
.settings
['grub']:
385 include
.append('grub2')
387 if not self
.settings
['no-kernel']:
388 if self
.settings
['arch'] == 'i386':
391 kernel_arch
= self
.settings
['arch']
392 kernel_image
= 'linux-image-%s' % kernel_arch
393 include
.append(kernel_image
)
395 if self
.settings
['sudo'] and 'sudo' not in include
:
396 include
.append('sudo')
398 args
= ['debootstrap', '--arch=%s' % self
.settings
['arch']]
400 if self
.settings
['package']:
402 '--include=%s' % ','.join(include
))
403 if self
.settings
['foreign']:
404 args
.append('--foreign')
405 if self
.settings
['variant']:
406 args
.append('--variant')
407 args
.append(self
.settings
['variant'])
408 args
+= [self
.settings
['distribution'],
409 rootdir
, self
.settings
['mirror']]
410 logging
.debug(" ".join(args
))
412 if self
.settings
['foreign']:
413 # set a noninteractive debconf environment for secondstage
415 "DEBIAN_FRONTEND": "noninteractive",
416 "DEBCONF_NONINTERACTIVE_SEEN": "true",
419 # add the mapping to the complete environment.
420 env
.update(os
.environ
)
421 # First copy the binfmt handler over
422 self
.message('Setting up binfmt handler')
423 shutil
.copy(self
.settings
['foreign'], '%s/usr/bin/' % rootdir
)
424 # Next, run the package install scripts etc.
425 self
.message('Running debootstrap second stage')
426 self
.runcmd(['chroot', rootdir
,
427 '/debootstrap/debootstrap', '--second-stage'],
430 def set_hostname(self
, rootdir
):
431 hostname
= self
.settings
['hostname']
432 with
open(os
.path
.join(rootdir
, 'etc', 'hostname'), 'w') as f
:
433 f
.write('%s\n' % hostname
)
435 etc_hosts
= os
.path
.join(rootdir
, 'etc', 'hosts')
437 with
open(etc_hosts
, 'r') as f
:
439 with
open(etc_hosts
, 'w') as f
:
440 for line
in data
.splitlines():
441 if line
.startswith('127.0.0.1'):
442 line
+= ' %s' % hostname
443 f
.write('%s\n' % line
)
447 def create_fstab(self
, rootdir
, rootdev
, roottype
, bootdev
, boottype
): # pylint: disable=too-many-arguments
449 out
= self
.runcmd(['blkid', '-c', '/dev/null', '-o', 'value',
450 '-s', 'UUID', device
])
451 return out
.splitlines()[0].strip()
454 rootdevstr
= 'UUID=%s' % fsuuid(rootdev
)
456 rootdevstr
= '/dev/sda1'
459 bootdevstr
= 'UUID=%s' % fsuuid(bootdev
)
463 fstab
= os
.path
.join(rootdir
, 'etc', 'fstab')
464 with
open(fstab
, 'w') as f
:
465 f
.write('proc /proc proc defaults 0 0\n')
466 f
.write('%s / %s errors=remount-ro 0 1\n' % (rootdevstr
, roottype
))
468 f
.write('%s /boot %s errors=remount-ro 0 2\n' % (bootdevstr
, boottype
))
470 def install_debs(self
, rootdir
):
471 if not self
.settings
['custom-package']:
473 self
.message('Installing custom packages')
474 tmp
= os
.path
.join(rootdir
, 'tmp', 'install_debs')
476 for deb
in self
.settings
['custom-package']:
477 shutil
.copy(deb
, tmp
)
478 filenames
= [os
.path
.join('/tmp/install_debs', os
.path
.basename(deb
))
479 for deb
in self
.settings
['custom-package']]
481 self
.runcmd_unchecked(['chroot', rootdir
, 'dpkg', '-i'] + filenames
)
482 logging
.debug('stdout:\n%s', out
)
483 logging
.debug('stderr:\n%s', err
)
484 out
= self
.runcmd(['chroot', rootdir
,
485 'apt-get', '-f', '--no-remove', 'install'])
486 logging
.debug('stdout:\n%s', out
)
489 def cleanup_apt_cache(self
, rootdir
):
490 out
= self
.runcmd(['chroot', rootdir
, 'apt-get', 'clean'])
491 logging
.debug('stdout:\n%s', out
)
493 def set_root_password(self
, rootdir
):
494 if self
.settings
['root-password']:
495 self
.message('Setting root password')
496 self
.set_password(rootdir
, 'root', self
.settings
['root-password'])
497 elif self
.settings
['lock-root-password']:
498 self
.message('Locking root password')
499 self
.runcmd(['chroot', rootdir
, 'passwd', '-l', 'root'])
501 self
.message('Give root an empty password')
502 self
.delete_password(rootdir
, 'root')
504 def create_users(self
, rootdir
):
505 def create_user(user
):
506 self
.runcmd(['chroot', rootdir
, 'adduser', '--gecos', user
,
507 '--disabled-password', user
])
508 if self
.settings
['sudo']:
509 self
.runcmd(['chroot', rootdir
, 'adduser', user
, 'sudo'])
511 for userpass
in self
.settings
['user']:
513 user
, password
= userpass
.split('/', 1)
515 self
.set_password(rootdir
, user
, password
)
517 create_user(userpass
)
518 self
.delete_password(rootdir
, userpass
)
520 def set_password(self
, rootdir
, user
, password
):
521 encrypted
= crypt
.crypt(password
, '..')
522 self
.runcmd(['chroot', rootdir
, 'usermod', '-p', encrypted
, user
])
524 def delete_password(self
, rootdir
, user
):
525 self
.runcmd(['chroot', rootdir
, 'passwd', '-d', user
])
527 def remove_udev_persistent_rules(self
, rootdir
):
528 self
.message('Removing udev persistent cd and net rules')
529 for x
in ['70-persistent-cd.rules', '70-persistent-net.rules']:
530 pathname
= os
.path
.join(rootdir
, 'etc', 'udev', 'rules.d', x
)
531 if os
.path
.exists(pathname
):
532 logging
.debug('rm %s', pathname
)
535 logging
.debug('not removing non-existent %s', pathname
)
537 def setup_networking(self
, rootdir
):
538 self
.message('Setting up networking')
540 f
= open(os
.path
.join(rootdir
, 'etc', 'network', 'interfaces'), 'w')
542 f
.write('iface lo inet loopback\n')
544 if self
.settings
['enable-dhcp']:
546 f
.write('auto eth0\n')
547 f
.write('iface eth0 inet dhcp\n')
551 def append_serial_console(self
, rootdir
):
552 if self
.settings
['serial-console']:
553 serial_command
= self
.settings
['serial-console-command']
554 logging
.debug('adding getty to serial console')
555 inittab
= os
.path
.join(rootdir
, 'etc/inittab')
556 # to autologin, serial_command can contain '-a root'
557 with
open(inittab
, 'a') as f
:
558 f
.write('\nS0:23:respawn:%s\n' % serial_command
)
560 def install_grub2(self
, rootdev
, rootdir
):
561 self
.message("Configuring grub2")
562 # rely on kpartx using consistent naming to map loop0p1 to loop0
563 install_dev
= os
.path
.join('/dev', os
.path
.basename(rootdev
)[:-2])
564 self
.runcmd(['mount', '/dev', '-t', 'devfs', '-obind',
565 '%s' % os
.path
.join(rootdir
, 'dev')])
566 self
.runcmd(['mount', '/proc', '-t', 'proc', '-obind',
567 '%s' % os
.path
.join(rootdir
, 'proc')])
568 self
.runcmd(['mount', '/sys', '-t', 'sysfs', '-obind',
569 '%s' % os
.path
.join(rootdir
, 'sys')])
571 self
.runcmd(['chroot', rootdir
, 'update-grub'])
572 self
.runcmd(['chroot', rootdir
, 'grub-install', install_dev
])
573 except cliapp
.AppException
:
574 self
.message("Failed. Is grub2-common installed? Using extlinux.")
575 self
.runcmd(['umount', os
.path
.join(rootdir
, 'sys')])
576 self
.runcmd(['umount', os
.path
.join(rootdir
, 'proc')])
577 self
.runcmd(['umount', os
.path
.join(rootdir
, 'dev')])
578 self
.install_extlinux(rootdev
, rootdir
)
580 def install_extlinux(self
, rootdev
, rootdir
):
581 if not os
.path
.exists("/usr/bin/extlinux"):
582 self
.message("extlinux not installed, skipping.")
584 self
.message('Installing extlinux')
587 dirname
= os
.path
.join(rootdir
, 'boot')
588 basenames
= os
.listdir(dirname
)
589 logging
.debug('find: %s', basenames
)
590 for basename
in basenames
:
591 if re
.search(pattern
, basename
):
592 return os
.path
.join('boot', basename
)
593 raise cliapp
.AppException('Cannot find match: %s' % pattern
)
596 kernel_image
= find('vmlinuz-.*')
597 initrd_image
= find('initrd.img-.*')
598 except cliapp
.AppException
as e
:
599 self
.message("Unable to find kernel. Not installing extlinux.")
600 logging
.debug("No kernel found. %s. Skipping install of extlinux.", e
)
603 out
= self
.runcmd(['blkid', '-c', '/dev/null', '-o', 'value',
604 '-s', 'UUID', rootdev
])
605 uuid
= out
.splitlines()[0].strip()
607 conf
= os
.path
.join(rootdir
, 'extlinux.conf')
608 logging
.debug('configure extlinux %s', conf
)
609 kserial
= 'console=ttyS0,115200' if self
.settings
['serial-console'] else ''
610 extserial
= 'serial 0 115200' if self
.settings
['serial-console'] else ''
617 append initrd=%(initrd)s root=UUID=%(uuid)s ro %(kserial)s
620 'kernel': kernel_image
, # pylint: disable=bad-continuation
621 'initrd': initrd_image
, # pylint: disable=bad-continuation
622 'uuid': uuid
, # pylint: disable=bad-continuation
623 'kserial': kserial
, # pylint: disable=bad-continuation
624 'extserial': extserial
, # pylint: disable=bad-continuation
625 } # pylint: disable=bad-continuation
626 logging
.debug("extlinux config:\n%s", msg
)
628 # python multiline string substitution is just ugly.
629 # use an external file or live with the mangling, no point in
630 # mangling the string to remove spaces just to keep it pretty in source.
634 self
.runcmd(['extlinux', '--install', rootdir
])
635 self
.runcmd(['sync'])
638 def optimize_image(self
, rootdir
):
640 Filing up the image with zeros will increase its compression rate
642 if not self
.settings
['sparse']:
643 zeros
= os
.path
.join(rootdir
, 'ZEROS')
644 self
.runcmd_unchecked(['dd', 'if=/dev/zero', 'of=' + zeros
, 'bs=1M'])
645 self
.runcmd(['rm', '-f', zeros
])
649 Run squashfs on the image.
651 if not os
.path
.exists('/usr/bin/mksquashfs'):
652 logging
.warning("Squash selected but mksquashfs not found!")
654 self
.message("Running mksquashfs")
655 suffixed
= "%s.squashfs" % self
.settings
['image']
656 self
.runcmd(['mksquashfs', self
.settings
['image'],
658 '-no-progress', '-comp', 'xz'], ignore_fail
=False)
659 os
.unlink(self
.settings
['image'])
660 self
.settings
['image'] = suffixed
662 def cleanup_system(self
):
663 # Clean up after any errors.
665 self
.message('Cleaning up')
667 # Umount in the reverse mount order
668 if self
.settings
['image']:
669 for i
in range(len(self
.mount_points
) - 1, -1, -1):
670 mount_point
= self
.mount_points
[i
]
672 self
.runcmd(['umount', mount_point
], ignore_fail
=False)
673 except cliapp
.AppException
:
674 logging
.debug("umount failed, sleeping and trying again")
676 self
.runcmd(['umount', mount_point
], ignore_fail
=False)
678 self
.runcmd(['kpartx', '-d', self
.settings
['image']], ignore_fail
=True)
680 for dirname
in self
.remove_dirs
:
681 shutil
.rmtree(dirname
)
683 def customize(self
, rootdir
):
684 script
= self
.settings
['customize']
687 if not os
.path
.exists(script
):
688 example
= os
.path
.join("/usr/share/vmdebootstrap/examples/", script
)
689 if not os
.path
.exists(example
):
690 self
.message("Unable to find %s" % script
)
693 self
.message('Running customize script %s' % script
)
694 logging
.info("rootdir=%s image=%s", rootdir
, self
.settings
['image'])
695 with
open('/dev/tty', 'w') as tty
:
696 cliapp
.runcmd([script
, rootdir
, self
.settings
['image']], stdout
=tty
, stderr
=tty
)
698 def create_tarball(self
, rootdir
):
699 # Create a tarball of the disk's contents
700 # shell out to runcmd since it more easily handles rootdir
701 self
.message('Creating tarball of disk contents')
702 self
.runcmd(['tar', '-cf', self
.settings
['tarball'], '-C', rootdir
, '.'])
705 # Change image owner after completed build
706 if self
.settings
['image']:
707 filename
= self
.settings
['image']
708 elif self
.settings
['tarball']:
709 filename
= self
.settings
['tarball']
712 self
.message("Changing owner to %s" % self
.settings
["owner"])
713 subprocess
.call(["chown", self
.settings
["owner"], filename
])
715 def list_installed_pkgs(self
, rootdir
):
716 # output the list of installed packages for sources identification
717 self
.message("Creating a list of installed binary package names")
718 out
= self
.runcmd(['chroot', rootdir
,
719 'dpkg-query', '-W', "-f='${Package}.deb\n'"])
720 with
open('dpkg.list', 'w') as dpkg
:
723 def configure_apt(self
, rootdir
):
724 # use the distribution and mirror to create an apt source
725 self
.message("Configuring apt to use distribution and mirror")
726 conf
= os
.path
.join(rootdir
, 'etc', 'apt', 'sources.list.d', 'base.list')
727 logging
.debug('configure apt %s', conf
)
728 mirror
= self
.settings
['mirror']
729 if self
.settings
['apt-mirror']:
730 mirror
= self
.settings
['apt-mirror']
731 self
.message("Setting apt mirror to %s" % mirror
)
732 os
.unlink(os
.path
.join(rootdir
, 'etc', 'apt', 'sources.list'))
734 line
= 'deb %s %s main\n' % (mirror
, self
.settings
['distribution'])
736 line
= '#deb-src %s %s main\n' % (mirror
, self
.settings
['distribution'])
739 # ensure the apt sources have valid lists
740 self
.runcmd(['chroot', rootdir
, 'apt-get', 'update'])
742 if __name__
== '__main__':
743 VmDebootstrap(version
=__version__
).run()