* [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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox