]> git.proxmox.com Git - pve-manager.git/blame - www/manager/Utils.js
imported from svn 'pve-manager/pve2'
[pve-manager.git] / www / manager / Utils.js
CommitLineData
aff192e6
DM
1Ext.ns('PVE');
2
3// avoid errors when running without development tools
4if (!Ext.isDefined(Ext.global.console)) {
5 var console = {
6 dir: function() {},
7 log: function() {}
8 };
9}
10console.log("Starting PVE Manager");
11
12Ext.Ajax.defaultHeaders = {
13 'Accept': 'application/json'
14};
15
16// do not send '_dc' parameter
17Ext.Ajax.disableCaching = false;
18
19Ext.Ajax.on('beforerequest', function(conn, options) {
20 if (PVE.CSRFPreventionToken) {
21 if (!options.headers) {
22 options.headers = {};
23 }
24 options.headers.CSRFPreventionToken = PVE.CSRFPreventionToken;
25 }
26});
27
28// custom Vtype for vtype:'IPAddress'
29Ext.apply(Ext.form.field.VTypes, {
30 IPAddress: function(v) {
31 return (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/).test(v);
32 },
33 IPAddressText: 'Must be a numeric IP address',
34 IPAddressMask: /[\d\.]/i,
35
36 MacAddress: function(v) {
37 return (/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/).test(v);
38 },
39 MacAddressMask: /[a-fA-F0-9:]/,
40 MacAddressText: 'Must be a valid MAC address (example: "01:23:45:67:89:ab")',
41
42 BridgeName: function(v) {
43 return (/^vmbr\d{1,4}$/).test(v);
44 },
45 BridgeNameText: 'Allowable bridge names: vmbr<b>N</b>, where 0 <= <b>N</b> <= 9999',
46
47 BondName: function(v) {
48 return (/^bond\d{1,4}$/).test(v);
49 },
50 BondNameText: 'Allowable bond names: bond<b>N</b>, where 0 <= <b>N</b> <= 9999',
51
52 QemuStartDate: function(v) {
53 return (/^(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)$/).test(v);
54 },
55 QemuStartDateText: 'Valid format for date are: "now" or "2006-06-17T16:01:21" or "2006-06-17"',
56
57 StorageId: function(v) {
58 return (/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i).test(v);
59 },
60 StorageIdText: "ID contains illegal characters (allowed characters: 'a-z', '0-9', '-', '_' and '.')"
61});
62
63// we dont want that a displayfield set the form dirty flag!
64Ext.override(Ext.form.field.Display, {
65 isDirty: function() { return false; }
66});
67
68// hack: ExtJS does not display the correct value if we
69// call setValue while the store is loading, so we need
70// to call it again after loading
71Ext.override(Ext.form.field.ComboBox, {
72 onLoad: function() {
73 this.setValue(this.value, false);
74 this.callOverridden(arguments);
75 }
76});
77
78Ext.define('PVE.Utils', { statics: {
79
80 // this class only contains static functions
81
82 log_severity_hash: {
83 0: "panic",
84 1: "alert",
85 2: "critical",
86 3: "error",
87 4: "warning",
88 5: "notice",
89 6: "info",
90 7: "debug"
91 },
92
93 kvm_ostypes: {
94 other: 'Other',
95 wxp: 'Microsoft Windows XP/2003',
96 w2k: 'Microsoft Windows 2000',
97 w2k8: 'Microsoft Windows Vista/2008',
98 win7: 'Microsoft Windows 7/2008r2',
99 l24: 'Linux 2.4 Kernel',
100 l26: 'Linux 2.6 Kernel'
101 },
102
103 render_kvm_ostype: function (value) {
104 if (!value) {
105 return 'Other';
106 }
107 var text = PVE.Utils.kvm_ostypes[value];
108 if (text) {
109 return text + ' (' + value + ')';
110 }
111 return value;
112 },
113
114 // fixme: auto-generate this
115 // for now, please keep in sync with PVE::Tools::kvmkeymaps
116 kvm_keymaps: {
117 //ar: 'Arabic',
118 dk: 'Danish',
119 de: 'German',
120 'de-ch': 'German (Swiss)',
121 'en-gb': 'English (UK)',
122 'en-us': 'English (USA',
123 es: 'Spanish',
124 //et: 'Estonia',
125 fi: 'Finnish',
126 //fo: 'Faroe Islands',
127 fr: 'French',
128 'fr-be': 'French (Belgium)',
129 'fr-ca': 'French (Canada)',
130 'fr-ch': 'French (Swiss)',
131 //hr: 'Croatia',
132 hu: 'Hungarian',
133 is: 'Icelandic',
134 it: 'Italian',
135 ja: 'Japanese',
136 lt: 'Lithuanian',
137 //lv: 'Latvian',
138 mk: 'Macedonian',
139 nl: 'Dutch',
140 //'nl-be': 'Dutch (Belgium)',
141 no: 'Norwegian',
142 pl: 'Polish',
143 pt: 'Portuguese',
144 'pt-br': 'Portuguese (Brazil)',
145 //ru: 'Russian',
146 si: 'Slovenian'
147 //sv: 'Swedish',
148 //th: 'Thai',
149 //tr: 'Turkish'
150 },
151
152 kvm_vga_drivers: {
153 std: 'Standard VGA',
154 vmware: 'VMWare compatible',
155 cirrus: 'Cirrus Logic GD5446'
156 },
157
158 render_kvm_language: function (value) {
159 if (!value) {
160 return 'Default';
161 }
162 var text = PVE.Utils.kvm_keymaps[value];
163 if (text) {
164 return text + ' (' + value + ')';
165 }
166 return value;
167 },
168
169 kvm_keymap_array: function() {
170 var data = [['', PVE.Utils.render_kvm_language('')]];
171 Ext.Object.each(PVE.Utils.kvm_keymaps, function(key, value) {
172 data.push([key, PVE.Utils.render_kvm_language(value)]);
173 });
174
175 return data;
176 },
177
178 render_kvm_vga_driver: function (value) {
179 if (!value) {
180 return 'Default';
181 }
182 var text = PVE.Utils.kvm_vga_drivers[value];
183 if (text) {
184 return text + ' (' + value + ')';
185 }
186 return value;
187 },
188
189 kvm_vga_driver_array: function() {
190 var data = [['', PVE.Utils.render_kvm_vga_driver('')]];
191 Ext.Object.each(PVE.Utils.kvm_vga_drivers, function(key, value) {
192 data.push([key, PVE.Utils.render_kvm_vga_driver(value)]);
193 });
194
195 return data;
196 },
197
198 authOK: function() {
199 return Ext.util.Cookies.get('PVEAuthCookie');
200 },
201
202 authClear: function() {
203 Ext.util.Cookies.clear("PVEAuthCookie");
204 },
205
206 // fixme: remove - not needed?
207 gridLineHeigh: function() {
208 return 21;
209
210 //if (Ext.isGecko)
211 //return 23;
212 //return 21;
213 },
214
215 extractRequestError: function(result, verbose) {
216 var msg = 'Successful';
217
218 if (!result.success) {
219 msg = "Unknown error";
220 if (result.message) {
221 msg = result.message;
222 if (result.status) {
223 msg += ' (' + result.status + ')';
224 }
225 }
226 if (verbose && Ext.isObject(result.errors)) {
227 msg += "<br>";
228 Ext.Object.each(result.errors, function(prop, desc) {
229 msg += "<br><b>" + Ext.htmlEncode(prop) + "</b>: " +
230 Ext.htmlEncode(desc);
231 });
232 }
233 }
234
235 return msg;
236 },
237
238 extractFormActionError: function(action) {
239 var msg;
240 switch (action.failureType) {
241 case Ext.form.action.Action.CLIENT_INVALID:
242 msg = 'Form fields may not be submitted with invalid values';
243 break;
244 case Ext.form.action.Action.CONNECT_FAILURE:
245 msg = 'Connect failure';
246 var resp = action.response;
247 if (resp.status && resp.statusText) {
248 msg += " " + resp.status + ": " + resp.statusText;
249 }
250 break;
251 case Ext.form.action.Action.LOAD_FAILURE:
252 case Ext.form.action.Action.SERVER_INVALID:
253 msg = PVE.Utils.extractRequestError(action.result, true);
254 break;
255 }
256 return msg;
257 },
258
259 // Ext.Ajax.request
260 API2Request: function(reqOpts) {
261
262 var newopts = Ext.apply({
263 waitMsg: 'Please wait...'
264 }, reqOpts);
265
266 if (!newopts.url.match(/^\/api2/)) {
267 newopts.url = '/api2/extjs' + newopts.url;
268 }
269 delete newopts.callback;
270
271 var createWrapper = function(successFn, callbackFn, failureFn) {
272 Ext.apply(newopts, {
273 success: function(response, options) {
274 if (options.waitMsgTarget) {
275 options.waitMsgTarget.setLoading(false);
276 }
277 var result = Ext.decode(response.responseText);
278 response.result = result;
279 if (!result.success) {
280 response.htmlStatus = PVE.Utils.extractRequestError(result, true);
281 Ext.callback(callbackFn, options.scope, [options, false, response]);
282 Ext.callback(failureFn, options.scope, [response, options]);
283 return;
284 }
285 Ext.callback(callbackFn, options.scope, [options, true, response]);
286 Ext.callback(successFn, options.scope, [response, options]);
287 },
288 failure: function(response, options) {
289 if (options.waitMsgTarget) {
290 options.waitMsgTarget.setLoading(false);
291 }
292 var result = Ext.decode(response.responseText);
293 response.result = result || {};
294 var msg = "Connection error - server offline?";
295 if (response.aborted) {
296 msg = 'Transaction aborted.';
297 } else if (response.timedout) {
298 msg = 'Communication failure: Timeout.';
299 } else if (response.status && response.statusText) {
300 msg = 'Connection error ' + response.status + ': ' + response.statusText;
301 }
302 response.htmlStatus = msg;
303 Ext.callback(callbackFn, options.scope, [options, false, response]);
304 Ext.callback(failureFn, options.scope, [response, options]);
305 }
306 });
307 };
308
309 createWrapper(reqOpts.success, reqOpts.callback, reqOpts.failure);
310
311 var target = newopts.waitMsgTarget;
312 if (target) {
313 // Note: ExtJS bug - this does not work when component is not rendered
314 target.setLoading(newopts.waitMsg, true);
315 }
316 Ext.Ajax.request(newopts);
317 },
318
319 assemble_field_data: function(values, data) {
320 if (Ext.isObject(data)) {
321 Ext.Object.each(data, function(name, val) {
322 if (values.hasOwnProperty(name)) {
323 var bucket = values[name];
324 if (!Ext.isArray(bucket)) {
325 bucket = values[name] = [bucket];
326 }
327 if (Ext.isArray(val)) {
328 values[name] = bucket.concat(val);
329 } else {
330 bucket.push(val);
331 }
332 } else {
333 values[name] = val;
334 }
335 });
336 }
337 },
338
339 format_task_description: function(type, id) {
340
341 if (type == 'vncproxy') {
342 return "VNC connection to VM " + id;
343 }
344
345 if (type == 'vncshell') {
346 return "VNC shell";
347 }
348
349 return type;
350 },
351
352
353 parse_task_upid: function(upid) {
354 var task = {};
355
356 var res = upid.match(/^UPID:(\w+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8}):([^:\s]+):([^:\s]*):([^:\s]+):$/);
357 if (!res) {
358 throw "unable to parse upid '" + upid + "'";
359 }
360 task.node = res[1];
361 task.pid = parseInt(res[2], 16);
362 task.pstart = parseInt(res[3], 16);
363 task.starttime = parseInt(res[4], 16);
364 task.type = res[5];
365 task.id = res[6];
366 task.user = res[7];
367
368 task.desc = PVE.Utils.format_task_description(task.type, task.id);
369
370 return task;
371 },
372
373 format_size: function(size) {
374
375 var kb = size / 1024;
376
377 if (kb < 1024) {
378 return kb.toFixed(0) + "KB";
379 }
380
381 var mb = size / (1024*1024);
382
383 if (mb < 1024) {
384 return mb.toFixed(0) + "MB";
385 }
386
387 var gb = mb / 1024;
388
389 if (gb < 1024) {
390 return gb.toFixed(2) + "GB";
391 }
392
393 var tb = gb / 1024;
394
395 return tb.toFixed(2) + "TB";
396
397 },
398
399 format_html_bar: function(per, text) {
400
401 return "<div class='pve-bar-wrap'>" + text + "<div class='pve-bar-border'>" +
402 "<div class='pve-bar-inner' style='width:" + per + "%;'></div>" +
403 "</div></div>";
404
405 },
406
407 format_cpu_bar: function(per1, per2, text) {
408
409 return "<div class='pve-bar-border'>" +
410 "<div class='pve-bar-inner' style='width:" + per1 + "%;'></div>" +
411 "<div class='pve-bar-inner2' style='width:" + per2 + "%;'></div>" +
412 "<div class='pve-bar-text'>" + text + "</div>" +
413 "</div>";
414 },
415
416 format_large_bar: function(per, text) {
417
418 if (!text) {
419 text = per.toFixed(1) + "%";
420 }
421
422 return "<div class='pve-largebar-border'>" +
423 "<div class='pve-largebar-inner' style='width:" + per + "%;'></div>" +
424 "<div class='pve-largebar-text'>" + text + "</div>" +
425 "</div>";
426 },
427
428 format_duration_long: function(ut) {
429
430 var days = Math.floor(ut / 86400);
431 ut -= days*86400;
432 var hours = Math.floor(ut / 3600);
433 ut -= hours*3600;
434 var mins = Math.floor(ut / 60);
435 ut -= mins*60;
436
437 var hours_str = '00' + hours.toString();
438 hours_str = hours_str.substr(hours_str.length - 2);
439 var mins_str = "00" + mins.toString();
440 mins_str = mins_str.substr(mins_str.length - 2);
441 var ut_str = "00" + ut.toString();
442 ut_str = ut_str.substr(ut_str.length - 2);
443
444 if (days) {
445 var ds = days > 1 ? 'days' : 'day';
446 return days.toString() + ' ' + ds + ' ' +
447 hours_str + ':' + mins_str + ':' + ut_str;
448 } else {
449 return hours_str + ':' + mins_str + ':' + ut_str;
450 }
451 },
452
453 format_duration_short: function(ut) {
454
455 if (ut < 60) {
456 return ut.toString() + 's';
457 }
458
459 if (ut < 3600) {
460 var mins = ut / 60;
461 return mins.toFixed(0) + 'm';
462 }
463
464 if (ut < 86400) {
465 var hours = ut / 3600;
466 return hours.toFixed(0) + 'h';
467 }
468
469 var days = ut / 86400;
470 return days.toFixed(0) + 'd';
471 },
472
473 format_storage_type: function(value) {
474 if (value === 'dir') {
475 return 'Directory';
476 } else if (value === 'nfs') {
477 return 'NFS';
478 } else if (value === 'lvm') {
479 return 'LVM';
480 } else if (value === 'iscsi') {
481 return 'iSCSI';
482 } else {
483 return 'unknown';
484 }
485 },
486
487 format_boolean_with_default: function(value) {
488 if (Ext.isDefined(value) && value !== '') {
489 return value ? 'Yes' : 'No';
490 }
491 return 'Default';
492 },
493
494 format_boolean: function(value) {
495 return value ? 'Yes' : 'No';
496 },
497
498 format_neg_boolean: function(value) {
499 return !value ? 'Yes' : 'No';
500 },
501
502 format_content_types: function(value) {
503 var cta = [];
504
505 Ext.each(value.split(','), function(ct) {
506 if (ct === 'images') {
507 cta.push('Images');
508 } else if (ct === 'backup') {
509 cta.push('Backups');
510 } else if (ct === 'vztmpl') {
511 cta.push('Templates');
512 } else if (ct === 'iso') {
513 cta.push('ISO');
514 }
515 });
516
517 return cta.join(', ');
518 },
519
520 render_storage_content: function(value, metaData, record) {
521 var data = record.data;
522 if (Ext.isNumber(data.channel) &&
523 Ext.isNumber(data.id) &&
524 Ext.isNumber(data.lun)) {
525 return "CH " +
526 Ext.String.leftPad(data.channel,2, '0') +
527 " ID " + data.id + " LUN " + data.lun;
528 }
529 return data.volid.replace(/^.*:(.*\/)?/,'');
530 },
531
532 render_serverity: function (value) {
533 return PVE.Utils.log_severity_hash[value] || value;
534 },
535
536 render_cpu: function(value, metaData, record, rowIndex, colIndex, store) {
537
538 var maxcpu = record.data.maxcpu;
539
540 if (!record.data.uptime) {
541 return '';
542 }
543
544 if (!(Ext.isNumeric(value) && Ext.isNumeric(maxcpu) && (maxcpu >= 1))) {
545 return '';
546 }
547
548 var per = (value * 100) / maxcpu;
549
550 return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU');
551 },
552
553 render_size: function(value, metaData, record, rowIndex, colIndex, store) {
554
555 if (!Ext.isNumeric(value)) {
556 return '';
557 }
558
559 return PVE.Utils.format_size(value);
560 },
561
562 render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) {
563 var servertime = new Date(value * 1000);
564 return Ext.Date.format(servertime, 'Y-m-d H:i:s');
565 },
566
567 render_mem_usage: function(value, metaData, record, rowIndex, colIndex, store) {
568
569 var mem = value;
570 var maxmem = record.data.maxmem;
571
572 if (!record.data.uptime) {
573 return '';
574 }
575
576 if (!(Ext.isNumeric(mem) && maxmem)) {
577 return '';
578 }
579
580 var per = (mem * 100) / maxmem;
581
582 return per.toFixed(1) + '%';
583 },
584
585 render_disk_usage: function(value, metaData, record, rowIndex, colIndex, store) {
586
587 var disk = value;
588 var maxdisk = record.data.maxdisk;
589
590 if (!(Ext.isNumeric(disk) && maxdisk)) {
591 return '';
592 }
593
594 var per = (disk * 100) / maxdisk;
595
596 return per.toFixed(1) + '%';
597 },
598
599 render_resource_type: function(value, metaData, record, rowIndex, colIndex, store) {
600
601 var cls = 'pve-itype-icon-' + value;
602
603 if (record.data.running) {
604 metaData.tdCls = cls + "-running";
605 } else {
606 metaData.tdCls = cls;
607 }
608
609 return value;
610 },
611
612 render_uptime: function(value, metaData, record, rowIndex, colIndex, store) {
613
614 var uptime = value;
615
616 if (uptime === undefined) {
617 return '';
618 }
619
620 if (uptime <= 0) {
621 return '-';
622 }
623
624 return PVE.Utils.format_duration_long(uptime);
625 },
626
627 render_upid: function(value, metaData, record) {
628 var type = record.data.type;
629 var id = record.data.id;
630
631 return PVE.Utils.format_task_description(type, id);
632 }
633}});
634