+__PACKAGE__->register_method({
+ name => 'cleanup',
+ path => 'cleanup',
+ method => 'POST',
+ protected => 1,
+ description => "Cleans up resources like tap devices, vgpus, etc. Called after a vm shuts down, crashes, etc.",
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ vmid => get_standard_option('pve-vmid', {
+ completion => \&PVE::QemuServer::complete_vmid_running }),
+ 'clean-shutdown' => {
+ type => 'boolean',
+ description => "Indicates if qemu shutdown cleanly.",
+ },
+ 'guest-requested' => {
+ type => 'boolean',
+ description => "Indicates if the shutdown was requested by the guest or via qmp.",
+ },
+ },
+ },
+ returns => { type => 'null', },
+ code => sub {
+ my ($param) = @_;
+
+ my $vmid = $param->{vmid};
+ my $clean = $param->{'clean-shutdown'};
+ my $guest = $param->{'guest-requested'};
+ my $restart = 0;
+
+ # return if we do not have the config anymore
+ return if !-f PVE::QemuConfig->config_file($vmid);
+
+ my $storecfg = PVE::Storage::config();
+ warn "Starting cleanup for $vmid\n";
+
+ # mdev cleanup can take a while, so wait up to 60 seconds
+ PVE::QemuConfig->lock_config_full($vmid, 60, sub {
+ my $conf = PVE::QemuConfig->load_config ($vmid);
+ my $pid = PVE::QemuServer::check_running ($vmid);
+ die "vm still running\n" if $pid;
+
+ if (!$clean) {
+ # we have to cleanup the tap devices after a crash
+
+ foreach my $opt (keys %$conf) {
+ next if $opt !~ m/^net(\d+)$/;
+ my $interface = $1;
+ PVE::Network::tap_unplug("tap${vmid}i${interface}");
+ }
+ }
+
+ if (!$clean || $guest) {
+ # vm was shutdown from inside the guest or crashed, doing api cleanup
+ PVE::QemuServer::vm_stop_cleanup($storecfg, $vmid, $conf, 0, 0);
+ }
+ PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'post-stop');
+
+ $restart = eval { PVE::QemuServer::clear_reboot_request($vmid) };
+ warn $@ if $@;
+ });
+
+ warn "Finished cleanup for $vmid\n";
+
+ if ($restart) {
+ warn "Restarting VM $vmid\n";
+ PVE::API2::Qemu->vm_start({
+ vmid => $vmid,
+ %node,
+ });
+ }
+
+ return;
+ }});
+
+__PACKAGE__->register_method({
+ name => 'vm_import',
+ path => 'vm-import',
+ description => "Import a foreign virtual guest from a supported import source, such as an ESXi storage.",
+ parameters => {
+ additionalProperties => 0,
+ properties => PVE::QemuServer::json_config_properties({
+ vmid => get_standard_option('pve-vmid', { completion => \&PVE::Cluster::complete_next_vmid }),
+ 'source' => {
+ type => 'string',
+ description => 'The import source volume id.',
+ },
+ storage => get_standard_option('pve-storage-id', {
+ description => "Default storage.",
+ completion => \&PVE::QemuServer::complete_storage,
+ }),
+ 'live-import' => {
+ type => 'boolean',
+ optional => 1,
+ default => 0,
+ description => "Immediately start the VM and copy the data in the background.",
+ },
+ 'dryrun' => {
+ type => 'boolean',
+ optional => 1,
+ default => 0,
+ description => "Show the create command and exit without doing anything.",
+ },
+ delete => {
+ type => 'string', format => 'pve-configid-list',
+ description => "A list of settings you want to delete.",
+ optional => 1,
+ },
+ format => {
+ type => 'string',
+ description => 'Target format',
+ enum => [ 'raw', 'qcow2', 'vmdk' ],
+ optional => 1,
+ },
+ }),
+ },
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ my ($vmid, $source, $storage, $format, $live_import, $dryrun, $delete) =
+ delete $param->@{qw(vmid source storage format live-import dryrun delete)};
+
+ if (defined($format)) {
+ $format = ",format=$format";
+ } else {
+ $format = '';
+ }
+
+ my $storecfg = PVE::Storage::config();
+ my $metadata = PVE::Storage::get_import_metadata($storecfg, $source);
+
+ my $create_args = $metadata->{'create-args'};
+ if (my $netdevs = $metadata->{net}) {
+ for my $net (keys $netdevs->%*) {
+ my $value = $netdevs->{$net};
+ $create_args->{$net} = join(',', map { $_ . '=' . $value->{$_} } sort keys %$value);
+ }
+ }
+ if (my $disks = $metadata->{disks}) {
+ if (delete $disks->{efidisk0}) {
+ $create_args->{efidisk0} = "$storage:1$format,efitype=4m";
+ }
+ for my $disk (keys $disks->%*) {
+ my $value = $disks->{$disk}->{volid};
+ $create_args->{$disk} = "$storage:0${format},import-from=$value";
+ }
+ }
+
+ $create_args->{'live-restore'} = 1 if $live_import;
+
+ $create_args->{$_} = $param->{$_} for keys $param->%*;
+ delete $create_args->{$_} for PVE::Tools::split_list($delete);
+
+ if ($dryrun) {
+ print("# dry-run – the resulting create command for the import would be:\n");
+ print("qm create $vmid \\\n ");
+ print(join(" \\\n ", map { "--$_ $create_args->{$_}" } sort keys $create_args->%*));
+ print("\n");
+ return;
+ }
+
+ PVE::API2::Qemu->create_vm({
+ %node,
+ vmid => $vmid,
+ %$create_args,
+ });
+ return;
+ }
+});
+