]> git.proxmox.com Git - proxmox-backup.git/blob - www/Utils.js
gui: add snapshot/file fingerprint tooltip
[proxmox-backup.git] / www / Utils.js
1 Ext.ns('PBS');
2
3 console.log("Starting Backup Server GUI");
4
5 Ext.define('PBS.Utils', {
6 singleton: true,
7
8 updateLoginData: function(data) {
9 Proxmox.Utils.setAuthData(data);
10 },
11
12 dataStorePrefix: 'DataStore-',
13
14 cryptmap: [
15 'none',
16 'mixed',
17 'sign-only',
18 'encrypt',
19 ],
20
21 cryptText: [
22 Proxmox.Utils.noText,
23 gettext('Mixed'),
24 gettext('Signed'),
25 gettext('Encrypted'),
26 ],
27
28 cryptIconCls: [
29 '',
30 '',
31 'lock faded',
32 'lock good',
33 ],
34
35 calculateCryptMode: function(data) {
36 let mixed = data.mixed;
37 let encrypted = data.encrypt;
38 let signed = data['sign-only'];
39 let files = data.count;
40 if (mixed > 0) {
41 return PBS.Utils.cryptmap.indexOf('mixed');
42 } else if (files === encrypted && encrypted > 0) {
43 return PBS.Utils.cryptmap.indexOf('encrypt');
44 } else if (files === signed && signed > 0) {
45 return PBS.Utils.cryptmap.indexOf('sign-only');
46 } else if ((signed+encrypted) === 0) {
47 return PBS.Utils.cryptmap.indexOf('none');
48 } else {
49 return PBS.Utils.cryptmap.indexOf('mixed');
50 }
51 },
52
53 noSubKeyHtml: 'You do not have a valid subscription for this server. Please visit <a target="_blank" href="https://www.proxmox.com/proxmox-backup-server/pricing">www.proxmox.com</a> to get a list of available options.',
54
55 getDataStoreFromPath: function(path) {
56 return path.slice(PBS.Utils.dataStorePrefix.length);
57 },
58
59 isDataStorePath: function(path) {
60 return path.indexOf(PBS.Utils.dataStorePrefix) === 0;
61 },
62
63 parsePropertyString: function(value, defaultKey) {
64 var res = {},
65 error;
66
67 if (typeof value !== 'string' || value === '') {
68 return res;
69 }
70
71 Ext.Array.each(value.split(','), function(p) {
72 var kv = p.split('=', 2);
73 if (Ext.isDefined(kv[1])) {
74 res[kv[0]] = kv[1];
75 } else if (Ext.isDefined(defaultKey)) {
76 if (Ext.isDefined(res[defaultKey])) {
77 error = 'defaultKey may be only defined once in propertyString';
78 return false; // break
79 }
80 res[defaultKey] = kv[0];
81 } else {
82 error = 'invalid propertyString, not a key=value pair and no defaultKey defined';
83 return false; // break
84 }
85 return true;
86 });
87
88 if (error !== undefined) {
89 console.error(error);
90 return null;
91 }
92
93 return res;
94 },
95
96 printPropertyString: function(data, defaultKey) {
97 var stringparts = [],
98 gotDefaultKeyVal = false,
99 defaultKeyVal;
100
101 Ext.Object.each(data, function(key, value) {
102 if (defaultKey !== undefined && key === defaultKey) {
103 gotDefaultKeyVal = true;
104 defaultKeyVal = value;
105 } else if (value !== '' && value !== undefined) {
106 stringparts.push(key + '=' + value);
107 }
108 });
109
110 stringparts = stringparts.sort();
111 if (gotDefaultKeyVal) {
112 stringparts.unshift(defaultKeyVal);
113 }
114
115 return stringparts.join(',');
116 },
117
118 // helper for deleting field which are set to there default values
119 delete_if_default: function(values, fieldname, default_val, create) {
120 if (values[fieldname] === '' || values[fieldname] === default_val) {
121 if (!create) {
122 if (values.delete) {
123 if (Ext.isArray(values.delete)) {
124 values.delete.push(fieldname);
125 } else {
126 values.delete += ',' + fieldname;
127 }
128 } else {
129 values.delete = [fieldname];
130 }
131 }
132
133 delete values[fieldname];
134 }
135 },
136
137
138 render_datetime_utc: function(datetime) {
139 let pad = (number) => number < 10 ? '0' + number : number;
140 return datetime.getUTCFullYear() +
141 '-' + pad(datetime.getUTCMonth() + 1) +
142 '-' + pad(datetime.getUTCDate()) +
143 'T' + pad(datetime.getUTCHours()) +
144 ':' + pad(datetime.getUTCMinutes()) +
145 ':' + pad(datetime.getUTCSeconds()) +
146 'Z';
147 },
148
149 render_datastore_worker_id: function(id, what) {
150 const res = id.match(/^(\S+?):(\S+?)\/(\S+?)(\/(.+))?$/);
151 if (res) {
152 let datastore = res[1], backupGroup = `${res[2]}/${res[3]}`;
153 if (res[4] !== undefined) {
154 let datetime = Ext.Date.parse(parseInt(res[5], 16), 'U');
155 let utctime = PBS.Utils.render_datetime_utc(datetime);
156 return `Datastore ${datastore} ${what} ${backupGroup}/${utctime}`;
157 } else {
158 return `Datastore ${datastore} ${what} ${backupGroup}`;
159 }
160 }
161 return `Datastore ${what} ${id}`;
162 },
163
164 // mimics Display trait in backend
165 renderKeyID: function(fingerprint) {
166 return fingerprint.substring(0, 23);
167 },
168
169 parse_datastore_worker_id: function(type, id) {
170 let result;
171 let res;
172 if (type.startsWith('verif')) {
173 res = PBS.Utils.VERIFICATION_JOB_ID_RE.exec(id);
174 if (res) {
175 result = res[1];
176 }
177 } else if (type.startsWith('sync')) {
178 res = PBS.Utils.SYNC_JOB_ID_RE.exec(id);
179 if (res) {
180 result = res[3];
181 }
182 } else if (type === 'backup') {
183 res = PBS.Utils.BACKUP_JOB_ID_RE.exec(id);
184 if (res) {
185 result = res[1];
186 }
187 } else if (type === 'garbage_collection') {
188 return id;
189 } else if (type === 'prune') {
190 return id;
191 }
192
193
194 return result;
195 },
196
197 extractTokenUser: function(tokenid) {
198 return tokenid.match(/^(.+)!([^!]+)$/)[1];
199 },
200
201 extractTokenName: function(tokenid) {
202 return tokenid.match(/^(.+)!([^!]+)$/)[2];
203 },
204
205 render_estimate: function(value) {
206 if (!value) {
207 return gettext('Not enough data');
208 }
209
210 let now = new Date();
211 let estimate = new Date(value*1000);
212
213 let timespan = (estimate - now)/1000;
214
215 if (Number(estimate) <= Number(now) || isNaN(timespan)) {
216 return gettext('Never');
217 }
218
219 let duration = Proxmox.Utils.format_duration_human(timespan);
220 return Ext.String.format(gettext("in {0}"), duration);
221 },
222
223 render_size_usage: function(val, max) {
224 if (max === 0) {
225 return gettext('N/A');
226 }
227 return (val*100/max).toFixed(2) + '% (' +
228 Ext.String.format(gettext('{0} of {1}'),
229 Proxmox.Utils.format_size(val), Proxmox.Utils.format_size(max)) + ')';
230 },
231
232 get_help_tool: function(blockid) {
233 let info = Proxmox.Utils.get_help_info(blockid);
234 if (info === undefined) {
235 info = Proxmox.Utils.get_help_info('pbs_documentation_index');
236 }
237 if (info === undefined) {
238 throw "get_help_info failed"; // should not happen
239 }
240
241 let docsURI = window.location.origin + info.link;
242 let title = info.title;
243 if (info.subtitle) {
244 title += ' - ' + info.subtitle;
245 }
246 return {
247 type: 'help',
248 tooltip: title,
249 handler: function() {
250 window.open(docsURI);
251 },
252 };
253 },
254
255 calculate_dedup_factor: function(gcstatus) {
256 let dedup = 1.0;
257 if (gcstatus['disk-bytes'] > 0) {
258 dedup = (gcstatus['index-data-bytes'] || 0)/gcstatus['disk-bytes'];
259 }
260 return dedup;
261 },
262
263 constructor: function() {
264 var me = this;
265
266 let PROXMOX_SAFE_ID_REGEX = "([A-Za-z0-9_][A-Za-z0-9._-]*)";
267 // only anchored at beginning
268 // only parses datastore for now
269 me.VERIFICATION_JOB_ID_RE = new RegExp("^" + PROXMOX_SAFE_ID_REGEX + ':?');
270 me.SYNC_JOB_ID_RE = new RegExp("^" + PROXMOX_SAFE_ID_REGEX + ':' +
271 PROXMOX_SAFE_ID_REGEX + ':' + PROXMOX_SAFE_ID_REGEX + ':');
272 me.BACKUP_JOB_ID_RE = new RegExp("^" + PROXMOX_SAFE_ID_REGEX + ':');
273
274 // do whatever you want here
275 Proxmox.Utils.override_task_descriptions({
276 backup: (type, id) => PBS.Utils.render_datastore_worker_id(id, gettext('Backup')),
277 dircreate: [gettext('Directory Storage'), gettext('Create')],
278 dirremove: [gettext('Directory'), gettext('Remove')],
279 garbage_collection: ['Datastore', gettext('Garbage collect')],
280 logrotate: [null, gettext('Log Rotation')],
281 prune: (type, id) => PBS.Utils.render_datastore_worker_id(id, gettext('Prune')),
282 reader: (type, id) => PBS.Utils.render_datastore_worker_id(id, gettext('Read objects')),
283 sync: ['Datastore', gettext('Remote Sync')],
284 syncjob: [gettext('Sync Job'), gettext('Remote Sync')],
285 verify: ['Datastore', gettext('Verification')],
286 verify_group: ['Group', gettext('Verification')],
287 verify_snapshot: ['Snapshot', gettext('Verification')],
288 verificationjob: [gettext('Verify Job'), gettext('Scheduled Verification')],
289 zfscreate: [gettext('ZFS Storage'), gettext('Create')],
290 });
291 },
292 });