]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/Utils.js
bump version to 4.2-8
[pve-manager.git] / www / manager6 / Utils.js
CommitLineData
b0a6d326
EK
1Ext.ns('PVE');
2
63b9faae
EK
3// avoid errors related to Accessible Rich Internet Applications
4// (access for people with disabilities)
0be88ae1 5// TODO reenable after all components are upgraded
63b9faae
EK
6Ext.enableAria = false;
7Ext.enableAriaButtons = false;
8Ext.enableAriaPanels = false;
9
b0a6d326 10// avoid errors when running without development tools
0be88ae1
DC
11if (!Ext.isDefined(Ext.global.console)) {
12 var console = {
13 dir: function() {},
14 log: function() {}
b0a6d326
EK
15 };
16}
0be88ae1 17console.log("Starting PVE Manager");
b0a6d326
EK
18
19Ext.Ajax.defaultHeaders = {
20 'Accept': 'application/json'
21};
22
23Ext.Ajax.on('beforerequest', function(conn, options) {
24 if (PVE.CSRFPreventionToken) {
0be88ae1 25 if (!options.headers) {
b0a6d326
EK
26 options.headers = {};
27 }
28 options.headers.CSRFPreventionToken = PVE.CSRFPreventionToken;
29 }
30});
31
32var IPV4_OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])";
33var IPV4_REGEXP = "(?:(?:" + IPV4_OCTET + "\\.){3}" + IPV4_OCTET + ")";
34var IPV6_H16 = "(?:[0-9a-fA-F]{1,4})";
35var IPV6_LS32 = "(?:(?:" + IPV6_H16 + ":" + IPV6_H16 + ")|" + IPV4_REGEXP + ")";
36
37
38var IP4_match = new RegExp("^(?:" + IPV4_REGEXP + ")$");
368df92e 39var IP4_cidr_match = new RegExp("^(?:" + IPV4_REGEXP + ")\/([0-9]{1,2})$");
b0a6d326
EK
40
41var IPV6_REGEXP = "(?:" +
42 "(?:(?:" + "(?:" + IPV6_H16 + ":){6})" + IPV6_LS32 + ")|" +
43 "(?:(?:" + "::" + "(?:" + IPV6_H16 + ":){5})" + IPV6_LS32 + ")|" +
44 "(?:(?:(?:" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){4})" + IPV6_LS32 + ")|" +
45 "(?:(?:(?:(?:" + IPV6_H16 + ":){0,1}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){3})" + IPV6_LS32 + ")|" +
46 "(?:(?:(?:(?:" + IPV6_H16 + ":){0,2}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){2})" + IPV6_LS32 + ")|" +
47 "(?:(?:(?:(?:" + IPV6_H16 + ":){0,3}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){1})" + IPV6_LS32 + ")|" +
48 "(?:(?:(?:(?:" + IPV6_H16 + ":){0,4}" + IPV6_H16 + ")?::" + ")" + IPV6_LS32 + ")|" +
49 "(?:(?:(?:(?:" + IPV6_H16 + ":){0,5}" + IPV6_H16 + ")?::" + ")" + IPV6_H16 + ")|" +
50 "(?:(?:(?:(?:" + IPV6_H16 + ":){0,7}" + IPV6_H16 + ")?::" + ")" + ")" +
51 ")";
52
8a52defd 53var IP6_match = new RegExp("^(?:" + IPV6_REGEXP + ")$");
368df92e 54var IP6_cidr_match = new RegExp("^(?:" + IPV6_REGEXP + ")\/([0-9]{1,3})$");
aa0819a8 55var IP6_bracket_match = new RegExp("^\\[(" + IPV6_REGEXP + ")\\]");
8a52defd 56
b0a6d326
EK
57var IP64_match = new RegExp("^(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + ")$");
58
59Ext.define('PVE.Utils', { statics: {
60
61 // this class only contains static functions
62
0be88ae1 63 toolkit: undefined, // (extjs|touch), set inside Toolkit.js
b0a6d326
EK
64
65 log_severity_hash: {
66 0: "panic",
67 1: "alert",
68 2: "critical",
69 3: "error",
70 4: "warning",
71 5: "notice",
72 6: "info",
73 7: "debug"
74 },
75
76 support_level_hash: {
77 'c': gettext('Community'),
78 'b': gettext('Basic'),
79 's': gettext('Standard'),
80 'p': gettext('Premium')
81 },
82
83 noSubKeyHtml: 'You do not have a valid subscription for this server. Please visit <a target="_blank" href="http://www.proxmox.com/products/proxmox-ve/subscription-service-plans">www.proxmox.com</a> to get a list of available options.',
84
85 kvm_ostypes: {
86 other: gettext('Other OS types'),
87 wxp: 'Microsoft Windows XP/2003',
88 w2k: 'Microsoft Windows 2000',
89 w2k8: 'Microsoft Windows Vista/2008',
90 win7: 'Microsoft Windows 7/2008r2',
d481d825 91 win8: 'Microsoft Windows 8.x/10/2012/r2',
b0a6d326 92 l24: 'Linux 2.4 Kernel',
5a4ba3c2 93 l26: 'Linux 4.X/3.X/2.6 Kernel',
b0a6d326
EK
94 solaris: 'Solaris Kernel'
95 },
96
97 render_kvm_ostype: function (value) {
98 if (!value) {
99 return gettext('Other OS types');
100 }
101 var text = PVE.Utils.kvm_ostypes[value];
102 if (text) {
b7159d8b 103 return text;
b0a6d326
EK
104 }
105 return value;
106 },
107
108 render_hotplug_features: function (value) {
23d3881a 109 var fa = [];
b0a6d326
EK
110
111 if (!value || (value === '0')) {
112 return gettext('disabled');
113 }
114
4dcca8a4
DC
115 if (value === '1') {
116 value = 'disk,network,usb';
117 }
118
b0a6d326
EK
119 Ext.each(value.split(','), function(el) {
120 if (el === 'disk') {
121 fa.push(gettext('Disk'));
122 } else if (el === 'network') {
123 fa.push(gettext('Network'));
124 } else if (el === 'usb') {
125 fa.push(gettext('USB'));
126 } else if (el === 'memory') {
127 fa.push(gettext('Memory'));
128 } else if (el === 'cpu') {
129 fa.push(gettext('CPU'));
130 } else {
131 fa.push(el);
132 }
133 });
134
135 return fa.join(', ');
136 },
137
138 network_iface_types: {
139 eth: gettext("Network Device"),
140 bridge: 'Linux Bridge',
141 bond: 'Linux Bond',
142 OVSBridge: 'OVS Bridge',
143 OVSBond: 'OVS Bond',
144 OVSPort: 'OVS Port',
145 OVSIntPort: 'OVS IntPort'
146 },
147
148 render_network_iface_type: function(value) {
0be88ae1 149 return PVE.Utils.network_iface_types[value] ||
b0a6d326
EK
150 PVE.Utils.unknownText;
151 },
152
17c71f27
DC
153 render_qemu_bios: function(value) {
154 if (!value) {
155 return PVE.Utils.defaultText + ' (SeaBIOS)';
156 } else if (value === 'seabios') {
157 return "SeaBIOS";
158 } else if (value === 'ovmf') {
159 return "OVMF (UEFI)";
160 } else {
161 return value;
162 }
163 },
164
b0a6d326
EK
165 render_scsihw: function(value) {
166 if (!value) {
167 return PVE.Utils.defaultText + ' (LSI 53C895A)';
168 } else if (value === 'lsi') {
169 return 'LSI 53C895A';
170 } else if (value === 'lsi53c810') {
171 return 'LSI 53C810';
172 } else if (value === 'megasas') {
173 return 'MegaRAID SAS 8708EM2';
174 } else if (value === 'virtio-scsi-pci') {
175 return 'VIRTIO';
176 } else if (value === 'pvscsi') {
177 return 'VMware PVSCSI';
178 } else {
179 return value;
180 }
181 },
182
183 // fixme: auto-generate this
184 // for now, please keep in sync with PVE::Tools::kvmkeymaps
185 kvm_keymaps: {
186 //ar: 'Arabic',
187 da: 'Danish',
0be88ae1
DC
188 de: 'German',
189 'de-ch': 'German (Swiss)',
190 'en-gb': 'English (UK)',
0a5b8747 191 'en-us': 'English (USA)',
b0a6d326
EK
192 es: 'Spanish',
193 //et: 'Estonia',
194 fi: 'Finnish',
0be88ae1
DC
195 //fo: 'Faroe Islands',
196 fr: 'French',
197 'fr-be': 'French (Belgium)',
b0a6d326
EK
198 'fr-ca': 'French (Canada)',
199 'fr-ch': 'French (Swiss)',
200 //hr: 'Croatia',
201 hu: 'Hungarian',
202 is: 'Icelandic',
0be88ae1 203 it: 'Italian',
b0a6d326
EK
204 ja: 'Japanese',
205 lt: 'Lithuanian',
206 //lv: 'Latvian',
0be88ae1 207 mk: 'Macedonian',
b0a6d326
EK
208 nl: 'Dutch',
209 //'nl-be': 'Dutch (Belgium)',
0be88ae1 210 no: 'Norwegian',
b0a6d326
EK
211 pl: 'Polish',
212 pt: 'Portuguese',
213 'pt-br': 'Portuguese (Brazil)',
214 //ru: 'Russian',
215 sl: 'Slovenian',
216 sv: 'Swedish',
217 //th: 'Thai',
218 tr: 'Turkish'
219 },
220
221 kvm_vga_drivers: {
222 std: gettext('Standard VGA'),
223 vmware: gettext('VMWare compatible'),
224 cirrus: 'Cirrus Logic GD5446',
225 qxl: 'SPICE',
226 qxl2: 'SPICE dual monitor',
227 qxl3: 'SPICE three monitors',
228 qxl4: 'SPICE four monitors',
229 serial0: gettext('Serial terminal') + ' 0',
230 serial1: gettext('Serial terminal') + ' 1',
231 serial2: gettext('Serial terminal') + ' 2',
232 serial3: gettext('Serial terminal') + ' 3'
233 },
234
235 render_kvm_language: function (value) {
236 if (!value) {
237 return PVE.Utils.defaultText;
238 }
239 var text = PVE.Utils.kvm_keymaps[value];
240 if (text) {
241 return text + ' (' + value + ')';
242 }
243 return value;
244 },
245
246 kvm_keymap_array: function() {
f2782813 247 var data = [['__default__', PVE.Utils.render_kvm_language('')]];
b0a6d326
EK
248 Ext.Object.each(PVE.Utils.kvm_keymaps, function(key, value) {
249 data.push([key, PVE.Utils.render_kvm_language(value)]);
250 });
251
252 return data;
253 },
254
255 render_console_viewer: function(value) {
f2782813 256 if (!value || value === '__default__') {
b0a6d326 257 return PVE.Utils.defaultText + ' (HTML5)';
b0a6d326
EK
258 } else if (value === 'vv') {
259 return 'SPICE (remote-viewer)';
260 } else if (value === 'html5') {
261 return 'HTML5 (noVNC)';
262 } else {
263 return value;
264 }
265 },
266
267 language_map: {
268 zh_CN: 'Chinese',
269 ca: 'Catalan',
270 da: 'Danish',
271 en: 'English',
272 eu: 'Euskera (Basque)',
273 fr: 'French',
274 de: 'German',
275 it: 'Italian',
276 ja: 'Japanese',
277 nb: 'Norwegian (Bokmal)',
278 nn: 'Norwegian (Nynorsk)',
279 fa: 'Persian (Farsi)',
280 pl: 'Polish',
281 pt_BR: 'Portuguese (Brazil)',
282 ru: 'Russian',
283 sl: 'Slovenian',
284 es: 'Spanish',
285 sv: 'Swedish',
286 tr: 'Turkish'
287 },
288
289 render_language: function (value) {
290 if (!value) {
291 return PVE.Utils.defaultText + ' (English)';
292 }
293 var text = PVE.Utils.language_map[value];
294 if (text) {
295 return text + ' (' + value + ')';
296 }
297 return value;
298 },
299
300 language_array: function() {
d98a2c3c 301 var data = [['__default__', PVE.Utils.render_language('')]];
b0a6d326
EK
302 Ext.Object.each(PVE.Utils.language_map, function(key, value) {
303 data.push([key, PVE.Utils.render_language(value)]);
304 });
305
306 return data;
307 },
308
309 render_kvm_vga_driver: function (value) {
310 if (!value) {
311 return PVE.Utils.defaultText;
312 }
313 var text = PVE.Utils.kvm_vga_drivers[value];
0be88ae1 314 if (text) {
b0a6d326
EK
315 return text + ' (' + value + ')';
316 }
317 return value;
318 },
319
320 kvm_vga_driver_array: function() {
f2782813 321 var data = [['__default__', PVE.Utils.render_kvm_vga_driver('')]];
b0a6d326
EK
322 Ext.Object.each(PVE.Utils.kvm_vga_drivers, function(key, value) {
323 data.push([key, PVE.Utils.render_kvm_vga_driver(value)]);
324 });
325
326 return data;
327 },
328
329 render_kvm_startup: function(value) {
330 var startup = PVE.Parser.parseStartup(value);
331
332 var res = 'order=';
333 if (startup.order === undefined) {
334 res += 'any';
335 } else {
336 res += startup.order;
337 }
338 if (startup.up !== undefined) {
339 res += ',up=' + startup.up;
340 }
341 if (startup.down !== undefined) {
342 res += ',down=' + startup.down;
343 }
344
345 return res;
346 },
347
348 authOK: function() {
349 return Ext.util.Cookies.get('PVEAuthCookie');
350 },
351
352 authClear: function() {
353 Ext.util.Cookies.clear("PVEAuthCookie");
354 },
355
356 // fixme: remove - not needed?
357 gridLineHeigh: function() {
358 return 21;
0be88ae1 359
b0a6d326
EK
360 //if (Ext.isGecko)
361 //return 23;
362 //return 21;
363 },
364
365 extractRequestError: function(result, verbose) {
366 var msg = gettext('Successful');
367
368 if (!result.success) {
369 msg = gettext("Unknown error");
370 if (result.message) {
371 msg = result.message;
372 if (result.status) {
373 msg += ' (' + result.status + ')';
374 }
375 }
376 if (verbose && Ext.isObject(result.errors)) {
377 msg += "<br>";
378 Ext.Object.each(result.errors, function(prop, desc) {
0be88ae1 379 msg += "<br><b>" + Ext.htmlEncode(prop) + "</b>: " +
b0a6d326
EK
380 Ext.htmlEncode(desc);
381 });
0be88ae1 382 }
b0a6d326
EK
383 }
384
385 return msg;
386 },
387
388 extractFormActionError: function(action) {
389 var msg;
390 switch (action.failureType) {
391 case Ext.form.action.Action.CLIENT_INVALID:
392 msg = gettext('Form fields may not be submitted with invalid values');
393 break;
394 case Ext.form.action.Action.CONNECT_FAILURE:
395 msg = gettext('Connection error');
396 var resp = action.response;
397 if (resp.status && resp.statusText) {
398 msg += " " + resp.status + ": " + resp.statusText;
399 }
400 break;
401 case Ext.form.action.Action.LOAD_FAILURE:
402 case Ext.form.action.Action.SERVER_INVALID:
403 msg = PVE.Utils.extractRequestError(action.result, true);
404 break;
405 }
406 return msg;
407 },
408
409 // Ext.Ajax.request
410 API2Request: function(reqOpts) {
411
412 var newopts = Ext.apply({
413 waitMsg: gettext('Please wait...')
414 }, reqOpts);
415
416 if (!newopts.url.match(/^\/api2/)) {
417 newopts.url = '/api2/extjs' + newopts.url;
418 }
419 delete newopts.callback;
420
421 var createWrapper = function(successFn, callbackFn, failureFn) {
422 Ext.apply(newopts, {
423 success: function(response, options) {
424 if (options.waitMsgTarget) {
425 if (PVE.Utils.toolkit === 'touch') {
426 options.waitMsgTarget.setMasked(false);
427 } else {
428 options.waitMsgTarget.setLoading(false);
429 }
430 }
431 var result = Ext.decode(response.responseText);
432 response.result = result;
433 if (!result.success) {
434 response.htmlStatus = PVE.Utils.extractRequestError(result, true);
435 Ext.callback(callbackFn, options.scope, [options, false, response]);
436 Ext.callback(failureFn, options.scope, [response, options]);
437 return;
438 }
439 Ext.callback(callbackFn, options.scope, [options, true, response]);
440 Ext.callback(successFn, options.scope, [response, options]);
441 },
442 failure: function(response, options) {
443 if (options.waitMsgTarget) {
444 if (PVE.Utils.toolkit === 'touch') {
445 options.waitMsgTarget.setMasked(false);
446 } else {
447 options.waitMsgTarget.setLoading(false);
448 }
449 }
450 response.result = {};
451 try {
452 response.result = Ext.decode(response.responseText);
453 } catch(e) {}
454 var msg = gettext('Connection error') + ' - server offline?';
455 if (response.aborted) {
456 msg = gettext('Connection error') + ' - aborted.';
457 } else if (response.timedout) {
458 msg = gettext('Connection error') + ' - Timeout.';
459 } else if (response.status && response.statusText) {
460 msg = gettext('Connection error') + ' ' + response.status + ': ' + response.statusText;
461 }
462 response.htmlStatus = msg;
463 Ext.callback(callbackFn, options.scope, [options, false, response]);
464 Ext.callback(failureFn, options.scope, [response, options]);
465 }
466 });
467 };
468
469 createWrapper(reqOpts.success, reqOpts.callback, reqOpts.failure);
470
471 var target = newopts.waitMsgTarget;
472 if (target) {
473 if (PVE.Utils.toolkit === 'touch') {
474 target.setMasked({ xtype: 'loadmask', message: newopts.waitMsg} );
475 } else {
476 // Note: ExtJS bug - this does not work when component is not rendered
477 target.setLoading(newopts.waitMsg);
478 }
479 }
480 Ext.Ajax.request(newopts);
481 },
482
483 assemble_field_data: function(values, data) {
484 if (Ext.isObject(data)) {
485 Ext.Object.each(data, function(name, val) {
486 if (values.hasOwnProperty(name)) {
487 var bucket = values[name];
488 if (!Ext.isArray(bucket)) {
489 bucket = values[name] = [bucket];
490 }
491 if (Ext.isArray(val)) {
492 values[name] = bucket.concat(val);
493 } else {
494 bucket.push(val);
495 }
496 } else {
497 values[name] = val;
498 }
499 });
500 }
501 },
502
503 checked_command: function(orig_cmd) {
504 PVE.Utils.API2Request({
505 url: '/nodes/localhost/subscription',
506 method: 'GET',
507 //waitMsgTarget: me,
508 failure: function(response, opts) {
509 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
510 },
511 success: function(response, opts) {
512 var data = response.result.data;
513
514 if (data.status !== 'Active') {
515 Ext.Msg.show({
516 title: gettext('No valid subscription'),
517 icon: Ext.Msg.WARNING,
518 msg: PVE.Utils.noSubKeyHtml,
519 buttons: Ext.Msg.OK,
520 callback: function(btn) {
521 if (btn !== 'ok') {
522 return;
523 }
524 orig_cmd();
525 }
526 });
527 } else {
528 orig_cmd();
529 }
530 }
531 });
532 },
533
534 task_desc_table: {
535 vncproxy: [ 'VM/CT', gettext('Console') ],
536 spiceproxy: [ 'VM/CT', gettext('Console') + ' (Spice)' ],
537 vncshell: [ '', gettext('Shell') ],
538 spiceshell: [ '', gettext('Shell') + ' (Spice)' ],
539 qmsnapshot: [ 'VM', gettext('Snapshot') ],
540 qmrollback: [ 'VM', gettext('Rollback') ],
541 qmdelsnapshot: [ 'VM', gettext('Delete Snapshot') ],
542 qmcreate: [ 'VM', gettext('Create') ],
543 qmrestore: [ 'VM', gettext('Restore') ],
544 qmdestroy: [ 'VM', gettext('Destroy') ],
545 qmigrate: [ 'VM', gettext('Migrate') ],
546 qmclone: [ 'VM', gettext('Clone') ],
547 qmmove: [ 'VM', gettext('Move disk') ],
548 qmtemplate: [ 'VM', gettext('Convert to template') ],
549 qmstart: [ 'VM', gettext('Start') ],
550 qmstop: [ 'VM', gettext('Stop') ],
551 qmreset: [ 'VM', gettext('Reset') ],
552 qmshutdown: [ 'VM', gettext('Shutdown') ],
553 qmsuspend: [ 'VM', gettext('Suspend') ],
554 qmresume: [ 'VM', gettext('Resume') ],
555 qmconfig: [ 'VM', gettext('Configure') ],
16152937
DM
556 vzsnapshot: [ 'CT', gettext('Snapshot') ],
557 vzrollback: [ 'CT', gettext('Rollback') ],
558 vzdelsnapshot: [ 'CT', gettext('Delete Snapshot') ],
b0a6d326
EK
559 vzcreate: ['CT', gettext('Create') ],
560 vzrestore: ['CT', gettext('Restore') ],
561 vzdestroy: ['CT', gettext('Destroy') ],
562 vzmigrate: [ 'CT', gettext('Migrate') ],
bfade4b4
DM
563 vzclone: [ 'CT', gettext('Clone') ],
564 vztemplate: [ 'CT', gettext('Convert to template') ],
b0a6d326
EK
565 vzstart: ['CT', gettext('Start') ],
566 vzstop: ['CT', gettext('Stop') ],
567 vzmount: ['CT', gettext('Mount') ],
568 vzumount: ['CT', gettext('Unmount') ],
569 vzshutdown: ['CT', gettext('Shutdown') ],
570 vzsuspend: [ 'CT', gettext('Suspend') ],
571 vzresume: [ 'CT', gettext('Resume') ],
572 hamigrate: [ 'HA', gettext('Migrate') ],
573 hastart: [ 'HA', gettext('Start') ],
574 hastop: [ 'HA', gettext('Stop') ],
575 srvstart: ['SRV', gettext('Start') ],
576 srvstop: ['SRV', gettext('Stop') ],
577 srvrestart: ['SRV', gettext('Restart') ],
578 srvreload: ['SRV', gettext('Reload') ],
579 cephcreatemon: ['Ceph Monitor', gettext('Create') ],
580 cephdestroymon: ['Ceph Monitor', gettext('Destroy') ],
581 cephcreateosd: ['Ceph OSD', gettext('Create') ],
582 cephdestroyosd: ['Ceph OSD', gettext('Destroy') ],
583 imgcopy: ['', gettext('Copy data') ],
584 imgdel: ['', gettext('Erase data') ],
585 download: ['', gettext('Download') ],
586 vzdump: ['', gettext('Backup') ],
587 aptupdate: ['', gettext('Update package database') ],
588 startall: [ '', gettext('Start all VMs and Containers') ],
589 stopall: [ '', gettext('Stop all VMs and Containers') ],
590 migrateall: [ '', gettext('Migrate all VMs and Containers') ]
591 },
592
0be88ae1 593 format_task_description: function(type, id) {
b0a6d326
EK
594 var farray = PVE.Utils.task_desc_table[type];
595 if (!farray) {
596 return type;
597 }
598 var prefix = farray[0];
599 var text = farray[1];
600 if (prefix) {
0be88ae1 601 return prefix + ' ' + id + ' - ' + text;
b0a6d326
EK
602 }
603 return text;
604 },
605
606 parse_task_upid: function(upid) {
607 var task = {};
608
609 var res = upid.match(/^UPID:(\S+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8,9}):([0-9A-Fa-f]{8}):([^:\s]+):([^:\s]*):([^:\s]+):$/);
610 if (!res) {
611 throw "unable to parse upid '" + upid + "'";
612 }
613 task.node = res[1];
614 task.pid = parseInt(res[2], 16);
615 task.pstart = parseInt(res[3], 16);
616 task.starttime = parseInt(res[4], 16);
617 task.type = res[5];
618 task.id = res[6];
619 task.user = res[7];
620
621 task.desc = PVE.Utils.format_task_description(task.type, task.id);
622
623 return task;
624 },
625
626 format_size: function(size) {
627 /*jslint confusion: true */
628
02f47fe8
DC
629 var units = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
630 var num = 0;
b0a6d326 631
02f47fe8
DC
632 while (size >= 1024 && ((num++)+1) < units.length) {
633 size = size / 1024;
b0a6d326
EK
634 }
635
02f47fe8 636 return size.toFixed((num > 0)?2:0) + " " + units[num] + "B";
b0a6d326
EK
637 },
638
639 format_html_bar: function(per, text) {
640
641 return "<div class='pve-bar-wrap'>" + text + "<div class='pve-bar-border'>" +
642 "<div class='pve-bar-inner' style='width:" + per + "%;'></div>" +
643 "</div></div>";
0be88ae1 644
b0a6d326
EK
645 },
646
647 format_cpu_bar: function(per1, per2, text) {
648
649 return "<div class='pve-bar-border'>" +
650 "<div class='pve-bar-inner' style='width:" + per1 + "%;'></div>" +
651 "<div class='pve-bar-inner2' style='width:" + per2 + "%;'></div>" +
0be88ae1 652 "<div class='pve-bar-text'>" + text + "</div>" +
b0a6d326
EK
653 "</div>";
654 },
655
656 format_large_bar: function(per, text) {
657
658 if (!text) {
659 text = per.toFixed(1) + "%";
660 }
661
662 return "<div class='pve-largebar-border'>" +
663 "<div class='pve-largebar-inner' style='width:" + per + "%;'></div>" +
0be88ae1 664 "<div class='pve-largebar-text'>" + text + "</div>" +
b0a6d326
EK
665 "</div>";
666 },
667
668 format_duration_long: function(ut) {
669
670 var days = Math.floor(ut / 86400);
671 ut -= days*86400;
672 var hours = Math.floor(ut / 3600);
673 ut -= hours*3600;
674 var mins = Math.floor(ut / 60);
675 ut -= mins*60;
676
677 var hours_str = '00' + hours.toString();
678 hours_str = hours_str.substr(hours_str.length - 2);
679 var mins_str = "00" + mins.toString();
680 mins_str = mins_str.substr(mins_str.length - 2);
681 var ut_str = "00" + ut.toString();
682 ut_str = ut_str.substr(ut_str.length - 2);
683
684 if (days) {
685 var ds = days > 1 ? PVE.Utils.daysText : PVE.Utils.dayText;
0be88ae1 686 return days.toString() + ' ' + ds + ' ' +
b0a6d326
EK
687 hours_str + ':' + mins_str + ':' + ut_str;
688 } else {
689 return hours_str + ':' + mins_str + ':' + ut_str;
690 }
691 },
692
693 format_duration_short: function(ut) {
0be88ae1 694
b0a6d326
EK
695 if (ut < 60) {
696 return ut.toString() + 's';
697 }
698
699 if (ut < 3600) {
700 var mins = ut / 60;
701 return mins.toFixed(0) + 'm';
702 }
703
704 if (ut < 86400) {
705 var hours = ut / 3600;
706 return hours.toFixed(0) + 'h';
707 }
708
709 var days = ut / 86400;
0be88ae1 710 return days.toFixed(0) + 'd';
b0a6d326
EK
711 },
712
713 yesText: gettext('Yes'),
714 noText: gettext('No'),
715 noneText: gettext('none'),
716 errorText: gettext('Error'),
717 unknownText: gettext('Unknown'),
718 defaultText: gettext('Default'),
719 daysText: gettext('days'),
720 dayText: gettext('day'),
721 runningText: gettext('running'),
722 stoppedText: gettext('stopped'),
723 neverText: gettext('never'),
724 totalText: gettext('Total'),
725 usedText: gettext('Used'),
726 directoryText: gettext('Directory'),
727 imagesText: gettext('Disk image'),
728 backupFileText: gettext('VZDump backup file'),
2c554952 729 vztmplText: gettext('Container template'),
b0a6d326 730 isoImageText: gettext('ISO image'),
2c554952 731 containersText: gettext('Container'),
45ab85bb
DM
732 stateText: gettext('State'),
733 groupText: gettext('Group'),
b0a6d326
EK
734
735 format_expire: function(date) {
736 if (!date) {
737 return PVE.Utils.neverText;
738 }
739 return Ext.Date.format(date, "Y-m-d");
740 },
741
742 format_storage_type: function(value) {
743 if (value === 'dir') {
744 return PVE.Utils.directoryText;
745 } else if (value === 'nfs') {
746 return 'NFS';
747 } else if (value === 'glusterfs') {
748 return 'GlusterFS';
749 } else if (value === 'lvm') {
750 return 'LVM';
ffd96a3b
DM
751 } else if (value === 'lvmthin') {
752 return 'LVM-Thin';
b0a6d326
EK
753 } else if (value === 'iscsi') {
754 return 'iSCSI';
755 } else if (value === 'rbd') {
756 return 'RBD';
757 } else if (value === 'sheepdog') {
758 return 'Sheepdog';
759 } else if (value === 'zfs') {
760 return 'ZFS over iSCSI';
761 } else if (value === 'zfspool') {
762 return 'ZFS';
763 } else if (value === 'iscsidirect') {
764 return 'iSCSIDirect';
ffd96a3b
DM
765 } else if (value === 'drbd') {
766 return 'DRBD';
b0a6d326
EK
767 } else {
768 return PVE.Utils.unknownText;
769 }
770 },
771
772 format_boolean_with_default: function(value) {
f2782813 773 if (Ext.isDefined(value) && value !== '__default__') {
b0a6d326
EK
774 return value ? PVE.Utils.yesText : PVE.Utils.noText;
775 }
776 return PVE.Utils.defaultText;
777 },
778
779 format_boolean: function(value) {
780 return value ? PVE.Utils.yesText : PVE.Utils.noText;
781 },
782
783 format_neg_boolean: function(value) {
784 return !value ? PVE.Utils.yesText : PVE.Utils.noText;
785 },
786
ced1677b
TL
787 format_ha: function(value) {
788 var text = PVE.Utils.format_boolean(value.managed);
789
790 if (value.managed) {
45ab85bb 791 text += ', ' + PVE.Utils.stateText + ': ';
f25d7c4a 792 text += value.state || PVE.Utils.noneText;
ced1677b 793
45ab85bb 794 text += ', ' + PVE.Utils.groupText + ': ';
f25d7c4a 795 text += value.group || PVE.Utils.noneText;
ced1677b
TL
796 }
797
798 return text;
799 },
800
b0a6d326
EK
801 format_content_types: function(value) {
802 var cta = [];
803
804 Ext.each(value.split(',').sort(), function(ct) {
805 if (ct === 'images') {
806 cta.push(PVE.Utils.imagesText);
807 } else if (ct === 'backup') {
808 cta.push(PVE.Utils.backupFileText);
809 } else if (ct === 'vztmpl') {
810 cta.push(PVE.Utils.vztmplText);
811 } else if (ct === 'iso') {
812 cta.push(PVE.Utils.isoImageText);
813 } else if (ct === 'rootdir') {
814 cta.push(PVE.Utils.containersText);
815 }
816 });
817
818 return cta.join(', ');
819 },
820
821 render_storage_content: function(value, metaData, record) {
822 var data = record.data;
823 if (Ext.isNumber(data.channel) &&
824 Ext.isNumber(data.id) &&
825 Ext.isNumber(data.lun)) {
0be88ae1
DC
826 return "CH " +
827 Ext.String.leftPad(data.channel,2, '0') +
b0a6d326
EK
828 " ID " + data.id + " LUN " + data.lun;
829 }
830 return data.volid.replace(/^.*:(.*\/)?/,'');
831 },
832
833 render_serverity: function (value) {
834 return PVE.Utils.log_severity_hash[value] || value;
835 },
836
837 render_cpu: function(value, metaData, record, rowIndex, colIndex, store) {
838
839 if (!(record.data.uptime && Ext.isNumeric(value))) {
840 return '';
841 }
842
843 var maxcpu = record.data.maxcpu || 1;
844
845 if (!Ext.isNumeric(maxcpu) && (maxcpu >= 1)) {
846 return '';
847 }
0be88ae1 848
b0a6d326
EK
849 var per = value * 100;
850
851 return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU');
852 },
853
854 render_size: function(value, metaData, record, rowIndex, colIndex, store) {
855 /*jslint confusion: true */
856
857 if (!Ext.isNumeric(value)) {
858 return '';
859 }
860
861 return PVE.Utils.format_size(value);
862 },
863
864 render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) {
865 var servertime = new Date(value * 1000);
866 return Ext.Date.format(servertime, 'Y-m-d H:i:s');
867 },
868
0bfc799f
DC
869 calculate_mem_usage: function(data) {
870 if (!Ext.isNumeric(data.mem) ||
871 data.maxmem === 0 ||
872 data.uptime < 1) {
873 return -1;
874 }
875
876 return (data.mem / data.maxmem);
877 },
878
879 render_mem_usage_percent: function(value, metaData, record, rowIndex, colIndex, store) {
880 if (!Ext.isNumeric(value) || value === -1) {
881 return '';
882 }
883 if (value > 1 ) {
884 // we got no percentage but bytes
885 var mem = value;
886 var maxmem = record.data.maxmem;
887 if (!record.data.uptime ||
888 maxmem === 0 ||
889 !Ext.isNumeric(mem)) {
890 return '';
891 }
892
893 return ((mem*100)/maxmem).toFixed(1) + " %";
894 }
895 return (value*100).toFixed(1) + " %";
896 },
897
b0a6d326
EK
898 render_mem_usage: function(value, metaData, record, rowIndex, colIndex, store) {
899
900 var mem = value;
901 var maxmem = record.data.maxmem;
0be88ae1 902
b0a6d326
EK
903 if (!record.data.uptime) {
904 return '';
905 }
906
907 if (!(Ext.isNumeric(mem) && maxmem)) {
908 return '';
909 }
910
728f1b97 911 return PVE.Utils.render_size(value);
b0a6d326
EK
912 },
913
0bfc799f
DC
914 calculate_disk_usage: function(data) {
915
916 if (!Ext.isNumeric(data.disk) ||
917 data.type === 'qemu' ||
918 (data.type === 'lxc' && data.uptime === 0) ||
919 data.maxdisk === 0) {
920 return -1;
921 }
922
923 return (data.disk / data.maxdisk);
924 },
925
926 render_disk_usage_percent: function(value, metaData, record, rowIndex, colIndex, store) {
927 if (!Ext.isNumeric(value) || value === -1) {
928 return '';
929 }
930
931 return (value * 100).toFixed(1) + " %";
932 },
933
b0a6d326
EK
934 render_disk_usage: function(value, metaData, record, rowIndex, colIndex, store) {
935
936 var disk = value;
937 var maxdisk = record.data.maxdisk;
728f1b97 938 var type = record.data.type;
b0a6d326 939
728f1b97
DC
940 if (!Ext.isNumeric(disk) ||
941 type === 'qemu' ||
942 maxdisk === 0 ||
943 (type === 'lxc' && record.data.uptime === 0)) {
b0a6d326
EK
944 return '';
945 }
946
728f1b97 947 return PVE.Utils.render_size(value);
b0a6d326
EK
948 },
949
950 render_resource_type: function(value, metaData, record, rowIndex, colIndex, store) {
951
b1d8e73d
DC
952 var icon = '';
953 var gridcls = '';
954
955 switch (value) {
956 case 'lxc': icon = 'cube';
957 gridcls = '-stopped';
958 break;
959 case 'qemu': icon = 'desktop';
960 gridcls = '-stopped';
961 break;
962 case 'node': icon = 'building';
963 gridcls = '-offline';
964 break;
965 case 'storage': icon = 'database'; break;
966 case 'pool': icon = 'tags'; break;
967 default: icon = 'file';
968 }
969
970 if (value === 'lxc' || value === 'qemu') {
971 if (record.data.running && record.data.status !== 'paused') {
972 gridcls = '-running';
973 } else if (record.data.running) {
974 gridcls = '-paused';
975 }
976 if (record.data.template) {
977 icon = 'file-o';
978 gridcls = '-template-' + value;
979 }
980 } else if (value === 'node') {
981 if (record.data.running) {
a764c5f7 982 gridcls = '-online';
b1d8e73d 983 }
b0a6d326
EK
984 }
985
a764c5f7 986 var fa = '<i class="fa fa-fw x-fa-grid' + gridcls + ' fa-' + icon + '"></i> ';
b1d8e73d 987 return fa + value;
b0a6d326
EK
988 },
989
990 render_uptime: function(value, metaData, record, rowIndex, colIndex, store) {
991
992 var uptime = value;
993
994 if (uptime === undefined) {
995 return '';
996 }
0be88ae1 997
b0a6d326
EK
998 if (uptime <= 0) {
999 return '-';
1000 }
1001
1002 return PVE.Utils.format_duration_long(uptime);
1003 },
1004
1005 render_support_level: function(value, metaData, record) {
1006 return PVE.Utils.support_level_hash[value] || '-';
1007 },
1008
0be88ae1 1009 render_upid: function(value, metaData, record) {
b0a6d326
EK
1010 var type = record.data.type;
1011 var id = record.data.id;
1012
1013 return PVE.Utils.format_task_description(type, id);
1014 },
1015
1016 dialog_title: function(subject, create, isAdd) {
1017 if (create) {
1018 if (isAdd) {
1019 return gettext('Add') + ': ' + subject;
1020 } else {
1021 return gettext('Create') + ': ' + subject;
1022 }
1023 } else {
1024 return gettext('Edit') + ': ' + subject;
1025 }
1026 },
aa0819a8
WB
1027
1028 windowHostname: function() {
1029 return window.location.hostname.replace(IP6_bracket_match,
1030 function(m, addr, offset, original) { return addr; });
1031 },
0be88ae1 1032
b0a6d326
EK
1033 openDefaultConsoleWindow: function(allowSpice, vmtype, vmid, nodename, vmname) {
1034 var dv = PVE.Utils.defaultViewer(allowSpice);
1035 PVE.Utils.openConsoleWindow(dv, vmtype, vmid, nodename, vmname);
1036 },
1037
1038 openConsoleWindow: function(viewer, vmtype, vmid, nodename, vmname) {
9e361643 1039 // kvm, lxc, shell, upgrade
b0a6d326 1040
9e361643 1041 if (vmid == undefined && (vmtype === 'kvm' || vmtype === 'lxc')) {
b0a6d326
EK
1042 throw "missing vmid";
1043 }
1044
1045 if (!nodename) {
1046 throw "no nodename specified";
1047 }
1048
c7218ab3
DC
1049 if (viewer === 'html5') {
1050 PVE.Utils.openVNCViewer(vmtype, vmid, nodename, vmname);
b0a6d326
EK
1051 } else if (viewer === 'vv') {
1052 var url;
aa0819a8 1053 var params = { proxy: PVE.Utils.windowHostname() };
b0a6d326
EK
1054 if (vmtype === 'kvm') {
1055 url = '/nodes/' + nodename + '/qemu/' + vmid.toString() + '/spiceproxy';
1056 PVE.Utils.openSpiceViewer(url, params);
9e361643
DM
1057 } else if (vmtype === 'lxc') {
1058 url = '/nodes/' + nodename + '/lxc/' + vmid.toString() + '/spiceproxy';
b0a6d326
EK
1059 PVE.Utils.openSpiceViewer(url, params);
1060 } else if (vmtype === 'shell') {
1061 url = '/nodes/' + nodename + '/spiceshell';
1062 PVE.Utils.openSpiceViewer(url, params);
1063 } else if (vmtype === 'upgrade') {
1064 url = '/nodes/' + nodename + '/spiceshell';
1065 params.upgrade = 1;
1066 PVE.Utils.openSpiceViewer(url, params);
1067 }
1068 } else {
1069 throw "unknown viewer type";
1070 }
1071 },
1072
1073 defaultViewer: function(allowSpice) {
1074 var vncdefault = 'html5';
1075 var dv = PVE.VersionInfo.console || vncdefault;
1076 if (dv === 'vv' && !allowSpice) {
1077 dv = vncdefault;
1078 }
1079
1080 return dv;
1081 },
1082
c7218ab3 1083 openVNCViewer: function(vmtype, vmid, nodename, vmname) {
b0a6d326 1084 var url = Ext.urlEncode({
9e361643 1085 console: vmtype, // kvm, lxc, upgrade or shell
c7218ab3 1086 novnc: 1,
b0a6d326
EK
1087 vmid: vmid,
1088 vmname: vmname,
1089 node: nodename
1090 });
1091 var nw = window.open("?" + url, '_blank', "innerWidth=745,innerheight=427");
1092 nw.focus();
1093 },
1094
1095 openSpiceViewer: function(url, params){
1096
1097 var downloadWithName = function(uri, name) {
1098 var link = Ext.DomHelper.append(document.body, {
1099 tag: 'a',
1100 href: uri,
1101 css : 'display:none;visibility:hidden;height:0px;'
1102 });
1103
1104 // Note: we need to tell android the correct file name extension
1105 // but we do not set 'download' tag for other environments, because
1106 // It can have strange side effects (additional user prompt on firefox)
1107 var andriod = navigator.userAgent.match(/Android/i) ? true : false;
1108 if (andriod) {
1109 link.download = name;
1110 }
1111
1112 if (link.fireEvent) {
1113 link.fireEvent('onclick');
1114 } else {
1115 var evt = document.createEvent("MouseEvents");
1116 evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
1117 link.dispatchEvent(evt);
1118 }
1119 };
1120
1121 PVE.Utils.API2Request({
1122 url: url,
1123 params: params,
1124 method: 'POST',
1125 failure: function(response, opts){
1126 Ext.Msg.alert('Error', response.htmlStatus);
1127 },
1128 success: function(response, opts){
1129 var raw = "[virt-viewer]\n";
1130 Ext.Object.each(response.result.data, function(k, v) {
1131 raw += k + "=" + v + "\n";
1132 });
1133 var url = 'data:application/x-virt-viewer;charset=UTF-8,' +
1134 encodeURIComponent(raw);
0be88ae1 1135
b0a6d326
EK
1136 downloadWithName(url, "pve-spice.vv");
1137 }
1138 });
1139 },
1140
0be88ae1 1141 // comp.setLoading() is buggy in ExtJS 4.0.7, so we
b0a6d326
EK
1142 // use el.mask() instead
1143 setErrorMask: function(comp, msg) {
1144 var el = comp.el;
1145 if (!el) {
1146 return;
1147 }
1148 if (!msg) {
1149 el.unmask();
1150 } else {
1151 if (msg === true) {
1152 el.mask(gettext("Loading..."));
1153 } else {
1154 el.mask(msg);
1155 }
1156 }
1157 },
1158
1159 monStoreErrors: function(me, store) {
1160 me.mon(store, 'beforeload', function(s, operation, eOpts) {
1161 if (!me.loadCount) {
1162 me.loadCount = 0; // make sure it is numeric
1163 PVE.Utils.setErrorMask(me, true);
1164 }
1165 });
1166
1167 // only works with 'pve' proxy
1168 me.mon(store.proxy, 'afterload', function(proxy, request, success) {
1169 me.loadCount++;
1170
1171 if (success) {
1172 PVE.Utils.setErrorMask(me, false);
1173 return;
1174 }
1175
1176 var msg;
23d3881a 1177 /*jslint nomen: true */
86826854 1178 var operation = request._operation;
b0a6d326
EK
1179 var error = operation.getError();
1180 if (error.statusText) {
1181 msg = error.statusText + ' (' + error.status + ')';
1182 } else {
1183 msg = gettext('Connection error');
1184 }
1185 PVE.Utils.setErrorMask(me, msg);
1186 });
685b7aa4 1187 },
b0a6d326 1188
685b7aa4
DC
1189 createCmdMenu: function(v, record, item, index, event) {
1190 event.stopEvent();
1191 v.select(record);
1192 var menu;
1193
1194 if (record.data.type === 'qemu' && !record.data.template) {
1195 menu = Ext.create('PVE.qemu.CmdMenu', {
1196 pveSelNode: record
1197 });
1198 } else if (record.data.type === 'qemu' && record.data.template) {
1199 menu = Ext.create('PVE.qemu.TemplateMenu', {
1200 pveSelNode: record
1201 });
1202 } else if (record.data.type === 'lxc' && !record.data.template) {
1203 menu = Ext.create('PVE.lxc.CmdMenu', {
1204 pveSelNode: record
1205 });
1206 } else if (record.data.type === 'lxc' && record.data.template) {
1207 /* since clone does not work reliably, disable for now
1208 menu = Ext.create('PVE.lxc.TemplateMenu', {
1209 pveSelNode: record
1210 });
1211 */
1212 return;
1213 } else {
1214 return;
1215 }
1216
1217 menu.showAt(event.getXY());
1218 }
b0a6d326
EK
1219}});
1220