[pve-devel] [PATCH manager 8/8] add CloudInit Config Panel and use it
Dominik Csapak
d.csapak at proxmox.com
Thu Mar 15 16:21:36 CET 2018
this lets the user edit the cloud init config
like username, sshkey, ipconfig, etc.
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
www/manager6/Makefile | 1 +
www/manager6/StateProvider.js | 1 +
www/manager6/qemu/CloudInit.js | 293 +++++++++++++++++++++++++++++++++++++++++
www/manager6/qemu/Config.js | 6 +
4 files changed, 301 insertions(+)
create mode 100644 www/manager6/qemu/CloudInit.js
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 1c7fdf92..646e7759 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -129,6 +129,7 @@ JSSRC= \
qemu/CreateWizard.js \
qemu/USBEdit.js \
qemu/AgentIPView.js \
+ qemu/CloudInit.js \
qemu/CIDriveEdit.js \
qemu/SSHKey.js \
qemu/IPConfigEdit.js \
diff --git a/www/manager6/StateProvider.js b/www/manager6/StateProvider.js
index db00e8b2..80e82c04 100644
--- a/www/manager6/StateProvider.js
+++ b/www/manager6/StateProvider.js
@@ -49,6 +49,7 @@ Ext.define('PVE.StateProvider', {
hprefix: 'v1',
compDict: {
+ cloudinit: 52,
replication: 51,
system: 50,
monitor: 49,
diff --git a/www/manager6/qemu/CloudInit.js b/www/manager6/qemu/CloudInit.js
new file mode 100644
index 00000000..52bbf542
--- /dev/null
+++ b/www/manager6/qemu/CloudInit.js
@@ -0,0 +1,293 @@
+Ext.define('PVE.qemu.CloudInit', {
+ extend: 'Proxmox.grid.PendingObjectGrid',
+ xtype: 'pveCiPanel',
+
+ tbar: [
+ {
+ xtype: 'proxmoxButton',
+ disabled: true,
+ dangerous: true,
+ confirmMsg: function(rec) {
+ var me = this.up('grid');
+ var warn = gettext('Are you sure you want to remove entry {0}');
+
+ var entry = rec.data.key;
+ var msg = Ext.String.format(warn, "'"
+ + me.renderKey(entry, {}, rec) + "'");
+
+ return msg;
+ },
+ enableFn: function(record) {
+ var me = this.up('grid');
+ var caps = Ext.state.Manager.get('GuiCap');
+ if (me.rows[record.data.key].never_delete ||
+ !caps.vms['VM.Config.Network']) {
+ return false;
+ }
+ return true;
+ },
+ handler: function() {
+ var me = this.up('grid');
+ },
+ text: gettext('Remove')
+ },
+ {
+ xtype: 'proxmoxButton',
+ disabled: true,
+ handler: function() {
+ var me = this.up('grid');
+ me.run_editor();
+ },
+ text: gettext('Edit')
+ },
+ '-',
+ {
+ xtype: 'button',
+ itemId: 'savebtn',
+ text: gettext('Regenerate Image'),
+ handler: function() {
+ var me = this.up('grid');
+ var eject_params = {};
+ var insert_params = {};
+ var disk = PVE.Parser.parseQemuDrive(me.ciDriveId, me.ciDrive);
+ var storage = '';
+ var stormatch = disk.file.match(/^([^\:]+)\:/);
+ if (stormatch) {
+ storage = stormatch[1];
+ }
+ eject_params[me.ciDriveId] = 'none,media=cdrom';
+ insert_params[me.ciDriveId] = storage + ':cloudinit';
+
+ var failure = function(response, opts) {
+ Ext.Msg.alert('Error', response.htmlStatus);
+ };
+
+ Proxmox.Utils.API2Request({
+ url: me.baseurl + '/config',
+ waitMsgTarget: me,
+ method: 'PUT',
+ params: eject_params,
+ failure: failure,
+ callback: function() {
+ Proxmox.Utils.API2Request({
+ url: me.baseurl + '/config',
+ waitMsgTarget: me,
+ method: 'PUT',
+ params: insert_params,
+ failure: failure,
+ callback: function() {
+ me.reload();
+ }
+ });
+ }
+ });
+ }
+ }
+ ],
+
+ border: false,
+
+ set_button_status: function(rstore, records, success) {
+ if (!success || records.length < 1) {
+ return;
+ }
+ var me = this;
+ var found;
+ records.forEach(function(record) {
+ if (found) {
+ return;
+ }
+ var id = record.data.key;
+ var value = record.data.value;
+ var ciregex = new RegExp("vm-" + me.pveSelNode.data.vmid + "-cloudinit");
+ if (id.match(/^(ide|scsi|sata)\d+$/) && ciregex.test(value)) {
+ found = id;
+ me.ciDriveId = found;
+ me.ciDrive = value;
+ }
+ });
+
+ me.down('#savebtn').setDisabled(!found);
+ me.setDisabled(!found);
+ if (!found) {
+ me.getView().mask(gettext('No CloudInit Drive found'), ['pve-static-mask']);
+ } else {
+ me.getView().unmask();
+ }
+ },
+
+ renderKey: function(key, metaData, rec, rowIndex, colIndex, store) {
+ var me = this;
+ var rows = me.rows;
+ var rowdef = rows[key] || {};
+
+ var icon = "";
+ if (rowdef.iconCls) {
+ icon = '<i class="' + rowdef.iconCls + '"></i> ';
+ }
+ return icon + (rowdef.header || key);
+ },
+
+ listeners: {
+ activate: function () {
+ var me = this;
+ me.rstore.startUpdate();
+ },
+ itemdblclick: function() {
+ var me = this;
+ me.run_editor();
+ }
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ 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');
+ me.baseurl = '/api2/extjs/nodes/' + nodename + '/qemu/' + vmid;
+ me.url = me.baseurl + '/pending';
+ me.editorConfig.url = me.baseurl + '/config';
+ me.editorConfig.pveSelNode = me.pveSelNode;
+
+ /*jslint confusion: true*/
+ /* editor is string and object */
+ me.rows = {
+ ciuser: {
+ header: gettext('User'),
+ iconCls: 'fa fa-user',
+ never_delete: true,
+ defaultValue: '',
+ editor: caps.vms['VM.Config.Options'] ? {
+ xtype: 'proxmoxWindowEdit',
+ subject: gettext('User'),
+ items: [
+ {
+ xtype: 'proxmoxtextfield',
+ deleteEmpty: true,
+ emptyText: Proxmox.Utils.defaultText,
+ fieldLabel: gettext('User'),
+ name: 'ciuser'
+ }
+ ]
+ } : undefined,
+ renderer: function(value) {
+ return value || Proxmox.Utils.defaultText;
+ }
+ },
+ cipassword: {
+ header: gettext('Password'),
+ iconCls: 'fa fa-unlock',
+ never_delete: true,
+ defaultValue: '',
+ editor: caps.vms['VM.Config.Options'] ? {
+ xtype: 'proxmoxWindowEdit',
+ subject: gettext('Password'),
+ items: [
+ {
+ xtype: 'proxmoxtextfield',
+ inputType: 'password',
+ deleteEmpty: true,
+ emptyText: Proxmox.Utils.noneText,
+ fieldLabel: gettext('Password'),
+ name: 'cipassword'
+ }
+ ]
+ } : undefined,
+ renderer: function(value) {
+ return value || Proxmox.Utils.noneText;
+ }
+ },
+ searchdomain: {
+ header: gettext('DNS domain'),
+ iconCls: 'fa fa-globe',
+ editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined,
+ never_delete: true,
+ defaultValue: gettext('use host settings')
+ },
+ nameserver: {
+ header: gettext('DNS servers'),
+ iconCls: 'fa fa-globe',
+ editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined,
+ never_delete: true,
+ defaultValue: gettext('use host settings')
+ },
+ sshkeys: {
+ header: gettext('SSH public key'),
+ iconCls: 'fa fa-key',
+ editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.SSHKeyEdit' : undefined,
+ never_delete: true,
+ renderer: function(value) {
+ value = decodeURIComponent(value);
+ var keys = value.split('\n');
+ var text = [];
+ keys.forEach(function(key) {
+ if (key.length) {
+ // First erase all quoted strings (eg. command="foo"
+ var v = key.replace(/"(?:\\.|[^"\\])*"/g, '');
+ // Now try to detect the comment:
+ var res = v.match(/^\s*(\S+\s+)?(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)\s+\S+\s+(.*?)\s*$/, '');
+ if (res) {
+ key = Ext.String.htmlEncode(res[2]);
+ if (res[1]) {
+ key += ' <span style="color:gray">(' + gettext('with options') + ')</span>';
+ }
+ text.push(key);
+ return;
+ }
+ // Most likely invalid at this point, so just stick to
+ // the old value.
+ text.push(Ext.String.htmlEncode(key));
+ }
+ });
+ if (text.length) {
+ return text.join('<br>');
+ } else {
+ return Proxmox.Utils.noneText;
+ }
+ },
+ defaultValue: ''
+ }
+ };
+ var i;
+ var ipconfig_renderer = function(value, md, record, ri, ci, store, pending) {
+ var id = record.data.key;
+ var match = id.match(/^net(\d+)$/);
+ var val = '';
+ if (match) {
+ val = me.getObjectValue('ipconfig'+match[1], '', pending);
+ }
+ return val;
+ };
+ for (i = 0; i < 32; i++) {
+ // we want to show an entry for every network device
+ // even if it is empty
+ me.rows['net' + i.toString()] = {
+ multiKey: ['ipconfig' + i.toString(), 'net' + i.toString()],
+ header: gettext('IP Config') + ' (net' + i.toString() +')',
+ editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.IPConfigEdit' : undefined,
+ iconCls: 'fa fa-exchange',
+ renderer: ipconfig_renderer
+ };
+ me.rows['ipconfig' + i.toString()] = {
+ visible: false
+ };
+ }
+ /*jslint confusion: false*/
+
+ PVE.Utils.forEachBus(['ide', 'scsi', 'sata'], function(type, id) {
+ me.rows[type+id] = {
+ visible: false
+ };
+ });
+ me.callParent();
+ me.mon(me.rstore, 'load', me.set_button_status, me);
+ }
+});
diff --git a/www/manager6/qemu/Config.js b/www/manager6/qemu/Config.js
index 4620fd20..5de39fc3 100644
--- a/www/manager6/qemu/Config.js
+++ b/www/manager6/qemu/Config.js
@@ -210,6 +210,12 @@ Ext.define('PVE.qemu.Config', {
xtype: 'PVE.qemu.HardwareView'
},
{
+ title: 'Cloud-Init',
+ itemId: 'cloudinit',
+ iconCls: 'fa fa-cloud',
+ xtype: 'pveCiPanel'
+ },
+ {
title: gettext('Options'),
iconCls: 'fa fa-gear',
itemId: 'options',
--
2.11.0
More information about the pve-devel
mailing list