]>
git.siccegge.de Git - forks/vmdebootstrap.git/blob - vmdebootstrap
84ee0079749ebbd98ba2b1af76ae3a89670db3c5
2 # Copyright 2011 Lars Wirzenius
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 class VmDebootstrap(cliapp
.Application
):
28 def add_settings(self
):
29 default_arch
= 'amd64'
31 self
.settings
.add_boolean_setting(['verbose'],
32 'report what is going on')
33 self
.settings
.add_string_setting(['image'],
34 'put created disk image in FILE',
36 self
.settings
.add_bytesize_setting(['size'],
37 'create a disk image of size SIZE '
41 self
.settings
.add_string_setting(['mirror'],
42 'use MIRROR as package source '
45 default
='http://cdn.debian.net/debian/')
46 self
.settings
.add_string_setting(['arch'],
47 'architecture to use '
51 self
.settings
.add_string_setting(['distribution'],
52 'release to use (%default)',
56 def process_args(self
, args
):
57 if not self
.settings
['image']:
58 raise cliapp
.AppException('You must give image filename.')
59 if not self
.settings
['size']:
60 raise cliapp
.AppException('You must give image size.')
63 self
.mount_points
= []
66 self
.create_empty_image()
67 self
.partition_image()
69 rootdev
= self
.setup_kpartx()
71 rootdir
= self
.mount(rootdev
)
72 self
.debootstrap(rootdir
)
73 self
.set_root_password(rootdir
)
74 self
.install_extlinux(rootdev
, rootdir
)
81 def message(self
, msg
):
82 if self
.settings
['verbose']:
85 def runcmd(self
, argv
, stdin
='', ignore_fail
=False, **kwargs
):
86 logging
.debug('runcmd: %s %s' % (argv
, kwargs
))
87 p
= subprocess
.Popen(argv
, stdin
=subprocess
.PIPE
,
88 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
,
90 out
, err
= p
.communicate(stdin
)
92 msg
= 'command failed: %s\n%s' % (argv
, err
)
95 raise cliapp
.AppException(msg
)
99 dirname
= tempfile
.mkdtemp()
100 self
.remove_dirs
.append(dirname
)
101 logging
.debug('mkdir %s' % dirname
)
104 def mount(self
, device
):
105 self
.message('Mounting %s' % device
)
106 mount_point
= self
.mkdtemp()
107 self
.runcmd(['mount', device
, mount_point
])
108 self
.mount_points
.append(mount_point
)
109 logging
.debug('mounted %s on %s' % (device
, mount_point
))
112 def create_empty_image(self
):
113 self
.message('Creating disk image')
114 self
.runcmd(['qemu-img', 'create', '-f', 'raw',
115 self
.settings
['image'],
116 str(self
.settings
['size'])])
118 def partition_image(self
):
119 self
.message('Creating partitions')
120 self
.runcmd(['parted', '-s', self
.settings
['image'],
122 self
.runcmd(['parted', '-s', self
.settings
['image'],
123 'mkpart', 'primary', '0%', '100%'])
124 self
.runcmd(['parted', '-s', self
.settings
['image'],
125 'set', '1', 'boot', 'on'])
127 def install_mbr(self
):
128 self
.message('Installing MBR')
129 self
.runcmd(['install-mbr', self
.settings
['image']])
131 def setup_kpartx(self
):
132 out
= self
.runcmd(['kpartx', '-av', self
.settings
['image']])
133 devices
= [line
.split()[2]
134 for line
in out
.splitlines()
135 if line
.startswith('add map ')]
136 if len(devices
) != 1:
137 raise cliapp
.AppException('Surprising number of partitions')
138 return '/dev/mapper/%s' % devices
[0]
140 def mkfs(self
, device
):
141 self
.message('Creating filesystem')
142 self
.runcmd(['mkfs', '-t', 'ext2', device
])
144 def debootstrap(self
, rootdir
):
145 self
.message('Debootstrapping')
147 if self
.settings
['arch'] == 'i386':
150 kernel_arch
= self
.settings
['arch']
151 kernel_image
= 'linux-image-2.6-%s' % kernel_arch
153 include
= [kernel_image
]
155 self
.runcmd(['debootstrap',
156 '--arch=%s' % self
.settings
['arch'],
157 '--include=%s' % ','.join(include
),
158 self
.settings
['distribution'],
160 self
.settings
['mirror']])
162 def set_root_password(self
, rootdir
):
163 self
.message('Removing root password')
164 self
.runcmd(['chroot', rootdir
, 'passwd', '-d', 'root'])
166 def install_extlinux(self
, rootdev
, rootdir
):
167 self
.message('Installing extlinux')
170 dirname
= os
.path
.join(rootdir
, 'boot')
171 basenames
= os
.listdir(dirname
)
172 logging
.debug('find: %s' % basenames
)
173 for basename
in basenames
:
174 if re
.search(pattern
, basename
):
175 return os
.path
.join('boot', basename
)
176 raise cliapp
.AppException('Cannot find match: %s' % pattern
)
178 kernel_image
= find('vmlinuz-.*')
179 initrd_image
= find('initrd.img-.*')
181 out
= self
.runcmd(['blkid', '-c', '/dev/null', '-o', 'value',
182 '-s', 'UUID', rootdev
])
183 uuid
= out
.splitlines()[0].strip()
185 conf
= os
.path
.join(rootdir
, 'extlinux.conf')
186 logging
.debug('configure extlinux %s' % conf
)
194 append initrd=%(initrd)s root=UUID=%(uuid)s ro
196 'kernel': kernel_image
,
197 'initrd': initrd_image
,
202 self
.runcmd(['extlinux', '--install', rootdir
])
203 self
.runcmd(['sync'])
204 import time
; time
.sleep(2)
207 # Clean up after any errors.
209 self
.message('Cleaning up')
211 for mount_point
in self
.mount_points
:
212 self
.runcmd(['umount', mount_point
], ignore_fail
=True)
214 self
.runcmd(['kpartx', '-d', self
.settings
['image']], ignore_fail
=True)
216 for dirname
in self
.remove_dirs
:
217 shutil
.rmtree(dirname
)
220 if __name__
== '__main__':
221 VmDebootstrap().run()