]> git.proxmox.com Git - proxmox-backup.git/blob - www/window/TrafficControlEdit.js
caf79b5799d9c4e416e6bf77ab1cd335f6adf55f
[proxmox-backup.git] / www / window / TrafficControlEdit.js
1 Ext.define('PBS.window.TrafficControlEdit', {
2 extend: 'Proxmox.window.Edit',
3 alias: 'widget.pbsTrafficControlEdit',
4 mixins: ['Proxmox.Mixin.CBind'],
5
6 onlineHelp: 'sysadmin_traffic_control',
7 width: 800,
8
9 isAdd: true,
10
11 subject: gettext('Traffic Control Rule'),
12
13 fieldDefaults: { labelWidth: 120 },
14
15 cbindData: function(initialConfig) {
16 let me = this;
17
18 let baseurl = '/api2/extjs/config/traffic-control';
19 let name = initialConfig.name;
20
21 me.isCreate = !name;
22 me.url = name ? `${baseurl}/${name}` : baseurl;
23 me.method = name ? 'PUT' : 'POST';
24 return { };
25 },
26
27 controller: {
28 xclass: 'Ext.app.ViewController',
29
30 weekdays: ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'],
31
32 dowChanged: function(field, value) {
33 let me = this;
34 let record = field.getWidgetRecord();
35 if (record === undefined) {
36 // this is sometimes called before a record/column is initialized
37 return;
38 }
39 let col = field.getWidgetColumn();
40 record.set(col.dataIndex, value);
41 record.commit();
42
43 me.updateTimeframeField();
44 },
45
46 timeChanged: function(field, value) {
47 let me = this;
48 if (value === null) {
49 return;
50 }
51 let record = field.getWidgetRecord();
52 if (record === undefined) {
53 // this is sometimes called before a record/column is initialized
54 return;
55 }
56 let col = field.getWidgetColumn();
57 let hours = value.getHours().toString().padStart(2, '0');
58 let minutes = value.getMinutes().toString().padStart(2, '0');
59 record.set(col.dataIndex, `${hours}:${minutes}`);
60 record.commit();
61
62 me.updateTimeframeField();
63 },
64
65 addTimeframe: function() {
66 let me = this;
67 me.lookup('timeframes').getStore().add({
68 start: "00:00",
69 end: "23:59",
70 mon: true,
71 tue: true,
72 wed: true,
73 thu: true,
74 fri: true,
75 sat: true,
76 sun: true,
77 });
78
79 me.updateTimeframeField();
80 },
81
82 updateTimeframeField: function() {
83 let me = this;
84
85 let timeframes = [];
86 me.lookup('timeframes').getStore().each((rec) => {
87 let timeframe = '';
88 let days = me.weekdays.filter(day => rec.data[day]);
89 if (days.length < 7 && days.length > 0) {
90 timeframe += days.join(',') + ' ';
91 }
92 let { start, end } = rec.data;
93
94 timeframe += `${start}-${end}`;
95 timeframes.push(timeframe);
96 });
97
98 let field = me.lookup('timeframe');
99 field.suspendEvent('change');
100 field.setValue(timeframes.join(';'));
101 field.resumeEvent('change');
102 },
103
104 removeTimeFrame: function(field) {
105 let me = this;
106 let record = field.getWidgetRecord();
107 if (record === undefined) {
108 // this is sometimes called before a record/column is initialized
109 return;
110 }
111
112 me.lookup('timeframes').getStore().remove(record);
113 me.updateTimeframeField();
114 },
115
116 parseTimeframe: function(timeframe) {
117 let me = this;
118 let [, days, start, end] = /^(?:(\S*)\s+)?([0-9:]+)-([0-9:]+)$/.exec(timeframe) || [];
119
120 if (start === '0') {
121 start = "00:00";
122 }
123
124 let record = {
125 start,
126 end,
127 };
128
129 if (!days) {
130 days = 'mon..sun';
131 }
132
133 days = days.split(',');
134 days.forEach((day) => {
135 if (record[day]) {
136 return;
137 }
138
139 if (me.weekdays.indexOf(day) !== -1) {
140 record[day] = true;
141 } else {
142 // we have a range 'xxx..yyy'
143 let [startDay, endDay] = day.split('..');
144 let startIdx = me.weekdays.indexOf(startDay);
145 let endIdx = me.weekdays.indexOf(endDay);
146
147 if (endIdx < startIdx) {
148 endIdx += me.weekdays.length;
149 }
150
151 for (let dayIdx = startIdx; dayIdx <= endIdx; dayIdx++) {
152 let curDay = me.weekdays[dayIdx%me.weekdays.length];
153 if (!record[curDay]) {
154 record[curDay] = true;
155 }
156 }
157 }
158 });
159
160 return record;
161 },
162
163 setGridData: function(field, value) {
164 let me = this;
165 if (!value) {
166 return;
167 }
168
169 value = value.split(';');
170 let records = value.map((timeframe) => me.parseTimeframe(timeframe));
171 me.lookup('timeframes').getStore().setData(records);
172 },
173
174 control: {
175 'grid checkbox': {
176 change: 'dowChanged',
177 },
178 'grid timefield': {
179 change: 'timeChanged',
180 },
181 'grid button': {
182 click: 'removeTimeFrame',
183 },
184 'field[name=timeframe]': {
185 change: 'setGridData',
186 },
187 },
188 },
189
190 items: {
191 xtype: 'inputpanel',
192 onGetValues: function(values) {
193 let me = this;
194 let isCreate = me.up('window').isCreate;
195
196 if (!values['network-select']) {
197 values.network = '0.0.0.0/0';
198 } else if (values.network) {
199 values.network = values.network.split(/\s*,\s*/);
200 }
201
202 if ('timeframe' in values && !values.timeframe) {
203 delete values.timeframe;
204 }
205 if (values.timeframe && !Ext.isArray(values.timeframe)) {
206 let timeframe = [], seen = {};
207 values.timeframe.split(';').forEach(tf => {
208 if (!seen[tf]) {
209 timeframe.push(tf);
210 seen[tf] = true;
211 }
212 });
213 values.timeframe = timeframe;
214 }
215
216 delete values['network-select'];
217
218 if (!isCreate) {
219 PBS.Utils.delete_if_default(values, 'timeframe');
220 PBS.Utils.delete_if_default(values, 'rate-in');
221 PBS.Utils.delete_if_default(values, 'rate-out');
222 PBS.Utils.delete_if_default(values, 'burst-in');
223 PBS.Utils.delete_if_default(values, 'burst-out');
224 if (typeof values.delete === 'string') {
225 values.delete = values.delete.split(',');
226 }
227 }
228
229 return values;
230 },
231 column1: [
232 {
233 xtype: 'pmxDisplayEditField',
234 name: 'name',
235 fieldLabel: gettext('Name'),
236 renderer: Ext.htmlEncode,
237 allowBlank: false,
238 minLength: 4,
239 cbind: {
240 editable: '{isCreate}',
241 },
242 },
243 {
244 xtype: 'pmxBandwidthField',
245 fieldLabel: gettext('Rate In'),
246 name: 'rate-in',
247 },
248 {
249 xtype: 'pmxBandwidthField',
250 fieldLabel: gettext('Rate Out'),
251 name: 'rate-out',
252 },
253 ],
254
255 column2: [
256 {
257 xtype: 'proxmoxtextfield',
258 name: 'comment',
259 cbind: {
260 deleteEmpty: '{!isCreate}',
261 },
262 fieldLabel: gettext('Comment'),
263 },
264 {
265 xtype: 'pmxBandwidthField',
266 fieldLabel: gettext('Burst In'),
267 name: 'burst-in',
268 },
269 {
270 xtype: 'pmxBandwidthField',
271 fieldLabel: gettext('Burst Out'),
272 name: 'burst-out',
273 },
274 ],
275
276 columnB: [
277 {
278 xtype: 'proxmoxtextfield',
279 fieldLabel: gettext('Network(s)'),
280 name: 'network',
281 emptyText: gettext('0.0.0.0/0 (Apply on all Networks)'),
282 autoEl: {
283 tag: 'div',
284 'data-qtip': gettext('A comma-separated list of networks to apply the (shared) limit.'),
285
286 },
287 },
288 {
289 xtype: 'displayfield',
290 fieldLabel: gettext('Timeframes'),
291 },
292 {
293 xtype: 'fieldcontainer',
294 items: [
295 {
296 xtype: 'grid',
297 height: 150,
298 scrollable: true,
299 reference: 'timeframes',
300 store: {
301 fields: ['start', 'end', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'],
302 data: [],
303 },
304 columns: [
305 {
306 text: gettext('Time Start'),
307 xtype: 'widgetcolumn',
308 dataIndex: 'start',
309 widget: {
310 xtype: 'timefield',
311 isFormField: false,
312 format: 'H:i',
313 formatText: 'HH:MM',
314 },
315 flex: 1,
316 },
317 {
318 text: gettext('Time End'),
319 xtype: 'widgetcolumn',
320 dataIndex: 'end',
321 widget: {
322 xtype: 'timefield',
323 isFormField: false,
324 format: 'H:i',
325 formatText: 'HH:MM',
326 maxValue: '23:59',
327 },
328 flex: 1,
329 },
330 {
331 text: gettext('Mon'),
332 xtype: 'widgetcolumn',
333 dataIndex: 'mon',
334 width: 60,
335 widget: {
336 xtype: 'checkbox',
337 isFormField: false,
338 },
339 },
340 {
341 text: gettext('Tue'),
342 xtype: 'widgetcolumn',
343 dataIndex: 'tue',
344 width: 60,
345 widget: {
346 xtype: 'checkbox',
347 isFormField: false,
348 },
349 },
350 {
351 text: gettext('Wed'),
352 xtype: 'widgetcolumn',
353 dataIndex: 'wed',
354 width: 60,
355 widget: {
356 xtype: 'checkbox',
357 isFormField: false,
358 },
359 },
360 {
361 text: gettext('Thu'),
362 xtype: 'widgetcolumn',
363 dataIndex: 'thu',
364 width: 60,
365 widget: {
366 xtype: 'checkbox',
367 isFormField: false,
368 },
369 },
370 {
371 text: gettext('Fri'),
372 xtype: 'widgetcolumn',
373 dataIndex: 'fri',
374 width: 60,
375 widget: {
376 xtype: 'checkbox',
377 isFormField: false,
378 },
379 },
380 {
381 text: gettext('Sat'),
382 xtype: 'widgetcolumn',
383 dataIndex: 'sat',
384 width: 60,
385 widget: {
386 xtype: 'checkbox',
387 isFormField: false,
388 },
389 },
390 {
391 text: gettext('Sun'),
392 xtype: 'widgetcolumn',
393 dataIndex: 'sun',
394 width: 60,
395 widget: {
396 xtype: 'checkbox',
397 isFormField: false,
398 },
399 },
400 {
401 xtype: 'widgetcolumn',
402 width: 40,
403 widget: {
404 xtype: 'button',
405 iconCls: 'fa fa-trash-o',
406 },
407 },
408 ],
409 },
410 ],
411 },
412 {
413 xtype: 'button',
414 text: gettext('Add'),
415 iconCls: 'fa fa-plus-circle',
416 handler: 'addTimeframe',
417 },
418 {
419 xtype: 'hidden',
420 reference: 'timeframe',
421 name: 'timeframe',
422 },
423 ],
424 },
425
426 initComponent: function() {
427 let me = this;
428 me.callParent();
429 if (!me.isCreate) {
430 me.load({
431 success: function(response) {
432 let data = response.result.data;
433 if (data.network?.length === 1 && data.network[0] === '0.0.0.0/0') {
434 data['network-select'] = 'all';
435 delete data.network;
436 } else {
437 data['network-select'] = 'limit';
438 }
439
440 if (Ext.isArray(data.timeframe)) {
441 data.timeframe = data.timeframe.join(';');
442 }
443
444 me.setValues(data);
445 },
446 });
447 }
448 },
449 });