]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/dc/OptionView.js
bump version to 8.2.7
[pve-manager.git] / www / manager6 / dc / OptionView.js
CommitLineData
d837e79e 1Ext.define('PVE.dc.OptionView', {
755b9083
TL
2 extend: 'Proxmox.grid.ObjectGrid',
3 alias: ['widget.pveDcOptionView'],
bb2948de 4
755b9083 5 onlineHelp: 'datacenter_configuration_file',
bb2948de 6
755b9083 7 monStoreErrors: true,
0a627d94 8 userCls: 'proxmox-tags-full',
bb2948de 9
8f17b496
TL
10 add_inputpanel_row: function(name, text, opts) {
11 var me = this;
12
13 opts = opts || {};
14 me.rows = me.rows || {};
15
03153d55 16 let canEdit = !Object.prototype.hasOwnProperty.call(opts, 'caps') || opts.caps;
8f17b496
TL
17 me.rows[name] = {
18 required: true,
19 defaultValue: opts.defaultValue,
20 header: text,
21 renderer: opts.renderer,
22 editor: canEdit ? {
23 xtype: 'proxmoxWindowEdit',
61c2b434 24 width: opts.width || 350,
8f17b496 25 subject: text,
17a1ffa1 26 onlineHelp: opts.onlineHelp,
8f17b496 27 fieldDefaults: {
f6710aac 28 labelWidth: opts.labelWidth || 100,
8f17b496
TL
29 },
30 setValues: function(values) {
31 var edit_value = values[name];
a89375f6
TL
32
33 if (opts.parseBeforeSet) {
34 edit_value = PVE.Parser.parsePropertyString(edit_value);
35 }
36
8f17b496
TL
37 Ext.Array.each(this.query('inputpanel'), function(panel) {
38 panel.setValues(edit_value);
39 });
40 },
41 url: opts.url,
42 items: [{
43 xtype: 'inputpanel',
44 onGetValues: function(values) {
45 if (values === undefined || Object.keys(values).length === 0) {
46 return { 'delete': name };
47 }
48 var ret_val = {};
49 ret_val[name] = PVE.Parser.printPropertyString(values);
50 return ret_val;
51 },
f6710aac
TL
52 items: opts.items,
53 }],
54 } : undefined,
8f17b496
TL
55 };
56 },
57
e670ffc2
SR
58 render_bwlimits: function(value) {
59 if (!value) {
60 return gettext("None");
61 }
62
e670ffc2 63 let parsed = PVE.Parser.parsePropertyString(value);
27af8ed0
TL
64 return Object.entries(parsed)
65 .map(([k, v]) => k + ": " + Proxmox.Utils.format_size(v * 1024) + "/s")
66 .join(',');
e670ffc2
SR
67 },
68
8058410f 69 initComponent: function() {
bb2948de
DM
70 var me = this;
71
755b9083
TL
72 me.add_combobox_row('keyboard', gettext('Keyboard Layout'), {
73 renderer: PVE.Utils.render_kvm_language,
56148f25 74 comboItems: Object.entries(PVE.Utils.kvm_keymaps),
755b9083 75 defaultValue: '__default__',
f6710aac 76 deleteEmpty: true,
bb2948de 77 });
755b9083
TL
78 me.add_text_row('http_proxy', gettext('HTTP proxy'), {
79 defaultValue: Proxmox.Utils.noneText,
80 vtype: 'HttpProxy',
f6710aac 81 deleteEmpty: true,
bb2948de 82 });
755b9083
TL
83 me.add_combobox_row('console', gettext('Console Viewer'), {
84 renderer: PVE.Utils.render_console_viewer,
56148f25 85 comboItems: Object.entries(PVE.Utils.console_map),
755b9083 86 defaultValue: '__default__',
f6710aac 87 deleteEmpty: true,
bb2948de 88 });
755b9083
TL
89 me.add_text_row('email_from', gettext('Email from address'), {
90 deleteEmpty: true,
21026cce 91 vtype: 'proxmoxMail',
f6710aac 92 defaultValue: 'root@$hostname',
bb2948de 93 });
755b9083 94 me.add_text_row('mac_prefix', gettext('MAC address prefix'), {
3f2cccbc 95 deleteEmpty: true,
755b9083 96 vtype: 'MacPrefix',
fea56c69 97 defaultValue: 'BC:24:11',
4050aade 98 });
bb469a11 99 me.add_inputpanel_row('migration', gettext('Migration Settings'), {
80cfedf8 100 renderer: PVE.Utils.render_as_property_string,
bb469a11
TL
101 labelWidth: 120,
102 url: "/api2/extjs/cluster/options",
103 defaultKey: 'type',
104 items: [{
105 xtype: 'displayfield',
106 name: 'type',
107 fieldLabel: gettext('Type'),
108 value: 'secure',
109 submitValue: true,
bb469a11 110 }, {
6619af8d 111 xtype: 'proxmoxNetworkSelector',
bb469a11
TL
112 name: 'network',
113 fieldLabel: gettext('Network'),
8144a0dd 114 value: null,
bb469a11 115 emptyText: Proxmox.Utils.defaultText,
6619af8d 116 autoSelect: false,
f6710aac
TL
117 skipEmptyText: true,
118 }],
bb469a11 119 });
8f17b496
TL
120 me.add_inputpanel_row('ha', gettext('HA Settings'), {
121 renderer: PVE.Utils.render_dc_ha_opts,
8f17b496
TL
122 labelWidth: 120,
123 url: "/api2/extjs/cluster/options",
17a1ffa1 124 onlineHelp: 'ha_manager_shutdown_policy',
8f17b496
TL
125 items: [{
126 xtype: 'proxmoxKVComboBox',
127 name: 'shutdown_policy',
128 fieldLabel: gettext('Shutdown Policy'),
129 deleteEmpty: false,
130 value: '__default__',
131 comboItems: [
8058410f 132 ['__default__', Proxmox.Utils.defaultText + ' (conditional)'],
8f17b496
TL
133 ['freeze', 'freeze'],
134 ['failover', 'failover'],
e86024b7 135 ['migrate', 'migrate'],
f6710aac 136 ['conditional', 'conditional'],
8f17b496 137 ],
f6710aac
TL
138 defaultValue: '__default__',
139 }],
8f17b496 140 });
9d7d39ef
TL
141 me.add_inputpanel_row('crs', gettext('Cluster Resource Scheduling'), {
142 renderer: PVE.Utils.render_as_property_string,
23921456 143 width: 450,
9d7d39ef
TL
144 labelWidth: 120,
145 url: "/api2/extjs/cluster/options",
146 onlineHelp: 'ha_manager_crs',
147 items: [{
148 xtype: 'proxmoxKVComboBox',
149 name: 'ha',
150 fieldLabel: gettext('HA Scheduling'),
151 deleteEmpty: false,
152 value: '__default__',
153 comboItems: [
154 ['__default__', Proxmox.Utils.defaultText + ' (basic)'],
155 ['basic', 'Basic (Resource Count)'],
156 ['static', 'Static Load'],
157 ],
158 defaultValue: '__default__',
23921456
TL
159 }, {
160 xtype: 'proxmoxcheckbox',
161 name: 'ha-rebalance-on-start',
162 fieldLabel: gettext('Rebalance on Start'),
163 boxLabel: gettext('Use CRS to select the least loaded node when starting an HA service'),
164 value: 0,
9d7d39ef
TL
165 }],
166 });
61c2b434 167 me.add_inputpanel_row('u2f', gettext('U2F Settings'), {
80cfedf8 168 renderer: v => !v ? Proxmox.Utils.NoneText : PVE.Parser.printPropertyString(v),
61c2b434
TL
169 width: 450,
170 url: "/api2/extjs/cluster/options",
17a1ffa1 171 onlineHelp: 'pveum_configure_u2f',
61c2b434
TL
172 items: [{
173 xtype: 'textfield',
174 name: 'appid',
175 fieldLabel: gettext('U2F AppID URL'),
176 emptyText: gettext('Defaults to origin'),
177 value: '',
61c2b434 178 deleteEmpty: true,
61c2b434 179 skipEmptyText: true,
03153d55 180 submitEmptyText: false,
61c2b434
TL
181 }, {
182 xtype: 'textfield',
183 name: 'origin',
184 fieldLabel: gettext('U2F Origin'),
185 emptyText: gettext('Defaults to requesting host URI'),
186 value: '',
187 deleteEmpty: true,
188 skipEmptyText: true,
189 submitEmptyText: false,
190 },
a2e0725a
TL
191 {
192 xtype: 'box',
193 height: 25,
194 html: `<span class='pmx-hint'>${gettext('Note:')}</span> `
195 + Ext.String.format(gettext('{0} is deprecated, use {1}'), 'U2F', 'WebAuthn'),
196 },
61c2b434
TL
197 {
198 xtype: 'displayfield',
199 userCls: 'pmx-hint',
200 value: gettext('NOTE: Changing an AppID breaks existing U2F registrations!'),
f6710aac 201 }],
61c2b434 202 });
cca2aea8 203 me.add_inputpanel_row('webauthn', gettext('WebAuthn Settings'), {
80cfedf8 204 renderer: v => !v ? Proxmox.Utils.NoneText : PVE.Parser.printPropertyString(v),
cca2aea8
TL
205 width: 450,
206 url: "/api2/extjs/cluster/options",
727b64ae 207 onlineHelp: 'pveum_configure_webauthn',
cca2aea8
TL
208 items: [{
209 xtype: 'textfield',
bad1742c
TL
210 fieldLabel: gettext('Name'),
211 name: 'rp', // NOTE: relying party consists of name and id, this is the name
cca2aea8 212 allowBlank: false,
cca2aea8
TL
213 },
214 {
215 xtype: 'textfield',
216 fieldLabel: gettext('Origin'),
727b64ae 217 emptyText: Ext.String.format(gettext("Domain Lockdown (e.g., {0})"), document.location.origin),
cca2aea8 218 name: 'origin',
727b64ae 219 allowBlank: true,
cca2aea8
TL
220 },
221 {
222 xtype: 'textfield',
223 fieldLabel: 'ID',
224 name: 'id',
225 allowBlank: false,
727b64ae
TL
226 listeners: {
227 dirtychange: (f, isDirty) =>
228 f.up('panel').down('box[id=idChangeWarning]').setHidden(!f.originalValue || !isDirty),
229 },
cca2aea8
TL
230 },
231 {
232 xtype: 'container',
233 layout: 'hbox',
234 items: [
235 {
236 xtype: 'box',
237 flex: 1,
238 },
239 {
240 xtype: 'button',
241 text: gettext('Auto-fill'),
242 iconCls: 'fa fa-fw fa-pencil-square-o',
243 handler: function(button, ev) {
244 let panel = this.up('panel');
727b64ae
TL
245 let fqdn = document.location.hostname;
246
247 panel.down('field[name=rp]').setValue(fqdn);
248
249 let idField = panel.down('field[name=id]');
250 let currentID = idField.getValue();
251 if (!currentID || currentID.length === 0) {
252 idField.setValue(fqdn);
253 }
cca2aea8
TL
254 },
255 },
256 ],
257 },
258 {
259 xtype: 'box',
260 height: 25,
261 html: `<span class='pmx-hint'>${gettext('Note:')}</span> `
262 + gettext('WebAuthn requires using a trusted certificate.'),
263 },
264 {
265 xtype: 'box',
727b64ae 266 id: 'idChangeWarning',
cca2aea8
TL
267 hidden: true,
268 padding: '5 0 0 0',
fd6c8794 269 html: '<i class="fa fa-exclamation-triangle warning"></i> '
727b64ae 270 + gettext('Changing the ID breaks existing WebAuthn TFA entries.'),
cca2aea8
TL
271 }],
272 });
a89375f6 273 me.add_inputpanel_row('bwlimit', gettext('Bandwidth Limits'), {
e670ffc2 274 renderer: me.render_bwlimits,
a89375f6
TL
275 width: 450,
276 url: "/api2/extjs/cluster/options",
277 parseBeforeSet: true,
7d01dad4 278 labelWidth: 120,
a89375f6
TL
279 items: [{
280 xtype: 'pveBandwidthField',
281 name: 'default',
282 fieldLabel: gettext('Default'),
e670ffc2 283 emptyText: gettext('none'),
136103b0 284 backendUnit: "KiB",
a89375f6
TL
285 },
286 {
287 xtype: 'pveBandwidthField',
288 name: 'restore',
289 fieldLabel: gettext('Backup Restore'),
e670ffc2 290 emptyText: gettext('default'),
136103b0 291 backendUnit: "KiB",
a89375f6
TL
292 },
293 {
294 xtype: 'pveBandwidthField',
295 name: 'migration',
296 fieldLabel: gettext('Migration'),
e670ffc2 297 emptyText: gettext('default'),
136103b0 298 backendUnit: "KiB",
a89375f6
TL
299 },
300 {
301 xtype: 'pveBandwidthField',
302 name: 'clone',
303 fieldLabel: gettext('Clone'),
e670ffc2 304 emptyText: gettext('default'),
136103b0 305 backendUnit: "KiB",
a89375f6
TL
306 },
307 {
308 xtype: 'pveBandwidthField',
309 name: 'move',
310 fieldLabel: gettext('Disk Move'),
e670ffc2 311 emptyText: gettext('default'),
136103b0 312 backendUnit: "KiB",
f6710aac 313 }],
a89375f6 314 });
df9ad357
TL
315 me.add_integer_row('max_workers', gettext('Maximal Workers/bulk-action'), {
316 deleteEmpty: true,
317 defaultValue: 4,
318 minValue: 1,
319 maxValue: 64, // arbitrary but generous limit as limits are good
320 });
47353b27
TL
321 me.add_inputpanel_row('next-id', gettext('Next Free VMID Range'), {
322 renderer: PVE.Utils.render_as_property_string,
323 url: "/api2/extjs/cluster/options",
324 items: [{
325 xtype: 'proxmoxintegerfield',
326 name: 'lower',
327 fieldLabel: gettext('Lower'),
328 emptyText: '100',
329 minValue: 100,
330 maxValue: 1000 * 1000 * 1000 - 1,
331 submitValue: true,
332 }, {
333 xtype: 'proxmoxintegerfield',
334 name: 'upper',
335 fieldLabel: gettext('Upper'),
336 emptyText: '1.000.000',
337 minValue: 100,
338 maxValue: 1000 * 1000 * 1000 - 1,
339 submitValue: true,
340 }],
341 });
0a627d94
DC
342 me.rows['tag-style'] = {
343 required: true,
344 renderer: (value) => {
345 if (value === undefined) {
346 return gettext('No Overrides');
347 }
731436ee 348 let colors = PVE.UIOptions.parseTagOverrides(value?.['color-map']);
0a627d94 349 let shape = value.shape;
731436ee 350 let shapeText = PVE.UIOptions.tagTreeStyles[shape ?? '__default__'];
0a627d94 351 let txt = Ext.String.format(gettext("Tree Shape: {0}"), shapeText);
731436ee 352 let orderText = PVE.UIOptions.tagOrderOptions[value.ordering ?? '__default__'];
8d8ba23d 353 txt += `, ${Ext.String.format(gettext("Ordering: {0}"), orderText)}`;
8fab87dc
TL
354 if (value['case-sensitive']) {
355 txt += `, ${gettext('Case-Sensitive')}`;
356 }
0a627d94 357 if (Object.keys(colors).length > 0) {
1d1a3319
TL
358 txt += `, ${gettext('Color Overrides')}: `;
359 for (const tag of Object.keys(colors)) {
360 txt += Proxmox.Utils.getTagElement(tag, colors);
361 }
0a627d94
DC
362 }
363 return txt;
364 },
365 header: gettext('Tag Style Override'),
366 editor: {
367 xtype: 'proxmoxWindowEdit',
368 width: 800,
369 subject: gettext('Tag Color Override'),
370 onlineHelp: 'datacenter_configuration_file',
371 fieldDefaults: {
372 labelWidth: 100,
373 },
374 url: '/api2/extjs/cluster/options',
375 items: [
376 {
377 xtype: 'inputpanel',
378 setValues: function(values) {
379 if (values === undefined) {
380 return undefined;
381 }
382 values = values?.['tag-style'] ?? {};
383 values.shape = values.shape || '__default__';
384 values.colors = values['color-map'];
385 return Proxmox.panel.InputPanel.prototype.setValues.call(this, values);
386 },
387 onGetValues: function(values) {
388 let style = {};
389 if (values.colors) {
390 style['color-map'] = values.colors;
391 }
1f359096 392 if (values.shape && values.shape !== '__default__') {
0a627d94
DC
393 style.shape = values.shape;
394 }
8d8ba23d
DC
395 if (values.ordering) {
396 style.ordering = values.ordering;
397 }
8fab87dc
TL
398 if (values['case-sensitive']) {
399 style['case-sensitive'] = 1;
400 }
0a627d94
DC
401 let value = PVE.Parser.printPropertyString(style);
402 if (value === '') {
403 return {
404 'delete': 'tag-style',
405 };
406 }
407 return {
408 'tag-style': value,
409 };
410 },
411 items: [
412 {
1f359096 413
0a627d94 414 name: 'shape',
1f359096 415 xtype: 'proxmoxComboGrid',
0a627d94 416 fieldLabel: gettext('Tree Shape'),
1f359096 417 valueField: 'value',
13a0c8bf 418 displayField: 'display',
56dee217 419 allowBlank: false,
1f359096
DC
420 listConfig: {
421 columns: [
422 {
423 header: gettext('Option'),
424 dataIndex: 'display',
425 flex: 1,
426 },
427 {
428 header: gettext('Preview'),
429 dataIndex: 'value',
430 renderer: function(value) {
431 let cls = value ?? '__default__';
432 if (value === '__default__') {
13a0c8bf 433 cls = 'circle';
1f359096
DC
434 }
435 let tags = PVE.Utils.renderTags('preview');
436 return `<div class="proxmox-tags-${cls}">${tags}</div>`;
437 },
438 flex: 1,
13a0c8bf 439 },
1f359096
DC
440 ],
441 },
442 store: {
731436ee 443 data: Object.entries(PVE.UIOptions.tagTreeStyles).map(v => ({
1f359096
DC
444 value: v[0],
445 display: v[1],
446 })),
447 },
448 deleteDefault: true,
0a627d94
DC
449 defaultValue: '__default__',
450 deleteEmpty: true,
451 },
8d8ba23d
DC
452 {
453 name: 'ordering',
454 xtype: 'proxmoxKVComboBox',
455 fieldLabel: gettext('Ordering'),
731436ee 456 comboItems: Object.entries(PVE.UIOptions.tagOrderOptions),
8d8ba23d
DC
457 defaultValue: '__default__',
458 value: '__default__',
459 deleteEmpty: true,
460 },
8fab87dc
TL
461 {
462 name: 'case-sensitive',
463 xtype: 'proxmoxcheckbox',
464 fieldLabel: gettext('Case-Sensitive'),
465 boxLabel: gettext('Applies to new edits'),
466 value: 0,
467 },
0a627d94
DC
468 {
469 xtype: 'displayfield',
470 fieldLabel: gettext('Color Overrides'),
471 },
472 {
473 name: 'colors',
474 xtype: 'pveTagColorGrid',
475 deleteEmpty: true,
476 height: 300,
477 },
478 ],
479 },
480 ],
481 },
482 };
483
484 me.rows['user-tag-access'] = {
485 required: true,
486 renderer: (value) => {
487 if (value === undefined) {
488 return Ext.String.format(gettext('Mode: {0}'), 'free');
489 }
490 let mode = value?.['user-allow'] ?? 'free';
56dee217
DC
491 let list = value?.['user-allow-list']?.join(',') ?? '';
492 let modeTxt = Ext.String.format(gettext('Mode: {0}'), mode);
731436ee 493 let overrides = PVE.UIOptions.tagOverrides;
0a627d94 494 let tags = PVE.Utils.renderTags(list, overrides);
6e3c1442 495 let listTxt = tags !== '' ? `, ${gettext('Pre-defined:')} ${tags}` : '';
56dee217 496 return `${modeTxt}${listTxt}`;
0a627d94
DC
497 },
498 header: gettext('User Tag Access'),
499 editor: {
500 xtype: 'pveUserTagAccessEdit',
501 },
502 };
503
504 me.rows['registered-tags'] = {
505 required: true,
506 renderer: (value) => {
507 if (value === undefined) {
508 return gettext('No Registered Tags');
509 }
731436ee 510 let overrides = PVE.UIOptions.tagOverrides;
0a627d94
DC
511 return PVE.Utils.renderTags(value.join(','), overrides);
512 },
513 header: gettext('Registered Tags'),
514 editor: {
515 xtype: 'pveRegisteredTagEdit',
516 },
517 };
8f17b496 518
755b9083 519 me.selModel = Ext.create('Ext.selection.RowModel', {});
bb2948de 520
bc5d0cf8 521 Ext.apply(me, {
755b9083
TL
522 tbar: [{
523 text: gettext('Edit'),
524 xtype: 'proxmoxButton',
525 disabled: true,
526 handler: function() { me.run_editor(); },
f6710aac 527 selModel: me.selModel,
755b9083 528 }],
bb2948de 529 url: "/api2/json/cluster/options",
755b9083 530 editorConfig: {
f6710aac 531 url: "/api2/extjs/cluster/options",
755b9083
TL
532 },
533 interval: 5000,
534 cwidth1: 200,
bb2948de 535 listeners: {
f6710aac
TL
536 itemdblclick: me.run_editor,
537 },
bb2948de
DM
538 });
539
540 me.callParent();
755b9083 541
5dc48aa8
DC
542 // set the new value for the default console
543 me.mon(me.rstore, 'load', function(store, records, success) {
544 if (!success) {
545 return;
546 }
547
548 var rec = store.getById('console');
731436ee 549 PVE.UIOptions.options.console = rec.data.value;
5dc48aa8 550 if (rec.data.value === '__default__') {
731436ee 551 delete PVE.UIOptions.options.console;
5dc48aa8 552 }
0a627d94 553
731436ee
DC
554 PVE.UIOptions.options['tag-style'] = store.getById('tag-style')?.data?.value;
555 PVE.UIOptions.updateTagSettings(PVE.UIOptions.options['tag-style']);
13d465c1 556 PVE.UIOptions.fireUIConfigChanged();
5dc48aa8
DC
557 });
558
755b9083
TL
559 me.on('activate', me.rstore.startUpdate);
560 me.on('destroy', me.rstore.stopUpdate);
561 me.on('deactivate', me.rstore.stopUpdate);
f6710aac 562 },
bb2948de 563});