]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/grid/Replication.js
ui: eslint: enforce "no-extra-parens" rule
[pve-manager.git] / www / manager6 / grid / Replication.js
CommitLineData
9058a478 1Ext.define('PVE.window.ReplicaEdit', {
9fccc702 2 extend: 'Proxmox.window.Edit',
9058a478
DC
3 xtype: 'pveReplicaEdit',
4
5 subject: gettext('Replication Job'),
6
7
8 url: '/cluster/replication',
9 method: 'POST',
10
11 initComponent: function() {
12 var me = this;
13
14 var vmid = me.pveSelNode.data.vmid;
15 var nodename = me.pveSelNode.data.node;
16
17 var items = [];
18
19 items.push({
53e3ea84 20 xtype: me.isCreate && !vmid?'pveGuestIDSelector':'displayfield',
9058a478
DC
21 name: 'guest',
22 fieldLabel: 'CT/VM ID',
f6710aac 23 value: vmid || '',
9058a478
DC
24 });
25
26 items.push(
27 {
28 xtype: me.isCreate ? 'pveNodeSelector':'displayfield',
29 name: 'target',
30 disallowedNodes: [nodename],
31 allowBlank: false,
32 onlineValidator: true,
f6710aac 33 fieldLabel: gettext("Target"),
9058a478
DC
34 },
35 {
36 xtype: 'pveCalendarEvent',
37 fieldLabel: gettext('Schedule'),
42b45f8f 38 emptyText: '*/15 - ' + Ext.String.format(gettext('Every {0} minutes'), 15),
f6710aac 39 name: 'schedule',
9058a478
DC
40 },
41 {
42 xtype: 'numberfield',
defa3bb7 43 fieldLabel: gettext('Rate limit') + ' (MB/s)',
9058a478
DC
44 step: 1,
45 minValue: 1,
46 emptyText: gettext('unlimited'),
f6710aac 47 name: 'rate',
9058a478
DC
48 },
49 {
50 xtype: 'textfield',
51 fieldLabel: gettext('Comment'),
f6710aac 52 name: 'comment',
959f37af
TL
53 },
54 {
896c0d50 55 xtype: 'proxmoxcheckbox',
959f37af
TL
56 name: 'enabled',
57 defaultValue: 'on',
58 checked: true,
f6710aac
TL
59 fieldLabel: gettext('Enabled'),
60 },
9058a478
DC
61 );
62
63 me.items = [
64 {
65 xtype: 'inputpanel',
66 itemId: 'ipanel',
1ab33d14 67 onlineHelp: 'pvesr_schedule_time_format',
9058a478
DC
68
69 onGetValues: function(values) {
70 var me = this.up('window');
71
959f37af
TL
72 values.disable = values.enabled ? 0 : 1;
73 delete values.enabled;
74
9058a478 75 PVE.Utils.delete_if_default(values, 'rate', '', me.isCreate);
959f37af 76 PVE.Utils.delete_if_default(values, 'disable', 0, me.isCreate);
9058a478
DC
77 PVE.Utils.delete_if_default(values, 'schedule', '*/15', me.isCreate);
78 PVE.Utils.delete_if_default(values, 'comment', '', me.isCreate);
79
80 if (me.isCreate) {
81 values.type = 'local';
de2022b8 82 var vm = vmid || values.guest;
9058a478 83 var id = -1;
de2022b8
DC
84 if (me.highestids[vm] !== undefined) {
85 id = me.highestids[vm];
9058a478
DC
86 }
87 id++;
de2022b8 88 values.id = vm + '-' + id.toString();
9058a478
DC
89 delete values.guest;
90 }
91 return values;
92 },
f6710aac
TL
93 items: items,
94 },
9058a478
DC
95 ];
96
97 me.callParent();
98
99 if (me.isCreate) {
100 me.load({
101 success: function(response) {
102 var jobs = response.result.data;
103 var highestids = {};
104 Ext.Array.forEach(jobs, function(job) {
105 var match = /^([0-9]+)\-([0-9]+)$/.exec(job.id);
106 if (match) {
f6710aac
TL
107 var vmid = parseInt(match[1], 10);
108 var id = parseInt(match[2], 10);
9058a478
DC
109 if (highestids[vmid] < id ||
110 highestids[vmid] === undefined) {
111 highestids[vmid] = id;
112 }
113 }
114 });
115
116 me.highestids = highestids;
f6710aac 117 },
9058a478 118 });
9058a478
DC
119 } else {
120 me.load({
121 success: function(response, options) {
959f37af 122 response.result.data.enabled = !response.result.data.disable;
9058a478
DC
123 me.setValues(response.result.data);
124 me.digest = response.result.data.digest;
f6710aac 125 },
9058a478
DC
126 });
127 }
f6710aac 128 },
9058a478
DC
129});
130
3b1ca3ff 131/* callback is a function and string */
9058a478
DC
132Ext.define('PVE.grid.ReplicaView', {
133 extend: 'Ext.grid.Panel',
134 xtype: 'pveReplicaView',
135
1ab33d14 136 onlineHelp: 'chapter_pvesr',
9058a478
DC
137
138 stateful: true,
139 stateId: 'grid-pve-replication-status',
140
141 controller: {
142 xclass: 'Ext.app.ViewController',
143
f6710aac 144 addJob: function(button, event, rec) {
9058a478
DC
145 var me = this.getView();
146 var controller = this;
147 var win = Ext.create('PVE.window.ReplicaEdit', {
148 isCreate: true,
149 method: 'POST',
f6710aac 150 pveSelNode: me.pveSelNode,
9058a478
DC
151 });
152 win.on('destroy', function() { controller.reload(); });
153 win.show();
154 },
155
f6710aac 156 editJob: function(button, event, rec) {
9058a478
DC
157 var me = this.getView();
158 var controller = this;
159 var data = rec.data;
160 var win = Ext.create('PVE.window.ReplicaEdit', {
161 url: '/cluster/replication/' + data.id,
162 method: 'PUT',
f6710aac 163 pveSelNode: me.pveSelNode,
9058a478
DC
164 });
165 win.on('destroy', function() { controller.reload(); });
166 win.show();
167 },
168
f6710aac 169 scheduleJobNow: function(button, event, rec) {
85167e5e
DM
170 var me = this.getView();
171 var controller = this;
172
e7ade592 173 Proxmox.Utils.API2Request({
85167e5e
DM
174 url: "/api2/extjs/nodes/" + me.nodename + "/replication/" + rec.data.id + "/schedule_now",
175 method: 'POST',
176 waitMsgTarget: me,
177 callback: function() { controller.reload(); },
8058410f 178 failure: function(response, opts) {
85167e5e 179 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
f6710aac 180 },
85167e5e
DM
181 });
182 },
183
9058a478
DC
184 showLog: function(button, event, rec) {
185 var me = this.getView();
186 var controller = this;
0ee5a21e 187 var logView = Ext.create('Proxmox.panel.LogView', {
9058a478 188 border: false,
f6710aac 189 url: "/api2/extjs/nodes/" + me.nodename + "/replication/" + rec.data.id + "/log",
9058a478
DC
190 });
191 var win = Ext.create('Ext.window.Window', {
8058410f 192 items: [logView],
9058a478
DC
193 layout: 'fit',
194 width: 800,
195 height: 400,
196 modal: true,
f6710aac 197 title: gettext("Replication Log"),
9058a478
DC
198 });
199 var task = {
200 run: function() {
201 logView.requestUpdate();
202 },
f6710aac 203 interval: 1000,
9058a478
DC
204 };
205 Ext.TaskManager.start(task);
206 win.on('destroy', function() {
207 Ext.TaskManager.stop(task);
208 controller.reload();
209 });
210 win.show();
211 },
212
213 reload: function() {
214 var me = this.getView();
215 me.rstore.load();
216 },
217
218 dblClick: function(grid, record, item) {
219 var me = this;
220 me.editJob(undefined, undefined, record);
221 },
222
223 // check for cluster
224 // currently replication is for cluster only, so we disable the whole
225 // component
226 checkPrerequisites: function() {
227 var me = this.getView();
228 if (PVE.data.ResourceStore.getNodes().length < 2) {
229 me.mask(gettext("Replication needs at least two nodes"), ['pve-static-mask']);
230 }
231 },
232
233 control: {
234 '#': {
235 itemdblclick: 'dblClick',
f6710aac
TL
236 afterlayout: 'checkPrerequisites',
237 },
238 },
9058a478
DC
239 },
240
241 tbar: [
242 {
243 text: gettext('Add'),
244 itemId: 'addButton',
f6710aac 245 handler: 'addJob',
9058a478
DC
246 },
247 {
5720fafa 248 xtype: 'proxmoxButton',
9058a478
DC
249 text: gettext('Edit'),
250 itemId: 'editButton',
251 handler: 'editJob',
f6710aac 252 disabled: true,
9058a478
DC
253 },
254 {
3b1ca3ff 255 xtype: 'proxmoxStdRemoveButton',
9058a478 256 itemId: 'removeButton',
3b1ca3ff 257 baseurl: '/api2/extjs/cluster/replication/',
9058a478 258 dangerous: true,
f6710aac 259 callback: 'reload',
9058a478
DC
260 },
261 {
5720fafa 262 xtype: 'proxmoxButton',
9058a478
DC
263 text: gettext('Log'),
264 itemId: 'logButton',
265 handler: 'showLog',
f6710aac 266 disabled: true,
85167e5e
DM
267 },
268 {
5720fafa 269 xtype: 'proxmoxButton',
85167e5e
DM
270 text: gettext('Schedule now'),
271 itemId: 'scheduleNowButton',
272 handler: 'scheduleJobNow',
f6710aac
TL
273 disabled: true,
274 },
9058a478
DC
275 ],
276
277 initComponent: function() {
278 var me = this;
279 var mode = '';
280 var url = '/cluster/replication';
281
282 me.nodename = me.pveSelNode.data.node;
283 me.vmid = me.pveSelNode.data.vmid;
284
285 me.columns = [
959f37af
TL
286 {
287 text: gettext('Enabled'),
288 dataIndex: 'enabled',
289 xtype: 'checkcolumn',
290 sortable: true,
f6710aac 291 disabled: true,
959f37af 292 },
9058a478 293 {
a29ff1bc 294 text: 'ID',
9058a478
DC
295 dataIndex: 'id',
296 width: 60,
f6710aac 297 hidden: true,
9058a478
DC
298 },
299 {
300 text: gettext('Guest'),
301 dataIndex: 'guest',
f6710aac 302 width: 75,
9058a478
DC
303 },
304 {
305 text: gettext('Job'),
306 dataIndex: 'jobnum',
f6710aac 307 width: 60,
9058a478
DC
308 },
309 {
310 text: gettext('Target'),
f6710aac
TL
311 dataIndex: 'target',
312 },
9058a478
DC
313 ];
314
315 if (!me.nodename) {
316 mode = 'dc';
317 me.stateId = 'grid-pve-replication-dc';
318 } else if (!me.vmid) {
319 mode = 'node';
320 url = '/nodes/' + me.nodename + '/replication';
321 } else {
322 mode = 'vm';
323 url = '/nodes/' + me.nodename + '/replication' + '?guest=' + me.vmid;
324 }
325
326 if (mode !== 'dc') {
327 me.columns.push(
328 {
329 text: gettext('Status'),
330 dataIndex: 'state',
3b0ab40a
TL
331 minWidth: 160,
332 flex: 1,
9058a478 333 renderer: function(value, metadata, record) {
9058a478
DC
334 if (record.data.pid) {
335 metadata.tdCls = 'x-grid-row-loading';
336 return '';
337 }
338
3b0ab40a 339 var icons = [];
9058a478
DC
340 var states = [];
341
342 if (record.data.remove_job) {
3b0ab40a 343 icons.push('<i class="fa fa-ban warning" title="'
9058a478 344 + gettext("Removal Scheduled") + '"></i>');
9058a478
DC
345 states.push(gettext("Removal Scheduled"));
346 }
347
348 if (record.data.error) {
3b0ab40a
TL
349 icons.push('<i class="fa fa-times critical" title="'
350 + gettext("Error") + '"></i>');
9058a478
DC
351 states.push(record.data.error);
352 }
353
3b0ab40a
TL
354 if (icons.length == 0) {
355 icons.push('<i class="fa fa-check good"></i>');
356 states.push(gettext('OK'));
9058a478
DC
357 }
358
3b0ab40a 359 return icons.join(',') + ' ' + states.join(',');
f6710aac 360 },
9058a478
DC
361 },
362 {
363 text: gettext('Last Sync'),
364 dataIndex: 'last_sync',
17975935 365 width: 150,
9058a478
DC
366 renderer: function(value, metadata, record) {
367 if (!value) {
368 return '-';
369 }
370
371 if (record.data.pid) {
372 return gettext('syncing');
373 }
374
e7ade592 375 return Proxmox.Utils.render_timestamp(value);
f6710aac 376 },
9058a478
DC
377 },
378 {
379 text: gettext('Duration'),
380 dataIndex: 'duration',
17975935 381 width: 60,
f6710aac 382 renderer: Proxmox.Utils.render_duration,
9058a478
DC
383 },
384 {
385 text: gettext('Next Sync'),
386 dataIndex: 'next_sync',
17975935 387 width: 150,
9058a478
DC
388 renderer: function(value) {
389 if (!value) {
390 return '-';
391 }
392
393 var now = new Date();
394 var next = new Date(value*1000);
395
396 if (next < now) {
0b142737 397 return gettext('pending');
9058a478
DC
398 }
399
e7ade592 400 return Proxmox.Utils.render_timestamp(value);
f6710aac
TL
401 },
402 },
9058a478
DC
403 );
404 }
405
406 me.columns.push(
407 {
408 text: gettext('Schedule'),
17975935 409 width: 75,
f6710aac 410 dataIndex: 'schedule',
9058a478
DC
411 },
412 {
defa3bb7 413 text: gettext('Rate limit'),
9058a478
DC
414 dataIndex: 'rate',
415 renderer: function(value) {
416 if (!value) {
417 return gettext('unlimited');
418 }
419
420 return value.toString() + ' MB/s';
421 },
f6710aac 422 hidden: true,
9058a478
DC
423 },
424 {
425 text: gettext('Comment'),
426 dataIndex: 'comment',
f6710aac
TL
427 renderer: Ext.htmlEncode,
428 },
9058a478
DC
429 );
430
0c7c0d6b 431 me.rstore = Ext.create('Proxmox.data.UpdateStore', {
9058a478 432 storeid: 'pve-replica-' + me.nodename + me.vmid,
53e3ea84 433 model: mode === 'dc'? 'pve-replication' : 'pve-replication-state',
9058a478
DC
434 interval: 3000,
435 proxy: {
56a353b9 436 type: 'proxmox',
f6710aac
TL
437 url: "/api2/json" + url,
438 },
9058a478
DC
439 });
440
eaa018d7 441 me.store = Ext.create('Proxmox.data.DiffStore', {
9058a478
DC
442 rstore: me.rstore,
443 sorters: [
444 {
f6710aac 445 property: 'guest',
9058a478
DC
446 },
447 {
f6710aac
TL
448 property: 'jobnum',
449 },
450 ],
9058a478
DC
451 });
452
453 me.callParent();
454
85167e5e
DM
455 // we cannot access the log and scheduleNow button
456 // in the datacenter, because
9058a478
DC
457 // we do not know where/if the jobs runs
458 if (mode === 'dc') {
459 me.down('#logButton').setHidden(true);
85167e5e 460 me.down('#scheduleNowButton').setHidden(true);
9058a478
DC
461 }
462
463 // if we set the warning mask, we do not want to load
464 // or set the mask on store errors
465 if (PVE.data.ResourceStore.getNodes().length < 2) {
466 return;
467 }
468
e7ade592 469 Proxmox.Utils.monStoreErrors(me, me.rstore);
9058a478
DC
470
471 me.on('destroy', me.rstore.stopUpdate);
472 me.rstore.startUpdate();
f6710aac 473 },
9058a478 474}, function() {
9058a478
DC
475 Ext.define('pve-replication', {
476 extend: 'Ext.data.Model',
477 fields: [
478 'id', 'target', 'comment', 'rate', 'type',
479 { name: 'guest', type: 'integer' },
480 { name: 'jobnum', type: 'integer' },
959f37af
TL
481 { name: 'schedule', defaultValue: '*/15' },
482 { name: 'disable', defaultValue: '' },
f6710aac
TL
483 { name: 'enabled', calculate: function(data) { return !data.disable; } },
484 ],
9058a478
DC
485 });
486
487 Ext.define('pve-replication-state', {
488 extend: 'pve-replication',
489 fields: [
490 'last_sync', 'next_sync', 'error', 'duration', 'state',
f6710aac
TL
491 'fail_count', 'remove_job', 'pid',
492 ],
9058a478 493 });
9058a478 494});