File Coverage

blib/lib/Tapper/Installer/Precondition/Image.pm
Criterion Covered Total %
statement 18 201 8.9
branch 1 98 1.0
condition 0 19 0.0
subroutine 5 23 21.7
pod n/a
total 24 341 7.0


line stmt bran cond sub pod time code
1             package Tapper::Installer::Precondition::Image;
2             BEGIN {
3 2     2   135762 $Tapper::Installer::Precondition::Image::AUTHORITY = 'cpan:TAPPER';
4             }
5             {
6             $Tapper::Installer::Precondition::Image::VERSION = '4.1.1';
7             }
8              
9 2     2   20 use strict;
  2         5  
  2         102  
10 2     2   12 use warnings;
  2         3  
  2         66  
11              
12 2     2   893 use Moose;
  2         552137  
  2         19  
13             extends 'Tapper::Installer::Precondition';
14              
15             has images => ( isa => 'ArrayRef',
16             is => 'rw',
17             default => sub {[]},
18             );
19              
20              
21              
22              
23             sub get_partition_number
24             {
25 0     0   0 my ($self, $device_file) = @_;
26 0         0 my ($partition_number) = $device_file =~ m/(\d+)/;
27 0         0 $partition_number--;
28 0         0 return $partition_number;
29             }
30              
31              
32             sub get_grub_device
33             {
34 0     0   0 my ($self, $device_file) = @_;
35 0         0 my $basedir = $self->cfg->{paths}{base_dir};
36 0         0 my $error = $self->log_and_exec("/usr/sbin/grub-install","--recheck",
37             "--root-directory=$basedir","--no-floppy","$device_file");
38 0         0 my ($grub_device) = $device_file =~ m/[hs]d([a-z])/;
39 0         0 $grub_device =~ tr/[a-j]/[0-9]/;
40 0 0       0 if ($grub_device eq "") {
41 0         0 $self->log->warn( "Grub device not found, took '0'");
42 0         0 $grub_device = 0;
43             }
44 0         0 return (0, $grub_device);
45             }
46              
47              
48              
49              
50             sub configure_fstab
51             {
52 0     0   0 my ($self) = @_;
53             # Creates fstab-entry for the final partition
54              
55 0         0 $self->log->debug("Configuring fstab to contain installed images");
56 0 0       0 open (my $FSTAB, ">", $self->cfg->{paths}{base_dir}."/etc/fstab") or return "Can't open fstab for appending: $!";
57              
58             # write defaults for fstab
59 0         0 print $FSTAB "proc\t/proc\tproc\tdefaults\t0 0\n","sysfs\t/sys\tsysfs\tauto\t0 0\n";
60 0         0 print $FSTAB "tapper:/data/tapper /data/tapper nfs vers=3 0 0\n";
61              
62             # put swap in fstab
63 0         0 foreach my $line (grep {$_ =~ m/swap/i} qx(fdisk -l)) {
  0         0  
64 0         0 my ($file_name) = split / +/, $line;
65 0         0 print $FSTAB "$file_name\tnone\tswap\tsw\t0\t0\n";
66             }
67              
68              
69 0         0 foreach my $image (@{$self->images}) {
  0         0  
70 0         0 print $FSTAB $image->{partition},"\t",$image->{mount},"\text3\tdefaults\t1 1\n";
71             }
72              
73 0 0       0 close $FSTAB or return "Can't write fstab: $!"; # well, cases when close fails are rare but still exist
74 0         0 return 0;
75             }
76              
77              
78              
79             sub generate_user_grub_conf
80             {
81 0     0   0 my ($self, $device_file) = @_;
82 0         0 my $mount_point=$self->cfg->{paths}{base_dir};
83 0         0 my $conf_string=$self->cfg->{grub};
84              
85 0         0 my $partition_number = $self->get_partition_number($device_file);
86 0         0 my ($error, $grub_device) = $self->get_grub_device( $device_file);
87 0 0       0 return $grub_device if $error;
88              
89 0         0 my $initrd_options = '';
90 0 0       0 $initrd_options = "initrd /boot/initrd" if -e $self->cfg->{paths}{base_dir}."/boot/initrd";
91              
92 0         0 $conf_string =~ s/\$root/$device_file/g;
93 0         0 $conf_string =~ s/\$grubroot/(hd$grub_device,$partition_number)/g;
94 0         0 $conf_string =~ s/\$initrd_options/$initrd_options/g;
95              
96 0         0 return $self->write_menu_lst($conf_string, "truncate");
97             }
98              
99              
100              
101             sub generate_grub_menu_lst
102             {
103 0     0   0 my ($self) = @_;
104 0         0 my $retval;
105              
106 0         0 my $partition = $self->images->[0]->{partition};
107              
108 0 0       0 return $retval if $retval = $self->generate_user_grub_conf($partition);
109 0         0 return 0;
110             }
111              
112              
113              
114             sub prepare_boot
115             {
116 0     0   0 my ($self) = @_;
117 0         0 my $retval = 0;
118 0 0       0 return $retval if $retval = $self->configure_fstab();
119 0 0       0 return $retval if $retval = $self->generate_grub_menu_lst( );
120 0 0       0 return $retval if $retval = $self->generate_pxe_grub();
121             # return $retval if $retval = $self->copy_menu_lst();
122 0         0 return 0;
123             }
124              
125              
126              
127             sub get_device
128             {
129 0     0   0 my ($self, $devices, $basedir) = @_;
130 0   0     0 $basedir ||= '/'; # basedir is only supposed for unit testing
131 0         0 my @device_alternatives;
132 0 0       0 if (ref($devices) eq 'ARRAY') {
133 0         0 @device_alternatives = @$devices;
134             } else {
135 0         0 @device_alternatives = ($devices);
136             }
137              
138 0         0 my $dev_symlink;
139             ALTERNATIVE:
140 0         0 foreach my $device_id (@device_alternatives) {
141 0 0 0     0 if (-e "$basedir/dev/disk/by-label/".$device_id) {
    0          
    0          
142 0         0 $dev_symlink=readlink("$basedir/dev/disk/by-label/$device_id");
143 0         0 last ALTERNATIVE;
144             } elsif (-e "$basedir/dev/disk/by-uuid/".$device_id) {
145 0         0 $dev_symlink = readlink("$basedir/dev/disk/by-uuid/$device_id");
146 0         0 last ALTERNATIVE;
147             } elsif (-e "$basedir/dev/".$device_id or -e "$basedir/$device_id") {
148 0         0 $dev_symlink = $device_id;
149 0         0 last ALTERNATIVE;
150             }
151              
152             }
153 0         0 my $error_string = "No device named ";
154 0         0 $error_string .= join ", ", @device_alternatives;
155 0         0 $error_string .= " could be found";
156 0 0       0 return(1, $error_string) if not $dev_symlink;
157              
158 0         0 my @linkpath=split("/", $dev_symlink); # split link to avoid /dev/disk/by-xyz/../../hda1, is way faster than regexp
159 0         0 my $partition = $linkpath[-1];
160 0         0 return (0,"/dev/$partition");
161             }
162              
163              
164              
165              
166             sub get_partition_label
167             {
168 0     0   0 my ($self, $device_file) = @_;
169 0         0 return $self->log_and_exec("e2label $device_file");
170             }
171              
172              
173              
174              
175             sub generate_pxe_grub
176             {
177 0     0   0 my ($self) = @_;
178              
179 0         0 my $partition = $self->cfg->{preconditions}->[0]->{partition};
180 0         0 my $hostname = $self->gethostname();
181 0         0 my $partition_number = $self->get_partition_number( $partition );
182 0         0 my ($error, $grub_device) = $self->get_grub_device( $partition );
183 0 0       0 return $grub_device if $error;
184              
185              
186              
187 0         0 my $filename = $self->cfg->{paths}{grubpath}."/$hostname.lst";
188 0 0       0 open my $fh, ">", $filename or return "Can not open PXE grub file $filename: $!";
189 0         0 print $fh
190             "serial --unit=0 --speed=115200\n",
191             "terminal serial\n",
192             "timeout 2\n\n",
193             "title Boot from first hard disc\n",
194             "chainloader (hd$grub_device,$partition_number)+1";
195 0 0       0 close $fh or return "Closing PXE grub file $filename of NFS failed: $!";
196 0         0 return 0;
197             }
198              
199              
200              
201              
202             sub copy_menu_lst
203             {
204 0     0   0 my ($self) = @_;
205              
206 0         0 my $hostname = $self->gethostname();
207 0         0 my $menu_lst_file = $self->cfg->{paths}{base_dir}."/boot/grub/menu.lst";
208 0         0 my $tapper_conf = $self->cfg->{paths}{grubpath};
209              
210 0         0 return $self->log_and_exec("cp $menu_lst_file $tapper_conf/$hostname.lst");
211             }
212              
213              
214              
215             sub write_menu_lst
216             {
217 0     0   0 my ($self, $content, $truncate) = @_;
218 0         0 $self->makedir($self->cfg->{paths}{base_dir}."/boot/grub/");
219 0         0 my $menu_lst_file = $self->cfg->{paths}{base_dir}."/boot/grub/menu.lst";
220 0         0 my $mode = '>>';
221 0 0       0 if ($truncate) {
222 0         0 $mode = '>';
223             }
224              
225 0 0       0 open (my $FILE, $mode,$menu_lst_file) or return "Can't open $menu_lst_file for writing: $!";
226 0         0 print $FILE $content;
227 0 0       0 close $FILE or return "Can't write $menu_lst_file: $!"; # well, cases when close fails are rare but still exist;
228 0         0 return 0;
229             }
230             ;
231              
232              
233             sub install
234             {
235 1     1   8574 my ($self, $image) = @_;
236 1         3 my $retval;
237              
238             # set partition name to the normalized value /dev/*
239 1         4 my $partition=$image->{partition};
240 1         3 my $error;
241 1         26 ($error, $partition) = $self->get_device($partition);
242 1 50       11 if ($error) {
243 0         0 return $partition;
244             }
245              
246 1         3 $image->{partition}=$partition;
247 1         127 $self->log->debug("partition = $partition");
248              
249             # mount points in image precondition are relative to test system root
250             # installation needs it relative to current root
251 0           my $mount_point = $self->cfg->{paths}{base_dir}.$image->{mount};
252 0           $error = $self->makedir($mount_point);
253 0 0         return $error if $error;
254              
255 0 0         if ($image->{image}) {
256 0           $retval = $self->copy_image( $partition, $image->{image}, $mount_point);
257 0 0         return $retval if $retval;
258             } else {
259 0           $self->log->debug("No image to install on $partition, mounting old image to $mount_point");
260 0           $retval = $self->log_and_exec("mount","$partition","$mount_point");
261 0 0         return $retval if $retval;
262             }
263              
264 0           $self->images([ @{$self->images}, $image ]);
  0            
265              
266 0           $self->log->debug("Image copied successfully");
267              
268 0           return 0;
269             }
270              
271              
272              
273             sub install_image
274             {
275 0     0     my ($self, $image_file, $device_file, $mount_point) = @_;
276 0           my ($error, $partition_size)=$self->log_and_exec("/sbin/blockdev --getsize64 $device_file");
277 0 0         if ($error) {
278 0           $self->log->warn("Can't get size of partition $device_file: $partition_size. Won't check if images fits.");
279 0           $partition_size = 0;
280             }
281              
282 0           my ($partition_label, $image_type);
283 0           ($error, $image_type)=$self->get_file_type($image_file);
284              
285 0           ($error, $partition_label) = $self->get_partition_label($device_file);
286 0 0         if ($error) {
287 0           $self->log->info("Can't get partition label of $device_file: $partition_label");
288 0           $partition_label='';
289             }
290 0   0       $partition_label ||= "testing";
291              
292 0 0         if ($image_type eq "iso") {
    0          
    0          
    0          
293             # ISO images are preformatted and thus bring their own
294             # partition label
295 0           return $self->install_image_iso($image_file, $partition_size, $device_file, $mount_point);
296             } elsif ($image_type eq "tar") {
297 0           return $self->install_image_tar($image_file, $partition_size, $device_file, $mount_point, $partition_label);
298             } elsif ($image_type eq "gzip"){
299 0           return $self->install_image_gz($image_file, $partition_size, $device_file, $mount_point, $partition_label);
300             } elsif ($image_type eq "bz2") {
301 0           return $self->install_image_bz2($image_file, $partition_size, $device_file, $mount_point, $partition_label);
302             } else {
303 0           return("Imagetype could not be detected");
304             }
305 0           return 0;
306             }
307              
308              
309              
310             sub install_image_iso
311             {
312 0     0     my ($self, $image_file, $partition_size, $device_file, $mount_point) = @_;
313             # -s return the size in byte
314 0 0 0       if ( $partition_size and (-s $image_file) > $partition_size) {
315 0           return("Image $image_file is to big for device $device_file");
316             }
317 0           $self->log->info( "Using image type iso" );
318 0           my $retval;
319 0 0         return $retval if $retval=$self->log_and_exec("dd if=$image_file of=$device_file");
320 0 0         return $retval if $retval=$self->log_and_exec("mount $device_file $mount_point");
321              
322 0           return(0);
323             }
324              
325              
326              
327             sub install_image_tar
328             {
329 0     0     my ($self, $image_file, $partition_size, $device_file, $mount_point, $partition_label) = @_;
330             # -s return the size in byte
331 0 0 0       if ( $partition_size and (-s $image_file) > $partition_size) {
332 0           return("Image $image_file is to big for device $device_file");
333             }
334 0           $self->log->info( "Using image type tar" );
335 0           my $retval;
336 0 0         return $retval if $retval=$self->log_and_exec("mkfs.ext3 -q -L $partition_label $device_file");
337 0 0         return $retval if $retval=$self->log_and_exec("mount $device_file $mount_point");
338              
339 0 0         return $retval if $retval=$self->log_and_exec("tar xf $image_file -C $mount_point");
340 0           return 0;
341             }
342              
343              
344             sub install_image_gz
345             {
346 0     0     my ($self, $image_file, $partition_size, $device_file, $mount_point, $partition_label) = @_;
347 0           my $gz_factor=3.82;
348 0 0 0       if ( $partition_size and (-s $image_file)*$gz_factor > $partition_size) {
349 0           return("Image $image_file is to big for device $device_file");
350             }
351 0           $self->log->info( "Using image type gzip" );
352 0           my $retval;
353 0 0         return $retval if $retval=$self->log_and_exec("mkfs.ext3 -q -L $partition_label $device_file");
354 0 0         return $retval if $retval=$self->log_and_exec("mount $device_file $mount_point");
355              
356 0 0         return $retval if $retval=$self->log_and_exec("tar xfz $image_file -C $mount_point");
357 0           return 0;
358             }
359              
360              
361             sub install_image_bz2
362             {
363 0     0     my ($self, $image_file, $partition_size, $device_file, $mount_point, $partition_label) = @_;
364 0           my $bz2_factor=4.30;
365 0 0 0       if ( $partition_size and (-s $image_file)*$bz2_factor > $partition_size) {
366 0           return("Image $image_file is to big for device $device_file");
367             }
368 0           $self->log->info( "Using image type bzip2" );
369 0           my $retval;
370 0 0         return $retval if $retval=$self->log_and_exec("mkfs.ext3 -q -L $partition_label $device_file");
371 0 0         return $retval if $retval=$self->log_and_exec("mount $device_file $mount_point");
372              
373 0 0         return $retval if $retval=$self->log_and_exec("tar xfj $image_file -C $mount_point");
374 0           return 0;
375             }
376              
377              
378              
379             sub copy_image
380             {
381 0     0     my ($self, $device_file, $image_file, $mount_point) = @_;
382 0 0         $image_file = $self->cfg->{paths}{image_dir}.$image_file unless $image_file =~m(^/);
383              
384             # Image exists?
385 0 0         if (not -e $image_file) {
386 0           return("Image $image_file could not be found");
387             }
388 0           return $self->install_image($image_file, $device_file, $mount_point);
389             }
390              
391              
392             sub unmount
393             {
394 0     0     my ($self) = @_;
395 0           my $basedir = $self->cfg->{paths}{base_dir};
396 0           foreach my $image (reverse @{$self->images}) {
  0            
397 0           my $mount = $basedir.$image->{mount};
398 0 0         next unless -d $mount;
399 0           $self->log_and_exec('umount', $mount);
400             }
401 0           return 0;
402             }
403              
404             1;
405              
406             __END__
407             =pod
408              
409             =encoding utf-8
410              
411             =head1 NAME
412              
413             Tapper::Installer::Precondition::Image
414              
415             =head1 SYNOPSIS
416              
417             use Tapper::Installer::Precondition::Image;
418              
419             =head1 NAME
420              
421             Tapper::Installer::Precondition::Package - Install a package to a given location
422              
423             =head1 FUNCTIONS
424              
425             =head2 get_partition_number
426              
427             Get the partition part of grub notation of a given device file eg. /dev/hda1.
428              
429             @param string - partition number
430              
431             @return int - grub device notation
432              
433             =head2 get_grub_device
434              
435             Get the disc part of grub notation of a given device file eg. /dev/hda1.
436              
437             @param string - device file name
438              
439             @return success - (0, grub device notation)
440             @return eror - (1, error string)
441              
442             =head2 configure_fstab
443              
444             Write fstab on installed system based upon the installed images and
445             partitions.
446              
447             @return success - 0
448             @return error - error string
449              
450             =head2 generate_user_grub_conf
451              
452             Generate grub config file menu.lst based upon user provided precondition.
453              
454             @param string - name of the root partition
455              
456             @return success - 0
457             @return error - error string
458              
459             =head2 generate_grub_menu_lst
460              
461             Create a grub config file (menu.lst) based on the options in the configuration
462             hash.
463              
464             @return success - 0
465             @return error - error string
466              
467             =head2 prepare_boot
468              
469             Make installed system ready for boot from hard disk.
470              
471             @return success - 0
472             @return error - error string
473              
474             =head2 get_device
475              
476             Return device name (i.e. /dev/$device) for a given device-id, partition label
477             or $device name (with or without preceding /dev/).
478             Doesn't work with dev-mapper.
479              
480             @param string - device or reference to array with device ids
481             @param string - base dir prepended to all paths (testing purpose)
482              
483             @returnlist success - ( 0, device name string)
484             @returnlist error - ( 1, error string)
485              
486             =head2 get_partition_label
487              
488             Get the label of a partition to be able to set it again at mkfs.
489              
490             @param string - device file
491              
492             @returnlist success - ( 0, partition label string)
493             @returnlist error - ( != 0, error string)
494              
495             =head2 generate_pxe_grub
496              
497             Generate a simple PXE grub config that forwards to local grub.
498              
499             @return success - 0
500             @return error - error string
501              
502             =head2 copy_menu_lst
503              
504             Copy menu.lst to NFS. We need the grub config file menu.lst on NFS because
505             thats where PXE grub expects it. Still we create the file on the local hard
506             drive because it's faster and allows users to boot with this config without
507             using PXE grub.
508              
509             @return success - 0
510             @return error - error string
511              
512             =head2 write_menu_lst
513              
514             Write content to grub file. This encapsulates writing to improve readability
515             and testability.
516              
517             @param string - what to write
518             @param bool - true = truncate ('>'), false = append ('>>') (append is default)
519              
520             @return success - 0
521             @return error - error string
522              
523             =head2 install
524              
525             Install a given image. This function is a wrapper for image
526             installer functions so the caller doesn't need to care for preparations.
527              
528             @param hash reference - containing image name (image), mount point (mount) and
529             partition name (partition)
530              
531             @return success - 0
532             @return error - error string
533              
534             =head2 install_image
535              
536             Install an image on a given device and mount it to a given mount point. Make
537             sure to set partition label reasonably.
538              
539             @param string - image file name
540             @param string - device file name
541             @param string - mount point relative to future test system
542              
543             @return success - 0
544             @return error - error string
545              
546             =head2 install_image_iso
547              
548             Install an image of type iso.
549              
550             @param string - image file name
551             @param int - size of the target partition
552             @param string - device name of the target partition
553             @param string - directory to mount the installed image to
554              
555             @return success - 0
556             @return error - error string
557              
558             =head2 install_image_tar
559              
560             Install an image of type tar.
561              
562             @param string - image file name
563             @param int - size of the target partition
564             @param string - device name of the target partition
565             @param string - directory to mount the installed image to
566             @param string - partition label
567              
568             @return success - 0
569             @return error - error string
570              
571             =head2 install_image_gz
572              
573             Install an image of type tar.gz.
574              
575             @param string - image file name
576             @param int - size of the target partition
577             @param string - device name of the target partition
578             @param string - directory to mount the installed image to
579             @param string - partition labeö
580              
581             @return success - 0
582             @return error - error string
583              
584             =head2 install_image_bz2
585              
586             Install an image of type tar.bz2.
587              
588             @param string - image file name
589             @param int - size of the target partition
590             @param string - device name of the target partition
591             @param string - directory to mount the installed image to
592             @param string - partition labeö
593              
594             @return success - 0
595             @return error - error string
596              
597             =head2 copy_image
598              
599             Install an image to a given partition and mount it to a given mount point.
600              
601             @param string - device name
602             @param string - image file
603             @param string - mount point
604              
605             @return success - 0
606             @return error - error string
607              
608             =head2 unmount
609              
610             Umounts all images that were mounted during installation in reverse
611             order.
612              
613             @return success - 0
614             @return error - error string
615              
616             =head1 AUTHOR
617              
618             AMD OSRC Tapper Team <tapper@amd64.org>
619              
620             =head1 COPYRIGHT AND LICENSE
621              
622             This software is Copyright (c) 2012 by Advanced Micro Devices, Inc..
623              
624             This is free software, licensed under:
625              
626             The (two-clause) FreeBSD License
627              
628             =cut
629