diff options
Diffstat (limited to 'perl-install/lvm.pm')
| -rw-r--r-- | perl-install/lvm.pm | 187 | 
1 files changed, 165 insertions, 22 deletions
| diff --git a/perl-install/lvm.pm b/perl-install/lvm.pm index 48abf5c84..bf212bd6f 100644 --- a/perl-install/lvm.pm +++ b/perl-install/lvm.pm @@ -1,4 +1,4 @@ -package lvm; # $Id$ +package lvm;  use diagnostics;  use strict; @@ -12,13 +12,24 @@ use devices;  use fs::type;  use run_program; +=head1 SYNOPSYS + +Manage LVM (PV, VG, LV) + +=head1 Functions + +=over 4 + +=cut +  #- for partition_table_xxx emulation  sub new {      my ($class, $name) = @_; -    $name =~ s/\W/_/g; +    $name =~ s/[^\w-]/_/g;      $name = substr($name, 0, 63); # max length must be < NAME_LEN / 2  where NAME_LEN is 128 -    bless { disks => [], VG_name => $name }, $class; +    bless { disks => [], VG_name => $name, device => $name }, $class;  } +sub use_pt_type { 0 }  sub hasExtended { 0 }  sub adjustStart {}  sub adjustEnd {} @@ -28,17 +39,38 @@ sub cylinder_size {      $hd->{extent_size};  } -init() or log::l("lvm::init failed"); +=item detect_durting_install() + +Explicitly scan VGs. + +=cut + +sub detect_during_install() { +    run_program::run('lvm2', 'vgscan'); +    run_program::run('lvm2', 'vgchange', '-a', 'y'); +} + +=item init() + +Loads LVM modules and scan VGs (if in installer, not in standalone tool). + +=cut  sub init() {      devices::init_device_mapper(); -    if ($::isInstall) { -	run_program::run('lvm2', 'vgscan'); -	run_program::run('lvm2', 'vgchange', '-a', 'y'); -    } +    detect_during_install() if $::isInstall || $::isLiveInstall;      1;  } +init() or log::l("lvm::init failed"); + +=item lvm_cmd(...) + +Run a LVM command, then rescan VG. +See run_program::run() for arguments. + +=cut +  sub lvm_cmd {      if (my $r = run_program::run('lvm2', @_)) {  	$r; @@ -46,27 +78,54 @@ sub lvm_cmd {  	$? >> 8 == 98 or return;  	#- sometimes, it needs running vgscan again, doing so: +	log::l("forcing rescan because of prior failure");  	run_program::run('lvm2', 'vgscan');  	run_program::run('lvm2', @_);      }  } + +=item lvm_cmd_or_die($prog, @para) + +Like lvm_cmd() but die if there's an error. + +=cut +  sub lvm_cmd_or_die {      my ($prog, @para) = @_; -    lvm_cmd($prog, @para) or die "$prog failed\n"; +    my @err; +    lvm_cmd("2>", \@err, $prog, @para) or do { +        my $err = $err[-1]; # prevent "Modification of non-creatable array value attempted" +        chomp($err); +        die "$prog failed: $err\n"; +    };  }  sub check { -    my ($in) = @_; - -    $in->do_pkgs->ensure_binary_is_installed('lvm2', 'lvm2') or return; +    my ($do_pkgs) = @_; +    local $::prefix = ''; # We want lvm2 on current system +    $do_pkgs->ensure_binary_is_installed('lvm2', 'lvm2') or return;      init();      1;  } -sub get_vg { -    my ($part) = @_; -    my $dev = expand_symlinks(devices::make($part->{device})); -    run_program::get_stdout('lvm2', 'pvs', '--noheadings', '-o', 'vg_name', $dev) =~ /(\S+)/ && $1; +sub get_pv_field { +    my ($pv, $field) = @_; +    my $dev = expand_symlinks(devices::make($pv->{device})); +    run_program::get_stdout('lvm2', 'pvs', '--noheadings', '--nosuffix', '-o', $field, $dev); +} +sub pv_physical_extents { +    my ($pv) = @_; +    split(' ', lvm::get_pv_field($pv, 'pv_pe_alloc_count,pv_pe_count')); +} +sub pv_to_vg { +    my ($pv) = @_; +    get_pv_field($pv, 'vg_name') =~ /(\S+)/ && $1; +} + +sub pv_move { +    my ($pv) = @_; +    my $dev = expand_symlinks(devices::make($pv->{device})); +    lvm_cmd('pvmove', '-v', $dev) or die N("Moving used physical extents to other physical volumes failed");  }  sub update_size { @@ -80,6 +139,21 @@ sub get_lv_size {      to_int(run_program::get_stdout('lvm2', 'lvs', '--noheadings', '--nosuffix', '--units', 's', '-o', 'lv_size', "/dev/$lvm_device"));  } +sub lv_to_pvs { +    my ($lv) = @_; +    map { m!(\S+)\(! } run_program::get_stdout('lvm2', 'lvs', '--noheadings', '-o', 'devices', "/dev/$lv->{device}"); +} +sub lv_nb_pvs { +    my ($lv) = @_; +    listlength(lv_to_pvs($lv));       +} + +=item get_lvs($lvm) + +Return list of LVs. + +=cut +  sub get_lvs {      my ($lvm) = @_;      my @l = run_program::get_stdout('lvm2', 'lvs', '--noheadings', '--nosuffix', '--units', 's', '-o', 'lv_name', $lvm->{VG_name}) =~ /(\S+)/g; @@ -87,12 +161,20 @@ sub get_lvs {        [         map {  	   my $device = "$lvm->{VG_name}/$_"; -	   my $fs_type = -e "/dev/$device" && fs::type::fs_type_from_magic({ device => $device }); - -	   { device => $device,  +	   my $p = fs::wild_device::to_subpart("/dev/$device"); +	   my $part = { +	     device => $device,   	     lv_name => $_, -	     fs_type => $fs_type || 'ext2', +	     rootDevice => $lvm->{VG_name}, +	     minor => $p->{minor}, +	     major => $p->{major},  	     size => get_lv_size($device) }; +	   if (my $type = -e "/dev/$device" && fs::type::type_subpart_from_magic($part)) { +                put_in_hash($part, $type); 	        +	   } else { +	       $part->{fs_type} = defaultFS(); +	   } +	   $part;         } @l        ];  } @@ -100,11 +182,22 @@ sub get_lvs {  sub vg_add {      my ($part) = @_;      my $dev = expand_symlinks(devices::make($part->{device})); +    output($dev, '\0' x 512); #- help pvcreate      lvm_cmd_or_die('pvcreate', '-y', '-ff', $dev);      my $prog = lvm_cmd('vgs', $part->{lvm}) ? 'vgextend' : 'vgcreate';      lvm_cmd_or_die($prog, $part->{lvm}, $dev);  } +sub vg_reduce { +    my ($lvm_vg, $part_pv) = @_; + +    lvm_cmd('vgreduce', $lvm_vg->{VG_name}, devices::make($part_pv->{device})) or die N("Physical volume %s is still in use", $part_pv->{device}); +    @{$lvm_vg->{disks}} = difference2($lvm_vg->{disks}, [ $part_pv ]); +    update_size($lvm_vg); +    delete $part_pv->{lvm}; +    set_isFormatted($part_pv, 0); +} +  sub vg_destroy {      my ($lvm) = @_; @@ -127,14 +220,37 @@ sub lv_delete {      @$list = grep { $_ != $lv } @$list;  } -sub lv_create { +sub suggest_lv_name_from_mnt_point { +    my ($lv) = @_; +    my $str = $lv->{mntpoint}; +    $str = "root" if $str eq '/'; +    $str =~ s!^/!!; +    $str =~ s!/!_!g; +    $str =~ s! !_!g; +    'lv_' . $str; +} + +sub suggest_lv_name {      my ($lvm, $lv) = @_;      my $list = $lvm->{primary}{normal} ||= []; -    $lv->{lv_name} ||= 1 + max(map { if_($_->{device} =~ /(\d+)$/, $1) } @$list); +    $lv->{lv_name} ||= suggest_lv_name_from_mnt_point($lv); +    $lv->{lv_name} ||= "lv_"  . (1 + max(map { if_($_->{device} =~ /(\d+)$/, $1) } @$list)); +} + +sub lv_create { +    my ($lvm, $lv) = @_; +    suggest_lv_name($lvm, $lv);      $lv->{device} = "$lvm->{VG_name}/$lv->{lv_name}";      lvm_cmd_or_die('lvcreate', '--size', int($lv->{size} / 2) . 'k', '-n', $lv->{lv_name}, $lvm->{VG_name}); + +    if ($lv->{mntpoint} eq '/boot' && lv_nb_pvs($lv) > 1) { +	lvm_cmd_or_die('lvremove', '-f', "/dev/$lv->{device}"); +	die N("The bootloader can't handle /boot on multiple physical volumes"); +    } +      $lv->{size} = get_lv_size($lv->{device}); #- the created size is smaller than asked size      set_isFormatted($lv, 0); +    my $list = $lvm->{primary}{normal} ||= [];      push @$list, $lv;  } @@ -145,4 +261,31 @@ sub lv_resize {      $lv->{size} = get_lv_size($lv->{device}); #- the resized partition may not be the exact asked size  } +sub add_to_VG { +    my ($part, $lvm) = @_; + +    $part->{lvm} = $lvm->{VG_name}; +    push @{$lvm->{disks}}, $part; +    delete $part->{mntpoint}; + +    vg_add($part); +    update_size($lvm); +} + +sub create_singleton_vg { +    my ($lvms, $part) = @_; + +    my %existing = map { $_->{VG_name} => 1 } @$lvms; +    my $VG_name = find { !$existing{$_} } map { "VG$_" } 1 .. 100 or internal_error();     + +    my $lvm = new lvm($VG_name); +    push @$lvms, $lvm; + +    add_to_VG($part, $lvm); +} + +=back + +=cut +  1; | 
