]>
Commit | Line | Data |
---|---|---|
e969d187 FE |
1 | Ext.define('pve-prune-list', { |
2 | extend: 'Ext.data.Model', | |
3 | fields: [ | |
4 | 'type', | |
5 | 'vmid', | |
6 | { | |
7 | name: 'ctime', | |
8 | type: 'date', | |
9 | dateFormat: 'timestamp', | |
10 | }, | |
11 | ], | |
12 | }); | |
13 | ||
14 | Ext.define('PVE.PruneInputPanel', { | |
15 | extend: 'Proxmox.panel.InputPanel', | |
16 | alias: 'widget.pvePruneInputPanel', | |
17 | mixins: ['Proxmox.Mixin.CBind'], | |
18 | ||
19 | onGetValues: function(values) { | |
20 | let me = this; | |
21 | ||
22 | // the API expects a single prune-backups property string | |
23 | let pruneBackups = PVE.Parser.printPropertyString(values); | |
24 | values = { | |
25 | 'prune-backups': pruneBackups, | |
26 | 'type': me.backup_type, | |
27 | 'vmid': me.backup_id, | |
28 | }; | |
29 | ||
30 | return values; | |
31 | }, | |
32 | ||
33 | controller: { | |
34 | xclass: 'Ext.app.ViewController', | |
35 | ||
36 | init: function(view) { | |
37 | if (!view.url) { | |
38 | throw "no url specified"; | |
39 | } | |
40 | if (!view.backup_type) { | |
41 | throw "no backup_type specified"; | |
42 | } | |
43 | if (!view.backup_id) { | |
44 | throw "no backup_id specified"; | |
45 | } | |
46 | ||
47 | this.reload(); // initial load | |
48 | }, | |
49 | ||
50 | reload: function() { | |
51 | let view = this.getView(); | |
52 | ||
53 | // helper to allow showing why a backup is kept | |
54 | let addKeepReasons = function(backups, params) { | |
55 | const rules = [ | |
56 | 'keep-last', | |
57 | 'keep-hourly', | |
58 | 'keep-daily', | |
59 | 'keep-weekly', | |
60 | 'keep-monthly', | |
61 | 'keep-yearly', | |
62 | 'keep-all', // when all keep options are not set | |
63 | ]; | |
64 | let counter = {}; | |
65 | ||
0eb284ed | 66 | backups.sort((a, b) => b.ctime - a.ctime); |
e969d187 FE |
67 | |
68 | let ruleIndex = -1; | |
69 | let nextRule = function() { | |
70 | let rule; | |
71 | do { | |
72 | ruleIndex++; | |
73 | rule = rules[ruleIndex]; | |
74 | } while (!params[rule] && rule !== 'keep-all'); | |
75 | counter[rule] = 0; | |
76 | return rule; | |
77 | }; | |
78 | ||
79 | let rule = nextRule(); | |
80 | for (let backup of backups) { | |
81 | if (backup.mark === 'keep') { | |
82 | counter[rule]++; | |
83 | if (rule !== 'keep-all') { | |
84 | backup.keepReason = rule + ': ' + counter[rule]; | |
85 | if (counter[rule] >= params[rule]) { | |
86 | rule = nextRule(); | |
87 | } | |
88 | } else { | |
89 | backup.keepReason = rule; | |
90 | } | |
91 | } | |
92 | } | |
93 | }; | |
94 | ||
95 | let params = view.getValues(); | |
96 | let keepParams = PVE.Parser.parsePropertyString(params["prune-backups"]); | |
97 | ||
98 | Proxmox.Utils.API2Request({ | |
99 | url: view.url, | |
100 | method: "GET", | |
101 | params: params, | |
102 | callback: function() { | |
103 | // for easy breakpoint setting | |
104 | }, | |
105 | failure: function(response, opts) { | |
106 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
107 | }, | |
108 | success: function(response, options) { | |
109 | var data = response.result.data; | |
110 | addKeepReasons(data, keepParams); | |
111 | view.pruneStore.setData(data); | |
112 | }, | |
113 | }); | |
114 | }, | |
115 | ||
116 | control: { | |
117 | field: { change: 'reload' }, | |
118 | }, | |
119 | }, | |
120 | ||
121 | column1: [ | |
122 | { | |
123 | xtype: 'pmxPruneKeepField', | |
124 | name: 'keep-last', | |
125 | fieldLabel: gettext('keep-last'), | |
126 | }, | |
127 | { | |
128 | xtype: 'pmxPruneKeepField', | |
129 | name: 'keep-hourly', | |
130 | fieldLabel: gettext('keep-hourly'), | |
131 | }, | |
132 | { | |
133 | xtype: 'pmxPruneKeepField', | |
134 | name: 'keep-daily', | |
135 | fieldLabel: gettext('keep-daily'), | |
136 | }, | |
137 | { | |
138 | xtype: 'pmxPruneKeepField', | |
139 | name: 'keep-weekly', | |
140 | fieldLabel: gettext('keep-weekly'), | |
141 | }, | |
142 | { | |
143 | xtype: 'pmxPruneKeepField', | |
144 | name: 'keep-monthly', | |
145 | fieldLabel: gettext('keep-monthly'), | |
146 | }, | |
147 | { | |
148 | xtype: 'pmxPruneKeepField', | |
149 | name: 'keep-yearly', | |
150 | fieldLabel: gettext('keep-yearly'), | |
151 | }, | |
152 | ], | |
153 | ||
154 | initComponent: function() { | |
155 | var me = this; | |
156 | ||
157 | me.pruneStore = Ext.create('Ext.data.Store', { | |
158 | model: 'pve-prune-list', | |
159 | sorters: { property: 'ctime', direction: 'DESC' }, | |
160 | }); | |
161 | ||
162 | me.column2 = [ | |
163 | { | |
164 | xtype: 'grid', | |
165 | height: 200, | |
166 | store: me.pruneStore, | |
167 | columns: [ | |
168 | { | |
169 | header: gettext('Backup Time'), | |
170 | sortable: true, | |
171 | dataIndex: 'ctime', | |
172 | renderer: function(value, metaData, record) { | |
173 | let text = Ext.Date.format(value, 'Y-m-d H:i:s'); | |
174 | if (record.data.mark === 'remove') { | |
175 | return '<div style="text-decoration: line-through;">'+ text +'</div>'; | |
176 | } else { | |
177 | return text; | |
178 | } | |
179 | }, | |
180 | flex: 1, | |
181 | }, | |
182 | { | |
183 | text: 'Keep (reason)', | |
184 | dataIndex: 'mark', | |
185 | renderer: function(value, metaData, record) { | |
186 | if (record.data.mark === 'keep') { | |
187 | return 'true (' + record.data.keepReason + ')'; | |
188 | } else if (record.data.mark === 'protected') { | |
7fec5354 FE |
189 | return 'true (protected)'; |
190 | } else if (record.data.mark === 'renamed') { | |
e969d187 FE |
191 | return 'true (renamed)'; |
192 | } else { | |
193 | return 'false'; | |
194 | } | |
195 | }, | |
196 | flex: 1, | |
197 | }, | |
198 | ], | |
199 | }, | |
200 | ]; | |
201 | ||
202 | me.callParent(); | |
203 | }, | |
204 | }); | |
205 | ||
206 | Ext.define('PVE.window.Prune', { | |
207 | extend: 'Proxmox.window.Edit', | |
208 | ||
209 | method: 'DELETE', | |
210 | submitText: gettext("Prune"), | |
211 | ||
212 | fieldDefaults: { labelWidth: 130 }, | |
213 | ||
214 | isCreate: true, | |
215 | ||
216 | initComponent: function() { | |
217 | var me = this; | |
218 | ||
219 | if (!me.nodename) { | |
220 | throw "no nodename specified"; | |
221 | } | |
222 | if (!me.storage) { | |
223 | throw "no storage specified"; | |
224 | } | |
225 | if (!me.backup_type) { | |
226 | throw "no backup_type specified"; | |
227 | } | |
228 | if (me.backup_type !== 'qemu' && me.backup_type !== 'lxc') { | |
229 | throw "unknown backup type: " + me.backup_type; | |
230 | } | |
231 | if (!me.backup_id) { | |
232 | throw "no backup_id specified"; | |
233 | } | |
234 | ||
235 | let title = Ext.String.format( | |
236 | gettext("Prune Backups for '{0}' on Storage '{1}'"), | |
237 | me.backup_type + '/' + me.backup_id, | |
238 | me.storage, | |
239 | ); | |
240 | ||
241 | Ext.apply(me, { | |
242 | url: '/api2/extjs/nodes/' + me.nodename + '/storage/' + me.storage + "/prunebackups", | |
243 | title: title, | |
244 | items: [ | |
245 | { | |
246 | xtype: 'pvePruneInputPanel', | |
247 | url: '/api2/extjs/nodes/' + me.nodename + '/storage/' + me.storage + "/prunebackups", | |
248 | backup_type: me.backup_type, | |
249 | backup_id: me.backup_id, | |
250 | storage: me.storage, | |
251 | }, | |
252 | ], | |
253 | }); | |
254 | ||
255 | me.callParent(); | |
256 | }, | |
257 | }); |