[pve-devel] [PATCH v2 manager 2/4] lxc: create/edit/remove mountpoints
Wolfgang Bumiller
w.bumiller at proxmox.com
Tue Feb 23 15:00:20 CET 2016
---
www/manager/lxc/ResourceEdit.js | 338 ++++++++++++++++++++++++++++++++++++++++
www/manager/lxc/Resources.js | 77 ++++++++-
2 files changed, 413 insertions(+), 2 deletions(-)
diff --git a/www/manager/lxc/ResourceEdit.js b/www/manager/lxc/ResourceEdit.js
index 3bff060..327c065 100644
--- a/www/manager/lxc/ResourceEdit.js
+++ b/www/manager/lxc/ResourceEdit.js
@@ -35,6 +35,65 @@ Ext.define('PVE.lxc.CPUEdit', {
}
});
+Ext.define('PVE.lxc.MountPointEdit', {
+ extend: 'PVE.window.Edit',
+
+ initComponent : function() {
+ var me = this;
+
+ var nodename = me.pveSelNode.data.node;
+ if (!nodename) {
+ throw "no node name specified";
+ }
+
+ var unused = me.confid && me.confid.match(/^unused\d+$/);
+
+ me.create = me.confid ? unused : true;
+
+ var ipanel = Ext.create('PVE.lxc.MountPointInputPanel', {
+ confid: me.confid,
+ nodename: nodename,
+ unused: unused,
+ create: me.create,
+ });
+
+ var subject;
+ if (unused) {
+ subject = gettext('Unused Mount Point');
+ } else if (me.create) {
+ subject = gettext('Mount Point');
+ } else {
+ subject = gettext('Mount Point') + ' (' + me.confid + ')';
+ }
+
+ Ext.apply(me, {
+ subject: subject,
+ items: ipanel,
+ });
+
+ me.callParent();
+
+ me.load({
+ success: function(response, options) {
+ ipanel.setVMConfig(response.result.data);
+ if (me.confid) {
+ var value = response.result.data[me.confid];
+ var mp = PVE.Parser.parseLxcMountPoint(value);
+
+ if (!mp) {
+ Ext.Msg.alert(gettext('Error'), gettext('Unable to parse mount point options'));
+ me.close();
+ return;
+ }
+
+ ipanel.setMountPoint(mp);
+ me.isValid(); // trigger validation
+ }
+ }
+ });
+ }
+});
+
Ext.define('PVE.lxc.CPUInputPanel', {
extend: 'PVE.panel.InputPanel',
alias: 'widget.pveLxcCPUInputPanel',
@@ -120,3 +179,282 @@ Ext.define('PVE.lxc.MemoryInputPanel', {
me.callParent();
}
});
+
+Ext.define('PVE.lxc.MountPointInputPanel', {
+ extend: 'PVE.panel.InputPanel',
+ alias: 'widget.pveLxcMountPointInputPanel',
+
+ insideWizard: false,
+
+ unused: false, // ADD usused disk imaged
+
+ vmconfig: {}, // used to select usused disks
+
+ onGetValues: function(values) {
+ var me = this;
+
+ var confid = me.confid || values.mpsel;
+
+ if (me.unused) {
+ me.mpdata.file = me.vmconfig[values.unusedId];
+ confid = values.mpsel;
+ } else if (me.create) {
+ me.mpdata.file = values.storage + ':' + values.disksize;
+ }
+
+ if (confid !== 'rootfs')
+ me.mpdata.mp = values.mp;
+
+ if (values.ro)
+ me.mpdata.ro = 1;
+ else
+ delete me.mpdata.ro;
+
+ if (values.quota)
+ me.mpdata.quota = 1;
+ else
+ delete me.mpdata.quota;
+
+ if (values.acl === 'Default')
+ delete me.mpdata.acl;
+ else
+ me.mpdata.acl = values.acl;
+
+ var res = {};
+ res[confid] = PVE.Parser.printLxcMountPoint(me.mpdata);
+ return res;
+ },
+
+ setMountPoint: function(mp) {
+ var me = this;
+
+ me.mpdata = mp;
+ if (!Ext.isDefined(me.mpdata['acl'])) {
+ me.mpdata['acl'] = 'Default';
+ }
+
+ if (mp.type === 'bind') {
+ me.quota.setDisabled(true);
+ me.quota.setValue(false);
+ }
+
+ me.setValues(mp);
+ },
+
+ setVMConfig: function(vmconfig) {
+ var me = this;
+
+ me.vmconfig = vmconfig;
+
+ if (me.mpsel) {
+ for (var i = 0; i != 8; ++i) {
+ var name = "mp" + i;
+ if (!Ext.isDefined(vmconfig[name])) {
+ me.mpsel.setValue(name);
+ break;
+ }
+ }
+ }
+
+ if (me.unusedDisks) {
+ var disklist = [];
+ Ext.Object.each(vmconfig, function(key, value) {
+ if (key.match(/^unused\d+$/)) {
+ disklist.push([key, value]);
+ }
+ });
+ me.unusedDisks.store.loadData(disklist);
+ me.unusedDisks.setValue(me.confid);
+ }
+ },
+
+ setNodename: function(nodename) {
+ var me = this;
+ me.hdstoragesel.setNodename(nodename);
+ me.hdfilesel.setStorage(undefined, nodename);
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ var isroot = me.confid === 'rootfs';
+
+ me.mpdata = {};
+
+ me.column1 = [];
+
+ if (!me.confid || me.unused) {
+ var names = [];
+ for (var i = 0; i != 8; ++i) {
+ var name = 'mp' + i;
+ names.push([name, name]);
+ }
+ me.mpsel = Ext.create('PVE.form.KVComboBox', {
+ name: 'mpsel',
+ fieldLabel: gettext('Mount Point'),
+ matchFieldWidth: false,
+ allowBlank: false,
+ data: names,
+ validator: function(value) {
+ if (!me.rendered)
+ return;
+ if (Ext.isDefined(me.vmconfig[value]))
+ return "Mount point is already in use.";
+ return true;
+ },
+ listeners: {
+ change: function(field, value) {
+ field.validate();
+ }
+ },
+ });
+ me.column1.push(me.mpsel);
+ }
+
+ // we always have this around, but only visible when creating a new mp
+ // since this handles per-filesystem capabilities
+ me.hdstoragesel = Ext.create('PVE.form.StorageSelector', {
+ name: 'storage',
+ nodename: me.nodename,
+ fieldLabel: gettext('Storage'),
+ storageContent: 'rootdir',
+ allowBlank: false,
+ autoSelect: true,
+ hidden: me.unused || !me.create,
+ listeners: {
+ change: function(f, value) {
+ if (me.mpdata.type === 'bind') {
+ me.quota.setDisabled(true);
+ me.quota.setValue(false);
+ return;
+ }
+ var rec = f.store.getById(value);
+ if (rec.data.type === 'zfs' ||
+ rec.data.type === 'zfspool') {
+ me.quota.setDisabled(true);
+ me.quota.setValue(false);
+ } else {
+ me.quota.setDisabled(false);
+ }
+ if (me.unused || !me.create)
+ return;
+ if (rec.data.type === 'iscsi') {
+ me.hdfilesel.setStorage(value);
+ me.hdfilesel.setDisabled(false);
+ me.hdfilesel.setVisible(true);
+ me.hdsizesel.setDisabled(true);
+ me.hdsizesel.setVisible(false);
+ } else if (rec.data.type === 'lvm' ||
+ rec.data.type === 'lvmthin' ||
+ rec.data.type === 'rbd' ||
+ rec.data.type === 'sheepdog' ||
+ rec.data.type === 'zfs' ||
+ rec.data.type === 'zfspool') {
+ me.hdfilesel.setDisabled(true);
+ me.hdfilesel.setVisible(false);
+ me.hdsizesel.setDisabled(false);
+ me.hdsizesel.setVisible(true);
+ } else {
+ me.hdfilesel.setDisabled(true);
+ me.hdfilesel.setVisible(false);
+ me.hdsizesel.setDisabled(false);
+ me.hdsizesel.setVisible(true);
+ }
+ },
+ },
+ });
+ me.column1.push(me.hdstoragesel);
+
+ if (me.unused) {
+ me.unusedDisks = Ext.create('PVE.form.KVComboBox', {
+ name: 'unusedId',
+ fieldLabel: gettext('Disk image'),
+ matchFieldWidth: false,
+ listConfig: {
+ width: 350
+ },
+ data: [],
+ allowBlank: false,
+ listeners: {
+ change: function(f, value) {
+ // make sure our buttons are enabled/disabled when switching
+ // between images on different storages:
+ var disk = me.vmconfig[value];
+ var storage = disk.split(':')[0];
+ me.hdstoragesel.setValue(storage);
+ },
+ },
+ });
+ me.column1.push(me.unusedDisks);
+ } else if (me.create) {
+ me.hdfilesel = Ext.create('PVE.form.FileSelector', {
+ name: 'file',
+ nodename: me.nodename,
+ storageContent: 'images',
+ fieldLabel: gettext('Disk image'),
+ disabled: true,
+ hidden: true,
+ allowBlank: false
+ });
+ me.hdsizesel = Ext.createWidget('numberfield', {
+ name: 'disksize',
+ minValue: 0.1,
+ maxValue: 128*1024,
+ decimalPrecision: 3,
+ value: '8',
+ step: 1,
+ fieldLabel: gettext('Disk size') + ' (GB)',
+ allowBlank: false
+ });
+ me.column1.push(me.hdfilesel);
+ me.column1.push(me.hdsizesel);
+ } else {
+ me.column1.push({
+ xtype: 'textfield',
+ disabled: true,
+ submitValue: false,
+ fieldLabel: gettext('Disk image'),
+ name: 'file'
+ });
+ }
+
+ me.quota = Ext.createWidget('pvecheckbox', {
+ name: 'quota',
+ defaultValue: 0,
+ fieldLabel: gettext('Enable quota'),
+ });
+
+ me.column2 = [
+ {
+ xtype: 'pvecheckbox',
+ name: 'ro',
+ defaultValue: 0,
+ fieldLabel: gettext('Read-only'),
+ hidden: me.insideWizard,
+ },
+ {
+ xtype: 'pveKVComboBox',
+ name: 'acl',
+ fieldLabel: gettext('ACLs'),
+ data: [['Default', 'Default'], ['1', 'On'], ['0', 'Off']],
+ value: 'Default',
+ allowBlank: true,
+ },
+ me.quota,
+ ];
+
+ if (!isroot) {
+ me.column2.push({
+ xtype: 'textfield',
+ name: 'mp',
+ value: '',
+ emptyText: gettext('Path'),
+ allowBlank: false,
+ hidden: isroot,
+ fieldLabel: gettext('/some/path'),
+ });
+ };
+
+ me.callParent();
+ }
+});
diff --git a/www/manager/lxc/Resources.js b/www/manager/lxc/Resources.js
index 7149ff0..396b6f9 100644
--- a/www/manager/lxc/Resources.js
+++ b/www/manager/lxc/Resources.js
@@ -35,6 +35,8 @@ Ext.define('PVE.lxc.RessourceView', {
var caps = Ext.state.Manager.get('GuiCap');
+ var mpeditor = caps.vms['VM.Config.Disk'] ? 'PVE.lxc.MountPointEdit' : undefined;
+
var rows = {
memory: {
header: gettext('Memory'),
@@ -77,6 +79,7 @@ Ext.define('PVE.lxc.RessourceView', {
rootfs: {
header: gettext('Root Disk'),
defaultValue: PVE.Utils.noneText,
+ editor: mpeditor,
tdCls: 'pve-itype-icon-storage'
}
};
@@ -86,10 +89,21 @@ Ext.define('PVE.lxc.RessourceView', {
rows[confid] = {
group: 1,
tdCls: 'pve-itype-icon-storage',
+ editor: mpeditor,
header: gettext('Mount Point') + ' (' + confid +')',
};
}
+ for (i = 0; i < 8; i++) {
+ confid = "unused" + i;
+ rows[confid] = {
+ group: 1,
+ tdCls: 'pve-itype-icon-storage',
+ editor: mpeditor,
+ header: gettext('Unused Disk') + ' ' + i,
+ };
+ }
+
var reload = function() {
me.rstore.load();
};
@@ -138,6 +152,23 @@ Ext.define('PVE.lxc.RessourceView', {
win.on('destroy', reload);
};
+ var run_remove = function(b, e, rec) {
+ PVE.Utils.API2Request({
+ url: '/api2/extjs/' + baseurl,
+ waitMsgTarget: me,
+ method: 'PUT',
+ params: {
+ 'delete': rec.data.key
+ },
+ callback: function() {
+ reload();
+ },
+ failure: function (response, opts) {
+ Ext.Msg.alert('Error', response.htmlStatus);
+ }
+ });
+ };
+
var edit_btn = new PVE.button.Button({
text: gettext('Edit'),
selModel: sm,
@@ -159,12 +190,30 @@ Ext.define('PVE.lxc.RessourceView', {
handler: run_resize
});
+ var remove_btn = new PVE.button.Button({
+ text: gettext('Remove'),
+ selModel: sm,
+ disabled: true,
+ dangerous: true,
+ confirmMsg: function(rec) {
+ var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
+ "'" + me.renderKey(rec.data.key, {}, rec) + "'");
+ if (rec.data.key.match(/^unused\d+$/)) {
+ msg += " " + gettext('This will permanently erase all image data.');
+ }
+
+ return msg;
+ },
+ handler: run_remove
+ });
+
var set_button_status = function() {
var sm = me.getSelectionModel();
var rec = sm.getSelection()[0];
if (!rec) {
edit_btn.disable();
+ remove_btn.disable();
resize_btn.disable();
return;
}
@@ -176,6 +225,7 @@ Ext.define('PVE.lxc.RessourceView', {
edit_btn.setDisabled(rec.data['delete'] || !rowdef.editor);
+ remove_btn.setDisabled(!isDisk || rec.data.key === 'rootfs');
resize_btn.setDisabled(!isDisk);
};
@@ -184,8 +234,31 @@ Ext.define('PVE.lxc.RessourceView', {
url: '/api2/json/' + baseurl,
selModel: sm,
cwidth1: 170,
- tbar: [ edit_btn,
- resize_btn],
+ tbar: [
+ {
+ text: gettext('Add'),
+ menu: new Ext.menu.Menu({
+ items: [
+ {
+ text: gettext('Mount Point'),
+ iconCls: 'pve-itype-icon-storage',
+ disabled: !caps.vms['VM.Config.Disk'],
+ handler: function() {
+ var win = Ext.create('PVE.lxc.MountPointEdit', {
+ url: '/api2/extjs/' + baseurl,
+ pveSelNode: me.pveSelNode
+ });
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ ]
+ })
+ },
+ edit_btn,
+ remove_btn,
+ resize_btn,
+ ],
rows: rows,
listeners: {
show: reload,
--
2.1.4
More information about the pve-devel
mailing list