server => 1,
export => 1,
options => 0,
- content => [ { images => 1, iso => 1, backup => 1},
+ content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1},
{ images => 1 }],
format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
},
$res->{$c} = 1;
}
- # only local storage may have several content types
- if ($res->{none} || !($storeid && $storeid eq 'local')) {
- if (scalar (keys %$res) > 1) {
+ if ($res->{none} && scalar (keys %$res) > 1) {
return undef if $noerr;
- die "storage does not support multiple content types\n";
- }
- }
-
- # no backup to local storage
- if ($storeid && $storeid eq 'local' && $res->{backup}) {
- return undef if $noerr;
- die "storage 'local' does not support backups\n";
+ die "unable to combine 'none' with other content types\n";
}
return $res;
my $ids = {};
- my $sha1 = Digest::SHA1->new;
+ my $digest = Digest::SHA1::sha1_hex(defined($raw) ? $raw : '');
my $pri = 0;
while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
my $line = $1;
- $sha1->add ($line); # compute digest
-
next if $line =~ m/^\#/;
next if $line =~ m/^\s*$/;
my $type = $1;
my $ignore = 0;
- if (!parse_storage_id ($storeid, 1)) {
+ if (!PVE::JSONSchema::parse_storage_id($storeid, 1)) {
$ignore = 1;
warn "ignoring storage '$storeid' - (illegal characters)\n";
} elsif (!$default_config->{$type}) {
}
}
- my $digest = $sha1->hexdigest;
-
my $cfg = { ids => $ids, digest => $digest};
return $cfg;
return $vmid ? "$path/images/$vmid" : "$path/images";
}
+sub get_private_dir {
+ my ($cfg, $storeid, $vmid) = @_;
+
+ my $path = $cfg->{ids}->{$storeid}->{path};
+ return $vmid ? "$path/private/$vmid" : "$path/private";
+}
+
sub get_iso_dir {
my ($cfg, $storeid) = @_;
my $isodir = $cfg->{ids}->{$storeid}->{path};
- $isodir .= '/template/iso' if $storeid eq 'local';
+ $isodir .= '/template/iso';
return $isodir;
}
my ($cfg, $storeid) = @_;
my $tmpldir = $cfg->{ids}->{$storeid}->{path};
- $tmpldir .= '/template/cache' if $storeid eq 'local';
+ $tmpldir .= '/template/cache';
return $tmpldir;
}
+sub get_backup_dir {
+ my ($cfg, $storeid) = @_;
+
+ my $dir = $cfg->{ids}->{$storeid}->{path};
+ $dir .= '/dump';
+
+ return $dir;
+}
+
# iscsi utility functions
sub iscsi_session_list {
my $res = {};
- run_command ($cmd, outfunc => sub {
+ run_command($cmd, outfunc => sub {
my $line = shift;
if ($line =~ m/^tcp:\s+\[(\S+)\]\s+\S+\s+(\S+)\s*$/) {
return $res if !iscsi_test_portal($portal); # fixme: raise exception here?
- run_command ($cmd, outfunc => sub {
+ run_command($cmd, outfunc => sub {
my $line = shift;
if ($line =~ m/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+)\,\S+\s+(\S+)\s*$/) {
warn $@ if $@;
my $cmd = [$ISCSIADM, '--mode', 'node', '--targetname', $target, '--login'];
- run_command ($cmd);
+ run_command($cmd);
}
sub iscsi_logout {
check_iscsi_support ();
my $cmd = [$ISCSIADM, '--mode', 'node', '--targetname', $target, '--logout'];
- run_command ($cmd);
+ run_command($cmd);
}
my $rescan_filename = "/var/run/pve-iscsi-rescan.lock";
foreach my $session (@$session_list) {
my $cmd = [$ISCSIADM, '--mode', 'session', '-r', $session, '-R'];
- eval { run_command ($cmd, outfunc => sub {}); };
+ eval { run_command($cmd, outfunc => sub {}); };
warn $@ if $@;
}
}
$res->{$target}->{$volid} = {
'format' => 'raw',
- 'size' => int($size / 2),
+ 'size' => int($size * 512),
'vmid' => 0, # not assigned to any vm
'channel' => int($channel),
'id' => int($id),
# library implementation
-
-PVE::JSONSchema::register_format('pve-storage-id', \&parse_storage_id);
-sub parse_storage_id {
- my ($storeid, $noerr) = @_;
-
- if ($storeid !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
- return undef if $noerr;
- die "storage ID '$storeid' contains illegal characters\n";
- }
- return $storeid;
-}
-
-PVE::JSONSchema::register_standard_option('pve-storage-id', {
- description => "The storage identifier.",
- type => 'string', format => 'pve-storage-id',
-});
-
PVE::JSONSchema::register_format('pve-storage-vgname', \&parse_lvm_name);
sub parse_lvm_name {
my ($name, $noerr) = @_;
return ('iso', $1);
} elsif ($volname =~ m!^vztmpl/([^/]+\.tar\.gz)$!) {
return ('vztmpl', $1);
- }
+ } elsif ($volname =~ m!^rootdir/(\d+)$!) {
+ return ('rootdir', $1, $1);
+ } elsif ($volname =~ m!^backup/([^/]+(\.(tar|tar\.gz|tar\.lzo|tgz)))$!) {
+ my $fn = $1;
+ if ($fn =~ m/^vzdump-(openvz|qemu)-(\d+)-.+/) {
+ return ('backup', $fn, $2);
+ }
+ return ('backup', $fn);
+ }
die "unable to parse directory volume name '$volname'\n";
}
my $type = $ids->{$sid}->{type};
next if !($type eq 'dir' || $type eq 'nfs');
- my $imagedir = $ids->{$sid}->{path} . "/images";
- my $isodir = get_iso_dir ($cfg, $sid);
- my $tmpldir = get_vztmpl_dir ($cfg, $sid);
+ my $imagedir = get_image_dir($cfg, $sid);
+ my $isodir = get_iso_dir($cfg, $sid);
+ my $tmpldir = get_vztmpl_dir($cfg, $sid);
+ my $backupdir = get_backup_dir($cfg, $sid);
+ my $privatedir = get_private_dir($cfg, $sid);
if ($path =~ m!^$imagedir/(\d+)/([^/\s]+)$!) {
my $vmid = $1;
} elsif ($path =~ m!^$tmpldir/([^/]+\.tar\.gz)$!) {
my $name = $1;
return ('vztmpl', "$sid:vztmpl/$name");
+ } elsif ($path =~ m!^$privatedir/(\d+)$!) {
+ my $vmid = $1;
+ return ('rootdir', "$sid:rootdir/$vmid");
+ } elsif ($path =~ m!^$backupdir/([^/]+\.(tar|tar\.gz|tar\.lzo|tgz))$!) {
+ my $name = $1;
+ return ('iso', "$sid:backup/$name");
}
}
my $path;
my $owner;
+ my $vtype = 'image';
if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') {
- my ($vtype, $name, $vmid) = parse_volname_dir ($volname);
+ my ($name, $vmid);
+ ($vtype, $name, $vmid) = parse_volname_dir ($volname);
$owner = $vmid;
- my $imagedir = get_image_dir ($cfg, $storeid, $vmid);
- my $isodir = get_iso_dir ($cfg, $storeid);
- my $tmpldir = get_vztmpl_dir ($cfg, $storeid);
+ my $imagedir = get_image_dir($cfg, $storeid, $vmid);
+ my $isodir = get_iso_dir($cfg, $storeid);
+ my $tmpldir = get_vztmpl_dir($cfg, $storeid);
+ my $backupdir = get_backup_dir($cfg, $storeid);
+ my $privatedir = get_private_dir($cfg, $storeid);
if ($vtype eq 'image') {
$path = "$imagedir/$name";
$path = "$isodir/$name";
} elsif ($vtype eq 'vztmpl') {
$path = "$tmpldir/$name";
+ } elsif ($vtype eq 'rootdir') {
+ $path = "$privatedir/$name";
+ } elsif ($vtype eq 'backup') {
+ $path = "$backupdir/$name";
} else {
die "should not be reached";
}
die "unknown storage type '$scfg->{type}'";
}
- return wantarray ? ($path, $owner) : $path;
+ return wantarray ? ($path, $owner, $vtype) : $path;
}
sub storage_migrate {
if ($tcfg->{shared}) { # we can do a local copy
- run_command (['/bin/mkdir', '-p', $dirname]);
+ run_command(['/bin/mkdir', '-p', $dirname]);
- run_command (['/bin/cp', $src, $dst]);
+ run_command(['/bin/cp', $src, $dst]);
} else {
- run_command (['/usr/bin/ssh', "root\@${target_host}",
- '/bin/mkdir', '-p', $dirname]);
+ run_command(['/usr/bin/ssh', "root\@${target_host}",
+ '/bin/mkdir', '-p', $dirname]);
# we use rsync with --sparse, so we can't use --inplace,
# so we remove file on the target if it already exists to
# save space
my ($size, $format) = file_size_info($src);
if ($format && ($format eq 'raw') && $size) {
- run_command (['/usr/bin/ssh', "root\@${target_host}",
- 'rm', '-f', $dst],
- outfunc => sub {});
+ run_command(['/usr/bin/ssh', "root\@${target_host}",
+ 'rm', '-f', $dst],
+ outfunc => sub {});
}
my $cmd = ['/usr/bin/rsync', '--progress', '--sparse', '--whole-file',
my $percent = -1;
- run_command ($cmd, outfunc => sub {
+ run_command($cmd, outfunc => sub {
my $line = shift;
if ($line =~ m/^\s*(\d+\s+(\d+)%\s.*)$/) {
die "no storage id specified\n" if !$storeid;
- parse_storage_id ($storeid);
+ PVE::JSONSchema::parse_storage_id($storeid);
- my $scfg = storage_config ($cfg, $storeid);
+ my $scfg = storage_config($cfg, $storeid);
die "no VMID specified\n" if !$vmid;
my $path = "$imagedir/$name";
- die "disk image '$path' already exists\n" if -f $path;
+ die "disk image '$path' already exists\n" if -e $path;
run_command("/usr/bin/qemu-img create -f $fmt '$path' ${size}K",
errmsg => "unable to create image");
die "unable to allocate an image name for VM $vmid in storage '$storeid'\n"
if !$name;
- my $cmd = ['/sbin/lvcreate', '--addtag', "pve-vm-$vmid", '--size', "${size}k", '--name', $name, $vg];
+ my $cmd = ['/sbin/lvcreate', '-aly', '--addtag', "pve-vm-$vmid", '--size', "${size}k", '--name', $name, $vg];
- run_command ($cmd);
+ run_command($cmd, errmsg => "lvcreate '$vg/pve-vm-$vmid' error");
return "$storeid:$name";
my $cmd = ['/sbin/lvremove', '-f', "$vg/$volname"];
- run_command ($cmd);
+ run_command($cmd, errmsg => "lvremove '$vg/$volname' error");
} elsif ($scfg->{type} eq 'iscsi') {
die "can't free space in iscsi storage\n";
} else {
my $has_label = 0;
my $cmd = ['/usr/bin/file', '-L', '-s', $device];
- run_command ($cmd, outfunc => sub {
+ run_command($cmd, outfunc => sub {
my $line = shift;
$has_label = 1 if $line =~ m/LVM2/;
});
'pv_name,pv_size,vg_name,pv_uuid', $device];
my $pvinfo;
- run_command ($cmd, outfunc => sub {
+ run_command($cmd, outfunc => sub {
my $line = shift;
$line = trim($line);
# so pe_start is aligned on a 128k boundary (advantage for SSDs)
my $cmd = ['/sbin/pvcreate', '--metadatasize', '250k', $device];
- run_command ($cmd);
+ run_command($cmd, errmsg => "pvcreate '$device' error");
$cmd = ['/sbin/vgcreate', $vgname, $device];
# push @$cmd, '-c', 'y' if $shared; # we do not use this yet
- run_command ($cmd);
+ run_command($cmd, errmsg => "vgcreate $vgname $device error");
}
sub lvm_vgs {
'vg_name,vg_size,vg_free'];
my $vgs = {};
- run_command ($cmd, outfunc => sub {
- my $line = shift;
+ eval {
+ run_command($cmd, outfunc => sub {
+ my $line = shift;
- $line = trim($line);
+ $line = trim($line);
- my ($name, $size, $free) = split (':', $line);
+ my ($name, $size, $free) = split (':', $line);
- $vgs->{$name} = { size => int ($size), free => int ($free) };
- });
+ $vgs->{$name} = { size => int ($size), free => int ($free) };
+ });
+ };
+ my $err = $@;
+
+ # just warn (vgs return error code 5 if clvmd does not run)
+ # but output is still OK (list without clustered VGs)
+ warn $err if $err;
return $vgs;
}
push @$cmd, $vgname if $vgname;
my $lvs = {};
- run_command ($cmd, outfunc => sub {
+ run_command($cmd, outfunc => sub {
my $line = shift;
$line = trim($line);
return $lvs;
}
-#install iso or openvz template ($tt = <iso|vztmpl>)
-# we simply overwrite when file already exists
-sub install_template {
- my ($cfg, $storeid, $tt, $srcfile, $destfile) = @_;
-
- my $scfg = storage_config ($cfg, $storeid);
-
- my $type = $scfg->{type};
-
- die "invalid storage type '$type'" if !($type eq 'dir' || $type eq 'nfs');
-
- my $path;
-
- if ($tt eq 'iso') {
- die "file '$destfile' has no '.iso' extension\n"
- if $destfile !~ m![^/]+\.[Ii][Ss][Oo]$!;
- die "storage '$storeid' does not support 'iso' content\n"
- if !$scfg->{content}->{iso};
- $path = get_iso_dir ($cfg, $storeid);
- } elsif ($tt eq 'vztmpl') {
- die "file '$destfile' has no '.tar.gz' extension\n"
- if $destfile !~ m![^/]+\.tar\.gz$!;
- die "storage '$storeid' does not support 'vztmpl' content\n"
- if !$scfg->{content}->{vztmpl};
- $path = get_vztmpl_dir ($cfg, $storeid);
- } else {
- die "unknown template type '$tt'";
- }
-
- activate_storage ($cfg, $storeid);
-
- my $dest = "$path/$destfile";
-
- my $cmd = ['cp', $srcfile, $dest];
-
- eval { run_command ($cmd); };
- my $err = $@;
-
- if ($err) {
- unlink $dest;
- die $err;
- }
-}
-
#list iso or openvz template ($tt = <iso|vztmpl|backup>)
sub template_list {
my ($cfg, $storeid, $tt) = @_;
my $path;
if ($tt eq 'iso') {
- $path = get_iso_dir ($cfg, $sid);
+ $path = get_iso_dir($cfg, $sid);
} elsif ($tt eq 'vztmpl') {
- $path = get_vztmpl_dir ($cfg, $sid);
+ $path = get_vztmpl_dir($cfg, $sid);
} elsif ($tt eq 'backup') {
- $path = $scfg->{path};
+ $path = get_backup_dir($cfg, $sid);
} else {
die "unknown template type '$tt'\n";
}
$info = { volid => "$sid:vztmpl/$1", format => 'tgz' };
} elsif ($tt eq 'backup') {
- next if $fn !~ m!/([^/]+\.(tar|tgz))$!;
+ next if $fn !~ m!/([^/]+\.(tar|tar\.gz|tar\.lzo|tgz))$!;
$info = { volid => "$sid:backup/$1", format => $2 };
}
my $used = 0;
eval {
- run_command ($cmd, timeout => $timeout, outfunc => sub {
+ run_command($cmd, timeout => $timeout, outfunc => sub {
my $line = shift;
if ($line =~ m/^file format:\s+(\S+)\s*$/) {
$mountdata = read_proc_mounts() if !$mountdata;
- if ($mountdata =~ m/^$source\s$mountpoint\snfs/m) {
+ if ($mountdata =~ m|^$source/?\s$mountpoint\snfs|m) {
return $mountpoint;
}
push @$cmd, '-o', $options;
}
- run_command ($cmd);
+ run_command($cmd, errmsg => "mount error");
}
sub uevent_seqnum {
"directory '$path' does not exist\n" if ! -d $path;
}
- my $imagedir = get_image_dir ($cfg, $storeid);
- my $isodir = get_iso_dir ($cfg, $storeid);
- my $tmpldir = get_vztmpl_dir ($cfg, $storeid);
+ my $imagedir = get_image_dir($cfg, $storeid);
+ my $isodir = get_iso_dir($cfg, $storeid);
+ my $tmpldir = get_vztmpl_dir($cfg, $storeid);
+ my $backupdir = get_backup_dir($cfg, $storeid);
+ my $privatedir = get_private_dir($cfg, $storeid);
if (defined($scfg->{content})) {
mkpath $imagedir if $scfg->{content}->{images} &&
$isodir ne $path;
mkpath $tmpldir if $scfg->{content}->{vztmpl} &&
$tmpldir ne $path;
+ mkpath $privatedir if $scfg->{content}->{rootdir} &&
+ $privatedir ne $path;
+ mkpath $backupdir if $scfg->{content}->{backup} &&
+ $backupdir ne $path;
}
} elsif ($type eq 'lvm') {
!$session->{vgs}->{$scfg->{vgname}}) {
$session->{vgscaned} = 1;
my $cmd = ['/sbin/vgscan', '--ignorelockingfailure', '--mknodes'];
- eval { run_command ($cmd, outfunc => sub {}); };
+ eval { run_command($cmd, outfunc => sub {}); };
warn $@ if $@;
}
- my $cmd = ['/sbin/vgchange', '-aly', $scfg->{vgname}];
- run_command ($cmd, outfunc => sub {});
+ # we do not acticate any volumes here ('vgchange -aly')
+ # instead, volumes are activate individually later
} elsif ($type eq 'iscsi') {
}
sub activate_volumes {
- my ($cfg, $vollist) = @_;
+ my ($cfg, $vollist, $exclusive) = @_;
+
+ return if !($vollist && scalar(@$vollist));
+
+ my $lvm_activate_mode = $exclusive ? 'ey' : 'ly';
my $storagehash = {};
foreach my $volid (@$vollist) {
my $path = path ($cfg, $volid);
if ($scfg->{type} eq 'lvm') {
- my $cmd = ['/sbin/lvchange', '-aly', $path];
- eval { run_command ($cmd); };
- warn $@ if $@;
+ my $cmd = ['/sbin/lvchange', "-a$lvm_activate_mode", $path];
+ run_command($cmd, errmsg => "can't activate LV '$volid'");
}
# check is volume exists
if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') {
- die "volume '$volid' does not exist\n" if ! -f $path;
+ die "volume '$volid' does not exist\n" if ! -e $path;
} else {
die "volume '$volid' does not exist\n" if ! -b $path;
}
sub deactivate_volumes {
my ($cfg, $vollist) = @_;
+ return if !($vollist && scalar(@$vollist));
+
my $lvs = lvm_lvs ();
+ my @errlist = ();
foreach my $volid (@$vollist) {
my ($storeid, $volname) = parse_volume_id ($volid);
if ($lvs->{$scfg->{vgname}}->{$name}) {
my $path = path ($cfg, $volid);
my $cmd = ['/sbin/lvchange', '-aln', $path];
- eval { run_command ($cmd); };
- warn $@ if $@;
+ eval { run_command($cmd, errmsg => "can't deactivate LV '$volid'"); };
+ if (my $err = $@) {
+ warn $err;
+ push @errlist, $volid;
+ }
}
}
}
+
+ die "volume deativation failed: " . join(' ', @errlist)
+ if scalar(@errlist);
}
sub deactivate_storage {
my $cmd = ['/bin/umount', $path];
- run_command ($cmd) if nfs_is_mounted ($server, $export, $path, $mountdata);
+ run_command($cmd, errmsg => 'umount error')
+ if nfs_is_mounted ($server, $export, $path, $mountdata);
+
} elsif ($type eq 'lvm') {
my $cmd = ['/sbin/vgchange', '-aln', $scfg->{vgname}];
- run_command ($cmd);
+ run_command($cmd, errmsg => "can't deactivate VG '$scfg->{vgname}'");
} elsif ($type eq 'iscsi') {
my $portal = $scfg->{portal};
my $target = $scfg->{target};
total => 0,
avail => 0,
used => 0,
+ shared => $ids->{$storeid}->{shared} ? 1 : 0,
content => content_hash_to_string($ids->{$storeid}->{content}),
active => 0,
};
my $cmd = ['/sbin/showmount', '--no-headers', '--exports', $server];
my $res = {};
- run_command ($cmd, outfunc => sub {
+ run_command($cmd, outfunc => sub {
my $line = shift;
# note: howto handle white spaces in export path??