]> git.proxmox.com Git - pve-manager.git/commitdiff
lxc: create/edit/remove mountpoints
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Tue, 23 Feb 2016 14:00:20 +0000 (15:00 +0100)
committerDietmar Maurer <dietmar@proxmox.com>
Tue, 23 Feb 2016 15:36:23 +0000 (16:36 +0100)
www/manager/lxc/ResourceEdit.js
www/manager/lxc/Resources.js

index 3bff06078a63fa07ae6a8c26a24491f2255b7c6b..327c0657c8e98ef500ebef14fca1ea0e8ccdbf1d 100644 (file)
@@ -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();
+    }
+});
index 7149ff0ec392bb9479fd2acf9be806b213320069..396b6f9a5df88dfc1d9e64207f412a58e2770445 100644 (file)
@@ -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,