SHARE
TWEET

Untitled

a guest Oct 27th, 2012 14 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. Ext.ns('PVE');
  2.  
  3. // avoid errors when running without development tools
  4. if (!Ext.isDefined(Ext.global.console)) {
  5.     var console = {
  6.         dir: function() {},
  7.         log: function() {}
  8.     };
  9. }
  10. console.log("Starting PVE Manager");
  11.  
  12. Ext.Ajax.defaultHeaders = {
  13.     'Accept': 'application/json'
  14. };
  15.  
  16. // do not send '_dc' parameter
  17. Ext.Ajax.disableCaching = false;
  18.  
  19. Ext.Ajax.on('beforerequest', function(conn, options) {
  20.     if (PVE.CSRFPreventionToken) {
  21.         if (!options.headers) {
  22.             options.headers = {};
  23.         }
  24.         options.headers.CSRFPreventionToken = PVE.CSRFPreventionToken;
  25.     }
  26. });
  27.  
  28. // custom Vtypes
  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);
  32.     },
  33.     IPAddressText:  gettext('Example') + ': 192.168.1.1',
  34.     IPAddressMask: /[\d\.]/i,
  35.  
  36.     MacAddress: function(v) {
  37.         return (/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/).test(v);
  38.     },
  39.     MacAddressMask: /[a-fA-F0-9:]/,
  40.     MacAddressText: gettext('Example') + ': 01:23:45:67:89:ab',
  41.  
  42.     BridgeName: function(v) {
  43.         return (/^vmbr\d{1,4}$/).test(v);
  44.     },
  45.     BridgeNameText: gettext('Format') + ': vmbr<b>N</b>, where 0 <= <b>N</b> <= 9999',
  46.  
  47.     BondName: function(v) {
  48.         return (/^bond\d{1,4}$/).test(v);
  49.     },
  50.     BondNameText: gettext('Format') + ': bond<b>N</b>, where 0 <= <b>N</b> <= 9999',
  51.  
  52.     QemuStartDate: function(v) {
  53.         return (/^(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)$/).test(v);
  54.     },
  55.     QemuStartDateText: gettext('Format') + ': "now" or "2006-06-17T16:01:21" or "2006-06-17"',
  56.  
  57.     StorageId:  function(v) {
  58.         return (/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i).test(v);
  59.     },
  60.     StorageIdText: gettext("Allowed characters") + ": 'a-z', '0-9', '-', '_', '.'",
  61.  
  62.     HttpProxy:  function(v) {
  63.         return (/^http:\/\/.*$/).test(v);
  64.     },
  65.     HttpProxyText: gettext('Example') + ": http://username:password&#64;host:port/",
  66.  
  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);
  69.     },
  70.     DnsNameText: gettext('This is not a valid DNS name')
  71. });
  72.  
  73. // we dont want that a displayfield set the form dirty flag!
  74. Ext.override(Ext.form.field.Display, {
  75.     isDirty: function() { return false; }
  76. });
  77.  
  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, {
  82.     onLoad: function() {
  83.         this.setValue(this.value, false);
  84.         this.callOverridden(arguments);
  85.     }
  86. });
  87.  
  88. Ext.define('PVE.Utils', { statics: {
  89.  
  90.     // this class only contains static functions
  91.  
  92.     log_severity_hash: {
  93.         0: "panic",
  94.         1: "alert",
  95.         2: "critical",
  96.         3: "error",
  97.         4: "warning",
  98.         5: "notice",
  99.         6: "info",
  100.         7: "debug"
  101.     },
  102.  
  103.     support_level_hash: {
  104.         'c': gettext('Community'),
  105.         'b': gettext('Basic'),
  106.         's': gettext('Standard'),
  107.         'p': gettext('Premium')
  108.     },
  109.  
  110.     kvm_ostypes: {
  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.         l24: 'Linux 2.4 Kernel',
  117.         l26: 'Linux 3.X/2.6 Kernel'
  118.     },
  119.  
  120.     render_kvm_ostype: function (value) {
  121.         if (!value) {
  122.             return gettext('Other OS types');
  123.         }
  124.         var text = PVE.Utils.kvm_ostypes[value];
  125.         if (text) {
  126.             return text + ' (' + value + ')';
  127.         }
  128.         return value;
  129.     },
  130.  
  131.     // fixme: auto-generate this
  132.     // for now, please keep in sync with PVE::Tools::kvmkeymaps
  133.     kvm_keymaps: {
  134.         //ar: 'Arabic',
  135.         da: 'Danish',
  136.         de: 'German',
  137.         'de-ch': 'German (Swiss)',
  138.         'en-gb': 'English (UK)',
  139.         'en-us': 'English (USA',
  140.         es: 'Spanish',
  141.         //et: 'Estonia',
  142.         fi: 'Finnish',
  143.         //fo: 'Faroe Islands',
  144.         fr: 'French',
  145.         'fr-be': 'French (Belgium)',
  146.         'fr-ca': 'French (Canada)',
  147.         'fr-ch': 'French (Swiss)',
  148.         //hr: 'Croatia',
  149.         hu: 'Hungarian',
  150.         is: 'Icelandic',
  151.         it: 'Italian',
  152.         ja: 'Japanese',
  153.         lt: 'Lithuanian',
  154.         //lv: 'Latvian',
  155.         mk: 'Macedonian',
  156.         nl: 'Dutch',
  157.         //'nl-be': 'Dutch (Belgium)',
  158.         no: 'Norwegian',
  159.         pl: 'Polish',
  160.         pt: 'Portuguese',
  161.         'pt-br': 'Portuguese (Brazil)',
  162.         //ru: 'Russian',
  163.         si: 'Slovenian',
  164.         sv: 'Swedish',
  165.         //th: 'Thai',
  166.         tr: 'Turkish'
  167.     },
  168.  
  169.     kvm_vga_drivers: {
  170.         std: 'Standard VGA',
  171.         vmware: 'VMWare compatible',
  172.         cirrus: 'Cirrus Logic GD5446'
  173.     },
  174.  
  175.     render_kvm_language: function (value) {
  176.         if (!value) {
  177.             return PVE.Utils.defaultText;
  178.         }
  179.         var text = PVE.Utils.kvm_keymaps[value];
  180.         if (text) {
  181.             return text + ' (' + value + ')';
  182.         }
  183.         return value;
  184.     },
  185.  
  186.     kvm_keymap_array: function() {
  187.         var data = [['', PVE.Utils.render_kvm_language('')]];
  188.         Ext.Object.each(PVE.Utils.kvm_keymaps, function(key, value) {
  189.             data.push([key, PVE.Utils.render_kvm_language(value)]);
  190.         });
  191.  
  192.         return data;
  193.     },
  194.  
  195.     language_map: {
  196.         zh_CN: 'Chinese',
  197.         ca: 'Catalan',
  198.         ja: 'Japanese',
  199.         en: 'English',
  200.         da: 'Danish',
  201.         de: 'German',
  202.         es: 'Spanish',
  203.         fr: 'French',
  204.         it: 'Italian',
  205.         ru: 'Russian',
  206.         sv: 'Swedish',
  207.         pl: 'Polish',
  208.         tr: 'Turkish'
  209.     },
  210.  
  211.     render_language: function (value) {
  212.         if (!value) {
  213.             return PVE.Utils.defaultText + ' (English)';
  214.         }
  215.         var text = PVE.Utils.language_map[value];
  216.         if (text) {
  217.             return text + ' (' + value + ')';
  218.         }
  219.         return value;
  220.     },
  221.  
  222.     language_array: function() {
  223.         var data = [['', PVE.Utils.render_language('')]];
  224.         Ext.Object.each(PVE.Utils.language_map, function(key, value) {
  225.             data.push([key, PVE.Utils.render_language(value)]);
  226.         });
  227.  
  228.         return data;
  229.     },
  230.  
  231.     render_kvm_vga_driver: function (value) {
  232.         if (!value) {
  233.             return PVE.Utils.defaultText;
  234.         }
  235.         var text = PVE.Utils.kvm_vga_drivers[value];
  236.         if (text) {
  237.             return text + ' (' + value + ')';
  238.         }
  239.         return value;
  240.     },
  241.  
  242.     kvm_vga_driver_array: function() {
  243.         var data = [['', PVE.Utils.render_kvm_vga_driver('')]];
  244.         Ext.Object.each(PVE.Utils.kvm_vga_drivers, function(key, value) {
  245.             data.push([key, PVE.Utils.render_kvm_vga_driver(value)]);
  246.         });
  247.  
  248.         return data;
  249.     },
  250.  
  251.     render_kvm_startup: function(value) {
  252.         var startup = PVE.Parser.parseStartup(value);
  253.  
  254.         var res = 'order=';
  255.         if (startup.order === undefined) {
  256.             res += 'any';
  257.         } else {
  258.             res += startup.order;
  259.         }
  260.         if (startup.up !== undefined) {
  261.             res += ',up=' + startup.up;
  262.         }
  263.         if (startup.down !== undefined) {
  264.             res += ',down=' + startup.down;
  265.         }
  266.  
  267.         return res;
  268.     },
  269.  
  270.     authOK: function() {
  271.         return Ext.util.Cookies.get('PVEAuthCookie');
  272.     },
  273.  
  274.     authClear: function() {
  275.         Ext.util.Cookies.clear("PVEAuthCookie");
  276.     },
  277.  
  278.     // fixme: remove - not needed?
  279.     gridLineHeigh: function() {
  280.         return 21;
  281.  
  282.         //if (Ext.isGecko)
  283.         //return 23;
  284.         //return 21;
  285.     },
  286.  
  287.     extractRequestError: function(result, verbose) {
  288.         var msg = gettext('Successful');
  289.  
  290.         if (!result.success) {
  291.             msg = gettext("Unknown error");
  292.             if (result.message) {
  293.                 msg = result.message;
  294.                 if (result.status) {
  295.                     msg += ' (' + result.status + ')';
  296.                 }
  297.             }
  298.             if (verbose && Ext.isObject(result.errors)) {
  299.                 msg += "<br>";
  300.                 Ext.Object.each(result.errors, function(prop, desc) {
  301.                     msg += "<br><b>" + Ext.htmlEncode(prop) + "</b>: " +
  302.                         Ext.htmlEncode(desc);
  303.                 });
  304.             }
  305.         }
  306.  
  307.         return msg;
  308.     },
  309.  
  310.     extractFormActionError: function(action) {
  311.         var msg;
  312.         switch (action.failureType) {
  313.         case Ext.form.action.Action.CLIENT_INVALID:
  314.             msg = gettext('Form fields may not be submitted with invalid values');
  315.             break;
  316.         case Ext.form.action.Action.CONNECT_FAILURE:
  317.             msg = gettext('Connection error');
  318.             var resp = action.response;
  319.             if (resp.status && resp.statusText) {
  320.                 msg += " " + resp.status + ": " + resp.statusText;
  321.             }
  322.             break;
  323.         case Ext.form.action.Action.LOAD_FAILURE:
  324.         case Ext.form.action.Action.SERVER_INVALID:
  325.             msg = PVE.Utils.extractRequestError(action.result, true);
  326.             break;
  327.         }
  328.         return msg;
  329.     },
  330.  
  331.     // Ext.Ajax.request
  332.     API2Request: function(reqOpts) {
  333.  
  334.         var newopts = Ext.apply({
  335.             waitMsg: gettext('Please wait...')
  336.         }, reqOpts);
  337.  
  338.         if (!newopts.url.match(/^\/api2/)) {
  339.             newopts.url = '/api2/extjs' + newopts.url;
  340.         }
  341.         delete newopts.callback;
  342.  
  343.         var createWrapper = function(successFn, callbackFn, failureFn) {
  344.             Ext.apply(newopts, {
  345.                 success: function(response, options) {
  346.                     if (options.waitMsgTarget) {
  347.                         options.waitMsgTarget.setLoading(false);
  348.                     }
  349.                     var result = Ext.decode(response.responseText);
  350.                     response.result = result;
  351.                     if (!result.success) {
  352.                         response.htmlStatus = PVE.Utils.extractRequestError(result, true);
  353.                         Ext.callback(callbackFn, options.scope, [options, false, response]);
  354.                         Ext.callback(failureFn, options.scope, [response, options]);
  355.                         return;
  356.                     }
  357.                     Ext.callback(callbackFn, options.scope, [options, true, response]);
  358.                     Ext.callback(successFn, options.scope, [response, options]);
  359.                 },
  360.                 failure: function(response, options) {
  361.                     if (options.waitMsgTarget) {
  362.                         options.waitMsgTarget.setLoading(false);
  363.                     }
  364.                     response.result = {};
  365.                     try {
  366.                         response.result = Ext.decode(response.responseText);
  367.                     } catch(e) {}
  368.                     var msg = gettext('Connection error') + ' - server offline?';
  369.                     if (response.aborted) {
  370.                         msg = gettext('Connection error') + ' - aborted.';
  371.                     } else if (response.timedout) {
  372.                         msg = gettext('Connection error') + ' - Timeout.';
  373.                     } else if (response.status && response.statusText) {
  374.                         msg = gettext('Connection error') + ' ' + response.status + ': ' + response.statusText;
  375.                     }
  376.                     response.htmlStatus = msg;
  377.                     Ext.callback(callbackFn, options.scope, [options, false, response]);
  378.                     Ext.callback(failureFn, options.scope, [response, options]);
  379.                 }
  380.             });
  381.         };
  382.  
  383.         createWrapper(reqOpts.success, reqOpts.callback, reqOpts.failure);
  384.  
  385.         var target = newopts.waitMsgTarget;
  386.         if (target) {
  387.             // Note: ExtJS bug - this does not work when component is not rendered
  388.             target.setLoading(newopts.waitMsg);
  389.         }
  390.         Ext.Ajax.request(newopts);
  391.     },
  392.  
  393.     assemble_field_data: function(values, data) {
  394.         if (Ext.isObject(data)) {
  395.             Ext.Object.each(data, function(name, val) {
  396.                 if (values.hasOwnProperty(name)) {
  397.                     var bucket = values[name];
  398.                     if (!Ext.isArray(bucket)) {
  399.                         bucket = values[name] = [bucket];
  400.                     }
  401.                     if (Ext.isArray(val)) {
  402.                         values[name] = bucket.concat(val);
  403.                     } else {
  404.                         bucket.push(val);
  405.                     }
  406.                 } else {
  407.                     values[name] = val;
  408.                 }
  409.             });
  410.         }
  411.     },
  412.  
  413.     task_desc_table: {
  414.         vncproxy: [ 'VM/CT', gettext('Console') ],
  415.         vncshell: [ '', gettext('Shell') ],
  416.         qmcreate: [ 'VM', gettext('Create') ],
  417.         qmrestore: [ 'VM', gettext('Restore') ],
  418.         qmdestroy: [ 'VM', gettext('Destroy') ],
  419.         qmigrate: [ 'VM', gettext('Migrate') ],
  420.         qmstart: [ 'VM', gettext('Start') ],
  421.         qmstop: [ 'VM', gettext('Stop') ],
  422.         qmreset: [ 'VM', gettext('Reset') ],
  423.         qmshutdown: [ 'VM', gettext('Shutdown') ],
  424.         qmsuspend: [ 'VM', gettext('Suspend') ],
  425.         qmresume: [ 'VM', gettext('Resume') ],
  426.         vzcreate: ['CT', gettext('Create') ],
  427.         vzrestore: ['CT', gettext('Restore') ],
  428.         vzdestroy: ['CT', gettext('Destroy') ],
  429.         vzmigrate: [ 'CT', gettext('Migrate') ],
  430.         vzstart: ['CT', gettext('Start') ],
  431.         vzstop: ['CT', gettext('Stop') ],
  432.         vzmount: ['CT', gettext('Mount') ],
  433.         vzumount: ['CT', gettext('Unmount') ],
  434.         vzshutdown: ['CT', gettext('Shutdown') ],
  435.         hamigrate: [ 'HA', gettext('Migrate') ],
  436.         hastart: [ 'HA', gettext('Start') ],
  437.         hastop: [ 'HA', gettext('Stop') ],
  438.         srvstart: ['SRV', gettext('Start') ],
  439.         srvstop: ['SRV', gettext('Stop') ],
  440.         srvrestart: ['SRV', gettext('Restart') ],
  441.         srvreload: ['SRV', gettext('Reload') ],
  442.         imgcopy: ['', gettext('Copy data') ],
  443.         imgdel: ['', gettext('Erase data') ],
  444.         download: ['', gettext('Download') ],
  445.         vzdump: ['', gettext('Backup') ],
  446.         startall: [ '', gettext('Start all VMs and Containers') ],
  447.         stopall: [ '', gettext('Stop all VMs and Containers') ]
  448.     },
  449.  
  450.     format_task_description: function(type, id) {
  451.         var farray = PVE.Utils.task_desc_table[type];
  452.         if (!farray) {
  453.             return type;
  454.         }
  455.         var prefix = farray[0];
  456.         var text = farray[1];
  457.         if (prefix) {
  458.             return prefix + ' ' + id + ' - ' + text;
  459.         }
  460.         return text;
  461.     },
  462.  
  463.     parse_task_upid: function(upid) {
  464.         var task = {};
  465.  
  466.         var res = upid.match(/^UPID:(\S+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8}):([^:\s]+):([^:\s]*):([^:\s]+):$/);
  467.         if (!res) {
  468.             throw "unable to parse upid '" + upid + "'";
  469.         }
  470.         task.node = res[1];
  471.         task.pid = parseInt(res[2], 16);
  472.         task.pstart = parseInt(res[3], 16);
  473.         task.starttime = parseInt(res[4], 16);
  474.         task.type = res[5];
  475.         task.id = res[6];
  476.         task.user = res[7];
  477.  
  478.         task.desc = PVE.Utils.format_task_description(task.type, task.id);
  479.  
  480.         return task;
  481.     },
  482.  
  483.     format_size: function(size) {
  484.         /*jslint confusion: true */
  485.  
  486.         if (size < 1024) {
  487.             return size;
  488.         }
  489.  
  490.         var kb = size / 1024;
  491.  
  492.         if (kb < 1024) {
  493.             return kb.toFixed(0) + "KB";
  494.         }
  495.  
  496.         var mb = size / (1024*1024);
  497.  
  498.         if (mb < 1024) {
  499.             return mb.toFixed(0) + "MB";
  500.         }
  501.  
  502.         var gb = mb / 1024;
  503.  
  504.         if (gb < 1024) {
  505.             return gb.toFixed(2) + "GB";
  506.         }
  507.  
  508.         var tb =  gb / 1024;
  509.  
  510.         return tb.toFixed(2) + "TB";
  511.  
  512.     },
  513.  
  514.     format_html_bar: function(per, text) {
  515.  
  516.         return "<div class='pve-bar-wrap'>" + text + "<div class='pve-bar-border'>" +
  517.             "<div class='pve-bar-inner' style='width:" + per + "%;'></div>" +
  518.             "</div></div>";
  519.  
  520.     },
  521.  
  522.     format_cpu_bar: function(per1, per2, text) {
  523.  
  524.         return "<div class='pve-bar-border'>" +
  525.             "<div class='pve-bar-inner' style='width:" + per1 + "%;'></div>" +
  526.             "<div class='pve-bar-inner2' style='width:" + per2 + "%;'></div>" +
  527.             "<div class='pve-bar-text'>" + text + "</div>" +
  528.             "</div>";
  529.     },
  530.  
  531.     format_large_bar: function(per, text) {
  532.  
  533.         if (!text) {
  534.             text = per.toFixed(1) + "%";
  535.         }
  536.  
  537.         return "<div class='pve-largebar-border'>" +
  538.             "<div class='pve-largebar-inner' style='width:" + per + "%;'></div>" +
  539.             "<div class='pve-largebar-text'>" + text + "</div>" +
  540.             "</div>";
  541.     },
  542.  
  543.     format_duration_long: function(ut) {
  544.  
  545.         var days = Math.floor(ut / 86400);
  546.         ut -= days*86400;
  547.         var hours = Math.floor(ut / 3600);
  548.         ut -= hours*3600;
  549.         var mins = Math.floor(ut / 60);
  550.         ut -= mins*60;
  551.  
  552.         var hours_str = '00' + hours.toString();
  553.         hours_str = hours_str.substr(hours_str.length - 2);
  554.         var mins_str = "00" + mins.toString();
  555.         mins_str = mins_str.substr(mins_str.length - 2);
  556.         var ut_str = "00" + ut.toString();
  557.         ut_str = ut_str.substr(ut_str.length - 2);
  558.  
  559.         if (days) {
  560.             var ds = days > 1 ? PVE.Utils.daysText : PVE.Utils.dayText;
  561.             return days.toString() + ' ' + ds + ' ' +
  562.                 hours_str + ':' + mins_str + ':' + ut_str;
  563.         } else {
  564.             return hours_str + ':' + mins_str + ':' + ut_str;
  565.         }
  566.     },
  567.  
  568.     format_duration_short: function(ut) {
  569.  
  570.         if (ut < 60) {
  571.             return ut.toString() + 's';
  572.         }
  573.  
  574.         if (ut < 3600) {
  575.             var mins = ut / 60;
  576.             return mins.toFixed(0) + 'm';
  577.         }
  578.  
  579.         if (ut < 86400) {
  580.             var hours = ut / 3600;
  581.             return hours.toFixed(0) + 'h';
  582.         }
  583.  
  584.         var days = ut / 86400;
  585.         return days.toFixed(0) + 'd';
  586.     },
  587.  
  588.     yesText: gettext('Yes'),
  589.     noText: gettext('No'),
  590.     errorText: gettext('Error'),
  591.     unknownText: gettext('Unknown'),
  592.     defaultText: gettext('Default'),
  593.     daysText: gettext('days'),
  594.     dayText: gettext('day'),
  595.     runningText: gettext('running'),
  596.     stoppedText: gettext('stopped'),
  597.     neverText: gettext('never'),
  598.  
  599.     format_expire: function(date) {
  600.         if (!date) {
  601.             return PVE.Utils.neverText;
  602.         }
  603.         return Ext.Date.format(date, "Y-m-d");
  604.     },
  605.  
  606.     format_storage_type: function(value) {
  607.         if (value === 'dir') {
  608.             return 'Directory';
  609.         } else if (value === 'nfs') {
  610.             return 'NFS';
  611.         } else if (value === 'lvm') {
  612.             return 'LVM';
  613.         } else if (value === 'iscsi') {
  614.             return 'iSCSI';
  615.         } else {
  616.             return PVE.Utils.unknownText;
  617.         }
  618.     },
  619.  
  620.     format_boolean_with_default: function(value) {
  621.         if (Ext.isDefined(value) && value !== '') {
  622.             return value ? PVE.Utils.yesText : PVE.Utils.noText;
  623.         }
  624.         return PVE.Utils.defaultText;
  625.     },
  626.  
  627.     format_boolean: function(value) {
  628.         return value ? PVE.Utils.yesText : PVE.Utils.noText;
  629.     },
  630.  
  631.     format_neg_boolean: function(value) {
  632.         return !value ? PVE.Utils.yesText : PVE.Utils.noText;
  633.     },
  634.  
  635.     format_content_types: function(value) {
  636.         var cta = [];
  637.  
  638.         Ext.each(value.split(',').sort(), function(ct) {
  639.             if (ct === 'images') {
  640.                 cta.push('Images');
  641.             } else if (ct === 'backup') {
  642.                 cta.push('Backups');
  643.             } else if (ct === 'vztmpl') {
  644.                 cta.push('Templates');
  645.             } else if (ct === 'iso') {
  646.                 cta.push('ISO');
  647.             } else if (ct === 'rootdir') {
  648.                 cta.push('Containers');
  649.             }
  650.         });
  651.  
  652.         return cta.join(', ');
  653.     },
  654.  
  655.     render_storage_content: function(value, metaData, record) {
  656.         var data = record.data;
  657.         if (Ext.isNumber(data.channel) &&
  658.             Ext.isNumber(data.id) &&
  659.             Ext.isNumber(data.lun)) {
  660.             return "CH " +
  661.                 Ext.String.leftPad(data.channel,2, '0') +
  662.                 " ID " + data.id + " LUN " + data.lun;
  663.         }
  664.         return data.volid.replace(/^.*:(.*\/)?/,'');
  665.     },
  666.  
  667.     render_serverity: function (value) {
  668.         return PVE.Utils.log_severity_hash[value] || value;
  669.     },
  670.  
  671.     render_cpu: function(value, metaData, record, rowIndex, colIndex, store) {
  672.  
  673.         if (!(record.data.uptime && Ext.isNumeric(value))) {
  674.             return '';
  675.         }
  676.  
  677.         var maxcpu = record.data.maxcpu || 1;
  678.  
  679.         if (!Ext.isNumeric(maxcpu) && (maxcpu >= 1)) {
  680.             return '';
  681.         }
  682.  
  683.         var per = value * 100;
  684.  
  685.         return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU');
  686.     },
  687.  
  688.     render_size: function(value, metaData, record, rowIndex, colIndex, store) {
  689.         /*jslint confusion: true */
  690.  
  691.         if (!Ext.isNumeric(value)) {
  692.             return '';
  693.         }
  694.  
  695.         return PVE.Utils.format_size(value);
  696.     },
  697.  
  698.     render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) {
  699.         var servertime = new Date(value * 1000);
  700.         return Ext.Date.format(servertime, 'Y-m-d H:i:s');
  701.     },
  702.  
  703.     render_mem_usage: function(value, metaData, record, rowIndex, colIndex, store) {
  704.  
  705.         var mem = value;
  706.         var maxmem = record.data.maxmem;
  707.  
  708.         if (!record.data.uptime) {
  709.             return '';
  710.         }
  711.  
  712.         if (!(Ext.isNumeric(mem) && maxmem)) {
  713.             return '';
  714.         }
  715.  
  716.         var per = (mem * 100) / maxmem;
  717.  
  718.         return per.toFixed(1) + '%';
  719.     },
  720.  
  721.     render_disk_usage: function(value, metaData, record, rowIndex, colIndex, store) {
  722.  
  723.         var disk = value;
  724.         var maxdisk = record.data.maxdisk;
  725.  
  726.         if (!(Ext.isNumeric(disk) && maxdisk)) {
  727.             return '';
  728.         }
  729.  
  730.         var per = (disk * 100) / maxdisk;
  731.  
  732.         return per.toFixed(1) + '%';
  733.     },
  734.  
  735.     render_resource_type: function(value, metaData, record, rowIndex, colIndex, store) {
  736.  
  737.         var cls = 'pve-itype-icon-' + value;
  738.  
  739.         if (record.data.running) {
  740.             metaData.tdCls = cls + "-running";
  741.         } else {
  742.             metaData.tdCls = cls;
  743.         }
  744.  
  745.         return value;
  746.     },
  747.  
  748.     render_uptime: function(value, metaData, record, rowIndex, colIndex, store) {
  749.  
  750.         var uptime = value;
  751.  
  752.         if (uptime === undefined) {
  753.             return '';
  754.         }
  755.  
  756.         if (uptime <= 0) {
  757.             return '-';
  758.         }
  759.  
  760.         return PVE.Utils.format_duration_long(uptime);
  761.     },
  762.  
  763.     render_support_level: function(value, metaData, record) {
  764.         return PVE.Utils.support_level_hash[value] || '-';
  765.     },
  766.  
  767.     render_upid: function(value, metaData, record) {
  768.         var type = record.data.type;
  769.         var id = record.data.id;
  770.  
  771.         return PVE.Utils.format_task_description(type, id);
  772.     },
  773.  
  774.     dialog_title: function(subject, create, isAdd) {
  775.         if (create) {
  776.             if (isAdd) {
  777.                 return gettext('Add') + ': ' + subject;
  778.             } else {
  779.                 return gettext('Create') + ': ' + subject;
  780.             }
  781.         } else {
  782.             return gettext('Edit') + ': ' + subject;
  783.         }
  784.     },
  785.  
  786.     openConoleWindow: function(vmtype, vmid, nodename, vmname) {
  787.         var url = Ext.urlEncode({
  788.             console: vmtype, // kvm, openvz or shell
  789.             vmid: vmid,
  790.             vmname: vmname,
  791.             node: nodename
  792.         });
  793.         var nw = window.open("?" + url, '_blank',
  794.                              "innerWidth=745,innerheight=427");
  795.         nw.focus();
  796.     },
  797.  
  798.     // comp.setLoading() is buggy in ExtJS 4.0.7, so we
  799.     // use el.mask() instead
  800.     setErrorMask: function(comp, msg) {
  801.         var el = comp.el;
  802.         if (!el) {
  803.             return;
  804.         }
  805.         if (!msg) {
  806.             el.unmask();
  807.         } else {
  808.             if (msg === true) {
  809.                 el.mask(gettext("Loading..."));
  810.             } else {
  811.                 el.mask(msg);
  812.             }
  813.         }
  814.     },
  815.  
  816.     monStoreErrors: function(me, store) {
  817.         me.mon(store, 'beforeload', function(s, operation, eOpts) {
  818.             if (!me.loadCount) {
  819.                 me.loadCount = 0; // make sure it is numeric
  820.                 PVE.Utils.setErrorMask(me, true);
  821.             }
  822.         });
  823.  
  824.         // only works with 'pve' proxy
  825.         me.mon(store.proxy, 'afterload', function(proxy, request, success) {
  826.             me.loadCount++;
  827.  
  828.             if (success) {
  829.                 PVE.Utils.setErrorMask(me, false);
  830.                 return;
  831.             }
  832.  
  833.             var msg;
  834.             var operation = request.operation;
  835.             var error = operation.getError();
  836.             if (error.statusText) {
  837.                 msg = error.statusText + ' (' + error.status + ')';
  838.             } else {
  839.                 msg = gettext('Connection error');
  840.             }
  841.             PVE.Utils.setErrorMask(me, msg);
  842.         });
  843.     }
  844.  
  845. }});
  846.  
  847. // Some configuration values are complex strings -
  848. // so we need parsers/generators for them.
  849.  
  850. Ext.define('PVE.Parser', { statics: {
  851.  
  852.     // this class only contains static functions
  853.  
  854.     parseQemuNetwork: function(key, value) {
  855.         if (!(key && value)) {
  856.             return;
  857.         }
  858.  
  859.         var res = {};
  860.  
  861.         var errors = false;
  862.         Ext.Array.each(value.split(','), function(p) {
  863.             if (!p || p.match(/^\s*$/)) {
  864.                 return; // continue
  865.             }
  866.  
  867.             var match_res;
  868.  
  869.             if ((match_res = p.match(/^(ne2k_pci|e1000|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i)) !== null) {
  870.                 res.model = match_res[1].toLowerCase();
  871.                 if (match_res[3]) {
  872.                     res.macaddr = match_res[3];
  873.                 }
  874.             } else if ((match_res = p.match(/^bridge=(\S+)$/)) !== null) {
  875.                 res.bridge = match_res[1];
  876.             } else if ((match_res = p.match(/^rate=(\d+(\.\d+)?)$/)) !== null) {
  877.                 res.rate = match_res[1];
  878.             } else if ((match_res = p.match(/^tag=(\d+(\.\d+)?)$/)) !== null) {
  879.                 res.tag = match_res[1];
  880.             } else {
  881.                 errors = true;
  882.                 return false; // break
  883.             }
  884.         });
  885.  
  886.         if (errors || !res.model) {
  887.             return;
  888.         }
  889.  
  890.         return res;
  891.     },
  892.  
  893.     printQemuNetwork: function(net) {
  894.  
  895.         var netstr = net.model;
  896.         if (net.macaddr) {
  897.             netstr += "=" + net.macaddr;
  898.         }
  899.         if (net.bridge) {
  900.             netstr += ",bridge=" + net.bridge;
  901.             if (net.tag) {
  902.                 netstr += ",tag=" + net.tag;
  903.             }
  904.         }
  905.         if (net.rate) {
  906.             netstr += ",rate=" + net.rate;
  907.         }
  908.         return netstr;
  909.     },
  910.  
  911.     parseQemuDrive: function(key, value) {
  912.         if (!(key && value)) {
  913.             return;
  914.         }
  915.  
  916.         var res = {};
  917.  
  918.         var match_res = key.match(/^([a-z]+)(\d+)$/);
  919.         if (!match_res) {
  920.             return;
  921.         }
  922.         res['interface'] = match_res[1];
  923.         res.index = match_res[2];
  924.  
  925.         var errors = false;
  926.         Ext.Array.each(value.split(','), function(p) {
  927.             if (!p || p.match(/^\s*$/)) {
  928.                 return; // continue
  929.             }
  930.             var match_res = p.match(/^([a-z_]+)=(\S+)$/);
  931.             if (!match_res) {
  932.                 if (!p.match(/\=/)) {
  933.                     res.file = p;
  934.                     return; // continue
  935.                 }
  936.                 errors = true;
  937.                 return false; // break
  938.             }
  939.             var k = match_res[1];
  940.             if (k === 'volume') {
  941.                 k = 'file';
  942.             }
  943.  
  944.             if (Ext.isDefined(res[k])) {
  945.                 errors = true;
  946.                 return false; // break
  947.             }
  948.  
  949.             var v = match_res[2];
  950.  
  951.             if (k === 'cache' && v === 'off') {
  952.                 v = 'none';
  953.             }
  954.  
  955.             res[k] = v;
  956.         });
  957.  
  958.         if (errors || !res.file) {
  959.             return;
  960.         }
  961.  
  962.         return res;
  963.     },
  964.  
  965.     printQemuDrive: function(drive) {
  966.  
  967.         var drivestr = drive.file;
  968.  
  969.         Ext.Object.each(drive, function(key, value) {
  970.             if (!Ext.isDefined(value) || key === 'file' ||
  971.                 key === 'index' || key === 'interface') {
  972.                 return; // continue
  973.             }
  974.             drivestr += ',' + key + '=' + value;
  975.         });
  976.  
  977.         return drivestr;
  978.     },
  979.  
  980.     parseOpenVZNetIf: function(value) {
  981.         if (!value) {
  982.             return;
  983.         }
  984.  
  985.         var res = {};
  986.  
  987.         var errors = false;
  988.         Ext.Array.each(value.split(';'), function(item) {
  989.             if (!item || item.match(/^\s*$/)) {
  990.                 return; // continue
  991.             }
  992.  
  993.             var data = {};
  994.             Ext.Array.each(item.split(','), function(p) {
  995.                 if (!p || p.match(/^\s*$/)) {
  996.                     return; // continue
  997.                 }
  998.                 var match_res = p.match(/^(ifname|mac|bridge|host_ifname|host_mac)=(\S+)$/);
  999.                 if (!match_res) {
  1000.                     errors = true;
  1001.                     return false; // break
  1002.                 }
  1003.                 data[match_res[1]] = match_res[2];
  1004.             });
  1005.  
  1006.             if (errors || !data.ifname) {
  1007.                 errors = true;
  1008.                 return false; // break
  1009.             }
  1010.  
  1011.             data.raw = item;
  1012.  
  1013.             res[data.ifname] = data;
  1014.         });
  1015.  
  1016.         return errors ? undefined: res;
  1017.     },
  1018.  
  1019.     printOpenVZNetIf: function(netif) {
  1020.         var netarray = [];
  1021.  
  1022.         Ext.Object.each(netif, function(iface, data) {
  1023.             var tmparray = [];
  1024.             Ext.Array.each(['ifname', 'mac', 'bridge', 'host_ifname' , 'host_mac'], function(key) {
  1025.                 var value = data[key];
  1026.                 if (value) {
  1027.                     tmparray.push(key + '=' + value);
  1028.                 }
  1029.             });
  1030.             netarray.push(tmparray.join(','));
  1031.         });
  1032.  
  1033.         return netarray.join(';');
  1034.     },
  1035.  
  1036.     parseStartup: function(value) {
  1037.         if (value === undefined) {
  1038.             return;
  1039.         }
  1040.  
  1041.         var res = {};
  1042.  
  1043.         var errors = false;
  1044.         Ext.Array.each(value.split(','), function(p) {
  1045.             if (!p || p.match(/^\s*$/)) {
  1046.                 return; // continue
  1047.             }
  1048.  
  1049.             var match_res;
  1050.  
  1051.             if ((match_res = p.match(/^(order)?=(\d+)$/)) !== null) {
  1052.                 res.order = match_res[2];
  1053.             } else if ((match_res = p.match(/^up=(\d+)$/)) !== null) {
  1054.                 res.up = match_res[1];
  1055.             } else if ((match_res = p.match(/^down=(\d+)$/)) !== null) {
  1056.                 res.down = match_res[1];
  1057.             } else {
  1058.                 errors = true;
  1059.                 return false; // break
  1060.             }
  1061.         });
  1062.  
  1063.         if (errors) {
  1064.             return;
  1065.         }
  1066.  
  1067.         return res;
  1068.     },
  1069.  
  1070.     printStartup: function(startup) {
  1071.         var arr = [];
  1072.         if (startup.order !== undefined && startup.order !== '') {
  1073.             arr.push('order=' + startup.order);
  1074.         }
  1075.         if (startup.up !== undefined && startup.up !== '') {
  1076.             arr.push('up=' + startup.up);
  1077.         }
  1078.         if (startup.down !== undefined && startup.down !== '') {
  1079.             arr.push('down=' + startup.down);
  1080.         }
  1081.  
  1082.         return arr.join(',');
  1083.     }
  1084.  
  1085. }});
  1086. /* This state provider keeps part of the state inside
  1087.  * the browser history.
  1088.  *
  1089.  * We compress (shorten) url using dictionary based compression
  1090.  * i.e. use column separated list instead of url encoded hash:
  1091.  * #v\d*       version/format
  1092.  * :=          indicates string values
  1093.  * :\d+        lookup value in dictionary hash
  1094.  * #v1:=value1:5:=value2:=value3:...
  1095. */
  1096.  
  1097. Ext.define('PVE.StateProvider', {
  1098.     extend: 'Ext.state.LocalStorageProvider',
  1099.  
  1100.     // private
  1101.     setHV: function(name, newvalue, fireEvents) {
  1102.         var me = this;
  1103.  
  1104.         var changes = false;
  1105.         var oldtext = Ext.encode(me.UIState[name]);
  1106.         var newtext = Ext.encode(newvalue);
  1107.         if (newtext != oldtext) {
  1108.             changes = true;
  1109.             me.UIState[name] = newvalue;
  1110.             //console.log("changed old " + name + " " + oldtext);
  1111.             //console.log("changed new " + name + " " + newtext);
  1112.             if (fireEvents) {
  1113.                 me.fireEvent("statechange", me, name, { value: newvalue });
  1114.             }
  1115.         }
  1116.         return changes;
  1117.     },
  1118.  
  1119.     // private
  1120.     hslist: [
  1121.         // order is important for notifications
  1122.         // [ name, default ]
  1123.         ['view', 'server'],
  1124.         ['rid', 'root'],
  1125.         ['ltab', 'tasks'],
  1126.         ['nodetab', ''],
  1127.         ['storagetab', ''],
  1128.         ['pooltab', ''],
  1129.         ['kvmtab', ''],
  1130.         ['ovztab', ''],
  1131.         ['dctab', '']
  1132.     ],
  1133.  
  1134.     hprefix: 'v1',
  1135.  
  1136.     compDict: {
  1137.         ha: 28,
  1138.         support: 27,
  1139.         pool: 26,
  1140.         syslog: 25,
  1141.         ubc: 24,
  1142.         initlog: 23,
  1143.         openvz: 22,
  1144.         backup: 21,
  1145.         ressources: 20,
  1146.         content: 19,
  1147.         root: 18,
  1148.         domains: 17,
  1149.         roles: 16,
  1150.         groups: 15,
  1151.         users: 14,
  1152.         time: 13,
  1153.         dns: 12,
  1154.         network: 11,
  1155.         services: 10,
  1156.         options: 9,
  1157.         console: 8,
  1158.         hardware: 7,
  1159.         permissions: 6,
  1160.         summary: 5,
  1161.         tasks: 4,
  1162.         clog: 3,
  1163.         storage: 2,
  1164.         folder: 1,
  1165.         server: 0
  1166.     },
  1167.  
  1168.     decodeHToken: function(token) {
  1169.         var me = this;
  1170.  
  1171.         var state = {};
  1172.         if (!token) {
  1173.             Ext.Array.each(me.hslist, function(rec) {
  1174.                 state[rec[0]] = rec[1];
  1175.             });
  1176.             return state;
  1177.         }
  1178.  
  1179.         // return Ext.urlDecode(token);
  1180.  
  1181.         var items = token.split(':');
  1182.         var prefix = items.shift();
  1183.  
  1184.         if (prefix != me.hprefix) {
  1185.             return me.decodeHToken();
  1186.         }
  1187.  
  1188.         Ext.Array.each(me.hslist, function(rec) {
  1189.             var value = items.shift();
  1190.             if (value) {
  1191.                 if (value[0] === '=') {
  1192.                     value = decodeURIComponent(value.slice(1));
  1193.                 } else {
  1194.                     Ext.Object.each(me.compDict, function(key, cv) {
  1195.                         if (value == cv) {
  1196.                             value = key;
  1197.                             return false;
  1198.                         }
  1199.                     });
  1200.                 }
  1201.             }
  1202.             state[rec[0]] = value;
  1203.         });
  1204.  
  1205.         return state;
  1206.     },
  1207.  
  1208.     encodeHToken: function(state) {
  1209.         var me = this;
  1210.  
  1211.         // return Ext.urlEncode(state);
  1212.  
  1213.         var ctoken = me.hprefix;
  1214.         Ext.Array.each(me.hslist, function(rec) {
  1215.             var value = state[rec[0]];
  1216.             if (!Ext.isDefined(value)) {
  1217.                 value = rec[1];
  1218.             }
  1219.             value = encodeURIComponent(value);
  1220.             if (!value) {
  1221.                 ctoken += ':';
  1222.             } else {
  1223.                 var comp = me.compDict[value];
  1224.                 if (Ext.isDefined(comp)) {
  1225.                     ctoken += ":" + comp;
  1226.                 } else {
  1227.                     ctoken += ":=" + value;
  1228.                 }
  1229.             }
  1230.         });
  1231.  
  1232.         return ctoken;
  1233.     },
  1234.  
  1235.     constructor: function(config){
  1236.         var me = this;
  1237.  
  1238.         me.callParent([config]);
  1239.  
  1240.         me.UIState = me.decodeHToken(); // set default
  1241.  
  1242.         var history_change_cb = function(token) {
  1243.             //console.log("HC " + token);
  1244.             if (!token) {
  1245.                 var res = window.confirm('Are you sure you want to navigate away from this page?');
  1246.                 if (res){
  1247.                     // process text value and close...
  1248.                     Ext.History.back();
  1249.                 } else {
  1250.                     Ext.History.forward();
  1251.                 }
  1252.                 return;
  1253.             }
  1254.  
  1255.             var newstate = me.decodeHToken(token);
  1256.             Ext.Array.each(me.hslist, function(rec) {
  1257.                 if (typeof newstate[rec[0]] == "undefined") {
  1258.                     return;
  1259.                 }
  1260.                 me.setHV(rec[0], newstate[rec[0]], true);
  1261.             });
  1262.         };
  1263.  
  1264.         var start_token = Ext.History.getToken();
  1265.         if (start_token) {
  1266.             history_change_cb(start_token);
  1267.         } else {
  1268.             var htext = me.encodeHToken(me.UIState);
  1269.             Ext.History.add(htext);
  1270.         }
  1271.  
  1272.         Ext.History.on('change', history_change_cb);
  1273.     },
  1274.  
  1275.     get: function(name, defaultValue){
  1276.         /*jslint confusion: true */
  1277.         var me = this;
  1278.         var data;
  1279.  
  1280.         if (typeof me.UIState[name] != "undefined") {
  1281.             data = { value: me.UIState[name] };
  1282.         } else {
  1283.             data = me.callParent(arguments);
  1284.             if (!data && name === 'GuiCap') {
  1285.                 data = { vms: {}, storage: {}, access: {}, nodes: {}, dc: {} };
  1286.             }
  1287.         }
  1288.  
  1289.         //console.log("GET " + name + " " + Ext.encode(data));
  1290.         return data;
  1291.     },
  1292.  
  1293.     clear: function(name){
  1294.         var me = this;
  1295.  
  1296.         if (typeof me.UIState[name] != "undefined") {
  1297.             me.UIState[name] = null;
  1298.         }
  1299.  
  1300.         me.callParent(arguments);
  1301.     },
  1302.  
  1303.     set: function(name, value){
  1304.         var me = this;
  1305.  
  1306.         //console.log("SET " + name + " " + Ext.encode(value));
  1307.         if (typeof me.UIState[name] != "undefined") {
  1308.             var newvalue = value ? value.value : null;
  1309.             if (me.setHV(name, newvalue, false)) {
  1310.                 var htext = me.encodeHToken(me.UIState);
  1311.                 Ext.History.add(htext);
  1312.             }
  1313.         } else {
  1314.             me.callParent(arguments);
  1315.         }
  1316.     }
  1317. });/* Button features:
  1318.  * - observe selection changes to enable/disable the button using enableFn()
  1319.  * - pop up confirmation dialog using confirmMsg()
  1320.  */
  1321. Ext.define('PVE.button.Button', {
  1322.     extend: 'Ext.button.Button',
  1323.     alias: 'widget.pveButton',
  1324.  
  1325.     // the selection model to observe
  1326.     selModel: undefined,
  1327.  
  1328.     // if 'false' handler will not be called (button disabled)
  1329.     enableFn: function(record) { },
  1330.  
  1331.     // function(record) or text
  1332.     confirmMsg: false,
  1333.  
  1334.     // take special care in confirm box (select no as default).
  1335.     dangerous: false,
  1336.  
  1337.     initComponent: function() {
  1338.         /*jslint confusion: true */
  1339.  
  1340.         var me = this;
  1341.  
  1342.         if (me.handler) {
  1343.             me.realHandler = me.handler;
  1344.  
  1345.             me.handler = function(button, event) {
  1346.                 var rec, msg;
  1347.                 if (me.selModel) {
  1348.                     rec = me.selModel.getSelection()[0];
  1349.                     if (!rec || (me.enableFn(rec) === false)) {
  1350.                         return;
  1351.                     }
  1352.                 }
  1353.  
  1354.                 if (me.confirmMsg) {
  1355.                     msg = me.confirmMsg;
  1356.                     if (Ext.isFunction(me.confirmMsg)) {
  1357.                         msg = me.confirmMsg(rec);
  1358.                     }
  1359.                     Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1;
  1360.                     Ext.Msg.show({
  1361.                         title: gettext('Confirm'),
  1362.                         icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION,
  1363.                         msg: msg,
  1364.                         buttons: Ext.Msg.YESNO,
  1365.                         callback: function(btn) {
  1366.                             if (btn !== 'yes') {
  1367.                                 return;
  1368.                             }
  1369.                             me.realHandler(button, event, rec);
  1370.                         }
  1371.                     });
  1372.                 } else {
  1373.                     me.realHandler(button, event, rec);
  1374.                 }
  1375.             };
  1376.         }
  1377.  
  1378.         me.callParent();
  1379.  
  1380.         if (me.selModel) {
  1381.  
  1382.             me.mon(me.selModel, "selectionchange", function() {
  1383.                 var rec = me.selModel.getSelection()[0];
  1384.                 if (!rec || (me.enableFn(rec) === false)) {
  1385.                     me.setDisabled(true);
  1386.                 } else  {
  1387.                     me.setDisabled(false);
  1388.                 }
  1389.             });
  1390.         }
  1391.     }
  1392. });
  1393. Ext.define('PVE.qemu.SendKeyMenu', {
  1394.     extend: 'Ext.button.Button',
  1395.     alias: ['widget.pveQemuSendKeyMenu'],
  1396.  
  1397.     initComponent : function() {
  1398.         var me = this;
  1399.  
  1400.         if (!me.nodename) {
  1401.             throw "no node name specified";
  1402.         }
  1403.  
  1404.         if (!me.vmid) {
  1405.             throw "no VM ID specified";
  1406.         }
  1407.  
  1408.         var sendKey = function(key) {
  1409.             PVE.Utils.API2Request({
  1410.                 params: { key: key },
  1411.                 url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/sendkey",
  1412.                 method: 'PUT',
  1413.                 waitMsgTarget: me,
  1414.                 failure: function(response, opts) {
  1415.                     Ext.Msg.alert('Error', response.htmlStatus);
  1416.                 }
  1417.             });
  1418.         };
  1419.  
  1420.         Ext.apply(me, {
  1421.             text: 'SendKey',
  1422.             menu: new Ext.menu.Menu({
  1423.                 height: 200,
  1424.                 items: [
  1425.                     {
  1426.                         text: 'Tab', handler: function() {
  1427.                             sendKey('tab');
  1428.                         }
  1429.                     },
  1430.                     {
  1431.                         text: 'Ctrl-Alt-Delete', handler: function() {
  1432.                             sendKey('ctrl-alt-delete');
  1433.                         }
  1434.                     },
  1435.                     {
  1436.                         text: 'Ctrl-Alt-Backspace', handler: function() {
  1437.                             sendKey('ctrl-alt-backspace');
  1438.                     }
  1439.                     },
  1440.                     {
  1441.                         text: 'Ctrl-Alt-F1', handler: function() {
  1442.                             sendKey('ctrl-alt-f1');
  1443.                         }
  1444.                     },
  1445.                     {
  1446.                         text: 'Ctrl-Alt-F2', handler: function() {
  1447.                             sendKey('ctrl-alt-f2');
  1448.                         }
  1449.                     },
  1450.                     {
  1451.                         text: 'Ctrl-Alt-F3', handler: function() {
  1452.                             sendKey('ctrl-alt-f3');
  1453.                         }
  1454.                     },
  1455.                     {
  1456.                         text: 'Ctrl-Alt-F4', handler: function() {
  1457.                         sendKey('ctrl-alt-f4');
  1458.                         }
  1459.                     },
  1460.                     {
  1461.                         text: 'Ctrl-Alt-F5', handler: function() {
  1462.                             sendKey('ctrl-alt-f5');
  1463.                         }
  1464.                     },
  1465.                     {
  1466.                         text: 'Ctrl-Alt-F6', handler: function() {
  1467.                             sendKey('ctrl-alt-f6');
  1468.                         }
  1469.                     },
  1470.                     {
  1471.                         text: 'Ctrl-Alt-F7', handler: function() {
  1472.                             sendKey('ctrl-alt-f7');
  1473.                         }
  1474.                     },
  1475.                     {
  1476.                         text: 'Ctrl-Alt-F8', handler: function() {
  1477.                             sendKey('ctrl-alt-f8');
  1478.                         }
  1479.                     },
  1480.                     {
  1481.                         text: 'Ctrl-Alt-F9', handler: function() {
  1482.                             sendKey('ctrl-alt-f9');
  1483.                         }
  1484.                     },
  1485.                     {
  1486.                         text: 'Ctrl-Alt-F10', handler: function() {
  1487.                             sendKey('ctrl-alt-f10');
  1488.                         }
  1489.                     },
  1490.                     {
  1491.                         text: 'Ctrl-Alt-F11', handler: function() {
  1492.                             sendKey('ctrl-alt-f11');
  1493.                         }
  1494.                     },
  1495.                     {
  1496.                         text: 'Ctrl-Alt-F12', handler: function() {
  1497.                             sendKey('ctrl-alt-f12');
  1498.                         }
  1499.                     }
  1500.                 ]
  1501.             })
  1502.         });
  1503.  
  1504.         me.callParent();
  1505.     }
  1506. });
  1507. Ext.define('PVE.qemu.CmdMenu', {
  1508.     extend: 'Ext.menu.Menu',
  1509.  
  1510.     initComponent: function() {
  1511.         var me = this;
  1512.  
  1513.         var nodename = me.pveSelNode.data.node;
  1514.         if (!nodename) {
  1515.             throw "no node name specified";
  1516.         }
  1517.  
  1518.         var vmid = me.pveSelNode.data.vmid;
  1519.         if (!vmid) {
  1520.             throw "no VM ID specified";
  1521.         }
  1522.  
  1523.         var vmname = me.pveSelNode.data.name;
  1524.  
  1525.         var vm_command = function(cmd, params) {
  1526.             PVE.Utils.API2Request({
  1527.                 params: params,
  1528.                 url: '/nodes/' + nodename + '/qemu/' + vmid + "/status/" + cmd,
  1529.                 method: 'POST',
  1530.                 failure: function(response, opts) {
  1531.                     Ext.Msg.alert('Error', response.htmlStatus);
  1532.                 }
  1533.             });
  1534.         };
  1535.  
  1536.         me.title = "VM " + vmid;
  1537.  
  1538.         me.items = [
  1539.             {
  1540.                 text: gettext('Start'),
  1541.                 icon: '/pve2/images/start.png',
  1542.                 handler: function() {
  1543.                     vm_command('start');
  1544.                 }
  1545.             },
  1546.             {
  1547.                 text: gettext('Migrate'),
  1548.                 icon: '/pve2/images/forward.png',
  1549.                 handler: function() {
  1550.                     var win = Ext.create('PVE.window.Migrate', {
  1551.                         vmtype: 'qemu',
  1552.                         nodename: nodename,
  1553.                         vmid: vmid
  1554.                     });
  1555.                     win.show();
  1556.                 }
  1557.             },
  1558.             {
  1559.                 text: gettext('Shutdown'),
  1560.                 icon: '/pve2/images/stop.png',
  1561.                 handler: function() {
  1562.                     var msg = Ext.String.format(gettext("Do you really want to shutdown VM {0}?"), vmid);
  1563.                     Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
  1564.                         if (btn !== 'yes') {
  1565.                             return;
  1566.                         }
  1567.  
  1568.                         vm_command('shutdown', { timeout: 30 });
  1569.                     });
  1570.                 }
  1571.             },
  1572.             {
  1573.                 text: gettext('Stop'),
  1574.                 icon: '/pve2/images/gtk-stop.png',
  1575.                 handler: function() {
  1576.                     var msg = Ext.String.format(gettext("Do you really want to stop VM {0}?"), vmid);
  1577.                     Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
  1578.                         if (btn !== 'yes') {
  1579.                             return;
  1580.                         }
  1581.  
  1582.                         vm_command("stop", { timeout: 30 });
  1583.                     });
  1584.                 }
  1585.             },
  1586.             {
  1587.                 text: gettext('Console'),
  1588.                 icon: '/pve2/images/display.png',
  1589.                 handler: function() {
  1590.                     PVE.Utils.openConoleWindow('kvm', vmid, nodename, vmname);
  1591.                 }
  1592.             }
  1593.         ];
  1594.  
  1595.         me.callParent();
  1596.     }
  1597. });
  1598. Ext.define('PVE.openvz.CmdMenu', {
  1599.     extend: 'Ext.menu.Menu',
  1600.  
  1601.     initComponent: function() {
  1602.         var me = this;
  1603.  
  1604.         var nodename = me.pveSelNode.data.node;
  1605.         if (!nodename) {
  1606.             throw "no node name specified";
  1607.         }
  1608.  
  1609.         var vmid = me.pveSelNode.data.vmid;
  1610.         if (!vmid) {
  1611.             throw "no VM ID specified";
  1612.         }
  1613.  
  1614.         var vmname = me.pveSelNode.data.name;
  1615.  
  1616.         var vm_command = function(cmd, params) {
  1617.             PVE.Utils.API2Request({
  1618.                 params: params,
  1619.                 url: '/nodes/' + nodename + '/openvz/' + vmid + "/status/" + cmd,
  1620.                 method: 'POST',
  1621.                 failure: function(response, opts) {
  1622.                     Ext.Msg.alert('Error', response.htmlStatus);
  1623.                 }
  1624.             });
  1625.         };
  1626.  
  1627.         me.title = "CT " + vmid;
  1628.  
  1629.         me.items = [
  1630.             {
  1631.                 text: gettext('Start'),
  1632.                 icon: '/pve2/images/start.png',
  1633.                 handler: function() {
  1634.                     vm_command('start');
  1635.                 }
  1636.             },
  1637.             {
  1638.                 text: gettext('Migrate'),
  1639.                 icon: '/pve2/images/forward.png',
  1640.                 handler: function() {
  1641.                     var win = Ext.create('PVE.window.Migrate', {
  1642.                         vmtype: 'openvz',
  1643.                         nodename: nodename,
  1644.                         vmid: vmid
  1645.                     });
  1646.                     win.show();
  1647.                 }
  1648.             },
  1649.             {
  1650.                 text: gettext('Shutdown'),
  1651.                 icon: '/pve2/images/stop.png',
  1652.                 handler: function() {
  1653.                     var msg = Ext.String.format(gettext("Do you really want to shutdown VM {0}?"), vmid);
  1654.                     Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
  1655.                         if (btn !== 'yes') {
  1656.                             return;
  1657.                         }
  1658.  
  1659.                         vm_command('shutdown');
  1660.                     });
  1661.                 }
  1662.             },
  1663.             {
  1664.                 text: gettext('Stop'),
  1665.                 icon: '/pve2/images/gtk-stop.png',
  1666.                 handler: function() {
  1667.                     var msg = Ext.String.format(gettext("Do you really want to stop VM {0}?"), vmid);
  1668.                     Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
  1669.                         if (btn !== 'yes') {
  1670.                             return;
  1671.                         }
  1672.  
  1673.                         vm_command("stop");
  1674.                     });
  1675.                 }
  1676.             },
  1677.             {
  1678.                 text: gettext('Console'),
  1679.                 icon: '/pve2/images/display.png',
  1680.                 handler: function() {
  1681.                     PVE.Utils.openConoleWindow('openvz', vmid, nodename, vmname);
  1682.                 }
  1683.             }
  1684.         ];
  1685.  
  1686.         me.callParent();
  1687.     }
  1688. });
  1689. PVE_vnc_console_event = function(appletid, action, err) {
  1690.     //console.log("TESTINIT param1 " + appletid + " action " + action);
  1691.  
  1692.     if (action === "error") {
  1693.         var compid = appletid.replace("-vncapp", "");
  1694.         var comp = Ext.getCmp(compid);
  1695.  
  1696.         if (!comp || !comp.vmid || !comp.toplevel) {
  1697.             return;
  1698.         }
  1699.  
  1700.         // try to detect migrated VM
  1701.         PVE.Utils.API2Request({
  1702.             url: '/cluster/resources',
  1703.             method: 'GET',
  1704.             success: function(response) {
  1705.                 var list = response.result.data;
  1706.                 Ext.Array.each(list, function(item) {
  1707.                     if (item.type === 'qemu' && item.vmid == comp.vmid) {
  1708.                         if (item.node !== comp.nodename) {
  1709.                             //console.log("MOVED VM to node " + item.node);
  1710.                             comp.nodename = item.node;
  1711.                             comp.url = "/nodes/" + comp.nodename + "/" + item.type + "/" + comp.vmid + "/vncproxy";
  1712.                             //console.log("NEW URL " + comp.url);
  1713.                             comp.reloadApplet();
  1714.                         }
  1715.                         return false; // break
  1716.                     }
  1717.                 });
  1718.             }
  1719.         });
  1720.     }
  1721.  
  1722.     return;
  1723.     /*
  1724.       var el = Ext.get(appletid);
  1725.       if (!el)
  1726.       return;
  1727.  
  1728.       if (action === "close") {
  1729.       //        el.remove();
  1730.       } else if (action === "error") {
  1731.       //        console.log("TESTERROR: " + err);
  1732.       //        var compid = appletid.replace("-vncapp", "");
  1733.       //        var comp = Ext.getCmp(compid);
  1734.       }
  1735.  
  1736.       //Ext.get('mytestid').remove();
  1737.       */
  1738.  
  1739. };
  1740.  
  1741. Ext.define('PVE.VNCConsole', {
  1742.     extend: 'Ext.panel.Panel',
  1743.     alias: ['widget.pveVNCConsole'],
  1744.  
  1745.     initComponent : function() {
  1746.         var me = this;
  1747.  
  1748.         if (!me.url) {
  1749.             throw "no url specified";
  1750.         }
  1751.  
  1752.         var myid = me.id + "-vncapp";
  1753.  
  1754.         me.appletID = myid;
  1755.  
  1756.         var box = Ext.create('Ext.Component', {
  1757.             border: false,
  1758.             html: ""
  1759.         });
  1760.  
  1761.         var resize_window = function() {
  1762.             //console.log("resize");
  1763.  
  1764.             var applet = Ext.getDom(myid);
  1765.             //console.log("resize " + myid + " " + applet);
  1766.  
  1767.             // try again when dom element is available
  1768.             if (!(applet && Ext.isFunction(applet.getPreferredSize))) {
  1769.                 return Ext.Function.defer(resize_window, 1000);
  1770.             }
  1771.  
  1772.             var tbar = me.getDockedItems("[dock=top]")[0];
  1773.             var tbh = tbar ? tbar.getHeight() : 0;
  1774.             var ps = applet.getPreferredSize();
  1775.             var aw = ps.width;
  1776.             var ah = ps.height;
  1777.  
  1778.             if (aw < 640) { aw = 640; }
  1779.             if (ah < 400) { ah = 400; }
  1780.  
  1781.             var oh;
  1782.             var ow;
  1783.  
  1784.             //console.log("size0 " + aw + " " + ah + " tbh " + tbh);
  1785.  
  1786.             if (window.innerHeight) {
  1787.                 oh = window.innerHeight;
  1788.                 ow = window.innerWidth;
  1789.             } else if (document.documentElement &&
  1790.                        document.documentElement.clientHeight) {
  1791.                 oh = document.documentElement.clientHeight;
  1792.                 ow = document.documentElement.clientWidth;
  1793.             } else if (document.body) {
  1794.                 oh = document.body.clientHeight;
  1795.                 ow = document.body.clientWidth;
  1796.             }  else {
  1797.                 throw "can't get window size";
  1798.             }
  1799.  
  1800.             Ext.fly(applet).setSize(aw, ah + tbh);
  1801.  
  1802.             var offsetw = aw - ow;
  1803.             var offseth = ah + tbh - oh;
  1804.  
  1805.             if (offsetw !== 0 || offseth !== 0) {
  1806.                 //console.log("try resize by " + offsetw + " " + offseth);
  1807.                 try { window.resizeBy(offsetw, offseth); } catch (e) {}
  1808.             }
  1809.  
  1810.             Ext.Function.defer(resize_window, 1000);
  1811.         };
  1812.  
  1813.         var resize_box = function() {
  1814.             var applet = Ext.getDom(myid);
  1815.  
  1816.             if ((applet && Ext.isFunction(applet.getPreferredSize))) {
  1817.                 var ps = applet.getPreferredSize();
  1818.                 Ext.fly(applet).setSize(ps.width, ps.height);
  1819.             }
  1820.  
  1821.             Ext.Function.defer(resize_box, 1000);
  1822.         };
  1823.  
  1824.         var start_vnc_viewer = function(param) {
  1825.             var cert = param.cert;
  1826.             cert = cert.replace(/\n/g, "|");
  1827.  
  1828.             box.update({
  1829.                 id: myid,
  1830.                 border: false,
  1831.                 tag: 'applet',
  1832.                 code: 'com.tigervnc.vncviewer.VncViewer',
  1833.                 archive: '/vncterm/VncViewer.jar',
  1834.                 // NOTE: set size to '100%' -  else resize does not work
  1835.                 width: "100%",
  1836.                 height: "100%",
  1837.                 cn: [
  1838.                     {tag: 'param', name: 'id', value: myid},
  1839.                     {tag: 'param', name: 'PORT', value: param.port},
  1840.                     {tag: 'param', name: 'PASSWORD', value: param.ticket},
  1841.                     {tag: 'param', name: 'USERNAME', value: param.user},
  1842.                     {tag: 'param', name: 'Show Controls', value: 'No'},
  1843.                     {tag: 'param', name: 'Offer Relogin', value: 'No'},
  1844.                     {tag: 'param', name: 'PVECert', value: cert}
  1845.                 ]
  1846.             });
  1847.             if (me.toplevel) {
  1848.                 Ext.Function.defer(resize_window, 1000);
  1849.             } else {
  1850.                 Ext.Function.defer(resize_box, 1000);
  1851.             }
  1852.         };
  1853.  
  1854.         Ext.apply(me, {
  1855.             layout: 'fit',
  1856.             border: false,
  1857.             autoScroll: me.toplevel ? false : true,
  1858.             items: box,
  1859.             reloadApplet: function() {
  1860.                 PVE.Utils.API2Request({
  1861.                     url: me.url,
  1862.                     params: me.params,
  1863.                     method: me.method || 'POST',
  1864.                     failure: function(response, opts) {
  1865.                         box.update(gettext('Error') + ' ' + response.htmlStatus);
  1866.                     },
  1867.                     success: function(response, opts) {
  1868.                         start_vnc_viewer(response.result.data);
  1869.                     }
  1870.                 });
  1871.             }
  1872.         });
  1873.  
  1874.         me.callParent();
  1875.  
  1876.         if (me.toplevel) {
  1877.             me.on("render", function() { me.reloadApplet();});
  1878.         } else {
  1879.             me.on("show", function() { me.reloadApplet();});
  1880.             me.on("hide", function() { box.update(""); });
  1881.         }
  1882.     }
  1883. });
  1884.  
  1885. Ext.define('PVE.KVMConsole', {
  1886.     extend: 'PVE.VNCConsole',
  1887.     alias: ['widget.pveKVMConsole'],
  1888.  
  1889.     initComponent : function() {
  1890.         var me = this;
  1891.  
  1892.         if (!me.nodename) {
  1893.             throw "no node name specified";
  1894.         }
  1895.  
  1896.         if (!me.vmid) {
  1897.             throw "no VM ID specified";
  1898.         }
  1899.  
  1900.         var vm_command = function(cmd, params, reload_applet) {
  1901.             PVE.Utils.API2Request({
  1902.                 params: params,
  1903.                 url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/status/" + cmd,
  1904.                 method: 'POST',
  1905.                 waitMsgTarget: me,
  1906.                 failure: function(response, opts) {
  1907.                     Ext.Msg.alert('Error', response.htmlStatus);
  1908.                 },
  1909.                 success: function() {
  1910.                     if (reload_applet) {
  1911.                         Ext.Function.defer(me.reloadApplet, 1000, me);
  1912.                     }
  1913.                 }
  1914.             });
  1915.         };
  1916.  
  1917.         var tbar = [
  1918.             {
  1919.                 text: gettext('Start'),
  1920.                 handler: function() {
  1921.                     vm_command("start", {}, 1);
  1922.                 }
  1923.             },
  1924.             {
  1925.                 text: gettext('Shutdown'),
  1926.                 handler: function() {
  1927.                     var msg = Ext.String.format(gettext("Do you really want to shutdown VM {0}?"), me.vmid);
  1928.                     Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
  1929.                         if (btn !== 'yes') {
  1930.                             return;
  1931.                         }
  1932.                         vm_command('shutdown', {timeout: 30});
  1933.                     });
  1934.                 }
  1935.             },
  1936.             {
  1937.                 text: gettext('Stop'),
  1938.                 handler: function() {
  1939.                     var msg = Ext.String.format(gettext("Do you really want to stop VM {0}?"), me.vmid);
  1940.                     Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
  1941.                         if (btn !== 'yes') {
  1942.                             return;
  1943.                         }
  1944.                         vm_command("stop", { timeout: 30});
  1945.                     });
  1946.                 }
  1947.             },
  1948.             {
  1949.                 xtype: 'pveQemuSendKeyMenu',
  1950.                 nodename: me.nodename,
  1951.                 vmid: me.vmid
  1952.             },
  1953.             {
  1954.                 text: gettext('Reset'),
  1955.                 handler: function() {
  1956.                     var msg = Ext.String.format(gettext("Do you really want to reset VM {0}?"), me.vmid);
  1957.                     Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
  1958.                         if (btn !== 'yes') {
  1959.                             return;
  1960.                         }
  1961.                         vm_command("reset");
  1962.                     });
  1963.                 }
  1964.             },
  1965.             {
  1966.                 text: gettext('Suspend'),
  1967.                 handler: function() {
  1968.                     var msg = Ext.String.format(gettext("Do you really want to suspend VM {0}?"), me.vmid);
  1969.                     Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
  1970.                         if (btn !== 'yes') {
  1971.                             return;
  1972.                         }
  1973.                         vm_command("suspend");
  1974.                     });
  1975.                 }
  1976.             },
  1977.             {
  1978.                 text: gettext('Resume'),
  1979.                 handler: function() {
  1980.                     vm_command("resume");
  1981.                 }
  1982.             },
  1983.             // Note: no migrate here, because we can't display migrate log
  1984.             {
  1985.                 text: gettext('Console'),
  1986.                 handler: function() {
  1987.                     PVE.Utils.openConoleWindow('kvm', me.vmid, me.nodename, me.vmname);
  1988.                 }
  1989.             },
  1990.             '->',
  1991.             {
  1992.                 text: gettext('Refresh'),
  1993.                 handler: function() {
  1994.                     var applet = Ext.getDom(me.appletID);
  1995.                     applet.sendRefreshRequest();
  1996.                 }
  1997.             },
  1998.             {
  1999.                 text: gettext('Reload'),
  2000.                 handler: function () {
  2001.                     me.reloadApplet();
  2002.                 }
  2003.             }
  2004.         ];
  2005.  
  2006.         Ext.apply(me, {
  2007.             tbar: tbar,
  2008.             url: "/nodes/" + me.nodename + "/qemu/" + me.vmid + "/vncproxy"
  2009.         });
  2010.  
  2011.         me.callParent();
  2012.     }
  2013. });
  2014.  
  2015. Ext.define('PVE.OpenVZConsole', {
  2016.     extend: 'PVE.VNCConsole',
  2017.     alias: ['widget.pveOpenVZConsole'],
  2018.  
  2019.     initComponent : function() {
  2020.         var me = this;
  2021.  
  2022.         if (!me.nodename) {
  2023.             throw "no node name specified";
  2024.         }
  2025.  
  2026.         if (!me.vmid) {
  2027.             throw "no VM ID specified";
  2028.         }
  2029.  
  2030.         var vm_command = function(cmd, params, reload_applet) {
  2031.             PVE.Utils.API2Request({
  2032.                 params: params,
  2033.                 url: '/nodes/' + me.nodename + '/openvz/' + me.vmid + "/status/" + cmd,
  2034.                 waitMsgTarget: me,
  2035.                 method: 'POST',
  2036.                 failure: function(response, opts) {
  2037.                     Ext.Msg.alert('Error', response.htmlStatus);
  2038.                 },
  2039.                 success: function() {
  2040.                     if (reload_applet) {
  2041.                         Ext.Function.defer(me.reloadApplet, 1000, me);
  2042.                     }
  2043.                 }
  2044.             });
  2045.         };
  2046.  
  2047.         var tbar = [
  2048.             {
  2049.                 text: gettext('Start'),
  2050.                 handler: function() {
  2051.                     vm_command("start", {}, 1);
  2052.                 }
  2053.             },
  2054.             {
  2055.                 text: gettext('Shutdown'),
  2056.                 handler: function() {
  2057.                     var msg = Ext.String.format(gettext("Do you really want to shutdown VM {0}?"), me.vmid);
  2058.                     Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
  2059.                         if (btn !== 'yes') {
  2060.                             return;
  2061.                         }
  2062.                         vm_command("shutdown");
  2063.                     });
  2064.                 }
  2065.             },
  2066.             {
  2067.                 text: gettext('Stop'),
  2068.                 handler: function() {
  2069.                     var msg = Ext.String.format(gettext("Do you really want to stop VM {0}?"), me.vmid);
  2070.                     Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
  2071.                         if (btn !== 'yes') {
  2072.                             return;
  2073.                         }
  2074.                         vm_command("stop");
  2075.                     });
  2076.                 }
  2077.             },
  2078.             // Note: no migrate here, because we can't display migrate log
  2079.             // and openvz migrate does not work if console is open
  2080.             {
  2081.                 text: gettext('Console'),
  2082.                 handler: function() {
  2083.                     PVE.Utils.openConoleWindow('openvz', me.vmid, me.nodename, me.vmname);
  2084.                 }
  2085.             },
  2086.             '->',
  2087.             {
  2088.                 text: gettext('Refresh'),
  2089.                 handler: function() {
  2090.                     var applet = Ext.getDom(me.appletID);
  2091.                     applet.sendRefreshRequest();
  2092.                 }
  2093.             },
  2094.             {
  2095.                 text: gettext('Reload'),
  2096.                 handler: function () {
  2097.                     me.reloadApplet();
  2098.                 }
  2099.             }
  2100.         ];
  2101.  
  2102.         Ext.apply(me, {
  2103.             tbar: tbar,
  2104.             url: "/nodes/" + me.nodename + "/openvz/" + me.vmid + "/vncproxy"
  2105.         });
  2106.  
  2107.         me.callParent();
  2108.     }
  2109. });
  2110.  
  2111. Ext.define('PVE.Shell', {
  2112.     extend: 'PVE.VNCConsole',
  2113.     alias: ['widget.pveShell'],
  2114.  
  2115.     initComponent : function() {
  2116.         var me = this;
  2117.  
  2118.         if (!me.nodename) {
  2119.             throw "no node name specified";
  2120.         }
  2121.  
  2122.         var tbar = [
  2123.            '->',
  2124.             {
  2125.                 text: gettext('Refresh'),
  2126.                 handler: function() {
  2127.                     var applet = Ext.getDom(me.appletID);
  2128.                     applet.sendRefreshRequest();
  2129.                 }
  2130.             },
  2131.             {
  2132.                 text: gettext('Reload'),
  2133.                 handler: function () { me.reloadApplet(); }
  2134.             },
  2135.             {
  2136.                 text: gettext('Shell'),
  2137.                 handler: function() {
  2138.                     PVE.Utils.openConoleWindow('shell', undefined, me.nodename);
  2139.                 }
  2140.             }
  2141.         ];
  2142.  
  2143.         Ext.apply(me, {
  2144.             tbar: tbar,
  2145.             url: "/nodes/" + me.nodename + "/vncshell"
  2146.         });
  2147.  
  2148.         me.callParent();
  2149.     }
  2150. });Ext.define('PVE.data.TimezoneStore', {
  2151.     extend: 'Ext.data.Store',
  2152.  
  2153.     statics: {
  2154.         timezones: [
  2155.             ['Africa/Abidjan'],
  2156.             ['Africa/Accra'],
  2157.             ['Africa/Addis_Ababa'],
  2158.             ['Africa/Algiers'],
  2159.             ['Africa/Asmara'],
  2160.             ['Africa/Bamako'],
  2161.             ['Africa/Bangui'],
  2162.             ['Africa/Banjul'],
  2163.             ['Africa/Bissau'],
  2164.             ['Africa/Blantyre'],
  2165.             ['Africa/Brazzaville'],
  2166.             ['Africa/Bujumbura'],
  2167.             ['Africa/Cairo'],
  2168.             ['Africa/Casablanca'],
  2169.             ['Africa/Ceuta'],
  2170.             ['Africa/Conakry'],
  2171.             ['Africa/Dakar'],
  2172.             ['Africa/Dar_es_Salaam'],
  2173.             ['Africa/Djibouti'],
  2174.             ['Africa/Douala'],
  2175.             ['Africa/El_Aaiun'],
  2176.             ['Africa/Freetown'],
  2177.             ['Africa/Gaborone'],
  2178.             ['Africa/Harare'],
  2179.             ['Africa/Johannesburg'],
  2180.             ['Africa/Kampala'],
  2181.             ['Africa/Khartoum'],
  2182.             ['Africa/Kigali'],
  2183.             ['Africa/Kinshasa'],
  2184.             ['Africa/Lagos'],
  2185.             ['Africa/Libreville'],
  2186.             ['Africa/Lome'],
  2187.             ['Africa/Luanda'],
  2188.             ['Africa/Lubumbashi'],
  2189.             ['Africa/Lusaka'],
  2190.             ['Africa/Malabo'],
  2191.             ['Africa/Maputo'],
  2192.             ['Africa/Maseru'],
  2193.             ['Africa/Mbabane'],
  2194.             ['Africa/Mogadishu'],
  2195.             ['Africa/Monrovia'],
  2196.             ['Africa/Nairobi'],
  2197.             ['Africa/Ndjamena'],
  2198.             ['Africa/Niamey'],
  2199.             ['Africa/Nouakchott'],
  2200.             ['Africa/Ouagadougou'],
  2201.             ['Africa/Porto-Novo'],
  2202.             ['Africa/Sao_Tome'],
  2203.             ['Africa/Tripoli'],
  2204.             ['Africa/Tunis'],
  2205.             ['Africa/Windhoek'],
  2206.             ['America/Adak'],
  2207.             ['America/Anchorage'],
  2208.             ['America/Anguilla'],
  2209.             ['America/Antigua'],
  2210.             ['America/Araguaina'],
  2211.             ['America/Argentina/Buenos_Aires'],
  2212.             ['America/Argentina/Catamarca'],
  2213.             ['America/Argentina/Cordoba'],
  2214.             ['America/Argentina/Jujuy'],
  2215.             ['America/Argentina/La_Rioja'],
  2216.             ['America/Argentina/Mendoza'],
  2217.             ['America/Argentina/Rio_Gallegos'],
  2218.             ['America/Argentina/Salta'],
  2219.             ['America/Argentina/San_Juan'],
  2220.             ['America/Argentina/San_Luis'],
  2221.             ['America/Argentina/Tucuman'],
  2222.             ['America/Argentina/Ushuaia'],
  2223.             ['America/Aruba'],
  2224.             ['America/Asuncion'],
  2225.             ['America/Atikokan'],
  2226.             ['America/Bahia'],
  2227.             ['America/Bahia_Banderas'],
  2228.             ['America/Barbados'],
  2229.             ['America/Belem'],
  2230.             ['America/Belize'],
  2231.             ['America/Blanc-Sablon'],
  2232.             ['America/Boa_Vista'],
  2233.             ['America/Bogota'],
  2234.             ['America/Boise'],
  2235.             ['America/Cambridge_Bay'],
  2236.             ['America/Campo_Grande'],
  2237.             ['America/Cancun'],
  2238.             ['America/Caracas'],
  2239.             ['America/Cayenne'],
  2240.             ['America/Cayman'],
  2241.             ['America/Chicago'],
  2242.             ['America/Chihuahua'],
  2243.             ['America/Costa_Rica'],
  2244.             ['America/Cuiaba'],
  2245.             ['America/Curacao'],
  2246.             ['America/Danmarkshavn'],
  2247.             ['America/Dawson'],
  2248.             ['America/Dawson_Creek'],
  2249.             ['America/Denver'],
  2250.             ['America/Detroit'],
  2251.             ['America/Dominica'],
  2252.             ['America/Edmonton'],
  2253.             ['America/Eirunepe'],
  2254.             ['America/El_Salvador'],
  2255.             ['America/Fortaleza'],
  2256.             ['America/Glace_Bay'],
  2257.             ['America/Godthab'],
  2258.             ['America/Goose_Bay'],
  2259.             ['America/Grand_Turk'],
  2260.             ['America/Grenada'],
  2261.             ['America/Guadeloupe'],
  2262.             ['America/Guatemala'],
  2263.             ['America/Guayaquil'],
  2264.             ['America/Guyana'],
  2265.             ['America/Halifax'],
  2266.             ['America/Havana'],
  2267.             ['America/Hermosillo'],
  2268.             ['America/Indiana/Indianapolis'],
  2269.             ['America/Indiana/Knox'],
  2270.             ['America/Indiana/Marengo'],
  2271.             ['America/Indiana/Petersburg'],
  2272.             ['America/Indiana/Tell_City'],
  2273.             ['America/Indiana/Vevay'],
  2274.             ['America/Indiana/Vincennes'],
  2275.             ['America/Indiana/Winamac'],
  2276.             ['America/Inuvik'],
  2277.             ['America/Iqaluit'],
  2278.             ['America/Jamaica'],
  2279.             ['America/Juneau'],
  2280.             ['America/Kentucky/Louisville'],
  2281.             ['America/Kentucky/Monticello'],
  2282.             ['America/La_Paz'],
  2283.             ['America/Lima'],
  2284.             ['America/Los_Angeles'],
  2285.             ['America/Maceio'],
  2286.             ['America/Managua'],
  2287.             ['America/Manaus'],
  2288.             ['America/Marigot'],
  2289.             ['America/Martinique'],
  2290.             ['America/Matamoros'],
  2291.             ['America/Mazatlan'],
  2292.             ['America/Menominee'],
  2293.             ['America/Merida'],
  2294.             ['America/Mexico_City'],
  2295.             ['America/Miquelon'],
  2296.             ['America/Moncton'],
  2297.             ['America/Monterrey'],
  2298.             ['America/Montevideo'],
  2299.             ['America/Montreal'],
  2300.             ['America/Montserrat'],
  2301.             ['America/Nassau'],
  2302.             ['America/New_York'],
  2303.             ['America/Nipigon'],
  2304.             ['America/Nome'],
  2305.             ['America/Noronha'],
  2306.             ['America/North_Dakota/Center'],
  2307.             ['America/North_Dakota/New_Salem'],
  2308.             ['America/Ojinaga'],
  2309.             ['America/Panama'],
  2310.             ['America/Pangnirtung'],
  2311.             ['America/Paramaribo'],
  2312.             ['America/Phoenix'],
  2313.             ['America/Port-au-Prince'],
  2314.             ['America/Port_of_Spain'],
  2315.             ['America/Porto_Velho'],
  2316.             ['America/Puerto_Rico'],
  2317.             ['America/Rainy_River'],
  2318.             ['America/Rankin_Inlet'],
  2319.             ['America/Recife'],
  2320.             ['America/Regina'],
  2321.             ['America/Resolute'],
  2322.             ['America/Rio_Branco'],
  2323.             ['America/Santa_Isabel'],
  2324.             ['America/Santarem'],
  2325.             ['America/Santiago'],
  2326.             ['America/Santo_Domingo'],
  2327.             ['America/Sao_Paulo'],
  2328.             ['America/Scoresbysund'],
  2329.             ['America/Shiprock'],
  2330.             ['America/St_Barthelemy'],
  2331.             ['America/St_Johns'],
  2332.             ['America/St_Kitts'],
  2333.             ['America/St_Lucia'],
  2334.             ['America/St_Thomas'],
  2335.             ['America/St_Vincent'],
  2336.             ['America/Swift_Current'],
  2337.             ['America/Tegucigalpa'],
  2338.             ['America/Thule'],
  2339.             ['America/Thunder_Bay'],
  2340.             ['America/Tijuana'],
  2341.             ['America/Toronto'],
  2342.             ['America/Tortola'],
  2343.             ['America/Vancouver'],
  2344.             ['America/Whitehorse'],
  2345.             ['America/Winnipeg'],
  2346.             ['America/Yakutat'],
  2347.             ['America/Yellowknife'],
  2348.             ['Antarctica/Casey'],
  2349.             ['Antarctica/Davis'],
  2350.             ['Antarctica/DumontDUrville'],
  2351.             ['Antarctica/Macquarie'],
  2352.             ['Antarctica/Mawson'],
  2353.             ['Antarctica/McMurdo'],
  2354.             ['Antarctica/Palmer'],
  2355.             ['Antarctica/Rothera'],
  2356.             ['Antarctica/South_Pole'],
  2357.             ['Antarctica/Syowa'],
  2358.             ['Antarctica/Vostok'],
  2359.             ['Arctic/Longyearbyen'],
  2360.             ['Asia/Aden'],
  2361.             ['Asia/Almaty'],
  2362.             ['Asia/Amman'],
  2363.             ['Asia/Anadyr'],
  2364.             ['Asia/Aqtau'],
  2365.             ['Asia/Aqtobe'],
  2366.             ['Asia/Ashgabat'],
  2367.             ['Asia/Baghdad'],
  2368.             ['Asia/Bahrain'],
  2369.             ['Asia/Baku'],
  2370.             ['Asia/Bangkok'],
  2371.             ['Asia/Beirut'],
  2372.             ['Asia/Bishkek'],
  2373.             ['Asia/Brunei'],
  2374.             ['Asia/Choibalsan'],
  2375.             ['Asia/Chongqing'],
  2376.             ['Asia/Colombo'],
  2377.             ['Asia/Damascus'],
  2378.             ['Asia/Dhaka'],
  2379.             ['Asia/Dili'],
  2380.             ['Asia/Dubai'],
  2381.             ['Asia/Dushanbe'],
  2382.             ['Asia/Gaza'],
  2383.             ['Asia/Harbin'],
  2384.             ['Asia/Ho_Chi_Minh'],
  2385.             ['Asia/Hong_Kong'],
  2386.             ['Asia/Hovd'],
  2387.             ['Asia/Irkutsk'],
  2388.             ['Asia/Jakarta'],
  2389.             ['Asia/Jayapura'],
  2390.             ['Asia/Jerusalem'],
  2391.             ['Asia/Kabul'],
  2392.             ['Asia/Kamchatka'],
  2393.             ['Asia/Karachi'],
  2394.             ['Asia/Kashgar'],
  2395.             ['Asia/Kathmandu'],
  2396.             ['Asia/Kolkata'],
  2397.             ['Asia/Krasnoyarsk'],
  2398.             ['Asia/Kuala_Lumpur'],
  2399.             ['Asia/Kuching'],
  2400.             ['Asia/Kuwait'],
  2401.             ['Asia/Macau'],
  2402.             ['Asia/Magadan'],
  2403.             ['Asia/Makassar'],
  2404.             ['Asia/Manila'],
  2405.             ['Asia/Muscat'],
  2406.             ['Asia/Nicosia'],
  2407.             ['Asia/Novokuznetsk'],
  2408.             ['Asia/Novosibirsk'],
  2409.             ['Asia/Omsk'],
  2410.             ['Asia/Oral'],
  2411.             ['Asia/Phnom_Penh'],
  2412.             ['Asia/Pontianak'],
  2413.             ['Asia/Pyongyang'],
  2414.             ['Asia/Qatar'],
  2415.             ['Asia/Qyzylorda'],
  2416.             ['Asia/Rangoon'],
  2417.             ['Asia/Riyadh'],
  2418.             ['Asia/Sakhalin'],
  2419.             ['Asia/Samarkand'],
  2420.             ['Asia/Seoul'],
  2421.             ['Asia/Shanghai'],
  2422.             ['Asia/Singapore'],
  2423.             ['Asia/Taipei'],
  2424.             ['Asia/Tashkent'],
  2425.             ['Asia/Tbilisi'],
  2426.             ['Asia/Tehran'],
  2427.             ['Asia/Thimphu'],
  2428.             ['Asia/Tokyo'],
  2429.             ['Asia/Ulaanbaatar'],
  2430.             ['Asia/Urumqi'],
  2431.             ['Asia/Vientiane'],
  2432.             ['Asia/Vladivostok'],
  2433.             ['Asia/Yakutsk'],
  2434.             ['Asia/Yekaterinburg'],
  2435.             ['Asia/Yerevan'],
  2436.             ['Atlantic/Azores'],
  2437.             ['Atlantic/Bermuda'],
  2438.             ['Atlantic/Canary'],
  2439.             ['Atlantic/Cape_Verde'],
  2440.             ['Atlantic/Faroe'],
  2441.             ['Atlantic/Madeira'],
  2442.             ['Atlantic/Reykjavik'],
  2443.             ['Atlantic/South_Georgia'],
  2444.             ['Atlantic/St_Helena'],
  2445.             ['Atlantic/Stanley'],
  2446.             ['Australia/Adelaide'],
  2447.             ['Australia/Brisbane'],
  2448.             ['Australia/Broken_Hill'],
  2449.             ['Australia/Currie'],
  2450.             ['Australia/Darwin'],
  2451.             ['Australia/Eucla'],
  2452.             ['Australia/Hobart'],
  2453.             ['Australia/Lindeman'],
  2454.             ['Australia/Lord_Howe'],
  2455.             ['Australia/Melbourne'],
  2456.             ['Australia/Perth'],
  2457.             ['Australia/Sydney'],
  2458.             ['Europe/Amsterdam'],
  2459.             ['Europe/Andorra'],
  2460.             ['Europe/Athens'],
  2461.             ['Europe/Belgrade'],
  2462.             ['Europe/Berlin'],
  2463.             ['Europe/Bratislava'],
  2464.             ['Europe/Brussels'],
  2465.             ['Europe/Bucharest'],
  2466.             ['Europe/Budapest'],
  2467.             ['Europe/Chisinau'],
  2468.             ['Europe/Copenhagen'],
  2469.             ['Europe/Dublin'],
  2470.             ['Europe/Gibraltar'],
  2471.             ['Europe/Guernsey'],
  2472.             ['Europe/Helsinki'],
  2473.             ['Europe/Isle_of_Man'],
  2474.             ['Europe/Istanbul'],
  2475.             ['Europe/Jersey'],
  2476.             ['Europe/Kaliningrad'],
  2477.             ['Europe/Kiev'],
  2478.             ['Europe/Lisbon'],
  2479.             ['Europe/Ljubljana'],
  2480.             ['Europe/London'],
  2481.             ['Europe/Luxembourg'],
  2482.             ['Europe/Madrid'],
  2483.             ['Europe/Malta'],
  2484.             ['Europe/Mariehamn'],
  2485.             ['Europe/Minsk'],
  2486.             ['Europe/Monaco'],
  2487.             ['Europe/Moscow'],
  2488.             ['Europe/Oslo'],
  2489.             ['Europe/Paris'],
  2490.             ['Europe/Podgorica'],
  2491.             ['Europe/Prague'],
  2492.             ['Europe/Riga'],
  2493.             ['Europe/Rome'],
  2494.             ['Europe/Samara'],
  2495.             ['Europe/San_Marino'],
  2496.             ['Europe/Sarajevo'],
  2497.             ['Europe/Simferopol'],
  2498.             ['Europe/Skopje'],
  2499.             ['Europe/Sofia'],
  2500.             ['Europe/Stockholm'],
  2501.             ['Europe/Tallinn'],
  2502.             ['Europe/Tirane'],
  2503.             ['Europe/Uzhgorod'],
  2504.             ['Europe/Vaduz'],
  2505.             ['Europe/Vatican'],
  2506.             ['Europe/Vienna'],
  2507.             ['Europe/Vilnius'],
  2508.             ['Europe/Volgograd'],
  2509.             ['Europe/Warsaw'],
  2510.             ['Europe/Zagreb'],
  2511.             ['Europe/Zaporozhye'],
  2512.             ['Europe/Zurich'],
  2513.             ['Indian/Antananarivo'],
  2514.             ['Indian/Chagos'],
  2515.             ['Indian/Christmas'],
  2516.             ['Indian/Cocos'],
  2517.             ['Indian/Comoro'],
  2518.             ['Indian/Kerguelen'],
  2519.             ['Indian/Mahe'],
  2520.             ['Indian/Maldives'],
  2521.             ['Indian/Mauritius'],
  2522.             ['Indian/Mayotte'],
  2523.             ['Indian/Reunion'],
  2524.             ['Pacific/Apia'],
  2525.             ['Pacific/Auckland'],
  2526.             ['Pacific/Chatham'],
  2527.             ['Pacific/Chuuk'],
  2528.             ['Pacific/Easter'],
  2529.             ['Pacific/Efate'],
  2530.             ['Pacific/Enderbury'],
  2531.             ['Pacific/Fakaofo'],
  2532.             ['Pacific/Fiji'],
  2533.             ['Pacific/Funafuti'],
  2534.             ['Pacific/Galapagos'],
  2535.             ['Pacific/Gambier'],
  2536.             ['Pacific/Guadalcanal'],
  2537.             ['Pacific/Guam'],
  2538.             ['Pacific/Honolulu'],
  2539.             ['Pacific/Johnston'],
  2540.             ['Pacific/Kiritimati'],
  2541.             ['Pacific/Kosrae'],
  2542.             ['Pacific/Kwajalein'],
  2543.             ['Pacific/Majuro'],
  2544.             ['Pacific/Marquesas'],
  2545.             ['Pacific/Midway'],
  2546.             ['Pacific/Nauru'],
  2547.             ['Pacific/Niue'],
  2548.             ['Pacific/Norfolk'],
  2549.             ['Pacific/Noumea'],
  2550.             ['Pacific/Pago_Pago'],
  2551.             ['Pacific/Palau'],
  2552.             ['Pacific/Pitcairn'],
  2553.             ['Pacific/Pohnpei'],
  2554.             ['Pacific/Port_Moresby'],
  2555.             ['Pacific/Rarotonga'],
  2556.             ['Pacific/Saipan'],
  2557.             ['Pacific/Tahiti'],
  2558.             ['Pacific/Tarawa'],
  2559.             ['Pacific/Tongatapu'],
  2560.             ['Pacific/Wake'],
  2561.             ['Pacific/Wallis']
  2562.         ]
  2563.     },
  2564.  
  2565.     constructor: function(config) {
  2566.         var me = this;
  2567.  
  2568.         config = config || {};
  2569.  
  2570.         Ext.regModel('Timezone', {
  2571.             fields: ['zone'],
  2572.             proxy: {
  2573.                 type: 'memory',
  2574.                 reader: 'array'
  2575.             }
  2576.         });
  2577.  
  2578.         Ext.apply(config, {
  2579.             model: 'Timezone',
  2580.             data: PVE.data.TimezoneStore.timezones
  2581.         });
  2582.  
  2583.         me.callParent([config]);
  2584.     }
  2585. });/* A reader to store a single JSON Object (hash) into a storage.
  2586.  * Also accepts an array containing a single hash.
  2587.  * So it can read:
  2588.  *
  2589.  * example1: { data: "xyz" }
  2590.  * example2: [ {  data: "xyz" } ]
  2591.  */
  2592.  
  2593. Ext.define('PVE.data.reader.JsonObject', {
  2594.     extend: 'Ext.data.reader.Json',
  2595.     alias : 'reader.jsonobject',
  2596.  
  2597.     root: 'data',
  2598.  
  2599.     constructor: function(config) {
  2600.         var me = this;
  2601.  
  2602.         Ext.apply(me, config || {});
  2603.  
  2604.         me.callParent([config]);
  2605.     },
  2606.  
  2607.     getResponseData: function(response) {
  2608.         var me = this;
  2609.  
  2610.         var data = [];
  2611.         try {
  2612.             var result = Ext.decode(response.responseText);
  2613.             var root = me.getRoot(result);
  2614.             var org_root = root;
  2615.  
  2616.             if (Ext.isArray(org_root)) {
  2617.                 if (org_root.length == 1) {
  2618.                     root = org_root[0];
  2619.                 } else {
  2620.                     root = {};
  2621.                 }
  2622.             }
  2623.  
  2624.             if (me.rows) {
  2625.                 Ext.Object.each(me.rows, function(key, rowdef) {
  2626.                     if (Ext.isDefined(root[key])) {
  2627.                         data.push({key: key, value: root[key]});
  2628.                     } else if (Ext.isDefined(rowdef.defaultValue)) {
  2629.                         data.push({key: key, value: rowdef.defaultValue});
  2630.                     } else if (rowdef.required) {
  2631.                         data.push({key: key, value: undefined});
  2632.                     }
  2633.                 });
  2634.             } else {
  2635.                 Ext.Object.each(root, function(key, value) {
  2636.                     data.push({key: key, value: value });
  2637.                 });
  2638.             }
  2639.         }
  2640.         catch (ex) {
  2641.             Ext.Error.raise({
  2642.                 response: response,
  2643.                 json: response.responseText,
  2644.                 parseError: ex,
  2645.                 msg: 'Unable to parse the JSON returned by the server: ' + ex.toString()
  2646.             });
  2647.         }
  2648.  
  2649.         return data;
  2650.     }
  2651. });
  2652.  
  2653. Ext.define('PVE.RestProxy', {
  2654.     extend: 'Ext.data.RestProxy',
  2655.     alias : 'proxy.pve',
  2656.  
  2657.     constructor: function(config) {
  2658.         var me = this;
  2659.  
  2660.         config = config || {};
  2661.  
  2662.         Ext.applyIf(config, {
  2663.             pageParam : null,
  2664.             startParam: null,
  2665.             limitParam: null,
  2666.             groupParam: null,
  2667.             sortParam: null,
  2668.             filterParam: null,
  2669.             noCache : false,
  2670.             reader: {
  2671.                 type: 'json',
  2672.                 root: config.root || 'data'
  2673.             },
  2674.             afterRequest: function(request, success) {
  2675.                 me.fireEvent('afterload', me, request, success);
  2676.                 return;
  2677.             }
  2678.         });
  2679.  
  2680.         me.callParent([config]);
  2681.     }
  2682.  
  2683. }, function() {
  2684.  
  2685.     Ext.define('pve-domains', {
  2686.         extend: "Ext.data.Model",
  2687.         fields: [ 'realm', 'type', 'comment', 'default',
  2688.                   {
  2689.                       name: 'descr',
  2690.                       // Note: We use this in the RealmComboBox.js
  2691.                       // (see Bug #125)
  2692.                       convert: function(value, record) {
  2693.                           var info = record.data;
  2694.                           var text;
  2695.  
  2696.                           if (value) {
  2697.                               return value;
  2698.                           }
  2699.                           // return realm if there is no comment
  2700.                           return info.comment || info.realm;
  2701.                       }
  2702.                   }
  2703.                 ],
  2704.         proxy: {
  2705.             type: 'pve',
  2706.             url: "/api2/json/access/domains"
  2707.         }
  2708.     });
  2709.  
  2710.     Ext.define('KeyValue', {
  2711.         extend: "Ext.data.Model",
  2712.         fields: [ 'key', 'value' ],
  2713.         idProperty: 'key'
  2714.     });
  2715.  
  2716.     Ext.define('pve-string-list', {
  2717.         extend: 'Ext.data.Model',
  2718.         fields:  [ 'n', 't' ],
  2719.         idProperty: 'n'
  2720.     });
  2721.  
  2722.     Ext.define('pve-tasks', {
  2723.         extend: 'Ext.data.Model',
  2724.         fields:  [
  2725.             { name: 'starttime', type : 'date', dateFormat: 'timestamp' },
  2726.             { name: 'endtime', type : 'date', dateFormat: 'timestamp' },
  2727.             { name: 'pid', type: 'int' },
  2728.             'node', 'upid', 'user', 'status', 'type', 'id'
  2729.         ],
  2730.         idProperty: 'upid'
  2731.     });
  2732.  
  2733.     Ext.define('pve-cluster-log', {
  2734.         extend: 'Ext.data.Model',
  2735.         fields:  [
  2736.             { name: 'uid' , type: 'int' },
  2737.             { name: 'time', type : 'date', dateFormat: 'timestamp' },
  2738.             { name: 'pri', type: 'int' },
  2739.             { name: 'pid', type: 'int' },
  2740.             'node', 'user', 'tag', 'msg',
  2741.             {
  2742.                 name: 'id',
  2743.                 convert: function(value, record) {
  2744.                     var info = record.data;
  2745.                     var text;
  2746.  
  2747.                     if (value) {
  2748.                         return value;
  2749.                     }
  2750.                     // compute unique ID
  2751.                     return info.uid + ':' + info.node;
  2752.                 }
  2753.             }
  2754.         ],
  2755.         idProperty: 'id'
  2756.     });
  2757. });
  2758. // Serialize load (avoid too many parallel connections)
  2759. Ext.define('PVE.data.UpdateQueue', {
  2760.     singleton: true,
  2761.  
  2762.     constructor : function(){
  2763.         var me = this;
  2764.  
  2765.         var queue = [];
  2766.         var queue_idx = {};
  2767.  
  2768.         var idle = true;
  2769.  
  2770.         var start_update = function() {
  2771.             if (!idle) {
  2772.                 return;
  2773.             }
  2774.  
  2775.             var store = queue.shift();
  2776.             if (!store) {
  2777.                 return;
  2778.             }
  2779.  
  2780.             queue_idx[store.storeid] = null;
  2781.  
  2782.             idle = false;
  2783.             store.load({
  2784.                 callback: function(records, operation, success) {
  2785.                     idle = true;
  2786.                     start_update();
  2787.                 }
  2788.             });
  2789.         };
  2790.  
  2791.         Ext.apply(me, {
  2792.             queue: function(store) {
  2793.                 if (!store.storeid) {
  2794.                     throw "unable to queue store without storeid";
  2795.                 }
  2796.                 if (!queue_idx[store.storeid]) {
  2797.                     queue_idx[store.storeid] = store;
  2798.                     queue.push(store);
  2799.                 }
  2800.                 start_update();
  2801.             }
  2802.         });
  2803.     }
  2804. });
  2805. Ext.define('PVE.data.UpdateStore', {
  2806.     extend: 'Ext.data.Store',
  2807.  
  2808.     constructor: function(config) {
  2809.         var me = this;
  2810.  
  2811.         config = config || {};
  2812.  
  2813.         if (!config.interval) {
  2814.             config.interval = 3000;
  2815.         }
  2816.  
  2817.         if (!config.storeid) {
  2818.             throw "no storeid specified";
  2819.         }
  2820.  
  2821.         var load_task = new Ext.util.DelayedTask();
  2822.  
  2823.         var run_load_task = function() {
  2824.             if (PVE.Utils.authOK()) {
  2825.                 PVE.data.UpdateQueue.queue(me);
  2826.                 load_task.delay(config.interval, run_load_task);
  2827.             } else {
  2828.                 load_task.delay(200, run_load_task);
  2829.             }
  2830.         };
  2831.  
  2832.         Ext.apply(config, {
  2833.             startUpdate: function() {
  2834.                 run_load_task();
  2835.             },
  2836.             stopUpdate: function() {
  2837.                 load_task.cancel();
  2838.             }
  2839.         });
  2840.  
  2841.         me.callParent([config]);
  2842.  
  2843.         me.on('destroy', function() {
  2844.             load_task.cancel();
  2845.         });
  2846.     }
  2847. });
  2848. /* Config properties:
  2849.  * rstore: A storage to track changes
  2850.  * Only works if rstore has a model and use 'idProperty'
  2851.  */
  2852. Ext.define('PVE.data.DiffStore', {
  2853.     extend: 'Ext.data.Store',
  2854.  
  2855.     constructor: function(config) {
  2856.         var me = this;
  2857.  
  2858.         config = config || {};
  2859.  
  2860.         if (!config.rstore) {
  2861.             throw "no rstore specified";
  2862.         }
  2863.  
  2864.         if (!config.rstore.model) {
  2865.             throw "no rstore model specified";
  2866.         }
  2867.  
  2868.         var rstore = config.rstore;
  2869.  
  2870.         Ext.apply(config, {
  2871.             model: rstore.model,
  2872.             proxy: { type: 'memory' }
  2873.         });
  2874.  
  2875.         me.callParent([config]);
  2876.  
  2877.         var first_load = true;
  2878.  
  2879.         var cond_add_item = function(data, id) {
  2880.             var olditem = me.getById(id);
  2881.             if (olditem) {
  2882.                 olditem.beginEdit();
  2883.                 me.model.prototype.fields.eachKey(function(field) {
  2884.                     if (olditem.data[field] !== data[field]) {
  2885.                         olditem.set(field, data[field]);
  2886.                     }
  2887.                 });
  2888.                 olditem.endEdit(true);
  2889.                 olditem.commit();
  2890.             } else {
  2891.                 var newrec = Ext.ModelMgr.create(data, me.model, id);
  2892.                 var pos = (me.appendAtStart && !first_load) ? 0 : me.data.length;
  2893.                 me.insert(pos, newrec);
  2894.             }
  2895.         };
  2896.  
  2897.         me.mon(rstore, 'load', function(s, records, success) {
  2898.  
  2899.             if (!success) {
  2900.                 return;
  2901.             }
  2902.  
  2903.             me.suspendEvents();
  2904.  
  2905.             // remove vanished items
  2906.             (me.snapshot || me.data).each(function(olditem) {
  2907.                 var item = rstore.getById(olditem.getId());
  2908.                 if (!item) {
  2909.                     me.remove(olditem);
  2910.                 }
  2911.             });
  2912.  
  2913.             rstore.each(function(item) {
  2914.                 cond_add_item(item.data, item.getId());
  2915.             });
  2916.  
  2917.             me.filter();
  2918.  
  2919.             first_load = false;
  2920.  
  2921.             me.resumeEvents();
  2922.             me.fireEvent('datachanged', me);
  2923.         });
  2924.     }
  2925. });
  2926. Ext.define('PVE.data.ObjectStore',  {
  2927.     extend: 'PVE.data.UpdateStore',
  2928.  
  2929.     constructor: function(config) {
  2930.         var me = this;
  2931.  
  2932.         config = config || {};
  2933.  
  2934.         if (!config.storeid) {
  2935.             config.storeid =  'pve-store-' + (++Ext.idSeed);
  2936.         }
  2937.  
  2938.         Ext.applyIf(config, {
  2939.             model: 'KeyValue',
  2940.             proxy: {
  2941.                 type: 'pve',
  2942.                 url: config.url,
  2943.                 extraParams: config.extraParams,
  2944.                 reader: {
  2945.                     type: 'jsonobject',
  2946.                     rows: config.rows
  2947.                 }
  2948.             }
  2949.         });
  2950.  
  2951.         me.callParent([config]);
  2952.     }
  2953. });
  2954. Ext.define('PVE.data.ResourceStore', {
  2955.     extend: 'PVE.data.UpdateStore',
  2956.     singleton: true,
  2957.  
  2958.     findNextVMID: function() {
  2959.         var me = this, i;
  2960.  
  2961.         for (i = 100; i < 10000; i++) {
  2962.             if (me.findExact('vmid', i) < 0) {
  2963.                 return i;
  2964.             }
  2965.         }
  2966.     },
  2967.  
  2968.     findVMID: function(vmid) {
  2969.         var me = this, i;
  2970.  
  2971.         return (me.findExact('vmid', parseInt(vmid, 10)) >= 0);
  2972.     },
  2973.  
  2974.  
  2975.     constructor: function(config) {
  2976.         // fixme: how to avoid those warnings
  2977.         /*jslint confusion: true */
  2978.  
  2979.         var me = this;
  2980.  
  2981.         config = config || {};
  2982.  
  2983.         var field_defaults = {
  2984.             type: {
  2985.                 header: gettext('Type'),
  2986.                 type: 'text',
  2987.                 renderer: PVE.Utils.render_resource_type,
  2988.                 sortable: true,
  2989.                 hideable: false,
  2990.                 width: 80
  2991.             },
  2992.             id: {
  2993.                 header: 'ID',
  2994.                 type: 'text',
  2995.                 hidden: true,
  2996.                 sortable: true,
  2997.                 width: 80
  2998.             },
  2999.             running: {
  3000.                 header: gettext('Online'),
  3001.                 type: 'boolean',
  3002.                 hidden: true,
  3003.                 convert: function(value, record) {
  3004.                     var info = record.data;
  3005.                     if (info.type === 'qemu' || info.type === 'openvz' || info.type === 'node') {
  3006.                         return (Ext.isNumeric(info.uptime) && (info.uptime > 0));
  3007.                     } else {
  3008.                         return false;
  3009.                     }
  3010.                 }
  3011.             },
  3012.             text: {
  3013.                 header: gettext('Description'),
  3014.                 type: 'text',
  3015.                 sortable: true,
  3016.                 width: 200,
  3017.                 convert: function(value, record) {
  3018.                     var info = record.data;
  3019.                     var text;
  3020.  
  3021.                     if (value) {
  3022.                         return value;
  3023.                     }
  3024.  
  3025.                     if (info.type === 'node') {
  3026.                         text = info.node;
  3027.                     } else if (info.type === 'pool') {
  3028.                         text = info.pool;
  3029.                     } else if (info.type === 'storage') {
  3030.                         text = info.storage + ' (' + info.node + ')';
  3031.                     } else if (info.type === 'qemu' || info.type === 'openvz') {
  3032.                         text = String(info.vmid);
  3033.                         if (info.name) {
  3034.                             text += " (" + info.name + ')';
  3035.                         }
  3036.                     } else {
  3037.                         text = info.id;
  3038.                     }
  3039.                     return text;
  3040.                 }
  3041.             },
  3042.             vmid: {
  3043.                 header: 'VMID',
  3044.                 type: 'integer',
  3045.                 hidden: true,
  3046.                 sortable: true,
  3047.                 width: 80
  3048.             },
  3049.             name: {
  3050.                 header: gettext('Name'),
  3051.                 hidden: true,
  3052.                 sortable: true,
  3053.                 type: 'text'
  3054.             },
  3055.             disk: {
  3056.                 header: gettext('Disk usage'),
  3057.                 type: 'integer',
  3058.                 renderer: PVE.Utils.render_disk_usage,
  3059.                 sortable: true,
  3060.                 width: 100
  3061.             },
  3062.             maxdisk: {
  3063.                 header: gettext('Disk size'),
  3064.                 type: 'integer',
  3065.                 renderer: PVE.Utils.render_size,
  3066.                 sortable: true,
  3067.                 hidden: true,
  3068.                 width: 100
  3069.             },
  3070.             mem: {
  3071.                 header: gettext('Memory usage'),
  3072.                 type: 'integer',
  3073.                 renderer: PVE.Utils.render_mem_usage,
  3074.                 sortable: true,
  3075.                 width: 100
  3076.             },
  3077.             maxmem: {
  3078.                 header: gettext('Memory size'),
  3079.                 type: 'integer',
  3080.                 renderer: PVE.Utils.render_size,
  3081.                 hidden: true,
  3082.                 sortable: true,
  3083.                 width: 100
  3084.             },
  3085.             cpu: {
  3086.                 header: gettext('CPU usage'),
  3087.                 type: 'float',
  3088.                 renderer: PVE.Utils.render_cpu,
  3089.                 sortable: true,
  3090.                 width: 100
  3091.             },
  3092.             maxcpu: {
  3093.                 header: 'maxcpu',
  3094.                 type: 'integer',
  3095.                 hidden: true,
  3096.                 sortable: true,
  3097.                 width: 60
  3098.             },
  3099.             diskread: {
  3100.                 header: 'Total Disk Read',
  3101.                 type: 'integer',
  3102.                 hidden: true,
  3103.                 sortable: true,
  3104.                 renderer: PVE.Utils.format_size,
  3105.                 width: 100
  3106.             },
  3107.             diskwrite: {
  3108.                 header: 'Total Disk Write',
  3109.                 type: 'integer',
  3110.                 hidden: true,
  3111.                 sortable: true,
  3112.                 renderer: PVE.Utils.format_size,
  3113.                 width: 100
  3114.             },
  3115.             netin: {
  3116.                 header: 'Total NetIn',
  3117.                 type: 'integer',
  3118.                 hidden: true,
  3119.                 sortable: true,
  3120.                 renderer: PVE.Utils.format_size,
  3121.                 width: 100
  3122.             },
  3123.             netout: {
  3124.                 header: 'Total NetOut',
  3125.                 type: 'integer',
  3126.                 hidden: true,
  3127.                 sortable: true,
  3128.                 renderer: PVE.Utils.format_size,
  3129.                 width: 100
  3130.             },
  3131.             uptime: {
  3132.                 header: gettext('Uptime'),
  3133.                 type: 'integer',
  3134.                 renderer: PVE.Utils.render_uptime,
  3135.                 sortable: true,
  3136.                 width: 110
  3137.             },
  3138.             node: {
  3139.                 header: gettext('Node'),
  3140.                 type: 'text',
  3141.                 hidden: true,
  3142.                 sortable: true,
  3143.                 width: 110
  3144.             },
  3145.             storage: {
  3146.                 header: gettext('Storage'),
  3147.                 type: 'text',
  3148.                 hidden: true,
  3149.                 sortable: true,
  3150.                 width: 110
  3151.             },
  3152.             pool: {
  3153.                 header: gettext('Pool'),
  3154.                 type: 'text',
  3155.                 hidden: true,
  3156.                 sortable: true,
  3157.                 width: 110
  3158.             }
  3159.         };
  3160.  
  3161.         var fields = [];
  3162.         var fieldNames = [];
  3163.         Ext.Object.each(field_defaults, function(key, value) {
  3164.             if (!Ext.isDefined(value.convert)) {
  3165.                 fields.push({name: key, type: value.type});
  3166.                 fieldNames.push(key);
  3167.             } else if (key === 'text' || key === 'running') {
  3168.                 fields.push({name: key, type: value.type, convert: value.convert});
  3169.                 fieldNames.push(key);
  3170.             }
  3171.         });
  3172.  
  3173.         Ext.define('PVEResources', {
  3174.             extend: "Ext.data.Model",
  3175.             fields: fields,
  3176.             proxy: {
  3177.                 type: 'pve',
  3178.                 url: '/api2/json/cluster/resources'
  3179.             }
  3180.         });
  3181.  
  3182.         Ext.define('PVETree', {
  3183.             extend: "Ext.data.Model",
  3184.             fields: fields,
  3185.             proxy: { type: 'memory' }
  3186.         });
  3187.  
  3188.         Ext.apply(config, {
  3189.             storeid: 'PVEResources',
  3190.             model: 'PVEResources',
  3191.             autoDestory: false,
  3192.             defaultColums: function() {
  3193.                 var res = [];
  3194.                 Ext.Object.each(field_defaults, function(field, info) {
  3195.                     var fi = Ext.apply({ dataIndex: field }, info);
  3196.                     res.push(fi);
  3197.                 });
  3198.                 return res;
  3199.             },
  3200.             fieldNames: fieldNames
  3201.         });
  3202.  
  3203.         me.callParent([config]);
  3204.     }
  3205. });
  3206. Ext.define('PVE.form.Checkbox', {
  3207.     extend: 'Ext.form.field.Checkbox',
  3208.     alias: ['widget.pvecheckbox'],
  3209.  
  3210.     defaultValue: undefined,
  3211.  
  3212.     deleteDefaultValue: false,
  3213.     deleteEmpty: false,
  3214.  
  3215.     inputValue: '1',
  3216.  
  3217.     height: 22, // hack: set same height as text fields
  3218.  
  3219.     getSubmitData: function() {
  3220.         var me = this,
  3221.             data = null,
  3222.             val;
  3223.         if (!me.disabled && me.submitValue) {
  3224.             val = me.getSubmitValue();
  3225.             if (val !== null) {
  3226.                 data = {};
  3227.                 if ((val == me.defaultValue) && me.deleteDefaultValue) {
  3228.                     data['delete'] = me.getName();
  3229.                 } else {
  3230.                     data[me.getName()] = val;
  3231.                 }
  3232.             } else if (me.deleteEmpty) {
  3233.                data = {};
  3234.                data['delete'] = me.getName();
  3235.             }
  3236.         }
  3237.         return data;
  3238.     },
  3239.  
  3240.     // also accept integer 1 as true
  3241.     setRawValue: function(value) {
  3242.         var me = this;
  3243.  
  3244.         if (value === 1) {
  3245.             me.callParent([true]);
  3246.         } else {
  3247.             me.callParent([value]);
  3248.         }
  3249.     }
  3250.  
  3251. });Ext.define('PVE.form.Textfield', {
  3252.     extend: 'Ext.form.field.Text',
  3253.     alias: ['widget.pvetextfield'],
  3254.  
  3255.     skipEmptyText: true,
  3256.  
  3257.     deleteEmpty: false,
  3258.  
  3259.     getSubmitData: function() {
  3260.         var me = this,
  3261.             data = null,
  3262.             val;
  3263.         if (!me.disabled && me.submitValue && !me.isFileUpload()) {
  3264.             val = me.getSubmitValue();
  3265.             if (val !== null) {
  3266.                 data = {};
  3267.                 data[me.getName()] = val;
  3268.             } else if (me.deleteEmpty) {
  3269.                 data = {};
  3270.                 data['delete'] = me.getName();
  3271.             }
  3272.         }
  3273.         return data;
  3274.     },
  3275.  
  3276.     getSubmitValue: function() {
  3277.         var me = this;
  3278.  
  3279.         var value = this.processRawValue(this.getRawValue());
  3280.         if (value !== '') {
  3281.             return value;
  3282.         }
  3283.  
  3284.         return me.skipEmptyText ? null: value;
  3285.     }
  3286. });Ext.define('PVE.form.RRDTypeSelector', {
  3287.     extend: 'Ext.form.field.ComboBox',
  3288.     alias: ['widget.pveRRDTypeSelector'],
  3289.  
  3290.     initComponent: function() {
  3291.         var me = this;
  3292.  
  3293.         var store = new Ext.data.ArrayStore({
  3294.             fields: [ 'id', 'timeframe', 'cf', 'text' ],
  3295.             data : [
  3296.                 [ 'hour', 'hour', 'AVERAGE', "Hour (average)" ],
  3297.                 [ 'hourmax', 'hour', 'MAX', "Hour (max)" ],
  3298.                 [ 'day', 'day', 'AVERAGE', "Day (average)" ],
  3299.                 [ 'daymax', 'day', 'MAX', "Day (max)" ],
  3300.                 [ 'week', 'week', 'AVERAGE', "Week (average)" ],
  3301.                 [ 'weekmax', 'week', 'MAX', "Week (max)" ],
  3302.                 [ 'month', 'month', 'AVERAGE', "Month (average)" ],
  3303.                 [ 'monthmax', 'month', 'MAX', "Month (max)" ],
  3304.                 [ 'year', 'year', 'AVERAGE', "Year (average)" ],
  3305.                 [ 'yearmax', 'year', 'MAX', "Year (max)" ]
  3306.             ]
  3307.         });
  3308.  
  3309.         Ext.apply(me, {
  3310.             store: store,
  3311.             displayField: 'text',
  3312.             valueField: 'id',
  3313.             editable: false,
  3314.             queryMode: 'local',
  3315.             value: 'hour',
  3316.             getState: function() {
  3317.                 var ind = store.findExact('id', me.getValue());
  3318.                 var rec = store.getAt(ind);
  3319.                 if (!rec) {
  3320.                     return;
  3321.                 }
  3322.                 return {
  3323.                     id: rec.data.id,
  3324.                     timeframe: rec.data.timeframe,
  3325.                     cf: rec.data.cf
  3326.                 };
  3327.             },
  3328.             applyState : function(state) {
  3329.                 if (state && state.id) {
  3330.                     me.setValue(state.id);
  3331.                 }
  3332.             },
  3333.             stateEvents: [ 'select' ],
  3334.             stateful: true,
  3335.             id: 'pveRRDTypeSelection'
  3336.         });
  3337.  
  3338.         me.callParent();
  3339.  
  3340.         var statechange = function(sp, key, value) {
  3341.             if (key === me.id) {
  3342.                 me.applyState(value);
  3343.             }
  3344.         };
  3345.  
  3346.         var sp = Ext.state.Manager.getProvider();
  3347.         me.mon(sp, 'statechange', statechange, me);
  3348.     }
  3349. });
  3350.  
  3351. Ext.define('PVE.form.ComboGrid', {
  3352.     extend: 'Ext.form.field.ComboBox',
  3353.     alias: ['widget.PVE.form.ComboGrid'],
  3354.  
  3355.     computeHeight: function() {
  3356.         var me = this;
  3357.         var lh = PVE.Utils.gridLineHeigh();
  3358.         var count = me.store.getCount();
  3359.         return (count > 10) ? 10*lh : 26+count*lh;
  3360.     },
  3361.  
  3362.     // hack: allow to select empty value
  3363.     // seems extjs does not allow that when 'editable == false'
  3364.     onKeyUp: function(e, t) {
  3365.         var me = this;
  3366.         var key = e.getKey();
  3367.  
  3368.         if (!me.editable && me.allowBlank && !me.multiSelect &&
  3369.             (key == e.BACKSPACE || key == e.DELETE)) {
  3370.             me.setValue('');
  3371.         }
  3372.  
  3373.         me.callParent(arguments);
  3374.     },
  3375.  
  3376.     // copied from ComboBox
  3377.     createPicker: function() {
  3378.         var me = this,
  3379.         picker,
  3380.         menuCls = Ext.baseCSSPrefix + 'menu',
  3381.  
  3382.         opts = Ext.apply({
  3383.             selModel: {
  3384.                 mode: me.multiSelect ? 'SIMPLE' : 'SINGLE'
  3385.             },
  3386.             floating: true,
  3387.             hidden: true,
  3388.             ownerCt: me.ownerCt,
  3389.             cls: me.el.up('.' + menuCls) ? menuCls : '',
  3390.             store: me.store,
  3391.             displayField: me.displayField,
  3392.             focusOnToFront: false,
  3393.             height: me.computeHeight(),
  3394.             pageSize: me.pageSize
  3395.         }, me.listConfig, me.defaultListConfig);
  3396.  
  3397.         // NOTE: we simply use a grid panel
  3398.         //picker = me.picker = Ext.create('Ext.view.BoundList', opts);
  3399.         picker = me.picker = Ext.create('Ext.grid.Panel', opts);
  3400.  
  3401.         // pass getNode() to the view
  3402.         picker.getNode = function() {
  3403.             picker.getView().getNode(arguments);
  3404.         };
  3405.  
  3406.         me.mon(picker, {
  3407.             itemclick: me.onItemClick,
  3408.             refresh: me.onListRefresh,
  3409.             show: function() {
  3410.                 picker.setHeight(me.computeHeight());
  3411.                 me.syncSelection();
  3412.             },
  3413.             scope: me
  3414.         });
  3415.  
  3416.         me.mon(picker.getSelectionModel(), 'selectionchange', me.onListSelectionChange, me);
  3417.  
  3418.         return picker;
  3419.     },
  3420.  
  3421.     initComponent: function() {
  3422.         var me = this;
  3423.  
  3424.         Ext.apply(me, {
  3425.             queryMode: 'local',
  3426.             editable: false,
  3427.             matchFieldWidth: false
  3428.         });
  3429.  
  3430.         Ext.applyIf(me, { value: ''}); // hack: avoid ExtJS validate() bug
  3431.  
  3432.         Ext.applyIf(me.listConfig, { width: 400 });
  3433.  
  3434.         me.callParent();
  3435.  
  3436.         me.store.on('beforeload', function() {
  3437.             if (!me.isDisabled()) {
  3438.                 me.setDisabled(true);
  3439.                 me.enableAfterLoad = true;
  3440.             }
  3441.         });
  3442.  
  3443.         // hack: autoSelect does not work
  3444.         me.store.on('load', function(store, r, success, o) {
  3445.             if (success) {
  3446.                 me.clearInvalid();
  3447.  
  3448.                 if (me.enableAfterLoad) {
  3449.                     delete me.enableAfterLoad;
  3450.                     me.setDisabled(false);
  3451.                 }
  3452.  
  3453.                 var def = me.getValue();
  3454.                 if (def) {
  3455.                     me.setValue(def, true); // sync with grid
  3456.                 }
  3457.                 var found = false;
  3458.                 if (def) {
  3459.                     if (Ext.isArray(def)) {
  3460.                         Ext.Array.each(def, function(v) {
  3461.                             if (store.findRecord(me.valueField, v)) {
  3462.                                 found = true;
  3463.                                 return false; // break
  3464.                             }
  3465.                         });
  3466.                     } else {
  3467.                         found = store.findRecord(me.valueField, def);
  3468.                     }
  3469.                 }
  3470.  
  3471.                 if (!found) {
  3472.                     var rec = me.store.first();
  3473.                     if (me.autoSelect && rec && rec.data) {
  3474.                         def = rec.data[me.valueField];
  3475.                         me.setValue(def, true);
  3476.                     } else {
  3477.                         me.setValue('', true);
  3478.                     }
  3479.                 }
  3480.             }
  3481.         });
  3482.     }
  3483. });
  3484. Ext.define('PVE.form.KVComboBox', {
  3485.     extend: 'Ext.form.field.ComboBox',
  3486.     alias: 'widget.pveKVComboBox',
  3487.  
  3488.     deleteEmpty: true,
  3489.  
  3490.     getSubmitData: function() {
  3491.         var me = this,
  3492.             data = null,
  3493.             val;
  3494.         if (!me.disabled && me.submitValue) {
  3495.             val = me.getSubmitValue();
  3496.             if (val !== null && val !== '') {
  3497.                 data = {};
  3498.                 data[me.getName()] = val;
  3499.             } else if (me.deleteEmpty) {
  3500.                 data = {};
  3501.                 data['delete'] = me.getName();
  3502.             }
  3503.         }
  3504.         return data;
  3505.     },
  3506.  
  3507.     initComponent: function() {
  3508.         var me = this;
  3509.  
  3510.         me.store = Ext.create('Ext.data.ArrayStore', {
  3511.             model: 'KeyValue',
  3512.             data : me.data
  3513.         });
  3514.  
  3515.         Ext.apply(me, {
  3516.             displayField: 'value',
  3517.             valueField: 'key',
  3518.             queryMode: 'local',
  3519.             editable: false
  3520.         });
  3521.  
  3522.         me.callParent();
  3523.     }
  3524. });
  3525. // boolean type including 'Default' (delete property from file)
  3526. Ext.define('PVE.form.Boolean', {
  3527.     extend: 'PVE.form.KVComboBox',
  3528.     alias: ['widget.booleanfield'],
  3529.  
  3530.     initComponent: function() {
  3531.         var me = this;
  3532.  
  3533.         me.data = [
  3534.             ['', gettext('Default')],
  3535.             [1, gettext('Yes')],
  3536.             [0, gettext('No')]
  3537.         ];
  3538.  
  3539.         me.callParent();
  3540.     }
  3541. });
  3542. Ext.define('PVE.form.CompressionSelector', {
  3543.     extend: 'PVE.form.KVComboBox',
  3544.     alias: ['widget.pveCompressionSelector'],
  3545.  
  3546.     initComponent: function() {
  3547.         var me = this;
  3548.  
  3549.         me.data = [
  3550.             ['', gettext('none')],
  3551.             ['lzo', 'LZO (' + gettext('fast') + ')'],
  3552.             ['gzip', 'GZIP (' + gettext('good') + ')']
  3553.         ];
  3554.  
  3555.         me.callParent();
  3556.     }
  3557. });
  3558. Ext.define('PVE.form.PoolSelector', {
  3559.     extend: 'PVE.form.ComboGrid',
  3560.     alias: ['widget.pvePoolSelector'],
  3561.  
  3562.     allowBlank: false,
  3563.  
  3564.     initComponent: function() {
  3565.         var me = this;
  3566.  
  3567.         var store = new Ext.data.Store({
  3568.             model: 'pve-pools'
  3569.         });
  3570.  
  3571.         Ext.apply(me, {
  3572.             store: store,
  3573.             autoSelect: false,
  3574.             valueField: 'poolid',
  3575.             displayField: 'poolid',
  3576.             listConfig: {
  3577.                 columns: [
  3578.                     {
  3579.                         header: gettext('Pool'),
  3580.                         sortable: true,
  3581.                         dataIndex: 'poolid',
  3582.                         flex: 1
  3583.                     },
  3584.                     {
  3585.                         id: 'comment',
  3586.                         header: 'Comment',
  3587.                         sortable: false,
  3588.                         dataIndex: 'comment',
  3589.                         flex: 1
  3590.                     }
  3591.                 ]
  3592.             }
  3593.         });
  3594.  
  3595.         me.callParent();
  3596.  
  3597.         store.load();
  3598.     }
  3599.  
  3600. }, function() {
  3601.  
  3602.     Ext.define('pve-pools', {
  3603.         extend: 'Ext.data.Model',
  3604.         fields: [ 'poolid', 'comment' ],
  3605.         proxy: {
  3606.             type: 'pve',
  3607.             url: "/api2/json/pools"
  3608.         },
  3609.         idProperty: 'poolid'
  3610.     });
  3611.  
  3612. });
  3613. Ext.define('PVE.form.GroupSelector', {
  3614.     extend: 'PVE.form.ComboGrid',
  3615.     alias: ['widget.pveGroupSelector'],
  3616.  
  3617.     allowBlank: false,
  3618.  
  3619.     initComponent: function() {
  3620.         var me = this;
  3621.  
  3622.         var store = new Ext.data.Store({
  3623.             model: 'pve-groups'
  3624.         });
  3625.  
  3626.         Ext.apply(me, {
  3627.             store: store,
  3628.             autoSelect: false,
  3629.             valueField: 'groupid',
  3630.             displayField: 'groupid',
  3631.             listConfig: {
  3632.                 columns: [
  3633.                     {
  3634.                         header: gettext('Group'),
  3635.                         sortable: true,
  3636.                         dataIndex: 'groupid',
  3637.                         flex: 1
  3638.                     },
  3639.                     {
  3640.                         id: 'comment',
  3641.                         header: 'Comment',
  3642.                         sortable: false,
  3643.                         dataIndex: 'comment',
  3644.                         flex: 1
  3645.                     }
  3646.                 ]
  3647.             }
  3648.         });
  3649.  
  3650.         me.callParent();
  3651.  
  3652.         store.load();
  3653.     }
  3654.  
  3655. }, function() {
  3656.  
  3657.     Ext.define('pve-groups', {
  3658.         extend: 'Ext.data.Model',
  3659.         fields: [ 'groupid', 'comment' ],
  3660.         proxy: {
  3661.             type: 'pve',
  3662.             url: "/api2/json/access/groups"
  3663.         },
  3664.         idProperty: 'groupid'
  3665.     });
  3666.  
  3667. });
  3668. Ext.define('PVE.form.UserSelector', {
  3669.     extend: 'PVE.form.ComboGrid',
  3670.     alias: ['widget.pveUserSelector'],
  3671.  
  3672.     initComponent: function() {
  3673.         var me = this;
  3674.  
  3675.         var store = new Ext.data.Store({
  3676.             model: 'pve-users'
  3677.         });
  3678.  
  3679.         var render_full_name = function(firstname, metaData, record) {
  3680.  
  3681.             var first = firstname || '';
  3682.             var last = record.data.lastname || '';
  3683.             return first + " " + last;
  3684.         };
  3685.  
  3686.         Ext.apply(me, {
  3687.             store: store,
  3688.             allowBlank: false,
  3689.             autoSelect: false,
  3690.             valueField: 'userid',
  3691.             displayField: 'userid',
  3692.             listConfig: {
  3693.                 columns: [
  3694.                     {
  3695.                         header: gettext('User'),
  3696.                         sortable: true,
  3697.                         dataIndex: 'userid',
  3698.                         flex: 1
  3699.                     },
  3700.                     {
  3701.                         header: 'Name',
  3702.                         sortable: true,
  3703.                         renderer: render_full_name,
  3704.                         dataIndex: 'firstname',
  3705.                         flex: 1
  3706.                     },
  3707.                     {
  3708.                         id: 'comment',
  3709.                         header: 'Comment',
  3710.                         sortable: false,
  3711.                         dataIndex: 'comment',
  3712.                         flex: 1
  3713.                     }
  3714.                 ]
  3715.             }
  3716.         });
  3717.  
  3718.         me.callParent();
  3719.  
  3720.         store.load({ params: { enabled: 1 }});
  3721.     }
  3722.  
  3723. }, function() {
  3724.  
  3725.     Ext.define('pve-users', {
  3726.         extend: 'Ext.data.Model',
  3727.         fields: [
  3728.             'userid', 'firstname', 'lastname' , 'email', 'comment',
  3729.             { type: 'boolean', name: 'enable' },
  3730.             { type: 'date', dateFormat: 'timestamp', name: 'expire' }
  3731.         ],
  3732.         proxy: {
  3733.             type: 'pve',
  3734.             url: "/api2/json/access/users"
  3735.         },
  3736.         idProperty: 'userid'
  3737.     });
  3738.  
  3739. });
  3740.  
  3741.  
  3742. Ext.define('PVE.form.RoleSelector', {
  3743.     extend: 'PVE.form.ComboGrid',
  3744.     alias: ['widget.pveRoleSelector'],
  3745.  
  3746.     initComponent: function() {
  3747.         var me = this;
  3748.  
  3749.         var store = new Ext.data.Store({
  3750.             model: 'pve-roles'
  3751.         });
  3752.  
  3753.         Ext.apply(me, {
  3754.             store: store,
  3755.             allowBlank: false,
  3756.             autoSelect: false,
  3757.             valueField: 'roleid',
  3758.             displayField: 'roleid',
  3759.             listConfig: {
  3760.                 columns: [
  3761.                     {
  3762.                         header: gettext('Role'),
  3763.                         sortable: true,
  3764.                         dataIndex: 'roleid',
  3765.                         flex: 1
  3766.                     }
  3767.                 ]
  3768.             }
  3769.         });
  3770.  
  3771.         me.callParent();
  3772.  
  3773.         store.load();
  3774.     }
  3775.  
  3776. }, function() {
  3777.  
  3778.     Ext.define('pve-roles', {
  3779.         extend: 'Ext.data.Model',
  3780.         fields: [ 'roleid', 'privs' ],
  3781.         proxy: {
  3782.             type: 'pve',
  3783.             url: "/api2/json/access/roles"
  3784.         },
  3785.         idProperty: 'roleid'
  3786.     });
  3787.  
  3788. });
  3789. Ext.define('PVE.form.VMIDSelector', {
  3790.     extend: 'Ext.form.field.Number',
  3791.     alias: 'widget.pveVMIDSelector',
  3792.  
  3793.     allowBlank: false,
  3794.  
  3795.     minValue: 100,
  3796.  
  3797.     maxValue: 999999999,
  3798.  
  3799.     validateExists: undefined,
  3800.  
  3801.     validator: function(value) {
  3802.         /*jslint confusion: true */
  3803.         var me = this;
  3804.  
  3805.         if (!Ext.isDefined(me.validateExists)) {
  3806.             return true;
  3807.         }
  3808.         if (PVE.data.ResourceStore.findVMID(value)) {
  3809.             if (me.validateExists === true) {
  3810.                 return true;
  3811.             }
  3812.             return "This VM ID is already in use.";
  3813.         } else {
  3814.             if (me.validateExists === false) {
  3815.                 return true;
  3816.             }
  3817.             return "This VM ID does not exists.";
  3818.         }
  3819.     },
  3820.  
  3821.     initComponent: function() {
  3822.         var me = this;
  3823.  
  3824.         Ext.applyIf(me, {
  3825.             fieldLabel: 'VM ID'
  3826.         });
  3827.  
  3828.         me.callParent();
  3829.     }
  3830. });
  3831. Ext.define('PVE.form.NetworkCardSelector', {
  3832.     extend: 'PVE.form.KVComboBox',
  3833.     alias: ['widget.PVE.form.NetworkCardSelector'],
  3834.  
  3835.     initComponent: function() {
  3836.         var me = this;
  3837.  
  3838.         me.data = [
  3839.             ['rtl8139', 'Realtec RTL8139'],
  3840.             ['e1000', 'Intel E1000'],
  3841.             ['virtio', 'VirtIO (paravirtualized)']
  3842.         ];
  3843.  
  3844.         me.callParent();
  3845.     }
  3846. });
  3847. Ext.define('PVE.form.DiskFormatSelector', {
  3848.     extend: 'PVE.form.KVComboBox',
  3849.     alias: ['widget.PVE.form.DiskFormatSelector'],
  3850.  
  3851.     initComponent: function() {
  3852.         var me = this;
  3853.  
  3854.         me.data = [
  3855.             ['raw', 'Raw disk image (raw)'],
  3856.             ['qcow2', 'QEMU image format (qcow2)'],
  3857.             ['vmdk', 'VMware image format (vmdk)']
  3858.         ];
  3859.  
  3860.         me.callParent();
  3861.     }
  3862. });
  3863. Ext.define('PVE.form.BusTypeSelector', {
  3864.     extend: 'PVE.form.KVComboBox',
  3865.     alias: ['widget.PVE.form.BusTypeSelector'],
  3866.  
  3867.     noVirtIO: false,
  3868.  
  3869.     noScsi: false,
  3870.  
  3871.     initComponent: function() {
  3872.         var me = this;
  3873.  
  3874.         me.data = [['ide', 'IDE']];
  3875.  
  3876.         if (!me.noVirtIO) {
  3877.             me.data.push(['virtio', 'VIRTIO']);
  3878.         }
  3879.  
  3880.         if (!me.noScsi) {
  3881.             me.data.push(['scsi', 'SCSI']);
  3882.         }
  3883.  
  3884.         me.callParent();
  3885.     }
  3886. });
  3887. Ext.define('PVE.form.ControllerSelector', {
  3888.     extend: 'Ext.form.FieldContainer',
  3889.     alias: ['widget.PVE.form.ControllerSelector'],
  3890.  
  3891.     statics: {
  3892.         maxIds: {
  3893.             ide: 3,
  3894.             virtio: 5,
  3895.             scsi: 13
  3896.         }
  3897.     },
  3898.  
  3899.     noVirtIO: false,
  3900.  
  3901.     noScsi: false,
  3902.  
  3903.     vmconfig: {}, // used to check for existing devices
  3904.  
  3905.     setVMConfig: function(vmconfig, autoSelect) {
  3906.         var me = this;
  3907.  
  3908.         me.vmconfig = Ext.apply({}, vmconfig);
  3909.         if (autoSelect) {
  3910.             var clist = ['ide', 'virtio', 'scsi'];
  3911.             if (autoSelect === 'cdrom') {
  3912.                 clist = ['ide', 'scsi'];
  3913.                 if (!Ext.isDefined(me.vmconfig.ide2)) {
  3914.                     me.down('field[name=controller]').setValue('ide');
  3915.                     me.down('field[name=deviceid]').setValue(2);
  3916.                     return;
  3917.                 }
  3918.             } else if (me.vmconfig.ostype === 'l26') {
  3919.                 clist = ['virtio', 'ide', 'scsi'];
  3920.             }
  3921.  
  3922.             Ext.Array.each(clist, function(controller) {
  3923.                 var confid, i;
  3924.                 if ((controller === 'virtio' && me.noVirtIO) ||
  3925.                     (controller === 'scsi' && me.noScsi)) {
  3926.                     return; //continue
  3927.                 }
  3928.                 me.down('field[name=controller]').setValue(controller);
  3929.                 for (i = 0; i <= PVE.form.ControllerSelector.maxIds[controller]; i++) {
  3930.                     confid = controller + i.toString();
  3931.                     if (!Ext.isDefined(me.vmconfig[confid])) {
  3932.                         me.down('field[name=deviceid]').setValue(i);
  3933.                         return false; // break
  3934.                     }
  3935.                 }
  3936.             });
  3937.         }
  3938.         me.down('field[name=deviceid]').validate();
  3939.     },
  3940.  
  3941.     initComponent: function() {
  3942.         var me = this;
  3943.  
  3944.         Ext.apply(me, {
  3945.             fieldLabel: 'Bus/Device',
  3946.             layout: 'hbox',
  3947.             height: 22, // hack: set to same height as other fields
  3948.             defaults: {
  3949.                 flex: 1,
  3950.                 hideLabel: true
  3951.             },
  3952.             items: [
  3953.                 {
  3954.                     xtype: 'PVE.form.BusTypeSelector',
  3955.                     name: 'controller',
  3956.                     value: 'ide',
  3957.                     noVirtIO: me.noVirtIO,
  3958.                     noScsi: me.noScsi,
  3959.                     allowBlank: false,
  3960.                     listeners: {
  3961.                         change: function(t, value) {
  3962.                             if (!me.rendered || !value) {
  3963.                                 return;
  3964.                             }
  3965.                             var field = me.down('field[name=deviceid]');
  3966.                             field.setMaxValue(PVE.form.ControllerSelector.maxIds[value]);
  3967.                             field.validate();
  3968.                         }
  3969.                     }
  3970.                 },
  3971.                 {
  3972.                     xtype: 'numberfield',
  3973.                     name: 'deviceid',
  3974.                     minValue: 0,
  3975.                     maxValue: PVE.form.ControllerSelector.maxIds.ide,
  3976.                     value: '0',
  3977.                     validator: function(value) {
  3978.                         /*jslint confusion: true */
  3979.                         if (!me.rendered) {
  3980.                             return;
  3981.                         }
  3982.                         var field = me.down('field[name=controller]');
  3983.                         var controller = field.getValue();
  3984.                         var confid = controller + value;
  3985.                         if (Ext.isDefined(me.vmconfig[confid])) {
  3986.                             return "This device is already in use.";
  3987.                         }
  3988.                         return true;
  3989.                     }
  3990.                 }
  3991.             ]
  3992.         });
  3993.  
  3994.         me.callParent();
  3995.     }
  3996. });   Ext.define('PVE.form.RealmComboBox', {
  3997.     extend: 'Ext.form.field.ComboBox',
  3998.     alias: ['widget.pveRealmComboBox'],
  3999.  
  4000.     initComponent: function() {
  4001.         var me = this;
  4002.  
  4003.         var stateid = 'pveloginrealm';
  4004.  
  4005.         var realmstore = Ext.create('Ext.data.Store', {
  4006.             model: 'pve-domains',
  4007.             autoDestory: true
  4008.         });
  4009.  
  4010.         Ext.apply(me, {
  4011.             fieldLabel: 'Realm',
  4012.             name: 'realm',
  4013.             store: realmstore,
  4014.             queryMode: 'local',
  4015.             allowBlank: false,
  4016.             forceSelection: true,
  4017.             autoSelect: false,
  4018.             triggerAction: 'all',
  4019.             valueField: 'realm',
  4020.             displayField: 'descr',
  4021.             getState: function() {
  4022.                 return { value: this.getValue() };
  4023.             },
  4024.             applyState : function(state) {
  4025.                 if (state && state.value) {
  4026.                     this.setValue(state.value);
  4027.                 }
  4028.             },
  4029.             stateEvents: [ 'select' ],
  4030.             stateful: true,
  4031.             id: stateid, // fixme: remove (Stateful does not work without)
  4032.             stateID: stateid
  4033.         });
  4034.  
  4035.         me.callParent();
  4036.  
  4037.         realmstore.load({
  4038.             callback: function(r, o, success) {
  4039.                 if (success) {
  4040.                     var def = me.getValue();
  4041.                     if (!def || !realmstore.findRecord('realm', def)) {
  4042.                         def = 'pam';
  4043.                         Ext.each(r, function(record) {
  4044.                             if (record.data && record.data["default"]) {
  4045.                                 def = record.data.realm;
  4046.                             }
  4047.                         });
  4048.                     }
  4049.                     if (def) {
  4050.                         me.setValue(def);
  4051.                     }
  4052.                 }
  4053.             }
  4054.         });
  4055.     }
  4056. });Ext.define('PVE.form.BondModeSelector', {
  4057.     extend: 'PVE.form.KVComboBox',
  4058.     alias: ['widget.bondModeSelector'],
  4059.  
  4060.     initComponent: function() {
  4061.         var me = this;
  4062.  
  4063.         me.data = [
  4064.             ['balance-rr', 'balance-rr'],
  4065.             ['active-backup', 'active-backup'],
  4066.             ['balance-xor', 'balance-xor'],
  4067.             ['broadcast', 'broadcast'],
  4068.             ['802.3ad', '802.3ad'],
  4069.             ['balance-tlb', 'balance-tlb'],
  4070.             ['balance-alb', 'balance-alb']
  4071.         ];
  4072.  
  4073.         me.callParent();
  4074.     }
  4075. });
  4076. Ext.define('PVE.form.ViewSelector', {
  4077.     extend: 'Ext.form.field.ComboBox',
  4078.     alias: ['widget.pveViewSelector'],
  4079.  
  4080.     initComponent: function() {
  4081.         var me = this;
  4082.  
  4083.         var default_views = {
  4084.             server: {
  4085.                 text: gettext('Server View'),
  4086.                 groups: ['node']
  4087.             },
  4088.             folder: {
  4089.                 text: gettext('Folder View'),
  4090.                 groups: ['type']
  4091.             },
  4092.             storage: {
  4093.                 text: gettext('Storage View'),
  4094.                 groups: ['node'],
  4095.                 filterfn: function(node) {
  4096.                     return node.data.type === 'storage';
  4097.                 }
  4098.             }
  4099.         };
  4100.  
  4101.         var groupdef = [];
  4102.         Ext.Object.each(default_views, function(viewname, value) {
  4103.             groupdef.push([viewname, value.text]);
  4104.         });
  4105.  
  4106.         var store = Ext.create('Ext.data.Store', {
  4107.             model: 'KeyValue',
  4108.             proxy: {
  4109.                 type: 'memory',
  4110.                 reader: 'array'
  4111.             },
  4112.             data: groupdef,
  4113.             autoload: true,
  4114.             autoDestory: true
  4115.         });
  4116.  
  4117.         Ext.apply(me, {
  4118.             hideLabel: true,
  4119.             store: store,
  4120.             value: groupdef[0][0],
  4121.             editable: false,
  4122.             queryMode: 'local',
  4123.             allowBlank: false,
  4124.             forceSelection: true,
  4125.             autoSelect: false,
  4126.             triggerAction: 'all',
  4127.             valueField: 'key',
  4128.             displayField: 'value',
  4129.  
  4130.             getViewFilter: function() {
  4131.                 var view = me.getValue();
  4132.                 return Ext.apply({ id: view }, default_views[view] || default_views.server);
  4133.             },
  4134.  
  4135.             getState: function() {
  4136.                 return { value: me.getValue() };
  4137.             },
  4138.  
  4139.             applyState : function(state, doSelect) {
  4140.                 var view = me.getValue();
  4141.                 if (state && state.value && (view != state.value)) {
  4142.                     var record = store.findRecord('key', state.value);
  4143.                     if (record) {
  4144.                         me.setValue(state.value, true);
  4145.                         if (doSelect) {
  4146.                             me.fireEvent('select', me, [record]);
  4147.                         }
  4148.                     }
  4149.                 }
  4150.             },
  4151.             stateEvents: [ 'select' ],
  4152.             stateful: true,
  4153.             id: 'view'
  4154.         });
  4155.  
  4156.         me.callParent();
  4157.  
  4158.         var statechange = function(sp, key, value) {
  4159.             if (key === me.id) {
  4160.                 me.applyState(value, true);
  4161.             }
  4162.         };
  4163.  
  4164.         var sp = Ext.state.Manager.getProvider();
  4165.  
  4166.         me.mon(sp, 'statechange', statechange, me);
  4167.     }
  4168. });Ext.define('PVE.form.NodeSelector', {
  4169.     extend: 'PVE.form.ComboGrid',
  4170.     alias: ['widget.PVE.form.NodeSelector'],
  4171.  
  4172.     // invalidate nodes which are offline
  4173.     onlineValidator: false,
  4174.  
  4175.     initComponent: function() {
  4176.         var me = this;
  4177.  
  4178.         var store = Ext.create('Ext.data.Store', {
  4179.             fields: [ 'node', 'cpu', 'maxcpu', 'mem', 'maxmem', 'uptime' ],
  4180.             autoLoad: true,
  4181.             proxy: {
  4182.                 type: 'pve',
  4183.                 url: '/api2/json/nodes'
  4184.             },
  4185.             autoDestory: true,
  4186.             sorters: [
  4187.                 {
  4188.                     property : 'mem',
  4189.                     direction: 'DESC'
  4190.                 },
  4191.                 {
  4192.                     property : 'node',
  4193.                     direction: 'ASC'
  4194.                 }
  4195.             ]
  4196.         });
  4197.  
  4198.         Ext.apply(me, {
  4199.             store: store,
  4200.             valueField: 'node',
  4201.             displayField: 'node',
  4202.             listConfig: {
  4203.                 columns: [
  4204.                     {
  4205.                         header: 'Node',
  4206.                         dataIndex: 'node',
  4207.                         hideable: false,
  4208.                         flex: 1
  4209.                     },
  4210.                     {
  4211.                         header: 'Memory usage',
  4212.                         renderer: PVE.Utils.render_mem_usage,
  4213.                         width: 100,
  4214.                         dataIndex: 'mem'
  4215.                     },
  4216.                     {
  4217.                         header: 'CPU usage',
  4218.                         renderer: PVE.Utils.render_cpu,
  4219.                         sortable: true,
  4220.                         width: 100,
  4221.                         dataIndex: 'cpu'
  4222.                     }
  4223.                 ]
  4224.             },
  4225.             validator: function(value) {
  4226.                 /*jslint confusion: true */
  4227.                 if (!me.onlineValidator || (me.allowBlank && !value)) {
  4228.                     return true;
  4229.                 }
  4230.  
  4231.                 var offline = [];
  4232.                 Ext.Array.each(value.split(/\s*,\s*/), function(node) {
  4233.                     var rec = me.store.findRecord(me.valueField, node);
  4234.                     if (!(rec && rec.data) || !Ext.isNumeric(rec.data.mem)) {
  4235.                         offline.push(node);
  4236.                     }
  4237.                 });
  4238.  
  4239.                 if (offline.length == 0) {
  4240.                     return true;
  4241.                 }
  4242.  
  4243.                 return "Node " + offline.join(', ') + " seems to be offline!";
  4244.             }
  4245.         });
  4246.  
  4247.         me.callParent();
  4248.     }
  4249. });Ext.define('PVE.form.FileSelector', {
  4250.     extend: 'PVE.form.ComboGrid',
  4251.     alias: ['widget.pveFileSelector'],
  4252.  
  4253.     setStorage: function(storage, nodename) {
  4254.         var me = this;
  4255.  
  4256.         var change = false;
  4257.         if (storage && (me.storage !== storage)) {
  4258.             me.storage = storage;
  4259.             change = true;
  4260.         }
  4261.  
  4262.         if (nodename && (me.nodename !== nodename)) {
  4263.             me.nodename = nodename;
  4264.             change = true;
  4265.         }
  4266.  
  4267.         if (!(me.storage && me.nodename && change)) {
  4268.             return;
  4269.         }
  4270.  
  4271.         var url = '/api2/json/nodes/' + me.nodename + '/storage/' + me.storage + '/content';
  4272.         if (me.storageContent) {
  4273.             url += '?content=' + me.storageContent;
  4274.         }
  4275.  
  4276.         me.store.setProxy({
  4277.             type: 'pve',
  4278.             url: url
  4279.         });
  4280.  
  4281.         me.store.load();
  4282.     },
  4283.  
  4284.     initComponent: function() {
  4285.         var me = this;
  4286.  
  4287.         var store = Ext.create('Ext.data.Store', {
  4288.             model: 'pve-storage-content'
  4289.         });
  4290.  
  4291.         Ext.apply(me, {
  4292.             store: store,
  4293.             allowBlank: false,
  4294.             autoSelect: false,
  4295.             valueField: 'volid',
  4296.             displayField: 'text',
  4297.             listConfig: {
  4298.                 columns: [
  4299.                     {
  4300.                         header: 'Name',
  4301.                         dataIndex: 'text',
  4302.                         hideable: false,
  4303.                         flex: 1
  4304.                     },
  4305.                     {
  4306.                         header: 'Format',
  4307.                         width: 60,
  4308.                         dataIndex: 'format'
  4309.                     },
  4310.                     {
  4311.                         header: 'Size',
  4312.                         width: 60,
  4313.                         dataIndex: 'size',
  4314.                         renderer: PVE.Utils.format_size
  4315.                     }
  4316.                 ]
  4317.             }
  4318.         });
  4319.  
  4320.         me.callParent();
  4321.  
  4322.         me.setStorage(me.storage, me.nodename);
  4323.     }
  4324. });Ext.define('PVE.form.StorageSelector', {
  4325.     extend: 'PVE.form.ComboGrid',
  4326.     alias: ['widget.PVE.form.StorageSelector'],
  4327.  
  4328.     setNodename: function(nodename) {
  4329.         var me = this;
  4330.  
  4331.         if (!nodename || (me.nodename === nodename)) {
  4332.             return;
  4333.         }
  4334.  
  4335.         me.nodename = nodename;
  4336.  
  4337.         var url = '/api2/json/nodes/' + me.nodename + '/storage';
  4338.         if (me.storageContent) {
  4339.             url += '?content=' + me.storageContent;
  4340.         }
  4341.  
  4342.         me.store.setProxy({
  4343.             type: 'pve',
  4344.             url: url
  4345.         });
  4346.  
  4347.         me.store.load();
  4348.     },
  4349.  
  4350.     initComponent: function() {
  4351.         var me = this;
  4352.  
  4353.         var nodename = me.nodename;
  4354.         me.nodename = undefined;
  4355.  
  4356.         var store = Ext.create('Ext.data.Store', {
  4357.             model: 'pve-storage-status',
  4358.             sorters: {
  4359.                 property: 'storage',
  4360.                 order: 'DESC'
  4361.             }
  4362.         });
  4363.  
  4364.         Ext.apply(me, {
  4365.             store: store,
  4366.             allowBlank: false,
  4367.             valueField: 'storage',
  4368.             displayField: 'storage',
  4369.             listConfig: {
  4370.                 columns: [
  4371.                     {
  4372.                         header: 'Name',
  4373.                         dataIndex: 'storage',
  4374.                         hideable: false,
  4375.                         flex: 1
  4376.                     },
  4377.                     {
  4378.                         header: 'Type',
  4379.                         width: 60,
  4380.                         dataIndex: 'type'
  4381.                     },
  4382.                     {
  4383.                         header: 'Avail',
  4384.                         width: 80,
  4385.                         dataIndex: 'avail',
  4386.                         renderer: PVE.Utils.format_size
  4387.                     },
  4388.                     {
  4389.                         header: 'Capacity',
  4390.                         width: 80,
  4391.                         dataIndex: 'total',
  4392.                         renderer: PVE.Utils.format_size
  4393.                     }
  4394.                 ]
  4395.             }
  4396.         });
  4397.  
  4398.         me.callParent();
  4399.  
  4400.         if (nodename) {
  4401.             me.setNodename(nodename);
  4402.         }
  4403.     }
  4404. }, function() {
  4405.  
  4406.     Ext.define('pve-storage-status', {
  4407.         extend: 'Ext.data.Model',
  4408.         fields: [ 'storage', 'active', 'type', 'avail', 'total' ],
  4409.         idProperty: 'storage'
  4410.     });
  4411.  
  4412. });
  4413. Ext.define('PVE.form.BridgeSelector', {
  4414.     extend: 'PVE.form.ComboGrid',
  4415.     alias: ['widget.PVE.form.BridgeSelector'],
  4416.  
  4417.     setNodename: function(nodename) {
  4418.         var me = this;
  4419.  
  4420.         if (!nodename || (me.nodename === nodename)) {
  4421.             return;
  4422.         }
  4423.  
  4424.         me.nodename = nodename;
  4425.  
  4426.         me.store.setProxy({
  4427.             type: 'pve',
  4428.             url: '/api2/json/nodes/' + me.nodename + '/network?type=bridge'
  4429.         });
  4430.  
  4431.         me.store.load();
  4432.     },
  4433.  
  4434.     initComponent: function() {
  4435.         var me = this;
  4436.  
  4437.         var nodename = me.nodename;
  4438.         me.nodename = undefined;
  4439.  
  4440.         var store = Ext.create('Ext.data.Store', {
  4441.             fields: [ 'iface', 'active', 'type' ],
  4442.             filterOnLoad: true
  4443.         });
  4444.  
  4445.         Ext.apply(me, {
  4446.             store: store,
  4447.             valueField: 'iface',
  4448.             displayField: 'iface',
  4449.             listConfig: {
  4450.                 columns: [
  4451.                     {
  4452.                         header: 'Bridge',
  4453.                         dataIndex: 'iface',
  4454.                         hideable: false,
  4455.                         flex: 1
  4456.                     },
  4457.                     {
  4458.                         header: gettext('Active'),
  4459.                         width: 60,
  4460.                         dataIndex: 'active',
  4461.                         renderer: PVE.Utils.format_boolean
  4462.                     }
  4463.                 ]
  4464.             }
  4465.         });
  4466.  
  4467.         me.callParent();
  4468.  
  4469.         if (nodename) {
  4470.             me.setNodename(nodename);
  4471.         }
  4472.     }
  4473. });
  4474.  
  4475. Ext.define('PVE.form.CPUModelSelector', {
  4476.     extend: 'PVE.form.KVComboBox',
  4477.     alias: ['widget.CPUModelSelector'],
  4478.  
  4479.     initComponent: function() {
  4480.         var me = this;
  4481.  
  4482.         me.data = [
  4483.             ['', 'Default (qemu64)'],
  4484.             ['486', '486'],
  4485.             ['athlon', 'athlon'],
  4486.             ['core2duo', 'core2duo'],
  4487.             ['coreduo', 'coreduo'],
  4488.             ['kvm32', 'kvm32'],
  4489.             ['kvm64', 'kvm64'],
  4490.             ['pentium', 'pentium'],
  4491.             ['pentium2', 'pentium2'],
  4492.             ['pentium3', 'pentium3'],
  4493.             ['phenom', 'phenom'],
  4494.             ['qemu32', 'qemu32'],
  4495.             ['qemu64', 'qemu64'],
  4496.             ['cpu64-rhel6', 'cpu64-rhel6'],
  4497.             ['cpu64-rhel5', 'cpu64-rhel5'],
  4498.             ['Conroe', 'Conroe'],
  4499.             ['Penryn', 'Penryn'],
  4500.             ['Nehalem', 'Nehalem'],
  4501.             ['Westmere', 'Westmere'],
  4502.             ['Opteron_G1', 'Opteron_G1'],
  4503.             ['Opteron_G2', 'Opteron_G2'],
  4504.             ['Opteron_G3', 'Opteron_G3'],
  4505.             ['host', 'host']
  4506.         ];
  4507.  
  4508.         me.callParent();
  4509.     }
  4510. });
  4511. Ext.define('PVE.form.VNCKeyboardSelector', {
  4512.     extend: 'PVE.form.KVComboBox',
  4513.     alias: ['widget.VNCKeyboardSelector'],
  4514.  
  4515.     initComponent: function() {
  4516.         var me = this;
  4517.         me.data = PVE.Utils.kvm_keymap_array();
  4518.         me.callParent();
  4519.     }
  4520. });
  4521. Ext.define('PVE.form.LanguageSelector', {
  4522.     extend: 'PVE.form.KVComboBox',
  4523.     alias: ['widget.pveLanguageSelector'],
  4524.  
  4525.     initComponent: function() {
  4526.         var me = this;
  4527.         me.data = PVE.Utils.language_array();
  4528.         me.callParent();
  4529.     }
  4530. });
  4531. Ext.define('PVE.form.DisplaySelector', {
  4532.     extend: 'PVE.form.KVComboBox',
  4533.     alias: ['widget.DisplaySelector'],
  4534.  
  4535.     initComponent: function() {
  4536.         var me = this;
  4537.  
  4538.         me.data = PVE.Utils.kvm_vga_driver_array();
  4539.         me.callParent();
  4540.     }
  4541. });
  4542. Ext.define('PVE.form.CacheTypeSelector', {
  4543.     extend: 'PVE.form.KVComboBox',
  4544.     alias: ['widget.CacheTypeSelector'],
  4545.  
  4546.     initComponent: function() {
  4547.         var me = this;
  4548.  
  4549.         me.data = [
  4550.             ['', 'Default (no cache)'],
  4551.             ['directsync', 'Direct sync'],
  4552.             ['writethrough', 'Write through'],
  4553.             ['writeback', 'Write back'],
  4554.             ['unsafe', 'Write back (unsafe)'],
  4555.             ['none', 'No cache']
  4556.         ];
  4557.  
  4558.         me.callParent();
  4559.     }
  4560. });
  4561. Ext.define('PVE.form.ContentTypeSelector', {
  4562.     extend: 'PVE.form.KVComboBox',
  4563.     alias: ['widget.pveContentTypeSelector'],
  4564.  
  4565.     initComponent: function() {
  4566.         var me = this;
  4567.  
  4568.         me.data = [];
  4569.  
  4570.         var cts = ['images', 'iso', 'vztmpl', 'backup', 'rootdir'];
  4571.         Ext.Array.each(cts, function(ct) {
  4572.             me.data.push([ct, PVE.Utils.format_content_types(ct)]);
  4573.         });
  4574.  
  4575.         me.callParent();
  4576.     }
  4577. });
  4578. Ext.define('PVE.form.DayOfWeekSelector', {
  4579.     extend: 'PVE.form.KVComboBox',
  4580.     alias: ['widget.pveDayOfWeekSelector'],
  4581.  
  4582.     initComponent: function() {
  4583.         var me = this;
  4584.  
  4585.         me.data = [
  4586.             ['sun', Ext.Date.dayNames[0]],
  4587.             ['mon', Ext.Date.dayNames[1]],
  4588.             ['tue', Ext.Date.dayNames[2]],
  4589.             ['wed', Ext.Date.dayNames[3]],
  4590.             ['thu', Ext.Date.dayNames[4]],
  4591.             ['fri', Ext.Date.dayNames[5]],
  4592.             ['sat', Ext.Date.dayNames[6]]
  4593.         ];
  4594.  
  4595.         me.callParent();
  4596.     }
  4597. });
  4598. Ext.define('PVE.form.BackupModeSelector', {
  4599.     extend: 'PVE.form.KVComboBox',
  4600.     alias: ['widget.pveBackupModeSelector'],
  4601.  
  4602.     initComponent: function() {
  4603.         var me = this;
  4604.  
  4605.         me.data = [
  4606.             ['snapshot', 'Snapshot'],
  4607.             ['suspend', 'Suspend'],
  4608.             ['stop', 'Stop']
  4609.         ];
  4610.  
  4611.         me.callParent();
  4612.     }
  4613. });
  4614. Ext.define('PVE.dc.Tasks', {
  4615.     extend: 'Ext.grid.GridPanel',
  4616.  
  4617.     alias: ['widget.pveClusterTasks'],
  4618.  
  4619.     initComponent : function() {
  4620.         var me = this;
  4621.  
  4622.         var taskstore = new PVE.data.UpdateStore({
  4623.             storeid: 'pve-cluster-tasks',
  4624.             model: 'pve-tasks',
  4625.             proxy: {
  4626.                 type: 'pve',
  4627.                 url: '/api2/json/cluster/tasks'
  4628.             },
  4629.             sorters: [
  4630.                 {
  4631.                     property : 'starttime',
  4632.                     direction: 'DESC'
  4633.                 }
  4634.             ]
  4635.         });
  4636.  
  4637.         var store = Ext.create('PVE.data.DiffStore', {
  4638.             rstore: taskstore,
  4639.             appendAtStart: true
  4640.         });
  4641.  
  4642.         var run_task_viewer = function() {
  4643.             var sm = me.getSelectionModel();
  4644.             var rec = sm.getSelection()[0];
  4645.             if (!rec) {
  4646.                 return;
  4647.             }
  4648.  
  4649.             var win = Ext.create('PVE.window.TaskViewer', {
  4650.                 upid: rec.data.upid
  4651.             });
  4652.             win.show();
  4653.         };
  4654.  
  4655.         Ext.apply(me, {
  4656.             store: store,
  4657.             stateful: false,
  4658.  
  4659.             viewConfig: {
  4660.                 trackOver: false,
  4661.                 stripeRows: false, // does not work with getRowClass()
  4662.  
  4663.                 getRowClass: function(record, index) {
  4664.                     var status = record.get('status');
  4665.  
  4666.                     if (status && status != 'OK') {
  4667.                         return "x-form-invalid-field";
  4668.                     }
  4669.                 }
  4670.             },
  4671.             sortableColumns: false,
  4672.             columns: [
  4673.                 {
  4674.                     header: gettext("Start Time"),
  4675.                     dataIndex: 'starttime',
  4676.                     width: 100,
  4677.                     renderer: function(value) {
  4678.                         return Ext.Date.format(value, "M d H:i:s");
  4679.                     }
  4680.                 },
  4681.                 {
  4682.                     header: gettext("End Time"),
  4683.                     dataIndex: 'endtime',
  4684.                     width: 100,
  4685.                     renderer: function(value, metaData, record) {
  4686.                         if (record.data.pid) {
  4687.                             metaData.tdCls =  "x-grid-row-loading";
  4688.                             return "";
  4689.                         }
  4690.                         return Ext.Date.format(value, "M d H:i:s");
  4691.                     }
  4692.                 },
  4693.                 {
  4694.                     header: gettext("Node"),
  4695.                     dataIndex: 'node',
  4696.                     width: 100
  4697.                 },
  4698.                 {
  4699.                     header: gettext("User name"),
  4700.                     dataIndex: 'user',
  4701.                     width: 150
  4702.                 },
  4703.                 {
  4704.                     header: gettext("Description"),
  4705.                     dataIndex: 'upid',
  4706.                     flex: 1,
  4707.                     renderer: PVE.Utils.render_upid
  4708.                 },
  4709.                 {
  4710.                     header: gettext("Status"),
  4711.                     dataIndex: 'status',
  4712.                     width: 200,
  4713.                     renderer: function(value, metaData, record) {
  4714.                         if (record.data.pid) {
  4715.                             metaData.tdCls =  "x-grid-row-loading";
  4716.                             return "";
  4717.                         }
  4718.                         if (value == 'OK') {
  4719.                             return 'OK';
  4720.                         }
  4721.                         // metaData.attr = 'style="color:red;"';
  4722.                         return PVE.Utils.errorText + ': ' + value;
  4723.                     }
  4724.                 }
  4725.             ],
  4726.             listeners: {
  4727.                 itemdblclick: run_task_viewer,
  4728.                 show: taskstore.startUpdate,
  4729.                 hide: taskstore.stopUpdate,
  4730.                 destroy: taskstore.stopUpdate
  4731.             }
  4732.         });
  4733.  
  4734.         me.callParent();
  4735.     }
  4736. });Ext.define('PVE.dc.Log', {
  4737.     extend: 'Ext.grid.GridPanel',
  4738.  
  4739.     alias: ['widget.pveClusterLog'],
  4740.  
  4741.     initComponent : function() {
  4742.         var me = this;
  4743.  
  4744.         var logstore = new PVE.data.UpdateStore({
  4745.             storeid: 'pve-cluster-log',
  4746.             model: 'pve-cluster-log',
  4747.             proxy: {
  4748.                 type: 'pve',
  4749.                 url: '/api2/json/cluster/log'
  4750.             }
  4751.         });
  4752.  
  4753.         var store = Ext.create('PVE.data.DiffStore', {
  4754.             rstore: logstore,
  4755.             appendAtStart: true
  4756.         });
  4757.  
  4758.         Ext.apply(me, {
  4759.             store: store,
  4760.             stateful: false,
  4761.  
  4762.             viewConfig: {
  4763.                 trackOver: false,
  4764.                 stripeRows: false, // does not work with getRowClass()
  4765.  
  4766.                 getRowClass: function(record, index) {
  4767.                     var pri = record.get('pri');
  4768.  
  4769.                     if (pri && pri <= 3) {
  4770.                         return "x-form-invalid-field";
  4771.                     }
  4772.                 }
  4773.             },
  4774.             sortableColumns: false,
  4775.             columns: [
  4776.                 {
  4777.                     header: gettext("Time"),
  4778.                     dataIndex: 'time',
  4779.                     width: 100,
  4780.                     renderer: function(value) {
  4781.                         return Ext.Date.format(value, "M d H:i:s");
  4782.                     }
  4783.                 },
  4784.                 {
  4785.                     header: gettext("Node"),
  4786.                     dataIndex: 'node',
  4787.                     width: 100
  4788.                 },
  4789.                 {
  4790.                     header: gettext("Service"),
  4791.                     dataIndex: 'tag',
  4792.                     width: 100
  4793.                 },
  4794.                 {
  4795.                     header: "PID",
  4796.                     dataIndex: 'pid',
  4797.                     width: 100
  4798.                 },
  4799.                 {
  4800.                     header: gettext("User name"),
  4801.                     dataIndex: 'user',
  4802.                     width: 150
  4803.                 },
  4804.                 {
  4805.                     header: gettext("Severity"),
  4806.                     dataIndex: 'pri',
  4807.                     renderer: PVE.Utils.render_serverity,
  4808.                     width: 100
  4809.                 },
  4810.                 {
  4811.                     header: gettext("Message"),
  4812.                     dataIndex: 'msg',
  4813.                     flex: 1
  4814.                 }
  4815.             ],
  4816.             listeners: {
  4817.                 show: logstore.startUpdate,
  4818.                 hide: logstore.stopUpdate,
  4819.                 destroy: logstore.stopUpdate
  4820.             }
  4821.         });
  4822.  
  4823.         me.callParent();
  4824.     }
  4825. });Ext.define('PVE.panel.StatusPanel', {
  4826.     extend: 'Ext.tab.Panel',
  4827.     alias: 'widget.pveStatusPanel',
  4828.  
  4829.  
  4830.     //title: "Logs",
  4831.     //tabPosition: 'bottom',
  4832.  
  4833.     initComponent: function() {
  4834.         var me = this;
  4835.  
  4836.         var stateid = 'ltab';
  4837.         var sp = Ext.state.Manager.getProvider();
  4838.  
  4839.         var state = sp.get(stateid);
  4840.         if (state && state.value) {
  4841.             me.activeTab = state.value;
  4842.         }
  4843.  
  4844.         Ext.apply(me, {
  4845.             listeners: {
  4846.                 tabchange: function() {
  4847.                     var atab = me.getActiveTab().itemId;
  4848.                     var state = { value: atab };
  4849.                     sp.set(stateid, state);
  4850.                 }
  4851.             },
  4852.             items: [
  4853.                 {
  4854.                     itemId: 'tasks',
  4855.                     title: gettext('Tasks'),
  4856.                     xtype: 'pveClusterTasks'
  4857.                 },
  4858.                 {
  4859.                     itemId: 'clog',
  4860.                     title: gettext('Cluster log'),
  4861.                     xtype: 'pveClusterLog'
  4862.                 }
  4863.             ]
  4864.         });
  4865.  
  4866.         me.callParent();
  4867.  
  4868.         me.items.get(0).fireEvent('show', me.items.get(0));
  4869.  
  4870.         var statechange = function(sp, key, state) {
  4871.             if (key === stateid) {
  4872.                 var atab = me.getActiveTab().itemId;
  4873.                 var ntab = state.value;
  4874.                 if (state && ntab && (atab != ntab)) {
  4875.                     me.setActiveTab(ntab);
  4876.                 }
  4877.             }
  4878.         };
  4879.  
  4880.         sp.on('statechange', statechange);
  4881.         me.on('destroy', function() {
  4882.             sp.un('statechange', statechange);
  4883.         });
  4884.  
  4885.     }
  4886. });
  4887. Ext.define('PVE.panel.RRDView', {
  4888.     extend: 'Ext.panel.Panel',
  4889.     alias: 'widget.pveRRDView',
  4890.  
  4891.     initComponent : function() {
  4892.         var me = this;
  4893.  
  4894.         if (!me.timeframe) {
  4895.             me.timeframe = 'hour';
  4896.         }
  4897.  
  4898.         if (!me.rrdcffn) {
  4899.             me.rrdcffn = 'AVERAGE';
  4900.         }
  4901.  
  4902.         if (!me.datasource) {
  4903.             throw "no datasource specified";
  4904.         }
  4905.  
  4906.         if (!me.rrdurl) {
  4907.             throw "no rrdurl specified";
  4908.         }
  4909.  
  4910.         var datasource = me.datasource;
  4911.  
  4912.         // fixme: dcindex??
  4913.         var dcindex = 0;
  4914.         var create_url = function() {
  4915.             var url = me.rrdurl + "?ds=" + datasource +
  4916.                 "&timeframe=" + me.timeframe + "&cf=" + me.rrdcffn +
  4917.                 "&_dc=" + dcindex.toString();
  4918.             dcindex++;
  4919.             return url;
  4920.         };
  4921.  
  4922.         var stateid = 'pveRRDTypeSelection';
  4923.  
  4924.         Ext.apply(me, {
  4925.             layout: 'fit',
  4926.             html: {
  4927.                 tag: 'img',
  4928.                 width: 800,
  4929.                 height: 200,
  4930.                 src:  create_url()
  4931.             },
  4932.             applyState : function(state) {
  4933.                 if (state && state.id) {
  4934.                     me.timeframe = state.timeframe;
  4935.                     me.rrdcffn = state.cf;
  4936.                     me.reload_task.delay(10);
  4937.                 }
  4938.             }
  4939.         });
  4940.  
  4941.         me.callParent();
  4942.  
  4943.         me.reload_task = new Ext.util.DelayedTask(function() {
  4944.             if (me.rendered) {
  4945.                 try {
  4946.                     var html = {
  4947.                         tag: 'img',
  4948.                         width: 800,
  4949.                         height: 200,
  4950.                         src:  create_url()
  4951.                     };
  4952.                     me.update(html);
  4953.                 } catch (e) {
  4954.                     // fixme:
  4955.                     console.log(e);
  4956.                 }
  4957.                 me.reload_task.delay(30000);
  4958.             } else {
  4959.                 me.reload_task.delay(1000);
  4960.             }
  4961.         });
  4962.  
  4963.         me.reload_task.delay(30000);
  4964.  
  4965.         me.on('destroy', function() {
  4966.             me.reload_task.cancel();
  4967.         });
  4968.  
  4969.         var sp = Ext.state.Manager.getProvider();
  4970.         me.applyState(sp.get(stateid));
  4971.  
  4972.         var state_change_fn = function(prov, key, value) {
  4973.             if (key == stateid) {
  4974.                 me.applyState(value);
  4975.             }
  4976.         };
  4977.  
  4978.         me.mon(sp, 'statechange', state_change_fn);
  4979.     }
  4980. });
  4981. Ext.define('PVE.panel.InputPanel', {
  4982.     extend: 'Ext.panel.Panel',
  4983.     alias: ['widget.inputpanel'],
  4984.  
  4985.     border: false,
  4986.  
  4987.     // overwrite this to modify submit data
  4988.     onGetValues: function(values) {
  4989.         return values;
  4990.     },
  4991.  
  4992.     getValues: function(dirtyOnly) {
  4993.         var me = this;
  4994.  
  4995.         if (Ext.isFunction(me.onGetValues)) {
  4996.             dirtyOnly = false;
  4997.         }
  4998.  
  4999.         var values = {};
  5000.  
  5001.         Ext.Array.each(me.query('[isFormField]'), function(field) {
  5002.             if (!dirtyOnly || field.isDirty()) {
  5003.                 PVE.Utils.assemble_field_data(values, field.getSubmitData());
  5004.             }
  5005.         });
  5006.  
  5007.         return me.onGetValues(values);
  5008.     },
  5009.  
  5010.     setValues: function(values) {
  5011.         var me = this;
  5012.  
  5013.         var form = me.up('form');
  5014.  
  5015.         Ext.iterate(values, function(fieldId, val) {
  5016.             var field = me.query('[isFormField][name=' + fieldId + ']')[0];
  5017.             if (field) {
  5018.                 field.setValue(val);
  5019.                 if (form.trackResetOnLoad) {
  5020.                     field.resetOriginalValue();
  5021.                 }
  5022.             }
  5023.         });
  5024.     },
  5025.  
  5026.     initComponent: function() {
  5027.         var me = this;
  5028.  
  5029.         var items;
  5030.  
  5031.         if (me.items) {
  5032.             me.columns = 1;
  5033.             items = [
  5034.                 {
  5035.                     columnWidth: 1,
  5036.                     layout: 'anchor',
  5037.                     items: me.items
  5038.                 }
  5039.             ];
  5040.             me.items = undefined;
  5041.         } else if (me.column1) {
  5042.             me.columns = 2;
  5043.             items = [
  5044.                 {
  5045.                     columnWidth: 0.5,
  5046.                     padding: '0 10 0 0',
  5047.                     layout: 'anchor',
  5048.                     items: me.column1
  5049.                 },
  5050.                 {
  5051.                     columnWidth: 0.5,
  5052.                     padding: '0 0 0 10',
  5053.                     layout: 'anchor',
  5054.                     items: me.column2 || [] // allow empty column
  5055.                 }
  5056.             ];
  5057.         } else {
  5058.             throw "unsupported config";
  5059.         }
  5060.  
  5061.         if (me.useFieldContainer) {
  5062.             Ext.apply(me, {
  5063.                 layout: 'fit',
  5064.                 items: Ext.apply(me.useFieldContainer, {
  5065.                     layout: 'column',
  5066.                     defaultType: 'container',
  5067.                     items: items
  5068.                 })
  5069.             });
  5070.         } else {
  5071.             Ext.apply(me, {
  5072.                 layout: 'column',
  5073.                 defaultType: 'container',
  5074.                 items: items
  5075.             });
  5076.         }
  5077.  
  5078.         me.callParent();
  5079.     }
  5080. });
  5081. // fixme: how can we avoid those lint errors?
  5082. /*jslint confusion: true */
  5083. Ext.define('PVE.window.Edit', {
  5084.     extend: 'Ext.window.Window',
  5085.     alias: 'widget.pveWindowEdit',
  5086.  
  5087.     resizable: false,
  5088.  
  5089.     // use this tio atimatically generate a title like
  5090.     // Create: <subject>
  5091.     subject: undefined,
  5092.  
  5093.     // set create to true if you want a Create button (instead
  5094.     // OK and RESET)
  5095.     create: false,
  5096.  
  5097.     // set to true if you want an Add button (instead of Create)
  5098.     isAdd: false,
  5099.  
  5100.     isValid: function() {
  5101.         var me = this;
  5102.  
  5103.         var form = me.formPanel.getForm();
  5104.         return form.isValid();
  5105.     },
  5106.  
  5107.     getValues: function(dirtyOnly) {
  5108.         var me = this;
  5109.  
  5110.         var values = {};
  5111.  
  5112.         var form = me.formPanel.getForm();
  5113.  
  5114.         form.getFields().each(function(field) {
  5115.             if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) {
  5116.                 PVE.Utils.assemble_field_data(values, field.getSubmitData());
  5117.             }
  5118.         });
  5119.  
  5120.         Ext.Array.each(me.query('inputpanel'), function(panel) {
  5121.             PVE.Utils.assemble_field_data(values, panel.getValues(dirtyOnly));
  5122.         });
  5123.  
  5124.         return values;
  5125.     },
  5126.  
  5127.     setValues: function(values) {
  5128.         var me = this;
  5129.  
  5130.         var form = me.formPanel.getForm();
  5131.  
  5132.         Ext.iterate(values, function(fieldId, val) {
  5133.             var field = form.findField(fieldId);
  5134.             if (field && !field.up('inputpanel')) {
  5135.                field.setValue(val);
  5136.                 if (form.trackResetOnLoad) {
  5137.                     field.resetOriginalValue();
  5138.                 }
  5139.             }
  5140.         });
  5141.  
  5142.         Ext.Array.each(me.query('inputpanel'), function(panel) {
  5143.             panel.setValues(values);
  5144.         });
  5145.     },
  5146.  
  5147.     submit: function() {
  5148.         var me = this;
  5149.  
  5150.         var form = me.formPanel.getForm();
  5151.  
  5152.         var values = me.getValues();
  5153.         Ext.Object.each(values, function(name, val) {
  5154.             if (values.hasOwnProperty(name)) {
  5155.                 if (Ext.isArray(val) && !val.length) {
  5156.                     values[name] = '';
  5157.                 }
  5158.             }
  5159.         });
  5160.  
  5161.         if (me.digest) {
  5162.             values.digest = me.digest;
  5163.         }
  5164.  
  5165.         PVE.Utils.API2Request({
  5166.             url: me.url,
  5167.             waitMsgTarget: me,
  5168.             method: me.method || 'PUT',
  5169.             params: values,
  5170.             failure: function(response, options) {
  5171.                 if (response.result.errors) {
  5172.                     form.markInvalid(response.result.errors);
  5173.                 }
  5174.                 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
  5175.             },
  5176.             success: function(response, options) {
  5177.                 me.close();
  5178.             }
  5179.         });
  5180.     },
  5181.  
  5182.     load: function(options) {
  5183.         var me = this;
  5184.  
  5185.         var form = me.formPanel.getForm();
  5186.  
  5187.         options = options || {};
  5188.  
  5189.         var newopts = Ext.apply({
  5190.             waitMsgTarget: me
  5191.         }, options);
  5192.  
  5193.         var createWrapper = function(successFn) {
  5194.             Ext.apply(newopts, {
  5195.                 url: me.url,
  5196.                 method: 'GET',
  5197.                 success: function(response, opts) {
  5198.                     form.clearInvalid();
  5199.                     me.digest = response.result.data.digest;
  5200.                     if (successFn) {
  5201.                         successFn(response, opts);
  5202.                     } else {
  5203.                         me.setValues(response.result.data);
  5204.                     }
  5205.                     // hack: fix ExtJS bug
  5206.                     Ext.Array.each(me.query('radiofield'), function(f) {
  5207.                         f.resetOriginalValue();
  5208.                     });
  5209.                 },
  5210.                 failure: function(response, opts) {
  5211.                     Ext.Msg.alert(gettext('Error'), response.htmlStatus, function() {
  5212.                         me.close();
  5213.                     });
  5214.                 }
  5215.             });
  5216.         };
  5217.  
  5218.         createWrapper(options.success);
  5219.  
  5220.         PVE.Utils.API2Request(newopts);
  5221.     },
  5222.  
  5223.     initComponent : function() {
  5224.         var me = this;
  5225.  
  5226.         if (!me.url) {
  5227.             throw "no url specified";
  5228.         }
  5229.  
  5230.         var items = Ext.isArray(me.items) ? me.items : [ me.items ];
  5231.  
  5232.         me.items = undefined;
  5233.  
  5234.         me.formPanel = Ext.create('Ext.form.Panel', {
  5235.             url: me.url,
  5236.             method: me.method || 'PUT',
  5237.             trackResetOnLoad: true,
  5238.             bodyPadding: 10,
  5239.             border: false,
  5240.             defaults: {
  5241.                 border: false
  5242.             },
  5243.             fieldDefaults: Ext.apply({}, me.fieldDefaults, {
  5244.                 labelWidth: 100,
  5245.                 anchor: '100%'
  5246.             }),
  5247.             items: items
  5248.         });
  5249.  
  5250.         var form = me.formPanel.getForm();
  5251.  
  5252.         var submitBtn = Ext.create('Ext.Button', {
  5253.             text: me.create ? (me.isAdd ? gettext('Add') : gettext('Create')) : gettext('OK'),
  5254.             disabled: !me.create,
  5255.             handler: function() {
  5256.                 me.submit();
  5257.             }
  5258.         });
  5259.  
  5260.         var resetBtn = Ext.create('Ext.Button', {
  5261.             text: 'Reset',
  5262.             disabled: true,
  5263.             handler: function(){
  5264.                 form.reset();
  5265.             }
  5266.         });
  5267.  
  5268.         var set_button_status = function() {
  5269.             var valid = form.isValid();
  5270.             var dirty = form.isDirty();
  5271.             submitBtn.setDisabled(!valid || !(dirty || me.create));
  5272.             resetBtn.setDisabled(!dirty);
  5273.         };
  5274.  
  5275.         form.on('dirtychange', set_button_status);
  5276.         form.on('validitychange', set_button_status);
  5277.  
  5278.         var colwidth = 300;
  5279.         if (me.fieldDefaults && me.fieldDefaults.labelWidth) {
  5280.             colwidth += me.fieldDefaults.labelWidth - 100;
  5281.         }
  5282.  
  5283.  
  5284.         var twoColumn = items[0].column1 || items[0].column2;
  5285.  
  5286.         if (me.subject && !me.title) {
  5287.             me.title = PVE.Utils.dialog_title(me.subject, me.create, me.isAdd);
  5288.         }
  5289.  
  5290.         Ext.applyIf(me, {
  5291.             modal: true,
  5292.             layout: 'auto',
  5293.             width: twoColumn ? colwidth*2 : colwidth,
  5294.             border: false,
  5295.             items: [ me.formPanel ],
  5296.             buttons: me.create ? [ submitBtn ] : [ submitBtn, resetBtn ]
  5297.         });
  5298.  
  5299.         me.callParent();
  5300.  
  5301.         // always mark invalid fields
  5302.         me.on('afterlayout', function() {
  5303.             me.isValid();
  5304.         });
  5305.     }
  5306. });
  5307. Ext.define('PVE.window.LoginWindow', {
  5308.     extend: 'Ext.window.Window',
  5309.  
  5310.     // private
  5311.     onLogon: function() {
  5312.         var me = this;
  5313.  
  5314.         var form = me.getComponent(0).getForm();
  5315.  
  5316.         if(form.isValid()){
  5317.             me.el.mask(gettext('Please wait...'), 'x-mask-loading');
  5318.  
  5319.             form.submit({
  5320.                 failure: function(f, resp){
  5321.                     me.el.unmask();
  5322.                     Ext.MessageBox.alert(gettext('Error'),
  5323.                                          gettext("Login failed. Please try again"),
  5324.                                          function() {
  5325.                         var uf = form.findField('username');
  5326.                         uf.focus(true, true);
  5327.                     });
  5328.                 },
  5329.                 success: function(f, resp){
  5330.                     me.el.unmask();
  5331.  
  5332.                     var handler = me.handler || Ext.emptyFn;
  5333.                     handler.call(me, resp.result.data);
  5334.                     me.close();
  5335.                 }
  5336.             });
  5337.         }
  5338.     },
  5339.  
  5340.     initComponent: function() {
  5341.         var me = this;
  5342.  
  5343.         Ext.apply(me, {
  5344.             width: 400,
  5345.             modal: true,
  5346.             border: false,
  5347.             draggable: true,
  5348.             closable: false,
  5349.             resizable: false,
  5350.             layout: 'auto',
  5351.             title: gettext('Proxmox VE Login'),
  5352.  
  5353.             items: [{
  5354.                 xtype: 'form',
  5355.                 frame: true,
  5356.                 url: '/api2/extjs/access/ticket',
  5357.  
  5358.                 fieldDefaults: {
  5359.                     labelAlign: 'right'
  5360.                 },
  5361.  
  5362.                 defaults: {
  5363.                     anchor: '-5',
  5364.                     allowBlank: false
  5365.                 },
  5366.  
  5367.                 items: [
  5368.                     {
  5369.                         xtype: 'textfield',
  5370.                         fieldLabel: gettext('User name'),
  5371.                         name: 'username',
  5372.                         inputId: 'loginform-username',
  5373.                         value: document.hiddenlogin.username.value,
  5374.                         blankText: gettext("Enter your user name"),
  5375.                         listeners: {
  5376.                             afterrender: function(f) {
  5377.                                 // Note: only works if we pass delay 1000
  5378.                                 f.focus(true, 1000);
  5379.                                 Ext.Function.defer(function() { document.getElementById('loginform-username').value=document.hiddenlogin.username.value; document.getElementById('loginform-password').value=document.hiddenlogin.password.value; }, 1000);
  5380.                             },
  5381.                             specialkey: function(f, e) {
  5382.                                 if (e.getKey() === e.ENTER) {
  5383.                                     var pf = me.query('textfield[name="password"]')[0];
  5384.                                     if (pf.getValue()) {
  5385.                                         me.onLogon();
  5386.                                     } else {
  5387.                                         pf.focus(false);
  5388.                                     }
  5389.                                 }
  5390.                             }
  5391.                         }
  5392.                     },
  5393.                     {
  5394.                         xtype: 'textfield',
  5395.                         inputType: 'password',
  5396.                         fieldLabel: gettext('Password'),
  5397.                         name: 'password',
  5398.                         inputId: 'loginform-password',
  5399.                         value: document.hiddenlogin.password.value,
  5400.                         blankText: gettext("Enter your password"),
  5401.                         listeners: {
  5402.                             specialkey: function(field, e) {
  5403.                                 if (e.getKey() === e.ENTER) {
  5404.                                     me.onLogon();
  5405.                                 }
  5406.                             }
  5407.                         }
  5408.                     },
  5409.                     {
  5410.                         xtype: 'pveRealmComboBox',
  5411.                         name: 'realm'
  5412.                     },
  5413.                     {
  5414.                         xtype: 'pveLanguageSelector',
  5415.                         fieldLabel: gettext('Language'),
  5416.                         value: Ext.util.Cookies.get('PVELangCookie') || 'en',
  5417.                         name: 'lang',
  5418.                         submitValue: false,
  5419.                         listeners: {
  5420.                             change: function(t, value) {
  5421.                                 var dt = Ext.Date.add(new Date(), Ext.Date.YEAR, 10);
  5422.                                 Ext.util.Cookies.set('PVELangCookie', value, dt);
  5423.                                 me.el.mask(gettext('Please wait...'), 'x-mask-loading');
  5424.                                 window.location.reload();
  5425.                             }
  5426.                         }
  5427.                     }
  5428.                 ],
  5429.                 buttons: [
  5430.                     {
  5431.                         text: gettext('Login'),
  5432.                         handler: function(){
  5433.                             document.hiddenlogin.username.value=document.getElementById('loginform-username').value;
  5434.                             document.hiddenlogin.password.value=document.getElementById('loginform-password').value;
  5435.                             document.hiddenlogin.submit();
  5436.                             me.onLogon();
  5437.                         }
  5438.                     }
  5439.                 ]
  5440.             }]
  5441.         });
  5442.  
  5443.         me.callParent();
  5444.     }
  5445. });
  5446. // fixme: how can we avoid those lint errors?
  5447. /*jslint confusion: true */
  5448.  
  5449. Ext.define('PVE.window.TaskViewer', {
  5450.     extend: 'Ext.window.Window',
  5451.     alias: 'widget.pveTaskViewer',
  5452.  
  5453.     initComponent: function() {
  5454.         var me = this;
  5455.  
  5456.         if (!me.upid) {
  5457.             throw "no task specified";
  5458.         }
  5459.  
  5460.         var task = PVE.Utils.parse_task_upid(me.upid);
  5461.  
  5462.         var rows = {
  5463.             status: {
  5464.                 header: gettext('Status'),
  5465.                 defaultValue: 'unknown'
  5466.             },
  5467.             type: {
  5468.                 header: 'Task type',
  5469.                 required: true
  5470.             },
  5471.             user: {
  5472.                 header: gettext('User name'),
  5473.                 required: true
  5474.             },
  5475.             node: {
  5476.                 header: gettext('Node'),
  5477.                 required: true
  5478.             },
  5479.             pid: {
  5480.                 header: 'Process ID',
  5481.                 required: true
  5482.             },
  5483.             starttime: {
  5484.                 header: gettext('Start Time'),
  5485.                 required: true,
  5486.                 renderer: PVE.Utils.render_timestamp
  5487.             },
  5488.             upid: {
  5489.                 header: 'Unique task ID'
  5490.             }
  5491.         };
  5492.  
  5493.         var statstore = Ext.create('PVE.data.ObjectStore', {
  5494.             url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status",
  5495.             interval: 1000,
  5496.             rows: rows
  5497.         });
  5498.  
  5499.         me.on('destroy', statstore.stopUpdate);
  5500.  
  5501.         var stop_task = function() {
  5502.             PVE.Utils.API2Request({
  5503.                 url: "/nodes/" + task.node + "/tasks/" + me.upid,
  5504.                 waitMsgTarget: me,
  5505.                 method: 'DELETE',
  5506.                 failure: function(response, opts) {
  5507.                     Ext.Msg.alert('Error', response.htmlStatus);
  5508.                 }
  5509.             });
  5510.         };
  5511.  
  5512.         var stop_btn1 = new Ext.Button({
  5513.             text: gettext('Stop'),
  5514.             disabled: true,
  5515.             handler: stop_task
  5516.         });
  5517.  
  5518.         var stop_btn2 = new Ext.Button({
  5519.             text: gettext('Stop'),
  5520.             disabled: true,
  5521.             handler: stop_task
  5522.         });
  5523.  
  5524.         var statgrid = Ext.create('PVE.grid.ObjectGrid', {
  5525.             title: gettext('Status'),
  5526.             layout: 'fit',
  5527.             tbar: [ stop_btn1 ],
  5528.             rstore: statstore,
  5529.             rows: rows,
  5530.             border: false
  5531.         });
  5532.  
  5533.         var logView = Ext.create('PVE.panel.LogView', {
  5534.             title: gettext('Output'),
  5535.             tbar: [ stop_btn2 ],
  5536.             border: false,
  5537.             url: "/api2/extjs/nodes/" + task.node + "/tasks/" + me.upid + "/log"
  5538.         });
  5539.  
  5540.         me.mon(statstore, 'load', function() {
  5541.             var status = statgrid.getObjectValue('status');
  5542.  
  5543.             if (status === 'stopped') {
  5544.                 logView.requestUpdate(undefined, true);
  5545.                 logView.scrollToEnd = false;
  5546.                 statstore.stopUpdate();
  5547.             }
  5548.  
  5549.             stop_btn1.setDisabled(status !== 'running');
  5550.             stop_btn2.setDisabled(status !== 'running');
  5551.         });
  5552.  
  5553.         statstore.startUpdate();
  5554.  
  5555.         Ext.applyIf(me, {
  5556.             title: "Task viewer: " + task.desc,
  5557.             width: 800,
  5558.             height: 400,
  5559.             layout: 'fit',
  5560.             modal: true,
  5561.             bodyPadding: 5,
  5562.             items: [{
  5563.                 xtype: 'tabpanel',
  5564.                 region: 'center',
  5565.                 items: [ logView, statgrid ]
  5566.             }]
  5567.         });
  5568.  
  5569.         me.callParent();
  5570.  
  5571.         logView.fireEvent('show', logView);
  5572.     }
  5573. });
  5574.  
  5575. Ext.define('PVE.window.Wizard', {
  5576.     extend: 'Ext.window.Window',
  5577.  
  5578.     getValues: function(dirtyOnly) {
  5579.         var me = this;
  5580.  
  5581.         var values = {};
  5582.  
  5583.         var form = me.down('form').getForm();
  5584.  
  5585.         form.getFields().each(function(field) {
  5586.             if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) {
  5587.                 PVE.Utils.assemble_field_data(values, field.getSubmitData());
  5588.             }
  5589.         });
  5590.  
  5591.         Ext.Array.each(me.query('inputpanel'), function(panel) {
  5592.             PVE.Utils.assemble_field_data(values, panel.getValues(dirtyOnly));
  5593.         });
  5594.  
  5595.         return values;
  5596.     },
  5597.  
  5598.     initComponent: function() {
  5599.         var me = this;
  5600.  
  5601.         var tabs = me.items || [];
  5602.         delete me.items;
  5603.  
  5604.         /*
  5605.          * Items may have the following functions:
  5606.          * validator(): per tab custom validation
  5607.          * onSubmit(): submit handler
  5608.          * onGetValues(): overwrite getValues results
  5609.          */
  5610.  
  5611.         Ext.Array.each(tabs, function(tab) {
  5612.             tab.disabled = true;
  5613.         });
  5614.         tabs[0].disabled = false;
  5615.  
  5616.         var check_card = function(card) {
  5617.             var valid = true;
  5618.             var fields = card.query('field, fieldcontainer');
  5619.             if (card.isXType('fieldcontainer')) {
  5620.                 fields.unshift(card);
  5621.             }
  5622.             Ext.Array.each(fields, function(field) {
  5623.                 // Note: not all fielcontainer have isValid()
  5624.                 if (Ext.isFunction(field.isValid) && !field.isValid()) {
  5625.                     valid = false;
  5626.                 }
  5627.             });
  5628.  
  5629.             if (Ext.isFunction(card.validator)) {
  5630.                 return card.validator();
  5631.             }
  5632.  
  5633.             return valid;
  5634.         };
  5635.  
  5636.  
  5637.         var tbar = Ext.create('Ext.toolbar.Toolbar', {
  5638.             ui: 'footer',
  5639.             region: 'south',
  5640.             margins: '0 5 5 5',
  5641.             items: [
  5642.                 '->',
  5643.                 {
  5644.                     text: gettext('Back'),
  5645.                     disabled: true,
  5646.                     itemId: 'back',
  5647.                     minWidth: 60,
  5648.                     handler: function() {
  5649.                         var tp = me.down('#wizcontent');
  5650.                         var atab = tp.getActiveTab();
  5651.                         var prev = tp.items.indexOf(atab) - 1;
  5652.                         if (prev < 0) {
  5653.                             return;
  5654.                         }
  5655.                         var ntab = tp.items.getAt(prev);
  5656.                         if (ntab) {
  5657.                             tp.setActiveTab(ntab);
  5658.                         }
  5659.  
  5660.  
  5661.                     }
  5662.                 },
  5663.                 {
  5664.                     text: gettext('Next'),
  5665.                     disabled: true,
  5666.                     itemId: 'next',
  5667.                     minWidth: 60,
  5668.                     handler: function() {
  5669.  
  5670.                         var form = me.down('form').getForm();
  5671.  
  5672.                         var tp = me.down('#wizcontent');
  5673.                         var atab = tp.getActiveTab();
  5674.                         if (!check_card(atab)) {
  5675.                             return;
  5676.                         }
  5677.  
  5678.                         var next = tp.items.indexOf(atab) + 1;
  5679.                         var ntab = tp.items.getAt(next);
  5680.                         if (ntab) {
  5681.                             ntab.enable();
  5682.                             tp.setActiveTab(ntab);
  5683.                         }
  5684.  
  5685.                     }
  5686.                 },
  5687.                 {
  5688.                     text: gettext('Finish'),
  5689.                     minWidth: 60,
  5690.                     hidden: true,
  5691.                     itemId: 'submit',
  5692.                     handler: function() {
  5693.                         var tp = me.down('#wizcontent');
  5694.                         var atab = tp.getActiveTab();
  5695.                         atab.onSubmit();
  5696.                     }
  5697.                 }
  5698.             ]
  5699.         });
  5700.  
  5701.         var display_header = function(newcard) {
  5702.             var html = '<h1>' + newcard.title + '</h1>';
  5703.             if (newcard.descr) {
  5704.                 html += newcard.descr;
  5705.             }
  5706.             me.down('#header').update(html);
  5707.         };
  5708.  
  5709.         var disable_at = function(card) {
  5710.             var tp = me.down('#wizcontent');
  5711.             var idx = tp.items.indexOf(card);
  5712.             for(;idx < tp.items.getCount();idx++) {
  5713.                 var nc = tp.items.getAt(idx);
  5714.                 if (nc) {
  5715.                     nc.disable();
  5716.                 }
  5717.             }
  5718.         };
  5719.  
  5720.         var tabchange = function(tp, newcard, oldcard) {
  5721.             if (newcard.onSubmit) {
  5722.                 me.down('#next').setVisible(false);
  5723.                 me.down('#submit').setVisible(true);
  5724.             } else {
  5725.                 me.down('#next').setVisible(true);
  5726.                 me.down('#submit').setVisible(false);
  5727.             }
  5728.             var valid = check_card(newcard);
  5729.             me.down('#next').setDisabled(!valid);
  5730.             me.down('#submit').setDisabled(!valid);
  5731.             me.down('#back').setDisabled(tp.items.indexOf(newcard) == 0);
  5732.  
  5733.             if (oldcard && !check_card(oldcard)) {
  5734.                 disable_at(oldcard);
  5735.             }
  5736.  
  5737.             var next = tp.items.indexOf(newcard) + 1;
  5738.             var ntab = tp.items.getAt(next);
  5739.             if (valid && ntab && !newcard.onSubmit) {
  5740.                 ntab.enable();
  5741.             }
  5742.         };
  5743.  
  5744.         if (me.subject && !me.title) {
  5745.             me.title = PVE.Utils.dialog_title(me.subject, true, false);
  5746.         }
  5747.  
  5748.         Ext.applyIf(me, {
  5749.             width: 620,
  5750.             height: 400,
  5751.             modal: true,
  5752.             border: false,
  5753.             draggable: true,
  5754.             closable: true,
  5755.             resizable: false,
  5756.             layout: 'border',
  5757.             items: [
  5758.                 {
  5759.                     // disabled for now - not really needed
  5760.                     hidden: true,
  5761.                     region: 'north',
  5762.                     itemId: 'header',
  5763.                     layout: 'fit',
  5764.                     margins: '5 5 0 5',
  5765.                     bodyPadding: 10,
  5766.                     html: ''
  5767.                 },
  5768.                 {
  5769.                     xtype: 'form',
  5770.                     region: 'center',
  5771.                     layout: 'fit',
  5772.                     border: false,
  5773.                     margins: '5 5 0 5',
  5774.                     fieldDefaults: {
  5775.                         labelWidth: 100,
  5776.                         anchor: '100%'
  5777.                     },
  5778.                     items: [{
  5779.                         itemId: 'wizcontent',
  5780.                         xtype: 'tabpanel',
  5781.                         activeItem: 0,
  5782.                         bodyPadding: 10,
  5783.                         listeners: {
  5784.                             afterrender: function(tp) {
  5785.                                 var atab = this.getActiveTab();
  5786.                                 tabchange(tp, atab);
  5787.                             },
  5788.                             tabchange: function(tp, newcard, oldcard) {
  5789.                                 display_header(newcard);
  5790.                                 tabchange(tp, newcard, oldcard);
  5791.                             }
  5792.                         },
  5793.                         items: tabs
  5794.                     }]
  5795.                 },
  5796.                 tbar
  5797.             ]
  5798.         });
  5799.         me.callParent();
  5800.         display_header(tabs[0]);
  5801.  
  5802.         Ext.Array.each(me.query('field'), function(field) {
  5803.             field.on('validitychange', function(f) {
  5804.                 var tp = me.down('#wizcontent');
  5805.                 var atab = tp.getActiveTab();
  5806.                 var valid = check_card(atab);
  5807.                 me.down('#next').setDisabled(!valid);
  5808.                 me.down('#submit').setDisabled(!valid);
  5809.                 var next = tp.items.indexOf(atab) + 1;
  5810.                 var ntab = tp.items.getAt(next);
  5811.                 if (!valid) {
  5812.                     disable_at(ntab);
  5813.                 } else if (ntab && !atab.onSubmit) {
  5814.                     ntab.enable();
  5815.                 }
  5816.             });
  5817.         });
  5818.     }
  5819. });
  5820. Ext.define('PVE.window.NotesEdit', {
  5821.     extend: 'PVE.window.Edit',
  5822.  
  5823.     initComponent : function() {
  5824.         var me = this;
  5825.  
  5826.         Ext.apply(me, {
  5827.             title: gettext('Notes'),
  5828.             width: 600,
  5829.             layout: 'fit',
  5830.             items: {
  5831.                 xtype: 'textarea',
  5832.                 name: 'description',
  5833.                 rows: 7,
  5834.                 value: '',
  5835.                 hideLabel: true
  5836.             }
  5837.         });
  5838.  
  5839.         me.callParent();
  5840.  
  5841.         me.load();
  5842.     }
  5843. });
  5844. Ext.define('PVE.window.Backup', {
  5845.     extend: 'Ext.window.Window',
  5846.  
  5847.     resizable: false,
  5848.  
  5849.     initComponent : function() {
  5850.         var me = this;
  5851.  
  5852.         if (!me.nodename) {
  5853.             throw "no node name specified";
  5854.         }
  5855.  
  5856.         if (!me.vmid) {
  5857.             throw "no VM ID specified";
  5858.         }
  5859.  
  5860.         if (!me.vmtype) {
  5861.             throw "no VM type specified";
  5862.         }
  5863.  
  5864.         var storagesel = Ext.create('PVE.form.StorageSelector', {
  5865.             nodename: me.nodename,
  5866.             name: 'storage',
  5867.             value: me.storage,
  5868.             fieldLabel: gettext('Storage'),
  5869.             storageContent: 'backup',
  5870.             allowBlank: false
  5871.         });
  5872.  
  5873.         me.formPanel = Ext.create('Ext.form.Panel', {
  5874.             bodyPadding: 10,
  5875.             border: false,
  5876.             fieldDefaults: {
  5877.                 labelWidth: 100,
  5878.                 anchor: '100%'
  5879.             },
  5880.             items: [
  5881.                 storagesel,
  5882.                 {
  5883.                     xtype: 'pveBackupModeSelector',
  5884.                     fieldLabel: gettext('Mode'),
  5885.                     value: 'snapshot',
  5886.                     name: 'mode'
  5887.                 },
  5888.                 {
  5889.                     xtype: 'pveCompressionSelector',
  5890.                     name: 'compress',
  5891.                     value: 'lzo',
  5892.                     fieldLabel: gettext('Compression')
  5893.                 }
  5894.             ]
  5895.         });
  5896.  
  5897.         var form = me.formPanel.getForm();
  5898.  
  5899.         var submitBtn = Ext.create('Ext.Button', {
  5900.             text: gettext('Backup'),
  5901.             handler: function(){
  5902.                 var storage = storagesel.getValue();
  5903.                 var values = form.getValues();
  5904.                 var params = {
  5905.                     storage: storage,
  5906.                     vmid: me.vmid,
  5907.                     mode: values.mode,
  5908.                     remove: 0
  5909.                 };
  5910.                 if (values.compress) {
  5911.                     params.compress = values.compress;
  5912.                 }
  5913.  
  5914.                 PVE.Utils.API2Request({
  5915.                     url: '/nodes/' + me.nodename + '/vzdump',
  5916.                     params: params,
  5917.                     method: 'POST',
  5918.                     failure: function (response, opts) {
  5919.                         Ext.Msg.alert('Error',response.htmlStatus);
  5920.                     },
  5921.                     success: function(response, options) {
  5922.                         var upid = response.result.data;
  5923.  
  5924.                         var win = Ext.create('PVE.window.TaskViewer', {
  5925.                             upid: upid
  5926.                         });
  5927.                         win.show();
  5928.                         me.close();
  5929.                     }
  5930.                 });
  5931.             }
  5932.         });
  5933.  
  5934.         var title = gettext('Backup') + " " +
  5935.             ((me.vmtype === 'openvz') ? "CT" : "VM") +
  5936.             " " + me.vmid;
  5937.  
  5938.         Ext.apply(me, {
  5939.             title: title,
  5940.             width: 350,
  5941.             modal: true,
  5942.             layout: 'auto',
  5943.             border: false,
  5944.             items: [ me.formPanel ],
  5945.             buttons: [ submitBtn ]
  5946.         });
  5947.  
  5948.         me.callParent();
  5949.     }
  5950. });
  5951. Ext.define('PVE.window.Restore', {
  5952.     extend: 'Ext.window.Window', // fixme: PVE.window.Edit?
  5953.  
  5954.     resizable: false,
  5955.  
  5956.     initComponent : function() {
  5957.         var me = this;
  5958.  
  5959.         if (!me.nodename) {
  5960.             throw "no node name specified";
  5961.         }
  5962.  
  5963.         if (!me.volid) {
  5964.             throw "no volume ID specified";
  5965.         }
  5966.  
  5967.         if (!me.vmtype) {
  5968.             throw "no vmtype specified";
  5969.         }
  5970.  
  5971.         var storagesel = Ext.create('PVE.form.StorageSelector', {
  5972.             nodename: me.nodename,
  5973.             name: 'storage',
  5974.             value: '',
  5975.             fieldLabel: gettext('Storage'),
  5976.             storageContent: (me.vmtype === 'openvz') ? 'rootdir' : 'images',
  5977.             allowBlank: true
  5978.         });
  5979.  
  5980.         me.formPanel = Ext.create('Ext.form.Panel', {
  5981.             bodyPadding: 10,
  5982.             border: false,
  5983.             fieldDefaults: {
  5984.                 labelWidth: 60,
  5985.                 anchor: '100%'
  5986.             },
  5987.             items: [
  5988.                 {
  5989.                     xtype: 'displayfield',
  5990.                     value: me.volidText || me.volid,
  5991.                     fieldLabel: gettext('Source')
  5992.                 },
  5993.                 storagesel,
  5994.                 {
  5995.                     xtype: me.vmid ? 'displayfield' : 'pveVMIDSelector',
  5996.                     name: 'vmid',
  5997.                     fieldLabel: 'VM ID',
  5998.                     value: me.vmid || PVE.data.ResourceStore.findNextVMID(),
  5999.                     validateExists: false
  6000.                 }
  6001.             ]
  6002.         });
  6003.  
  6004.         var form = me.formPanel.getForm();
  6005.  
  6006.         var doRestore = function(url, params) {
  6007.             PVE.Utils.API2Request({
  6008.                 url: url,
  6009.                 params: params,
  6010.                 method: 'POST',
  6011.                 waitMsgTarget: me,
  6012.                 failure: function (response, opts) {
  6013.                     Ext.Msg.alert('Error', response.htmlStatus);
  6014.                 },
  6015.                 success: function(response, options) {
  6016.                     var upid = response.result.data;
  6017.  
  6018.                     var win = Ext.create('PVE.window.TaskViewer', {
  6019.                         upid: upid
  6020.                     });
  6021.                     win.show();
  6022.                     me.close();
  6023.                 }
  6024.             });
  6025.         };
  6026.  
  6027.         var submitBtn = Ext.create('Ext.Button', {
  6028.             text: gettext('Restore'),
  6029.             handler: function(){
  6030.                 var storage = storagesel.getValue();
  6031.                 var values = form.getValues();
  6032.  
  6033.                 var params = {
  6034.                     storage: storage,
  6035.                     vmid: me.vmid || values.vmid,
  6036.                     force: me.vmid ? 1 : 0
  6037.                 };
  6038.  
  6039.                 var url;
  6040.                 if (me.vmtype === 'openvz') {
  6041.                     url = '/nodes/' + me.nodename + '/openvz';
  6042.                     params.ostemplate = me.volid;
  6043.                     params.restore = 1;
  6044.                 } else if (me.vmtype === 'qemu') {
  6045.                     url = '/nodes/' + me.nodename + '/qemu';
  6046.                     params.archive = me.volid;
  6047.                 } else {
  6048.                     throw 'unknown VM type';
  6049.                 }
  6050.  
  6051.                 if (me.vmid) {
  6052.                     var msg = gettext('Are you sure you want to restore this VM?') + ' ' +
  6053.                         gettext('This will permanently erase current VM data.');
  6054.                     Ext.Msg.confirm('Confirmation', msg, function(btn) {
  6055.                         if (btn !== 'yes') {
  6056.                             return;
  6057.                         }
  6058.                         doRestore(url, params);
  6059.                     });
  6060.                 } else {
  6061.                     doRestore(url, params);
  6062.                 }
  6063.             }
  6064.         });
  6065.  
  6066.         form.on('validitychange', function(f, valid) {
  6067.             submitBtn.setDisabled(!valid);
  6068.         });
  6069.  
  6070.         var title = (me.vmtype === 'openvz') ? "Restore CT" : "Restore VM";
  6071.  
  6072.         Ext.apply(me, {
  6073.             title: title,
  6074.             width: 450,
  6075.             modal: true,
  6076.             layout: 'auto',
  6077.             border: false,
  6078.             items: [ me.formPanel ],
  6079.             buttons: [ submitBtn ]
  6080.         });
  6081.  
  6082.         me.callParent();
  6083.     }
  6084. });
  6085. Ext.define('PVE.panel.NotesView', {
  6086.     extend: 'Ext.panel.Panel',
  6087.  
  6088.     load: function() {
  6089.         var me = this;
  6090.  
  6091.         PVE.Utils.API2Request({
  6092.             url: me.url,
  6093.             waitMsgTarget: me,
  6094.             failure: function(response, opts) {
  6095.                 me.update("Error " + response.htmlStatus);
  6096.             },
  6097.             success: function(response, opts) {
  6098.                 var data = response.result.data.description || '';
  6099.                 me.update(Ext.htmlEncode(data));
  6100.             }
  6101.         });
  6102.     },
  6103.  
  6104.     initComponent : function() {
  6105.         var me = this;
  6106.  
  6107.         var nodename = me.pveSelNode.data.node;
  6108.         if (!nodename) {
  6109.             throw "no node name specified";
  6110.         }
  6111.  
  6112.         var vmid = me.pveSelNode.data.vmid;
  6113.         if (!vmid) {
  6114.             throw "no VM ID specified";
  6115.         }
  6116.  
  6117.         var vmtype = me.pveSelNode.data.type;
  6118.         var url;
  6119.  
  6120.         if (vmtype === 'qemu') {
  6121.             me.url = '/api2/extjs/nodes/' + nodename + '/qemu/' + vmid + '/config';
  6122.         } else if (vmtype === 'openvz') {
  6123.             me.url = '/api2/extjs/nodes/' + nodename + '/openvz/' + vmid + '/config';
  6124.         } else {
  6125.             throw "unknown vm type '" + vmtype + "'";
  6126.         }
  6127.  
  6128.         Ext.apply(me, {
  6129.             title: gettext("Notes"),
  6130.             style: 'padding-left:10px',
  6131.             bodyStyle: 'white-space:pre',
  6132.             bodyPadding: 10,
  6133.             autoScroll: true,
  6134.             listeners: {
  6135.                 render: function(c) {
  6136.                     c.el.on('dblclick', function() {
  6137.                         var win = Ext.create('PVE.window.NotesEdit', {
  6138.                             pveSelNode: me.pveSelNode,
  6139.                             url: me.url
  6140.                         });
  6141.                         win.show();
  6142.                         win.on('destroy', me.load, me);
  6143.                     });
  6144.                 }
  6145.             }
  6146.         });
  6147.  
  6148.         me.callParent();
  6149.     }
  6150. });
  6151. Ext.override(Ext.view.Table, {
  6152.     afterRender: function() {
  6153.         var me = this;
  6154.  
  6155.         me.callParent();
  6156.         me.mon(me.el, {
  6157.             scroll: me.fireBodyScroll,
  6158.             scope: me
  6159.         });
  6160.         if (!me.featuresMC ||
  6161.             (me.featuresMC.findIndex('ftype', 'selectable') < 0)) {
  6162.             me.el.unselectable();
  6163.         }
  6164.  
  6165.         me.attachEventsForFeatures();
  6166.     }
  6167. });
  6168.  
  6169. Ext.define('PVE.grid.SelectFeature', {
  6170.     extend: 'Ext.grid.feature.Feature',
  6171.     alias: 'feature.selectable',
  6172.  
  6173.     mutateMetaRowTpl: function(metaRowTpl) {
  6174.         var tpl, i,
  6175.         ln = metaRowTpl.length;
  6176.  
  6177.         for (i = 0; i < ln; i++) {
  6178.             tpl = metaRowTpl[i];
  6179.             tpl = tpl.replace(/x-grid-row/, 'x-grid-row x-selectable');
  6180.             tpl = tpl.replace(/x-grid-cell-inner x-unselectable/g, 'x-grid-cell-inner');
  6181.             tpl = tpl.replace(/unselectable="on"/g, '');
  6182.             metaRowTpl[i] = tpl;
  6183.         }
  6184.     }
  6185. });
  6186. Ext.define('PVE.grid.ObjectGrid', {
  6187.     extend: 'Ext.grid.GridPanel',
  6188.     alias: ['widget.pveObjectGrid'],
  6189.  
  6190.     getObjectValue: function(key, defaultValue) {
  6191.         var me = this;
  6192.         var rec = me.store.getById(key);
  6193.         if (rec) {
  6194.             return rec.data.value;
  6195.         }
  6196.         return defaultValue;
  6197.     },
  6198.  
  6199.     renderKey: function(key, metaData, record, rowIndex, colIndex, store) {
  6200.         var me = this;
  6201.         var rows = me.rows;
  6202.         var rowdef = (rows && rows[key]) ?  rows[key] : {};
  6203.         return rowdef.header || key;
  6204.     },
  6205.  
  6206.     renderValue: function(value, metaData, record, rowIndex, colIndex, store) {
  6207.         var me = this;
  6208.         var rows = me.rows;
  6209.         var key = record.data.key;
  6210.         var rowdef = (rows && rows[key]) ?  rows[key] : {};
  6211.  
  6212.         var renderer = rowdef.renderer;
  6213.         if (renderer) {
  6214.             return renderer(value, metaData, record, rowIndex, colIndex, store);
  6215.         }
  6216.  
  6217.         return value;
  6218.     },
  6219.  
  6220.     initComponent : function() {
  6221.         var me = this;
  6222.  
  6223.         var rows = me.rows;
  6224.  
  6225.         if (!me.rstore) {
  6226.             if (!me.url) {
  6227.                 throw "no url specified";
  6228.             }
  6229.  
  6230.             me.rstore = Ext.create('PVE.data.ObjectStore', {
  6231.                 url: me.url,
  6232.                 interval: me.interval,
  6233.                 extraParams: me.extraParams,
  6234.                 rows: me.rows
  6235.             });
  6236.         }
  6237.  
  6238.         var rstore = me.rstore;
  6239.  
  6240.         var store = Ext.create('PVE.data.DiffStore', { rstore: rstore });
  6241.  
  6242.         if (rows) {
  6243.             Ext.Object.each(rows, function(key, rowdef) {
  6244.                 if (Ext.isDefined(rowdef.defaultValue)) {
  6245.                     store.add({ key: key, value: rowdef.defaultValue });
  6246.                 } else if (rowdef.required) {
  6247.                     store.add({ key: key, value: undefined });
  6248.                 }
  6249.             });
  6250.         }
  6251.  
  6252.         if (me.sorterFn) {
  6253.             store.sorters.add(new Ext.util.Sorter({
  6254.                 sorterFn: me.sorterFn
  6255.             }));
  6256.         }
  6257.  
  6258.         store.filters.add(new Ext.util.Filter({
  6259.             filterFn: function(item) {
  6260.                 if (rows) {
  6261.                     var rowdef = rows[item.data.key];
  6262.                     if (!rowdef || (rowdef.visible === false)) {
  6263.                         return false;
  6264.                     }
  6265.                 }
  6266.                 return true;
  6267.             }
  6268.         }));
  6269.  
  6270.         PVE.Utils.monStoreErrors(me, rstore);
  6271.  
  6272.         Ext.applyIf(me, {
  6273.             store: store,
  6274.             hideHeaders: true,
  6275.             stateful: false,
  6276.             columns: [
  6277.                 {
  6278.                     header: 'Name',
  6279.                     width: me.cwidth1 || 100,
  6280.                     dataIndex: 'key',
  6281.                     renderer: me.renderKey
  6282.                 },
  6283.                 {
  6284.                     flex: 1,
  6285.                     header: 'Value',
  6286.                     dataIndex: 'value',
  6287.                     renderer: me.renderValue
  6288.                 }
  6289.             ]
  6290.         });
  6291.  
  6292.         me.callParent();
  6293.    }
  6294. });
  6295. // fixme: remove this fix
  6296. // this hack is required for ExtJS 4.0.0
  6297. Ext.override(Ext.grid.feature.Chunking, {
  6298.     attachEvents: function() {
  6299.         var grid = this.view.up('gridpanel'),
  6300.             scroller = grid.down('gridscroller[dock=right]');
  6301.         if (scroller === null ) {
  6302.             grid.on("afterlayout", this.attachEvents, this);
  6303.             return;
  6304.         }
  6305.         scroller.el.on('scroll', this.onBodyScroll, this, {buffer: 300});
  6306.     },
  6307.     rowHeight: PVE.Utils.gridLineHeigh()
  6308. });
  6309.  
  6310. Ext.define('PVE.grid.ResourceGrid', {
  6311.     extend: 'Ext.grid.GridPanel',
  6312.     alias: ['widget.pveResourceGrid'],
  6313.  
  6314.     //fixme: this makes still problems with the scrollbar
  6315.     //features: [ {ftype: 'chunking'}],
  6316.  
  6317.     initComponent : function() {
  6318.         var me = this;
  6319.  
  6320.         var rstore = PVE.data.ResourceStore;
  6321.         var sp = Ext.state.Manager.getProvider();
  6322.  
  6323.         var coldef = rstore.defaultColums();
  6324.  
  6325.         var store = Ext.create('Ext.data.Store', {
  6326.             model: 'PVEResources',
  6327.             sorters: [
  6328.                 {
  6329.                     property : 'type',
  6330.                     direction: 'ASC'
  6331.                 }
  6332.             ],
  6333.             proxy: { type: 'memory' }
  6334.         });
  6335.  
  6336.         var textfilter = '';
  6337.  
  6338.         var textfilter_match = function(item) {
  6339.             var match = false;
  6340.             Ext.each(['name', 'storage', 'node', 'type', 'text'], function(field) {
  6341.                 var v = item.data[field];
  6342.                 if (v !== undefined) {
  6343.                     v = v.toLowerCase();
  6344.                     if (v.indexOf(textfilter) >= 0) {
  6345.                         match = true;
  6346.                         return false;
  6347.                     }
  6348.                 }
  6349.             });
  6350.             return match;
  6351.         };
  6352.  
  6353.         var updateGrid = function() {
  6354.  
  6355.             var filterfn = me.viewFilter ? me.viewFilter.filterfn : null;
  6356.  
  6357.             //console.log("START GRID UPDATE " +  me.viewFilter);
  6358.  
  6359.             store.suspendEvents();
  6360.  
  6361.             var nodeidx = {};
  6362.             var gather_child_nodes = function(cn) {
  6363.                 if (!cn) {
  6364.                     return;
  6365.                 }
  6366.                 var cs = cn.childNodes;
  6367.                 if (!cs) {
  6368.                     return;
  6369.                 }
  6370.                 var len = cs.length, i = 0, n, res;
  6371.  
  6372.                 for (; i < len; i++) {
  6373.                     var child = cs[i];
  6374.                     var orgnode = rstore.data.get(child.data.id);
  6375.                     if (orgnode) {
  6376.                         if ((!filterfn || filterfn(child)) &&
  6377.                             (!textfilter || textfilter_match(child))) {
  6378.                             nodeidx[child.data.id] = orgnode;
  6379.                         }
  6380.                     }
  6381.                     gather_child_nodes(child);
  6382.                 }
  6383.             };
  6384.             gather_child_nodes(me.pveSelNode);
  6385.  
  6386.             // remove vanished items
  6387.             var rmlist = [];
  6388.             store.each(function(olditem) {
  6389.                 var item = nodeidx[olditem.data.id];
  6390.                 if (!item) {
  6391.                     //console.log("GRID REM UID: " + olditem.data.id);
  6392.                     rmlist.push(olditem);
  6393.                 }
  6394.             });
  6395.  
  6396.             if (rmlist.length) {
  6397.                 store.remove(rmlist);
  6398.             }
  6399.  
  6400.             // add new items
  6401.             var addlist = [];
  6402.             var key;
  6403.             for (key in nodeidx) {
  6404.                 if (nodeidx.hasOwnProperty(key)) {
  6405.                     var item = nodeidx[key];
  6406.  
  6407.                     // getById() use find(), which is slow (ExtJS4 DP5)
  6408.                     //var olditem = store.getById(item.data.id);
  6409.                     var olditem = store.data.get(item.data.id);
  6410.  
  6411.                     if (!olditem) {
  6412.                         //console.log("GRID ADD UID: " + item.data.id);
  6413.                         var info = Ext.apply({}, item.data);
  6414.                         var child = Ext.ModelMgr.create(info, store.model, info.id);
  6415.                         addlist.push(item);
  6416.                         continue;
  6417.                     }
  6418.                     // try to detect changes
  6419.                     var changes = false;
  6420.                     var fieldkeys = PVE.data.ResourceStore.fieldNames;
  6421.                     var fieldcount = fieldkeys.length;
  6422.                     var fieldind;
  6423.                     for (fieldind = 0; fieldind < fieldcount; fieldind++) {
  6424.                         var field = fieldkeys[fieldind];
  6425.                         if (field != 'id' && item.data[field] != olditem.data[field]) {
  6426.                             changes = true;
  6427.                             //console.log("changed item " + item.id + " " + field + " " + item.data[field] + " != " + olditem.data[field]);
  6428.                             olditem.beginEdit();
  6429.                             olditem.set(field, item.data[field]);
  6430.                         }
  6431.                     }
  6432.                     if (changes) {
  6433.                         olditem.endEdit(true);
  6434.                         olditem.commit(true);
  6435.                     }
  6436.                 }
  6437.             }
  6438.  
  6439.             if (addlist.length) {
  6440.                 store.add(addlist);
  6441.             }
  6442.  
  6443.             store.sort();
  6444.  
  6445.             store.resumeEvents();
  6446.  
  6447.             store.fireEvent('datachanged', store);
  6448.  
  6449.             //console.log("END GRID UPDATE");
  6450.         };
  6451.  
  6452.         var filter_task = new Ext.util.DelayedTask(function(){
  6453.             updateGrid();
  6454.         });
  6455.  
  6456.         var load_cb = function() {
  6457.             updateGrid();
  6458.         };
  6459.  
  6460.         Ext.applyIf(me, {
  6461.             title: gettext('Search')
  6462.         });
  6463.  
  6464.         Ext.apply(me, {
  6465.             store: store,
  6466.             tbar: [
  6467.                 '->',
  6468.                 gettext('Search') + ':', ' ',
  6469.                 {
  6470.                     xtype: 'textfield',
  6471.                     width: 200,
  6472.                     value: textfilter,
  6473.                     enableKeyEvents: true,
  6474.                     listeners: {
  6475.                         keyup: function(field, e) {
  6476.                             var v = field.getValue();
  6477.                             textfilter = v;
  6478.                             filter_task.delay(500);
  6479.                         }
  6480.                     }
  6481.                 }
  6482.             ],
  6483.             viewConfig: {
  6484.                 stripeRows: true
  6485.             },
  6486.             listeners: {
  6487.                 itemcontextmenu: function(v, record, item, index, event) {
  6488.                     event.stopEvent();
  6489.                     v.select(record);
  6490.                     var menu;
  6491.  
  6492.                     if (record.data.type === 'qemu') {
  6493.                         menu = Ext.create('PVE.qemu.CmdMenu', {
  6494.                             pveSelNode: record
  6495.                         });
  6496.                     } else if (record.data.type === 'openvz') {
  6497.                         menu = Ext.create('PVE.openvz.CmdMenu', {
  6498.                             pveSelNode: record
  6499.                         });
  6500.                     } else {
  6501.                         return;
  6502.                     }
  6503.  
  6504.                     menu.showAt(event.getXY());
  6505.                 },
  6506.                 itemdblclick: function(v, record) {
  6507.                     var ws = me.up('pveStdWorkspace');
  6508.                     ws.selectById(record.data.id);
  6509.                 },
  6510.                 destroy: function() {
  6511.                     rstore.un("load", load_cb);
  6512.                 }
  6513.             },
  6514.             columns: coldef
  6515.         });
  6516.  
  6517.         me.callParent();
  6518.  
  6519.         updateGrid();
  6520.         rstore.on("load", load_cb);
  6521.     }
  6522. });Ext.define('PVE.pool.AddVM', {
  6523.     extend: 'PVE.window.Edit',
  6524.  
  6525.     initComponent : function() {
  6526.         /*jslint confusion: true */
  6527.         var me = this;
  6528.  
  6529.         if (!me.pool) {
  6530.             throw "no pool specified";
  6531.         }
  6532.  
  6533.         me.create = true;
  6534.         me.isAdd = true;
  6535.         me.url = "/pools/" + me.pool;
  6536.         me.method = 'PUT';
  6537.  
  6538.         Ext.apply(me, {
  6539.             subject: gettext('Virtual Machine'),
  6540.             width: 350,
  6541.             items: [
  6542.                 {
  6543.                     xtype: 'pveVMIDSelector',
  6544.                     name: 'vms',
  6545.                     validateExists: true,
  6546.                     value:  '',
  6547.                     fieldLabel: "VM ID"
  6548.                 }
  6549.             ]
  6550.         });
  6551.  
  6552.         me.callParent();
  6553.     }
  6554. });
  6555.  
  6556. Ext.define('PVE.pool.AddStorage', {
  6557.     extend: 'PVE.window.Edit',
  6558.  
  6559.     initComponent : function() {
  6560.         /*jslint confusion: true */
  6561.         var me = this;
  6562.  
  6563.         if (!me.pool) {
  6564.             throw "no pool specified";
  6565.         }
  6566.  
  6567.         me.create = true;
  6568.         me.isAdd = true;
  6569.         me.url = "/pools/" + me.pool;
  6570.         me.method = 'PUT';
  6571.  
  6572.         Ext.apply(me, {
  6573.             subject: gettext('Storage'),
  6574.             width: 350,
  6575.             items: [
  6576.                 {
  6577.                     xtype: 'PVE.form.StorageSelector',
  6578.                     name: 'storage',
  6579.                     nodename: 'localhost',
  6580.                     autoSelect: false,
  6581.                     value:  '',
  6582.                     fieldLabel: gettext("Storage")
  6583.                 }
  6584.             ]
  6585.         });
  6586.  
  6587.         me.callParent();
  6588.     }
  6589. });
  6590.  
  6591. Ext.define('PVE.grid.PoolMembers', {
  6592.     extend: 'Ext.grid.GridPanel',
  6593.     alias: ['widget.pvePoolMembers'],
  6594.  
  6595.     // fixme: dynamic status update ?
  6596.  
  6597.     initComponent : function() {
  6598.         var me = this;
  6599.  
  6600.         if (!me.pool) {
  6601.             throw "no pool specified";
  6602.         }
  6603.  
  6604.         var store = Ext.create('Ext.data.Store', {
  6605.             model: 'PVEResources',
  6606.             sorters: [
  6607.                 {
  6608.                     property : 'type',
  6609.                     direction: 'ASC'
  6610.                 }
  6611.             ],
  6612.             proxy: {
  6613.                 type: 'pve',
  6614.                 root: 'data.members',
  6615.                 url: "/api2/json/pools/" + me.pool
  6616.             }
  6617.         });
  6618.  
  6619.         var coldef = PVE.data.ResourceStore.defaultColums();
  6620.  
  6621.         var reload = function() {
  6622.             store.load();
  6623.         };
  6624.  
  6625.         var sm = Ext.create('Ext.selection.RowModel', {});
  6626.  
  6627.         var remove_btn = new PVE.button.Button({
  6628.             text: gettext('Remove'),
  6629.             disabled: true,
  6630.             selModel: sm,
  6631.             confirmMsg: function (rec) {
  6632.                 return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
  6633.                                          "'" + rec.data.id + "'");
  6634.             },
  6635.             handler: function(btn, event, rec) {
  6636.                 var params = { 'delete': 1 };
  6637.                 if (rec.data.type === 'storage') {
  6638.                     params.storage = rec.data.storage;
  6639.                 } else if (rec.data.type === 'qemu' || rec.data.type === 'openvz') {
  6640.                     params.vms = rec.data.vmid;
  6641.                 } else {
  6642.                     throw "unknown resource type";
  6643.                 }
  6644.  
  6645.                 PVE.Utils.API2Request({
  6646.                     url: '/pools/' + me.pool,
  6647.                     method: 'PUT',
  6648.                     params: params,
  6649.                     waitMsgTarget: me,
  6650.                     callback: function() {
  6651.                         reload();
  6652.                     },
  6653.                     failure: function (response, opts) {
  6654.                         Ext.Msg.alert(gettext('Error'), response.htmlStatus);
  6655.                     }
  6656.                 });
  6657.             }
  6658.         });
  6659.  
  6660.         Ext.apply(me, {
  6661.             store: store,
  6662.             selModel: sm,
  6663.             tbar: [
  6664.                 {
  6665.                     text: gettext('Add'),
  6666.                     menu: new Ext.menu.Menu({
  6667.                         items: [
  6668.                             {
  6669.                                 text: gettext('Virtual Machine'),
  6670.                                 iconCls: 'pve-itype-icon-qemu',
  6671.                                 handler: function() {
  6672.                                     var win = Ext.create('PVE.pool.AddVM', { pool: me.pool });
  6673.                                     win.on('destroy', reload);
  6674.                                     win.show();
  6675.                                 }
  6676.                             },
  6677.                             {
  6678.                                 text: gettext('Storage'),
  6679.                                 iconCls: 'pve-itype-icon-storage',
  6680.                                 handler: function() {
  6681.                                     var win = Ext.create('PVE.pool.AddStorage', { pool: me.pool });
  6682.                                     win.on('destroy', reload);
  6683.                                     win.show();
  6684.                                 }
  6685.                             }
  6686.                         ]
  6687.                     })
  6688.                 },
  6689.                 remove_btn
  6690.             ],
  6691.             viewConfig: {
  6692.                 stripeRows: true
  6693.             },
  6694.             columns: coldef,
  6695.             listeners: {
  6696.                 show: reload
  6697.             }
  6698.         });
  6699.  
  6700.         me.callParent();
  6701.     }
  6702. });Ext.define('PVE.tree.ResourceTree', {
  6703.     extend: 'Ext.tree.TreePanel',
  6704.     alias: ['widget.pveResourceTree'],
  6705.  
  6706.     statics: {
  6707.         typeDefaults: {
  6708.             node: {
  6709.                 iconCls: 'x-tree-node-server',
  6710.                 text: gettext('Node list')
  6711.             },
  6712.             pool: {
  6713.                 iconCls: 'x-tree-node-pool',
  6714.                 text: gettext('Resource Pool')
  6715.             },
  6716.             storage: {
  6717.                 iconCls: 'x-tree-node-harddisk',
  6718.                 text: gettext('Storage list')
  6719.             },
  6720.             qemu: {
  6721.                 iconCls: 'x-tree-node-computer',
  6722.                 text: gettext('Virtual Machine')
  6723.             },
  6724.             openvz: {
  6725.                 iconCls: 'x-tree-node-openvz',
  6726.                 text: gettext('OpenVZ Container')
  6727.             }
  6728.         }
  6729.     },
  6730.  
  6731.     // private
  6732.     nodeSortFn: function(node1, node2) {
  6733.         var n1 = node1.data;
  6734.         var n2 = node2.data;
  6735.  
  6736.         if ((n1.groupbyid && n2.groupbyid) ||
  6737.             !(n1.groupbyid || n2.groupbyid)) {
  6738.  
  6739.             var tcmp;
  6740.  
  6741.             var v1 = n1.type;
  6742.             var v2 = n2.type;
  6743.  
  6744.             if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) {
  6745.                 return tcmp;
  6746.             }
  6747.  
  6748.             // numeric compare for VM IDs
  6749.             if (v1 === 'qemu' || v1 === 'openvz') {
  6750.                 v1 = n1.vmid;
  6751.                 v2 = n2.vmid;
  6752.                 if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) {
  6753.                     return tcmp;
  6754.                 }
  6755.             }
  6756.  
  6757.             return n1.text > n2.text ? 1 : (n1.text < n2.text ? -1 : 0);
  6758.         } else if (n1.groupbyid) {
  6759.             return -1;
  6760.         } else if (n2.groupbyid) {
  6761.             return 1;
  6762.         }
  6763.     },
  6764.  
  6765.     // private: fast binary search
  6766.     findInsertIndex: function(node, child, start, end) {
  6767.         var me = this;
  6768.  
  6769.         var diff = end - start;
  6770.  
  6771.         var mid = start + (diff>>1);
  6772.  
  6773.         if (diff <= 0) {
  6774.             return start;
  6775.         }
  6776.  
  6777.         var res = me.nodeSortFn(child, node.childNodes[mid]);
  6778.         if (res <= 0) {
  6779.             return me.findInsertIndex(node, child, start, mid);
  6780.         } else {
  6781.             return me.findInsertIndex(node, child, mid + 1, end);
  6782.         }
  6783.     },
  6784.  
  6785.     setIconCls: function(info) {
  6786.         var me = this;
  6787.  
  6788.         var defaults = PVE.tree.ResourceTree.typeDefaults[info.type];
  6789.         if (defaults && defaults.iconCls) {
  6790.             if (info.running) {
  6791.                 info.iconCls = defaults.iconCls + "-running";
  6792.             } else {
  6793.                 info.iconCls = defaults.iconCls;
  6794.             }
  6795.         }
  6796.     },
  6797.  
  6798.     // private
  6799.     addChildSorted: function(node, info) {
  6800.         var me = this;
  6801.  
  6802.         me.setIconCls(info);
  6803.  
  6804.         var defaults;
  6805.         if (info.groupbyid) {
  6806.             info.text = info.groupbyid;
  6807.             if (info.type === 'type') {
  6808.                 defaults = PVE.tree.ResourceTree.typeDefaults[info.groupbyid];
  6809.                 if (defaults && defaults.text) {
  6810.                     info.text = defaults.text;
  6811.                 }
  6812.             }
  6813.         }
  6814.         var child = Ext.ModelMgr.create(info, 'PVETree', info.id);
  6815.  
  6816.         var cs = node.childNodes;
  6817.         var pos;
  6818.         if (cs) {
  6819.             pos = cs[me.findInsertIndex(node, child, 0, cs.length)];
  6820.         }
  6821.  
  6822.         node.insertBefore(child, pos);
  6823.  
  6824.         return child;
  6825.     },
  6826.  
  6827.     // private
  6828.     groupChild: function(node, info, groups, level) {
  6829.         var me = this;
  6830.  
  6831.         var groupby = groups[level];
  6832.         var v = info[groupby];
  6833.  
  6834.         if (v) {
  6835.             var group = node.findChild('groupbyid', v);
  6836.             if (!group) {
  6837.                 var groupinfo;
  6838.                 if (info.type === groupby) {
  6839.                     groupinfo = info;
  6840.                 } else {
  6841.                     groupinfo = {
  6842.                         type: groupby,
  6843.                         id : groupby + "/" + v
  6844.                     };
  6845.                     if (groupby !== 'type') {
  6846.                         groupinfo[groupby] = v;
  6847.                     }
  6848.                 }
  6849.                 groupinfo.leaf = false;
  6850.                 groupinfo.groupbyid = v;
  6851.                 group = me.addChildSorted(node, groupinfo);
  6852.                 // fixme: remove when EXTJS has fixed those bugs?!
  6853.                 group.expand(); group.collapse();
  6854.             }
  6855.             if (info.type === groupby) {
  6856.                 return group;
  6857.             }
  6858.             if (group) {
  6859.                 return me.groupChild(group, info, groups, level + 1);
  6860.             }
  6861.         }
  6862.  
  6863.         return me.addChildSorted(node, info);
  6864.     },
  6865.  
  6866.     initComponent : function() {
  6867.         var me = this;
  6868.  
  6869.         var rstore = PVE.data.ResourceStore;
  6870.         var sp = Ext.state.Manager.getProvider();
  6871.  
  6872.         if (!me.viewFilter) {
  6873.             me.viewFilter = {};
  6874.         }
  6875.  
  6876.         var pdata = {
  6877.             dataIndex: {},
  6878.             updateCount: 0
  6879.         };
  6880.  
  6881.         var store = Ext.create('Ext.data.TreeStore', {
  6882.             model: 'PVETree',
  6883.             root: {
  6884.                 expanded: true,
  6885.                 id: 'root',
  6886.                 text: gettext('Datacenter')
  6887.             }
  6888.         });
  6889.  
  6890.         var stateid = 'rid';
  6891.  
  6892.         var updateTree = function() {
  6893.             var tmp;
  6894.  
  6895.             // fixme: suspend events ?
  6896.  
  6897.             var rootnode = me.store.getRootNode();
  6898.  
  6899.             // remember selected node (and all parents)
  6900.             var sm = me.getSelectionModel();
  6901.  
  6902.             var lastsel = sm.getSelection()[0];
  6903.             var parents = [];
  6904.             var p = lastsel;
  6905.             while (p && !!(p = p.parentNode)) {
  6906.                 parents.push(p);
  6907.             }
  6908.  
  6909.             var index = pdata.dataIndex;
  6910.  
  6911.             var groups = me.viewFilter.groups || [];
  6912.             var filterfn = me.viewFilter.filterfn;
  6913.  
  6914.             // remove vanished or changed items
  6915.             var key;
  6916.             for (key in index) {
  6917.                 if (index.hasOwnProperty(key)) {
  6918.                     var olditem = index[key];
  6919.  
  6920.                     // getById() use find(), which is slow (ExtJS4 DP5)
  6921.                     //var item = rstore.getById(olditem.data.id);
  6922.                     var item = rstore.data.get(olditem.data.id);
  6923.  
  6924.                     var changed = false;
  6925.                     if (item) {
  6926.                         // test if any grouping attributes changed
  6927.                         var i, len;
  6928.                         for (i = 0, len = groups.length; i < len; i++) {
  6929.                             var attr = groups[i];
  6930.                             if (item.data[attr] != olditem.data[attr]) {
  6931.                                 //console.log("changed " + attr);
  6932.                                 changed = true;
  6933.                                 break;
  6934.                             }
  6935.                         }
  6936.                         if ((item.data.text !== olditem.data.text) ||
  6937.                             (item.data.node !== olditem.data.node) ||
  6938.                             (item.data.running !== olditem.data.running)) {
  6939.                             //console.log("changed node/text/running " + olditem.data.id);
  6940.                             changed = true;
  6941.                         }
  6942.  
  6943.                         // fixme: also test filterfn()?
  6944.                     }
  6945.  
  6946.                     if (!item || changed) {
  6947.                         //console.log("REM UID: " + key + " ITEM " + olditem.data.id);
  6948.                         if (olditem.isLeaf()) {
  6949.                             delete index[key];
  6950.                             var parentNode = olditem.parentNode;
  6951.                             parentNode.removeChild(olditem, true);
  6952.                         } else {
  6953.                             if (item && changed) {
  6954.                                 olditem.beginEdit();
  6955.                                 //console.log("REM UPDATE UID: " + key + " ITEM " + item.data.running);
  6956.                                 var info = olditem.data;
  6957.                                 Ext.apply(info, item.data);
  6958.                                 me.setIconCls(info);
  6959.                                 olditem.commit();
  6960.                             }
  6961.                         }
  6962.                     }
  6963.                 }
  6964.             }
  6965.  
  6966.             // add new items
  6967.             rstore.each(function(item) {
  6968.                 var olditem = index[item.data.id];
  6969.                 if (olditem) {
  6970.                     return;
  6971.                 }
  6972.  
  6973.                 if (filterfn && !filterfn(item)) {
  6974.                     return;
  6975.                 }
  6976.  
  6977.                 //console.log("ADD UID: " + item.data.id);
  6978.  
  6979.                 var info = Ext.apply({ leaf: true }, item.data);
  6980.  
  6981.                 var child = me.groupChild(rootnode, info, groups, 0);
  6982.                 if (child) {
  6983.                     index[item.data.id] = child;
  6984.                 }
  6985.             });
  6986.  
  6987.             // select parent node is selection vanished
  6988.             if (lastsel && !rootnode.findChild('id', lastsel.data.id, true)) {
  6989.                 lastsel = rootnode;
  6990.                 while (!!(p = parents.shift())) {
  6991.                     if (!!(tmp = rootnode.findChild('id', p.data.id, true))) {
  6992.                         lastsel = tmp;
  6993.                         break;
  6994.                     }
  6995.                 }
  6996.                 me.selectById(lastsel.data.id);
  6997.             }
  6998.  
  6999.             if (!pdata.updateCount) {
  7000.                 rootnode.collapse();
  7001.                 rootnode.expand();
  7002.                 me.applyState(sp.get(stateid));
  7003.             }
  7004.  
  7005.             pdata.updateCount++;
  7006.         };
  7007.  
  7008.         var statechange = function(sp, key, value) {
  7009.             if (key === stateid) {
  7010.                 me.applyState(value);
  7011.             }
  7012.         };
  7013.  
  7014.         sp.on('statechange', statechange);
  7015.  
  7016.         Ext.apply(me, {
  7017.             store: store,
  7018.             viewConfig: {
  7019.                 // note: animate cause problems with applyState
  7020.                 animate: false
  7021.             },
  7022.             //useArrows: true,
  7023.             //rootVisible: false,
  7024.             //title: 'Resource Tree',
  7025.             listeners: {
  7026.                 itemcontextmenu: function(v, record, item, index, event) {
  7027.                     event.stopEvent();
  7028.                     //v.select(record);
  7029.                     var menu;
  7030.  
  7031.                     if (record.data.type === 'qemu') {
  7032.                         menu = Ext.create('PVE.qemu.CmdMenu', {
  7033.                             pveSelNode: record
  7034.                         });
  7035.                     } else if (record.data.type === 'openvz') {
  7036.                         menu = Ext.create('PVE.openvz.CmdMenu', {
  7037.                             pveSelNode: record
  7038.                         });
  7039.                     } else {
  7040.                         return;
  7041.                     }
  7042.  
  7043.                     menu.showAt(event.getXY());
  7044.                 },
  7045.                 destroy: function() {
  7046.                     rstore.un("load", updateTree);
  7047.                 }
  7048.             },
  7049.             setViewFilter: function(view) {
  7050.                 me.viewFilter = view;
  7051.                 me.clearTree();
  7052.                 updateTree();
  7053.             },
  7054.             clearTree: function() {
  7055.                 pdata.updateCount = 0;
  7056.                 var rootnode = me.store.getRootNode();
  7057.                 rootnode.collapse();
  7058.                 rootnode.removeAll(true);
  7059.                 pdata.dataIndex = {};
  7060.                 me.getSelectionModel().deselectAll();
  7061.             },
  7062.             selectExpand: function(node) {
  7063.                 var sm = me.getSelectionModel();
  7064.                 if (!sm.isSelected(node)) {
  7065.                     sm.select(node);
  7066.                     var cn = node;
  7067.                     while (!!(cn = cn.parentNode)) {
  7068.                         if (!cn.isExpanded()) {
  7069.                             cn.expand();
  7070.                         }
  7071.                     }
  7072.                 }
  7073.             },
  7074.             selectById: function(nodeid) {
  7075.                 var rootnode = me.store.getRootNode();
  7076.                 var sm = me.getSelectionModel();
  7077.                 var node;
  7078.                 if (nodeid === 'root') {
  7079.                     node = rootnode;
  7080.                 } else {
  7081.                     node = rootnode.findChild('id', nodeid, true);
  7082.                 }
  7083.                 if (node) {
  7084.                     me.selectExpand(node);
  7085.                 }
  7086.             },
  7087.             checkVmMigration: function(record) {
  7088.                 if (!(record.data.type === 'qemu' || record.data.type === 'openvz')) {
  7089.                     throw "not a vm type";
  7090.                 }
  7091.  
  7092.                 var rootnode = me.store.getRootNode();
  7093.                 var node = rootnode.findChild('id', record.data.id, true);
  7094.  
  7095.                 if (node && node.data.type === record.data.type &&
  7096.                     node.data.node !== record.data.node) {
  7097.                     // defer select (else we get strange errors)
  7098.                     Ext.defer(function() { me.selectExpand(node); }, 100, me);
  7099.                 }
  7100.             },
  7101.             applyState : function(state) {
  7102.                 var sm = me.getSelectionModel();
  7103.                 if (state && state.value) {
  7104.                     me.selectById(state.value);
  7105.                 } else {
  7106.                     sm.deselectAll();
  7107.                 }
  7108.             }
  7109.         });
  7110.  
  7111.         me.callParent();
  7112.  
  7113.         var sm = me.getSelectionModel();
  7114.         sm.on('select', function(sm, n) {
  7115.             sp.set(stateid, { value: n.data.id});
  7116.         });
  7117.  
  7118.         rstore.on("load", updateTree);
  7119.         rstore.startUpdate();
  7120.         //rstore.stopUpdate();
  7121.     }
  7122.  
  7123. });
  7124. Ext.define('PVE.panel.Config', {
  7125.     extend: 'Ext.panel.Panel',
  7126.     alias: 'widget.pvePanelConfig',
  7127.  
  7128.     initComponent: function() {
  7129.         var me = this;
  7130.  
  7131.         var stateid = me.hstateid;
  7132.  
  7133.         var sp = Ext.state.Manager.getProvider();
  7134.  
  7135.         var activeTab;
  7136.  
  7137.         if (stateid) {
  7138.             var state = sp.get(stateid);
  7139.             if (state && state.value) {
  7140.                 activeTab = state.value;
  7141.             }
  7142.         }
  7143.  
  7144.         var items = me.items || [];
  7145.         me.items = undefined;
  7146.  
  7147.         var tbar = me.tbar || [];
  7148.         me.tbar = undefined;
  7149.  
  7150.         var title = me.title || me.pveSelNode.data.text;
  7151.         me.title = undefined;
  7152.  
  7153.         tbar.unshift('->');
  7154.         tbar.unshift({
  7155.             xtype: 'tbtext',
  7156.             text: title,
  7157.             baseCls: 'x-panel-header-text',
  7158.             padding: '0 0 5 0'
  7159.         });
  7160.  
  7161.         Ext.applyIf(me, { showSearch: true });
  7162.  
  7163.         if (me.showSearch) {
  7164.             items.unshift({
  7165.                 itemId: 'search',
  7166.                 xtype: 'pveResourceGrid'
  7167.             });
  7168.         }
  7169.  
  7170.         var toolbar = Ext.create('Ext.toolbar.Toolbar', {
  7171.             items: tbar,
  7172.             style: 'border:0px;',
  7173.             height: 28
  7174.         });
  7175.  
  7176.         var tab = Ext.create('Ext.tab.Panel', {
  7177.             flex: 1,
  7178.             border: true,
  7179.             activeTab: activeTab,
  7180.             defaults: Ext.apply(me.defaults ||  {}, {
  7181.                 pveSelNode: me.pveSelNode,
  7182.                 viewFilter: me.viewFilter,
  7183.                 workspace: me.workspace,
  7184.                 border: false
  7185.             }),
  7186.             items: items,
  7187.             listeners: {
  7188.                 afterrender: function(tp) {
  7189.                     var first =  tp.items.get(0);
  7190.                     if (first) {
  7191.                         first.fireEvent('show', first);
  7192.                     }
  7193.                 },
  7194.                 tabchange: function(tp, newcard, oldcard) {
  7195.                     var ntab = newcard.itemId;
  7196.                     // Note: '' is alias for first tab.
  7197.                     // First tab can be 'search' or something else
  7198.                     if (newcard.itemId === items[0].itemId) {
  7199.                         ntab = '';
  7200.                     }
  7201.                     var state = { value: ntab };
  7202.                     if (stateid) {
  7203.                         sp.set(stateid, state);
  7204.                     }
  7205.                 }
  7206.             }
  7207.         });
  7208.  
  7209.         Ext.apply(me, {
  7210.             layout: { type: 'vbox', align: 'stretch' },
  7211.             items: [ toolbar, tab]
  7212.         });
  7213.  
  7214.         me.callParent();
  7215.  
  7216.         var statechange = function(sp, key, state) {
  7217.             if (stateid && key === stateid) {
  7218.                 var atab = tab.getActiveTab().itemId;
  7219.                 var ntab = state.value || items[0].itemId;
  7220.                 if (state && ntab && (atab != ntab)) {
  7221.                     tab.setActiveTab(ntab);
  7222.                 }
  7223.             }
  7224.         };
  7225.  
  7226.         if (stateid) {
  7227.             me.mon(sp, 'statechange', statechange);
  7228.         }
  7229.     }
  7230. });
  7231. Ext.define('PVE.grid.BackupView', {
  7232.     extend: 'Ext.grid.GridPanel',
  7233.  
  7234.     alias: ['widget.pveBackupView'],
  7235.  
  7236.  
  7237.     initComponent : function() {
  7238.         var me = this;
  7239.  
  7240.         var nodename = me.pveSelNode.data.node;
  7241.         if (!nodename) {
  7242.             throw "no node name specified";
  7243.         }
  7244.  
  7245.         var vmid = me.pveSelNode.data.vmid;
  7246.         if (!vmid) {
  7247.             throw "no VM ID specified";
  7248.         }
  7249.  
  7250.         var vmtype = me.pveSelNode.data.type;
  7251.         if (!vmtype) {
  7252.             throw "no VM type specified";
  7253.         }
  7254.  
  7255.         var filterFn;
  7256.         if (vmtype === 'openvz') {
  7257.             filterFn = function(item) {
  7258.                 return item.data.volid.match(':backup/vzdump-openvz-');
  7259.             };
  7260.         } else if (vmtype === 'qemu') {
  7261.             filterFn = function(item) {
  7262.                 return item.data.volid.match(':backup/vzdump-qemu-');
  7263.             };
  7264.         } else {
  7265.             throw "unsupported VM type '" + vmtype + "'";
  7266.         }
  7267.  
  7268.         me.store = Ext.create('Ext.data.Store', {
  7269.             model: 'pve-storage-content',
  7270.             sorters: {
  7271.                 property: 'volid',
  7272.                 order: 'DESC'
  7273.             },
  7274.             filters: { filterFn: filterFn }
  7275.         });
  7276.  
  7277.         var reload = Ext.Function.createBuffered(function() {
  7278.             if (me.store.proxy.url) {
  7279.                 me.store.load();
  7280.             }
  7281.         }, 100);
  7282.  
  7283.         var setStorage = function(storage) {
  7284.             var url = '/api2/json/nodes/' + nodename + '/storage/' + storage + '/content';
  7285.             url += '?content=backup';
  7286.  
  7287.             me.store.setProxy({
  7288.                 type: 'pve',
  7289.                 url: url
  7290.             });
  7291.  
  7292.             reload();
  7293.         };
  7294.  
  7295.         var storagesel = Ext.create('PVE.form.StorageSelector', {
  7296.             nodename: nodename,
  7297.             fieldLabel: gettext('Storage'),
  7298.             labelAlign: 'right',
  7299.             storageContent: 'backup',
  7300.             allowBlank: false,
  7301.             listeners: {
  7302.                 change: function(f, value) {
  7303.                     setStorage(value);
  7304.                 }
  7305.             }
  7306.         });
  7307.  
  7308.         var sm = Ext.create('Ext.selection.RowModel', {});
  7309.  
  7310.         var backup_btn = Ext.create('Ext.button.Button', {
  7311.             text: gettext('Backup now'),
  7312.             handler: function() {
  7313.                 var win = Ext.create('PVE.window.Backup', {
  7314.                     nodename: nodename,
  7315.                     vmid: vmid,
  7316.                     vmtype: vmtype,
  7317.                     storage: storagesel.getValue()
  7318.                 });
  7319.                 win.show();
  7320.             }
  7321.         });
  7322.  
  7323.         var restore_btn = Ext.create('PVE.button.Button', {
  7324.             text: gettext('Restore'),
  7325.             disabled: true,
  7326.             selModel: sm,
  7327.             enableFn: function(rec) {
  7328.                 return !!rec;
  7329.             },
  7330.             handler: function(b, e, rec) {
  7331.                 var volid = rec.data.volid;
  7332.  
  7333.                 var win = Ext.create('PVE.window.Restore', {
  7334.                     nodename: nodename,
  7335.                     vmid: vmid,
  7336.                     volid: rec.data.volid,
  7337.                     volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec),
  7338.                     vmtype: vmtype
  7339.                 });
  7340.                 win.show();
  7341.                 win.on('destroy', reload);
  7342.             }
  7343.         });
  7344.  
  7345.         var delete_btn = Ext.create('PVE.button.Button', {
  7346.             text: gettext('Remove'),
  7347.             disabled: true,
  7348.             selModel: sm,
  7349.             dangerous: true,
  7350.             confirmMsg: function(rec) {
  7351.                 var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
  7352.                                             "'" + rec.data.volid + "'");
  7353.                 msg += " " + gettext('This will permanently erase all image data.');
  7354.  
  7355.                 return msg;
  7356.             },
  7357.             enableFn: function(rec) {
  7358.                 return !!rec;
  7359.             },
  7360.             handler: function(b, e, rec){
  7361.                 var storage = storagesel.getValue();
  7362.                 if (!storage) {
  7363.                     return;
  7364.                 }
  7365.  
  7366.                 var volid = rec.data.volid;
  7367.                 PVE.Utils.API2Request({
  7368.                     url: "/nodes/" + nodename + "/storage/" + storage + "/content/" + volid,
  7369.                     method: 'DELETE',
  7370.                     waitMsgTarget: me,
  7371.                     failure: function(response, opts) {
  7372.                         Ext.Msg.alert('Error', response.htmlStatus);
  7373.                     },
  7374.                     success: function(response, options) {
  7375.                         reload();
  7376.                     }
  7377.                 });
  7378.             }
  7379.         });
  7380.  
  7381.         Ext.apply(me, {
  7382.             stateful: false,
  7383.             selModel: sm,
  7384.             tbar: [ backup_btn, restore_btn, delete_btn, '->', storagesel ],
  7385.             columns: [
  7386.                 {
  7387.                     header: gettext('Name'),
  7388.                     flex: 1,
  7389.                     sortable: true,
  7390.                     renderer: PVE.Utils.render_storage_content,
  7391.                     dataIndex: 'volid'
  7392.                 },
  7393.                 {
  7394.                     header: gettext('Format'),
  7395.                     width: 100,
  7396.                     dataIndex: 'format'
  7397.                 },
  7398.                 {
  7399.                     header: gettext('Size'),
  7400.                     width: 100,
  7401.                     renderer: PVE.Utils.format_size,
  7402.                     dataIndex: 'size'
  7403.                 }
  7404.             ],
  7405.             listeners: {
  7406.                 show: reload
  7407.             }
  7408.         });
  7409.  
  7410.         me.callParent();
  7411.     }
  7412. });
  7413. Ext.define('PVE.panel.LogView', {
  7414.     extend: 'Ext.panel.Panel',
  7415.  
  7416.     alias: ['widget.pveLogView'],
  7417.  
  7418.     pageSize: 500,
  7419.  
  7420.     lineHeight: 16,
  7421.  
  7422.     viewInfo: undefined,
  7423.  
  7424.     scrollToEnd: true,
  7425.  
  7426.     getMaxDown: function(scrollToEnd) {
  7427.         var me = this;
  7428.  
  7429.         var target = me.getTargetEl();
  7430.         var dom = target.dom;
  7431.         if (scrollToEnd) {
  7432.             dom.scrollTop = dom.scrollHeight - dom.clientHeight;
  7433.         }
  7434.  
  7435.         var maxDown = dom.scrollHeight - dom.clientHeight -
  7436.             dom.scrollTop;
  7437.  
  7438.         return maxDown;
  7439.     },
  7440.  
  7441.     updateView: function(start, end, total, text) {
  7442.         var me = this;
  7443.         var el = me.dataCmp.el;
  7444.  
  7445.         if (me.viewInfo && me.viewInfo.start === start &&
  7446.             me.viewInfo.end === end && me.viewInfo.total === total &&
  7447.             me.viewInfo.textLength === text.length) {
  7448.             return; // same content
  7449.         }
  7450.  
  7451.         var maxDown = me.getMaxDown();
  7452.         var scrollToEnd = (maxDown <= 0) && me.scrollToEnd;
  7453.  
  7454.         el.setStyle('padding-top', start*me.lineHeight);
  7455.         el.update(text);
  7456.         me.dataCmp.setHeight(total*me.lineHeight);
  7457.  
  7458.         if (scrollToEnd) {
  7459.             me.getMaxDown(true);
  7460.         }
  7461.  
  7462.         me.viewInfo = {
  7463.             start: start,
  7464.             end: end,
  7465.             total: total,
  7466.             textLength:  text.length
  7467.         };
  7468.     },
  7469.  
  7470.     doAttemptLoad: function(start) {
  7471.         var me = this;
  7472.  
  7473.         PVE.Utils.API2Request({
  7474.             url: me.url,
  7475.             params: {
  7476.                 start: start,
  7477.                 limit: me.pageSize
  7478.             },
  7479.             method: 'GET',
  7480.             success: function(response) {
  7481.                 PVE.Utils.setErrorMask(me, false);
  7482.                 var list = response.result.data;
  7483.                 var total = response.result.total;
  7484.                 var first = 0, last = 0;
  7485.                 var text = '';
  7486.                 Ext.Array.each(list, function(item) {
  7487.                     if (!first|| item.n < first) {
  7488.                         first = item.n;
  7489.                     }
  7490.                     if (!last || item.n > last) {
  7491.                         last = item.n;
  7492.                     }
  7493.                     text = text + Ext.htmlEncode(item.t) + "<br>";
  7494.                 });
  7495.  
  7496.                 if (first && last && total) {
  7497.                     me.updateView(first -1 , last -1, total, text);
  7498.                 } else {
  7499.                     me.updateView(0, 0, 0, '');
  7500.                 }
  7501.             },
  7502.             failure: function(response) {
  7503.                 var msg = response.htmlStatus;
  7504.                 PVE.Utils.setErrorMask(me, msg);
  7505.             }
  7506.         });
  7507.     },
  7508.  
  7509.     attemptLoad: function(start) {
  7510.         var me = this;
  7511.         if (!me.loadTask) {
  7512.             me.loadTask = Ext.create('Ext.util.DelayedTask', me.doAttemptLoad, me, []);
  7513.         }
  7514.         me.loadTask.delay(200, me.doAttemptLoad, me, [start]);
  7515.     },
  7516.  
  7517.     requestUpdate: function(top, force) {
  7518.         var me = this;
  7519.  
  7520.         if (top === undefined) {
  7521.             var target = me.getTargetEl();
  7522.             top = target.dom.scrollTop;
  7523.         }
  7524.  
  7525.         var viewStart = parseInt((top / me.lineHeight) - 1, 10);
  7526.         if (viewStart < 0) {
  7527.             viewStart = 0;
  7528.         }
  7529.         var viewEnd = parseInt(((top + me.getHeight())/ me.lineHeight) + 1, 10);
  7530.         var info = me.viewInfo;
  7531.  
  7532.         if (info && !force) {
  7533.             if (viewStart >= info.start && viewEnd <= info.end) {
  7534.                 return;
  7535.             }
  7536.         }
  7537.  
  7538.         var line = parseInt((top / me.lineHeight) - (me.pageSize / 2) + 10, 10);
  7539.         if (line < 0) {
  7540.             line = 0;
  7541.         }
  7542.  
  7543.         me.attemptLoad(line);
  7544.     },
  7545.  
  7546.     afterRender: function() {
  7547.         var me = this;
  7548.  
  7549.         me.callParent(arguments);
  7550.  
  7551.         Ext.Function.defer(function() {
  7552.             var target = me.getTargetEl();
  7553.             target.on('scroll',  function(e) {
  7554.                 me.requestUpdate();
  7555.             });
  7556.             me.requestUpdate(0);
  7557.         }, 20);
  7558.     },
  7559.  
  7560.     initComponent : function() {
  7561.         /*jslint confusion: true */
  7562.  
  7563.         var me = this;
  7564.  
  7565.         if (!me.url) {
  7566.             throw "no url specified";
  7567.         }
  7568.  
  7569.         me.dataCmp = Ext.create('Ext.Component', {
  7570.             style: 'font:normal 11px tahoma, arial, verdana, sans-serif;' +
  7571.                 'line-height: ' + me.lineHeight.toString() + 'px; white-space: pre;'
  7572.         });
  7573.  
  7574.         me.task = Ext.TaskManager.start({
  7575.             run: function() {
  7576.                 if (!me.isVisible() || !me.scrollToEnd || !me.viewInfo) {
  7577.                     return;
  7578.                 }
  7579.  
  7580.                 var maxDown = me.getMaxDown();
  7581.                 if (maxDown > 0) {
  7582.                     return;
  7583.                 }
  7584.  
  7585.                 me.requestUpdate(undefined, true);
  7586.             },
  7587.             interval: 1000
  7588.         });
  7589.  
  7590.         Ext.apply(me, {
  7591.             autoScroll: true,
  7592.             layout: 'auto',
  7593.             items: me.dataCmp,
  7594.             bodyStyle: 'padding: 5px;',
  7595.             listeners: {
  7596.                 show: function() {
  7597.                     var target = me.getTargetEl();
  7598.                     if (target && target.dom) {
  7599.                         target.dom.scrollTop = me.savedScrollTop;
  7600.                     }
  7601.                 },
  7602.                 beforehide: function() {
  7603.                     // Hack: chrome reset scrollTop to 0, so we save/restore
  7604.                     var target = me.getTargetEl();
  7605.                     if (target && target.dom) {
  7606.                         me.savedScrollTop = target.dom.scrollTop;
  7607.                     }
  7608.                 },
  7609.                 destroy: function() {
  7610.                     Ext.TaskManager.stop(me.task);
  7611.                 }
  7612.             }
  7613.         });
  7614.  
  7615.         me.callParent();
  7616.     }
  7617. });
  7618. Ext.define('PVE.node.DNSEdit', {
  7619.     extend: 'PVE.window.Edit',
  7620.     alias: ['widget.pveNodeDNSEdit'],
  7621.  
  7622.     initComponent : function() {
  7623.         var me = this;
  7624.  
  7625.         var nodename = me.pveSelNode.data.node;
  7626.         if (!nodename) {
  7627.             throw "no node name specified";
  7628.         }
  7629.  
  7630.         me.items = [
  7631.             {
  7632.                 xtype: 'textfield',
  7633.                 fieldLabel: 'Search domain',
  7634.                 name: 'search',
  7635.                 allowBlank: false
  7636.             },
  7637.             {
  7638.                 xtype: 'pvetextfield',
  7639.                 fieldLabel: gettext('DNS server') + " 1",
  7640.                 vtype: 'IPAddress',
  7641.                 skipEmptyText: true,
  7642.                 name: 'dns1'
  7643.             },
  7644.             {
  7645.                 xtype: 'pvetextfield',
  7646.                 fieldLabel: gettext('DNS server') + " 2",
  7647.                 vtype: 'IPAddress',
  7648.                 skipEmptyText: true,
  7649.                 name: 'dns2'
  7650.             },
  7651.             {
  7652.                 xtype: 'pvetextfield',
  7653.                 fieldLabel: gettext('DNS server') + " 3",
  7654.                 vtype: 'IPAddress',
  7655.                 skipEmptyText: true,
  7656.                 name: 'dns3'
  7657.             }
  7658.         ];
  7659.  
  7660.         Ext.applyIf(me, {
  7661.             subject: 'DNS',
  7662.             url: "/api2/extjs/nodes/" + nodename + "/dns",
  7663.             fieldDefaults: {
  7664.                 labelWidth: 120
  7665.             }
  7666.         });
  7667.  
  7668.         me.callParent();
  7669.  
  7670.         me.load();
  7671.     }
  7672. });
  7673. Ext.define('PVE.node.DNSView', {
  7674.     extend: 'PVE.grid.ObjectGrid',
  7675.     alias: ['widget.pveNodeDNSView'],
  7676.  
  7677.     initComponent : function() {
  7678.         var me = this;
  7679.  
  7680.         var nodename = me.pveSelNode.data.node;
  7681.         if (!nodename) {
  7682.             throw "no node name specified";
  7683.         }
  7684.  
  7685.         var run_editor = function() {
  7686.             var win = Ext.create('PVE.node.DNSEdit', {
  7687.                 pveSelNode: me.pveSelNode
  7688.             });
  7689.             win.show();
  7690.         };
  7691.  
  7692.         Ext.applyIf(me, {
  7693.             url: "/api2/json/nodes/" + nodename + "/dns",
  7694.             cwidth1: 130,
  7695.             interval: 1000,
  7696.             rows: {
  7697.                 search: { header: 'Search domain', required: true },
  7698.                 dns1: { header: gettext('DNS server') + " 1", required: true },
  7699.                 dns2: { header: gettext('DNS server') + " 2" },
  7700.                 dns3: { header: gettext('DNS server') + " 3" }
  7701.             },
  7702.             tbar: [
  7703.                 {
  7704.                     text: gettext("Edit"),
  7705.                     handler: run_editor
  7706.                 }
  7707.             ],
  7708.             listeners: {
  7709.                 itemdblclick: run_editor
  7710.             }
  7711.         });
  7712.  
  7713.         me.callParent();
  7714.  
  7715.         me.on('show', me.rstore.startUpdate);
  7716.         me.on('hide', me.rstore.stopUpdate);
  7717.         me.on('destroy', me.rstore.stopUpdate);
  7718.     }
  7719. });
  7720. Ext.define('PVE.node.TimeView', {
  7721.     extend: 'PVE.grid.ObjectGrid',
  7722.     alias: ['widget.pveNodeTimeView'],
  7723.  
  7724.     initComponent : function() {
  7725.         var me = this;
  7726.  
  7727.         var nodename = me.pveSelNode.data.node;
  7728.         if (!nodename) {
  7729.             throw "no node name specified";
  7730.         }
  7731.  
  7732.         var tzoffset = (new Date()).getTimezoneOffset()*60000;
  7733.         var renderlocaltime = function(value) {
  7734.             var servertime = new Date((value * 1000) + tzoffset);
  7735.             return Ext.Date.format(servertime, 'Y-m-d H:i:s');
  7736.         };
  7737.  
  7738.         var run_editor = function() {
  7739.             var win = Ext.create('PVE.node.TimeEdit', {
  7740.                 pveSelNode: me.pveSelNode
  7741.             });
  7742.             win.show();
  7743.         };
  7744.  
  7745.         Ext.applyIf(me, {
  7746.             url: "/api2/json/nodes/" + nodename + "/time",
  7747.             cwidth1: 150,
  7748.             interval: 1000,
  7749.             rows: {
  7750.                 timezone: {
  7751.                     header: gettext('Time zone'),
  7752.                     required: true
  7753.                 },
  7754.                 localtime: {
  7755.                     header: gettext('Server time'),
  7756.                     required: true,
  7757.                     renderer: renderlocaltime
  7758.                 }
  7759.             },
  7760.             tbar: [
  7761.                 {
  7762.                     text: gettext("Edit"),
  7763.                     handler: run_editor
  7764.                 }
  7765.             ],
  7766.             listeners: {
  7767.                 itemdblclick: run_editor
  7768.             }
  7769.         });
  7770.  
  7771.         me.callParent();
  7772.  
  7773.         me.on('show', me.rstore.startUpdate);
  7774.         me.on('hide', me.rstore.stopUpdate);
  7775.         me.on('destroy', me.rstore.stopUpdate);
  7776.     }
  7777. });
  7778. Ext.define('PVE.node.TimeEdit', {
  7779.     extend: 'PVE.window.Edit',
  7780.     alias: ['widget.pveNodeTimeEdit'],
  7781.  
  7782.     initComponent : function() {
  7783.         var me = this;
  7784.  
  7785.         var nodename = me.pveSelNode.data.node;
  7786.         if (!nodename) {
  7787.             throw "no node name specified";
  7788.         }
  7789.  
  7790.         Ext.applyIf(me, {
  7791.             subject: gettext('Time zone'),
  7792.             url: "/api2/extjs/nodes/" + nodename + "/time",
  7793.             fieldDefaults: {
  7794.                 labelWidth: 70
  7795.             },
  7796.             width: 400,
  7797.             items: {
  7798.                 xtype: 'combo',
  7799.                 fieldLabel: gettext('Time zone'),
  7800.                 name: 'timezone',
  7801.                 queryMode: 'local',
  7802.                 store: new PVE.data.TimezoneStore({autoDestory: true}),
  7803.                 valueField: 'zone',
  7804.                 displayField: 'zone',
  7805.                 triggerAction: 'all',
  7806.                 forceSelection: true,
  7807.                 editable: false,
  7808.                 allowBlank: false
  7809.             }
  7810.         });
  7811.  
  7812.         me.callParent();
  7813.  
  7814.         me.load();
  7815.     }
  7816. });
  7817. Ext.define('PVE.node.StatusView', {
  7818.     extend: 'PVE.grid.ObjectGrid',
  7819.     alias: ['widget.pveNodeStatusView'],
  7820.  
  7821.     initComponent : function() {
  7822.         var me = this;
  7823.  
  7824.         var nodename = me.pveSelNode.data.node;
  7825.         if (!nodename) {
  7826.             throw "no node name specified";
  7827.         }
  7828.  
  7829.         var render_cpuinfo = function(value) {
  7830.             return value.cpus + " x " + value.model;
  7831.         };
  7832.  
  7833.         var render_loadavg = function(value) {
  7834.             return value[0] + ", " + value[1] + ", " + value[2];
  7835.         };
  7836.  
  7837.         var render_cpu = function(value) {
  7838.             var per = value * 100;
  7839.             return per.toFixed(2) + "%";
  7840.         };
  7841.  
  7842.         var render_meminfo = function(value) {
  7843.             var per = (value.used / value.total)*100;
  7844.             var text = "<div>Total: " + PVE.Utils.format_size(value.total) + "</div>" +
  7845.                 "<div>Used: " + PVE.Utils.format_size(value.used) + "</div>";
  7846.             return text;
  7847.         };
  7848.  
  7849.         var rows = {
  7850.             uptime: { header: 'Uptime', required: true, renderer: PVE.Utils.format_duration_long },
  7851.             loadavg: { header: 'Load average', required: true, renderer: render_loadavg },
  7852.             cpuinfo: { header: 'CPUs', required: true, renderer: render_cpuinfo },
  7853.             cpu: { header: 'CPU usage',required: true,  renderer: render_cpu },
  7854.             wait: { header: 'IO delay', required: true, renderer: render_cpu },
  7855.             memory: { header: 'RAM usage', required: true, renderer: render_meminfo },
  7856.             swap: { header: 'SWAP usage', required: true, renderer: render_meminfo },
  7857.             rootfs: { header: 'HD space (root)', required: true, renderer: render_meminfo },
  7858.             pveversion: { header: 'PVE Manager version', required: true },
  7859.             kversion: { header: 'Kernel version', required: true }
  7860.         };
  7861.  
  7862.         Ext.applyIf(me, {
  7863.             cwidth1: 150,
  7864.             //height: 276,
  7865.             rows: rows
  7866.         });
  7867.  
  7868.         me.callParent();
  7869.     }
  7870. });
  7871. /*jslint confusion: true */
  7872. Ext.define('PVE.node.BCFailCnt', {
  7873.     extend: 'Ext.grid.GridPanel',
  7874.     alias: ['widget.pveNodeBCFailCnt'],
  7875.  
  7876.     initComponent : function() {
  7877.         var me = this;
  7878.  
  7879.         var nodename = me.pveSelNode.data.node;
  7880.         if (!nodename) {
  7881.             throw "no node name specified";
  7882.         }
  7883.  
  7884.         var store = new Ext.data.Store({
  7885.             model: 'pve-openvz-ubc',
  7886.             proxy: {
  7887.                 type: 'pve',
  7888.                 url: '/api2/json/nodes/' + nodename + '/ubcfailcnt'
  7889.             },
  7890.             sorters: [
  7891.                 {
  7892.                     property : 'id',
  7893.                     direction: 'ASC'
  7894.                 }
  7895.             ]
  7896.         });
  7897.  
  7898.         var reload = function() {
  7899.             store.load();
  7900.         };
  7901.  
  7902.         Ext.applyIf(me, {
  7903.             store: store,
  7904.             stateful: false,
  7905.             columns: [
  7906.                 {
  7907.                     header: 'Container',
  7908.                     width: 100,
  7909.                     dataIndex: 'id'
  7910.                 },
  7911.                 {
  7912.                     header: 'failcnt',
  7913.                     flex: 1,
  7914.                     dataIndex: 'failcnt'
  7915.                 }
  7916.             ],
  7917.             listeners: {
  7918.                 show: reload,
  7919.                 itemdblclick: function(v, record) {
  7920.                     var ws = me.up('pveStdWorkspace');
  7921.                     ws.selectById('openvz/' + record.data.id);
  7922.                 }
  7923.             }
  7924.         });
  7925.  
  7926.         me.callParent();
  7927.  
  7928.    }
  7929. }, function() {
  7930.  
  7931.     Ext.define('pve-openvz-ubc', {
  7932.         extend: "Ext.data.Model",
  7933.         fields: [ 'id', { name: 'failcnt', type: 'number' } ]
  7934.     });
  7935.  
  7936. });
  7937. Ext.define('PVE.node.Summary', {
  7938.     extend: 'Ext.panel.Panel',
  7939.     alias: 'widget.pveNodeSummary',
  7940.  
  7941.     initComponent: function() {
  7942.         var me = this;
  7943.  
  7944.         var nodename = me.pveSelNode.data.node;
  7945.         if (!nodename) {
  7946.             throw "no node name specified";
  7947.         }
  7948.  
  7949.         if (!me.statusStore) {
  7950.             throw "no status storage specified";
  7951.         }
  7952.  
  7953.         var rstore = me.statusStore;
  7954.  
  7955.         var statusview = Ext.create('PVE.node.StatusView', {
  7956.             title: 'Status',
  7957.             pveSelNode: me.pveSelNode,
  7958.             style: 'padding-top:0px',
  7959.             rstore: rstore
  7960.         });
  7961.  
  7962.         var rrdurl = "/api2/png/nodes/" + nodename + "/rrd";
  7963.  
  7964.         Ext.apply(me, {
  7965.             autoScroll: true,
  7966.             bodyStyle: 'padding:10px',
  7967.             defaults: {
  7968.                 width: 800,
  7969.                 style: 'padding-top:10px'
  7970.             },
  7971.             tbar: [ '->', { xtype: 'pveRRDTypeSelector' } ],
  7972.             items: [
  7973.                 statusview,
  7974.                 {
  7975.                     xtype: 'pveRRDView',
  7976.                     title: "CPU usage %",
  7977.                     datasource: 'cpu,iowait',
  7978.                     rrdurl: rrdurl
  7979.                 },
  7980.                 {
  7981.                     xtype: 'pveRRDView',
  7982.                     title: "Server load",
  7983.                     datasource: 'loadavg',
  7984.                     rrdurl: rrdurl
  7985.                 },
  7986.                 {
  7987.                     xtype: 'pveRRDView',
  7988.                     title: "Memory usage",
  7989.                     datasource: 'memtotal,memused',
  7990.                     rrdurl: rrdurl
  7991.                 },
  7992.                 {
  7993.                     xtype: 'pveRRDView',
  7994.                     title: "Network traffic",
  7995.                     datasource: 'netin,netout',
  7996.                     rrdurl: rrdurl
  7997.                 }
  7998.             ],
  7999.             listeners: {
  8000.                 show: rstore.startUpdate,
  8001.                 hide: rstore.stopUpdate,
  8002.                 destroy: rstore.stopUpdate
  8003.             }
  8004.         });
  8005.  
  8006.         me.callParent();
  8007.     }
  8008. });
  8009. Ext.define('PVE.node.ServiceView', {
  8010.     extend: 'Ext.grid.GridPanel',
  8011.  
  8012.     alias: ['widget.pveNodeServiceView'],
  8013.  
  8014.     initComponent : function() {
  8015.         var me = this;
  8016.  
  8017.         var nodename = me.pveSelNode.data.node;
  8018.         if (!nodename) {
  8019.             throw "no node name specified";
  8020.         }
  8021.  
  8022.         var rstore = Ext.create('PVE.data.UpdateStore', {
  8023.             interval: 1000,
  8024.             storeid: 'pve-services',
  8025.             model: 'pve-services',
  8026.             proxy: {
  8027.                 type: 'pve',
  8028.                 url: "/api2/json/nodes/" + nodename + "/services"
  8029.             }
  8030.         });
  8031.  
  8032.         var store = Ext.create('PVE.data.DiffStore', { rstore: rstore });
  8033.  
  8034.         var service_cmd = function(cmd) {
  8035.             var sm = me.getSelectionModel();
  8036.             var rec = sm.getSelection()[0];
  8037.             PVE.Utils.API2Request({
  8038.                 url: "/nodes/" + nodename + "/services/" + rec.data.service + "/" + cmd,
  8039.                 method: 'POST',
  8040.                 failure: function(response, opts) {
  8041.                     Ext.Msg.alert('Error', response.htmlStatus);
  8042.                     me.loading = true;
  8043.                 },
  8044.                 success: function(response, opts) {
  8045.                     rstore.startUpdate();
  8046.                     var upid = response.result.data;
  8047.  
  8048.                     var win = Ext.create('PVE.window.TaskViewer', {
  8049.                         upid: upid
  8050.                     });
  8051.                     win.show();
  8052.                 }
  8053.             });
  8054.         };
  8055.  
  8056.         var start_btn = new Ext.Button({
  8057.             text: gettext('Start'),
  8058.             disabled: true,
  8059.             handler: function(){
  8060.                 service_cmd("start");
  8061.             }
  8062.         });
  8063.  
  8064.         var stop_btn = new Ext.Button({
  8065.             text: gettext('Stop'),
  8066.             disabled: true,
  8067.             handler: function(){
  8068.                 service_cmd("stop");
  8069.             }
  8070.         });
  8071.  
  8072.         var restart_btn = new Ext.Button({
  8073.             text: gettext('Restart'),
  8074.             disabled: true,
  8075.             handler: function(){
  8076.                 service_cmd("restart");
  8077.             }
  8078.         });
  8079.  
  8080.         var set_button_status = function() {
  8081.             var sm = me.getSelectionModel();
  8082.             var rec = sm.getSelection()[0];
  8083.  
  8084.             if (!rec) {
  8085.                 start_btn.disable();
  8086.                 stop_btn.disable();
  8087.                 restart_btn.disable();
  8088.                 return;
  8089.             }
  8090.             var service = rec.data.service;
  8091.             var state = rec.data.state;
  8092.             if (service == 'apache' ||
  8093.                 service == 'pvecluster' ||
  8094.                 service == 'pvedaemon') {
  8095.                 if (state == 'running') {
  8096.                     start_btn.disable();
  8097.                     restart_btn.enable();
  8098.                 } else {
  8099.                     start_btn.enable();
  8100.                     restart_btn.disable();
  8101.                 }
  8102.                 stop_btn.disable();
  8103.             } else {
  8104.                 if (state == 'running') {
  8105.                     start_btn.disable();
  8106.                     restart_btn.enable();
  8107.                     stop_btn.enable();
  8108.                 } else {
  8109.                     start_btn.enable();
  8110.                     restart_btn.disable();
  8111.                     stop_btn.disable();
  8112.                 }
  8113.             }
  8114.         };
  8115.  
  8116.         me.mon(store, 'datachanged', set_button_status);
  8117.  
  8118.         PVE.Utils.monStoreErrors(me, rstore);
  8119.  
  8120.         Ext.apply(me, {
  8121.             store: store,
  8122.             stateful: false,
  8123.             tbar: [ start_btn, stop_btn, restart_btn ],
  8124.             columns: [
  8125.                 {
  8126.                     header: gettext('Name'),
  8127.                     width: 100,
  8128.                     sortable: true,
  8129.                     dataIndex: 'name'
  8130.                 },
  8131.                 {
  8132.                     header: gettext('Status'),
  8133.                     width: 100,
  8134.                     sortable: true,
  8135.                     dataIndex: 'state'
  8136.                 },
  8137.                 {
  8138.                     header: gettext('Description'),
  8139.                     dataIndex: 'desc',
  8140.                     flex: 1
  8141.                 }
  8142.             ],
  8143.             listeners: {
  8144.                 selectionchange: set_button_status,
  8145.                 show: rstore.startUpdate,
  8146.                 hide: rstore.stopUpdate,
  8147.                 destroy: rstore.stopUpdate
  8148.             }
  8149.         });
  8150.  
  8151.         me.callParent();
  8152.     }
  8153. }, function() {
  8154.  
  8155.     Ext.define('pve-services', {
  8156.         extend: 'Ext.data.Model',
  8157.         fields: [ 'service', 'name', 'desc', 'state' ],
  8158.         idProperty: 'service'
  8159.     });
  8160.  
  8161. });
  8162. Ext.define('PVE.node.NetworkEdit', {
  8163.     extend: 'PVE.window.Edit',
  8164.     alias: ['widget.pveNodeNetworkEdit'],
  8165.  
  8166.     initComponent : function() {
  8167.         var me = this;
  8168.  
  8169.         var nodename = me.pveSelNode.data.node;
  8170.         if (!nodename) {
  8171.             throw "no node name specified";
  8172.         }
  8173.  
  8174.         if (!me.iftype) {
  8175.             throw "no network device type specified";
  8176.         }
  8177.  
  8178.         me.create = !me.iface;
  8179.  
  8180.         var iface_vtype;
  8181.  
  8182.         if (me.iftype === 'bridge') {
  8183.             me.subject = "Bridge";
  8184.             iface_vtype = 'BridgeName';
  8185.         } else if (me.iftype === 'bond') {
  8186.             me.subject = "Bond";
  8187.             iface_vtype = 'BondName';
  8188.         } else if (me.iftype === 'eth' && !me.create) {
  8189.             me.subject = gettext("Network Device");
  8190.         } else {
  8191.             throw "no known network device type specified";
  8192.         }
  8193.  
  8194.         var column2 = [
  8195.             {
  8196.                 xtype: 'pvecheckbox',
  8197.                 fieldLabel: 'Autostart',
  8198.                 name: 'autostart',
  8199.                 uncheckedValue: 0,
  8200.                 checked: me.create ? true : undefined
  8201.             }
  8202.         ];
  8203.  
  8204.         if (me.iftype === 'bridge') {
  8205.             column2.push({
  8206.                 xtype: 'textfield',
  8207.                 fieldLabel: 'Bridge ports',
  8208.                 name: 'bridge_ports'
  8209.             });
  8210.         } else if (me.iftype === 'bond') {
  8211.             column2.push({
  8212.                 xtype: 'textfield',
  8213.                 fieldLabel: 'Slaves',
  8214.                 name: 'slaves'
  8215.             });
  8216.             column2.push({
  8217.                 xtype: 'bondModeSelector',
  8218.                 fieldLabel: 'Mode',
  8219.                 name: 'bond_mode',
  8220.                 value: me.create ? 'balance-rr' : undefined,
  8221.                 allowBlank: false
  8222.             });
  8223.         }
  8224.  
  8225.         var url;
  8226.         var method;
  8227.  
  8228.         if (me.create) {
  8229.             url = "/api2/extjs/nodes/" + nodename + "/network";
  8230.             method = 'POST';
  8231.         } else {
  8232.             url = "/api2/extjs/nodes/" + nodename + "/network/" + me.iface;
  8233.             method = 'PUT';
  8234.         }
  8235.  
  8236.         var column1 = [
  8237.             {
  8238.                 xtype: me.create ? 'textfield' : 'displayfield',
  8239.                 fieldLabel: gettext('Name'),
  8240.                 height: 22, // hack: set same height as text fields
  8241.                 name: 'iface',
  8242.                 value: me.iface,
  8243.                 vtype: iface_vtype,
  8244.                 allowBlank: false
  8245.             },
  8246.             {
  8247.                 xtype: 'pvetextfield',
  8248.                 deleteEmpty: !me.create,
  8249.                 fieldLabel: gettext('IP address'),
  8250.                 vtype: 'IPAddress',
  8251.                 name: 'address'
  8252.             },
  8253.             {
  8254.                 xtype: 'pvetextfield',
  8255.                 deleteEmpty: !me.create,
  8256.                 fieldLabel: gettext('Subnet mask'),
  8257.                 vtype: 'IPAddress',
  8258.                 name: 'netmask',
  8259.                 validator: function(value) {
  8260.                     /*jslint confusion: true */
  8261.                     if (!me.items) {
  8262.                         return true;
  8263.                     }
  8264.                     var address = me.down('field[name=address]').getValue();
  8265.                     if (value !== '') {
  8266.                         if (address === '') {
  8267.                             return "Subnet mask requires option 'IP address'";
  8268.                         }
  8269.                     } else {
  8270.                         if (address !== '') {
  8271.                             return "Option 'IP address' requires a subnet mask";
  8272.                         }
  8273.                     }
  8274.  
  8275.                     return true;
  8276.                 }
  8277.             },
  8278.             {
  8279.                 xtype: 'pvetextfield',
  8280.                 deleteEmpty: !me.create,
  8281.                 fieldLabel: 'Gateway',
  8282.                 vtype: 'IPAddress',
  8283.                 name: 'gateway'
  8284.             }
  8285.         ];
  8286.  
  8287.         Ext.applyIf(me, {
  8288.             url: url,
  8289.             method: method,
  8290.             items: {
  8291.                 xtype: 'inputpanel',
  8292.                 column1: column1,
  8293.                 column2: column2
  8294.             }
  8295.         });
  8296.  
  8297.         me.callParent();
  8298.  
  8299.         if (me.create) {
  8300.             me.down('field[name=iface]').setValue(me.iface_default);
  8301.         } else {
  8302.             me.load({
  8303.                 success: function(response, options) {
  8304.                     var data = response.result.data;
  8305.                     if (data.type !== me.iftype) {
  8306.                         var msg = "Got unexpected device type";
  8307.                         Ext.Msg.alert(gettext('Error'), msg, function() {
  8308.                             me.close();
  8309.                         });
  8310.                         return;
  8311.                     }
  8312.                     me.setValues(data);
  8313.                     me.isValid(); // trigger validation
  8314.                 }
  8315.             });
  8316.         }
  8317.     }
  8318. });
  8319. Ext.define('PVE.node.NetworkView', {
  8320.     extend: 'Ext.panel.Panel',
  8321.  
  8322.     alias: ['widget.pveNodeNetworkView'],
  8323.  
  8324.     initComponent : function() {
  8325.         var me = this;
  8326.  
  8327.         var nodename = me.pveSelNode.data.node;
  8328.         if (!nodename) {
  8329.             throw "no node name specified";
  8330.         }
  8331.  
  8332.         var store = Ext.create('Ext.data.Store', {
  8333.             model: 'pve-networks',
  8334.             proxy: {
  8335.                 type: 'pve',
  8336.                 url: "/api2/json/nodes/" + nodename + "/network"
  8337.             },
  8338.             sorters: [
  8339.                 {
  8340.                     property : 'iface',
  8341.                     direction: 'ASC'
  8342.                 }
  8343.             ]
  8344.         });
  8345.  
  8346.         var reload = function() {
  8347.             var changeitem = me.down('#changes');
  8348.             PVE.Utils.API2Request({
  8349.                 url: '/nodes/' + nodename + '/network',
  8350.                 failure: function(response, opts) {
  8351.                     changeitem.update('Error: ' + response.htmlStatus);
  8352.                     store.loadData({});
  8353.                 },
  8354.                 success: function(response, opts) {
  8355.                     var result = Ext.decode(response.responseText);
  8356.                     store.loadData(result.data);
  8357.                     var changes = result.changes;
  8358.                     if (changes === undefined || changes === '') {
  8359.                         changes = gettext("No changes");
  8360.                     }
  8361.                     changeitem.update("<pre>" + Ext.htmlEncode(changes) + "</pre>");
  8362.                 }
  8363.             });
  8364.         };
  8365.  
  8366.         var run_editor = function() {
  8367.             var grid = me.down('gridpanel');
  8368.             var sm = grid.getSelectionModel();
  8369.             var rec = sm.getSelection()[0];
  8370.             if (!rec) {
  8371.                 return;
  8372.             }
  8373.  
  8374.             var win = Ext.create('PVE.node.NetworkEdit', {
  8375.                 pveSelNode: me.pveSelNode,
  8376.                 iface: rec.data.iface,
  8377.                 iftype: rec.data.type
  8378.             });
  8379.             win.show();
  8380.             win.on('destroy', reload);
  8381.         };
  8382.  
  8383.         var edit_btn = new Ext.Button({
  8384.             text: gettext('Edit'),
  8385.             disabled: true,
  8386.             handler: run_editor
  8387.         });
  8388.  
  8389.         var del_btn = new Ext.Button({
  8390.             text: gettext('Remove'),
  8391.             disabled: true,
  8392.             handler: function(){
  8393.                 var grid = me.down('gridpanel');
  8394.                 var sm = grid.getSelectionModel();
  8395.                 var rec = sm.getSelection()[0];
  8396.                 if (!rec) {
  8397.                     return;
  8398.                 }
  8399.  
  8400.                 var iface = rec.data.iface;
  8401.  
  8402.                 PVE.Utils.API2Request({
  8403.                     url: '/nodes/' + nodename + '/network/' + iface,
  8404.                     method: 'DELETE',
  8405.                     waitMsgTarget: me,
  8406.                     callback: function() {
  8407.                         reload();
  8408.                     },
  8409.                     failure: function(response, opts) {
  8410.                         Ext.Msg.alert('Error', response.htmlStatus);
  8411.                     }
  8412.                 });
  8413.             }
  8414.         });
  8415.  
  8416.         var set_button_status = function() {
  8417.             var grid = me.down('gridpanel');
  8418.             var sm = grid.getSelectionModel();
  8419.             var rec = sm.getSelection()[0];
  8420.  
  8421.             edit_btn.setDisabled(!rec);
  8422.             del_btn.setDisabled(!rec);
  8423.         };
  8424.  
  8425.         PVE.Utils.monStoreErrors(me, store);
  8426.  
  8427.         var render_ports = function(value, metaData, record) {
  8428.             if (value === 'bridge') {
  8429.                 return record.data.bridge_ports;
  8430.             } else if (value === 'bond') {
  8431.                 return record.data.slaves;
  8432.             }
  8433.         };
  8434.  
  8435.         Ext.apply(me, {
  8436.             layout: 'border',
  8437.             tbar: [
  8438.                 {
  8439.                     text: gettext('Create'),
  8440.                     menu: new Ext.menu.Menu({
  8441.                         items: [
  8442.                             {
  8443.                                 text: 'Bridge',
  8444.                                 handler: function() {
  8445.                                     var next;
  8446.                                     for (next = 0; next <= 9999; next++) {
  8447.                                         if (!store.data.get('vmbr' + next.toString())) {
  8448.                                             break;
  8449.                                         }
  8450.                                     }
  8451.  
  8452.                                     var win = Ext.create('PVE.node.NetworkEdit', {
  8453.                                         pveSelNode: me.pveSelNode,
  8454.                                         iftype: 'bridge',
  8455.                                         iface_default: 'vmbr' + next.toString()
  8456.                                     });
  8457.                                     win.on('destroy', reload);
  8458.                                     win.show();
  8459.                                 }
  8460.                             },
  8461.                             {
  8462.                                 text: 'Bond',
  8463.                                 handler: function() {
  8464.                                     var next;
  8465.                                     for (next = 0; next <= 9999; next++) {
  8466.                                         if (!store.data.get('bond' + next.toString())) {
  8467.                                             break;
  8468.                                         }
  8469.                                     }
  8470.                                     var win = Ext.create('PVE.node.NetworkEdit', {
  8471.                                         pveSelNode: me.pveSelNode,
  8472.                                         iftype: 'bond',
  8473.                                         iface_default: 'bond' + next.toString()
  8474.                                     });
  8475.                                     win.on('destroy', reload);
  8476.                                     win.show();
  8477.                                 }
  8478.                             }
  8479.                         ]
  8480.                     })
  8481.                 }, ' ',
  8482.                 {
  8483.                     text: gettext('Revert changes'),
  8484.                     handler: function() {
  8485.                         PVE.Utils.API2Request({
  8486.                             url: '/nodes/' + nodename + '/network',
  8487.                             method: 'DELETE',
  8488.                             waitMsgTarget: me,
  8489.                             callback: function() {
  8490.                                 reload();
  8491.                             },
  8492.                             failure: function(response, opts) {
  8493.                                 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
  8494.                             }
  8495.                         });
  8496.                     }
  8497.                 },
  8498.                 edit_btn,
  8499.                 del_btn
  8500.             ],
  8501.             items: [
  8502.                 {
  8503.                     xtype: 'gridpanel',
  8504.                     stateful: false,
  8505.                     store: store,
  8506.                     region: 'center',
  8507.                     border: false,
  8508.                     columns: [
  8509.                         {
  8510.                             header: gettext('Name'),
  8511.                             width: 100,
  8512.                             sortable: true,
  8513.                             dataIndex: 'iface'
  8514.                         },
  8515.                         {
  8516.                             xtype: 'booleancolumn',
  8517.                             header: gettext('Active'),
  8518.                             width: 80,
  8519.                             sortable: true,
  8520.                             dataIndex: 'active',
  8521.                             trueText: 'Yes',
  8522.                             falseText: 'No',
  8523.                             undefinedText: 'No'
  8524.                         },
  8525.                         {
  8526.                             xtype: 'booleancolumn',
  8527.                             header: 'Autostart',
  8528.                             width: 80,
  8529.                             sortable: true,
  8530.                             dataIndex: 'autostart',
  8531.                             trueText: 'Yes',
  8532.                             falseText: 'No',
  8533.                             undefinedText: 'No'
  8534.                         },
  8535.                         {
  8536.                             header: 'Ports/Slaves',
  8537.                             dataIndex: 'type',
  8538.                             renderer: render_ports
  8539.                         },
  8540.                         {
  8541.                             header: gettext('IP address'),
  8542.                             sortable: true,
  8543.                             dataIndex: 'address'
  8544.                         },
  8545.                         {
  8546.                             header: gettext('Subnet mask'),
  8547.                             sortable: true,
  8548.                             dataIndex: 'netmask'
  8549.                         },
  8550.                         {
  8551.                             header: 'Gateway',
  8552.                             sortable: true,
  8553.                             dataIndex: 'gateway'
  8554.                         }
  8555.                     ],
  8556.                     listeners: {
  8557.                         selectionchange: set_button_status,
  8558.                         itemdblclick: run_editor
  8559.                     }
  8560.                 },
  8561.                 {
  8562.                     border: false,
  8563.                     region: 'south',
  8564.                     autoScroll: true,
  8565.                     itemId: 'changes',
  8566.                     tbar: [
  8567.                         gettext('Pending changes') + ' (' +
  8568.                             gettext('Please reboot to activate changes') + ')'
  8569.                     ],
  8570.                     split: true,
  8571.                     bodyPadding: 5,
  8572.                     flex: 0.6,
  8573.                     html: gettext("No changes")
  8574.                 }
  8575.             ],
  8576.             listeners: {
  8577.                 show: reload
  8578.             }
  8579.         });
  8580.  
  8581.         me.callParent();
  8582.     }
  8583. }, function() {
  8584.  
  8585.     Ext.define('pve-networks', {
  8586.         extend: 'Ext.data.Model',
  8587.         fields: [
  8588.             'iface', 'type', 'active', 'autostart',
  8589.             'bridge_ports', 'slaves', 'address',
  8590.             'netmask', 'gateway'
  8591.         ],
  8592.         idProperty: 'iface'
  8593.     });
  8594.  
  8595. });
  8596.     Ext.define('PVE.node.Tasks', {
  8597.     extend: 'Ext.grid.GridPanel',
  8598.  
  8599.     alias: ['widget.pveNodeTasks'],
  8600.  
  8601.     initComponent : function() {
  8602.         var me = this;
  8603.  
  8604.         var nodename = me.pveSelNode.data.node;
  8605.         if (!nodename) {
  8606.             throw "no node name specified";
  8607.         }
  8608.  
  8609.         var store = Ext.create('Ext.data.Store', {
  8610.             pageSize: 500,
  8611.             buffered: true,
  8612.             remoteFilter: true,
  8613.             model: 'pve-tasks',
  8614.             proxy: {
  8615.                 type: 'pve',
  8616.                 startParam: 'start',
  8617.                 limitParam: 'limit',
  8618.                 url: "/api2/json/nodes/" + nodename + "/tasks"
  8619.             }
  8620.         });
  8621.  
  8622.         var userfilter = '';
  8623.         var filter_errors = 0;
  8624.  
  8625.         // fixme: scroller update fails
  8626.         // http://www.sencha.com/forum/showthread.php?133677-scroller-does-not-adjust-to-the-filtered-grid-data&p=602887
  8627.         var reload_task = new Ext.util.DelayedTask(function() {
  8628.             var params = {
  8629.                 errors: filter_errors
  8630.             };
  8631.             if (userfilter) {
  8632.                 params.userfilter = userfilter;
  8633.             }
  8634.             store.proxy.extraParams = params;
  8635.             store.filter();
  8636.         });
  8637.  
  8638.         var run_task_viewer = function() {
  8639.             var sm = me.getSelectionModel();
  8640.             var rec = sm.getSelection()[0];
  8641.             if (!rec) {
  8642.                 return;
  8643.             }
  8644.  
  8645.             var win = Ext.create('PVE.window.TaskViewer', {
  8646.                 upid: rec.data.upid
  8647.             });
  8648.             win.show();
  8649.         };
  8650.  
  8651.         var view_btn = new Ext.Button({
  8652.             text: gettext('View'),
  8653.             disabled: true,
  8654.             handler: run_task_viewer
  8655.         });
  8656.  
  8657.  
  8658.         Ext.apply(me, {
  8659.             store: store,
  8660.             stateful: false,
  8661.             verticalScrollerType: 'paginggridscroller',
  8662.             loadMask: true,
  8663.             invalidateScrollerOnRefresh: false,
  8664.             viewConfig: {
  8665.                 trackOver: false,
  8666.                 stripeRows: false, // does not work with getRowClass()
  8667.  
  8668.                 getRowClass: function(record, index) {
  8669.                     var status = record.get('status');
  8670.  
  8671.                     if (status && status != 'OK') {
  8672.                         return "x-form-invalid-field";
  8673.                     }
  8674.                 }
  8675.             },
  8676.             tbar: [
  8677.                 view_btn, '->', gettext('User name') +':', ' ',
  8678.                 {
  8679.                     xtype: 'textfield',
  8680.                     width: 200,
  8681.                     value: userfilter,
  8682.                     enableKeyEvents: true,
  8683.                     listeners: {
  8684.                         keyup: function(field, e) {
  8685.                             userfilter = field.getValue();
  8686.                             reload_task.delay(500);
  8687.                         }
  8688.                     }
  8689.                 }, ' ', gettext('Only Errors') + ':', ' ',
  8690.                 {
  8691.                     xtype: 'checkbox',
  8692.                     hideLabel: true,
  8693.                     checked: filter_errors,
  8694.                     listeners: {
  8695.                         change: function(field, checked) {
  8696.                             filter_errors = checked ? 1 : 0;
  8697.                             reload_task.delay(10);
  8698.                         }
  8699.                     }
  8700.                 }, ' '
  8701.             ],
  8702.             sortableColumns: false,
  8703.             columns: [
  8704.                 {
  8705.                     header: gettext("Start Time"),
  8706.                     dataIndex: 'starttime',
  8707.                     width: 100,
  8708.                     renderer: function(value) {
  8709.                         return Ext.Date.format(value, "M d H:i:s");
  8710.                     }
  8711.                 },
  8712.                 {
  8713.                     header: gettext("End Time"),
  8714.                     dataIndex: 'endtime',
  8715.                     width: 100,
  8716.                     renderer: function(value, metaData, record) {
  8717.                         return  Ext.Date.format(value,"M d H:i:s");
  8718.                     }
  8719.                 },
  8720.                 {
  8721.                     header: gettext("Node"),
  8722.                     dataIndex: 'node',
  8723.                     width: 100
  8724.                 },
  8725.                 {
  8726.                     header: gettext("User name"),
  8727.                     dataIndex: 'user',
  8728.                     width: 150
  8729.                 },
  8730.                 {
  8731.                     header: gettext("Description"),
  8732.                     dataIndex: 'upid',
  8733.                     flex: 1,
  8734.                     renderer: PVE.Utils.render_upid
  8735.                 },
  8736.                 {
  8737.                     header: gettext("Status"),
  8738.                     dataIndex: 'status',
  8739.                     width: 200,
  8740.                     renderer: function(value, metaData, record) {
  8741.                         if (value == 'OK') {
  8742.                             return 'OK';
  8743.                         }
  8744.                         // metaData.attr = 'style="color:red;"';
  8745.                         return "ERROR: " + value;
  8746.                     }
  8747.                 }
  8748.             ],
  8749.             listeners: {
  8750.                 itemdblclick: run_task_viewer,
  8751.                 selectionchange: function(v, selections) {
  8752.                     view_btn.setDisabled(!(selections && selections[0]));
  8753.                 },
  8754.                 show: function() { reload_task.delay(10); }
  8755.             }
  8756.         });
  8757.  
  8758.         me.callParent();
  8759.  
  8760.         store.guaranteeRange(0, store.pageSize - 1);
  8761.     }
  8762. });
  8763.  
  8764. Ext.define('PVE.node.SubscriptionKeyEdit', {
  8765.     extend: 'PVE.window.Edit',
  8766.  
  8767.     initComponent : function() {
  8768.         var me = this;
  8769.  
  8770.         Ext.apply(me, {
  8771.             title: gettext('Upload Subscription Key'),
  8772.             width: 300,
  8773.             items: {
  8774.                 xtype: 'textfield',
  8775.                 name: 'key',
  8776.                 value: '',
  8777.                 fieldLabel: gettext('Subscription Key')
  8778.             }
  8779.         });
  8780.  
  8781.         me.callParent();
  8782.  
  8783.         me.load();
  8784.     }
  8785. });
  8786.  
  8787. Ext.define('PVE.node.Subscription', {
  8788.     extend: 'PVE.grid.ObjectGrid',
  8789.  
  8790.     alias: ['widget.pveNodeSubscription'],
  8791.  
  8792.     features: [ {ftype: 'selectable'}],
  8793.  
  8794.     initComponent : function() {
  8795.         var me = this;
  8796.  
  8797.         if (!me.nodename) {
  8798.             throw "no node name specified";
  8799.         }
  8800.  
  8801.         var reload = function() {
  8802.             me.rstore.load();
  8803.         };
  8804.  
  8805.         var baseurl = '/nodes/' + me.nodename + '/subscription';
  8806.  
  8807.         var render_status = function(value) {
  8808.  
  8809.             var message = me.getObjectValue('message');
  8810.  
  8811.             if (message) {
  8812.                 return value + ": " + message;
  8813.             }
  8814.             return value;
  8815.         };
  8816.  
  8817.         var rows = {
  8818.             productname: {
  8819.                 header: gettext('Type')
  8820.             },
  8821.             key: {
  8822.                 header: gettext('Subscription Key')
  8823.             },
  8824.             status: {
  8825.                 header: gettext('Status'),
  8826.                 renderer: render_status
  8827.             },
  8828.             message: {
  8829.                 visible: false
  8830.             },
  8831.             serverid: {
  8832.                 header: gettext('Server ID')
  8833.             },
  8834.             sockets: {
  8835.                 header: 'Sockets'
  8836.             },
  8837.             checktime: {
  8838.                 header: 'Last checked',
  8839.                 renderer: PVE.Utils.render_timestamp
  8840.             }
  8841.         };
  8842.  
  8843.         Ext.applyIf(me, {
  8844.             url: '/api2/json' + baseurl,
  8845.             cwidth1: 170,
  8846.             tbar: [
  8847.                 {
  8848.                     text: gettext('Upload Subscription Key'),
  8849.                     handler: function() {
  8850.                         var win = Ext.create('PVE.node.SubscriptionKeyEdit', {
  8851.                             url: '/api2/extjs/' + baseurl
  8852.                         });
  8853.                         win.show();
  8854.                         win.on('destroy', reload);
  8855.                     }
  8856.                 },
  8857.                 {
  8858.                     text: gettext('Check'),
  8859.                     handler: function() {
  8860.                         PVE.Utils.API2Request({
  8861.                             params: { force: 1 },
  8862.                             url: baseurl,
  8863.                             method: 'POST',
  8864.                             waitMsgTarget: me,
  8865.                             failure: function(response, opts) {
  8866.                                 Ext.Msg.alert('Error', response.htmlStatus);
  8867.                             },
  8868.                             callback: reload
  8869.                         });
  8870.                     }
  8871.                 }
  8872.             ],
  8873.             rows: rows,
  8874.             listeners: {
  8875.                 show: reload
  8876.             }
  8877.         });
  8878.  
  8879.         me.callParent();
  8880.     }
  8881. }, function() {
  8882.  
  8883.     Ext.define('pve-services', {
  8884.         extend: 'Ext.data.Model',
  8885.         fields: [ 'service', 'name', 'desc', 'state' ],
  8886.         idProperty: 'service'
  8887.     });
  8888.  
  8889. });
  8890. Ext.define('PVE.node.Config', {
  8891.     extend: 'PVE.panel.Config',
  8892.     alias: 'widget.PVE.node.Config',
  8893.  
  8894.     initComponent: function() {
  8895.         var me = this;
  8896.  
  8897.         var nodename = me.pveSelNode.data.node;
  8898.         if (!nodename) {
  8899.             throw "no node name specified";
  8900.         }
  8901.  
  8902.         var caps = Ext.state.Manager.get('GuiCap');
  8903.  
  8904.         me.statusStore = Ext.create('PVE.data.ObjectStore', {
  8905.             url: "/api2/json/nodes/" + nodename + "/status",
  8906.             interval: 1000
  8907.         });
  8908.  
  8909.         var node_command = function(cmd) {
  8910.             PVE.Utils.API2Request({
  8911.                 params: { command: cmd },
  8912.                 url: '/nodes/' + nodename + '/status',
  8913.                 method: 'POST',
  8914.                 waitMsgTarget: me,
  8915.                 failure: function(response, opts) {
  8916.                     Ext.Msg.alert('Error', response.htmlStatus);
  8917.                 }
  8918.             });
  8919.         };
  8920.  
  8921.         var restartBtn = Ext.create('PVE.button.Button', {
  8922.             text: gettext('Restart'),
  8923.             disabled: !caps.nodes['Sys.PowerMgmt'],
  8924.             confirmMsg: Ext.String.format(gettext("Do you really want to restart node {0}?"), nodename),
  8925.             handler: function() {
  8926.                 node_command('reboot');
  8927.             }
  8928.         });
  8929.  
  8930.         var shutdownBtn = Ext.create('PVE.button.Button', {
  8931.             text: gettext('Shutdown'),
  8932.             disabled: !caps.nodes['Sys.PowerMgmt'],
  8933.             confirmMsg: Ext.String.format(gettext("Do you really want to shutdown node {0}?"), nodename),
  8934.             handler: function() {
  8935.                 node_command('shutdown');
  8936.             }
  8937.         });
  8938.  
  8939.         var shellBtn = Ext.create('Ext.Button', {
  8940.             text: gettext('Shell'),
  8941.             disabled: !caps.nodes['Sys.Console'],
  8942.             handler: function() {
  8943.                 var url = Ext.urlEncode({
  8944.                     console: 'shell',
  8945.                     node: nodename
  8946.                 });
  8947.                 var nw = window.open("?" + url, '_blank',
  8948.                                      "innerWidth=745,innerheight=427");
  8949.                 nw.focus();
  8950.             }
  8951.         });
  8952.  
  8953.         me.items = [];
  8954.  
  8955.         Ext.apply(me, {
  8956.             title: gettext('Node') + " '" + nodename + "'",
  8957.             hstateid: 'nodetab',
  8958.             defaults: { statusStore: me.statusStore },
  8959.             tbar: [ restartBtn, shutdownBtn, shellBtn ]
  8960.         });
  8961.  
  8962.         if (caps.nodes['Sys.Audit']) {
  8963.             me.items.push([
  8964.                 {
  8965.                     title: gettext('Summary'),
  8966.                     itemId: 'summary',
  8967.                     xtype: 'pveNodeSummary'
  8968.                 },
  8969.                 {
  8970.                     title: gettext('Services'),
  8971.                     itemId: 'services',
  8972.                     xtype: 'pveNodeServiceView'
  8973.                 },
  8974.                 {
  8975.                     title: gettext('Network'),
  8976.                     itemId: 'network',
  8977.                     xtype: 'pveNodeNetworkView'
  8978.                 },
  8979.                 {
  8980.                     title: 'DNS',
  8981.                     itemId: 'dns',
  8982.                     xtype: 'pveNodeDNSView'
  8983.                 },
  8984.                 {
  8985.                     title: gettext('Time'),
  8986.                     itemId: 'time',
  8987.                     xtype: 'pveNodeTimeView'
  8988.                 }
  8989.             ]);
  8990.         }
  8991.  
  8992.         if (caps.nodes['Sys.Syslog']) {
  8993.             me.items.push([
  8994.                 {
  8995.                     title: 'Syslog',
  8996.                     itemId: 'syslog',
  8997.                     xtype: 'pveLogView',
  8998.                     url: "/api2/extjs/nodes/" + nodename + "/syslog"
  8999.                 }
  9000.             ]);
  9001.         }
  9002.  
  9003.         me.items.push([
  9004.             {
  9005.                 title: 'Task History',
  9006.                 itemId: 'tasks',
  9007.                 xtype: 'pveNodeTasks'
  9008.             }
  9009.         ]);
  9010.  
  9011.  
  9012.         if (caps.nodes['Sys.Audit']) {
  9013.             me.items.push([
  9014.                 {
  9015.                     title: 'UBC',
  9016.                     itemId: 'ubc',
  9017.                     xtype: 'pveNodeBCFailCnt'
  9018.                 }
  9019.             ]);
  9020.         }
  9021.  
  9022.         me.items.push([
  9023.             {
  9024.                 title: 'Subscription',
  9025.                 itemId: 'support',
  9026.                 xtype: 'pveNodeSubscription',
  9027.                 nodename: nodename
  9028.             }
  9029.         ]);
  9030.  
  9031.         me.callParent();
  9032.  
  9033.         me.statusStore.on('load', function(s, records, success) {
  9034.             var uptimerec = s.data.get('uptime');
  9035.             var powermgmt = uptimerec ? uptimerec.data.value : false;
  9036.             if (!caps.nodes['Sys.PowerMgmt']) {
  9037.                 powermgmt = false;
  9038.             }
  9039.             restartBtn.setDisabled(!powermgmt);
  9040.             shutdownBtn.setDisabled(!powermgmt);
  9041.             shellBtn.setDisabled(!powermgmt);
  9042.         });
  9043.  
  9044.         me.on('afterrender', function() {
  9045.             me.statusStore.startUpdate();
  9046.         });
  9047.  
  9048.         me.on('destroy', function() {
  9049.             me.statusStore.stopUpdate();
  9050.         });
  9051.     }
  9052. });
  9053. Ext.define('PVE.qemu.StatusView', {
  9054.     extend: 'PVE.grid.ObjectGrid',
  9055.     alias: ['widget.pveQemuStatusView'],
  9056.  
  9057.     initComponent : function() {
  9058.         var me = this;
  9059.  
  9060.         var nodename = me.pveSelNode.data.node;
  9061.         if (!nodename) {
  9062.             throw "no node name specified";
  9063.         }
  9064.  
  9065.         var vmid = me.pveSelNode.data.vmid;
  9066.         if (!vmid) {
  9067.             throw "no VM ID specified";
  9068.         }
  9069.  
  9070.         var render_cpu = function(value, metaData, record, rowIndex, colIndex, store) {
  9071.             if (!me.getObjectValue('uptime')) {
  9072.                 return '-';
  9073.             }
  9074.  
  9075.             var maxcpu = me.getObjectValue('cpus', 1);
  9076.  
  9077.             if (!(Ext.isNumeric(value) && Ext.isNumeric(maxcpu) && (maxcpu >= 1))) {
  9078.                 return '-';
  9079.             }
  9080.  
  9081.             var per = (value * 100);
  9082.  
  9083.             return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU');
  9084.         };
  9085.  
  9086.         var render_mem = function(value, metaData, record, rowIndex, colIndex, store) {
  9087.             var maxmem = me.getObjectValue('maxmem', 0);
  9088.             var per = (value / maxmem)*100;
  9089.             var text = "<div>Total: " + PVE.Utils.format_size(maxmem) + "</div>" +
  9090.                 "<div>Used: " + PVE.Utils.format_size(value) + "</div>";
  9091.             return text;
  9092.         };
  9093.  
  9094.         var rows = {
  9095.             name: { header: gettext('Name'), defaultValue: 'no name specified' },
  9096.             qmpstatus: { header: gettext('Status'), defaultValue: 'unknown' },
  9097.             cpu: { header: 'CPU usage', required: true,  renderer: render_cpu },
  9098.             cpus: { visible: false },
  9099.             mem: { header: 'Memory usage', required: true,  renderer: render_mem },
  9100.             maxmem: { visible: false },
  9101.             uptime: { header: gettext('Uptime'), required: true, renderer: PVE.Utils.render_uptime },
  9102.             ha: { header: 'Managed by HA', required: true, renderer: PVE.Utils.format_boolean }
  9103.         };
  9104.  
  9105.         Ext.applyIf(me, {
  9106.             cwidth1: 150,
  9107.             height: 166,
  9108.             rows: rows
  9109.         });
  9110.  
  9111.         me.callParent();
  9112.     }
  9113. });
  9114. Ext.define('PVE.window.Migrate', {
  9115.     extend: 'Ext.window.Window',
  9116.  
  9117.     resizable: false,
  9118.  
  9119.     migrate: function(target, online) {
  9120.         var me = this;
  9121.         PVE.Utils.API2Request({
  9122.             params: { target: target, online: online },
  9123.             url: '/nodes/' + me.nodename + '/' + me.vmtype + '/' + me.vmid + "/migrate",
  9124.             waitMsgTarget: me,
  9125.             method: 'POST',
  9126.             failure: function(response, opts) {
  9127.                 Ext.Msg.alert('Error', response.htmlStatus);
  9128.             },
  9129.             success: function(response, options) {
  9130.                 var upid = response.result.data;
  9131.  
  9132.                 var win = Ext.create('PVE.window.TaskViewer', {
  9133.                     upid: upid
  9134.                 });
  9135.                 win.show();
  9136.                 me.close();
  9137.             }
  9138.         });
  9139.     },
  9140.  
  9141.     initComponent : function() {
  9142.         var me = this;
  9143.  
  9144.         if (!me.nodename) {
  9145.             throw "no node name specified";
  9146.         }
  9147.  
  9148.         if (!me.vmid) {
  9149.             throw "no VM ID specified";
  9150.         }
  9151.  
  9152.         if (!me.vmtype) {
  9153.             throw "no VM type specified";
  9154.         }
  9155.  
  9156.         me.formPanel = Ext.create('Ext.form.Panel', {
  9157.             bodyPadding: 10,
  9158.             border: false,
  9159.             fieldDefaults: {
  9160.                 labelWidth: 100,
  9161.                 anchor: '100%'
  9162.             },
  9163.             items: [
  9164.                 {
  9165.                     xtype: 'PVE.form.NodeSelector',
  9166.                     name: 'target',
  9167.                     fieldLabel: 'Target node',
  9168.                     allowBlank: false,
  9169.                     onlineValidator: true
  9170.                 },
  9171.                 {
  9172.                     xtype: 'pvecheckbox',
  9173.                     name: 'online',
  9174.                     uncheckedValue: 0,
  9175.                     defaultValue: 0,
  9176.                     fieldLabel: 'Online'
  9177.                 }
  9178.             ]
  9179.         });
  9180.  
  9181.         var form = me.formPanel.getForm();
  9182.  
  9183.         var submitBtn = Ext.create('Ext.Button', {
  9184.             text: 'Migrate',
  9185.             handler: function() {
  9186.                 var values = form.getValues();
  9187.                 me.migrate(values.target, values.online);
  9188.             }
  9189.         });
  9190.  
  9191.         Ext.apply(me, {
  9192.             title: "Migrate VM " + me.vmid,
  9193.             width: 350,
  9194.             modal: true,
  9195.             layout: 'auto',
  9196.             border: false,
  9197.             items: [ me.formPanel ],
  9198.             buttons: [ submitBtn ]
  9199.         });
  9200.  
  9201.         me.callParent();
  9202.     }
  9203. });
  9204. Ext.define('PVE.qemu.Monitor', {
  9205.     extend: 'Ext.panel.Panel',
  9206.  
  9207.     alias: 'widget.pveQemuMonitor',
  9208.  
  9209.     maxLines: 500,
  9210.  
  9211.     initComponent : function() {
  9212.         var me = this;
  9213.  
  9214.         var nodename = me.pveSelNode.data.node;
  9215.         if (!nodename) {
  9216.             throw "no node name specified";
  9217.         }
  9218.  
  9219.         var vmid = me.pveSelNode.data.vmid;
  9220.         if (!vmid) {
  9221.             throw "no VM ID specified";
  9222.         }
  9223.  
  9224.         var lines = [];
  9225.  
  9226.         var textbox = Ext.createWidget('panel', {
  9227.             region: 'center',
  9228.             xtype: 'panel',
  9229.             autoScroll: true,
  9230.             border: true,
  9231.             margins: '5 5 5 5',
  9232.             bodyStyle: 'font-family: monospace;'
  9233.         });
  9234.  
  9235.         var scrollToEnd = function() {
  9236.             var el = textbox.getTargetEl();
  9237.             var dom = Ext.getDom(el);
  9238.  
  9239.             var clientHeight = dom.clientHeight;
  9240.             // BrowserBug: clientHeight reports 0 in IE9 StrictMode
  9241.             // Instead we are using offsetHeight and hardcoding borders
  9242.             if (Ext.isIE9 && Ext.isStrict) {
  9243.                 clientHeight = dom.offsetHeight + 2;
  9244.             }
  9245.             dom.scrollTop = dom.scrollHeight - clientHeight;
  9246.         };
  9247.  
  9248.         var refresh = function() {
  9249.             textbox.update('<pre>' + lines.join('\n') + '</pre>');
  9250.             scrollToEnd();
  9251.         };
  9252.  
  9253.         var addLine = function(line) {
  9254.             lines.push(line);
  9255.             if (lines.length > me.maxLines) {
  9256.                 lines.shift();
  9257.             }
  9258.         };
  9259.  
  9260.         var executeCmd = function(cmd) {
  9261.             addLine("# " + Ext.htmlEncode(cmd));
  9262.             refresh();
  9263.             PVE.Utils.API2Request({
  9264.                 params: { command: cmd },
  9265.                 url: '/nodes/' + nodename + '/qemu/' + vmid + "/monitor",
  9266.                 method: 'POST',
  9267.                 waitMsgTarget: me,
  9268.                 success: function(response, opts) {
  9269.                     var res = response.result.data;
  9270.                     Ext.Array.each(res.split('\n'), function(line) {
  9271.                         addLine(Ext.htmlEncode(line));
  9272.                     });
  9273.                     refresh();
  9274.                 },
  9275.                 failure: function(response, opts) {
  9276.                     Ext.Msg.alert('Error', response.htmlStatus);
  9277.                 }
  9278.             });
  9279.         };
  9280.  
  9281.         Ext.apply(me, {
  9282.             layout: { type: 'border' },
  9283.             border: false,
  9284.             items: [
  9285.                 textbox,
  9286.                 {
  9287.                     region: 'south',
  9288.                     margins:'0 5 5 5',
  9289.                     border: false,
  9290.                     xtype: 'textfield',
  9291.                     name: 'cmd',
  9292.                     value: '',
  9293.                     fieldStyle: 'font-family: monospace;',
  9294.                     allowBlank: true,
  9295.                     listeners: {
  9296.                         afterrender: function(f) {
  9297.                             f.focus(false);
  9298.                             addLine("Type 'help' for help.");
  9299.                             refresh();
  9300.                         },
  9301.                         specialkey: function(f, e) {
  9302.                             if (e.getKey() === e.ENTER) {
  9303.                                 var cmd = f.getValue();
  9304.                                 f.setValue('');
  9305.                                 executeCmd(cmd);
  9306.                             }
  9307.                         }
  9308.                     }
  9309.                 }
  9310.             ],
  9311.             listeners: {
  9312.                 show: function() {
  9313.                     var field = me.query('textfield[name="cmd"]')[0];
  9314.                     field.focus(false, true);
  9315.                 }
  9316.             }
  9317.         });
  9318.  
  9319.         me.callParent();
  9320.     }
  9321. });
  9322. Ext.define('PVE.qemu.Summary', {
  9323.     extend: 'Ext.panel.Panel',
  9324.     alias: 'widget.pveQemuSummary',
  9325.  
  9326.     initComponent: function() {
  9327.         var me = this;
  9328.  
  9329.         var nodename = me.pveSelNode.data.node;
  9330.         if (!nodename) {
  9331.             throw "no node name specified";
  9332.         }
  9333.  
  9334.         var vmid = me.pveSelNode.data.vmid;
  9335.         if (!vmid) {
  9336.             throw "no VM ID specified";
  9337.         }
  9338.  
  9339.         if (!me.workspace) {
  9340.             throw "no workspace specified";
  9341.         }
  9342.  
  9343.         if (!me.statusStore) {
  9344.             throw "no status storage specified";
  9345.         }
  9346.  
  9347.         var rstore = me.statusStore;
  9348.  
  9349.         var statusview = Ext.create('PVE.qemu.StatusView', {
  9350.             title: 'Status',
  9351.             pveSelNode: me.pveSelNode,
  9352.             width: 400,
  9353.             rstore: rstore
  9354.         });
  9355.  
  9356.         var rrdurl = "/api2/png/nodes/" + nodename + "/qemu/" + vmid + "/rrd";
  9357.  
  9358.         var notesview = Ext.create('PVE.panel.NotesView', {
  9359.             pveSelNode: me.pveSelNode,
  9360.             flex: 1
  9361.         });
  9362.  
  9363.         Ext.apply(me, {
  9364.             tbar: [ '->', { xtype: 'pveRRDTypeSelector' } ],
  9365.             autoScroll: true,
  9366.             bodyStyle: 'padding:10px',
  9367.             defaults: {
  9368.                 style: 'padding-top:10px',
  9369.                 width: 800
  9370.             },
  9371.             items: [
  9372.                 {
  9373.                     style: 'padding-top:0px',
  9374.                     layout: {
  9375.                         type: 'hbox',
  9376.                         align: 'stretchmax'
  9377.                     },
  9378.                     border: false,
  9379.                     items: [ statusview, notesview ]
  9380.                 },
  9381.                 {
  9382.                     xtype: 'pveRRDView',
  9383.                     title: "CPU usage %",
  9384.                     pveSelNode: me.pveSelNode,
  9385.                     datasource: 'cpu',
  9386.                     rrdurl: rrdurl
  9387.                 },
  9388.                 {
  9389.                     xtype: 'pveRRDView',
  9390.                     title: "Memory usage",
  9391.                     pveSelNode: me.pveSelNode,
  9392.                     datasource: 'mem,maxmem',
  9393.                     rrdurl: rrdurl
  9394.                 },
  9395.                 {
  9396.                     xtype: 'pveRRDView',
  9397.                     title: "Network traffic",
  9398.                     pveSelNode: me.pveSelNode,
  9399.                     datasource: 'netin,netout',
  9400.                     rrdurl: rrdurl
  9401.                 },
  9402.                 {
  9403.                     xtype: 'pveRRDView',
  9404.                     title: "Disk IO",
  9405.                     pveSelNode: me.pveSelNode,
  9406.                     datasource: 'diskread,diskwrite',
  9407.                     rrdurl: rrdurl
  9408.                 }
  9409.             ]
  9410.         });
  9411.  
  9412.         me.on('show', function() {
  9413.             notesview.load();
  9414.         });
  9415.  
  9416.         me.callParent();
  9417.     }
  9418. });
  9419. Ext.define('PVE.qemu.OSTypeInputPanel', {
  9420.     extend: 'PVE.panel.InputPanel',
  9421.     alias: 'widget.PVE.qemu.OSTypeInputPanel',
  9422.  
  9423.     initComponent : function() {
  9424.         var me = this;
  9425.  
  9426.         me.column1 = [
  9427.             {
  9428.                 xtype: 'component',
  9429.                 html: 'Microsoft Windows',
  9430.                 cls:'x-form-check-group-label'
  9431.             },
  9432.             {
  9433.                 xtype: 'radiofield',
  9434.                 name: 'ostype',
  9435.                 inputValue: 'win7'
  9436.             },
  9437.             {
  9438.                 xtype: 'radiofield',
  9439.                 name: 'ostype',
  9440.                 inputValue: 'w2k8'
  9441.             },
  9442.             {
  9443.                 xtype: 'radiofield',
  9444.                 name: 'ostype',
  9445.                 inputValue: 'wxp'
  9446.             },
  9447.             {
  9448.                 xtype: 'radiofield',
  9449.                 name: 'ostype',
  9450.                 inputValue: 'w2k'
  9451.             }
  9452.         ];
  9453.  
  9454.         me.column2 = [
  9455.             {
  9456.                 xtype: 'component',
  9457.                 html: 'Linux/Other',
  9458.                 cls:'x-form-check-group-label'
  9459.             },
  9460.             {
  9461.                 xtype: 'radiofield',
  9462.                 name: 'ostype',
  9463.                 inputValue: 'l26'
  9464.             },
  9465.             {
  9466.                 xtype: 'radiofield',
  9467.                 name: 'ostype',
  9468.                 inputValue: 'l24'
  9469.             },
  9470.             {
  9471.                 xtype: 'radiofield',
  9472.                 name: 'ostype',
  9473.                 inputValue: 'other'
  9474.             }
  9475.         ];
  9476.  
  9477.         Ext.Array.each(me.column1, function(def) {
  9478.             if (def.inputValue) {
  9479.                 def.boxLabel = PVE.Utils.render_kvm_ostype(def.inputValue);
  9480.             }
  9481.         });
  9482.         Ext.Array.each(me.column2, function(def) {
  9483.             if (def.inputValue) {
  9484.                 def.boxLabel = PVE.Utils.render_kvm_ostype(def.inputValue);
  9485.             }
  9486.         });
  9487.  
  9488.         Ext.apply(me, {
  9489.             useFieldContainer: {
  9490.                 xtype: 'radiogroup',
  9491.                 allowBlank: false
  9492.             }
  9493.         });
  9494.  
  9495.         me.callParent();
  9496.     }
  9497. });
  9498.  
  9499. Ext.define('PVE.qemu.OSTypeEdit', {
  9500.     extend: 'PVE.window.Edit',
  9501.  
  9502.     initComponent : function() {
  9503.         var me = this;
  9504.  
  9505.         Ext.apply(me, {
  9506.             subject: 'OS Type',
  9507.             items: Ext.create('PVE.qemu.OSTypeInputPanel')
  9508.         });
  9509.  
  9510.         me.callParent();
  9511.  
  9512.         me.load({
  9513.             success: function(response, options) {
  9514.                 var value = response.result.data.ostype || 'other';
  9515.                 me.setValues({ ostype: value});
  9516.             }
  9517.         });
  9518.     }
  9519. });
  9520. Ext.define('PVE.qemu.ProcessorInputPanel', {
  9521.     extend: 'PVE.panel.InputPanel',
  9522.     alias: 'widget.PVE.qemu.ProcessorInputPanel',
  9523.  
  9524.     initComponent : function() {
  9525.         var me = this;
  9526.  
  9527.         me.column1 = [
  9528.             {
  9529.                 xtype: 'numberfield',
  9530.                 name: 'sockets',
  9531.                 minValue: 1,
  9532.                 maxValue: 4,
  9533.                 value: '1',
  9534.                 fieldLabel: 'Sockets',
  9535.                 allowBlank: false,
  9536.                 listeners: {
  9537.                     change: function(f, value) {
  9538.                         var sockets = me.down('field[name=sockets]').getValue();
  9539.                         var cores = me.down('field[name=cores]').getValue();
  9540.                         me.down('field[name=totalcores]').setValue(sockets*cores);
  9541.                     }
  9542.                 }
  9543.             },
  9544.             {
  9545.                 xtype: 'numberfield',
  9546.                 name: 'cores',
  9547.                 minValue: 1,
  9548.                 maxValue: 32,
  9549.                 value: '1',
  9550.                 fieldLabel: 'Cores',
  9551.                 allowBlank: false,
  9552.                 listeners: {
  9553.                     change: function(f, value) {
  9554.                         var sockets = me.down('field[name=sockets]').getValue();
  9555.                         var cores = me.down('field[name=cores]').getValue();
  9556.                         me.down('field[name=totalcores]').setValue(sockets*cores);
  9557.                     }
  9558.                 }
  9559.             }
  9560.         ];
  9561.  
  9562.  
  9563.         me.column2 = [
  9564.             {
  9565.                 xtype: 'CPUModelSelector',
  9566.                 name: 'cpu',
  9567.                 value: '',
  9568.                 fieldLabel: 'CPU type'
  9569.             },
  9570.             {
  9571.                 xtype: 'displayfield',
  9572.                 fieldLabel: 'Total cores',
  9573.                 name: 'totalcores',
  9574.                 value: '1'
  9575.             }
  9576.  
  9577.         ];
  9578.  
  9579.         me.callParent();
  9580.     }
  9581. });
  9582.  
  9583. Ext.define('PVE.qemu.ProcessorEdit', {
  9584.     extend: 'PVE.window.Edit',
  9585.  
  9586.     initComponent : function() {
  9587.         var me = this;
  9588.  
  9589.         Ext.apply(me, {
  9590.             subject: gettext('Processors'),
  9591.             items: Ext.create('PVE.qemu.ProcessorInputPanel')
  9592.         });
  9593.  
  9594.         me.callParent();
  9595.  
  9596.         me.load();
  9597.     }
  9598. });Ext.define('PVE.qemu.BootOrderPanel', {
  9599.     extend: 'PVE.panel.InputPanel',
  9600.  
  9601.     vmconfig: {}, // store loaded vm config
  9602.  
  9603.     bootdisk: undefined,
  9604.     curSel1: '',
  9605.     curSel2: '',
  9606.     curSel3: '',
  9607.  
  9608.     onGetValues: function(values) {
  9609.         var me = this;
  9610.  
  9611.         var order = '';
  9612.  
  9613.         if (me.curSel1) {
  9614.             order = order + me.curSel1;
  9615.         }
  9616.         if (me.curSel2) {
  9617.             order = order + me.curSel2;
  9618.         }
  9619.         if (me.curSel3) {
  9620.             order = order + me.curSel3;
  9621.         }
  9622.  
  9623.         var res = { boot: order };
  9624.         if (me.bootdisk && (me.curSel1 === 'c' || me.curSel2 === 'c' || me.curSel3 === 'c') ) {
  9625.             res.bootdisk =  me.bootdisk;
  9626.         } else {
  9627.             res['delete'] = 'bootdisk';
  9628.         }
  9629.  
  9630.         return res;
  9631.     },
  9632.  
  9633.     setVMConfig: function(vmconfig) {
  9634.         var me = this;
  9635.  
  9636.         me.vmconfig = vmconfig;
  9637.  
  9638.         var order = me.vmconfig.boot || 'cdn';
  9639.         me.bootdisk = me.vmconfig.bootdisk;
  9640.         if (!me.vmconfig[me.bootdisk]) {
  9641.             me.bootdisk = undefined;
  9642.         }
  9643.         me.curSel1 = order.substring(0, 1) || '';
  9644.         me.curSel2 = order.substring(1, 2) || '';
  9645.         me.curSel3 = order.substring(2, 3) || '';
  9646.  
  9647.         me.compute_sel1();
  9648.  
  9649.         me.kv1.resetOriginalValue();
  9650.         me.kv2.resetOriginalValue();
  9651.         me.kv3.resetOriginalValue();
  9652.     },
  9653.  
  9654.     genList: function(includeNone, sel1, sel2) {
  9655.         var me = this;
  9656.         var list = [];
  9657.  
  9658.         if (sel1 !== 'c' && (sel2 !== 'c')) {
  9659.             Ext.Object.each(me.vmconfig, function(key, value) {
  9660.                 if ((/^(ide|scsi|virtio)\d+$/).test(key) &&
  9661.                     !(/media=cdrom/).test(value)) {
  9662.                     list.push([key, "Disk '" + key + "'"]);
  9663.                 }
  9664.             });
  9665.         }
  9666.  
  9667.         if (sel1 !== 'd' && (sel2 !== 'd')) {
  9668.             list.push(['d', 'CD-ROM']);
  9669.         }
  9670.         if (sel1 !== 'n' && (sel2 !== 'n')) {
  9671.             list.push(['n', gettext('Network')]);
  9672.         }
  9673.         //if (sel1 !== 'a' && (sel2 !== 'a')) {
  9674.         //    list.push(['a', 'Floppy']);
  9675.         //}
  9676.  
  9677.         if (includeNone) {
  9678.             list.push(['', 'none']);
  9679.         }
  9680.  
  9681.         return list;
  9682.     },
  9683.  
  9684.     compute_sel3: function() {
  9685.         var me = this;
  9686.         var list = me.genList(true, me.curSel1, me.curSel2);
  9687.         me.kv3.store.loadData(list);
  9688.         me.kv3.setValue((me.curSel3 === 'c') ? me.bootdisk : me.curSel3);
  9689.     },
  9690.  
  9691.     compute_sel2: function() {
  9692.         var me = this;
  9693.         var list = me.genList(true, me.curSel1);
  9694.         me.kv2.store.loadData(list);
  9695.         me.kv2.setValue((me.curSel2 === 'c') ? me.bootdisk : me.curSel2);
  9696.         me.compute_sel3();
  9697.     },
  9698.  
  9699.     compute_sel1: function() {
  9700.         var me = this;
  9701.         var list = me.genList(false);
  9702.         me.kv1.store.loadData(list);
  9703.         me.kv1.setValue((me.curSel1 === 'c') ? me.bootdisk : me.curSel1);
  9704.         me.compute_sel2();
  9705.     },
  9706.  
  9707.     initComponent : function() {
  9708.         var me = this;
  9709.  
  9710.         me.kv1 = Ext.create('PVE.form.KVComboBox', {
  9711.             fieldLabel: gettext('Boot device') + " 1",
  9712.             labelWidth: 120,
  9713.             name: 'bd1',
  9714.             allowBlank: false,
  9715.             data: []
  9716.         });
  9717.  
  9718.         me.kv2 = Ext.create('PVE.form.KVComboBox', {
  9719.             fieldLabel: gettext('Boot device') + " 2",
  9720.             labelWidth: 120,
  9721.             name: 'bd2',
  9722.             allowBlank: false,
  9723.             data: []
  9724.         });
  9725.  
  9726.         me.kv3 = Ext.create('PVE.form.KVComboBox', {
  9727.             fieldLabel: gettext('Boot device') + " 3",
  9728.             labelWidth: 120,
  9729.             name: 'bd3',
  9730.             allowBlank: false,
  9731.             data: []
  9732.         });
  9733.  
  9734.         me.mon(me.kv1, 'change', function(t, value) {
  9735.             if ((/^(ide|scsi|virtio)\d+$/).test(value)) {
  9736.                 me.curSel1 = 'c';
  9737.                 me.bootdisk = value;
  9738.             } else {
  9739.                 me.curSel1 = value;
  9740.             }
  9741.             me.compute_sel2();
  9742.         });
  9743.  
  9744.         me.mon(me.kv2, 'change', function(t, value) {
  9745.             if ((/^(ide|scsi|virtio)\d+$/).test(value)) {
  9746.                 me.curSel2 = 'c';
  9747.                 me.bootdisk = value;
  9748.             } else {
  9749.                 me.curSel2 = value;
  9750.             }
  9751.             me.compute_sel3();
  9752.         });
  9753.  
  9754.         me.mon(me.kv3, 'change', function(t, value) {
  9755.             if ((/^(ide|scsi|virtio)\d+$/).test(value)) {
  9756.                 me.curSel3 = 'c';
  9757.                 me.bootdisk = value;
  9758.             } else {
  9759.                 me.curSel3 = value;
  9760.             }
  9761.         });
  9762.  
  9763.         Ext.apply(me, {
  9764.             items: [ me.kv1, me.kv2, me.kv3 ]
  9765.         });
  9766.  
  9767.         me.callParent();
  9768.     }
  9769. });
  9770.  
  9771. Ext.define('PVE.qemu.BootOrderEdit', {
  9772.     extend: 'PVE.window.Edit',
  9773.  
  9774.     initComponent : function() {
  9775.         var me = this;
  9776.  
  9777.         var ipanel = Ext.create('PVE.qemu.BootOrderPanel', {});
  9778.  
  9779.         me.items = [ ipanel ];
  9780.  
  9781.         me.subject = gettext('Boot order');
  9782.  
  9783.         me.callParent();
  9784.  
  9785.         me.load({
  9786.             success: function(response, options) {
  9787.                 ipanel.setVMConfig(response.result.data);
  9788.             }
  9789.         });
  9790.     }
  9791. });
  9792. Ext.define('PVE.qemu.MemoryInputPanel', {
  9793.     extend: 'PVE.panel.InputPanel',
  9794.     alias: 'widget.PVE.qemu.MemoryInputPanel',
  9795.  
  9796.     insideWizard: false,
  9797.  
  9798.     initComponent : function() {
  9799.         var me = this;
  9800.  
  9801.         var labelWidth = 120;
  9802.  
  9803.         var items = {
  9804.             xtype: 'numberfield',
  9805.             name: 'memory',
  9806.             minValue: 32,
  9807.             maxValue: 512*1024,
  9808.             value: '512',
  9809.             step: 32,
  9810.             fieldLabel: gettext('Memory') + ' (MB)',
  9811.             labelWidth: labelWidth,
  9812.             allowBlank: false
  9813.         };
  9814.  
  9815.         if (me.insideWizard) {
  9816.             me.column1 = items;
  9817.         } else {
  9818.             me.items = items;
  9819.         }
  9820.  
  9821.         me.callParent();
  9822.     }
  9823. });
  9824.  
  9825. Ext.define('PVE.qemu.MemoryEdit', {
  9826.     extend: 'PVE.window.Edit',
  9827.  
  9828.     initComponent : function() {
  9829.         var me = this;
  9830.  
  9831.         Ext.apply(me, {
  9832.             subject: gettext('Memory'),
  9833.             items: Ext.create('PVE.qemu.MemoryInputPanel')
  9834.         });
  9835.  
  9836.         me.callParent();
  9837.  
  9838.         me.load();
  9839.     }
  9840. });Ext.define('PVE.qemu.NetworkInputPanel', {
  9841.     extend: 'PVE.panel.InputPanel',
  9842.     alias: 'widget.PVE.qemu.NetworkInputPanel',
  9843.  
  9844.     insideWizard: false,
  9845.  
  9846.     onGetValues: function(values) {
  9847.         var me = this;
  9848.  
  9849.         me.network.model = values.model;
  9850.         if (values.networkmode === 'none') {
  9851.             return {};
  9852.         } else if (values.networkmode === 'bridge') {
  9853.             me.network.bridge = values.bridge;
  9854.             me.network.tag = values.tag;
  9855.         } else {
  9856.             me.network.bridge = undefined;
  9857.         }
  9858.         me.network.macaddr = values.macaddr;
  9859.  
  9860.         if (values.rate) {
  9861.             me.network.rate = values.rate;
  9862.         } else {
  9863.             delete me.network.rate;
  9864.         }
  9865.  
  9866.         var params = {};
  9867.  
  9868.         params[me.confid] = PVE.Parser.printQemuNetwork(me.network);
  9869.  
  9870.         return params;
  9871.     },
  9872.  
  9873.     setNetwork: function(confid, data) {
  9874.         var me = this;
  9875.  
  9876.         me.confid = confid;
  9877.  
  9878.         if (data) {
  9879.             data.networkmode = data.bridge ? 'bridge' : 'nat';
  9880.         } else {
  9881.             data = {};
  9882.             data.networkmode = 'bridge';
  9883.         }
  9884.         me.network = data;
  9885.  
  9886.         me.setValues(me.network);
  9887.     },
  9888.  
  9889.     setNodename: function(nodename) {
  9890.         var me = this;
  9891.  
  9892.         me.bridgesel.setNodename(nodename);
  9893.     },
  9894.  
  9895.     initComponent : function() {
  9896.         var me = this;
  9897.  
  9898.         me.network = {};
  9899.         me.confid = 'net0';
  9900.  
  9901.         me.bridgesel = Ext.create('PVE.form.BridgeSelector', {
  9902.             name: 'bridge',
  9903.             fieldLabel: 'Bridge',
  9904.             nodename: me.nodename,
  9905.             labelAlign: 'right',
  9906.             autoSelect: true,
  9907.             allowBlank: false
  9908.         });
  9909.  
  9910.         me.column1 = [
  9911.             {
  9912.                 xtype: 'radiofield',
  9913.                 name: 'networkmode',
  9914.                 height: 22, // hack: set same height as text fields
  9915.                 inputValue: 'bridge',
  9916.                 boxLabel: 'Bridged mode',
  9917.                 checked: true,
  9918.                 listeners: {
  9919.                     change: function(f, value) {
  9920.                         if (!me.rendered) {
  9921.                             return;
  9922.                         }
  9923.                         me.down('field[name=bridge]').setDisabled(!value);
  9924.                         me.down('field[name=bridge]').validate();
  9925.                         me.down('field[name=tag]').setDisabled(!value);
  9926.                     }
  9927.                 }
  9928.             },
  9929.             me.bridgesel,
  9930.             {
  9931.                 xtype: 'numberfield',
  9932.                 name: 'tag',
  9933.                 minValue: 1,
  9934.                 maxValue: 4094,
  9935.                 value: '',
  9936.                 emptyText: 'no VLAN',
  9937.                 fieldLabel: 'VLAN Tag',
  9938.                 labelAlign: 'right',
  9939.                 allowBlank: true
  9940.             },
  9941.             {
  9942.                 xtype: 'radiofield',
  9943.                 name: 'networkmode',
  9944.                 height: 22, // hack: set same height as text fields
  9945.                 inputValue: 'nat',
  9946.                 boxLabel: 'NAT mode'
  9947.             }
  9948.         ];
  9949.  
  9950.         if (me.insideWizard) {
  9951.             me.column1.push({
  9952.                 xtype: 'radiofield',
  9953.                 name: 'networkmode',
  9954.                 height: 22, // hack: set same height as text fields
  9955.                 inputValue: 'none',
  9956.                 boxLabel: 'No network device'
  9957.             });
  9958.         }
  9959.  
  9960.         me.column2 = [
  9961.             {
  9962.                 xtype: 'PVE.form.NetworkCardSelector',
  9963.                 name: 'model',
  9964.                 fieldLabel: 'Model',
  9965.                 value: 'rtl8139',
  9966.                 allowBlank: false
  9967.             },
  9968.             {
  9969.                 xtype: 'textfield',
  9970.                 name: 'macaddr',
  9971.                 fieldLabel: 'MAC address',
  9972.                 vtype: 'MacAddress',
  9973.                 allowBlank: true,
  9974.                 emptyText: 'auto'
  9975.             },
  9976.             {
  9977.                 xtype: 'numberfield',
  9978.                 name: 'rate',
  9979.                 fieldLabel: 'Rate limit (MB/s)',
  9980.                 minValue: 0,
  9981.                 maxValue: 10*1024,
  9982.                 value: '',
  9983.                 emptyText: 'unlimited',
  9984.                 allowBlank: true
  9985.             }
  9986.         ];
  9987.  
  9988.         me.callParent();
  9989.     }
  9990. });
  9991.  
  9992. Ext.define('PVE.qemu.NetworkEdit', {
  9993.     extend: 'PVE.window.Edit',
  9994.  
  9995.     isAdd: true,
  9996.  
  9997.     initComponent : function() {
  9998.         /*jslint confusion: true */
  9999.  
  10000.         var me = this;
  10001.  
  10002.         var nodename = me.pveSelNode.data.node;
  10003.         if (!nodename) {
  10004.             throw "no node name specified";
  10005.         }
  10006.  
  10007.         me.create = me.confid ? false : true;
  10008.  
  10009.         var ipanel = Ext.create('PVE.qemu.NetworkInputPanel', {
  10010.             confid: me.confid,
  10011.             nodename: nodename
  10012.         });
  10013.  
  10014.         Ext.applyIf(me, {
  10015.             subject: gettext('Network Device'),
  10016.             items: ipanel
  10017.         });
  10018.  
  10019.         me.callParent();
  10020.  
  10021.         me.load({
  10022.             success: function(response, options) {
  10023.                 var i, confid;
  10024.                 me.vmconfig = response.result.data;
  10025.                 if (!me.create) {
  10026.                     var value = me.vmconfig[me.confid];
  10027.                     var network = PVE.Parser.parseQemuNetwork(me.confid, value);
  10028.                     if (!network) {
  10029.                         Ext.Msg.alert('Error', 'Unable to parse network options');
  10030.                         me.close();
  10031.                         return;
  10032.                     }
  10033.                     ipanel.setNetwork(me.confid, network);
  10034.                 } else {
  10035.                     for (i = 0; i < 100; i++) {
  10036.                         confid = 'net' + i.toString();
  10037.                         if (!Ext.isDefined(me.vmconfig[confid])) {
  10038.                             me.confid = confid;
  10039.                             break;
  10040.                         }
  10041.                     }
  10042.                     ipanel.setNetwork(me.confid);
  10043.                 }
  10044.             }
  10045.         });
  10046.     }
  10047. });
  10048. // fixme: howto avoid jslint type confusion?
  10049. /*jslint confusion: true */
  10050. Ext.define('PVE.qemu.CDInputPanel', {
  10051.     extend: 'PVE.panel.InputPanel',
  10052.     alias: 'widget.PVE.qemu.CDInputPanel',
  10053.  
  10054.     insideWizard: false,
  10055.  
  10056.     onGetValues: function(values) {
  10057.         var me = this;
  10058.  
  10059.         var confid = me.confid || (values.controller + values.deviceid);
  10060.  
  10061.         me.drive.media = 'cdrom';
  10062.         if (values.mediaType === 'iso') {
  10063.             me.drive.file = values.cdimage;
  10064.         } else if (values.mediaType === 'cdrom') {
  10065.             me.drive.file = 'cdrom';
  10066.         } else {
  10067.             me.drive.file = 'none';
  10068.         }
  10069.  
  10070.         var params = {};
  10071.  
  10072.         params[confid] = PVE.Parser.printQemuDrive(me.drive);
  10073.  
  10074.         return params;
  10075.     },
  10076.  
  10077.     setVMConfig: function(vmconfig) {
  10078.         var me = this;
  10079.  
  10080.         if (me.bussel) {
  10081.             me.bussel.setVMConfig(vmconfig, 'cdrom');
  10082.         }
  10083.     },
  10084.  
  10085.     setDrive: function(drive) {
  10086.         var me = this;
  10087.  
  10088.         var values = {};
  10089.         if (drive.file === 'cdrom') {
  10090.             values.mediaType = 'cdrom';
  10091.         } else if (drive.file === 'none') {
  10092.             values.mediaType = 'none';
  10093.         } else {
  10094.             values.mediaType = 'iso';
  10095.             var match = drive.file.match(/^([^:]+):/);
  10096.             if (match) {
  10097.                 values.cdstorage = match[1];
  10098.                 values.cdimage = drive.file;
  10099.             }
  10100.         }
  10101.  
  10102.         me.drive = drive;
  10103.  
  10104.         me.setValues(values);
  10105.     },
  10106.  
  10107.     setNodename: function(nodename) {
  10108.         var me = this;
  10109.  
  10110.         me.cdstoragesel.setNodename(nodename);
  10111.         me.cdfilesel.setStorage(undefined, nodename);
  10112.     },
  10113.  
  10114.     initComponent : function() {
  10115.         var me = this;
  10116.  
  10117.         me.drive = {};
  10118.  
  10119.         var items = [];
  10120.  
  10121.         if (!me.confid) {
  10122.             me.bussel = Ext.createWidget('PVE.form.ControllerSelector', {
  10123.                 noVirtIO: true
  10124.             });
  10125.             items.push(me.bussel);
  10126.         }
  10127.  
  10128.         items.push({
  10129.             xtype: 'radiofield',
  10130.             name: 'mediaType',
  10131.             inputValue: 'iso',
  10132.             boxLabel: 'Use CD/DVD disc image file (iso)',
  10133.             checked: true,
  10134.             listeners: {
  10135.                 change: function(f, value) {
  10136.                     if (!me.rendered) {
  10137.                         return;
  10138.                     }
  10139.                     me.down('field[name=cdstorage]').setDisabled(!value);
  10140.                     me.down('field[name=cdimage]').setDisabled(!value);
  10141.                     me.down('field[name=cdimage]').validate();
  10142.                 }
  10143.             }
  10144.         });
  10145.  
  10146.         me.cdfilesel = Ext.create('PVE.form.FileSelector', {
  10147.             name: 'cdimage',
  10148.             nodename: me.nodename,
  10149.             storageContent: 'iso',
  10150.             fieldLabel: 'ISO Image',
  10151.             labelAlign: 'right',
  10152.             allowBlank: false