[pve-devel] [PATCH xtermjs] termproxy: rewrite in rust
termproxy is now completely written in rust (instead of perl) but it is a drop-in replacement this contains all other necessary changes to the build-system for it to successfully build Signed-off-by: Dominik Csapak --- changelog needs to be adapted to have 'rust-termproxy' as src package, since it seems that you cannot have a 'pve-xtermjs' package with a 'pve-xtermjs' crate but the binary is named 'termproxy' so we have a 'termproxy' crate+binary and a binary package with name 'pve-xtermjs' also the version in Cargo.toml needs then to be bumped to 4.7.0 .cargo/config | 5 + Cargo.toml | 14 ++ Makefile| 52 +++-- debian/compat | 1 - debian/control | 16 -- debian/debcargo.toml| 14 ++ debian/install | 1 + debian/rules| 7 +- debian/source/format| 1 - debian/source/lintian-overrides | 4 +- src/Makefile| 7 - src/PVE/CLI/Makefile| 8 - src/PVE/CLI/termproxy.pm| 250 - src/PVE/Makefile| 3 - src/bin/Makefile| 7 - src/bin/termproxy | 8 - src/main.rs | 378 src/www/Makefile| 21 -- 18 files changed, 458 insertions(+), 339 deletions(-) create mode 100644 .cargo/config create mode 100644 Cargo.toml delete mode 100644 debian/compat delete mode 100644 debian/control create mode 100644 debian/debcargo.toml create mode 100644 debian/install delete mode 100644 debian/source/format delete mode 100644 src/Makefile delete mode 100644 src/PVE/CLI/Makefile delete mode 100644 src/PVE/CLI/termproxy.pm delete mode 100644 src/PVE/Makefile delete mode 100644 src/bin/Makefile delete mode 100755 src/bin/termproxy create mode 100644 src/main.rs delete mode 100644 src/www/Makefile diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 000..3b5b6e4 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,5 @@ +[source] +[source.debian-packages] +directory = "/usr/share/cargo/registry" +[source.crates-io] +replace-with = "debian-packages" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000..028ca23 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "termproxy" +version = "4.3.0" +authors = ["Dominik Csapak "] +edition = "2018" +license = "AGPL-3" + +exclude = [ "build", "debian" ] + +[dependencies] +mio = "0.6" +curl = "0.4" +clap = "2.33" +proxmox = "0.1.42" diff --git a/Makefile b/Makefile index d4aeee4..7a73fe7 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ include /usr/share/dpkg/pkg-info.mk +include /usr/share/dpkg/architecture.mk PACKAGE=pve-xtermjs +CRATENAME=termproxy export VERSION=${DEB_VERSION_UPSTREAM_REVISION} @@ -11,31 +13,53 @@ FITADDONVER=0.4.0 FITADDONTGZ=xterm-addon-fit-${FITADDONVER}.tgz SRCDIR=src -BUILDDIR ?= ${PACKAGE}-${DEB_VERSION_UPSTREAM} GITVERSION:=$(shell git rev-parse HEAD) -DEB=${PACKAGE}_${VERSION}_all.deb -DSC=${PACKAGE}_${VERSION}.dsc +DEB=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}_${DEB_BUILD_ARCH}.deb +DSC=rust-${CRATENAME}_${DEB_VERSION_UPSTREAM_REVISION}.dsc -all: ${DEB} - @echo ${DEB} +ifeq ($(BUILD_MODE), release) +CARGO_BUILD_ARGS += --release +COMPILEDIR := target/release +else +COMPILEDIR := target/debug +endif + +all: cargo-build $(SRCIDR) + +.PHONY: $(SUBDIRS) +$(SUBDIRS): + make -C $@ + +.PHONY: cargo-build +cargo-build: + cargo build $(CARGO_BUILD_ARGS) -${BUILDDIR}: ${SRCDIR} debian - rm -rf ${BUILDDIR} - rsync -a ${SRCDIR}/ debian ${BUILDDIR} - echo "git clone git://git.proxmox.com/git/pve-xtermjs.git\\ngit checkout ${GITVERSION}" > ${BUILDDIR}/debian/SOURCE +.PHONY: build +build: + rm -rf build + debcargo package \ + --config debian/debcargo.toml \ + --changelog-ready \ + --no-overlay-write-back \ + --directory build \ + $(CRATENAME) \ + $(shell dpkg-parsechangelog -l debian/changelog -SVersion | sed -e 's/-.*//') + rm build/Cargo.lock + find build/debian -name "*.hint" -delete + echo "git clone git://git.proxmox.com/git/pve-xtermjs.git\\ngit checkout ${GITVERSION}" > build/debian/SOURCE .PHONY: deb deb: ${DEB} -${DEB}: ${BUILDDIR} - cd ${BUILDDIR}; dpkg-buildpackage -b -uc -us +$(DEB): build + cd build; dpkg-buildpackage -b -uc -us --no-pre-clean lintian ${DEB} @echo ${DEB} .PHONY: dsc dsc: ${DSC} -${DSC}: ${BUILDDIR} - cd ${BUILDDIR}; dpkg-buildpackage -S -us -uc -d +$(DSC): build + cd build; dpkg-buildpackage -S -us -uc -d -nc lintian ${DSC} X_EXCLUSIONS=--exclude=addons/attach --exclude=addons/full
[pve-devel] [PATCH manager 1/1] ui: use ZFS components and functions from widget-toolkit
render_zfs_health is now in widget-toolkit as well as the 'MultiDiskSelector' and the Detailwindow and drop the now unnecessary classes Signed-off-by: Dominik Csapak --- www/manager6/Utils.js| 26 www/manager6/node/ZFS.js | 255 +-- 2 files changed, 6 insertions(+), 275 deletions(-) diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js index 1dae292e..37327ded 100644 --- a/www/manager6/Utils.js +++ b/www/manager6/Utils.js @@ -193,32 +193,6 @@ Ext.define('PVE.Utils', { utilities: { return state; }, -render_zfs_health: function(value) { - if (typeof value == 'undefined'){ - return ""; - } - var iconCls = 'question-circle'; - switch (value) { - case 'AVAIL': - case 'ONLINE': - iconCls = 'check-circle good'; - break; - case 'REMOVED': - case 'DEGRADED': - iconCls = 'exclamation-circle warning'; - break; - case 'UNAVAIL': - case 'FAULTED': - case 'OFFLINE': - iconCls = 'times-circle critical'; - break; - default: //unknown - } - - return ' ' + value; - -}, - get_kvm_osinfo: function(value) { var info = { base: 'Other' }; // default if (value) { diff --git a/www/manager6/node/ZFS.js b/www/manager6/node/ZFS.js index 0b63ccd7..3c83361f 100644 --- a/www/manager6/node/ZFS.js +++ b/www/manager6/node/ZFS.js @@ -19,24 +19,6 @@ Ext.define('PVE.node.CreateZFS', { me.isCreate = true; - var update_disklist = function() { - var grid = me.down('#disklist'); - var disks = grid.getSelection(); - - var val = []; - disks.sort(function(a,b) { - var aorder = a.get('order') || 0; - var border = b.get('order') || 0; - return (aorder - border); - }); - - disks.forEach(function(disk) { - val.push(disk.get('devpath')); - }); - - me.down('field[name=devices]').setValue(val.join(',')); - }; - Ext.apply(me, { url: '/nodes/' + me.nodename + '/disks/zfs', method: 'POST', @@ -47,12 +29,6 @@ Ext.define('PVE.node.CreateZFS', { return values; }, column1: [ - { - xtype: 'textfield', - hidden: true, - name: 'devices', - allowBlank: false - }, { xtype: 'proxmoxtextfield', name: 'name', @@ -106,62 +82,11 @@ Ext.define('PVE.node.CreateZFS', { ], columnB: [ { - xtype: 'grid', + xtype: 'pmxMultiDiskSelector', + name: 'devices', + nodename: me.nodename, height: 200, emptyText: gettext('No Disks unused'), - itemId: 'disklist', - selModel: 'checkboxmodel', - listeners: { - selectionchange: update_disklist - }, - store: { - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/disks/list?type=unused' - } - }, - columns: [ - { - text: gettext('Device'), - dataIndex: 'devpath', - flex: 2, - }, - { - text: gettext('Model'), - dataIndex: 'model', - flex: 2, - }, - { - text: gettext('Serial'), - dataIndex: 'serial', - flex: 2, - }, - { - text: gettext('Size'), - dataIndex: 'size', - renderer: PVE.Utils.render_size, - flex: 1, - }, - { - header: gettext('Order'), - xtype: 'widgetcolumn', - dataInd
[pve-devel] [PATCH widget-toolkit 2/3] add form/MultiDiskSelector
from pve's ZFSCreate window, refactored to be self-contained using field mixin, as well as be configureable enough to be used by pve as well as other products Signed-off-by: Dominik Csapak --- src/Makefile | 1 + src/form/MultiDiskSelector.js | 164 ++ 2 files changed, 165 insertions(+) create mode 100644 src/form/MultiDiskSelector.js diff --git a/src/Makefile b/src/Makefile index 3311bbe..f4f8bf5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -30,6 +30,7 @@ JSSRC=\ form/RealmComboBox.js \ form/RoleSelector.js\ form/DiskSelector.js\ + form/MultiDiskSelector.js \ button/Button.js\ button/HelpButton.js\ grid/ObjectGrid.js \ diff --git a/src/form/MultiDiskSelector.js b/src/form/MultiDiskSelector.js new file mode 100644 index 000..9e989a4 --- /dev/null +++ b/src/form/MultiDiskSelector.js @@ -0,0 +1,164 @@ +Ext.define('Proxmox.form.MultiDiskSelector', { +extend: 'Ext.grid.Panel', +alias: 'widget.pmxMultiDiskSelector', + +mixins: { + field: 'Ext.form.field.Field', +}, + +selModel: 'checkboxmodel', + +store: { + data: [], + proxy: { + type: 'proxmox', + }, +}, + +// which field of the disklist is used for getValue +valueField: 'devpath', + +// which parameter is used for the type +typeParameter: 'type', + +// the type of disks to show +diskType: 'unused', + +disks: [], + +allowBlank: false, + +getValue: function() { + let me = this; + return me.disks; +}, + +setValue: function(value) { + let me = this; + + if (!Ext.isArray(value)) { + value = value.split(/;, /); + } + + let store = me.getStore(); + let selection = []; + + let keyField = me.valueField; + + value.forEach(item => { + let rec = store.findRecord(keyField, item, 0, false, true, true); + if (rec) { + selection.push(rec); + } + }); + + me.setSelection(selection); + + return me.mixins.field.setValue.call(me, value); +}, + +getErrors: function(value) { + let me = this; + if (me.allowBlank === false && + me.getSelectionModel().getCount() === 0) { + me.addBodyCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']); + return [gettext('No Disk selected')]; + } + + me.removeBodyCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']); + return []; +}, + +update_disklist: function() { + var me = this; + var disks = me.getSelection(); + + var val = []; + disks.sort(function(a, b) { + var aorder = a.get('order') || 0; + var border = b.get('order') || 0; + return aorder - border; + }); + + disks.forEach(function(disk) { + val.push(disk.get(me.valueField)); + }); + + me.validate(); + me.disks = val; +}, + +columns: [ + { + text: gettext('Device'), + dataIndex: 'devpath', + flex: 2, + }, + { + text: gettext('Model'), + dataIndex: 'model', + flex: 2, + }, + { + text: gettext('Serial'), + dataIndex: 'serial', + flex: 2, + }, + { + text: gettext('Size'), + dataIndex: 'size', + renderer: Proxmox.Utils.format_size, + flex: 1, + }, + { + header: gettext('Order'), + xtype: 'widgetcolumn', + dataIndex: 'order', + sortable: true, + flex: 1, + widget: { + xtype: 'proxmoxintegerfield', + minValue: 1, + isFormField: false, + listeners: { + change: function(numberfield, value, old_value) { + let grid = this.up('pmxMultiDiskSelector'); + var record = numberfield.getWidgetRecord(); + record.set('order', value); + grid.update_disklist(record); + }, + }, + }, + }, +], + +listeners: { + selectionchange: function() { + this.update_disklist(); + }, +}, + +initComponent: function() { + let me = this; + + if (!me.url) { + if (!me.nodename) { + throw "no url or nodename given"; + } + + let node = me.nodename; + let param = me.typeParameter; + let type = me.diskType; + me.url = `/api2/json/nodes/${node}/disks/list?${param}=${type}`; + } + + me.disks = []; + + me.callParent(); + let store = me.getStore(); + st
[pve-devel] [PATCH widget-toolkit 3/3] add window/ZFSDetail
inspired by pve's detail window, which used two sub components (ZFSStatus, ZFSDevices; which were never used elsewhere) combined into one self-contained window Signed-off-by: Dominik Csapak --- src/Makefile| 1 + src/window/ZFSDetail.js | 152 2 files changed, 153 insertions(+) create mode 100644 src/window/ZFSDetail.js diff --git a/src/Makefile b/src/Makefile index f4f8bf5..12dda30 100644 --- a/src/Makefile +++ b/src/Makefile @@ -46,6 +46,7 @@ JSSRC=\ window/TaskViewer.js\ window/LanguageEdit.js \ window/DiskSmart.js \ + window/ZFSDetail.js \ node/APT.js \ node/NetworkEdit.js \ node/NetworkView.js \ diff --git a/src/window/ZFSDetail.js b/src/window/ZFSDetail.js new file mode 100644 index 000..8eb6a87 --- /dev/null +++ b/src/window/ZFSDetail.js @@ -0,0 +1,152 @@ +Ext.define('Proxmox.window.ZFSDetail', { +extend: 'Ext.window.Window', +alias: 'widget.pmxZFSDetail', +mixins: ['Proxmox.Mixin.CBind'], + +cbindData: function(initialConfig) { + let me = this; + me.url = `/nodes/${me.nodename}/disks/zfs/${encodeURIComponent(me.zpool)}`; + return { + zpoolUri: `/api2/json/${me.url}`, + title: `${gettext('Status')}: ${me.zpool}`, + }; +}, + +controller: { + xclass: 'Ext.app.ViewController', + + reload: function() { + let me = this; + let view = me.getView(); + me.lookup('status').reload(); + + Proxmox.Utils.API2Request({ + url: `/api2/extjs/${view.url}`, + waitMsgTarget: view, + method: 'GET', + failure: function(response, opts) { + Proxmox.Utils.setErrorMask(view, response.htmlStatus); + }, + success: function(response, opts) { + let devices = me.lookup('devices'); + devices.getSelectionModel().deselectAll(); + devices.setRootNode(response.result.data); + devices.expandAll(); + }, + }); + }, + + init: function(view) { + let me = this; + Proxmox.Utils.monStoreErrors(me, me.lookup('status').getStore().rstore); + me.reload(); + }, +}, + +modal: true, +width: 800, +height: 400, +resizable: true, +cbind: { + title: '{title}', +}, + +layout: { + type: 'vbox', + align: 'stretch', +}, +defaults: { + layout: 'fit', + border: false, +}, + +tbar: [ + { + text: gettext('Reload'), + iconCls: 'fa fa-refresh', + handler: 'reload', + }, +], + +items: [ + { + xtype: 'proxmoxObjectGrid', + reference: 'status', + flex: 0, + cbind: { + url: '{zpoolUri}', + nodename: '{nodename}', + }, + rows: { + scan: { + header: gettext('Scan'), + }, + status: { + header: gettext('Status'), + }, + action: { + header: gettext('Action'), + }, + errors: { + header: gettext('Errors'), + }, + }, + }, + { + xtype: 'treepanel', + reference: 'devices', + title: gettext('Devices'), + stateful: true, + stateId: 'grid-node-zfsstatus', + rootVisible: true, + fields: ['name', 'status', + { + type: 'string', + name: 'iconCls', + calculate: function(data) { + var txt = 'fa x-fa-tree fa-'; + if (data.leaf) { + return txt + 'hdd-o'; + } + return undefined; + }, + }, + ], + sorters: 'name', + flex: 1, + cbind: { + zpool: '{zpoolUri}', + nodename: '{nodename}', + }, + columns: [ + { + xtype: 'treecolumn', + text: gettext('Name'), + dataIndex: 'name', + flex: 1, + }, + { + text: gettext('Health'), + renderer: Proxmox.Utils.render_zfs_health, + dataIndex: 'state', + }, + { + text: 'READ', + dataIndex: 'read', + }, + { + text: 'WRITE', + dataIndex: 'write
[pve-devel] [PATCH widget-toolkit/manager] move/refactor ZFS related gui components into widget-toolkit
to be used outside of pve includes some refactor/rewriting, but the components are not that big, so it should be rather straightforward manager patches depend on widget-toolkit, but they do not conflict so we could apply manager sometime later proxmox-widget-toolkit: Dominik Csapak (3): Utils: add render_zfs_health add form/MultiDiskSelector add window/ZFSDetail src/Makefile | 2 + src/Utils.js | 25 ++ src/form/MultiDiskSelector.js | 164 ++ src/window/ZFSDetail.js | 152 +++ 4 files changed, 343 insertions(+) create mode 100644 src/form/MultiDiskSelector.js create mode 100644 src/window/ZFSDetail.js pve-manager: Dominik Csapak (1): ui: use ZFS components and functions from widget-toolkit www/manager6/Utils.js| 26 www/manager6/node/ZFS.js | 255 +-- 2 files changed, 6 insertions(+), 275 deletions(-) -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 1/3] Utils: add render_zfs_health
from pve-manager Signed-off-by: Dominik Csapak --- src/Utils.js | 25 + 1 file changed, 25 insertions(+) diff --git a/src/Utils.js b/src/Utils.js index b5b1acb..d959dee 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -740,6 +740,31 @@ utilities: { return Ext.Date.format(servertime, 'Y-m-d H:i:s'); }, +render_zfs_health: function(value) { + if (typeof value === 'undefined') { + return ""; + } + var iconCls = 'question-circle'; + switch (value) { + case 'AVAIL': + case 'ONLINE': + iconCls = 'check-circle good'; + break; + case 'REMOVED': + case 'DEGRADED': + iconCls = 'exclamation-circle warning'; + break; + case 'UNAVAIL': + case 'FAULTED': + case 'OFFLINE': + iconCls = 'times-circle critical'; + break; + default: //unknown + } + + return ' ' + value; +}, + get_help_info: function(section) { let helpMap; if (typeof proxmoxOnlineHelpInfo !== 'undefined') { -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager] fix #2810: reset state of mounts array in initComponent
so that each new instance has an empty mounts list Signed-off-by: Dominik Csapak --- @fabian @oguz, i remembered that i know this issue and had a fix already^^ www/manager6/lxc/FeaturesEdit.js | 8 +++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/www/manager6/lxc/FeaturesEdit.js b/www/manager6/lxc/FeaturesEdit.js index 1275a2e0..e0b851de 100644 --- a/www/manager6/lxc/FeaturesEdit.js +++ b/www/manager6/lxc/FeaturesEdit.js @@ -108,7 +108,13 @@ Ext.define('PVE.lxc.FeaturesInputPanel', { } this.callParent([res]); } -} +}, + +initComponent: function() { + let me = this; + me.mounts = []; // reset state + me.callParent(); +}, }); Ext.define('PVE.lxc.FeaturesEdit', { -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] [PATCH manager] ui: fw: Close #2815: Add warning if fw is disabled
Looks mosty ok, but i noticed two things: 1. We use this grid also on datacenter and node level, so we should adapt the message for those somehow 2. is inline On 6/23/20 11:31 AM, Dominic Jäger wrote: Currently people add firewall rules but forget to activate the firewall on guest level. This commit adds a warning to the top bar of the firewall panel to make them aware of this if necessary. It seems a little cheaper but still sufficient to check only if some rule exists and not for every rule if it is really enabled. Signed-off-by: Dominic Jäger --- www/manager6/grid/FirewallRules.js | 32 -- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/www/manager6/grid/FirewallRules.js b/www/manager6/grid/FirewallRules.js index ec2d1c84..7cb2baf0 100644 --- a/www/manager6/grid/FirewallRules.js +++ b/www/manager6/grid/FirewallRules.js @@ -483,8 +483,26 @@ Ext.define('PVE.FirewallRules', { throw "no list_refs_url specified"; } + let checkWarning = function () { + Proxmox.Utils.API2Request({ + url: me.base_url.replace('rules', 'options'), + method: 'GET', + failure: function (response) { + Ext.Msg.alert(gettext('Error'), response.htmlStatus); + }, + success: function (response) { + let warningRequired = me.store.getCount() != 0 && !response.result.data.enable; + me.down('displayfield[name=fw-warning]').setVisible(warningRequired) + }, + }); + + }; + var store = Ext.create('Ext.data.Store',{ - model: 'pve-fw-rule' + model: 'pve-fw-rule', + listeners: { + 'load': checkWarning, + }, }); var reload = function() { @@ -606,12 +624,22 @@ Ext.define('PVE.FirewallRules', { } }); + me.warningField = Ext.create('Ext.form.field.Display',{ + xtype: 'displayfield', + dock: 'top', + userCls: 'pmx-hint', + name: 'fw-warning', + margin: '10 0 0 0', i guess you wanted the margin on the left side not top? the order is: top right bottom left, so you'd have to do margin: '0 0 0 10' with this currently the tbar changes height when the warning is displayed, which looks odd... (i don't think we need any margin/padding in tbar) also the 'dock' property is not necessary (the tbar is already docked) + value: gettext('Warning: Firewall still disabled at guest level! This can be changed in Firewall->Options.'), + hidden: true, + }); + var tbar = me.tbar_prefix ? [ me.tbar_prefix ] : []; tbar.push(me.addBtn, me.copyBtn); if (me.groupBtn) { tbar.push(me.groupBtn); } - tbar.push(me.removeBtn, me.editBtn); + tbar.push(me.removeBtn, me.editBtn, me.warningField); var render_errors = function(name, value, metaData, record) { var errors = record.data.errors; ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] [PATCH qemu-server] vncproxy: allow to request a generated VNC password
looks good to me and works as expected if we wanted we could probably make this the default by either * applying the novnc patch * alternatively putting the generated password at the beginning of the vncticket and removing it before verifying in the websocket api call this would be ok since the node where the api call is made is always the local node (tunneling is over ssh) but since it really does not matter if we do it at all, we can simply omit the novnc patch for now and leave it optional Tested-By: Dominik Csapak Reviewed-By: Dominik Csapak On 6/18/20 6:20 PM, Thomas Lamprecht wrote: We used the VNC API $ticket as password for VNC, but QEMU limits the password to the first 8 chars and ignores the rest[0]. As our tickets start with a static string (e.g., "PVE") the entropy was a bit limited. For Proxmox VE this does not matters much as the noVNC viewer provided by has to go always over the API call, and so a valid ticket and correct permissions for the requested VM are enforced anyway. This patch helps external users, which often use NoVNC-Websockify, circumventing the API and relying solely on the VNC password to avoid snooping on VNC sessions. A 'generate-password' parameter is added, if set a password from good entropy (using libopenssl) is generated. For simplicity of mapping random bits to ranges we extract 6 bit of entropy per character and add the integer value of '!' (first printable ASCII char) to that. This way we get 64^8 possibilities, which even with millions of guesses per second one would need years of guessing and mostly just DDOS the server with websocket upgrade requests. Signed-off-by: Thomas Lamprecht --- We could also extract the last 8 chars of the ticket, but as that ticket is only secure as a whole, as seen with this issue, I'd like to avoid that Initially I had a variant with using perls core rand and all 95 printable ascii chars, but that wasn't crypthographical secure PVE/API2/Qemu.pm | 39 --- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 3e0d59f..dcb364d 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -7,6 +7,7 @@ use Net::SSLeay; use POSIX; use IO::Socket::IP; use URI::Escape; +use Crypt::OpenSSL::Random; use PVE::Cluster qw (cfs_read_file cfs_write_file);; use PVE::RRD; @@ -1589,6 +1590,19 @@ __PACKAGE__->register_method({ return undef; }}); +# uses good entropy, each char is limited to 6 bit to get printable chars simply +my $gen_rand_chars = sub { +my ($length) = @_; + +die "invalid length $length" if $length < 1; + +my $min = ord('!'); # first printable ascii +my @rand_bytes = split '', Crypt::OpenSSL::Random::random_bytes($length); +my $str = join('', map { chr((ord($_) & 0x3F) + $min) } @rand_bytes); + +return $str; +}; + my $sslcert; __PACKAGE__->register_method({ @@ -1610,6 +1624,12 @@ __PACKAGE__->register_method({ type => 'boolean', description => "starts websockify instead of vncproxy", }, + 'generate-password' => { + optional => 1, + type => 'boolean', + default => 0, + description => "Generates a random password to be used as ticket instead of the API ticket.", + }, }, }, returns => { @@ -1617,6 +1637,12 @@ __PACKAGE__->register_method({ properties => { user => { type => 'string' }, ticket => { type => 'string' }, + password => { + optional => 1, + description => "Returned if requested with 'generate-password' param." + ." Consists of printable ASCII characters ('!' .. '~').", + type => 'string', + }, cert => { type => 'string' }, port => { type => 'integer' }, upid => { type => 'string' }, @@ -1644,6 +1670,10 @@ __PACKAGE__->register_method({ my $authpath = "/vms/$vmid"; my $ticket = PVE::AccessControl::assemble_vnc_ticket($authuser, $authpath); + my $password = $ticket; + if ($param->{'generate-password'}) { + $password = $gen_rand_chars->(8); + } $sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192) if !$sslcert; @@ -1680,7 +1710,7 @@ __PACKAGE__->register_method({ '-perm', 'Sys.Console']; if ($param->{websocket}) { - $ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm + $ENV{PVE_VNC_TICKET} = $password; # pass ticket to vncterm push @$cmd, '-notls', '-listen', 'localhost'; } @@ -1690,7 +1720,7 @@ __PACKAGE__-
Re: [pve-devel] [PATCH widget-toolkit v2 3/4] TaskViewer: show endtime and duration in status
On 6/16/20 11:19 AM, Thomas Lamprecht wrote: Am 6/15/20 um 4:07 PM schrieb Dominik Csapak: but only when the caller gives us the endtime, since the status api call does not give us the endtime Signed-off-by: Dominik Csapak --- changes from v1: * always show duration and calculate the endtime from 'now' src/node/Tasks.js| 1 + src/window/TaskViewer.js | 25 + 2 files changed, 26 insertions(+) diff --git a/src/node/Tasks.js b/src/node/Tasks.js index 5aff06d..c41f0b5 100644 --- a/src/node/Tasks.js +++ b/src/node/Tasks.js @@ -60,6 +60,7 @@ Ext.define('Proxmox.node.Tasks', { let win = Ext.create('Proxmox.window.TaskViewer', { upid: rec.data.upid, + endtime: rec.data.endtime, }); win.show(); }; diff --git a/src/window/TaskViewer.js b/src/window/TaskViewer.js index 2f31023..51715f8 100644 --- a/src/window/TaskViewer.js +++ b/src/window/TaskViewer.js @@ -152,6 +152,31 @@ Ext.define('Proxmox.window.TaskViewer', { }, }; + if (me.endtime) { + if (typeof me.endtime === 'object') { + // convert to epoch + me.endtime = parseInt(me.endtime.getTime()/1000, 10); + } + rows.endtime = { + header: gettext('End Time'), + required: true, + renderer: function() { + return Proxmox.Utils.render_timestamp(me.endtime); + }, + }; + } + + rows.duration = { + header: gettext('Duration'), + required: true, + renderer: function() { + let starttime = statgrid.getObjectValue('starttime'); + let endtime = me.endtime || Date.now()/1000; We'd need to check also the status (running or not) to know if we can still fallback to the current time, or? As else, if a running task gets opened we never will get supplied the endtime (as we only pass that once on window creation) even if the task ends after a bit. We could solve this by remembering the last duration and then fallback to that one if we have no endtime and the task status switches from running to something else? we already stop the store when the task is stopped, so the renderer will also not be called then anymore and is stopped the only thing i do not completely like is that we do not show the endtime in that case i toyed with the idea to also always show the endtime as '-' and then update it with the last update of the renderer, but this would then be probably not consistent with the 'real' endtime and also i could not get it to work... + let duration = endtime - starttime; + return Proxmox.Utils.format_duration_human(duration); + }, + }; + let statstore = Ext.create('Proxmox.data.ObjectStore', { url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", interval: 1000, ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit/manager] move diskrelated code to widget-toolkti
so that we can reuse it. where applicable i refactored some code or added some mappings/properties, but nothing substantial proxmox-widget-toolkit: Dominik Csapak (2): add DiskSelector from PVE add DiskSmart window and DiskList from PVE src/Makefile | 3 + src/form/DiskSelector.js | 80 ++ src/grid/DiskList.js | 233 +++ src/window/DiskSmart.js | 133 ++ 4 files changed, 449 insertions(+) create mode 100644 src/form/DiskSelector.js create mode 100644 src/grid/DiskList.js create mode 100644 src/window/DiskSmart.js pve-manager: Dominik Csapak (2): ui: use pmxDiskSelector from widget-toolkit ui: use pmxDiskList from widget-toolkit www/manager6/Makefile | 2 - www/manager6/ceph/OSD.js | 6 +- www/manager6/form/DiskSelector.js | 80 -- www/manager6/node/Config.js | 3 +- www/manager6/node/Directory.js| 2 +- www/manager6/node/Disks.js| 387 -- www/manager6/node/LVM.js | 2 +- www/manager6/node/LVMThin.js | 2 +- 8 files changed, 8 insertions(+), 476 deletions(-) delete mode 100644 www/manager6/form/DiskSelector.js delete mode 100644 www/manager6/node/Disks.js -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 1/2] ui: use pmxDiskSelector from widget-toolkit
Signed-off-by: Dominik Csapak --- www/manager6/Makefile | 1 - www/manager6/ceph/OSD.js | 6 +-- www/manager6/form/DiskSelector.js | 80 --- www/manager6/node/Directory.js| 2 +- www/manager6/node/LVM.js | 2 +- www/manager6/node/LVMThin.js | 2 +- 6 files changed, 6 insertions(+), 87 deletions(-) delete mode 100644 www/manager6/form/DiskSelector.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index 5656ba27..7ebd4db3 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -30,7 +30,6 @@ JSSRC= \ form/MemoryField.js \ form/NetworkCardSelector.js \ form/DiskFormatSelector.js \ - form/DiskSelector.js\ form/BusTypeSelector.js \ form/ControllerSelector.js \ form/EmailNotificationSelector.js \ diff --git a/www/manager6/ceph/OSD.js b/www/manager6/ceph/OSD.js index ffdfbee7..88109315 100644 --- a/www/manager6/ceph/OSD.js +++ b/www/manager6/ceph/OSD.js @@ -34,7 +34,7 @@ Ext.define('PVE.CephCreateOsd', { }, column1: [ { - xtype: 'pveDiskSelector', + xtype: 'pmxDiskSelector', name: 'dev', nodename: me.nodename, diskType: 'unused', @@ -44,7 +44,7 @@ Ext.define('PVE.CephCreateOsd', { ], column2: [ { - xtype: 'pveDiskSelector', + xtype: 'pmxDiskSelector', name: 'db_dev', nodename: me.nodename, diskType: 'journal_disks', @@ -80,7 +80,7 @@ Ext.define('PVE.CephCreateOsd', { ], advancedColumn2: [ { - xtype: 'pveDiskSelector', + xtype: 'pmxDiskSelector', name: 'wal_dev', nodename: me.nodename, diskType: 'journal_disks', diff --git a/www/manager6/form/DiskSelector.js b/www/manager6/form/DiskSelector.js deleted file mode 100644 index a1ea02e7.. --- a/www/manager6/form/DiskSelector.js +++ /dev/null @@ -1,80 +0,0 @@ -Ext.define('PVE.form.DiskSelector', { -extend: 'Proxmox.form.ComboGrid', -xtype: 'pveDiskSelector', - -// can be -// undefined: all -// unused: only unused -// journal_disk: all disks with gpt -diskType: undefined, - -valueField: 'devpath', -displayField: 'devpath', -emptyText: gettext('No Disks unused'), -listConfig: { - width: 600, - columns: [ - { - header: gettext('Device'), - flex: 3, - sortable: true, - dataIndex: 'devpath' - }, - { - header: gettext('Size'), - flex: 2, - sortable: false, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Serial'), - flex: 5, - sortable: true, - dataIndex: 'serial' - } - ] -}, - -initComponent: function() { - var me = this; - - var nodename = me.nodename; - if (!nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.Store', { - filterOnLoad: true, - model: 'pve-disk-list', - proxy: { -type: 'proxmox', -url: "/api2/json/nodes/" + nodename + "/disks/list", - extraParams: { type: me.diskType } - }, - sorters: [ - { - property : 'devpath', - direction: 'ASC' - } - ] - }); - - Ext.apply(me, { - store: store - }); - -me.callParent(); - - store.load(); -} -}, function() { - -Ext.define('pve-disk-list', { - extend: 'Ext.data.Model', - fields: [ 'devpath', 'used', { name: 'size', type: 'number'}, - {name: 'osdid', type: 'number'}, - 'vendor', 'model', 'serial'], - idProperty: 'devpath' -}); -}); diff --git a/www/manager6/node/Directory.js b/www/manager6/node/Directory.js index e72ebe86..6e2e3c2f 100644 --- a/www/manager6/node/Directory.js +++ b/www/manager6/node/Directory.js @@ -22,7 +22,7 @@ Ext.define('PVE.node.CreateDirectory', { method: 'POST', items: [ { -
[pve-devel] [PATCH widget-toolkit 1/2] add DiskSelector from PVE
to be usable with other products. also add a parameter 'typeProperty' to be able to configure the backend property for the usage type Signed-off-by: Dominik Csapak --- src/Makefile | 1 + src/form/DiskSelector.js | 80 2 files changed, 81 insertions(+) create mode 100644 src/form/DiskSelector.js diff --git a/src/Makefile b/src/Makefile index 659e876..2696103 100644 --- a/src/Makefile +++ b/src/Makefile @@ -29,6 +29,7 @@ JSSRC=\ form/NetworkSelector.js \ form/RealmComboBox.js \ form/RoleSelector.js\ + form/DiskSelector.js\ button/Button.js\ button/HelpButton.js\ grid/ObjectGrid.js \ diff --git a/src/form/DiskSelector.js b/src/form/DiskSelector.js new file mode 100644 index 000..4552beb --- /dev/null +++ b/src/form/DiskSelector.js @@ -0,0 +1,80 @@ +Ext.define('Proxmox.form.DiskSelector', { +extend: 'Proxmox.form.ComboGrid', +xtype: 'pmxDiskSelector', + +// can be +// undefined: all +// unused: only unused +// journal_disk: all disks with gpt +diskType: undefined, + +// the property the backend wnats for the type ('type' by default) +typeProperty: 'type', + +valueField: 'devpath', +displayField: 'devpath', +emptyText: gettext('No Disks unused'), +listConfig: { + width: 600, + columns: [ + { + header: gettext('Device'), + flex: 3, + sortable: true, + dataIndex: 'devpath', + }, + { + header: gettext('Size'), + flex: 2, + sortable: false, + renderer: Proxmox.Utils.format_size, + dataIndex: 'size', + }, + { + header: gettext('Serial'), + flex: 5, + sortable: true, + dataIndex: 'serial', + }, + ], +}, + +initComponent: function() { + var me = this; + + var nodename = me.nodename; + if (!nodename) { + throw "no node name specified"; + } + + let extraParams = {}; + + if (me.diskType) { + extraParams[me.typeProperty] = me.diskType; + } + + var store = Ext.create('Ext.data.Store', { + filterOnLoad: true, + model: 'pmx-disk-list', + proxy: { +type: 'proxmox', +url: `/api2/json/nodes/${nodename}/disks/list`, + extraParams, + }, + sorters: [ + { + property: 'devpath', + direction: 'ASC', + }, + ], + }); + + Ext.apply(me, { + store: store, + }); + +me.callParent(); + + store.load(); +}, +}); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 2/2] add DiskSmart window and DiskList from PVE
for use with other produts. the models are now all prefixed with 'pmx' instead of pve, so they should not conflict includes some changes to the model for remapping some fields and some small refactors (change to controller for the DiskList, some cleanup of the initComponent of the DiskSmart window) Signed-off-by: Dominik Csapak --- src/Makefile| 2 + src/grid/DiskList.js| 233 src/window/DiskSmart.js | 133 +++ 3 files changed, 368 insertions(+) create mode 100644 src/grid/DiskList.js create mode 100644 src/window/DiskSmart.js diff --git a/src/Makefile b/src/Makefile index 2696103..3311bbe 100644 --- a/src/Makefile +++ b/src/Makefile @@ -34,6 +34,7 @@ JSSRC=\ button/HelpButton.js\ grid/ObjectGrid.js \ grid/PendingObjectGrid.js \ + grid/DiskList.js\ panel/InputPanel.js \ panel/LogView.js\ panel/JournalView.js\ @@ -43,6 +44,7 @@ JSSRC=\ window/PasswordEdit.js \ window/TaskViewer.js\ window/LanguageEdit.js \ + window/DiskSmart.js \ node/APT.js \ node/NetworkEdit.js \ node/NetworkView.js \ diff --git a/src/grid/DiskList.js b/src/grid/DiskList.js new file mode 100644 index 000..03d6725 --- /dev/null +++ b/src/grid/DiskList.js @@ -0,0 +1,233 @@ +Ext.define('pmx-disk-list', { +extend: 'Ext.data.Model', +fields: [ + 'devpath', 'used', + { name: 'size', type: 'number' }, + { name: 'osdid', type: 'number' }, + { + name: 'status', + convert: function(value, rec) { + if (value) return value; + if (rec.data.health) { + return rec.data.health; + } + return Proxmox.Utils.unknownText; + }, + }, + { + name: 'name', + convert: function(value, rec) { + if (value) return value; + if (rec.data.devpath) return rec.data.devpath; + return undefined; + }, + }, + { + name: 'disk-type', + convert: function(value, rec) { + if (value) return value; + if (rec.data.type) return rec.data.type; + return undefined; + }, + }, + 'vendor', 'model', 'serial', 'rpm', 'type', 'wearout', 'health', +], +idProperty: 'devpath', +}); + +Ext.define('Proxmox.DiskList', { +extend: 'Ext.grid.GridPanel', +alias: 'widget.pmxDiskList', + +emptyText: gettext('No Disks found'), + +stateful: true, +stateId: 'grid-node-disks', + +controller: { + xclass: 'Ext.app.ViewController', + + reload: function() { + let me = this; + me.getView().getStore().load(); + }, + + openSmartWindow: function() { + let me = this; + let view = me.getView(); + let selection = view.getSelection(); + if (!selection || selection.length < 1) return; + + let rec = selection[0]; + Ext.create('Proxmox.window.DiskSmart', { + baseurl: view.baseurl, + dev: rec.data.name, + }).show(); + }, + + initGPT: function() { + let me = this; + let view = me.getView(); + let selection = view.getSelection(); + if (!selection || selection.length < 1) return; + + let rec = selection[0]; + Proxmox.Utils.API2Request({ + url: `${view.baseurl}/initgpt`, + waitMsgTarget: view, + method: 'POST', + params: { disk: rec.data.devpath }, + failure: function(response, options) { + Ext.Msg.alert(gettext('Error'), response.htmlStatus); + }, + success: function(response, options) { + var upid = response.result.data; + var win = Ext.create('Proxmox.window.TaskProgress', { + upid: upid, + }); + win.show(); + }, + }); + }, + + init: function(view) { + Proxmox.Utils.monStoreErrors(view, view.getStore(), true); + + let nodename = view.nodename || 'localhost'; + view.baseurl = `/api2/json/nodes/${nodename}/disks`; + view.getStore().getProxy().setUrl(`${view.baseurl}/list`); + view.getStore().load(); + }, +}, + +store: { + model: 'pmx-disk-list', + proxy: { + type: 'proxmox', + }, + sorters: [ + { + property: 'dev', + direction
[pve-devel] [PATCH manager 2/2] ui: use pmxDiskList from widget-toolkit
Signed-off-by: Dominik Csapak --- www/manager6/Makefile | 1 - www/manager6/node/Config.js | 3 +- www/manager6/node/Disks.js | 387 3 files changed, 2 insertions(+), 389 deletions(-) delete mode 100644 www/manager6/node/Disks.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index 7ebd4db3..ff452184 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -116,7 +116,6 @@ JSSRC= \ ceph/Config.js \ ceph/Log.js \ ceph/CephInstallWizard.js \ - node/Disks.js \ node/LVM.js \ node/LVMThin.js \ node/Directory.js \ diff --git a/www/manager6/node/Config.js b/www/manager6/node/Config.js index b5e6fd1a..f45bd370 100644 --- a/www/manager6/node/Config.js +++ b/www/manager6/node/Config.js @@ -275,7 +275,8 @@ Ext.define('PVE.node.Config', { itemId: 'storage', expandedOnInit: true, iconCls: 'fa fa-hdd-o', - xtype: 'pveNodeDiskList' + nodename: nodename, + xtype: 'pmxDiskList' }, { title: 'LVM', diff --git a/www/manager6/node/Disks.js b/www/manager6/node/Disks.js deleted file mode 100644 index e8ae2767.. --- a/www/manager6/node/Disks.js +++ /dev/null @@ -1,387 +0,0 @@ -Ext.define('PVE.node.DiskList', { -extend: 'Ext.grid.GridPanel', -alias: 'widget.pveNodeDiskList', - -emptyText: gettext('No Disks found'), - -stateful: true, -stateId: 'grid-node-disks', - -columns: [ - { - header: gettext('Device'), - width: 150, - sortable: true, - dataIndex: 'devpath' - }, - { - header: gettext('Type'), - width: 80, - sortable: true, - dataIndex: 'type', - renderer: function(v) { - if (v === 'ssd') { - return 'SSD'; - } else if (v === 'hdd') { - return 'Hard Disk'; - } else if (v === 'usb'){ - return 'USB'; - } else { - return gettext('Unknown'); - } - } - }, - { - header: gettext('Usage'), - width: 150, - sortable: false, - renderer: function(v, metaData, rec) { - if (rec) { - if (rec.data.osdid >= 0) { - var bluestore = ''; - if (rec.data.bluestore === 1) { - bluestore = ' (Bluestore)'; - } - return "Ceph osd." + rec.data.osdid.toString() + bluestore; - } - - var types = []; - if (rec.data.journals > 0) { - types.push('Journal'); - } - - if (rec.data.db > 0) { - types.push('DB'); - } - - if (rec.data.wal > 0) { - types.push('WAL'); - } - - if (types.length > 0) { - return 'Ceph (' + types.join(', ') + ')'; - } - } - - return v || Proxmox.Utils.noText; - }, - dataIndex: 'used' - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: 'GPT', - width: 60, - align: 'right', - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'gpt' - }, - { - header: gettext('Vendor'), - width: 100, - sortable: true, - hidden: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'vendor' - }, - { - header: gettext('Model'), - width: 200, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'model' - }, - { - header: gettext('Serial'), - width: 200, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'serial' - }, - { - header: 'S.M.A.R.T.', - width: 100, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'health' - }, - { - header: 'Wearout', - width: 90, - sortable: true, - align: 'righ
[pve-devel] [PATCH widget-toolkit v2 2/4] show Task warnings differently
tasks can now show also 'WARNINGS: ' filter it out and provide a 'parse_task_status' function for easy reuse Signed-off-by: Dominik Csapak --- src/Utils.js | 17 + src/css/ext6-pmx.css | 4 src/node/Tasks.js| 22 -- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/Utils.js b/src/Utils.js index c3b13f4..2163794 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -711,6 +711,23 @@ utilities: { return task; }, +parse_task_status: function(status) { + if (status === 'OK') { + return 'ok'; + } + + if (status === 'unknown') { + return 'unknown'; + } + + let match = status.match(/^WARNINGS: (.*)$/); + if (match) { + return 'warning'; + } + + return 'error'; +}, + render_duration: function(value) { if (value === undefined) { return '-'; diff --git a/src/css/ext6-pmx.css b/src/css/ext6-pmx.css index a7d446b..df5c73d 100644 --- a/src/css/ext6-pmx.css +++ b/src/css/ext6-pmx.css @@ -14,6 +14,10 @@ background-color: #f3d6d7; } +.proxmox-warning-row { +background-color: #f5e5d8; +} + /* some icons have to be color manually */ .black { color: #000; diff --git a/src/node/Tasks.js b/src/node/Tasks.js index 3d9267e..5aff06d 100644 --- a/src/node/Tasks.js +++ b/src/node/Tasks.js @@ -81,8 +81,13 @@ Ext.define('Proxmox.node.Tasks', { getRowClass: function(record, index) { let status = record.get('status'); - if (status && status !== 'OK') { - return "proxmox-invalid-row"; + if (status) { + let parsed = Proxmox.Utils.parse_task_status(status); + if (parsed === 'error') { + return "proxmox-invalid-row"; + } else if (parsed === 'warning') { + return "proxmox-warning-row"; + } } return ''; }, @@ -162,14 +167,19 @@ Ext.define('Proxmox.node.Tasks', { dataIndex: 'status', width: 200, renderer: function(value, metaData, record) { - if (value === 'OK') { - return 'OK'; - } if (value === undefined && !record.data.endtime) { metaData.tdCls = "x-grid-row-loading"; return ''; } - return "ERROR: " + value; + + let parsed = Proxmox.Utils.parse_task_status(value); + switch (parsed) { + case 'unknown': return Proxmox.Utils.unknownText; + case 'error': return Proxmox.Utils.errorText + ': ' + value; + case 'ok': // fall-through + case 'warning': // fall-through + default: return value; + } }, }, ], -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit v2 3/4] TaskViewer: show endtime and duration in status
but only when the caller gives us the endtime, since the status api call does not give us the endtime Signed-off-by: Dominik Csapak --- changes from v1: * always show duration and calculate the endtime from 'now' src/node/Tasks.js| 1 + src/window/TaskViewer.js | 25 + 2 files changed, 26 insertions(+) diff --git a/src/node/Tasks.js b/src/node/Tasks.js index 5aff06d..c41f0b5 100644 --- a/src/node/Tasks.js +++ b/src/node/Tasks.js @@ -60,6 +60,7 @@ Ext.define('Proxmox.node.Tasks', { let win = Ext.create('Proxmox.window.TaskViewer', { upid: rec.data.upid, + endtime: rec.data.endtime, }); win.show(); }; diff --git a/src/window/TaskViewer.js b/src/window/TaskViewer.js index 2f31023..51715f8 100644 --- a/src/window/TaskViewer.js +++ b/src/window/TaskViewer.js @@ -152,6 +152,31 @@ Ext.define('Proxmox.window.TaskViewer', { }, }; + if (me.endtime) { + if (typeof me.endtime === 'object') { + // convert to epoch + me.endtime = parseInt(me.endtime.getTime()/1000, 10); + } + rows.endtime = { + header: gettext('End Time'), + required: true, + renderer: function() { + return Proxmox.Utils.render_timestamp(me.endtime); + }, + }; + } + + rows.duration = { + header: gettext('Duration'), + required: true, + renderer: function() { + let starttime = statgrid.getObjectValue('starttime'); + let endtime = me.endtime || Date.now()/1000; + let duration = endtime - starttime; + return Proxmox.Utils.format_duration_human(duration); + }, + }; + let statstore = Ext.create('Proxmox.data.ObjectStore', { url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", interval: 1000, -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit v2 1/4] ProxmoxProxy: add duration fields for proxmox-tasks
Signed-off-by: Dominik Csapak --- changes from v1: * do not truncate the decimal places src/data/ProxmoxProxy.js | 12 1 file changed, 12 insertions(+) diff --git a/src/data/ProxmoxProxy.js b/src/data/ProxmoxProxy.js index 53e92f3..7df8f28 100644 --- a/src/data/ProxmoxProxy.js +++ b/src/data/ProxmoxProxy.js @@ -43,6 +43,18 @@ Ext.define('Proxmox.RestProxy', { { name: 'starttime', type: 'date', dateFormat: 'timestamp' }, { name: 'endtime', type: 'date', dateFormat: 'timestamp' }, { name: 'pid', type: 'int' }, + { + name: 'duration', + sortType: 'asInt', + calculate: function(data) { + let endtime = data.endtime; + let starttime = data.starttime; + if (endtime !== undefined) { + return (endtime - starttime)/1000; + } + return 0; + }, + }, 'node', 'upid', 'user', 'status', 'type', 'id', ], idProperty: 'upid', -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit v2 4/4] format_duration_human: say <0.1s instead of 0s
if we get a duration of <=0.1s it should actually be somewhere betweeen 0 and 0.1 so return <0.1s Signed-off-by: Dominik Csapak --- changes from v1: * change <1s to <0.1s which is more inline with our normal display (e.g. 1.5s) src/Utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Utils.js b/src/Utils.js index 2163794..b5b1acb 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -157,8 +157,8 @@ utilities: { format_duration_human: function(ut) { let seconds = 0, minutes = 0, hours = 0, days = 0; - if (ut <= 0) { - return '0s'; + if (ut <= 0.1) { + return '<0.1s'; } let remaining = ut; -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 2/4] show Task warnings differently
tasks can now show also 'WARNINGS: ' filter it out and provide a 'parse_task_status' function for easy reuse Signed-off-by: Dominik Csapak --- src/Utils.js | 17 + src/css/ext6-pmx.css | 4 src/node/Tasks.js| 22 -- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/Utils.js b/src/Utils.js index c3b13f4..2163794 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -711,6 +711,23 @@ utilities: { return task; }, +parse_task_status: function(status) { + if (status === 'OK') { + return 'ok'; + } + + if (status === 'unknown') { + return 'unknown'; + } + + let match = status.match(/^WARNINGS: (.*)$/); + if (match) { + return 'warning'; + } + + return 'error'; +}, + render_duration: function(value) { if (value === undefined) { return '-'; diff --git a/src/css/ext6-pmx.css b/src/css/ext6-pmx.css index a7d446b..df5c73d 100644 --- a/src/css/ext6-pmx.css +++ b/src/css/ext6-pmx.css @@ -14,6 +14,10 @@ background-color: #f3d6d7; } +.proxmox-warning-row { +background-color: #f5e5d8; +} + /* some icons have to be color manually */ .black { color: #000; diff --git a/src/node/Tasks.js b/src/node/Tasks.js index 3d9267e..5aff06d 100644 --- a/src/node/Tasks.js +++ b/src/node/Tasks.js @@ -81,8 +81,13 @@ Ext.define('Proxmox.node.Tasks', { getRowClass: function(record, index) { let status = record.get('status'); - if (status && status !== 'OK') { - return "proxmox-invalid-row"; + if (status) { + let parsed = Proxmox.Utils.parse_task_status(status); + if (parsed === 'error') { + return "proxmox-invalid-row"; + } else if (parsed === 'warning') { + return "proxmox-warning-row"; + } } return ''; }, @@ -162,14 +167,19 @@ Ext.define('Proxmox.node.Tasks', { dataIndex: 'status', width: 200, renderer: function(value, metaData, record) { - if (value === 'OK') { - return 'OK'; - } if (value === undefined && !record.data.endtime) { metaData.tdCls = "x-grid-row-loading"; return ''; } - return "ERROR: " + value; + + let parsed = Proxmox.Utils.parse_task_status(value); + switch (parsed) { + case 'unknown': return Proxmox.Utils.unknownText; + case 'error': return Proxmox.Utils.errorText + ': ' + value; + case 'ok': // fall-through + case 'warning': // fall-through + default: return value; + } }, }, ], -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 4/4] format_duration_human: say <1s instead of 0s
if we get a duration of <=0s it should actually be somewhere betweeen 0 and 1s so return <1s Signed-off-by: Dominik Csapak --- src/Utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utils.js b/src/Utils.js index 2163794..a914795 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -158,7 +158,7 @@ utilities: { let seconds = 0, minutes = 0, hours = 0, days = 0; if (ut <= 0) { - return '0s'; + return '<1s'; } let remaining = ut; -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 3/4] TaskViewer: show endtime and duration in status
but only when the caller gives us the endtime, since the status api call does not give us the endtime Signed-off-by: Dominik Csapak --- src/node/Tasks.js| 1 + src/window/TaskViewer.js | 23 +++ 2 files changed, 24 insertions(+) diff --git a/src/node/Tasks.js b/src/node/Tasks.js index 5aff06d..c41f0b5 100644 --- a/src/node/Tasks.js +++ b/src/node/Tasks.js @@ -60,6 +60,7 @@ Ext.define('Proxmox.node.Tasks', { let win = Ext.create('Proxmox.window.TaskViewer', { upid: rec.data.upid, + endtime: rec.data.endtime, }); win.show(); }; diff --git a/src/window/TaskViewer.js b/src/window/TaskViewer.js index 2f31023..fedf693 100644 --- a/src/window/TaskViewer.js +++ b/src/window/TaskViewer.js @@ -152,6 +152,29 @@ Ext.define('Proxmox.window.TaskViewer', { }, }; + if (me.endtime) { + if (typeof me.endtime === 'object') { + // convert to epoch + me.endtime = parseInt(me.endtime.getTime()/1000, 10); + } + rows.endtime = { + header: gettext('End Time'), + required: true, + renderer: function() { + return Proxmox.Utils.render_timestamp(me.endtime); + }, + }; + rows.duration = { + header: gettext('Duration'), + required: true, + renderer: function() { + let starttime = statgrid.getObjectValue('starttime'); + let duration = me.endtime - starttime; + return Proxmox.Utils.format_duration_human(duration); + }, + }; + } + let statstore = Ext.create('Proxmox.data.ObjectStore', { url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", interval: 1000, -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 1/4] ProxmoxProxy: add duration fields for proxmox-tasks
Signed-off-by: Dominik Csapak --- src/data/ProxmoxProxy.js | 12 1 file changed, 12 insertions(+) diff --git a/src/data/ProxmoxProxy.js b/src/data/ProxmoxProxy.js index 53e92f3..40fdc08 100644 --- a/src/data/ProxmoxProxy.js +++ b/src/data/ProxmoxProxy.js @@ -43,6 +43,18 @@ Ext.define('Proxmox.RestProxy', { { name: 'starttime', type: 'date', dateFormat: 'timestamp' }, { name: 'endtime', type: 'date', dateFormat: 'timestamp' }, { name: 'pid', type: 'int' }, + { + name: 'duration', + sortType: 'asInt', + calculate: function(data) { + let endtime = data.endtime; + let starttime = data.starttime; + if (endtime !== undefined) { + return ((endtime - starttime)/1000).toFixed(0); + } + return 0; + }, + }, 'node', 'upid', 'user', 'status', 'type', 'id', ], idProperty: 'upid', -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit] button: make xtype of parent configurable
to get the selection model of the parent, we use by default the xtype 'grid', but sometimes we want to use something else (e.g. 'treepanel') to be flexible we make this configurable Signed-off-by: Dominik Csapak --- we could of course fall back to 'treepanel' directly in the code if thats preferred button/Button.js | 9 ++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/button/Button.js b/button/Button.js index 68c6e98..9b8d66e 100644 --- a/button/Button.js +++ b/button/Button.js @@ -20,6 +20,9 @@ Ext.define('Proxmox.button.Button', { // take special care in confirm box (select no as default). dangerous: false, +// is used to get the parent container for its selection model +parentXType: 'grid', + initComponent: function() { var me = this; @@ -65,9 +68,9 @@ Ext.define('Proxmox.button.Button', { var grid; if (!me.selModel && me.selModel !== null && me.selModel !== false) { - grid = me.up('grid'); - if (grid && grid.selModel) { - me.selModel = grid.selModel; + parent = me.up(me.parentXType); + if (parent && parent.selModel) { + me.selModel = parent.selModel; } } -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH http-server 2/2] fix post if variable declaration
Signed-off-by: Dominik Csapak --- PVE/APIServer/AnyEvent.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PVE/APIServer/AnyEvent.pm b/PVE/APIServer/AnyEvent.pm index e5f2cb4..efb8168 100644 --- a/PVE/APIServer/AnyEvent.pm +++ b/PVE/APIServer/AnyEvent.pm @@ -1322,7 +1322,8 @@ sub unshift_read_header { } my $ctype = $r->header('Content-Type'); - my ($ct, $boundary) = parse_content_type($ctype) if $ctype; + my ($ct, $boundary); + ($ct, $boundary)= parse_content_type($ctype) if $ctype; if ($auth->{isUpload} && !$self->{trusted_env}) { die "upload 'Content-Type '$ctype' not implemented\n" -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH http-server 1/2] fix #2766: allow application/json as content-type for post/put requests
this makes creating an api client much nicer Signed-off-by: Dominik Csapak --- PVE/APIServer/AnyEvent.pm | 13 +++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/PVE/APIServer/AnyEvent.pm b/PVE/APIServer/AnyEvent.pm index 3d755d4..e5f2cb4 100644 --- a/PVE/APIServer/AnyEvent.pm +++ b/PVE/APIServer/AnyEvent.pm @@ -44,6 +44,7 @@ use HTTP::Headers; use HTTP::Request; use HTTP::Response; use Data::Dumper; +use JSON; my $limit_max_headers = 30; my $limit_max_header_size = 8*1024; @@ -693,7 +694,15 @@ sub extract_params { my $params = {}; if ($method eq 'PUT' || $method eq 'POST') { - $params = decode_urlencoded($r->content); + my $ct; + if (my $ctype = $r->header('Content-Type')) { + $ct = parse_content_type($ctype); + } + if (defined($ct) && $ct eq 'application/json') { + $params = decode_json($r->content); + } else { + $params = decode_urlencoded($r->content); + } } my $query_params = decode_urlencoded($r->url->query()); @@ -1356,7 +1365,7 @@ sub unshift_read_header { return; } - if (!$ct || $ct eq 'application/x-www-form-urlencoded') { + if (!$ct || $ct eq 'application/x-www-form-urlencoded' || $ct eq 'application/json') { $reqstate->{hdl}->unshift_read(chunk => $len, sub { my ($hdl, $data) = @_; $r->content($data); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager] ui: add checkbox for vmid filter for backupview
instead of hardcoding the text 'type-id-' into the searchbar to accomodate for the additional size, add an overflowHandler to the toolbar (for very small display sizes) Signed-off-by: Dominik Csapak --- www/manager6/grid/BackupView.js | 49 ++--- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/www/manager6/grid/BackupView.js b/www/manager6/grid/BackupView.js index a74c6ccb..5d80bdaf 100644 --- a/www/manager6/grid/BackupView.js +++ b/www/manager6/grid/BackupView.js @@ -41,13 +41,16 @@ Ext.define('PVE.grid.BackupView', { var searchFilter = { property: 'volid', - // on initial store display only our vmid backups - // surround with minus sign to prevent the 2016 VMID bug - value: vmtype + '-' + vmid + '-', + value: '', anyMatch: true, caseSensitive: false }; + var vmidFilter = { + property: 'vmid', + value: vmid, + }; + me.store = Ext.create('Ext.data.Store', { model: 'pve-storage-content', sorters: { @@ -56,10 +59,19 @@ Ext.define('PVE.grid.BackupView', { }, filters: [ vmtypeFilter, - searchFilter + searchFilter, + vmidFilter, ] }); + let updateFilter = function() { + me.store.filter([ + vmtypeFilter, + searchFilter, + vmidFilter, + ]); + }; + var reload = Ext.Function.createBuffered(function() { if (me.store) { me.store.load(); @@ -102,14 +114,23 @@ Ext.define('PVE.grid.BackupView', { keyup: function(field) { me.store.clearFilter(true); searchFilter.value = field.getValue(); - me.store.filter([ - vmtypeFilter, - searchFilter - ]); + updateFilter(); } } }); + var vmidfilterCB = Ext.create('Ext.form.field.Checkbox', { + fieldLabel: gettext('Filter ID'), + labelAlign: 'right', + value: '1', + listeners: { + change: function(cb, value) { + vmidFilter.value = !!value ? vmid : ''; + updateFilter(); + }, + }, + }); + var sm = Ext.create('Ext.selection.RowModel', {}); var backup_btn = Ext.create('Ext.button.Button', { @@ -196,7 +217,10 @@ Ext.define('PVE.grid.BackupView', { Ext.apply(me, { selModel: sm, - tbar: [ backup_btn, restore_btn, delete_btn,config_btn, '->', storagesel, storagefilter ], + tbar: { + overflowHandler: 'scroller', + items: [ backup_btn, restore_btn, delete_btn,config_btn, '->', storagesel, vmidfilterCB, storagefilter ], + }, columns: [ { header: gettext('Name'), @@ -220,7 +244,12 @@ Ext.define('PVE.grid.BackupView', { width: 100, renderer: Proxmox.Utils.format_size, dataIndex: 'size' - } + }, + { + header: gettext('VMID'), + dataIndex: 'vmid', + hidden: true, + }, ] }); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 1/2] ui: fix missing change from 'pve-' to 'pmx-' models
we forgot to change these Signed-off-by: Dominik Csapak --- www/manager6/dc/AuthView.js | 2 +- www/manager6/dc/RoleView.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/www/manager6/dc/AuthView.js b/www/manager6/dc/AuthView.js index 3e5a8517..bb5ec851 100644 --- a/www/manager6/dc/AuthView.js +++ b/www/manager6/dc/AuthView.js @@ -41,7 +41,7 @@ Ext.define('PVE.dc.AuthView', { ], store: { - model: 'pve-domains', + model: 'pmx-domains', sorters: { property: 'realm', order: 'DESC', diff --git a/www/manager6/dc/RoleView.js b/www/manager6/dc/RoleView.js index e21e2920..3a25da80 100644 --- a/www/manager6/dc/RoleView.js +++ b/www/manager6/dc/RoleView.js @@ -12,7 +12,7 @@ Ext.define('PVE.dc.RoleView', { var me = this; var store = new Ext.data.Store({ - model: 'pve-roles', + model: 'pmx-roles', sorters: { property: 'roleid', order: 'DESC' -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 2/2] ui: fix HotplugFeatureSelector
we recently changed the setValue behaviour of the inputpanel and editwindow (we now set all fields with the same names), which leads to wrong behaviour here use a different name for the internal checkboxes to avoid this Signed-off-by: Dominik Csapak --- www/manager6/form/HotplugFeatureSelector.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/manager6/form/HotplugFeatureSelector.js b/www/manager6/form/HotplugFeatureSelector.js index 3bf394bc..a79fb845 100644 --- a/www/manager6/form/HotplugFeatureSelector.js +++ b/www/manager6/form/HotplugFeatureSelector.js @@ -6,7 +6,7 @@ Ext.define('PVE.form.HotplugFeatureSelector', { vertical: true, defaults: { - name: 'hotplug', + name: 'hotplugCbGroup', submitValue: false }, items: [ @@ -43,7 +43,7 @@ Ext.define('PVE.form.HotplugFeatureSelector', { } else if (value !== '0') { newVal = value.split(','); } - me.callParent([{ hotplug: newVal }]); + me.callParent([{ hotplugCbGroup: newVal }]); }, // override framework function to -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit/manager] some refactorings
just moving things to widget-toolkit manager patches need the toolkit ones, but they are not breaking manager proxmox-widget-toolkit: Dominik Csapak (2): Utils: add duration format/render css: add icon colors Utils.js | 27 +++ css/ext6-pmx.css | 25 + 2 files changed, 52 insertions(+) pve-manager: Dominik Csapak (2): ui: Replication: use render_duration from widget-toolkit css: remove icon colors www/css/ext6-pve.css | 25 - www/manager6/Utils.js| 27 --- www/manager6/grid/Replication.js | 2 +- 3 files changed, 1 insertion(+), 53 deletions(-) -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 1/2] ui: Replication: use render_duration from widget-toolkit
Signed-off-by: Dominik Csapak --- www/manager6/Utils.js| 27 --- www/manager6/grid/Replication.js | 2 +- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js index 676f56a8..5c81d7f8 100644 --- a/www/manager6/Utils.js +++ b/www/manager6/Utils.js @@ -536,26 +536,6 @@ Ext.define('PVE.Utils', { utilities: { return msg; }, -format_duration_short: function(ut) { - - if (ut < 60) { - return ut.toFixed(1) + 's'; - } - - if (ut < 3600) { - var mins = ut / 60; - return mins.toFixed(1) + 'm'; - } - - if (ut < 86400) { - var hours = ut / 3600; - return hours.toFixed(1) + 'h'; - } - - var days = ut / 86400; - return days.toFixed(1) + 'd'; -}, - contentTypes: { 'images': gettext('Disk image'), 'backup': gettext('VZDump backup file'), @@ -836,13 +816,6 @@ Ext.define('PVE.Utils', { utilities: { return Ext.Date.format(new Date(value * 1000), 'l d F Y H:i:s'); }, -render_duration: function(value) { - if (value === undefined) { - return '-'; - } - return PVE.Utils.format_duration_short(value); -}, - calculate_mem_usage: function(data) { if (!Ext.isNumeric(data.mem) || data.maxmem === 0 || diff --git a/www/manager6/grid/Replication.js b/www/manager6/grid/Replication.js index 40f15982..eabf5524 100644 --- a/www/manager6/grid/Replication.js +++ b/www/manager6/grid/Replication.js @@ -382,7 +382,7 @@ Ext.define('PVE.grid.ReplicaView', { text: gettext('Duration'), dataIndex: 'duration', width: 60, - renderer: PVE.Utils.render_duration + renderer: Proxmox.Utils.render_duration }, { text: gettext('Next Sync'), -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 1/2] Utils: add duration format/render
from pve-manager Signed-off-by: Dominik Csapak --- Utils.js | 27 +++ 1 file changed, 27 insertions(+) diff --git a/Utils.js b/Utils.js index 56b1c9a..328164d 100644 --- a/Utils.js +++ b/Utils.js @@ -145,6 +145,26 @@ Ext.define('Proxmox.Utils', { utilities: { return Ext.Date.format(date, "Y-m-d"); }, +format_duration_short: function(ut) { + + if (ut < 60) { + return ut.toFixed(1) + 's'; + } + + if (ut < 3600) { + var mins = ut / 60; + return mins.toFixed(1) + 'm'; + } + + if (ut < 86400) { + var hours = ut / 3600; + return hours.toFixed(1) + 'h'; + } + + var days = ut / 86400; + return days.toFixed(1) + 'd'; +}, + format_duration_long: function(ut) { var days = Math.floor(ut / 86400); @@ -643,6 +663,13 @@ Ext.define('Proxmox.Utils', { utilities: { return task; }, +render_duration: function(value) { + if (value === undefined) { + return '-'; + } + return Proxmox.Utils.format_duration_short(value); +}, + render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) { var servertime = new Date(value * 1000); return Ext.Date.format(servertime, 'Y-m-d H:i:s'); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 2/2] css: remove icon colors
they are now in the widget-toolkit Signed-off-by: Dominik Csapak --- www/css/ext6-pve.css | 25 - 1 file changed, 25 deletions(-) diff --git a/www/css/ext6-pve.css b/www/css/ext6-pve.css index 4294f659..8f0407df 100644 --- a/www/css/ext6-pve.css +++ b/www/css/ext6-pve.css @@ -532,31 +532,6 @@ div.right-aligned { color: #000; } -/* some icons have to be color manually */ -.black { -color: #000; -} - -.normal { -color: #c2ddf2; -} - -.faded { -color: #cfcfcf; -} - -.good { -color: #21BF4B; -} - -.warning { -color: #fc0; -} - -.critical { -color: #FF6C59; -} - /* for the ceph monitor widgets */ div.monitor { text-align:left; -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 2/2] css: add icon colors
from pve-manager Signed-off-by: Dominik Csapak --- css/ext6-pmx.css | 25 + 1 file changed, 25 insertions(+) diff --git a/css/ext6-pmx.css b/css/ext6-pmx.css index 9b15392..37ee6aa 100644 --- a/css/ext6-pmx.css +++ b/css/ext6-pmx.css @@ -13,3 +13,28 @@ .proxmox-invalid-row { background-color: #f3d6d7; } + +/* some icons have to be color manually */ +.black { +color: #000; +} + +.normal { +color: #c2ddf2; +} + +.faded { +color: #cfcfcf; +} + +.good { +color: #21BF4B; +} + +.warning { +color: #fc0; +} + +.critical { +color: #FF6C59; +} -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] [RFC manager 2/2] hardware view: Add disk import button
sry for the delay of the review a few comments inline On 5/22/20 12:08 PM, Dominic Jäger wrote: Is it a bad idea to move column2 as I did here? Seems strange to have column1 and 2 so different but I haven't found an easier way to make it available to the subclass yet. @Thomas Is this sort of what you had in mind for this feature? i do not know what you're talked about, but seeing this i would rather put the 'importing' code directly into the hdedit panel, and make the 'mode' configurable since most of the logic is in the window anyway. that would avoid the whole issue of the accessing columns of the superclass etc. Signed-off-by: Dominic Jäger --- www/manager6/Makefile| 1 + www/manager6/form/DiskStorageSelector.js | 5 + www/manager6/qemu/HDEdit.js | 50 + www/manager6/qemu/HDImport.js| 128 +++ www/manager6/qemu/HardwareView.js| 13 +++ 5 files changed, 176 insertions(+), 21 deletions(-) create mode 100644 www/manager6/qemu/HDImport.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index a29e280d..c1645748 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -143,6 +143,7 @@ JSSRC= \ qemu/Smbios1Edit.js \ qemu/CDEdit.js \ qemu/HDEdit.js \ + qemu/HDImport.js\ qemu/HDResize.js\ qemu/HDMove.js \ qemu/HDEfi.js \ diff --git a/www/manager6/form/DiskStorageSelector.js b/www/manager6/form/DiskStorageSelector.js index 445e3ac0..2c1555e1 100644 --- a/www/manager6/form/DiskStorageSelector.js +++ b/www/manager6/form/DiskStorageSelector.js @@ -32,6 +32,11 @@ Ext.define('PVE.form.DiskStorageSelector', { // string because else we get a type confusion defaultSize: '32', +setDiskSize: function(newSize) { + let field = this.getComponent('disksize'); + field.setValue(newSize); +}, + changeStorage: function(f, value) { var me = this; var formatsel = me.getComponent('diskformat'); diff --git a/www/manager6/qemu/HDEdit.js b/www/manager6/qemu/HDEdit.js index fd890600..5d6c12e9 100644 --- a/www/manager6/qemu/HDEdit.js +++ b/www/manager6/qemu/HDEdit.js @@ -13,6 +13,28 @@ Ext.define('PVE.qemu.HDInputPanel', { viewModel: {}, +diskStorageSelector: { + xtype: 'pveDiskStorageSelector', + storageContent: 'images', + name: 'disk', + reference: 'storageSelector', +}, + +column2: [ + { + xtype: 'CacheTypeSelector', + name: 'cache', + value: '__default__', + fieldLabel: gettext('Cache') + }, + { + xtype: 'proxmoxcheckbox', + fieldLabel: gettext('Discard'), + reference: 'discard', + name: 'discard' + }, +], + controller: { xclass: 'Ext.app.ViewController', @@ -164,7 +186,6 @@ Ext.define('PVE.qemu.HDInputPanel', { me.drive = {}; me.column1 = []; - me.column2 = []; me.advancedColumn1 = []; me.advancedColumn2 = []; @@ -188,6 +209,8 @@ Ext.define('PVE.qemu.HDInputPanel', { me.column1.push(me.scsiController); } + me.diskStorageSelector.nodename = me.nodename; + me.diskStorageSelector.autoSelect = me.insideWizard; if (me.unused) { me.unusedDisks = Ext.create('Proxmox.form.KVComboBox', { name: 'unusedId', @@ -201,13 +224,7 @@ Ext.define('PVE.qemu.HDInputPanel', { }); me.column1.push(me.unusedDisks); } else if (me.isCreate) { - me.column1.push({ - xtype: 'pveDiskStorageSelector', - storageContent: 'images', - name: 'disk', - nodename: me.nodename, - autoSelect: me.insideWizard - }); + me.column1.push(me.diskStorageSelector); } else { me.column1.push({ xtype: 'textfield', @@ -219,18 +236,6 @@ Ext.define('PVE.qemu.HDInputPanel', { } me.column2.push( - { - xtype: 'CacheTypeSelector', - name: 'cache', - value: '__default__', - fieldLabel: gettext('Cache') - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Discard'), - reference: 'discard', - name: 'discard' - } ); me.advancedColumn1.push( @@ -358,7 +363,9 @@ Ext.define('PVE.qemu.HDEdit', { backgroundDelay: 5, -initComponent : function() { +isImport: false, + +initComponent:
[pve-devel] [PATCH widget-toolkit] improve error extraction for monStoreErrors
by printing the whole error body when it cannot be parsed as JSON Signed-off-by: Dominik Csapak --- Utils.js | 15 ++- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Utils.js b/Utils.js index 33c9b77..56b1c9a 100644 --- a/Utils.js +++ b/Utils.js @@ -244,17 +244,22 @@ Ext.define('Proxmox.Utils', { utilities: { if (!err.statusText) { return gettext('Connection error'); } - let msg = `${err.statusText} (${err.status})`; + let msg = [`${err.statusText} (${err.status})`]; if (err.response && err.response.responseText) { let txt = err.response.responseText; try { let res = JSON.parse(txt) - for (let [key, value] of Object.entries(res.errors)) { - msg += `${key}: ${value}`; + if (res.errors && typeof res.errors === 'object') { + for (let [key, value] of Object.entries(res.errors)) { + msg.push(Ext.String.htmlEncode(`${key}: ${value}`)); + } } - } catch (e) { /* TODO? */ } + } catch (e) { + // fallback to string + msg.push(Ext.String.htmlEncode(txt)); + } } - return msg; + return msg.join(''); }, monStoreErrors: function(me, store, clearMaskBeforeLoad) { -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit] return cookie again in authOK
the calling code did require that authOK returns the cookie if there is a valid one make it now very explicit that the cookie gets returned instead of using implicit short-circuit behaviour Signed-off-by: Dominik Csapak --- Utils.js | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Utils.js b/Utils.js index cae25b2..402349a 100644 --- a/Utils.js +++ b/Utils.js @@ -208,7 +208,11 @@ Ext.define('Proxmox.Utils', { utilities: { return undefined; } let cookie = Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name); - return (Proxmox.UserName !== '') && (cookie && !cookie.startsWith("PVE:tfa!")); + if (Proxmox.UserName !== '' && cookie && !cookie.startsWith("PVE:tfa!")) { + return cookie; + } else { + return false; + } }, authClear: function() { -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit] fix #2758: reject 'tfa' cookies
return false on authOK when the ticket is a tfa ticket (starts with PVE:tfa!) when a user now loads the page with only a tfa ticket, it shows the login window again Signed-off-by: Dominik Csapak --- Utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Utils.js b/Utils.js index 22eddd2..cae25b2 100644 --- a/Utils.js +++ b/Utils.js @@ -207,7 +207,8 @@ Ext.define('Proxmox.Utils', { utilities: { if (Proxmox.LoggedOut) { return undefined; } - return (Proxmox.UserName !== '') && Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name); + let cookie = Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name); + return (Proxmox.UserName !== '') && (cookie && !cookie.startsWith("PVE:tfa!")); }, authClear: function() { -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 1/1] add pmxRoleSelector
copied+refactored from pve-manager for use with other projects also show privs now in the combobox Signed-off-by: Dominik Csapak --- Makefile | 1 + form/RoleSelector.js | 41 + 2 files changed, 42 insertions(+) create mode 100644 form/RoleSelector.js diff --git a/Makefile b/Makefile index a23ad04..13f88d6 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,7 @@ JSSRC=\ form/BondModeSelector.js\ form/NetworkSelector.js \ form/RealmComboBox.js \ + form/RoleSelector.js\ button/Button.js\ button/HelpButton.js\ grid/ObjectGrid.js \ diff --git a/form/RoleSelector.js b/form/RoleSelector.js new file mode 100644 index 000..142cdfd --- /dev/null +++ b/form/RoleSelector.js @@ -0,0 +1,41 @@ +Ext.define('pmx-roles', { +extend: 'Ext.data.Model', +fields: ['roleid', 'privs'], +proxy: { + type: 'proxmox', + url: "/api2/json/access/roles", +}, +idProperty: 'roleid', +}); + +Ext.define('Proxmox.form.RoleSelector', { +extend: 'Proxmox.form.ComboGrid', +alias: 'widget.pmxRoleSelector', + +allowBlank: false, +autoSelect: false, +valueField: 'roleid', +displayField: 'roleid', + +listConfig: { + columns: [ + { + header: gettext('Role'), + sortable: true, + dataIndex: 'roleid', + flex: 1, + }, + { + header: gettext('Privileges'), + dataIndex: 'privs', + flex: 1, + }, + ], +}, + +store: { + autoLoad: true, + model: 'pmx-roles', + sorters: 'roleid', +}, +}); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 2/2] use RealmCombobox from widget-toolkit
Signed-off-by: Dominik Csapak --- www/manager6/dc/UserEdit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/manager6/dc/UserEdit.js b/www/manager6/dc/UserEdit.js index 692eb277..584fe19f 100644 --- a/www/manager6/dc/UserEdit.js +++ b/www/manager6/dc/UserEdit.js @@ -119,7 +119,7 @@ Ext.define('PVE.dc.UserEdit', { if (me.isCreate) { column1.splice(1,0,{ -xtype: 'pveRealmComboBox', +xtype: 'pmxRealmComboBox', name: 'realm', fieldLabel: gettext('Realm'), allowBlank: false, -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 1/2] ui: use RoleSelector from widget-toolkit
Signed-off-by: Dominik Csapak --- www/manager6/Makefile | 1 - www/manager6/dc/ACLView.js| 2 +- www/manager6/form/RoleSelector.js | 50 --- 3 files changed, 1 insertion(+), 52 deletions(-) delete mode 100644 www/manager6/form/RoleSelector.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index a29e280d..5656ba27 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -26,7 +26,6 @@ JSSRC= \ form/PrivilegesSelector.js \ form/GroupSelector.js \ form/UserSelector.js\ - form/RoleSelector.js\ form/GuestIDSelector.js \ form/MemoryField.js \ form/NetworkCardSelector.js \ diff --git a/www/manager6/dc/ACLView.js b/www/manager6/dc/ACLView.js index 24fd67d9..e9928b0f 100644 --- a/www/manager6/dc/ACLView.js +++ b/www/manager6/dc/ACLView.js @@ -46,7 +46,7 @@ Ext.define('PVE.dc.ACLAdd', { } items.push({ - xtype: 'pveRoleSelector', + xtype: 'pmxRoleSelector', name: 'roles', value: 'NoAccess', fieldLabel: gettext('Role') diff --git a/www/manager6/form/RoleSelector.js b/www/manager6/form/RoleSelector.js deleted file mode 100644 index 04b5b538.. --- a/www/manager6/form/RoleSelector.js +++ /dev/null @@ -1,50 +0,0 @@ -Ext.define('PVE.form.RoleSelector', { -extend: 'Proxmox.form.ComboGrid', -alias: ['widget.pveRoleSelector'], - -allowBlank: false, -autoSelect: false, -valueField: 'roleid', -displayField: 'roleid', -initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-roles', - sorters: [{ - property: 'roleid' - }] - }); - - Ext.apply(me, { - store: store, -listConfig: { - columns: [ - { - header: gettext('Role'), - sortable: true, - dataIndex: 'roleid', - flex: 1 - } - ] - } - }); - -me.callParent(); - - store.load(); -} - -}, function() { - -Ext.define('pve-roles', { - extend: 'Ext.data.Model', - fields: [ 'roleid', 'privs' ], - proxy: { -type: 'proxmox', - url: "/api2/json/access/roles" - }, - idProperty: 'roleid' -}); - -}); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit/manager] move RoleSelector to widget-toolkit
for use in other projects also fixes a missing rename of the RealmCombobox in dc/UserEdit (manager 2/2) proxmox-widget-toolkit: Dominik Csapak (1): add pmxRoleSelector Makefile | 1 + form/RoleSelector.js | 41 + 2 files changed, 42 insertions(+) create mode 100644 form/RoleSelector.js pve-manager: Dominik Csapak (2): ui: use RoleSelector from widget-toolkit use RealmCombobox from widget-toolkit www/manager6/Makefile | 1 - www/manager6/dc/ACLView.js| 2 +- www/manager6/dc/UserEdit.js | 2 +- www/manager6/form/RoleSelector.js | 50 --- 4 files changed, 2 insertions(+), 53 deletions(-) delete mode 100644 www/manager6/form/RoleSelector.js -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 2/2] ui: remove Realm model and RealmComboBox
and use it from widget-toolkit Signed-off-by: Dominik Csapak --- www/manager6/Makefile | 2 - www/manager6/data/model/Realm.js | 30 -- www/manager6/form/RealmComboBox.js | 65 -- www/manager6/window/LoginWindow.js | 2 +- 4 files changed, 1 insertion(+), 98 deletions(-) delete mode 100644 www/manager6/data/model/Realm.js delete mode 100644 www/manager6/form/RealmComboBox.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index 08861cca..a29e280d 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -17,7 +17,6 @@ JSSRC= \ VNCConsole.js \ data/PermPathStore.js \ data/ResourceStore.js \ - data/model/Realm.js \ data/model/RRDModels.js \ form/VLanField.js \ form/Boolean.js \ @@ -36,7 +35,6 @@ JSSRC= \ form/BusTypeSelector.js \ form/ControllerSelector.js \ form/EmailNotificationSelector.js \ - form/RealmComboBox.js \ form/ViewSelector.js\ form/NodeSelector.js\ form/FileSelector.js\ diff --git a/www/manager6/data/model/Realm.js b/www/manager6/data/model/Realm.js deleted file mode 100644 index 01f36351.. --- a/www/manager6/data/model/Realm.js +++ /dev/null @@ -1,30 +0,0 @@ -Ext.define('pve-domains', { -extend: "Ext.data.Model", -fields: [ - 'realm', 'type', 'comment', 'default', 'tfa', - { - name: 'descr', - // Note: We use this in the RealmComboBox.js (see Bug #125) - convert: function(value, record) { - if (value) { - return value; - } - - var info = record.data; - // return realm if there is no comment - var text = info.comment || info.realm; - - if (info.tfa) { - text += " (+ " + info.tfa + ")"; - } - - return Ext.String.htmlEncode(text); - } - } -], -idProperty: 'realm', -proxy: { - type: 'proxmox', - url: "/api2/json/access/domains" -} -}); diff --git a/www/manager6/form/RealmComboBox.js b/www/manager6/form/RealmComboBox.js deleted file mode 100644 index c85fe7d2.. --- a/www/manager6/form/RealmComboBox.js +++ /dev/null @@ -1,65 +0,0 @@ -/*global Proxmox*/ -Ext.define('PVE.form.RealmComboBox', { -extend: 'Ext.form.field.ComboBox', -alias: ['widget.pveRealmComboBox'], - -controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.store.on('load', this.onLoad, view); - }, - - onLoad: function(store, records, success) { - if (!success) { - return; - } - var me = this; - var val = me.getValue(); - if (!val || !me.store.findRecord('realm', val)) { - var def = 'pam'; - Ext.each(records, function(rec) { - if (rec.data && rec.data['default']) { - def = rec.data.realm; - } - }); - me.setValue(def); - } - } -}, - -fieldLabel: gettext('Realm'), -name: 'realm', -queryMode: 'local', -allowBlank: false, -editable: false, -forceSelection: true, -autoSelect: false, -triggerAction: 'all', -valueField: 'realm', -displayField: 'descr', -getState: function() { - return { value: this.getValue() }; -}, -applyState : function(state) { - if (state && state.value) { - this.setValue(state.value); - } -}, -stateEvents: [ 'select' ], -stateful: true, // last chosen auth realm is saved between page reloads -id: 'pveloginrealm', // We need stable ids when using stateful, not autogenerated -stateID: 'pveloginrealm', - -needOTP: function(realm) { - var me = this; - // use exact match - var rec = me.store.findRecord('realm', realm, 0, false, false, true); - return rec && rec.data && rec.data.tfa ? rec.data.tfa : undefined; -}, - -store: { - model: 'pve-domains', - autoLoad: true -} -}); diff --git a/www/manager6/window/LoginWindow.js b/www/manager6/window/LoginWindow.js index e29b7352..6123c655 100644 --- a/www/manager6/window/LoginWindow.js +++ b/www/manager6/window/LoginWindow.js @@ -227,7 +227,7 @@ Ext.defi
[pve-devel] [PATCH widget-toolkit 2/3] add PMX.image.Logo
copied from pmg-gui, adapted to be able to set a custom url prefix when we want to use something other than '/pve2' Signed-off-by: Dominik Csapak --- Logo.js | 21 + Makefile | 1 + 2 files changed, 22 insertions(+) create mode 100644 Logo.js diff --git a/Logo.js b/Logo.js new file mode 100644 index 000..78d9aad --- /dev/null +++ b/Logo.js @@ -0,0 +1,21 @@ +Ext.define('PMX.image.Logo', { +extend: 'Ext.Img', +xtype: 'proxmoxlogo', + +height: 30, +width: 172, +src: '/images/proxmox_logo.png', +alt: 'Proxmox', +autoEl: { + tag: 'a', + href: 'https://www.proxmox.com', + target: '_blank', +}, + +initComponent: function() { + let me = this; + let prefix = me.prefix !== undefined ? me.prefix : '/pve2'; + me.src = prefix + me.src; + me.callParent(); +}, +}); diff --git a/Makefile b/Makefile index 2907419..85fad6b 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ SUBDIRS= css images JSSRC= \ Utils.js\ Toolkit.js \ + Logo.js \ mixin/CBind.js \ data/reader/JsonObject.js \ data/ProxmoxProxy.js\ -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 1/2] ui: use PMX.image.Logo from widget-toolkit
Signed-off-by: Dominik Csapak --- www/manager6/Workspace.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/www/manager6/Workspace.js b/www/manager6/Workspace.js index 57cb1bb9..8d224f5b 100644 --- a/www/manager6/Workspace.js +++ b/www/manager6/Workspace.js @@ -307,8 +307,7 @@ Ext.define('PVE.StdWorkspace', { margin: '2 0 2 5', items: [ { - html: 'https://www.proxmox.com;>' + - '' + xtype: 'proxmoxlogo', }, { minWidth: 150, -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 1/3] window/Edit: read digest also from top level response
we want to have the digest in the top level object, like: { data: { /* the real data */ }, digest: "fa123asf123123123", // the digest } instead of in the data itself, so read it preferably from there (with fallback to stay compatible) Signed-off-by: Dominik Csapak --- window/Edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window/Edit.js b/window/Edit.js index d21c165..6e6c0d4 100644 --- a/window/Edit.js +++ b/window/Edit.js @@ -192,7 +192,7 @@ Ext.define('Proxmox.window.Edit', { method: 'GET', success: function(response, opts) { form.clearInvalid(); - me.digest = response.result.data.digest; + me.digest = response.result.digest || response.result.data.digest; if (successFn) { successFn(response, opts); } else { -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH pmg-gui 1/1] remove Logo.js
it is now in the widget-toolkit Signed-off-by: Dominik Csapak --- js/Logo.js | 14 -- js/Makefile | 1 - 2 files changed, 15 deletions(-) delete mode 100644 js/Logo.js diff --git a/js/Logo.js b/js/Logo.js deleted file mode 100644 index 96ec14b..000 --- a/js/Logo.js +++ /dev/null @@ -1,14 +0,0 @@ -Ext.define('PMG.image.Logo', { -extend: 'Ext.Img', -xtype: 'proxmoxlogo', - -height: 30, -width: 172, -src: '/pve2/images/proxmox_logo.png', -alt: 'Proxmox', -autoEl: { - tag: 'a', - href: 'https://www.proxmox.com', - target: '_blank' -} -}); diff --git a/js/Makefile b/js/Makefile index 672eedf..d35f3bc 100644 --- a/js/Makefile +++ b/js/Makefile @@ -18,7 +18,6 @@ JSSRC= \ ActionList.js \ RuleInfo.js \ RuleEditor.js \ - Logo.js \ MainView.js \ QuarantineList.js \ SpamInfoGrid.js \ -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 3/3] add Realm model and RealmComboBox
copied from pve-manager, with adaptions for modern js (let, parameter destructuring,...) and dropped the not needed 'needOTP' method Signed-off-by: Dominik Csapak --- Makefile | 2 ++ data/model/Realm.js | 29 ++ form/RealmComboBox.js | 57 +++ 3 files changed, 88 insertions(+) create mode 100644 data/model/Realm.js create mode 100644 form/RealmComboBox.js diff --git a/Makefile b/Makefile index 85fad6b..a23ad04 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ JSSRC=\ data/ObjectStore.js \ data/RRDStore.js\ data/TimezoneStore.js \ + data/model/Realm.js \ form/DisplayEdit.js \ form/ExpireDate.js \ form/IntegerField.js\ @@ -28,6 +29,7 @@ JSSRC=\ form/RRDTypeSelector.js \ form/BondModeSelector.js\ form/NetworkSelector.js \ + form/RealmComboBox.js \ button/Button.js\ button/HelpButton.js\ grid/ObjectGrid.js \ diff --git a/data/model/Realm.js b/data/model/Realm.js new file mode 100644 index 000..dce270d --- /dev/null +++ b/data/model/Realm.js @@ -0,0 +1,29 @@ +Ext.define('pmx-domains', { +extend: "Ext.data.Model", +fields: [ + 'realm', 'type', 'comment', 'default', + { + name: 'tfa', + allowNull: true, + }, + { + name: 'descr', + convert: function(value, { data={} }) { + if (value) return Ext.String.htmlEncode(value); + + let text = data.comment || data.realm; + + if (data.tfa) { + text += ` (+ ${data.tfa})`; + } + + return Ext.String.htmlEncode(text); + }, + }, +], +idProperty: 'realm', +proxy: { + type: 'proxmox', + url: "/api2/json/access/domains", +}, +}); diff --git a/form/RealmComboBox.js b/form/RealmComboBox.js new file mode 100644 index 000..e391fbf --- /dev/null +++ b/form/RealmComboBox.js @@ -0,0 +1,57 @@ +Ext.define('Proxmox.form.RealmComboBox', { +extend: 'Ext.form.field.ComboBox', +alias: 'widget.pmxRealmComboBox', + +controller: { + xclass: 'Ext.app.ViewController', + + init: function(view) { + view.store.on('load', this.onLoad, view); + }, + + onLoad: function(store, records, success) { + if (!success) { + return; + } + var me = this; + var val = me.getValue(); + if (!val || !me.store.findRecord('realm', val)) { + var def = 'pam'; + Ext.each(records, function(rec) { + if (rec.data && rec.data.default) { + def = rec.data.realm; + } + }); + me.setValue(def); + } + }, +}, + +fieldLabel: gettext('Realm'), +name: 'realm', +queryMode: 'local', +allowBlank: false, +editable: false, +forceSelection: true, +autoSelect: false, +triggerAction: 'all', +valueField: 'realm', +displayField: 'descr', +getState: function() { + return { value: this.getValue() }; +}, +applyState: function(state) { + if (state && state.value) { + this.setValue(state.value); + } +}, +stateEvents: ['select'], +stateful: true, // last chosen auth realm is saved between page reloads +id: 'pveloginrealm', // We need stable ids when using stateful, not autogenerated +stateID: 'pveloginrealm', + +store: { + model: 'pmx-domains', + autoLoad: true, +}, +}); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [RFC PATCH manager] ui: ACMEAccountCreate: improve layout of account dialog
by increasing the space for the link, and using a boxLabel instead of fieldLabel, which has better spacing for longer text also move the e-mail before the directory to have all textboxes together Signed-off-by: Dominik Csapak --- not really sure if it looks good now, but the current layout is rather ugly, so sending it as rfc www/manager6/node/ACME.js | 19 +-- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js index fd3b..6a2730cb 100644 --- a/www/manager6/node/ACME.js +++ b/www/manager6/node/ACME.js @@ -2,7 +2,7 @@ Ext.define('PVE.node.ACMEAccountCreate', { extend: 'Proxmox.window.Edit', mixins: ['Proxmox.Mixin.CBind'], -width: 400, +width: 450, title: gettext('Register Account'), isCreate: true, method: 'POST', @@ -21,6 +21,13 @@ Ext.define('PVE.node.ACMEAccountCreate', { allowBlank: (get) => !get('defaultExists'), }, }, + { + xtype: 'textfield', + name: 'contact', + vtype: 'email', + allowBlank: false, + fieldLabel: gettext('E-Mail') + }, { xtype: 'proxmoxComboGrid', name: 'directory', @@ -90,7 +97,6 @@ Ext.define('PVE.node.ACMEAccountCreate', { { xtype: 'displayfield', itemId: 'tos_url_display', - fieldLabel: gettext('Terms of Service'), renderer: PVE.Utils.render_optional_url, name: 'tos_url_display' }, @@ -102,7 +108,7 @@ Ext.define('PVE.node.ACMEAccountCreate', { { xtype: 'proxmoxcheckbox', itemId: 'tos_checkbox', - fieldLabel: gettext('Accept TOS'), + boxLabel: gettext('Accept TOS'), submitValue: false, validateValue: function(value) { if (value && this.checked) { @@ -111,13 +117,6 @@ Ext.define('PVE.node.ACMEAccountCreate', { return false; } }, - { - xtype: 'textfield', - name: 'contact', - vtype: 'email', - allowBlank: false, - fieldLabel: gettext('E-Mail') - } ] }); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH stable-5 manager 3/3] pve5to6: add check for ovmf vms with potentially broken efi disk
we wrongly mapped some efidisks into the vm, and fixed it in pve6 this potentially needs manual intervention, so warn the user about which vms might be affected Signed-off-by: Dominik Csapak --- PVE/CLI/pve5to6.pm | 35 +++ 1 file changed, 35 insertions(+) diff --git a/PVE/CLI/pve5to6.pm b/PVE/CLI/pve5to6.pm index 01a3a819..8ccca076 100644 --- a/PVE/CLI/pve5to6.pm +++ b/PVE/CLI/pve5to6.pm @@ -18,6 +18,7 @@ use PVE::RPCEnvironment; use PVE::Storage; use PVE::Tools qw(run_command $IPV4RE $IPV6RE); use PVE::QemuServer; +use PVE::QemuConfig; use AptPkg::Cache; use Socket qw(AF_INET AF_INET6 inet_ntop); @@ -335,6 +336,38 @@ sub check_kvm_nested { } } +sub check_vms_with_uefi { +log_info("Checking VMs with OVMF enabled, which may need manual intervention..."); + +my $vmlist = PVE::QemuServer::vzlist(); + +my $vms = []; + +foreach my $vmid ( sort { $a <=> $b } keys %$vmlist ) { + my $conf = PVE::QemuConfig->load_config($vmid); + if ($conf->{bios} && $conf->{bios} eq 'ovmf' && $conf->{efidisk0}) { + my $disk = PVE::QemuServer::parse_drive('efidisk0', $conf->{efidisk0}); + if (!defined($disk->{size}) || $disk->{size} > 128*1024) { + # all efidisks bigger than the default 128k and those + # without size in the config + push @$vms, $vmid; + } elsif ($disk->{file} !~ /\.(raw|qcow2|vmdk)$/) { + # all efidisks not on file storage + push @$vms, $vmid; + } + } +} + +if (scalar(@$vms) > 0) { + my $warnmsg = "VMs with OVMF configured and potentially broken EFI disks: \n"; + $warnmsg .= " " . join(',', @$vms); + $warnmsg .= "\nThere may be manual intervention required. See Known upgrade issues for details\n"; + log_warn($warnmsg); +} else { + log_pass("No VMs with OVMF and potentially broken EFI disk found."); +} +} + sub check_storage_health { print_header("CHECKING CONFIGURED STORAGES"); my $cfg = PVE::Storage::config(); @@ -741,6 +774,8 @@ sub check_misc { } check_kvm_nested(); + +check_vms_with_uefi(); } __PACKAGE__->register_method ({ -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH stable-5 manager 2/3] pve5to6: add check for stock debian kernel package
on current debian buster, stock kernel images recommend firmware-linux-free which conflict with our pve-firmware package which leads to apt wanting to remove promxox-ve check for the meta package in the update check script Signed-off-by: Dominik Csapak --- PVE/CLI/pve5to6.pm | 14 ++ 1 file changed, 14 insertions(+) diff --git a/PVE/CLI/pve5to6.pm b/PVE/CLI/pve5to6.pm index 42c41bbd..01a3a819 100644 --- a/PVE/CLI/pve5to6.pm +++ b/PVE/CLI/pve5to6.pm @@ -19,6 +19,7 @@ use PVE::Storage; use PVE::Tools qw(run_command $IPV4RE $IPV6RE); use PVE::QemuServer; +use AptPkg::Cache; use Socket qw(AF_INET AF_INET6 inet_ntop); use Term::ANSIColor; @@ -249,6 +250,19 @@ sub check_pve_packages { } else { log_warn("unexpected running and installed kernel '$kernel_ver'."); } + +} +print "\nChecking for installed Debian Kernel..\n"; +if(my $apt_cache = AptPkg::Cache->new()) { + my $p = $apt_cache->{'linux-image-amd64'}; + if ($p && $p->{SelectedState} eq 'Install') { + log_fail("Stock Debian kernel package installed. Please remove package 'linux-image-amd64'."); + } else { + log_pass("Stock Debian kernel package not installed."); + } + +} else { + log_fail("unable to initialize AptPkg::Cache\n"); } } -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH stable-5 manager 1/3] ui: fix missing htmlEncodes
username can include some special characters, so we have to escape them backport from pve6 Signed-off-by: Dominik Csapak --- www/manager6/Workspace.js | 2 +- www/manager6/dc/ACLView.js| 2 +- www/manager6/dc/Log.js| 2 ++ www/manager6/dc/TFAEdit.js| 1 + www/manager6/dc/Tasks.js | 1 + www/manager6/dc/UserEdit.js | 1 + www/manager6/dc/UserView.js | 4 ++-- www/manager6/form/UserSelector.js | 1 + www/manager6/window/Settings.js | 2 +- 9 files changed, 11 insertions(+), 5 deletions(-) diff --git a/www/manager6/Workspace.js b/www/manager6/Workspace.js index ca67b7d9..ae41915e 100644 --- a/www/manager6/Workspace.js +++ b/www/manager6/Workspace.js @@ -170,7 +170,7 @@ Ext.define('PVE.StdWorkspace', { var ui = me.query('#userinfo')[0]; if (Proxmox.UserName) { - var msg = Ext.String.format(gettext("You are logged in as {0}"), "'" + Proxmox.UserName + "'"); + var msg = Ext.String.format(gettext("You are logged in as {0}"), "'" + Ext.String.htmlEncode(Proxmox.UserName) + "'"); ui.update('' + msg + ''); } else { ui.update(''); diff --git a/www/manager6/dc/ACLView.js b/www/manager6/dc/ACLView.js index 1322f952..07d8f136 100644 --- a/www/manager6/dc/ACLView.js +++ b/www/manager6/dc/ACLView.js @@ -111,7 +111,7 @@ Ext.define('PVE.dc.ACLView', { return '@' + ugid; } - return ugid; + return Ext.String.htmlEncode(ugid); }; var columns = [ diff --git a/www/manager6/dc/Log.js b/www/manager6/dc/Log.js index 0106af99..2b6e06ad 100644 --- a/www/manager6/dc/Log.js +++ b/www/manager6/dc/Log.js @@ -68,6 +68,7 @@ Ext.define('PVE.dc.Log', { { header: gettext("User name"), dataIndex: 'user', + renderer: Ext.String.htmlEncode, width: 150 }, { @@ -79,6 +80,7 @@ Ext.define('PVE.dc.Log', { { header: gettext("Message"), dataIndex: 'msg', + renderer: Ext.String.htmlEncode, flex: 1 } ], diff --git a/www/manager6/dc/TFAEdit.js b/www/manager6/dc/TFAEdit.js index ed2ff30d..b39bed13 100644 --- a/www/manager6/dc/TFAEdit.js +++ b/www/manager6/dc/TFAEdit.js @@ -368,6 +368,7 @@ Ext.define('PVE.window.TFAEdit', { { xtype: 'displayfield', fieldLabel: gettext('User name'), + renderer: Ext.String.htmlEncode, cbind: { value: '{userid}' } diff --git a/www/manager6/dc/Tasks.js b/www/manager6/dc/Tasks.js index 62e5ac71..5220bcb2 100644 --- a/www/manager6/dc/Tasks.js +++ b/www/manager6/dc/Tasks.js @@ -101,6 +101,7 @@ Ext.define('PVE.dc.Tasks', { { header: gettext("User name"), dataIndex: 'user', + renderer: Ext.String.htmlEncode, width: 150 }, { diff --git a/www/manager6/dc/UserEdit.js b/www/manager6/dc/UserEdit.js index 1665f4b0..26382d60 100644 --- a/www/manager6/dc/UserEdit.js +++ b/www/manager6/dc/UserEdit.js @@ -72,6 +72,7 @@ Ext.define('PVE.dc.UserEdit', { name: 'userid', fieldLabel: gettext('User name'), value: me.userid, + renderer: Ext.String.htmlEncode, allowBlank: false, submitValue: me.isCreate ? true : false }, diff --git a/www/manager6/dc/UserView.js b/www/manager6/dc/UserView.js index 8918fb2b..57dda809 100644 --- a/www/manager6/dc/UserView.js +++ b/www/manager6/dc/UserView.js @@ -110,11 +110,11 @@ Ext.define('PVE.dc.UserView', { ]; var render_username = function(userid) { - return userid.match(/^(.+)(@[^@]+)$/)[1]; + return Ext.String.htmlEncode(userid.match(/^(.+)(@[^@]+)$/)[1]); }; var render_realm = function(userid) { - return userid.match(/@([^@]+)$/)[1]; + return Ext.String.htmlEncode(userid.match(/@([^@]+)$/)[1]); }; Ext.apply(me, { diff --git a/www/manager6/form/UserSelector.js b/www/manager6/form/UserSelector.js index cd01bc3e..8f6f9fa4 100644 --- a/www/manager6/form/UserSelector.js +++ b/www/manager6/form/UserSelector.js @@ -29,6 +29,7 @@ Ext.define('PVE.form.UserSelector', { header: gettext('User'), sortable: true, dataIndex: 'userid', + renderer: Ext.String.htmlEncode, flex: 1
[pve-devel] [PATCH manager] ui: dc/ACME: fix not refreshing api column
When using a diffstore, we have to specify all fields that are displayed, otherwise the store does not know which fields to check for change for the acme plugin view, 'api' was missing Signed-off-by: Dominik Csapak --- www/manager6/dc/ACMEClusterView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/manager6/dc/ACMEClusterView.js b/www/manager6/dc/ACMEClusterView.js index 9c6ca377..cb45a2ba 100644 --- a/www/manager6/dc/ACMEClusterView.js +++ b/www/manager6/dc/ACMEClusterView.js @@ -10,7 +10,7 @@ Ext.define('pve-acme-accounts', { Ext.define('pve-acme-plugins', { extend: 'Ext.data.Model', -fields: ['type', 'plugin'], +fields: ['type', 'plugin', 'api'], proxy: { type: 'proxmox', url: "/api2/json/cluster/acme/plugins", -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH docs] pveum.adoc: use correct cli command for realm sync
Signed-off-by: Dominik Csapak --- pveum.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pveum.adoc b/pveum.adoc index 8f229a6..0057564 100644 --- a/pveum.adoc +++ b/pveum.adoc @@ -178,7 +178,7 @@ It is possible to sync users and groups for LDAP based realms. You can use the CLI command - pveum sync + pveum realm sync or in the `Authentication` panel of the GUI. Users and groups are synced to the cluster-wide user configuration file `/etc/pve/user.cfg`. -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH access-control] LDAP: skip anonymous bind when clientcert/key is given
It seems that servers associate the client-cert/key with an account, so doing an explicit anonymous bind then 'logs out' the already verified user, limiting the search results in some cases before refactoring to PVE::LDAP, we did not do '$ldap->bind' at all when there was no bind_dn, but it is not really clear if Net::LDAP does this automatically when searching (other libraries do this), so leave the anonymous bind (for compatibility with PMG) but skip it when a client certificate and key is given. Signed-off-by: Dominik Csapak --- PVE/Auth/LDAP.pm | 14 +++--- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/PVE/Auth/LDAP.pm b/PVE/Auth/LDAP.pm index 9fa9095..09b2202 100755 --- a/PVE/Auth/LDAP.pm +++ b/PVE/Auth/LDAP.pm @@ -203,17 +203,17 @@ sub connect_and_bind { my $ldap = PVE::LDAP::ldap_connect($servers, $scheme, $port, \%ldap_args); -my $bind_dn; -my $bind_pass; - if ($config->{bind_dn}) { - $bind_dn = $config->{bind_dn}; - $bind_pass = ldap_get_credentials($realm); + my $bind_dn = $config->{bind_dn}; + my $bind_pass = ldap_get_credentials($realm); die "missing password for realm $realm\n" if !defined($bind_pass); + PVE::LDAP::ldap_bind($ldap, $bind_dn, $bind_pass); +} elsif ($config->{cert} && $config->{certkey}) { + warn "skipping anonymous bind with clientcert\n"; +} else { + PVE::LDAP::ldap_bind($ldap); } -PVE::LDAP::ldap_bind($ldap, $bind_dn, $bind_pass); - if (!$config->{base_dn}) { my $root = $ldap->root_dse(attrs => [ 'defaultNamingContext' ]); $config->{base_dn} = $root->get_value('defaultNamingContext'); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager] dc/Realms: fix adding of new ldap realm
we cannot pass 'delete' on create api call, and we have to make sure that 'default_opts' and 'sync_attributes' are alwyas available, since they are used in onGetValues (they were only created during setValues, which is not called when adding a new realm) Signed-off-by: Dominik Csapak --- www/manager6/dc/AuthEditLDAP.js | 8 ++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/www/manager6/dc/AuthEditLDAP.js b/www/manager6/dc/AuthEditLDAP.js index 0ca6f6ac..c5976769 100644 --- a/www/manager6/dc/AuthEditLDAP.js +++ b/www/manager6/dc/AuthEditLDAP.js @@ -66,6 +66,8 @@ Ext.define('PVE.panel.LDAPSyncInputPanel', { editableAttributes: ['email'], editableDefaults: ['scope', 'full', 'enable-new', 'purge'], +default_opts: {}, +sync_attributes: {}, // (de)construct the sync-attributes from the list above, // not touching all others @@ -93,12 +95,15 @@ Ext.define('PVE.panel.LDAPSyncInputPanel', { PVE.Utils.delete_if_default(values, 'sync-defaults-options'); PVE.Utils.delete_if_default(values, 'sync_attributes'); + if (me.isCreate) { + delete values.delete; // on create we cannot delete values + } + return values; }, setValues: function(values) { let me = this; - me.sync_attributes = {}; if (values.sync_attributes) { me.sync_attributes = PVE.Parser.parsePropertyString(values.sync_attributes); delete values.sync_attributes; @@ -108,7 +113,6 @@ Ext.define('PVE.panel.LDAPSyncInputPanel', { } }); } - me.default_opts = {}; if (values['sync-defaults-options']) { me.default_opts = PVE.Parser.parsePropertyString(values['sync-defaults-options']); delete values.default_opts; -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 1/3] ui: ACMEAccountCreate: make name only optional if no default exists
if the 'default' account exists, make the name field required and remove the emptytext get the information by querying the grid store. this may be not up-to-date, but it is less intrusive that an extra api call that blocks the window Signed-off-by: Dominik Csapak --- www/manager6/dc/ACMEClusterView.js | 3 +++ www/manager6/node/ACME.js | 8 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/www/manager6/dc/ACMEClusterView.js b/www/manager6/dc/ACMEClusterView.js index 00db8e9f..aaf2fc19 100644 --- a/www/manager6/dc/ACMEClusterView.js +++ b/www/manager6/dc/ACMEClusterView.js @@ -29,7 +29,10 @@ Ext.define('PVE.dc.ACMEAccountView', { addAccount: function() { let me = this; + let view = me.getView(); + let defaultExists = view.getStore().findExact('name', 'default') !== -1; Ext.create('PVE.node.ACMEAccountCreate', { + defaultExists, taskDone: function() { me.reload(); }, diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js index 0418f406..fd3b 100644 --- a/www/manager6/node/ACME.js +++ b/www/manager6/node/ACME.js @@ -1,5 +1,6 @@ Ext.define('PVE.node.ACMEAccountCreate', { extend: 'Proxmox.window.Edit', +mixins: ['Proxmox.Mixin.CBind'], width: 400, title: gettext('Register Account'), @@ -8,14 +9,17 @@ Ext.define('PVE.node.ACMEAccountCreate', { submitText: gettext('Register'), url: '/cluster/acme/account', showTaskViewer: true, +defaultExists: false, items: [ { xtype: 'proxmoxtextfield', fieldLabel: gettext('Name'), name: 'name', - emptyText: 'default', - allowBlank: true, + cbind: { + emptyText: (get) => get('defaultExists') ? '' : 'default', + allowBlank: (get) => !get('defaultExists'), + }, }, { xtype: 'proxmoxComboGrid', -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 2/3] ui: dc/ACMEClusterView: load the correct store on reload
if we use a diff/update store combo, we have to load the updatestore not the diff store, else we get spurious empty grids Signed-off-by: Dominik Csapak --- www/manager6/dc/ACMEClusterView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/manager6/dc/ACMEClusterView.js b/www/manager6/dc/ACMEClusterView.js index aaf2fc19..e73de7b7 100644 --- a/www/manager6/dc/ACMEClusterView.js +++ b/www/manager6/dc/ACMEClusterView.js @@ -52,7 +52,7 @@ Ext.define('PVE.dc.ACMEAccountView', { reload: function() { let me = this; let view = me.getView(); - view.getStore().load(); + view.getStore().rstore.load(); }, }, @@ -142,7 +142,7 @@ Ext.define('PVE.dc.ACMEPluginView', { reload: function() { let me = this; let view = me.getView(); - view.getStore().load(); + view.getStore().rstore.load(); }, }, -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 3/3] ui: dc/ACMEClusterView: show TaskProgress on account removal
this is not a synchronous api call, so open a taskprogress window and reload after the task is done Signed-off-by: Dominik Csapak --- www/manager6/dc/ACMEClusterView.js | 15 ++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/www/manager6/dc/ACMEClusterView.js b/www/manager6/dc/ACMEClusterView.js index e73de7b7..74868cb7 100644 --- a/www/manager6/dc/ACMEClusterView.js +++ b/www/manager6/dc/ACMEClusterView.js @@ -54,6 +54,19 @@ Ext.define('PVE.dc.ACMEAccountView', { let view = me.getView(); view.getStore().rstore.load(); }, + + showTaskAndReload: function(options, success, response) { + let me = this; + if (!success) return; + + let upid = response.result.data; + Ext.create('Proxmox.window.TaskProgress', { + upid, + taskDone: function() { + me.reload(); + }, + }).show(); + }, }, minHeight: 150, @@ -84,7 +97,7 @@ Ext.define('PVE.dc.ACMEAccountView', { { xtype: 'proxmoxStdRemoveButton', baseurl: '/cluster/acme/account', - callback: 'reload', + callback: 'showTaskAndReload', }, ], -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] [PATCH manager 3/7] ui: dc/ACMEClusterView: use a diff/update store combo for the grids
On 5/7/20 1:08 PM, Thomas Lamprecht wrote: On 5/7/20 10:27 AM, Dominik Csapak wrote: so that they are get automatically reloaded with the default interval (3seconds) Signed-off-by: Dominik Csapak --- this patch needs the widget-toolkit patches applied we can of course leave those out, and to it manually in initComponent, but this is a pattern we often use and maybe we can remove more manually crated rstore/diffstore combos in other places hmm, I now get a short glitch off "No accounts configured" shown when adding a second or removing a second account. yes i see that, but its only tangentially related to this patch. we load the wrong store on 'reload' (the diff instead of the updatestore; which does not work) can send a v2 of this or a follow-up, up to you also noticed that on account removal we need to show a taskprogress/viewer since it is not synchronous Semi-related, if an account with "default" already exists it would be great if the emptyText gets dropped and the field gets required, as a second account with "default" cannot be added anyway, but rather minor nit.. ok sounds reasonable www/manager6/dc/ACMEClusterView.js | 24 +++- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/www/manager6/dc/ACMEClusterView.js b/www/manager6/dc/ACMEClusterView.js index 228d1cf9..00db8e9f 100644 --- a/www/manager6/dc/ACMEClusterView.js +++ b/www/manager6/dc/ACMEClusterView.js @@ -90,8 +90,15 @@ Ext.define('PVE.dc.ACMEAccountView', { }, store: { - model: 'pve-acme-accounts', - autoLoad: true, + type: 'diff', + autoDestroy: true, + autoDestroyRstore: true, + rstore: { + type: 'update', + storeid: 'pve-acme-accounts', + model: 'pve-acme-accounts', + autoStart: true, + }, sorters: 'name', }, }); @@ -179,9 +186,16 @@ Ext.define('PVE.dc.ACMEPluginView', { }, store: { - model: 'pve-acme-plugins', - autoLoad: true, - filters: item => !!item.data.api, + type: 'diff', + autoDestroy: true, + autoDestroyRstore: true, + rstore: { + type: 'update', + storeid: 'pve-acme-plugins', + model: 'pve-acme-plugins', + autoStart: true, + filters: item => !!item.data.api, + }, sorters: 'plugin', }, }); ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 2/7] ui: ACME: add emptyText and add minHeight
without the minHeight, the panel does resize weirdly on the first load Signed-off-by: Dominik Csapak --- www/manager6/dc/ACMEClusterView.js | 6 ++ www/manager6/node/ACME.js | 2 ++ 2 files changed, 8 insertions(+) diff --git a/www/manager6/dc/ACMEClusterView.js b/www/manager6/dc/ACMEClusterView.js index f3cae866..228d1cf9 100644 --- a/www/manager6/dc/ACMEClusterView.js +++ b/www/manager6/dc/ACMEClusterView.js @@ -53,6 +53,9 @@ Ext.define('PVE.dc.ACMEAccountView', { }, }, +minHeight: 150, +emptyText: gettext('No Accounts configured'), + columns: [ { dataIndex: 'name', @@ -133,6 +136,9 @@ Ext.define('PVE.dc.ACMEPluginView', { }, }, +minHeight: 150, +emptyText: gettext('No Plugins configured'), + columns: [ { dataIndex: 'plugin', diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js index 0b38f9c7..a2ac35f5 100644 --- a/www/manager6/node/ACME.js +++ b/www/manager6/node/ACME.js @@ -346,6 +346,8 @@ Ext.define('PVE.node.ACME', { margin: '10 0 0 0', title: 'ACME', +emptyText: gettext('No Domains configured'), + viewModel: { data: { account: undefined, -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 1/7] NodeConfig/get_acme_conf: make domains always a hash
on all call sites, we assume $cfg->{domains} is a hash, but if we do not have any domains configured, that fails with 'Can't use an undefined value as a HASH reference at ...' so always make domains a hash to avoid this Signed-off-by: Dominik Csapak --- PVE/NodeConfig.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/PVE/NodeConfig.pm b/PVE/NodeConfig.pm index 2de9015e..af726b15 100644 --- a/PVE/NodeConfig.pm +++ b/PVE/NodeConfig.pm @@ -251,6 +251,7 @@ sub get_acme_conf { die $err; } my $standalone_domains = delete($res->{domains}) // ''; + $res->{domains} = {}; for my $domain (split(";", $standalone_domains)) { $res->{domains}->{$domain}->{plugin} = 'standalone'; $res->{domains}->{$domain}->{_configkey} = 'acme'; -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 4/7] ui: node/ACME: fix some eslint errors/warnings
* unneeded brackets for arrow function * unused variables * me not defined * trailing commas Signed-off-by: Dominik Csapak --- www/manager6/node/ACME.js | 19 --- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js index a2ac35f5..ba1906b4 100644 --- a/www/manager6/node/ACME.js +++ b/www/manager6/node/ACME.js @@ -356,9 +356,7 @@ Ext.define('PVE.node.ACME', { }, formulas: { - editBtnIcon: (get) => { - return 'fa black fa-' + (get('accountEditable') ? 'check' : 'pencil'); - }, + editBtnIcon: (get) => 'fa black fa-' + (get('accountEditable') ? 'check' : 'pencil'), accountTextHidden: (get) => get('accountEditable') || !get('accountsAvailable'), accountValueHidden: (get) => !get('accountEditable') || !get('accountsAvailable'), }, @@ -368,18 +366,18 @@ Ext.define('PVE.node.ACME', { xclass: 'Ext.app.ViewController', init: function(view) { - let vm = this.getViewModel(); let accountSelector = this.lookup('accountselector'); accountSelector.store.on('load', this.onAccountsLoad, this); }, onAccountsLoad: function(store, records, success) { - let vm = this.getViewModel(); + let me = this; + let vm = me.getViewModel(); vm.set('accountsAvailable', records.length > 0); - if (this.autoChangeAccount && records.length > 0) { - this.changeAccount(records[0].data.name, () => { + if (me.autoChangeAccount && records.length > 0) { + me.changeAccount(records[0].data.name, () => { vm.set('accountEditable', false); - this.reload(); + me.reload(); }); me.autoChangeAccount = false; } @@ -616,7 +614,7 @@ Ext.define('PVE.node.ACME', { hidden: '{accountsAvailable}', }, handler: 'addAccount', - } + }, ], updateStore: function(store, records, success) { @@ -627,7 +625,7 @@ Ext.define('PVE.node.ACME', { rec = records[0]; } else { rec = { - data: {} + data: {}, }; } @@ -652,7 +650,6 @@ Ext.define('PVE.node.ACME', { } } - let accounttext = me.lookup('accounttext'); let vm = me.getViewModel(); let oldaccount = vm.get('account'); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 1/2] data/DiffStore: add autoDestroyRstore flag
when this flag is set, the diffstore will automatically try to destroy the rstore when it is destroyed itself for this we have to move the rstore into the object (instead of using a closure) Signed-off-by: Dominik Csapak --- data/DiffStore.js | 26 +- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/data/DiffStore.js b/data/DiffStore.js index d43f111..6f335f0 100644 --- a/data/DiffStore.js +++ b/data/DiffStore.js @@ -24,6 +24,20 @@ Ext.define('Proxmox.data.DiffStore', { sortAfterUpdate: false, +// if set to true, destroy rstore on destruction +autoDestroyRstore: false, + +onDestroy: function() { + let me = this; + if (me.autoDestroyRstore) { + if (Ext.isFunction(me.rstore.destroy)) { + me.rstore.destroy(); + } + delete me.rstore; + } + me.callParent(); +}, + constructor: function(config) { var me = this; @@ -46,6 +60,8 @@ Ext.define('Proxmox.data.DiffStore', { me.callParent([config]); + me.rstore = rstore; + var first_load = true; var cond_add_item = function(data, id) { @@ -80,13 +96,13 @@ Ext.define('Proxmox.data.DiffStore', { // remove vanished items allItems.each(function(olditem) { - var item = rstore.getById(olditem.getId()); + var item = me.rstore.getById(olditem.getId()); if (!item) { me.remove(olditem); } }); - rstore.each(function(item) { + me.rstore.each(function(item) { cond_add_item(item.data, item.getId()); }); @@ -103,12 +119,12 @@ Ext.define('Proxmox.data.DiffStore', { me.fireEvent('datachanged', me); }; - if (rstore.isLoaded()) { + if (me.rstore.isLoaded()) { // if store is already loaded, // insert items instantly - loadFn(rstore, [], true); + loadFn(me.rstore, [], true); } - me.mon(rstore, 'load', loadFn); + me.mon(me.rstore, 'load', loadFn); } }); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 6/7] ui: node/ACME: only enable order button when it should work
to order a ceritificate, we need at least one configured domain, and the configured account (or default) must exist so track the domaincount in the viewmodel and introduce a 'canOrder' formula which is only true when domaincount > 0 and account is set (if the configured account does not exist, or no account exists at all we set 'account' to 'null') Signed-off-by: Dominik Csapak --- www/manager6/node/ACME.js | 5 - 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js index 1e91be4e..d21d18e4 100644 --- a/www/manager6/node/ACME.js +++ b/www/manager6/node/ACME.js @@ -350,6 +350,7 @@ Ext.define('PVE.node.ACME', { viewModel: { data: { + domaincount: 0, account: undefined, // the account we display configaccount: undefined, // the account set in the config accountEditable: false, @@ -357,6 +358,7 @@ Ext.define('PVE.node.ACME', { }, formulas: { + canOrder: (get) => !!get('account') && get('domaincount') > 0, editBtnIcon: (get) => 'fa black fa-' + (get('accountEditable') ? 'check' : 'pencil'), accountTextHidden: (get) => get('accountEditable') || !get('accountsAvailable'), accountValueHidden: (get) => !get('accountEditable') || !get('accountsAvailable'), @@ -566,7 +568,7 @@ Ext.define('PVE.node.ACME', { reference: 'order', text: gettext('Order Certificates Now'), bind: { - disabled: '{!accountsAvailable}', + disabled: '{!canOrder}', }, handler: 'order', }, @@ -678,6 +680,7 @@ Ext.define('PVE.node.ACME', { data.push(record); } + vm.set('domaincount', data.length); me.store.loadData(data, false); }, -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit 2/2] data/DiffStore: auto-create the rstore if its just a config
when rstore is not instanciated but only a config, auto-create it with its type this allows us to configure an diff/rstore combination completely declaratively like this: store: { type: 'diff', autoDestroy: true, autoDestroyRstore: true, rstore: { type: 'update', model: 'some-model', autoStart: true, interval: 5000, }, }, the only thing we have to be careful about is to either do a manual 'stopUpdate' somewhere, or use the 'autoDestroyRstore' flag Signed-off-by: Dominik Csapak --- we might want to set the 'autoDestroyRstore' automatically when we define 'autoDestroy' and autocreated it? in that case the chance that someone manually extracts an instance of the rstore and uses it somewhere else is probably very low data/DiffStore.js | 9 - 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/data/DiffStore.js b/data/DiffStore.js index 6f335f0..2fc08be 100644 --- a/data/DiffStore.js +++ b/data/DiffStore.js @@ -51,7 +51,14 @@ Ext.define('Proxmox.data.DiffStore', { throw "no rstore model specified"; } - var rstore = config.rstore; + let rstore; + if (config.rstore.isInstance) { + rstore = config.rstore; + } else if (config.rstore.type) { + rstore = Ext.create(`store.${config.rstore.type}`, config.rstore); + } else { + throw 'rstore is not an instance, and cannot autocreate without "type"'; + } Ext.apply(config, { model: rstore.model, -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH widget-toolkit/manager] fixups for ACME UI
this series fixes some problems and improves the ACME UI, namely: * auto update dc->Acme grids * add emptyText * adds missing stopUpdates * improve order button enable logic * reuse existing store for account verification * add auto-create/destroy feature to the diffstore path 3/7 from manager needs the widget-toolkit patches applied, but all others do not need them proxmox-widget-toolkit: Dominik Csapak (2): data/DiffStore: add autoDestroyRstore flag data/DiffStore: auto-create the rstore if its just a config data/DiffStore.js | 35 +-- 1 file changed, 29 insertions(+), 6 deletions(-) pve-manager: Dominik Csapak (7): NodeConfig/get_acme_conf: make domains always a hash ui: ACME: add emptyText and add minHeight ui: dc/ACMEClusterView: use a diff/update store combo for the grids ui: node/ACME: fix some eslint errors/warnings ui: node/ACME: use accountselector for verification ui: node/ACME: only enable order button when it should work ui: node/{ACME,Certificates}: add stopUpdate on destruction PVE/NodeConfig.pm | 1 + www/manager6/dc/ACMEClusterView.js | 30 +++--- www/manager6/node/ACME.js | 50 -- www/manager6/node/Certificates.js | 1 + 4 files changed, 54 insertions(+), 28 deletions(-) -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 5/7] ui: node/ACME: use accountselector for verification
instead of using API2Request manually, just reload the store of the accountselector and check if the configured account is in it this should fix the spurious loading mask of the panel when loading the accounts Signed-off-by: Dominik Csapak --- www/manager6/node/ACME.js | 23 --- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js index ba1906b4..1e91be4e 100644 --- a/www/manager6/node/ACME.js +++ b/www/manager6/node/ACME.js @@ -350,7 +350,8 @@ Ext.define('PVE.node.ACME', { viewModel: { data: { - account: undefined, + account: undefined, // the account we display + configaccount: undefined, // the account set in the config accountEditable: false, accountsAvailable: false, }, @@ -373,6 +374,7 @@ Ext.define('PVE.node.ACME', { onAccountsLoad: function(store, records, success) { let me = this; let vm = me.getViewModel(); + let configaccount = vm.get('configaccount'); vm.set('accountsAvailable', records.length > 0); if (me.autoChangeAccount && records.length > 0) { me.changeAccount(records[0].data.name, () => { @@ -380,6 +382,12 @@ Ext.define('PVE.node.ACME', { me.reload(); }); me.autoChangeAccount = false; + } else if (configaccount) { + if (store.findExact('name', configaccount) !== -1) { + vm.set('account', configaccount); + } else { + vm.set('account', null); + } } }, @@ -573,6 +581,7 @@ Ext.define('PVE.node.ACME', { { xtype: 'displayfield', reference: 'accounttext', + renderer: (val) => val || Proxmox.Utils.NoneText, bind: { value: '{account}', hidden: '{accountTextHidden}', @@ -655,16 +664,8 @@ Ext.define('PVE.node.ACME', { // account changed, and we do not edit currently, load again to verify if (oldaccount !== account && !vm.get('accountEditable')) { - Proxmox.Utils.API2Request({ - url: `/cluster/acme/account/${account}`, - waitMsgTarget: me, - success: function(response, opt) { - vm.set('account', account); - }, - failure: function(response, opt) { - vm.set('account', Proxmox.Utils.NoneText); - }, - }); + vm.set('configaccount', account); + me.lookup('accountselector').store.load(); } for (let i = 0; i < PVE.Utils.acmedomain_count; i++) { -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 7/7] ui: node/{ACME, Certificates}: add stopUpdate on destruction
else the stores never stop updating Signed-off-by: Dominik Csapak --- www/manager6/node/ACME.js | 1 + www/manager6/node/Certificates.js | 1 + 2 files changed, 2 insertions(+) diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js index d21d18e4..0418f406 100644 --- a/www/manager6/node/ACME.js +++ b/www/manager6/node/ACME.js @@ -731,5 +731,6 @@ Ext.define('PVE.node.ACME', { me.callParent(); me.mon(me.rstore, 'load', 'updateStore', me); Proxmox.Utils.monStoreErrors(me, me.rstore); + me.on('destroy', me.rstore.stopUpdate, me.rstore); }, }); diff --git a/www/manager6/node/Certificates.js b/www/manager6/node/Certificates.js index df2fd7de..0c5dfc68 100644 --- a/www/manager6/node/Certificates.js +++ b/www/manager6/node/Certificates.js @@ -399,5 +399,6 @@ Ext.define('PVE.node.Certificates', { me.mon(me.rstore, 'load', me.set_button_status, me); me.rstore.startUpdate(); + me.on('destroy', me.rstore.stopUpdate, me.rstore); } }); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 3/7] ui: dc/ACMEClusterView: use a diff/update store combo for the grids
so that they are get automatically reloaded with the default interval (3seconds) Signed-off-by: Dominik Csapak --- this patch needs the widget-toolkit patches applied we can of course leave those out, and to it manually in initComponent, but this is a pattern we often use and maybe we can remove more manually crated rstore/diffstore combos in other places www/manager6/dc/ACMEClusterView.js | 24 +++- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/www/manager6/dc/ACMEClusterView.js b/www/manager6/dc/ACMEClusterView.js index 228d1cf9..00db8e9f 100644 --- a/www/manager6/dc/ACMEClusterView.js +++ b/www/manager6/dc/ACMEClusterView.js @@ -90,8 +90,15 @@ Ext.define('PVE.dc.ACMEAccountView', { }, store: { - model: 'pve-acme-accounts', - autoLoad: true, + type: 'diff', + autoDestroy: true, + autoDestroyRstore: true, + rstore: { + type: 'update', + storeid: 'pve-acme-accounts', + model: 'pve-acme-accounts', + autoStart: true, + }, sorters: 'name', }, }); @@ -179,9 +186,16 @@ Ext.define('PVE.dc.ACMEPluginView', { }, store: { - model: 'pve-acme-plugins', - autoLoad: true, - filters: item => !!item.data.api, + type: 'diff', + autoDestroy: true, + autoDestroyRstore: true, + rstore: { + type: 'update', + storeid: 'pve-acme-plugins', + model: 'pve-acme-plugins', + autoStart: true, + filters: item => !!item.data.api, + }, sorters: 'plugin', }, }); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] [PATCH manager v2 0/5] ACME node adaptions for plugins
On 5/6/20 8:11 PM, Thomas Lamprecht wrote: On 5/6/20 4:31 PM, Dominik Csapak wrote: this series adapts the node->certificates->acme panel to include an 'accountselector' and to be able to add/edit/remove single domains, including ones with a plugin changes from v1: * drop fieldLabel in ACMEAccountSelector * reword 'Account' in 'Used Account' * use different approach to change the account (use viewModel and a displayfield/combobox/editbutton to better see when the account actually changes) Dominik Csapak (5): ui: add ACME selector formfields for account and plugins ui: Parser: add printACME ui: Utils: add helper functions for acme domains ui: node/ACME: add ACMEDomainEdit ui: node/ACME: rework ACME grid for plugin based domains www/manager6/Makefile| 2 + www/manager6/Parser.js | 7 + www/manager6/Utils.js| 23 + www/manager6/form/ACMEAccountSelector.js | 21 + www/manager6/form/ACMEPluginSelector.js | 19 + www/manager6/node/ACME.js| 617 --- 6 files changed, 517 insertions(+), 172 deletions(-) create mode 100644 www/manager6/form/ACMEAccountSelector.js create mode 100644 www/manager6/form/ACMEPluginSelector.js applied series, but this wasn't much tested, or the wrong thing was sent.. hi, sorry yes should have tested more (was a bit in a hurry yesterday) I had to fix the acme undefined access exceptions I mentioned to you for v1 off-list, i actually tested this, but did not trigger it, but looking at the code now, yes i can see where it has tripped up the "show button to go to account page" didn't worked at all (no code doing it included), yes, sorry, after my initial tests with this, i did actually never remove my acme accounts again I fixed that but threw it out for something different all together.. looks good AFAICS renamed: form/ACMEAPiSelector.js -> form/ACMEAPISelector.js > (Which I also mentioned for v1..) and threw in a few smaller followups. but this was in my other series (which you applied the day before), anyway, i would've been happy to send a v3 (you can ofc fix up my patches, but no need to generate more work for yourself :) ) The store glitches still on load for the Certificates ACME view, I increased the interval to 10 seconds as I really did not wanted to investigate that too. i think what you mean with 'glitch' is the loading mask on account verification... after looking at the thing again, i am not so sure anymore that we actually need that (we already have a list off accounts in the accountselector). i'll see if i can improve that bit The DC -> ACME ones could profit from update/diffstore to get changes more live, even if seldom made - they are really cheap to query. yes makes sense, i found also some other issues (backend as well) i'll send some patches later today thanks for fixing my bugs :) ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager v2 2/5] ui: Parser: add printACME
since we decode the domain list in parseACME into an array, we have to join them again to a string when printing otherwise printPropertyString attaches them just with ',' which does not work here Signed-off-by: Dominik Csapak --- www/manager6/Parser.js | 7 +++ 1 file changed, 7 insertions(+) diff --git a/www/manager6/Parser.js b/www/manager6/Parser.js index 4cecb3e1..20d81d4a 100644 --- a/www/manager6/Parser.js +++ b/www/manager6/Parser.js @@ -5,6 +5,13 @@ Ext.define('PVE.Parser', { statics: { // this class only contains static functions +printACME: function(value) { + if (Ext.isArray(value.domains)) { + value.domains = value.domains.join(';'); + } + return PVE.Parser.printPropertyString(value); +}, + parseACME: function(value) { if (!value) { return; -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager v2 4/5] ui: node/ACME: add ACMEDomainEdit
which expects a nodeconfig (for digest and domaincount) and for the edit case, the parsed 'domain' object this editwindow has three fields: * type selector (standalone/dns) * domain * plugin (only for dns) if the user chooses dns but there are already the maximum count of acmedomainX entries, the type field gets invalid (with a error tooltip) the onGetValues method is non-trivial, because of the mixing of acmedomainX and acme.domain values, so we have to be careful that we delete/edit the correct entry Signed-off-by: Dominik Csapak --- www/manager6/node/ACME.js | 136 ++ 1 file changed, 136 insertions(+) diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js index 40ecc00e..a8bb39d6 100644 --- a/www/manager6/node/ACME.js +++ b/www/manager6/node/ACME.js @@ -230,6 +230,142 @@ Ext.define('PVE.node.ACMEAccountView', { } }); +Ext.define('PVE.node.ACMEDomainEdit', { +extend: 'Proxmox.window.Edit', +alias: 'widget.pveACMEDomainEdit', + +subject: gettext('Domain'), +isCreate: false, + +items: [ + { + xtype: 'inputpanel', + onGetValues: function(values) { + let me = this; + let win = me.up('pveACMEDomainEdit'); + let nodeconfig = win.nodeconfig; + let olddomain = win.domain || {}; + + let params = { + digest: nodeconfig.digest, + }; + + let configkey = olddomain.configkey; + let acmeObj = PVE.Parser.parseACME(nodeconfig.acme) || {}; + + if (values.type === 'dns') { + if (!olddomain.configkey || olddomain.configkey === 'acme') { + // look for first free slot + for (let i = 0; i < PVE.Utils.acmedomain_count; i++) { + if (nodeconfig[`acmedomain${i}`] === undefined) { + configkey = `acmedomain${i}`; + break; + } + } + if (olddomain.domain) { + // we have to remove the domain from the acme domainlist + PVE.Utils.remove_domain_from_acme(acmeObj, olddomain.domain); + params.acme = PVE.Parser.printACME(acmeObj); + } + } + + delete values.type; + params[configkey] = PVE.Parser.printPropertyString(values, 'domain'); + } else { + if (olddomain.configkey && olddomain.configkey !== 'acme') { + // delete the old dns entry + params.delete = [olddomain.configkey]; + } + + // add new, remove old and make entries unique + PVE.Utils.add_domain_to_acme(acmeObj, values.domain); + PVE.Utils.remove_domain_from_acme(acmeObj, olddomain.domain); + params.acme = PVE.Parser.printACME(acmeObj); + } + + return params; + }, + items: [ + { + xtype: 'proxmoxKVComboBox', + name: 'type', + fieldLabel: gettext('Type'), + allowBlank: false, + comboItems: [ + ['standalone', 'standalone'], + ['dns', 'DNS'], + ], + validator: function(value) { + let me = this; + let win = me.up('pveACMEDomainEdit'); + let oldconfigkey = win.domain ? win.domain.configkey : undefined; + let val = me.getValue(); + if (val === 'dns' && (!oldconfigkey || oldconfigkey === 'acme')) { + // we have to check if there is a 'acmedomain' slot left + let found = false; + for (let i = 0; i < PVE.Utils.acmedomain_count; i++) { + if (!win.nodeconfig[`acmedomain${i}`]) { + found = true; + } + } + if (!found) { + return gettext('Only 5 Domains with type DNS can be configured'); + } + } + + return true; + }, + listeners: { + change: function(cb, value) { + let me = this; + let view = me.up('pveACMEDomainEdit'); + view.down('field[name=plugin]').setDisabled(value !== 'dns'); + }, + }, + }, + { +
[pve-devel] [PATCH manager v2 5/5] ui: node/ACME: rework ACME grid for plugin based domains
This is basically a complete rework of the ACME grid. Instead of having an ObjectGrid, we now have a normal GridPanel which allows us to show a row for each Domain. But to achieve this, we need to manually fill the store with data from the 'acme' and 'acmedomainX' entries of the node config. We also add an AccountSelector to the tbar and a link to the datacenter->acme panel (when there is no account) this also removes the 'register account' and 'view account' buttons, since those are now available in datacenter->acme Signed-off-by: Dominik Csapak --- changes from v1: * different approach for editing accounts uses now a displayfield/combobox and button to switch between display and edit, and only do it when the 'check' is clicked * use viewmodel binding for account name, makes the code a little more compact www/manager6/node/ACME.js | 481 -- 1 file changed, 309 insertions(+), 172 deletions(-) diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js index a8bb39d6..33159b2e 100644 --- a/www/manager6/node/ACME.js +++ b/www/manager6/node/ACME.js @@ -1,49 +1,3 @@ -Ext.define('PVE.node.ACMEEditor', { -extend: 'Proxmox.window.Edit', -xtype: 'pveACMEEditor', - -subject: gettext('Domains'), -items: [ - { - xtype: 'inputpanel', - items: [ - { - xtype: 'textarea', - fieldLabel: gettext('Domains'), - emptyText: "domain1.example.com\ndomain2.example.com", - name: 'domains' - } - ], - onGetValues: function(values) { - if (!values.domains) { - return { - 'delete': 'acme' - }; - } - var domains = values.domains.split(/\n/).join(';'); - return { - 'acme': 'domains=' + domains - }; - } - } -], - -initComponent: function() { - var me = this; - me.callParent(); - - me.load({ - success: function(response, opts) { - var res = PVE.Parser.parseACME(response.result.data.acme); - if (res) { - res.domains = res.domains.join(' '); - me.setValues(res); - } - } - }); -} -}); - Ext.define('PVE.node.ACMEAccountCreate', { extend: 'Proxmox.window.Edit', @@ -366,138 +320,330 @@ Ext.define('PVE.node.ACMEDomainEdit', { }, }); +Ext.define('pve-acme-domains', { +extend: 'Ext.data.Model', +fields: ['domain', 'type', 'alias', 'plugin', 'configkey'], +idProperty: 'domain', +}); + Ext.define('PVE.node.ACME', { -extend: 'Proxmox.grid.ObjectGrid', -xtype: 'pveACMEView', +extend: 'Ext.grid.Panel', +alias: 'widget.pveACMEView', margin: '10 0 0 0', title: 'ACME', +viewModel: { + data: { + account: null, + accountEditable: false, + }, + + formulas: { + editBtnIcon: (get) => { + return 'fa black fa-' + (get('accountEditable') ? 'check' : 'pencil'); + }, + }, +}, + +controller: { + xclass: 'Ext.app.ViewController', + + addDomain: function() { + let me = this; + let view = me.getView(); + + Ext.create('PVE.node.ACMEDomainEdit', { + nodename: view.nodename, + nodeconfig: view.nodeconfig, + apiCallDone: function() { + me.reload(); + }, + }).show(); + }, + + editDomain: function() { + let me = this; + let view = me.getView(); + + let selection = view.getSelection(); + if (selection.length < 1) return; + + Ext.create('PVE.node.ACMEDomainEdit', { + nodename: view.nodename, + nodeconfig: view.nodeconfig, + domain: selection[0].data, + apiCallDone: function() { + me.reload(); + }, + }).show(); + }, + + removeDomain: function() { + let me = this; + let view = me.getView(); + let selection = view.getSelection(); + if (selection.length < 1) return; + + let rec = selection[0].data; + let params = {}; + if (rec.configkey !== 'acme') { + params.delete = rec.configkey; + } else { + let acme = PVE.Parser.parseACME(view.nodeconfig.acme); + PVE.Utils.remove_domain_from_acme(acme, rec.domain); + params.acme = PVE.Parser.printACME(acme); + } + + Proxmox.Utils.API2Request({ + method: 'PUT', + url: `/nodes/${view.nodename}/config`, + params, + success: function(response, opt) { +
[pve-devel] [PATCH manager v2 3/5] ui: Utils: add helper functions for acme domains
to convieniently add and remove domains from a parsed ACME object they also make domains unique in the array also add the count of configureable acmedomainX entries Signed-off-by: Dominik Csapak --- www/manager6/Utils.js | 23 +++ 1 file changed, 23 insertions(+) diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js index 872b7c29..31e262c0 100644 --- a/www/manager6/Utils.js +++ b/www/manager6/Utils.js @@ -1354,6 +1354,29 @@ Ext.define('PVE.Utils', { utilities: { } }, +acmedomain_count: 5, + +add_domain_to_acme: function(acme, domain) { + if (acme.domains === undefined) { + acme.domains = [domain]; + } else { + acme.domains.push(domain); + acme.domains = acme.domains.filter((value, index, self) => { + return self.indexOf(value) === index; + }); + } + return acme; +}, + +remove_domain_from_acme: function(acme, domain) { + if (acme.domains !== undefined) { + acme.domains = acme.domains.filter((value, index, self) => { + return self.indexOf(value) === index && value !== domain; + }); + } + return acme; +}, + handleStoreErrorOrMask: function(me, store, regex, callback) { me.mon(store, 'load', function (proxy, response, success, operation) { -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager v2 1/5] ui: add ACME selector formfields for account and plugins
filter the plugins by type === 'dns' and add a convenience method for ACMEAccountSelector to check if there are any accounts Signed-off-by: Dominik Csapak --- changes from v1: * drop fieldLabel in ACMEAccountSelector www/manager6/Makefile| 2 ++ www/manager6/form/ACMEAccountSelector.js | 21 + www/manager6/form/ACMEPluginSelector.js | 19 +++ 3 files changed, 42 insertions(+) create mode 100644 www/manager6/form/ACMEAccountSelector.js create mode 100644 www/manager6/form/ACMEPluginSelector.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index e2214588..563e7dc5 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -73,6 +73,8 @@ JSSRC= \ form/SDNControllerSelector.js \ form/TFASelector.js \ form/ACMEAPiSelector.js \ + form/ACMEAccountSelector.js \ + form/ACMEPluginSelector.js \ dc/Tasks.js \ dc/Log.js \ panel/StatusPanel.js\ diff --git a/www/manager6/form/ACMEAccountSelector.js b/www/manager6/form/ACMEAccountSelector.js new file mode 100644 index ..12d5848c --- /dev/null +++ b/www/manager6/form/ACMEAccountSelector.js @@ -0,0 +1,21 @@ +Ext.define('PVE.form.ACMEAccountSelector', { +extend: 'Ext.form.field.ComboBox', +alias: 'widget.pveACMEAccountSelector', + +displayField: 'name', +valueField: 'name', + +store: { + model: 'pve-acme-accounts', + autoLoad: true, +}, + +triggerAction: 'all', +queryMode: 'local', +allowBlank: false, +editable: false, + +isEmpty: function() { + return this.getStore().getData().length === 0; +} +}); diff --git a/www/manager6/form/ACMEPluginSelector.js b/www/manager6/form/ACMEPluginSelector.js new file mode 100644 index ..ccce97b8 --- /dev/null +++ b/www/manager6/form/ACMEPluginSelector.js @@ -0,0 +1,19 @@ +Ext.define('PVE.form.ACMEPluginSelector', { +extend: 'Ext.form.field.ComboBox', +alias: 'widget.pveACMEPluginSelector', + +fieldLabel: gettext('Plugin'), +displayField: 'plugin', +valueField: 'plugin', + +store: { + model: 'pve-acme-plugins', + autoLoad: true, + filters: item => item.data.type === 'dns', +}, + +triggerAction: 'all', +queryMode: 'local', +allowBlank: false, +editable: false, +}); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager v2 0/5] ACME node adaptions for plugins
this series adapts the node->certificates->acme panel to include an 'accountselector' and to be able to add/edit/remove single domains, including ones with a plugin changes from v1: * drop fieldLabel in ACMEAccountSelector * reword 'Account' in 'Used Account' * use different approach to change the account (use viewModel and a displayfield/combobox/editbutton to better see when the account actually changes) Dominik Csapak (5): ui: add ACME selector formfields for account and plugins ui: Parser: add printACME ui: Utils: add helper functions for acme domains ui: node/ACME: add ACMEDomainEdit ui: node/ACME: rework ACME grid for plugin based domains www/manager6/Makefile| 2 + www/manager6/Parser.js | 7 + www/manager6/Utils.js| 23 + www/manager6/form/ACMEAccountSelector.js | 21 + www/manager6/form/ACMEPluginSelector.js | 19 + www/manager6/node/ACME.js| 617 --- 6 files changed, 517 insertions(+), 172 deletions(-) create mode 100644 www/manager6/form/ACMEAccountSelector.js create mode 100644 www/manager6/form/ACMEPluginSelector.js -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 1/5] ui: add ACME selector formfields for account and plugins
filter the plugins by type === 'dns' and add a convenience method for ACMEAccountSelector to check if there are any accounts Signed-off-by: Dominik Csapak --- www/manager6/Makefile| 2 ++ www/manager6/form/ACMEAccountSelector.js | 22 ++ www/manager6/form/ACMEPluginSelector.js | 19 +++ 3 files changed, 43 insertions(+) create mode 100644 www/manager6/form/ACMEAccountSelector.js create mode 100644 www/manager6/form/ACMEPluginSelector.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index e2214588..563e7dc5 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -73,6 +73,8 @@ JSSRC= \ form/SDNControllerSelector.js \ form/TFASelector.js \ form/ACMEAPiSelector.js \ + form/ACMEAccountSelector.js \ + form/ACMEPluginSelector.js \ dc/Tasks.js \ dc/Log.js \ panel/StatusPanel.js\ diff --git a/www/manager6/form/ACMEAccountSelector.js b/www/manager6/form/ACMEAccountSelector.js new file mode 100644 index ..5b533c1b --- /dev/null +++ b/www/manager6/form/ACMEAccountSelector.js @@ -0,0 +1,22 @@ +Ext.define('PVE.form.ACMEAccountSelector', { +extend: 'Ext.form.field.ComboBox', +alias: 'widget.pveACMEAccountSelector', + +fieldLabel: gettext('Account'), +displayField: 'name', +valueField: 'name', + +store: { + model: 'pve-acme-accounts', + autoLoad: true, +}, + +triggerAction: 'all', +queryMode: 'local', +allowBlank: false, +editable: false, + +isEmpty: function() { + return this.getStore().getData().length === 0; +} +}); diff --git a/www/manager6/form/ACMEPluginSelector.js b/www/manager6/form/ACMEPluginSelector.js new file mode 100644 index ..ccce97b8 --- /dev/null +++ b/www/manager6/form/ACMEPluginSelector.js @@ -0,0 +1,19 @@ +Ext.define('PVE.form.ACMEPluginSelector', { +extend: 'Ext.form.field.ComboBox', +alias: 'widget.pveACMEPluginSelector', + +fieldLabel: gettext('Plugin'), +displayField: 'plugin', +valueField: 'plugin', + +store: { + model: 'pve-acme-plugins', + autoLoad: true, + filters: item => item.data.type === 'dns', +}, + +triggerAction: 'all', +queryMode: 'local', +allowBlank: false, +editable: false, +}); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 0/5] ACME node adaptions for plugins
this series adapts the node->certificates->acme panel to include an 'accountselector' and to be able to add/edit/remove single domains, including ones with a plugin Dominik Csapak (5): ui: add ACME selector formfields for account and plugins ui: Parser: add printACME ui: Utils: add helper functions for acme domains ui: node/ACME: add ACMEDomainEdit ui: node/ACME: rework ACME grid for plugin based domains www/manager6/Makefile| 2 + www/manager6/Parser.js | 7 + www/manager6/Utils.js| 23 + www/manager6/form/ACMEAccountSelector.js | 22 + www/manager6/form/ACMEPluginSelector.js | 19 + www/manager6/node/ACME.js| 556 --- 6 files changed, 463 insertions(+), 166 deletions(-) create mode 100644 www/manager6/form/ACMEAccountSelector.js create mode 100644 www/manager6/form/ACMEPluginSelector.js -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 3/5] ui: Utils: add helper functions for acme domains
to convieniently add and remove domains from a parsed ACME object they also make domains unique in the array also add the count of configureable acmedomainX entries Signed-off-by: Dominik Csapak --- www/manager6/Utils.js | 23 +++ 1 file changed, 23 insertions(+) diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js index 872b7c29..31e262c0 100644 --- a/www/manager6/Utils.js +++ b/www/manager6/Utils.js @@ -1354,6 +1354,29 @@ Ext.define('PVE.Utils', { utilities: { } }, +acmedomain_count: 5, + +add_domain_to_acme: function(acme, domain) { + if (acme.domains === undefined) { + acme.domains = [domain]; + } else { + acme.domains.push(domain); + acme.domains = acme.domains.filter((value, index, self) => { + return self.indexOf(value) === index; + }); + } + return acme; +}, + +remove_domain_from_acme: function(acme, domain) { + if (acme.domains !== undefined) { + acme.domains = acme.domains.filter((value, index, self) => { + return self.indexOf(value) === index && value !== domain; + }); + } + return acme; +}, + handleStoreErrorOrMask: function(me, store, regex, callback) { me.mon(store, 'load', function (proxy, response, success, operation) { -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 2/5] ui: Parser: add printACME
since we decode the domain list in parseACME into an array, we have to join them again to a string when printing otherwise printPropertyString attaches them just with ',' which does not work here Signed-off-by: Dominik Csapak --- www/manager6/Parser.js | 7 +++ 1 file changed, 7 insertions(+) diff --git a/www/manager6/Parser.js b/www/manager6/Parser.js index 4cecb3e1..20d81d4a 100644 --- a/www/manager6/Parser.js +++ b/www/manager6/Parser.js @@ -5,6 +5,13 @@ Ext.define('PVE.Parser', { statics: { // this class only contains static functions +printACME: function(value) { + if (Ext.isArray(value.domains)) { + value.domains = value.domains.join(';'); + } + return PVE.Parser.printPropertyString(value); +}, + parseACME: function(value) { if (!value) { return; -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 4/5] ui: node/ACME: add ACMEDomainEdit
which expects a nodeconfig (for digest and domaincount) and for the edit case, the parsed 'domain' object this editwindow has three fields: * type selector (standalone/dns) * domain * plugin (only for dns) if the user chooses dns but there are already the maximum count of acmedomainX entries, the type field gets invalid (with a error tooltip) the onGetValues method is non-trivial, because of the mixing of acmedomainX and acme.domain values, so we have to be careful that we delete/edit the correct entry Signed-off-by: Dominik Csapak --- www/manager6/node/ACME.js | 136 ++ 1 file changed, 136 insertions(+) diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js index 40ecc00e..a8bb39d6 100644 --- a/www/manager6/node/ACME.js +++ b/www/manager6/node/ACME.js @@ -230,6 +230,142 @@ Ext.define('PVE.node.ACMEAccountView', { } }); +Ext.define('PVE.node.ACMEDomainEdit', { +extend: 'Proxmox.window.Edit', +alias: 'widget.pveACMEDomainEdit', + +subject: gettext('Domain'), +isCreate: false, + +items: [ + { + xtype: 'inputpanel', + onGetValues: function(values) { + let me = this; + let win = me.up('pveACMEDomainEdit'); + let nodeconfig = win.nodeconfig; + let olddomain = win.domain || {}; + + let params = { + digest: nodeconfig.digest, + }; + + let configkey = olddomain.configkey; + let acmeObj = PVE.Parser.parseACME(nodeconfig.acme) || {}; + + if (values.type === 'dns') { + if (!olddomain.configkey || olddomain.configkey === 'acme') { + // look for first free slot + for (let i = 0; i < PVE.Utils.acmedomain_count; i++) { + if (nodeconfig[`acmedomain${i}`] === undefined) { + configkey = `acmedomain${i}`; + break; + } + } + if (olddomain.domain) { + // we have to remove the domain from the acme domainlist + PVE.Utils.remove_domain_from_acme(acmeObj, olddomain.domain); + params.acme = PVE.Parser.printACME(acmeObj); + } + } + + delete values.type; + params[configkey] = PVE.Parser.printPropertyString(values, 'domain'); + } else { + if (olddomain.configkey && olddomain.configkey !== 'acme') { + // delete the old dns entry + params.delete = [olddomain.configkey]; + } + + // add new, remove old and make entries unique + PVE.Utils.add_domain_to_acme(acmeObj, values.domain); + PVE.Utils.remove_domain_from_acme(acmeObj, olddomain.domain); + params.acme = PVE.Parser.printACME(acmeObj); + } + + return params; + }, + items: [ + { + xtype: 'proxmoxKVComboBox', + name: 'type', + fieldLabel: gettext('Type'), + allowBlank: false, + comboItems: [ + ['standalone', 'standalone'], + ['dns', 'DNS'], + ], + validator: function(value) { + let me = this; + let win = me.up('pveACMEDomainEdit'); + let oldconfigkey = win.domain ? win.domain.configkey : undefined; + let val = me.getValue(); + if (val === 'dns' && (!oldconfigkey || oldconfigkey === 'acme')) { + // we have to check if there is a 'acmedomain' slot left + let found = false; + for (let i = 0; i < PVE.Utils.acmedomain_count; i++) { + if (!win.nodeconfig[`acmedomain${i}`]) { + found = true; + } + } + if (!found) { + return gettext('Only 5 Domains with type DNS can be configured'); + } + } + + return true; + }, + listeners: { + change: function(cb, value) { + let me = this; + let view = me.up('pveACMEDomainEdit'); + view.down('field[name=plugin]').setDisabled(value !== 'dns'); + }, + }, + }, + { +
[pve-devel] [PATCH manager 5/5] ui: node/ACME: rework ACME grid for plugin based domains
This is basically a complete rework of the ACME grid. Instead of having an ObjectGrid, we now have a normal GridPanel which allows us to show a row for each Domain. But to achieve this, we need to manually fill the store with data from the 'acme' and 'acmedomainX' entries of the node config. We also add an AccountSelector to the tbar and a link to the datacenter->acme panel (when there is no account) this also removes the 'register account' and 'view account' buttons, since those are now available in datacenter->acme removes the old 'acmeeditor' since that is no longer needed Signed-off-by: Dominik Csapak --- www/manager6/node/ACME.js | 420 +++--- 1 file changed, 254 insertions(+), 166 deletions(-) diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js index a8bb39d6..f6499d6e 100644 --- a/www/manager6/node/ACME.js +++ b/www/manager6/node/ACME.js @@ -1,49 +1,3 @@ -Ext.define('PVE.node.ACMEEditor', { -extend: 'Proxmox.window.Edit', -xtype: 'pveACMEEditor', - -subject: gettext('Domains'), -items: [ - { - xtype: 'inputpanel', - items: [ - { - xtype: 'textarea', - fieldLabel: gettext('Domains'), - emptyText: "domain1.example.com\ndomain2.example.com", - name: 'domains' - } - ], - onGetValues: function(values) { - if (!values.domains) { - return { - 'delete': 'acme' - }; - } - var domains = values.domains.split(/\n/).join(';'); - return { - 'acme': 'domains=' + domains - }; - } - } -], - -initComponent: function() { - var me = this; - me.callParent(); - - me.load({ - success: function(response, opts) { - var res = PVE.Parser.parseACME(response.result.data.acme); - if (res) { - res.domains = res.domains.join(' '); - me.setValues(res); - } - } - }); -} -}); - Ext.define('PVE.node.ACMEAccountCreate', { extend: 'Proxmox.window.Edit', @@ -366,138 +320,279 @@ Ext.define('PVE.node.ACMEDomainEdit', { }, }); +Ext.define('pve-acme-domains', { +extend: 'Ext.data.Model', +fields: ['domain', 'type', 'alias', 'plugin', 'configkey'], +idProperty: 'domain', +}); + Ext.define('PVE.node.ACME', { -extend: 'Proxmox.grid.ObjectGrid', -xtype: 'pveACMEView', +extend: 'Ext.grid.Panel', +alias: 'widget.pveACMEView', margin: '10 0 0 0', title: 'ACME', +controller: { + xclass: 'Ext.app.ViewController', + + addDomain: function() { + let me = this; + let view = me.getView(); + + Ext.create('PVE.node.ACMEDomainEdit', { + nodename: view.nodename, + nodeconfig: view.nodeconfig, + apiCallDone: function() { + me.reload(); + }, + }).show(); + }, + + editDomain: function() { + let me = this; + let view = me.getView(); + + let selection = view.getSelection(); + if (selection.length < 1) return; + + Ext.create('PVE.node.ACMEDomainEdit', { + nodename: view.nodename, + nodeconfig: view.nodeconfig, + domain: selection[0].data, + apiCallDone: function() { + me.reload(); + }, + }).show(); + }, + + removeDomain: function() { + let me = this; + let view = me.getView(); + let selection = view.getSelection(); + if (selection.length < 1) return; + + let rec = selection[0].data; + let params = {}; + if (rec.configkey !== 'acme') { + params.delete = rec.configkey; + } else { + let acme = PVE.Parser.parseACME(view.nodeconfig.acme); + PVE.Utils.remove_domain_from_acme(acme, rec.domain); + params.acme = PVE.Parser.printACME(acme); + } + + Proxmox.Utils.API2Request({ + method: 'PUT', + url: `/nodes/${view.nodename}/config`, + params, + success: function(response, opt) { + me.reload(); + }, + failure: function(response, opt) { + Ext.Msg.alert(gettext('Error'), response.htmlStatus); + }, + }); + }, + + changeAccount: function(cb, value, oldvalue) { + if (value === oldvalue) return; + let me = this; + let view = me.getView(); + let params = {}; + + let acme = PVE.Parser.parseACME(view.nodeconfig.acme); +
[pve-devel] [PATCH manager 1/6] ACMEPlugin: check digest on update
and extract the param, otherwise the check dies because of an unknown field 'digest' Signed-off-by: Dominik Csapak --- PVE/API2/ACMEPlugin.pm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PVE/API2/ACMEPlugin.pm b/PVE/API2/ACMEPlugin.pm index 92e0dfb6..71f53a35 100644 --- a/PVE/API2/ACMEPlugin.pm +++ b/PVE/API2/ACMEPlugin.pm @@ -173,9 +173,11 @@ __PACKAGE__->register_method({ my $id = extract_param($param, 'id'); my $delete = extract_param($param, 'delete'); + my $digest = extract_param($param, 'digest'); cfs_lock_file($plugin_config_file, undef, sub { my $cfg = load_config(); + PVE::Tools::assert_if_modified($cfg->{digest}, $digest); my $plugin_cfg = $cfg->{ids}->{$id}; die "ACME plugin ID '$id' does not exist\n" if !$plugin_cfg; -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH 1/2] DNSChallenge: make plugins a hash with an optional schema
so that we can use that schema to generate form fields in the gui Signed-off-by: Dominik Csapak --- src/PVE/ACME/DNSChallenge.pm | 235 --- 1 file changed, 132 insertions(+), 103 deletions(-) diff --git a/src/PVE/ACME/DNSChallenge.pm b/src/PVE/ACME/DNSChallenge.pm index 2b4f051..8a393f4 100644 --- a/src/PVE/ACME/DNSChallenge.pm +++ b/src/PVE/ACME/DNSChallenge.pm @@ -18,109 +18,138 @@ sub type { return 'dns'; } -my $plugin_names = [ -'acmedns', -'acmeproxy', -'active24', -'ad', -'ali', -'autodns', -'aws', -'azure', -'cf', -'clouddns', -'cloudns', -'cn', -'conoha', -'constellix', -'cx', -'cyon', -'da', -'ddnss', -'desec', -'dgon', -'dnsimple', -'do', -'doapi', -'domeneshop', -'dp', -'dpi', -'dreamhost', -'duckdns', -'durabledns', -'dyn', -'dynu', -'dynv6', -'easydns', -'euserv', -'exoscale', -'freedns', -'gandi_livedns', -'gcloud', -'gd', -'gdnsdk', -'he', -'hexonet', -'hostingde', -'infoblox', -'internetbs', -'inwx', -'ispconfig', -'jd', -'kas', -'kinghost', -'knot', -'leaseweb', -'lexicon', -'linode', -'linode_v4', -'loopia', -'lua', -'maradns', -'me', -'miab', -'misaka', -'myapi', -'mydevil', -'mydnsjp', -'namecheap', -'namecom', -'namesilo', -'nederhost', -'neodigit', -'netcup', -'nic', -'nsd', -'nsone', -'nsupdate', -'nw', -'one', -'online', -'openprovider', -'opnsense', -'ovh', -'pdns', -'pleskxml', -'pointhq', -'rackspace', -'rcode0', -'regru', -'schlundtech', -'selectel', -'servercow', -'tele3', -'ultra', -'unoeuro', -'variomedia', -'vscale', -'vultr', -'yandex', -'zilore', -'zone', -'zonomi', -]; +# describe the data schema of the supported plugins +my $plugins = { +'acmedns' => {}, +'acmeproxy' => {}, +'active24' => {}, +'ad' => {}, +'ali' => {}, +'autodns' => {}, +'aws' => {}, +'azure' => {}, +'cf' => {}, +'clouddns' => {}, +'cloudns' => {}, +'cn' => {}, +'conoha' => {}, +'constellix' => {}, +'cx' => {}, +'cyon' => {}, +'da' => {}, +'ddnss' => {}, +'desec' => {}, +'dgon' => {}, +'dnsimple' => {}, +'do' => {}, +'doapi' => {}, +'domeneshop' => {}, +'dp' => {}, +'dpi' => {}, +'dreamhost' => {}, +'duckdns' => {}, +'durabledns' => {}, +'dyn' => {}, +'dynu' => {}, +'dynv6' => {}, +'easydns' => {}, +'euserv' => {}, +'exoscale' => {}, +'freedns' => {}, +'gandi_livedns' => {}, +'gcloud' => {}, +'gd' => {}, +'gdnsdk' => {}, +'he' => {}, +'hexonet' => {}, +'hostingde' => {}, +'infoblox' => {}, +'internetbs' => {}, +'inwx' => {}, +'ispconfig' => {}, +'jd' => {}, +'kas' => {}, +'kinghost' => {}, +'knot' => {}, +'leaseweb' => {}, +'lexicon' => {}, +'linode' => {}, +'linode_v4' => {}, +'loopia' => {}, +'lua' => {}, +'maradns' => {}, +'me' => {}, +'miab' => {}, +'misaka' => {}, +'myapi' => {}, +'mydevil' => {}, +'mydnsjp' => {}, +'namecheap' => {}, +'namecom' => {}, +'namesilo' => {}, +'nederhost' => {}, +'neodigit' => {}, +'netcup' => {}, +'nic' => {}, +'nsd' => {}, +'nsone' => {}, +'nsupdate' => {}, +'nw' => {}, +'one' => {}, +'online' => {}, +'openprovider' => {}, +'opnsense' => {}, +'ovh' => { + 'OVH_END_POINT' => { + description => "The OVH endpoint", + type => 'string', + }, + 'OVH_AK' => { + description => "The application key.", + type => 'string', + }, + 'OVH_AS' => { + description => "The application secret.", + type => 'string', + }, +}, +'pdns' => { + 'PDNS_Url' => { + description => "The PowerDNS API endpoint.", + type => 'string', + }, + 'PDNS_ServerId'=> { + type => 'string', + }, + 'PDNS_Token'=> { + type => 'string', + }, + 'PDNS_Ttl'=> { + type => 'integer', + }, +}, +'pleskxml' => {}, +'pointhq' => {}, +'rackspace' => {}, +'rcode0' => {}, +'regru' => {}, +'schlundtech' => {}, +'selectel' => {}, +'servercow' => {}, +'tele3' => {}, +'ultra' =>
[pve-devel] [PATCH 2/2] add note that the data has to be base64 encoded
but only via api, on the cli it is a file which contains the data in plaintext Signed-off-by: Dominik Csapak --- src/PVE/ACME/DNSChallenge.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PVE/ACME/DNSChallenge.pm b/src/PVE/ACME/DNSChallenge.pm index 8a393f4..534922d 100644 --- a/src/PVE/ACME/DNSChallenge.pm +++ b/src/PVE/ACME/DNSChallenge.pm @@ -161,7 +161,7 @@ sub properties { }, data => { type => 'string', - description => 'DNS plugin data.', + description => 'DNS plugin data. (base64 encoded)', }, 'validation-delay' => { type => 'integer', -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH proxmox-acme/pve-manager] add crud for acme accounts/plugin
this series adds the gui (and necessary api calls) for adding/editing/deleting acme accounts and plugins gui parts still missing are: * changing the account on the node * selecting a plugin for a domain proxmox-acme: Dominik Csapak (2): DNSChallenge: make plugins a hash with an optional schema add note that the data has to be base64 encoded src/PVE/ACME/DNSChallenge.pm | 237 --- 1 file changed, 133 insertions(+), 104 deletions(-) pve-manager: Dominik Csapak (6): ACMEPlugin: check digest on update ACME: add challengeschema api call ui: ACMEAccountCreate: add optional name field ui: add ACMEAPiSelector field ui: add ACMEPluginEdit window ui: add ACMEClusterView PVE/API2/ACMEAccount.pm | 55 +++ PVE/API2/ACMEPlugin.pm | 2 + www/manager6/Makefile| 3 + www/manager6/Parser.js | 17 ++- www/manager6/dc/ACMEClusterView.js | 206 +++ www/manager6/dc/ACMEPluginEdit.js| 190 www/manager6/dc/Config.js| 10 ++ www/manager6/form/ACMEAPiSelector.js | 40 ++ www/manager6/node/ACME.js| 7 + 9 files changed, 529 insertions(+), 1 deletion(-) create mode 100644 www/manager6/dc/ACMEClusterView.js create mode 100644 www/manager6/dc/ACMEPluginEdit.js create mode 100644 www/manager6/form/ACMEAPiSelector.js -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 6/6] ui: add ACMEClusterView
to show the list of accounts and defined plugins for now we ignore 'standalone' plugins here and only show 'dns' ones Signed-off-by: Dominik Csapak --- www/manager6/Makefile | 1 + www/manager6/dc/ACMEClusterView.js | 206 + www/manager6/dc/Config.js | 10 ++ 3 files changed, 217 insertions(+) create mode 100644 www/manager6/dc/ACMEClusterView.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index fb4c51bb..4deb7990 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -217,6 +217,7 @@ JSSRC= \ ha/GroupEdit.js \ ha/Groups.js\ ha/Fencing.js \ + dc/ACMEClusterView.js \ dc/ACMEPluginEdit.js\ dc/Summary.js \ grid/Replication.js \ diff --git a/www/manager6/dc/ACMEClusterView.js b/www/manager6/dc/ACMEClusterView.js new file mode 100644 index ..a0bbd8a5 --- /dev/null +++ b/www/manager6/dc/ACMEClusterView.js @@ -0,0 +1,206 @@ +Ext.define('pve-acme-accounts', { +extend: 'Ext.data.Model', +fields: ['name'], +proxy: { + type: 'proxmox', + url: "/api2/json/cluster/acme/account", +}, +idProperty: 'name', +}); + +Ext.define('pve-acme-plugins', { +extend: 'Ext.data.Model', +fields: ['type', 'plugin'], +proxy: { + type: 'proxmox', + url: "/api2/json/cluster/acme/plugins", +}, +idProperty: 'plugin', +}); + +Ext.define('PVE.dc.ACMEAccountView', { +extend: 'Ext.grid.Panel', +alias: 'widget.pveACMEAccountView', + +title: gettext('Accounts'), + +controller: { + xclass: 'Ext.app.ViewController', + + addAccount: function() { + let me = this; + Ext.create('PVE.node.ACMEAccountCreate', { + taskDone: function() { + me.reload(); + }, + }).show(); + }, + + viewAccount: function() { + let me = this; + let view = me.getView(); + let selection = view.getSelection(); + if (selection.length < 1) return; + Ext.create('PVE.node.ACMEAccountView', { + accountname: selection[0].data.name, + }).show(); + }, + + reload: function() { + let me = this; + let view = me.getView(); + view.getStore().load(); + }, +}, + +columns: [ + { + dataIndex: 'name', + text: gettext('Name'), + renderer: Ext.String.htmlEncode, + flex: 1, + }, +], + +tbar: [ + { + xtype: 'proxmoxButton', + text: gettext('Add'), + selModel: false, + handler: 'addAccount', + }, + { + xtype: 'proxmoxButton', + text: gettext('View'), + handler: 'viewAccount', + disabled: true, + }, + { + xtype: 'proxmoxStdRemoveButton', + baseurl: '/cluster/acme/account', + callback: 'reload', + }, +], + +listeners: { + itemdblclick: 'viewAccount', +}, + +store: { + model: 'pve-acme-accounts', + autoLoad: true, + sorters: 'name', +}, +}); + +Ext.define('PVE.dc.ACMEPluginView', { +extend: 'Ext.grid.Panel', +alias: 'widget.pveACMEPluginView', + +title: gettext('Plugins'), + +controller: { + xclass: 'Ext.app.ViewController', + + addPlugin: function() { + let me = this; + Ext.create('PVE.dc.ACMEPluginEditor', { + isCreate: true, + apiCallDone: function() { + me.reload(); + }, + }).show(); + }, + + editPlugin: function() { + let me = this; + let view = me.getView(); + let selection = view.getSelection(); + if (selection.length < 1) return; + let plugin = selection[0].data.plugin; + Ext.create('PVE.dc.ACMEPluginEditor', { + url: `/cluster/acme/plugins/${plugin}`, + apiCallDone: function() { + me.reload(); + }, + }).show(); + }, + + reload: function() { + let me = this; + let view = me.getView(); + view.getStore().load(); + }, +}, + +columns: [ + { + dataIndex: 'plugin', + text: gettext('Plugin'), + renderer: Ext.String.htmlEncode, + flex: 1, + }, + { + dataIndex: 'api', + text: gettext('API'), + renderer: Ext.String.htmlEncode, + flex: 1, + }, +], + +tbar: [ + { +
[pve-devel] [PATCH manager 2/6] ACME: add challengeschema api call
which returns a list of challenge api types with the schema of their required data (if it exists) Signed-off-by: Dominik Csapak --- PVE/API2/ACMEAccount.pm | 55 + 1 file changed, 55 insertions(+) diff --git a/PVE/API2/ACMEAccount.pm b/PVE/API2/ACMEAccount.pm index baff26bd..cbfa19a7 100644 --- a/PVE/API2/ACMEAccount.pm +++ b/PVE/API2/ACMEAccount.pm @@ -9,6 +9,7 @@ use PVE::Exception qw(raise_param_exc); use PVE::JSONSchema qw(get_standard_option); use PVE::RPCEnvironment; use PVE::Tools qw(extract_param); +use PVE::ACME::Challenge; use PVE::API2::ACMEPlugin; @@ -63,6 +64,7 @@ __PACKAGE__->register_method ({ { name => 'tos' }, { name => 'directories' }, { name => 'plugins' }, + { name => 'challengeschema' }, ]; }}); @@ -366,4 +368,57 @@ __PACKAGE__->register_method ({ return $acme_directories; }}); +__PACKAGE__->register_method ({ +name => 'challengeschema', +path => 'challengeschema', +method => 'GET', +description => "Get schema of ACME challenge types.", +permissions => { user => 'all' }, +parameters => { + additionalProperties => 0, + properties => {}, +}, +returns => { + type => 'array', + items => { + type => 'object', + additionalProperties => 0, + properties => { + name => { + type => 'string', + }, + type => { + type => 'string', + }, + schema => { + type => 'object', + }, + }, + }, +}, +code => sub { + my ($param) = @_; + + my $plugin_type_enum = PVE::ACME::Challenge->lookup_types(); + + my $res = []; + + for my $type (@$plugin_type_enum) { + my $plugin = PVE::ACME::Challenge->lookup($type); + if ($plugin->can('get_supported_plugins')) { + my $type = $plugin->type(); + my $plugins = $plugin->get_supported_plugins(); + for my $name (sort keys %$plugins) { + push @$res, { + name => $name, + type => $type, + schema => $plugins->{$name}, + }; + } + } + } + + return $res; +}}); + 1; -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 4/6] ui: add ACMEAPiSelector field
which return all api types from /cluster/acme/challengeschema and has a convenience method for getting the schema of the current value Signed-off-by: Dominik Csapak --- www/manager6/Makefile| 1 + www/manager6/form/ACMEAPiSelector.js | 40 2 files changed, 41 insertions(+) create mode 100644 www/manager6/form/ACMEAPiSelector.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index ff93b224..62253ede 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -72,6 +72,7 @@ JSSRC= \ form/SDNZoneSelector.js \ form/SDNControllerSelector.js \ form/TFASelector.js \ + form/ACMEAPiSelector.js \ dc/Tasks.js \ dc/Log.js \ panel/StatusPanel.js\ diff --git a/www/manager6/form/ACMEAPiSelector.js b/www/manager6/form/ACMEAPiSelector.js new file mode 100644 index ..1ae40849 --- /dev/null +++ b/www/manager6/form/ACMEAPiSelector.js @@ -0,0 +1,40 @@ +Ext.define('pve-acme-challenges', { +extend: 'Ext.data.Model', +fields: ['name', 'schema'], +proxy: { + type: 'proxmox', + url: "/api2/json/cluster/acme/challengeschema", +}, +idProperty: 'name', +}); + +Ext.define('PVE.form.ACMEApiSelector', { +extend: 'Ext.form.field.ComboBox', +alias: 'widget.pveACMEApiSelector', + +fieldLabel: gettext('API'), +displayField: 'name', +valueField: 'name', + +store: { + model: 'pve-acme-challenges', + autoLoad: true, +}, + +triggerAction: 'all', +queryMode: 'local', +allowBlank: false, +editable: false, + +getSchema: function() { + let me = this; + let val = me.getValue(); + if (val) { + let record = me.getStore().findRecord('name', val); + if (record) { + return record.data.schema; + } + } + return {}; +}, +}); -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager 5/6] ui: add ACMEPluginEdit window
this is a rather complex edit window, because we dynamically create form fields according to the schema we get from the api to do this properly we have to handle a few things: * we have to properly set the values on edit * we have to properly track the original values * we have to merge and split with/from the generic 'data' field (so that if a plugin has some extra fields that we did not include in the schema the user can still enter them) Signed-off-by: Dominik Csapak --- www/manager6/Makefile | 1 + www/manager6/Parser.js| 17 ++- www/manager6/dc/ACMEPluginEdit.js | 190 ++ 3 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 www/manager6/dc/ACMEPluginEdit.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index 62253ede..fb4c51bb 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -217,6 +217,7 @@ JSSRC= \ ha/GroupEdit.js \ ha/Groups.js\ ha/Fencing.js \ + dc/ACMEPluginEdit.js\ dc/Summary.js \ grid/Replication.js \ dc/Health.js\ diff --git a/www/manager6/Parser.js b/www/manager6/Parser.js index 43cc4f5f..4cecb3e1 100644 --- a/www/manager6/Parser.js +++ b/www/manager6/Parser.js @@ -696,5 +696,20 @@ Ext.define('PVE.Parser', { statics: { }; } return null; -} +}, + +parseACMEPluginData: function(data) { + let res = {}; + let extradata = []; + data.split('\n').forEach((line) => { + // capture everything after the first = as value + let [key, value] = line.split(/=(.+)/); + if (value !== undefined) { + res[key] = value; + } else { + extradata.push(line); + } + }); + return [res, extradata]; +}, }}); diff --git a/www/manager6/dc/ACMEPluginEdit.js b/www/manager6/dc/ACMEPluginEdit.js new file mode 100644 index ..fd07017c --- /dev/null +++ b/www/manager6/dc/ACMEPluginEdit.js @@ -0,0 +1,190 @@ +Ext.define('PVE.dc.ACMEPluginEditor', { +extend: 'Proxmox.window.Edit', +xtype: 'pveACMEPluginEditor', +mixins: ['Proxmox.Mixin.CBind'], + +isAdd: true, +isCreate: false, + +width: 400, +url: '/cluster/acme/plugins/', + +subject: gettext('Plugin'), +items: [ + { + xtype: 'inputpanel', + // we dynamically create fields from the given schema + // things we have to do here: + // * save which fields we created to remove them again + // * split the data from the generic 'data' field into the boxes + // * on deletion collect those values again + // * save the original values of the data field + createdFields: {}, + createdInitially: false, + originalValues: {}, + createSchemaFields: function(schema) { + let me = this; + // we know where to add because we define it right below + let container = me.down('container'); + let datafield = me.down('field[name=data]'); + if (!me.createdInitially) { + [me.originalValues] = PVE.Parser.parseACMEPluginData(datafield.getValue()); + } + + // collect values from custom fields and add it to 'data'', + // then remove the custom fields + let data = []; + for (const [name, field] of Object.entries(me.createdFields)) { + let value = field.getValue(); + if (value !== undefined && value !== null && value !== '') { + data.push(`${name}=${value}`); + } + container.remove(field); + } + let datavalue = datafield.getValue(); + if (datavalue !== undefined && datavalue !== null && datavalue !== '') { + data.push(datavalue); + } + datafield.setValue(data.join('\n')); + + me.createdFields = {}; + + // create custom fields according to schema + for (const [name, definition] of Object.entries(schema)) { + let xtype; + switch (definition.type) { + case 'string': + xtype = 'proxmoxtextfield'; + break; + case 'integer': + xtype = 'proxmoxintegerfield'; + break; + case 'number': + xtype = 'numberfield'; + bre
[pve-devel] [PATCH manager 3/6] ui: ACMEAccountCreate: add optional name field
so that we can create multiple accounts, but leave it empty and set the emptyText to the default name 'default' Signed-off-by: Dominik Csapak --- www/manager6/node/ACME.js | 7 +++ 1 file changed, 7 insertions(+) diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js index cfdcc5cf..40ecc00e 100644 --- a/www/manager6/node/ACME.js +++ b/www/manager6/node/ACME.js @@ -56,6 +56,13 @@ Ext.define('PVE.node.ACMEAccountCreate', { showTaskViewer: true, items: [ + { + xtype: 'proxmoxtextfield', + fieldLabel: gettext('Name'), + name: 'name', + emptyText: 'default', + allowBlank: true, + }, { xtype: 'proxmoxComboGrid', name: 'directory', -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH docs v3] add documenation for ldap syncing
explaining the main Requirements and limitations, as well as the most important sync options Signed-off-by: Dominik Csapak --- changes from v2: * incorporated suggestions from aaron @aaron, regarding linking to character limitations, sadly no, this is a sub based format, so even if we would have a place were we could link to for formats (we don't afaik) since it's sub based there would be no auto-generated information just for completeness, atm the limit is >2 and <60 characters, and no '/', ':' (maybe we can change it to a regex?) pveum.adoc | 48 1 file changed, 48 insertions(+) diff --git a/pveum.adoc b/pveum.adoc index c89d4b8..7f8bd67 100644 --- a/pveum.adoc +++ b/pveum.adoc @@ -170,6 +170,54 @@ A server and authentication domain need to be specified. Like with ldap an optional fallback server, optional port, and SSL encryption can be configured. +[[pveum_ldap_sync]] +Syncing LDAP-based realms +~ + +It is possible to sync users and groups for LDAP based realms using + pveum sync +or in the `Authentication` panel of the GUI. Users and groups are synced +to `/etc/pve/user.cfg`. + +Requirements and limitations + + +The `bind_dn` is used to query the users and groups. This account +needs access to all desired entries. + +The fields which represent the names of the users and groups can be configured +via the `user_attr` and `group_name_attr` respectively. Only entries which +adhere to the usual character limitations of the user.cfg are synced. + +Groups are synced with `-$realm` attached to the name, to avoid naming +conflicts. Please make sure that a sync does not overwrite manually created +groups. + +Options +^^^ + +The main options for syncing are: + +* `dry-run`: No data is written to the config. This is useful if you want to + see which users and groups would get synced to the user.cfg. This is set + when you click `Preview` in the GUI. + +* `enable-new`: If set, the newly synced users are enabled and can login. + The default is `true`. + +* `full`: If set, the sync uses the LDAP Directory as a source of truth, + overwriting information set manually in the user.cfg and deletes users + and groups which are not present in the LDAP directory. If not set, + only new data is written to the config, and no stale users are deleted. + +* `purge`: If set, sync removes all corresponding ACLs when removing users + and groups. This is only useful with the option `full`. + +* `scope`: The scope of what to sync. It can be either `users`, `groups` or + `both`. + +These options are either set as parameters or as defaults, via the +realm option `sync-defaults-options`. [[pveum_tfa_auth]] Two-factor authentication -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager] ui: fix missing htmlEncodes
username can include some special characters, so we have to escape them Signed-off-by: Dominik Csapak --- www/manager6/Workspace.js | 2 +- www/manager6/dc/ACLView.js | 2 +- www/manager6/dc/GroupView.js | 1 + www/manager6/dc/Log.js | 2 ++ www/manager6/dc/PermissionView.js | 3 ++- www/manager6/dc/TFAEdit.js | 1 + www/manager6/dc/Tasks.js | 1 + www/manager6/dc/TokenEdit.js | 1 + www/manager6/dc/TokenView.js | 4 ++-- www/manager6/dc/UserEdit.js| 1 + www/manager6/dc/UserView.js| 4 ++-- www/manager6/form/GroupSelector.js | 1 + www/manager6/form/TokenSelector.js | 1 + www/manager6/form/UserSelector.js | 1 + www/manager6/window/Settings.js| 2 +- 15 files changed, 19 insertions(+), 8 deletions(-) diff --git a/www/manager6/Workspace.js b/www/manager6/Workspace.js index 01b462c7..a95b88d7 100644 --- a/www/manager6/Workspace.js +++ b/www/manager6/Workspace.js @@ -182,7 +182,7 @@ Ext.define('PVE.StdWorkspace', { updateUserInfo: function() { var me = this; var ui = me.query('#userinfo')[0]; - ui.setText(Proxmox.UserName || ''); + ui.setText(Ext.String.htmlEncode(Proxmox.UserName || '')); ui.updateLayout(); }, diff --git a/www/manager6/dc/ACLView.js b/www/manager6/dc/ACLView.js index d0efe22e..24fd67d9 100644 --- a/www/manager6/dc/ACLView.js +++ b/www/manager6/dc/ACLView.js @@ -118,7 +118,7 @@ Ext.define('PVE.dc.ACLView', { return '@' + ugid; } - return ugid; + return Ext.String.htmlEncode(ugid); }; var columns = [ diff --git a/www/manager6/dc/GroupView.js b/www/manager6/dc/GroupView.js index c40c5ba1..960ad114 100644 --- a/www/manager6/dc/GroupView.js +++ b/www/manager6/dc/GroupView.js @@ -92,6 +92,7 @@ Ext.define('PVE.dc.GroupView', { header: gettext('Users'), sortable: false, dataIndex: 'users', + renderer: Ext.String.htmlEncode, flex: 1 } ], diff --git a/www/manager6/dc/Log.js b/www/manager6/dc/Log.js index 48ce272e..fa58c08a 100644 --- a/www/manager6/dc/Log.js +++ b/www/manager6/dc/Log.js @@ -68,6 +68,7 @@ Ext.define('PVE.dc.Log', { { header: gettext("User name"), dataIndex: 'user', + renderer: Ext.String.htmlEncode, width: 150 }, { @@ -79,6 +80,7 @@ Ext.define('PVE.dc.Log', { { header: gettext("Message"), dataIndex: 'msg', + renderer: Ext.String.htmlEncode, flex: 1 } ], diff --git a/www/manager6/dc/PermissionView.js b/www/manager6/dc/PermissionView.js index 483ab015..cc582261 100644 --- a/www/manager6/dc/PermissionView.js +++ b/www/manager6/dc/PermissionView.js @@ -140,7 +140,8 @@ Ext.define('PVE.dc.PermissionView', { height: 600, layout: 'fit', cbind: { - title: '{userid} - ' + gettext('Granted Permissions'), + title: (get) => Ext.String.htmlEncode(get('userid')) + + ` - ${gettext('Granted Permissions')}`, }, items: [{ xtype: 'pveUserPermissionGrid', diff --git a/www/manager6/dc/TFAEdit.js b/www/manager6/dc/TFAEdit.js index bf51b8c9..3aada4cd 100644 --- a/www/manager6/dc/TFAEdit.js +++ b/www/manager6/dc/TFAEdit.js @@ -376,6 +376,7 @@ Ext.define('PVE.window.TFAEdit', { { xtype: 'displayfield', fieldLabel: gettext('User name'), + renderer: Ext.String.htmlEncode, cbind: { value: '{userid}' } diff --git a/www/manager6/dc/Tasks.js b/www/manager6/dc/Tasks.js index a011fe4f..b1441a72 100644 --- a/www/manager6/dc/Tasks.js +++ b/www/manager6/dc/Tasks.js @@ -101,6 +101,7 @@ Ext.define('PVE.dc.Tasks', { { header: gettext("User name"), dataIndex: 'user', + renderer: Ext.String.htmlEncode, width: 150 }, { diff --git a/www/manager6/dc/TokenEdit.js b/www/manager6/dc/TokenEdit.js index cdb5d911..13f1dff8 100644 --- a/www/manager6/dc/TokenEdit.js +++ b/www/manager6/dc/TokenEdit.js @@ -41,6 +41,7 @@ Ext.define('PVE.dc.TokenEdit', { }, name: 'userid', value: Proxmox.UserName, + renderer: Ext.String.htmlEncode, fieldLabel: gettext('User'), }, { diff --git a/www/manager6/dc/TokenView.js b/www/manager6/dc/TokenView.js index c81d5f2f..69c60569 100644 --- a/www/manager6/dc/T
[pve-devel] [PATCH widget-toolkit] add missing htmlEncodes
username can include some special characters, so we have to escape them Signed-off-by: Dominik Csapak --- window/TaskViewer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/window/TaskViewer.js b/window/TaskViewer.js index 347542e..bbbf716 100644 --- a/window/TaskViewer.js +++ b/window/TaskViewer.js @@ -127,6 +127,7 @@ Ext.define('Proxmox.window.TaskViewer', { }, user: { header: gettext('User name'), + renderer: Ext.String.htmlEncode, required: true }, node: { @@ -146,7 +147,8 @@ Ext.define('Proxmox.window.TaskViewer', { renderer: Proxmox.Utils.render_timestamp }, upid: { - header: gettext('Unique task ID') + header: gettext('Unique task ID'), + renderer: Ext.String.htmlEncode, } }; -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH docs v2] add documenation for ldap syncing
explaining the main Requirements and limitations, as well as the most important sync options Signed-off-by: Dominik Csapak --- changes from v1: * incorporated suggestions from Alwin, thanks :) * re-worded the sentence about limitations to specify the character limitations of user.cfg pveum.adoc | 47 +++ 1 file changed, 47 insertions(+) diff --git a/pveum.adoc b/pveum.adoc index c89d4b8..80b3385 100644 --- a/pveum.adoc +++ b/pveum.adoc @@ -170,6 +170,53 @@ A server and authentication domain need to be specified. Like with ldap an optional fallback server, optional port, and SSL encryption can be configured. +[[pveum_ldap_sync]] +Syncing LDAP-based realms +~ + +It is possible to sync users and groups for LDAP based realms using + pveum sync +or in the `Authentication` panel of the GUI to the user.cfg. + +Requirements and limitations + + +The `bind_dn` is used to query the users and groups, so this account +should be able to see all desired entries. + +The fields which represent the names of the users and groups can be configured +via the `user_attr` and `group_name_attr` respectively. Only entries which +adhere to the usual character limitations of the user.cfg are synced. + +Groups are synced with `-$realm` attached to the name, to avoid naming +conflicts. Please make sure that a sync does not overwrite manually created +groups. + +Options +^^^ + +The main options for syncing are: + +* `dry-run`: No data is written to the config. This is useful if you want to + see which users and groups would get synced to the user.cfg. This is set + when you click `Preview` in the GUI. + +* `enable-new`: If set, the newly synced users are enabled and can login. + The default is `true`. + +* `full`: If set, the sync uses the LDAP Directory as a source of truth, + overwriting information set manually in the user.cfg and deleting users + and groups which are not returned. If not set, only new data is + written to the config, and no stale users are deleted. + +* `purge`: If set, sync removes all corresponding ACLs when removing users + and groups. This is only useful with the option `full`. + +* `scope`: The scope of what to sync. It can be either `users`, `groups` or + `both`. + +These options are either set as parameters or as defaults, via the +realm option `sync-defaults-options`. [[pveum_tfa_auth]] Two-factor authentication -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] [PATCH manager 2/3] Allow setting targetstorage for offline migration
On 4/30/20 1:15 PM, Fabian Ebner wrote: Let's make this one an RFC ;) The problem is that offline migration with target storage might not always work depending on supported export/import formats. Then users might start an offline migration, which then fails after a few disks have already been copied. If we had a check for the export/import formats before starting migration, it would improve. But currently the erroring out happens on a per disk basis inside storage_migrate. So I'm not sure anymore if this is an improvement. If not, and if patch #3 is fine, I'll send a v2 without this one. if we cannot produce a warning/error beforehand, i would abstain from allowing it in the gui (for now) we had a similar issue with local disks, which is the reason most of this panel exists in the first place having the user wait for an error late into the migration is really not nice and frustrating for the user but for 3/3 i wanted to send a very similar patch myself, so that one is ack'ed by me ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] [PATCH manager 1/3] Don't show empty parentheses when size is not known
one comment inline On 4/30/20 12:59 PM, Fabian Ebner wrote: The size of VM state files and the size of unused disks not referenced by any snapshot is not saved in the VM configuration, so it's not available here either. Signed-off-by: Fabian Ebner --- www/manager6/window/Migrate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/manager6/window/Migrate.js b/www/manager6/window/Migrate.js index 61bc6a49..9fc66a9b 100644 --- a/www/manager6/window/Migrate.js +++ b/www/manager6/window/Migrate.js @@ -269,7 +269,7 @@ Ext.define('PVE.window.Migrate', { migration['with-local-disks'] = 1; migration.preconditions.push({ text:'Migration with local disk might take long: ' + disk.volid - +' (' + PVE.Utils.render_size(disk.size) + ')', + + (disk.size ? ' (' + PVE.Utils.render_size(disk.size) + ')' : ''),\ we are already deeply nested here and the line is already very long, i would rather put the text above in a variable (and maybe use template strings) severity: 'warning' }); } ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH manager] ui: dc/SyncWindow: add help button
with link to the LDAP Syncing section of the documentation Signed-off-by: Dominik Csapak --- www/manager6/dc/SyncWindow.js | 8 1 file changed, 8 insertions(+) diff --git a/www/manager6/dc/SyncWindow.js b/www/manager6/dc/SyncWindow.js index e85e5b88..9355c551 100644 --- a/www/manager6/dc/SyncWindow.js +++ b/www/manager6/dc/SyncWindow.js @@ -21,6 +21,7 @@ Ext.define('PVE.dc.SyncWindow', { }, 'button': { click: function(btn) { + if (btn.reference === 'help_btn') return; this.sync_realm(btn.reference === 'preview_btn'); }, }, @@ -144,6 +145,13 @@ Ext.define('PVE.dc.SyncWindow', { ], buttons: [ + { + xtype: 'proxmoxHelpButton', + reference: 'help_btn', + onlineHelp: 'pveum_ldap_sync', + hidden: false, + }, + '->', { text: gettext('Preview'), reference: 'preview_btn', -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH docs] add documenation for ldap syncing
explaining the main Requirements and limitations, as well as the most important sync options Signed-off-by: Dominik Csapak --- pveum.adoc | 47 +++ 1 file changed, 47 insertions(+) diff --git a/pveum.adoc b/pveum.adoc index c89d4b8..5881fa9 100644 --- a/pveum.adoc +++ b/pveum.adoc @@ -170,6 +170,53 @@ A server and authentication domain need to be specified. Like with ldap an optional fallback server, optional port, and SSL encryption can be configured. +[[pveum_ldap_sync]] +Syncing LDAP-based realms +~ + +It is possible to sync users and groups for ldap based realms using + pveum sync +or in the `Authentication` panel of the GUI to the user.cfg. + +Requirements and limitations + + +The `bind_dn` will be used to query the users and groups, so this account +should be able to see all desired entries. + +The names of the users and groups (configurable via `user_attr` and +`group_name_attr` respectively) have to adhere to the limitations of usual +users and groups in the config. + +Groups will be synced with `-$realm` attached to the name, to avoid naming +conflicts. Please make sure that a sync does not overwrite manually created +groups. + +Options +^^^ + +The main options for syncing are: + +* `dry-run`: No data will actually be synced. This is useful if you want to + see which users and groups would get synced to the user.cfg. This is set + when you click `Preview` in the GUI. + +* `enable-new`: If set, the newly synced users are enabled and can login. + The default is `true`. + +* `full`: If set, the sync usses the LDAP Directory as source of truth, + overwriting information set manually in the user.cfg and deleting users + and groups which were not returned. If not set, only new data + will be written to the config, and no stale users will be deleted. + +* `purge`: If set, sync removes all corresponding ACLs when removing users + and groups. This is only useful with the option `full`. + +* `scope`: The scope of what to sync. Can be either `users`, `groups` or + `both`. + +These options either to be set either as parameters, or as defaults, via the +realm option `sync-defaults-options`. [[pveum_tfa_auth]] Two-factor authentication -- 2.20.1 ___ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel