]>
git.proxmox.com Git - pve-manager.git/blob - www/manager6/Utils.js
3 // avoid errors related to Accessible Rich Internet Applications
4 // (access for people with disabilities)
5 // TODO reenable after all components are upgraded
6 Ext
. enableAria
= false ;
7 Ext
. enableAriaButtons
= false ;
8 Ext
. enableAriaPanels
= false ;
10 // avoid errors when running without development tools
11 if (! Ext
. isDefined ( Ext
. global
. console
)) {
16 console
. log ( "Starting PVE Manager" );
18 Ext
. Ajax
. defaultHeaders
= {
19 'Accept' : 'application/json' ,
22 Ext
. define ( 'PVE.Utils' , {
25 // this singleton contains miscellaneous utilities
27 toolkit
: undefined , // (extjs|touch), set inside Toolkit.js
29 bus_match
: /^(ide|sata|virtio|scsi)\d+$/ ,
43 'c' : gettext ( 'Community' ),
44 'b' : gettext ( 'Basic' ),
45 's' : gettext ( 'Standard' ),
46 'p' : gettext ( 'Premium' ),
49 noSubKeyHtml
: 'You do not have a valid subscription for this server. Please visit <a target="_blank" href="https://www.proxmox.com/products/proxmox-ve/subscription-service-plans">www.proxmox.com</a> to get a list of available options.' ,
53 { desc
: '5.x - 2.6 Kernel' , val
: 'l26' },
54 { desc
: '2.4 Kernel' , val
: 'l24' },
56 'Microsoft Windows' : [
57 { desc
: '10/2016/2019' , val
: 'win10' },
58 { desc
: '8.x/2012/2012r2' , val
: 'win8' },
59 { desc
: '7/2008r2' , val
: 'win7' },
60 { desc
: 'Vista/2008' , val
: 'w2k8' },
61 { desc
: 'XP/2003' , val
: 'wxp' },
62 { desc
: '2000' , val
: 'w2k' },
65 { desc
: '-' , val
: 'solaris' },
68 { desc
: '-' , val
: 'other' },
72 is_windows : function ( ostype
) {
73 for ( let entry
of PVE
. Utils
. kvm_ostypes
[ 'Microsoft Windows' ]) {
74 if ( entry
. val
=== ostype
) {
81 get_health_icon : function ( state
, circle
) {
82 if ( circle
=== undefined ) {
86 if ( state
=== undefined ) {
90 var icon
= 'faded fa-question' ;
93 icon
= 'good fa-check' ;
96 icon
= 'warning fa-upload' ;
99 icon
= 'warning fa-refresh' ;
102 icon
= 'warning fa-exclamation' ;
105 icon
= 'critical fa-times' ;
117 parse_ceph_version : function ( service
) {
118 if ( service
. ceph_version_short
) {
119 return service
. ceph_version_short
;
122 if ( service
. ceph_version
) {
123 var match
= service
. ceph_version
. match ( /version (\d+(\.\d+)*)/ );
132 compare_ceph_versions : function ( a
, b
) {
140 if ( Ext
. isArray ( a
)) {
141 avers
= a
. slice (); // copy array
143 avers
= a
. toString (). split ( '.' );
146 if ( Ext
. isArray ( b
)) {
147 bvers
= b
. slice (); // copy array
149 bvers
= b
. toString (). split ( '.' );
153 let av
= avers
. shift ();
154 let bv
= bvers
. shift ();
156 if ( av
=== undefined && bv
=== undefined ) {
158 } else if ( av
=== undefined ) {
160 } else if ( bv
=== undefined ) {
163 let diff
= parseInt ( av
, 10 ) - parseInt ( bv
, 10 );
164 if ( diff
!= 0 ) return diff
;
165 // else we need to look at the next parts
170 get_ceph_icon_html : function ( health
, fw
) {
171 var state
= PVE
. Utils
. map_ceph_health
[ health
];
172 var cls
= PVE
. Utils
. get_health_icon ( state
);
176 return "<i class='fa " + cls
+ "'></i> " ;
181 'HEALTH_UPGRADE' : 'upgrade' ,
183 'HEALTH_WARN' : 'warning' ,
184 'HEALTH_ERR' : 'critical' ,
187 render_ceph_health : function ( healthObj
) {
189 iconCls
: PVE
. Utils
. get_health_icon (),
193 if (! healthObj
|| ! healthObj
. status
) {
197 var health
= PVE
. Utils
. map_ceph_health
[ healthObj
. status
];
199 state
. iconCls
= PVE
. Utils
. get_health_icon ( health
, true );
200 state
. text
= healthObj
. status
;
205 render_zfs_health : function ( value
) {
206 if ( typeof value
== 'undefined' ) {
209 var iconCls
= 'question-circle' ;
213 iconCls
= 'check-circle good' ;
217 iconCls
= 'exclamation-circle warning' ;
222 iconCls
= 'times-circle critical' ;
227 return '<i class="fa fa-' + iconCls
+ '"></i> ' + value
;
230 render_pbs_fingerprint
: fp
=> fp
. substring ( 0 , 23 ),
232 render_backup_encryption : function ( v
, meta
, record
) {
234 return gettext ( 'No' );
238 if ( v
. match ( /^[a-fA-F0-9]{2}:/ )) { // fingerprint
239 tip
= `Key fingerprint ${PVE.Utils.render_pbs_fingerprint(v)} ` ;
241 let icon
= `<i class="fa fa-fw fa-lock good"></i>` ;
242 return `<span data-qtip=" ${tip} "> ${icon} ${gettext('Encrypted')} </span>` ;
245 render_backup_verification : function ( v
, meta
, record
) {
246 let i
= ( cls
, txt
) => `<i class="fa fa-fw fa- ${cls} "></i> ${txt} ` ;
247 if ( v
=== undefined || v
=== null ) {
248 return i ( 'question-circle-o warning' , gettext ( 'None' ));
251 let txt
= gettext ( 'Failed' );
252 let iconCls
= 'times critical' ;
253 if ( v
. state
=== 'ok' ) {
255 iconCls
= 'check good' ;
256 let now
= Date
. now () / 1000 ;
257 let task
= Proxmox
. Utils
. parse_task_upid ( v
. upid
);
258 let verify_time
= Proxmox
. Utils
. render_timestamp ( task
. starttime
);
259 tip
= `Last verify task started on ${verify_time} ` ;
260 if ( now
- v
. starttime
> 30 * 24 * 60 * 60 ) {
261 tip
= `Last verify task over 30 days ago: ${verify_time} ` ;
262 iconCls
= 'check warning' ;
265 return `<span data-qtip=" ${tip} "> ${i(iconCls, txt)} </span>` ;
268 render_backup_status : function ( value
, meta
, record
) {
269 if ( typeof value
== 'undefined' ) {
273 let iconCls
= 'check-circle good' ;
274 let text
= gettext ( 'Yes' );
276 if (! PVE
. Parser
. parseBoolean ( value
. toString ())) {
277 iconCls
= 'times-circle critical' ;
279 text
= gettext ( 'No' );
281 let reason
= record
. get ( 'reason' );
282 if ( typeof reason
!== 'undefined' ) {
283 if ( reason
in PVE
. Utils
. backup_reasons_table
) {
284 reason
= PVE
. Utils
. backup_reasons_table
[ record
. get ( 'reason' )];
286 text
= ` ${text} - ${reason} ` ;
290 return `<i class="fa fa- ${iconCls} "></i> ${text} ` ;
293 render_backup_days_of_week : function ( val
) {
294 var dows
= [ 'sun' , 'mon' , 'tue' , 'wed' , 'thu' , 'fri' , 'sat' ];
297 val
. split ( ',' ). forEach ( function ( day
) {
299 var dow
= ( dows
. indexOf ( day
)+ 6 )% 7 ;
301 if ( selected
. length
=== 0 || selected
[ selected
. length
- 1 ] === 0 ) {
304 selected
[ selected
. length
- 1 ]++;
317 selected
. forEach ( function ( item
) {
320 days
. push ( Ext
. Date
. dayNames
[ cur
+ 1 ] + '-' + Ext
. Date
. dayNames
[( cur
+ item
)% 7 ]);
322 } else if ( item
== 2 ) {
323 days
. push ( Ext
. Date
. dayNames
[ cur
+ 1 ]);
324 days
. push ( Ext
. Date
. dayNames
[( cur
+ 2 )% 7 ]);
326 } else if ( item
== 1 ) {
327 days
. push ( Ext
. Date
. dayNames
[( cur
+ 1 )% 7 ]);
330 return days
. join ( ', ' );
333 render_backup_selection : function ( value
, metaData
, record
) {
334 let allExceptText
= gettext ( 'All except {0}' );
335 let allText
= '-- ' + gettext ( 'All' ) + ' --' ;
336 if ( record
. data
. all
) {
337 if ( record
. data
. exclude
) {
338 return Ext
. String
. format ( allExceptText
, record
. data
. exclude
);
342 if ( record
. data
. vmid
) {
343 return record
. data
. vmid
;
346 if ( record
. data
. pool
) {
347 return "Pool '" + record
. data
. pool
+ "'" ;
353 backup_reasons_table
: {
354 'backup=yes' : gettext ( 'Enabled' ),
355 'backup=no' : gettext ( 'Disabled' ),
356 'enabled' : gettext ( 'Enabled' ),
357 'disabled' : gettext ( 'Disabled' ),
358 'not a volume' : gettext ( 'Not a volume' ),
359 'efidisk but no OMVF BIOS' : gettext ( 'EFI Disk without OMVF BIOS' ),
362 get_kvm_osinfo : function ( value
) {
363 var info
= { base
: 'Other' }; // default
365 Ext
. each ( Object
. keys ( PVE
. Utils
. kvm_ostypes
), function ( k
) {
366 Ext
. each ( PVE
. Utils
. kvm_ostypes
[ k
], function ( e
) {
367 if ( e
. val
=== value
) {
368 info
= { desc
: e
. desc
, base
: k
};
376 render_kvm_ostype : function ( value
) {
377 var osinfo
= PVE
. Utils
. get_kvm_osinfo ( value
);
378 if ( osinfo
. desc
&& osinfo
. desc
!== '-' ) {
379 return osinfo
. base
+ ' ' + osinfo
. desc
;
385 render_hotplug_features : function ( value
) {
388 if (! value
|| value
=== '0' ) {
389 return gettext ( 'Disabled' );
393 value
= 'disk,network,usb' ;
396 Ext
. each ( value
. split ( ',' ), function ( el
) {
398 fa
. push ( gettext ( 'Disk' ));
399 } else if ( el
=== 'network' ) {
400 fa
. push ( gettext ( 'Network' ));
401 } else if ( el
=== 'usb' ) {
403 } else if ( el
=== 'memory' ) {
404 fa
. push ( gettext ( 'Memory' ));
405 } else if ( el
=== 'cpu' ) {
406 fa
. push ( gettext ( 'CPU' ));
412 return fa
. join ( ', ' );
415 render_localtime : function ( value
) {
416 if ( value
=== '__default__' ) {
417 return Proxmox
. Utils
. defaultText
+ ' (' + gettext ( 'Enabled for Windows' ) + ')' ;
419 return Proxmox
. Utils
. format_boolean ( value
);
422 render_qga_features : function ( value
) {
424 return Proxmox
. Utils
. defaultText
+ ' (' + Proxmox
. Utils
. disabledText
+ ')' ;
426 var props
= PVE
. Parser
. parsePropertyString ( value
, 'enabled' );
427 if (! PVE
. Parser
. parseBoolean ( props
. enabled
)) {
428 return Proxmox
. Utils
. disabledText
;
431 delete props
. enabled
;
432 var agentstring
= Proxmox
. Utils
. enabledText
;
434 Ext
. Object
. each ( props
, function ( key
, value
) {
436 agentstring
+= ', ' + key
+ ': ' ;
438 if ( key
=== 'type' ) {
443 agentstring
+= map
[ value
] || Proxmox
. Utils
. unknownText
;
445 if ( PVE
. Parser
. parseBoolean ( value
)) {
446 agentstring
+= Proxmox
. Utils
. enabledText
;
448 agentstring
+= Proxmox
. Utils
. disabledText
;
456 render_qemu_machine : function ( value
) {
457 return value
|| Proxmox
. Utils
. defaultText
+ ' (i440fx)' ;
460 render_qemu_bios : function ( value
) {
462 return Proxmox
. Utils
. defaultText
+ ' (SeaBIOS)' ;
463 } else if ( value
=== 'seabios' ) {
465 } else if ( value
=== 'ovmf' ) {
466 return "OVMF (UEFI)" ;
472 render_dc_ha_opts : function ( value
) {
474 return Proxmox
. Utils
. defaultText
;
476 return PVE
. Parser
. printPropertyString ( value
);
479 render_as_property_string : function ( value
) {
480 return ! value
? Proxmox
. Utils
. defaultText
481 : PVE
. Parser
. printPropertyString ( value
);
484 render_scsihw : function ( value
) {
486 return Proxmox
. Utils
. defaultText
+ ' (LSI 53C895A)' ;
487 } else if ( value
=== 'lsi' ) {
488 return 'LSI 53C895A' ;
489 } else if ( value
=== 'lsi53c810' ) {
491 } else if ( value
=== 'megasas' ) {
492 return 'MegaRAID SAS 8708EM2' ;
493 } else if ( value
=== 'virtio-scsi-pci' ) {
494 return 'VirtIO SCSI' ;
495 } else if ( value
=== 'virtio-scsi-single' ) {
496 return 'VirtIO SCSI single' ;
497 } else if ( value
=== 'pvscsi' ) {
498 return 'VMware PVSCSI' ;
504 render_spice_enhancements : function ( values
) {
505 let props
= PVE
. Parser
. parsePropertyString ( values
);
506 if ( Ext
. Object
. isEmpty ( props
)) {
507 return Proxmox
. Utils
. noneText
;
511 if ( PVE
. Parser
. parseBoolean ( props
. foldersharing
)) {
512 output
. push ( 'Folder Sharing: ' + gettext ( 'Enabled' ));
514 if ( props
. videostreaming
=== 'all' || props
. videostreaming
=== 'filter' ) {
515 output
. push ( 'Video Streaming: ' + props
. videostreaming
);
517 return output
. join ( ', ' );
520 // fixme: auto-generate this
521 // for now, please keep in sync with PVE::Tools::kvmkeymaps
526 'de-ch' : 'German (Swiss)' ,
527 'en-gb' : 'English (UK)' ,
528 'en-us' : 'English (USA)' ,
532 //fo: 'Faroe Islands',
534 'fr-be' : 'French (Belgium)' ,
535 'fr-ca' : 'French (Canada)' ,
536 'fr-ch' : 'French (Swiss)' ,
546 //'nl-be': 'Dutch (Belgium)',
550 'pt-br' : 'Portuguese (Brazil)' ,
559 std
: gettext ( 'Standard VGA' ),
560 vmware
: gettext ( 'VMware compatible' ),
562 qxl2
: 'SPICE dual monitor' ,
563 qxl3
: 'SPICE three monitors' ,
564 qxl4
: 'SPICE four monitors' ,
565 serial0
: gettext ( 'Serial terminal' ) + ' 0' ,
566 serial1
: gettext ( 'Serial terminal' ) + ' 1' ,
567 serial2
: gettext ( 'Serial terminal' ) + ' 2' ,
568 serial3
: gettext ( 'Serial terminal' ) + ' 3' ,
569 virtio
: 'VirtIO-GPU' ,
570 none
: Proxmox
. Utils
. noneText
,
573 render_kvm_language : function ( value
) {
574 if (! value
|| value
=== '__default__' ) {
575 return Proxmox
. Utils
. defaultText
;
577 var text
= PVE
. Utils
. kvm_keymaps
[ value
];
579 return text
+ ' (' + value
+ ')' ;
584 kvm_keymap_array : function () {
585 var data
= [[ '__default__' , PVE
. Utils
. render_kvm_language ( '' )]];
586 Ext
. Object
. each ( PVE
. Utils
. kvm_keymaps
, function ( key
, value
) {
587 data
. push ([ key
, PVE
. Utils
. render_kvm_language ( value
)]);
594 '__default__' : Proxmox
. Utils
. defaultText
+ ' (xterm.js)' ,
595 'vv' : 'SPICE (remote-viewer)' ,
596 'html5' : 'HTML5 (noVNC)' ,
597 'xtermjs' : 'xterm.js' ,
600 render_console_viewer : function ( value
) {
601 value
= value
|| '__default__' ;
602 if ( PVE
. Utils
. console_map
[ value
]) {
603 return PVE
. Utils
. console_map
[ value
];
608 console_viewer_array : function () {
609 return Ext
. Array
. map ( Object
. keys ( PVE
. Utils
. console_map
), function ( v
) {
610 return [ v
, PVE
. Utils
. render_console_viewer ( v
)];
614 render_kvm_vga_driver : function ( value
) {
616 return Proxmox
. Utils
. defaultText
;
618 var vga
= PVE
. Parser
. parsePropertyString ( value
, 'type' );
619 var text
= PVE
. Utils
. kvm_vga_drivers
[ vga
. type
];
621 text
= Proxmox
. Utils
. defaultText
;
624 return text
+ ' (' + value
+ ')' ;
629 kvm_vga_driver_array : function () {
630 var data
= [[ '__default__' , PVE
. Utils
. render_kvm_vga_driver ( '' )]];
631 Ext
. Object
. each ( PVE
. Utils
. kvm_vga_drivers
, function ( key
, value
) {
632 data
. push ([ key
, PVE
. Utils
. render_kvm_vga_driver ( value
)]);
638 render_kvm_startup : function ( value
) {
639 var startup
= PVE
. Parser
. parseStartup ( value
);
642 if ( startup
. order
=== undefined ) {
645 res
+= startup
. order
;
647 if ( startup
. up
!== undefined ) {
648 res
+= ',up=' + startup
. up
;
650 if ( startup
. down
!== undefined ) {
651 res
+= ',down=' + startup
. down
;
657 extractFormActionError : function ( action
) {
659 switch ( action
. failureType
) {
660 case Ext
. form
. action
. Action
. CLIENT_INVALID
:
661 msg
= gettext ( 'Form fields may not be submitted with invalid values' );
663 case Ext
. form
. action
. Action
. CONNECT_FAILURE
:
664 msg
= gettext ( 'Connection error' );
665 var resp
= action
. response
;
666 if ( resp
. status
&& resp
. statusText
) {
667 msg
+= " " + resp
. status
+ ": " + resp
. statusText
;
670 case Ext
. form
. action
. Action
. LOAD_FAILURE
:
671 case Ext
. form
. action
. Action
. SERVER_INVALID
:
672 msg
= Proxmox
. Utils
. extractRequestError ( action
. result
, true );
679 'images' : gettext ( 'Disk image' ),
680 'backup' : gettext ( 'VZDump backup file' ),
681 'vztmpl' : gettext ( 'Container template' ),
682 'iso' : gettext ( 'ISO image' ),
683 'rootdir' : gettext ( 'Container' ),
684 'snippets' : gettext ( 'Snippets' ),
687 volume_is_qemu_backup : function ( volid
, format
) {
688 return format
=== 'pbs-vm' || volid
. match ( ':backup/vzdump-qemu-' );
691 volume_is_lxc_backup : function ( volid
, format
) {
692 return format
=== 'pbs-ct' || volid
. match ( ':backup/vzdump-(lxc|openvz)-' );
697 name
: gettext ( 'Active Directory Server' ),
698 ipanel
: 'pveAuthADPanel' ,
699 syncipanel
: 'pveAuthLDAPSyncPanel' ,
703 name
: gettext ( 'LDAP Server' ),
704 ipanel
: 'pveAuthLDAPPanel' ,
705 syncipanel
: 'pveAuthLDAPSyncPanel' ,
710 ipanel
: 'pveAuthBasePanel' ,
714 name
: 'Proxmox VE authentication server' ,
715 ipanel
: 'pveAuthBasePanel' ,
722 name
: Proxmox
. Utils
. directoryText
,
723 ipanel
: 'DirInputPanel' ,
729 ipanel
: 'LVMInputPanel' ,
735 ipanel
: 'LvmThinInputPanel' ,
741 ipanel
: 'NFSInputPanel' ,
747 ipanel
: 'CIFSInputPanel' ,
753 ipanel
: 'GlusterFsInputPanel' ,
759 ipanel
: 'IScsiInputPanel' ,
765 ipanel
: 'CephFSInputPanel' ,
770 name
: 'CephFS (PVE)' ,
771 ipanel
: 'CephFSInputPanel' ,
778 ipanel
: 'RBDInputPanel' ,
784 ipanel
: 'RBDInputPanel' ,
790 name
: 'ZFS over iSCSI' ,
791 ipanel
: 'ZFSInputPanel' ,
797 ipanel
: 'ZFSPoolInputPanel' ,
802 name
: 'Proxmox Backup Server' ,
803 ipanel
: 'PBSInputPanel' ,
828 ipanel
: 'SimpleInputPanel' ,
833 ipanel
: 'VlanInputPanel' ,
838 ipanel
: 'QinQInputPanel' ,
843 ipanel
: 'VxlanInputPanel' ,
848 ipanel
: 'EvpnInputPanel' ,
853 sdncontrollerSchema
: {
860 ipanel
: 'EvpnInputPanel' ,
861 faIcon
: 'crosshairs' ,
865 format_sdnvnet_type : function ( value
, md
, record
) {
866 var schema
= PVE
. Utils
. sdnvnetSchema
[ value
];
870 return Proxmox
. Utils
. unknownText
;
873 format_sdnzone_type : function ( value
, md
, record
) {
874 var schema
= PVE
. Utils
. sdnzoneSchema
[ value
];
878 return Proxmox
. Utils
. unknownText
;
881 format_sdncontroller_type : function ( value
, md
, record
) {
882 var schema
= PVE
. Utils
. sdncontrollerSchema
[ value
];
886 return Proxmox
. Utils
. unknownText
;
889 format_storage_type : function ( value
, md
, record
) {
890 if ( value
=== 'rbd' ) {
891 value
= ! record
|| record
. get ( 'monhost' ) ? 'rbd' : 'pveceph' ;
892 } else if ( value
=== 'cephfs' ) {
893 value
= ! record
|| record
. get ( 'monhost' ) ? 'cephfs' : 'pvecephfs' ;
896 var schema
= PVE
. Utils
. storageSchema
[ value
];
900 return Proxmox
. Utils
. unknownText
;
903 format_ha : function ( value
) {
904 var text
= Proxmox
. Utils
. noneText
;
907 text
= value
. state
|| Proxmox
. Utils
. noneText
;
909 text
+= ', ' + Proxmox
. Utils
. groupText
+ ': ' ;
910 text
+= value
. group
|| Proxmox
. Utils
. noneText
;
916 format_content_types : function ( value
) {
917 return value
. split ( ',' ). sort (). map ( function ( ct
) {
918 return PVE
. Utils
. contentTypes
[ ct
] || ct
;
922 render_storage_content : function ( value
, metaData
, record
) {
923 var data
= record
. data
;
924 if ( Ext
. isNumber ( data
. channel
) &&
925 Ext
. isNumber ( data
. id
) &&
926 Ext
. isNumber ( data
. lun
)) {
928 Ext
. String
. leftPad ( data
. channel
, 2 , '0' ) +
929 " ID " + data
. id
+ " LUN " + data
. lun
;
931 return data
. volid
. replace ( /^.*?:(.*?\/)?/ , '' );
934 render_serverity : function ( value
) {
935 return PVE
. Utils
. log_severity_hash
[ value
] || value
;
938 render_cpu : function ( value
, metaData
, record
, rowIndex
, colIndex
, store
) {
939 if (!( record
. data
. uptime
&& Ext
. isNumeric ( value
))) {
943 var maxcpu
= record
. data
. maxcpu
|| 1 ;
945 if (! Ext
. isNumeric ( maxcpu
) && maxcpu
>= 1 ) {
949 var per
= value
* 100 ;
951 return per
. toFixed ( 1 ) + '% of ' + maxcpu
. toString () + ( maxcpu
> 1 ? 'CPUs' : 'CPU' );
954 calculate_hostcpu : function ( data
) {
956 if (!( data
. uptime
&& Ext
. isNumeric ( data
. cpu
))) {
960 if ( data
. type
!== 'qemu' && data
. type
!== 'lxc' ) {
964 var index
= PVE
. data
. ResourceStore
. findExact ( 'id' , 'node/' + data
. node
);
965 var node
= PVE
. data
. ResourceStore
. getAt ( index
);
966 if (! Ext
. isDefined ( node
) || node
=== null ) {
969 var maxcpu
= node
. data
. maxcpu
|| 1 ;
971 if (! Ext
. isNumeric ( maxcpu
) && ( maxcpu
>= 1 )) {
975 return (( data
. cpu
/ maxcpu
) * data
. maxcpu
);
978 render_hostcpu : function ( value
, metaData
, record
, rowIndex
, colIndex
, store
) {
980 if (!( record
. data
. uptime
&& Ext
. isNumeric ( record
. data
. cpu
))) {
984 if ( record
. data
. type
!== 'qemu' && record
. data
. type
!== 'lxc' ) {
988 var index
= PVE
. data
. ResourceStore
. findExact ( 'id' , 'node/' + record
. data
. node
);
989 var node
= PVE
. data
. ResourceStore
. getAt ( index
);
990 if (! Ext
. isDefined ( node
) || node
=== null ) {
993 var maxcpu
= node
. data
. maxcpu
|| 1 ;
995 if (! Ext
. isNumeric ( maxcpu
) && ( maxcpu
>= 1 )) {
999 var per
= ( record
. data
. cpu
/ maxcpu
) * record
. data
. maxcpu
* 100 ;
1001 return per
. toFixed ( 1 ) + '% of ' + maxcpu
. toString () + ( maxcpu
> 1 ? 'CPUs' : 'CPU' );
1004 render_size : function ( value
, metaData
, record
, rowIndex
, colIndex
, store
) {
1005 if (! Ext
. isNumeric ( value
)) {
1009 return Proxmox
. Utils
. format_size ( value
);
1012 render_bandwidth : function ( value
) {
1013 if (! Ext
. isNumeric ( value
)) {
1017 return Proxmox
. Utils
. format_size ( value
) + '/s' ;
1020 render_timestamp_human_readable : function ( value
) {
1021 return Ext
. Date
. format ( new Date ( value
* 1000 ), 'l d F Y H:i:s' );
1024 calculate_mem_usage : function ( data
) {
1025 if (! Ext
. isNumeric ( data
. mem
) ||
1026 data
. maxmem
=== 0 ||
1031 return data
. mem
/ data
. maxmem
;
1034 calculate_hostmem_usage : function ( data
) {
1036 if ( data
. type
!== 'qemu' && data
. type
!== 'lxc' ) {
1040 var index
= PVE
. data
. ResourceStore
. findExact ( 'id' , 'node/' + data
. node
);
1041 var node
= PVE
. data
. ResourceStore
. getAt ( index
);
1043 if (! Ext
. isDefined ( node
) || node
=== null ) {
1046 var maxmem
= node
. data
. maxmem
|| 0 ;
1048 if (! Ext
. isNumeric ( data
. mem
) ||
1054 return ( data
. mem
/ maxmem
);
1057 render_mem_usage_percent : function ( value
, metaData
, record
, rowIndex
, colIndex
, store
) {
1058 if (! Ext
. isNumeric ( value
) || value
=== - 1 ) {
1062 // we got no percentage but bytes
1064 var maxmem
= record
. data
. maxmem
;
1065 if (! record
. data
. uptime
||
1067 ! Ext
. isNumeric ( mem
)) {
1071 return ( mem
* 100 / maxmem
). toFixed ( 1 ) + " %" ;
1073 return ( value
* 100 ). toFixed ( 1 ) + " %" ;
1076 render_hostmem_usage_percent : function ( value
, metaData
, record
, rowIndex
, colIndex
, store
) {
1078 if (! Ext
. isNumeric ( record
. data
. mem
) || value
=== - 1 ) {
1082 if ( record
. data
. type
!== 'qemu' && record
. data
. type
!== 'lxc' ) {
1086 var index
= PVE
. data
. ResourceStore
. findExact ( 'id' , 'node/' + record
. data
. node
);
1087 var node
= PVE
. data
. ResourceStore
. getAt ( index
);
1088 var maxmem
= node
. data
. maxmem
|| 0 ;
1090 if ( record
. data
. mem
> 1 ) {
1091 // we got no percentage but bytes
1092 var mem
= record
. data
. mem
;
1093 if (! record
. data
. uptime
||
1095 ! Ext
. isNumeric ( mem
)) {
1099 return (( mem
* 100 )/ maxmem
). toFixed ( 1 ) + " %" ;
1101 return ( value
* 100 ). toFixed ( 1 ) + " %" ;
1104 render_mem_usage : function ( value
, metaData
, record
, rowIndex
, colIndex
, store
) {
1106 var maxmem
= record
. data
. maxmem
;
1108 if (! record
. data
. uptime
) {
1112 if (!( Ext
. isNumeric ( mem
) && maxmem
)) {
1116 return PVE
. Utils
. render_size ( value
);
1119 calculate_disk_usage : function ( data
) {
1120 if (! Ext
. isNumeric ( data
. disk
) ||
1121 data
. type
=== 'qemu' ||
1122 data
. type
=== 'lxc' && data
. uptime
=== 0 ||
1123 data
. maxdisk
=== 0 ) {
1127 return data
. disk
/ data
. maxdisk
;
1130 render_disk_usage_percent : function ( value
, metaData
, record
, rowIndex
, colIndex
, store
) {
1131 if (! Ext
. isNumeric ( value
) || value
=== - 1 ) {
1135 return ( value
* 100 ). toFixed ( 1 ) + " %" ;
1138 render_disk_usage : function ( value
, metaData
, record
, rowIndex
, colIndex
, store
) {
1140 var maxdisk
= record
. data
. maxdisk
;
1141 var type
= record
. data
. type
;
1143 if (! Ext
. isNumeric ( disk
) ||
1146 type
=== 'lxc' && record
. data
. uptime
=== 0 ) {
1150 return PVE
. Utils
. render_size ( value
);
1153 get_object_icon_class : function ( type
, record
) {
1157 if ( type
=== 'type' ) {
1159 objType
= record
. groupbyid
;
1160 } else if ( record
. template
) {
1162 objType
= 'template' ;
1166 status
= record
. status
+ ' ha-' + record
. hastate
;
1170 status
+= ' locked lock-' + record
. lock
;
1173 var defaults
= PVE
. tree
. ResourceTree
. typeDefaults
[ objType
];
1174 if ( defaults
&& defaults
. iconCls
) {
1175 var retVal
= defaults
. iconCls
+ ' ' + status
;
1182 render_resource_type : function ( value
, metaData
, record
, rowIndex
, colIndex
, store
) {
1183 var cls
= PVE
. Utils
. get_object_icon_class ( value
, record
. data
);
1185 var fa
= '<i class="fa-fw x-grid-icon-custom ' + cls
+ '"></i> ' ;
1189 render_support_level : function ( value
, metaData
, record
) {
1190 return PVE
. Utils
. support_level_hash
[ value
] || '-' ;
1193 render_upid : function ( value
, metaData
, record
) {
1194 var type
= record
. data
. type
;
1195 var id
= record
. data
. id
;
1197 return Proxmox
. Utils
. format_task_description ( type
, id
);
1200 /* render functions for new status panel */
1202 render_usage : function ( val
) {
1203 return ( val
* 100 ). toFixed ( 2 ) + '%' ;
1206 render_cpu_usage : function ( val
, max
) {
1207 return Ext
. String
. format ( gettext ( '{0}% of {1}' ) +
1208 ' ' + gettext ( 'CPU(s)' ), ( val
* 100 ). toFixed ( 2 ), max
);
1211 render_size_usage : function ( val
, max
) {
1213 return gettext ( 'N/A' );
1215 return ( val
* 100 / max
). toFixed ( 2 ) + '% ' + '(' +
1216 Ext
. String
. format ( gettext ( '{0} of {1}' ),
1217 PVE
. Utils
. render_size ( val
), PVE
. Utils
. render_size ( max
)) + ')' ;
1220 /* this is different for nodes */
1221 render_node_cpu_usage : function ( value
, record
) {
1222 return PVE
. Utils
. render_cpu_usage ( value
, record
. cpus
);
1225 /* this is different for nodes */
1226 render_node_size_usage : function ( record
) {
1227 return PVE
. Utils
. render_size_usage ( record
. used
, record
. total
);
1230 render_optional_url : function ( value
) {
1232 if ( value
&& ( match
= value
. match ( /^https?:\/\// )) !== null ) {
1233 return '<a target="_blank" href="' + value
+ '">' + value
+ '</a>' ;
1238 render_san : function ( value
) {
1240 if ( Ext
. isArray ( value
)) {
1241 value
. forEach ( function ( val
) {
1242 if (! Ext
. isNumber ( val
)) {
1246 return names
. join ( '<br>' );
1251 render_full_name : function ( firstname
, metaData
, record
) {
1252 var first
= firstname
|| '' ;
1253 var last
= record
. data
. lastname
|| '' ;
1254 return Ext
. htmlEncode ( first
+ " " + last
);
1257 render_u2f_error : function ( error
) {
1259 '1' : gettext ( 'Other Error' ),
1260 '2' : gettext ( 'Bad Request' ),
1261 '3' : gettext ( 'Configuration Unsupported' ),
1262 '4' : gettext ( 'Device Ineligible' ),
1263 '5' : gettext ( 'Timeout' ),
1265 return "U2F Error: " + ErrorNames
[ error
] || Proxmox
. Utils
. unknownText
;
1268 windowHostname : function () {
1269 return window
. location
. hostname
. replace ( Proxmox
. Utils
. IP6_bracket_match
,
1270 function ( m
, addr
, offset
, original
) { return addr
; });
1273 openDefaultConsoleWindow : function ( consoles
, consoleType
, vmid
, nodename
, vmname
, cmd
) {
1274 var dv
= PVE
. Utils
. defaultViewer ( consoles
);
1275 PVE
. Utils
. openConsoleWindow ( dv
, consoleType
, vmid
, nodename
, vmname
, cmd
);
1278 openConsoleWindow : function ( viewer
, consoleType
, vmid
, nodename
, vmname
, cmd
) {
1279 if ( vmid
== undefined && ( consoleType
=== 'kvm' || consoleType
=== 'lxc' )) {
1280 throw "missing vmid" ;
1283 throw "no nodename specified" ;
1286 if ( viewer
=== 'html5' ) {
1287 PVE
. Utils
. openVNCViewer ( consoleType
, vmid
, nodename
, vmname
, cmd
);
1288 } else if ( viewer
=== 'xtermjs' ) {
1289 Proxmox
. Utils
. openXtermJsViewer ( consoleType
, vmid
, nodename
, vmname
, cmd
);
1290 } else if ( viewer
=== 'vv' ) {
1291 let url
= '/nodes/' + nodename
+ '/spiceshell' ;
1293 proxy
: PVE
. Utils
. windowHostname (),
1295 if ( consoleType
=== 'kvm' ) {
1296 url
= '/nodes/' + nodename
+ '/qemu/' + vmid
. toString () + '/spiceproxy' ;
1297 } else if ( consoleType
=== 'lxc' ) {
1298 url
= '/nodes/' + nodename
+ '/lxc/' + vmid
. toString () + '/spiceproxy' ;
1299 } else if ( consoleType
=== 'upgrade' ) {
1300 params
. cmd
= 'upgrade' ;
1301 } else if ( consoleType
=== 'cmd' ) {
1303 } else if ( consoleType
!== 'shell' ) {
1304 throw `unknown spice viewer type ' ${consoleType} '` ;
1306 PVE
. Utils
. openSpiceViewer ( url
, params
);
1308 throw `unknown viewer type ' ${viewer} '` ;
1312 defaultViewer : function ( consoles
) {
1313 var allowSpice
, allowXtermjs
;
1315 if ( consoles
=== true ) {
1317 allowXtermjs
= true ;
1318 } else if ( typeof consoles
=== 'object' ) {
1319 allowSpice
= consoles
. spice
;
1320 allowXtermjs
= !! consoles
. xtermjs
;
1322 var dv
= PVE
. VersionInfo
. console
|| 'xtermjs' ;
1323 if ( dv
=== 'vv' && ! allowSpice
) {
1324 dv
= allowXtermjs
? 'xtermjs' : 'html5' ;
1325 } else if ( dv
=== 'xtermjs' && ! allowXtermjs
) {
1326 dv
= allowSpice
? 'vv' : 'html5' ;
1332 openVNCViewer : function ( vmtype
, vmid
, nodename
, vmname
, cmd
) {
1333 let scaling
= 'off' ;
1334 if ( Proxmox
. Utils
. toolkit
!== 'touch' ) {
1335 var sp
= Ext
. state
. Manager
. getProvider ();
1336 scaling
= sp
. get ( 'novnc-scaling' , 'off' );
1338 var url
= Ext
. Object
. toQueryString ({
1339 console
: vmtype
, // kvm, lxc, upgrade or shell
1347 var nw
= window
. open ( "?" + url
, '_blank' , "innerWidth=745,innerheight=427" );
1353 openSpiceViewer : function ( url
, params
) {
1354 var downloadWithName = function ( uri
, name
) {
1355 var link
= Ext
. DomHelper
. append ( document
. body
, {
1358 css
: 'display:none;visibility:hidden;height:0px;' ,
1361 // Note: we need to tell android the correct file name extension
1362 // but we do not set 'download' tag for other environments, because
1363 // It can have strange side effects (additional user prompt on firefox)
1364 var andriod
= !! navigator
. userAgent
. match ( /Android/i );
1366 link
. download
= name
;
1369 if ( link
. fireEvent
) {
1370 link
. fireEvent ( 'onclick' );
1372 let evt
= document
. createEvent ( "MouseEvents" );
1373 evt
. initMouseEvent ( 'click' , true , true , window
, 1 , 0 , 0 , 0 , 0 , false , false , false , false , 0 , null );
1374 link
. dispatchEvent ( evt
);
1378 Proxmox
. Utils
. API2Request ({
1382 failure : function ( response
, opts
) {
1383 Ext
. Msg
. alert ( 'Error' , response
. htmlStatus
);
1385 success : function ( response
, opts
) {
1386 var raw
= "[virt-viewer] \n " ;
1387 Ext
. Object
. each ( response
. result
. data
, function ( k
, v
) {
1388 raw
+= k
+ "=" + v
+ " \n " ;
1390 var url
= 'data:application/x-virt-viewer;charset=UTF-8,' +
1391 encodeURIComponent ( raw
);
1393 downloadWithName ( url
, "pve-spice.vv" );
1398 openTreeConsole : function ( tree
, record
, item
, index
, e
) {
1400 var nodename
= record
. data
. node
;
1401 var vmid
= record
. data
. vmid
;
1402 var vmname
= record
. data
. name
;
1403 if ( record
. data
. type
=== 'qemu' && ! record
. data
. template
) {
1404 Proxmox
. Utils
. API2Request ({
1405 url
: '/nodes/' + nodename
+ '/qemu/' + vmid
+ '/status/current' ,
1406 failure : function ( response
, opts
) {
1407 Ext
. Msg
. alert ( 'Error' , response
. htmlStatus
);
1409 success : function ( response
, opts
) {
1410 let conf
= response
. result
. data
;
1412 spice
: !! conf
. spice
,
1413 xtermjs
: !! conf
. serial
,
1415 PVE
. Utils
. openDefaultConsoleWindow ( consoles
, 'kvm' , vmid
, nodename
, vmname
);
1418 } else if ( record
. data
. type
=== 'lxc' && ! record
. data
. template
) {
1419 PVE
. Utils
. openDefaultConsoleWindow ( true , 'lxc' , vmid
, nodename
, vmname
);
1423 // test automation helper
1424 call_menu_handler : function ( menu
, text
) {
1425 var list
= menu
. query ( 'menuitem' );
1427 Ext
. Array
. each ( list
, function ( item
) {
1428 if ( item
. text
=== text
) {
1439 createCmdMenu : function ( v
, record
, item
, index
, event
) {
1441 if (!( v
instanceof Ext
. tree
. View
)) {
1445 var template
= !! record
. data
. template
;
1446 var type
= record
. data
. type
;
1449 if ( type
=== 'qemu' || type
== 'lxc' ) {
1450 menu
= Ext
. create ( 'PVE.menu.TemplateMenu' , {
1454 } else if ( type
=== 'qemu' ||
1457 menu
= Ext
. create ( 'PVE.' + type
+ '.CmdMenu' , {
1459 nodename
: record
. data
. node
,
1465 menu
. showAt ( event
. getXY ());
1469 // helper for deleting field which are set to there default values
1470 delete_if_default : function ( values
, fieldname
, default_val
, create
) {
1471 if ( values
[ fieldname
] === '' || values
[ fieldname
] === default_val
) {
1473 if ( values
. delete ) {
1474 if ( Ext
. isArray ( values
. delete )) {
1475 values
. delete . push ( fieldname
);
1477 values
. delete += ',' + fieldname
;
1480 values
. delete = fieldname
;
1484 delete values
[ fieldname
];
1488 loadSSHKeyFromFile : function ( file
, callback
) {
1489 // ssh-keygen produces 740 bytes for an average 4096 bit rsa key, with
1490 // a user@host comment, 1420 for 8192 bits; current max is 16kbit
1491 // assume: 740*8 for max. 32kbit (5920 byte file)
1492 // round upwards to nearest nice number => 8192 bytes, leaves lots of comment space
1493 if ( file
. size
> 8192 ) {
1494 Ext
. Msg
. alert ( gettext ( 'Error' ), gettext ( "Invalid file size: " ) + file
. size
);
1500 var reader
= new FileReader ();
1501 reader
. onload = function ( evt
) {
1502 callback ( evt
. target
. result
);
1504 reader
. readAsText ( file
);
1507 loadTextFromFile : function ( file
, callback
, maxBytes
) {
1508 let maxSize
= maxBytes
|| 8192 ;
1509 if ( file
. size
> maxSize
) {
1510 Ext
. Msg
. alert ( gettext ( 'Error' ), gettext ( "Invalid file size: " ) + file
. size
);
1516 let reader
= new FileReader ();
1517 reader
. onload
= evt
=> callback ( evt
. target
. result
);
1518 reader
. readAsText ( file
);
1521 diskControllerMaxIDs
: {
1528 // types is either undefined (all busses), an array of busses, or a single bus
1529 forEachBus : function ( types
, func
) {
1530 var busses
= Object
. keys ( PVE
. Utils
. diskControllerMaxIDs
);
1531 var i
, j
, count
, cont
;
1533 if ( Ext
. isArray ( types
)) {
1535 } else if ( Ext
. isDefined ( types
)) {
1539 // check if we only have valid busses
1540 for ( i
= 0 ; i
< busses
. length
; i
++) {
1541 if (! PVE
. Utils
. diskControllerMaxIDs
[ busses
[ i
]]) {
1542 throw "invalid bus: '" + busses
[ i
] + "'" ;
1546 for ( i
= 0 ; i
< busses
. length
; i
++) {
1547 count
= PVE
. Utils
. diskControllerMaxIDs
[ busses
[ i
]];
1548 for ( j
= 0 ; j
< count
; j
++) {
1549 cont
= func ( busses
[ i
], j
);
1550 if (! cont
&& cont
!== undefined ) {
1557 mp_counts
: { mps
: 256 , unused
: 256 },
1559 forEachMP : function ( func
, includeUnused
) {
1561 for ( i
= 0 ; i
< PVE
. Utils
. mp_counts
. mps
; i
++) {
1562 cont
= func ( 'mp' , i
);
1563 if (! cont
&& cont
!== undefined ) {
1568 if (! includeUnused
) {
1572 for ( i
= 0 ; i
< PVE
. Utils
. mp_counts
. unused
; i
++) {
1573 cont
= func ( 'unused' , i
);
1574 if (! cont
&& cont
!== undefined ) {
1580 hardware_counts
: { net
: 32 , usb
: 5 , hostpci
: 16 , audio
: 1 , efidisk
: 1 , serial
: 4 , rng
: 1 },
1582 cleanEmptyObjectKeys : function ( obj
) {
1584 for ( propName
in obj
) {
1585 if ( obj
. hasOwnProperty ( propName
)) {
1586 if ( obj
[ propName
] === null || obj
[ propName
] === undefined ) {
1587 delete obj
[ propName
];
1593 acmedomain_count
: 5 ,
1595 add_domain_to_acme : function ( acme
, domain
) {
1596 if ( acme
. domains
=== undefined ) {
1597 acme
. domains
= [ domain
];
1599 acme
. domains
. push ( domain
);
1600 acme
. domains
= acme
. domains
. filter (( value
, index
, self
) => self
. indexOf ( value
) === index
);
1605 remove_domain_from_acme : function ( acme
, domain
) {
1606 if ( acme
. domains
!== undefined ) {
1607 acme
. domains
= acme
. domains
. filter (( value
, index
, self
) => self
. indexOf ( value
) === index
&& value
!== domain
);
1612 handleStoreErrorOrMask : function ( me
, store
, regex
, callback
) {
1613 me
. mon ( store
, 'load' , function ( proxy
, response
, success
, operation
) {
1615 Proxmox
. Utils
. setErrorMask ( me
, false );
1620 if ( operation
. error
. statusText
) {
1621 if ( operation
. error
. statusText
. match ( regex
)) {
1622 callback ( me
, operation
. error
);
1625 msg
= operation
. error
. statusText
+ ' (' + operation
. error
. status
+ ')' ;
1628 msg
= gettext ( 'Connection error' );
1630 Proxmox
. Utils
. setErrorMask ( me
, msg
);
1634 showCephInstallOrMask : function ( container
, msg
, nodename
, callback
) {
1635 var regex
= new RegExp ( "not (installed|initialized)" , "i" );
1636 if ( msg
. match ( regex
)) {
1637 if ( Proxmox
. UserName
=== 'root@pam' ) {
1638 container
. el
. mask ();
1639 if (! container
. down ( 'pveCephInstallWindow' )) {
1640 var isInstalled
= !! msg
. match ( /not initialized/i );
1641 var win
= Ext
. create ( 'PVE.ceph.Install' , {
1644 win
. getViewModel (). set ( 'isInstalled' , isInstalled
);
1650 container
. mask ( Ext
. String
. format ( gettext ( '{0} not installed.' ) +
1651 ' ' + gettext ( 'Log in as root to install.' ), 'Ceph' ), [ 'pve-static-mask' ]);
1659 propertyStringSet : function ( target
, source
, name
, value
) {
1661 if ( value
=== undefined ) {
1662 target
[ name
] = source
;
1664 target
[ name
] = value
;
1667 delete target
[ name
];
1671 updateColumns : function ( container
) {
1672 let mode
= Ext
. state
. Manager
. get ( 'summarycolumns' ) || 'auto' ;
1674 if ( mode
!== 'auto' ) {
1675 factor
= parseInt ( mode
, 10 );
1676 if ( Number
. isNaN ( factor
)) {
1680 factor
= container
. getSize (). width
< 1400 ? 1 : 2 ;
1683 if ( container
. oldFactor
=== factor
) {
1687 let items
= container
. query ( '>' ); // direct childs
1688 factor
= Math
. min ( factor
, items
. length
);
1689 container
. oldFactor
= factor
;
1691 items
. forEach (( item
) => {
1692 item
. columnWidth
= 1 / factor
;
1695 // we have to update the layout twice, since the first layout change
1696 // can trigger the scrollbar which reduces the amount of space left
1697 container
. updateLayout ();
1698 container
. updateLayout ();
1701 forEachCorosyncLink : function ( nodeinfo
, cb
) {
1702 let re
= /(?:ring|link)(\d+)_addr/ ;
1703 Ext
. iterate ( nodeinfo
, ( prop
, val
) => {
1704 let match
= re
. exec ( prop
);
1706 cb ( Number ( match
[ 1 ]), val
);
1713 'AuthenticAMD' : 'AMD' ,
1714 'GenuineIntel' : 'Intel' ,
1722 "_default_" : 5 , // includes custom models
1725 verify_ip64_address_list : function ( value
, with_suffix
) {
1726 for ( let addr
of value
. split ( /[ ,;]+/ )) {
1732 let parts
= addr
. split ( '%' );
1735 if ( parts
. length
> 2 ) {
1739 if ( parts
. length
> 1 && ! addr
. startsWith ( 'fe80:' )) {
1744 if (! Proxmox
. Utils
. IP64_match
. test ( addr
)) {
1754 constructor : function () {
1756 Ext
. apply ( me
, me
. utilities
);
1758 Proxmox
. Utils
. override_task_descriptions ({
1759 acmedeactivate
: [ 'ACME Account' , gettext ( 'Deactivate' )],
1760 acmenewcert
: [ 'SRV' , gettext ( 'Order Certificate' )],
1761 acmerefresh
: [ 'ACME Account' , gettext ( 'Refresh' )],
1762 acmeregister
: [ 'ACME Account' , gettext ( 'Register' )],
1763 acmerenew
: [ 'SRV' , gettext ( 'Renew Certificate' )],
1764 acmerevoke
: [ 'SRV' , gettext ( 'Revoke Certificate' )],
1765 acmeupdate
: [ 'ACME Account' , gettext ( 'Update' )],
1766 'auth-realm-sync' : [ gettext ( 'Realm' ), gettext ( 'Sync' )],
1767 'auth-realm-sync-test' : [ gettext ( 'Realm' ), gettext ( 'Sync Preview' )],
1768 cephcreatemds
: [ 'Ceph Metadata Server' , gettext ( 'Create' )],
1769 cephcreatemgr
: [ 'Ceph Manager' , gettext ( 'Create' )],
1770 cephcreatemon
: [ 'Ceph Monitor' , gettext ( 'Create' )],
1771 cephcreateosd
: [ 'Ceph OSD' , gettext ( 'Create' )],
1772 cephcreatepool
: [ 'Ceph Pool' , gettext ( 'Create' )],
1773 cephdestroymds
: [ 'Ceph Metadata Server' , gettext ( 'Destroy' )],
1774 cephdestroymgr
: [ 'Ceph Manager' , gettext ( 'Destroy' )],
1775 cephdestroymon
: [ 'Ceph Monitor' , gettext ( 'Destroy' )],
1776 cephdestroyosd
: [ 'Ceph OSD' , gettext ( 'Destroy' )],
1777 cephdestroypool
: [ 'Ceph Pool' , gettext ( 'Destroy' )],
1778 cephfscreate
: [ 'CephFS' , gettext ( 'Create' )],
1779 clustercreate
: [ '' , gettext ( 'Create Cluster' )],
1780 clusterjoin
: [ '' , gettext ( 'Join Cluster' )],
1781 dircreate
: [ gettext ( 'Directory Storage' ), gettext ( 'Create' )],
1782 dirremove
: [ gettext ( 'Directory' ), gettext ( 'Remove' )],
1783 download
: [ '' , gettext ( 'Download' )],
1784 hamigrate
: [ 'HA' , gettext ( 'Migrate' )],
1785 hashutdown
: [ 'HA' , gettext ( 'Shutdown' )],
1786 hastart
: [ 'HA' , gettext ( 'Start' )],
1787 hastop
: [ 'HA' , gettext ( 'Stop' )],
1788 imgcopy
: [ '' , gettext ( 'Copy data' )],
1789 imgdel
: [ '' , gettext ( 'Erase data' )],
1790 lvmcreate
: [ gettext ( 'LVM Storage' ), gettext ( 'Create' )],
1791 lvmthincreate
: [ gettext ( 'LVM-Thin Storage' ), gettext ( 'Create' )],
1792 migrateall
: [ '' , gettext ( 'Migrate all VMs and Containers' )],
1793 'move_volume' : [ 'CT' , gettext ( 'Move Volume' )],
1794 pull_file
: [ 'CT' , gettext ( 'Pull file' )],
1795 push_file
: [ 'CT' , gettext ( 'Push file' )],
1796 qmclone
: [ 'VM' , gettext ( 'Clone' )],
1797 qmconfig
: [ 'VM' , gettext ( 'Configure' )],
1798 qmcreate
: [ 'VM' , gettext ( 'Create' )],
1799 qmdelsnapshot
: [ 'VM' , gettext ( 'Delete Snapshot' )],
1800 qmdestroy
: [ 'VM' , gettext ( 'Destroy' )],
1801 qmigrate
: [ 'VM' , gettext ( 'Migrate' )],
1802 qmmove
: [ 'VM' , gettext ( 'Move disk' )],
1803 qmpause
: [ 'VM' , gettext ( 'Pause' )],
1804 qmreboot
: [ 'VM' , gettext ( 'Reboot' )],
1805 qmreset
: [ 'VM' , gettext ( 'Reset' )],
1806 qmrestore
: [ 'VM' , gettext ( 'Restore' )],
1807 qmresume
: [ 'VM' , gettext ( 'Resume' )],
1808 qmrollback
: [ 'VM' , gettext ( 'Rollback' )],
1809 qmshutdown
: [ 'VM' , gettext ( 'Shutdown' )],
1810 qmsnapshot
: [ 'VM' , gettext ( 'Snapshot' )],
1811 qmstart
: [ 'VM' , gettext ( 'Start' )],
1812 qmstop
: [ 'VM' , gettext ( 'Stop' )],
1813 qmsuspend
: [ 'VM' , gettext ( 'Hibernate' )],
1814 qmtemplate
: [ 'VM' , gettext ( 'Convert to template' )],
1815 spiceproxy
: [ 'VM/CT' , gettext ( 'Console' ) + ' (Spice)' ],
1816 spiceshell
: [ '' , gettext ( 'Shell' ) + ' (Spice)' ],
1817 startall
: [ '' , gettext ( 'Start all VMs and Containers' )],
1818 stopall
: [ '' , gettext ( 'Stop all VMs and Containers' )],
1819 unknownimgdel
: [ '' , gettext ( 'Destroy image from unknown guest' )],
1820 vncproxy
: [ 'VM/CT' , gettext ( 'Console' )],
1821 vncshell
: [ '' , gettext ( 'Shell' )],
1822 vzclone
: [ 'CT' , gettext ( 'Clone' )],
1823 vzcreate
: [ 'CT' , gettext ( 'Create' )],
1824 vzdelsnapshot
: [ 'CT' , gettext ( 'Delete Snapshot' )],
1825 vzdestroy
: [ 'CT' , gettext ( 'Destroy' )],
1826 vzdump
: ( type
, id
) => id
? `VM/CT ${id} - ${gettext('Backup')} ` : gettext ( 'Backup Job' ),
1827 vzmigrate
: [ 'CT' , gettext ( 'Migrate' )],
1828 vzmount
: [ 'CT' , gettext ( 'Mount' )],
1829 vzreboot
: [ 'CT' , gettext ( 'Reboot' )],
1830 vzrestore
: [ 'CT' , gettext ( 'Restore' )],
1831 vzresume
: [ 'CT' , gettext ( 'Resume' )],
1832 vzrollback
: [ 'CT' , gettext ( 'Rollback' )],
1833 vzshutdown
: [ 'CT' , gettext ( 'Shutdown' )],
1834 vzsnapshot
: [ 'CT' , gettext ( 'Snapshot' )],
1835 vzstart
: [ 'CT' , gettext ( 'Start' )],
1836 vzstop
: [ 'CT' , gettext ( 'Stop' )],
1837 vzsuspend
: [ 'CT' , gettext ( 'Suspend' )],
1838 vztemplate
: [ 'CT' , gettext ( 'Convert to template' )],
1839 vzumount
: [ 'CT' , gettext ( 'Unmount' )],
1840 zfscreate
: [ gettext ( 'ZFS Storage' ), gettext ( 'Create' )],