This is only a draft! Please comment!

* Added a CloudInit tab with network configuration
* The 'Generate' is renamed to 'Regenerate' when a cloudinit
  image exists (regenerate functionality not yet
  implemented).
* Deletion of the cloudinit image from out of the CloudInit
  tab defaults to using the 'force' flag to avoid leaving
  'unused' disks around.
* Added a PVE.Utils.forEachBus to not duplicate all bus
  names and counts.
---
 www/css/ext-pve.css                  |   6 +
 www/images/Makefile                  |   1 +
 www/images/cloudinit.png             | Bin 0 -> 193 bytes
 www/manager/Makefile                 |   3 +
 www/manager/Parser.js                |  57 +++++++
 www/manager/Utils.js                 |  27 +++-
 www/manager/qemu/CloudInit.js        | 296 +++++++++++++++++++++++++++++++++++
 www/manager/qemu/CloudInitCreator.js |  84 ++++++++++
 www/manager/qemu/Config.js           |   5 +
 www/manager/qemu/HardwareView.js     |  52 ++----
 www/manager/qemu/IPConfigEdit.js     | 234 +++++++++++++++++++++++++++
 11 files changed, 728 insertions(+), 37 deletions(-)
 create mode 100644 www/images/cloudinit.png
 create mode 100644 www/manager/qemu/CloudInit.js
 create mode 100644 www/manager/qemu/CloudInitCreator.js
 create mode 100644 www/manager/qemu/IPConfigEdit.js

diff --git a/www/css/ext-pve.css b/www/css/ext-pve.css
index 238c30e..461e22a 100644
--- a/www/css/ext-pve.css
+++ b/www/css/ext-pve.css
@@ -55,6 +55,7 @@
 .pve-itype-icon-node-running,
 .pve-itype-icon-storage,
 .pve-itype-icon-pool,
+.pve-itype-icon-cloudinit,
 .pve-itype-icon-itype
 {
     background-repeat: no-repeat;
@@ -186,6 +187,11 @@
     background-image:url(../images/virt-viewer.png);
 }
 
