hotplug => {
optional => 1,
type => 'boolean',
- description => "Activate hotplug for disk and network device",
+ description => "Allow hotplug for disk and network device",
default => 0,
},
reboot => {
typetext => '[[order=]\d+] [,up=\d+] [,down=\d+] ',
description => "Startup and shutdown behavior. Order is a non-negative number defining the general startup order. Shutdown in done with reverse ordering. Additionally you can set the 'up' or 'down' delay in seconds, which specifies a delay to wait before the next VM is started or stopped.",
},
+ template => {
+ optional => 1,
+ type => 'boolean',
+ description => "Enable/disable Template.",
+ default => 0,
+ },
args => {
optional => 1,
type => 'string',
},
migrate_downtime => {
optional => 1,
- type => 'integer',
+ type => 'number',
description => "Set maximum tolerated downtime (in seconds) for migrations.",
minimum => 0,
- default => 1,
+ default => 0.1,
},
cdrom => {
optional => 1,
description => "Emulated CPU type.",
type => 'string',
enum => [ qw(486 athlon pentium pentium2 pentium3 coreduo core2duo kvm32 kvm64 qemu32 qemu64 phenom Conroe Penryn Nehalem Westmere SandyBridge Haswell Opteron_G1 Opteron_G2 Opteron_G3 Opteron_G4 Opteron_G5 host) ],
- default => 'qemu64',
+ default => 'kvm64',
},
parent => get_standard_option('pve-snapshot-name', {
optional => 1,
} else {
$path = PVE::Storage::path($storecfg, $volid);
}
- if (!$drive->{cache} && ($path =~ m|^/dev/| || $path =~ m|\.raw$|)) {
- $opts .= ",cache=none";
- }
}
+ $opts .= ",cache=none" if !$drive->{cache} && !drive_is_cdrom($drive);
+
my $pathinfo = $path ? "file=$path," : '';
return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
if ($kvp =~ m/^(ne2k_pci|e1000|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i) {
my $model = lc($1);
- my $mac = uc($3) || PVE::Tools::random_ether_addr();
+ my $mac = defined($3) ? uc($3) : PVE::Tools::random_ether_addr();
$res->{model} = $model;
$res->{macaddr} = $mac;
} elsif ($kvp =~ m/^bridge=(\S+)$/) {
} elsif ($type eq 'integer') {
return int($1) if $value =~ m/^(\d+)$/;
die "type check ('integer') failed - got '$value'\n";
+ } elsif ($type eq 'number') {
+ return $value if $value =~ m/^(\d+)(\.\d+)?$/;
+ die "type check ('number') failed - got '$value'\n";
} elsif ($type eq 'string') {
if (my $fmt = $confdesc->{$key}->{format}) {
if ($fmt eq 'pve-qm-drive') {
return if drive_is_cdrom($drive);
my $volid = $drive->{file};
+
return if !$volid || $volid =~ m|^/|;
my ($path, $owner) = PVE::Storage::path($storecfg, $volid);
$cref->{$key} = $value;
if (valid_drivename($key)) {
- my $drive = PVE::QemuServer::parse_drive($key, $value);
+ my $drive = parse_drive($key, $value);
$used_volids->{$drive->{file}} = 1 if $drive && $drive->{file};
}
}
my @param = split(/\0/, $line);
my $cmd = $param[0];
- return if !$cmd || ($cmd !~ m|kvm$|);
+ return if !$cmd || ($cmd !~ m|kvm$| && $cmd !~ m|qemu-system-x86_64$|);
for (my $i = 0; $i < scalar (@param); $i++) {
my $p = $param[$i];
$d->{maxmem} = $conf->{memory} ? $conf->{memory}*(1024*1024) : 0;
if ($conf->{balloon}) {
- $d->{balloon_min} = $conf->{balloon};
- $d->{shares} = $conf->{shares} || 1000;
+ $d->{balloon_min} = $conf->{balloon}*(1024*1024);
+ $d->{shares} = defined($conf->{shares}) ? $conf->{shares} : 1000;
}
$d->{uptime} = 0;
$d->{diskread} = 0;
$d->{diskwrite} = 0;
+ $d->{template} = is_template($conf);
+
$res->{$vmid} = $d;
}
$volhash->{$volid} = $is_cdrom || 0;
};
- PVE::QemuServer::foreach_drive($conf, sub {
+ foreach_drive($conf, sub {
my ($ds, $drive) = @_;
&$test_volid($drive->{file}, drive_is_cdrom($drive));
});
foreach my $snapname (keys %{$conf->{snapshots}}) {
my $snap = $conf->{snapshots}->{$snapname};
&$test_volid($snap->{vmstate}, 0);
- PVE::QemuServer::foreach_drive($snap, sub {
+ foreach_drive($snap, sub {
my ($ds, $drive) = @_;
&$test_volid($drive->{file}, drive_is_cdrom($drive));
});
push @$cmd, '-daemonize';
+ $pciaddr = print_pci_addr("piix3", $bridges);
+ push @$devices, '-device', "piix3-usb-uhci,id=uhci$pciaddr.0x2";
+
my $use_usb2 = 0;
for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
next if !$conf->{"usb$i"};
# enable absolute mouse coordinates (needed by vnc)
my $tablet = defined($conf->{tablet}) ? $conf->{tablet} : $defaults->{tablet};
- if ($tablet) {
- if ($use_usb2) {
- push @$devices, '-device', 'usb-tablet,bus=ehci.0,port=6';
- } else {
- push @$devices, '-usbdevice', 'tablet';
- }
- }
+ push @$devices, '-device', 'usb-tablet,id=tablet,bus=uhci.0,port=1' if $tablet;
# host pci devices
for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
sub vm_deviceplug {
my ($storecfg, $conf, $vmid, $deviceid, $device) = @_;
- return 1 if !check_running($vmid) || !$conf->{hotplug};
+ return 1 if !check_running($vmid);
+
+ if ($deviceid eq 'tablet') {
+ my $devicefull = "usb-tablet,id=tablet,bus=uhci.0,port=1";
+ qemu_deviceadd($vmid, $devicefull);
+ return 1;
+ }
+
+ return 1 if !$conf->{hotplug};
my $devices_list = vm_devices_list($vmid);
return 1 if defined($devices_list->{$deviceid});
sub vm_deviceunplug {
my ($vmid, $conf, $deviceid) = @_;
- return 1 if !check_running ($vmid) || !$conf->{hotplug};
+ return 1 if !check_running ($vmid);
+
+ if ($deviceid eq 'tablet') {
+ qemu_devicedel($vmid, $deviceid);
+ return 1;
+ }
+
+ return 1 if !$conf->{hotplug};
my $devices_list = vm_devices_list($vmid);
return 1 if !defined($devices_list->{$deviceid});
die "can't unplug bootdisk" if $conf->{bootdisk} && $conf->{bootdisk} eq $deviceid;
if ($deviceid =~ m/^(virtio)(\d+)$/) {
- return undef if !qemu_drivedel($vmid, $deviceid);
qemu_devicedel($vmid, $deviceid);
return undef if !qemu_devicedelverify($vmid, $deviceid);
+ return undef if !qemu_drivedel($vmid, $deviceid);
}
if ($deviceid =~ m/^(lsi)(\d+)$/) {
}
if ($deviceid =~ m/^(net)(\d+)$/) {
- return undef if !qemu_netdevdel($vmid, $deviceid);
qemu_devicedel($vmid, $deviceid);
return undef if !qemu_devicedelverify($vmid, $deviceid);
+ return undef if !qemu_netdevdel($vmid, $deviceid);
}
return 1;
sub qemu_deviceadd {
my ($vmid, $devicefull) = @_;
- my $ret = vm_human_monitor_command($vmid, "device_add $devicefull");
- $ret =~ s/^\s+//;
- # Otherwise, if the command succeeds, no output is sent. So any non-empty string shows an error
- return 1 if $ret eq "";
- syslog("err", "error on hotplug device : $ret");
- return undef;
+ $devicefull = "driver=".$devicefull;
+ my %options = split(/[=,]/, $devicefull);
+ vm_mon_cmd($vmid, "device_add" , %options);
+ return 1;
}
sub qemu_devicedel {
my($vmid, $deviceid) = @_;
-
- my $ret = vm_human_monitor_command($vmid, "device_del $deviceid");
- $ret =~ s/^\s+//;
- return 1 if $ret eq "";
- syslog("err", "detaching device $deviceid failed : $ret");
- return undef;
+ my $ret = vm_mon_cmd($vmid, "device_del", id => $deviceid);
+ return 1;
}
sub qemu_driveadd {
my ($vmid, $conf, $device, $deviceid) = @_;
my $netdev = print_netdev_full($vmid, $conf, $device, $deviceid);
- my $ret = vm_human_monitor_command($vmid, "netdev_add $netdev");
- $ret =~ s/^\s+//;
+ my %options = split(/[=,]/, $netdev);
- #if the command succeeds, no output is sent. So any non-empty string shows an error
- return 1 if $ret eq "";
- syslog("err", "adding netdev failed: $ret");
- return undef;
+ vm_mon_cmd($vmid, "netdev_add", %options);
+ return 1;
}
sub qemu_netdevdel {
my ($vmid, $deviceid) = @_;
- my $ret = vm_human_monitor_command($vmid, "netdev_del $deviceid");
- $ret =~ s/^\s+//;
- #if the command succeeds, no output is sent. So any non-empty string shows an error
- return 1 if $ret eq "";
- syslog("err", "deleting netdev failed: $ret");
- return undef;
+ vm_mon_cmd($vmid, "netdev_del", id => $deviceid);
+ return 1;
}
sub qemu_block_set_io_throttle {
sub qemu_block_resize {
my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
- my $running = PVE::QemuServer::check_running($vmid);
+ my $running = check_running($vmid);
return if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
sub qemu_volume_snapshot {
my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
- my $running = PVE::QemuServer::check_running($vmid);
+ my $running = check_running($vmid);
return if !PVE::Storage::volume_snapshot($storecfg, $volid, $snap, $running);
sub qemu_volume_snapshot_delete {
my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
- my $running = PVE::QemuServer::check_running($vmid);
+ my $running = check_running($vmid);
return if !PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
lock_config($vmid, sub {
my $conf = load_config($vmid, $migratedfrom);
+ die "you can't start a vm if it's a template\n" if is_template($conf);
+
check_lock($conf) if !$skiplock;
die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
print "migration listens on port $migrate_port\n" if $migrate_port;
if ($statefile && $statefile ne 'tcp') {
- eval { vm_mon_cmd($vmid, "cont"); };
+ eval { vm_mon_cmd_nocheck($vmid, "cont"); };
warn $@ if $@;
}
- # always set migrate speed (overwrite kvm default of 32m)
- # we set a very hight default of 8192m which is basically unlimited
- my $migrate_speed = $defaults->{migrate_speed} || 8192;
- $migrate_speed = $conf->{migrate_speed} || $migrate_speed;
- $migrate_speed = $migrate_speed * 1048576;
- eval {
- vm_mon_cmd($vmid, "migrate_set_speed", value => $migrate_speed);
- };
-
- my $migrate_downtime = $defaults->{migrate_downtime};
- $migrate_downtime = $conf->{migrate_downtime} if defined($conf->{migrate_downtime});
- if (defined($migrate_downtime)) {
- eval { vm_mon_cmd($vmid, "migrate_set_downtime", value => $migrate_downtime); };
- }
-
if($migratedfrom) {
my $capabilities = {};
$capabilities->{capability} = "xbzrle";
$capabilities->{state} = JSON::true;
- eval { PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => [$capabilities]); };
+ eval { vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => [$capabilities]); };
}
-
- # fixme: how do we handle that on migration?
-
- if (!defined($conf->{balloon}) || $conf->{balloon}) {
- vm_mon_cmd($vmid, "balloon", value => $conf->{balloon}*1024*1024)
- if $conf->{balloon};
- vm_mon_cmd($vmid, 'qom-set',
- path => "machine/peripheral/balloon0",
- property => "stats-polling-interval",
- value => 2);
+ else{
+
+ if (!defined($conf->{balloon}) || $conf->{balloon}) {
+ vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024)
+ if $conf->{balloon};
+ vm_mon_cmd_nocheck($vmid, 'qom-set',
+ path => "machine/peripheral/balloon0",
+ property => "guest-stats-polling-interval",
+ value => 2);
+ }
}
});
}
eval {
die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
- my $sname = PVE::QemuServer::qmp_socket($vmid);
+ my $sname = qmp_socket($vmid);
if (-e $sname) {
my $qmpclient = PVE::QMPClient->new();
my $conf = load_config($vmid);
- check_lock($conf) if !$skiplock;
+ check_lock($conf) if !($skiplock || ($conf->{lock} && $conf->{lock} eq 'backup'));
vm_mon_cmd($vmid, "stop");
});
my $conf = load_config($vmid);
- check_lock($conf) if !$skiplock;
+ check_lock($conf) if !($skiplock || ($conf->{lock} && $conf->{lock} eq 'backup'));
vm_mon_cmd($vmid, "cont");
});
my $res = '';
my $devices = {
- #addr1 : ide,parallel,serial (motherboard)
+ piix3 => { bus => 0, addr => 1 },
#addr2 : first videocard
balloon0 => { bus => 0, addr => 3 },
watchdog => { bus => 0, addr => 4 },
# vzdump restore implementaion
-sub archive_read_firstfile {
+sub tar_archive_read_firstfile {
my $archive = shift;
die "ERROR: file '$archive' does not exist\n" if ! -f $archive;
return $firstfile;
}
-sub restore_cleanup {
- my $statfile = shift;
+sub tar_restore_cleanup {
+ my ($storecfg, $statfile) = @_;
print STDERR "starting cleanup\n";
if ($volid =~ m|^/|) {
unlink $volid || die 'unlink failed\n';
} else {
- my $cfg = cfs_read_file('storage.cfg');
- PVE::Storage::vdisk_free($cfg, $volid);
+ PVE::Storage::vdisk_free($storecfg, $volid);
}
print STDERR "temporary volume '$volid' sucessfuly removed\n";
};
print $outfd "$id: $netstr\n";
} elsif ($line =~ m/^((ide|scsi|virtio|sata)\d+):\s*(\S+)\s*$/) {
my $virtdev = $1;
- my $value = $2;
+ my $value = $3;
if ($line =~ m/backup=no/) {
print $outfd "#$line";
} elsif ($virtdev && $map->{$virtdev}) {
- my $di = PVE::QemuServer::parse_drive($virtdev, $value);
+ my $di = parse_drive($virtdev, $value);
$di->{file} = $map->{$virtdev};
- $value = PVE::QemuServer::print_drive($vmid, $di);
+ $value = print_drive($vmid, $di);
print $outfd "$virtdev: $value\n";
} else {
print $outfd $line;
# update size info
foreach my $opt (keys %$conf) {
- if (PVE::QemuServer::valid_drivename($opt)) {
- my $drive = PVE::QemuServer::parse_drive($opt, $conf->{$opt});
+ if (valid_drivename($opt)) {
+ my $drive = parse_drive($opt, $conf->{$opt});
my $volid = $drive->{file};
next if !$volid;
$used->{$volid} = 1;
- next if PVE::QemuServer::drive_is_cdrom($drive);
+ next if drive_is_cdrom($drive);
next if !$volid_hash->{$volid};
$drive->{size} = $volid_hash->{$volid}->{size};
$changes = 1;
- $conf->{$opt} = PVE::QemuServer::print_drive($vmid, $drive);
+ $conf->{$opt} = print_drive($vmid, $drive);
}
}
next if $volid =~ m/vm-$vmid-state-/;
next if $used->{$volid};
$changes = 1;
- PVE::QemuServer::add_unused_volume($conf, $volid);
+ add_unused_volume($conf, $volid);
}
return $changes;
my $updatefn = sub {
my ($vmid) = @_;
- my $conf = PVE::QemuServer::load_config($vmid);
+ my $conf = load_config($vmid);
- PVE::QemuServer::check_lock($conf);
+ check_lock($conf);
+
+ my $vm_volids = {};
+ foreach my $volid (keys %$volid_hash) {
+ my $info = $volid_hash->{$volid};
+ $vm_volids->{$volid} = $info if $info->{vmid} && $info->{vmid} == $vmid;
+ }
- my $changes = PVE::QemuServer::update_disksize($vmid, $conf, $volid_hash);
+ my $changes = update_disksize($vmid, $conf, $vm_volids);
- PVE::QemuServer::update_config_nolock($vmid, $conf, 1) if $changes;
+ update_config_nolock($vmid, $conf, 1) if $changes;
};
if (defined($vmid)) {
if ($nolock) {
&$updatefn($vmid);
} else {
- PVE::QemuServer::lock_config($vmid, $updatefn, $vmid);
+ lock_config($vmid, $updatefn, $vmid);
}
} else {
my $vmlist = config_list();
if ($nolock) {
&$updatefn($vmid);
} else {
- PVE::QemuServer::lock_config($vmid, $updatefn, $vmid);
+ lock_config($vmid, $updatefn, $vmid);
}
}
}
my $rpcenv = PVE::RPCEnvironment::get();
- my $conffile = PVE::QemuServer::config_file($vmid);
+ my $conffile = config_file($vmid);
my $tmpfn = "$conffile.$$.tmp";
+ # Note: $oldconf is undef if VM does not exists
+ my $oldconf = PVE::Cluster::cfs_read_file(cfs_config_path($vmid));
+
my $print_devmap = sub {
my $virtdev_hash = {};
if !$devinfo->{$devname}->{virtdev};
}
- my $map = {};
my $cfg = cfs_read_file('storage.cfg');
+
+ # create empty/temp config
+ if ($oldconf) {
+ PVE::Tools::file_set_contents($conffile, "memory: 128\n");
+ foreach_drive($oldconf, sub {
+ my ($ds, $drive) = @_;
+
+ return if drive_is_cdrom($drive);
+
+ my $volid = $drive->{file};
+
+ return if !$volid || $volid =~ m|^/|;
+
+ my ($path, $owner) = PVE::Storage::path($cfg, $volid);
+ return if !$path || !$owner || ($owner != $vmid);
+
+ # Note: only delete disk we want to restore
+ # other volumes will become unused
+ if ($virtdev_hash->{$ds}) {
+ PVE::Storage::vdisk_free($cfg, $volid);
+ }
+ });
+ }
+
+ my $map = {};
foreach my $virtdev (sort keys %$virtdev_hash) {
my $d = $virtdev_hash->{$virtdev};
my $alloc_size = int(($d->{size} + 1024 - 1)/1024);
my $write_zeros = 1;
# fixme: what other storages types initialize volumes with zero?
- if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') {
+ if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' ||
+ $scfg->{type} eq 'sheepdog' || $scfg->{type} eq 'rbd') {
$write_zeros = 0;
}
}
rmtree $tmpdir;
-
- rename $tmpfn, $conffile ||
+
+ rename($tmpfn, $conffile) ||
die "unable to commit configuration file '$conffile'\n";
+ PVE::Cluster::cfs_update(); # make sure we read new file
+
eval { rescan($vmid, 1); };
warn $@ if $@;
}
my ($archive, $vmid, $user, $opts) = @_;
if ($archive ne '-') {
- my $firstfile = archive_read_firstfile($archive);
+ my $firstfile = tar_archive_read_firstfile($archive);
die "ERROR: file '$archive' dos not lock like a QemuServer vzdump backup\n"
if $firstfile ne 'qemu-server.conf';
}
+ my $storecfg = cfs_read_file('storage.cfg');
+
+ # destroy existing data - keep empty config
+ my $vmcfgfn = PVE::QemuServer::config_file($vmid);
+ destroy_vm($storecfg, $vmid, 1) if -f $vmcfgfn;
+
my $tocmd = "/usr/lib/qemu-server/qmextract";
$tocmd .= " --storage " . PVE::Tools::shellquote($opts->{storage}) if $opts->{storage};
local $ENV{VZDUMP_VMID} = $vmid;
local $ENV{VZDUMP_USER} = $user;
- my $conffile = PVE::QemuServer::config_file($vmid);
+ my $conffile = config_file($vmid);
my $tmpfn = "$conffile.$$.tmp";
# disable interrupts (always do cleanups)
unlink $tmpfn;
- restore_cleanup("$tmpdir/qmrestore.stat") if !$opts->{info};
+ tar_restore_cleanup($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info};
die $err;
}
rename $tmpfn, $conffile ||
die "unable to commit configuration file '$conffile'\n";
+ PVE::Cluster::cfs_update(); # make sure we read new file
+
eval { rescan($vmid, 1); };
warn $@ if $@;
};
my $conf = load_config($vmid);
+ die "you can't take a snapshot if it's a template\n"
+ if is_template($conf);
+
check_lock($conf);
$conf->{lock} = 'snapshot';
if defined($conf->{snapshots}->{$snapname});
my $storecfg = PVE::Storage::config();
-
- foreach_drive($conf, sub {
- my ($ds, $drive) = @_;
-
- return if drive_is_cdrom($drive);
- my $volid = $drive->{file};
-
- my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
- if ($storeid) {
- my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
- die "can't snapshot volume '$volid'\n"
- if !(($scfg->{path} && $volname =~ m/\.qcow2$/) ||
- ($scfg->{type} eq 'nexenta') ||
- ($scfg->{type} eq 'rbd') ||
- ($scfg->{type} eq 'sheepdog'));
- } elsif ($volid =~ m|^(/.+)$| && -e $volid) {
- die "snapshot device '$volid' is not possible\n";
- } else {
- die "can't snapshot volume '$volid'\n";
- }
- });
-
+ die "snapshot feature is not available" if !has_feature('snapshot', $conf, $storecfg);
$snap = $conf->{snapshots}->{$snapname} = {};
my $conf = load_config($vmid);
+ die "you can't rollback if vm is a template\n" if is_template($conf);
+
$snap = $conf->{snapshots}->{$snapname};
die "snapshot '$snapname' does not exist\n" if !defined($snap);
my ($vmid) = @_;
for(;;) {
- my $stat = PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "query-savevm");
+ my $stat = vm_mon_cmd_nocheck($vmid, "query-savevm");
if (!$stat->{status}) {
die "savevm not active\n";
} elsif ($stat->{status} eq 'active') {
my $conf = load_config($vmid);
- check_lock($conf) if !$drivehash;
+ if (!$drivehash) {
+ check_lock($conf);
+ die "you can't delete a snapshot if vm is a template\n"
+ if is_template($conf);
+ }
$snap = $conf->{snapshots}->{$snapname};
lock_config($vmid, $updatefn);
}
+sub has_feature {
+ my ($feature, $conf, $storecfg, $snapname, $running) = @_;
+
+ my $err = undef;
+ foreach_drive($conf, sub {
+ my ($ds, $drive) = @_;
+
+ return if drive_is_cdrom($drive);
+ my $volid = $drive->{file};
+ $err = 1 if !PVE::Storage::volume_has_feature($storecfg, $feature, $volid, $snapname, $running);
+ });
+
+ return 1 if !$err;
+}
+
+sub template_create {
+ my ($vmid, $conf, $disk) = @_;
+
+ my $running = check_running($vmid);
+ die "you can't convert a vm to template if vm is running vm\n" if $running;
+
+ my $storecfg = PVE::Storage::config();
+
+ foreach_drive($conf, sub {
+ my ($ds, $drive) = @_;
+
+ return if drive_is_cdrom($drive);
+ return if $disk && $ds ne $disk;
+
+ my $volid = $drive->{file};
+ return if !PVE::Storage::volume_has_feature($storecfg, 'template', $volid);
+
+ my $voliddst = PVE::Storage::vdisk_create_base($storecfg, $volid);
+ $drive->{file} = $voliddst;
+ $conf->{$ds} = PVE::QemuServer::print_drive($vmid, $drive);
+ PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
+ });
+}
+
+sub is_template {
+ my ($conf) = @_;
+
+ return 1 if defined $conf->{template} && $conf->{template} == 1;
+}
+
1;