]>
Commit | Line | Data |
---|---|---|
52245de7 DM |
1 | Ext.define('PVE.qemu.SnapshotTree', { |
2 | extend: 'Ext.tree.Panel', | |
3 | alias: ['widget.pveQemuSnapshotTree'], | |
4 | ||
5 | load_delay: 3000, | |
6 | ||
7 | old_digest: 'invalid', | |
8 | ||
af603c15 DC |
9 | stateful: true, |
10 | stateId: 'grid-qemu-snapshots', | |
11 | ||
52245de7 DM |
12 | sorterFn: function(rec1, rec2) { |
13 | var v1 = rec1.data.snaptime; | |
14 | var v2 = rec2.data.snaptime; | |
15 | ||
16 | if (rec1.data.name === 'current') { | |
17 | return 1; | |
18 | } | |
19 | if (rec2.data.name === 'current') { | |
20 | return -1; | |
21 | } | |
22 | ||
23 | return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)); | |
24 | }, | |
25 | ||
26 | reload: function(repeat) { | |
27 | var me = this; | |
28 | ||
29 | PVE.Utils.API2Request({ | |
30 | url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot', | |
31 | method: 'GET', | |
32 | failure: function(response, opts) { | |
33 | PVE.Utils.setErrorMask(me, response.htmlStatus); | |
34 | me.load_task.delay(me.load_delay); | |
35 | }, | |
36 | success: function(response, opts) { | |
37 | PVE.Utils.setErrorMask(me, false); | |
38 | var digest = 'invalid'; | |
39 | var idhash = {}; | |
40 | var root = { name: '__root', expanded: true, children: [] }; | |
41 | Ext.Array.each(response.result.data, function(item) { | |
42 | item.leaf = true; | |
43 | item.children = []; | |
44 | if (item.name === 'current') { | |
45 | digest = item.digest + item.running; | |
46 | if (item.running) { | |
79ae73ee | 47 | item.iconCls = 'fa fa-fw fa-desktop x-fa-tree-running'; |
52245de7 | 48 | } else { |
79ae73ee | 49 | item.iconCls = 'fa fa-fw fa-desktop x-fa-tree'; |
52245de7 DM |
50 | } |
51 | } else { | |
79ae73ee | 52 | item.iconCls = 'fa fa-fw fa-history x-fa-tree'; |
52245de7 DM |
53 | } |
54 | idhash[item.name] = item; | |
55 | }); | |
56 | ||
57 | if (digest !== me.old_digest) { | |
58 | me.old_digest = digest; | |
59 | ||
60 | Ext.Array.each(response.result.data, function(item) { | |
61 | if (item.parent && idhash[item.parent]) { | |
62 | var parent_item = idhash[item.parent]; | |
63 | parent_item.children.push(item); | |
64 | parent_item.leaf = false; | |
65 | parent_item.expanded = true; | |
3061bc4f | 66 | parent_item.expandable = false; |
52245de7 DM |
67 | } else { |
68 | root.children.push(item); | |
69 | } | |
70 | }); | |
71 | ||
72 | me.setRootNode(root); | |
73 | } | |
74 | ||
75 | me.load_task.delay(me.load_delay); | |
76 | } | |
77 | }); | |
78 | ||
79 | PVE.Utils.API2Request({ | |
80 | url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/feature', | |
81 | params: { feature: 'snapshot' }, | |
82 | method: 'GET', | |
83 | success: function(response, options) { | |
84 | var res = response.result.data; | |
85 | if (res.hasFeature) { | |
cd9da2e9 DC |
86 | var snpBtns = Ext.ComponentQuery.query('#snapshotBtn'); |
87 | snpBtns.forEach(function(item){ | |
88 | item.enable(); | |
89 | }); | |
52245de7 DM |
90 | } |
91 | } | |
92 | }); | |
93 | ||
94 | ||
95 | }, | |
96 | ||
90ba9a4e DC |
97 | listeners: { |
98 | beforestatesave: function(grid, state, eopts) { | |
99 | // extjs cannot serialize functions, | |
100 | // so a the sorter with only the sorterFn will | |
101 | // not be a valid sorter when restoring the state | |
102 | delete state.storeState.sorters; | |
103 | } | |
104 | }, | |
105 | ||
52245de7 DM |
106 | initComponent: function() { |
107 | var me = this; | |
108 | ||
109 | me.nodename = me.pveSelNode.data.node; | |
110 | if (!me.nodename) { | |
111 | throw "no node name specified"; | |
112 | } | |
113 | ||
114 | me.vmid = me.pveSelNode.data.vmid; | |
115 | if (!me.vmid) { | |
116 | throw "no VM ID specified"; | |
117 | } | |
118 | ||
119 | me.load_task = new Ext.util.DelayedTask(me.reload, me); | |
120 | ||
121 | var sm = Ext.create('Ext.selection.RowModel', {}); | |
122 | ||
123 | var valid_snapshot = function(record) { | |
124 | return record && record.data && record.data.name && | |
125 | record.data.name !== 'current'; | |
126 | }; | |
127 | ||
128 | var valid_snapshot_rollback = function(record) { | |
129 | return record && record.data && record.data.name && | |
130 | record.data.name !== 'current' && !record.data.snapstate; | |
131 | }; | |
132 | ||
133 | var run_editor = function() { | |
134 | var rec = sm.getSelection()[0]; | |
135 | if (valid_snapshot(rec)) { | |
136 | var win = Ext.create('PVE.window.Snapshot', { | |
137 | snapname: rec.data.name, | |
138 | nodename: me.nodename, | |
139 | vmid: me.vmid | |
140 | }); | |
141 | win.show(); | |
142 | me.mon(win, 'close', me.reload, me); | |
143 | } | |
144 | }; | |
145 | ||
5720fafa | 146 | var editBtn = new Proxmox.button.Button({ |
52245de7 DM |
147 | text: gettext('Edit'), |
148 | disabled: true, | |
149 | selModel: sm, | |
150 | enableFn: valid_snapshot, | |
151 | handler: run_editor | |
152 | }); | |
153 | ||
5720fafa | 154 | var rollbackBtn = new Proxmox.button.Button({ |
52245de7 DM |
155 | text: gettext('Rollback'), |
156 | disabled: true, | |
157 | selModel: sm, | |
158 | enableFn: valid_snapshot_rollback, | |
159 | confirmMsg: function(rec) { | |
16152937 DM |
160 | return PVE.Utils.format_task_description('qmrollback', me.vmid) + |
161 | " '" + rec.data.name + "'"; | |
52245de7 DM |
162 | }, |
163 | handler: function(btn, event) { | |
164 | var rec = sm.getSelection()[0]; | |
165 | if (!rec) { | |
166 | return; | |
167 | } | |
168 | var snapname = rec.data.name; | |
169 | ||
170 | PVE.Utils.API2Request({ | |
171 | url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot/' + snapname + '/rollback', | |
172 | method: 'POST', | |
173 | waitMsgTarget: me, | |
174 | callback: function() { | |
175 | me.reload(); | |
176 | }, | |
177 | failure: function (response, opts) { | |
178 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
179 | }, | |
180 | success: function(response, options) { | |
181 | var upid = response.result.data; | |
182 | var win = Ext.create('PVE.window.TaskProgress', { upid: upid }); | |
183 | win.show(); | |
184 | } | |
185 | }); | |
186 | } | |
187 | }); | |
188 | ||
5720fafa | 189 | var removeBtn = new Proxmox.button.Button({ |
52245de7 DM |
190 | text: gettext('Remove'), |
191 | disabled: true, | |
192 | selModel: sm, | |
193 | confirmMsg: function(rec) { | |
194 | var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), | |
195 | "'" + rec.data.name + "'"); | |
196 | return msg; | |
197 | }, | |
198 | enableFn: valid_snapshot, | |
199 | handler: function(btn, event) { | |
200 | var rec = sm.getSelection()[0]; | |
201 | if (!rec) { | |
202 | return; | |
203 | } | |
204 | var snapname = rec.data.name; | |
205 | ||
206 | PVE.Utils.API2Request({ | |
207 | url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot/' + snapname, | |
208 | method: 'DELETE', | |
209 | waitMsgTarget: me, | |
210 | callback: function() { | |
211 | me.reload(); | |
212 | }, | |
213 | failure: function (response, opts) { | |
214 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
215 | }, | |
216 | success: function(response, options) { | |
217 | var upid = response.result.data; | |
218 | var win = Ext.create('PVE.window.TaskProgress', { upid: upid }); | |
219 | win.show(); | |
220 | } | |
221 | }); | |
222 | } | |
223 | }); | |
224 | ||
225 | var snapshotBtn = Ext.create('Ext.Button', { | |
cd9da2e9 | 226 | itemId: 'snapshotBtn', |
52245de7 DM |
227 | text: gettext('Take Snapshot'), |
228 | disabled: true, | |
229 | handler: function() { | |
230 | var win = Ext.create('PVE.window.Snapshot', { | |
231 | nodename: me.nodename, | |
232 | vmid: me.vmid | |
233 | }); | |
234 | win.show(); | |
235 | } | |
236 | }); | |
237 | ||
238 | Ext.apply(me, { | |
239 | layout: 'fit', | |
240 | rootVisible: false, | |
241 | animate: false, | |
242 | sortableColumns: false, | |
243 | selModel: sm, | |
244 | tbar: [ snapshotBtn, rollbackBtn, removeBtn, editBtn ], | |
245 | fields: [ | |
246 | 'name', 'description', 'snapstate', 'vmstate', 'running', | |
247 | { name: 'snaptime', type: 'date', dateFormat: 'timestamp' } | |
248 | ], | |
249 | columns: [ | |
250 | { | |
251 | xtype: 'treecolumn', | |
252 | text: gettext('Name'), | |
253 | dataIndex: 'name', | |
254 | width: 200, | |
255 | renderer: function(value, metaData, record) { | |
256 | if (value === 'current') { | |
257 | return "NOW"; | |
258 | } else { | |
259 | return value; | |
260 | } | |
261 | } | |
262 | }, | |
263 | { | |
264 | text: gettext('RAM'), | |
265 | align: 'center', | |
266 | resizable: false, | |
267 | dataIndex: 'vmstate', | |
268 | width: 50, | |
269 | renderer: function(value, metaData, record) { | |
270 | if (record.data.name !== 'current') { | |
271 | return PVE.Utils.format_boolean(value); | |
272 | } | |
273 | } | |
274 | }, | |
275 | { | |
276 | text: gettext('Date') + "/" + gettext("Status"), | |
277 | dataIndex: 'snaptime', | |
5f80c67e | 278 | width: 150, |
52245de7 DM |
279 | renderer: function(value, metaData, record) { |
280 | if (record.data.snapstate) { | |
281 | return record.data.snapstate; | |
282 | } | |
283 | if (value) { | |
284 | return Ext.Date.format(value,'Y-m-d H:i:s'); | |
285 | } | |
286 | } | |
287 | }, | |
288 | { | |
289 | text: gettext('Description'), | |
290 | dataIndex: 'description', | |
291 | flex: 1, | |
292 | renderer: function(value, metaData, record) { | |
293 | if (record.data.name === 'current') { | |
294 | return gettext("You are here!"); | |
295 | } else { | |
296 | return Ext.String.htmlEncode(value); | |
297 | } | |
298 | } | |
299 | } | |
300 | ], | |
301 | columnLines: true, // will work in 4.1? | |
302 | listeners: { | |
cd9da2e9 | 303 | activate: me.reload, |
52245de7 | 304 | destroy: me.load_task.cancel, |
52245de7 DM |
305 | itemdblclick: run_editor |
306 | } | |
307 | }); | |
308 | ||
309 | me.callParent(); | |
310 | ||
311 | me.store.sorters.add(new Ext.util.Sorter({ | |
312 | sorterFn: me.sorterFn | |
313 | })); | |
314 | } | |
315 | }); | |
316 |