[pve-devel] [RFC v2 cloudinit pve-manager 1/2] cloudinit: draft
Wolfgang Bumiller
w.bumiller at proxmox.com
Wed Aug 19 12:56:37 CEST 2015
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 | 233 +++++++++++++++++++++++++++
11 files changed, 727 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 39c5e50..3a0fa83 100644
--- a/www/css/ext-pve.css
+++ b/www/css/ext-pve.css
@@ -56,6 +56,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;
@@ -194,6 +195,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 at 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 at 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..47b3241
--- /dev/null
+++ b/www/manager/qemu/IPConfigEdit.js
@@ -0,0 +1,233 @@
+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 = {};
+
+ var cfg = PVE.Parser.printIPConfig(data);
+ if (cfg === '') {
+ params['delete'] = [me.confid];
+ } else {
+ params[me.confid] = cfg;
+ }
+ 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
More information about the pve-devel
mailing list