Upstream proxmox-git/pve-storage state of the touched files before 1860d190cb

This commit is contained in:
upstream committed 2026-10-03 02:00:08 +02:00
1 parent ab149ea5d1
commit 095cd282f3
3 files changed
+326 -44

No files matched your search

-18
View File
@@ -369,24 +369,6 @@ ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id,
};
}
# the address is only unique per controller, so disks on different controllers of the same
# type can end up with the same address, move those to the next free one of that bus
my $used_addresses = { map { $_->{disk_address} => 1 } @disks };
my $assigned_addresses = {};
for my $disk (@disks) {
if (
$assigned_addresses->{ $disk->{disk_address} }
&& $disk->{disk_address} =~ m/^([a-z]+)\d+$/
) {
my $bus = $1;
my $index = 0;
$index++ while $used_addresses->{"$bus$index"};
$disk->{disk_address} = "$bus$index";
$used_addresses->{ $disk->{disk_address} } = 1;
}
$assigned_addresses->{ $disk->{disk_address} } = 1;
}
my $nic_id = dtmf_name_to_id('Ethernet Adapter');
my $xpath_find_nics =
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType=${nic_id}]";
+326
View File
@@ -0,0 +1,326 @@
package PVE::Storage::DirPlugin;
use strict;
use warnings;
use Cwd;
use Encode qw(decode encode);
use File::Path;
use File::Spec;
use IO::File;
use POSIX;
use PVE::Storage::Plugin;
use PVE::GuestImport::OVF;
use PVE::JSONSchema qw(get_standard_option);
use base qw(PVE::Storage::Plugin);
# Configuration
sub type {
return 'dir';
}
sub plugindata {
return {
content => [
{
images => 1,
rootdir => 1,
vztmpl => 1,
iso => 1,
backup => 1,
snippets => 1,
none => 1,
import => 1,
},
{ images => 1, rootdir => 1 },
],
format => [{ raw => 1, qcow2 => 1, vmdk => 1, subvol => 1 }, 'raw'],
'sensitive-properties' => {},
};
}
sub properties {
return {
path => {
description => "File system path.",
type => 'string',
format => 'pve-storage-path',
},
mkdir => {
description =>
"Create the directory if it doesn't exist and populate it with default sub-dirs."
. " NOTE: Deprecated, use the 'create-base-path' and 'create-subdirs' options instead.",
type => 'boolean',
default => 'yes',
},
'create-base-path' => {
description => "Create the base directory if it doesn't exist.",
type => 'boolean',
default => 'yes',
},
'create-subdirs' => {
description => "Populate the directory with the default structure.",
type => 'boolean',
default => 'yes',
},
is_mountpoint => {
description => "Assume the given path is an externally managed mountpoint "
. "and consider the storage offline if it is not mounted. "
. "Using a boolean (yes/no) value serves as a shortcut to using the target path in this field.",
type => 'string',
default => 'no',
},
bwlimit => get_standard_option('bwlimit'),
};
}
sub options {
return {
path => { fixed => 1 },
'content-dirs' => { optional => 1 },
nodes => { optional => 1 },
shared => { optional => 1 },
disable => { optional => 1 },
'prune-backups' => { optional => 1 },
'max-protected-backups' => { optional => 1 },
content => { optional => 1 },
format => { optional => 1 },
mkdir => { optional => 1 },
'create-base-path' => { optional => 1 },
'create-subdirs' => { optional => 1 },
is_mountpoint => { optional => 1 },
bwlimit => { optional => 1 },
preallocation => { optional => 1 },
'snapshot-as-volume-chain' => { optional => 1, fixed => 1 },
};
}
# Storage implementation
#
# NOTE: should ProcFSTools::is_mounted accept an optional cache like this?
sub path_is_mounted {
my ($mountpoint, $mountdata) = @_;
$mountpoint = Cwd::realpath($mountpoint); # symlinks
return 0 if !defined($mountpoint); # path does not exist
$mountdata = PVE::ProcFSTools::parse_proc_mounts() if !$mountdata;
return 1 if grep { $_->[1] eq $mountpoint } @$mountdata;
return undef;
}
sub parse_is_mountpoint {
my ($scfg) = @_;
my $is_mp = $scfg->{is_mountpoint};
return undef if !defined $is_mp;
if (defined(my $bool = PVE::JSONSchema::parse_boolean($is_mp))) {
return $bool ? $scfg->{path} : undef;
}
return $is_mp; # contains a path
}
# FIXME move into 'get_volume_attribute' when removing 'get_volume_notes'
my $get_volume_notes_impl = sub {
my ($class, $scfg, $storeid, $volname, $timeout) = @_;
my ($vtype) = $class->parse_volname($volname);
return if $vtype ne 'backup';
my $path = $class->filesystem_path($scfg, $volname);
$path .= $class->SUPER::NOTES_EXT;
if (-f $path) {
my $data = PVE::Tools::file_get_contents($path);
return eval { decode('UTF-8', $data, 1) } // $data;
}
return '';
};
# FIXME remove on the next APIAGE reset.
# Deprecated, use get_volume_attribute instead.
sub get_volume_notes {
my ($class, $scfg, $storeid, $volname, $timeout) = @_;
return $get_volume_notes_impl->($class, $scfg, $storeid, $volname, $timeout);
}
# FIXME move into 'update_volume_attribute' when removing 'update_volume_notes'
my $update_volume_notes_impl = sub {
my ($class, $scfg, $storeid, $volname, $notes, $timeout) = @_;
my ($vtype) = $class->parse_volname($volname);
die "only backups can have notes\n" if $vtype ne 'backup';
my $path = $class->filesystem_path($scfg, $volname);
$path .= $class->SUPER::NOTES_EXT;
if (defined($notes) && $notes ne '') {
my $encoded = encode('UTF-8', $notes);
PVE::Tools::file_set_contents($path, $encoded);
} else {
unlink $path or $! == ENOENT or die "could not delete notes - $!\n";
}
return;
};
# FIXME remove on the next APIAGE reset.
# Deprecated, use update_volume_attribute instead.
sub update_volume_notes {
my ($class, $scfg, $storeid, $volname, $notes, $timeout) = @_;
return $update_volume_notes_impl->($class, $scfg, $storeid, $volname, $notes, $timeout);
}
sub get_volume_attribute {
my ($class, $scfg, $storeid, $volname, $attribute) = @_;
if ($attribute eq 'notes') {
return $get_volume_notes_impl->($class, $scfg, $storeid, $volname);
}
my ($vtype) = $class->parse_volname($volname);
return if $vtype ne 'backup';
if ($attribute eq 'protected') {
my $path = $class->filesystem_path($scfg, $volname);
return -e PVE::Storage::protection_file_path($path) ? 1 : 0;
}
return;
}
sub update_volume_attribute {
my ($class, $scfg, $storeid, $volname, $attribute, $value) = @_;
if ($attribute eq 'notes') {
return $update_volume_notes_impl->($class, $scfg, $storeid, $volname, $value);
}
my ($vtype) = $class->parse_volname($volname);
die "only backups support attribute '$attribute'\n" if $vtype ne 'backup';
if ($attribute eq 'protected') {
my $path = $class->filesystem_path($scfg, $volname);
my $protection_path = PVE::Storage::protection_file_path($path);
return if !((-e $protection_path) xor $value); # protection status already correct
if ($value) {
my $fh = IO::File->new($protection_path, O_CREAT, 0644)
or die "unable to create protection file '$protection_path' - $!\n";
close($fh);
} else {
unlink $protection_path
or $! == ENOENT
or die "could not delete protection file '$protection_path' - $!\n";
}
return;
}
die "attribute '$attribute' is not supported for storage type '$scfg->{type}'\n";
}
sub status {
my ($class, $storeid, $scfg, $cache) = @_;
if (defined(my $mp = parse_is_mountpoint($scfg))) {
$cache->{mountdata} = PVE::ProcFSTools::parse_proc_mounts()
if !$cache->{mountdata};
return undef if !path_is_mounted($mp, $cache->{mountdata});
}
return $class->SUPER::status($storeid, $scfg, $cache);
}
sub activate_storage {
my ($class, $storeid, $scfg, $cache) = @_;
my $path = $scfg->{path};
my $mp = parse_is_mountpoint($scfg);
if (defined($mp) && !path_is_mounted($mp, $cache->{mountdata})) {
die "unable to activate storage '$storeid' - "
. "directory is expected to be a mount point but is not mounted: '$mp'\n";
}
$class->config_aware_base_mkdir($scfg, $path);
$class->SUPER::activate_storage($storeid, $scfg, $cache);
}
sub check_config {
my ($self, $sectionId, $config, $create, $skipSchemaCheck) = @_;
my $opts =
PVE::SectionConfig::check_config($self, $sectionId, $config, $create, $skipSchemaCheck);
return $opts if !$create;
if ($opts->{path} !~ m|^/[-/a-zA-Z0-9_.@]+$|) {
die "illegal path for directory storage: $opts->{path}\n";
}
# remove trailing slashes from path
$opts->{path} = File::Spec->canonpath($opts->{path});
return $opts;
}
sub get_import_metadata {
my ($class, $scfg, $volname, $storeid) = @_;
my ($vtype, $name, undef, undef, undef, undef, $fmt) = $class->parse_volname($volname);
die "invalid content type '$vtype'\n" if $vtype ne 'import';
die "invalid format\n" if $fmt ne 'ova' && $fmt ne 'ovf';
# NOTE: all types of warnings must be added to the return schema of the import-metadata API endpoint
my $warnings = [];
my $isOva = 0;
if ($fmt =~ m/^ova/) {
$isOva = 1;
push @$warnings, { type => 'ova-needs-extracting' };
}
my $path = $class->path($scfg, $volname, $storeid, undef);
my $res = PVE::GuestImport::OVF::parse_ovf($path, $isOva);
my $disks = {};
for my $disk ($res->{disks}->@*) {
my $id = $disk->{disk_address};
my $size = $disk->{virtual_size};
my $path = $disk->{relative_path};
my $volid;
if ($isOva) {
$volid = "$storeid:$volname/$path";
} else {
$volid = "$storeid:import/$path",;
}
$disks->{$id} = {
volid => $volid,
defined($size) ? (size => $size) : (),
};
}
if (defined($res->{qm}->{bios}) && $res->{qm}->{bios} eq 'ovmf') {
$disks->{efidisk0} = 1;
push @$warnings, { type => 'efi-state-lost', key => 'bios', value => 'ovmf' };
}
return {
type => 'vm',
source => $volname,
'create-args' => $res->{qm},
'disks' => $disks,
warnings => $warnings,
net => $res->{net},
};
}
sub volume_qemu_snapshot_method {
my ($class, $storeid, $scfg, $volname) = @_;
my $format = ($class->parse_volname($volname))[6];
return 'storage' if $format ne 'qcow2';
return $scfg->{'snapshot-as-volume-chain'} ? 'mixed' : 'qemu';
}
1;
-26
View File
@@ -38,15 +38,6 @@ if (my $err = $@) {
ok('parse win10 no default rasd NS');
}
my $two_controllers =
eval { PVE::GuestImport::OVF::parse_ovf("$test_manifests/two-scsi-controllers.ovf") };
if (my $err = $@) {
fail('parse two scsi controllers');
warn("error: $err\n");
} else {
ok('parse two scsi controllers');
}
my $xee = eval { PVE::GuestImport::OVF::parse_ovf("$test_manifests/XEE.ovf") };
if (my $err = $@) {
ok("parsing XEE.ovf failed as expected");
@@ -104,17 +95,6 @@ is(
'single disk vm (no default rasd NS) has the correct size',
);
is_deeply(
[map { $_->{disk_address} } $two_controllers->{disks}->@*],
['scsi0', 'scsi2', 'scsi1'],
'disks with the same address on different controllers get unique addresses',
);
is(
$two_controllers->{disks}->[1]->{backing_file},
"$test_manifests/Win10-Liz-disk1.vmdk",
'disk on the second controller has the correct backing device',
);
print "testing nics\n";
is($win2008->{net}->{net0}->{model}, 'e1000', 'win2008 has correct nic model');
is($win10->{net}->{net0}->{model}, 'e1000e', 'win10 has correct nic model');
@@ -129,12 +109,6 @@ is($win2008->{qm}->{memory}, '2048', 'win2008 VM memory is correct');
is($win2008->{qm}->{cores}, '1', 'win2008 VM cores are correct');
is($win2008->{qm}->{ostype}, 'win7', 'win2008 VM ostype is correcty');
is(
$two_controllers->{qm}->{boot},
'order=scsi0;scsi2;scsi1',
'VM with two scsi controllers boot is correct',
);
is($win10->{qm}->{boot}, 'order=scsi0', 'win10 VM boot is correct');
is($win10->{qm}->{name}, 'Win10-Liz', 'win10 VM name is correct');
is($win10->{qm}->{memory}, '6144', 'win10 VM memory is correct');