]>
git.proxmox.com Git - pve-manager.git/blob - www/manager/Utils.js
d5de2e2ee93e665df9f27a9e7f7fa8908063c51c
3 // avoid errors when running without development tools
4 if (!Ext
.isDefined(Ext
.global
.console
)) {
10 console
.log("Starting PVE Manager");
12 Ext
.Ajax
.defaultHeaders
= {
13 'Accept': 'application/json'
16 // do not send '_dc' parameter
17 Ext
.Ajax
.disableCaching
= false;
19 Ext
.Ajax
.on('beforerequest', function(conn
, options
) {
20 if (PVE
.CSRFPreventionToken
) {
21 if (!options
.headers
) {
24 options
.headers
.CSRFPreventionToken
= PVE
.CSRFPreventionToken
;
29 Ext
.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
);
33 IPAddressText
: gettext('Example') + ': 192.168.1.1',
34 IPAddressMask
: /[\d\.]/i,
36 MacAddress: function(v
) {
37 return (/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/).test(v
);
39 MacAddressMask
: /[a-fA-F0-9:]/,
40 MacAddressText
: gettext('Example') + ': 01:23:45:67:89:ab',
42 BridgeName: function(v
) {
43 return (/^vmbr\d{1,4}$/).test(v
);
45 BridgeNameText
: gettext('Format') + ': vmbr<b>N</b>, where 0 <= <b>N</b> <= 9999',
47 BondName: function(v
) {
48 return (/^bond\d{1,4}$/).test(v
);
50 BondNameText
: gettext('Format') + ': bond<b>N</b>, where 0 <= <b>N</b> <= 9999',
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
);
55 QemuStartDateText
: gettext('Format') + ': "now" or "2006-06-17T16:01:21" or "2006-06-17"',
57 StorageId: function(v
) {
58 return (/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i).test(v
);
60 StorageIdText
: gettext("Allowed characters") + ": 'a-z', '0-9', '-', '_', '.'",
62 HttpProxy: function(v
) {
63 return (/^http:\/\/.*$/).test(v
);
65 HttpProxyText
: gettext('Example') + ": http://username:password@host:port/",
67 DnsName: function(v
) {
68 return (/^(([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)\.)*([A-Za-z0-9]([A-Za-z0-9\-]*[A-Za-z0-9])?)$/).test(v
);
70 DnsNameText
: gettext('This is not a valid DNS name')
73 // we dont want that a displayfield set the form dirty flag!
74 Ext
.override(Ext
.form
.field
.Display
, {
75 isDirty: function() { return false; }
78 // hack: ExtJS does not display the correct value if we
79 // call setValue while the store is loading, so we need
80 // to call it again after loading
81 Ext
.override(Ext
.form
.field
.ComboBox
, {
83 this.setValue(this.value
, false);
84 this.callOverridden(arguments
);
88 Ext
.define('PVE.Utils', { statics
: {
90 // this class only contains static functions
103 support_level_hash
: {
104 'c': gettext('Community'),
105 'b': gettext('Basic'),
106 's': gettext('Standard'),
107 'p': gettext('Premium')
111 other
: gettext('Other OS types'),
112 wxp
: 'Microsoft Windows XP/2003',
113 w2k
: 'Microsoft Windows 2000',
114 w2k8
: 'Microsoft Windows Vista/2008',
115 win7
: 'Microsoft Windows 7/2008r2',
116 win8
: 'Microsoft Windows 8/2012',
117 l24
: 'Linux 2.4 Kernel',
118 l26
: 'Linux 3.X/2.6 Kernel'
121 render_kvm_ostype: function (value
) {
123 return gettext('Other OS types');
125 var text
= PVE
.Utils
.kvm_ostypes
[value
];
127 return text
+ ' (' + value
+ ')';
132 render_scsihw: function(value
) {
134 return PVE
.Utils
.defaultText
+ ' (lsi)';
135 } else if (value
=== 'lsi') {
136 return 'LSI 53C895A';
137 } else if (value
=== 'megasas') {
138 return 'MegaRAID SAS 8708EM2';
139 } else if (value
=== 'virtio-scsi-pci') {
146 // fixme: auto-generate this
147 // for now, please keep in sync with PVE::Tools::kvmkeymaps
152 'de-ch': 'German (Swiss)',
153 'en-gb': 'English (UK)',
154 'en-us': 'English (USA',
158 //fo: 'Faroe Islands',
160 'fr-be': 'French (Belgium)',
161 'fr-ca': 'French (Canada)',
162 'fr-ch': 'French (Swiss)',
172 //'nl-be': 'Dutch (Belgium)',
176 'pt-br': 'Portuguese (Brazil)',
186 vmware
: 'VMWare compatible',
187 cirrus
: 'Cirrus Logic GD5446'
190 render_kvm_language: function (value
) {
192 return PVE
.Utils
.defaultText
;
194 var text
= PVE
.Utils
.kvm_keymaps
[value
];
196 return text
+ ' (' + value
+ ')';
201 kvm_keymap_array: function() {
202 var data
= [['', PVE
.Utils
.render_kvm_language('')]];
203 Ext
.Object
.each(PVE
.Utils
.kvm_keymaps
, function(key
, value
) {
204 data
.push([key
, PVE
.Utils
.render_kvm_language(value
)]);
220 nb
: 'Norwegian (Bokmal)',
221 nn
: 'Norwegian (Nynorsk)',
226 pt_BR
: 'Portuguese (Brazil)',
230 render_language: function (value
) {
232 return PVE
.Utils
.defaultText
+ ' (English)';
234 var text
= PVE
.Utils
.language_map
[value
];
236 return text
+ ' (' + value
+ ')';
241 language_array: function() {
242 var data
= [['', PVE
.Utils
.render_language('')]];
243 Ext
.Object
.each(PVE
.Utils
.language_map
, function(key
, value
) {
244 data
.push([key
, PVE
.Utils
.render_language(value
)]);
250 render_kvm_vga_driver: function (value
) {
252 return PVE
.Utils
.defaultText
;
254 var text
= PVE
.Utils
.kvm_vga_drivers
[value
];
256 return text
+ ' (' + value
+ ')';
261 kvm_vga_driver_array: function() {
262 var data
= [['', PVE
.Utils
.render_kvm_vga_driver('')]];
263 Ext
.Object
.each(PVE
.Utils
.kvm_vga_drivers
, function(key
, value
) {
264 data
.push([key
, PVE
.Utils
.render_kvm_vga_driver(value
)]);
270 render_kvm_startup: function(value
) {
271 var startup
= PVE
.Parser
.parseStartup(value
);
274 if (startup
.order
=== undefined) {
277 res
+= startup
.order
;
279 if (startup
.up
!== undefined) {
280 res
+= ',up=' + startup
.up
;
282 if (startup
.down
!== undefined) {
283 res
+= ',down=' + startup
.down
;
290 return Ext
.util
.Cookies
.get('PVEAuthCookie');
293 authClear: function() {
294 Ext
.util
.Cookies
.clear("PVEAuthCookie");
297 // fixme: remove - not needed?
298 gridLineHeigh: function() {
306 extractRequestError: function(result
, verbose
) {
307 var msg
= gettext('Successful');
309 if (!result
.success
) {
310 msg
= gettext("Unknown error");
311 if (result
.message
) {
312 msg
= result
.message
;
314 msg
+= ' (' + result
.status
+ ')';
317 if (verbose
&& Ext
.isObject(result
.errors
)) {
319 Ext
.Object
.each(result
.errors
, function(prop
, desc
) {
320 msg
+= "<br><b>" + Ext
.htmlEncode(prop
) + "</b>: " +
321 Ext
.htmlEncode(desc
);
329 extractFormActionError: function(action
) {
331 switch (action
.failureType
) {
332 case Ext
.form
.action
.Action
.CLIENT_INVALID
:
333 msg
= gettext('Form fields may not be submitted with invalid values');
335 case Ext
.form
.action
.Action
.CONNECT_FAILURE
:
336 msg
= gettext('Connection error');
337 var resp
= action
.response
;
338 if (resp
.status
&& resp
.statusText
) {
339 msg
+= " " + resp
.status
+ ": " + resp
.statusText
;
342 case Ext
.form
.action
.Action
.LOAD_FAILURE
:
343 case Ext
.form
.action
.Action
.SERVER_INVALID
:
344 msg
= PVE
.Utils
.extractRequestError(action
.result
, true);
351 API2Request: function(reqOpts
) {
353 var newopts
= Ext
.apply({
354 waitMsg
: gettext('Please wait...')
357 if (!newopts
.url
.match(/^\/api2/)) {
358 newopts
.url
= '/api2/extjs' + newopts
.url
;
360 delete newopts
.callback
;
362 var createWrapper = function(successFn
, callbackFn
, failureFn
) {
364 success: function(response
, options
) {
365 if (options
.waitMsgTarget
) {
366 options
.waitMsgTarget
.setLoading(false);
368 var result
= Ext
.decode(response
.responseText
);
369 response
.result
= result
;
370 if (!result
.success
) {
371 response
.htmlStatus
= PVE
.Utils
.extractRequestError(result
, true);
372 Ext
.callback(callbackFn
, options
.scope
, [options
, false, response
]);
373 Ext
.callback(failureFn
, options
.scope
, [response
, options
]);
376 Ext
.callback(callbackFn
, options
.scope
, [options
, true, response
]);
377 Ext
.callback(successFn
, options
.scope
, [response
, options
]);
379 failure: function(response
, options
) {
380 if (options
.waitMsgTarget
) {
381 options
.waitMsgTarget
.setLoading(false);
383 response
.result
= {};
385 response
.result
= Ext
.decode(response
.responseText
);
387 var msg
= gettext('Connection error') + ' - server offline?';
388 if (response
.aborted
) {
389 msg
= gettext('Connection error') + ' - aborted.';
390 } else if (response
.timedout
) {
391 msg
= gettext('Connection error') + ' - Timeout.';
392 } else if (response
.status
&& response
.statusText
) {
393 msg
= gettext('Connection error') + ' ' + response
.status
+ ': ' + response
.statusText
;
395 response
.htmlStatus
= msg
;
396 Ext
.callback(callbackFn
, options
.scope
, [options
, false, response
]);
397 Ext
.callback(failureFn
, options
.scope
, [response
, options
]);
402 createWrapper(reqOpts
.success
, reqOpts
.callback
, reqOpts
.failure
);
404 var target
= newopts
.waitMsgTarget
;
406 // Note: ExtJS bug - this does not work when component is not rendered
407 target
.setLoading(newopts
.waitMsg
);
409 Ext
.Ajax
.request(newopts
);
412 assemble_field_data: function(values
, data
) {
413 if (Ext
.isObject(data
)) {
414 Ext
.Object
.each(data
, function(name
, val
) {
415 if (values
.hasOwnProperty(name
)) {
416 var bucket
= values
[name
];
417 if (!Ext
.isArray(bucket
)) {
418 bucket
= values
[name
] = [bucket
];
420 if (Ext
.isArray(val
)) {
421 values
[name
] = bucket
.concat(val
);
433 vncproxy
: [ 'VM/CT', gettext('Console') ],
434 vncshell
: [ '', gettext('Shell') ],
435 qmsnapshot
: [ 'VM', gettext('Snapshot') ],
436 qmrollback
: [ 'VM', gettext('Rollback') ],
437 qmdelsnapshot
: [ 'VM', gettext('Delete Snapshot') ],
438 qmcreate
: [ 'VM', gettext('Create') ],
439 qmrestore
: [ 'VM', gettext('Restore') ],
440 qmdestroy
: [ 'VM', gettext('Destroy') ],
441 qmigrate
: [ 'VM', gettext('Migrate') ],
442 qmclone
: [ 'VM', gettext('Clone') ],
443 qmmove
: [ 'VM', gettext('Move disk') ],
444 qmtemplate
: [ 'VM', gettext('Convert to template') ],
445 qmstart
: [ 'VM', gettext('Start') ],
446 qmstop
: [ 'VM', gettext('Stop') ],
447 qmreset
: [ 'VM', gettext('Reset') ],
448 qmshutdown
: [ 'VM', gettext('Shutdown') ],
449 qmsuspend
: [ 'VM', gettext('Suspend') ],
450 qmresume
: [ 'VM', gettext('Resume') ],
451 qmconfig
: [ 'VM', gettext('Configure') ],
452 vzcreate
: ['CT', gettext('Create') ],
453 vzrestore
: ['CT', gettext('Restore') ],
454 vzdestroy
: ['CT', gettext('Destroy') ],
455 vzmigrate
: [ 'CT', gettext('Migrate') ],
456 vzstart
: ['CT', gettext('Start') ],
457 vzstop
: ['CT', gettext('Stop') ],
458 vzmount
: ['CT', gettext('Mount') ],
459 vzumount
: ['CT', gettext('Unmount') ],
460 vzshutdown
: ['CT', gettext('Shutdown') ],
461 hamigrate
: [ 'HA', gettext('Migrate') ],
462 hastart
: [ 'HA', gettext('Start') ],
463 hastop
: [ 'HA', gettext('Stop') ],
464 srvstart
: ['SRV', gettext('Start') ],
465 srvstop
: ['SRV', gettext('Stop') ],
466 srvrestart
: ['SRV', gettext('Restart') ],
467 srvreload
: ['SRV', gettext('Reload') ],
468 imgcopy
: ['', gettext('Copy data') ],
469 imgdel
: ['', gettext('Erase data') ],
470 download
: ['', gettext('Download') ],
471 vzdump
: ['', gettext('Backup') ],
472 aptupdate
: ['', gettext('Update package database') ],
473 aptupgrade
: ['', gettext('System upgrade') ],
474 startall
: [ '', gettext('Start all VMs and Containers') ],
475 stopall
: [ '', gettext('Stop all VMs and Containers') ]
478 format_task_description: function(type
, id
) {
479 var farray
= PVE
.Utils
.task_desc_table
[type
];
483 var prefix
= farray
[0];
484 var text
= farray
[1];
486 return prefix
+ ' ' + id
+ ' - ' + text
;
491 parse_task_upid: function(upid
) {
494 var res
= upid
.match(/^UPID:(\S+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8}):([^:\s]+):([^:\s]*):([^:\s]+):$/);
496 throw "unable to parse upid '" + upid
+ "'";
499 task
.pid
= parseInt(res
[2], 16);
500 task
.pstart
= parseInt(res
[3], 16);
501 task
.starttime
= parseInt(res
[4], 16);
506 task
.desc
= PVE
.Utils
.format_task_description(task
.type
, task
.id
);
511 format_size: function(size
) {
512 /*jslint confusion: true */
518 var kb
= size
/ 1024;
521 return kb
.toFixed(0) + "KB";
524 var mb
= size
/ (1024*1024);
527 return mb
.toFixed(0) + "MB";
533 return gb
.toFixed(2) + "GB";
538 return tb
.toFixed(2) + "TB";
542 format_html_bar: function(per
, text
) {
544 return "<div class='pve-bar-wrap'>" + text
+ "<div class='pve-bar-border'>" +
545 "<div class='pve-bar-inner' style='width:" + per
+ "%;'></div>" +
550 format_cpu_bar: function(per1
, per2
, text
) {
552 return "<div class='pve-bar-border'>" +
553 "<div class='pve-bar-inner' style='width:" + per1
+ "%;'></div>" +
554 "<div class='pve-bar-inner2' style='width:" + per2
+ "%;'></div>" +
555 "<div class='pve-bar-text'>" + text
+ "</div>" +
559 format_large_bar: function(per
, text
) {
562 text
= per
.toFixed(1) + "%";
565 return "<div class='pve-largebar-border'>" +
566 "<div class='pve-largebar-inner' style='width:" + per
+ "%;'></div>" +
567 "<div class='pve-largebar-text'>" + text
+ "</div>" +
571 format_duration_long: function(ut
) {
573 var days
= Math
.floor(ut
/ 86400);
575 var hours
= Math
.floor(ut
/ 3600);
577 var mins
= Math
.floor(ut
/ 60);
580 var hours_str
= '00' + hours
.toString();
581 hours_str
= hours_str
.substr(hours_str
.length
- 2);
582 var mins_str
= "00" + mins
.toString();
583 mins_str
= mins_str
.substr(mins_str
.length
- 2);
584 var ut_str
= "00" + ut
.toString();
585 ut_str
= ut_str
.substr(ut_str
.length
- 2);
588 var ds
= days
> 1 ? PVE
.Utils
.daysText
: PVE
.Utils
.dayText
;
589 return days
.toString() + ' ' + ds
+ ' ' +
590 hours_str
+ ':' + mins_str
+ ':' + ut_str
;
592 return hours_str
+ ':' + mins_str
+ ':' + ut_str
;
596 format_duration_short: function(ut
) {
599 return ut
.toString() + 's';
604 return mins
.toFixed(0) + 'm';
608 var hours
= ut
/ 3600;
609 return hours
.toFixed(0) + 'h';
612 var days
= ut
/ 86400;
613 return days
.toFixed(0) + 'd';
616 yesText
: gettext('Yes'),
617 noText
: gettext('No'),
618 errorText
: gettext('Error'),
619 unknownText
: gettext('Unknown'),
620 defaultText
: gettext('Default'),
621 daysText
: gettext('days'),
622 dayText
: gettext('day'),
623 runningText
: gettext('running'),
624 stoppedText
: gettext('stopped'),
625 neverText
: gettext('never'),
627 format_expire: function(date
) {
629 return PVE
.Utils
.neverText
;
631 return Ext
.Date
.format(date
, "Y-m-d");
634 format_storage_type: function(value
) {
635 if (value
=== 'dir') {
637 } else if (value
=== 'nfs') {
639 } else if (value
=== 'lvm') {
641 } else if (value
=== 'iscsi') {
643 } else if (value
=== 'rbd') {
645 } else if (value
=== 'sheepdog') {
647 } else if (value
=== 'nexenta') {
649 } else if (value
=== 'iscsidirect') {
650 return 'iSCSIDirect';
652 return PVE
.Utils
.unknownText
;
656 format_boolean_with_default: function(value
) {
657 if (Ext
.isDefined(value
) && value
!== '') {
658 return value
? PVE
.Utils
.yesText
: PVE
.Utils
.noText
;
660 return PVE
.Utils
.defaultText
;
663 format_boolean: function(value
) {
664 return value
? PVE
.Utils
.yesText
: PVE
.Utils
.noText
;
667 format_neg_boolean: function(value
) {
668 return !value
? PVE
.Utils
.yesText
: PVE
.Utils
.noText
;
671 format_content_types: function(value
) {
674 Ext
.each(value
.split(',').sort(), function(ct
) {
675 if (ct
=== 'images') {
677 } else if (ct
=== 'backup') {
679 } else if (ct
=== 'vztmpl') {
680 cta
.push('Templates');
681 } else if (ct
=== 'iso') {
683 } else if (ct
=== 'rootdir') {
684 cta
.push('Containers');
688 return cta
.join(', ');
691 render_storage_content: function(value
, metaData
, record
) {
692 var data
= record
.data
;
693 if (Ext
.isNumber(data
.channel
) &&
694 Ext
.isNumber(data
.id
) &&
695 Ext
.isNumber(data
.lun
)) {
697 Ext
.String
.leftPad(data
.channel
,2, '0') +
698 " ID " + data
.id
+ " LUN " + data
.lun
;
700 return data
.volid
.replace(/^.*:(.*\/)?/,'');
703 render_serverity: function (value
) {
704 return PVE
.Utils
.log_severity_hash
[value
] || value
;
707 render_cpu: function(value
, metaData
, record
, rowIndex
, colIndex
, store
) {
709 if (!(record
.data
.uptime
&& Ext
.isNumeric(value
))) {
713 var maxcpu
= record
.data
.maxcpu
|| 1;
715 if (!Ext
.isNumeric(maxcpu
) && (maxcpu
>= 1)) {
719 var per
= value
* 100;
721 return per
.toFixed(1) + '% of ' + maxcpu
.toString() + (maxcpu
> 1 ? 'CPUs' : 'CPU');
724 render_size: function(value
, metaData
, record
, rowIndex
, colIndex
, store
) {
725 /*jslint confusion: true */
727 if (!Ext
.isNumeric(value
)) {
731 return PVE
.Utils
.format_size(value
);
734 render_timestamp: function(value
, metaData
, record
, rowIndex
, colIndex
, store
) {
735 var servertime
= new Date(value
* 1000);
736 return Ext
.Date
.format(servertime
, 'Y-m-d H:i:s');
739 render_mem_usage: function(value
, metaData
, record
, rowIndex
, colIndex
, store
) {
742 var maxmem
= record
.data
.maxmem
;
744 if (!record
.data
.uptime
) {
748 if (!(Ext
.isNumeric(mem
) && maxmem
)) {
752 var per
= (mem
* 100) / maxmem
;
754 return per
.toFixed(1) + '%';
757 render_disk_usage: function(value
, metaData
, record
, rowIndex
, colIndex
, store
) {
760 var maxdisk
= record
.data
.maxdisk
;
762 if (!(Ext
.isNumeric(disk
) && maxdisk
)) {
766 var per
= (disk
* 100) / maxdisk
;
768 return per
.toFixed(1) + '%';
771 render_resource_type: function(value
, metaData
, record
, rowIndex
, colIndex
, store
) {
773 var cls
= 'pve-itype-icon-' + value
;
775 if (record
.data
.running
) {
776 metaData
.tdCls
= cls
+ "-running";
777 } else if (record
.data
.template
) {
778 metaData
.tdCls
= cls
+ "-template";
780 metaData
.tdCls
= cls
;
786 render_uptime: function(value
, metaData
, record
, rowIndex
, colIndex
, store
) {
790 if (uptime
=== undefined) {
798 return PVE
.Utils
.format_duration_long(uptime
);
801 render_support_level: function(value
, metaData
, record
) {
802 return PVE
.Utils
.support_level_hash
[value
] || '-';
805 render_upid: function(value
, metaData
, record
) {
806 var type
= record
.data
.type
;
807 var id
= record
.data
.id
;
809 return PVE
.Utils
.format_task_description(type
, id
);
812 dialog_title: function(subject
, create
, isAdd
) {
815 return gettext('Add') + ': ' + subject
;
817 return gettext('Create') + ': ' + subject
;
820 return gettext('Edit') + ': ' + subject
;
824 openConoleWindow: function(vmtype
, vmid
, nodename
, vmname
) {
825 var url
= Ext
.urlEncode({
826 console
: vmtype
, // kvm, openvz or shell
831 var nw
= window
.open("?" + url
, '_blank',
832 "innerWidth=745,innerheight=427");
836 // comp.setLoading() is buggy in ExtJS 4.0.7, so we
837 // use el.mask() instead
838 setErrorMask: function(comp
, msg
) {
847 el
.mask(gettext("Loading..."));
854 monStoreErrors: function(me
, store
) {
855 me
.mon(store
, 'beforeload', function(s
, operation
, eOpts
) {
857 me
.loadCount
= 0; // make sure it is numeric
858 PVE
.Utils
.setErrorMask(me
, true);
862 // only works with 'pve' proxy
863 me
.mon(store
.proxy
, 'afterload', function(proxy
, request
, success
) {
867 PVE
.Utils
.setErrorMask(me
, false);
872 var operation
= request
.operation
;
873 var error
= operation
.getError();
874 if (error
.statusText
) {
875 msg
= error
.statusText
+ ' (' + error
.status
+ ')';
877 msg
= gettext('Connection error');
879 PVE
.Utils
.setErrorMask(me
, msg
);