* [pbs-devel] [PATCH widget-toolkit/proxmox-backup] implement traffic-control ui
@ 2021-11-19 14:42 Dominik Csapak
2021-11-19 14:42 ` [pbs-devel] [PATCH widget-toolkit 1/1] form: copy BandwidthSelector/SizeField from PVE Dominik Csapak
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Dominik Csapak @ 2021-11-19 14:42 UTC (permalink / raw)
To: pbs-devel
this series implements a ui for the recent traffic-control feature
it consists of a basic grid with config + status display
as well as an edit window to create/edit rules
for now, it assumes that the api returns/expects only bytes, so if
we want to extend this, we'll have to extend the bandwithselector too
proxmox-widget-toolkit:
Dominik Csapak (1):
form: copy BandwidthSelector/SizeField from PVE
src/Makefile | 1 +
src/form/BandwidthSelector.js | 154 ++++++++++++++++++++++++++++++++++
2 files changed, 155 insertions(+)
create mode 100644 src/form/BandwidthSelector.js
proxmox-backup:
Dominik Csapak (2):
api: traffic_control: add missing rename to 'kebab-case'
ui: add Traffic Control UI
src/api2/config/traffic_control.rs | 1 +
www/Makefile | 2 +
www/NavigationTree.js | 6 +
www/config/TrafficControlView.js | 197 ++++++++++++
www/window/TrafficControlEdit.js | 464 +++++++++++++++++++++++++++++
5 files changed, 670 insertions(+)
create mode 100644 www/config/TrafficControlView.js
create mode 100644 www/window/TrafficControlEdit.js
--
2.30.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [pbs-devel] [PATCH widget-toolkit 1/1] form: copy BandwidthSelector/SizeField from PVE
2021-11-19 14:42 [pbs-devel] [PATCH widget-toolkit/proxmox-backup] implement traffic-control ui Dominik Csapak
@ 2021-11-19 14:42 ` Dominik Csapak
2021-11-19 15:12 ` [pbs-devel] applied: " Thomas Lamprecht
2021-11-19 14:42 ` [pbs-devel] [PATCH proxmox-backup 1/2] api: traffic_control: add missing rename to 'kebab-case' Dominik Csapak
2021-11-19 14:42 ` [pbs-devel] [PATCH proxmox-backup 2/2] ui: add Traffic Control UI Dominik Csapak
2 siblings, 1 reply; 7+ messages in thread
From: Dominik Csapak @ 2021-11-19 14:42 UTC (permalink / raw)
To: pbs-devel
and replace pve with pmx
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/Makefile | 1 +
src/form/BandwidthSelector.js | 154 ++++++++++++++++++++++++++++++++++
2 files changed, 155 insertions(+)
create mode 100644 src/form/BandwidthSelector.js
diff --git a/src/Makefile b/src/Makefile
index c245a53..de34531 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -23,6 +23,7 @@ JSSRC= \
data/model/Realm.js \
data/model/Certificates.js \
data/model/ACME.js \
+ form/BandwidthSelector.js \
form/DisplayEdit.js \
form/ExpireDate.js \
form/IntegerField.js \
diff --git a/src/form/BandwidthSelector.js b/src/form/BandwidthSelector.js
new file mode 100644
index 0000000..ce941cd
--- /dev/null
+++ b/src/form/BandwidthSelector.js
@@ -0,0 +1,154 @@
+Ext.define('Proxmox.form.SizeField', {
+ extend: 'Ext.form.FieldContainer',
+ alias: 'widget.pmxSizeField',
+
+ mixins: ['Proxmox.Mixin.CBind'],
+
+ viewModel: {
+ data: {
+ unit: 'MiB',
+ unitPostfix: '',
+ },
+ formulas: {
+ unitlabel: (get) => get('unit') + get('unitPostfix'),
+ },
+ },
+
+ emptyText: '',
+
+ layout: 'hbox',
+ defaults: {
+ hideLabel: true,
+ },
+
+ units: {
+ 'B': 1,
+ 'KiB': 1024,
+ 'MiB': 1024*1024,
+ 'GiB': 1024*1024*1024,
+ 'TiB': 1024*1024*1024*1024,
+ 'KB': 1000,
+ 'MB': 1000*1000,
+ 'GB': 1000*1000*1000,
+ 'TB': 1000*1000*1000*1000,
+ },
+
+ // display unit (TODO: make (optionally) selectable)
+ unit: 'MiB',
+ unitPostfix: '',
+
+ // use this if the backend saves values in another unit tha bytes, e.g.,
+ // for KiB set it to 'KiB'
+ backendUnit: undefined,
+
+ // allow setting 0 and using it as a submit value
+ allowZero: false,
+
+ emptyValue: null,
+
+ items: [
+ {
+ xtype: 'numberfield',
+ cbind: {
+ name: '{name}',
+ emptyText: '{emptyText}',
+ allowZero: '{allowZero}',
+ emptyValue: '{emptyValue}',
+ },
+ minValue: 0,
+ step: 1,
+ submitLocaleSeparator: false,
+ fieldStyle: 'text-align: right',
+ flex: 1,
+ enableKeyEvents: true,
+ setValue: function(v) {
+ if (!this._transformed) {
+ let fieldContainer = this.up('fieldcontainer');
+ let vm = fieldContainer.getViewModel();
+ let unit = vm.get('unit');
+
+ v /= fieldContainer.units[unit];
+ v *= fieldContainer.backendFactor;
+
+ this._transformed = true;
+ }
+
+ if (Number(v) === 0 && !this.allowZero) {
+ v = undefined;
+ }
+
+ return Ext.form.field.Text.prototype.setValue.call(this, v);
+ },
+ getSubmitValue: function() {
+ let v = this.processRawValue(this.getRawValue());
+ v = v.replace(this.decimalSeparator, '.');
+
+ if (v === undefined || v === '') {
+ return this.emptyValue;
+ }
+
+ if (Number(v) === 0) {
+ return this.allowZero ? 0 : null;
+ }
+
+ let fieldContainer = this.up('fieldcontainer');
+ let vm = fieldContainer.getViewModel();
+ let unit = vm.get('unit');
+
+ v = parseFloat(v) * fieldContainer.units[unit];
+ v /= fieldContainer.backendFactor;
+
+ return String(Math.floor(v));
+ },
+ listeners: {
+ // our setValue gets only called if we have a value, avoid
+ // transformation of the first user-entered value
+ keydown: function() { this._transformed = true; },
+ },
+ },
+ {
+ xtype: 'displayfield',
+ name: 'unit',
+ submitValue: false,
+ padding: '0 0 0 10',
+ bind: {
+ value: '{unitlabel}',
+ },
+ listeners: {
+ change: (f, v) => {
+ f.originalValue = v;
+ },
+ },
+ width: 40,
+ },
+ ],
+
+ initComponent: function() {
+ let me = this;
+
+ me.unit = me.unit || 'MiB';
+ if (!(me.unit in me.units)) {
+ throw "unknown unit: " + me.unit;
+ }
+
+ me.backendFactor = 1;
+ if (me.backendUnit !== undefined) {
+ if (!(me.unit in me.units)) {
+ throw "unknown backend unit: " + me.backendUnit;
+ }
+ me.backendFactor = me.units[me.backendUnit];
+ }
+
+ me.callParent(arguments);
+
+ me.getViewModel().set('unit', me.unit);
+ me.getViewModel().set('unitPostfix', me.unitPostfix);
+ },
+});
+
+Ext.define('Proxmox.form.BandwidthField', {
+ extend: 'Proxmox.form.SizeField',
+ alias: 'widget.pmxBandwidthField',
+
+ unitPostfix: '/s',
+});
--
2.30.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 1/2] api: traffic_control: add missing rename to 'kebab-case'
2021-11-19 14:42 [pbs-devel] [PATCH widget-toolkit/proxmox-backup] implement traffic-control ui Dominik Csapak
2021-11-19 14:42 ` [pbs-devel] [PATCH widget-toolkit 1/1] form: copy BandwidthSelector/SizeField from PVE Dominik Csapak
@ 2021-11-19 14:42 ` Dominik Csapak
2021-11-20 21:47 ` [pbs-devel] applied: " Thomas Lamprecht
2021-11-19 14:42 ` [pbs-devel] [PATCH proxmox-backup 2/2] ui: add Traffic Control UI Dominik Csapak
2 siblings, 1 reply; 7+ messages in thread
From: Dominik Csapak @ 2021-11-19 14:42 UTC (permalink / raw)
To: pbs-devel
otherwise the 'delete' properties need underscores
(e.g. 'burst_in' instead of 'burst-in')
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/api2/config/traffic_control.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/api2/config/traffic_control.rs b/src/api2/config/traffic_control.rs
index fa580a55..dec0e3b2 100644
--- a/src/api2/config/traffic_control.rs
+++ b/src/api2/config/traffic_control.rs
@@ -99,6 +99,7 @@ pub fn read_traffic_control(
#[api()]
#[derive(Serialize, Deserialize)]
#[allow(non_camel_case_types)]
+#[serde(rename_all = "kebab-case")]
/// Deletable property name
pub enum DeletableProperty {
/// Delete the rate_in property.
--
2.30.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 2/2] ui: add Traffic Control UI
2021-11-19 14:42 [pbs-devel] [PATCH widget-toolkit/proxmox-backup] implement traffic-control ui Dominik Csapak
2021-11-19 14:42 ` [pbs-devel] [PATCH widget-toolkit 1/1] form: copy BandwidthSelector/SizeField from PVE Dominik Csapak
2021-11-19 14:42 ` [pbs-devel] [PATCH proxmox-backup 1/2] api: traffic_control: add missing rename to 'kebab-case' Dominik Csapak
@ 2021-11-19 14:42 ` Dominik Csapak
2021-11-20 21:57 ` [pbs-devel] applied: " Thomas Lamprecht
2 siblings, 1 reply; 7+ messages in thread
From: Dominik Csapak @ 2021-11-19 14:42 UTC (permalink / raw)
To: pbs-devel
adds a list of traffic control rules (with their current usage)
and let the user add/edit/remove them
the edit window currently has a grid for timeframes to add/remove
with input fields for start/endtime and checkboxes for the days
there are still some improvements possible, like having a seperate
grid for networks (the input field is maybe too small), or
optimizing consecutive days to a range (e.g. mon..wed instead of mon,tue,wed)
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
www/Makefile | 2 +
www/NavigationTree.js | 6 +
www/config/TrafficControlView.js | 197 +++++++++++++
www/window/TrafficControlEdit.js | 464 +++++++++++++++++++++++++++++++
4 files changed, 669 insertions(+)
create mode 100644 www/config/TrafficControlView.js
create mode 100644 www/window/TrafficControlEdit.js
diff --git a/www/Makefile b/www/Makefile
index 32a6d7d5..616c3e12 100644
--- a/www/Makefile
+++ b/www/Makefile
@@ -47,6 +47,7 @@ JSSRC= \
config/UserView.js \
config/TokenView.js \
config/RemoteView.js \
+ config/TrafficControlView.js \
config/ACLView.js \
config/SyncView.js \
config/VerifyView.js \
@@ -60,6 +61,7 @@ JSSRC= \
window/DataStoreEdit.js \
window/NotesEdit.js \
window/RemoteEdit.js \
+ window/TrafficControlEdit.js \
window/NotifyOptions.js \
window/SyncJobEdit.js \
window/UserEdit.js \
diff --git a/www/NavigationTree.js b/www/NavigationTree.js
index 6035526c..3b4e54ce 100644
--- a/www/NavigationTree.js
+++ b/www/NavigationTree.js
@@ -50,6 +50,12 @@ Ext.define('PBS.store.NavigationStore', {
path: 'pbsRemoteView',
leaf: true,
},
+ {
+ text: gettext('Traffic Control'),
+ iconCls: 'fa fa-exchange fa-rotate-90',
+ path: 'pbsTrafficControlView',
+ leaf: true,
+ },
{
text: gettext('Certificates'),
iconCls: 'fa fa-certificate',
diff --git a/www/config/TrafficControlView.js b/www/config/TrafficControlView.js
new file mode 100644
index 00000000..70532d6c
--- /dev/null
+++ b/www/config/TrafficControlView.js
@@ -0,0 +1,197 @@
+Ext.define('pmx-traffic-control', {
+ extend: 'Ext.data.Model',
+ fields: [
+ 'name', 'rate-in', 'rate-out', 'burst-in', 'burst-out', 'network',
+ 'timeframe', 'comment', 'cur-rate-in', 'cur-rate-out',
+ {
+ name: 'rateInUsed',
+ calculate: function(data) {
+ return (data['cur-rate-in'] || 0) / (data['rate-in'] || Infinity);
+ },
+ },
+ {
+ name: 'rateOutUsed',
+ calculate: function(data) {
+ return (data['cur-rate-out'] || 0) / (data['rate-out'] || Infinity);
+ },
+ },
+ ],
+ idProperty: 'name',
+ proxy: {
+ type: 'proxmox',
+ url: '/api2/json/admin/traffic-control',
+ },
+});
+
+Ext.define('PBS.config.TrafficControlView', {
+ extend: 'Ext.grid.GridPanel',
+ alias: 'widget.pbsTrafficControlView',
+
+ stateful: true,
+ stateId: 'grid-traffic-control',
+
+ title: gettext('Traffic Control'),
+
+// tools: [PBS.Utils.get_help_tool("backup-remote")],
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+
+ addRemote: function() {
+ let me = this;
+ Ext.create('PBS.window.TrafficControlEdit', {
+ listeners: {
+ destroy: function() {
+ me.reload();
+ },
+ },
+ }).show();
+ },
+
+ editRemote: function() {
+ let me = this;
+ let view = me.getView();
+ let selection = view.getSelection();
+ if (selection.length < 1) return;
+
+ Ext.create('PBS.window.TrafficControlEdit', {
+ name: selection[0].data.name,
+ listeners: {
+ destroy: function() {
+ me.reload();
+ },
+ },
+ }).show();
+ },
+
+ render_bandwidth: (value) => value ? Proxmox.Utils.format_size(value) + '/s' : '',
+
+ reload: function() { this.getView().getStore().rstore.load(); },
+
+ init: function(view) {
+ Proxmox.Utils.monStoreErrors(view, view.getStore().rstore);
+ },
+ },
+
+ listeners: {
+ activate: 'reload',
+ itemdblclick: 'editRemote',
+ },
+
+ store: {
+ type: 'diff',
+ autoDestroy: true,
+ autoDestroyRstore: true,
+ sorters: 'name',
+ rstore: {
+ type: 'update',
+ storeid: 'pmx-traffic-control',
+ model: 'pmx-traffic-control',
+ autoStart: true,
+ interval: 5000,
+ },
+ },
+
+ tbar: [
+ {
+ xtype: 'proxmoxButton',
+ text: gettext('Add'),
+ handler: 'addRemote',
+ selModel: false,
+ },
+ {
+ xtype: 'proxmoxButton',
+ text: gettext('Edit'),
+ handler: 'editRemote',
+ disabled: true,
+ },
+ {
+ xtype: 'proxmoxStdRemoveButton',
+ baseurl: '/config/traffic-control',
+ callback: 'reload',
+ },
+ ],
+
+ viewConfig: {
+ trackOver: false,
+ },
+
+ columns: [
+ {
+ header: gettext('Rule'),
+ width: 200,
+ sortable: true,
+ renderer: Ext.String.htmlEncode,
+ dataIndex: 'name',
+ },
+ {
+ header: gettext('Rate In'),
+ width: 200,
+ sortable: true,
+ renderer: 'render_bandwidth',
+ dataIndex: 'rate-in',
+ },
+ {
+ header: gettext('Rate In Used'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'rateInUsed',
+ widget: {
+ xtype: 'progressbarwidget',
+ textTpl: '{percent:number("0")}%',
+ animate: true,
+ },
+ },
+ {
+ header: gettext('Rate Out'),
+ width: 200,
+ sortable: true,
+ renderer: 'render_bandwidth',
+ dataIndex: 'rate-out',
+ },
+ {
+ header: gettext('Rate Out Used'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'rateOutUsed',
+ widget: {
+ xtype: 'progressbarwidget',
+ textTpl: '{percent:number("0")}%',
+ animate: true,
+ },
+ },
+ {
+ header: gettext('Burst In'),
+ width: 200,
+ sortable: true,
+ renderer: 'render_bandwidth',
+ dataIndex: 'burst-in',
+ },
+ {
+ header: gettext('Burst Out'),
+ width: 200,
+ sortable: true,
+ renderer: 'render_bandwidth',
+ dataIndex: 'burst-out',
+ },
+ {
+ header: gettext('Networks'),
+ width: 200,
+ sortable: true,
+ renderer: Ext.String.htmlEncode,
+ dataIndex: 'network',
+ },
+ {
+ header: gettext('Timeframes'),
+ sortable: false,
+ renderer: (timeframes) => Ext.String.htmlEncode(timeframes.join('; ')),
+ dataIndex: 'timeframe',
+ width: 200,
+ },
+ {
+ header: gettext('Comment'),
+ sortable: false,
+ renderer: Ext.String.htmlEncode,
+ dataIndex: 'comment',
+ flex: 1,
+ },
+ ],
+});
diff --git a/www/window/TrafficControlEdit.js b/www/window/TrafficControlEdit.js
new file mode 100644
index 00000000..24e6b63f
--- /dev/null
+++ b/www/window/TrafficControlEdit.js
@@ -0,0 +1,464 @@
+Ext.define('PBS.window.TrafficControlEdit', {
+ extend: 'Proxmox.window.Edit',
+ alias: 'widget.pbsTrafficControlEdit',
+ mixins: ['Proxmox.Mixin.CBind'],
+
+ onlineHelp: 'sysadmin_traffic_control',
+ width: 800,
+
+ isAdd: true,
+
+ subject: gettext('Traffic Control Rule'),
+
+ fieldDefaults: { labelWidth: 120 },
+
+ cbindData: function(initialConfig) {
+ let me = this;
+
+ let baseurl = '/api2/extjs/config/traffic-control';
+ let name = initialConfig.name;
+
+ me.isCreate = !name;
+ me.url = name ? `${baseurl}/${name}` : baseurl;
+ me.method = name ? 'PUT' : 'POST';
+ return { };
+ },
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+
+ weekdays: ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'],
+
+ dowChanged: function(field, value) {
+ let me = this;
+ let record = field.getWidgetRecord();
+ if (record === undefined) {
+ // this is sometimes called before a record/column is initialized
+ return;
+ }
+ let col = field.getWidgetColumn();
+ record.set(col.dataIndex, value);
+ record.commit();
+
+ me.updateTimeframeField();
+ },
+
+ timeChanged: function(field, value) {
+ let me = this;
+ if (value === null) {
+ return;
+ }
+ let record = field.getWidgetRecord();
+ if (record === undefined) {
+ // this is sometimes called before a record/column is initialized
+ return;
+ }
+ let col = field.getWidgetColumn();
+ let hours = value.getHours().toString().padStart(2, '0');
+ let minutes = value.getMinutes().toString().padStart(2, '0');
+ record.set(col.dataIndex, `${hours}:${minutes}`);
+ record.commit();
+
+ me.updateTimeframeField();
+ },
+
+ addTimeframe: function() {
+ let me = this;
+ me.lookup('timeframes').getStore().add({
+ start: "00:00",
+ end: "23:59",
+ mon: true,
+ tue: true,
+ wed: true,
+ thu: true,
+ fri: true,
+ sat: true,
+ sun: true,
+ });
+
+ me.updateTimeframeField();
+ },
+
+ updateTimeframeField: function() {
+ let me = this;
+
+ let timeframes = [];
+ me.lookup('timeframes').getStore().each((rec) => {
+ let timeframe = '';
+ let days = me.weekdays.filter(day => rec.data[day]);
+ if (days.length < 7 && days.length > 0) {
+ timeframe += days.join(',') + ' ';
+ }
+ let { start, end } = rec.data;
+
+ timeframe += `${start}-${end}`;
+ timeframes.push(timeframe);
+ });
+
+ let field = me.lookup('timeframe');
+ field.suspendEvent('change');
+ field.setValue(timeframes.join(';'));
+ field.resumeEvent('change');
+ },
+
+ removeTimeFrame: function(field) {
+ let me = this;
+ let record = field.getWidgetRecord();
+ if (record === undefined) {
+ // this is sometimes called before a record/column is initialized
+ return;
+ }
+
+ me.lookup('timeframes').getStore().remove(record);
+ me.updateTimeframeField();
+ },
+
+ parseTimeframe: function(timeframe) {
+ let me = this;
+ let [, days, start, end] = /^(?:(\S*)\s+)?([0-9:]+)-([0-9:]+)$/.exec(timeframe) || [];
+
+ if (start === '0') {
+ start = "00:00";
+ }
+
+ let record = {
+ start,
+ end,
+ };
+
+ if (!days) {
+ days = 'mon..sun';
+ }
+
+ days = days.split(',');
+ days.forEach((day) => {
+ if (record[day]) {
+ return;
+ }
+
+ if (me.weekdays.indexOf(day) !== -1) {
+ record[day] = true;
+ } else {
+ // we have a range 'xxx..yyy'
+ let [startDay, endDay] = day.split('..');
+ let startIdx = me.weekdays.indexOf(startDay);
+ let endIdx = me.weekdays.indexOf(endDay);
+
+ if (endIdx < startIdx) {
+ endIdx += me.weekdays.length;
+ }
+
+ for (let dayIdx = startIdx; dayIdx <= endIdx; dayIdx++) {
+ let curDay = me.weekdays[dayIdx%me.weekdays.length];
+ if (!record[curDay]) {
+ record[curDay] = true;
+ }
+ }
+ }
+ });
+
+ return record;
+ },
+
+ setGridData: function(field, value) {
+ let me = this;
+ if (!value) {
+ return;
+ }
+
+ value = value.split(';');
+ let records = value.map((timeframe) => me.parseTimeframe(timeframe));
+ me.lookup('timeframes').getStore().setData(records);
+ },
+
+ control: {
+ 'grid checkbox': {
+ change: 'dowChanged',
+ },
+ 'grid timefield': {
+ change: 'timeChanged',
+ },
+ 'grid button': {
+ click: 'removeTimeFrame',
+ },
+ 'field[name=timeframe]': {
+ change: 'setGridData',
+ },
+ },
+ },
+
+ items: {
+ xtype: 'inputpanel',
+ onGetValues: function(values) {
+ let me = this;
+ let isCreate = me.up('window').isCreate;
+
+ if (values['network-select'] === 'all') {
+ values.network = '0.0.0.0/0';
+ } else if (values.network) {
+ values.network = values.network.split(/\s*,\s*/);
+ }
+
+ if (!Ext.isArray(values.timeframe)) {
+ values.timeframe = values.timeframe.split(';');
+ }
+
+ delete values['network-select'];
+
+ if (!isCreate) {
+ PBS.Utils.delete_if_default(values, 'rate-in');
+ PBS.Utils.delete_if_default(values, 'rate-out');
+ PBS.Utils.delete_if_default(values, 'burst-in');
+ PBS.Utils.delete_if_default(values, 'burst-out');
+ if (typeof values.delete === 'string') {
+ values.delete = values.delete.split(',');
+ }
+ }
+
+ return values;
+ },
+ column1: [
+ {
+ xtype: 'pmxDisplayEditField',
+ name: 'name',
+ fieldLabel: gettext('Name'),
+ renderer: Ext.htmlEncode,
+ allowBlank: false,
+ minLength: 4,
+ cbind: {
+ editable: '{isCreate}',
+ },
+ },
+ {
+ xtype: 'pmxBandwidthField',
+ fieldLabel: gettext('Rate In'),
+ name: 'rate-in',
+ },
+ {
+ xtype: 'pmxBandwidthField',
+ fieldLabel: gettext('Rate Out'),
+ name: 'rate-out',
+ },
+ ],
+
+ column2: [
+ {
+ xtype: 'proxmoxtextfield',
+ name: 'comment',
+ cbind: {
+ deleteEmpty: '{!isCreate}',
+ },
+ fieldLabel: gettext('Comment'),
+ },
+ {
+ xtype: 'pmxBandwidthField',
+ fieldLabel: gettext('Burst In'),
+ name: 'burst-in',
+ },
+ {
+ xtype: 'pmxBandwidthField',
+ fieldLabel: gettext('Burst Out'),
+ name: 'burst-out',
+ },
+ ],
+
+ columnB: [
+ {
+ xtype: 'fieldcontainer',
+ fieldLabel: gettext('Network'),
+ layout: {
+ type: 'hbox',
+ align: 'stretch',
+ },
+ items: [
+ {
+ flex: 1,
+ xtype: 'radiofield',
+ boxLabel: gettext('All Networks'),
+ name: 'network-select',
+ value: true,
+ inputValue: 'all',
+ },
+ {
+ xtype: 'radiofield',
+ boxLabel: gettext('Limit to'),
+ name: 'network-select',
+ inputValue: 'limit',
+ listeners: {
+ change: function(field, value) {
+ this.up('window').lookup('network').setDisabled(!value);
+ },
+ },
+ },
+ {
+ flex: 1,
+ margin: '0 0 0 10',
+ xtype: 'proxmoxtextfield',
+ name: 'network',
+ reference: 'network',
+ disabled: true,
+ },
+ ],
+ },
+ {
+ xtype: 'displayfield',
+ fieldLabel: gettext('Timeframes'),
+ },
+ {
+ xtype: 'fieldcontainer',
+ items: [
+ {
+ xtype: 'grid',
+ height: 150,
+ scrollable: true,
+ reference: 'timeframes',
+ store: {
+ fields: ['start', 'end', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'],
+ data: [],
+ },
+ columns: [
+ {
+ text: gettext('Time Start'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'start',
+ widget: {
+ xtype: 'timefield',
+ isFormField: false,
+ format: 'H:i',
+ formatText: 'HH:MM',
+ },
+ flex: 1,
+ },
+ {
+ text: gettext('Time End'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'end',
+ widget: {
+ xtype: 'timefield',
+ isFormField: false,
+ format: 'H:i',
+ formatText: 'HH:MM',
+ maxValue: '23:59',
+ },
+ flex: 1,
+ },
+ {
+ text: gettext('Mon'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'mon',
+ width: 60,
+ widget: {
+ xtype: 'checkbox',
+ isFormField: false,
+ },
+ },
+ {
+ text: gettext('Tue'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'tue',
+ width: 60,
+ widget: {
+ xtype: 'checkbox',
+ isFormField: false,
+ },
+ },
+ {
+ text: gettext('Wed'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'wed',
+ width: 60,
+ widget: {
+ xtype: 'checkbox',
+ isFormField: false,
+ },
+ },
+ {
+ text: gettext('Thu'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'thu',
+ width: 60,
+ widget: {
+ xtype: 'checkbox',
+ isFormField: false,
+ },
+ },
+ {
+ text: gettext('Fri'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'fri',
+ width: 60,
+ widget: {
+ xtype: 'checkbox',
+ isFormField: false,
+ },
+ },
+ {
+ text: gettext('Sat'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'sat',
+ width: 60,
+ widget: {
+ xtype: 'checkbox',
+ isFormField: false,
+ },
+ },
+ {
+ text: gettext('Sun'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'sun',
+ width: 60,
+ widget: {
+ xtype: 'checkbox',
+ isFormField: false,
+ },
+ },
+ {
+ xtype: 'widgetcolumn',
+ width: 40,
+ widget: {
+ xtype: 'button',
+ iconCls: 'fa fa-trash-o',
+ },
+ },
+ ],
+ },
+ ],
+ },
+ {
+ xtype: 'button',
+ text: gettext('Add'),
+ iconCls: 'fa fa-plus-circle',
+ handler: 'addTimeframe',
+ },
+ {
+ xtype: 'hidden',
+ reference: 'timeframe',
+ name: 'timeframe',
+ },
+ ],
+ },
+
+ initComponent: function() {
+ let me = this;
+ me.callParent();
+ if (!me.isCreate) {
+ me.load({
+ success: function(response) {
+ let data = response.result.data;
+ if (data.network?.length === 1 && data.network[0] === '0.0.0.0/0') {
+ data['network-select'] = 'all';
+ delete data.network;
+ } else {
+ data['network-select'] = 'limit';
+ }
+
+ if (Ext.isArray(data.timeframe)) {
+ data.timeframe = data.timeframe.join(';');
+ }
+
+ me.setValues(data);
+ },
+ });
+ }
+ },
+});
--
2.30.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [pbs-devel] applied: [PATCH widget-toolkit 1/1] form: copy BandwidthSelector/SizeField from PVE
2021-11-19 14:42 ` [pbs-devel] [PATCH widget-toolkit 1/1] form: copy BandwidthSelector/SizeField from PVE Dominik Csapak
@ 2021-11-19 15:12 ` Thomas Lamprecht
0 siblings, 0 replies; 7+ messages in thread
From: Thomas Lamprecht @ 2021-11-19 15:12 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Dominik Csapak
On 19.11.21 15:42, Dominik Csapak wrote:
> and replace pve with pmx
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
> src/Makefile | 1 +
> src/form/BandwidthSelector.js | 154 ++++++++++++++++++++++++++++++++++
> 2 files changed, 155 insertions(+)
> create mode 100644 src/form/BandwidthSelector.js
>
>
applied, thanks!
^ permalink raw reply [flat|nested] 7+ messages in thread
* [pbs-devel] applied: [PATCH proxmox-backup 1/2] api: traffic_control: add missing rename to 'kebab-case'
2021-11-19 14:42 ` [pbs-devel] [PATCH proxmox-backup 1/2] api: traffic_control: add missing rename to 'kebab-case' Dominik Csapak
@ 2021-11-20 21:47 ` Thomas Lamprecht
0 siblings, 0 replies; 7+ messages in thread
From: Thomas Lamprecht @ 2021-11-20 21:47 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Dominik Csapak
On 19.11.21 15:42, Dominik Csapak wrote:
> otherwise the 'delete' properties need underscores
> (e.g. 'burst_in' instead of 'burst-in')
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
> src/api2/config/traffic_control.rs | 1 +
> 1 file changed, 1 insertion(+)
>
>
applied, thanks!
^ permalink raw reply [flat|nested] 7+ messages in thread
* [pbs-devel] applied: [PATCH proxmox-backup 2/2] ui: add Traffic Control UI
2021-11-19 14:42 ` [pbs-devel] [PATCH proxmox-backup 2/2] ui: add Traffic Control UI Dominik Csapak
@ 2021-11-20 21:57 ` Thomas Lamprecht
0 siblings, 0 replies; 7+ messages in thread
From: Thomas Lamprecht @ 2021-11-20 21:57 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Dominik Csapak
On 19.11.21 15:42, Dominik Csapak wrote:
> adds a list of traffic control rules (with their current usage)
> and let the user add/edit/remove them
>
> the edit window currently has a grid for timeframes to add/remove
> with input fields for start/endtime and checkboxes for the days
>
> there are still some improvements possible, like having a seperate
> grid for networks (the input field is maybe too small), or
> optimizing consecutive days to a range (e.g. mon..wed instead of mon,tue,wed)
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
> www/Makefile | 2 +
> www/NavigationTree.js | 6 +
> www/config/TrafficControlView.js | 197 +++++++++++++
> www/window/TrafficControlEdit.js | 464 +++++++++++++++++++++++++++++++
> 4 files changed, 669 insertions(+)
> create mode 100644 www/config/TrafficControlView.js
> create mode 100644 www/window/TrafficControlEdit.js
>
>
applied, thanks! Made a few follow ups for some bug fixes (e.g., for empty/no
timeframes on add/edit) and adapted it to the HumanByte change of the backend.
We can still go with bytes only if we think this is not a good idea (I won't
upload any changes to public yet til at least Monday).
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2021-11-20 21:57 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-19 14:42 [pbs-devel] [PATCH widget-toolkit/proxmox-backup] implement traffic-control ui Dominik Csapak
2021-11-19 14:42 ` [pbs-devel] [PATCH widget-toolkit 1/1] form: copy BandwidthSelector/SizeField from PVE Dominik Csapak
2021-11-19 15:12 ` [pbs-devel] applied: " Thomas Lamprecht
2021-11-19 14:42 ` [pbs-devel] [PATCH proxmox-backup 1/2] api: traffic_control: add missing rename to 'kebab-case' Dominik Csapak
2021-11-20 21:47 ` [pbs-devel] applied: " Thomas Lamprecht
2021-11-19 14:42 ` [pbs-devel] [PATCH proxmox-backup 2/2] ui: add Traffic Control UI Dominik Csapak
2021-11-20 21:57 ` [pbs-devel] applied: " Thomas Lamprecht
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal