]>
git.proxmox.com Git - pve-manager.git/blob - www/manager6/grid/Replication.js
b17288b9b6a7c39f2742ce0bbaacecfa4c9e08c4
1 Ext
.define('PVE.window.ReplicaEdit', {
2 extend
: 'Proxmox.window.Edit',
3 xtype
: 'pveReplicaEdit',
5 subject
: gettext('Replication Job'),
8 url
: '/cluster/replication',
11 initComponent: function() {
14 var vmid
= me
.pveSelNode
.data
.vmid
;
15 var nodename
= me
.pveSelNode
.data
.node
;
20 xtype
: me
.isCreate
&& !vmid
?'pveGuestIDSelector':'displayfield',
22 fieldLabel
: 'CT/VM ID',
28 xtype
: me
.isCreate
? 'pveNodeSelector':'displayfield',
30 disallowedNodes
: [nodename
],
32 onlineValidator
: true,
33 fieldLabel
: gettext("Target"),
36 xtype
: 'pveCalendarEvent',
37 fieldLabel
: gettext('Schedule'),
38 emptyText
: '*/15 - ' + Ext
.String
.format(gettext('Every {0} minutes'), 15),
43 fieldLabel
: gettext('Rate limit') + ' (MB/s)',
46 emptyText
: gettext('unlimited'),
51 fieldLabel
: gettext('Comment'),
55 xtype
: 'proxmoxcheckbox',
59 fieldLabel
: gettext('Enabled'),
67 onlineHelp
: 'pvesr_schedule_time_format',
69 onGetValues: function(values
) {
70 let win
= this.up('window');
72 values
.disable
= values
.enabled
? 0 : 1;
73 delete values
.enabled
;
75 PVE
.Utils
.delete_if_default(values
, 'rate', '', win
.isCreate
);
76 PVE
.Utils
.delete_if_default(values
, 'disable', 0, win
.isCreate
);
77 PVE
.Utils
.delete_if_default(values
, 'schedule', '*/15', win
.isCreate
);
78 PVE
.Utils
.delete_if_default(values
, 'comment', '', win
.isCreate
);
81 values
.type
= 'local';
82 let vm
= vmid
|| values
.guest
;
84 if (win
.highestids
[vm
] !== undefined) {
85 id
= win
.highestids
[vm
];
88 values
.id
= vm
+ '-' + id
.toString();
101 success: function(response
) {
102 var jobs
= response
.result
.data
;
104 Ext
.Array
.forEach(jobs
, function(job
) {
105 var match
= /^([0-9]+)-([0-9]+)$/.exec(job
.id
);
107 let jobVMID
= parseInt(match
[1], 10);
108 let id
= parseInt(match
[2], 10);
109 if (highestids
[jobVMID
] === undefined || highestids
[jobVMID
] < id
) {
110 highestids
[jobVMID
] = id
;
114 me
.highestids
= highestids
;
119 success: function(response
, options
) {
120 response
.result
.data
.enabled
= !response
.result
.data
.disable
;
121 me
.setValues(response
.result
.data
);
122 me
.digest
= response
.result
.data
.digest
;
129 /* callback is a function and string */
130 Ext
.define('PVE.grid.ReplicaView', {
131 extend
: 'Ext.grid.Panel',
132 xtype
: 'pveReplicaView',
134 onlineHelp
: 'chapter_pvesr',
137 stateId
: 'grid-pve-replication-status',
140 xclass
: 'Ext.app.ViewController',
142 addJob: function(button
, event
, rec
) {
144 let view
= me
.getView();
145 Ext
.create('PVE.window.ReplicaEdit', {
148 pveSelNode
: view
.pveSelNode
,
150 destroy
: () => me
.reload(),
156 editJob: function(button
, event
, { data
}) {
158 let view
= me
.getView();
159 Ext
.create('PVE.window.ReplicaEdit', {
160 url
: `/cluster/replication/${data.id}`,
162 pveSelNode
: view
.pveSelNode
,
164 destroy
: () => me
.reload(),
170 scheduleJobNow: function(button
, event
, rec
) {
172 let view
= me
.getView();
173 Proxmox
.Utils
.API2Request({
174 url
: `/api2/extjs/nodes/${view.nodename}/replication/${rec.data.id}/schedule_now`,
177 callback
: () => me
.reload(),
178 failure
: (response
, opts
) => Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
),
182 showLog: function(button
, event
, rec
) {
184 let view
= this.getView();
186 let logView
= Ext
.create('Proxmox.panel.LogView', {
188 url
: `/api2/extjs/nodes/${view.nodename}/replication/${rec.data.id}/log`,
190 let task
= Ext
.TaskManager
.newTask({
191 run
: () => logView
.requestUpdate(),
194 let win
= Ext
.create('Ext.window.Window', {
200 title
: gettext("Replication Log"),
202 destroy: function() {
213 this.getView().rstore
.load();
216 dblClick: function(grid
, record
, item
) {
217 this.editJob(undefined, undefined, record
);
220 // currently replication is for cluster only, so disable the whole component for non-cluster
221 checkPrerequisites: function() {
222 let view
= this.getView();
223 if (PVE
.data
.ResourceStore
.getNodes().length
< 2) {
224 view
.mask(gettext("Replication needs at least two nodes"), ['pve-static-mask']);
230 itemdblclick
: 'dblClick',
231 afterlayout
: 'checkPrerequisites',
238 text
: gettext('Add'),
243 xtype
: 'proxmoxButton',
244 text
: gettext('Edit'),
245 itemId
: 'editButton',
250 xtype
: 'proxmoxStdRemoveButton',
251 itemId
: 'removeButton',
252 baseurl
: '/api2/extjs/cluster/replication/',
257 xtype
: 'proxmoxButton',
258 text
: gettext('Log'),
264 xtype
: 'proxmoxButton',
265 text
: gettext('Schedule now'),
266 itemId
: 'scheduleNowButton',
267 handler
: 'scheduleJobNow',
272 initComponent: function() {
275 var url
= '/cluster/replication';
277 me
.nodename
= me
.pveSelNode
.data
.node
;
278 me
.vmid
= me
.pveSelNode
.data
.vmid
;
282 text
: gettext('Enabled'),
283 dataIndex
: 'enabled',
284 xtype
: 'checkcolumn',
295 text
: gettext('Guest'),
300 text
: gettext('Job'),
305 text
: gettext('Target'),
312 me
.stateId
= 'grid-pve-replication-dc';
313 } else if (!me
.vmid
) {
315 url
= `/nodes/${me.nodename}/replication`;
318 url
= `/nodes/${me.nodename}/replication?guest=${me.vmid}`;
324 text
: gettext('Status'),
328 renderer: function(value
, metadata
, record
) {
329 if (record
.data
.pid
) {
330 metadata
.tdCls
= 'x-grid-row-loading';
334 let icons
= [], states
= [];
336 if (record
.data
.remove_job
) {
337 icons
.push('<i class="fa fa-ban warning" title="'
338 + gettext("Removal Scheduled") + '"></i>');
339 states
.push(gettext("Removal Scheduled"));
341 if (record
.data
.error
) {
342 icons
.push('<i class="fa fa-times critical" title="'
343 + gettext("Error") + '"></i>');
344 states
.push(record
.data
.error
);
346 if (icons
.length
=== 0) {
347 icons
.push('<i class="fa fa-check good"></i>');
348 states
.push(gettext('OK'));
351 return icons
.join(',') + ' ' + states
.join(',');
355 text
: gettext('Last Sync'),
356 dataIndex
: 'last_sync',
358 renderer: function(value
, metadata
, record
) {
362 if (record
.data
.pid
) {
363 return gettext('syncing');
365 return Proxmox
.Utils
.render_timestamp(value
);
369 text
: gettext('Duration'),
370 dataIndex
: 'duration',
372 renderer
: Proxmox
.Utils
.render_duration
,
375 text
: gettext('Next Sync'),
376 dataIndex
: 'next_sync',
378 renderer: function(value
) {
383 let now
= new Date(), next
= new Date(value
* 1000);
385 return gettext('pending');
387 return Proxmox
.Utils
.render_timestamp(value
);
395 text
: gettext('Schedule'),
397 dataIndex
: 'schedule',
400 text
: gettext('Rate limit'),
402 renderer: function(value
) {
404 return gettext('unlimited');
407 return value
.toString() + ' MB/s';
412 text
: gettext('Comment'),
413 dataIndex
: 'comment',
414 renderer
: Ext
.htmlEncode
,
418 me
.rstore
= Ext
.create('Proxmox.data.UpdateStore', {
419 storeid
: 'pve-replica-' + me
.nodename
+ me
.vmid
,
420 model
: mode
=== 'dc'? 'pve-replication' : 'pve-replication-state',
424 url
: "/api2/json" + url
,
428 me
.store
= Ext
.create('Proxmox.data.DiffStore', {
442 // we cannot access the log and scheduleNow button
443 // in the datacenter, because
444 // we do not know where/if the jobs runs
446 me
.down('#logButton').setHidden(true);
447 me
.down('#scheduleNowButton').setHidden(true);
450 // if we set the warning mask, we do not want to load
451 // or set the mask on store errors
452 if (PVE
.data
.ResourceStore
.getNodes().length
< 2) {
456 Proxmox
.Utils
.monStoreErrors(me
, me
.rstore
);
458 me
.on('destroy', me
.rstore
.stopUpdate
);
459 me
.rstore
.startUpdate();
462 Ext
.define('pve-replication', {
463 extend
: 'Ext.data.Model',
465 'id', 'target', 'comment', 'rate', 'type',
466 { name
: 'guest', type
: 'integer' },
467 { name
: 'jobnum', type
: 'integer' },
468 { name
: 'schedule', defaultValue
: '*/15' },
469 { name
: 'disable', defaultValue
: '' },
470 { name
: 'enabled', calculate: function(data
) { return !data
.disable
; } },
474 Ext
.define('pve-replication-state', {
475 extend
: 'pve-replication',
477 'last_sync', 'next_sync', 'error', 'duration', 'state',
478 'fail_count', 'remove_job', 'pid',