[! use strict; use PVE::pvecfg; use PVE::I18N; use PVE::ConfigServer; use PVE::HTMLTable; use PVE::HTMLGrid; use PVE::Config; use PVE::Cluster; use PVE::HTMLForm; use PVE::HTMLUtils; use PVE::QemuServer; use PVE::Storage; !] [- use strict; my $out = ''; my $cinfo = $fdat{__cinfo}; my $veid = $fdat{veid}; my $cid = $fdat{cid}; my $conn = PVE::ConfigClient::connect ($udat{auth_cookie}); my $rconn = $conn; if ($cid != $cinfo->{local}->{cid}) { $rconn = PVE::ConfigClient::connect ($udat{auth_cookie}, $cinfo, $cid); } my $vminfo = $rconn->vmconfig ($veid, 'qemu')->result; my $status = $vminfo->{vzlist}->{"VEID_$veid"}->{status}; my $vmconf = $vminfo->{config}; my $form = PVE::HTMLForm->new (\%fdat); my $grid = PVE::HTMLGrid->new ('fw1', 'fw2', 'fw3:right', 'fw4'); my $stinfo = $rconn->storage_user_info ($veid)->result; my $stcfg = $stinfo->{cfg}; my $imagelist = $stinfo->{imagelist}; my @disks = PVE::QemuServer::disknames(); my $used_volids = {}; foreach my $ds (@disks) { next if !defined ($vmconf->{$ds}); my $disk = PVE::QemuServer::parse_drive ($ds, $vmconf->{$ds}); next if !$disk || PVE::QemuServer::drive_is_cdrom ($disk); $used_volids->{$disk->{file}} = 1; } sub device_list { my ($conf, $disktype) = @_; my $devsel; foreach my $ds (@disks) { next if $ds !~ m/^$disktype\d+$/; next if defined ($conf->{$ds}); my $devinfo = PVE::QemuServer::disk_devive_info ($ds); push @$devsel, [ $ds, $devinfo->{desc} ]; } $devsel = [[ '', sprintf (__("no %s devices available"), $disktype)]] if !$devsel; return $devsel; } if ($udat{action}) { my $data = $fdat{aa}; if (($udat{action} eq 'deldisk') || ($udat{action} eq 'delcdrom')) { eval { my $settings = { $data => '' }; $conn->vmconfig_set ($cid, $veid, 'qemu', $settings)->result; }; if ($@) { $udat{popup_error} = $@; } } elsif (($udat{action} eq 'delunused')) { eval { $conn->qemu_unlink_disk ($cid, $veid, $data)->result; }; if ($@) { $udat{popup_error} = $@; } } elsif ($udat{action} eq 'delnic') { foreach my $vlan (keys %$vmconf) { next if $vlan !~ m/^vlan(\d+|u)$/; my $cc = $vmconf->{$vlan}; next if !$cc; my $net = PVE::QemuServer::parse_vlan ($cc); next if !$net || !$net->{nics}; my $na = []; my $found = 0; foreach my $nic (@{$net->{nics}}) { if (!$nic->{macaddr} || ($nic->{macaddr} ne $data)) { push @$na, $nic; } else { $found = 1; } } if ($found) { $net->{nics} = $na; $cc = PVE::QemuServer::print_vlan ($net); my $settings = { $vlan => $cc }; eval { $conn->vmconfig_set ($cid, $veid, 'qemu', $settings)->result; }; $udat{popup_error} = $@ if $@; last; } } } print OUT $out; return; } if ($form->action eq 'setcdrom') { if (!$fdat{device}) { $udat{popup_error} = __("Please select a device"); } else { eval { $conn->vmconfig_setcdrom ($cid, $veid, $fdat{device}, $fdat{cdrom})->result; }; if ($@) { $udat{popup_error} = $@; } else { $udat{redirect} = $fdat{__uri}; delete ($fdat{edit}); } } } elsif ($form->action eq 'add') { if ($fdat{itype} eq 'disk') { if (!$fdat{unuseddevice}) { $udat{popup_error} = __("Please select a device"); } else { my $settings = { $fdat{unuseddevice} => $fdat{unused} }; eval { $conn->vmconfig_set ($cid, $veid, 'qemu', $settings)->result; }; if ($@) { $udat{popup_error} = $@; } else { $udat{redirect} = $fdat{__uri}; delete ($fdat{edit}); } } } elsif ($fdat{itype} eq 'cdrom') { if (!$fdat{device}) { $udat{popup_error} = __("Please select a device"); } else { my $settings = { $fdat{device} => "$fdat{cdrom},media=cdrom" }; eval { $conn->vmconfig_set ($cid, $veid, 'qemu', $settings)->result; }; if ($@) { $udat{popup_error} = $@; } else { $udat{redirect} = $fdat{__uri}; delete ($fdat{edit}); } } } else { delete ($fdat{edit}); } } elsif ($form->action eq 'create') { if ($fdat{itype} eq 'nic') { my $vlan = $fdat{bridge} || 'vlan0'; my $cc = $vmconf->{$vlan}; my $network = "$fdat{model}=$fdat{mac}"; $cc = $cc ? "$cc,$network" : $network; my $settings = { $vlan => $cc }; eval { check_field (__("MAC Address"), $fdat{mac}, 'NOTEMPTY', 'NOWHITESPACES', 'MAC'); $conn->vmconfig_set ($cid, $veid, 'qemu', $settings)->result; }; if ($@) { $udat{popup_error} = $@; } else { $udat{redirect} = $fdat{__uri}; } } elsif ($fdat{itype} eq 'disk') { if (!$fdat{device}) { $udat{popup_error} = __("Please select a device"); } else { eval { check_field (__("Disk Storage"), $fdat{diskstore}, 'NOTEMPTY'); my $scfg = PVE::Storage::storage_config ($stcfg, $fdat{diskstore}); my $storagespec; if ($scfg->{type} eq 'iscsi') { check_field (__("ISCSI Device"), $fdat{image}, 'NOTEMPTY'); $storagespec = $fdat{image}; } else { check_field (__("Disk space"), $fdat{hdsize}, 'NOTEMPTY', 'FLOAT'); $storagespec = "$fdat{diskstore}:$fdat{hdsize},format=$fdat{format}"; } my $settings = { $fdat{device} => $storagespec}; $conn->vmconfig_set ($cid, $veid, 'qemu', $settings)->result; }; if ($@) { $udat{popup_error} = $@; } else { $udat{redirect} = $fdat{__uri}; } } } else { delete ($fdat{edit}); } } if ($fdat{edit}) { $out .= $form->create_header($fdat{__uri}); my $create = ($fdat{edit} == 2); if ($fdat{itype} eq 'disk') { my $devsel1 = device_list ($vmconf, $fdat{disktype} || 'ide'); my $dtypes = [['ide', 'IDE'], ['scsi', 'SCSI'], ['virtio', 'VIRTIO']]; $fdat{diskstore} = $stinfo->{images_default} if !$fdat{diskstore}; my $diskstorelist = PVE::HTMLUtils::storage_format_storage_list ($stinfo); my ($defformat, $formatlist) = PVE::Storage::storage_default_format ($stcfg, $fdat{diskstore}); $fdat{format} = $defformat if !$fdat{format} || !grep {$_ eq $fdat{format}} @$formatlist; $grid->add_row (__("Disk Storage") . ':', $form->create_element ('diskstore', 'dynamicdropdown', $fdat{diskstore}, $diskstorelist), __("Image Format") . ':', $form->create_element ('format', 'dropdown', $fdat{format}, $formatlist)); my @ea; my $scfg = PVE::Storage::storage_config ($stcfg, $fdat{diskstore}); if ($scfg && $scfg->{type} eq 'iscsi') { my $tmp = $rconn->storage_list_volumes ($fdat{diskstore})->result; my $iscsilist = PVE::HTMLUtils::storage_format_volume_list_iscsi ($stcfg, $tmp); push @ea, __("ISCSI Devive") . ':', $form->create_element ('image', 'dropdown', $fdat{image}, $iscsilist); } else { push @ea, __("Disk space") . ' (GB):', $form->create_element ('hdsize', 'float', $fdat{hdsize} || 32); } $grid->add_row (@ea, __("Bus") . ':', $form->create_element ('disktype', 'dynamicdropdown', $fdat{disktype}, $dtypes)); $grid->add_row ('', '', __("Device") . ':', $form->create_element ('device', 'dropdown', $fdat{device}, $devsel1)); my $html = $grid->html(); $html .= "
" . $form->create_cmdbutton ('create'); $out .= PVE::HTMLUtils::create_statusframe (undef, __("Create a new virtual disk"), undef, $html); my $di = $vmconf->{diskinfo} || {}; my $unused; my $ddown; PVE::Storage::foreach_volid ($imagelist, sub { my ($volid, $sid, $volname, $info) = @_; return if $used_volids->{$volid}; $unused = 1; my $size = int ($info->{size} / (1024*1024)); push @$ddown, [ $volid, "$volid (${size}GB)" ]; }); if ($unused) { $out .= "
\n"; my $devsel2 = device_list ($vmconf, $fdat{unuseddisktype} || 'ide'); $grid->add_row (__("Existing Disks"), $form->create_element ('unused', 'dropdown', $fdat{unused}, $ddown), __("Bus") . ':', $form->create_element ('unuseddisktype', 'dynamicdropdown', $fdat{unuseddisktype}, $dtypes)); $grid->add_row ('', '', __("Device"), $form->create_element ('unuseddevice', 'dropdown', $fdat{unuseddevice}, $devsel2)); my $html = $grid->html(); $html .= "
" . $form->create_cmdbutton ('add'); $out .= PVE::HTMLUtils::create_statusframe (undef, __("Use an existing virtual disk"), undef, $html); } } elsif ($fdat{itype} eq 'cdrom') { my $devsel1 = device_list ($vmconf, $fdat{disktype} || 'ide'); my $dtypes = [['ide', 'IDE'], ['scsi', 'SCSI']]; if (!$create) { my $ds = $fdat{aa}; die "no such cdrom drive '$ds'\n" if !($ds && $vmconf->{$ds}); $ds =~ m/^(ide|scsi|virtio)\d+$/ || die "got strange cdrom device '$ds'\n"; $fdat{disktype} = uc($1); $fdat{device} = $ds; if (my $disk = PVE::QemuServer::parse_drive ($ds, $vmconf->{$ds})) { if ($disk->{file} eq 'none' || $disk->{file} eq 'cdrom') { $fdat{cdrom} = $disk->{file} if !$fdat{cdrom}; } else { my ($sid, $volname) = PVE::Storage::parse_volume_id ($disk->{file}); $fdat{isostore} = $sid if !$fdat{isostore}; $fdat{cdrom} = $disk->{file} if !$fdat{cdrom}; } } } if (!$fdat{isostore} || !PVE::Storage::storage_config ($stcfg, $fdat{isostore}, 1)) { $fdat{isostore} = $stinfo->{iso_default}; } my $tmp = $rconn->storage_list_iso ($fdat{isostore})->result; my $medialist = PVE::HTMLUtils::storage_format_iso_list ($stcfg, $tmp); # symbolic name for cdrom, because cdrom path can be different at each node unshift @$medialist, ['cdrom', 'cdrom device']; unshift @$medialist, ['none', 'none (eject)']; my $isostorelist = PVE::HTMLUtils::storage_format_storage_list($stinfo, 'iso'); $grid->add_row (__("ISO Storage") . ':', $form->create_element ('isostore', 'dynamicdropdown', $fdat{isostore}, $isostorelist), __("Bus") . ':', $form->create_element ('disktype', $create ? 'dynamicdropdown' : 'rotext', $fdat{disktype}, $dtypes)); $grid->add_row (__("CDROM") . ':', $form->create_element ('cdrom', 'dropdown', $fdat{cdrom} || 'none', $medialist), __("Device"), $form->create_element ('device', $create ? 'dropdown' : 'rotext', $fdat{device}, $devsel1)); my $html = $grid->html(); $html .= "
"; if ($create) { $html .= $form->create_cmdbutton ('add'); } else { $html .= $form->create_cmdbutton ('setcdrom', 'apply'); } my $title = $create ? __("Add a CD-ROM drive") : __("Change CD-ROM"); $out .= PVE::HTMLUtils::create_statusframe (undef, $title, undef, $html); } elsif ($fdat{itype} eq 'nic') { my $nics = PVE::QemuServer::nic_models (); $fdat{mac} = PVE::QemuServer::random_ether_addr() if !$fdat{mac}; my $bridges = PVE::Utils::get_bridges (); my $brg; foreach my $bn (@$bridges) { if ($bn =~ m/^vmbr([0-9]{1,4})$/) { push @$brg, [ "vlan$1", $bn ]; } } push @$brg, [ "vlanu", "NAT (User Mode Network)" ]; $grid->add_row (__("Model"), $form->create_element ('model', 'dropdown', $fdat{model}, $nics), __("Bridge") . ':', $form->create_element ('bridge', 'dropdown', 'vmbr0', $brg)); $grid->add_row ('', '', __("MAC Address") . ':', $form->create_element ('mac', 'text', $fdat{mac})); my $html = $grid->html(); $html .= "
" . $form->create_cmdbutton ('create'); $out .= PVE::HTMLUtils::create_statusframe (undef, __("Add a network card"), undef, $html); } $out .= $form->create_element("edit", "hidden", $fdat{edit}); $out .= $form->create_element("itype", "hidden", $fdat{itype}); $out .= $form->create_element("aa", "hidden", $fdat{aa}) if !$create; $out .= $form->create_footer(); print OUT $out; return; } $out .= $form->create_header($fdat{__uri}); my $ddown = PVE::HTMLDropDown->new (); $ddown->add_item ("menu0", "?itype=disk&edit=2", __("Add a hard disk")); $ddown->add_item ("menu0", "?itype=nic&edit=2", __("Add an ethernet device")); $ddown->add_item ("menu0", "?itype=cdrom&edit=2", __("Add a CD-ROM drive")); $ddown->add_item ('menudisk', "?action=deldisk", __('Delete')); $ddown->add_item ('menuunused', "?action=delunused", __('Delete from disk')); $ddown->add_item ('menunic', "?action=delnic", __('Delete')); $ddown->add_item ('menucdrom', "?itype=cdrom&edit=1", __('Edit')); $ddown->add_item ('menucdrom', "?action=delcdrom", __('Delete')); $out .= $ddown->out_dropdown_menu ("menu0"); $out .= "

" . $ddown->out_symbol("menu0", "iarrdown") . " " . sprintf(__("Hardware device list for VM %s") , $veid) . "


"; $out .= $ddown->out_dropdown_menu ("menudisk"); $out .= $ddown->out_dropdown_menu ("menuunused"); $out .= $ddown->out_dropdown_menu ("menunic"); $out .= $ddown->out_dropdown_menu ("menucdrom"); my $header_disk = ['1', '20px', ' ', '1', '60px', __("Bus"), '1', '60px', __('Device'), '1', '60px', __('Size') . " (GB)", '1', '500px', __("Volume ID")]; my $table = PVE::HTMLTable->new ([]); $table->add_headline ($header_disk); my $found = 0; foreach my $ds (@disks) { next if !defined ($vmconf->{$ds}); my $disk = PVE::QemuServer::parse_drive ($ds, $vmconf->{$ds}); next if !$disk || PVE::QemuServer::drive_is_cdrom ($disk); my $size = '-'; if ($vmconf->{diskinfo} && $vmconf->{diskinfo}->{$ds}) { $size = sprintf "%.2f", $vmconf->{diskinfo}->{$ds}->{disksize}; } my $devinfo = PVE::QemuServer::disk_devive_info ($ds); my $menu = $ddown->out_symbol ('menudisk', '', "&aa=$ds"); $table->add_row ('' ,$menu, uc($devinfo->{bus}), $devinfo->{desc}, $size, $disk->{file}); $found = 1; } my $html; if ($found) { $html = $table->out_table (); } my $header_unused = ['1', '20px', ' ', '1', '60px', __('Size') . " (GB)", '1', '660px', __("Unused disk images"), ]; $table = PVE::HTMLTable->new ([]); $table->add_headline ($header_unused); my $ufound = 0; my $uhtml = "
"; PVE::Storage::foreach_volid ($imagelist, sub { my ($volid, $sid, $volname, $info) = @_; return if $used_volids->{$volid}; my $menu = $ddown->out_symbol ('menuunused', '', "&aa=$volid"); my $size = sprintf "%.2f", $info->{size} / (1024*1024); $table->add_row ('' , $menu, $size, $volid); $ufound = 1; }); $uhtml .= $table->out_table (); $html .= $uhtml if $ufound; if (!$found && !$ufound) { $html = "no devices"; } $out .= PVE::HTMLUtils::create_statusframe (undef, __("Hard disks"), undef, $html); my $header_cdrom = ['1', '20px', ' ', '1', '60px', __("Bus"), '1', '60px', __('Device'), '1', '60px', ' ', '1', '500px', __("Media"), ]; my $table = PVE::HTMLTable->new ([]); $table->add_headline ($header_cdrom); $found = 0; foreach my $ds (@disks) { next if !defined ($vmconf->{$ds}); my $disk = PVE::QemuServer::parse_drive ($ds, $vmconf->{$ds}); next if !PVE::QemuServer::drive_is_cdrom ($disk); my $menu = $ddown->out_symbol ('menucdrom', '', "&aa=$ds"); my $devinfo = PVE::QemuServer::disk_devive_info ($ds); my $key = "cdrom-$ds"; my $volid = defined ($fdat{$key}) ? $fdat{$key} : $disk->{file} || 'none'; $table->set_row_link ("?itype=cdrom&edit=1&aa=$ds"); $table->add_row ('' ,$menu, uc($devinfo->{bus}), $devinfo->{desc}, '', $volid); $found = 1; } if ($found) { $html = $table->out_table (); } else { $html = "no devices"; } $out .= '
'; $out .= PVE::HTMLUtils::create_statusframe (undef, __("CD-ROM drives"), undef, $html); my $header_nic = ['1', '20px', ' ', '1', '60px', __("Model"), '1', '60px', __('Bridge'), '1', '60px', ' ', '1', '500px', __("MAC address")]; my $table = PVE::HTMLTable->new ([]); $table->add_headline ($header_nic); $found = 0; foreach my $key (keys %$vmconf) { next if $key !~ m/^vlan(\d+|u)$/; my $id = $1; next if !defined ($vmconf->{$key}); my $net = PVE::QemuServer::parse_vlan ($vmconf->{$key}); next if !$net || !$net->{nics}; my $brname = $id eq 'u' ? "NAT" : "vmbr$id"; foreach my $nic (sort { $a->{model} cmp $b->{model} } @{$net->{nics}}) { next if !$nic->{model} || !$nic->{macaddr}; my $menu = $ddown->out_symbol ('menunic', '', "&aa=$nic->{macaddr}"); $table->add_row ('' ,$menu, $nic->{model}, $brname, '', $nic->{macaddr}); $found = 1; } } if ($found) { $html = $table->out_table (); } else { $html = "no devices"; } $out .= '
'; $out .= PVE::HTMLUtils::create_statusframe (undef, __("Ethernet devices"), undef, $html); $out .= $form->create_footer(); print OUT $out; return; -]