summaryrefslogtreecommitdiffstats
path: root/perl-install/lvm.pm
blob: a8837c91a3442affbdb42dbd3c87bd2e800cd67d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
Diffstat (limited to 'perl-install/Xconfig/xfree.pm')
0 files changed, 0 insertions, 0 deletions
5'>135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
package lvm; # $Id: lvm.pm 268652 2010-05-13 17:09:00Z pterjan $

use diagnostics;
use strict;

#-######################################################################################
#- misc imports
#-######################################################################################
use common;
use modules;
use devices;
use fs::type;
use run_program;

#- for partition_table_xxx emulation
sub new {
    my ($class, $name) = @_;
    $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, device => $name }, $class;
}
sub use_pt_type { 0 }
sub hasExtended { 0 }
sub adjustStart {}
sub adjustEnd {}
sub write {}
sub cylinder_size { 
    my ($hd) = @_;
    $hd->{extent_size};
}

init() or log::l("lvm::init failed");

sub detect_during_install() {
    run_program::run('lvm2', 'vgscan');
    run_program::run('lvm2', 'vgchange', '-a', 'y');
}

sub init() {
    devices::init_device_mapper();
    detect_during_install() if $::isInstall;
    1;
}

sub lvm_cmd {
    if (my $r = run_program::run('lvm2', @_)) {
	$r;
    } else {
	$? >> 8 == 98 or return;

	#- sometimes, it needs running vgscan again, doing so:
	run_program::run('lvm2', 'vgscan');
	run_program::run('lvm2', @_);
    }
}
sub lvm_cmd_or_die {
    my ($prog, @para) = @_;
    lvm_cmd($prog, @para) or die "$prog failed\n";
}

sub check {
    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_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 {
    my ($lvm) = @_;
    $lvm->{extent_size} = to_int(run_program::get_stdout('lvm2', 'vgs', '--noheadings', '--nosuffix', '--units', 's', '-o', 'vg_extent_size', $lvm->{VG_name}));
    $lvm->{totalsectors} = to_int(run_program::get_stdout('lvm2', 'vgs', '--noheadings', '--nosuffix', '--units', 's', '-o', 'vg_size', $lvm->{VG_name}));
}

sub get_lv_size {
    my ($lvm_device) = @_;
    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));      
}

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;
    $lvm->{primary}{normal} = 
      [
       map {
	   my $device = "$lvm->{VG_name}/$_";
	   my $part = {
	     device => $device, 
	     lv_name => $_,
	     rootDevice => $lvm->{VG_name},
	     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
      ];
}

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) = @_;

    is_empty_array_ref($lvm->{primary}{normal}) or die N("Remove the logical volumes first\n");
    lvm_cmd('vgchange', '-a', 'n', $lvm->{VG_name});
    lvm_cmd_or_die('vgremove', $lvm->{VG_name});
    foreach (@{$lvm->{disks}}) {
	lvm_cmd_or_die('pvremove', devices::make($_->{device}));
	delete $_->{lvm};
	set_isFormatted($_, 0);
    }
}

sub lv_delete {
    my ($lvm, $lv) = @_;

    lvm_cmd_or_die('lvremove', '-f', "/dev/$lv->{device}");

    my $list = $lvm->{primary}{normal};
    @$list = grep { $_ != $lv } @$list;
}

sub suggest_lv_name {
    my ($lvm, $lv) = @_;
    my $list = $lvm->{primary}{normal} ||= [];
    $lv->{lv_name} ||= 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;
}

sub lv_resize {
    my ($lv, $oldsize) = @_;
    lvm_cmd_or_die($oldsize > $lv->{size} ? ('lvreduce', '-f') : 'lvextend', 
		   '--size', int($lv->{size} / 2) . 'k', "/dev/$lv->{device}");
    $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);
}

1;