mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-08-15 22:00:18 +00:00
ui: lxc/qemu: add disk reassign and action submenu
For the new HDReassign component, we follow the approach of HDMove to have one componend for qemu and lxc. To avoid button clutter, a new "Disk/Volume action" button is introduced. It holds the Move, Reassign and Resize buttons in a sub-menu. Signed-off-by: Aaron Lauterer <a.lauterer@proxmox.com> Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com> Reviewed-by: Fabian Ebner <f.ebner@proxmox.com>
This commit is contained in:
parent
000b5537fc
commit
a8d854afe2
@ -213,6 +213,7 @@ JSSRC= \
|
||||
qemu/HDEfi.js \
|
||||
qemu/HDTPM.js \
|
||||
qemu/HDMove.js \
|
||||
qemu/HDReassign.js \
|
||||
qemu/HDResize.js \
|
||||
qemu/HardwareView.js \
|
||||
qemu/IPConfigEdit.js \
|
||||
|
@ -162,7 +162,8 @@ Ext.define('PVE.lxc.RessourceView', {
|
||||
});
|
||||
};
|
||||
|
||||
var run_move = function(b, e, rec) {
|
||||
let run_move = function() {
|
||||
let rec = me.selModel.getSelection()[0];
|
||||
if (!rec) {
|
||||
return;
|
||||
}
|
||||
@ -179,6 +180,24 @@ Ext.define('PVE.lxc.RessourceView', {
|
||||
win.on('destroy', me.reload, me);
|
||||
};
|
||||
|
||||
let run_reassign = function() {
|
||||
let rec = me.selModel.getSelection()[0];
|
||||
if (!rec) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ext.create('PVE.window.HDReassign', {
|
||||
disk: rec.data.key,
|
||||
nodename: nodename,
|
||||
autoShow: true,
|
||||
vmid: vmid,
|
||||
type: 'lxc',
|
||||
listeners: {
|
||||
destroy: () => me.reload(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
var edit_btn = new Proxmox.button.Button({
|
||||
text: gettext('Edit'),
|
||||
selModel: me.selModel,
|
||||
@ -193,13 +212,6 @@ Ext.define('PVE.lxc.RessourceView', {
|
||||
handler: function() { me.run_editor(); },
|
||||
});
|
||||
|
||||
var resize_btn = new Proxmox.button.Button({
|
||||
text: gettext('Resize disk'),
|
||||
selModel: me.selModel,
|
||||
disabled: true,
|
||||
handler: run_resize,
|
||||
});
|
||||
|
||||
var remove_btn = new Proxmox.button.Button({
|
||||
text: gettext('Remove'),
|
||||
defaultText: gettext('Remove'),
|
||||
@ -238,14 +250,41 @@ Ext.define('PVE.lxc.RessourceView', {
|
||||
},
|
||||
});
|
||||
|
||||
var move_btn = new Proxmox.button.Button({
|
||||
text: gettext('Move Volume'),
|
||||
let move_menuitem = new Ext.menu.Item({
|
||||
text: gettext('Move Storage'),
|
||||
tooltip: gettext('Move volume to another storage'),
|
||||
iconCls: 'fa fa-database',
|
||||
selModel: me.selModel,
|
||||
disabled: true,
|
||||
dangerous: true,
|
||||
handler: run_move,
|
||||
});
|
||||
|
||||
let reassign_menuitem = new Ext.menu.Item({
|
||||
text: gettext('Reassign Owner'),
|
||||
tooltip: gettext('Reassign volume to another CT'),
|
||||
iconCls: 'fa fa-cube',
|
||||
handler: run_reassign,
|
||||
reference: 'reassing_item',
|
||||
});
|
||||
|
||||
let resize_menuitem = new Ext.menu.Item({
|
||||
text: gettext('Resize'),
|
||||
iconCls: 'fa fa-plus',
|
||||
selModel: me.selModel,
|
||||
handler: run_resize,
|
||||
});
|
||||
|
||||
let volumeaction_btn = new Proxmox.button.Button({
|
||||
text: gettext('Volume Action'),
|
||||
disabled: true,
|
||||
menu: {
|
||||
items: [
|
||||
move_menuitem,
|
||||
reassign_menuitem,
|
||||
resize_menuitem,
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
var revert_btn = new PVE.button.PendingRevert();
|
||||
|
||||
var set_button_status = function() {
|
||||
@ -254,7 +293,7 @@ Ext.define('PVE.lxc.RessourceView', {
|
||||
if (!rec) {
|
||||
edit_btn.disable();
|
||||
remove_btn.disable();
|
||||
resize_btn.disable();
|
||||
volumeaction_btn.disable();
|
||||
revert_btn.disable();
|
||||
return;
|
||||
}
|
||||
@ -263,7 +302,8 @@ Ext.define('PVE.lxc.RessourceView', {
|
||||
var rowdef = rows[key];
|
||||
|
||||
var pending = rec.data.delete || me.hasPendingChanges(key);
|
||||
let isDisk = key === 'rootfs' || key.match(/^(mp|unused)\d+/);
|
||||
let isRootFS = key === 'rootfs';
|
||||
let isDisk = isRootFS || key.match(/^(mp|unused)\d+/);
|
||||
var isUnusedDisk = key.match(/^unused\d+/);
|
||||
var isUsedDisk = isDisk && !isUnusedDisk;
|
||||
|
||||
@ -276,9 +316,12 @@ Ext.define('PVE.lxc.RessourceView', {
|
||||
}
|
||||
edit_btn.setDisabled(noedit);
|
||||
|
||||
remove_btn.setDisabled(!isDisk || rec.data.key === 'rootfs' || !diskCap || pending);
|
||||
resize_btn.setDisabled(!isDisk || !diskCap || isUnusedDisk);
|
||||
move_btn.setDisabled(!isDisk || !diskCap);
|
||||
volumeaction_btn.setDisabled(!isDisk || !diskCap);
|
||||
move_menuitem.setDisabled(isUnusedDisk);
|
||||
reassign_menuitem.setDisabled(isRootFS);
|
||||
resize_menuitem.setDisabled(isUnusedDisk);
|
||||
|
||||
remove_btn.setDisabled(!isDisk || isRootFS || !diskCap || pending);
|
||||
revert_btn.setDisabled(!pending);
|
||||
|
||||
remove_btn.setText(isUsedDisk ? remove_btn.altText : remove_btn.defaultText);
|
||||
@ -340,8 +383,7 @@ Ext.define('PVE.lxc.RessourceView', {
|
||||
},
|
||||
edit_btn,
|
||||
remove_btn,
|
||||
resize_btn,
|
||||
move_btn,
|
||||
volumeaction_btn,
|
||||
revert_btn,
|
||||
],
|
||||
rows: rows,
|
||||
|
272
www/manager6/qemu/HDReassign.js
Normal file
272
www/manager6/qemu/HDReassign.js
Normal file
@ -0,0 +1,272 @@
|
||||
Ext.define('PVE.window.HDReassign', {
|
||||
extend: 'Proxmox.window.Edit',
|
||||
mixins: ['Proxmox.Mixin.CBind'],
|
||||
|
||||
resizable: false,
|
||||
modal: true,
|
||||
width: 350,
|
||||
border: false,
|
||||
layout: 'fit',
|
||||
showReset: false,
|
||||
showProgress: true,
|
||||
method: 'POST',
|
||||
|
||||
viewModel: {
|
||||
data: {
|
||||
mpType: '',
|
||||
},
|
||||
formulas: {
|
||||
mpMaxCount: get => get('mpType') === 'mp'
|
||||
? PVE.Utils.mp_counts.mps - 1
|
||||
: PVE.Utils.mp_counts.unused - 1,
|
||||
},
|
||||
},
|
||||
|
||||
cbindData: function() {
|
||||
let me = this;
|
||||
return {
|
||||
vmid: me.vmid,
|
||||
disk: me.disk,
|
||||
isQemu: me.type === 'qemu',
|
||||
nodename: me.nodename,
|
||||
url: () => {
|
||||
let endpoint = me.type === 'qemu' ? 'move_disk' : 'move_volume';
|
||||
return `/nodes/${me.nodename}/${me.type}/${me.vmid}/${endpoint}`;
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
cbind: {
|
||||
title: get => get('isQemu') ? gettext('Reassign disk') : gettext('Reassign volume'),
|
||||
submitText: get => get('title'),
|
||||
qemu: '{isQemu}',
|
||||
url: '{url}',
|
||||
},
|
||||
|
||||
getValues: function() {
|
||||
let me = this;
|
||||
let values = me.formPanel.getForm().getValues();
|
||||
|
||||
let params = {
|
||||
vmid: me.vmid,
|
||||
'target-vmid': values.targetVmid,
|
||||
};
|
||||
|
||||
params[me.qemu ? 'disk' : 'volume'] = me.disk;
|
||||
|
||||
if (me.qemu) {
|
||||
params['target-disk'] = `${values.controller}${values.deviceid}`;
|
||||
} else {
|
||||
params['target-volume'] = `${values.mpType}${values.mpId}`;
|
||||
}
|
||||
return params;
|
||||
},
|
||||
|
||||
controller: {
|
||||
xclass: 'Ext.app.ViewController',
|
||||
|
||||
initViewModel: function(model) {
|
||||
let view = this.getView();
|
||||
let mpTypeValue = view.disk.match(/^unused\d+/) ? 'unused' : 'mp';
|
||||
model.set('mpType', mpTypeValue);
|
||||
},
|
||||
|
||||
onMpTypeChange: function(value) {
|
||||
this.getView().getViewModel().set('mpType', value.getValue());
|
||||
this.getView().lookup('mpIdSelector').validate();
|
||||
},
|
||||
|
||||
onTargetVMChange: function(f, vmid) {
|
||||
let me = this;
|
||||
let view = me.getView();
|
||||
let diskSelector = view.lookup('diskSelector');
|
||||
if (!vmid) {
|
||||
diskSelector.setVMConfig(null);
|
||||
me.VMConfig = null;
|
||||
return;
|
||||
}
|
||||
|
||||
let type = view.qemu ? 'qemu' : 'lxc';
|
||||
|
||||
let url = `/nodes/${view.nodename}/${type}/${vmid}/config`;
|
||||
Proxmox.Utils.API2Request({
|
||||
url: url,
|
||||
method: 'GET',
|
||||
failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
|
||||
success: function(response, options) {
|
||||
if (view.qemu) {
|
||||
diskSelector.setVMConfig(response.result.data);
|
||||
diskSelector.setDisabled(false);
|
||||
} else {
|
||||
let mpIdSelector = view.lookup('mpIdSelector');
|
||||
let mpType = view.lookup('mpType');
|
||||
|
||||
view.VMConfig = response.result.data;
|
||||
|
||||
mpIdSelector.setValue(
|
||||
PVE.Utils.nextFreeMP(
|
||||
view.getViewModel().get('mpType'),
|
||||
view.VMConfig,
|
||||
).id,
|
||||
);
|
||||
|
||||
mpType.setDisabled(false);
|
||||
mpIdSelector.setDisabled(false);
|
||||
mpIdSelector.validate();
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
items: [
|
||||
{
|
||||
xtype: 'form',
|
||||
reference: 'moveFormPanel',
|
||||
border: false,
|
||||
fieldDefaults: {
|
||||
labelWidth: 100,
|
||||
anchor: '100%',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
xtype: 'displayfield',
|
||||
name: 'sourceDisk',
|
||||
fieldLabel: gettext('Source'),
|
||||
cbind: {
|
||||
name: get => get('isQemu') ? 'disk' : 'volume',
|
||||
value: '{disk}',
|
||||
},
|
||||
allowBlank: false,
|
||||
},
|
||||
{
|
||||
xtype: 'vmComboSelector',
|
||||
reference: 'targetVMID',
|
||||
name: 'targetVmid',
|
||||
allowBlank: false,
|
||||
fieldLabel: gettext('Target'),
|
||||
bind: {
|
||||
value: '{targetVMID}',
|
||||
},
|
||||
store: {
|
||||
model: 'PVEResources',
|
||||
autoLoad: true,
|
||||
sorters: 'vmid',
|
||||
cbind: {}, // for nested cbinds
|
||||
filters: [
|
||||
{
|
||||
property: 'type',
|
||||
cbind: {
|
||||
value: get => get('isQemu') ? 'qemu' : 'lxc',
|
||||
},
|
||||
},
|
||||
{
|
||||
property: 'node',
|
||||
cbind: {
|
||||
value: '{nodename}',
|
||||
},
|
||||
},
|
||||
{
|
||||
property: 'vmid',
|
||||
operator: '!=',
|
||||
cbind: {
|
||||
value: '{vmid}',
|
||||
},
|
||||
},
|
||||
{
|
||||
property: 'template',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
listeners: { change: 'onTargetVMChange' },
|
||||
},
|
||||
{
|
||||
xtype: 'pveControllerSelector',
|
||||
reference: 'diskSelector',
|
||||
withUnused: true,
|
||||
disabled: true,
|
||||
cbind: {
|
||||
hidden: '{!isQemu}',
|
||||
},
|
||||
},
|
||||
{
|
||||
xtype: 'container',
|
||||
layout: 'hbox',
|
||||
cbind: {
|
||||
hidden: '{isQemu}',
|
||||
disabled: '{isQemu}',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
xtype: 'pmxDisplayEditField',
|
||||
cbind: {
|
||||
editable: get => !get('disk').match(/^unused\d+/),
|
||||
value: get => get('disk').match(/^unused\d+/) ? 'unused' : 'mp',
|
||||
},
|
||||
disabled: true,
|
||||
name: 'mpType',
|
||||
reference: 'mpType',
|
||||
fieldLabel: gettext('Add as'),
|
||||
submitValue: true,
|
||||
flex: 4,
|
||||
editConfig: {
|
||||
xtype: 'proxmoxKVComboBox',
|
||||
name: 'mpTypeCombo',
|
||||
reference: 'mpTypeCombo',
|
||||
deleteEmpty: false,
|
||||
cbind: {
|
||||
hidden: '{isQemu}',
|
||||
},
|
||||
comboItems: [
|
||||
['mp', gettext('Mount Point')],
|
||||
['unused', gettext('Unused')],
|
||||
],
|
||||
listeners: { change: 'onMpTypeChange' },
|
||||
},
|
||||
},
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
name: 'mpId',
|
||||
reference: 'mpIdSelector',
|
||||
minValue: 0,
|
||||
flex: 1,
|
||||
allowBlank: false,
|
||||
validateOnChange: true,
|
||||
disabled: true,
|
||||
bind: {
|
||||
maxValue: '{mpMaxCount}',
|
||||
},
|
||||
validator: function(value) {
|
||||
let view = this.up('window');
|
||||
let type = view.getViewModel().get('mpType');
|
||||
if (Ext.isDefined(view.VMConfig[`${type}${value}`])) {
|
||||
return "Mount point is already in use.";
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
initComponent: function() {
|
||||
let me = this;
|
||||
|
||||
if (!me.nodename) {
|
||||
throw "no node name specified";
|
||||
}
|
||||
|
||||
if (!me.vmid) {
|
||||
throw "no VM ID specified";
|
||||
}
|
||||
|
||||
if (!me.type) {
|
||||
throw "no type specified";
|
||||
}
|
||||
|
||||
me.callParent();
|
||||
},
|
||||
});
|
@ -377,10 +377,56 @@ Ext.define('PVE.qemu.HardwareView', {
|
||||
handler: run_editor,
|
||||
});
|
||||
|
||||
let resize_btn = new Proxmox.button.Button({
|
||||
text: gettext('Resize disk'),
|
||||
let move_menuitem = new Ext.menu.Item({
|
||||
text: gettext('Move Storage'),
|
||||
tooltip: gettext('Move disk to another storage'),
|
||||
iconCls: 'fa fa-database',
|
||||
selModel: sm,
|
||||
handler: () => {
|
||||
let rec = sm.getSelection()[0];
|
||||
if (!rec) {
|
||||
return;
|
||||
}
|
||||
Ext.create('PVE.window.HDMove', {
|
||||
autoShow: true,
|
||||
disk: rec.data.key,
|
||||
nodename: nodename,
|
||||
vmid: vmid,
|
||||
listeners: {
|
||||
destroy: () => me.reload(),
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
let reassign_menuitem = new Ext.menu.Item({
|
||||
text: gettext('Reassign Owner'),
|
||||
tooltip: gettext('Reassign disk to another VM'),
|
||||
iconCls: 'fa fa-desktop',
|
||||
selModel: sm,
|
||||
handler: () => {
|
||||
let rec = sm.getSelection()[0];
|
||||
if (!rec) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ext.create('PVE.window.HDReassign', {
|
||||
autoShow: true,
|
||||
disk: rec.data.key,
|
||||
nodename: nodename,
|
||||
vmid: vmid,
|
||||
type: 'qemu',
|
||||
listeners: {
|
||||
destroy: () => me.reload(),
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
let resize_menuitem = new Ext.menu.Item({
|
||||
text: gettext('Resize'),
|
||||
iconCls: 'fa fa-plus',
|
||||
selModel: sm,
|
||||
disabled: true,
|
||||
handler: () => {
|
||||
let rec = sm.getSelection()[0];
|
||||
if (!rec) {
|
||||
@ -398,27 +444,19 @@ Ext.define('PVE.qemu.HardwareView', {
|
||||
},
|
||||
});
|
||||
|
||||
let move_btn = new Proxmox.button.Button({
|
||||
text: gettext('Move disk'),
|
||||
selModel: sm,
|
||||
let diskaction_btn = new Proxmox.button.Button({
|
||||
text: gettext('Disk Action'),
|
||||
disabled: true,
|
||||
handler: () => {
|
||||
var rec = sm.getSelection()[0];
|
||||
if (!rec) {
|
||||
return;
|
||||
}
|
||||
Ext.create('PVE.window.HDMove', {
|
||||
autoShow: true,
|
||||
disk: rec.data.key,
|
||||
nodename: nodename,
|
||||
vmid: vmid,
|
||||
listeners: {
|
||||
destroy: () => me.reload(),
|
||||
},
|
||||
});
|
||||
menu: {
|
||||
items: [
|
||||
move_menuitem,
|
||||
reassign_menuitem,
|
||||
resize_menuitem,
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
let remove_btn = new Proxmox.button.Button({
|
||||
text: gettext('Remove'),
|
||||
defaultText: gettext('Remove'),
|
||||
@ -544,8 +582,7 @@ Ext.define('PVE.qemu.HardwareView', {
|
||||
if (!rec) {
|
||||
remove_btn.disable();
|
||||
edit_btn.disable();
|
||||
resize_btn.disable();
|
||||
move_btn.disable();
|
||||
diskaction_btn.disable();
|
||||
revert_btn.disable();
|
||||
return;
|
||||
}
|
||||
@ -572,9 +609,15 @@ Ext.define('PVE.qemu.HardwareView', {
|
||||
edit_btn.setDisabled(
|
||||
deleted || !row.editor || isCloudInit || (isCDRom && !cdromCap) || (isDisk && !diskCap));
|
||||
|
||||
resize_btn.setDisabled(pending || !isUsedDisk || !diskCap);
|
||||
|
||||
move_btn.setDisabled(pending || !(isUsedDisk || isEfi || tpmMoveable) || !diskCap);
|
||||
diskaction_btn.setDisabled(
|
||||
pending ||
|
||||
!diskCap ||
|
||||
isCloudInit ||
|
||||
!(isDisk || isEfi || tpmMoveable),
|
||||
);
|
||||
move_menuitem.setDisabled(isUnusedDisk);
|
||||
reassign_menuitem.setDisabled(pending || (isEfi || tpmMoveable));
|
||||
resize_menuitem.setDisabled(pending || !isUsedDisk);
|
||||
|
||||
revert_btn.setDisabled(!pending);
|
||||
};
|
||||
@ -679,8 +722,7 @@ Ext.define('PVE.qemu.HardwareView', {
|
||||
},
|
||||
remove_btn,
|
||||
edit_btn,
|
||||
resize_btn,
|
||||
move_btn,
|
||||
diskaction_btn,
|
||||
revert_btn,
|
||||
],
|
||||
rows: rows,
|
||||
|
Loading…
Reference in New Issue
Block a user