]> git.proxmox.com Git - pve-manager.git/blobdiff - www/manager/Utils.js
GUI: mobe startall/stopall/migrateall into submenu
[pve-manager.git] / www / manager / Utils.js
index 5decba95c91095bde60ade340e602929df751970..c293b0613eac0d5e0e72e501db3b6010cb648a30 100644 (file)
@@ -13,9 +13,6 @@ Ext.Ajax.defaultHeaders = {
     'Accept': 'application/json'
 };
 
-// do not send '_dc' parameter
-Ext.Ajax.disableCaching = false;
-
 Ext.Ajax.on('beforerequest', function(conn, options) {
     if (PVE.CSRFPreventionToken) {
        if (!options.headers) { 
@@ -47,76 +44,12 @@ var IPV6_REGEXP = "(?:" +
 
 var IP64_match = new RegExp("^(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + ")$");
 
-// custom Vtypes
-Ext.apply(Ext.form.field.VTypes, {
-    IPAddress:  function(v) {
-       return IP4_match.test(v);
-    },
-    IPAddressText:  gettext('Example') + ': 192.168.1.1',
-    IPAddressMask: /[\d\.]/i,
-
-    IP64Address:  function(v) {
-        return IP64_match.test(v);
-    },
-    IP64AddressText:  gettext('Example') + ': 192.168.1.1 2001:DB8::42',
-    IP64AddressMask: /[A-Fa-f0-9\.:]/,
-
-    MacAddress: function(v) {
-       return (/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/).test(v);
-    },
-    MacAddressMask: /[a-fA-F0-9:]/,
-    MacAddressText: gettext('Example') + ': 01:23:45:67:89:ab',
-
-    BridgeName: function(v) {
-        return (/^vmbr\d{1,4}$/).test(v);
-    },
-    BridgeNameText: gettext('Format') + ': vmbr<b>N</b>, where 0 <= <b>N</b> <= 9999',
-
-    BondName: function(v) {
-        return (/^bond\d{1,4}$/).test(v);
-    },
-    BondNameText: gettext('Format') + ': bond<b>N</b>, where 0 <= <b>N</b> <= 9999',
-
-    QemuStartDate: function(v) {
-       return (/^(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)$/).test(v);
-    },
-    QemuStartDateText: gettext('Format') + ': "now" or "2006-06-17T16:01:21" or "2006-06-17"',
-
-    StorageId:  function(v) {
-        return (/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i).test(v);
-    },
-    StorageIdText: gettext("Allowed characters") + ": 'a-z', '0-9', '-', '_', '.'",
-
-    HttpProxy:  function(v) {
-        return (/^http:\/\/.*$/).test(v);
-    },
-    HttpProxyText: gettext('Example') + ": http://username:password&#64;host:port/",
-
-    DnsName: function(v) {
-       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);
-    },
-    DnsNameText: gettext('This is not a valid DNS name')
-});
-
-// we dont want that a displayfield set the form dirty flag! 
-Ext.override(Ext.form.field.Display, {
-    isDirty: function() { return false; }
-});
-
-// hack: ExtJS does not display the correct value if we
-// call setValue while the store is loading, so we need
-// to call it again after loading
-Ext.override(Ext.form.field.ComboBox, {
-    onLoad: function() {
-       this.setValue(this.value, false);
-        this.callOverridden(arguments);
-    }
-});
-
 Ext.define('PVE.Utils', { statics: {
 
     // this class only contains static functions
 
+    toolkit: undefined, // (extjs|touch), set inside Toolkit.js 
+
     log_severity_hash: {
        0: "panic",
        1: "alert",
@@ -160,11 +93,54 @@ Ext.define('PVE.Utils', { statics: {
        return value;
     },
 
+    render_hotplug_features: function (value) {
+       var fa = [];
+
+       if (!value || (value === '0')) {
+           return gettext('disabled');
+       }
+
+       Ext.each(value.split(','), function(el) {
+           if (el === 'disk') {
+               fa.push(gettext('Disk'));
+           } else if (el === 'network') {
+               fa.push(gettext('Network'));
+           } else if (el === 'usb') {
+               fa.push(gettext('USB'));
+           } else if (el === 'memory') {
+               fa.push(gettext('Memory'));
+           } else if (el === 'cpu') {
+               fa.push(gettext('CPU'));
+           } else {
+               fa.push(el);
+           }
+       });
+
+       return fa.join(', ');
+    },
+
+    network_iface_types: {
+       eth: gettext("Network Device"),
+       bridge: 'Linux Bridge',
+       bond: 'Linux Bond',
+       OVSBridge: 'OVS Bridge',
+       OVSBond: 'OVS Bond',
+       OVSPort: 'OVS Port',
+       OVSIntPort: 'OVS IntPort'
+    },
+
+    render_network_iface_type: function(value) {
+       return PVE.Utils.network_iface_types[value] || 
+           PVE.Utils.unknownText;
+    },
+
     render_scsihw: function(value) {
        if (!value) {
-           return PVE.Utils.defaultText + ' (lsi)';
+           return PVE.Utils.defaultText + ' (LSI 53C895A)';
        } else if (value === 'lsi') {
            return 'LSI 53C895A';
+       } else if (value === 'lsi53c810') {
+           return 'LSI 53C810';
        } else if (value === 'megasas') {
            return 'MegaRAID SAS 8708EM2';
        } else if (value === 'virtio-scsi-pci') {
@@ -248,23 +224,39 @@ Ext.define('PVE.Utils', { statics: {
        return data;
     },
 
+    render_console_viewer: function(value) {
+       if (!value) {
+           return PVE.Utils.defaultText + ' (HTML5)';
+       } else if (value === 'applet') {
+           return 'Java VNC Applet';
+       } else if (value === 'vv') {
+           return  'SPICE (remote-viewer)';
+       } else if (value === 'html5') {
+           return  'HTML5 (noVNC)';
+       } else {
+           return value;
+       }
+    },
+
     language_map: {
        zh_CN: 'Chinese',
        ca: 'Catalan',
-       ja: 'Japanese',
-       en: 'English',
        da: 'Danish',
-       de: 'German',
-       es: 'Spanish',
+       en: 'English',
+       eu: 'Euskera (Basque)',
        fr: 'French',
+       de: 'German',
        it: 'Italian',
+       ja: 'Japanese',
        nb: 'Norwegian (Bokmal)',
        nn: 'Norwegian (Nynorsk)',
+       fa: 'Persian (Farsi)',
+       pl: 'Polish',
+       pt_BR: 'Portuguese (Brazil)',
        ru: 'Russian',
        sl: 'Slovenian',
+       es: 'Spanish',
        sv: 'Swedish',
-       pl: 'Polish',
-       pt_BR: 'Portuguese (Brazil)',
        tr: 'Turkish'
     },
 
@@ -404,7 +396,11 @@ Ext.define('PVE.Utils', { statics: {
            Ext.apply(newopts, {
                success: function(response, options) {
                    if (options.waitMsgTarget) {
-                       options.waitMsgTarget.setLoading(false);
+                       if (PVE.Utils.toolkit === 'touch') {
+                           options.waitMsgTarget.setMasked(false);
+                       } else {
+                           options.waitMsgTarget.setLoading(false);
+                       }
                    }
                    var result = Ext.decode(response.responseText);
                    response.result = result;
@@ -419,7 +415,11 @@ Ext.define('PVE.Utils', { statics: {
                },
                failure: function(response, options) {
                    if (options.waitMsgTarget) {
-                       options.waitMsgTarget.setLoading(false);
+                       if (PVE.Utils.toolkit === 'touch') {
+                           options.waitMsgTarget.setMasked(false);
+                       } else {
+                           options.waitMsgTarget.setLoading(false);
+                       }
                    }
                    response.result = {};
                    try {
@@ -444,8 +444,12 @@ Ext.define('PVE.Utils', { statics: {
 
        var target = newopts.waitMsgTarget;
        if (target) {
-           // Note: ExtJS bug - this does not work when component is not rendered
-           target.setLoading(newopts.waitMsg);
+           if (PVE.Utils.toolkit === 'touch') {
+               target.setMasked({ xtype: 'loadmask', message: newopts.waitMsg} );
+           } else {
+               // Note: ExtJS bug - this does not work when component is not rendered
+               target.setLoading(newopts.waitMsg);
+           }
        }
        Ext.Ajax.request(newopts);
     },
@@ -505,6 +509,7 @@ Ext.define('PVE.Utils', { statics: {
        vncproxy: [ 'VM/CT', gettext('Console') ],
        spiceproxy: [ 'VM/CT', gettext('Console') + ' (Spice)' ],
        vncshell: [ '', gettext('Shell') ],
+       spiceshell: [ '', gettext('Shell')  + ' (Spice)' ],
        qmsnapshot: [ 'VM', gettext('Snapshot') ],
        qmrollback: [ 'VM', gettext('Rollback') ],
        qmdelsnapshot: [ 'VM', gettext('Delete Snapshot') ],
@@ -531,6 +536,8 @@ Ext.define('PVE.Utils', { statics: {
        vzmount: ['CT', gettext('Mount') ],
        vzumount: ['CT', gettext('Unmount') ],
        vzshutdown: ['CT', gettext('Shutdown') ],
+       vzsuspend: [ 'CT', gettext('Suspend') ],
+       vzresume: [ 'CT', gettext('Resume') ],
        hamigrate: [ 'HA', gettext('Migrate') ],
        hastart: [ 'HA', gettext('Start') ],
        hastop: [ 'HA', gettext('Stop') ],
@@ -538,13 +545,18 @@ Ext.define('PVE.Utils', { statics: {
        srvstop: ['SRV', gettext('Stop') ],
        srvrestart: ['SRV', gettext('Restart') ],
        srvreload: ['SRV', gettext('Reload') ],
+       cephcreatemon: ['Ceph Monitor', gettext('Create') ],
+       cephdestroymon: ['Ceph Monitor', gettext('Destroy') ],
+       cephcreateosd: ['Ceph OSD', gettext('Create') ],
+       cephdestroyosd: ['Ceph OSD', gettext('Destroy') ],
        imgcopy: ['', gettext('Copy data') ],
        imgdel: ['', gettext('Erase data') ],
        download: ['', gettext('Download') ],
        vzdump: ['', gettext('Backup') ],
        aptupdate: ['', gettext('Update package database') ],
        startall: [ '', gettext('Start all VMs and Containers') ],
-       stopall: [ '', gettext('Stop all VMs and Containers') ]
+       stopall: [ '', gettext('Stop all VMs and Containers') ],
+       migrateall: [ '', gettext('Migrate all VMs and Containers') ]
     },
 
     format_task_description: function(type, id) {      
@@ -687,6 +699,7 @@ Ext.define('PVE.Utils', { statics: {
 
     yesText: gettext('Yes'),
     noText: gettext('No'),
+    noneText: gettext('none'),
     errorText: gettext('Error'),
     unknownText: gettext('Unknown'),
     defaultText: gettext('Default'),
@@ -697,6 +710,12 @@ Ext.define('PVE.Utils', { statics: {
     neverText: gettext('never'),
     totalText: gettext('Total'),
     usedText: gettext('Used'),
+    directoryText: gettext('Directory'),
+    imagesText: gettext('Disk image'),
+    backupFileText: gettext('VZDump backup file'),
+    vztmplText: gettext('OpenVZ template'),
+    isoImageText: gettext('ISO image'),
+    containersText: gettext('OpenVZ Container'),
 
     format_expire: function(date) {
        if (!date) {
@@ -707,7 +726,7 @@ Ext.define('PVE.Utils', { statics: {
 
     format_storage_type: function(value) {
        if (value === 'dir') {
-           return 'Directory';
+           return PVE.Utils.directoryText;
        } else if (value === 'nfs') {
            return 'NFS';
        } else if (value === 'glusterfs') {
@@ -721,6 +740,8 @@ Ext.define('PVE.Utils', { statics: {
        } else if (value === 'sheepdog') {
            return 'Sheepdog';
        } else if (value === 'zfs') {
+           return 'ZFS over iSCSI';
+       } else if (value === 'zfspool') {
            return 'ZFS';
        } else if (value === 'iscsidirect') {
            return 'iSCSIDirect';
@@ -749,15 +770,15 @@ Ext.define('PVE.Utils', { statics: {
 
        Ext.each(value.split(',').sort(), function(ct) {
            if (ct === 'images') {
-               cta.push('Images');
+               cta.push(PVE.Utils.imagesText);
            } else if (ct === 'backup') {
-               cta.push('Backups');
+               cta.push(PVE.Utils.backupFileText);
            } else if (ct === 'vztmpl') {
-               cta.push('Templates');
+               cta.push(PVE.Utils.vztmplText);
            } else if (ct === 'iso') {
-               cta.push('ISO');
+               cta.push(PVE.Utils.isoImageText);
            } else if (ct === 'rootdir') {
-               cta.push('Containers');
+               cta.push(PVE.Utils.containersText);
            }
        });
 
@@ -897,18 +918,114 @@ Ext.define('PVE.Utils', { statics: {
        }
     },
  
-    openConoleWindow: function(vmtype, vmid, nodename, vmname) {
+    openDefaultConsoleWindow: function(allowSpice, vmtype, vmid, nodename, vmname) {
+       var dv = PVE.Utils.defaultViewer(allowSpice);
+       PVE.Utils.openConsoleWindow(dv, vmtype, vmid, nodename, vmname);
+    },
+
+    openConsoleWindow: function(viewer, vmtype, vmid, nodename, vmname) {
+       // kvm, openvz, shell, upgrade
+
+       if (vmid == undefined && (vmtype === 'kvm' || vmtype === 'openvz')) {
+           throw "missing vmid";
+       }
+
+       if (!nodename) {
+           throw "no nodename specified";
+       }
+
+       if (viewer === 'applet' || viewer === 'html5') {
+           PVE.Utils.openVNCViewer(vmtype, vmid, nodename, vmname, viewer === 'html5');
+       } else if (viewer === 'vv') {
+           var url;
+           var params = { proxy: window.location.hostname };
+           if (vmtype === 'kvm') {
+               url = '/nodes/' + nodename + '/qemu/' + vmid.toString() + '/spiceproxy';
+               PVE.Utils.openSpiceViewer(url, params);
+           } else if (vmtype === 'openvz') {
+               url = '/nodes/' + nodename + '/openvz/' + vmid.toString() + '/spiceproxy';
+               PVE.Utils.openSpiceViewer(url, params);
+           } else if (vmtype === 'shell') {
+               url = '/nodes/' + nodename + '/spiceshell';
+               PVE.Utils.openSpiceViewer(url, params);
+           } else if (vmtype === 'upgrade') {
+               url = '/nodes/' + nodename + '/spiceshell';
+               params.upgrade = 1;
+               PVE.Utils.openSpiceViewer(url, params);
+           }
+       } else {
+           throw "unknown viewer type";
+       }
+    },
+
+    defaultViewer: function(allowSpice) {
+       var vncdefault = 'html5';
+       var dv = PVE.VersionInfo.console || vncdefault;
+       if (dv === 'vv' && !allowSpice) {
+           dv = vncdefault;
+       }
+
+       return dv;
+    },
+
+    openVNCViewer: function(vmtype, vmid, nodename, vmname, novnc) {
        var url = Ext.urlEncode({
-           console: vmtype, // kvm, openvz or shell
+           console: vmtype, // kvm, openvz, upgrade or shell
+           novnc: novnc ? 1 : 0,
            vmid: vmid,
            vmname: vmname,
            node: nodename
        });
-       var nw = window.open("?" + url, '_blank', 
-                            "innerWidth=745,innerheight=427");
+       var nw = window.open("?" + url, '_blank', "innerWidth=745,innerheight=427");
        nw.focus();
     },
 
+    openSpiceViewer: function(url, params){
+
+       var downloadWithName = function(uri, name) {
+           var link = Ext.DomHelper.append(document.body, {
+               tag: 'a',
+               href: uri,
+               css : 'display:none;visibility:hidden;height:0px;'
+           });
+
+           // Note: we need to tell android the correct file name extension
+           // but we do not set 'download' tag for other environments, because
+           // It can have strange side effects (additional user prompt on firefox)
+           var andriod = navigator.userAgent.match(/Android/i) ? true : false;
+           if (andriod) {
+               link.download = name;
+           }
+
+           if (link.fireEvent) {
+               link.fireEvent('onclick');
+           } else {
+                var evt = document.createEvent("MouseEvents");
+                evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
+               link.dispatchEvent(evt);
+           }
+       };
+
+       PVE.Utils.API2Request({
+           url: url,
+           params: params,
+           method: 'POST',
+           failure: function(response, opts){
+               Ext.Msg.alert('Error', response.htmlStatus);
+           },
+           success: function(response, opts){
+               var raw = "[virt-viewer]\n";
+               Ext.Object.each(response.result.data, function(k, v) {
+                   raw += k + "=" + v + "\n";
+               });
+               var url = 'data:application/x-virt-viewer;charset=UTF-8,' +
+                   encodeURIComponent(raw);
+                   
+               downloadWithName(url, "pve-spice.vv");
+           }
+       });
+    },
+
     // comp.setLoading() is buggy in ExtJS 4.0.7, so we 
     // use el.mask() instead
     setErrorMask: function(comp, msg) {