|
358
by Marc Deslauriers
- Add initial CentOS support |
1 |
#
|
2 |
# Uncomplicated VM Builder
|
|
3 |
# Copyright (C) 2007-2009 Canonical Ltd.
|
|
4 |
#
|
|
5 |
# See AUTHORS for list of contributors
|
|
6 |
#
|
|
7 |
# This program is free software: you can redistribute it and/or modify
|
|
8 |
# it under the terms of the GNU General Public License version 3, as
|
|
9 |
# published by the Free Software Foundation.
|
|
10 |
#
|
|
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.
|
|
15 |
#
|
|
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/>.
|
|
18 |
#
|
|
19 |
import logging |
|
20 |
import os, subprocess |
|
21 |
import socket |
|
22 |
import types |
|
23 |
import shutil |
|
24 |
import VMBuilder |
|
25 |
from VMBuilder import register_distro, Distro |
|
26 |
from VMBuilder.util import run_cmd |
|
27 |
from VMBuilder.exception import VMBuilderUserError, VMBuilderException |
|
28 |
||
29 |
class Centos(Distro): |
|
30 |
name = 'Centos' |
|
31 |
arg = 'centos' |
|
32 |
suites = ['centos-4', 'centos-5'] |
|
33 |
||
34 |
# Maps host arch to valid guest archs
|
|
35 |
valid_archs = { 'amd64' : ['amd64', 'i386' ], |
|
36 |
'i386' : [ 'i386' ], |
|
37 |
'lpia' : [ 'i386' ] } |
|
38 |
||
39 |
xen_kernel = '' |
|
40 |
||
41 |
def register_options(self): |
|
42 |
group = self.vm.setting_group('Package options') |
|
43 |
group.add_option('--addpkg', action='append', metavar='PKG', help='Install PKG into the guest (can be specfied multiple times).') |
|
44 |
group.add_option('--removepkg', action='append', metavar='PKG', help='Remove PKG from the guest (can be specfied multiple times)') |
|
45 |
self.vm.register_setting_group(group) |
|
46 |
||
47 |
group = self.vm.setting_group('General OS options') |
|
48 |
self.host_arch = run_cmd('dpkg', '--print-architecture').rstrip() |
|
49 |
group.add_option('-a', '--arch', default=self.host_arch, help='Specify the target architecture. Valid options: amd64 i386 (defaults to host arch)') |
|
50 |
group.add_option('--hostname', default='centos', help='Set NAME as the hostname of the guest. Default: centos. Also uses this name as the VM name.') |
|
51 |
self.vm.register_setting_group(group) |
|
52 |
||
53 |
group = self.vm.setting_group('Installation options') |
|
54 |
group.add_option('--suite', default='centos-4', help='Suite to install. Valid options: %s [default: %%default]' % ' '.join(self.suites)) |
|
55 |
group.add_option('--flavour', '--kernel-flavour', help='Kernel flavour to use. Default and valid options depend on architecture and suite') |
|
|
360
by Marc Deslauriers
- Make --mirror work |
56 |
group.add_option('--mirror', metavar='URL', help='Use Centos mirror at URL instead of the default CentOS mirror list') |
|
366
by Marc Deslauriers
- Add proxy support |
57 |
group.add_option('--proxy', metavar='URL', help='Use proxy at URL for Yum packages') |
|
360
by Marc Deslauriers
- Make --mirror work |
58 |
group.add_option('--install-mirror', metavar='URL', help='Use Centos mirror at URL for the installation only. Yum will still use the default or the URL set by --mirror. Default is: http://mirror.bytemark.co.uk/centos') |
|
358
by Marc Deslauriers
- Add initial CentOS support |
59 |
group.add_option('--lang', metavar='LANG', default=self.get_locale(), help='Set the locale to LANG [default: %default]') |
60 |
group.add_option('--timezone', metavar='TZ', default='UTC', help='Set the timezone to TZ in the vm. [default: %default]') |
|
61 |
self.vm.register_setting_group(group) |
|
62 |
||
63 |
group = self.vm.setting_group('Settings for the initial user') |
|
64 |
group.add_option('--user', default='centos', help='Username of initial user [default: %default]') |
|
65 |
group.add_option('--name', default='Centos', help='Full name of initial user [default: %default]') |
|
66 |
group.add_option('--pass', default='centos', help='Password of initial user [default: %default]') |
|
67 |
group.add_option('--rootpass', default='centos', help='Password of root user [default: %default]') |
|
68 |
group.add_option('--uid', help='Initial UID value.') |
|
69 |
group.add_option('--gid', help='Initial GID value.') |
|
70 |
group.add_option('--lock-user', action='store_true', help='Lock the initial user [default %default]') |
|
71 |
self.vm.register_setting_group(group) |
|
72 |
||
73 |
group = self.vm.setting_group('Other options') |
|
74 |
group.add_option('--ssh-key', metavar='PATH', help='Add PATH to root\'s ~/.ssh/authorized_keys (WARNING: this has strong security implications).') |
|
75 |
group.add_option('--ssh-user-key', help='Add PATH to the user\'s ~/.ssh/authorized_keys.') |
|
76 |
group.add_option('--manifest', metavar='PATH', help='If passed, a manifest will be written to PATH') |
|
77 |
self.vm.register_setting_group(group) |
|
78 |
||
79 |
def set_defaults(self): |
|
|
364
by Marc Deslauriers
- More work on mirror logic |
80 |
pass
|
|
358
by Marc Deslauriers
- Add initial CentOS support |
81 |
|
82 |
def get_locale(self): |
|
83 |
return os.getenv('LANG') |
|
84 |
||
85 |
def preflight_check(self): |
|
86 |
"""While not all of these are strictly checks, their failure would inevitably
|
|
87 |
lead to failure, and since we can check them before we start setting up disk
|
|
88 |
and whatnot, we might as well go ahead an do this now."""
|
|
89 |
||
90 |
if not self.vm.suite in self.suites: |
|
91 |
raise VMBuilderUserError('Invalid suite. Valid suites are: %s' % ' '.join(self.suites)) |
|
92 |
||
93 |
modname = 'VMBuilder.plugins.centos.%s' % (self.vm.suite.replace('-',''), ) |
|
94 |
mod = __import__(modname, fromlist=[self.vm.suite.replace('-','')]) |
|
95 |
self.suite = getattr(mod, self.vm.suite.replace('-','').capitalize())(self.vm) |
|
96 |
||
97 |
if self.vm.arch not in self.valid_archs[self.host_arch] or \ |
|
98 |
not self.suite.check_arch_validity(self.vm.arch): |
|
99 |
raise VMBuilderUserError('%s is not a valid architecture. Valid architectures are: %s' % (self.vm.arch, |
|
100 |
' '.join(self.valid_archs[self.host_arch]))) |
|
101 |
||
102 |
if self.vm.hypervisor.name == 'Xen': |
|
103 |
logging.info('Xen kernel default: linux-image-%s %s', self.suite.xen_kernel_flavour, self.xen_kernel_version()) |
|
104 |
||
105 |
self.vm.virtio_net = self.use_virtio_net() |
|
106 |
||
107 |
if self.vm.lang: |
|
108 |
try: |
|
109 |
run_cmd('locale-gen', '%s' % self.vm.lang) |
|
110 |
except VMBuilderException, e: |
|
111 |
msg = "locale-gen does not recognize your locale '%s'" % self.vm.lang |
|
112 |
raise VMBuilderUserError(msg) |
|
113 |
||
|
359
by Marc Deslauriers
- VMBuilder/plugins/centos/distro.py: remove unused options, make sure |
114 |
# Make sure rinse is installed
|
115 |
try: |
|
116 |
run_cmd('/usr/sbin/rinse') |
|
117 |
except VMBuilderException, e: |
|
118 |
msg = "The 'rinse' utility doesn't seem to be installed." |
|
119 |
raise VMBuilderUserError(msg) |
|
120 |
||
|
358
by Marc Deslauriers
- Add initial CentOS support |
121 |
if getattr(self.vm, 'ec2', False): |
122 |
self.get_ec2_kernel() |
|
123 |
self.get_ec2_ramdisk() |
|
124 |
self.apply_ec2_settings() |
|
125 |
||
126 |
def install(self, destdir): |
|
127 |
self.destdir = destdir |
|
128 |
self.suite.install(destdir) |
|
129 |
||
130 |
def install_vmbuilder_log(self, logfile, rootdir): |
|
131 |
self.suite.install_vmbuilder_log(logfile, rootdir) |
|
132 |
||
133 |
def post_mount(self, fs): |
|
134 |
self.suite.post_mount(fs) |
|
135 |
||
136 |
def use_virtio_net(self): |
|
137 |
return self.suite.virtio_net |
|
138 |
||
139 |
def install_bootloader_cleanup(self): |
|
140 |
self.vm.cancel_cleanup(self.install_bootloader_cleanup) |
|
141 |
tmpdir = '%s/tmp/vmbuilder-grub' % self.destdir |
|
142 |
for disk in os.listdir(tmpdir): |
|
143 |
if disk != 'device.map': |
|
144 |
run_cmd('umount', os.path.join(tmpdir, disk)) |
|
145 |
shutil.rmtree(tmpdir) |
|
146 |
||
147 |
def install_bootloader(self): |
|
148 |
tmpdir = '/tmp/vmbuilder-grub' |
|
149 |
os.makedirs('%s%s' % (self.destdir, tmpdir)) |
|
150 |
self.vm.add_clean_cb(self.install_bootloader_cleanup) |
|
151 |
devmapfile = os.path.join(tmpdir, 'device.map') |
|
152 |
devmap = open('%s%s' % (self.destdir, devmapfile), 'w') |
|
153 |
for (disk, id) in zip(self.vm.disks, range(len(self.vm.disks))): |
|
154 |
new_filename = os.path.join(tmpdir, os.path.basename(disk.filename)) |
|
155 |
open('%s%s' % (self.destdir, new_filename), 'w').close() |
|
156 |
run_cmd('mount', '--bind', disk.filename, '%s%s' % (self.destdir, new_filename)) |
|
157 |
devmap.write("(hd%d) %s\n" % (id, new_filename)) |
|
158 |
devmap.close() |
|
159 |
#
|
|
160 |
# There are a couple of reasons why grub installation can fail:
|
|
161 |
#
|
|
162 |
# "Error 2: Bad file or directory type" can be caused by an ext3
|
|
163 |
# partition with 256 bit inodes and an older distro. See workaround
|
|
164 |
# in disk.py.
|
|
165 |
#
|
|
166 |
# "Error 18: Selected cylinder exceeds maximum supported by BIOS"
|
|
167 |
# can be caused by grub detecting a geometry that may not be
|
|
168 |
# compatible with an older BIOS. We work around this below by
|
|
169 |
# setting the geometry with bogus values:
|
|
170 |
#
|
|
171 |
self.run_in_target('grub', '--device-map=%s' % devmapfile, '--batch', stdin='''root (hd0,0) |
|
172 |
geometry (hd0) 800 800 800
|
|
173 |
setup (hd0)
|
|
174 |
EOT''') |
|
175 |
self.install_bootloader_cleanup() |
|
176 |
||
177 |
def xen_kernel_version(self): |
|
178 |
if self.suite.xen_kernel_flavour: |
|
179 |
if not self.xen_kernel: |
|
180 |
rmad = run_cmd('rmadison', 'linux-image-%s' % self.suite.xen_kernel_flavour) |
|
181 |
version = ['0', '0','0', '0'] |
|
182 |
||
183 |
for line in rmad.splitlines(): |
|
184 |
sline = line.split('|') |
|
185 |
||
186 |
if sline[2].strip().startswith(self.vm.suite): |
|
187 |
vt = sline[1].strip().split('.') |
|
188 |
for i in range(4): |
|
189 |
if int(vt[i]) > int(version[i]): |
|
190 |
version = vt |
|
191 |
break
|
|
192 |
||
193 |
if version[0] == '0': |
|
194 |
raise VMBuilderException('Something is wrong, no valid xen kernel for the suite %s found by rmadison' % self.vm.suite) |
|
195 |
||
196 |
self.xen_kernel = '%s.%s.%s-%s' % (version[0],version[1],version[2],version[3]) |
|
197 |
return self.xen_kernel |
|
198 |
else: |
|
199 |
raise VMBuilderUserError('There is no valid xen kernel for the suite selected.') |
|
200 |
||
201 |
def xen_kernel_initrd_path(self, which): |
|
202 |
path = '/boot/%s-%s-%s' % (which, self.xen_kernel_version(), self.suite.xen_kernel_flavour) |
|
203 |
return path |
|
204 |
||
205 |
def xen_kernel_path(self): |
|
206 |
return self.xen_kernel_initrd_path('kernel') |
|
207 |
||
208 |
def xen_ramdisk_path(self): |
|
209 |
return self.xen_kernel_initrd_path('ramdisk') |
|
210 |
||
211 |
def get_ec2_kernel(self): |
|
212 |
if self.suite.ec2_kernel_info: |
|
213 |
return self.suite.ec2_kernel_info[self.vm.arch] |
|
214 |
else: |
|
215 |
raise VMBuilderUserError('EC2 is not supported for the suite selected') |
|
216 |
||
217 |
def get_ec2_ramdisk(self): |
|
218 |
if self.suite.ec2_ramdisk_info: |
|
219 |
return self.suite.ec2_ramdisk_info[self.vm.arch] |
|
220 |
else: |
|
221 |
raise VMBuilderUserError('EC2 is not supported for the suite selected') |
|
222 |
||
223 |
def disable_hwclock_access(self): |
|
224 |
return self.suite.disable_hwclock_access() |
|
225 |
||
226 |
def apply_ec2_settings(self): |
|
227 |
return self.suite.apply_ec2_settings() |
|
228 |
||
229 |
register_distro(Centos) |