+.pve-itype-icon-cloudinit
+{
+    background-image:url(../images/cloudinit.png);
+}
+
 .pve-bar-wrap
 {
 
diff --git a/www/images/Makefile b/www/images/Makefile
index 0a6cf85..5bd27bf 100644
--- a/www/images/Makefile
+++ b/www/images/Makefile
@@ -24,6 +24,7 @@ GNOME_IMAGES =                        \
        keyboard.png            \
        cdrom.png               \
        network.png             \
+       cloudinit.png           \
        drive-harddisk.png      \
        network-server.png      \
        connect_established.png \
diff --git a/www/images/cloudinit.png b/www/images/cloudinit.png
new file mode 100644
index 
0000000000000000000000000000000000000000..3b80f9966a057a199637726c977aa139c5114429
GIT binary patch
literal 193
zcmeAS@N?(olHy`uVBq!ia0vp^0zk~e!3HF=pW8M9DYhhUcNd2LAh=-f^2tCE&H|6f
zVxW#|Aj~)+zj6*xkiEpy*OmP~2R9#wqTA#8V?ZH4PZ!4!jq}NO%Ku-lXX9cJW^PS*
zz?;y}#werw`QpuppC^P)m>9CX-TnWy{n5CDgO60%)LqR)woO)&KD_VuehDB5cmg6N
d+%GdSGt{cYOsoob-2gP1!PC{xWt~$(697@YJX!z%

literal 0
HcmV?d00001

diff --git a/www/manager/Makefile b/www/manager/Makefile
index 129eca7..eecb64d 100644
--- a/www/manager/Makefile
+++ b/www/manager/Makefile
@@ -121,6 +121,8 @@ JSSRC=                                                      
\
        qemu/BootOrderEdit.js                           \
        qemu/MemoryEdit.js                              \
        qemu/NetworkEdit.js                             \
+       qemu/IPConfigEdit.js                            \
+       qemu/CloudInitCreator.js                        \
        qemu/Smbios1Edit.js                             \
        qemu/CDEdit.js                                  \
        qemu/HDEdit.js                                  \
@@ -134,6 +136,7 @@ JSSRC=                                                      
\
        qemu/StartupEdit.js                             \
        qemu/ScsiHwEdit.js                              \
        qemu/Options.js                                 \
+       qemu/CloudInit.js                               \
        qemu/Snapshot.js                                \
        qemu/Clone.js                                   \
        qemu/SnapshotTree.js                            \
diff --git a/www/manager/Parser.js b/www/manager/Parser.js
index 5f15a76..fe2cc41 100644
--- a/www/manager/Parser.js
+++ b/www/manager/Parser.js
@@ -146,6 +146,63 @@ Ext.define('PVE.Parser', { statics: {
        return drivestr;
     },
 
+    parseIPConfig: function(key, value) {
+       if (!(key && value)) {
+           return;
+       }
+
+       var res = {};
+
+       var errors = false;
+       Ext.Array.each(value.split(','), function(p) {
+           if (!p || p.match(/^\s*$/)) {
+               return; // continue
+           }
+
+           var match_res;
+           if ((match_res = p.match(/^ip=(\S+)$/)) !== null) {
+               res.ip = match_res[1];
+           } else if ((match_res = p.match(/^gw=(\S+)$/)) !== null) {
+               res.gw = match_res[1];
+           } else if ((match_res = p.match(/^ip6=(\S+)$/)) !== null) {
+               res.ip6 = match_res[1];
+           } else if ((match_res = p.match(/^gw6=(\S+)$/)) !== null) {
+               res.gw6 = match_res[1];
+           } else {
+               errors = true;
+               return false; // break
+           }
+       });
+
+       if (errors) {
+           return;
+       }
+
+       return res;
+    },
+
+    printIPConfig: function(cfg) {
+       var c = "";
+       var str = "";
+       if (cfg.ip) {
+           str += "ip=" + cfg.ip;
+           c = ",";
+       }
+       if (cfg.gw) {
+           str += c + "gw=" + cfg.gw;
+           c = ",";
+       }
+       if (cfg.ip6) {
+           str += c + "ip6=" + cfg.ip6;
+           c = ",";
+       }
+       if (cfg.gw6) {
+           str += c + "gw6=" + cfg.gw6;
+           c = ",";
+       }
+       return str;
+    },
+
     parseOpenVZNetIf: function(value) {
        if (!value) {
            return;
diff --git a/www/manager/Utils.js b/www/manager/Utils.js
index 0e2e8a2..adbff79 100644
--- a/www/manager/Utils.js
+++ b/www/manager/Utils.js
@@ -1083,7 +1083,32 @@ Ext.define('PVE.Utils', { statics: {
            }
            PVE.Utils.setErrorMask(me, msg);
        });
-    }
+    },
+
+    bus_counts: { ide: 4, sata: 6, scsi: 16, virtio: 16 },
 
+    forEachBus: function(type, func) {
+       if (Ext.isDefined(type)) {
+           var count = PVE.Utils.bus_counts[type];
+           if (!count) {
+               Ext.Msg.alert(gettext('Invalid bus type') + ' (' + type + ')');
+               return;
+           }
+           for (var i = 0; i < count; i++) {
+               var cont = func(type, i);
+               if (Ext.isDefined(cont) && cont < 0)
+                   return;
+           }
+       } else {
+           Ext.each(Object.keys(PVE.Utils.bus_counts), function(busname) {
+               var count = PVE.Utils.bus_counts[busname];
+               for (var i = 0; i < count; i++) {
+                   var cont = func(busname, i);
+                   if (Ext.isDefined(cont) && !cont)
+                       return false;
+               }
+           });
+       }
+    },
 }});
 
diff --git a/www/manager/qemu/CloudInit.js b/www/manager/qemu/CloudInit.js
new file mode 100644
index 0000000..1a4b880
--- /dev/null
+++ b/www/manager/qemu/CloudInit.js
@@ -0,0 +1,296 @@
+/*jslint confusion: true */
+Ext.define('PVE.qemu.CloudInit', {
+    extend: 'PVE.grid.PendingObjectGrid',
+    alias: ['widget.PVE.qemu.CloudInit'],
+
+    initComponent : function() {
+       var me = this;
+       var i, confid;
+
+       me.pveBusId = undefined;
+
+       var nodename = me.pveSelNode.data.node;
+       if (!nodename) {
+           throw "no node name specified";
+       }
+
+       var vmid = me.pveSelNode.data.vmid;
+       if (!vmid) {
+           throw "no VM ID specified";
+       }
+
+       var caps = Ext.state.Manager.get('GuiCap');
+
+       var rows = {};
+
+       for (i = 0; i < 32; i++) {
+           var confid = "ipconfig" + i;
+           rows[confid] = {
+               tdCls: 'pve-itype-icon-network',
+               editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.IPConfigEdit' 
: undefined,
+               never_delete: caps.vms['VM.Config.Network'] ? false : true,
+               header: gettext('Network') + ' ' + i,
+           };
+           rows["net" + i] = {
+               never_delete: caps.vms['VM.Config.Network'] ? false : true,
+               visible: false
+           };
+       }
+
+       // we also need to know whether there's a cloudinit image already
+       // available
+       PVE.Utils.forEachBus(undefined, function(type, id) {
+           rows[type + id] = { visible: false };
+       });
+
+       var reload = function() {
+           me.rstore.load();
+       };
+
+       var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config';
+
+       var sm = Ext.create('Ext.selection.RowModel', {});
+
+       var run_editor = function() {
+           var rec = sm.getSelection()[0];
+           if (!rec) {
+               return;
+           }
+
+           var rowdef = rows[rec.data.key];
+           if (!rowdef.editor) {
+               return;
+           }
+
+           var editor = rowdef.editor;
+           var win;
+
+           if (Ext.isString(editor)) {
+               win = Ext.create(editor, {
+                   pveSelNode: me.pveSelNode,
+                   confid: rec.data.key,
+                   url: '/api2/extjs/' + baseurl
+               });
+           } else {
+               var config = Ext.apply({
+                   pveSelNode: me.pveSelNode,
+                   confid: rec.data.key,
+                   url: '/api2/extjs/' + baseurl
+               }, rowdef.editor);
+               win = Ext.createWidget(rowdef.editor.xtype, config);
+               win.load();
+           }
+
+           win.show();
+           win.on('destroy', reload);
+       };
+
+       var add_btn = new PVE.button.Button({
+           text: gettext('Generate'),
+           disabled: true,
+           handler: function() {
+               var win = Ext.create('PVE.qemu.CloudInitCreator', {
+                   url: '/api2/extjs/' + baseurl,
+                   pveSelNode: me.pveSelNode,
+                   pveBusId: me.pveBusId
+               });
+               win.on('destroy', reload);
+               win.show();
+           }
+        });
+
+       var delete_btn = new PVE.button.Button({
+           text: gettext('Delete'),
+           disabled: true,
+           dangerous: true,
+           confirmMsg: function() {
+               var msg = gettext('Are you sure you want to remove the 
CloudInit Image?');
+               if (me.pveBusId.match(/^unused\d+$/)) {
+                   msg += " " + gettext('This will permanently erase all image 
data.');
+               }
+
+               return msg;
+           },
+           handler: function(b, e) {
+               PVE.Utils.API2Request({
+                   url: '/api2/extjs/' + baseurl,
+                   waitMsgTarget: me,
+                   method: 'PUT',
+                   params: {
+                       'force': 1,
+                       'delete': me.pveBusId
+                   },
+                   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,
+           disabled: true,
+           handler: run_editor
+        });
+
+       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) + "'");
+               return msg;
+           },
+           handler: 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 revert_btn = new PVE.button.Button({
+           text: gettext('Revert'),
+           selModel: sm,
+           disabled: true,
+           handler: function(b, e, rec) {
+               var rowdef = me.rows[rec.data.key] || {};
+               var keys = rowdef.multiKey ||  [ rec.data.key ];
+               var revert = keys.join(',');
+               PVE.Utils.API2Request({
+                   url: '/api2/extjs/' + baseurl,
+                   waitMsgTarget: me,
+                   method: 'PUT',
+                   params: {
+                       'revert': revert
+                   },
+                   callback: function() {
+                       reload();
+                   },
+                   failure: function (response, opts) {
+                       Ext.Msg.alert('Error',response.htmlStatus);
+                   }
+               });
+           }
+       });
+
+       var set_button_status = function() {
+           var sm = me.getSelectionModel();
+           var rec = sm.getSelection()[0];
+
+           if (!rec) {
+               remove_btn.disable();
+               edit_btn.disable();
+               revert_btn.disable();
+               return;
+           }
+           var key = rec.data.key;
+           var value = rec.data.value;
+           var rowdef = rows[key];
+
+           var pending = rec.data['delete'] || me.hasPendingChanges(key);
+
+           remove_btn.setDisabled(rec.data['delete'] || (rowdef.never_delete 
=== true));
+
+           edit_btn.setDisabled(rec.data['delete'] || !rowdef.editor);
+
+           revert_btn.setDisabled(!pending);
+       };
+
+       var update_data = function() {
+           var i;
+           me.pveBusId = undefined;
+           PVE.Utils.forEachBus(undefined, function(type, id) {
+               var confid = type + id;
+               var entry = me.rstore.getById(confid);
+               if (!entry)
+                   return; // continue
+               if (entry.data.value.match(/vm-\d+-cloudinit/)) {
+                   me.pveBusId = confid;
+                   return false; // break
+               }
+           });
+           if (Ext.isDefined(me.pveBusId)) {
+               add_btn.setText(gettext('Regenerate'));
+               add_btn.enable();
+               delete_btn.enable();
+           } else {
+               add_btn.setText(gettext('Generate'));
+               add_btn.enable();
+               delete_btn.disable();
+           }
+
+           // add/remove arrays because .add/.remove recurses into the
+           // 'datachange' signal
+           var to_add = [];
+           var to_remove = [];
+           for (i = 0; i < 32; i++) {
+               var cid = "ipconfig" + i;
+               var nid = "net" + i;
+               var dev = me.rstore.getById(nid);
+               var conf = me.rstore.getById(cid);
+               if (!dev) {
+                   if (conf)
+                       to_remove.push(conf);
+                   continue;
+               }
+               if (!conf) {
+                   to_add.push({ key: cid, value: '' });
+                   rows[cid].visible = true;
+               } else
+                   rows[conf.data.key].visible = !!dev;
+           }
+           if (to_remove.length)
+               me.rstore.remove(to_remove);
+           if (to_add.length)
+               me.rstore.add(to_add);
+       };
+
+       Ext.applyIf(me, {
+           url: '/api2/json/' + 'nodes/' + nodename + '/qemu/' + vmid + 
'/pending',
+           interval: 5000,
+           selModel: sm,
+           cwidth1: 170,
+           tbar: [ 
+               add_btn,
+               delete_btn,
+               remove_btn,
+               edit_btn,
+               revert_btn
+           ],
+           rows: rows,
+           listeners: {
+               itemdblclick: run_editor,
+               selectionchange: set_button_status
+           }
+       });
+
+       me.callParent();
+
+       me.on('show', me.rstore.startUpdate);
+       me.on('hide', me.rstore.stopUpdate);
+       me.on('destroy', me.rstore.stopUpdate); 
+
+       me.rstore.on('datachanged', function() {
+           update_data();
+           set_button_status();
+       });
+    }
+});
+
diff --git a/www/manager/qemu/CloudInitCreator.js 
b/www/manager/qemu/CloudInitCreator.js
new file mode 100644
index 0000000..757fca0
--- /dev/null
+++ b/www/manager/qemu/CloudInitCreator.js
@@ -0,0 +1,84 @@
+Ext.define('PVE.qemu.CloudInitCreatePanel', {
+    extend: 'PVE.panel.InputPanel',
+    alias: 'widget.PVE.qemu.CloudInitCreatePanel',
+
+    insideWizard: false,
+
+    vmconfig: {},
+
+    onGetValues: function(values) {
+       var me = this;
+
+       var confid = me.confid || (values.controller + values.deviceid);
+       var params = {};
+
+       params[confid] = values.cdstorage + ":cloudinit";
+
+       return params;
+    },
+
+    setVMConfig: function(vmconfig) {
+       var me = this;
+       me.vmconfig = vmconfig;
+       me.bussel.setVMConfig(vmconfig, true);
+    },
+
+    setNodename: function(nodename) {
+       var me = this;
+       me.cdstoragesel.setNodename(nodename);
+    },
+
+    initComponent : function() {
+       var me = this;
+
+       me.bussel = Ext.createWidget('PVE.form.ControllerSelector', {
+           noVirtIO: true
+       });
+       me.cdstoragesel = Ext.create('PVE.form.StorageSelector', {
+           name: 'cdstorage',
+           nodename: me.nodename,
+           fieldLabel: gettext('Storage'),
+           storageContent: 'images',
+           autoSelect: true,
+           allowBlank: false,
+       });
+
+       me.column1 = [me.bussel];
+       me.column2 = [me.cdstoragesel];
+
+       me.callParent();
+    }
+});
+
+Ext.define('PVE.qemu.CloudInitCreator', {
+    extend: 'PVE.window.Edit',
+
+    initComponent : function() {
+       /*jslint confusion: true */
+
+       var me = this;
+
+       var nodename = me.pveSelNode.data.node;
+       if (!nodename) { 
+           throw "no node name specified";         
+       }
+
+       var ipanel = Ext.create('PVE.qemu.CloudInitCreatePanel', {
+           nodename: nodename
+       });
+
+       Ext.applyIf(me, {
+           subject: gettext('Config Drive'),
+           items: ipanel
+       });
+
+       me.callParent();
+
+       me.load({
+           success: function(response, options) {
+               me.vmconfig = response.result.data;
+               ipanel.setVMConfig(me.vmconfig);
+           }
+       });
+    }
+});
diff --git a/www/manager/qemu/Config.js b/www/manager/qemu/Config.js
index 19a331b..005f3e9 100644
--- a/www/manager/qemu/Config.js
+++ b/www/manager/qemu/Config.js
@@ -147,6 +147,11 @@ Ext.define('PVE.qemu.Config', {
                    xtype: 'PVE.qemu.Options'
                },
                {
+                   title: gettext('CloudInit'),
+                   itemId: 'cloudinit',
+                   xtype: 'PVE.qemu.CloudInit'
+               },
+               {
                    title: gettext('Task History'),
                    itemId: 'tasks',
                    xtype: 'pveNodeTasks',
diff --git a/www/manager/qemu/HardwareView.js b/www/manager/qemu/HardwareView.js
index 89f2f4e..d2b437a 100644
--- a/www/manager/qemu/HardwareView.js
+++ b/www/manager/qemu/HardwareView.js
@@ -143,8 +143,8 @@ Ext.define('PVE.qemu.HardwareView', {
 
        };
 
-       for (i = 0; i < 4; i++) {
-           confid = "ide" + i;
+       PVE.Utils.forEachBus(undefined, function(type, id) {
+           confid = type + id;
            rows[confid] = {
                group: 1,
                tdCls: 'pve-itype-icon-storage',
@@ -153,40 +153,7 @@ Ext.define('PVE.qemu.HardwareView', {
                header: gettext('Hard Disk') + ' (' + confid +')',
                cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
            };
-       }
-       for (i = 0; i < 6; i++) {
-           confid = "sata" + i;
-           rows[confid] = {
-               group: 1,
-               tdCls: 'pve-itype-icon-storage',
-               editor: 'PVE.qemu.HDEdit',
-               never_delete: caps.vms['VM.Config.Disk'] ? false : true,
-               header: gettext('Hard Disk') + ' (' + confid +')',
-               cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
-           };
-       }
-       for (i = 0; i < 16; i++) {
-           confid = "scsi" + i;
-           rows[confid] = {
-               group: 1,
-               tdCls: 'pve-itype-icon-storage',
-               editor: 'PVE.qemu.HDEdit',
-               never_delete: caps.vms['VM.Config.Disk'] ? false : true,
-               header: gettext('Hard Disk') + ' (' + confid +')',
-               cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
-           };
-       }
-       for (i = 0; i < 16; i++) {
-           confid = "virtio" + i;
-           rows[confid] = {
-               group: 1,
-               tdCls: 'pve-itype-icon-storage',
-               editor: 'PVE.qemu.HDEdit',
-               never_delete: caps.vms['VM.Config.Disk'] ? false : true,
-               header: gettext('Hard Disk') + ' (' + confid +')',
-               cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
-           };
-       }
+       });
        for (i = 0; i < 32; i++) {
            confid = "net" + i;
            rows[confid] = {
@@ -504,6 +471,19 @@ Ext.define('PVE.qemu.HardwareView', {
                                    win.on('destroy', reload);
                                    win.show();
                                }
+                           },
+                           {
+                               text: gettext('Config Drive'),
+                               iconCls: 'pve-itype-icon-cloudinit',
+                               disabled: !caps.vms['VM.Config.Network'] || 
!caps.vms['VM.Config.Disk'],
+                               handler: function() {
+                                   var win = 
Ext.create('PVE.qemu.CloudInitCreator', {
+                                       url: '/api2/extjs/' + baseurl,
+                                       pveSelNode: me.pveSelNode
+                                   });
+                                   win.on('destroy', reload);
+                                   win.show();
+                               }
                            }
                        ]
                    })
diff --git a/www/manager/qemu/IPConfigEdit.js b/www/manager/qemu/IPConfigEdit.js
new file mode 100644
index 0000000..8931fae
--- /dev/null
+++ b/www/manager/qemu/IPConfigEdit.js
@@ -0,0 +1,234 @@
+Ext.define('PVE.qemu.IPConfigPanel', {
+    extend: 'PVE.panel.InputPanel',
+    alias: 'widget.PVE.qemu.IPConfigPanel',
+
+    insideWizard: false,
+
+    onGetValues: function(values) {
+       var me = this;
+
+       var data = {};
+
+       if (values['ipv4mode'] !== 'static')
+           data['ip'] = values['ipv4mode'];
+       else {
+           data['ip'] = values['ip'];
+       }
+
+       if (values['ipv6mode'] !== 'static')
+           data['ip6'] = values['ipv6mode'];
+       else
+           data['ip6'] = values['ip6'];
+
+       var params = {};
+
+       if (data['ip'] === '' && data['ip6'] === '' &&
+           data['gw'] === '' && data['gw6'] === '')
+       {
+           params['delete'] = [me.confid];
+       } else {
+           params[me.confid] = PVE.Parser.printIPConfig(data);
+       }
+       return params;
+    },
+
+    setIPConfig: function(confid, data) {
+       var me = this;
+
+       me.confid = confid;
+
+       if (data['ip'] === 'dhcp') {
+           data['ipv4mode'] = data['ip'];
+           data['ip'] = '';
+       } else {
+           data['ipv4mode'] = 'static';
+       }
+       if (data['ip6'] === 'dhcp' || data['ip6'] === 'auto') {
+           data['ipv6mode'] = data['ip6'];
+           data['ip6'] = '';
+       } else {
+           data['ipv6mode'] = 'static';
+       }
+
+       me.ipconfig = data;
+       me.setValues(me.ipconfig);
+    },
+
+    initComponent : function() {
+       var me = this;
+
+       me.ipconfig = {};
+       me.confid = 'ipconfig0';
+
+       me.column1 = [
+           {
+               layout: {
+                   type: 'hbox',
+                   align: 'middle'
+               },
+               border: false,
+               margin: '0 0 5 0',
+               height: 22, // hack: set same height as text fields
+               items: [
+                   {
+                       xtype: 'label',
+                       text: gettext('IPv4') + ':',
+                   },
+                   {
+                       xtype: 'radiofield',
+                       boxLabel: gettext('Static'),
+                       name: 'ipv4mode',
+                       inputValue: 'static',
+                       checked: false,
+                       margin: '0 0 0 10',
+                       listeners: {
+                           change: function(cb, value) {
+                               me.down('field[name=ip]').setDisabled(!value);
+                               me.down('field[name=gw]').setDisabled(!value);
+                           }
+                       }
+                   },
+                   {
+                       xtype: 'radiofield',
+                       boxLabel: gettext('DHCP'),
+                       name: 'ipv4mode',
+                       inputValue: 'dhcp',
+                       checked: false,
+                       margin: '0 0 0 10'
+                   }
+               ]
+           },
+           {
+               xtype: 'textfield',
+               name: 'ip',
+               vtype: 'IPCIDRAddress',
+               value: '',
+               disabled: true,
+               fieldLabel: gettext('IPv4/CIDR')
+           },
+           {
+               xtype: 'textfield',
+               name: 'gw',
+               value: '',
+               vtype: 'IPAddress',
+               disabled: true,
+               fieldLabel: gettext('Gateway') + ' (' + gettext('IPv4') +')',
+               margin: '0 0 3 0' // override bottom margin to account for the 
menuseparator
+           },
+       ];
+
+       me.column2 = [
+           {
+               layout: {
+                   type: 'hbox',
+                   align: 'middle'
+               },
+               border: false,
+               margin: '0 0 5 0',
+               height: 22, // hack: set same height as text fields
+               items: [
+                   {
+                       xtype: 'label',
+                       text: gettext('IPv6') + ':',
+                   },
+                   {
+                       xtype: 'radiofield',
+                       boxLabel: gettext('Static'),
+                       name: 'ipv6mode',
+                       inputValue: 'static',
+                       checked: false,
+                       margin: '0 0 0 10',
+                       listeners: {
+                           change: function(cb, value) {
+                               me.down('field[name=ip6]').setDisabled(!value);
+                               me.down('field[name=gw6]').setDisabled(!value);
+                           }
+                       }
+                   },
+                   {
+                       xtype: 'radiofield',
+                       boxLabel: gettext('DHCP'),
+                       name: 'ipv6mode',
+                       inputValue: 'dhcp',
+                       checked: false,
+                       margin: '0 0 0 10'
+                   },
+                   {
+                       xtype: 'radiofield',
+                       boxLabel: gettext('SLAAC'),
+                       name: 'ipv6mode',
+                       inputValue: 'auto',
+                       checked: false,
+                       margin: '0 0 0 10'
+                   }
+               ]
+           },
+           {
+               xtype: 'textfield',
+               name: 'ip6',
+               value: '',
+               vtype: 'IP6CIDRAddress',
+               disabled: true,
+               fieldLabel: gettext('IPv6/CIDR')
+           },
+           {
+               xtype: 'textfield',
+               name: 'gw6',
+               vtype: 'IP6Address',
+               value: '',
+               disabled: true,
+               fieldLabel: gettext('Gateway') + ' (' + gettext('IPv6') +')'
+           }
+       ];
+
+       me.callParent();
+    }
+});
+
+Ext.define('PVE.qemu.IPConfigEdit', {
+    extend: 'PVE.window.Edit',
+
+    isAdd: true,
+
+    initComponent : function() {
+       /*jslint confusion: true */
+
+       var me = this;
+
+       var nodename = me.pveSelNode.data.node;
+       if (!nodename) { 
+           throw "no node name specified";         
+       }
+
+       me.create = me.confid ? false : true;
+
+       var ipanel = Ext.create('PVE.qemu.IPConfigPanel', {
+           confid: me.confid,
+           nodename: nodename
+       });
+
+       Ext.applyIf(me, {
+           subject: gettext('Network Config'),
+           items: ipanel
+       });
+
+       me.callParent();
+
+       me.load({
+           success: function(response, options) {
+               me.vmconfig = response.result.data;
+               var ipconfig = {};
+               var value = me.vmconfig[me.confid];
+               if (value) {
+                   ipconfig = PVE.Parser.parseIPConfig(me.confid, value);
+                   if (!ipconfig) {
+                       Ext.Msg.alert(gettext('Error'), gettext('Unable to 
parse network configuration'));
+                       me.close();
+                       return;
+                   }
+               }
+               ipanel.setIPConfig(me.confid, ipconfig);
+           }
+       });
+    }
+});
-- 
2.1.4


_______________________________________________
pve-devel mailing list
pve-devel@pve.proxmox.com
http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to