source: ProjectBuilder/devel/rpmbootstrap/bin/rpmbootstrap@ 990

Last change on this file since 990 was 990, checked in by Bruno Cornec, 14 years ago

r3709@localhost: bruno | 2010-02-22 22:05:32 +0100

  • First addition of OpenSUSE support in rpmbootstrap
File size: 13.6 KB
Line 
1#!/usr/bin/perl -w
2#
3# rpmbootstrap application, a debootstrap like for RPM distros
4#
5# $Id$
6#
7# Copyright B. Cornec 2010
8# Provided under the GPL v2
9
10# Syntax: see at end
11
12use strict 'vars';
13use Getopt::Long qw(:config auto_abbrev no_ignore_case);
14use Data::Dumper;
15use English;
16use LWP::UserAgent;
17use File::Basename;
18use File::Copy;
19use File::Find;
20use ProjectBuilder::Version;
21use ProjectBuilder::Base;
22use ProjectBuilder::Env;
23use ProjectBuilder::Conf;
24use ProjectBuilder::Distribution;
25
26# Global variables
27my %opts; # CLI Options
28
29=pod
30
31=head1 NAME
32
33rpmbootstrap - creates a chrooted RPM based distribution a la debootstrap, aka Virtual Environment (VE)
34
35=head1 DESCRIPTION
36
37rpmbootstrap creates a chroot environment (Virtual Environment or VE) with a minimal distribution in it,
38suited for building packages for example. It's very much like debootstrap but for RPM based distribution.
39It aims at supporting all distributions supported by project-builder;org (RHEL, RH, Fedora, OpeSUSE, SLES, Mandriva, ...)
40
41It is inspired by work done by Steve Kemp for rinse (http://www.steve.org.uk/), and similar to mock, but fully integrated with project-builder.org (which also supports rinse and mock).
42
43=head1 SYNOPSIS
44
45rpmbootstrap [-vhmqpdk][-s script][-i iso] distribution-version-arch [target-dir] [mirror [script]]
46
47pb [--verbose][--help][--man][--quiet][--print-rpms][--download-only][--keep][--include pkg1, pkg2, ...][--script script][--iso iso] distribution-version-arch [target-dir] [mirror [script]]
48
49=head1 OPTIONS
50
51=over 4
52
53=item B<-v|--verbose>
54
55Print a brief help message and exits.
56
57=item B<-h|--help>
58
59Print a brief help message and exits.
60
61=item B<--man>
62
63Prints the manual page and exits.
64
65=item B<-q|--quiet>
66
67Do not print any output.
68
69=item B<-p|--print-rpms>
70
71Print the packages to be installed, and exit. Note that a target directory must be specified so
72rpmbootstrap can determine which packages should be installed, and to resolve dependencies. The target directory will be deleted.
73
74=item B<-d|--download-only>
75
76Download packages, but don't perform installation.
77
78=item B<-k|--keep>
79
80Keep packages in the cache dir for later reuse. By default remove them.
81
82=item B<-s|--script script>
83
84Name of the script you want to execute on the related VEs after the installation.
85It is executed in host environment. You can use the chroot command to execute actions in the VE.
86
87=item B<-i|--iso iso_image>
88
89Name of the ISO image of the distribution you want to install on the related VE.
90
91=back
92
93=head1 ARGUMENTS
94
95=item B<distribution-version-arch>
96
97Full name of the distribution that needs to be installed in the VE. E.g. fedora-11-x86_64.
98
99=item B<target-dir>
100
101This is the target directory under which the VE will be created. Created on the fly if needed. If none is given use the default directory hosting VE for project-builder.org (Cf: vepath parameter in $HOME/.pbrc)
102
103=head1 EXAMPLE
104
105To setup a Fedora 12 distribution with an i386 architecture issue:
106
107rpmbootstrap fedora-12-i386 /tmp/fedora/12/i386
108
109
110=head1 WEB SITES
111
112The main Web site of the project is available at L<http://www.project-builder.org/>. Bug reports should be filled using the trac instance of the project at L<http://trac.project-builder.org/>.
113
114=head1 USER MAILING LIST
115
116Cf: L<http://www.mondorescue.org/sympa/info/pb-announce> for announces and L<http://www.mondorescue.org/sympa/info/pb-devel> for the development of the pb project.
117
118=head1 CONFIGURATION FILE
119
120Uses Project-Builder.org configuration file (/etc/pb/pb.conf or /usr/local/etc/pb/pb.conf)
121
122=head1 AUTHORS
123
124The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
125
126=head1 COPYRIGHT
127
128Project-Builder.org is distributed under the GPL v2.0 license
129described in the file C<COPYING> included with the distribution.
130
131=cut
132
133# ---------------------------------------------------------------------------
134
135my ($projectbuilderver,$projectbuilderrev) = pb_version_init();
136my $appname = "rpmbootstrap";
137$ENV{'PBPROJ'} = $appname;
138
139# Initialize the syntax string
140
141pb_syntax_init("$appname Version $projectbuilderver-$projectbuilderrev\n");
142pb_temp_init();
143
144GetOptions("help|?|h" => \$opts{'h'},
145 "man|m" => \$opts{'man'},
146 "verbose|v+" => \$opts{'v'},
147 "quiet|q" => \$opts{'q'},
148 "log-files|l=s" => \$opts{'l'},
149 "script|s=s" => \$opts{'s'},
150 "print-rpms|p" => \$opts{'p'},
151 "download-only|d" => \$opts{'d'},
152 "keep|k" => \$opts{'k'},
153 "iso|i=s" => \$opts{'i'},
154 "version|V=s" => \$opts{'V'},
155) || pb_syntax(-1,0);
156
157if (defined $opts{'h'}) {
158 pb_syntax(0,1);
159}
160if (defined $opts{'man'}) {
161 pb_syntax(0,2);
162}
163if (defined $opts{'v'}) {
164 $pbdebug = $opts{'v'};
165}
166if (defined $opts{'q'}) {
167 $pbdebug=-1;
168}
169if (defined $opts{'l'}) {
170 open(pbLOG,"> $opts{'l'}") || die "Unable to log to $opts{'l'}: $!";
171 $pbLOG = \*pbLOG;
172 $pbdebug = 0 if ($pbdebug == -1);
173 }
174pb_log_init($pbdebug, $pbLOG);
175#pb_display_init("text","");
176
177#if (defined $opts{'s'}) {
178#$pbscript = $opts{'s'};
179#}
180#if (defined $opts{'i'}) {
181#$iso = $opts{'i'};
182#}
183
184# Get VE name
185$ENV{'PBV'} = shift @ARGV;
186die pb_syntax(-1,1) if (not defined $ENV{'PBV'});
187
188die "Needs to be run as root" if ($EFFECTIVE_USER_ID != 0);
189
190#
191# Initialize distribution info from pb conf file
192#
193pb_log(0,"Starting VE build for $ENV{'PBV'}\n");
194my ($name,$ver,$darch) = split(/-/,$ENV{'PBV'});
195chomp($darch);
196my ($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd) = pb_distro_init($name,$ver,$darch);
197
198#
199# Check target dir
200# Create if not existent and use default if none given
201#
202pb_env_init_pbrc(); # to get content of HOME/.pbrc
203my $vepath = shift @ARGV;
204
205#
206# Check for command requirements
207#
208my ($req,$opt) = pb_conf_get_if("oscmd","oscmdopt");
209my ($req2,$opt2) = (undef,undef);
210$req2 = $req->{$appname} if (defined $req);
211$opt2 = $opt->{$appname} if (defined $opt);
212pb_check_requirements($req2,$opt2);
213
214if (not defined $vepath) {
215 my ($vestdpath) = pb_conf_get_if("vepath");
216 $vepath = "$vestdpath->{'default'}/$ddir/$dver/$darch";
217}
218
219die pb_log(0,"No target-dir specified and no default vepath found in $ENV{'PBETC'}\n") if (not defined $vepath);
220
221pb_mkdir_p($vepath) if (! -d $vepath);
222
223#
224# Get the package list to download, store them in a cache directory
225#
226my ($rbsmindep,$rbsmirrorsrv) = pb_conf_get("rbsmindep","rbsmirrorsrv");
227my ($rbscachedir) = pb_conf_get_if("rbscachedir");
228my $pkgs = pb_distro_get_param($ddir,$dver,$darch,$rbsmindep);
229my $mirror = pb_distro_get_param($ddir,$dver,$darch,$rbsmirrorsrv);
230
231my $cachedir = "/var/cache/rpmbootstrap";
232$cachedir = $rbscachedir->{'default'} if (defined $rbscachedir->{'default'});
233
234# Point to the right subdir and create it if needed
235$cachedir .= "/$ddir-$dver-$darch";
236pb_mkdir_p($cachedir) if (! -d $cachedir);
237
238# Get the complete package name from the mirror
239#
240my $ua = LWP::UserAgent->new;
241$ua->timeout(10);
242$ua->env_proxy;
243
244pb_log(0,"Downloading package list from $mirror ...\n");
245my $response = $ua->get($mirror);
246if (! $response->is_success) {
247 die "Unable to download packages from $mirror for $ddir-$dver-$darch";
248}
249pb_log(3,"Mirror $mirror gave answer: ".Dumper($response->dump(maxlength => 0))."\n");
250
251# Manages architectures specificities
252my $parch = $darch;
253$parch = "i[3456]86" if ($darch eq "i386");
254
255# Get the list of packages and their URL in this hash
256my %url;
257foreach my $l (split(/\n/,$response->as_string())) {
258 # Find a href ref
259 if ($l =~ /<a href="(.*)">(.*)<\/a>/i) {
260 my $url = $1;
261 my $pkg = $1;
262 my $desc = $2;
263 pb_log(3,"Found desc URL $desc: ");
264 # find an rpm package ref name-ver-tag.arch.rpm
265 if ($pkg =~ /(.+)-([^-]+)-([^-]+)\.(noarch|$parch)\.rpm$/) {
266 pb_log(3,"package ($1 + $2 + $3 + $4)\n");
267 $url{$1} = "$mirror/$url";
268 } else {
269 pb_log(3,"not a package\n");
270 }
271 }
272}
273
274#
275# Prepare early the yum cache env for the VE in order to copy in it packages on the fly
276#
277my $oscachedir = "/tmp";
278my $osupdcachedir;
279my $osupdname = "";
280
281pb_log(1,"Setting up $osupdname cache in VE\n");
282if ($pbupd =~ /yum/) {
283 $oscachedir = "$vepath/var/cache/yum/core/packages/";
284 $osupdcachedir = "$vepath/var/cache/yum/updates-released/packages/";
285 $osupdname = "YUM";
286 # Recent Fedora release use a new yum cache dir
287 if (($ddir eq "fedora") && ($dver > 8)) {
288 $oscachedir = "$vepath/var/cache/yum/$darch/$dver/fedora/packages";
289 $osupdcachedir = "$vepath/var/cache/yum/$darch/$dver/updates/packages";
290 $osupdcachedir = "$vepath/var/cache/yum/updates-released/packages/";
291 }
292} elsif ($pbupd =~ /zypper/) {
293 pb_log(1,"Setting up Zypper cache in VE\n");
294 $oscachedir = "$vepath/var/cache/zypp/packages/opensuse/suse/$darch";
295 $osupdname = "Zypper";
296}
297pb_mkdir_p($oscachedir);
298pb_mkdir_p($osupdcachedir) if (defined $osupdcachedir);
299
300# For each package to process, get it, put it in the cache dir
301# and extract it in the target dir. If not asked to keep, remove it
302# Just download if asked so.
303
304my $warning = 0;
305my $lwpkg ="";
306foreach my $p (split(/,/,$pkgs)) {
307 pb_log(1,"Processing package $p ...\n");
308 # Just print packages names if asked so.
309 if (defined $url{$p}) {
310 if ($opts{'p'}) {
311 pb_log(0,"$url{$p}\n");
312 next;
313 } else {
314 # Now download if not already in cache
315 my $p1 = basename($url{$p});
316 if (! -f "$cachedir/$p1") {
317 pb_system("wget --quiet -O $cachedir/$p1 $url{$p}","Downloading package $p1 ...");
318 } else {
319 pb_log(1,"Package $p1 already in cache\n");
320 }
321
322 # End if download only
323 if ($opts{'d'}) {
324 next;
325 }
326
327 #
328 # Copy the cached .RPM files into the oscachedir directory, so that os doesn't need to download them again.
329 #
330 pb_log(1,"Link package into $oscachedir\n");
331 copy("$cachedir/$p1",$oscachedir) if (defined $oscachedir);
332 symlink("$oscachedir/$p1","$osupdcachedir/p1") if (defined $osupdcachedir);
333
334 # And extract it to the finale dir
335 pb_system("cd $vepath ; rpm2cpio $cachedir/$p1 | cpio -ivdum","Extracting package $p1 into $vepath");
336
337 # Remove cached package if not asked to keep
338 if (! $opts{'k'}) {
339 unlink("$cachedir/$p1");
340 }
341
342 }
343 } else {
344 pb_log(0,"WARNING: unable to find URL for $p\n");
345 $warning++;
346 $lwpkg .= " $p";
347 }
348}
349
350if ($warning ge 1) {
351 pb_log(0,"$warning WARNINGS found.\nMaybe you should review your package list for $ddir-$dver-$darch\nand remove$lwpkg\n");
352}
353
354# Stop here if we just print
355if ($opts{'p'}) {
356 exit(0);
357}
358
359# Now executes the VE finalization steps required for it to work correctly
360pb_log(0,"VE post configuration\n");
361
362# yum needs that distro-release package be installed, so force it
363if ($pbupd =~ /yum/) {
364 foreach my $p1 (<$cachedir/($ddir|redhat)-release-*.rpm>) {
365 copy("$cachedir/$p1","$vepath/tmp");
366 pb_system("chroot $vepath rpm -ivh --force --nodeps /tmp/$p1","Forcing RPM installation of $p1");
367 unlink("$vepath/tmp/$p1");
368 }
369}
370#
371# Make sure there is a resolv.conf file present, such that DNS lookups succeed.
372#
373pb_log(1,"Creating resolv.conf\n");
374pb_mkdir_p("$vepath/etc");
375copy("/etc/resolv.conf","$vepath/etc/");
376
377#
378# BUGFIX:
379#
380if ((($ddir eq "centos") || ($ddir eq "rhel")) && ($dver eq "5")) {
381 pb_log(1,"BUGFIX for centos-5\n");
382 pb_mkdir_p("$vepath/usr/lib/python2.4/site-packages/urlgrabber.skx");
383 foreach my $i (<$vepath/usr/lib/python2.4/site-packages/urlgrabber/keepalive.*>) {
384 move($i,"$vepath/usr/lib/python2.4/site-packages/urlgrabber.skx/");
385 }
386}
387
388#
389# /proc needed
390#
391pb_mkdir_p("$vepath/proc");
392pb_system("mount -o bind /proc $vepath/proc","Mounting /proc");
393
394#
395# Some devices may be needed
396#
397pb_system("mknod -m 644 $vepath/dev/random c 1 8","Creating $vepath/dev/random") if (! -c "$vepath/dev/random");
398pb_system("mknod -m 644 $vepath/dev/urandom c 1 9","Creating $vepath/dev/urandom") if (! -c "$vepath/dev/urandom");
399pb_system("mknod -m 666 $vepath/dev/zero c 1 5","Creating $vepath/dev/zero") if (! -c "$vepath/dev/zero");
400
401my $minipkglist;
402
403pb_log(1,"Adapting $osupdname repository entries");
404if ($pbupd =~ /yum/) {
405 #
406 # Force the architecture for yum
407 # The goal is to allow i386 chroot on x86_64
408 #
409 # FIX: Not sufficient to have yum working
410 # mirrorlist is not usable
411 # $releasever also needs to be filtered
412 # yum.conf as well
413 foreach my $i (<$vepath/etc/yum.repos.d/*>,"$vepath/etc/yum.conf") {
414 pb_system("sed -i -e 's/\$basearch/$darch/g' $i","","quiet");
415 pb_system("sed -i -e 's/\$releasever/$dver/g' $i","","quiet");
416 pb_system("sed -i -e 's/^mirrorlist/#mirrorlist/' $i","","quiet");
417 pb_system("sed -i -e 's/^#baseurl/baseurl/' $i","","quiet");
418 }
419 $minipkglist = "ldconfig yum passwd vim-minimal dhclient authconfig";
420} elsif ($pbupd =~ /zypper/) {
421 pb_mkdir_p("$vepath/etc/zypp/repos.d");
422 open(REPO,"> $vepath/etc/zypp/repos.d/$ddir-$dver") || die "Unable to create repo file";
423 my $baseurl = dirname(dirname($mirror));
424 print REPO << 'EOF';
425[opensuse]
426name=$ddir-$dver
427baseurl=$baseurl
428enabled=1
429gpgcheck=1
430
431EOF
432 $minipkglist = "zypper vim-minimal dhclient";
433 # Bootstraping zypper
434 if ($dver eq "10.2") {
435 pb_system("chroot $vepath /bin/bash -c \"yes | /usr/bin/zypper sa $baseurl $ddir-$dver\"","Bootstrapping Zypper");
436 }
437}
438
439#
440# Run "install the necessary modules".
441# No need for sudo here
442#
443$pbupd =~ s/sudo//g;
444pb_system("chroot $vepath /bin/bash -c \"$pbupd $minipkglist \"","Bootstrapping OS by running $pbupd $minipkglist");
445
446#
447# make 'passwd' work.
448#
449pb_log(1,"Authfix\n");
450pb_system("chroot $vepath /bin/bash -c \"if [ -x /usr/bin/authconfig ]; then /usr/bin/authconfig --enableshadow --update; fi\"","Calling authconfig");
451
452#
453# Clean up
454#
455pb_log(1,"Cleaning up\n");
456if ($pbupd =~ /yum/) {
457 pb_system("chroot $vepath /usr/bin/yum clean all","Cleaning yum");
458}
459pb_system("umount $vepath/proc","Unmounting /proc");
460find(\&unlink_old_conf, $vepath);
461
462# Add additional packages if asked for
463
464# Executes post-install step if asked for
465if ($opts{'s'}) {
466 pb_system("$opts{'s'} $vepath","Executing the post-install script: $opts{'s'} $vepath");
467}
468
469# Function for File::Find
470sub unlink_old_conf {
471
472 unlink($_) if ($_ =~ /\.rpmorig$/);
473 unlink($_) if ($_ =~ /\.rpmnew$/);
474}
475
476
Note: See TracBrowser for help on using the repository browser.