File Coverage

blib/lib/Tapper/Installer/Precondition/Image.pm
Criterion Covered Total %
statement 42 200 21.0
branch 10 98 10.2
condition 0 19 0.0
subroutine 6 22 27.2
pod 19 19 100.0
total 77 358 21.5


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