mirror of
https://github.com/webmin/webmin.git
synced 2026-03-01 16:52:57 +00:00
368 lines
9.0 KiB
Perl
368 lines
9.0 KiB
Perl
# lvm-lib.pl
|
|
# Common functions for managing VGs, PVs and LVs
|
|
|
|
do '../web-lib.pl';
|
|
&init_config();
|
|
&foreign_require("mount", "mount-lib.pl");
|
|
if (&foreign_check("raid")) {
|
|
&foreign_require("raid", "raid-lib.pl");
|
|
$has_raid++;
|
|
}
|
|
|
|
$lvm_proc = "/proc/lvm";
|
|
$lvm_tab = "/etc/lvmtab";
|
|
|
|
# list_physical_volumes(vg)
|
|
# Returns a list of all physical volumes for some volume group
|
|
sub list_physical_volumes
|
|
{
|
|
local @rv;
|
|
opendir(DIR, "$lvm_proc/VGs/$_[0]/PVs");
|
|
foreach $f (readdir(DIR)) {
|
|
next if ($f eq '.' || $f eq '..');
|
|
local $pv = { 'name' => $f,
|
|
'vg' => $_[0] };
|
|
local %p = &parse_colon_file("$lvm_proc/VGs/$_[0]/PVs/$f");
|
|
$pv->{'device'} = $p{'name'};
|
|
$pv->{'number'} = $p{'number'};
|
|
$pv->{'size'} = $p{'size'}/2;
|
|
$pv->{'status'} = $p{'status'};
|
|
$pv->{'number'} = $p{'number'};
|
|
$pv->{'pe_size'} = $p{'PE size'};
|
|
$pv->{'pe_total'} = $p{'PE total'};
|
|
$pv->{'pe_alloc'} = $p{'PE allocated'};
|
|
$pv->{'alloc'} = $p{'allocatable'} == 2 ? 'y' : 'n';
|
|
push(@rv, $pv);
|
|
}
|
|
closedir(DIR);
|
|
return @rv;
|
|
}
|
|
|
|
# create_physical_volume(&pv)
|
|
sub create_physical_volume
|
|
{
|
|
local $cmd = "pvcreate -f -y '$_[0]->{'device'}'";
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $out if ($?);
|
|
$cmd = "vgextend '$_[0]->{'vg'}' '$_[0]->{'device'}'";
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
# change_physical_volume(&pv)
|
|
sub change_physical_volume
|
|
{
|
|
local $cmd = "pvchange -x $_[0]->{'alloc'} '$_[0]->{'device'}'";
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
# delete_physical_volume(&pv)
|
|
sub delete_physical_volume
|
|
{
|
|
if ($_[0]->{'pe_alloc'}) {
|
|
local $cmd = "(yes | pvmove '$_[0]->{'device'}')";
|
|
local $out = &backquote_logged("$cmd 2>&1");
|
|
return $out if ($?);
|
|
}
|
|
local $cmd = "vgreduce '$_[0]->{'vg'}' '$_[0]->{'device'}'";
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
|
|
# list_volume_groups()
|
|
# Returns a list of all volume groups
|
|
sub list_volume_groups
|
|
{
|
|
local (@rv, $f);
|
|
opendir(DIR, "$lvm_proc/VGs");
|
|
foreach $f (readdir(DIR)) {
|
|
next if ($f eq '.' || $f eq '..');
|
|
local $vg = { 'name' => $f };
|
|
local %g = &parse_colon_file("$lvm_proc/VGs/$f/group");
|
|
$vg->{'number'} = $g{'number'};
|
|
$vg->{'size'} = $g{'size'};
|
|
$vg->{'pe_size'} = $g{'PE size'};
|
|
$vg->{'pe_total'} = $g{'PE total'};
|
|
$vg->{'pe_alloc'} = $g{'PE allocated'};
|
|
push(@rv, $vg);
|
|
}
|
|
closedir(DIR);
|
|
return @rv;
|
|
}
|
|
|
|
# delete_volume_group(&vg)
|
|
sub delete_volume_group
|
|
{
|
|
local $cmd = "vgchange -a n '$_[0]->{'name'}'";
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $out if ($?);
|
|
$cmd = "vgremove '$_[0]->{'name'}'";
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
# create_volume_group(&vg, device)
|
|
sub create_volume_group
|
|
{
|
|
&system_logged("vgscan >/dev/null 2>&1 </dev/null");
|
|
local $cmd = "pvcreate -f -y '$_[1]'";
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $out if ($?);
|
|
$cmd = "vgcreate";
|
|
$cmd .= " -s $_[0]->{'pe_size'}k" if ($_[0]->{'pe_size'});
|
|
$cmd .= " '$_[0]->{'name'}' '$_[1]'";
|
|
$out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
# rename_volume_group(&vg, name)
|
|
sub rename_volume_group
|
|
{
|
|
local $cmd = "vgrename '$_[0]->{'name'}' '$_[1]'";
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $out if ($?);
|
|
$cmd = "vgchange -a n '$_[1]'";
|
|
$out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $out if ($?);
|
|
$cmd = "vgchange -a y '$_[1]'";
|
|
$out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
|
|
# list_logical_volumes(vg)
|
|
sub list_logical_volumes
|
|
{
|
|
local @rv;
|
|
opendir(DIR, "$lvm_proc/VGs/$_[0]/LVs");
|
|
foreach $f (readdir(DIR)) {
|
|
next if ($f eq '.' || $f eq '..');
|
|
local $lv = { 'name' => $f,
|
|
'vg' => $_[0] };
|
|
local %p = &parse_colon_file("$lvm_proc/VGs/$_[0]/LVs/$f");
|
|
$lv->{'device'} = $p{'name'};
|
|
$lv->{'number'} = $p{'number'};
|
|
$lv->{'size'} = $p{'size'}/2;
|
|
$lv->{'perm'} = $p{'access'} == 3 ? 'rw' : 'r';
|
|
$lv->{'alloc'} = $p{'allocation'} == 2 ? 'y' : 'n';
|
|
$lv->{'has_snap'} = $p{'access'} == 11;
|
|
$lv->{'is_snap'} = $p{'access'} == 5;
|
|
push(@rv, $lv);
|
|
}
|
|
closedir(DIR);
|
|
return @rv;
|
|
}
|
|
|
|
# create_logical_volume(&lv)
|
|
sub create_logical_volume
|
|
{
|
|
local $cmd = "lvcreate -n$_[0]->{'name'} -L$_[0]->{'size'}k";
|
|
if ($_[0]->{'is_snap'}) {
|
|
$cmd .= " -s '/dev/$_[0]->{'vg'}/$_[0]->{'snapof'}'";
|
|
}
|
|
else {
|
|
$cmd .= " -p $_[0]->{'perm'}";
|
|
$cmd .= " -C $_[0]->{'alloc'}";
|
|
$cmd .= " -i $_[0]->{'stripe'}" if ($_[0]->{'stripe'});
|
|
$cmd .= " $_[0]->{'vg'}";
|
|
}
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
# delete_logical_volume(&lv)
|
|
sub delete_logical_volume
|
|
{
|
|
local $cmd = "lvremove -f '$_[0]->{'device'}'";
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
# resize_logical_volume(&lv, size)
|
|
sub resize_logical_volume
|
|
{
|
|
local $cmd = $_[1] > $_[0]->{'size'} ? "lvextend" : "lvreduce -f";
|
|
$cmd .= " -L$_[1]k";
|
|
$cmd .= " '$_[0]->{'device'}'";
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
# change_logical_volume(&lv)
|
|
sub change_logical_volume
|
|
{
|
|
local $cmd = "lvchange ";
|
|
$cmd .= " -p $_[0]->{'perm'}";
|
|
$cmd .= " -C $_[0]->{'alloc'}";
|
|
$cmd .= " '$_[0]->{'device'}'";
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
# rename_logical_volume(&lv, name)
|
|
sub rename_logical_volume
|
|
{
|
|
local $cmd = "lvrename '$_[0]->{'device'}' '/dev/$_[0]->{'vg'}/$_[1]'";
|
|
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
# can_resize_filesystem(type)
|
|
sub can_resize_filesystem
|
|
{
|
|
if ($_[0] eq "ext2" || $_[0] eq "ext3") {
|
|
return &has_command("e2fsadm") ? 2 : 0;
|
|
}
|
|
# xfs_growfs doesn't work on LVMs
|
|
#elsif ($_[0] eq "xfs") {
|
|
# return &has_command("xfs_growfs") ? 1 : 0;
|
|
# }
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
# resize_filesystem(&lv, type, size)
|
|
sub resize_filesystem
|
|
{
|
|
if ($_[1] eq "ext2" || $_[1] eq "ext3") {
|
|
&foreign_require("proc", "proc-lib.pl");
|
|
local $cmd = "e2fsadm -L $_[2]k '$_[0]->{'device'}'";
|
|
local ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", $cmd);
|
|
local $out;
|
|
while(<$fh>) {
|
|
$out .= $_;
|
|
}
|
|
close($fh);
|
|
waitpid($fpid, 0);
|
|
&additional_log("exec", undef, $cmd);
|
|
return $? ? $out : undef;
|
|
}
|
|
elsif ($_[1] eq "xfs") {
|
|
# Resize the logical volume first
|
|
|
|
# XXX resize the filesystem
|
|
}
|
|
else {
|
|
return "???";
|
|
}
|
|
}
|
|
|
|
|
|
# parse_colon_file(file)
|
|
sub parse_colon_file
|
|
{
|
|
local %rv;
|
|
open(FILE, $_[0]);
|
|
while(<FILE>) {
|
|
if (/^([^:]+):\s*(.*)/) {
|
|
$rv{$1} = $2;
|
|
}
|
|
}
|
|
close(FILE);
|
|
return %rv;
|
|
}
|
|
|
|
# device_status(device)
|
|
# Returns an array of directory, type, mounted
|
|
sub device_status
|
|
{
|
|
@mounted = &foreign_call("mount", "list_mounted") if (!@mounted);
|
|
@mounts = &foreign_call("mount", "list_mounts") if (!@mounts);
|
|
local $label = &fdisk::get_label($_[0]);
|
|
|
|
local ($mounted) = grep { &same_file($_->[1], $_[0]) ||
|
|
$_->[1] eq "LABEL=$label" } @mounted;
|
|
local ($mount) = grep { &same_file($_->[1], $_[0]) ||
|
|
$_->[1] eq "LABEL=$label" } @mounts;
|
|
print STDERR "for $_[0] : mount = $mount mounted = $mounted\n";
|
|
if ($mounted) { return ($mounted->[0], $mounted->[2], 1,
|
|
&indexof($mount, @mounts),
|
|
&indexof($mounted, @mounted)); }
|
|
elsif ($mount) { return ($mount->[0], $mount->[2], 0,
|
|
&indexof($mount, @mounts)); }
|
|
elsif ($has_raid) {
|
|
$raidconf = &foreign_call("raid", "get_raidtab") if (!$raidconf);
|
|
foreach $c (@$raidconf) {
|
|
foreach $d (raid::find_value('device', $c->{'members'})) {
|
|
return ( $c->{'value'}, "raid", 1 ) if ($d eq $_[0]);
|
|
}
|
|
}
|
|
}
|
|
return ();
|
|
}
|
|
|
|
sub device_message
|
|
{
|
|
$msg = $_[2] ? 'lv_mount' : 'lv_umount';
|
|
$msg .= 'vm' if ($_[1] eq 'swap');
|
|
$msg .= 'raid' if ($_[1] eq 'raid');
|
|
return &text($msg, "<tt>$_[0]</tt>", "<tt>$_[1]</tt>");
|
|
}
|
|
|
|
# list_lvmtab()
|
|
sub list_lvmtab
|
|
{
|
|
local @rv;
|
|
open(TAB, $lvm_tab);
|
|
local $/ = "\0";
|
|
while(<TAB>) {
|
|
chop;
|
|
push(@rv, $_) if ($_);
|
|
}
|
|
close(TAB);
|
|
return @rv;
|
|
}
|
|
|
|
# device_input()
|
|
sub device_input
|
|
{
|
|
local (%used, $vg, $pv, $d, $p);
|
|
|
|
# Find partitions that are part of an LVM
|
|
foreach $vg (&list_volume_groups()) {
|
|
foreach $pv (&list_physical_volumes($vg->{'name'})) {
|
|
$used{$pv->{'device'}}++;
|
|
}
|
|
}
|
|
|
|
# Show available partitions
|
|
print "<select name=device>\n";
|
|
foreach $d (&foreign_call("fdisk", "list_disks_partitions")) {
|
|
foreach $p (@{$d->{'parts'}}) {
|
|
next if ($used{$p->{'device'}} || $p->{'extended'});
|
|
local @ds = &device_status($p->{'device'});
|
|
next if (@ds);
|
|
if ($p->{'type'} eq '83') {
|
|
local $label = &fdisk::get_label($p->{'device'});
|
|
next if ($used{"LABEL=$label"});
|
|
}
|
|
local $tag = &foreign_call("fdisk", "tag_name", $p->{'type'});
|
|
printf "<option value='%s'>%s %s\n",
|
|
$p->{'device'}, $p->{'desc'},
|
|
$tag ? "($tag)" : "";
|
|
}
|
|
}
|
|
|
|
# Show available RAID devices
|
|
local $conf = &foreign_call("raid", "get_raidtab");
|
|
foreach $c (@$conf) {
|
|
next if ($used{$c->{'value'}});
|
|
local @ds = &device_status($c->{'value'});
|
|
next if (@ds);
|
|
printf "<option value='%s'>%s\n",
|
|
$c->{'value'}, &text('pv_raid', $c->{'value'} =~ /md(\d+)$/ ? "$1" : $c->{'value'});
|
|
}
|
|
|
|
print "<option value=''>$text{'pv_other'}\n";
|
|
print "</select>\n";
|
|
print "<input name=other size=30> ",
|
|
&file_chooser_button("other"),"<br>\n";
|
|
print "<b>$text{'pv_warn'}</b>\n";
|
|
}
|
|
|
|
1;
|
|
|