install -m 0644 copyright ${DOCDIR}
install -m 0644 debian/changelog.Debian ${DOCDIR}
install -m 0644 country.dat ${DESTDIR}/usr/share/${PACKAGE}
- # temporary: set ExtJS 5 migration devel directory
- install -d ${DESTDIR}/usr/share/${PACKAGE}/manager5
+ # temporary: set ExtJS 6 migration devel directory
+ install -d ${DESTDIR}/usr/share/${PACKAGE}/manager6
set -e && for i in ${SUBDIRS}; do ${MAKE} -C $$i $@; done
.PHONY: distclean
+++ /dev/null
-// Some configuration values are complex strings -
-// so we need parsers/generators for them.
-
-Ext.define('PVE.Parser', { statics: {
-
- // this class only contains static functions
-
- parseQemuNetwork: function(key, value) {
- if (!(key && value)) {
- return;
- }
-
- var res = {};
-
- var errors = false;
- Ext.Array.each(value.split(','), function(p) {
- if (!p || p.match(/^\s*$/)) {
- return; // continue
- }
-
- var match_res;
-
- if ((match_res = p.match(/^(ne2k_pci|e1000|e1000-82540em|e1000-82544gc|e1000-82545em|vmxnet3|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i)) !== null) {
- res.model = match_res[1].toLowerCase();
- if (match_res[3]) {
- res.macaddr = match_res[3];
- }
- } else if ((match_res = p.match(/^bridge=(\S+)$/)) !== null) {
- res.bridge = match_res[1];
- } else if ((match_res = p.match(/^rate=(\d+(\.\d+)?)$/)) !== null) {
- res.rate = match_res[1];
- } else if ((match_res = p.match(/^tag=(\d+(\.\d+)?)$/)) !== null) {
- res.tag = match_res[1];
- } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) {
- res.firewall = match_res[1];
- } else if ((match_res = p.match(/^link_down=(\d+)$/)) !== null) {
- res.disconnect = match_res[1];
- } else if ((match_res = p.match(/^queues=(\d+)$/)) !== null) {
- res.queues = match_res[1];
- } else {
- errors = true;
- return false; // break
- }
- });
-
- if (errors || !res.model) {
- return;
- }
-
- return res;
- },
-
- printQemuNetwork: function(net) {
-
- var netstr = net.model;
- if (net.macaddr) {
- netstr += "=" + net.macaddr;
- }
- if (net.bridge) {
- netstr += ",bridge=" + net.bridge;
- if (net.tag) {
- netstr += ",tag=" + net.tag;
- }
- if (net.firewall) {
- netstr += ",firewall=" + net.firewall;
- }
- }
- if (net.rate) {
- netstr += ",rate=" + net.rate;
- }
- if (net.queues) {
- netstr += ",queues=" + net.queues;
- }
- if (net.disconnect) {
- netstr += ",link_down=" + net.disconnect;
- }
- return netstr;
- },
-
- parseQemuDrive: function(key, value) {
- if (!(key && value)) {
- return;
- }
-
- var res = {};
-
- var match_res = key.match(/^([a-z]+)(\d+)$/);
- if (!match_res) {
- return;
- }
- res['interface'] = match_res[1];
- res.index = match_res[2];
-
- var errors = false;
- Ext.Array.each(value.split(','), function(p) {
- if (!p || p.match(/^\s*$/)) {
- return; // continue
- }
- var match_res = p.match(/^([a-z_]+)=(\S+)$/);
- if (!match_res) {
- if (!p.match(/\=/)) {
- res.file = p;
- return; // continue
- }
- errors = true;
- return false; // break
- }
- var k = match_res[1];
- if (k === 'volume') {
- k = 'file';
- }
-
- if (Ext.isDefined(res[k])) {
- errors = true;
- return false; // break
- }
-
- var v = match_res[2];
-
- if (k === 'cache' && v === 'off') {
- v = 'none';
- }
-
- res[k] = v;
- });
-
- if (errors || !res.file) {
- return;
- }
-
- return res;
- },
-
- printQemuDrive: function(drive) {
-
- var drivestr = drive.file;
-
- Ext.Object.each(drive, function(key, value) {
- if (!Ext.isDefined(value) || key === 'file' ||
- key === 'index' || key === 'interface') {
- return; // continue
- }
- drivestr += ',' + key + '=' + value;
- });
-
- return drivestr;
- },
-
- parseOpenVZNetIf: function(value) {
- if (!value) {
- return;
- }
-
- var res = {};
-
- var errors = false;
- Ext.Array.each(value.split(';'), function(item) {
- if (!item || item.match(/^\s*$/)) {
- return; // continue
- }
-
- var data = {};
- Ext.Array.each(item.split(','), function(p) {
- if (!p || p.match(/^\s*$/)) {
- return; // continue
- }
- var match_res = p.match(/^(ifname|mac|bridge|host_ifname|host_mac|mac_filter)=(\S+)$/);
- if (!match_res) {
- errors = true;
- return false; // break
- }
- if (match_res[1] === 'bridge'){
- var bridgevlanf = match_res[2];
- var bridge_res = bridgevlanf.match(/^(vmbr(\d+))(v(\d+))?(f)?$/);
- if (!bridge_res) {
- errors = true;
- return false; // break
- }
- data['bridge'] = bridge_res[1];
- data['tag'] = bridge_res[4];
- data['firewall'] = bridge_res[5] ? 1 : 0;
- } else {
- data[match_res[1]] = match_res[2];
- }
- });
-
- if (errors || !data.ifname) {
- errors = true;
- return false; // break
- }
-
- data.raw = item;
-
- res[data.ifname] = data;
- });
-
- return errors ? undefined: res;
- },
-
- printOpenVZNetIf: function(netif) {
- var netarray = [];
-
- Ext.Object.each(netif, function(iface, data) {
- var tmparray = [];
- Ext.Array.each(['ifname', 'mac', 'bridge', 'host_ifname' , 'host_mac', 'mac_filter', 'tag', 'firewall'], function(key) {
- var value = data[key];
- if (key === 'bridge'){
- if(data['tag']){
- value = value + 'v' + data['tag'];
- }
- if (data['firewall']){
- value = value + 'f';
- }
- }
- if (value) {
- tmparray.push(key + '=' + value);
- }
-
- });
- netarray.push(tmparray.join(','));
- });
-
- return netarray.join(';');
- },
-
- parseLxcNetwork: function(value) {
- if (!value) {
- return;
- }
-
- var data = {};
- Ext.Array.each(value.split(','), function(p) {
- if (!p || p.match(/^\s*$/)) {
- return; // continue
- }
- var match_res = p.match(/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|firewall|tag)=(\S+)$/);
- if (!match_res) {
- // todo: simply ignore errors ?
- return; // continue
- }
- data[match_res[1]] = match_res[2];
- });
-
- return data;
- },
-
- printLxcNetwork: function(data) {
- var tmparray = [];
- Ext.Array.each(['bridge', 'hwaddr', 'mtu', 'name', 'ip',
- 'gw', 'ip6', 'gw6', 'firewall', 'tag'], function(key) {
- var value = data[key];
- if (value) {
- tmparray.push(key + '=' + value);
- }
- });
-
- return tmparray.join(',');
- },
-
- parseStartup: function(value) {
- if (value === undefined) {
- return;
- }
-
- var res = {};
-
- var errors = false;
- Ext.Array.each(value.split(','), function(p) {
- if (!p || p.match(/^\s*$/)) {
- return; // continue
- }
-
- var match_res;
-
- if ((match_res = p.match(/^(order)?=(\d+)$/)) !== null) {
- res.order = match_res[2];
- } else if ((match_res = p.match(/^up=(\d+)$/)) !== null) {
- res.up = match_res[1];
- } else if ((match_res = p.match(/^down=(\d+)$/)) !== null) {
- res.down = match_res[1];
- } else {
- errors = true;
- return false; // break
- }
- });
-
- if (errors) {
- return;
- }
-
- return res;
- },
-
- printStartup: function(startup) {
- var arr = [];
- if (startup.order !== undefined && startup.order !== '') {
- arr.push('order=' + startup.order);
- }
- if (startup.up !== undefined && startup.up !== '') {
- arr.push('up=' + startup.up);
- }
- if (startup.down !== undefined && startup.down !== '') {
- arr.push('down=' + startup.down);
- }
-
- return arr.join(',');
- },
-
- parseQemuSmbios1: function(value) {
- var res = {};
-
- Ext.Array.each(value.split(','), function(p) {
- var kva = p.split(/=/, 2);
- res[kva[0]] = kva[1];
- });
-
- return res;
- },
-
- printQemuSmbios1: function(data) {
-
- var datastr = '';
-
- Ext.Object.each(data, function(key, value) {
- if (value === '') return;
- datastr += (datastr !== '' ? ',' : '') + key + '=' + value;
- });
-
- return datastr;
- },
-
- parseTfaConfig: function(value) {
- var res = {};
-
- Ext.Array.each(value.split(','), function(p) {
- var kva = p.split(/=/, 2);
- res[kva[0]] = kva[1];
- });
-
- return res;
- }
-
-}});
+++ /dev/null
-pveproxy with ExtJS 6 developpement mini howto
-==============================================
-
-unpack the ExtJS 6 sources, and copy them to /usr/share/pve-manager/ext6
-
- cd www/ext6/
- make install
-
-symlink the ext6 dir in pve-manager to the manager5 directory
-
- cd /usr/share/pve-manager
- ln -s PATH_TO_YOUR_GIT_REPO/www/manager5 #this is not a typo
-
-access the PVE proxy with ExtJS 6
-
- https://localhost:8006/?ext6=1
-
-
-With the extra parameter **ext6=1**, pve-proxy will call the function **PVE::ExtJSIndex6::get_index()**
-which returns a HTML page, with all javascript symlinked from your git repository.
-Provided you included the javascript files in **PVE/ExtJSIndex5.pm**, after editing a file in the git repository, a simple refresh is enough to see your changes in the browser.
+++ /dev/null
-/* This state provider keeps part of the state inside
- * the browser history.
- *
- * We compress (shorten) url using dictionary based compression
- * i.e. use column separated list instead of url encoded hash:
- * #v\d* version/format
- * := indicates string values
- * :\d+ lookup value in dictionary hash
- * #v1:=value1:5:=value2:=value3:...
-*/
-
-Ext.define('PVE.StateProvider', {
- extend: 'Ext.state.LocalStorageProvider',
-
- // private
- setHV: function(name, newvalue, fireEvents) {
- var me = this;
-
- var changes = false;
- var oldtext = Ext.encode(me.UIState[name]);
- var newtext = Ext.encode(newvalue);
- if (newtext != oldtext) {
- changes = true;
- me.UIState[name] = newvalue;
- //console.log("changed old " + name + " " + oldtext);
- //console.log("changed new " + name + " " + newtext);
- if (fireEvents) {
- me.fireEvent("statechange", me, name, { value: newvalue });
- }
- }
- return changes;
- },
-
- // private
- hslist: [
- // order is important for notifications
- // [ name, default ]
- ['view', 'server'],
- ['rid', 'root'],
- ['ltab', 'tasks'],
- ['nodetab', ''],
- ['storagetab', ''],
- ['pooltab', ''],
- ['kvmtab', ''],
- ['ovztab', ''],
- ['dctab', '']
- ],
-
- hprefix: 'v1',
-
- compDict: {
- snapshot: 29,
- ha: 28,
- support: 27,
- pool: 26,
- syslog: 25,
- ubc: 24,
- initlog: 23,
- openvz: 22,
- backup: 21,
- ressources: 20,
- content: 19,
- root: 18,
- domains: 17,
- roles: 16,
- groups: 15,
- users: 14,
- time: 13,
- dns: 12,
- network: 11,
- services: 10,
- options: 9,
- console: 8,
- hardware: 7,
- permissions: 6,
- summary: 5,
- tasks: 4,
- clog: 3,
- storage: 2,
- folder: 1,
- server: 0
- },
-
- decodeHToken: function(token) {
- var me = this;
-
- var state = {};
- if (!token) {
- Ext.Array.each(me.hslist, function(rec) {
- state[rec[0]] = rec[1];
- });
- return state;
- }
-
- // return Ext.urlDecode(token);
-
- var items = token.split(':');
- var prefix = items.shift();
-
- if (prefix != me.hprefix) {
- return me.decodeHToken();
- }
-
- Ext.Array.each(me.hslist, function(rec) {
- var value = items.shift();
- if (value) {
- if (value[0] === '=') {
- value = decodeURIComponent(value.slice(1));
- } else {
- Ext.Object.each(me.compDict, function(key, cv) {
- if (value == cv) {
- value = key;
- return false;
- }
- });
- }
- }
- state[rec[0]] = value;
- });
-
- return state;
- },
-
- encodeHToken: function(state) {
- var me = this;
-
- // return Ext.urlEncode(state);
-
- var ctoken = me.hprefix;
- Ext.Array.each(me.hslist, function(rec) {
- var value = state[rec[0]];
- if (!Ext.isDefined(value)) {
- value = rec[1];
- }
- value = encodeURIComponent(value);
- if (!value) {
- ctoken += ':';
- } else {
- var comp = me.compDict[value];
- if (Ext.isDefined(comp)) {
- ctoken += ":" + comp;
- } else {
- ctoken += ":=" + value;
- }
- }
- });
-
- return ctoken;
- },
-
- constructor: function(config){
- var me = this;
-
- me.callParent([config]);
-
- me.UIState = me.decodeHToken(); // set default
-
- var history_change_cb = function(token) {
- //console.log("HC " + token);
- if (!token) {
- var res = window.confirm(gettext('Are you sure you want to navigate away from this page?'));
- if (res){
- // process text value and close...
- Ext.History.back();
- } else {
- Ext.History.forward();
- }
- return;
- }
-
- var newstate = me.decodeHToken(token);
- Ext.Array.each(me.hslist, function(rec) {
- if (typeof newstate[rec[0]] == "undefined") {
- return;
- }
- me.setHV(rec[0], newstate[rec[0]], true);
- });
- };
-
- var start_token = Ext.History.getToken();
- if (start_token) {
- history_change_cb(start_token);
- } else {
- var htext = me.encodeHToken(me.UIState);
- Ext.History.add(htext);
- }
-
- Ext.History.on('change', history_change_cb);
- },
-
- get: function(name, defaultValue){
- /*jslint confusion: true */
- var me = this;
- var data;
-
- if (typeof me.UIState[name] != "undefined") {
- data = { value: me.UIState[name] };
- } else {
- data = me.callParent(arguments);
- if (!data && name === 'GuiCap') {
- data = { vms: {}, storage: {}, access: {}, nodes: {}, dc: {} };
- }
- }
-
- //console.log("GET " + name + " " + Ext.encode(data));
- return data;
- },
-
- clear: function(name){
- var me = this;
-
- if (typeof me.UIState[name] != "undefined") {
- me.UIState[name] = null;
- }
-
- me.callParent(arguments);
- },
-
- set: function(name, value){
- var me = this;
-
- //console.log("SET " + name + " " + Ext.encode(value));
- if (typeof me.UIState[name] != "undefined") {
- var newvalue = value ? value.value : null;
- if (me.setHV(name, newvalue, false)) {
- var htext = me.encodeHToken(me.UIState);
- Ext.History.add(htext);
- }
- } else {
- me.callParent(arguments);
- }
- }
-});
+++ /dev/null
-// ExtJS related things
-
-PVE.Utils.toolkit = 'extjs',
-
- // do not send '_dc' parameter
-Ext.Ajax.disableCaching = false;
-
-// custom Vtypes
-Ext.apply(Ext.form.field.VTypes, {
- IPAddress: function(v) {
- return IP4_match.test(v);
- },
- IPAddressText: gettext('Example') + ': 192.168.1.1',
- IPAddressMask: /[\d\.]/i,
-
- IPCIDRAddress: function(v) {
- return IP4_cidr_match.test(v);
- },
- IPCIDRAddressText: gettext('Example') + ': 192.168.1.1/24',
- IPCIDRAddressMask: /[\d\.\/]/i,
-
- IP6Address: function(v) {
- return IP6_match.test(v);
- },
- IP6AddressText: gettext('Example') + ': 2001:DB8::42',
- IP6AddressMask: /[A-Fa-f0-9:]/,
-
- IP6CIDRAddress: function(v) {
- return IP6_cidr_match.test(v);
- },
- IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64',
- IP6CIDRAddressMask: /[A-Fa-f0-9:\/]/,
-
- IP6PrefixLength: function(v) {
- return v >= 0 && v <= 128;
- },
- IP6PrefixLengthText: gettext('Example') + ': X, where 0 <= X <= 128',
- IP6PrefixLengthMask: /[0-9]/,
-
- IP64Address: function(v) {
- return IP64_match.test(v);
- },
- IP64AddressText: gettext('Example') + ': 192.168.1.1 2001:DB8::42',
- IP64AddressMask: /[A-Fa-f0-9\.:]/,
-
- MacAddress: function(v) {
- return (/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/).test(v);
- },
- MacAddressMask: /[a-fA-F0-9:]/,
- MacAddressText: gettext('Example') + ': 01:23:45:67:89:ab',
-
- BridgeName: function(v) {
- return (/^vmbr\d{1,4}$/).test(v);
- },
- BridgeNameText: gettext('Format') + ': vmbr<b>N</b>, where 0 <= <b>N</b> <= 9999',
-
- BondName: function(v) {
- return (/^bond\d{1,4}$/).test(v);
- },
- BondNameText: gettext('Format') + ': bond<b>N</b>, where 0 <= <b>N</b> <= 9999',
-
- InterfaceName: function(v) {
- return (/^[a-z][a-z0-9_]{1,20}$/).test(v);
- },
- InterfaceNameText: gettext('Format') + ': [a-z][a-z0-9_]{1,20}',
-
-
- QemuStartDate: function(v) {
- return (/^(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)$/).test(v);
- },
- QemuStartDateText: gettext('Format') + ': "now" or "2006-06-17T16:01:21" or "2006-06-17"',
-
- StorageId: function(v) {
- return (/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i).test(v);
- },
- StorageIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '-', '_', '.'",
-
- ConfigId: function(v) {
- return (/^[a-z][a-z0-9\_]+$/i).test(v);
- },
- ConfigIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '_'",
-
- HttpProxy: function(v) {
- return (/^http:\/\/.*$/).test(v);
- },
- HttpProxyText: gettext('Example') + ": http://username:password@host:port/",
-
- DnsName: function(v) {
- return (/^(([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)\.)*([A-Za-z0-9]([A-Za-z0-9\-]*[A-Za-z0-9])?)$/).test(v);
- },
- DnsNameText: gettext('This is not a valid DNS name')
-});
-
-// we dont want that a displayfield set the form dirty flag!
-Ext.override(Ext.form.field.Display, {
- isDirty: function() { return false; }
-});
-
-// hack: ExtJS does not display the correct value if we
-// call setValue while the store is loading, so we need
-// to call it again after loading
-Ext.override(Ext.form.field.ComboBox, {
- onLoad: function() {
- this.setValue(this.value, false);
- this.callOverridden(arguments);
- }
-});
-
-Ext.define('Ext.ux.IFrame', {
- extend: 'Ext.Component',
-
- alias: 'widget.uxiframe',
-
- loadMask: 'Loading...',
-
- src: 'about:blank',
-
- renderTpl: [
- '<iframe src="{src}" name="{frameName}" width="100%" height="100%" frameborder="0"></iframe>'
- ],
-
- initComponent: function () {
- this.callParent();
-
- this.frameName = this.frameName || this.id + '-frame';
-
- this.addEvents(
- 'beforeload',
- 'load'
- );
-
- Ext.apply(this.renderSelectors, {
- iframeEl: 'iframe'
- });
- },
-
- initEvents : function() {
- var me = this;
- me.callParent();
- me.iframeEl.on('load', me.onLoad, me);
- },
-
- initRenderData: function() {
- return Ext.apply(this.callParent(), {
- src: this.src,
- frameName: this.frameName
- });
- },
-
- getBody: function() {
- var doc = this.getDoc();
- return doc.body || doc.documentElement;
- },
-
- getDoc: function() {
- try {
- return this.getWin().document;
- } catch (ex) {
- return null;
- }
- },
-
- getWin: function() {
- var me = this,
- name = me.frameName,
- win = Ext.isIE
- ? me.iframeEl.dom.contentWindow
- : window.frames[name];
- return win;
- },
-
- getFrame: function() {
- var me = this;
- return me.iframeEl.dom;
- },
-
- beforeDestroy: function () {
- this.cleanupListeners(true);
- this.callParent();
- },
-
- cleanupListeners: function(destroying){
- var doc, prop;
-
- if (this.rendered) {
- try {
- doc = this.getDoc();
- if (doc) {
- Ext.EventManager.removeAll(doc);
- if (destroying) {
- for (prop in doc) {
- if (doc.hasOwnProperty && doc.hasOwnProperty(prop)) {
- delete doc[prop];
- }
- }
- }
- }
- } catch(e) { }
- }
- },
-
- onLoad: function() {
- var me = this,
- doc = me.getDoc(),
- fn = me.onRelayedEvent;
-
- if (doc) {
- try {
- Ext.EventManager.removeAll(doc);
-
- // These events need to be relayed from the inner document (where they stop
- // bubbling) up to the outer document. This has to be done at the DOM level so
- // the event reaches listeners on elements like the document body. The effected
- // mechanisms that depend on this bubbling behavior are listed to the right
- // of the event.
- Ext.EventManager.on(doc, {
- mousedown: fn, // menu dismisal (MenuManager) and Window onMouseDown (toFront)
- mousemove: fn, // window resize drag detection
- mouseup: fn, // window resize termination
- click: fn, // not sure, but just to be safe
- dblclick: fn, // not sure again
- scope: me
- });
- } catch(e) {
- // cannot do this xss
- }
-
- // We need to be sure we remove all our events from the iframe on unload or we're going to LEAK!
- Ext.EventManager.on(this.getWin(), 'beforeunload', me.cleanupListeners, me);
-
- this.el.unmask();
- this.fireEvent('load', this);
-
- } else if(me.src && me.src != '') {
-
- this.el.unmask();
- this.fireEvent('error', this);
- }
-
-
- },
-
- load: function (src) {
- var me = this,
- text = me.loadMask,
- frame = me.getFrame();
-
- if (me.fireEvent('beforeload', me, src) !== false) {
- if (text && me.el) {
- me.el.mask(text);
- }
-
- frame.src = me.src = (src || me.src);
- }
- }
-});
+++ /dev/null
-Ext.ns('PVE');
-
-// avoid errors when running without development tools
-if (!Ext.isDefined(Ext.global.console)) {
- var console = {
- dir: function() {},
- log: function() {}
- };
-}
-console.log("Starting PVE Manager");
-
-Ext.Ajax.defaultHeaders = {
- 'Accept': 'application/json'
-};
-
-Ext.Ajax.on('beforerequest', function(conn, options) {
- if (PVE.CSRFPreventionToken) {
- if (!options.headers) {
- options.headers = {};
- }
- options.headers.CSRFPreventionToken = PVE.CSRFPreventionToken;
- }
-});
-
-var IPV4_OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])";
-var IPV4_REGEXP = "(?:(?:" + IPV4_OCTET + "\\.){3}" + IPV4_OCTET + ")";
-var IPV6_H16 = "(?:[0-9a-fA-F]{1,4})";
-var IPV6_LS32 = "(?:(?:" + IPV6_H16 + ":" + IPV6_H16 + ")|" + IPV4_REGEXP + ")";
-
-
-var IP4_match = new RegExp("^(?:" + IPV4_REGEXP + ")$");
-var IP4_cidr_match = new RegExp("^(?:" + IPV4_REGEXP + ")\/[1-3][0-9]?$");
-
-var IPV6_REGEXP = "(?:" +
- "(?:(?:" + "(?:" + IPV6_H16 + ":){6})" + IPV6_LS32 + ")|" +
- "(?:(?:" + "::" + "(?:" + IPV6_H16 + ":){5})" + IPV6_LS32 + ")|" +
- "(?:(?:(?:" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){4})" + IPV6_LS32 + ")|" +
- "(?:(?:(?:(?:" + IPV6_H16 + ":){0,1}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){3})" + IPV6_LS32 + ")|" +
- "(?:(?:(?:(?:" + IPV6_H16 + ":){0,2}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){2})" + IPV6_LS32 + ")|" +
- "(?:(?:(?:(?:" + IPV6_H16 + ":){0,3}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){1})" + IPV6_LS32 + ")|" +
- "(?:(?:(?:(?:" + IPV6_H16 + ":){0,4}" + IPV6_H16 + ")?::" + ")" + IPV6_LS32 + ")|" +
- "(?:(?:(?:(?:" + IPV6_H16 + ":){0,5}" + IPV6_H16 + ")?::" + ")" + IPV6_H16 + ")|" +
- "(?:(?:(?:(?:" + IPV6_H16 + ":){0,7}" + IPV6_H16 + ")?::" + ")" + ")" +
- ")";
-
-var IP6_match = new RegExp("^(?:" + IPV6_REGEXP + ")$");
-var IP6_cidr_match = new RegExp("^(?:" + IPV6_REGEXP + ")\/[0-9]{1,3}?$");
-var IP6_bracket_match = new RegExp("^\\[(" + IPV6_REGEXP + ")\\]");
-
-var IP64_match = new RegExp("^(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + ")$");
-
-Ext.define('PVE.Utils', { statics: {
-
- // this class only contains static functions
-
- toolkit: undefined, // (extjs|touch), set inside Toolkit.js
-
- log_severity_hash: {
- 0: "panic",
- 1: "alert",
- 2: "critical",
- 3: "error",
- 4: "warning",
- 5: "notice",
- 6: "info",
- 7: "debug"
- },
-
- support_level_hash: {
- 'c': gettext('Community'),
- 'b': gettext('Basic'),
- 's': gettext('Standard'),
- 'p': gettext('Premium')
- },
-
- noSubKeyHtml: 'You do not have a valid subscription for this server. Please visit <a target="_blank" href="http://www.proxmox.com/products/proxmox-ve/subscription-service-plans">www.proxmox.com</a> to get a list of available options.',
-
- kvm_ostypes: {
- other: gettext('Other OS types'),
- wxp: 'Microsoft Windows XP/2003',
- w2k: 'Microsoft Windows 2000',
- w2k8: 'Microsoft Windows Vista/2008',
- win7: 'Microsoft Windows 7/2008r2',
- win8: 'Microsoft Windows 8/2012',
- l24: 'Linux 2.4 Kernel',
- l26: 'Linux 3.X/2.6 Kernel',
- solaris: 'Solaris Kernel'
- },
-
- render_kvm_ostype: function (value) {
- if (!value) {
- return gettext('Other OS types');
- }
- var text = PVE.Utils.kvm_ostypes[value];
- if (text) {
- return text + ' (' + value + ')';
- }
- return value;
- },
-
- render_hotplug_features: function (value) {
- var fa = [];
-
- if (!value || (value === '0')) {
- return gettext('disabled');
- }
-
- Ext.each(value.split(','), function(el) {
- if (el === 'disk') {
- fa.push(gettext('Disk'));
- } else if (el === 'network') {
- fa.push(gettext('Network'));
- } else if (el === 'usb') {
- fa.push(gettext('USB'));
- } else if (el === 'memory') {
- fa.push(gettext('Memory'));
- } else if (el === 'cpu') {
- fa.push(gettext('CPU'));
- } else {
- fa.push(el);
- }
- });
-
- return fa.join(', ');
- },
-
- network_iface_types: {
- eth: gettext("Network Device"),
- bridge: 'Linux Bridge',
- bond: 'Linux Bond',
- OVSBridge: 'OVS Bridge',
- OVSBond: 'OVS Bond',
- OVSPort: 'OVS Port',
- OVSIntPort: 'OVS IntPort'
- },
-
- render_network_iface_type: function(value) {
- return PVE.Utils.network_iface_types[value] ||
- PVE.Utils.unknownText;
- },
-
- render_scsihw: function(value) {
- if (!value) {
- return PVE.Utils.defaultText + ' (LSI 53C895A)';
- } else if (value === 'lsi') {
- return 'LSI 53C895A';
- } else if (value === 'lsi53c810') {
- return 'LSI 53C810';
- } else if (value === 'megasas') {
- return 'MegaRAID SAS 8708EM2';
- } else if (value === 'virtio-scsi-pci') {
- return 'VIRTIO';
- } else if (value === 'pvscsi') {
- return 'VMware PVSCSI';
- } else {
- return value;
- }
- },
-
- // fixme: auto-generate this
- // for now, please keep in sync with PVE::Tools::kvmkeymaps
- kvm_keymaps: {
- //ar: 'Arabic',
- da: 'Danish',
- de: 'German',
- 'de-ch': 'German (Swiss)',
- 'en-gb': 'English (UK)',
- 'en-us': 'English (USA)',
- es: 'Spanish',
- //et: 'Estonia',
- fi: 'Finnish',
- //fo: 'Faroe Islands',
- fr: 'French',
- 'fr-be': 'French (Belgium)',
- 'fr-ca': 'French (Canada)',
- 'fr-ch': 'French (Swiss)',
- //hr: 'Croatia',
- hu: 'Hungarian',
- is: 'Icelandic',
- it: 'Italian',
- ja: 'Japanese',
- lt: 'Lithuanian',
- //lv: 'Latvian',
- mk: 'Macedonian',
- nl: 'Dutch',
- //'nl-be': 'Dutch (Belgium)',
- no: 'Norwegian',
- pl: 'Polish',
- pt: 'Portuguese',
- 'pt-br': 'Portuguese (Brazil)',
- //ru: 'Russian',
- sl: 'Slovenian',
- sv: 'Swedish',
- //th: 'Thai',
- tr: 'Turkish'
- },
-
- kvm_vga_drivers: {
- std: gettext('Standard VGA'),
- vmware: gettext('VMWare compatible'),
- cirrus: 'Cirrus Logic GD5446',
- qxl: 'SPICE',
- qxl2: 'SPICE dual monitor',
- qxl3: 'SPICE three monitors',
- qxl4: 'SPICE four monitors',
- serial0: gettext('Serial terminal') + ' 0',
- serial1: gettext('Serial terminal') + ' 1',
- serial2: gettext('Serial terminal') + ' 2',
- serial3: gettext('Serial terminal') + ' 3'
- },
-
- render_kvm_language: function (value) {
- if (!value) {
- return PVE.Utils.defaultText;
- }
- var text = PVE.Utils.kvm_keymaps[value];
- if (text) {
- return text + ' (' + value + ')';
- }
- return value;
- },
-
- kvm_keymap_array: function() {
- var data = [['', PVE.Utils.render_kvm_language('')]];
- Ext.Object.each(PVE.Utils.kvm_keymaps, function(key, value) {
- data.push([key, PVE.Utils.render_kvm_language(value)]);
- });
-
- return data;
- },
-
- render_console_viewer: function(value) {
- if (!value) {
- return PVE.Utils.defaultText + ' (HTML5)';
- } else if (value === 'applet') {
- return 'Java VNC Applet';
- } else if (value === 'vv') {
- return 'SPICE (remote-viewer)';
- } else if (value === 'html5') {
- return 'HTML5 (noVNC)';
- } else {
- return value;
- }
- },
-
- language_map: {
- zh_CN: 'Chinese',
- ca: 'Catalan',
- da: 'Danish',
- en: 'English',
- eu: 'Euskera (Basque)',
- fr: 'French',
- de: 'German',
- it: 'Italian',
- ja: 'Japanese',
- nb: 'Norwegian (Bokmal)',
- nn: 'Norwegian (Nynorsk)',
- fa: 'Persian (Farsi)',
- pl: 'Polish',
- pt_BR: 'Portuguese (Brazil)',
- ru: 'Russian',
- sl: 'Slovenian',
- es: 'Spanish',
- sv: 'Swedish',
- tr: 'Turkish'
- },
-
- render_language: function (value) {
- if (!value) {
- return PVE.Utils.defaultText + ' (English)';
- }
- var text = PVE.Utils.language_map[value];
- if (text) {
- return text + ' (' + value + ')';
- }
- return value;
- },
-
- language_array: function() {
- var data = [['', PVE.Utils.render_language('')]];
- Ext.Object.each(PVE.Utils.language_map, function(key, value) {
- data.push([key, PVE.Utils.render_language(value)]);
- });
-
- return data;
- },
-
- render_kvm_vga_driver: function (value) {
- if (!value) {
- return PVE.Utils.defaultText;
- }
- var text = PVE.Utils.kvm_vga_drivers[value];
- if (text) {
- return text + ' (' + value + ')';
- }
- return value;
- },
-
- kvm_vga_driver_array: function() {
- var data = [['', PVE.Utils.render_kvm_vga_driver('')]];
- Ext.Object.each(PVE.Utils.kvm_vga_drivers, function(key, value) {
- data.push([key, PVE.Utils.render_kvm_vga_driver(value)]);
- });
-
- return data;
- },
-
- render_kvm_startup: function(value) {
- var startup = PVE.Parser.parseStartup(value);
-
- var res = 'order=';
- if (startup.order === undefined) {
- res += 'any';
- } else {
- res += startup.order;
- }
- if (startup.up !== undefined) {
- res += ',up=' + startup.up;
- }
- if (startup.down !== undefined) {
- res += ',down=' + startup.down;
- }
-
- return res;
- },
-
- authOK: function() {
- return Ext.util.Cookies.get('PVEAuthCookie');
- },
-
- authClear: function() {
- Ext.util.Cookies.clear("PVEAuthCookie");
- },
-
- // fixme: remove - not needed?
- gridLineHeigh: function() {
- return 21;
-
- //if (Ext.isGecko)
- //return 23;
- //return 21;
- },
-
- extractRequestError: function(result, verbose) {
- var msg = gettext('Successful');
-
- if (!result.success) {
- msg = gettext("Unknown error");
- if (result.message) {
- msg = result.message;
- if (result.status) {
- msg += ' (' + result.status + ')';
- }
- }
- if (verbose && Ext.isObject(result.errors)) {
- msg += "<br>";
- Ext.Object.each(result.errors, function(prop, desc) {
- msg += "<br><b>" + Ext.htmlEncode(prop) + "</b>: " +
- Ext.htmlEncode(desc);
- });
- }
- }
-
- return msg;
- },
-
- extractFormActionError: function(action) {
- var msg;
- switch (action.failureType) {
- case Ext.form.action.Action.CLIENT_INVALID:
- msg = gettext('Form fields may not be submitted with invalid values');
- break;
- case Ext.form.action.Action.CONNECT_FAILURE:
- msg = gettext('Connection error');
- var resp = action.response;
- if (resp.status && resp.statusText) {
- msg += " " + resp.status + ": " + resp.statusText;
- }
- break;
- case Ext.form.action.Action.LOAD_FAILURE:
- case Ext.form.action.Action.SERVER_INVALID:
- msg = PVE.Utils.extractRequestError(action.result, true);
- break;
- }
- return msg;
- },
-
- // Ext.Ajax.request
- API2Request: function(reqOpts) {
-
- var newopts = Ext.apply({
- waitMsg: gettext('Please wait...')
- }, reqOpts);
-
- if (!newopts.url.match(/^\/api2/)) {
- newopts.url = '/api2/extjs' + newopts.url;
- }
- delete newopts.callback;
-
- var createWrapper = function(successFn, callbackFn, failureFn) {
- Ext.apply(newopts, {
- success: function(response, options) {
- if (options.waitMsgTarget) {
- if (PVE.Utils.toolkit === 'touch') {
- options.waitMsgTarget.setMasked(false);
- } else {
- options.waitMsgTarget.setLoading(false);
- }
- }
- var result = Ext.decode(response.responseText);
- response.result = result;
- if (!result.success) {
- response.htmlStatus = PVE.Utils.extractRequestError(result, true);
- Ext.callback(callbackFn, options.scope, [options, false, response]);
- Ext.callback(failureFn, options.scope, [response, options]);
- return;
- }
- Ext.callback(callbackFn, options.scope, [options, true, response]);
- Ext.callback(successFn, options.scope, [response, options]);
- },
- failure: function(response, options) {
- if (options.waitMsgTarget) {
- if (PVE.Utils.toolkit === 'touch') {
- options.waitMsgTarget.setMasked(false);
- } else {
- options.waitMsgTarget.setLoading(false);
- }
- }
- response.result = {};
- try {
- response.result = Ext.decode(response.responseText);
- } catch(e) {}
- var msg = gettext('Connection error') + ' - server offline?';
- if (response.aborted) {
- msg = gettext('Connection error') + ' - aborted.';
- } else if (response.timedout) {
- msg = gettext('Connection error') + ' - Timeout.';
- } else if (response.status && response.statusText) {
- msg = gettext('Connection error') + ' ' + response.status + ': ' + response.statusText;
- }
- response.htmlStatus = msg;
- Ext.callback(callbackFn, options.scope, [options, false, response]);
- Ext.callback(failureFn, options.scope, [response, options]);
- }
- });
- };
-
- createWrapper(reqOpts.success, reqOpts.callback, reqOpts.failure);
-
- var target = newopts.waitMsgTarget;
- if (target) {
- if (PVE.Utils.toolkit === 'touch') {
- target.setMasked({ xtype: 'loadmask', message: newopts.waitMsg} );
- } else {
- // Note: ExtJS bug - this does not work when component is not rendered
- target.setLoading(newopts.waitMsg);
- }
- }
- Ext.Ajax.request(newopts);
- },
-
- assemble_field_data: function(values, data) {
- if (Ext.isObject(data)) {
- Ext.Object.each(data, function(name, val) {
- if (values.hasOwnProperty(name)) {
- var bucket = values[name];
- if (!Ext.isArray(bucket)) {
- bucket = values[name] = [bucket];
- }
- if (Ext.isArray(val)) {
- values[name] = bucket.concat(val);
- } else {
- bucket.push(val);
- }
- } else {
- values[name] = val;
- }
- });
- }
- },
-
- checked_command: function(orig_cmd) {
- PVE.Utils.API2Request({
- url: '/nodes/localhost/subscription',
- method: 'GET',
- //waitMsgTarget: me,
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, opts) {
- var data = response.result.data;
-
- if (data.status !== 'Active') {
- Ext.Msg.show({
- title: gettext('No valid subscription'),
- icon: Ext.Msg.WARNING,
- msg: PVE.Utils.noSubKeyHtml,
- buttons: Ext.Msg.OK,
- callback: function(btn) {
- if (btn !== 'ok') {
- return;
- }
- orig_cmd();
- }
- });
- } else {
- orig_cmd();
- }
- }
- });
- },
-
- task_desc_table: {
- vncproxy: [ 'VM/CT', gettext('Console') ],
- spiceproxy: [ 'VM/CT', gettext('Console') + ' (Spice)' ],
- vncshell: [ '', gettext('Shell') ],
- spiceshell: [ '', gettext('Shell') + ' (Spice)' ],
- qmsnapshot: [ 'VM', gettext('Snapshot') ],
- qmrollback: [ 'VM', gettext('Rollback') ],
- qmdelsnapshot: [ 'VM', gettext('Delete Snapshot') ],
- qmcreate: [ 'VM', gettext('Create') ],
- qmrestore: [ 'VM', gettext('Restore') ],
- qmdestroy: [ 'VM', gettext('Destroy') ],
- qmigrate: [ 'VM', gettext('Migrate') ],
- qmclone: [ 'VM', gettext('Clone') ],
- qmmove: [ 'VM', gettext('Move disk') ],
- qmtemplate: [ 'VM', gettext('Convert to template') ],
- qmstart: [ 'VM', gettext('Start') ],
- qmstop: [ 'VM', gettext('Stop') ],
- qmreset: [ 'VM', gettext('Reset') ],
- qmshutdown: [ 'VM', gettext('Shutdown') ],
- qmsuspend: [ 'VM', gettext('Suspend') ],
- qmresume: [ 'VM', gettext('Resume') ],
- qmconfig: [ 'VM', gettext('Configure') ],
- vzcreate: ['CT', gettext('Create') ],
- vzrestore: ['CT', gettext('Restore') ],
- vzdestroy: ['CT', gettext('Destroy') ],
- vzmigrate: [ 'CT', gettext('Migrate') ],
- vzstart: ['CT', gettext('Start') ],
- vzstop: ['CT', gettext('Stop') ],
- vzmount: ['CT', gettext('Mount') ],
- vzumount: ['CT', gettext('Unmount') ],
- vzshutdown: ['CT', gettext('Shutdown') ],
- vzsuspend: [ 'CT', gettext('Suspend') ],
- vzresume: [ 'CT', gettext('Resume') ],
- hamigrate: [ 'HA', gettext('Migrate') ],
- hastart: [ 'HA', gettext('Start') ],
- hastop: [ 'HA', gettext('Stop') ],
- srvstart: ['SRV', gettext('Start') ],
- srvstop: ['SRV', gettext('Stop') ],
- srvrestart: ['SRV', gettext('Restart') ],
- srvreload: ['SRV', gettext('Reload') ],
- cephcreatemon: ['Ceph Monitor', gettext('Create') ],
- cephdestroymon: ['Ceph Monitor', gettext('Destroy') ],
- cephcreateosd: ['Ceph OSD', gettext('Create') ],
- cephdestroyosd: ['Ceph OSD', gettext('Destroy') ],
- imgcopy: ['', gettext('Copy data') ],
- imgdel: ['', gettext('Erase data') ],
- download: ['', gettext('Download') ],
- vzdump: ['', gettext('Backup') ],
- aptupdate: ['', gettext('Update package database') ],
- startall: [ '', gettext('Start all VMs and Containers') ],
- stopall: [ '', gettext('Stop all VMs and Containers') ],
- migrateall: [ '', gettext('Migrate all VMs and Containers') ]
- },
-
- format_task_description: function(type, id) {
- var farray = PVE.Utils.task_desc_table[type];
- if (!farray) {
- return type;
- }
- var prefix = farray[0];
- var text = farray[1];
- if (prefix) {
- return prefix + ' ' + id + ' - ' + text;
- }
- return text;
- },
-
- parse_task_upid: function(upid) {
- var task = {};
-
- var res = upid.match(/^UPID:(\S+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8,9}):([0-9A-Fa-f]{8}):([^:\s]+):([^:\s]*):([^:\s]+):$/);
- if (!res) {
- throw "unable to parse upid '" + upid + "'";
- }
- task.node = res[1];
- task.pid = parseInt(res[2], 16);
- task.pstart = parseInt(res[3], 16);
- task.starttime = parseInt(res[4], 16);
- task.type = res[5];
- task.id = res[6];
- task.user = res[7];
-
- task.desc = PVE.Utils.format_task_description(task.type, task.id);
-
- return task;
- },
-
- format_size: function(size) {
- /*jslint confusion: true */
-
- if (size < 1024) {
- return size;
- }
-
- var kb = size / 1024;
-
- if (kb < 1024) {
- return kb.toFixed(0) + "KB";
- }
-
- var mb = size / (1024*1024);
-
- if (mb < 1024) {
- return mb.toFixed(0) + "MB";
- }
-
- var gb = mb / 1024;
-
- if (gb < 1024) {
- return gb.toFixed(2) + "GB";
- }
-
- var tb = gb / 1024;
-
- return tb.toFixed(2) + "TB";
-
- },
-
- format_html_bar: function(per, text) {
-
- return "<div class='pve-bar-wrap'>" + text + "<div class='pve-bar-border'>" +
- "<div class='pve-bar-inner' style='width:" + per + "%;'></div>" +
- "</div></div>";
-
- },
-
- format_cpu_bar: function(per1, per2, text) {
-
- return "<div class='pve-bar-border'>" +
- "<div class='pve-bar-inner' style='width:" + per1 + "%;'></div>" +
- "<div class='pve-bar-inner2' style='width:" + per2 + "%;'></div>" +
- "<div class='pve-bar-text'>" + text + "</div>" +
- "</div>";
- },
-
- format_large_bar: function(per, text) {
-
- if (!text) {
- text = per.toFixed(1) + "%";
- }
-
- return "<div class='pve-largebar-border'>" +
- "<div class='pve-largebar-inner' style='width:" + per + "%;'></div>" +
- "<div class='pve-largebar-text'>" + text + "</div>" +
- "</div>";
- },
-
- format_duration_long: function(ut) {
-
- var days = Math.floor(ut / 86400);
- ut -= days*86400;
- var hours = Math.floor(ut / 3600);
- ut -= hours*3600;
- var mins = Math.floor(ut / 60);
- ut -= mins*60;
-
- var hours_str = '00' + hours.toString();
- hours_str = hours_str.substr(hours_str.length - 2);
- var mins_str = "00" + mins.toString();
- mins_str = mins_str.substr(mins_str.length - 2);
- var ut_str = "00" + ut.toString();
- ut_str = ut_str.substr(ut_str.length - 2);
-
- if (days) {
- var ds = days > 1 ? PVE.Utils.daysText : PVE.Utils.dayText;
- return days.toString() + ' ' + ds + ' ' +
- hours_str + ':' + mins_str + ':' + ut_str;
- } else {
- return hours_str + ':' + mins_str + ':' + ut_str;
- }
- },
-
- format_duration_short: function(ut) {
-
- if (ut < 60) {
- return ut.toString() + 's';
- }
-
- if (ut < 3600) {
- var mins = ut / 60;
- return mins.toFixed(0) + 'm';
- }
-
- if (ut < 86400) {
- var hours = ut / 3600;
- return hours.toFixed(0) + 'h';
- }
-
- var days = ut / 86400;
- return days.toFixed(0) + 'd';
- },
-
- yesText: gettext('Yes'),
- noText: gettext('No'),
- noneText: gettext('none'),
- errorText: gettext('Error'),
- unknownText: gettext('Unknown'),
- defaultText: gettext('Default'),
- daysText: gettext('days'),
- dayText: gettext('day'),
- runningText: gettext('running'),
- stoppedText: gettext('stopped'),
- neverText: gettext('never'),
- totalText: gettext('Total'),
- usedText: gettext('Used'),
- directoryText: gettext('Directory'),
- imagesText: gettext('Disk image'),
- backupFileText: gettext('VZDump backup file'),
- vztmplText: gettext('Container template'),
- isoImageText: gettext('ISO image'),
- containersText: gettext('Container'),
-
- format_expire: function(date) {
- if (!date) {
- return PVE.Utils.neverText;
- }
- return Ext.Date.format(date, "Y-m-d");
- },
-
- format_storage_type: function(value) {
- if (value === 'dir') {
- return PVE.Utils.directoryText;
- } else if (value === 'nfs') {
- return 'NFS';
- } else if (value === 'glusterfs') {
- return 'GlusterFS';
- } else if (value === 'lvm') {
- return 'LVM';
- } else if (value === 'iscsi') {
- return 'iSCSI';
- } else if (value === 'rbd') {
- return 'RBD';
- } else if (value === 'sheepdog') {
- return 'Sheepdog';
- } else if (value === 'zfs') {
- return 'ZFS over iSCSI';
- } else if (value === 'zfspool') {
- return 'ZFS';
- } else if (value === 'iscsidirect') {
- return 'iSCSIDirect';
- } else {
- return PVE.Utils.unknownText;
- }
- },
-
- format_boolean_with_default: function(value) {
- if (Ext.isDefined(value) && value !== '') {
- return value ? PVE.Utils.yesText : PVE.Utils.noText;
- }
- return PVE.Utils.defaultText;
- },
-
- format_boolean: function(value) {
- return value ? PVE.Utils.yesText : PVE.Utils.noText;
- },
-
- format_neg_boolean: function(value) {
- return !value ? PVE.Utils.yesText : PVE.Utils.noText;
- },
-
- format_content_types: function(value) {
- var cta = [];
-
- Ext.each(value.split(',').sort(), function(ct) {
- if (ct === 'images') {
- cta.push(PVE.Utils.imagesText);
- } else if (ct === 'backup') {
- cta.push(PVE.Utils.backupFileText);
- } else if (ct === 'vztmpl') {
- cta.push(PVE.Utils.vztmplText);
- } else if (ct === 'iso') {
- cta.push(PVE.Utils.isoImageText);
- } else if (ct === 'rootdir') {
- cta.push(PVE.Utils.containersText);
- }
- });
-
- return cta.join(', ');
- },
-
- render_storage_content: function(value, metaData, record) {
- var data = record.data;
- if (Ext.isNumber(data.channel) &&
- Ext.isNumber(data.id) &&
- Ext.isNumber(data.lun)) {
- return "CH " +
- Ext.String.leftPad(data.channel,2, '0') +
- " ID " + data.id + " LUN " + data.lun;
- }
- return data.volid.replace(/^.*:(.*\/)?/,'');
- },
-
- render_serverity: function (value) {
- return PVE.Utils.log_severity_hash[value] || value;
- },
-
- render_cpu: function(value, metaData, record, rowIndex, colIndex, store) {
-
- if (!(record.data.uptime && Ext.isNumeric(value))) {
- return '';
- }
-
- var maxcpu = record.data.maxcpu || 1;
-
- if (!Ext.isNumeric(maxcpu) && (maxcpu >= 1)) {
- return '';
- }
-
- var per = value * 100;
-
- return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU');
- },
-
- render_size: function(value, metaData, record, rowIndex, colIndex, store) {
- /*jslint confusion: true */
-
- if (!Ext.isNumeric(value)) {
- return '';
- }
-
- return PVE.Utils.format_size(value);
- },
-
- render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) {
- var servertime = new Date(value * 1000);
- return Ext.Date.format(servertime, 'Y-m-d H:i:s');
- },
-
- render_mem_usage: function(value, metaData, record, rowIndex, colIndex, store) {
-
- var mem = value;
- var maxmem = record.data.maxmem;
-
- if (!record.data.uptime) {
- return '';
- }
-
- if (!(Ext.isNumeric(mem) && maxmem)) {
- return '';
- }
-
- var per = (mem * 100) / maxmem;
-
- return per.toFixed(1) + '%';
- },
-
- render_disk_usage: function(value, metaData, record, rowIndex, colIndex, store) {
-
- var disk = value;
- var maxdisk = record.data.maxdisk;
-
- if (!(Ext.isNumeric(disk) && maxdisk)) {
- return '';
- }
-
- var per = (disk * 100) / maxdisk;
-
- return per.toFixed(1) + '%';
- },
-
- render_resource_type: function(value, metaData, record, rowIndex, colIndex, store) {
-
- var cls = 'pve-itype-icon-' + value;
-
- if (record.data.running) {
- metaData.tdCls = cls + "-running";
- } else if (record.data.template) {
- metaData.tdCls = cls + "-template";
- } else {
- metaData.tdCls = cls;
- }
-
- return value;
- },
-
- render_uptime: function(value, metaData, record, rowIndex, colIndex, store) {
-
- var uptime = value;
-
- if (uptime === undefined) {
- return '';
- }
-
- if (uptime <= 0) {
- return '-';
- }
-
- return PVE.Utils.format_duration_long(uptime);
- },
-
- render_support_level: function(value, metaData, record) {
- return PVE.Utils.support_level_hash[value] || '-';
- },
-
- render_upid: function(value, metaData, record) {
- var type = record.data.type;
- var id = record.data.id;
-
- return PVE.Utils.format_task_description(type, id);
- },
-
- dialog_title: function(subject, create, isAdd) {
- if (create) {
- if (isAdd) {
- return gettext('Add') + ': ' + subject;
- } else {
- return gettext('Create') + ': ' + subject;
- }
- } else {
- return gettext('Edit') + ': ' + subject;
- }
- },
-
- windowHostname: function() {
- return window.location.hostname.replace(IP6_bracket_match,
- function(m, addr, offset, original) { return addr; });
- },
-
- openDefaultConsoleWindow: function(allowSpice, vmtype, vmid, nodename, vmname) {
- var dv = PVE.Utils.defaultViewer(allowSpice);
- PVE.Utils.openConsoleWindow(dv, vmtype, vmid, nodename, vmname);
- },
-
- openConsoleWindow: function(viewer, vmtype, vmid, nodename, vmname) {
- // kvm, lxc, shell, upgrade
-
- if (vmid == undefined && (vmtype === 'kvm' || vmtype === 'lxc')) {
- throw "missing vmid";
- }
-
- if (!nodename) {
- throw "no nodename specified";
- }
-
- if (viewer === 'applet' || viewer === 'html5') {
- PVE.Utils.openVNCViewer(vmtype, vmid, nodename, vmname, viewer === 'html5');
- } else if (viewer === 'vv') {
- var url;
- var params = { proxy: PVE.Utils.windowHostname() };
- if (vmtype === 'kvm') {
- url = '/nodes/' + nodename + '/qemu/' + vmid.toString() + '/spiceproxy';
- PVE.Utils.openSpiceViewer(url, params);
- } else if (vmtype === 'lxc') {
- url = '/nodes/' + nodename + '/lxc/' + vmid.toString() + '/spiceproxy';
- PVE.Utils.openSpiceViewer(url, params);
- } else if (vmtype === 'shell') {
- url = '/nodes/' + nodename + '/spiceshell';
- PVE.Utils.openSpiceViewer(url, params);
- } else if (vmtype === 'upgrade') {
- url = '/nodes/' + nodename + '/spiceshell';
- params.upgrade = 1;
- PVE.Utils.openSpiceViewer(url, params);
- }
- } else {
- throw "unknown viewer type";
- }
- },
-
- defaultViewer: function(allowSpice) {
- var vncdefault = 'html5';
- var dv = PVE.VersionInfo.console || vncdefault;
- if (dv === 'vv' && !allowSpice) {
- dv = vncdefault;
- }
-
- return dv;
- },
-
- openVNCViewer: function(vmtype, vmid, nodename, vmname, novnc) {
- var url = Ext.urlEncode({
- console: vmtype, // kvm, lxc, upgrade or shell
- novnc: novnc ? 1 : 0,
- vmid: vmid,
- vmname: vmname,
- node: nodename
- });
- var nw = window.open("?" + url, '_blank', "innerWidth=745,innerheight=427");
- nw.focus();
- },
-
- openSpiceViewer: function(url, params){
-
- var downloadWithName = function(uri, name) {
- var link = Ext.DomHelper.append(document.body, {
- tag: 'a',
- href: uri,
- css : 'display:none;visibility:hidden;height:0px;'
- });
-
- // Note: we need to tell android the correct file name extension
- // but we do not set 'download' tag for other environments, because
- // It can have strange side effects (additional user prompt on firefox)
- var andriod = navigator.userAgent.match(/Android/i) ? true : false;
- if (andriod) {
- link.download = name;
- }
-
- if (link.fireEvent) {
- link.fireEvent('onclick');
- } else {
- var evt = document.createEvent("MouseEvents");
- evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
- link.dispatchEvent(evt);
- }
- };
-
- PVE.Utils.API2Request({
- url: url,
- params: params,
- method: 'POST',
- failure: function(response, opts){
- Ext.Msg.alert('Error', response.htmlStatus);
- },
- success: function(response, opts){
- var raw = "[virt-viewer]\n";
- Ext.Object.each(response.result.data, function(k, v) {
- raw += k + "=" + v + "\n";
- });
- var url = 'data:application/x-virt-viewer;charset=UTF-8,' +
- encodeURIComponent(raw);
-
- downloadWithName(url, "pve-spice.vv");
- }
- });
- },
-
- // comp.setLoading() is buggy in ExtJS 4.0.7, so we
- // use el.mask() instead
- setErrorMask: function(comp, msg) {
- var el = comp.el;
- if (!el) {
- return;
- }
- if (!msg) {
- el.unmask();
- } else {
- if (msg === true) {
- el.mask(gettext("Loading..."));
- } else {
- el.mask(msg);
- }
- }
- },
-
- monStoreErrors: function(me, store) {
- me.mon(store, 'beforeload', function(s, operation, eOpts) {
- if (!me.loadCount) {
- me.loadCount = 0; // make sure it is numeric
- PVE.Utils.setErrorMask(me, true);
- }
- });
-
- // only works with 'pve' proxy
- me.mon(store.proxy, 'afterload', function(proxy, request, success) {
- me.loadCount++;
-
- if (success) {
- PVE.Utils.setErrorMask(me, false);
- return;
- }
-
- var msg;
- var operation = request.operation;
- var error = operation.getError();
- if (error.statusText) {
- msg = error.statusText + ' (' + error.status + ')';
- } else {
- msg = gettext('Connection error');
- }
- PVE.Utils.setErrorMask(me, msg);
- });
- }
-
-}});
-
+++ /dev/null
-Ext.define('PVE.noVncConsole', {
- extend: 'Ext.panel.Panel',
- alias: 'widget.pveNoVncConsole',
-
- nodename: undefined,
-
- vmid: undefined,
-
- consoleType: undefined, // lxc or kvm
-
- initComponent : function() {
- var me = this;
-
- if (!me.nodename) {
- throw "no node name specified";
- }
-
- if (!me.consoleType) {
- throw "no console type specified";
- }
-
- if (!me.vmid && me.consoleType !== 'shell') {
- throw "no VM ID specified";
- }
-
- // always use same iframe, to avoid running several noVnc clients
- // at same time (to avoid performance problems)
- var box = Ext.create('widget.uxiframe', { id: "vncconsole" });
-
- Ext.apply(me, {
- layout: { type: 'fit' },
- border: false,
- items: box,
- listeners: {
- show: function() {
- var url = '/?console=' + me.consoleType + '&novnc=1&node=' + me.nodename + '&resize=scale';
- if (me.vmid) {
- url += '&vmid='+ me.vmid;
- }
- box.load(url);
- }
- }
- });
-
- me.callParent();
- }
-});
-
-PVE_vnc_console_event = function(appletid, action, err) {
- //console.log("TESTINIT param1 " + appletid + " action " + action);
-
- if (action === "error") {
- var compid = appletid.replace("-vncapp", "");
- var comp = Ext.getCmp(compid);
-
- if (!comp || !comp.vmid || !comp.toplevel) {
- return;
- }
-
- comp.detectMigratedVM();
- }
-
- return;
- };
-
-Ext.define('PVE.VNCConsole', {
- extend: 'Ext.panel.Panel',
- alias: ['widget.pveVNCConsole'],
-
- novnc: false,
-
- last_novnc_state: undefined,
- last_novnc_msg: undefined,
-
- detectMigratedVM: function() {
- var me = this;
-
- if (!me.vmid) {
- return;
- }
-
- // try to detect migrated VM
- PVE.Utils.API2Request({
- url: '/cluster/resources',
- method: 'GET',
- success: function(response) {
- var list = response.result.data;
- Ext.Array.each(list, function(item) {
- if (item.type === 'qemu' && item.vmid == me.vmid) {
- if (item.node !== me.nodename) {
- me.nodename = item.node;
- me.url = "/nodes/" + me.nodename + "/" + item.type + "/" + me.vmid + "/vncproxy";
- me.wsurl = "/nodes/" + me.nodename + "/" + item.type + "/" + me.vmid + "/vncwebsocket";
- me.reloadApplet();
- }
- return false; // break
- }
- });
- }
- });
- },
-
- initComponent : function() {
- var me = this;
-
- if (!me.url) {
- throw "no url specified";
- }
-
- var myid = me.id + "-vncapp";
-
- me.appletID = myid;
-
- var box;
-
- if (me.novnc) {
- if (!me.wsurl) {
- throw "no web socket url specified";
- }
- box = Ext.create('widget.uxiframe', { id: myid });
- } else {
- box = Ext.create('Ext.Component', { border: false, html: "" });
- }
-
- var resize_window = function() {
- //console.log("resize");
-
- var aw;
- var ah;
- var applet;
-
- if (me.novnc) {
- var novnciframe = box.getFrame();
- // noVNC_canvas
- var innerDoc = novnciframe.contentDocument || novnciframe.contentWindow.document;
- aw = innerDoc.getElementById('noVNC_canvas').width;
- ah = innerDoc.getElementById('noVNC_canvas').height + 8;
-
- var novnc_state = innerDoc.getElementById('noVNC_status_state').innerHTML;
- var novnc_msg = innerDoc.getElementById('noVNC_status_msg').innerHTML;
-
- if (novnc_state !== me.last_novnc_state || novnc_msg !== me.last_novnc_msg) {
- me.last_novnc_state = novnc_state;
- me.last_novnc_msg = novnc_msg;
-
- if (novnc_state !== 'normal') {
- PVE.Utils.setErrorMask(box, novnc_msg || 'unknown');
- } else {
- PVE.Utils.setErrorMask(box); // clear mask
- }
-
- if (novnc_state === 'disconnected') {
- me.detectMigratedVM();
- }
- }
-
- } else {
- applet = Ext.getDom(myid);
-
- // try again when dom element is available
- if (!(applet && Ext.isFunction(applet.getPreferredSize))) {
- return Ext.Function.defer(resize_window, 1000);
- }
-
- var ps = applet.getPreferredSize();
- aw = ps.width;
- ah = ps.height;
- }
-
- if (aw < 640) { aw = 640; }
- if (ah < 400) { ah = 400; }
-
- var tbar = me.getDockedItems("[dock=top]")[0];
- var tbh = tbar ? tbar.getHeight() : 0;
-
- var oh;
- var ow;
-
- //console.log("size0 " + aw + " " + ah + " tbh " + tbh);
-
- if (window.innerHeight) {
- oh = window.innerHeight;
- ow = window.innerWidth;
- } else if (document.documentElement &&
- document.documentElement.clientHeight) {
- oh = document.documentElement.clientHeight;
- ow = document.documentElement.clientWidth;
- } else if (document.body) {
- oh = document.body.clientHeight;
- ow = document.body.clientWidth;
- } else {
- throw "can't get window size";
- }
-
- if (!me.novnc) {
- Ext.fly(applet).setSize(aw, ah + tbh);
- }
-
- var offsetw = aw - ow;
- var offseth = ah + tbh - oh;
-
- if (offsetw !== 0 || offseth !== 0) {
- //console.log("try resize by " + offsetw + " " + offseth);
- try { window.resizeBy(offsetw, offseth); } catch (e) {}
- }
-
- Ext.Function.defer(resize_window, 1000);
- };
-
- var resize_box = function() {
- if (me.novnc) {
- throw "implement me";
- } else {
- var applet = Ext.getDom(myid);
-
- if ((applet && Ext.isFunction(applet.getPreferredSize))) {
- var ps = applet.getPreferredSize();
- Ext.fly(applet).setSize(ps.width, ps.height);
- }
- }
-
- Ext.Function.defer(resize_box, 1000);
- };
-
- var start_vnc_viewer = function(param) {
-
- if (me.novnc) {
-
- var pveparams = Ext.urlEncode({
- port: param.port,
- vncticket: param.ticket
- });
-
- var urlparams = Ext.urlEncode({
- encrypt: 1,
- path: "api2/json" + me.wsurl + "?" + pveparams,
- password: param.ticket
- });
- box.load('/novnc/vnc_pve.html?' + urlparams);
-
- } else {
-
- var cert = param.cert;
- cert = cert.replace(/\n/g, "|");
-
- box.update({
- id: myid,
- border: false,
- tag: 'applet',
- code: 'com.tigervnc.vncviewer.VncViewer',
- archive: '/vncterm/VncViewer.jar',
- // NOTE: set size to '100%' - else resize does not work
- width: "100%",
- height: "100%",
- cn: [
- {tag: 'param', name: 'id', value: myid},
- {tag: 'param', name: 'PORT', value: param.port},
- {tag: 'param', name: 'PASSWORD', value: param.ticket},
- {tag: 'param', name: 'USERNAME', value: param.user},
- {tag: 'param', name: 'Show Controls', value: 'No'},
- {tag: 'param', name: 'Offer Relogin', value: 'No'},
- {tag: 'param', name: 'PVECert', value: cert}
- ]
- });
- }
-
- if (me.toplevel) {
- Ext.Function.defer(resize_window, 1000);
- } else {
- Ext.Function.defer(resize_box, 1000);
- }
- };
-
- Ext.apply(me, {
- layout: 'fit',
- border: false,
- autoScroll: me.toplevel ? false : true,
- items: box,
- reloadApplet: function() {
- var params = Ext.apply({}, me.params);
- if (me.novnc) {
- params.websocket = 1;
- }
- PVE.Utils.API2Request({
- url: me.url,
- params: params,
- method: me.method || 'POST',
- failure: function(response, opts) {
- box.update(gettext('Error') + ' ' + response.htmlStatus);
- },
- success: function(response, opts) {
- start_vnc_viewer(response.result.data);
- }
- });
- }
- });
-
- me.callParent();
-
- if (me.toplevel) {
- me.on("render", me.reloadApplet);
- } else {
- me.on("show", me.reloadApplet);
- me.on("hide", function() { box.update(""); });
- }
- }
-});
-
-Ext.define('PVE.KVMConsole', {
- extend: 'PVE.VNCConsole',
- alias: ['widget.pveKVMConsole'],
-
- initComponent : function() {
- var me = this;
-
- if (!me.nodename) {
- throw "no node name specified";
- }
-
- if (!me.vmid) {
- throw "no VM ID specified";
- }
-
- var baseUrl = "/nodes/" + me.nodename + "/qemu/" + me.vmid;
-
- var vm_command = function(cmd, params, reload_applet) {
- PVE.Utils.API2Request({
- params: params,
- url: baseUrl + "/status/" + cmd,
- method: 'POST',
- waitMsgTarget: me,
- failure: function(response, opts) {
- Ext.Msg.alert('Error', response.htmlStatus);
- },
- success: function() {
- if (reload_applet) {
- Ext.Function.defer(me.reloadApplet, 1000, me);
- }
- }
- });
- };
-
- var tbar = [
- {
- text: gettext('Start'),
- handler: function() {
- vm_command("start", {}, 1);
- }
- },
- {
- text: gettext('Shutdown'),
- handler: function() {
- var msg = Ext.String.format(gettext("Do you really want to shutdown VM {0}?"), me.vmid);
- Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
- if (btn !== 'yes') {
- return;
- }
- vm_command('shutdown');
- });
- }
- },
- {
- text: gettext('Stop'),
- handler: function() {
- var msg = Ext.String.format(gettext("Do you really want to stop VM {0}?"), me.vmid);
- Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
- if (btn !== 'yes') {
- return;
- }
- vm_command("stop");
- });
- }
- },
- {
- xtype: 'pveQemuSendKeyMenu',
- nodename: me.nodename,
- vmid: me.vmid
- },
- {
- text: gettext('Reset'),
- handler: function() {
- var msg = Ext.String.format(gettext("Do you really want to reset VM {0}?"), me.vmid);
- Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
- if (btn !== 'yes') {
- return;
- }
- vm_command("reset");
- });
- }
- },
- {
- text: gettext('Suspend'),
- handler: function() {
- var msg = Ext.String.format(gettext("Do you really want to suspend VM {0}?"), me.vmid);
- Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
- if (btn !== 'yes') {
- return;
- }
- vm_command("suspend");
- });
- }
- },
- {
- text: gettext('Resume'),
- handler: function() {
- vm_command("resume");
- }
- },
- // Note: no migrate here, because we can't display migrate log
- {
- text: gettext('Console'),
- handler: function() {
- PVE.Utils.openVNCViewer('kvm', me.vmid, me.nodename, me.vmname, me.novnc);
- }
- },
- '->',
- {
- text: gettext('Refresh'),
- hidden: me.novnc ? true : false,
- handler: function() {
- var applet = Ext.getDom(me.appletID);
- applet.sendRefreshRequest();
- }
- },
- {
- text: gettext('Reload'),
- handler: function () {
- me.reloadApplet();
- }
- }
- ];
-
-
- Ext.apply(me, {
- tbar: tbar,
- url: baseUrl + "/vncproxy",
- wsurl: baseUrl + "/vncwebsocket"
- });
-
- me.callParent();
- }
-});
-
-Ext.define('PVE.LxcConsole', {
- extend: 'PVE.VNCConsole',
- alias: ['widget.pveLxcConsole'],
-
- initComponent : function() {
- var me = this;
-
- if (!me.nodename) {
- throw "no node name specified";
- }
-
- if (!me.vmid) {
- throw "no VM ID specified";
- }
-
- var baseUrl = "/nodes/" + me.nodename + "/lxc/" + me.vmid;
-
- var vm_command = function(cmd, params, reload_applet) {
- PVE.Utils.API2Request({
- params: params,
- url: baseUrl + "/status/" + cmd,
- waitMsgTarget: me,
- method: 'POST',
- failure: function(response, opts) {
- Ext.Msg.alert('Error', response.htmlStatus);
- },
- success: function() {
- if (reload_applet) {
- Ext.Function.defer(me.reloadApplet, 1000, me);
- }
- }
- });
- };
-
- var tbar = [
- {
- text: gettext('Start'),
- handler: function() {
- vm_command("start");
- }
- },
- {
- text: gettext('Shutdown'),
- handler: function() {
- var msg = Ext.String.format(gettext("Do you really want to shutdown VM {0}?"), me.vmid);
- Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
- if (btn !== 'yes') {
- return;
- }
- vm_command("shutdown");
- });
- }
- },
- {
- text: gettext('Stop'),
- handler: function() {
- var msg = Ext.String.format(gettext("Do you really want to stop VM {0}?"), me.vmid);
- Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
- if (btn !== 'yes') {
- return;
- }
- vm_command("stop");
- });
- }
- },
- // Note: no migrate here, because we can't display migrate log
- '->',
- {
- text: gettext('Refresh'),
- hidden: me.novnc ? true : false,
- handler: function() {
- var applet = Ext.getDom(me.appletID);
- applet.sendRefreshRequest();
- }
- },
- {
- text: gettext('Reload'),
- handler: function () {
- me.reloadApplet();
- }
- }
- ];
-
- Ext.apply(me, {
- tbar: tbar,
- url: baseUrl + "/vncproxy",
- wsurl: baseUrl + "/vncwebsocket"
- });
-
- me.callParent();
- }
-});
-
-Ext.define('PVE.Shell', {
- extend: 'PVE.VNCConsole',
- alias: ['widget.pveShell'],
-
- ugradeSystem: false, // set to true to run "apt-get dist-upgrade"
-
- initComponent : function() {
- var me = this;
-
- if (!me.nodename) {
- throw "no node name specified";
- }
-
- var tbar = [ '->' ];
-
- if (!me.novnc) {
- tbar.push({
- text: gettext('Refresh'),
- handler: function() {
- var applet = Ext.getDom(me.appletID);
- applet.sendRefreshRequest();
- }
- });
- }
-
- if (!me.ugradeSystem) {
- // we dont want to restart the upgrade script
- tbar.push({
- text: gettext('Reload'),
- handler: function () { me.reloadApplet(); }
- });
- }
-
- tbar.push({
- text: gettext('Shell'),
- handler: function() {
- PVE.Utils.openVNCViewer('shell', undefined, me.nodename, undefined, me.novnc);
- }
- });
-
- var baseUrl = "/nodes/" + me.nodename;
-
- Ext.apply(me, {
- tbar: tbar,
- url: baseUrl + "/vncshell",
- wsurl: baseUrl + "/vncwebsocket"
- });
-
- if (me.ugradeSystem) {
- me.params = { upgrade: 1 };
- }
-
- me.callParent();
- }
-});
+++ /dev/null
-/*
- * Workspace base class
- *
- * popup login window when auth fails (call onLogin handler)
- * update (re-login) ticket every 15 minutes
- *
- */
-
-Ext.define('PVE.Workspace', {
- extend: 'Ext.container.Viewport',
-
- title: 'Proxmox Virtual Environment',
-
- loginData: null, // Data from last login call
-
- onLogin: function(loginData) {},
-
- // private
- updateLoginData: function(loginData) {
- var me = this;
- me.loginData = loginData;
- PVE.CSRFPreventionToken = loginData.CSRFPreventionToken;
- PVE.UserName = loginData.username;
-
- if (loginData.cap) {
- Ext.state.Manager.set('GuiCap', loginData.cap);
- }
-
- // creates a session cookie (expire = null)
- // that way the cookie gets deleted after browser window close
- Ext.util.Cookies.set('PVEAuthCookie', loginData.ticket, null, '/', null, true);
- me.onLogin(loginData);
- },
-
- // private
- showLogin: function() {
- var me = this;
-
- PVE.Utils.authClear();
- PVE.UserName = null;
- me.loginData = null;
-
- if (!me.login) {
- me.login = Ext.create('PVE.window.LoginWindow', {
- handler: function(data) {
- me.login = null;
- me.updateLoginData(data);
- PVE.Utils.checked_command(function() {}); // display subscription status
- }
- });
- }
- me.onLogin(null);
- me.login.show();
- },
-
- initComponent : function() {
- var me = this;
-
- Ext.tip.QuickTipManager.init();
-
- // fixme: what about other errors
- Ext.Ajax.on('requestexception', function(conn, response, options) {
- if (response.status == 401) { // auth failure
- me.showLogin();
- }
- });
-
- document.title = me.title;
-
- me.callParent();
-
- if (!PVE.Utils.authOK()) {
- me.showLogin();
- } else {
- if (me.loginData) {
- me.onLogin(me.loginData);
- }
- }
-
- Ext.TaskManager.start({
- run: function() {
- var ticket = PVE.Utils.authOK();
- if (!ticket || !PVE.UserName) {
- return;
- }
-
- Ext.Ajax.request({
- params: {
- username: PVE.UserName,
- password: ticket
- },
- url: '/api2/json/access/ticket',
- method: 'POST',
- success: function(response, opts) {
- var obj = Ext.decode(response.responseText);
- me.updateLoginData(obj.data);
- }
- });
- },
- interval: 15*60*1000
- });
-
- }
-});
-
-Ext.define('PVE.ConsoleWorkspace', {
- extend: 'PVE.Workspace',
-
- alias: ['widget.pveConsoleWorkspace'],
-
- title: gettext('Console'),
-
- initComponent : function() {
- var me = this;
-
- var param = Ext.Object.fromQueryString(window.location.search);
- var consoleType = me.consoleType || param.console;
-
- param.novnc = (param.novnc === '1') ? true : false;
-
- var content;
- if (consoleType === 'kvm') {
- me.title = "VM " + param.vmid;
- if (param.vmname) {
- me.title += " ('" + param.vmname + "')";
- }
- content = {
- xtype: 'pveKVMConsole',
- novnc: param.novnc,
- vmid: param.vmid,
- nodename: param.node,
- vmname: param.vmname,
- toplevel: true
- };
- } else if (consoleType === 'lxc') {
- me.title = "CT " + param.vmid;
- if (param.vmname) {
- me.title += " ('" + param.vmname + "')";
- }
- content = {
- xtype: 'pveLxcConsole',
- novnc: param.novnc,
- vmid: param.vmid,
- nodename: param.node,
- vmname: param.vmname,
- toplevel: true
- };
- } else if (consoleType === 'shell') {
- me.title = "node '" + param.node + "'";
- content = {
- xtype: 'pveShell',
- novnc: param.novnc,
- nodename: param.node,
- toplevel: true
- };
- } else if (consoleType === 'upgrade') {
- me.title = Ext.String.format(gettext('System upgrade on node {0}'), "'" + param.node + "'");
- content = {
- xtype: 'pveShell',
- novnc: param.novnc,
- nodename: param.node,
- ugradeSystem: true,
- toplevel: true
- };
- } else {
- content = {
- border: false,
- bodyPadding: 10,
- html: gettext('Error') + ': No such console type'
- };
- }
-
- Ext.apply(me, {
- layout: { type: 'fit' },
- border: false,
- items: [ content ]
- });
-
- me.callParent();
- }
-});
-
-Ext.define('PVE.StdWorkspace', {
- extend: 'PVE.Workspace',
-
- alias: ['widget.pveStdWorkspace'],
-
- // private
- setContent: function(comp) {
- var me = this;
-
- var cont = me.child('#content');
- cont.removeAll(true);
-
- if (comp) {
- PVE.Utils.setErrorMask(cont, false);
- comp.border = false;
- cont.add(comp);
- cont.doLayout();
- }
- // else {
- // TODO: display something useful
-
- // Note:: error mask has wrong zindex, so we do not
- // use that - see bug 114
- // PVE.Utils.setErrorMask(cont, 'nothing selected');
- //}
- },
-
- selectById: function(nodeid) {
- var me = this;
- var tree = me.down('pveResourceTree');
- tree.selectById(nodeid);
- },
-
- checkVmMigration: function(record) {
- var me = this;
- var tree = me.down('pveResourceTree');
- tree.checkVmMigration(record);
- },
-
- onLogin: function(loginData) {
- var me = this;
-
- me.updateUserInfo();
-
- if (loginData) {
- PVE.data.ResourceStore.startUpdate();
-
- PVE.Utils.API2Request({
- url: '/version',
- method: 'GET',
- success: function(response) {
- PVE.VersionInfo = response.result.data;
- me.updateVersionInfo();
- }
- });
- }
- },
-
- updateUserInfo: function() {
- var me = this;
-
- var ui = me.query('#userinfo')[0];
-
- if (PVE.UserName) {
- var msg = Ext.String.format(gettext("You are logged in as {0}"), "'" + PVE.UserName + "'");
- ui.update('<div class="x-unselectable" style="white-space:nowrap;">' + msg + '</div>');
- } else {
- ui.update('');
- }
- ui.doLayout();
- },
-
- updateVersionInfo: function() {
- var me = this;
-
- var ui = me.query('#versioninfo')[0];
-
- if (PVE.VersionInfo) {
- var version = PVE.VersionInfo.version + '-' + PVE.VersionInfo.release + '/' +
- PVE.VersionInfo.repoid;
- ui.update('Proxmox Virtual Environment<br>' + gettext('Version') + ': ' + version);
- } else {
- ui.update('Proxmox Virtual Environment');
- }
- ui.doLayout();
- },
-
- initComponent : function() {
- var me = this;
-
- Ext.History.init();
-
- var sprovider = Ext.create('PVE.StateProvider');
- Ext.state.Manager.setProvider(sprovider);
-
- var selview = new PVE.form.ViewSelector({});
-
- var rtree = Ext.createWidget('pveResourceTree', {
- viewFilter: selview.getViewFilter(),
- flex: 1,
- selModel: new Ext.selection.TreeModel({
- listeners: {
- selectionchange: function(sm, selected) {
- var comp;
- var tlckup = {
- root: 'PVE.dc.Config',
- node: 'PVE.node.Config',
- qemu: 'PVE.qemu.Config',
- lxc: 'PVE.lxc.Config',
- storage: 'PVE.storage.Browser',
- pool: 'pvePoolConfig'
- };
-
- if (selected.length > 0) {
- var n = selected[0];
- comp = {
- xtype: tlckup[n.data.type || 'root'] ||
- 'pvePanelConfig',
- layout: { type: 'fit' },
- showSearch: (n.data.id === 'root') ||
- Ext.isDefined(n.data.groupbyid),
- pveSelNode: n,
- workspace: me,
- viewFilter: selview.getViewFilter()
- };
- PVE.curSelectedNode = n;
- }
-
- me.setContent(comp);
- }
- }
- })
- });
-
- selview.on('select', function(combo, records) {
- if (records && records.length) {
- var view = combo.getViewFilter();
- rtree.setViewFilter(view);
- }
- });
-
- var caps = sprovider.get('GuiCap');
-
- var createVM = Ext.createWidget('button', {
- pack: 'end',
- margin: '3 5 0 0',
- baseCls: 'x-btn',
- text: gettext("Create VM"),
- disabled: !caps.vms['VM.Allocate'],
- handler: function() {
- var wiz = Ext.create('PVE.qemu.CreateWizard', {});
- wiz.show();
- }
- });
-
- var createCT = Ext.createWidget('button', {
- pack: 'end',
- margin: '3 5 0 0',
- baseCls: 'x-btn',
- text: gettext("Create CT"),
- disabled: !caps.vms['VM.Allocate'],
- handler: function() {
- var wiz = Ext.create('PVE.lxc.CreateWizard', {});
- wiz.show();
- }
- });
-
- sprovider.on('statechange', function(sp, key, value) {
- if (key === 'GuiCap' && value) {
- caps = value;
- createVM.setDisabled(!caps.vms['VM.Allocate']);
- createCT.setDisabled(!caps.vms['VM.Allocate']);
- }
- });
-
- Ext.apply(me, {
- layout: { type: 'border' },
- border: false,
- items: [
- {
- region: 'north',
- height: 30,
- layout: {
- type: 'hbox',
- },
- baseCls: 'x-plain',
- defaults: {
- baseCls: 'x-plain'
- },
- border: false,
- margin: '2 0 5 0',
- items: [
- {
- margin: '0 0 0 4',
- html: '<a class="x-unselectable" target=_blank href="http://www.proxmox.com">' +
- '<img height=30 width=209 src="/pve2/images/proxmox_logo.png"/></a>'
- },
- {
- minWidth: 200,
- flex: 1,
- id: 'versioninfo',
- html: 'Proxmox Virtual Environment',
- height: 30
- },
- {
- pack: 'end',
- margin: '8 10 0 10',
- id: 'userinfo',
- stateful: false
- },
- {
- pack: 'end',
- margin: '3 5 0 0',
- xtype: 'button',
- baseCls: 'x-btn',
- text: gettext("Logout"),
- handler: function() {
- PVE.data.ResourceStore.stopUpdate();
- me.showLogin();
- me.setContent();
- var rt = me.down('pveResourceTree');
- rt.clearTree();
- }
- },
- createVM,
- createCT
- ]
- },
- {
- region: 'center',
- id: 'content',
- xtype: 'container',
- layout: { type: 'fit' },
- border: false,
- stateful: false,
- margin: '0 5 0 0',
- items: []
- },
- {
- region: 'west',
- xtype: 'container',
- border: false,
- layout: { type: 'vbox', align: 'stretch' },
- margin: '0 0 0 5',
- split: true,
- width: 200,
- items: [ selview, rtree ]
- },
- {
- xtype: 'pveStatusPanel',
- region: 'south',
- margin:'0 5 5 5',
- height: 200,
- split:true
- }
- ]
- });
-
- me.callParent();
-
- me.updateUserInfo();
- }
-});
-
+++ /dev/null
-/* Button features:
- * - observe selection changes to enable/disable the button using enableFn()
- * - pop up confirmation dialog using confirmMsg()
- */
-Ext.define('PVE.button.Button', {
- extend: 'Ext.button.Button',
- alias: 'widget.pveButton',
-
- // the selection model to observe
- selModel: undefined,
-
- // if 'false' handler will not be called (button disabled)
- enableFn: function(record) { },
-
- // function(record) or text
- confirmMsg: false,
-
- // take special care in confirm box (select no as default).
- dangerous: false,
-
- initComponent: function() {
- /*jslint confusion: true */
-
- var me = this;
-
- if (me.handler) {
- me.realHandler = me.handler;
-
- me.handler = function(button, event) {
- var rec, msg;
- if (me.selModel) {
- rec = me.selModel.getSelection()[0];
- if (!rec || (me.enableFn(rec) === false)) {
- return;
- }
- }
-
- if (me.confirmMsg) {
- msg = me.confirmMsg;
- if (Ext.isFunction(me.confirmMsg)) {
- msg = me.confirmMsg(rec);
- }
- Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1;
- Ext.Msg.show({
- title: gettext('Confirm'),
- icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION,
- msg: msg,
- buttons: Ext.Msg.YESNO,
- callback: function(btn) {
- if (btn !== 'yes') {
- return;
- }
- me.realHandler(button, event, rec);
- }
- });
- } else {
- me.realHandler(button, event, rec);
- }
- };
- }
-
- me.callParent();
-
- if (me.selModel) {
-
- me.mon(me.selModel, "selectionchange", function() {
- var rec = me.selModel.getSelection()[0];
- if (!rec || (me.enableFn(rec) === false)) {
- me.setDisabled(true);
- } else {
- me.setDisabled(false);
- }
- });
- }
- }
-});
+++ /dev/null
-Ext.define('PVE.button.ConsoleButton', {
- extend: 'Ext.button.Split',
- alias: 'widget.pveConsoleButton',
-
- consoleType: 'shell', // one of 'shell', 'kvm', 'lxc', 'upgrade'
-
- consoleName: undefined,
-
- enableSpice: true,
-
- nodename: undefined,
-
- vmid: 0,
-
- setEnableSpice: function(enable){
- var me = this;
-
- me.enableSpice = enable;
- me.spiceMenu.setDisabled(!enable);
- },
-
- initComponent: function() {
- var me = this;
-
- if (!me.nodename) {
- throw "no node name specified";
- }
-
- me.spiceMenu = Ext.create('Ext.menu.Item', {
- text: 'SPICE',
- iconCls: 'pve-itype-icon-virt-viewer',
- handler: function() {
- PVE.Utils.openConsoleWindow('vv', me.consoleType, me.vmid, me.nodename, me.consoleName);
- }
- });
-
- var noVncMenu = Ext.create('Ext.menu.Item', {
- text: 'noVNC',
- iconCls: 'pve-itype-icon-novnc',
- handler: function() {
- PVE.Utils.openConsoleWindow('html5', me.consoleType, me.vmid, me.nodename, me.consoleName);
- }
- });
-
- Ext.applyIf(me, { text: gettext('Console') });
-
- Ext.apply(me, {
- handler: function() {
- PVE.Utils.openDefaultConsoleWindow(me.enableSpice, me.consoleType, me.vmid,
- me.nodename, me.consoleName);
- },
- menu: new Ext.menu.Menu({
- items: [ noVncMenu, me.spiceMenu ]
- })
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-/*
- * The DiffStore acts as proxy between an UpdateStore instance and a component.
- * Its purpose is to redisplay the component *only* if the data has been changed
- * inside the UpdateStore, to avoid the annoying visual flickering of using
- * the UpdateStore directly.
- *
- * Implementation:
- * The DiffStore monitors via mon() the 'load' events sent by the target store.
- * On each 'load' event, the DiffStore compares its own content with the target
- * store (call to cond_add_item()) and then fires a 'refresh' event.
- * The 'refresh' event will automatically trigger a view refresh on the component
- * who binds to this store.
- */
-
-/* Config properties:
- * rstore: A target store to track changes
- * Only works if rstore has a model and use 'idProperty'
- */
-Ext.define('PVE.data.DiffStore', {
- extend: 'Ext.data.Store',
-
- sortAfterUpdate: false,
-
- constructor: function(config) {
- var me = this;
-
- config = config || {};
-
- if (!config.rstore) {
- throw "no rstore specified";
- }
-
- if (!config.rstore.model) {
- throw "no rstore model specified";
- }
-
- var rstore = config.rstore;
-
- Ext.apply(config, {
- model: rstore.model,
- proxy: { type: 'memory' }
- });
-
- me.callParent([config]);
-
- var first_load = true;
-
- var cond_add_item = function(data, id) {
- var olditem = me.getById(id);
- if (olditem) {
- olditem.beginEdit();
- Ext.Array.each(me.model.prototype.fields, function(field) {
- if (olditem.data[field.name] !== data[field.name]) {
- olditem.set(field.name, data[field.name]);
- }
- });
- olditem.endEdit(true);
- olditem.commit();
- } else {
- var newrec = Ext.create(me.model, data);
- var pos = (me.appendAtStart && !first_load) ? 0 : me.data.length;
- me.insert(pos, newrec);
- }
- };
-
- me.mon(rstore, 'load', function(s, records, success) {
-
- if (!success) {
- return;
- }
-
- me.suspendEvents();
-
- // remove vanished items
- (me.snapshot || me.data).each(function(olditem) {
- var item = rstore.getById(olditem.getId());
- if (!item) {
- me.remove(olditem);
- }
- });
-
- rstore.each(function(item) {
- cond_add_item(item.data, item.getId());
- });
-
- me.filter();
-
- if (me.sortAfterUpdate) {
- me.sort();
- }
-
- first_load = false;
-
- me.resumeEvents();
- me.fireEvent('refresh', me);
- });
- }
-});
+++ /dev/null
-/* This store encapsulates data items which are organized as an Array of key-values Objects
- * ie data[0] contains something like {key: "keyboard", value: "da"}
-*
-* Designed to work with the KeyValue model and the JsonObject data reader
-*/
-Ext.define('PVE.data.ObjectStore', {
- extend: 'PVE.data.UpdateStore',
-
- constructor: function(config) {
- var me = this;
-
- config = config || {};
-
- if (!config.storeid) {
- config.storeid = 'pve-store-' + (++Ext.idSeed);
- }
-
- Ext.applyIf(config, {
- model: 'KeyValue',
- proxy: {
- type: 'pve',
- url: config.url,
- extraParams: config.extraParams,
- reader: {
- type: 'jsonobject',
- rows: config.rows,
- readArray: config.readArray,
- rootProperty: config.root || 'data'
- }
- }
- });
-
- me.callParent([config]);
- }
-});
+++ /dev/null
-Ext.define('PVE.RestProxy', {
- extend: 'Ext.data.RestProxy',
- alias : 'proxy.pve',
-
- pageParam : null,
- startParam: null,
- limitParam: null,
- groupParam: null,
- sortParam: null,
- filterParam: null,
- noCache : false,
- afterRequest: function(request, success) {
- this.fireEvent('afterload', this, request, success);
- return;
- },
-
- constructor: function(config) {
-
- Ext.applyIf(config, {
- reader: {
- type: 'json',
- rootProperty: config.root || 'data'
- },
- });
-
- this.callParent([config]);
- }
-
-}, function() {
-
- Ext.define('pve-domains', {
- extend: "Ext.data.Model",
- fields: [ 'realm', 'type', 'comment', 'default', 'tfa',
- {
- name: 'descr',
- // Note: We use this in the RealmComboBox.js
- // (see Bug #125)
- convert: function(value, record) {
- var info = record.data;
- var text;
-
- if (value) {
- return value;
- }
- // return realm if there is no comment
- text = info.comment || info.realm;
-
- if (info.tfa) {
- text += " (+ " + info.tfa + ")";
- }
-
- return text;
- }
- }
- ],
- proxy: {
- type: 'pve',
- url: "/api2/json/access/domains"
- }
- });
-
- Ext.define('KeyValue', {
- extend: "Ext.data.Model",
- fields: [ 'key', 'value' ]
- });
-
- Ext.define('KeyValuePendingDelete', {
- extend: "Ext.data.Model",
- fields: [ 'key', 'value', 'pending', 'delete' ],
- idProperty: 'key'
- });
-
- Ext.define('pve-string-list', {
- extend: 'Ext.data.Model',
- fields: [ 'n', 't' ],
- idProperty: 'n'
- });
-
- Ext.define('pve-tasks', {
- extend: 'Ext.data.Model',
- fields: [
- { name: 'starttime', type : 'date', dateFormat: 'timestamp' },
- { name: 'endtime', type : 'date', dateFormat: 'timestamp' },
- { name: 'pid', type: 'int' },
- 'node', 'upid', 'user', 'status', 'type', 'id'
- ],
- idProperty: 'upid'
- });
-
- Ext.define('pve-cluster-log', {
- extend: 'Ext.data.Model',
- fields: [
- { name: 'uid' , type: 'int' },
- { name: 'time', type : 'date', dateFormat: 'timestamp' },
- { name: 'pri', type: 'int' },
- { name: 'pid', type: 'int' },
- 'node', 'user', 'tag', 'msg',
- {
- name: 'id',
- convert: function(value, record) {
- var info = record.data;
- var text;
-
- if (value) {
- return value;
- }
- // compute unique ID
- return info.uid + ':' + info.node;
- }
- }
- ],
- idProperty: 'id'
- });
-});
+++ /dev/null
-Ext.define('PVE.data.ResourceStore', {
- extend: 'PVE.data.UpdateStore',
- singleton: true,
-
- findVMID: function(vmid) {
- var me = this, i;
-
- return (me.findExact('vmid', parseInt(vmid, 10)) >= 0);
- },
-
- constructor: function(config) {
- // fixme: how to avoid those warnings
- /*jslint confusion: true */
-
- var me = this;
-
- config = config || {};
-
- var field_defaults = {
- type: {
- header: gettext('Type'),
- type: 'string',
- renderer: PVE.Utils.render_resource_type,
- sortable: true,
- hideable: false,
- width: 80
- },
- id: {
- header: 'ID',
- type: 'string',
- hidden: true,
- sortable: true,
- width: 80
- },
- running: {
- header: gettext('Online'),
- type: 'boolean',
- renderer: PVE.Utils.format_boolean,
- hidden: true,
- convert: function(value, record) {
- var info = record.data;
- if (info.type === 'qemu' || info.type === 'lxc' || info.type === 'node') {
- return (Ext.isNumeric(info.uptime) && (info.uptime > 0));
- } else {
- return false;
- }
- }
- },
- text: {
- header: gettext('Description'),
- type: 'string',
- sortable: true,
- width: 200,
- convert: function(value, record) {
- var info = record.data;
- var text;
-
- if (value) {
- return value;
- }
-
- if (info.type === 'node') {
- text = info.node;
- } else if (info.type === 'pool') {
- text = info.pool;
- } else if (info.type === 'storage') {
- text = info.storage + ' (' + info.node + ')';
- } else if (info.type === 'qemu' || info.type === 'lxc') {
- text = String(info.vmid);
- if (info.name) {
- text += " (" + info.name + ')';
- }
- } else {
- text = info.id;
- }
- return text;
- }
- },
- vmid: {
- header: 'VMID',
- type: 'integer',
- hidden: true,
- sortable: true,
- width: 80
- },
- name: {
- header: gettext('Name'),
- hidden: true,
- sortable: true,
- type: 'string'
- },
- disk: {
- header: gettext('Disk usage'),
- type: 'integer',
- renderer: PVE.Utils.render_disk_usage,
- sortable: true,
- width: 100
- },
- maxdisk: {
- header: gettext('Disk size'),
- type: 'integer',
- renderer: PVE.Utils.render_size,
- sortable: true,
- hidden: true,
- width: 100
- },
- mem: {
- header: gettext('Memory usage'),
- type: 'integer',
- renderer: PVE.Utils.render_mem_usage,
- sortable: true,
- width: 100
- },
- maxmem: {
- header: gettext('Memory size'),
- type: 'integer',
- renderer: PVE.Utils.render_size,
- hidden: true,
- sortable: true,
- width: 100
- },
- cpu: {
- header: gettext('CPU usage'),
- type: 'float',
- renderer: PVE.Utils.render_cpu,
- sortable: true,
- width: 100
- },
- maxcpu: {
- header: gettext('maxcpu'),
- type: 'integer',
- hidden: true,
- sortable: true,
- width: 60
- },
- diskread: {
- header: gettext('Total Disk Read'),
- type: 'integer',
- hidden: true,
- sortable: true,
- renderer: PVE.Utils.format_size,
- width: 100
- },
- diskwrite: {
- header: gettext('Total Disk Write'),
- type: 'integer',
- hidden: true,
- sortable: true,
- renderer: PVE.Utils.format_size,
- width: 100
- },
- netin: {
- header: gettext('Total NetIn'),
- type: 'integer',
- hidden: true,
- sortable: true,
- renderer: PVE.Utils.format_size,
- width: 100
- },
- netout: {
- header: gettext('Total NetOut'),
- type: 'integer',
- hidden: true,
- sortable: true,
- renderer: PVE.Utils.format_size,
- width: 100
- },
- template: {
- header: gettext('Template'),
- type: 'integer',
- hidden: true,
- sortable: true,
- width: 60
- },
- uptime: {
- header: gettext('Uptime'),
- type: 'integer',
- renderer: PVE.Utils.render_uptime,
- sortable: true,
- width: 110
- },
- node: {
- header: gettext('Node'),
- type: 'string',
- hidden: true,
- sortable: true,
- width: 110
- },
- storage: {
- header: gettext('Storage'),
- type: 'string',
- hidden: true,
- sortable: true,
- width: 110
- },
- pool: {
- header: gettext('Pool'),
- type: 'string',
- hidden: true,
- sortable: true,
- width: 110
- }
- };
-
- var fields = [];
- var fieldNames = [];
- Ext.Object.each(field_defaults, function(key, value) {
- if (!Ext.isDefined(value.convert)) {
- fields.push({name: key, type: value.type});
- fieldNames.push(key);
- } else if (key === 'text' || key === 'running') {
- fields.push({name: key, type: value.type, convert: value.convert});
- fieldNames.push(key);
- }
- });
-
- Ext.define('PVEResources', {
- extend: "Ext.data.Model",
- fields: fields,
- proxy: {
- type: 'pve',
- url: '/api2/json/cluster/resources'
- }
- });
-
- Ext.define('PVETree', {
- extend: "Ext.data.Model",
- fields: fields,
- proxy: { type: 'memory' }
- });
-
- Ext.apply(config, {
- storeid: 'PVEResources',
- model: 'PVEResources',
- defaultColums: function() {
- var res = [];
- Ext.Object.each(field_defaults, function(field, info) {
- var fi = Ext.apply({ dataIndex: field }, info);
- res.push(fi);
- });
- return res;
- },
- fieldNames: fieldNames
- });
-
- me.callParent([config]);
- }
-});
+++ /dev/null
-Ext.define('PVE.data.TimezoneStore', {
- extend: 'Ext.data.Store',
-
- statics: {
- timezones: [
- ['Africa/Abidjan'],
- ['Africa/Accra'],
- ['Africa/Addis_Ababa'],
- ['Africa/Algiers'],
- ['Africa/Asmara'],
- ['Africa/Bamako'],
- ['Africa/Bangui'],
- ['Africa/Banjul'],
- ['Africa/Bissau'],
- ['Africa/Blantyre'],
- ['Africa/Brazzaville'],
- ['Africa/Bujumbura'],
- ['Africa/Cairo'],
- ['Africa/Casablanca'],
- ['Africa/Ceuta'],
- ['Africa/Conakry'],
- ['Africa/Dakar'],
- ['Africa/Dar_es_Salaam'],
- ['Africa/Djibouti'],
- ['Africa/Douala'],
- ['Africa/El_Aaiun'],
- ['Africa/Freetown'],
- ['Africa/Gaborone'],
- ['Africa/Harare'],
- ['Africa/Johannesburg'],
- ['Africa/Kampala'],
- ['Africa/Khartoum'],
- ['Africa/Kigali'],
- ['Africa/Kinshasa'],
- ['Africa/Lagos'],
- ['Africa/Libreville'],
- ['Africa/Lome'],
- ['Africa/Luanda'],
- ['Africa/Lubumbashi'],
- ['Africa/Lusaka'],
- ['Africa/Malabo'],
- ['Africa/Maputo'],
- ['Africa/Maseru'],
- ['Africa/Mbabane'],
- ['Africa/Mogadishu'],
- ['Africa/Monrovia'],
- ['Africa/Nairobi'],
- ['Africa/Ndjamena'],
- ['Africa/Niamey'],
- ['Africa/Nouakchott'],
- ['Africa/Ouagadougou'],
- ['Africa/Porto-Novo'],
- ['Africa/Sao_Tome'],
- ['Africa/Tripoli'],
- ['Africa/Tunis'],
- ['Africa/Windhoek'],
- ['America/Adak'],
- ['America/Anchorage'],
- ['America/Anguilla'],
- ['America/Antigua'],
- ['America/Araguaina'],
- ['America/Argentina/Buenos_Aires'],
- ['America/Argentina/Catamarca'],
- ['America/Argentina/Cordoba'],
- ['America/Argentina/Jujuy'],
- ['America/Argentina/La_Rioja'],
- ['America/Argentina/Mendoza'],
- ['America/Argentina/Rio_Gallegos'],
- ['America/Argentina/Salta'],
- ['America/Argentina/San_Juan'],
- ['America/Argentina/San_Luis'],
- ['America/Argentina/Tucuman'],
- ['America/Argentina/Ushuaia'],
- ['America/Aruba'],
- ['America/Asuncion'],
- ['America/Atikokan'],
- ['America/Bahia'],
- ['America/Bahia_Banderas'],
- ['America/Barbados'],
- ['America/Belem'],
- ['America/Belize'],
- ['America/Blanc-Sablon'],
- ['America/Boa_Vista'],
- ['America/Bogota'],
- ['America/Boise'],
- ['America/Cambridge_Bay'],
- ['America/Campo_Grande'],
- ['America/Cancun'],
- ['America/Caracas'],
- ['America/Cayenne'],
- ['America/Cayman'],
- ['America/Chicago'],
- ['America/Chihuahua'],
- ['America/Costa_Rica'],
- ['America/Cuiaba'],
- ['America/Curacao'],
- ['America/Danmarkshavn'],
- ['America/Dawson'],
- ['America/Dawson_Creek'],
- ['America/Denver'],
- ['America/Detroit'],
- ['America/Dominica'],
- ['America/Edmonton'],
- ['America/Eirunepe'],
- ['America/El_Salvador'],
- ['America/Fortaleza'],
- ['America/Glace_Bay'],
- ['America/Godthab'],
- ['America/Goose_Bay'],
- ['America/Grand_Turk'],
- ['America/Grenada'],
- ['America/Guadeloupe'],
- ['America/Guatemala'],
- ['America/Guayaquil'],
- ['America/Guyana'],
- ['America/Halifax'],
- ['America/Havana'],
- ['America/Hermosillo'],
- ['America/Indiana/Indianapolis'],
- ['America/Indiana/Knox'],
- ['America/Indiana/Marengo'],
- ['America/Indiana/Petersburg'],
- ['America/Indiana/Tell_City'],
- ['America/Indiana/Vevay'],
- ['America/Indiana/Vincennes'],
- ['America/Indiana/Winamac'],
- ['America/Inuvik'],
- ['America/Iqaluit'],
- ['America/Jamaica'],
- ['America/Juneau'],
- ['America/Kentucky/Louisville'],
- ['America/Kentucky/Monticello'],
- ['America/La_Paz'],
- ['America/Lima'],
- ['America/Los_Angeles'],
- ['America/Maceio'],
- ['America/Managua'],
- ['America/Manaus'],
- ['America/Marigot'],
- ['America/Martinique'],
- ['America/Matamoros'],
- ['America/Mazatlan'],
- ['America/Menominee'],
- ['America/Merida'],
- ['America/Mexico_City'],
- ['America/Miquelon'],
- ['America/Moncton'],
- ['America/Monterrey'],
- ['America/Montevideo'],
- ['America/Montreal'],
- ['America/Montserrat'],
- ['America/Nassau'],
- ['America/New_York'],
- ['America/Nipigon'],
- ['America/Nome'],
- ['America/Noronha'],
- ['America/North_Dakota/Center'],
- ['America/North_Dakota/New_Salem'],
- ['America/Ojinaga'],
- ['America/Panama'],
- ['America/Pangnirtung'],
- ['America/Paramaribo'],
- ['America/Phoenix'],
- ['America/Port-au-Prince'],
- ['America/Port_of_Spain'],
- ['America/Porto_Velho'],
- ['America/Puerto_Rico'],
- ['America/Rainy_River'],
- ['America/Rankin_Inlet'],
- ['America/Recife'],
- ['America/Regina'],
- ['America/Resolute'],
- ['America/Rio_Branco'],
- ['America/Santa_Isabel'],
- ['America/Santarem'],
- ['America/Santiago'],
- ['America/Santo_Domingo'],
- ['America/Sao_Paulo'],
- ['America/Scoresbysund'],
- ['America/Shiprock'],
- ['America/St_Barthelemy'],
- ['America/St_Johns'],
- ['America/St_Kitts'],
- ['America/St_Lucia'],
- ['America/St_Thomas'],
- ['America/St_Vincent'],
- ['America/Swift_Current'],
- ['America/Tegucigalpa'],
- ['America/Thule'],
- ['America/Thunder_Bay'],
- ['America/Tijuana'],
- ['America/Toronto'],
- ['America/Tortola'],
- ['America/Vancouver'],
- ['America/Whitehorse'],
- ['America/Winnipeg'],
- ['America/Yakutat'],
- ['America/Yellowknife'],
- ['Antarctica/Casey'],
- ['Antarctica/Davis'],
- ['Antarctica/DumontDUrville'],
- ['Antarctica/Macquarie'],
- ['Antarctica/Mawson'],
- ['Antarctica/McMurdo'],
- ['Antarctica/Palmer'],
- ['Antarctica/Rothera'],
- ['Antarctica/South_Pole'],
- ['Antarctica/Syowa'],
- ['Antarctica/Vostok'],
- ['Arctic/Longyearbyen'],
- ['Asia/Aden'],
- ['Asia/Almaty'],
- ['Asia/Amman'],
- ['Asia/Anadyr'],
- ['Asia/Aqtau'],
- ['Asia/Aqtobe'],
- ['Asia/Ashgabat'],
- ['Asia/Baghdad'],
- ['Asia/Bahrain'],
- ['Asia/Baku'],
- ['Asia/Bangkok'],
- ['Asia/Beirut'],
- ['Asia/Bishkek'],
- ['Asia/Brunei'],
- ['Asia/Choibalsan'],
- ['Asia/Chongqing'],
- ['Asia/Colombo'],
- ['Asia/Damascus'],
- ['Asia/Dhaka'],
- ['Asia/Dili'],
- ['Asia/Dubai'],
- ['Asia/Dushanbe'],
- ['Asia/Gaza'],
- ['Asia/Harbin'],
- ['Asia/Ho_Chi_Minh'],
- ['Asia/Hong_Kong'],
- ['Asia/Hovd'],
- ['Asia/Irkutsk'],
- ['Asia/Jakarta'],
- ['Asia/Jayapura'],
- ['Asia/Jerusalem'],
- ['Asia/Kabul'],
- ['Asia/Kamchatka'],
- ['Asia/Karachi'],
- ['Asia/Kashgar'],
- ['Asia/Kathmandu'],
- ['Asia/Kolkata'],
- ['Asia/Krasnoyarsk'],
- ['Asia/Kuala_Lumpur'],
- ['Asia/Kuching'],
- ['Asia/Kuwait'],
- ['Asia/Macau'],
- ['Asia/Magadan'],
- ['Asia/Makassar'],
- ['Asia/Manila'],
- ['Asia/Muscat'],
- ['Asia/Nicosia'],
- ['Asia/Novokuznetsk'],
- ['Asia/Novosibirsk'],
- ['Asia/Omsk'],
- ['Asia/Oral'],
- ['Asia/Phnom_Penh'],
- ['Asia/Pontianak'],
- ['Asia/Pyongyang'],
- ['Asia/Qatar'],
- ['Asia/Qyzylorda'],
- ['Asia/Rangoon'],
- ['Asia/Riyadh'],
- ['Asia/Sakhalin'],
- ['Asia/Samarkand'],
- ['Asia/Seoul'],
- ['Asia/Shanghai'],
- ['Asia/Singapore'],
- ['Asia/Taipei'],
- ['Asia/Tashkent'],
- ['Asia/Tbilisi'],
- ['Asia/Tehran'],
- ['Asia/Thimphu'],
- ['Asia/Tokyo'],
- ['Asia/Ulaanbaatar'],
- ['Asia/Urumqi'],
- ['Asia/Vientiane'],
- ['Asia/Vladivostok'],
- ['Asia/Yakutsk'],
- ['Asia/Yekaterinburg'],
- ['Asia/Yerevan'],
- ['Atlantic/Azores'],
- ['Atlantic/Bermuda'],
- ['Atlantic/Canary'],
- ['Atlantic/Cape_Verde'],
- ['Atlantic/Faroe'],
- ['Atlantic/Madeira'],
- ['Atlantic/Reykjavik'],
- ['Atlantic/South_Georgia'],
- ['Atlantic/St_Helena'],
- ['Atlantic/Stanley'],
- ['Australia/Adelaide'],
- ['Australia/Brisbane'],
- ['Australia/Broken_Hill'],
- ['Australia/Currie'],
- ['Australia/Darwin'],
- ['Australia/Eucla'],
- ['Australia/Hobart'],
- ['Australia/Lindeman'],
- ['Australia/Lord_Howe'],
- ['Australia/Melbourne'],
- ['Australia/Perth'],
- ['Australia/Sydney'],
- ['Europe/Amsterdam'],
- ['Europe/Andorra'],
- ['Europe/Athens'],
- ['Europe/Belgrade'],
- ['Europe/Berlin'],
- ['Europe/Bratislava'],
- ['Europe/Brussels'],
- ['Europe/Bucharest'],
- ['Europe/Budapest'],
- ['Europe/Chisinau'],
- ['Europe/Copenhagen'],
- ['Europe/Dublin'],
- ['Europe/Gibraltar'],
- ['Europe/Guernsey'],
- ['Europe/Helsinki'],
- ['Europe/Isle_of_Man'],
- ['Europe/Istanbul'],
- ['Europe/Jersey'],
- ['Europe/Kaliningrad'],
- ['Europe/Kiev'],
- ['Europe/Lisbon'],
- ['Europe/Ljubljana'],
- ['Europe/London'],
- ['Europe/Luxembourg'],
- ['Europe/Madrid'],
- ['Europe/Malta'],
- ['Europe/Mariehamn'],
- ['Europe/Minsk'],
- ['Europe/Monaco'],
- ['Europe/Moscow'],
- ['Europe/Oslo'],
- ['Europe/Paris'],
- ['Europe/Podgorica'],
- ['Europe/Prague'],
- ['Europe/Riga'],
- ['Europe/Rome'],
- ['Europe/Samara'],
- ['Europe/San_Marino'],
- ['Europe/Sarajevo'],
- ['Europe/Simferopol'],
- ['Europe/Skopje'],
- ['Europe/Sofia'],
- ['Europe/Stockholm'],
- ['Europe/Tallinn'],
- ['Europe/Tirane'],
- ['Europe/Uzhgorod'],
- ['Europe/Vaduz'],
- ['Europe/Vatican'],
- ['Europe/Vienna'],
- ['Europe/Vilnius'],
- ['Europe/Volgograd'],
- ['Europe/Warsaw'],
- ['Europe/Zagreb'],
- ['Europe/Zaporozhye'],
- ['Europe/Zurich'],
- ['Indian/Antananarivo'],
- ['Indian/Chagos'],
- ['Indian/Christmas'],
- ['Indian/Cocos'],
- ['Indian/Comoro'],
- ['Indian/Kerguelen'],
- ['Indian/Mahe'],
- ['Indian/Maldives'],
- ['Indian/Mauritius'],
- ['Indian/Mayotte'],
- ['Indian/Reunion'],
- ['Pacific/Apia'],
- ['Pacific/Auckland'],
- ['Pacific/Chatham'],
- ['Pacific/Chuuk'],
- ['Pacific/Easter'],
- ['Pacific/Efate'],
- ['Pacific/Enderbury'],
- ['Pacific/Fakaofo'],
- ['Pacific/Fiji'],
- ['Pacific/Funafuti'],
- ['Pacific/Galapagos'],
- ['Pacific/Gambier'],
- ['Pacific/Guadalcanal'],
- ['Pacific/Guam'],
- ['Pacific/Honolulu'],
- ['Pacific/Johnston'],
- ['Pacific/Kiritimati'],
- ['Pacific/Kosrae'],
- ['Pacific/Kwajalein'],
- ['Pacific/Majuro'],
- ['Pacific/Marquesas'],
- ['Pacific/Midway'],
- ['Pacific/Nauru'],
- ['Pacific/Niue'],
- ['Pacific/Norfolk'],
- ['Pacific/Noumea'],
- ['Pacific/Pago_Pago'],
- ['Pacific/Palau'],
- ['Pacific/Pitcairn'],
- ['Pacific/Pohnpei'],
- ['Pacific/Port_Moresby'],
- ['Pacific/Rarotonga'],
- ['Pacific/Saipan'],
- ['Pacific/Tahiti'],
- ['Pacific/Tarawa'],
- ['Pacific/Tongatapu'],
- ['Pacific/Wake'],
- ['Pacific/Wallis']
- ]
- },
-
- constructor: function(config) {
- var me = this;
-
- config = config || {};
-
- Ext.regModel('Timezone', {
- fields: ['zone'],
- proxy: {
- type: 'memory',
- reader: 'array'
- }
- });
-
- Ext.apply(config, {
- model: 'Timezone',
- data: PVE.data.TimezoneStore.timezones
- });
-
- me.callParent([config]);
- }
-});
\ No newline at end of file
+++ /dev/null
-// Serialize load (avoid too many parallel connections)
-Ext.define('PVE.data.UpdateQueue', {
- singleton: true,
-
- constructor : function(){
- var me = this;
-
- var queue = [];
- var queue_idx = {};
-
- var idle = true;
-
- var start_update = function() {
- if (!idle) {
- return;
- }
-
- var storeid = queue.shift();
- if (!storeid) {
- return;
- }
- var info = queue_idx[storeid];
- queue_idx[storeid] = null;
-
- info.updatestart = new Date();
-
- idle = false;
- info.store.load({
- callback: function(records, operation, success) {
- idle = true;
- if (info.callback) {
- var runtime = (new Date()).getTime() - info.updatestart.getTime();
- info.callback(runtime, success);
- }
- start_update();
- }
- });
- };
-
- Ext.apply(me, {
- queue: function(store, cb) {
- var storeid = store.storeid;
- if (!storeid) {
- throw "unable to queue store without storeid";
- }
- if (!queue_idx[storeid]) {
- queue_idx[storeid] = {
- store: store,
- callback: cb
- };
- queue.push(storeid);
- }
- start_update();
- }
- });
- }
-});
+++ /dev/null
-/* Extends the Ext.data.Store type
- * with startUpdate() and stopUpdate() methods
- * to refresh the store data in the background
- * Components using this store directly will flicker
- * due to the redisplay of the element ater 'config.interval' ms
- */
-Ext.define('PVE.data.UpdateStore', {
- extend: 'Ext.data.Store',
-
- constructor: function(config) {
- var me = this;
-
- config = config || {};
-
- if (!config.interval) {
- config.interval = 3000;
- }
-
- if (!config.storeid) {
- throw "no storeid specified";
- }
-
- var load_task = new Ext.util.DelayedTask();
-
- var run_load_task = function() {
- if (PVE.Utils.authOK()) {
- PVE.data.UpdateQueue.queue(me, function(runtime, success) {
- var interval = config.interval + runtime*2;
- load_task.delay(interval, run_load_task);
- });
- } else {
- load_task.delay(200, run_load_task);
- }
- };
-
- Ext.apply(config, {
- startUpdate: function() {
- run_load_task();
- },
- stopUpdate: function() {
- load_task.cancel();
- }
- });
-
- me.callParent([config]);
-
- me.on('destroy', function() {
- load_task.cancel();
- });
- }
-});
+++ /dev/null
-/* A reader to store a single JSON Object (hash) into a storage.
- * Also accepts an array containing a single hash.
- *
- * So it can read:
- *
- * example1: {data1: "xyz", data2: "abc"}
- * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}]
- *
- * example2: [ {data1: "xyz", data2: "abc"} ]
- * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}]
- *
- * If you set 'readArray', the reader expexts the object as array:
- *
- * example3: [ { key: "data1", value: "xyz", p2: "cde" }, { key: "data2", value: "abc", p2: "efg" }]
- * returns [{key: "data1", value: "xyz", p2: "cde}, {key: "data2", value: "abc", p2: "efg"}]
- *
- * Note: The records can contain additional properties (like 'p2' above) when you use 'readArray'
- *
- * Additional feature: specify allowed properties with default values with 'rows' object
- *
- * var rows = {
- * memory: {
- * required: true,
- * defaultValue: 512
- * }
- * }
- *
- */
-
-Ext.define('PVE.data.reader.JsonObject', {
- extend: 'Ext.data.reader.Json',
- alias : 'reader.jsonobject',
-
- readArray: false,
-
- rows: undefined,
-
- constructor: function(config) {
- var me = this;
-
- Ext.apply(me, config || {});
-
- me.callParent([config]);
- },
-
- getResponseData: function(response) {
- var me = this;
-
- var data = [];
- try {
- var result = Ext.decode(response.responseText);
- // get our data items inside the server response
- var root = result[me.getRootProperty()];
-
- if (me.readArray) {
-
- var rec_hash = {};
- Ext.Array.each(root, function(rec) {
- if (Ext.isDefined(rec.key)) {
- rec_hash[rec.key] = rec;
- }
- });
-
- if (me.rows) {
- Ext.Object.each(me.rows, function(key, rowdef) {
- var rec = rec_hash[key];
- if (Ext.isDefined(rec)) {
- if (!Ext.isDefined(rec.value)) {
- rec.value = rowdef.defaultValue;
- }
- data.push(rec);
- } else if (Ext.isDefined(rowdef.defaultValue)) {
- data.push({key: key, value: rowdef.defaultValue} );
- } else if (rowdef.required) {
- data.push({key: key, value: undefined });
- }
- });
- } else {
- Ext.Array.each(root, function(rec) {
- if (Ext.isDefined(rec.key)) {
- data.push(rec);
- }
- });
- }
-
- } else {
-
- var org_root = root;
-
- if (Ext.isArray(org_root)) {
- if (root.length == 1) {
- root = org_root[0];
- } else {
- root = {};
- }
- }
-
- if (me.rows) {
- Ext.Object.each(me.rows, function(key, rowdef) {
- if (Ext.isDefined(root[key])) {
- data.push({key: key, value: root[key]});
- } else if (Ext.isDefined(rowdef.defaultValue)) {
- data.push({key: key, value: rowdef.defaultValue});
- } else if (rowdef.required) {
- data.push({key: key, value: undefined});
- }
- });
- } else {
- Ext.Object.each(root, function(key, value) {
- data.push({key: key, value: value });
- });
- }
- }
- }
- catch (ex) {
- Ext.Error.raise({
- response: response,
- json: response.responseText,
- parseError: ex,
- msg: 'Unable to parse the JSON returned by the server: ' + ex.toString()
- });
- }
-
- return data;
- }
-});
-
+++ /dev/null
-Ext.define('PVE.dc.ACLAdd', {
- extend: 'PVE.window.Edit',
- alias: ['widget.pveACLAdd'],
-
- initComponent : function() {
- /*jslint confusion: true */
- var me = this;
-
- me.create = true;
-
- var items = [
- {
- xtype: me.path ? 'hiddenfield' : 'textfield',
- name: 'path',
- value: me.path,
- allowBlank: false,
- fieldLabel: gettext('Path')
- }
- ];
-
- if (me.aclType === 'group') {
- me.subject = gettext("Group Permission");
- items.push({
- xtype: 'pveGroupSelector',
- name: 'groups',
- fieldLabel: gettext('Group')
- });
- } else if (me.aclType === 'user') {
- me.subject = gettext("User Permission");
- items.push({
- xtype: 'pveUserSelector',
- name: 'users',
- fieldLabel: gettext('User')
- });
- } else {
- throw "unknown ACL type";
- }
-
- items.push({
- xtype: 'pveRoleSelector',
- name: 'roles',
- value: 'NoAccess',
- fieldLabel: gettext('Role')
- });
-
- if (!me.path) {
- items.push({
- xtype: 'pvecheckbox',
- name: 'propagate',
- checked: true,
- fieldLabel: gettext('Propagate')
- });
- }
-
- var ipanel = Ext.create('PVE.panel.InputPanel', {
- items: items
- });
-
- Ext.apply(me, {
- url: '/access/acl',
- method: 'PUT',
- isAdd: true,
- items: [ ipanel ]
- });
-
- me.callParent();
- }
-});
-
-Ext.define('PVE.dc.ACLView', {
- extend: 'Ext.grid.GridPanel',
-
- alias: ['widget.pveACLView'],
-
- // use fixed path
- path: undefined,
-
- initComponent : function() {
- var me = this;
-
- var store = new Ext.data.Store({
- model: 'pve-acl',
- proxy: {
- type: 'pve',
- url: "/api2/json/access/acl"
- },
- sorters: {
- property: 'path',
- order: 'DESC'
- }
- });
-
- if (me.path) {
- store.filters.add(new Ext.util.Filter({
- filterFn: function(item) {
- if (item.data.path === me.path) {
- return true;
- }
- }
- }));
- }
-
- var render_ugid = function(ugid, metaData, record) {
- if (record.data.type == 'group') {
- return '@' + ugid;
- }
-
- return ugid;
- };
-
- var columns = [
- {
- header: gettext('User') + '/' + gettext('Group'),
- flex: 1,
- sortable: true,
- renderer: render_ugid,
- dataIndex: 'ugid'
- },
- {
- header: gettext('Role'),
- flex: 1,
- sortable: true,
- dataIndex: 'roleid'
- }
- ];
-
- if (!me.path) {
- columns.unshift({
- header: gettext('Path'),
- flex: 1,
- sortable: true,
- dataIndex: 'path'
- });
- columns.push({
- header: gettext('Propagate'),
- width: 80,
- sortable: true,
- dataIndex: 'propagate'
- });
- }
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var reload = function() {
- store.load();
- };
-
- var remove_btn = new PVE.button.Button({
- text: gettext('Remove'),
- disabled: true,
- selModel: sm,
- confirmMsg: gettext('Are you sure you want to remove this entry'),
- handler: function(btn, event, rec) {
- var params = {
- 'delete': 1,
- path: rec.data.path,
- roles: rec.data.roleid
- };
- if (rec.data.type === 'group') {
- params.groups = rec.data.ugid;
- } else if (rec.data.type === 'user') {
- params.users = rec.data.ugid;
- } else {
- throw 'unknown data type';
- }
-
- PVE.Utils.API2Request({
- url: '/access/acl',
- params: params,
- method: 'PUT',
- waitMsgTarget: me,
- callback: function() {
- reload();
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- }
- });
-
- PVE.Utils.monStoreErrors(me, store);
-
- Ext.apply(me, {
- store: store,
- selModel: sm,
- stateful: false,
- tbar: [
- {
- text: gettext('Add'),
- menu: new Ext.menu.Menu({
- items: [
- {
- text: gettext('Group Permission'),
- handler: function() {
- var win = Ext.create('PVE.dc.ACLAdd',{
- aclType: 'group',
- path: me.path
- });
- win.on('destroy', reload);
- win.show();
- }
- },
- {
- text: gettext('User Permission'),
- handler: function() {
- var win = Ext.create('PVE.dc.ACLAdd',{
- aclType: 'user',
- path: me.path
- });
- win.on('destroy', reload);
- win.show();
- }
- }
- ]
- })
- },
- remove_btn
- ],
- viewConfig: {
- trackOver: false
- },
- columns: columns,
- listeners: {
- show: reload
- }
- });
-
- me.callParent();
- }
-}, function() {
-
- Ext.define('pve-acl', {
- extend: 'Ext.data.Model',
- fields: [
- 'path', 'type', 'ugid', 'roleid',
- {
- name: 'propagate',
- type: 'boolean'
- }
- ]
- });
-
-});
\ No newline at end of file
+++ /dev/null
-Ext.define('PVE.dc.AuthEdit', {
- extend: 'PVE.window.Edit',
- alias: ['widget.pveDcAuthEdit'],
-
- isAdd: true,
-
- initComponent : function() {
- var me = this;
-
- me.create = !me.realm;
-
- var url;
- var method;
- var serverlist;
-
- if (me.create) {
- url = '/api2/extjs/access/domains';
- method = 'POST';
- } else {
- url = '/api2/extjs/access/domains/' + me.realm;
- method = 'PUT';
- }
-
- var column1 = [
- {
- xtype: me.create ? 'textfield' : 'displayfield',
- height: 22, // hack: set same height as text fields
- name: 'realm',
- fieldLabel: gettext('Realm'),
- value: me.realm,
- allowBlank: false
- }
- ];
-
- if (me.authType === 'ad') {
-
- me.subject = gettext('Active Directory Server');
-
- column1.push({
- xtype: 'textfield',
- name: 'domain',
- fieldLabel: gettext('Domain'),
- emptyText: 'company.net',
- allowBlank: false
- });
-
- } else if (me.authType === 'ldap') {
-
- me.subject = gettext('LDAP Server');
-
- column1.push({
- xtype: 'textfield',
- name: 'base_dn',
- fieldLabel: gettext('Base Domain Name'),
- emptyText: 'CN=Users,DC=Company,DC=net',
- allowBlank: false
- });
-
- column1.push({
- xtype: 'textfield',
- name: 'user_attr',
- emptyText: 'uid / sAMAccountName',
- fieldLabel: gettext('User Attribute Name'),
- allowBlank: false
- });
- } else if (me.authType === 'pve') {
-
- if (me.create) throw 'unknown auth type';
-
- me.subject = 'Proxmox VE authentication server';
-
- } else if (me.authType === 'pam') {
-
- if (me.create) throw 'unknown auth type';
-
- me.subject = 'linux PAM';
-
- } else {
- throw 'unknown auth type ';
- }
-
- column1.push({
- xtype: 'pvecheckbox',
- fieldLabel: gettext('Default'),
- name: 'default',
- uncheckedValue: 0
- });
-
- var column2 = [];
-
- if (me.authType === 'ldap' || me.authType === 'ad') {
- column2.push([
- {
- xtype: 'textfield',
- fieldLabel: gettext('Server'),
- name: 'server1',
- allowBlank: false
- },
- {
- xtype: 'pvetextfield',
- fieldLabel: gettext('Fallback Server'),
- deleteEmpty: !me.create,
- name: 'server2'
- },
- {
- xtype: 'numberfield',
- name: 'port',
- fieldLabel: gettext('Port'),
- minValue: 1,
- maxValue: 65535,
- emptyText: gettext('Default'),
- submitEmptyText: false
- },
- {
- xtype: 'pvecheckbox',
- fieldLabel: 'SSL',
- name: 'secure',
- uncheckedValue: 0
- }
- ]);
- }
-
- // Two Factor Auth settings
-
- column2.push({
- xtype: 'pveKVComboBox',
- name: 'tfa',
- deleteEmpty: !me.create,
- value: '',
- fieldLabel: gettext('TFA'),
- data: [ ['', PVE.Utils.noneText], ['oath', 'OATH'], ['yubico', 'Yubico']],
- listeners: {
- change: function(f, value) {
- if (!me.rendered) {
- return;
- }
- me.down('field[name=oath_step]').setVisible(value === 'oath');
- me.down('field[name=oath_digits]').setVisible(value === 'oath');
- me.down('field[name=yubico_api_id]').setVisible(value === 'yubico');
- me.down('field[name=yubico_api_key]').setVisible(value === 'yubico');
- me.down('field[name=yubico_url]').setVisible(value === 'yubico');
- }
- }
- });
-
- column2.push({
- xtype: 'numberfield',
- name: 'oath_step',
- value: '',
- minValue: 10,
- step: 1,
- allowDecimals: false,
- allowBlank: true,
- emptyText: PVE.Utils.defaultText + ' (30)',
- submitEmptyText: false,
- hidden: true,
- fieldLabel: 'OATH time step'
- });
-
- column2.push({
- xtype: 'numberfield',
- name: 'oath_digits',
- value: '',
- minValue: 6,
- maxValue: 8,
- step: 1,
- allowDecimals: false,
- allowBlank: true,
- emptyText: PVE.Utils.defaultText + ' (6)',
- submitEmptyText: false,
- hidden: true,
- fieldLabel: 'OATH password length'
- });
-
- column2.push({
- xtype: 'textfield',
- name: 'yubico_api_id',
- hidden: true,
- fieldLabel: 'Yubico API Id'
- });
-
- column2.push({
- xtype: 'textfield',
- name: 'yubico_api_key',
- hidden: true,
- fieldLabel: 'Yubico API Key'
- });
-
- column2.push({
- xtype: 'textfield',
- name: 'yubico_url',
- hidden: true,
- fieldLabel: 'Yubico URL'
- });
-
- var ipanel = Ext.create('PVE.panel.InputPanel', {
- column1: column1,
- column2: column2,
- columnB: [{
- xtype: 'textfield',
- name: 'comment',
- fieldLabel: gettext('Comment')
- }],
- onGetValues: function(values) {
- if (!values.port) {
- if (!me.create) {
- PVE.Utils.assemble_field_data(values, { 'delete': 'port' });
- }
- delete values.port;
- }
-
- if (me.create) {
- values.type = me.authType;
- }
-
- if (values.tfa === 'oath') {
- values.tfa = "type=oath";
- if (values.oath_step) {
- values.tfa += ",step=" + values.oath_step;
- }
- if (values.oath_digits) {
- values.tfa += ",digits=" + values.oath_digits;
- }
- } else if (values.tfa === 'yubico') {
- values.tfa = "type=yubico";
- values.tfa += ",id=" + values.yubico_api_id;
- values.tfa += ",key=" + values.yubico_api_key;
- if (values.yubico_url) {
- values.tfa += ",url=" + values.yubico_url;
- }
- } else {
- delete values.tfa;
- }
-
- delete values.oath_step;
- delete values.oath_digits;
- delete values.yubico_api_id;
- delete values.yubico_api_key;
- delete values.yubico_url;
-
- return values;
- }
- });
-
- Ext.applyIf(me, {
- url: url,
- method: method,
- fieldDefaults: {
- labelWidth: 120
- },
- items: [ ipanel ]
- });
-
- me.callParent();
-
- if (!me.create) {
- me.load({
- success: function(response, options) {
- var data = response.result.data || {};
- // just to be sure (should not happen)
- if (data.type !== me.authType) {
- me.close();
- throw "got wrong auth type";
- }
-
- if (data.tfa) {
- var tfacfg = PVE.Parser.parseTfaConfig(data.tfa);
- data.tfa = tfacfg.type;
- if (tfacfg.type === 'yubico') {
- data.yubico_api_key = tfacfg.key;
- data.yubico_api_id = tfacfg.id;
- data.yubico_url = tfacfg.url;
- } else if (tfacfg.type === 'oath') {
- data.oath_step = tfacfg.step;
- data.oath_digits = tfacfg.digits;
- }
- }
-
- me.setValues(data);
- }
- });
- }
- }
-});
+++ /dev/null
-Ext.define('PVE.dc.AuthView', {
- extend: 'Ext.grid.GridPanel',
-
- alias: ['widget.pveAuthView'],
-
- initComponent : function() {
- var me = this;
-
- var store = new Ext.data.Store({
- model: 'pve-domains',
- sorters: {
- property: 'realm',
- order: 'DESC'
- }
- });
-
- var reload = function() {
- store.load();
- };
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
-
- var win = Ext.create('PVE.dc.AuthEdit',{
- realm: rec.data.realm,
- authType: rec.data.type
- });
- win.on('destroy', reload);
- win.show();
- };
-
- var edit_btn = new PVE.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- handler: run_editor
- });
-
- var remove_btn = new PVE.button.Button({
- text: gettext('Remove'),
- disabled: true,
- selModel: sm,
- confirmMsg: function (rec) {
- return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
- "'" + rec.data.realm + "'");
- },
- enableFn: function(rec) {
- return !(rec.data.type === 'pve' || rec.data.type === 'pam');
- },
- handler: function(btn, event, rec) {
- var realm = rec.data.realm;
-
- PVE.Utils.API2Request({
- url: '/access/domains/' + realm,
- method: 'DELETE',
- waitMsgTarget: me,
- callback: function() {
- reload();
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- }
- });
-
- var tbar = [
- {
- text: gettext('Add'),
- menu: new Ext.menu.Menu({
- items: [
- {
- text: gettext('Active Directory Server'),
- handler: function() {
- var win = Ext.create('PVE.dc.AuthEdit', {
- authType: 'ad'
- });
- win.on('destroy', reload);
- win.show();
- }
- },
- {
- text: gettext('LDAP Server'),
- handler: function() {
- var win = Ext.create('PVE.dc.AuthEdit',{
- authType: 'ldap'
- });
- win.on('destroy', reload);
- win.show();
- }
- }
- ]
- })
- },
- edit_btn, remove_btn
- ];
-
- Ext.apply(me, {
- store: store,
- selModel: sm,
- stateful: false,
- tbar: tbar,
- viewConfig: {
- trackOver: false
- },
- columns: [
- {
- header: gettext('Realm'),
- width: 100,
- sortable: true,
- dataIndex: 'realm'
- },
- {
- header: gettext('Type'),
- width: 100,
- sortable: true,
- dataIndex: 'type'
- },
- {
- header: gettext('TFA'),
- width: 100,
- sortable: true,
- dataIndex: 'tfa'
- },
- {
- id: 'comment',
- header: gettext('Comment'),
- sortable: false,
- dataIndex: 'comment',
- flex: 1
- }
- ],
- listeners: {
- show: reload,
- itemdblclick: run_editor
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.dc.BackupEdit', {
- extend: 'PVE.window.Edit',
- alias: ['widget.pveDcBackupEdit'],
-
- initComponent : function() {
- /*jslint confusion: true */
- var me = this;
-
- me.create = !me.jobid;
-
- var url;
- var method;
-
- if (me.create) {
- url = '/api2/extjs/cluster/backup';
- method = 'POST';
- } else {
- url = '/api2/extjs/cluster/backup/' + me.jobid;
- method = 'PUT';
- }
-
- var vmidField = Ext.create('Ext.form.field.Hidden', {
- name: 'vmid'
- });
-
- var selModeField = Ext.create('PVE.form.KVComboBox', {
- xtype: 'pveKVComboBox',
- data: [
- ['include', gettext('Include selected VMs')],
- ['all', gettext('All')],
- ['exclude', gettext('Exclude selected VMs')]
- ],
- fieldLabel: gettext('Selection mode'),
- name: 'selMode',
- value: ''
- });
-
- var insideUpdate = false;
-
- var sm = Ext.create('Ext.selection.CheckboxModel', {
- mode: 'SIMPLE',
- listeners: {
- selectionchange: function(model, selected) {
- if (!insideUpdate) { // avoid endless loop
- var sel = [];
- Ext.Array.each(selected, function(record) {
- sel.push(record.data.vmid);
- });
-
- insideUpdate = true;
- vmidField.setValue(sel);
- insideUpdate = false;
- }
- }
- }
- });
-
- var storagesel = Ext.create('PVE.form.StorageSelector', {
- fieldLabel: gettext('Storage'),
- nodename: 'localhost',
- storageContent: 'backup',
- allowBlank: false,
- name: 'storage'
- });
-
- var store = new Ext.data.Store({
- model: 'PVEResources',
- sorters: {
- property: 'vmid',
- order: 'ASC'
- }
- });
-
- var vmgrid = Ext.createWidget('grid', {
- store: store,
- border: true,
- height: 300,
- selModel: sm,
- disabled: true,
- columns: [
- {
- header: 'ID',
- dataIndex: 'vmid',
- width: 60
- },
- {
- header: gettext('Node'),
- dataIndex: 'node'
- },
- {
- header: gettext('Status'),
- dataIndex: 'uptime',
- renderer: function(value) {
- if (value) {
- return PVE.Utils.runningText;
- } else {
- return PVE.Utils.stoppedText;
- }
- }
- },
- {
- header: gettext('Name'),
- dataIndex: 'name',
- flex: 1
- },
- {
- header: gettext('Type'),
- dataIndex: 'type'
- }
- ]
- });
-
- var nodesel = Ext.create('PVE.form.NodeSelector', {
- name: 'node',
- fieldLabel: gettext('Node'),
- allowBlank: true,
- editable: true,
- autoSelect: false,
- emptyText: '-- ' + gettext('All') + ' --',
- listeners: {
- change: function(f, value) {
- storagesel.setNodename(value || 'localhost');
- var mode = selModeField.getValue();
- store.clearFilter();
- store.filterBy(function(rec) {
- return (!value || rec.get('node') === value);
- });
- if (mode === 'all') {
- sm.selectAll(true);
- }
- }
- }
- });
-
- var column1 = [
- nodesel,
- storagesel,
- {
- xtype: 'pveDayOfWeekSelector',
- name: 'dow',
- fieldLabel: gettext('Day of week'),
- multiSelect: true,
- value: ['sat'],
- allowBlank: false
- },
- {
- xtype: 'timefield',
- fieldLabel: gettext('Start Time'),
- name: 'starttime',
- format: 'H:i',
- value: '00:00',
- allowBlank: false
- },
- selModeField
- ];
-
- var column2 = [
- {
- xtype: 'textfield',
- fieldLabel: gettext('Send email to'),
- name: 'mailto'
- },
- {
- xtype: 'pveEmailNotificationSelector',
- fieldLabel: gettext('Email notification'),
- name: 'mailnotification',
- deleteEmpty: me.create ? false : true,
- value: me.create ? 'always' : ''
- },
- {
- xtype: 'pveCompressionSelector',
- fieldLabel: gettext('Compression'),
- name: 'compress',
- deleteEmpty: me.create ? false : true,
- value: me.create ? 'lzo' : ''
- },
- {
- xtype: 'pveBackupModeSelector',
- fieldLabel: gettext('Mode'),
- value: 'snapshot',
- name: 'mode'
- },
- vmidField
- ];
-
- var ipanel = Ext.create('PVE.panel.InputPanel', {
- column1: column1,
- column2: column2,
- onGetValues: function(values) {
- if (!values.node) {
- if (!me.create) {
- PVE.Utils.assemble_field_data(values, { 'delete': 'node' });
- }
- delete values.node;
- }
-
- var selMode = values.selMode;
- delete values.selMode;
-
- if (selMode === 'all') {
- values.all = 1;
- values.exclude = '';
- delete values.vmid;
- } else if (selMode === 'exclude') {
- values.all = 1;
- values.exclude = values.vmid;
- delete values.vmid;
- }
- return values;
- }
- });
-
- var update_vmid_selection = function(list, mode) {
- if (insideUpdate) {
- return; // should not happen - just to be sure
- }
- insideUpdate = true;
- if (mode !== 'all') {
- sm.deselectAll(true);
- if (list) {
- Ext.Array.each(list.split(','), function(vmid) {
- var rec = store.findRecord('vmid', vmid);
- if (rec) {
- sm.select(rec, true);
- }
- });
- }
- }
- insideUpdate = false;
- };
-
- vmidField.on('change', function(f, value) {
- var mode = selModeField.getValue();
- update_vmid_selection(value, mode);
- });
-
- selModeField.on('change', function(f, value, oldValue) {
- if (value === 'all') {
- sm.selectAll(true);
- vmgrid.setDisabled(true);
- } else {
- vmgrid.setDisabled(false);
- }
- if (oldValue === 'all') {
- sm.deselectAll(true);
- vmidField.setValue('');
- }
- var list = vmidField.getValue();
- update_vmid_selection(list, value);
- });
-
- var reload = function() {
- store.load({
- params: { type: 'vm' },
- callback: function() {
- var node = nodesel.getValue();
- store.clearFilter();
- store.filterBy(function(rec) {
- return (!node || rec.get('node') === node);
- });
- var list = vmidField.getValue();
- var mode = selModeField.getValue();
- if (mode === 'all') {
- sm.selectAll(true);
- } else {
- update_vmid_selection(list, mode);
- }
- }
- });
- };
-
- Ext.applyIf(me, {
- subject: gettext("Backup Job"),
- url: url,
- method: method,
- items: [ ipanel, vmgrid ]
- });
-
- me.callParent();
-
- if (me.create) {
- selModeField.setValue('include');
- } else {
- me.load({
- success: function(response, options) {
- var data = response.result.data;
-
- data.dow = data.dow.split(',');
-
- if (data.all || data.exclude) {
- if (data.exclude) {
- data.vmid = data.exclude;
- data.selMode = 'exclude';
- } else {
- data.vmid = '';
- data.selMode = 'all';
- }
- } else {
- data.selMode = 'include';
- }
-
- me.setValues(data);
- }
- });
- }
-
- reload();
- }
-});
-
-
-Ext.define('PVE.dc.BackupView', {
- extend: 'Ext.grid.GridPanel',
-
- alias: ['widget.pveDcBackupView'],
-
- allText: '-- ' + gettext('All') + ' --',
- allExceptText: gettext('All except {0}'),
-
- initComponent : function() {
- var me = this;
-
- var store = new Ext.data.Store({
- model: 'pve-cluster-backup',
- proxy: {
- type: 'pve',
- url: "/api2/json/cluster/backup"
- }
- });
-
- var reload = function() {
- store.load();
- };
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
-
- var win = Ext.create('PVE.dc.BackupEdit',{
- jobid: rec.data.id
- });
- win.on('destroy', reload);
- win.show();
- };
-
- var edit_btn = new PVE.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- handler: run_editor
- });
-
- var remove_btn = new PVE.button.Button({
- text: gettext('Remove'),
- disabled: true,
- selModel: sm,
- confirmMsg: gettext('Are you sure you want to remove this entry'),
- handler: function(btn, event, rec) {
- PVE.Utils.API2Request({
- url: '/cluster/backup/' + rec.data.id,
- method: 'DELETE',
- waitMsgTarget: me,
- callback: function() {
- reload();
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- }
- });
-
- PVE.Utils.monStoreErrors(me, store);
-
- Ext.apply(me, {
- store: store,
- selModel: sm,
- stateful: false,
- viewConfig: {
- trackOver: false
- },
- tbar: [
- {
- text: gettext('Add'),
- handler: function() {
- var win = Ext.create('PVE.dc.BackupEdit',{});
- win.on('destroy', reload);
- win.show();
- }
- },
- remove_btn,
- edit_btn
- ],
- columns: [
- {
- header: gettext('Node'),
- width: 100,
- sortable: true,
- dataIndex: 'node',
- renderer: function(value) {
- if (value) {
- return value;
- }
- return me.allText;
- }
- },
- {
- header: gettext('Day of week'),
- width: 200,
- sortable: false,
- dataIndex: 'dow'
- },
- {
- header: gettext('Start Time'),
- width: 60,
- sortable: true,
- dataIndex: 'starttime'
- },
- {
- header: gettext('Storage'),
- width: 100,
- sortable: true,
- dataIndex: 'storage'
- },
- {
- header: gettext('Selection'),
- flex: 1,
- sortable: false,
- dataIndex: 'vmid',
- renderer: function(value, metaData, record) {
- /*jslint confusion: true */
- if (record.data.all) {
- if (record.data.exclude) {
- return Ext.String.format(me.allExceptText, record.data.exclude);
- }
- return me.allText;
- }
- if (record.data.vmid) {
- return record.data.vmid;
- }
-
- return "-";
- }
- }
- ],
- listeners: {
- show: reload,
- itemdblclick: run_editor
- }
- });
-
- me.callParent();
- }
-}, function() {
-
- Ext.define('pve-cluster-backup', {
- extend: 'Ext.data.Model',
- fields: [
- 'id', 'starttime', 'dow',
- 'storage', 'node', 'vmid', 'exclude',
- 'mailto',
- { name: 'all', type: 'boolean' },
- { name: 'snapshot', type: 'boolean' },
- { name: 'stop', type: 'boolean' },
- { name: 'suspend', type: 'boolean' },
- { name: 'compress', type: 'boolean' }
- ]
- });
-});
\ No newline at end of file
+++ /dev/null
-/*
- * Datacenter config panel, located in the center of the ViewPort after the Datacenter view is selected
- */
-
-Ext.define('PVE.dc.Config', {
- extend: 'PVE.panel.Config',
- alias: 'widget.PVE.dc.Config',
-
- initComponent: function() {
- var me = this;
-
- var caps = Ext.state.Manager.get('GuiCap');
-
- me.items = [];
-
- Ext.apply(me, {
- title: gettext("Datacenter"),
- hstateid: 'dctab'
- });
-
- if (caps.dc['Sys.Audit']) {
- me.items.push({
- title: gettext('Summary'),
- xtype: 'pveDcSummary',
- itemId: 'summary'
- });
-
- me.items.push({
- xtype: 'pveDcOptionView',
- title: gettext('Options'),
- itemId: 'options'
- });
- }
-
- if (caps.storage['Datastore.Allocate'] || caps.dc['Sys.Audit']) {
- me.items.push({
- xtype: 'pveStorageView',
- title: gettext('Storage'),
- itemId: 'storage'
- });
- }
-
- if (caps.dc['Sys.Audit']) {
- me.items.push({
- xtype: 'pveDcBackupView',
- title: gettext('Backup'),
- itemId: 'backup'
- });
- }
-
- me.items.push({
- xtype: 'pveUserView',
- title: gettext('Users'),
- itemId: 'users'
- });
-
- if (caps.dc['Sys.Audit']) {
- me.items.push({
- xtype: 'pveGroupView',
- title: gettext('Groups'),
- itemId: 'groups'
- });
-
- me.items.push({
- xtype: 'pvePoolView',
- title: gettext('Pools'),
- itemId: 'pools'
- });
-
- me.items.push({
- xtype: 'pveACLView',
- title: gettext('Permissions'),
- itemId: 'permissions'
- });
-
- me.items.push({
- xtype: 'pveRoleView',
- title: gettext('Roles'),
- itemId: 'roles'
- });
-
- me.items.push({
-// xtype: 'pveAuthView',
-// title: gettext('Authentication'),
- xtype: 'gridpanel',
- title: 'AuthenTODO',
- itemId: 'domains'
- });
-
- me.items.push({
-// xtype: 'pveHAPanel',
-// title: 'HA',
- xtype: 'gridpanel',
- title: 'HATODO',
- phstateid: me.hstateid,
- itemId: 'ha'
- });
-
- me.items.push({
-// xtype: 'pveFirewallPanel',
-// title: gettext('Firewall'),
- xtype: 'gridpanel',
- title: 'FireTODO',
- base_url: '/cluster/firewall',
- fwtype: 'dc',
- phstateid: me.hstateid,
- itemId: 'firewall'
- });
-
- me.items.push({
- xtype: 'pveDcSupport',
- title: gettext('Support'),
- itemId: 'support'
- });
- }
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.dc.GroupEdit', {
- extend: 'PVE.window.Edit',
- alias: ['widget.pveDcGroupEdit'],
-
- initComponent : function() {
- var me = this;
-
- me.create = !me.groupid;
-
- var url;
- var method;
-
- if (me.create) {
- url = '/api2/extjs/access/groups';
- method = 'POST';
- } else {
- url = '/api2/extjs/access/groups/' + me.groupid;
- method = 'PUT';
- }
-
- Ext.applyIf(me, {
- subject: gettext('Group'),
- url: url,
- method: method,
- items: [
- {
- xtype: me.create ? 'pvetextfield' : 'displayfield',
- fieldLabel: gettext('Name'),
- name: 'groupid',
- value: me.groupid,
- allowBlank: false
- },
- {
- xtype: 'textfield',
- fieldLabel: gettext('Comment'),
- name: 'comment',
- allowBlank: true
- }
- ]
- });
-
- me.callParent();
-
- if (!me.create) {
- me.load();
- }
- }
-});
+++ /dev/null
-Ext.define('PVE.dc.GroupView', {
- extend: 'Ext.grid.GridPanel',
-
- alias: ['widget.pveGroupView'],
-
- initComponent : function() {
- var me = this;
-
- var store = new Ext.data.Store({
- model: 'pve-groups',
- sorters: {
- property: 'groupid',
- order: 'DESC'
- }
- });
-
- var reload = function() {
- store.load();
- };
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var remove_btn = new PVE.button.Button({
- text: gettext('Remove'),
- disabled: true,
- selModel: sm,
- confirmMsg: function (rec) {
- return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
- "'" + rec.data.groupid + "'");
- },
- handler: function(btn, event, rec) {
- PVE.Utils.API2Request({
- url: '/access/groups/' + rec.data.groupid,
- method: 'DELETE',
- waitMsgTarget: me,
- callback: function() {
- reload();
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- }
- });
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
-
- var win = Ext.create('PVE.dc.GroupEdit',{
- groupid: rec.data.groupid
- });
- win.on('destroy', reload);
- win.show();
- };
-
- var edit_btn = new PVE.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- handler: run_editor
- });
-
- var tbar = [
- {
- text: gettext('Create'),
- handler: function() {
- var win = Ext.create('PVE.dc.GroupEdit', {});
- win.on('destroy', reload);
- win.show();
- }
- },
- edit_btn, remove_btn
- ];
-
- PVE.Utils.monStoreErrors(me, store);
-
- Ext.apply(me, {
- store: store,
- selModel: sm,
- stateful: false,
- tbar: tbar,
- viewConfig: {
- trackOver: false
- },
- columns: [
- {
- header: gettext('Name'),
- width: 200,
- sortable: true,
- dataIndex: 'groupid'
- },
- {
- header: gettext('Comment'),
- sortable: false,
- dataIndex: 'comment',
- flex: 1
- }
- ],
- listeners: {
- show: reload,
- itemdblclick: run_editor
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-/* This class defines the "Cluster log" tab of the bottom status panel
- * A log entry is a timestamp associated with an action on a cluster
- */
-
-Ext.define('PVE.dc.Log', {
- extend: 'Ext.grid.GridPanel',
-
- alias: ['widget.pveClusterLog'],
-
- initComponent : function() {
- var me = this;
-
- var logstore = new PVE.data.UpdateStore({
- storeid: 'pve-cluster-log',
- model: 'pve-cluster-log',
- proxy: {
- type: 'pve',
- url: '/api2/json/cluster/log'
- }
- });
-
- var store = Ext.create('PVE.data.DiffStore', {
- rstore: logstore,
- appendAtStart: true
- });
-
- Ext.apply(me, {
- store: store,
- stateful: false,
-
- viewConfig: {
- trackOver: false,
- stripeRows: true,
-
- getRowClass: function(record, index) {
- var pri = record.get('pri');
-
- if (pri && pri <= 3) {
- return "x-form-invalid-field";
- }
- }
- },
- sortableColumns: false,
- columns: [
- {
- header: gettext("Time"),
- dataIndex: 'time',
- width: 150,
- renderer: function(value) {
- return Ext.Date.format(value, "M d H:i:s");
- }
- },
- {
- header: gettext("Node"),
- dataIndex: 'node',
- width: 150
- },
- {
- header: gettext("Service"),
- dataIndex: 'tag',
- width: 100
- },
- {
- header: "PID",
- dataIndex: 'pid',
- width: 100
- },
- {
- header: gettext("User name"),
- dataIndex: 'user',
- width: 150
- },
- {
- header: gettext("Severity"),
- dataIndex: 'pri',
- renderer: PVE.Utils.render_serverity,
- width: 100
- },
- {
- header: gettext("Message"),
- dataIndex: 'msg',
- flex: 1
- }
- ],
- listeners: {
- show: logstore.startUpdate,
- hide: logstore.stopUpdate,
- destroy: logstore.stopUpdate
- }
- });
-
- me.callParent();
- }
-});
\ No newline at end of file
+++ /dev/null
-Ext.define('PVE.dc.HttpProxyEdit', {
- extend: 'PVE.window.Edit',
-
- initComponent : function() {
- var me = this;
-
- Ext.applyIf(me, {
- subject: gettext('HTTP proxy'),
- items: {
- xtype: 'pvetextfield',
- name: 'http_proxy',
- vtype: 'HttpProxy',
- emptyText: gettext('Do not use any proxy'),
- deleteEmpty: true,
- value: '',
- fieldLabel: gettext('HTTP proxy')
- }
- });
-
- me.callParent();
-
- me.load();
- }
-});
-
-Ext.define('PVE.dc.KeyboardEdit', {
- extend: 'PVE.window.Edit',
-
- initComponent : function() {
- var me = this;
-
- Ext.applyIf(me, {
- subject: gettext('Keyboard Layout'),
- items: {
- xtype: 'VNCKeyboardSelector',
- name: 'keyboard',
- value: '',
- fieldLabel: gettext('Keyboard Layout')
- }
- });
-
- me.callParent();
-
- me.load();
- }
-});
-
-Ext.define('PVE.dc.ConsoleViewerEdit', {
- extend: 'PVE.window.Edit',
-
- initComponent : function() {
- var me = this;
-
- var data = [];
-
- Ext.Array.each(['', 'vv', 'html5'], function(value) {
- data.push([value, PVE.Utils.render_console_viewer(value)]);
- });
-
- Ext.applyIf(me, {
- subject: gettext('Console Viewer'),
- items: {
- xtype: 'pveKVComboBox',
- name: 'console',
- value: '',
- fieldLabel: gettext('Console Viewer'),
- comboItems: data,
- }
- });
-
- me.callParent();
-
- me.load();
- }
-});
-
-Ext.define('PVE.dc.EmailFromEdit', {
- extend: 'PVE.window.Edit',
-
- initComponent : function() {
- var me = this;
-
- Ext.applyIf(me, {
- subject: gettext('Email from address'),
- items: {
- xtype: 'pvetextfield',
- name: 'email_from',
- vtype: 'email',
- emptyText: gettext('Send emails from root@$hostname'),
- deleteEmpty: true,
- value: '',
- fieldLabel: gettext('Email from address')
- }
- });
-
- me.callParent();
-
- me.load();
- }
-});
-
-Ext.define('PVE.dc.OptionView', {
- extend: 'PVE.grid.ObjectGrid',
- alias: ['widget.pveDcOptionView'],
-
- noProxyText: gettext('Do not use any proxy'),
- noEmailFromText: gettext('Send emails from root@$hostname'),
-
- initComponent : function() {
- var me = this;
-
- var reload = function() {
- me.rstore.load();
- };
-
- var rows = {
- keyboard: {
- header: gettext('Keyboard Layout'),
- editor: 'PVE.dc.KeyboardEdit',
- renderer: PVE.Utils.render_kvm_language,
- required: true
- },
- http_proxy: {
- header: gettext('HTTP proxy'),
- editor: 'PVE.dc.HttpProxyEdit',
- required: true,
- renderer: function(value) {
- if (!value) {
- return me.noProxyText;
- }
- return value;
- }
- },
- console: {
- header: gettext('Console Viewer'),
- editor: 'PVE.dc.ConsoleViewerEdit',
- required: true,
- renderer: PVE.Utils.render_console_viewer
- },
- email_from: {
- header: gettext('Email from address'),
- editor: 'PVE.dc.EmailFromEdit',
- required: true,
- renderer: function(value) {
- if (!value) {
- return me.noEmailFromText;
- }
- return value;
- }
- }
- };
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
-
- var rowdef = rows[rec.data.key];
- if (!rowdef.editor) {
- return;
- }
-
- var win = Ext.create(rowdef.editor, {
- url: "/api2/extjs/cluster/options",
- confid: rec.data.key
- });
- win.show();
- win.on('destroy', reload);
- };
-
- var edit_btn = new PVE.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- handler: run_editor
- });
-
- Ext.applyIf(me, {
- url: "/api2/json/cluster/options",
- cwidth1: 130,
- interval: 1000,
- selModel: sm,
- tbar: [ edit_btn ],
- rows: rows,
- listeners: {
- itemdblclick: run_editor
- }
- });
-
- me.callParent();
-
- me.on('show', reload);
- }
-});
+++ /dev/null
-Ext.define('PVE.dc.PoolEdit', {
- extend: 'PVE.window.Edit',
- alias: ['widget.pveDcPoolEdit'],
-
- initComponent : function() {
- var me = this;
-
- me.create = !me.poolid;
-
- var url;
- var method;
-
- if (me.create) {
- url = '/api2/extjs/pools';
- method = 'POST';
- } else {
- url = '/api2/extjs/pools/' + me.poolid;
- method = 'PUT';
- }
-
- Ext.applyIf(me, {
- subject: gettext('Pool'),
- url: url,
- method: method,
- items: [
- {
- xtype: me.create ? 'pvetextfield' : 'displayfield',
- fieldLabel: gettext('Name'),
- name: 'poolid',
- value: me.poolid,
- allowBlank: false
- },
- {
- xtype: 'textfield',
- fieldLabel: gettext('Comment'),
- name: 'comment',
- allowBlank: true
- }
- ]
- });
-
- me.callParent();
-
- if (!me.create) {
- me.load();
- }
- }
-});
+++ /dev/null
-Ext.define('PVE.dc.PoolView', {
- extend: 'Ext.grid.GridPanel',
-
- alias: ['widget.pvePoolView'],
-
- initComponent : function() {
- var me = this;
-
- var store = new Ext.data.Store({
- model: 'pve-pools',
- sorters: {
- property: 'poolid',
- order: 'DESC'
- }
- });
-
- var reload = function() {
- store.load();
- };
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var remove_btn = new PVE.button.Button({
- text: gettext('Remove'),
- disabled: true,
- selModel: sm,
- confirmMsg: function (rec) {
- return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
- "'" + rec.data.poolid + "'");
- },
- handler: function(btn, event, rec) {
- PVE.Utils.API2Request({
- url: '/pools/' + rec.data.poolid,
- method: 'DELETE',
- waitMsgTarget: me,
- callback: function() {
- reload();
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- }
- });
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
-
- var win = Ext.create('PVE.dc.PoolEdit',{
- poolid: rec.data.poolid
- });
- win.on('destroy', reload);
- win.show();
- };
-
- var edit_btn = new PVE.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- handler: run_editor
- });
-
- var tbar = [
- {
- text: gettext('Create'),
- handler: function() {
- var win = Ext.create('PVE.dc.PoolEdit', {});
- win.on('destroy', reload);
- win.show();
- }
- },
- edit_btn, remove_btn
- ];
-
- PVE.Utils.monStoreErrors(me, store);
-
- Ext.apply(me, {
- store: store,
- selModel: sm,
- stateful: false,
- tbar: tbar,
- viewConfig: {
- trackOver: false
- },
- columns: [
- {
- header: gettext('Name'),
- width: 200,
- sortable: true,
- dataIndex: 'poolid'
- },
- {
- header: gettext('Comment'),
- sortable: false,
- dataIndex: 'comment',
- flex: 1
- }
- ],
- listeners: {
- show: reload,
- itemdblclick: run_editor
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.dc.RoleView', {
- extend: 'Ext.grid.GridPanel',
-
- alias: ['widget.pveRoleView'],
-
- initComponent : function() {
- var me = this;
-
- var store = new Ext.data.Store({
- model: 'pve-roles',
- sorters: {
- property: 'roleid',
- order: 'DESC'
- }
- });
-
- var render_privs = function(value, metaData) {
-
- if (!value) {
- return '-';
- }
-
- // allow word wrap
- metaData.style = 'white-space:normal;';
-
- return value.replace(/\,/g, ' ');
- };
-
- PVE.Utils.monStoreErrors(me, store);
-
- Ext.apply(me, {
- store: store,
- stateful: false,
-
- viewConfig: {
- trackOver: false
- },
- columns: [
- {
- header: gettext('Name'),
- width: 150,
- sortable: true,
- dataIndex: 'roleid'
- },
- {
- id: 'privs',
- header: gettext('Privileges'),
- sortable: false,
- renderer: render_privs,
- dataIndex: 'privs',
- flex: 1
- }
- ],
- listeners: {
- show: function() {
- store.load();
- }
- }
- });
-
- me.callParent();
- }
-});
\ No newline at end of file
+++ /dev/null
-Ext.define('PVE.SecurityGroupEdit', {
- extend: 'PVE.window.Edit',
-
- base_url: "/cluster/firewall/groups",
-
- allow_iface: false,
-
- initComponent : function() {
- /*jslint confusion: true */
- var me = this;
-
- me.create = (me.group_name === undefined);
-
- var subject;
-
- me.url = '/api2/extjs' + me.base_url;
- me.method = 'POST';
-
- var items = [
- {
- xtype: 'textfield',
- name: 'group',
- value: me.group_name || '',
- fieldLabel: gettext('Name'),
- allowBlank: false
- },
- {
- xtype: 'textfield',
- name: 'comment',
- value: me.group_comment || '',
- fieldLabel: gettext('Comment')
- }
- ];
-
- if (me.create) {
- subject = gettext('Security Group');
- } else {
- subject = gettext('Security Group') + " '" + me.group_name + "'";
- items.push({
- xtype: 'hiddenfield',
- name: 'rename',
- value: me.group_name
- });
- }
-
- var ipanel = Ext.create('PVE.panel.InputPanel', {
- create: me.create,
- items: items
- });
-
-
- Ext.apply(me, {
- subject: subject,
- items: [ ipanel ]
- });
-
- me.callParent();
- }
-});
-
-Ext.define('PVE.SecurityGroupList', {
- extend: 'Ext.grid.Panel',
- alias: 'widget.pveSecurityGroupList',
-
- rule_panel: undefined,
-
- addBtn: undefined,
- removeBtn: undefined,
- editBtn: undefined,
-
- base_url: "/cluster/firewall/groups",
-
- initComponent: function() {
- /*jslint confusion: true */
- var me = this;
-
- if (me.rule_panel == undefined) {
- throw "no rule panel specified";
- }
-
- if (me.base_url == undefined) {
- throw "no base_url specified";
- }
-
- var store = new Ext.data.Store({
- fields: [ 'group', 'comment', 'digest' ],
- proxy: {
- type: 'pve',
- url: '/api2/json' + me.base_url
- },
- idProperty: 'group',
- sorters: {
- property: 'group',
- order: 'DESC'
- }
- });
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var reload = function() {
- var oldrec = sm.getSelection()[0];
- store.load(function(records, operation, success) {
- if (oldrec) {
- var rec = store.findRecord('group', oldrec.data.group);
- if (rec) {
- sm.select(rec);
- }
- }
- });
- };
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var win = Ext.create('PVE.SecurityGroupEdit', {
- digest: rec.data.digest,
- group_name: rec.data.group,
- group_comment: rec.data.comment
- });
- win.show();
- win.on('destroy', reload);
- };
-
- me.editBtn = new PVE.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- handler: run_editor
- });
-
- me.addBtn = new PVE.button.Button({
- text: gettext('Create'),
- handler: function() {
- sm.deselectAll();
- var win = Ext.create('PVE.SecurityGroupEdit', {});
- win.show();
- win.on('destroy', reload);
- }
- });
-
- me.removeBtn = new PVE.button.Button({
- text: gettext('Remove'),
- selModel: sm,
- disabled: true,
- handler: function() {
- var rec = sm.getSelection()[0];
- if (!rec || !me.base_url) {
- return;
- }
- PVE.Utils.API2Request({
- url: me.base_url + '/' + rec.data.group,
- method: 'DELETE',
- waitMsgTarget: me,
- failure: function(response, options) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- callback: reload
- });
- }
- });
-
- Ext.apply(me, {
- store: store,
- tbar: [ '<b>' + gettext('Group') + ':</b>', me.addBtn, me.removeBtn, me.editBtn ],
- selModel: sm,
- columns: [
- { header: gettext('Group'), dataIndex: 'group', width: 100 },
- { header: gettext('Comment'), dataIndex: 'comment', flex: 1 }
- ],
- listeners: {
- itemdblclick: run_editor,
- select: function(sm, rec) {
- var url = '/cluster/firewall/groups/' + rec.data.group;
- me.rule_panel.setBaseUrl(url);
- },
- deselect: function() {
- me.rule_panel.setBaseUrl(undefined);
- },
- show: reload
- }
- });
-
- me.callParent();
-
- store.load();
- }
-});
-
-Ext.define('PVE.SecurityGroups', {
- extend: 'Ext.panel.Panel',
- alias: 'widget.pveSecurityGroups',
-
- title: 'Security Groups',
-
- initComponent: function() {
- var me = this;
-
- var rule_panel = Ext.createWidget('pveFirewallRules', {
- region: 'center',
- allow_groups: false,
- list_refs_url: '/cluster/firewall/refs',
- tbar_prefix: '<b>' + gettext('Rules') + ':</b>',
- flex: 0.75,
- border: false
- });
-
- var sglist = Ext.createWidget('pveSecurityGroupList', {
- region: 'west',
- rule_panel: rule_panel,
- flex: 0.25,
- border: false,
- split: true
- });
-
-
- Ext.apply(me, {
- layout: 'border',
- items: [ sglist, rule_panel ],
- listeners: {
- show: function() {
- sglist.fireEvent('show', sglist);
- }
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-
-Ext.define('PVE.dc.StorageView', {
- extend: 'Ext.grid.GridPanel',
-
- alias: ['widget.pveStorageView'],
-
- initComponent : function() {
- var me = this;
-
- var store = new Ext.data.Store({
- model: 'pve-storage',
- proxy: {
- type: 'pve',
- url: "/api2/json/storage"
- },
- sorters: {
- property: 'storage',
- order: 'DESC'
- }
- });
-
- var reload = function() {
- store.load();
- };
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var type = rec.data.type;
-
- var editor;
- if (type === 'dir') {
- editor = 'PVE.storage.DirEdit';
- } else if (type === 'nfs') {
- editor = 'PVE.storage.NFSEdit';
- } else if (type === 'glusterfs') {
- editor = 'PVE.storage.GlusterFsEdit';
- } else if (type === 'lvm') {
- editor = 'PVE.storage.LVMEdit';
- } else if (type === 'iscsi') {
- editor = 'PVE.storage.IScsiEdit';
- } else if (type === 'rbd') {
- editor = 'PVE.storage.RBDEdit';
- } else if (type === 'sheepdog') {
- editor = 'PVE.storage.SheepdogEdit';
- } else if (type === 'zfs') {
- editor = 'PVE.storage.ZFSEdit';
- } else if (type === 'zfspool') {
- editor = 'PVE.storage.ZFSPoolEdit';
- } else {
- return;
- }
- var win = Ext.create(editor, {
- storageId: rec.data.storage
- });
-
- win.show();
- win.on('destroy', reload);
- };
-
- var edit_btn = new PVE.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- handler: run_editor
- });
-
- var remove_btn = new PVE.button.Button({
- text: gettext('Remove'),
- disabled: true,
- selModel: sm,
- confirmMsg: function (rec) {
- return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
- "'" + rec.data.storage + "'");
- },
- handler: function(btn, event, rec) {
- PVE.Utils.API2Request({
- url: '/storage/' + rec.data.storage,
- method: 'DELETE',
- waitMsgTarget: me,
- callback: function() {
- reload();
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- }
- });
-
- Ext.apply(me, {
- store: store,
- selModel: sm,
- stateful: false,
- viewConfig: {
- trackOver: false
- },
- tbar: [
- {
- text: gettext('Add'),
- menu: new Ext.menu.Menu({
- items: [
- {
- text: PVE.Utils.format_storage_type('dir'),
- iconCls: 'pve-itype-icon-itype',
- handler: function() {
- var win = Ext.create('PVE.storage.DirEdit', {});
- win.on('destroy', reload);
- win.show();
- }
-
- },
- {
- text: PVE.Utils.format_storage_type('lvm'),
- iconCls: 'pve-itype-icon-storage',
- handler: function() {
- var win = Ext.create('PVE.storage.LVMEdit', {});
- win.on('destroy', reload);
- win.show();
- }
- },
- {
- text: PVE.Utils.format_storage_type('nfs'),
- iconCls: 'pve-itype-icon-network-server',
- handler: function() {
- var win = Ext.create('PVE.storage.NFSEdit', {});
- win.on('destroy', reload);
- win.show();
- }
- },
- {
- text: PVE.Utils.format_storage_type('iscsi'),
- iconCls: 'pve-itype-icon-network-server',
- handler: function() {
- var win = Ext.create('PVE.storage.IScsiEdit', {});
- win.on('destroy', reload);
- win.show();
- }
- },
- {
- text: PVE.Utils.format_storage_type('glusterfs'),
- iconCls: 'pve-itype-icon-network-server',
- handler: function() {
- var win = Ext.create('PVE.storage.GlusterFsEdit', {});
- win.on('destroy', reload);
- win.show();
- }
- },
- {
- text: PVE.Utils.format_storage_type('rbd'),
- iconCls: 'pve-itype-icon-network-server',
- handler: function() {
- var win = Ext.create('PVE.storage.RBDEdit', {});
- win.on('destroy', reload);
- win.show();
- }
- },
- {
- text: PVE.Utils.format_storage_type('zfs'),
- iconCls: 'pve-itype-icon-network-server',
- handler: function() {
- var win = Ext.create('PVE.storage.ZFSEdit', {});
- win.on('destroy', reload);
- win.show();
- }
- },
- {
- text: PVE.Utils.format_storage_type('zfspool'),
- iconCls: 'pve-itype-icon-storage',
- handler: function() {
- var win = Ext.create('PVE.storage.ZFSPoolEdit', {});
- win.on('destroy', reload);
- win.show();
- }
- },
-
-/* the following type are conidered unstable
- * so we do not enable that on the GUI for now
- {
- text: PVE.Utils.format_storage_type('sheepdog'),
- iconCls: 'pve-itype-icon-network-server',
- handler: function() {
- var win = Ext.create('PVE.storage.SheepdogEdit', {});
- win.on('destroy', reload);
- win.show();
- }
- }
-*/
- ]
- })
- },
- remove_btn,
- edit_btn
- ],
- columns: [
- {
- header: 'ID',
- width: 100,
- sortable: true,
- dataIndex: 'storage'
- },
- {
- header: gettext('Type'),
- width: 60,
- sortable: true,
- dataIndex: 'type',
- renderer: PVE.Utils.format_storage_type
- },
- {
- header: gettext('Content'),
- width: 150,
- sortable: true,
- dataIndex: 'content',
- renderer: PVE.Utils.format_content_types
- },
- {
- header: gettext('Path') + '/' + gettext('Target'),
- flex: 1,
- sortable: true,
- dataIndex: 'path',
- renderer: function(value, metaData, record) {
- if (record.data.target) {
- return record.data.target;
- }
- return value;
- }
- },
- {
- header: gettext('Shared'),
- width: 80,
- sortable: true,
- dataIndex: 'shared',
- renderer: PVE.Utils.format_boolean
- },
- {
- header: gettext('Enable'),
- width: 80,
- sortable: true,
- dataIndex: 'disable',
- renderer: PVE.Utils.format_neg_boolean
- }
- ],
- listeners: {
- show: reload,
- itemdblclick: run_editor
- }
- });
-
- me.callParent();
- }
-}, function() {
-
- Ext.define('pve-storage', {
- extend: 'Ext.data.Model',
- fields: [
- 'path', 'type', 'content', 'server', 'portal', 'target', 'export', 'storage',
- { name: 'shared', type: 'boolean'},
- { name: 'disable', type: 'boolean'}
- ],
- idProperty: 'storage'
- });
-
-});
+++ /dev/null
-Ext.define('PVE.dc.NodeView', {
- extend: 'Ext.grid.GridPanel',
-
- alias: ['widget.pveDcNodeView'],
-
- initComponent : function() {
- var me = this;
-
- var rstore = Ext.create('PVE.data.UpdateStore', {
- interval: 3000,
- storeid: 'pve-dc-nodes',
- model: 'pve-dc-nodes',
- proxy: {
- type: 'pve',
- url: "/api2/json/cluster/status"
- },
- filters: {
- property: 'type',
- value : 'node'
- }
- });
-
- var store = Ext.create('PVE.data.DiffStore', { rstore: rstore });
-
- var noClusterText = gettext("Standalone node - no cluster defined");
- var status = Ext.create('Ext.Component', {
- padding: 2,
- html: ' ',
- dock: 'bottom'
- });
-
- Ext.apply(me, {
- store: store,
- stateful: false,
- bbar: [ status ],
- columns: [
- {
- header: gettext('Name'),
- width: 200,
- sortable: true,
- dataIndex: 'name'
- },
- {
- header: 'ID',
- width: 50,
- sortable: true,
- dataIndex: 'nodeid'
- },
- {
- header: gettext('Online'),
- width: 100,
- sortable: true,
- dataIndex: 'online',
- renderer: PVE.Utils.format_boolean
- },
- {
- header: gettext('Support'),
- width: 100,
- sortable: true,
- dataIndex: 'level',
- renderer: PVE.Utils.render_support_level
- },
- {
- header: gettext('Server Address'),
- flex: 1,
- sortable: true,
- dataIndex: 'ip'
- }
- ],
- listeners: {
- show: rstore.startUpdate,
- hide: rstore.stopUpdate,
- destroy: rstore.stopUpdate
- }
- });
-
- me.callParent();
-
- rstore.on('load', function(s, records, success) {
- if (!success) {
- return;
- }
-
- var cluster_rec = rstore.getById('cluster');
-
- if (!cluster_rec) {
- status.update(noClusterText);
- return;
- }
-
- var cluster_raw = cluster_rec.raw;
- if (!cluster_raw) {
- status.update(noClusterText);
- return;
- }
- var text = gettext("Cluster") + ": " + cluster_raw.name + ", " +
- gettext("Quorate") + ": " + PVE.Utils.format_boolean(cluster_raw.quorate);
- status.update(text);
- });
-
- }
-}, function() {
-
- Ext.define('pve-dc-nodes', {
- extend: 'Ext.data.Model',
- fields: [ 'id', 'type', 'name', 'nodeid', 'ip', 'level', 'local', 'online'],
- idProperty: 'id'
- });
-
-});
-
-Ext.define('PVE.dc.Summary', {
- extend: 'Ext.panel.Panel',
-
- alias: ['widget.pveDcSummary'],
-
- initComponent: function() {
- var me = this;
-
- var nodegrid = Ext.create('PVE.dc.NodeView', {
- title: gettext('Nodes'),
- border: false,
- region: 'center',
- flex: 3
- });
-
- Ext.apply(me, {
- layout: 'border',
- items: [ nodegrid ],
- listeners: {
- show: function() {
- nodegrid.fireEvent('show', nodegrid);
- },
- hide: function() {
- nodegrid.fireEvent('hide', nodegrid);
- }
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.dc.Support', {
- extend: 'Ext.panel.Panel',
- alias: 'widget.pveDcSupport',
-
- invalidHtml: '<h1>No valid subscription</h1>' + PVE.Utils.noSubKeyHtml,
-
- communityHtml: 'Please use the public community <a target="_blank" href="http://forum.proxmox.com">forum</a> for any questions.',
-
- activeHtml: 'Please use our <a target="_blank" href="https://my.proxmox.com">support portal</a> for any questions. You can also use the public community <a target="_blank" href="http://forum.proxmox.com">forum</a> to get additional information.',
-
- bugzillaHtml: '<h1>Bug Tracking</h1>Our bug tracking system is available <a target="_blank" href="https://bugzilla.proxmox.com">here</a>.',
-
- docuHtml: '<h1>Documentation</h1>Complete documentation, tutorials, videos and more is available at our <a target="_blank" href="http://pve.proxmox.com/wiki/Documentation">wiki</a>.',
-
- updateActive: function(data) {
- var me = this;
-
- var html = '<h1>' + data.productname + '</h1>' + me.activeHtml;
- html += '<br><br>' + me.docuHtml;
- html += '<br><br>' + me.bugzillaHtml;
-
- me.update(html);
- },
-
- updateCommunity: function(data) {
- var me = this;
-
- var html = '<h1>' + data.productname + '</h1>' + me.communityHtml;
- html += '<br><br>' + me.docuHtml;
- html += '<br><br>' + me.bugzillaHtml;
-
- me.update(html);
- },
-
- updateInactive: function(data) {
- var me = this;
- me.update(me.invalidHtml);
- },
-
- initComponent: function() {
- var me = this;
-
- var reload = function() {
- PVE.Utils.API2Request({
- url: '/nodes/localhost/subscription',
- method: 'GET',
- waitMsgTarget: me,
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- me.update(gettext('Unable to load subscription status') + ": " + response.htmlStatus);
- },
- success: function(response, opts) {
- var data = response.result.data;
-
- if (data.status === 'Active') {
- if (data.level === 'c') {
- me.updateCommunity(data);
- } else {
- me.updateActive(data);
- }
- } else {
- me.updateInactive(data);
- }
- }
- });
- };
-
- Ext.apply(me, {
- autoScroll: true,
- bodyStyle: 'padding:10px',
- listeners: {
- show: reload
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-/* This class defines the "Tasks" tab of the bottom status panel
- * Tasks are jobs with a start, end and log output
- */
-
-Ext.define('PVE.dc.Tasks', {
- extend: 'Ext.grid.GridPanel',
-
- alias: ['widget.pveClusterTasks'],
-
- initComponent : function() {
- var me = this;
-
- var taskstore = new PVE.data.UpdateStore({
- storeid: 'pve-cluster-tasks',
- model: 'pve-tasks',
- proxy: {
- type: 'pve',
- url: '/api2/json/cluster/tasks'
- }
- });
-
- var store = Ext.create('PVE.data.DiffStore', {
- rstore: taskstore,
- sortAfterUpdate: true,
- appendAtStart: true,
- sorters: [
- {
- property : 'pid',
- direction: 'DESC'
- },
- {
- property : 'starttime',
- direction: 'DESC'
- }
- ]
-
- });
-
- var run_task_viewer = function() {
- var sm = me.getSelectionModel();
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
-
- var win = Ext.create('PVE.window.TaskViewer', {
- upid: rec.data.upid
- });
- win.show();
- };
-
- Ext.apply(me, {
- store: store,
- stateful: false,
-
- viewConfig: {
- trackOver: false,
- stripeRows: true, // does not work with getRowClass()
-
- getRowClass: function(record, index) {
- var status = record.get('status');
-
- if (status && status != 'OK') {
- return "x-form-invalid-field";
- }
- }
- },
- sortableColumns: false,
- columns: [
- {
- header: gettext("Start Time"),
- dataIndex: 'starttime',
- width: 150,
- renderer: function(value) {
- return Ext.Date.format(value, "M d H:i:s");
- }
- },
- {
- header: gettext("End Time"),
- dataIndex: 'endtime',
- width: 150,
- renderer: function(value, metaData, record) {
- if (record.data.pid) {
- if (record.data.type == "vncproxy" ||
- record.data.type == "vncshell" ||
- record.data.type == "spiceproxy") {
- metaData.tdCls = "x-grid-row-console";
- } else {
- metaData.tdCls = "x-grid-row-loading";
- }
- return "";
- }
- return Ext.Date.format(value, "M d H:i:s");
- }
- },
- {
- header: gettext("Node"),
- dataIndex: 'node',
- width: 100
- },
- {
- header: gettext("User name"),
- dataIndex: 'user',
- width: 150
- },
- {
- header: gettext("Description"),
- dataIndex: 'upid',
- flex: 1,
- renderer: PVE.Utils.render_upid
- },
- {
- header: gettext("Status"),
- dataIndex: 'status',
- width: 200,
- renderer: function(value, metaData, record) {
- if (record.data.pid) {
- if (record.data.type != "vncproxy") {
- metaData.tdCls = "x-grid-row-loading";
- }
- return "";
- }
- if (value == 'OK') {
- return 'OK';
- }
- // metaData.attr = 'style="color:red;"';
- return PVE.Utils.errorText + ': ' + value;
- }
- }
- ],
- listeners: {
- itemdblclick: run_task_viewer,
- show: taskstore.startUpdate,
- hide: taskstore.stopUpdate,
- destroy: taskstore.stopUpdate
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.dc.UserEdit', {
- extend: 'PVE.window.Edit',
- alias: ['widget.pveDcUserEdit'],
-
- isAdd: true,
-
- initComponent : function() {
- var me = this;
-
- me.create = !me.userid;
-
- var url;
- var method;
- var realm;
-
- if (me.create) {
- url = '/api2/extjs/access/users';
- method = 'POST';
- } else {
- url = '/api2/extjs/access/users/' + me.userid;
- method = 'PUT';
- }
-
- var verifypw;
- var pwfield;
-
- var validate_pw = function() {
- if (verifypw.getValue() !== pwfield.getValue()) {
- return gettext("Passwords does not match");
- }
- return true;
- };
-
- verifypw = Ext.createWidget('textfield', {
- inputType: 'password',
- fieldLabel: gettext('Confirm password'),
- name: 'verifypassword',
- submitValue: false,
- disabled: true,
- hidden: true,
- validator: validate_pw
- });
-
- pwfield = Ext.createWidget('textfield', {
- inputType: 'password',
- fieldLabel: gettext('Password'),
- minLength: 5,
- name: 'password',
- disabled: true,
- hidden: true,
- validator: validate_pw
- });
-
- var update_passwd_field = function(realm) {
- if (realm === 'pve') {
- pwfield.setVisible(true);
- pwfield.setDisabled(false);
- verifypw.setVisible(true);
- verifypw.setDisabled(false);
- } else {
- pwfield.setVisible(false);
- pwfield.setDisabled(true);
- verifypw.setVisible(false);
- verifypw.setDisabled(true);
- }
-
- };
-
- var column1 = [
- {
- xtype: me.create ? 'textfield' : 'displayfield',
- height: 22, // hack: set same height as text fields
- name: 'userid',
- fieldLabel: gettext('User name'),
- value: me.userid,
- allowBlank: false,
- submitValue: me.create ? true : false
- },
- pwfield, verifypw,
- {
- xtype: 'pveGroupSelector',
- name: 'groups',
- multiSelect: true,
- allowBlank: true,
- fieldLabel: gettext('Group')
- },
- {
- xtype: 'datefield',
- name: 'expire',
- emptyText: 'never',
- format: 'Y-m-d',
- submitFormat: 'U',
- fieldLabel: gettext('Expire')
- },
- {
- xtype: 'pvecheckbox',
- fieldLabel: gettext('Enabled'),
- name: 'enable',
- uncheckedValue: 0,
- defaultValue: 1,
- checked: true
- }
- ];
-
- var column2 = [
- {
- xtype: 'textfield',
- name: 'firstname',
- fieldLabel: gettext('First Name')
- },
- {
- xtype: 'textfield',
- name: 'lastname',
- fieldLabel: gettext('Last Name')
- },
- {
- xtype: 'textfield',
- name: 'email',
- fieldLabel: gettext('E-Mail'),
- vtype: 'email'
- }
- ];
-
- var columnB = [
- {
- xtype: 'textfield',
- name: 'comment',
- fieldLabel: gettext('Comment')
- },
- {
- xtype: 'textfield',
- name: 'keys',
- fieldLabel: gettext('Key IDs')
- }
- ];
-
- if (me.create) {
- column1.splice(1,0,{
- xtype: 'pveRealmComboBox',
- name: 'realm',
- fieldLabel: gettext('Realm'),
- allowBlank: false,
- matchFieldWidth: false,
- listConfig: { width: 300 },
- listeners: {
- change: function(combo, newValue){
- realm = newValue;
- update_passwd_field(realm);
- }
- },
- submitValue: false
- });
- }
-
- var ipanel = Ext.create('PVE.panel.InputPanel', {
- column1: column1,
- column2: column2,
- columnB: columnB,
- onGetValues: function(values) {
- // hack: ExtJS datefield does not submit 0, so we need to set that
- if (!values.expire) {
- values.expire = 0;
- }
-
- if (realm) {
- values.userid = values.userid + '@' + realm;
- }
-
- if (!values.password) {
- delete values.password;
- }
-
- return values;
- }
- });
-
- Ext.applyIf(me, {
- subject: gettext('User'),
- url: url,
- method: method,
- fieldDefaults: {
- labelWidth: 110 // for spanish translation
- },
- items: [ ipanel ]
- });
-
- me.callParent();
-
- if (!me.create) {
- me.load({
- success: function(response, options) {
- var data = response.result.data;
- if (Ext.isDefined(data.expire)) {
- if (data.expire) {
- data.expire = new Date(data.expire * 1000);
- } else {
- // display 'never' instead of '1970-01-01'
- data.expire = null;
- }
- }
- me.setValues(data);
- }
- });
- }
- }
-});
+++ /dev/null
-Ext.define('PVE.window.PasswordEdit', {
- extend: 'PVE.window.Edit',
-
- initComponent : function() {
- var me = this;
-
- if (!me.userid) {
- throw "no userid specified";
- }
-
- var verifypw;
- var pwfield;
-
- var validate_pw = function() {
- if (verifypw.getValue() !== pwfield.getValue()) {
- return gettext("Passwords does not match");
- }
- return true;
- };
-
- verifypw = Ext.createWidget('textfield', {
- inputType: 'password',
- fieldLabel: gettext('Confirm password'),
- name: 'verifypassword',
- submitValue: false,
- validator: validate_pw
- });
-
- pwfield = Ext.createWidget('textfield', {
- inputType: 'password',
- fieldLabel: gettext('Password'),
- minLength: 5,
- name: 'password',
- validator: validate_pw
- });
-
- Ext.apply(me, {
- subject: gettext('Password'),
- url: '/api2/extjs/access/password',
- items: [
- pwfield, verifypw,
- {
- xtype: 'hiddenfield',
- name: 'userid',
- value: me.userid
- }
- ]
- });
-
- me.callParent();
- }
-});
-
-Ext.define('PVE.dc.UserView', {
- extend: 'Ext.grid.GridPanel',
-
- alias: ['widget.pveUserView'],
-
- initComponent : function() {
- var me = this;
-
- var caps = Ext.state.Manager.get('GuiCap');
-
- var store = new Ext.data.Store({
- id: "users",
- model: 'pve-users',
- sorters: {
- property: 'userid',
- order: 'DESC'
- }
- });
-
- var reload = function() {
- store.load();
- };
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var remove_btn = new PVE.button.Button({
- text: gettext('Remove'),
- disabled: true,
- selModel: sm,
- enableFn: function(rec) {
- if (!caps.access['User.Modify']) {
- return false;
- }
- return rec.data.userid !== 'root@pam';
- },
- confirmMsg: function (rec) {
- return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
- "'" + rec.data.userid + "'");
- },
- handler: function(btn, event, rec) {
- var userid = rec.data.userid;
-
- PVE.Utils.API2Request({
- url: '/access/users/' + userid,
- method: 'DELETE',
- waitMsgTarget: me,
- callback: function() {
- reload();
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- }
- });
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (!rec || !caps.access['User.Modify']) {
- return;
- }
-
- var win = Ext.create('PVE.dc.UserEdit',{
- userid: rec.data.userid
- });
- win.on('destroy', reload);
- win.show();
- };
-
- var edit_btn = new PVE.button.Button({
- text: gettext('Edit'),
- disabled: true,
- enableFn: function(rec) {
- return !!caps.access['User.Modify'];
- },
- selModel: sm,
- handler: run_editor
- });
-
- var pwchange_btn = new PVE.button.Button({
- text: gettext('Password'),
- disabled: true,
- selModel: sm,
- handler: function(btn, event, rec) {
- var win = Ext.create('PVE.window.PasswordEdit',{
- userid: rec.data.userid
- });
- win.on('destroy', reload);
- win.show();
- }
- });
-
- var tbar = [
- {
- text: gettext('Add'),
- disabled: !caps.access['User.Modify'],
- handler: function() {
- var win = Ext.create('PVE.dc.UserEdit',{
- });
- win.on('destroy', reload);
- win.show();
- }
- },
- edit_btn, remove_btn, pwchange_btn
- ];
-
- var render_full_name = function(firstname, metaData, record) {
-
- var first = firstname || '';
- var last = record.data.lastname || '';
- return first + " " + last;
- };
-
- var render_username = function(userid) {
- return userid.match(/^(.+)(@[^@]+)$/)[1];
- };
-
- var render_realm = function(userid) {
- return userid.match(/@([^@]+)$/)[1];
- };
-
- Ext.apply(me, {
- store: store,
- selModel: sm,
- stateful: false,
- tbar: tbar,
- viewConfig: {
- trackOver: false
- },
- columns: [
- {
- header: gettext('User name'),
- width: 200,
- sortable: true,
- renderer: render_username,
- dataIndex: 'userid'
- },
- {
- header: gettext('Realm'),
- width: 100,
- sortable: true,
- renderer: render_realm,
- dataIndex: 'userid'
- },
- {
- header: gettext('Enabled'),
- width: 80,
- sortable: true,
- renderer: PVE.Utils.format_boolean,
- dataIndex: 'enable'
- },
- {
- header: gettext('Expire'),
- width: 80,
- sortable: true,
- renderer: PVE.Utils.format_expire,
- dataIndex: 'expire'
- },
- {
- header: gettext('Name'),
- width: 150,
- sortable: true,
- renderer: render_full_name,
- dataIndex: 'firstname'
- },
- {
- id: 'comment',
- header: gettext('Comment'),
- sortable: false,
- dataIndex: 'comment',
- flex: 1
- }
- ],
- listeners: {
- show: reload,
- itemdblclick: run_editor
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.BackupModeSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.pveBackupModeSelector'],
- comboItems: [
- ['snapshot', gettext('Snapshot')],
- ['suspend', gettext('Suspend')],
- ['stop', gettext('Stop')]
- ]
-});
+++ /dev/null
-Ext.define('PVE.form.BondModeSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.bondModeSelector'],
-
- openvswitch: false,
-
- initComponent: function() {
- var me = this;
-
- if (me.openvswitch) {
- me.data = [
- ['active-backup', 'active-backup'],
- ['balance-slb', 'balance-slb'],
- ['lacp-balance-slb', 'LACP (balance-slb)'],
- ['lacp-balance-tcp', 'LACP (balance-tcp)']
- ];
- } else {
- me.data = [
- ['balance-rr', 'balance-rr'],
- ['active-backup', 'active-backup'],
- ['balance-xor', 'balance-xor'],
- ['broadcast', 'broadcast'],
- ['802.3ad', 'LACP (802.3ad)'],
- ['balance-tlb', 'balance-tlb'],
- ['balance-alb', 'balance-alb']
- ];
- }
-
- me.callParent();
- }
-});
-
-Ext.define('PVE.form.BondPolicySelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.bondPolicySelector'],
-
- initComponent: function() {
- var me = this;
- me.data = [
- ['layer2', 'layer2'],
- ['layer2+3', 'layer2+3'],
- ['layer3+4', 'layer3+4']
- ];
-
- me.callParent();
- }
-});
-
+++ /dev/null
-// boolean type including 'Default' (delete property from file)
-Ext.define('PVE.form.Boolean', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.booleanfield'],
-
- initComponent: function() {
- var me = this;
-
- me.data = [
- ['', gettext('Default')],
- [1, gettext('Yes')],
- [0, gettext('No')]
- ];
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.BridgeSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: ['widget.PVE.form.BridgeSelector'],
-
- bridgeType: 'any_bridge', // bridge, OVSBridge or any_bridge
-
- setNodename: function(nodename) {
- var me = this;
-
- if (!nodename || (me.nodename === nodename)) {
- return;
- }
-
- me.nodename = nodename;
-
- me.store.setProxy({
- type: 'pve',
- url: '/api2/json/nodes/' + me.nodename + '/network?type=' +
- me.bridgeType
- });
-
- me.store.load();
- },
-
- initComponent: function() {
- var me = this;
-
- var nodename = me.nodename;
- me.nodename = undefined;
-
- var store = Ext.create('Ext.data.Store', {
- fields: [ 'iface', 'active', 'type' ],
- filterOnLoad: true,
- sorters: [
- {
- property : 'iface',
- direction: 'ASC'
- }
- ]
- });
-
- Ext.apply(me, {
- store: store,
- valueField: 'iface',
- displayField: 'iface',
- listConfig: {
- columns: [
- {
- header: gettext('Bridge'),
- dataIndex: 'iface',
- hideable: false,
- flex: 1
- },
- {
- header: gettext('Active'),
- width: 60,
- dataIndex: 'active',
- renderer: PVE.Utils.format_boolean
- }
- ]
- }
- });
-
- me.callParent();
-
- if (nodename) {
- me.setNodename(nodename);
- }
- }
-});
-
+++ /dev/null
-Ext.define('PVE.form.BusTypeSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.PVE.form.BusTypeSelector'],
-
- noVirtIO: false,
-
- noScsi: false,
-
- initComponent: function() {
- var me = this;
-
- me.data = [['ide', 'IDE'], ['sata', 'SATA']];
-
- if (!me.noVirtIO) {
- me.data.push(['virtio', 'VIRTIO']);
- }
-
- if (!me.noScsi) {
- me.data.push(['scsi', 'SCSI']);
- }
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.CPUModelSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.CPUModelSelector'],
-
- initComponent: function() {
- var me = this;
-
- me.data = [
- ['', PVE.Utils.defaultText + ' (kvm64)'],
- ['486', '486'],
- ['athlon', 'athlon'],
- ['core2duo', 'core2duo'],
- ['coreduo', 'coreduo'],
- ['kvm32', 'kvm32'],
- ['kvm64', 'kvm64'],
- ['pentium', 'pentium'],
- ['pentium2', 'pentium2'],
- ['pentium3', 'pentium3'],
- ['phenom', 'phenom'],
- ['qemu32', 'qemu32'],
- ['qemu64', 'qemu64'],
- ['Conroe', 'Conroe'],
- ['Penryn', 'Penryn'],
- ['Nehalem', 'Nehalem'],
- ['Westmere', 'Westmere'],
- ['SandyBridge', 'SandyBridge'],
- ['IvyBridge', 'IvyBridge'],
- ['Haswell', 'Haswell'],
- ['Broadwell', 'Broadwell'],
- ['Opteron_G1', 'Opteron_G1'],
- ['Opteron_G2', 'Opteron_G2'],
- ['Opteron_G3', 'Opteron_G3'],
- ['Opteron_G4', 'Opteron_G4'],
- ['Opteron_G5', 'Opteron_G5'],
- ['host', 'host']
- ];
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.CacheTypeSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.CacheTypeSelector'],
-
- initComponent: function() {
- var me = this;
-
- me.data = [
- ['', PVE.Utils.defaultText + " (" + gettext('No cache') + ")"],
- ['directsync', 'Direct sync'],
- ['writethrough', 'Write through'],
- ['writeback', 'Write back'],
- ['unsafe', 'Write back (' + gettext('unsafe') + ')'],
- ['none', gettext('No cache')]
- ];
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.Checkbox', {
- extend: 'Ext.form.field.Checkbox',
- alias: ['widget.pvecheckbox'],
-
- defaultValue: undefined,
-
- deleteDefaultValue: false,
- deleteEmpty: false,
-
- inputValue: '1',
-
- height: 22, // hack: set same height as text fields
-
- getSubmitData: function() {
- var me = this,
- data = null,
- val;
- if (!me.disabled && me.submitValue) {
- val = me.getSubmitValue();
- if (val !== null) {
- data = {};
- if ((val == me.defaultValue) && me.deleteDefaultValue) {
- data['delete'] = me.getName();
- } else {
- data[me.getName()] = val;
- }
- } else if (me.deleteEmpty) {
- data = {};
- data['delete'] = me.getName();
- }
- }
- return data;
- },
-
- // also accept integer 1 as true
- setRawValue: function(value) {
- var me = this;
-
- if (value === 1) {
- me.callParent([true]);
- } else {
- me.callParent([value]);
- }
- }
-
-});
\ No newline at end of file
+++ /dev/null
-Ext.define('PVE.form.ComboGrid', {
- extend: 'Ext.form.field.ComboBox',
- alias: ['widget.PVE.form.ComboGrid'],
-
- // this value is used as default value after load()
- preferredValue: undefined,
-
- computeHeight: function() {
- var me = this;
- var lh = PVE.Utils.gridLineHeigh();
- var count = me.store.getTotalCount();
- return (count > 10) ? 10*lh : 26+count*lh;
- },
-
- // hack: allow to select empty value
- // seems extjs does not allow that when 'editable == false'
- onKeyUp: function(e, t) {
- var me = this;
- var key = e.getKey();
-
- if (!me.editable && me.allowBlank && !me.multiSelect &&
- (key == e.BACKSPACE || key == e.DELETE)) {
- me.setValue('');
- }
-
- me.callParent(arguments);
- },
-
- // copied from ComboBox
- createPicker: function() {
- var me = this,
- picker,
- menuCls = Ext.baseCSSPrefix + 'menu',
-
- opts = Ext.apply({
- selModel: {
- mode: me.multiSelect ? 'SIMPLE' : 'SINGLE'
- },
- floating: true,
- hidden: true,
- ownerCt: me.ownerCt,
- cls: me.el.up('.' + menuCls) ? menuCls : '',
- store: me.store,
- displayField: me.displayField,
- focusOnToFront: false,
- height: me.computeHeight(),
- pageSize: me.pageSize
- }, me.listConfig, me.defaultListConfig);
-
- // NOTE: we simply use a grid panel
- //picker = me.picker = Ext.create('Ext.view.BoundList', opts);
- picker = me.picker = Ext.create('Ext.grid.Panel', opts);
-
- // pass getNode() to the view
- picker.getNode = function() {
- picker.getView().getNode(arguments);
- };
-
- me.mon(picker, {
- itemclick: me.onItemClick,
- refresh: me.onListRefresh,
- show: function() {
- picker.setHeight(me.computeHeight());
- me.syncSelection();
- },
- scope: me
- });
-
- me.mon(picker.getSelectionModel(), 'selectionchange', me.onListSelectionChange, me);
-
- return picker;
- },
-
- initComponent: function() {
- var me = this;
-
- if (me.initialConfig.editable === undefined) {
- me.editable = false;
- }
-
- Ext.apply(me, {
- queryMode: 'local',
- matchFieldWidth: false
- });
-
- Ext.applyIf(me, { value: ''}); // hack: avoid ExtJS validate() bug
-
- Ext.applyIf(me.listConfig, { width: 400 });
-
- me.callParent();
-
- me.store.on('beforeload', function() {
- if (!me.isDisabled()) {
- me.setDisabled(true);
- me.enableAfterLoad = true;
- }
- });
-
- // hack: autoSelect does not work
- me.store.on('load', function(store, r, success, o) {
- if (success) {
- me.clearInvalid();
-
- if (me.enableAfterLoad) {
- delete me.enableAfterLoad;
- me.setDisabled(false);
- }
-
- var def = me.getValue() || me.preferredValue;
- if (def) {
- me.setValue(def, true); // sync with grid
- }
- var found = false;
- if (def) {
- if (Ext.isArray(def)) {
- Ext.Array.each(def, function(v) {
- if (store.findRecord(me.valueField, v)) {
- found = true;
- return false; // break
- }
- });
- } else {
- found = store.findRecord(me.valueField, def);
- }
- }
-
- if (!found) {
- var rec = me.store.first();
- if (me.autoSelect && rec && rec.data) {
- def = rec.data[me.valueField];
- me.setValue(def, true);
- } else {
- me.setValue(me.editable ? def : '', true);
- }
- }
- }
- });
- }
-});
+++ /dev/null
-Ext.define('PVE.form.CompressionSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.pveCompressionSelector'],
- comboItems: [
- ['', PVE.Utils.noneText],
- ['lzo', 'LZO (' + gettext('fast') + ')'],
- ['gzip', 'GZIP (' + gettext('good') + ')']
- ]
-});
+++ /dev/null
-Ext.define('PVE.form.ContentTypeSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.pveContentTypeSelector'],
-
- cts: undefined,
-
- initComponent: function() {
- var me = this;
-
- me.data = [];
-
- if (me.cts === undefined) {
- me.cts = ['images', 'iso', 'vztmpl', 'backup', 'rootdir'];
- }
-
- Ext.Array.each(me.cts, function(ct) {
- me.data.push([ct, PVE.Utils.format_content_types(ct)]);
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.ControllerSelector', {
- extend: 'Ext.form.FieldContainer',
- alias: ['widget.PVE.form.ControllerSelector'],
-
- statics: {
- maxIds: {
- ide: 3,
- sata: 5,
- virtio: 15,
- scsi: 13
- }
- },
-
- noVirtIO: false,
-
- noScsi: false,
-
- vmconfig: {}, // used to check for existing devices
-
- setVMConfig: function(vmconfig, autoSelect) {
- var me = this;
-
- me.vmconfig = Ext.apply({}, vmconfig);
- if (autoSelect) {
- var clist = ['ide', 'virtio', 'scsi', 'sata'];
- if (autoSelect === 'cdrom') {
- clist = ['ide', 'scsi', 'sata'];
- if (!Ext.isDefined(me.vmconfig.ide2)) {
- me.down('field[name=controller]').setValue('ide');
- me.down('field[name=deviceid]').setValue(2);
- return;
- }
- } else if (me.vmconfig.ostype === 'l26') {
- clist = ['virtio', 'ide', 'scsi', 'sata'];
- }
-
- Ext.Array.each(clist, function(controller) {
- var confid, i;
- if ((controller === 'virtio' && me.noVirtIO) ||
- (controller === 'scsi' && me.noScsi)) {
- return; //continue
- }
- me.down('field[name=controller]').setValue(controller);
- for (i = 0; i <= PVE.form.ControllerSelector.maxIds[controller]; i++) {
- confid = controller + i.toString();
- if (!Ext.isDefined(me.vmconfig[confid])) {
- me.down('field[name=deviceid]').setValue(i);
- return false; // break
- }
- }
- });
- }
- me.down('field[name=deviceid]').validate();
- },
-
- initComponent: function() {
- var me = this;
-
- Ext.apply(me, {
- fieldLabel: gettext('Bus/Device'),
- layout: 'hbox',
- height: 22, // hack: set to same height as other fields
- defaults: {
- flex: 1,
- hideLabel: true
- },
- items: [
- {
- xtype: 'PVE.form.BusTypeSelector',
- name: 'controller',
- value: 'ide',
- noVirtIO: me.noVirtIO,
- noScsi: me.noScsi,
- allowBlank: false,
- listeners: {
- change: function(t, value) {
- if (!me.rendered || !value) {
- return;
- }
- var field = me.down('field[name=deviceid]');
- field.setMaxValue(PVE.form.ControllerSelector.maxIds[value]);
- field.validate();
- }
- }
- },
- {
- xtype: 'numberfield',
- name: 'deviceid',
- minValue: 0,
- maxValue: PVE.form.ControllerSelector.maxIds.ide,
- value: '0',
- validator: function(value) {
- /*jslint confusion: true */
- if (!me.rendered) {
- return;
- }
- var field = me.down('field[name=controller]');
- var controller = field.getValue();
- var confid = controller + value;
- if (Ext.isDefined(me.vmconfig[confid])) {
- return "This device is already in use.";
- }
- return true;
- }
- }
- ]
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.DayOfWeekSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.pveDayOfWeekSelector'],
- comboItems: [
- ['sun', Ext.util.Format.htmlDecode(Ext.Date.dayNames[0])],
- ['mon', Ext.util.Format.htmlDecode(Ext.Date.dayNames[1])],
- ['tue', Ext.util.Format.htmlDecode(Ext.Date.dayNames[2])],
- ['wed', Ext.util.Format.htmlDecode(Ext.Date.dayNames[3])],
- ['thu', Ext.util.Format.htmlDecode(Ext.Date.dayNames[4])],
- ['fri', Ext.util.Format.htmlDecode(Ext.Date.dayNames[5])],
- ['sat', Ext.util.Format.htmlDecode(Ext.Date.dayNames[6])]
- ]
-});
+++ /dev/null
-Ext.define('PVE.form.DiskFormatSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.PVE.form.DiskFormatSelector'],
-
- initComponent: function() {
- var me = this;
-
- me.data = [
- ['raw', gettext('Raw disk image') + ' (raw)'],
- ['qcow2', gettext('QEMU image format') + ' (qcow2)'],
- ['vmdk', gettext('VMware image format') + ' (vmdk)']
- ];
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.DisplaySelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.DisplaySelector'],
-
- initComponent: function() {
- var me = this;
-
- me.data = PVE.Utils.kvm_vga_driver_array();
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.EmailNotificationSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.pveEmailNotificationSelector'],
- comboItems: [
- ['always', gettext('Always')],
- ['failure', gettext('On failure only')]
- ]
-});
+++ /dev/null
-Ext.define('PVE.form.FileSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: ['widget.pveFileSelector'],
-
- setStorage: function(storage, nodename) {
- var me = this;
-
- var change = false;
- if (storage && (me.storage !== storage)) {
- me.storage = storage;
- change = true;
- }
-
- if (nodename && (me.nodename !== nodename)) {
- me.nodename = nodename;
- change = true;
- }
-
- if (!(me.storage && me.nodename && change)) {
- return;
- }
-
- var url = '/api2/json/nodes/' + me.nodename + '/storage/' + me.storage + '/content';
- if (me.storageContent) {
- url += '?content=' + me.storageContent;
- }
-
- me.store.setProxy({
- type: 'pve',
- url: url
- });
-
- me.store.load();
- },
-
- initComponent: function() {
- var me = this;
-
- var store = Ext.create('Ext.data.Store', {
- model: 'pve-storage-content'
- });
-
- Ext.apply(me, {
- store: store,
- allowBlank: false,
- autoSelect: false,
- valueField: 'volid',
- displayField: 'text',
- listConfig: {
- columns: [
- {
- header: gettext('Name'),
- dataIndex: 'text',
- hideable: false,
- flex: 1
- },
- {
- header: gettext('Format'),
- width: 60,
- dataIndex: 'format'
- },
- {
- header: gettext('Size'),
- width: 60,
- dataIndex: 'size',
- renderer: PVE.Utils.format_size
- }
- ]
- }
- });
-
- me.callParent();
-
- me.setStorage(me.storage, me.nodename);
- }
-});
+++ /dev/null
-Ext.define('PVE.form.FirewallPolicySelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.pveFirewallPolicySelector'],
-
- initComponent: function() {
- var me = this;
-
- me.data = [
- ['ACCEPT', 'ACCEPT'],
- ['REJECT', 'REJECT'],
- [ 'DROP', 'DROP']
- ];
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.GroupSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: ['widget.pveGroupSelector'],
-
- allowBlank: false,
-
- initComponent: function() {
- var me = this;
-
- var store = new Ext.data.Store({
- model: 'pve-groups'
- });
-
- Ext.apply(me, {
- store: store,
- autoSelect: false,
- valueField: 'groupid',
- displayField: 'groupid',
- listConfig: {
- columns: [
- {
- header: gettext('Group'),
- sortable: true,
- dataIndex: 'groupid',
- flex: 1
- },
- {
- id: 'comment',
- header: gettext('Comment'),
- sortable: false,
- dataIndex: 'comment',
- flex: 1
- }
- ]
- }
- });
-
- me.callParent();
-
- store.load();
- }
-
-}, function() {
-
- Ext.define('pve-groups', {
- extend: 'Ext.data.Model',
- fields: [ 'groupid', 'comment' ],
- proxy: {
- type: 'pve',
- url: "/api2/json/access/groups"
- },
- idProperty: 'groupid'
- });
-
-});
+++ /dev/null
-Ext.define('PVE.form.HotplugFeatureSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.pveHotplugFeatureSelector'],
-
- multiSelect: true,
- allowBlank: true,
- deleteEmpty: false,
-
- setValue: function(value, doSelect) {
- var me = this;
-
- if (me.multiSelect && Ext.isString(value)) {
- if (value === '0') {
- value = [];
- } else if (value === '1') {
- value = ['disk', 'network', 'usb'];
- } else {
- value = value.split(',');
- }
- }
-
- me.callParent([value, doSelect]);
- },
-
- getSubmitData: function() {
- var me = this,
- data = null,
- val;
- if (!me.disabled && me.submitValue) {
- val = me.getSubmitValue();
- if (Ext.isArray(val)) {
- val = val.join(',') || '0';
- }
- if (val !== null && val !== '') {
- data = {};
- data[me.getName()] = val;
- } else if (me.deleteEmpty) {
- data = {};
- data['delete'] = me.getName();
- }
- }
- return data;
- },
-
-
- initComponent: function() {
- var me = this;
-
- me.data = [['disk', gettext('Disk')],
- ['network', gettext('Network')],
- ['usb', gettext('USB')],
- ['memory', gettext('Memory')],
- ['cpu', gettext('CPU')]];
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.IPProtocolSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: ['widget.pveIPProtocolSelector'],
-
- initComponent: function() {
- var me = this;
-
- var store = Ext.create('Ext.data.Store', {
- fields: [ 'p', 'd', 'n'],
- data: [
- { p: 'tcp', n: 6, d: 'Transmission Control Protocol' },
- { p: 'udp', n: 17, d: 'User Datagram Protocol' },
- { p: 'icmp', n: 1, d: 'Internet Control Message Protocol' },
- { p: 'igmp', n: 2, d: 'Internet Group Management' },
- { p: 'ggp', n: 3, d: 'gateway-gateway protocol' },
- { p: 'ipencap', n: 4, d: 'IP encapsulated in IP' },
- { p: 'st', n: 5, d: 'ST datagram mode' },
- { p: 'egp', n: 8, d: 'exterior gateway protocol' },
- { p: 'igp', n: 9, d: 'any private interior gateway (Cisco)' },
- { p: 'pup', n: 12, d: 'PARC universal packet protocol' },
- { p: 'hmp', n: 20, d: 'host monitoring protocol' },
- { p: 'xns-idp', n: 22, d: 'Xerox NS IDP' },
- { p: 'rdp', n: 27, d: '"reliable datagram" protocol' },
- { p: 'iso-tp4', n: 29, d: 'ISO Transport Protocol class 4 [RFC905]' },
- { p: 'dccp', n: 33, d: 'Datagram Congestion Control Prot. [RFC4340]' },
- { p: 'xtp', n: 36, d: 'Xpress Transfer Protocol' },
- { p: 'ddp', n: 37, d: 'Datagram Delivery Protocol' },
- { p: 'idpr-cmtp', n: 38, d: 'IDPR Control Message Transport' },
- { p: 'ipv6', n: 41, d: 'Internet Protocol, version 6' },
- { p: 'ipv6-route', n: 43, d: 'Routing Header for IPv6' },
- { p: 'ipv6-frag', n: 44, d: 'Fragment Header for IPv6' },
- { p: 'idrp', n: 45, d: 'Inter-Domain Routing Protocol' },
- { p: 'rsvp', n: 46, d: 'Reservation Protocol' },
- { p: 'gre', n: 47, d: 'General Routing Encapsulation' },
- { p: 'esp', n: 50, d: 'Encap Security Payload [RFC2406]' },
- { p: 'ah', n: 51, d: 'Authentication Header [RFC2402]' },
- { p: 'skip', n: 57, d: 'SKIP' },
- { p: 'ipv6-icmp', n: 58, d: 'ICMP for IPv6' },
- { p: 'ipv6-nonxt', n: 59, d: 'No Next Header for IPv6' },
- { p: 'ipv6-opts', n: 60, d: 'Destination Options for IPv6' },
- { p: 'vmtp', n: 81, d: 'Versatile Message Transport' },
- { p: 'eigrp', n: 88, d: 'Enhanced Interior Routing Protocol (Cisco)' },
- { p: 'ospf', n: 89, d: 'Open Shortest Path First IGP' },
- { p: 'ax.25', n: 93, d: 'AX.25 frames' },
- { p: 'ipip', n: 94, d: 'IP-within-IP Encapsulation Protocol' },
- { p: 'etherip', n: 97, d: 'Ethernet-within-IP Encapsulation [RFC3378]' },
- { p: 'encap', n: 98, d: 'Yet Another IP encapsulation [RFC1241]' },
- { p: 'pim', n: 103, d: 'Protocol Independent Multicast' },
- { p: 'ipcomp', n: 108, d: 'IP Payload Compression Protocol' },
- { p: 'vrrp', n: 112, d: 'Virtual Router Redundancy Protocol [RFC5798]' },
- { p: 'l2tp', n: 115, d: 'Layer Two Tunneling Protocol [RFC2661]' },
- { p: 'isis', n: 124, d: 'IS-IS over IPv4' },
- { p: 'sctp', n: 132, d: 'Stream Control Transmission Protocol' },
- { p: 'fc', n: 133, d: 'Fibre Channel' },
- { p: 'mobility-header', n: 135, d: 'Mobility Support for IPv6 [RFC3775]' },
- { p: 'udplite', n: 136, d: 'UDP-Lite [RFC3828]' },
- { p: 'mpls-in-ip', n: 137, d: 'MPLS-in-IP [RFC4023]' },
- { p: 'hip', n: 139, d: 'Host Identity Protocol' },
- { p: 'shim6', n: 140, d: 'Shim6 Protocol [RFC5533]' },
- { p: 'wesp', n: 141, d: 'Wrapped Encapsulating Security Payload' },
- { p: 'rohc', n: 142, d: 'Robust Header Compression' }
- ]
- });
-
- Ext.apply(me, {
- store: store,
- valueField: 'p',
- displayField: 'p',
- listConfig: {
- columns: [
- {
- header: gettext('Protocol'),
- dataIndex: 'p',
- hideable: false,
- sortable: false,
- width: 100
- },
- {
- header: gettext('Number'),
- dataIndex: 'n',
- hideable: false,
- sortable: false,
- width: 50
- },
- {
- header: gettext('Description'),
- dataIndex: 'd',
- hideable: false,
- sortable: false,
- flex: 1
- }
- ]
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.IPRefSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: ['widget.pveIPRefSelector'],
-
- base_url: undefined,
-
- preferredValue: '', // hack: else Form sets dirty flag?
-
- ref_type: undefined, // undefined = any [undefined, 'ipset' or 'alias']
-
- initComponent: function() {
- var me = this;
-
- if (!me.base_url) {
- throw "no base_url specified";
- }
-
- var url = "/api2/json" + me.base_url;
- if (me.ref_type) {
- url += "?type=" + me.ref_type;
- }
-
- var store = Ext.create('Ext.data.Store', {
- autoLoad: true,
- fields: [ 'type', 'name', 'ref', 'comment' ],
- idProperty: 'ref',
- proxy: {
- type: 'pve',
- url: url
- },
- sorters: {
- property: 'ref',
- order: 'DESC'
- }
- });
-
- var disable_query_for_ips = function(f, value) {
- if (value === null ||
- value.match(/^\d/)) { // IP address starts with \d
- f.queryDelay = 9999999999; // hack: disbale with long delay
- } else {
- f.queryDelay = 10;
- }
- };
-
- var columns = [];
-
- if (!me.ref_type) {
- columns.push({
- header: gettext('Type'),
- dataIndex: 'type',
- hideable: false,
- width: 60
- });
- }
-
- columns.push([
- {
- header: gettext('Name'),
- dataIndex: 'ref',
- hideable: false,
- width: 140
- },
- {
- header: gettext('Comment'),
- dataIndex: 'comment',
- flex: 1
- }
- ]);
-
- Ext.apply(me, {
- store: store,
- valueField: 'ref',
- displayField: 'ref',
- listConfig: { columns: columns }
- });
-
- me.on('change', disable_query_for_ips);
-
- me.callParent();
- }
-});
-
+++ /dev/null
-/* Key-Value ComboBox
- *
- * config properties:
- * comboItems: an array of Key - Value pairs
- * deleteEmpty: if set to true (default), an empty value received from the
- * comboBox will reset the property to its default value
- */
-Ext.define('PVE.form.KVComboBox', {
- extend: 'Ext.form.field.ComboBox',
- alias: 'widget.pveKVComboBox',
-
- deleteEmpty: true,
- comboItems: undefined,
-
- getSubmitData: function() {
- var me = this,
- data = null,
- val;
- if (!me.disabled && me.submitValue) {
- val = me.getSubmitValue();
- if (val !== null && val !== '') {
- data = {};
- data[me.getName()] = val;
- } else if (me.deleteEmpty) {
- data = {};
- data['delete'] = me.getName();
- }
- }
- return data;
- },
-
- initComponent: function() {
- var me = this;
-
- me.store = Ext.create('Ext.data.ArrayStore', {
- model: 'KeyValue',
- data : me.comboItems,
- });
-
- if (me.initialConfig.editable === undefined) {
- me.editable = false;
- }
-
- Ext.apply(me, {
- displayField: 'value',
- valueField: 'key',
- queryMode: 'local'
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.LanguageSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.pveLanguageSelector'],
- comboItems: PVE.Utils.language_array()
-});
+++ /dev/null
-Ext.define('PVE.form.MemoryField', {
- extend: 'Ext.form.field.Number',
- alias: 'widget.pveMemoryField',
-
- allowBlank: false,
-
- hotplug: false,
-
- minValue: 32,
-
- maxValue: 4178944,
-
- step: 32,
-
- value: '512', // qm default
-
- computeUpDown: function(value) {
- var me = this;
-
- if (!me.hotplug) {
- return { up: value + me.step, down: value - me.step };
- }
-
- var dimm_size = 512;
- var prev_dimm_size = 0;
- var min_size = 1024;
- var current_size = min_size;
- var value_up = min_size;
- var value_down = min_size;
- var value_start = min_size;
-
- var i, j;
- for (j = 0; j < 9; j++) {
- for (i = 0; i < 32; i++) {
- if ((value >= current_size) && (value < (current_size + dimm_size))) {
- value_start = current_size,
- value_up = current_size + dimm_size;
- value_down = current_size - ((i === 0) ? prev_dimm_size : dimm_size);
- }
- current_size += dimm_size;
- }
- prev_dimm_size = dimm_size;
- dimm_size = dimm_size*2;
- }
-
- return { up: value_up, down: value_down, start: value_start };
- },
-
- onSpinUp: function() {
- var me = this;
- if (!me.readOnly) {
- var res = me.computeUpDown(me.getValue());
- me.setValue(Ext.Number.constrain(res.up, me.minValue, me.maxValue));
- }
- },
-
- onSpinDown: function() {
- var me = this;
- if (!me.readOnly) {
- var res = me.computeUpDown(me.getValue());
- me.setValue(Ext.Number.constrain(res.down, me.minValue, me.maxValue));
- }
- },
-
- initComponent: function() {
- var me = this;
-
- if (me.hotplug) {
- me.minValue = 1024;
-
- me.on('blur', function(field) {
- value = me.getValue();
- var res = me.computeUpDown(value);
- if (value === res.start || value === res.up || value === res.down) {
- return;
- }
- field.setValue(res.up);
- });
- }
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.NetworkCardSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.PVE.form.NetworkCardSelector'],
-
- initComponent: function() {
- var me = this;
-
- me.data = [
- ['e1000', 'Intel E1000'],
- ['virtio', 'VirtIO (' + gettext('paravirtualized') + ')'],
- ['rtl8139', 'Realtek RTL8139'],
- ['vmxnet3', 'VMWare vmxnet3']
- ];
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.NodeSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: ['widget.PVE.form.NodeSelector'],
-
- // invalidate nodes which are offline
- onlineValidator: false,
-
- selectCurNode: false,
-
- // only allow those nodes (array)
- allowedNodes: undefined,
-
- initComponent: function() {
- var me = this;
-
- var store = Ext.create('Ext.data.Store', {
- fields: [ 'node', 'cpu', 'maxcpu', 'mem', 'maxmem', 'uptime' ],
- autoLoad: true,
- proxy: {
- type: 'pve',
- url: '/api2/json/nodes'
- },
- sorters: [
- {
- property : 'node',
- direction: 'ASC'
- },
- {
- property : 'mem',
- direction: 'DESC'
- }
- ]
- });
-
- Ext.apply(me, {
- store: store,
- valueField: 'node',
- displayField: 'node',
- listConfig: {
- columns: [
- {
- header: gettext('Node'),
- dataIndex: 'node',
- sortable: true,
- hideable: false,
- flex: 1
- },
- {
- header: gettext('Memory usage'),
- renderer: PVE.Utils.render_mem_usage,
- sortable: true,
- width: 100,
- dataIndex: 'mem'
- },
- {
- header: gettext('CPU usage'),
- renderer: PVE.Utils.render_cpu,
- sortable: true,
- width: 100,
- dataIndex: 'cpu'
- }
- ]
- },
- validator: function(value) {
- /*jslint confusion: true */
- if (!me.onlineValidator || (me.allowBlank && !value)) {
- return true;
- }
-
- var offline = [];
- var notAllowed = [];
-
- Ext.Array.each(value.split(/\s*,\s*/), function(node) {
- var rec = me.store.findRecord(me.valueField, node);
- if (!(rec && rec.data) || !Ext.isNumeric(rec.data.mem)) {
- offline.push(node);
- } else if (me.allowedNodes && !Ext.Array.contains(me.allowedNodes, node)) {
- notAllowed.push(node);
- }
- });
-
- if (notAllowed.length !== 0) {
- return "Node " + notAllowed.join(', ') + " is not allowed for this action!";
- }
-
- if (offline.length !== 0) {
- return "Node " + offline.join(', ') + " seems to be offline!";
- }
- return true;
- }
- });
-
- if (me.selectCurNode && PVE.curSelectedNode.data.node) {
- me.preferredValue = PVE.curSelectedNode.data.node;
- }
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.PoolSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: ['widget.pvePoolSelector'],
-
- allowBlank: false,
-
- initComponent: function() {
- var me = this;
-
- var store = new Ext.data.Store({
- model: 'pve-pools'
- });
-
- Ext.apply(me, {
- store: store,
- autoSelect: false,
- valueField: 'poolid',
- displayField: 'poolid',
- listConfig: {
- columns: [
- {
- header: gettext('Pool'),
- sortable: true,
- dataIndex: 'poolid',
- flex: 1
- },
- {
- id: 'comment',
- header: gettext('Comment'),
- sortable: false,
- dataIndex: 'comment',
- flex: 1
- }
- ]
- }
- });
-
- me.callParent();
-
- store.load();
- }
-
-}, function() {
-
- Ext.define('pve-pools', {
- extend: 'Ext.data.Model',
- fields: [ 'poolid', 'comment' ],
- proxy: {
- type: 'pve',
- url: "/api2/json/pools"
- },
- idProperty: 'poolid'
- });
-
-});
+++ /dev/null
-Ext.define('PVE.form.RRDTypeSelector', {
- extend: 'Ext.form.field.ComboBox',
- alias: ['widget.pveRRDTypeSelector'],
-
- initComponent: function() {
- var me = this;
-
- var store = new Ext.data.ArrayStore({
- fields: [ 'id', 'timeframe', 'cf', 'text' ],
- data : [
- [ 'hour', 'hour', 'AVERAGE', "Hour (average)" ],
- [ 'hourmax', 'hour', 'MAX', "Hour (max)" ],
- [ 'day', 'day', 'AVERAGE', "Day (average)" ],
- [ 'daymax', 'day', 'MAX', "Day (max)" ],
- [ 'week', 'week', 'AVERAGE', "Week (average)" ],
- [ 'weekmax', 'week', 'MAX', "Week (max)" ],
- [ 'month', 'month', 'AVERAGE', "Month (average)" ],
- [ 'monthmax', 'month', 'MAX', "Month (max)" ],
- [ 'year', 'year', 'AVERAGE', "Year (average)" ],
- [ 'yearmax', 'year', 'MAX', "Year (max)" ]
- ]
- });
-
- Ext.apply(me, {
- store: store,
- displayField: 'text',
- valueField: 'id',
- editable: false,
- queryMode: 'local',
- value: 'hour',
- getState: function() {
- var ind = store.findExact('id', me.getValue());
- var rec = store.getAt(ind);
- if (!rec) {
- return;
- }
- return {
- id: rec.data.id,
- timeframe: rec.data.timeframe,
- cf: rec.data.cf
- };
- },
- applyState : function(state) {
- if (state && state.id) {
- me.setValue(state.id);
- }
- },
- stateEvents: [ 'select' ],
- stateful: true,
- id: 'pveRRDTypeSelection'
- });
-
- me.callParent();
-
- var statechange = function(sp, key, value) {
- if (key === me.id) {
- me.applyState(value);
- }
- };
-
- var sp = Ext.state.Manager.getProvider();
- me.mon(sp, 'statechange', statechange, me);
- }
-});
-
+++ /dev/null
-Ext.define('PVE.form.RealmComboBox', {
- extend: 'Ext.form.field.ComboBox',
- alias: ['widget.pveRealmComboBox'],
-
- needOTP: function(realm) {
- var me = this;
-
- var rec = me.store.findRecord('realm', realm);
-
- return rec && rec.data && rec.data.tfa ? rec.data.tfa : undefined;
- },
-
- initComponent: function() {
- var me = this;
-
- var stateid = 'pveloginrealm';
-
- var realmstore = Ext.create('Ext.data.Store', {
- model: 'pve-domains',
- });
-
- Ext.apply(me, {
- fieldLabel: gettext('Realm'),
- name: 'realm',
- store: realmstore,
- queryMode: 'local',
- allowBlank: false,
- forceSelection: true,
- autoSelect: false,
- triggerAction: 'all',
- valueField: 'realm',
- displayField: 'descr',
- getState: function() {
- return { value: this.getValue() };
- },
- applyState : function(state) {
- if (state && state.value) {
- this.setValue(state.value);
- }
- },
- stateEvents: [ 'select' ],
- stateful: true,
- id: stateid, // fixme: remove (Stateful does not work without)
- stateID: stateid
- });
-
- me.callParent();
-
- realmstore.load({
- callback: function(r, o, success) {
- if (success) {
- var def = me.getValue();
- if (!def || !realmstore.findRecord('realm', def)) {
- def = 'pam';
- Ext.each(r, function(record) {
- if (record.data && record.data["default"]) {
- def = record.data.realm;
- }
- });
- }
- if (def) {
- me.setValue(def);
- }
- }
- }
- });
- }
-});
+++ /dev/null
-Ext.define('PVE.form.RoleSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: ['widget.pveRoleSelector'],
-
- initComponent: function() {
- var me = this;
-
- var store = new Ext.data.Store({
- model: 'pve-roles'
- });
-
- Ext.apply(me, {
- store: store,
- allowBlank: false,
- autoSelect: false,
- valueField: 'roleid',
- displayField: 'roleid',
- listConfig: {
- columns: [
- {
- header: gettext('Role'),
- sortable: true,
- dataIndex: 'roleid',
- flex: 1
- }
- ]
- }
- });
-
- me.callParent();
-
- store.load();
- }
-
-}, function() {
-
- Ext.define('pve-roles', {
- extend: 'Ext.data.Model',
- fields: [ 'roleid', 'privs' ],
- proxy: {
- type: 'pve',
- url: "/api2/json/access/roles"
- },
- idProperty: 'roleid'
- });
-
-});
+++ /dev/null
-Ext.define('PVE.form.ScsiHwSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.pveScsiHwSelector'],
-
- initComponent: function() {
- var me = this;
-
- me.data = [
- ['', PVE.Utils.render_scsihw('')],
- ['lsi', PVE.Utils.render_scsihw('lsi')],
- ['lsi53c810', PVE.Utils.render_scsihw('lsi53c810')],
- ['megasas', PVE.Utils.render_scsihw('megasas')],
- ['virtio-scsi-pci', PVE.Utils.render_scsihw('virtio-scsi-pci')],
- ['virtio-scsi-single', PVE.Utils.render_scsihw('virtio-scsi-single')],
- ['pvscsi', PVE.Utils.render_scsihw('pvscsi')]
- ];
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.SecurityGroupsSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: ['widget.pveSecurityGroupsSelector'],
-
- initComponent: function() {
- var me = this;
-
- var store = Ext.create('Ext.data.Store', {
- autoLoad: true,
- fields: [ 'group', 'comment' ],
- idProperty: 'group',
- proxy: {
- type: 'pve',
- url: "/api2/json/cluster/firewall/groups"
- },
- sorters: {
- property: 'group',
- order: 'DESC'
- }
- });
-
- Ext.apply(me, {
- store: store,
- valueField: 'group',
- displayField: 'group',
- listConfig: {
- columns: [
- {
- header: gettext('Security Group'),
- dataIndex: 'group',
- hideable: false,
- width: 100
- },
- {
- header: gettext('Comment'),
- dataIndex: 'comment',
- flex: 1
- }
- ]
- }
- });
-
- me.callParent();
- }
-});
-
+++ /dev/null
-Ext.define('PVE.form.SnapshotSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: ['widget.PVE.form.SnapshotSelector'],
-
- loadStore: function(nodename, vmid) {
- var me = this;
-
- if (!nodename) {
- return;
- }
-
- me.nodename = nodename;
-
- if (!vmid) {
- return;
- }
-
- me.vmid = vmid;
-
- me.store.setProxy({
- type: 'pve',
- url: '/api2/json/nodes/' + me.nodename + '/qemu/' + me.vmid +'/snapshot'
- });
-
- me.store.load();
- },
-
- initComponent: function() {
- var me = this;
-
- if (!me.nodename) {
- throw "no node name specified";
- }
-
- if (!me.vmid) {
- throw "no VM ID specified";
- }
-
- var store = Ext.create('Ext.data.Store', {
- fields: [ 'name'],
- filterOnLoad: true
- });
-
- Ext.apply(me, {
- store: store,
- valueField: 'name',
- displayField: 'name',
- listConfig: {
- columns: [
- {
- header: gettext('Snapshot'),
- dataIndex: 'name',
- hideable: false,
- flex: 1
- }
- ]
- }
- });
-
- me.callParent();
-
- me.loadStore(me.nodename, me.vmid);
- }
-});
+++ /dev/null
-Ext.define('PVE.form.StorageSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: ['widget.PVE.form.StorageSelector'],
-
- reloadStorageList: function() {
- var me = this;
- if (!me.nodename) {
- return;
- }
-
- var params = {};
- var url = '/api2/json/nodes/' + me.nodename + '/storage';
- if (me.storageContent) {
- params.content = me.storageContent;
- }
- if (me.targetNode) {
- params.target = me.targetNode;
- params.enabled = 1; // skip disabled storages
- }
- me.store.setProxy({
- type: 'pve',
- url: url,
- extraParams: params
- });
-
- me.store.load();
-
- },
-
- setTargetNode: function(targetNode) {
- var me = this;
-
- if (!targetNode || (me.targetNode === targetNode)) {
- return;
- }
-
- me.targetNode = targetNode;
-
- me.reloadStorageList();
- },
-
- setNodename: function(nodename) {
- var me = this;
-
- if (!nodename || (me.nodename === nodename)) {
- return;
- }
-
- me.nodename = nodename;
-
- me.reloadStorageList();
- },
-
- initComponent: function() {
- var me = this;
-
- var nodename = me.nodename;
- me.nodename = undefined;
-
- var store = Ext.create('Ext.data.Store', {
- model: 'pve-storage-status',
- sorters: {
- property: 'storage',
- order: 'DESC'
- }
- });
-
- Ext.apply(me, {
- store: store,
- allowBlank: false,
- valueField: 'storage',
- displayField: 'storage',
- listConfig: {
- columns: [
- {
- header: gettext('Name'),
- dataIndex: 'storage',
- hideable: false,
- flex: 1
- },
- {
- header: gettext('Type'),
- width: 60,
- dataIndex: 'type'
- },
- {
- header: gettext('Avail'),
- width: 80,
- dataIndex: 'avail',
- renderer: PVE.Utils.format_size
- },
- {
- header: gettext('Capacity'),
- width: 80,
- dataIndex: 'total',
- renderer: PVE.Utils.format_size
- }
- ]
- }
- });
-
- me.callParent();
-
- if (nodename) {
- me.setNodename(nodename);
- }
- }
-}, function() {
-
- Ext.define('pve-storage-status', {
- extend: 'Ext.data.Model',
- fields: [ 'storage', 'active', 'type', 'avail', 'total' ],
- idProperty: 'storage'
- });
-
-});
+++ /dev/null
-Ext.define('PVE.form.Textfield', {
- extend: 'Ext.form.field.Text',
- alias: ['widget.pvetextfield'],
-
- skipEmptyText: true,
-
- deleteEmpty: false,
-
- getSubmitData: function() {
- var me = this,
- data = null,
- val;
- if (!me.disabled && me.submitValue && !me.isFileUpload()) {
- val = me.getSubmitValue();
- if (val !== null) {
- data = {};
- data[me.getName()] = val;
- } else if (me.deleteEmpty) {
- data = {};
- data['delete'] = me.getName();
- }
- }
- return data;
- },
-
- getSubmitValue: function() {
- var me = this;
-
- var value = this.processRawValue(this.getRawValue());
- if (value !== '') {
- return value;
- }
-
- return me.skipEmptyText ? null: value;
- }
-});
\ No newline at end of file
+++ /dev/null
-Ext.define('PVE.form.UserSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: ['widget.pveUserSelector'],
-
- initComponent: function() {
- var me = this;
-
- var store = new Ext.data.Store({
- model: 'pve-users'
- });
-
- var render_full_name = function(firstname, metaData, record) {
-
- var first = firstname || '';
- var last = record.data.lastname || '';
- return first + " " + last;
- };
-
- Ext.apply(me, {
- store: store,
- allowBlank: false,
- autoSelect: false,
- valueField: 'userid',
- displayField: 'userid',
- listConfig: {
- columns: [
- {
- header: gettext('User'),
- sortable: true,
- dataIndex: 'userid',
- flex: 1
- },
- {
- header: gettext('Name'),
- sortable: true,
- renderer: render_full_name,
- dataIndex: 'firstname',
- flex: 1
- },
- {
- id: 'comment',
- header: gettext('Comment'),
- sortable: false,
- dataIndex: 'comment',
- flex: 1
- }
- ]
- }
- });
-
- me.callParent();
-
- store.load({ params: { enabled: 1 }});
- }
-
-}, function() {
-
- Ext.define('pve-users', {
- extend: 'Ext.data.Model',
- fields: [
- 'userid', 'firstname', 'lastname' , 'email', 'comment',
- { type: 'boolean', name: 'enable' },
- { type: 'date', dateFormat: 'timestamp', name: 'expire' }
- ],
- proxy: {
- type: 'pve',
- url: "/api2/json/access/users"
- },
- idProperty: 'userid'
- });
-
-});
-
-
+++ /dev/null
-Ext.define('PVE.form.VlanField', {
- extend: 'Ext.form.field.Number',
- alias: ['widget.pveVlanField'],
-
- deleteEmpty: false,
-
- emptyText: 'no VLAN',
-
- fieldLabel: gettext('VLAN Tag'),
-
- allowBlank: true,
-
- getSubmitData: function() {
- var me = this,
- data = null,
- val;
- if (!me.disabled && me.submitValue) {
- val = me.getSubmitValue();
- if (val) {
- data = {};
- data[me.getName()] = val;
- } else if (me.deleteEmpty) {
- data = {};
- data['delete'] = me.getName();
- }
- }
- return data;
- },
-
- initComponent: function() {
- var me = this;
-
- Ext.apply(me, {
- minValue: 1,
- maxValue: 4094
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.form.VMIDSelector', {
- extend: 'Ext.form.field.Number',
- alias: 'widget.pveVMIDSelector',
-
- allowBlank: false,
-
- minValue: 100,
-
- maxValue: 999999999,
-
- validateExists: undefined,
-
- loadNextFreeVMID: false,
-
- initComponent: function() {
- var me = this;
-
- Ext.applyIf(me, {
- fieldLabel: 'VM ID',
- listeners: {
- 'change': function(field, newValue, oldValue) {
- if (!Ext.isDefined(me.validateExists)) {
- return;
- }
- PVE.Utils.API2Request({
- params: { vmid: newValue },
- url: '/cluster/nextid',
- method: 'GET',
- success: function(response, opts) {
- if (me.validateExists === true) {
- me.markInvalid(gettext('This VM ID does not exists'));
- }
- },
- failure: function(response, opts) {
- if (me.validateExists === false) {
- me.markInvalid(gettext('This VM ID is already in use'));
- }
- }
- });
- }
- }
- });
-
- me.callParent();
-
- if (me.loadNextFreeVMID) {
- PVE.Utils.API2Request({
- url: '/cluster/nextid',
- method: 'GET',
- success: function(response, opts) {
- me.setRawValue(response.result.data);
- }
- });
- }
- }
-});
+++ /dev/null
-Ext.define('PVE.form.VNCKeyboardSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.VNCKeyboardSelector'],
- comboItems: PVE.Utils.kvm_keymap_array()
-});
+++ /dev/null
-/*
- * Top left combobox, used to select a view of the underneath RessourceTree
- */
-Ext.define('PVE.form.ViewSelector', {
- extend: 'Ext.form.field.ComboBox',
- alias: ['widget.pveViewSelector'],
-
- initComponent: function() {
- var me = this;
-
- var default_views = {
- server: {
- text: gettext('Server View'),
- groups: ['node']
- },
- folder: {
- text: gettext('Folder View'),
- groups: ['type']
- },
- storage: {
- text: gettext('Storage View'),
- groups: ['node'],
- filterfn: function(node) {
- return node.data.type === 'storage' || node.data.type === 'node';
- }
- },
- pool: {
- text: gettext('Pool View'),
- groups: ['pool'],
- // Pool View only lists VMs and Containers
- filterfn: function(node) {
- return node.data.type === 'qemu' || node.data.type === 'lxc' || node.data.type === 'openvz' ||
- node.data.type === 'pool';
- }
- }
- };
-
- var groupdef = [];
- Ext.Object.each(default_views, function(viewname, value) {
- groupdef.push([viewname, value.text]);
- });
-
- var store = Ext.create('Ext.data.Store', {
- model: 'KeyValue',
- proxy: {
- type: 'memory',
- reader: 'array'
- },
- data: groupdef,
- autoload: true,
- });
-
- Ext.apply(me, {
- hideLabel: true,
- store: store,
- value: groupdef[0][0],
- editable: false,
- allowBlank: false,
- forceSelection: true,
- autoSelect: false,
- valueField: 'key',
- displayField: 'value',
-
- getViewFilter: function() {
- var view = me.getValue();
- return Ext.apply({ id: view }, default_views[view] || default_views.server);
- },
-
- getState: function() {
- return { value: me.getValue() };
- },
-
- applyState : function(state, doSelect) {
- var view = me.getValue();
- if (state && state.value && (view != state.value)) {
- var record = store.findRecord('key', state.value);
- if (record) {
- me.setValue(state.value, true);
- if (doSelect) {
- me.fireEvent('select', me, [record]);
- }
- }
- }
- },
- stateEvents: [ 'select' ],
- stateful: true,
- id: 'view'
- });
-
- me.callParent();
-
- var statechange = function(sp, key, value) {
- if (key === me.id) {
- me.applyState(value, true);
- }
- };
-
- var sp = Ext.state.Manager.getProvider();
-
- me.mon(sp, 'statechange', statechange, me);
- }
-});
+++ /dev/null
-Ext.define('PVE.form.iScsiProviderSelector', {
- extend: 'PVE.form.KVComboBox',
- alias: ['widget.pveiScsiProviderSelector'],
-
- initComponent: function() {
- var me = this;
-
- me.data = [
- ['comstar', 'Comstar'],
- [ 'istgt', 'istgt'],
- [ 'iet', 'IET']
- ];
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.grid.BackupView', {
- extend: 'Ext.grid.GridPanel',
-
- alias: ['widget.pveBackupView'],
-
-
- initComponent : function() {
- var me = this;
-
- var nodename = me.pveSelNode.data.node;
- if (!nodename) {
- throw "no node name specified";
- }
-
- var vmid = me.pveSelNode.data.vmid;
- if (!vmid) {
- throw "no VM ID specified";
- }
-
- var vmtype = me.pveSelNode.data.type;
- if (!vmtype) {
- throw "no VM type specified";
- }
-
- var filterFn;
- if (vmtype === 'openvz') {
- filterFn = function(item) {
- return item.data.volid.match(':backup/vzdump-openvz-');
- };
- } else if (vmtype === 'lxc') {
- filterFn = function(item) {
- return item.data.volid.match(':backup/vzdump-lxc-');
- };
- } else if (vmtype === 'qemu') {
- filterFn = function(item) {
- return item.data.volid.match(':backup/vzdump-qemu-');
- };
- } else {
- throw "unsupported VM type '" + vmtype + "'";
- }
-
- me.store = Ext.create('Ext.data.Store', {
- model: 'pve-storage-content',
- sorters: {
- property: 'volid',
- order: 'DESC'
- },
- filters: { filterFn: filterFn }
- });
-
- var reload = Ext.Function.createBuffered(function() {
- if (me.store.proxy.url) {
- me.store.load();
- }
- }, 100);
-
- var setStorage = function(storage) {
- var url = '/api2/json/nodes/' + nodename + '/storage/' + storage + '/content';
- url += '?content=backup';
-
- me.store.setProxy({
- type: 'pve',
- url: url
- });
-
- reload();
- };
-
- var storagesel = Ext.create('PVE.form.StorageSelector', {
- nodename: nodename,
- fieldLabel: gettext('Storage'),
- labelAlign: 'right',
- storageContent: 'backup',
- allowBlank: false,
- listeners: {
- change: function(f, value) {
- setStorage(value);
- }
- }
- });
-
- var storagefilter = Ext.create('Ext.form.field.Text', {
- fieldLabel: gettext('Search'),
- labelWidth: 50,
- labelAlign: 'right',
- enableKeyEvents: true,
- listeners: {
- buffer: 500,
- keyup: function(field) {
- me.store.clearFilter(true);
- me.store.filter([
- {
- property: 'volid',
- value: field.getValue(),
- anyMatch: true,
- caseSensitive: false
- }
- ]);
- }
- }
- });
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var backup_btn = Ext.create('Ext.button.Button', {
- text: gettext('Backup now'),
- handler: function() {
- var win = Ext.create('PVE.window.Backup', {
- nodename: nodename,
- vmid: vmid,
- vmtype: vmtype,
- storage: storagesel.getValue()
- });
- win.show();
- }
- });
-
- var restore_btn = Ext.create('PVE.button.Button', {
- text: gettext('Restore'),
- disabled: true,
- selModel: sm,
- enableFn: function(rec) {
- return !!rec;
- },
- handler: function(b, e, rec) {
- var volid = rec.data.volid;
-
- var win = Ext.create('PVE.window.Restore', {
- nodename: nodename,
- vmid: vmid,
- volid: rec.data.volid,
- volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec),
- vmtype: vmtype
- });
- win.show();
- win.on('destroy', reload);
- }
- });
-
- var delete_btn = Ext.create('PVE.button.Button', {
- text: gettext('Remove'),
- disabled: true,
- selModel: sm,
- dangerous: true,
- confirmMsg: function(rec) {
- var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
- "'" + rec.data.volid + "'");
- msg += " " + gettext('This will permanently erase all image data.');
-
- return msg;
- },
- enableFn: function(rec) {
- return !!rec;
- },
- handler: function(b, e, rec){
- var storage = storagesel.getValue();
- if (!storage) {
- return;
- }
-
- var volid = rec.data.volid;
- PVE.Utils.API2Request({
- url: "/nodes/" + nodename + "/storage/" + storage + "/content/" + volid,
- method: 'DELETE',
- waitMsgTarget: me,
- failure: function(response, opts) {
- Ext.Msg.alert('Error', response.htmlStatus);
- },
- success: function(response, options) {
- reload();
- }
- });
- }
- });
-
- Ext.apply(me, {
- stateful: false,
- selModel: sm,
- tbar: [ backup_btn, restore_btn, delete_btn, '->', storagesel, storagefilter ],
- columns: [
- {
- header: gettext('Name'),
- flex: 1,
- sortable: true,
- renderer: PVE.Utils.render_storage_content,
- dataIndex: 'volid'
- },
- {
- header: gettext('Format'),
- width: 100,
- dataIndex: 'format'
- },
- {
- header: gettext('Size'),
- width: 100,
- renderer: PVE.Utils.format_size,
- dataIndex: 'size'
- }
- ],
- listeners: {
- show: reload
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-
-// partly copied from extjs/examples/ux/CheckColumn.js
-
-Ext.define('PVE.CheckColumn', {
- extend: 'Ext.grid.column.Column',
- alias: 'widget.checkcolumn',
-
- constructor: function(cfg) {
- this.renderer = function(value){
- var cssPrefix = Ext.baseCSSPrefix,
- cls = [cssPrefix + 'grid-checkheader'];
-
- if (value) {
- cls.push(cssPrefix + 'grid-checkheader-checked');
- }
- return '<div class="' + cls.join(' ') + '"> </div>';
- };
-
- this.addEvents('checkchange');
-
- this.callParent(arguments);
- },
-
- processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
- if (type == 'mousedown') {
- var record = view.panel.store.getAt(recordIndex),
- dataIndex = this.dataIndex,
- checked = !record.get(dataIndex);
- record.set(dataIndex, checked);
- this.fireEvent('checkchange', this, record, checked);
- return false;
- } else {
- return this.callParent(arguments);
- }
- }
-
-});
-
+++ /dev/null
-Ext.define('PVE.FirewallAliasEdit', {
- extend: 'PVE.window.Edit',
-
- base_url: undefined,
-
- alias_name: undefined,
-
- initComponent : function() {
- /*jslint confusion: true */
- var me = this;
-
- me.create = (me.alias_name === undefined);
-
- if (me.create) {
- me.url = '/api2/extjs' + me.base_url;
- me.method = 'POST';
- } else {
- me.url = '/api2/extjs' + me.base_url + '/' + me.alias_name;
- me.method = 'PUT';
- }
-
- var items = [
- {
- xtype: 'textfield',
- name: me.create ? 'name' : 'rename',
- fieldLabel: gettext('Name'),
- allowBlank: false
- },
- {
- xtype: 'textfield',
- name: 'cidr',
- fieldLabel: gettext('IP/CIDR'),
- allowBlank: false
- },
- {
- xtype: 'textfield',
- name: 'comment',
- fieldLabel: gettext('Comment')
- }
- ];
-
- var ipanel = Ext.create('PVE.panel.InputPanel', {
- create: me.create,
- items: items
- });
-
- Ext.apply(me, {
- subject: gettext('Alias'),
- isAdd: true,
- items: [ ipanel ]
- });
-
- me.callParent();
-
- if (!me.create) {
- me.load({
- success: function(response, options) {
- var values = response.result.data;
- values.rename = values.name;
- ipanel.setValues(values);
- }
- });
- }
- }
-});
-
-Ext.define('PVE.FirewallAliases', {
- extend: 'Ext.grid.Panel',
- alias: ['widget.pveFirewallAliases'],
-
- base_url: undefined,
-
- title: gettext('Alias'),
-
- initComponent : function() {
- /*jslint confusion: true */
-
- var me = this;
-
- if (!me.base_url) {
- throw "missing base_url configuration";
- }
-
- var store = new Ext.data.Store({
- fields: [ 'name', 'cidr', 'comment', 'digest' ],
- proxy: {
- type: 'pve',
- url: "/api2/json" + me.base_url
- },
- idProperty: 'name',
- sorters: {
- property: 'name',
- order: 'DESC'
- }
- });
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var reload = function() {
- var oldrec = sm.getSelection()[0];
- store.load(function(records, operation, success) {
- if (oldrec) {
- var rec = store.findRecord('name', oldrec.data.name);
- if (rec) {
- sm.select(rec);
- }
- }
- });
- };
-
- var run_editor = function() {
- var sm = me.getSelectionModel();
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
-
- var win = Ext.create('PVE.FirewallAliasEdit', {
- base_url: me.base_url,
- alias_name: rec.data.name
- });
-
- win.show();
- win.on('destroy', reload);
- };
-
- me.editBtn = new PVE.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- handler: run_editor
- });
-
- me.addBtn = Ext.create('Ext.Button', {
- text: gettext('Add'),
- handler: function() {
- var win = Ext.create('PVE.FirewallAliasEdit', {
- base_url: me.base_url
- });
- win.on('destroy', reload);
- win.show();
- }
- });
-
- me.removeBtn = new PVE.button.Button({
- text: gettext('Remove'),
- selModel: sm,
- disabled: true,
- handler: function() {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- PVE.Utils.API2Request({
- url: me.base_url + '/' + rec.data.name,
- method: 'DELETE',
- waitMsgTarget: me,
- failure: function(response, options) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- callback: reload
- });
- }
- });
-
-
- Ext.applyIf(me, {
- store: store,
- tbar: [ me.addBtn, me.removeBtn, me.editBtn ],
- selModel: sm,
- columns: [
- { header: gettext('Name'), dataIndex: 'name', width: 100 },
- { header: gettext('IP/CIDR'), dataIndex: 'cidr', width: 100 },
- { header: gettext('Comment'), dataIndex: 'comment', flex: 1 }
- ],
- listeners: {
- itemdblclick: run_editor
- }
- });
-
- me.callParent();
-
- me.on('show', reload);
- }
-});
+++ /dev/null
-Ext.define('PVE.FirewallOptions', {
- extend: 'PVE.grid.ObjectGrid',
- alias: ['widget.pveFirewallOptions'],
-
- fwtype: undefined, // 'dc', 'node' or 'vm'
-
- base_url: undefined,
-
- initComponent : function() {
- /*jslint confusion: true */
-
- var me = this;
-
- if (!me.base_url) {
- throw "missing base_url configuration";
- }
-
- if (me.fwtype === 'dc' || me.fwtype === 'node' || me.fwtype === 'vm') {
- if (me.fwtype === 'node') {
- me.cwidth1 = 250;
- }
- } else {
- throw "unknown firewall option type";
- }
-
- var rows = {};
-
- var add_boolean_row = function(name, text, defaultValue, labelWidth) {
- rows[name] = {
- header: text,
- required: true,
- defaultValue: defaultValue || 0,
- renderer: PVE.Utils.format_boolean,
- editor: {
- xtype: 'pveWindowEdit',
- subject: text,
- fieldDefaults: { labelWidth: labelWidth || 100 },
- items: {
- xtype: 'pvecheckbox',
- defaultValue: defaultValue || 0,
- checked: defaultValue ? true : false,
- name: name,
- uncheckedValue: 0,
- fieldLabel: text
- }
- }
- };
- };
-
- var add_integer_row = function(name, text, labelWidth, minValue) {
- rows[name] = {
- header: text,
- required: true,
- renderer: function(value) {
- return value || PVE.Utils.defaultText;
- },
- editor: {
- xtype: 'pveWindowEdit',
- subject: text,
- fieldDefaults: { labelWidth: labelWidth || 100 },
- items: {
- xtype: 'numberfield',
- name: name,
- minValue: minValue,
- decimalPrecision: 0,
- fieldLabel: text,
- emptyText: gettext('Default'),
- getSubmitData: function() {
- var me = this;
- var val = me.getSubmitValue();
- if (val !== null && val !== '') {
- var data = {};
- data[name] = val;
- return data;
- } else {
- return { 'delete' : name };
- }
- }
- }
- }
- };
- };
-
- var add_log_row = function(name, labelWidth) {
- rows[name] = {
- header: name,
- required: true,
- defaultValue: 'nolog',
- editor: {
- xtype: 'pveWindowEdit',
- subject: name,
- fieldDefaults: { labelWidth: labelWidth || 100 },
- items: {
- xtype: 'pveKVComboBox',
- name: name,
- fieldLabel: name,
- data: [['nolog', 'nolog'], ['info', 'info'], ['err', 'err'],
- ['warning', 'warning'], ['crit', 'crit'], ['alert', 'alert'],
- ['emerg', 'emerg'], ['debug', 'debug']]
- }
- }
- };
- };
-
-
- if (me.fwtype === 'node') {
- add_boolean_row('enable', gettext('Enable Firewall'), 1);
- add_boolean_row('nosmurfs', gettext('SMURFS filter'), 1);
- add_boolean_row('tcpflags', gettext('TCP flags filter'), 0);
- add_integer_row('nf_conntrack_max', 'nf_conntrack_max', 120, 32768);
- add_integer_row('nf_conntrack_tcp_timeout_established',
- 'nf_conntrack_tcp_timeout_established', 250, 7875);
- add_log_row('log_level_in');
- add_log_row('log_level_out');
- add_log_row('tcp_flags_log_level', 120);
- add_log_row('smurf_log_level');
- } else if (me.fwtype === 'vm') {
- add_boolean_row('enable', gettext('Enable Firewall'), 0);
- add_boolean_row('dhcp', gettext('Enable DHCP'), 0);
- add_boolean_row('macfilter', gettext('MAC filter'), 1);
- add_log_row('log_level_in');
- add_log_row('log_level_out');
- } else if (me.fwtype === 'dc') {
- add_boolean_row('enable', gettext('Enable Firewall'), 0);
- }
-
- if (me.fwtype === 'dc' || me.fwtype === 'vm') {
- rows.policy_in = {
- header: gettext('Input Policy'),
- required: true,
- defaultValue: 'DROP',
- editor: {
- xtype: 'pveWindowEdit',
- subject: gettext('Input Policy'),
- items: {
- xtype: 'pveFirewallPolicySelector',
- name: 'policy_in',
- value: 'DROP',
- fieldLabel: gettext('Input Policy')
- }
- }
- };
-
- rows.policy_out = {
- header: gettext('Output Policy'),
- required: true,
- defaultValue: 'ACCEPT',
- editor: {
- xtype: 'pveWindowEdit',
- subject: gettext('Output Policy'),
- items: {
- xtype: 'pveFirewallPolicySelector',
- name: 'policy_out',
- value: 'ACCEPT',
- fieldLabel: gettext('Output Policy')
- }
- }
- };
- }
-
- var reload = function() {
- me.rstore.load();
- };
-
- var run_editor = function() {
- var sm = me.getSelectionModel();
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
-
- var rowdef = rows[rec.data.key];
- if (!rowdef.editor) {
- return;
- }
-
- var win;
- if (Ext.isString(rowdef.editor)) {
- win = Ext.create(rowdef.editor, {
- pveSelNode: me.pveSelNode,
- confid: rec.data.key,
- url: '/api2/extjs' + me.base_url
- });
- } else {
- var config = Ext.apply({
- pveSelNode: me.pveSelNode,
- confid: rec.data.key,
- url: '/api2/extjs' + me.base_url
- }, rowdef.editor);
- win = Ext.createWidget(rowdef.editor.xtype, config);
- win.load();
- }
-
- win.show();
- win.on('destroy', reload);
- };
-
- var edit_btn = new Ext.Button({
- text: gettext('Edit'),
- disabled: true,
- handler: run_editor
- });
-
- var set_button_status = function() {
- var sm = me.getSelectionModel();
- var rec = sm.getSelection()[0];
-
- if (!rec) {
- edit_btn.disable();
- return;
- }
- var rowdef = rows[rec.data.key];
- edit_btn.setDisabled(!rowdef.editor);
- };
-
- Ext.applyIf(me, {
- url: "/api2/json" + me.base_url,
- cwidth1: 150,
- tbar: [ edit_btn ],
- rows: rows,
- listeners: {
- itemdblclick: run_editor,
- selectionchange: set_button_status
- }
- });
-
- me.callParent();
-
- me.on('show', reload);
- }
-});
+++ /dev/null
-Ext.define('PVE.form.FWMacroSelector', {
- extend: 'PVE.form.ComboGrid',
- alias: 'widget.pveFWMacroSelector',
-
- initComponent: function() {
- var me = this;
-
- var store = Ext.create('Ext.data.Store', {
- autoLoad: true,
- fields: [ 'macro', 'descr' ],
- idProperty: 'macro',
- proxy: {
- type: 'pve',
- url: "/api2/json/cluster/firewall/macros"
- },
- sorters: {
- property: 'macro',
- order: 'DESC'
- }
- });
-
- Ext.apply(me, {
- store: store,
- allowBlank: true,
- autoSelect: false,
- valueField: 'macro',
- displayField: 'macro',
- listConfig: {
- columns: [
- {
- header: gettext('Macro'),
- dataIndex: 'macro',
- hideable: false,
- width: 100
- },
- {
- header: gettext('Description'),
- flex: 1,
- dataIndex: 'descr'
- }
- ]
- }
- });
-
- me.callParent();
- }
-});
-
-Ext.define('PVE.FirewallRulePanel', {
- extend: 'PVE.panel.InputPanel',
-
- allow_iface: false,
-
- list_refs_url: undefined,
-
- onGetValues: function(values) {
- var me = this;
-
- // hack: editable ComboGrid returns nothing when empty, so we need to set ''
- // Also, disabled text fields return nothing, so we need to set ''
-
- Ext.Array.each(['source', 'dest', 'proto', 'sport', 'dport'], function(key) {
- if (values[key] === undefined) {
- values[key] = '';
- }
- });
-
- delete values.modified_marker;
-
- return values;
- },
-
- initComponent : function() {
- var me = this;
-
- if (!me.list_refs_url) {
- throw "no list_refs_url specified";
- }
-
- me.column1 = [
- {
- // hack: we use this field to mark the form 'dirty' when the
- // record has errors- so that the user can safe the unmodified
- // form again.
- xtype: 'hiddenfield',
- name: 'modified_marker',
- value: '',
- },
- {
- xtype: 'pveKVComboBox',
- name: 'type',
- value: 'in',
- data: [['in', 'in'], ['out', 'out']],
- fieldLabel: gettext('Direction'),
- allowBlank: false
- },
- {
- xtype: 'pveKVComboBox',
- name: 'action',
- value: 'ACCEPT',
- data: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']],
- fieldLabel: gettext('Action'),
- allowBlank: false
- }
- ];
-
- if (me.allow_iface) {
- me.column1.push({
- xtype: 'pvetextfield',
- name: 'iface',
- deleteEmpty: !me.create,
- value: '',
- fieldLabel: gettext('Interface')
- });
- } else {
- me.column1.push({
- xtype: 'displayfield',
- fieldLabel: '',
- height: 22, // hack: set same height as text fields
- value: ''
- });
- }
-
- me.column1.push([
- {
- xtype: 'displayfield',
- fieldLabel: '',
- height: 7,
- value: ''
- },
- {
- xtype: 'pveIPRefSelector',
- name: 'source',
- autoSelect: false,
- editable: true,
- base_url: me.list_refs_url,
- value: '',
- fieldLabel: gettext('Source')
-
- },
- {
- xtype: 'pveIPRefSelector',
- name: 'dest',
- autoSelect: false,
- editable: true,
- base_url: me.list_refs_url,
- value: '',
- fieldLabel: gettext('Destination')
- }
- ]);
-
-
- me.column2 = [
- {
- xtype: 'pvecheckbox',
- name: 'enable',
- checked: false,
- height: 22, // hack: set same height as text fields
- uncheckedValue: 0,
- fieldLabel: gettext('Enable')
- },
- {
- xtype: 'pveFWMacroSelector',
- name: 'macro',
- value: '',
- fieldLabel: gettext('Macro'),
- allowBlank: true,
- listeners: {
- change: function(f, value) {
- if (value === '') {
- me.down('field[name=proto]').setDisabled(false);
- me.down('field[name=sport]').setDisabled(false);
- me.down('field[name=dport]').setDisabled(false);
- } else {
- me.down('field[name=proto]').setDisabled(true);
- me.down('field[name=proto]').setValue('');
- me.down('field[name=sport]').setDisabled(true);
- me.down('field[name=sport]').setValue('');
- me.down('field[name=dport]').setDisabled(true);
- me.down('field[name=dport]').setValue('');
- }
- }
- }
- },
- {
- xtype: 'pveIPProtocolSelector',
- name: 'proto',
- autoSelect: false,
- editable: true,
- value: '',
- fieldLabel: gettext('Protocol')
- },
- {
- xtype: 'displayfield',
- fieldLabel: '',
- height: 7,
- value: ''
- },
- {
- xtype: 'textfield',
- name: 'sport',
- value: '',
- fieldLabel: gettext('Source port')
- },
- {
- xtype: 'textfield',
- name: 'dport',
- height: 22, // hack: set same height as text fields
- value: '',
- fieldLabel: gettext('Dest. port')
- }
- ];
-
- me.columnB = [
- {
- xtype: 'textfield',
- name: 'comment',
- value: '',
- fieldLabel: gettext('Comment')
- }
- ];
-
- me.callParent();
- }
-});
-
-Ext.define('PVE.FirewallRuleEdit', {
- extend: 'PVE.window.Edit',
-
- base_url: undefined,
- list_refs_url: undefined,
-
- allow_iface: false,
-
- initComponent : function() {
- /*jslint confusion: true */
- var me = this;
-
- if (!me.base_url) {
- throw "no base_url specified";
- }
- if (!me.list_refs_url) {
- throw "no list_refs_url specified";
- }
-
- me.create = (me.rule_pos === undefined);
-
- if (me.create) {
- me.url = '/api2/extjs' + me.base_url;
- me.method = 'POST';
- } else {
- me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString();
- me.method = 'PUT';
- }
-
- var ipanel = Ext.create('PVE.FirewallRulePanel', {
- create: me.create,
- list_refs_url: me.list_refs_url,
- allow_iface: me.allow_iface,
- rule_pos: me.rule_pos
- });
-
- Ext.apply(me, {
- subject: gettext('Rule'),
- isAdd: true,
- items: [ ipanel ]
- });
-
- me.callParent();
-
- if (!me.create) {
- me.load({
- success: function(response, options) {
- var values = response.result.data;
- ipanel.setValues(values);
- if (values.errors) {
- var field = me.query('[isFormField][name=modified_marker]')[0];
- field.setValue(1);
- Ext.Function.defer(function() {
- var form = ipanel.up('form').getForm();
- form.markInvalid(values.errors)
- }, 100);
- }
- }
- });
- }
- }
-});
-
-Ext.define('PVE.FirewallGroupRuleEdit', {
- extend: 'PVE.window.Edit',
-
- base_url: undefined,
-
- allow_iface: false,
-
- initComponent : function() {
- /*jslint confusion: true */
- var me = this;
-
- me.create = (me.rule_pos === undefined);
-
- if (me.create) {
- me.url = '/api2/extjs' + me.base_url;
- me.method = 'POST';
- } else {
- me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString();
- me.method = 'PUT';
- }
-
- var column1 = [
- {
- xtype: 'hiddenfield',
- name: 'type',
- value: 'group'
- },
- {
- xtype: 'pveSecurityGroupsSelector',
- name: 'action',
- value: '',
- fieldLabel: gettext('Security Group'),
- allowBlank: false
- }
- ];
-
- if (me.allow_iface) {
- column1.push({
- xtype: 'pvetextfield',
- name: 'iface',
- deleteEmpty: !me.create,
- value: '',
- fieldLabel: gettext('Interface')
- });
- }
-
- var ipanel = Ext.create('PVE.panel.InputPanel', {
- create: me.create,
- column1: column1,
- column2: [
- {
- xtype: 'pvecheckbox',
- name: 'enable',
- checked: false,
- height: 22, // hack: set same height as text fields
- uncheckedValue: 0,
- fieldLabel: gettext('Enable')
- }
- ],
- columnB: [
- {
- xtype: 'textfield',
- name: 'comment',
- value: '',
- fieldLabel: gettext('Comment')
- }
- ]
- });
-
- Ext.apply(me, {
- subject: gettext('Rule'),
- isAdd: true,
- items: [ ipanel ]
- });
-
- me.callParent();
-
- if (!me.create) {
- me.load({
- success: function(response, options) {
- var values = response.result.data;
- ipanel.setValues(values);
- }
- });
- }
- }
-});
-
-Ext.define('PVE.FirewallRules', {
- extend: 'Ext.grid.Panel',
- alias: 'widget.pveFirewallRules',
-
- base_url: undefined,
- list_refs_url: undefined,
-
- addBtn: undefined,
- removeBtn: undefined,
- editBtn: undefined,
- groupBtn: undefined,
-
- tbar_prefix: undefined,
-
- allow_groups: true,
- allow_iface: false,
-
- setBaseUrl: function(url) {
- var me = this;
-
- me.base_url = url;
-
- if (url === undefined) {
- me.addBtn.setDisabled(true);
- if (me.groupBtn) {
- me.groupBtn.setDisabled(true);
- }
- me.store.removeAll();
- } else {
- me.addBtn.setDisabled(false);
- if (me.groupBtn) {
- me.groupBtn.setDisabled(false);
- }
- me.store.setProxy({
- type: 'pve',
- url: '/api2/json' + url
- });
-
- me.store.load();
- }
- },
-
- moveRule: function(from, to) {
- var me = this;
-
- if (!me.base_url) {
- return;
- }
-
- PVE.Utils.API2Request({
- url: me.base_url + "/" + from,
- method: 'PUT',
- params: { moveto: to },
- waitMsgTarget: me,
- failure: function(response, options) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- callback: function() {
- me.store.load();
- }
- });
- },
-
- updateRule: function(rule) {
- var me = this;
-
- if (!me.base_url) {
- return;
- }
-
- rule.enable = rule.enable ? 1 : 0;
-
- var pos = rule.pos;
- delete rule.pos;
- delete rule.errors;
-
- PVE.Utils.API2Request({
- url: me.base_url + '/' + pos.toString(),
- method: 'PUT',
- params: rule,
- waitMsgTarget: me,
- failure: function(response, options) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- callback: function() {
- me.store.load();
- }
- });
- },
-
- deleteRule: function(rule) {
- var me = this;
-
- if (!me.base_url) {
- return;
- }
-
- PVE.Utils.API2Request({
- url: me.base_url + '/' + rule.pos.toString() +
- '?digest=' + encodeURIComponent(rule.digest),
- method: 'DELETE',
- waitMsgTarget: me,
- failure: function(response, options) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- callback: function() {
- me.store.load();
- }
- });
- },
-
- initComponent: function() {
- /*jslint confusion: true */
- var me = this;
-
- if (!me.list_refs_url) {
- throw "no list_refs_url specified";
- }
-
- var store = new Ext.data.Store({
- model: 'pve-fw-rule'
- });
-
- var reload = function() {
- store.load();
- };
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var type = rec.data.type;
-
- var editor;
- if (type === 'in' || type === 'out') {
- editor = 'PVE.FirewallRuleEdit';
- } else if (type === 'group') {
- editor = 'PVE.FirewallGroupRuleEdit';
- } else {
- return;
- }
-
- var win = Ext.create(editor, {
- digest: rec.data.digest,
- allow_iface: me.allow_iface,
- base_url: me.base_url,
- list_refs_url: me.list_refs_url,
- rule_pos: rec.data.pos
- });
-
- win.show();
- win.on('destroy', reload);
- };
-
- me.editBtn = new PVE.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- handler: run_editor
- });
-
- me.addBtn = Ext.create('Ext.Button', {
- text: gettext('Add'),
- disabled: true,
- handler: function() {
- var win = Ext.create('PVE.FirewallRuleEdit', {
- allow_iface: me.allow_iface,
- base_url: me.base_url,
- list_refs_url: me.list_refs_url
- });
- win.on('destroy', reload);
- win.show();
- }
- });
-
- if (me.allow_groups) {
- me.groupBtn = Ext.create('Ext.Button', {
- text: gettext('Insert') + ': ' +
- gettext('Security Group'),
- disabled: true,
- handler: function() {
- var win = Ext.create('PVE.FirewallGroupRuleEdit', {
- allow_iface: me.allow_iface,
- base_url: me.base_url
- });
- win.on('destroy', reload);
- win.show();
- }
- });
- }
-
- me.removeBtn = new PVE.button.Button({
- text: gettext('Remove'),
- selModel: sm,
- disabled: true,
- handler: function() {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- me.deleteRule(rec.data);
- }
- });
-
- var tbar = me.tbar_prefix ? [ me.tbar_prefix ] : [];
- tbar.push(me.addBtn);
- if (me.groupBtn) {
- tbar.push(me.groupBtn);
- }
- tbar.push([ me.removeBtn, me.editBtn ]);
-
- var render_errors = function(name, value, metaData, record) {
- var errors = record.data.errors;
- if (errors && errors[name]) {
- metaData.tdCls = 'x-form-invalid-field';
- var html = '<p>' + Ext.htmlEncode(errors[name]) + '</p>';
- metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' +
- html.replace(/\"/g,'"') + '"';
- }
- return value;
- };
-
- var columns = [
- {
- // similar to xtype: 'rownumberer',
- dataIndex: 'pos',
- resizable: false,
- width: 23,
- sortable: false,
- align: 'right',
- hideable: false,
- menuDisabled: true,
- renderer: function(value, metaData, record, rowIdx, colIdx, store) {
- metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
- if (value >= 0) {
- return value;
- }
- return '';
- }
- },
- {
- xtype: 'checkcolumn',
- header: gettext('Enable'),
- dataIndex: 'enable',
- listeners: {
- checkchange: function(column, record, checked) {
- record.commit();
- var data = {};
- record.fields.each(function(field) {
- data[field.name] = record.get(field.name);
- });
- if (!me.allow_iface || !data.iface) {
- delete data.iface;
- }
- me.updateRule(data);
- }
- },
- width: 50
- },
- {
- header: gettext('Type'),
- dataIndex: 'type',
- renderer: function(value, metaData, record) {
- return render_errors('type', value, metaData, record);
- },
- width: 50
- },
- {
- header: gettext('Action'),
- dataIndex: 'action',
- renderer: function(value, metaData, record) {
- return render_errors('action', value, metaData, record);
- },
- width: 80
- },
- {
- header: gettext('Macro'),
- dataIndex: 'macro',
- renderer: function(value, metaData, record) {
- return render_errors('macro', value, metaData, record);
- },
- width: 80
- }
- ];
-
- if (me.allow_iface) {
- columns.push({
- header: gettext('Interface'),
- dataIndex: 'iface',
- renderer: function(value, metaData, record) {
- return render_errors('iface', value, metaData, record);
- },
- width: 80
- });
- }
-
- columns.push([
- {
- header: gettext('Source'),
- dataIndex: 'source',
- renderer: function(value, metaData, record) {
- return render_errors('source', value, metaData, record);
- },
- width: 100
- },
- {
- header: gettext('Destination'),
- dataIndex: 'dest',
- renderer: function(value, metaData, record) {
- return render_errors('dest', value, metaData, record);
- },
- width: 100
- },
- {
- header: gettext('Protocol'),
- dataIndex: 'proto',
- renderer: function(value, metaData, record) {
- return render_errors('proto', value, metaData, record);
- },
- width: 100
- },
- {
- header: gettext('Dest. port'),
- dataIndex: 'dport',
- renderer: function(value, metaData, record) {
- return render_errors('dport', value, metaData, record);
- },
- width: 100
- },
- {
- header: gettext('Source port'),
- dataIndex: 'sport',
- renderer: function(value, metaData, record) {
- return render_errors('sport', value, metaData, record);
- },
- width: 100
- },
- {
- header: gettext('Comment'),
- dataIndex: 'comment',
- flex: 1,
- renderer: function(value, metaData, record) {
- return render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record);
- }
- }
- ]);
-
- Ext.apply(me, {
- store: store,
- selModel: sm,
- tbar: tbar,
- viewConfig: {
- plugins: [
- {
- ptype: 'gridviewdragdrop',
- dragGroup: 'FWRuleDDGroup',
- dropGroup: 'FWRuleDDGroup'
- }
- ],
- listeners: {
- beforedrop: function(node, data, dropRec, dropPosition) {
- if (!dropRec) {
- return false; // empty view
- }
- var moveto = dropRec.get('pos');
- if (dropPosition === 'after') {
- moveto++;
- }
- var pos = data.records[0].get('pos');
- me.moveRule(pos, moveto);
- return 0;
- },
- itemdblclick: run_editor
- }
- },
- sortableColumns: false,
- columns: columns
- });
-
- me.callParent();
-
- if (me.base_url) {
- me.setBaseUrl(me.base_url); // load
- }
- }
-}, function() {
-
- Ext.define('pve-fw-rule', {
- extend: 'Ext.data.Model',
- fields: [ { name: 'enable', type: 'boolean' },
- 'type', 'action', 'macro', 'source', 'dest', 'proto', 'iface',
- 'dport', 'sport', 'comment', 'pos', 'digest', 'errors' ],
- idProperty: 'pos'
- });
-
-});
+++ /dev/null
-Ext.define('PVE.grid.ObjectGrid', {
- extend: 'Ext.grid.GridPanel',
- alias: ['widget.pveObjectGrid'],
-
- getObjectValue: function(key, defaultValue) {
- var me = this;
- var rec = me.store.getById(key);
- if (rec) {
- return rec.data.value;
- }
- return defaultValue;
- },
-
- renderKey: function(key, metaData, record, rowIndex, colIndex, store) {
- var me = this;
- var rows = me.rows;
- var rowdef = (rows && rows[key]) ? rows[key] : {};
- return rowdef.header || key;
- },
-
- renderValue: function(value, metaData, record, rowIndex, colIndex, store) {
- var me = this;
- var rows = me.rows;
- var key = record.data.key;
- var rowdef = (rows && rows[key]) ? rows[key] : {};
-
- var renderer = rowdef.renderer;
- if (renderer) {
- return renderer(value, metaData, record, rowIndex, colIndex, store);
- }
-
- return value;
- },
-
- initComponent : function() {
- var me = this;
-
- var rows = me.rows;
-
- if (!me.rstore) {
- if (!me.url) {
- throw "no url specified";
- }
-
- me.rstore = Ext.create('PVE.data.ObjectStore', {
- url: me.url,
- interval: me.interval,
- extraParams: me.extraParams,
- rows: me.rows
- });
- }
-
- var rstore = me.rstore;
-
- var store = Ext.create('PVE.data.DiffStore', { rstore: rstore });
-
- if (rows) {
- Ext.Object.each(rows, function(key, rowdef) {
- if (Ext.isDefined(rowdef.defaultValue)) {
- store.add({ key: key, value: rowdef.defaultValue });
- } else if (rowdef.required) {
- store.add({ key: key, value: undefined });
- }
- });
- }
-
- if (me.sorterFn) {
- store.sorters.add(new Ext.util.Sorter({
- sorterFn: me.sorterFn
- }));
- }
-
- store.filters.add(new Ext.util.Filter({
- filterFn: function(item) {
- if (rows) {
- var rowdef = rows[item.data.key];
- if (!rowdef || (rowdef.visible === false)) {
- return false;
- }
- }
- return true;
- }
- }));
-
- PVE.Utils.monStoreErrors(me, rstore);
-
- Ext.applyIf(me, {
- store: store,
- hideHeaders: true,
- stateful: false,
- columns: [
- {
- header: gettext('Name'),
- width: me.cwidth1 || 100,
- dataIndex: 'key',
- renderer: me.renderKey
- },
- {
- flex: 1,
- header: gettext('Value'),
- dataIndex: 'value',
- renderer: me.renderValue
- }
- ]
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.grid.PendingObjectGrid', {
- extend: 'PVE.grid.ObjectGrid',
- alias: ['widget.pvePendingObjectGrid'],
-
- getObjectValue: function(key, defaultValue, pending) {
- var me = this;
- var rec = me.store.getById(key);
- if (rec) {
- var value = (pending && Ext.isDefined(rec.data.pending) && (rec.data.pending !== '')) ?
- rec.data.pending : rec.data.value;
-
- if (Ext.isDefined(value) && (value !== '')) {
- return value;
- } else {
- return defaultValue;
- }
- }
- return defaultValue;
- },
-
- hasPendingChanges: function(key) {
- var me = this;
- var rows = me.rows;
- var rowdef = (rows && rows[key]) ? rows[key] : {};
- var keys = rowdef.multiKey || [ key ];
- var pending = false;
-
- Ext.Array.each(keys, function(k) {
- var rec = me.store.getById(k);
- if (rec && rec.data && Ext.isDefined(rec.data.pending) && (rec.data.pending !== '')) {
- pending = true;
- }
- });
-
- return pending;
- },
-
- renderValue: function(value, metaData, record, rowIndex, colIndex, store) {
- var me = this;
- var rows = me.rows;
- var key = record.data.key;
- var rowdef = (rows && rows[key]) ? rows[key] : {};
- var renderer = rowdef.renderer;
- var current = '';
- var pendingdelete = '';
- var pending = '';
-
- if (renderer) {
- current = renderer(value, metaData, record, rowIndex, colIndex, store, false);
- if (me.hasPendingChanges(key)) {
- pending = renderer(record.data.pending, metaData, record, rowIndex, colIndex, store, true);
- }
- if (pending == current) {
- pending = undefined;
- }
- } else {
- current = value;
- pending = record.data.pending;
- }
-
- if (record.data['delete']) {
- pendingdelete = '<div style="text-decoration: line-through;">'+ current +'</div>';
- }
-
- if (pending || pendingdelete) {
- return current + '<div style="color:red">' + pending + pendingdelete + '</div>';
- } else {
- return current;
- }
- },
-
- initComponent : function() {
- var me = this;
-
- var rows = me.rows;
-
- if (!me.rstore) {
- if (!me.url) {
- throw "no url specified";
- }
-
- me.rstore = Ext.create('PVE.data.ObjectStore', {
- model: 'KeyValuePendingDelete',
- readArray: true,
- url: me.url,
- interval: me.interval,
- extraParams: me.extraParams,
- rows: me.rows
- });
- }
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.pool.AddVM', {
- extend: 'PVE.window.Edit',
-
- initComponent : function() {
- /*jslint confusion: true */
- var me = this;
-
- if (!me.pool) {
- throw "no pool specified";
- }
-
- me.create = true;
- me.isAdd = true;
- me.url = "/pools/" + me.pool;
- me.method = 'PUT';
-
- Ext.apply(me, {
- subject: gettext('Virtual Machine'),
- width: 350,
- items: [
- {
- xtype: 'pveVMIDSelector',
- name: 'vms',
- validateExists: true,
- value: '',
- fieldLabel: "VM ID"
- }
- ]
- });
-
- me.callParent();
- }
-});
-
-Ext.define('PVE.pool.AddStorage', {
- extend: 'PVE.window.Edit',
-
- initComponent : function() {
- /*jslint confusion: true */
- var me = this;
-
- if (!me.pool) {
- throw "no pool specified";
- }
-
- me.create = true;
- me.isAdd = true;
- me.url = "/pools/" + me.pool;
- me.method = 'PUT';
-
- Ext.apply(me, {
- subject: gettext('Storage'),
- width: 350,
- items: [
- {
- xtype: 'PVE.form.StorageSelector',
- name: 'storage',
- nodename: 'localhost',
- autoSelect: false,
- value: '',
- fieldLabel: gettext("Storage")
- }
- ]
- });
-
- me.callParent();
- }
-});
-
-Ext.define('PVE.grid.PoolMembers', {
- extend: 'Ext.grid.GridPanel',
- alias: ['widget.pvePoolMembers'],
-
- // fixme: dynamic status update ?
-
- initComponent : function() {
- var me = this;
-
- if (!me.pool) {
- throw "no pool specified";
- }
-
- var store = Ext.create('Ext.data.Store', {
- model: 'PVEResources',
- sorters: [
- {
- property : 'type',
- direction: 'ASC'
- }
- ],
- proxy: {
- type: 'pve',
- root: 'data.members',
- url: "/api2/json/pools/" + me.pool
- }
- });
-
- var coldef = PVE.data.ResourceStore.defaultColums();
-
- var reload = function() {
- store.load();
- };
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var remove_btn = new PVE.button.Button({
- text: gettext('Remove'),
- disabled: true,
- selModel: sm,
- confirmMsg: function (rec) {
- return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
- "'" + rec.data.id + "'");
- },
- handler: function(btn, event, rec) {
- var params = { 'delete': 1 };
- if (rec.data.type === 'storage') {
- params.storage = rec.data.storage;
- } else if (rec.data.type === 'qemu' || rec.data.type === 'lxc' || rec.data.type === 'openvz') {
- params.vms = rec.data.vmid;
- } else {
- throw "unknown resource type";
- }
-
- PVE.Utils.API2Request({
- url: '/pools/' + me.pool,
- method: 'PUT',
- params: params,
- waitMsgTarget: me,
- callback: function() {
- reload();
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- }
- });
-
- Ext.apply(me, {
- store: store,
- selModel: sm,
- tbar: [
- {
- text: gettext('Add'),
- menu: new Ext.menu.Menu({
- items: [
- {
- text: gettext('Virtual Machine'),
- iconCls: 'pve-itype-icon-qemu',
- handler: function() {
- var win = Ext.create('PVE.pool.AddVM', { pool: me.pool });
- win.on('destroy', reload);
- win.show();
- }
- },
- {
- text: gettext('Storage'),
- iconCls: 'pve-itype-icon-storage',
- handler: function() {
- var win = Ext.create('PVE.pool.AddStorage', { pool: me.pool });
- win.on('destroy', reload);
- win.show();
- }
- }
- ]
- })
- },
- remove_btn
- ],
- viewConfig: {
- stripeRows: true
- },
- columns: coldef,
- listeners: {
- show: reload
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.grid.ResourceGrid', {
- extend: 'Ext.grid.GridPanel',
- alias: ['widget.pveResourceGrid'],
-
- //fixme: this makes still problems with the scrollbar
- //features: [ {ftype: 'chunking'}],
-
- title: gettext('Search'),
-
- initComponent : function() {
- var me = this;
-
- var rstore = PVE.data.ResourceStore;
- var sp = Ext.state.Manager.getProvider();
-
- var coldef = rstore.defaultColums();
-
- var store = Ext.create('Ext.data.Store', {
- model: 'PVEResources',
- sorters: [
- {
- property : 'type',
- direction: 'ASC'
- }
- ],
- proxy: { type: 'memory' }
- });
-
- var textfilter = '';
-
- var textfilter_match = function(item) {
- var match = false;
- Ext.each(['name', 'storage', 'node', 'type', 'text'], function(field) {
- var v = item.data[field];
- if (v !== undefined) {
- v = v.toLowerCase();
- if (v.indexOf(textfilter) >= 0) {
- match = true;
- return false;
- }
- }
- });
- return match;
- };
-
- var updateGrid = function() {
-
- var filterfn = me.viewFilter ? me.viewFilter.filterfn : null;
-
- //console.log("START GRID UPDATE " + me.viewFilter);
-
- store.suspendEvents();
-
- var nodeidx = {};
- var gather_child_nodes = function(cn) {
- if (!cn) {
- return;
- }
- var cs = cn.childNodes;
- if (!cs) {
- return;
- }
- var len = cs.length, i = 0, n, res;
-
- for (; i < len; i++) {
- var child = cs[i];
- var orgnode = rstore.data.get(child.data.id);
- if (orgnode) {
- if ((!filterfn || filterfn(child)) &&
- (!textfilter || textfilter_match(child))) {
- nodeidx[child.data.id] = orgnode;
- }
- }
- gather_child_nodes(child);
- }
- };
- gather_child_nodes(me.pveSelNode);
-
- // remove vanished items
- var rmlist = [];
- store.each(function(olditem) {
- var item = nodeidx[olditem.data.id];
- if (!item) {
- //console.log("GRID REM UID: " + olditem.data.id);
- rmlist.push(olditem);
- }
- });
-
- if (rmlist.length) {
- store.remove(rmlist);
- }
-
- // add new items
- var addlist = [];
- var key;
- for (key in nodeidx) {
- if (nodeidx.hasOwnProperty(key)) {
- var item = nodeidx[key];
-
- // getById() use find(), which is slow (ExtJS4 DP5)
- //var olditem = store.getById(item.data.id);
- var olditem = store.data.get(item.data.id);
-
- if (!olditem) {
- //console.log("GRID ADD UID: " + item.data.id);
- var info = Ext.apply({}, item.data);
- var child = Ext.create(store.model, info);
- addlist.push(item);
- continue;
- }
- // try to detect changes
- var changes = false;
- var fieldkeys = PVE.data.ResourceStore.fieldNames;
- var fieldcount = fieldkeys.length;
- var fieldind;
- for (fieldind = 0; fieldind < fieldcount; fieldind++) {
- var field = fieldkeys[fieldind];
- if (field != 'id' && item.data[field] != olditem.data[field]) {
- changes = true;
- //console.log("changed item " + item.id + " " + field + " " + item.data[field] + " != " + olditem.data[field]);
- olditem.beginEdit();
- olditem.set(field, item.data[field]);
- }
- }
- if (changes) {
- olditem.endEdit(true);
- olditem.commit(true);
- }
- }
- }
-
- if (addlist.length) {
- store.add(addlist);
- }
-
- store.sort();
-
- store.resumeEvents();
-
- store.fireEvent('datachanged', store);
-
- //console.log("END GRID UPDATE");
- };
-
- var filter_task = new Ext.util.DelayedTask(function(){
- updateGrid();
- });
-
- var load_cb = function() {
- updateGrid();
- };
-
- Ext.apply(me, {
- store: store,
- tbar: [
- '->',
- gettext('Search') + ':', ' ',
- {
- xtype: 'textfield',
- width: 200,
- value: textfilter,
- enableKeyEvents: true,
- listeners: {
- keyup: function(field, e) {
- var v = field.getValue();
- textfilter = v.toLowerCase();
- filter_task.delay(500);
- }
- }
- }
- ],
- viewConfig: {
- stripeRows: true
- },
- listeners: {
- itemcontextmenu: function(v, record, item, index, event) {
- event.stopEvent();
- v.select(record);
- var menu;
-
- if (record.data.type === 'qemu' && !record.data.template) {
- menu = Ext.create('PVE.qemu.CmdMenu', {
- pveSelNode: record
- });
- } else if (record.data.type === 'qemu' && record.data.template) {
- menu = Ext.create('PVE.qemu.TemplateMenu', {
- pveSelNode: record
- });
- } else if (record.data.type === 'lxc') {
- menu = Ext.create('PVE.lxc.CmdMenu', {
- pveSelNode: record
- });
- } else {
- return;
- }
-
- menu.showAt(event.getXY());
- },
- itemdblclick: function(v, record) {
- var ws = me.up('pveStdWorkspace');
- ws.selectById(record.data.id);
- },
- destroy: function() {
- rstore.un("load", load_cb);
- }
- },
- columns: coldef
- });
-
- me.callParent();
-
- updateGrid();
- rstore.on("load", load_cb);
- }
-});
+++ /dev/null
- Ext.override(Ext.view.Table, {
- afterRender: function() {
- var me = this;
-
- me.callParent();
-// EXT5DEBUG
-// me.mon(me.el, {
-// scroll: me.fireBodyScroll,
-// scope: me
-// });
-// if (!me.featuresMC ||
-// (me.featuresMC.findIndex('ftype', 'selectable') < 0)) {
-// me.el.unselectable();
-// }
-//
-// me.attachEventsForFeatures();
- }
-});
-
-Ext.define('PVE.grid.SelectFeature', {
- extend: 'Ext.grid.feature.Feature',
- alias: 'feature.selectable',
-
- mutateMetaRowTpl: function(metaRowTpl) {
- var tpl, i,
- ln = metaRowTpl.length;
-
- for (i = 0; i < ln; i++) {
- tpl = metaRowTpl[i];
- tpl = tpl.replace(/x-grid-row/, 'x-grid-row x-selectable');
- tpl = tpl.replace(/x-grid-cell-inner x-unselectable/g, 'x-grid-cell-inner');
- tpl = tpl.replace(/unselectable="on"/g, '');
- metaRowTpl[i] = tpl;
- }
- }
-});
+++ /dev/null
-Ext.define('PVE.panel.Config', {
- extend: 'Ext.panel.Panel',
- alias: 'widget.pvePanelConfig',
-
- initComponent: function() {
- var me = this;
-
- var stateid = me.hstateid;
-
- var sp = Ext.state.Manager.getProvider();
-
- var activeTab;
-
- var hsregex = /^([^\-\s]+)(-\S+)?$/;
-
- if (stateid) {
- var state = sp.get(stateid);
- if (state && state.value) {
- var res = hsregex.exec(state.value);
- if (res && res[1]) {
- activeTab = res[1];
- }
- }
- }
-
- var items = me.items || [];
- me.items = undefined;
-
- var tbar = me.tbar || [];
- me.tbar = undefined;
-
- var title = me.title || me.pveSelNode.data.text;
- me.title = undefined;
-
- tbar.unshift('->');
- tbar.unshift({
- xtype: 'tbtext',
- text: title,
- baseCls: 'x-panel-header-text',
- padding: '0 0 5 0'
- });
-
- Ext.applyIf(me, { showSearch: true });
-
- if (me.showSearch) {
- items.unshift({
- itemId: 'search',
- xtype: 'pveResourceGrid'
- });
- }
-
- var toolbar = Ext.create('Ext.toolbar.Toolbar', {
- items: tbar,
- style: 'border:0px;',
- height: 28
- });
-
- var tab = Ext.create('Ext.tab.Panel', {
- flex: 1,
- border: true,
- activeTab: activeTab,
- defaults: Ext.apply(me.defaults || {}, {
- pveSelNode: me.pveSelNode,
- viewFilter: me.viewFilter,
- workspace: me.workspace,
- border: false
- }),
- items: items,
- listeners: {
- afterrender: function(tp) {
- var first = tp.items.get(0);
- if (first) {
- first.fireEvent('show', first);
- }
- },
- tabchange: function(tp, newcard, oldcard) {
- var ntab = newcard.itemId;
-
- // Note: '' is alias for first tab.
- // First tab can be 'search' or something else
- if (newcard.itemId === items[0].itemId) {
- ntab = '';
- }
- if (stateid) {
- if (newcard.phstateid) {
- sp.set(newcard.phstateid, newcard.getHState());
- } else {
- sp.set(stateid, { value: ntab });
- }
- }
- }
- }
- });
-
- Ext.apply(me, {
- layout: { type: 'vbox', align: 'stretch' },
- items: [ toolbar, tab]
- });
-
- me.callParent();
-
- var statechange = function(sp, key, state) {
- if (stateid && (key === stateid) && state) {
- var atab = tab.getActiveTab().itemId;
- var res = hsregex.exec(state.value);
- var ntab = (res && res[1]) ? res[1] : items[0].itemId;
- if (ntab && (atab != ntab)) {
- tab.setActiveTab(ntab);
- }
- }
- };
-
- if (stateid) {
- me.mon(sp, 'statechange', statechange);
- }
- }
-});
+++ /dev/null
-Ext.define('PVE.panel.Firewall', {
- extend: 'PVE.panel.SubConfig',
- alias: 'widget.pveFirewallPanel',
-
- configPrefix: 'firewall',
-
- fwtype: undefined, // 'dc', 'node' or 'vm'
-
- base_url: undefined,
-
- initComponent: function() {
- /*jslint confusion: true */
- var me = this;
-
- if (!me.base_url) {
- throw "no base_url specified";
- }
-
- if (!(me.fwtype === 'dc' || me.fwtype === 'node' || me.fwtype === 'vm')) {
- throw "unknown firewall panel type";
- }
-
- var list_refs_url = me.fwtype === 'vm' ? (me.base_url + '/refs') :
- '/cluster/firewall/refs';
-
- var items = [
- {
- xtype: 'pveFirewallRules',
- title: gettext('Rules'),
- allow_iface: true,
- base_url: me.base_url + '/rules',
- list_refs_url: list_refs_url,
- itemId: 'rules'
- }
- ];
-
- if (me.fwtype === 'dc') {
- items.push({
- xtype: 'pveSecurityGroups',
- title: gettext('Security Group'),
- itemId: 'sg'
- });
- items.push({
- xtype: 'pveFirewallAliases',
- base_url: '/cluster/firewall/aliases',
- itemId: 'aliases'
- });
- items.push({
- xtype: 'pveIPSet',
- base_url: '/cluster/firewall/ipset',
- list_refs_url: list_refs_url,
- itemId: 'ipset'
- });
- }
-
- if (me.fwtype === 'vm') {
- items.push({
- xtype: 'pveFirewallAliases',
- base_url: me.base_url + '/aliases',
- itemId: 'aliases'
- });
- items.push({
- xtype: 'pveIPSet',
- base_url: me.base_url + '/ipset',
- list_refs_url: list_refs_url,
- itemId: 'ipset'
- });
- }
-
- items.push({
- xtype: 'pveFirewallOptions',
- title: gettext('Options'),
- base_url: me.base_url + '/options',
- fwtype: me.fwtype,
- itemId: 'options'
- });
-
- if (me.fwtype !== 'dc') {
- items.push({
- title: 'Log',
- itemId: 'fwlog',
- xtype: 'pveLogView',
- url: '/api2/extjs' + me.base_url + '/log'
- });
- }
-
- Ext.apply(me, {
- defaults: {
- border: false,
- pveSelNode: me.pveSelNode
- },
- items: items
- });
-
- me.callParent();
- }
-});
\ No newline at end of file
+++ /dev/null
-Ext.define('PVE.IPSetList', {
- extend: 'Ext.grid.Panel',
- alias: 'widget.pveIPSetList',
-
- ipset_panel: undefined,
-
- base_url: undefined,
-
- addBtn: undefined,
- removeBtn: undefined,
- editBtn: undefined,
-
- initComponent: function() {
- /*jslint confusion: true */
- var me = this;
-
- if (me.ipset_panel == undefined) {
- throw "no rule panel specified";
- }
-
- if (me.base_url == undefined) {
- throw "no base_url specified";
- }
-
- var store = new Ext.data.Store({
- fields: [ 'name', 'comment', 'digest' ],
- proxy: {
- type: 'pve',
- url: "/api2/json" + me.base_url
- },
- idProperty: 'name',
- sorters: {
- property: 'name',
- order: 'DESC'
- }
- });
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var reload = function() {
- var oldrec = sm.getSelection()[0];
- store.load(function(records, operation, success) {
- if (oldrec) {
- var rec = store.findRecord('name', oldrec.data.name);
- if (rec) {
- sm.select(rec);
- }
- }
- });
- };
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var win = Ext.create('PVE.window.Edit', {
- subject: "IPSet '" + rec.data.name + "'",
- url: me.base_url,
- method: 'POST',
- digest: rec.data.digest,
- items: [
- {
- xtype: 'hiddenfield',
- name: 'rename',
- value: rec.data.name
- },
- {
- xtype: 'textfield',
- name: 'name',
- value: rec.data.name,
- fieldLabel: gettext('Name'),
- allowBlank: false
- },
- {
- xtype: 'textfield',
- name: 'comment',
- value: rec.data.comment,
- fieldLabel: gettext('Comment')
- }
- ]
- });
- win.show();
- win.on('destroy', reload);
- };
-
- me.editBtn = new PVE.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- handler: run_editor
- });
-
- me.addBtn = new PVE.button.Button({
- text: gettext('Create'),
- handler: function() {
- sm.deselectAll();
- var win = Ext.create('PVE.window.Edit', {
- subject: 'IPSet',
- url: me.base_url,
- method: 'POST',
- items: [
- {
- xtype: 'textfield',
- name: 'name',
- value: '',
- fieldLabel: gettext('Name'),
- allowBlank: false
- },
- {
- xtype: 'textfield',
- name: 'comment',
- value: '',
- fieldLabel: gettext('Comment')
- }
- ]
- });
- win.show();
- win.on('destroy', reload);
-
- }
- });
-
- me.removeBtn = new PVE.button.Button({
- text: gettext('Remove'),
- selModel: sm,
- disabled: true,
- handler: function() {
- var rec = sm.getSelection()[0];
- if (!rec || !me.base_url) {
- return;
- }
- PVE.Utils.API2Request({
- url: me.base_url + '/' + rec.data.name,
- method: 'DELETE',
- waitMsgTarget: me,
- failure: function(response, options) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- callback: reload
- });
- }
- });
-
- Ext.apply(me, {
- store: store,
- tbar: [ '<b>IPSet:</b>', me.addBtn, me.removeBtn, me.editBtn ],
- selModel: sm,
- columns: [
- { header: 'IPSet', dataIndex: 'name', width: 100 },
- { header: gettext('Comment'), dataIndex: 'comment', flex: 1 }
- ],
- listeners: {
- itemdblclick: run_editor,
- select: function(sm, rec) {
- var url = me.base_url + '/' + rec.data.name;
- me.ipset_panel.setBaseUrl(url);
- },
- deselect: function() {
- me.ipset_panel.setBaseUrl(undefined);
- },
- show: reload
- }
- });
-
- me.callParent();
-
- store.load();
- }
-});
-
-Ext.define('PVE.IPSetCidrEdit', {
- extend: 'PVE.window.Edit',
-
- cidr: undefined,
-
- initComponent : function() {
- /*jslint confusion: true */
- var me = this;
-
- me.create = (me.cidr === undefined);
-
-
- if (me.create) {
- me.url = '/api2/extjs' + me.base_url;
- me.method = 'POST';
- } else {
- me.url = '/api2/extjs' + me.base_url + '/' + me.cidr;
- me.method = 'PUT';
- }
-
- var column1 = [];
-
- if (me.create) {
- if (!me.list_refs_url) {
- throw "no alias_base_url specified";
- }
-
- column1.push({
- xtype: 'pveIPRefSelector',
- name: 'cidr',
- ref_type: 'alias',
- autoSelect: false,
- editable: true,
- base_url: me.list_refs_url,
- value: '',
- fieldLabel: gettext('IP/CIDR')
- });
- } else {
- column1.push({
- xtype: 'displayfield',
- name: 'cidr',
- height: 22, // hack: set same height as text fields
- value: '',
- fieldLabel: gettext('IP/CIDR')
- });
- }
-
- var ipanel = Ext.create('PVE.panel.InputPanel', {
- create: me.create,
- column1: column1,
- column2: [
- {
- xtype: 'pvecheckbox',
- name: 'nomatch',
- checked: false,
- height: 22, // hack: set same height as text fields
- uncheckedValue: 0,
- fieldLabel: gettext('nomatch')
- }
- ],
- columnB: [
- {
- xtype: 'textfield',
- name: 'comment',
- value: '',
- fieldLabel: gettext('Comment')
- }
- ]
- });
-
- Ext.apply(me, {
- subject: gettext('IP/CIDR'),
- items: [ ipanel ]
- });
-
- me.callParent();
-
- if (!me.create) {
- me.load({
- success: function(response, options) {
- var values = response.result.data;
- ipanel.setValues(values);
- }
- });
- }
- }
-});
-
-Ext.define('PVE.IPSetGrid', {
- extend: 'Ext.grid.Panel',
- alias: 'widget.pveIPSetGrid',
-
- base_url: undefined,
- list_refs_url: undefined,
-
- addBtn: undefined,
- removeBtn: undefined,
- editBtn: undefined,
-
- setBaseUrl: function(url) {
- var me = this;
-
- me.base_url = url;
-
- if (url === undefined) {
- me.addBtn.setDisabled(true);
- me.store.removeAll();
- } else {
- me.addBtn.setDisabled(false);
- me.store.setProxy({
- type: 'pve',
- url: '/api2/json' + url
- });
-
- me.store.load();
- }
- },
-
- initComponent: function() {
- /*jslint confusion: true */
- var me = this;
-
- if (!me.list_refs_url) {
- throw "no1 list_refs_url specified";
- }
-
- var store = new Ext.data.Store({
- model: 'pve-ipset'
- });
-
- var reload = function() {
- store.load();
- };
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var win = Ext.create('PVE.IPSetCidrEdit', {
- base_url: me.base_url,
- cidr: rec.data.cidr
- });
- win.show();
- win.on('destroy', reload);
- };
-
- me.editBtn = new PVE.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- handler: run_editor
- });
-
- me.addBtn = new PVE.button.Button({
- text: gettext('Add'),
- disabled: true,
- handler: function() {
- if (!me.base_url) {
- return;
- }
- var win = Ext.create('PVE.IPSetCidrEdit', {
- base_url: me.base_url,
- list_refs_url: me.list_refs_url
- });
- win.show();
- win.on('destroy', reload);
- }
- });
-
- me.removeBtn = new PVE.button.Button({
- text: gettext('Remove'),
- selModel: sm,
- disabled: true,
- handler: function() {
- var rec = sm.getSelection()[0];
- if (!rec || !me.base_url) {
- return;
- }
-
- PVE.Utils.API2Request({
- url: me.base_url + '/' + rec.data.cidr,
- method: 'DELETE',
- waitMsgTarget: me,
- failure: function(response, options) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- callback: reload
- });
- }
- });
-
- var render_errors = function(value, metaData, record) {
- var errors = record.data.errors;
- if (errors) {
- var msg = errors.cidr || errors.nomatch;
- if (msg) {
- metaData.tdCls = 'x-form-invalid-field';
- var html = '<p>' + Ext.htmlEncode(msg) + '</p>';
- metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' +
- html.replace(/\"/g,'"') + '"';
- }
- }
- return value;
- };
-
- Ext.apply(me, {
- tbar: [ '<b>IP/CIDR:</b>', me.addBtn, me.removeBtn, me.editBtn ],
- store: store,
- selModel: sm,
- listeners: {
- itemdblclick: run_editor
- },
- columns: [
- {
- xtype: 'rownumberer'
- },
- {
- header: gettext('IP/CIDR'),
- dataIndex: 'cidr',
- width: 150,
- renderer: function(value, metaData, record) {
- value = render_errors(value, metaData, record);
- if (record.data.nomatch) {
- return '<b>! </b>' + value;
- }
- return value;
- }
- },
- {
- header: gettext('Comment'),
- dataIndex: 'comment',
- flex: 1,
- renderer: function(value) {
- return Ext.util.Format.htmlEncode(value);
- }
- }
- ]
- });
-
- me.callParent();
-
- if (me.base_url) {
- me.setBaseUrl(me.base_url); // load
- }
- }
-}, function() {
-
- Ext.define('pve-ipset', {
- extend: 'Ext.data.Model',
- fields: [ { name: 'nomatch', type: 'boolean' },
- 'cidr', 'comment', 'errors' ],
- idProperty: 'cidr'
- });
-
-});
-
-Ext.define('PVE.IPSet', {
- extend: 'Ext.panel.Panel',
- alias: 'widget.pveIPSet',
-
- title: 'IPSet',
-
- list_refs_url: undefined,
-
- initComponent: function() {
- var me = this;
-
- if (!me.list_refs_url) {
- throw "no list_refs_url specified";
- }
-
- var ipset_panel = Ext.createWidget('pveIPSetGrid', {
- region: 'center',
- list_refs_url: me.list_refs_url,
- flex: 0.5,
- border: false
- });
-
- var ipset_list = Ext.createWidget('pveIPSetList', {
- region: 'west',
- ipset_panel: ipset_panel,
- base_url: me.base_url,
- flex: 0.5,
- border: false,
- split: true
- });
-
- Ext.apply(me, {
- layout: 'border',
- items: [ ipset_list, ipset_panel ],
- listeners: {
- show: function() {
- ipset_list.fireEvent('show', ipset_list);
- }
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.panel.InputPanel', {
- extend: 'Ext.panel.Panel',
- alias: ['widget.inputpanel'],
-
- border: false,
-
- // overwrite this to modify submit data
- onGetValues: function(values) {
- return values;
- },
-
- getValues: function(dirtyOnly) {
- var me = this;
-
- if (Ext.isFunction(me.onGetValues)) {
- dirtyOnly = false;
- }
-
- var values = {};
-
- Ext.Array.each(me.query('[isFormField]'), function(field) {
- if (!dirtyOnly || field.isDirty()) {
- PVE.Utils.assemble_field_data(values, field.getSubmitData());
- }
- });
-
- return me.onGetValues(values);
- },
-
- setValues: function(values) {
- var me = this;
-
- var form = me.up('form');
-
- Ext.iterate(values, function(fieldId, val) {
- var field = me.query('[isFormField][name=' + fieldId + ']')[0];
- if (field) {
- field.setValue(val);
- if (form.trackResetOnLoad) {
- field.resetOriginalValue();
- }
- }
- });
- },
-
- initComponent: function() {
- var me = this;
-
- var items;
-
- if (me.items) {
- me.columns = 1;
- items = [
- {
- columnWidth: 1,
- layout: 'anchor',
- items: me.items
- }
- ];
- me.items = undefined;
- } else if (me.column1) {
- me.columns = 2;
- items = [
- {
- columnWidth: 0.5,
- padding: '0 10 0 0',
- layout: 'anchor',
- items: me.column1
- },
- {
- columnWidth: 0.5,
- padding: '0 0 0 10',
- layout: 'anchor',
- items: me.column2 || [] // allow empty column
- }
- ];
- if (me.columnB) {
- items.push({
- columnWidth: 1,
- padding: '10 0 0 0',
- layout: 'anchor',
- items: me.columnB
- });
- }
- } else {
- throw "unsupported config";
- }
-
- if (me.useFieldContainer) {
- Ext.apply(me, {
- layout: 'fit',
- items: Ext.apply(me.useFieldContainer, {
- layout: 'column',
- defaultType: 'container',
- items: items
- })
- });
- } else {
- Ext.apply(me, {
- layout: 'column',
- defaultType: 'container',
- items: items
- });
- }
-
- me.callParent();
- }
-});
+++ /dev/null
-/*
- * Display log entries in a panel with scrollbar
- * The log entries are automatically refreshed via a background task,
- * with newest entries comming at the bottom
- */
-Ext.define('PVE.panel.LogView', {
- extend: 'Ext.panel.Panel',
-
- alias: ['widget.pveLogView'],
-
- pageSize: 500,
-
- lineHeight: 16,
-
- viewInfo: undefined,
-
- scrollToEnd: true,
-
- getMaxDown: function(scrollToEnd) {
- var me = this;
-
- var target = me.getTargetEl();
- var dom = target.dom;
- if (scrollToEnd) {
- dom.scrollTop = dom.scrollHeight - dom.clientHeight;
- }
-
- var maxDown = dom.scrollHeight - dom.clientHeight -
- dom.scrollTop;
-
- return maxDown;
- },
-
- updateView: function(start, end, total, text) {
- var me = this;
- var el = me.dataCmp.el;
-
- if (me.viewInfo && me.viewInfo.start === start &&
- me.viewInfo.end === end && me.viewInfo.total === total &&
- me.viewInfo.textLength === text.length) {
- return; // same content
- }
-
- var maxDown = me.getMaxDown();
- var scrollToEnd = (maxDown <= 0) && me.scrollToEnd;
-
- el.setStyle('padding-top', start*me.lineHeight + 'px');
- el.update(text);
- me.dataCmp.setHeight(total*me.lineHeight);
-
- if (scrollToEnd) {
- me.getMaxDown(true);
- }
-
- me.viewInfo = {
- start: start,
- end: end,
- total: total,
- textLength: text.length
- };
- },
-
- doAttemptLoad: function(start) {
- var me = this;
-
- PVE.Utils.API2Request({
- url: me.url,
- params: {
- start: start,
- limit: me.pageSize
- },
- method: 'GET',
- success: function(response) {
- PVE.Utils.setErrorMask(me, false);
- var list = response.result.data;
- var total = response.result.total;
- var first = 0, last = 0;
- var text = '';
- Ext.Array.each(list, function(item) {
- if (!first|| item.n < first) {
- first = item.n;
- }
- if (!last || item.n > last) {
- last = item.n;
- }
- text = text + Ext.htmlEncode(item.t) + "<br>";
- });
-
- if (first && last && total) {
- me.updateView(first -1 , last -1, total, text);
- } else {
- me.updateView(0, 0, 0, '');
- }
- },
- failure: function(response) {
- var msg = response.htmlStatus;
- PVE.Utils.setErrorMask(me, msg);
- }
- });
- },
-
- attemptLoad: function(start) {
- var me = this;
- if (!me.loadTask) {
- me.loadTask = Ext.create('Ext.util.DelayedTask', me.doAttemptLoad, me, []);
- }
- me.loadTask.delay(200, me.doAttemptLoad, me, [start]);
- },
-
- requestUpdate: function(top, force) {
- var me = this;
-
- if (top === undefined) {
- var target = me.getTargetEl();
- top = target.dom.scrollTop;
- }
-
- var viewStart = parseInt((top / me.lineHeight) - 1, 10);
- if (viewStart < 0) {
- viewStart = 0;
- }
- var viewEnd = parseInt(((top + me.getHeight())/ me.lineHeight) + 1, 10);
- var info = me.viewInfo;
-
- if (info && !force) {
- if (viewStart >= info.start && viewEnd <= info.end) {
- return;
- }
- }
-
- var line = parseInt((top / me.lineHeight) - (me.pageSize / 2) + 10, 10);
- if (line < 0) {
- line = 0;
- }
-
- me.attemptLoad(line);
- },
-
- afterRender: function() {
- var me = this;
-
- me.callParent(arguments);
-
- Ext.Function.defer(function() {
- var target = me.getTargetEl();
- target.on('scroll', function(e) {
- me.requestUpdate();
- });
- me.requestUpdate(0);
- }, 20);
- },
-
- initComponent : function() {
- /*jslint confusion: true */
-
- var me = this;
-
- if (!me.url) {
- throw "no url specified";
- }
-
- me.dataCmp = Ext.create('Ext.Component', {
- style: 'font:normal 11px tahoma, arial, verdana, sans-serif;' +
- 'line-height: ' + me.lineHeight.toString() + 'px; white-space: pre;'
- });
-
- me.task = Ext.TaskManager.start({
- run: function() {
- if (!me.isVisible() || !me.scrollToEnd || !me.viewInfo) {
- return;
- }
-
- var maxDown = me.getMaxDown();
- if (maxDown > 0) {
- return;
- }
-
- me.requestUpdate(undefined, true);
- },
- interval: 1000
- });
-
- Ext.apply(me, {
- autoScroll: true,
- layout: 'auto',
- items: me.dataCmp,
- bodyStyle: 'padding: 5px;',
- listeners: {
- show: function() {
- var target = me.getTargetEl();
- if (target && target.dom) {
- target.dom.scrollTop = me.savedScrollTop;
- }
- },
- beforehide: function() {
- // Hack: chrome reset scrollTop to 0, so we save/restore
- var target = me.getTargetEl();
- if (target && target.dom) {
- me.savedScrollTop = target.dom.scrollTop;
- }
- },
- destroy: function() {
- Ext.TaskManager.stop(me.task);
- }
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.panel.NotesView', {
- extend: 'Ext.panel.Panel',
-
- load: function() {
- var me = this;
-
- PVE.Utils.API2Request({
- url: me.url,
- waitMsgTarget: me,
- failure: function(response, opts) {
- me.update(gettext('Error') + " " + response.htmlStatus);
- },
- success: function(response, opts) {
- var data = response.result.data.description || '';
- me.update(Ext.htmlEncode(data));
- }
- });
- },
-
- initComponent : function() {
- var me = this;
-
- var nodename = me.pveSelNode.data.node;
- if (!nodename) {
- throw "no node name specified";
- }
-
- var vmid = me.pveSelNode.data.vmid;
- if (!vmid) {
- throw "no VM ID specified";
- }
-
- var vmtype = me.pveSelNode.data.type;
- var url;
-
- if (vmtype === 'qemu') {
- me.url = '/api2/extjs/nodes/' + nodename + '/qemu/' + vmid + '/config';
- } else if (vmtype === 'lxc') {
- me.url = '/api2/extjs/nodes/' + nodename + '/lxc/' + vmid + '/config';
- } else {
- throw "unknown vm type '" + vmtype + "'";
- }
-
- Ext.apply(me, {
- title: gettext("Notes"),
- style: 'padding-left:10px',
- bodyStyle: 'white-space:pre',
- bodyPadding: 10,
- autoScroll: true,
- listeners: {
- render: function(c) {
- c.el.on('dblclick', function() {
- var win = Ext.create('PVE.window.NotesEdit', {
- pveSelNode: me.pveSelNode,
- url: me.url
- });
- win.show();
- win.on('destroy', me.load, me);
- });
- }
- }
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.panel.RRDView', {
- extend: 'Ext.panel.Panel',
- alias: 'widget.pveRRDView',
-
- initComponent : function() {
- var me = this;
-
- if (!me.datasource) {
- throw "no datasource specified";
- }
-
- if (!me.rrdurl) {
- throw "no rrdurl specified";
- }
-
- var stateid = 'pveRRDTypeSelection';
- var sp = Ext.state.Manager.getProvider();
- var stateinit = sp.get(stateid);
-
- if (stateinit) {
- if(stateinit.timeframe !== me.timeframe || stateinit.cf !== me.rrdcffn){
- me.timeframe = stateinit.timeframe;
- me.rrdcffn = stateinit.cf;
- }
- }
-
- if (!me.timeframe) {
- if(stateinit && stateinit.timeframe){
- me.timeframe = stateinit.timeframe;
- }else{
- me.timeframe = 'hour';
- }
- }
-
- if (!me.rrdcffn) {
- if(stateinit && stateinit.rrdcffn){
- me.rrdcffn = stateinit.cf;
- }else{
- me.rrdcffn = 'AVERAGE';
- }
- }
-
-
- var datasource = me.datasource;
-
- // fixme: dcindex??
- var dcindex = 0;
- var create_url = function() {
- var url = me.rrdurl + "?ds=" + datasource +
- "&timeframe=" + me.timeframe + "&cf=" + me.rrdcffn +
- "&_dc=" + dcindex.toString();
- dcindex++;
- return url;
- };
-
-
- Ext.apply(me, {
- layout: 'fit',
- html: {
- tag: 'img',
- width: 800,
- height: 200,
- src: create_url()
- },
- applyState : function(state) {
- if (state && state.id) {
- if(state.timeframe !== me.timeframe || state.cf !== me.rrdcffn){
- me.timeframe = state.timeframe;
- me.rrdcffn = state.cf;
- me.reload_task.delay(10);
- }
- }
- }
- });
-
- me.callParent();
-
- me.reload_task = new Ext.util.DelayedTask(function() {
- if (me.rendered) {
- try {
- var html = {
- tag: 'img',
- width: 800,
- height: 200,
- src: create_url()
- };
- me.update(html);
- } catch (e) {
- // fixme:
- console.log(e);
- }
- me.reload_task.delay(30000);
- } else {
- me.reload_task.delay(1000);
- }
- });
-
- me.reload_task.delay(30000);
-
- me.on('destroy', function() {
- me.reload_task.cancel();
- });
-
- var state_change_fn = function(prov, key, value) {
- if (key == stateid) {
- me.applyState(value);
- }
- };
-
- me.mon(sp, 'statechange', state_change_fn);
- }
-});
+++ /dev/null
-/*
- * This class describes the bottom panel
- */
-Ext.define('PVE.panel.StatusPanel', {
- extend: 'Ext.tab.Panel',
- alias: 'widget.pveStatusPanel',
-
-
- //title: "Logs",
- //tabPosition: 'bottom',
-
- initComponent: function() {
- var me = this;
-
- var stateid = 'ltab';
- var sp = Ext.state.Manager.getProvider();
-
- var state = sp.get(stateid);
- if (state && state.value) {
- me.activeTab = state.value;
- }
-
- Ext.apply(me, {
- listeners: {
- tabchange: function() {
- var atab = me.getActiveTab().itemId;
- var state = { value: atab };
- sp.set(stateid, state);
- }
- },
- items: [
- {
- itemId: 'tasks',
- title: gettext('Tasks'),
- xtype: 'pveClusterTasks'
- },
- {
- itemId: 'clog',
- title: gettext('Cluster log'),
- xtype: 'pveClusterLog'
- }
- ]
- });
-
- me.callParent();
-
- me.items.get(0).fireEvent('show', me.items.get(0));
-
- var statechange = function(sp, key, state) {
- if (key === stateid) {
- var atab = me.getActiveTab().itemId;
- var ntab = state.value;
- if (state && ntab && (atab != ntab)) {
- me.setActiveTab(ntab);
- }
- }
- };
-
- sp.on('statechange', statechange);
- me.on('destroy', function() {
- sp.un('statechange', statechange);
- });
-
- }
-});
+++ /dev/null
-Ext.define('PVE.panel.SubConfig', {
- extend: 'Ext.tab.Panel',
- alias: ['widget.pvePanelSubConfig'],
-
- configPrefix: undefined,
-
- getHState: function(itemId) {
- /*jslint confusion: true */
- var me = this;
-
- if (!itemId) {
- itemId = me.getActiveTab().itemId;
- }
-
- var first = me.items.get(0);
- var ntab;
-
- // Note: '' is alias for first tab.
- if (itemId === first.itemId) {
- ntab = me.configPrefix;
- } else {
- ntab = me.configPrefix + '-' + itemId;
- }
-
- return { value: ntab };
- },
-
- initComponent: function() {
- var me = this;
-
- if (!me.phstateid) {
- throw "no parent history state specified";
- }
-
- var sp = Ext.state.Manager.getProvider();
- var state = sp.get(me.phstateid);
-
- var hsregex = /^([^\-\s]+)-(\S+)?$/;
-
- if (state && state.value) {
- var res = hsregex.exec(state.value);
- if (res && res[1] && res[2] && res[1] === me.configPrefix) {
- me.activeTab = res[2];
- }
- }
-
- Ext.apply(me, {
- plain: true,
- tabPosition: 'bottom',
- listeners: {
- afterrender: function(tp) {
- var first = tp.items.get(0);
- if (first) {
- first.fireEvent('show', first);
- }
- },
- tabchange: function(tp, newcard, oldcard) {
- var state = me.getHState(newcard.itemId);
- sp.set(me.phstateid, state);
- }
- }
- });
-
- me.callParent();
-
- var statechange = function(sp, key, state) {
- if ((key === me.phstateid) && state) {
- var first = me.items.get(0);
- var atab = me.getActiveTab().itemId;
- var res = hsregex.exec(state.value);
- var ntab = (res && res[1]) ? res[1] : first.itemId;
- if (ntab && (atab != ntab)) {
- me.setActiveTab(ntab);
- }
- }
- };
-
- me.mon(sp, 'statechange', statechange);
- }
-});
+++ /dev/null
-Ext.define('PVE.pool.Config', {
- extend: 'PVE.panel.Config',
- alias: 'widget.pvePoolConfig',
-
- initComponent: function() {
- var me = this;
-
- var pool = me.pveSelNode.data.pool;
- if (!pool) {
- throw "no pool specified";
- }
-
- Ext.apply(me, {
- title: Ext.String.format(gettext("Resource Pool") + ': ' + pool),
- hstateid: 'pooltab',
- items: [
- {
- title: gettext('Summary'),
- xtype: 'pvePoolSummary',
- itemId: 'summary'
- },
- {
- title: gettext('Members'),
- xtype: 'pvePoolMembers',
- pool: pool,
- itemId: 'members'
- },
- {
- xtype: 'pveACLView',
- title: gettext('Permissions'),
- itemId: 'permissions',
- path: '/pool/' + pool
- }
- ]
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.pool.StatusView', {
- extend: 'PVE.grid.ObjectGrid',
- alias: ['widget.pvePoolStatusView'],
-
- initComponent : function() {
- var me = this;
-
- var pool = me.pveSelNode.data.pool;
- if (!pool) {
- throw "no pool specified";
- }
-
- var rows = {
- comment: {
- header: gettext('Comment'),
- required: true
- }
- };
-
- Ext.applyIf(me, {
- title: gettext('Status'),
- url: "/api2/json/pools/" + pool,
- cwidth1: 150,
- interval: 30000,
- //height: 195,
- rows: rows
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.pool.Summary', {
- extend: 'Ext.panel.Panel',
- alias: 'widget.pvePoolSummary',
-
- initComponent: function() {
- var me = this;
-
- var pool = me.pveSelNode.data.pool;
- if (!pool) {
- throw "no pool specified";
- }
-
- var statusview = Ext.create('PVE.pool.StatusView', {
- pveSelNode: me.pveSelNode,
- style: 'padding-top:0px'
- });
-
- var rstore = statusview.rstore;
-
- Ext.apply(me, {
- autoScroll: true,
- bodyStyle: 'padding:10px',
- defaults: {
- style: 'padding-top:10px',
- width: 800
- },
- items: [ statusview ]
- });
-
- me.on('show', rstore.startUpdate);
- me.on('hide', rstore.stopUpdate);
- me.on('destroy', rstore.stopUpdate);
-
- me.callParent();
- }
-});
+++ /dev/null
-/*
- * Left Treepanel, containing all the ressources we manage in this datacenter: server nodes, server storages, VMs and Containers
- */
-Ext.define('PVE.tree.ResourceTree', {
- extend: 'Ext.tree.TreePanel',
- alias: ['widget.pveResourceTree'],
-
- statics: {
- typeDefaults: {
- node: {
- iconCls: 'x-tree-node-server',
- text: gettext('Node list')
- },
- pool: {
- iconCls: 'x-tree-node-pool',
- text: gettext('Resource Pool')
- },
- storage: {
- iconCls: 'x-tree-node-harddisk',
- text: gettext('Storage list')
- },
- qemu: {
- iconCls: 'x-tree-node-computer',
- text: gettext('Virtual Machine')
- },
- lxc: {
- iconCls: 'x-tree-node-lxc',
- text: gettext('LXC Container')
- }
- }
- },
-
- // private
- nodeSortFn: function(node1, node2) {
- var n1 = node1.data;
- var n2 = node2.data;
-
- if ((n1.groupbyid && n2.groupbyid) ||
- !(n1.groupbyid || n2.groupbyid)) {
-
- var tcmp;
-
- var v1 = n1.type;
- var v2 = n2.type;
-
- if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) {
- return tcmp;
- }
-
- // numeric compare for VM IDs
- // sort templates after regular VMs
- if (v1 === 'qemu' || v1 === 'lxc') {
- if (n1.template && !n2.template) {
- return 1;
- } else if (n2.template && !n1.template) {
- return -1;
- }
- v1 = n1.vmid;
- v2 = n2.vmid;
- if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) {
- return tcmp;
- }
- }
-
- return n1.text > n2.text ? 1 : (n1.text < n2.text ? -1 : 0);
- } else if (n1.groupbyid) {
- return -1;
- } else if (n2.groupbyid) {
- return 1;
- }
- },
-
- // private: fast binary search
- findInsertIndex: function(node, child, start, end) {
- var me = this;
-
- var diff = end - start;
-
- var mid = start + (diff>>1);
-
- if (diff <= 0) {
- return start;
- }
-
- var res = me.nodeSortFn(child, node.childNodes[mid]);
- if (res <= 0) {
- return me.findInsertIndex(node, child, start, mid);
- } else {
- return me.findInsertIndex(node, child, mid + 1, end);
- }
- },
-
- setIconCls: function(info) {
- var me = this;
-
- var defaults = PVE.tree.ResourceTree.typeDefaults[info.type];
- if (defaults && defaults.iconCls) {
- var running = info.running ? '-running' : '';
- var template = info.template ? '-template' : '';
- info.iconCls = defaults.iconCls + running + template;
- }
- },
-
- // private
- addChildSorted: function(node, info) {
- var me = this;
-
- me.setIconCls(info);
-
- var defaults;
- if (info.groupbyid) {
- info.text = info.groupbyid;
- if (info.type === 'type') {
- defaults = PVE.tree.ResourceTree.typeDefaults[info.groupbyid];
- if (defaults && defaults.text) {
- info.text = defaults.text;
- }
- }
- }
- var child = Ext.create('PVETree', info);
-
- var cs = node.childNodes;
- var pos;
- if (cs) {
- pos = cs[me.findInsertIndex(node, child, 0, cs.length)];
- }
-
- node.insertBefore(child, pos);
-
- return child;
- },
-
- // private
- groupChild: function(node, info, groups, level) {
- var me = this;
-
- var groupby = groups[level];
- var v = info[groupby];
-
- if (v) {
- var group = node.findChild('groupbyid', v);
- if (!group) {
- var groupinfo;
- if (info.type === groupby) {
- groupinfo = info;
- } else {
- groupinfo = {
- type: groupby,
- id : groupby + "/" + v
- };
- if (groupby !== 'type') {
- groupinfo[groupby] = v;
- }
- }
- groupinfo.leaf = false;
- groupinfo.groupbyid = v;
- group = me.addChildSorted(node, groupinfo);
- // fixme: remove when EXTJS has fixed those bugs?!
- group.expand(); group.collapse();
- }
- if (info.type === groupby) {
- return group;
- }
- if (group) {
- return me.groupChild(group, info, groups, level + 1);
- }
- }
-
- return me.addChildSorted(node, info);
- },
-
- initComponent : function() {
- var me = this;
-
- var rstore = PVE.data.ResourceStore;
- var sp = Ext.state.Manager.getProvider();
-
- if (!me.viewFilter) {
- me.viewFilter = {};
- }
-
- var pdata = {
- dataIndex: {},
- updateCount: 0
- };
-
- var store = Ext.create('Ext.data.TreeStore', {
- model: 'PVETree',
- root: {
- expanded: true,
- id: 'root',
- text: gettext('Datacenter')
- }
- });
-
- var stateid = 'rid';
-
- var updateTree = function() {
- var tmp;
-
- // fixme: suspend events ?
-
- var rootnode = me.store.getRootNode();
-
- // remember selected node (and all parents)
- var sm = me.getSelectionModel();
-
- var lastsel = sm.getSelection()[0];
- var parents = [];
- var p = lastsel;
- while (p && !!(p = p.parentNode)) {
- parents.push(p);
- }
-
- var index = pdata.dataIndex;
-
- var groups = me.viewFilter.groups || [];
- var filterfn = me.viewFilter.filterfn;
-
- // remove vanished or changed items
- var key;
- for (key in index) {
- if (index.hasOwnProperty(key)) {
- var olditem = index[key];
-
- // getById() use find(), which is slow (ExtJS4 DP5)
- //var item = rstore.getById(olditem.data.id);
- var item = rstore.data.get(olditem.data.id);
-
- var changed = false;
- if (item) {
- // test if any grouping attributes changed
- var i, len;
- for (i = 0, len = groups.length; i < len; i++) {
- var attr = groups[i];
- if (item.data[attr] != olditem.data[attr]) {
- //console.log("changed " + attr);
- changed = true;
- break;
- }
- }
- if ((item.data.text !== olditem.data.text) ||
- (item.data.node !== olditem.data.node) ||
- (item.data.running !== olditem.data.running) ||
- (item.data.template !== olditem.data.template)) {
- //console.log("changed node/text/running " + olditem.data.id);
- changed = true;
- }
-
- // fixme: also test filterfn()?
- }
-
- if (!item || changed) {
- //console.log("REM UID: " + key + " ITEM " + olditem.data.id);
- if (olditem.isLeaf()) {
- delete index[key];
- var parentNode = olditem.parentNode;
- parentNode.removeChild(olditem, true);
- } else {
- if (item && changed) {
- olditem.beginEdit();
- //console.log("REM UPDATE UID: " + key + " ITEM " + item.data.running);
- var info = olditem.data;
- Ext.apply(info, item.data);
- me.setIconCls(info);
- olditem.commit();
- }
- }
- }
- }
- }
-
- // add new items
- rstore.each(function(item) {
- var olditem = index[item.data.id];
- if (olditem) {
- return;
- }
-
- if (filterfn && !filterfn(item)) {
- return;
- }
-
- //console.log("ADD UID: " + item.data.id);
-
- var info = Ext.apply({ leaf: true }, item.data);
-
- var child = me.groupChild(rootnode, info, groups, 0);
- if (child) {
- index[item.data.id] = child;
- }
- });
-
- // select parent node is selection vanished
- if (lastsel && !rootnode.findChild('id', lastsel.data.id, true)) {
- lastsel = rootnode;
- while (!!(p = parents.shift())) {
- if (!!(tmp = rootnode.findChild('id', p.data.id, true))) {
- lastsel = tmp;
- break;
- }
- }
- me.selectById(lastsel.data.id);
- }
-
- if (!pdata.updateCount) {
- rootnode.collapse();
- rootnode.expand();
- me.applyState(sp.get(stateid));
- }
-
- pdata.updateCount++;
- };
-
- var statechange = function(sp, key, value) {
- if (key === stateid) {
- me.applyState(value);
- }
- };
-
- sp.on('statechange', statechange);
-
- Ext.apply(me, {
- store: store,
- viewConfig: {
- // note: animate cause problems with applyState
- animate: false
- },
- //useArrows: true,
- //rootVisible: false,
- //title: 'Resource Tree',
- listeners: {
- itemcontextmenu: function(v, record, item, index, event) {
- event.stopEvent();
- //v.select(record);
- var menu;
-
- if (record.data.type === 'qemu' && !record.data.template) {
- menu = Ext.create('PVE.qemu.CmdMenu', {
- pveSelNode: record
- });
- } else if (record.data.type === 'qemu' && record.data.template) {
- menu = Ext.create('PVE.qemu.TemplateMenu', {
- pveSelNode: record
- });
- } else if (record.data.type === 'lxc') {
- menu = Ext.create('PVE.lxc.CmdMenu', {
- pveSelNode: record
- });
- } else {
- return;
- }
-
- menu.showAt(event.getXY());
- },
- destroy: function() {
- rstore.un("load", updateTree);
- }
- },
- setViewFilter: function(view) {
- me.viewFilter = view;
- me.clearTree();
- updateTree();
- },
- clearTree: function() {
- pdata.updateCount = 0;
- var rootnode = me.store.getRootNode();
- rootnode.collapse();
- rootnode.removeAll(true);
- pdata.dataIndex = {};
- me.getSelectionModel().deselectAll();
- },
- selectExpand: function(node) {
- var sm = me.getSelectionModel();
- if (!sm.isSelected(node)) {
- sm.select(node);
- var cn = node;
- while (!!(cn = cn.parentNode)) {
- if (!cn.isExpanded()) {
- cn.expand();
- }
- }
- }
- },
- selectById: function(nodeid) {
- var rootnode = me.store.getRootNode();
- var sm = me.getSelectionModel();
- var node;
- if (nodeid === 'root') {
- node = rootnode;
- } else {
- node = rootnode.findChild('id', nodeid, true);
- }
- if (node) {
- me.selectExpand(node);
- }
- },
- checkVmMigration: function(record) {
- if (!(record.data.type === 'qemu' || record.data.type === 'lxc')) {
- throw "not a vm type";
- }
-
- var rootnode = me.store.getRootNode();
- var node = rootnode.findChild('id', record.data.id, true);
-
- if (node && node.data.type === record.data.type &&
- node.data.node !== record.data.node) {
- // defer select (else we get strange errors)
- Ext.defer(function() { me.selectExpand(node); }, 100, me);
- }
- },
- applyState : function(state) {
- var sm = me.getSelectionModel();
- if (state && state.value) {
- me.selectById(state.value);
- } else {
- sm.deselectAll();
- }
- }
- });
-
- me.callParent();
-
- var sm = me.getSelectionModel();
- sm.on('select', function(sm, n) {
- sp.set(stateid, { value: n.data.id});
- });
-
- rstore.on("load", updateTree);
- rstore.startUpdate();
- //rstore.stopUpdate();
- }
-
-});
+++ /dev/null
-Ext.define('PVE.window.Backup', {
- extend: 'Ext.window.Window',
-
- resizable: false,
-
- initComponent : function() {
- var me = this;
-
- if (!me.nodename) {
- throw "no node name specified";
- }
-
- if (!me.vmid) {
- throw "no VM ID specified";
- }
-
- if (!me.vmtype) {
- throw "no VM type specified";
- }
-
- var storagesel = Ext.create('PVE.form.StorageSelector', {
- nodename: me.nodename,
- name: 'storage',
- value: me.storage,
- fieldLabel: gettext('Storage'),
- storageContent: 'backup',
- allowBlank: false
- });
-
- me.formPanel = Ext.create('Ext.form.Panel', {
- bodyPadding: 10,
- border: false,
- fieldDefaults: {
- labelWidth: 100,
- anchor: '100%'
- },
- items: [
- storagesel,
- {
- xtype: 'pveBackupModeSelector',
- fieldLabel: gettext('Mode'),
- value: 'snapshot',
- name: 'mode'
- },
- {
- xtype: 'pveCompressionSelector',
- name: 'compress',
- value: 'lzo',
- fieldLabel: gettext('Compression')
- }
- ]
- });
-
- var form = me.formPanel.getForm();
-
- var submitBtn = Ext.create('Ext.Button', {
- text: gettext('Backup'),
- handler: function(){
- var storage = storagesel.getValue();
- var values = form.getValues();
- var params = {
- storage: storage,
- vmid: me.vmid,
- mode: values.mode,
- remove: 0
- };
- if (values.compress) {
- params.compress = values.compress;
- }
-
- PVE.Utils.API2Request({
- url: '/nodes/' + me.nodename + '/vzdump',
- params: params,
- method: 'POST',
- failure: function (response, opts) {
- Ext.Msg.alert('Error',response.htmlStatus);
- },
- success: function(response, options) {
- var upid = response.result.data;
-
- var win = Ext.create('PVE.window.TaskViewer', {
- upid: upid
- });
- win.show();
- me.close();
- }
- });
- }
- });
-
- var title = gettext('Backup') + " " +
- ((me.vmtype === 'openvz') ? "CT" : "VM") +
- " " + me.vmid;
-
- Ext.apply(me, {
- title: title,
- width: 350,
- modal: true,
- layout: 'auto',
- border: false,
- items: [ me.formPanel ],
- buttons: [ submitBtn ]
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-// fixme: how can we avoid those lint errors?
-/*jslint confusion: true */
-Ext.define('PVE.window.Edit', {
- extend: 'Ext.window.Window',
- alias: 'widget.pveWindowEdit',
-
- resizable: false,
-
- // use this tio atimatically generate a title like
- // Create: <subject>
- subject: undefined,
-
- // set create to true if you want a Create button (instead
- // OK and RESET)
- create: false,
-
- // set to true if you want an Add button (instead of Create)
- isAdd: false,
-
- // set to true if you want an Remove button (instead of Create)
- isRemove: false,
-
- backgroundDelay: 0,
-
- showProgress: false,
-
- isValid: function() {
- var me = this;
-
- var form = me.formPanel.getForm();
- return form.isValid();
- },
-
- getValues: function(dirtyOnly) {
- var me = this;
-
- var values = {};
-
- var form = me.formPanel.getForm();
-
- form.getFields().each(function(field) {
- if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) {
- PVE.Utils.assemble_field_data(values, field.getSubmitData());
- }
- });
-
- Ext.Array.each(me.query('inputpanel'), function(panel) {
- PVE.Utils.assemble_field_data(values, panel.getValues(dirtyOnly));
- });
-
- return values;
- },
-
- setValues: function(values) {
- var me = this;
-
- var form = me.formPanel.getForm();
-
- Ext.iterate(values, function(fieldId, val) {
- var field = form.findField(fieldId);
- if (field && !field.up('inputpanel')) {
- field.setValue(val);
- if (form.trackResetOnLoad) {
- field.resetOriginalValue();
- }
- }
- });
-
- Ext.Array.each(me.query('inputpanel'), function(panel) {
- panel.setValues(values);
- });
- },
-
- submit: function() {
- var me = this;
-
- var form = me.formPanel.getForm();
-
- var values = me.getValues();
- Ext.Object.each(values, function(name, val) {
- if (values.hasOwnProperty(name)) {
- if (Ext.isArray(val) && !val.length) {
- values[name] = '';
- }
- }
- });
-
- if (me.digest) {
- values.digest = me.digest;
- }
-
- if (me.backgroundDelay) {
- values.background_delay = me.backgroundDelay;
- }
-
- var url = me.url;
- if (me.method === 'DELETE') {
- url = url + "?" + Ext.Object.toQueryString(values);
- values = undefined;
- }
-
- PVE.Utils.API2Request({
- url: url,
- waitMsgTarget: me,
- method: me.method || (me.backgroundDelay ? 'POST' : 'PUT'),
- params: values,
- failure: function(response, options) {
- if (response.result && response.result.errors) {
- form.markInvalid(response.result.errors);
- }
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, options) {
- me.close();
- if ((me.backgroundDelay || me.showProgress) &&
- response.result.data) {
- var upid = response.result.data;
- var win = Ext.create('PVE.window.TaskProgress', {
- upid: upid
- });
- win.show();
- }
- }
- });
- },
-
- load: function(options) {
- var me = this;
-
- var form = me.formPanel.getForm();
-
- options = options || {};
-
- var newopts = Ext.apply({
- waitMsgTarget: me
- }, options);
-
- var createWrapper = function(successFn) {
- Ext.apply(newopts, {
- url: me.url,
- method: 'GET',
- success: function(response, opts) {
- form.clearInvalid();
- me.digest = response.result.data.digest;
- if (successFn) {
- successFn(response, opts);
- } else {
- me.setValues(response.result.data);
- }
- // hack: fix ExtJS bug
- Ext.Array.each(me.query('radiofield'), function(f) {
- f.resetOriginalValue();
- });
- },
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus, function() {
- me.close();
- });
- }
- });
- };
-
- createWrapper(options.success);
-
- PVE.Utils.API2Request(newopts);
- },
-
- initComponent : function() {
- var me = this;
-
- if (!me.url) {
- throw "no url specified";
- }
-
- var items = Ext.isArray(me.items) ? me.items : [ me.items ];
-
- me.items = undefined;
-
- me.formPanel = Ext.create('Ext.form.Panel', {
- url: me.url,
- method: me.method || 'PUT',
- trackResetOnLoad: true,
- bodyPadding: 10,
- border: false,
- defaults: {
- border: false
- },
- fieldDefaults: Ext.apply({}, me.fieldDefaults, {
- labelWidth: 100,
- anchor: '100%'
- }),
- items: items
- });
-
- var form = me.formPanel.getForm();
-
- var submitBtn = Ext.create('Ext.Button', {
- text: me.create ? (me.isAdd ? gettext('Add') : ( me.isRemove ? gettext('Remove') : gettext('Create'))) : gettext('OK'),
- disabled: !me.create,
- handler: function() {
- me.submit();
- }
- });
-
- var resetBtn = Ext.create('Ext.Button', {
- text: 'Reset',
- disabled: true,
- handler: function(){
- form.reset();
- }
- });
-
- var set_button_status = function() {
- var valid = form.isValid();
- var dirty = form.isDirty();
- submitBtn.setDisabled(!valid || !(dirty || me.create));
- resetBtn.setDisabled(!dirty);
- };
-
- form.on('dirtychange', set_button_status);
- form.on('validitychange', set_button_status);
-
- var colwidth = 300;
- if (me.fieldDefaults && me.fieldDefaults.labelWidth) {
- colwidth += me.fieldDefaults.labelWidth - 100;
- }
-
-
- var twoColumn = items[0].column1 || items[0].column2;
-
- if (me.subject && !me.title) {
- me.title = PVE.Utils.dialog_title(me.subject, me.create, me.isAdd);
- }
-
- if (me.create) {
- me.buttons = [ submitBtn ] ;
- } else {
- me.buttons = [ submitBtn, resetBtn ];
- }
-
- Ext.applyIf(me, {
- modal: true,
- width: twoColumn ? colwidth*2 : colwidth,
- border: false,
- items: [ me.formPanel ],
- });
-
- me.callParent();
-
- // always mark invalid fields
- me.on('afterlayout', function() {
- me.isValid();
- });
- }
-});
+++ /dev/null
-Ext.define('PVE.window.LoginWindow', {
- extend: 'Ext.window.Window',
-
- // private
- onLogon: function() {
- var me = this;
-
- var form = me.getComponent(0).getForm();
-
- if(form.isValid()){
- me.el.mask(gettext('Please wait...'), 'x-mask-loading');
-
- form.submit({
- failure: function(f, resp){
- me.el.unmask();
- Ext.MessageBox.alert(gettext('Error'),
- gettext("Login failed. Please try again"),
- function() {
- var uf = form.findField('username');
- uf.focus(true, true);
- });
- },
- success: function(f, resp){
- me.el.unmask();
-
- var handler = me.handler || Ext.emptyFn;
- handler.call(me, resp.result.data);
- me.close();
- }
- });
- }
- },
-
- initComponent: function() {
- var me = this;
-
- var otp_field = Ext.createWidget('textfield', {
- fieldLabel: gettext('OTP'),
- name: 'otp',
- allowBlank: false,
- hidden: true
- });
-
- Ext.apply(me, {
- width: 400,
- modal: true,
- border: false,
- draggable: true,
- closable: false,
- resizable: false,
- layout: 'auto',
- title: gettext('Proxmox VE Login'),
-
- items: [{
- xtype: 'form',
- frame: true,
- url: '/api2/extjs/access/ticket',
-
- fieldDefaults: {
- labelAlign: 'right'
- },
-
- defaults: {
- anchor: '-5',
- allowBlank: false
- },
-
- items: [
- {
- xtype: 'textfield',
- fieldLabel: gettext('User name'),
- name: 'username',
- blankText: gettext("Enter your user name"),
- listeners: {
- afterrender: function(f) {
- // Note: only works if we pass delay 1000
- f.focus(true, 1000);
- },
- specialkey: function(f, e) {
- if (e.getKey() === e.ENTER) {
- var pf = me.query('textfield[name="password"]')[0];
- if (pf.getValue()) {
- me.onLogon();
- } else {
- pf.focus(false);
- }
- }
- }
- }
- },
- {
- xtype: 'textfield',
- inputType: 'password',
- fieldLabel: gettext('Password'),
- name: 'password',
- blankText: gettext("Enter your password"),
- listeners: {
- specialkey: function(field, e) {
- if (e.getKey() === e.ENTER) {
- me.onLogon();
- }
- }
- }
- },
- otp_field,
- {
- xtype: 'pveRealmComboBox',
- name: 'realm',
- listeners: {
- change: function(f, value) {
- if (f.needOTP(value)) {
- otp_field.setVisible(true);
- otp_field.setDisabled(false);
- } else {
- otp_field.setVisible(false);
- otp_field.setDisabled(true);
- }
- }
- }
- },
- {
- xtype: 'pveLanguageSelector',
- fieldLabel: gettext('Language'),
- value: Ext.util.Cookies.get('PVELangCookie') || 'en',
- name: 'lang',
- submitValue: false,
- listeners: {
- change: function(t, value) {
- var dt = Ext.Date.add(new Date(), Ext.Date.YEAR, 10);
- Ext.util.Cookies.set('PVELangCookie', value, dt);
- me.el.mask(gettext('Please wait...'), 'x-mask-loading');
- window.location.reload();
- }
- }
- }
- ],
- buttons: [
- {
- text: gettext('Login'),
- handler: function(){
- me.onLogon();
- }
- }
- ]
- }]
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.window.Migrate', {
- extend: 'Ext.window.Window',
-
- resizable: false,
-
- migrate: function(target, online) {
- var me = this;
- PVE.Utils.API2Request({
- params: { target: target, online: online },
- url: '/nodes/' + me.nodename + '/' + me.vmtype + '/' + me.vmid + "/migrate",
- waitMsgTarget: me,
- method: 'POST',
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, options) {
- var upid = response.result.data;
-
- var win = Ext.create('PVE.window.TaskViewer', {
- upid: upid
- });
- win.show();
- me.close();
- }
- });
- },
-
- initComponent : function() {
- var me = this;
-
- if (!me.nodename) {
- throw "no node name specified";
- }
-
- if (!me.vmid) {
- throw "no VM ID specified";
- }
-
- if (!me.vmtype) {
- throw "no VM type specified";
- }
-
- var running = false;
- var vmrec = PVE.data.ResourceStore.findRecord('vmid', me.vmid,
- 0, false, false, true);
- if (vmrec && vmrec.data && vmrec.data.running) {
- running = true;
- }
-
- me.formPanel = Ext.create('Ext.form.Panel', {
- bodyPadding: 10,
- border: false,
- fieldDefaults: {
- labelWidth: 100,
- anchor: '100%'
- },
- items: [
- {
- xtype: 'PVE.form.NodeSelector',
- name: 'target',
- fieldLabel: gettext('Target node'),
- allowBlank: false,
- onlineValidator: true
- },
- {
- xtype: 'pvecheckbox',
- name: 'online',
- uncheckedValue: 0,
- defaultValue: 0,
- checked: running,
- fieldLabel: gettext('Online')
- }
- ]
- });
-
- var form = me.formPanel.getForm();
-
- var submitBtn = Ext.create('Ext.Button', {
- text: gettext('Migrate'),
- handler: function() {
- var values = form.getValues();
- me.migrate(values.target, values.online);
- }
- });
-
- Ext.apply(me, {
- title: gettext('Migrate') + ' VM ' + me.vmid,
- width: 350,
- modal: true,
- layout: 'auto',
- border: false,
- items: [ me.formPanel ],
- buttons: [ submitBtn ]
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.window.MigrateAll', {
- extend: 'Ext.window.Window',
-
- resizable: false,
-
- migrate: function(target, maxworkers) {
- var me = this;
- PVE.Utils.API2Request({
- params: { target: target, maxworkers: maxworkers},
- url: '/nodes/' + me.nodename + '/' + "/migrateall",
- waitMsgTarget: me,
- method: 'POST',
- failure: function(response, opts) {
- Ext.Msg.alert('Error', response.htmlStatus);
- },
- success: function(response, options) {
- var upid = response.result.data;
-
- var win = Ext.create('PVE.window.TaskViewer', {
- upid: upid
- });
- win.show();
- me.close();
- }
- });
- },
-
- initComponent : function() {
- var me = this;
-
- if (!me.nodename) {
- throw "no node name specified";
- }
-
- me.formPanel = Ext.create('Ext.form.Panel', {
- bodyPadding: 10,
- border: false,
- fieldDefaults: {
- labelWidth: 100,
- anchor: '100%'
- },
- items: [
- {
- xtype: 'PVE.form.NodeSelector',
- name: 'target',
- fieldLabel: 'Target node',
- allowBlank: false,
- onlineValidator: true
- },
- {
- xtype: 'numberfield',
- name: 'maxworkers',
- minValue: 1,
- maxValue: 100,
- value: 1,
- fieldLabel: 'Parallel jobs',
- allowBlank: false
- },
- ]
- });
-
- var form = me.formPanel.getForm();
-
- var submitBtn = Ext.create('Ext.Button', {
- text: 'Migrate',
- handler: function() {
- var values = form.getValues();
- me.migrate(values.target, values.maxworkers);
- }
- });
-
- Ext.apply(me, {
- title: "Migrate All VMs",
- width: 350,
- modal: true,
- layout: 'auto',
- border: false,
- items: [ me.formPanel ],
- buttons: [ submitBtn ]
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.window.NotesEdit', {
- extend: 'PVE.window.Edit',
-
- initComponent : function() {
- var me = this;
-
- Ext.apply(me, {
- title: gettext('Notes'),
- width: 600,
- layout: 'fit',
- items: {
- xtype: 'textarea',
- name: 'description',
- rows: 7,
- value: '',
- hideLabel: true
- }
- });
-
- me.callParent();
-
- me.load();
- }
-});
+++ /dev/null
-Ext.define('PVE.window.Restore', {
- extend: 'Ext.window.Window', // fixme: PVE.window.Edit?
-
- resizable: false,
-
- initComponent : function() {
- var me = this;
-
- if (!me.nodename) {
- throw "no node name specified";
- }
-
- if (!me.volid) {
- throw "no volume ID specified";
- }
-
- if (!me.vmtype) {
- throw "no vmtype specified";
- }
-
- var storagesel = Ext.create('PVE.form.StorageSelector', {
- nodename: me.nodename,
- name: 'storage',
- value: '',
- fieldLabel: gettext('Storage'),
- storageContent: (me.vmtype === 'lxc') ? 'rootdir' : 'images',
- allowBlank: true
- });
-
- me.formPanel = Ext.create('Ext.form.Panel', {
- bodyPadding: 10,
- border: false,
- fieldDefaults: {
- labelWidth: 100,
- anchor: '100%'
- },
- items: [
- {
- xtype: 'displayfield',
- value: me.volidText || me.volid,
- fieldLabel: gettext('Source')
- },
- storagesel,
- {
- xtype: me.vmid ? 'displayfield' : 'pveVMIDSelector',
- name: 'vmid',
- fieldLabel: 'VM ID',
- value: me.vmid,
- loadNextFreeVMID: me.vmid ? false: true,
- validateExists: false
- }
- ]
- });
-
- var form = me.formPanel.getForm();
-
- var doRestore = function(url, params) {
- PVE.Utils.API2Request({
- url: url,
- params: params,
- method: 'POST',
- waitMsgTarget: me,
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, options) {
- var upid = response.result.data;
-
- var win = Ext.create('PVE.window.TaskViewer', {
- upid: upid
- });
- win.show();
- me.close();
- }
- });
- };
-
- var submitBtn = Ext.create('Ext.Button', {
- text: gettext('Restore'),
- handler: function(){
- var storage = storagesel.getValue();
- var values = form.getValues();
-
- var params = {
- storage: storage,
- vmid: me.vmid || values.vmid,
- force: me.vmid ? 1 : 0
- };
-
- var url;
- if (me.vmtype === 'lxc') {
- url = '/nodes/' + me.nodename + '/lxc';
- params.ostemplate = me.volid;
- params.restore = 1;
- } else if (me.vmtype === 'qemu') {
- url = '/nodes/' + me.nodename + '/qemu';
- params.archive = me.volid;
- } else {
- throw 'unknown VM type';
- }
-
- if (me.vmid) {
- var msg = gettext('Are you sure you want to restore this VM?') + ' ' +
- gettext('This will permanently erase current VM data.');
- Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
- if (btn !== 'yes') {
- return;
- }
- doRestore(url, params);
- });
- } else {
- doRestore(url, params);
- }
- }
- });
-
- form.on('validitychange', function(f, valid) {
- submitBtn.setDisabled(!valid);
- });
-
- var title = (me.vmtype === 'openvz') ? gettext('Restore CT') :
- gettext('Restore VM');
-
- Ext.apply(me, {
- title: title,
- width: 500,
- modal: true,
- layout: 'auto',
- border: false,
- items: [ me.formPanel ],
- buttons: [ submitBtn ]
- });
-
- me.callParent();
- }
-});
+++ /dev/null
-Ext.define('PVE.window.TaskProgress', {
- extend: 'Ext.window.Window',
- alias: 'widget.pveTaskProgress',
-
- initComponent: function() {
- var me = this;
-
- if (!me.upid) {
- throw "no task specified";
- }
-
- var task = PVE.Utils.parse_task_upid(me.upid);
-
- var statstore = Ext.create('PVE.data.ObjectStore', {
- url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status",
- interval: 1000,
- rows: {
- status: { defaultValue: 'unknown' },
- exitstatus: { defaultValue: 'unknown' }
- }
- });
-
- me.on('destroy', statstore.stopUpdate);
-
- var getObjectValue = function(key, defaultValue) {
- var rec = statstore.getById(key);
- if (rec) {
- return rec.data.value;
- }
- return defaultValue;
- };
-
- var pbar = Ext.create('Ext.ProgressBar', { text: 'running...' });
-
- me.mon(statstore, 'load', function() {
- var status = getObjectValue('status');
- if (status === 'stopped') {
- var exitstatus = getObjectValue('exitstatus');
- if (exitstatus == 'OK') {
- pbar.reset();
- pbar.updateText("Done!");
- Ext.Function.defer(me.close, 1000, me);
- } else {
- me.close();
- Ext.Msg.alert('Task failed', exitstatus);
- }
- }
- });
-
- var descr = PVE.Utils.format_task_description(task.type, task.id);
-
- Ext.applyIf(me, {
- title: "Task: " + descr,
- width: 300,
- layout: 'auto',
- modal: true,
- bodyPadding: 5,
- items: pbar,
- buttons: [
- {
- text: gettext('Details'),
- handler: function() {
- var win = Ext.create('PVE.window.TaskViewer', {
- upid: me.upid
- });
- win.show();
- me.close();
- }
- }
- ]
- });
-
- me.callParent();
-
- statstore.startUpdate();
-
- pbar.wait();
- }
-});
-
-// fixme: how can we avoid those lint errors?
-/*jslint confusion: true */
-
-Ext.define('PVE.window.TaskViewer', {
- extend: 'Ext.window.Window',
- alias: 'widget.pveTaskViewer',
-
- initComponent: function() {
- var me = this;
-
- if (!me.upid) {
- throw "no task specified";
- }
-
- var task = PVE.Utils.parse_task_upid(me.upid);
-
- var statgrid;
-
- var rows = {
- status: {
- header: gettext('Status'),
- defaultValue: 'unknown',
- renderer: function(value) {
- if (value != 'stopped') {
- return value;
- }
- var es = statgrid.getObjectValue('exitstatus');
- if (es) {
- return value + ': ' + es;
- }
- }
- },
- exitstatus: {
- visible: false
- },
- type: {
- header: gettext('Task type'),
- required: true
- },
- user: {
- header: gettext('User name'),
- required: true
- },
- node: {
- header: gettext('Node'),
- required: true
- },
- pid: {
- header: gettext('Process ID'),
- required: true
- },
- starttime: {
- header: gettext('Start Time'),
- required: true,
- renderer: PVE.Utils.render_timestamp
- },
- upid: {
- header: gettext('Unique task ID')
- }
- };
-
- var statstore = Ext.create('PVE.data.ObjectStore', {
- url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status",
- interval: 1000,
- rows: rows
- });
-
- me.on('destroy', statstore.stopUpdate);
-
- var stop_task = function() {
- PVE.Utils.API2Request({
- url: "/nodes/" + task.node + "/tasks/" + me.upid,
- waitMsgTarget: me,
- method: 'DELETE',
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- };
-
- var stop_btn1 = new Ext.Button({
- text: gettext('Stop'),
- disabled: true,
- handler: stop_task
- });
-
- var stop_btn2 = new Ext.Button({
- text: gettext('Stop'),
- disabled: true,
- handler: stop_task
- });
-
- statgrid = Ext.create('PVE.grid.ObjectGrid', {
- title: gettext('Status'),
- layout: 'fit',
- tbar: [ stop_btn1 ],
- rstore: statstore,
- rows: rows,
- border: false
- });
-
- var logView = Ext.create('PVE.panel.LogView', {
- title: gettext('Output'),
- tbar: [ stop_btn2 ],
- border: false,
- url: "/api2/extjs/nodes/" + task.node + "/tasks/" + me.upid + "/log"
- });
-
- me.mon(statstore, 'load', function() {
- var status = statgrid.getObjectValue('status');
-
- if (status === 'stopped') {
- logView.requestUpdate(undefined, true);
- logView.scrollToEnd = false;
- statstore.stopUpdate();
- }
-
- stop_btn1.setDisabled(status !== 'running');
- stop_btn2.setDisabled(status !== 'running');
- });
-
- statstore.startUpdate();
-
- Ext.applyIf(me, {
- title: "Task viewer: " + task.desc,
- width: 800,
- height: 400,
- layout: 'fit',
- modal: true,
- bodyPadding: 5,
- items: [{
- xtype: 'tabpanel',
- region: 'center',
- items: [ logView, statgrid ]
- }]
- });
-
- me.callParent();
-
- logView.fireEvent('show', logView);
- }
-});
-
+++ /dev/null
-Ext.define('PVE.window.Wizard', {
- extend: 'Ext.window.Window',
-
- getValues: function(dirtyOnly) {
- var me = this;
-
- var values = {};
-
- var form = me.down('form').getForm();
-
- form.getFields().each(function(field) {
- if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) {
- PVE.Utils.assemble_field_data(values, field.getSubmitData());
- }
- });
-
- Ext.Array.each(me.query('inputpanel'), function(panel) {
- PVE.Utils.assemble_field_data(values, panel.getValues(dirtyOnly));
- });
-
- return values;
- },
-
- initComponent: function() {
- var me = this;
-
- var tabs = me.items || [];
- delete me.items;
-
- /*
- * Items may have the following functions:
- * validator(): per tab custom validation
- * onSubmit(): submit handler
- * onGetValues(): overwrite getValues results
- */
-
- Ext.Array.each(tabs, function(tab) {
- tab.disabled = true;
- });
- tabs[0].disabled = false;
-
- var check_card = function(card) {
- var valid = true;
- var fields = card.query('field, fieldcontainer');
- if (card.isXType('fieldcontainer')) {
- fields.unshift(card);
- }
- Ext.Array.each(fields, function(field) {
- // Note: not all fielcontainer have isValid()
- if (Ext.isFunction(field.isValid) && !field.isValid()) {
- valid = false;
- }
- });
-
- if (Ext.isFunction(card.validator)) {
- return card.validator();
- }
-
- return valid;
- };
-
-
- var tbar = Ext.create('Ext.toolbar.Toolbar', {
- ui: 'footer',
- region: 'south',
- margins: '0 5 5 5',
- items: [
- '->',
- {
- text: gettext('Back'),
- disabled: true,
- itemId: 'back',
- minWidth: 60,
- handler: function() {
- var tp = me.down('#wizcontent');
- var atab = tp.getActiveTab();
- var prev = tp.items.indexOf(atab) - 1;
- if (prev < 0) {
- return;
- }
- var ntab = tp.items.getAt(prev);
- if (ntab) {
- tp.setActiveTab(ntab);
- }
-
-
- }
- },
- {
- text: gettext('Next'),
- disabled: true,
- itemId: 'next',
- minWidth: 60,
- handler: function() {
-
- var form = me.down('form').getForm();
-
- var tp = me.down('#wizcontent');
- var atab = tp.getActiveTab();
- if (!check_card(atab)) {
- return;
- }
-
- var next = tp.items.indexOf(atab) + 1;
- var ntab = tp.items.getAt(next);
- if (ntab) {
- ntab.enable();
- tp.setActiveTab(ntab);
- }
-
- }
- },
- {
- text: gettext('Finish'),
- minWidth: 60,
- hidden: true,
- itemId: 'submit',
- handler: function() {
- var tp = me.down('#wizcontent');
- var atab = tp.getActiveTab();
- atab.onSubmit();
- }
- }
- ]
- });
-
- var display_header = function(newcard) {
- var html = '<h1>' + newcard.title + '</h1>';
- if (newcard.descr) {
- html += newcard.descr;
- }
- me.down('#header').update(html);
- };
-
- var disable_at = function(card) {
- var tp = me.down('#wizcontent');
- var idx = tp.items.indexOf(card);
- for(;idx < tp.items.getCount();idx++) {
- var nc = tp.items.getAt(idx);
- if (nc) {
- nc.disable();
- }
- }
- };
-
- var tabchange = function(tp, newcard, oldcard) {
- if (newcard.onSubmit) {
- me.down('#next').setVisible(false);
- me.down('#submit').setVisible(true);
- } else {
- me.down('#next').setVisible(true);
- me.down('#submit').setVisible(false);
- }
- var valid = check_card(newcard);
- me.down('#next').setDisabled(!valid);
- me.down('#submit').setDisabled(!valid);
- me.down('#back').setDisabled(tp.items.indexOf(newcard) == 0);
-
- if (oldcard && !check_card(oldcard)) {
- disable_at(oldcard);
- }
-
- var next = tp.items.indexOf(newcard) + 1;
- var ntab = tp.items.getAt(next);
- if (valid && ntab && !newcard.onSubmit) {
- ntab.enable();
- }
- };
-
- if (me.subject && !me.title) {
- me.title = PVE.Utils.dialog_title(me.subject, true, false);
- }
-
- Ext.applyIf(me, {
- width: 620,
- height: 400,
- modal: true,
- border: false,
- draggable: true,
- closable: true,
- resizable: false,
- layout: 'border',
- items: [
- {
- // disabled for now - not really needed
- hidden: true,
- region: 'north',
- itemId: 'header',
- layout: 'fit',
- margins: '5 5 0 5',
- bodyPadding: 10,
- html: ''
- },
- {
- xtype: 'form',
- region: 'center',
- layout: 'fit',
- border: false,
- margins: '5 5 0 5',
- fieldDefaults: {
- labelWidth: 100,
- anchor: '100%'
- },
- items: [{
- itemId: 'wizcontent',
- xtype: 'tabpanel',
- activeItem: 0,
- bodyPadding: 10,
- listeners: {
- afterrender: function(tp) {
- var atab = this.getActiveTab();
- tabchange(tp, atab);
- },
- tabchange: function(tp, newcard, oldcard) {
- display_header(newcard);
- tabchange(tp, newcard, oldcard);
- }
- },
- items: tabs
- }]
- },
- tbar
- ]
- });
- me.callParent();
- display_header(tabs[0]);
-
- Ext.Array.each(me.query('field'), function(field) {
- field.on('validitychange', function(f) {
- var tp = me.down('#wizcontent');
- var atab = tp.getActiveTab();
- var valid = check_card(atab);
- me.down('#next').setDisabled(!valid);
- me.down('#submit').setDisabled(!valid);
- var next = tp.items.indexOf(atab) + 1;
- var ntab = tp.items.getAt(next);
- if (!valid) {
- disable_at(ntab);
- } else if (ntab && !atab.onSubmit) {
- ntab.enable();
- }
- });
- });
- }
-});
--- /dev/null
+// Some configuration values are complex strings -
+// so we need parsers/generators for them.
+
+Ext.define('PVE.Parser', { statics: {
+
+ // this class only contains static functions
+
+ parseQemuNetwork: function(key, value) {
+ if (!(key && value)) {
+ return;
+ }
+
+ var res = {};
+
+ var errors = false;
+ Ext.Array.each(value.split(','), function(p) {
+ if (!p || p.match(/^\s*$/)) {
+ return; // continue
+ }
+
+ var match_res;
+
+ if ((match_res = p.match(/^(ne2k_pci|e1000|e1000-82540em|e1000-82544gc|e1000-82545em|vmxnet3|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i)) !== null) {
+ res.model = match_res[1].toLowerCase();
+ if (match_res[3]) {
+ res.macaddr = match_res[3];
+ }
+ } else if ((match_res = p.match(/^bridge=(\S+)$/)) !== null) {
+ res.bridge = match_res[1];
+ } else if ((match_res = p.match(/^rate=(\d+(\.\d+)?)$/)) !== null) {
+ res.rate = match_res[1];
+ } else if ((match_res = p.match(/^tag=(\d+(\.\d+)?)$/)) !== null) {
+ res.tag = match_res[1];
+ } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) {
+ res.firewall = match_res[1];
+ } else if ((match_res = p.match(/^link_down=(\d+)$/)) !== null) {
+ res.disconnect = match_res[1];
+ } else if ((match_res = p.match(/^queues=(\d+)$/)) !== null) {
+ res.queues = match_res[1];
+ } else {
+ errors = true;
+ return false; // break
+ }
+ });
+
+ if (errors || !res.model) {
+ return;
+ }
+
+ return res;
+ },
+
+ printQemuNetwork: function(net) {
+
+ var netstr = net.model;
+ if (net.macaddr) {
+ netstr += "=" + net.macaddr;
+ }
+ if (net.bridge) {
+ netstr += ",bridge=" + net.bridge;
+ if (net.tag) {
+ netstr += ",tag=" + net.tag;
+ }
+ if (net.firewall) {
+ netstr += ",firewall=" + net.firewall;
+ }
+ }
+ if (net.rate) {
+ netstr += ",rate=" + net.rate;
+ }
+ if (net.queues) {
+ netstr += ",queues=" + net.queues;
+ }
+ if (net.disconnect) {
+ netstr += ",link_down=" + net.disconnect;
+ }
+ return netstr;
+ },
+
+ parseQemuDrive: function(key, value) {
+ if (!(key && value)) {
+ return;
+ }
+
+ var res = {};
+
+ var match_res = key.match(/^([a-z]+)(\d+)$/);
+ if (!match_res) {
+ return;
+ }
+ res['interface'] = match_res[1];
+ res.index = match_res[2];
+
+ var errors = false;
+ Ext.Array.each(value.split(','), function(p) {
+ if (!p || p.match(/^\s*$/)) {
+ return; // continue
+ }
+ var match_res = p.match(/^([a-z_]+)=(\S+)$/);
+ if (!match_res) {
+ if (!p.match(/\=/)) {
+ res.file = p;
+ return; // continue
+ }
+ errors = true;
+ return false; // break
+ }
+ var k = match_res[1];
+ if (k === 'volume') {
+ k = 'file';
+ }
+
+ if (Ext.isDefined(res[k])) {
+ errors = true;
+ return false; // break
+ }
+
+ var v = match_res[2];
+
+ if (k === 'cache' && v === 'off') {
+ v = 'none';
+ }
+
+ res[k] = v;
+ });
+
+ if (errors || !res.file) {
+ return;
+ }
+
+ return res;
+ },
+
+ printQemuDrive: function(drive) {
+
+ var drivestr = drive.file;
+
+ Ext.Object.each(drive, function(key, value) {
+ if (!Ext.isDefined(value) || key === 'file' ||
+ key === 'index' || key === 'interface') {
+ return; // continue
+ }
+ drivestr += ',' + key + '=' + value;
+ });
+
+ return drivestr;
+ },
+
+ parseOpenVZNetIf: function(value) {
+ if (!value) {
+ return;
+ }
+
+ var res = {};
+
+ var errors = false;
+ Ext.Array.each(value.split(';'), function(item) {
+ if (!item || item.match(/^\s*$/)) {
+ return; // continue
+ }
+
+ var data = {};
+ Ext.Array.each(item.split(','), function(p) {
+ if (!p || p.match(/^\s*$/)) {
+ return; // continue
+ }
+ var match_res = p.match(/^(ifname|mac|bridge|host_ifname|host_mac|mac_filter)=(\S+)$/);
+ if (!match_res) {
+ errors = true;
+ return false; // break
+ }
+ if (match_res[1] === 'bridge'){
+ var bridgevlanf = match_res[2];
+ var bridge_res = bridgevlanf.match(/^(vmbr(\d+))(v(\d+))?(f)?$/);
+ if (!bridge_res) {
+ errors = true;
+ return false; // break
+ }
+ data['bridge'] = bridge_res[1];
+ data['tag'] = bridge_res[4];
+ data['firewall'] = bridge_res[5] ? 1 : 0;
+ } else {
+ data[match_res[1]] = match_res[2];
+ }
+ });
+
+ if (errors || !data.ifname) {
+ errors = true;
+ return false; // break
+ }
+
+ data.raw = item;
+
+ res[data.ifname] = data;
+ });
+
+ return errors ? undefined: res;
+ },
+
+ printOpenVZNetIf: function(netif) {
+ var netarray = [];
+
+ Ext.Object.each(netif, function(iface, data) {
+ var tmparray = [];
+ Ext.Array.each(['ifname', 'mac', 'bridge', 'host_ifname' , 'host_mac', 'mac_filter', 'tag', 'firewall'], function(key) {
+ var value = data[key];
+ if (key === 'bridge'){
+ if(data['tag']){
+ value = value + 'v' + data['tag'];
+ }
+ if (data['firewall']){
+ value = value + 'f';
+ }
+ }
+ if (value) {
+ tmparray.push(key + '=' + value);
+ }
+
+ });
+ netarray.push(tmparray.join(','));
+ });
+
+ return netarray.join(';');
+ },
+
+ parseLxcNetwork: function(value) {
+ if (!value) {
+ return;
+ }
+
+ var data = {};
+ Ext.Array.each(value.split(','), function(p) {
+ if (!p || p.match(/^\s*$/)) {
+ return; // continue
+ }
+ var match_res = p.match(/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|firewall|tag)=(\S+)$/);
+ if (!match_res) {
+ // todo: simply ignore errors ?
+ return; // continue
+ }
+ data[match_res[1]] = match_res[2];
+ });
+
+ return data;
+ },
+
+ printLxcNetwork: function(data) {
+ var tmparray = [];
+ Ext.Array.each(['bridge', 'hwaddr', 'mtu', 'name', 'ip',
+ 'gw', 'ip6', 'gw6', 'firewall', 'tag'], function(key) {
+ var value = data[key];
+ if (value) {
+ tmparray.push(key + '=' + value);
+ }
+ });
+
+ return tmparray.join(',');
+ },
+
+ parseStartup: function(value) {
+ if (value === undefined) {
+ return;
+ }
+
+ var res = {};
+
+ var errors = false;
+ Ext.Array.each(value.split(','), function(p) {
+ if (!p || p.match(/^\s*$/)) {
+ return; // continue
+ }
+
+ var match_res;
+
+ if ((match_res = p.match(/^(order)?=(\d+)$/)) !== null) {
+ res.order = match_res[2];
+ } else if ((match_res = p.match(/^up=(\d+)$/)) !== null) {
+ res.up = match_res[1];
+ } else if ((match_res = p.match(/^down=(\d+)$/)) !== null) {
+ res.down = match_res[1];
+ } else {
+ errors = true;
+ return false; // break
+ }
+ });
+
+ if (errors) {
+ return;
+ }
+
+ return res;
+ },
+
+ printStartup: function(startup) {
+ var arr = [];
+ if (startup.order !== undefined && startup.order !== '') {
+ arr.push('order=' + startup.order);
+ }
+ if (startup.up !== undefined && startup.up !== '') {
+ arr.push('up=' + startup.up);
+ }
+ if (startup.down !== undefined && startup.down !== '') {
+ arr.push('down=' + startup.down);
+ }
+
+ return arr.join(',');
+ },
+
+ parseQemuSmbios1: function(value) {
+ var res = {};
+
+ Ext.Array.each(value.split(','), function(p) {
+ var kva = p.split(/=/, 2);
+ res[kva[0]] = kva[1];
+ });
+
+ return res;
+ },
+
+ printQemuSmbios1: function(data) {
+
+ var datastr = '';
+
+ Ext.Object.each(data, function(key, value) {
+ if (value === '') return;
+ datastr += (datastr !== '' ? ',' : '') + key + '=' + value;
+ });
+
+ return datastr;
+ },
+
+ parseTfaConfig: function(value) {
+ var res = {};
+
+ Ext.Array.each(value.split(','), function(p) {
+ var kva = p.split(/=/, 2);
+ res[kva[0]] = kva[1];
+ });
+
+ return res;
+ }
+
+}});
--- /dev/null
+pveproxy with ExtJS 6 developpement mini howto
+==============================================
+
+unpack the ExtJS 6 sources, and copy them to /usr/share/pve-manager/ext6
+
+ cd www/ext6/
+ make install
+
+symlink the ext6 dir in pve-manager to the manager5 directory
+
+ cd /usr/share/pve-manager
+ ln -s PATH_TO_YOUR_GIT_REPO/www/manager6
+
+access the PVE proxy with ExtJS 6
+
+ https://localhost:8006/?ext6=1
+
+
+With the extra parameter **ext6=1**, pve-proxy will call the function **PVE::ExtJSIndex6::get_index()**
+which returns a HTML page, with all javascript symlinked from your git repository.
+Provided you included the javascript files in **PVE/ExtJSIndex5.pm**, after editing a file in the git repository, a simple refresh is enough to see your changes in the browser.
--- /dev/null
+/* This state provider keeps part of the state inside
+ * the browser history.
+ *
+ * We compress (shorten) url using dictionary based compression
+ * i.e. use column separated list instead of url encoded hash:
+ * #v\d* version/format
+ * := indicates string values
+ * :\d+ lookup value in dictionary hash
+ * #v1:=value1:5:=value2:=value3:...
+*/
+
+Ext.define('PVE.StateProvider', {
+ extend: 'Ext.state.LocalStorageProvider',
+
+ // private
+ setHV: function(name, newvalue, fireEvents) {
+ var me = this;
+
+ var changes = false;
+ var oldtext = Ext.encode(me.UIState[name]);
+ var newtext = Ext.encode(newvalue);
+ if (newtext != oldtext) {
+ changes = true;
+ me.UIState[name] = newvalue;
+ //console.log("changed old " + name + " " + oldtext);
+ //console.log("changed new " + name + " " + newtext);
+ if (fireEvents) {
+ me.fireEvent("statechange", me, name, { value: newvalue });
+ }
+ }
+ return changes;
+ },
+
+ // private
+ hslist: [
+ // order is important for notifications
+ // [ name, default ]
+ ['view', 'server'],
+ ['rid', 'root'],
+ ['ltab', 'tasks'],
+ ['nodetab', ''],
+ ['storagetab', ''],
+ ['pooltab', ''],
+ ['kvmtab', ''],
+ ['ovztab', ''],
+ ['dctab', '']
+ ],
+
+ hprefix: 'v1',
+
+ compDict: {
+ snapshot: 29,
+ ha: 28,
+ support: 27,
+ pool: 26,
+ syslog: 25,
+ ubc: 24,
+ initlog: 23,
+ openvz: 22,
+ backup: 21,
+ ressources: 20,
+ content: 19,
+ root: 18,
+ domains: 17,
+ roles: 16,
+ groups: 15,
+ users: 14,
+ time: 13,
+ dns: 12,
+ network: 11,
+ services: 10,
+ options: 9,
+ console: 8,
+ hardware: 7,
+ permissions: 6,
+ summary: 5,
+ tasks: 4,
+ clog: 3,
+ storage: 2,
+ folder: 1,
+ server: 0
+ },
+
+ decodeHToken: function(token) {
+ var me = this;
+
+ var state = {};
+ if (!token) {
+ Ext.Array.each(me.hslist, function(rec) {
+ state[rec[0]] = rec[1];
+ });
+ return state;
+ }
+
+ // return Ext.urlDecode(token);
+
+ var items = token.split(':');
+ var prefix = items.shift();
+
+ if (prefix != me.hprefix) {
+ return me.decodeHToken();
+ }
+
+ Ext.Array.each(me.hslist, function(rec) {
+ var value = items.shift();
+ if (value) {
+ if (value[0] === '=') {
+ value = decodeURIComponent(value.slice(1));
+ } else {
+ Ext.Object.each(me.compDict, function(key, cv) {
+ if (value == cv) {
+ value = key;
+ return false;
+ }
+ });
+ }
+ }
+ state[rec[0]] = value;
+ });
+
+ return state;
+ },
+
+ encodeHToken: function(state) {
+ var me = this;
+
+ // return Ext.urlEncode(state);
+
+ var ctoken = me.hprefix;
+ Ext.Array.each(me.hslist, function(rec) {
+ var value = state[rec[0]];
+ if (!Ext.isDefined(value)) {
+ value = rec[1];
+ }
+ value = encodeURIComponent(value);
+ if (!value) {
+ ctoken += ':';
+ } else {
+ var comp = me.compDict[value];
+ if (Ext.isDefined(comp)) {
+ ctoken += ":" + comp;
+ } else {
+ ctoken += ":=" + value;
+ }
+ }
+ });
+
+ return ctoken;
+ },
+
+ constructor: function(config){
+ var me = this;
+
+ me.callParent([config]);
+
+ me.UIState = me.decodeHToken(); // set default
+
+ var history_change_cb = function(token) {
+ //console.log("HC " + token);
+ if (!token) {
+ var res = window.confirm(gettext('Are you sure you want to navigate away from this page?'));
+ if (res){
+ // process text value and close...
+ Ext.History.back();
+ } else {
+ Ext.History.forward();
+ }
+ return;
+ }
+
+ var newstate = me.decodeHToken(token);
+ Ext.Array.each(me.hslist, function(rec) {
+ if (typeof newstate[rec[0]] == "undefined") {
+ return;
+ }
+ me.setHV(rec[0], newstate[rec[0]], true);
+ });
+ };
+
+ var start_token = Ext.History.getToken();
+ if (start_token) {
+ history_change_cb(start_token);
+ } else {
+ var htext = me.encodeHToken(me.UIState);
+ Ext.History.add(htext);
+ }
+
+ Ext.History.on('change', history_change_cb);
+ },
+
+ get: function(name, defaultValue){
+ /*jslint confusion: true */
+ var me = this;
+ var data;
+
+ if (typeof me.UIState[name] != "undefined") {
+ data = { value: me.UIState[name] };
+ } else {
+ data = me.callParent(arguments);
+ if (!data && name === 'GuiCap') {
+ data = { vms: {}, storage: {}, access: {}, nodes: {}, dc: {} };
+ }
+ }
+
+ //console.log("GET " + name + " " + Ext.encode(data));
+ return data;
+ },
+
+ clear: function(name){
+ var me = this;
+
+ if (typeof me.UIState[name] != "undefined") {
+ me.UIState[name] = null;
+ }
+
+ me.callParent(arguments);
+ },
+
+ set: function(name, value){
+ var me = this;
+
+ //console.log("SET " + name + " " + Ext.encode(value));
+ if (typeof me.UIState[name] != "undefined") {
+ var newvalue = value ? value.value : null;
+ if (me.setHV(name, newvalue, false)) {
+ var htext = me.encodeHToken(me.UIState);
+ Ext.History.add(htext);
+ }
+ } else {
+ me.callParent(arguments);
+ }
+ }
+});
--- /dev/null
+// ExtJS related things
+
+PVE.Utils.toolkit = 'extjs',
+
+ // do not send '_dc' parameter
+Ext.Ajax.disableCaching = false;
+
+// custom Vtypes
+Ext.apply(Ext.form.field.VTypes, {
+ IPAddress: function(v) {
+ return IP4_match.test(v);
+ },
+ IPAddressText: gettext('Example') + ': 192.168.1.1',
+ IPAddressMask: /[\d\.]/i,
+
+ IPCIDRAddress: function(v) {
+ return IP4_cidr_match.test(v);
+ },
+ IPCIDRAddressText: gettext('Example') + ': 192.168.1.1/24',
+ IPCIDRAddressMask: /[\d\.\/]/i,
+
+ IP6Address: function(v) {
+ return IP6_match.test(v);
+ },
+ IP6AddressText: gettext('Example') + ': 2001:DB8::42',
+ IP6AddressMask: /[A-Fa-f0-9:]/,
+
+ IP6CIDRAddress: function(v) {
+ return IP6_cidr_match.test(v);
+ },
+ IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64',
+ IP6CIDRAddressMask: /[A-Fa-f0-9:\/]/,
+
+ IP6PrefixLength: function(v) {
+ return v >= 0 && v <= 128;
+ },
+ IP6PrefixLengthText: gettext('Example') + ': X, where 0 <= X <= 128',
+ IP6PrefixLengthMask: /[0-9]/,
+
+ IP64Address: function(v) {
+ return IP64_match.test(v);
+ },
+ IP64AddressText: gettext('Example') + ': 192.168.1.1 2001:DB8::42',
+ IP64AddressMask: /[A-Fa-f0-9\.:]/,
+
+ MacAddress: function(v) {
+ return (/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/).test(v);
+ },
+ MacAddressMask: /[a-fA-F0-9:]/,
+ MacAddressText: gettext('Example') + ': 01:23:45:67:89:ab',
+
+ BridgeName: function(v) {
+ return (/^vmbr\d{1,4}$/).test(v);
+ },
+ BridgeNameText: gettext('Format') + ': vmbr<b>N</b>, where 0 <= <b>N</b> <= 9999',
+
+ BondName: function(v) {
+ return (/^bond\d{1,4}$/).test(v);
+ },
+ BondNameText: gettext('Format') + ': bond<b>N</b>, where 0 <= <b>N</b> <= 9999',
+
+ InterfaceName: function(v) {
+ return (/^[a-z][a-z0-9_]{1,20}$/).test(v);
+ },
+ InterfaceNameText: gettext('Format') + ': [a-z][a-z0-9_]{1,20}',
+
+
+ QemuStartDate: function(v) {
+ return (/^(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)$/).test(v);
+ },
+ QemuStartDateText: gettext('Format') + ': "now" or "2006-06-17T16:01:21" or "2006-06-17"',
+
+ StorageId: function(v) {
+ return (/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i).test(v);
+ },
+ StorageIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '-', '_', '.'",
+
+ ConfigId: function(v) {
+ return (/^[a-z][a-z0-9\_]+$/i).test(v);
+ },
+ ConfigIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '_'",
+
+ HttpProxy: function(v) {
+ return (/^http:\/\/.*$/).test(v);
+ },
+ HttpProxyText: gettext('Example') + ": http://username:password@host:port/",
+
+ DnsName: function(v) {
+ return (/^(([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)\.)*([A-Za-z0-9]([A-Za-z0-9\-]*[A-Za-z0-9])?)$/).test(v);
+ },
+ DnsNameText: gettext('This is not a valid DNS name')
+});
+
+// we dont want that a displayfield set the form dirty flag!
+Ext.override(Ext.form.field.Display, {
+ isDirty: function() { return false; }
+});
+
+// hack: ExtJS does not display the correct value if we
+// call setValue while the store is loading, so we need
+// to call it again after loading
+Ext.override(Ext.form.field.ComboBox, {
+ onLoad: function() {
+ this.setValue(this.value, false);
+ this.callOverridden(arguments);
+ }
+});
+
+Ext.define('Ext.ux.IFrame', {
+ extend: 'Ext.Component',
+
+ alias: 'widget.uxiframe',
+
+ loadMask: 'Loading...',
+
+ src: 'about:blank',
+
+ renderTpl: [
+ '<iframe src="{src}" name="{frameName}" width="100%" height="100%" frameborder="0"></iframe>'
+ ],
+
+ initComponent: function () {
+ this.callParent();
+
+ this.frameName = this.frameName || this.id + '-frame';
+
+ this.addEvents(
+ 'beforeload',
+ 'load'
+ );
+
+ Ext.apply(this.renderSelectors, {
+ iframeEl: 'iframe'
+ });
+ },
+
+ initEvents : function() {
+ var me = this;
+ me.callParent();
+ me.iframeEl.on('load', me.onLoad, me);
+ },
+
+ initRenderData: function() {
+ return Ext.apply(this.callParent(), {
+ src: this.src,
+ frameName: this.frameName
+ });
+ },
+
+ getBody: function() {
+ var doc = this.getDoc();
+ return doc.body || doc.documentElement;
+ },
+
+ getDoc: function() {
+ try {
+ return this.getWin().document;
+ } catch (ex) {
+ return null;
+ }
+ },
+
+ getWin: function() {
+ var me = this,
+ name = me.frameName,
+ win = Ext.isIE
+ ? me.iframeEl.dom.contentWindow
+ : window.frames[name];
+ return win;
+ },
+
+ getFrame: function() {
+ var me = this;
+ return me.iframeEl.dom;
+ },
+
+ beforeDestroy: function () {
+ this.cleanupListeners(true);
+ this.callParent();
+ },
+
+ cleanupListeners: function(destroying){
+ var doc, prop;
+
+ if (this.rendered) {
+ try {
+ doc = this.getDoc();
+ if (doc) {
+ Ext.EventManager.removeAll(doc);
+ if (destroying) {
+ for (prop in doc) {
+ if (doc.hasOwnProperty && doc.hasOwnProperty(prop)) {
+ delete doc[prop];
+ }
+ }
+ }
+ }
+ } catch(e) { }
+ }
+ },
+
+ onLoad: function() {
+ var me = this,
+ doc = me.getDoc(),
+ fn = me.onRelayedEvent;
+
+ if (doc) {
+ try {
+ Ext.EventManager.removeAll(doc);
+
+ // These events need to be relayed from the inner document (where they stop
+ // bubbling) up to the outer document. This has to be done at the DOM level so
+ // the event reaches listeners on elements like the document body. The effected
+ // mechanisms that depend on this bubbling behavior are listed to the right
+ // of the event.
+ Ext.EventManager.on(doc, {
+ mousedown: fn, // menu dismisal (MenuManager) and Window onMouseDown (toFront)
+ mousemove: fn, // window resize drag detection
+ mouseup: fn, // window resize termination
+ click: fn, // not sure, but just to be safe
+ dblclick: fn, // not sure again
+ scope: me
+ });
+ } catch(e) {
+ // cannot do this xss
+ }
+
+ // We need to be sure we remove all our events from the iframe on unload or we're going to LEAK!
+ Ext.EventManager.on(this.getWin(), 'beforeunload', me.cleanupListeners, me);
+
+ this.el.unmask();
+ this.fireEvent('load', this);
+
+ } else if(me.src && me.src != '') {
+
+ this.el.unmask();
+ this.fireEvent('error', this);
+ }
+
+
+ },
+
+ load: function (src) {
+ var me = this,
+ text = me.loadMask,
+ frame = me.getFrame();
+
+ if (me.fireEvent('beforeload', me, src) !== false) {
+ if (text && me.el) {
+ me.el.mask(text);
+ }
+
+ frame.src = me.src = (src || me.src);
+ }
+ }
+});
--- /dev/null
+Ext.ns('PVE');
+
+// avoid errors when running without development tools
+if (!Ext.isDefined(Ext.global.console)) {
+ var console = {
+ dir: function() {},
+ log: function() {}
+ };
+}
+console.log("Starting PVE Manager");
+
+Ext.Ajax.defaultHeaders = {
+ 'Accept': 'application/json'
+};
+
+Ext.Ajax.on('beforerequest', function(conn, options) {
+ if (PVE.CSRFPreventionToken) {
+ if (!options.headers) {
+ options.headers = {};
+ }
+ options.headers.CSRFPreventionToken = PVE.CSRFPreventionToken;
+ }
+});
+
+var IPV4_OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])";
+var IPV4_REGEXP = "(?:(?:" + IPV4_OCTET + "\\.){3}" + IPV4_OCTET + ")";
+var IPV6_H16 = "(?:[0-9a-fA-F]{1,4})";
+var IPV6_LS32 = "(?:(?:" + IPV6_H16 + ":" + IPV6_H16 + ")|" + IPV4_REGEXP + ")";
+
+
+var IP4_match = new RegExp("^(?:" + IPV4_REGEXP + ")$");
+var IP4_cidr_match = new RegExp("^(?:" + IPV4_REGEXP + ")\/[1-3][0-9]?$");
+
+var IPV6_REGEXP = "(?:" +
+ "(?:(?:" + "(?:" + IPV6_H16 + ":){6})" + IPV6_LS32 + ")|" +
+ "(?:(?:" + "::" + "(?:" + IPV6_H16 + ":){5})" + IPV6_LS32 + ")|" +
+ "(?:(?:(?:" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){4})" + IPV6_LS32 + ")|" +
+ "(?:(?:(?:(?:" + IPV6_H16 + ":){0,1}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){3})" + IPV6_LS32 + ")|" +
+ "(?:(?:(?:(?:" + IPV6_H16 + ":){0,2}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){2})" + IPV6_LS32 + ")|" +
+ "(?:(?:(?:(?:" + IPV6_H16 + ":){0,3}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){1})" + IPV6_LS32 + ")|" +
+ "(?:(?:(?:(?:" + IPV6_H16 + ":){0,4}" + IPV6_H16 + ")?::" + ")" + IPV6_LS32 + ")|" +
+ "(?:(?:(?:(?:" + IPV6_H16 + ":){0,5}" + IPV6_H16 + ")?::" + ")" + IPV6_H16 + ")|" +
+ "(?:(?:(?:(?:" + IPV6_H16 + ":){0,7}" + IPV6_H16 + ")?::" + ")" + ")" +
+ ")";
+
+var IP6_match = new RegExp("^(?:" + IPV6_REGEXP + ")$");
+var IP6_cidr_match = new RegExp("^(?:" + IPV6_REGEXP + ")\/[0-9]{1,3}?$");
+var IP6_bracket_match = new RegExp("^\\[(" + IPV6_REGEXP + ")\\]");
+
+var IP64_match = new RegExp("^(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + ")$");
+
+Ext.define('PVE.Utils', { statics: {
+
+ // this class only contains static functions
+
+ toolkit: undefined, // (extjs|touch), set inside Toolkit.js
+
+ log_severity_hash: {
+ 0: "panic",
+ 1: "alert",
+ 2: "critical",
+ 3: "error",
+ 4: "warning",
+ 5: "notice",
+ 6: "info",
+ 7: "debug"
+ },
+
+ support_level_hash: {
+ 'c': gettext('Community'),
+ 'b': gettext('Basic'),
+ 's': gettext('Standard'),
+ 'p': gettext('Premium')
+ },
+
+ noSubKeyHtml: 'You do not have a valid subscription for this server. Please visit <a target="_blank" href="http://www.proxmox.com/products/proxmox-ve/subscription-service-plans">www.proxmox.com</a> to get a list of available options.',
+
+ kvm_ostypes: {
+ other: gettext('Other OS types'),
+ wxp: 'Microsoft Windows XP/2003',
+ w2k: 'Microsoft Windows 2000',
+ w2k8: 'Microsoft Windows Vista/2008',
+ win7: 'Microsoft Windows 7/2008r2',
+ win8: 'Microsoft Windows 8/2012',
+ l24: 'Linux 2.4 Kernel',
+ l26: 'Linux 3.X/2.6 Kernel',
+ solaris: 'Solaris Kernel'
+ },
+
+ render_kvm_ostype: function (value) {
+ if (!value) {
+ return gettext('Other OS types');
+ }
+ var text = PVE.Utils.kvm_ostypes[value];
+ if (text) {
+ return text + ' (' + value + ')';
+ }
+ return value;
+ },
+
+ render_hotplug_features: function (value) {
+ var fa = [];
+
+ if (!value || (value === '0')) {
+ return gettext('disabled');
+ }
+
+ Ext.each(value.split(','), function(el) {
+ if (el === 'disk') {
+ fa.push(gettext('Disk'));
+ } else if (el === 'network') {
+ fa.push(gettext('Network'));
+ } else if (el === 'usb') {
+ fa.push(gettext('USB'));
+ } else if (el === 'memory') {
+ fa.push(gettext('Memory'));
+ } else if (el === 'cpu') {
+ fa.push(gettext('CPU'));
+ } else {
+ fa.push(el);
+ }
+ });
+
+ return fa.join(', ');
+ },
+
+ network_iface_types: {
+ eth: gettext("Network Device"),
+ bridge: 'Linux Bridge',
+ bond: 'Linux Bond',
+ OVSBridge: 'OVS Bridge',
+ OVSBond: 'OVS Bond',
+ OVSPort: 'OVS Port',
+ OVSIntPort: 'OVS IntPort'
+ },
+
+ render_network_iface_type: function(value) {
+ return PVE.Utils.network_iface_types[value] ||
+ PVE.Utils.unknownText;
+ },
+
+ render_scsihw: function(value) {
+ if (!value) {
+ return PVE.Utils.defaultText + ' (LSI 53C895A)';
+ } else if (value === 'lsi') {
+ return 'LSI 53C895A';
+ } else if (value === 'lsi53c810') {
+ return 'LSI 53C810';
+ } else if (value === 'megasas') {
+ return 'MegaRAID SAS 8708EM2';
+ } else if (value === 'virtio-scsi-pci') {
+ return 'VIRTIO';
+ } else if (value === 'pvscsi') {
+ return 'VMware PVSCSI';
+ } else {
+ return value;
+ }
+ },
+
+ // fixme: auto-generate this
+ // for now, please keep in sync with PVE::Tools::kvmkeymaps
+ kvm_keymaps: {
+ //ar: 'Arabic',
+ da: 'Danish',
+ de: 'German',
+ 'de-ch': 'German (Swiss)',
+ 'en-gb': 'English (UK)',
+ 'en-us': 'English (USA)',
+ es: 'Spanish',
+ //et: 'Estonia',
+ fi: 'Finnish',
+ //fo: 'Faroe Islands',
+ fr: 'French',
+ 'fr-be': 'French (Belgium)',
+ 'fr-ca': 'French (Canada)',
+ 'fr-ch': 'French (Swiss)',
+ //hr: 'Croatia',
+ hu: 'Hungarian',
+ is: 'Icelandic',
+ it: 'Italian',
+ ja: 'Japanese',
+ lt: 'Lithuanian',
+ //lv: 'Latvian',
+ mk: 'Macedonian',
+ nl: 'Dutch',
+ //'nl-be': 'Dutch (Belgium)',
+ no: 'Norwegian',
+ pl: 'Polish',
+ pt: 'Portuguese',
+ 'pt-br': 'Portuguese (Brazil)',
+ //ru: 'Russian',
+ sl: 'Slovenian',
+ sv: 'Swedish',
+ //th: 'Thai',
+ tr: 'Turkish'
+ },
+
+ kvm_vga_drivers: {
+ std: gettext('Standard VGA'),
+ vmware: gettext('VMWare compatible'),
+ cirrus: 'Cirrus Logic GD5446',
+ qxl: 'SPICE',
+ qxl2: 'SPICE dual monitor',
+ qxl3: 'SPICE three monitors',
+ qxl4: 'SPICE four monitors',
+ serial0: gettext('Serial terminal') + ' 0',
+ serial1: gettext('Serial terminal') + ' 1',
+ serial2: gettext('Serial terminal') + ' 2',
+ serial3: gettext('Serial terminal') + ' 3'
+ },
+
+ render_kvm_language: function (value) {
+ if (!value) {
+ return PVE.Utils.defaultText;
+ }
+ var text = PVE.Utils.kvm_keymaps[value];
+ if (text) {
+ return text + ' (' + value + ')';
+ }
+ return value;
+ },
+
+ kvm_keymap_array: function() {
+ var data = [['', PVE.Utils.render_kvm_language('')]];
+ Ext.Object.each(PVE.Utils.kvm_keymaps, function(key, value) {
+ data.push([key, PVE.Utils.render_kvm_language(value)]);
+ });
+
+ return data;
+ },
+
+ render_console_viewer: function(value) {
+ if (!value) {
+ return PVE.Utils.defaultText + ' (HTML5)';
+ } else if (value === 'applet') {
+ return 'Java VNC Applet';
+ } else if (value === 'vv') {
+ return 'SPICE (remote-viewer)';
+ } else if (value === 'html5') {
+ return 'HTML5 (noVNC)';
+ } else {
+ return value;
+ }
+ },
+
+ language_map: {
+ zh_CN: 'Chinese',
+ ca: 'Catalan',
+ da: 'Danish',
+ en: 'English',
+ eu: 'Euskera (Basque)',
+ fr: 'French',
+ de: 'German',
+ it: 'Italian',
+ ja: 'Japanese',
+ nb: 'Norwegian (Bokmal)',
+ nn: 'Norwegian (Nynorsk)',
+ fa: 'Persian (Farsi)',
+ pl: 'Polish',
+ pt_BR: 'Portuguese (Brazil)',
+ ru: 'Russian',
+ sl: 'Slovenian',
+ es: 'Spanish',
+ sv: 'Swedish',
+ tr: 'Turkish'
+ },
+
+ render_language: function (value) {
+ if (!value) {
+ return PVE.Utils.defaultText + ' (English)';
+ }
+ var text = PVE.Utils.language_map[value];
+ if (text) {
+ return text + ' (' + value + ')';
+ }
+ return value;
+ },
+
+ language_array: function() {
+ var data = [['', PVE.Utils.render_language('')]];
+ Ext.Object.each(PVE.Utils.language_map, function(key, value) {
+ data.push([key, PVE.Utils.render_language(value)]);
+ });
+
+ return data;
+ },
+
+ render_kvm_vga_driver: function (value) {
+ if (!value) {
+ return PVE.Utils.defaultText;
+ }
+ var text = PVE.Utils.kvm_vga_drivers[value];
+ if (text) {
+ return text + ' (' + value + ')';
+ }
+ return value;
+ },
+
+ kvm_vga_driver_array: function() {
+ var data = [['', PVE.Utils.render_kvm_vga_driver('')]];
+ Ext.Object.each(PVE.Utils.kvm_vga_drivers, function(key, value) {
+ data.push([key, PVE.Utils.render_kvm_vga_driver(value)]);
+ });
+
+ return data;
+ },
+
+ render_kvm_startup: function(value) {
+ var startup = PVE.Parser.parseStartup(value);
+
+ var res = 'order=';
+ if (startup.order === undefined) {
+ res += 'any';
+ } else {
+ res += startup.order;
+ }
+ if (startup.up !== undefined) {
+ res += ',up=' + startup.up;
+ }
+ if (startup.down !== undefined) {
+ res += ',down=' + startup.down;
+ }
+
+ return res;
+ },
+
+ authOK: function() {
+ return Ext.util.Cookies.get('PVEAuthCookie');
+ },
+
+ authClear: function() {
+ Ext.util.Cookies.clear("PVEAuthCookie");
+ },
+
+ // fixme: remove - not needed?
+ gridLineHeigh: function() {
+ return 21;
+
+ //if (Ext.isGecko)
+ //return 23;
+ //return 21;
+ },
+
+ extractRequestError: function(result, verbose) {
+ var msg = gettext('Successful');
+
+ if (!result.success) {
+ msg = gettext("Unknown error");
+ if (result.message) {
+ msg = result.message;
+ if (result.status) {
+ msg += ' (' + result.status + ')';
+ }
+ }
+ if (verbose && Ext.isObject(result.errors)) {
+ msg += "<br>";
+ Ext.Object.each(result.errors, function(prop, desc) {
+ msg += "<br><b>" + Ext.htmlEncode(prop) + "</b>: " +
+ Ext.htmlEncode(desc);
+ });
+ }
+ }
+
+ return msg;
+ },
+
+ extractFormActionError: function(action) {
+ var msg;
+ switch (action.failureType) {
+ case Ext.form.action.Action.CLIENT_INVALID:
+ msg = gettext('Form fields may not be submitted with invalid values');
+ break;
+ case Ext.form.action.Action.CONNECT_FAILURE:
+ msg = gettext('Connection error');
+ var resp = action.response;
+ if (resp.status && resp.statusText) {
+ msg += " " + resp.status + ": " + resp.statusText;
+ }
+ break;
+ case Ext.form.action.Action.LOAD_FAILURE:
+ case Ext.form.action.Action.SERVER_INVALID:
+ msg = PVE.Utils.extractRequestError(action.result, true);
+ break;
+ }
+ return msg;
+ },
+
+ // Ext.Ajax.request
+ API2Request: function(reqOpts) {
+
+ var newopts = Ext.apply({
+ waitMsg: gettext('Please wait...')
+ }, reqOpts);
+
+ if (!newopts.url.match(/^\/api2/)) {
+ newopts.url = '/api2/extjs' + newopts.url;
+ }
+ delete newopts.callback;
+
+ var createWrapper = function(successFn, callbackFn, failureFn) {
+ Ext.apply(newopts, {
+ success: function(response, options) {
+ if (options.waitMsgTarget) {
+ if (PVE.Utils.toolkit === 'touch') {
+ options.waitMsgTarget.setMasked(false);
+ } else {
+ options.waitMsgTarget.setLoading(false);
+ }
+ }
+ var result = Ext.decode(response.responseText);
+ response.result = result;
+ if (!result.success) {
+ response.htmlStatus = PVE.Utils.extractRequestError(result, true);
+ Ext.callback(callbackFn, options.scope, [options, false, response]);
+ Ext.callback(failureFn, options.scope, [response, options]);
+ return;
+ }
+ Ext.callback(callbackFn, options.scope, [options, true, response]);
+ Ext.callback(successFn, options.scope, [response, options]);
+ },
+ failure: function(response, options) {
+ if (options.waitMsgTarget) {
+ if (PVE.Utils.toolkit === 'touch') {
+ options.waitMsgTarget.setMasked(false);
+ } else {
+ options.waitMsgTarget.setLoading(false);
+ }
+ }
+ response.result = {};
+ try {
+ response.result = Ext.decode(response.responseText);
+ } catch(e) {}
+ var msg = gettext('Connection error') + ' - server offline?';
+ if (response.aborted) {
+ msg = gettext('Connection error') + ' - aborted.';
+ } else if (response.timedout) {
+ msg = gettext('Connection error') + ' - Timeout.';
+ } else if (response.status && response.statusText) {
+ msg = gettext('Connection error') + ' ' + response.status + ': ' + response.statusText;
+ }
+ response.htmlStatus = msg;
+ Ext.callback(callbackFn, options.scope, [options, false, response]);
+ Ext.callback(failureFn, options.scope, [response, options]);
+ }
+ });
+ };
+
+ createWrapper(reqOpts.success, reqOpts.callback, reqOpts.failure);
+
+ var target = newopts.waitMsgTarget;
+ if (target) {
+ if (PVE.Utils.toolkit === 'touch') {
+ target.setMasked({ xtype: 'loadmask', message: newopts.waitMsg} );
+ } else {
+ // Note: ExtJS bug - this does not work when component is not rendered
+ target.setLoading(newopts.waitMsg);
+ }
+ }
+ Ext.Ajax.request(newopts);
+ },
+
+ assemble_field_data: function(values, data) {
+ if (Ext.isObject(data)) {
+ Ext.Object.each(data, function(name, val) {
+ if (values.hasOwnProperty(name)) {
+ var bucket = values[name];
+ if (!Ext.isArray(bucket)) {
+ bucket = values[name] = [bucket];
+ }
+ if (Ext.isArray(val)) {
+ values[name] = bucket.concat(val);
+ } else {
+ bucket.push(val);
+ }
+ } else {
+ values[name] = val;
+ }
+ });
+ }
+ },
+
+ checked_command: function(orig_cmd) {
+ PVE.Utils.API2Request({
+ url: '/nodes/localhost/subscription',
+ method: 'GET',
+ //waitMsgTarget: me,
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ success: function(response, opts) {
+ var data = response.result.data;
+
+ if (data.status !== 'Active') {
+ Ext.Msg.show({
+ title: gettext('No valid subscription'),
+ icon: Ext.Msg.WARNING,
+ msg: PVE.Utils.noSubKeyHtml,
+ buttons: Ext.Msg.OK,
+ callback: function(btn) {
+ if (btn !== 'ok') {
+ return;
+ }
+ orig_cmd();
+ }
+ });
+ } else {
+ orig_cmd();
+ }
+ }
+ });
+ },
+
+ task_desc_table: {
+ vncproxy: [ 'VM/CT', gettext('Console') ],
+ spiceproxy: [ 'VM/CT', gettext('Console') + ' (Spice)' ],
+ vncshell: [ '', gettext('Shell') ],
+ spiceshell: [ '', gettext('Shell') + ' (Spice)' ],
+ qmsnapshot: [ 'VM', gettext('Snapshot') ],
+ qmrollback: [ 'VM', gettext('Rollback') ],
+ qmdelsnapshot: [ 'VM', gettext('Delete Snapshot') ],
+ qmcreate: [ 'VM', gettext('Create') ],
+ qmrestore: [ 'VM', gettext('Restore') ],
+ qmdestroy: [ 'VM', gettext('Destroy') ],
+ qmigrate: [ 'VM', gettext('Migrate') ],
+ qmclone: [ 'VM', gettext('Clone') ],
+ qmmove: [ 'VM', gettext('Move disk') ],
+ qmtemplate: [ 'VM', gettext('Convert to template') ],
+ qmstart: [ 'VM', gettext('Start') ],
+ qmstop: [ 'VM', gettext('Stop') ],
+ qmreset: [ 'VM', gettext('Reset') ],
+ qmshutdown: [ 'VM', gettext('Shutdown') ],
+ qmsuspend: [ 'VM', gettext('Suspend') ],
+ qmresume: [ 'VM', gettext('Resume') ],
+ qmconfig: [ 'VM', gettext('Configure') ],
+ vzcreate: ['CT', gettext('Create') ],
+ vzrestore: ['CT', gettext('Restore') ],
+ vzdestroy: ['CT', gettext('Destroy') ],
+ vzmigrate: [ 'CT', gettext('Migrate') ],
+ vzstart: ['CT', gettext('Start') ],
+ vzstop: ['CT', gettext('Stop') ],
+ vzmount: ['CT', gettext('Mount') ],
+ vzumount: ['CT', gettext('Unmount') ],
+ vzshutdown: ['CT', gettext('Shutdown') ],
+ vzsuspend: [ 'CT', gettext('Suspend') ],
+ vzresume: [ 'CT', gettext('Resume') ],
+ hamigrate: [ 'HA', gettext('Migrate') ],
+ hastart: [ 'HA', gettext('Start') ],
+ hastop: [ 'HA', gettext('Stop') ],
+ srvstart: ['SRV', gettext('Start') ],
+ srvstop: ['SRV', gettext('Stop') ],
+ srvrestart: ['SRV', gettext('Restart') ],
+ srvreload: ['SRV', gettext('Reload') ],
+ cephcreatemon: ['Ceph Monitor', gettext('Create') ],
+ cephdestroymon: ['Ceph Monitor', gettext('Destroy') ],
+ cephcreateosd: ['Ceph OSD', gettext('Create') ],
+ cephdestroyosd: ['Ceph OSD', gettext('Destroy') ],
+ imgcopy: ['', gettext('Copy data') ],
+ imgdel: ['', gettext('Erase data') ],
+ download: ['', gettext('Download') ],
+ vzdump: ['', gettext('Backup') ],
+ aptupdate: ['', gettext('Update package database') ],
+ startall: [ '', gettext('Start all VMs and Containers') ],
+ stopall: [ '', gettext('Stop all VMs and Containers') ],
+ migrateall: [ '', gettext('Migrate all VMs and Containers') ]
+ },
+
+ format_task_description: function(type, id) {
+ var farray = PVE.Utils.task_desc_table[type];
+ if (!farray) {
+ return type;
+ }
+ var prefix = farray[0];
+ var text = farray[1];
+ if (prefix) {
+ return prefix + ' ' + id + ' - ' + text;
+ }
+ return text;
+ },
+
+ parse_task_upid: function(upid) {
+ var task = {};
+
+ var res = upid.match(/^UPID:(\S+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8,9}):([0-9A-Fa-f]{8}):([^:\s]+):([^:\s]*):([^:\s]+):$/);
+ if (!res) {
+ throw "unable to parse upid '" + upid + "'";
+ }
+ task.node = res[1];
+ task.pid = parseInt(res[2], 16);
+ task.pstart = parseInt(res[3], 16);
+ task.starttime = parseInt(res[4], 16);
+ task.type = res[5];
+ task.id = res[6];
+ task.user = res[7];
+
+ task.desc = PVE.Utils.format_task_description(task.type, task.id);
+
+ return task;
+ },
+
+ format_size: function(size) {
+ /*jslint confusion: true */
+
+ if (size < 1024) {
+ return size;
+ }
+
+ var kb = size / 1024;
+
+ if (kb < 1024) {
+ return kb.toFixed(0) + "KB";
+ }
+
+ var mb = size / (1024*1024);
+
+ if (mb < 1024) {
+ return mb.toFixed(0) + "MB";
+ }
+
+ var gb = mb / 1024;
+
+ if (gb < 1024) {
+ return gb.toFixed(2) + "GB";
+ }
+
+ var tb = gb / 1024;
+
+ return tb.toFixed(2) + "TB";
+
+ },
+
+ format_html_bar: function(per, text) {
+
+ return "<div class='pve-bar-wrap'>" + text + "<div class='pve-bar-border'>" +
+ "<div class='pve-bar-inner' style='width:" + per + "%;'></div>" +
+ "</div></div>";
+
+ },
+
+ format_cpu_bar: function(per1, per2, text) {
+
+ return "<div class='pve-bar-border'>" +
+ "<div class='pve-bar-inner' style='width:" + per1 + "%;'></div>" +
+ "<div class='pve-bar-inner2' style='width:" + per2 + "%;'></div>" +
+ "<div class='pve-bar-text'>" + text + "</div>" +
+ "</div>";
+ },
+
+ format_large_bar: function(per, text) {
+
+ if (!text) {
+ text = per.toFixed(1) + "%";
+ }
+
+ return "<div class='pve-largebar-border'>" +
+ "<div class='pve-largebar-inner' style='width:" + per + "%;'></div>" +
+ "<div class='pve-largebar-text'>" + text + "</div>" +
+ "</div>";
+ },
+
+ format_duration_long: function(ut) {
+
+ var days = Math.floor(ut / 86400);
+ ut -= days*86400;
+ var hours = Math.floor(ut / 3600);
+ ut -= hours*3600;
+ var mins = Math.floor(ut / 60);
+ ut -= mins*60;
+
+ var hours_str = '00' + hours.toString();
+ hours_str = hours_str.substr(hours_str.length - 2);
+ var mins_str = "00" + mins.toString();
+ mins_str = mins_str.substr(mins_str.length - 2);
+ var ut_str = "00" + ut.toString();
+ ut_str = ut_str.substr(ut_str.length - 2);
+
+ if (days) {
+ var ds = days > 1 ? PVE.Utils.daysText : PVE.Utils.dayText;
+ return days.toString() + ' ' + ds + ' ' +
+ hours_str + ':' + mins_str + ':' + ut_str;
+ } else {
+ return hours_str + ':' + mins_str + ':' + ut_str;
+ }
+ },
+
+ format_duration_short: function(ut) {
+
+ if (ut < 60) {
+ return ut.toString() + 's';
+ }
+
+ if (ut < 3600) {
+ var mins = ut / 60;
+ return mins.toFixed(0) + 'm';
+ }
+
+ if (ut < 86400) {
+ var hours = ut / 3600;
+ return hours.toFixed(0) + 'h';
+ }
+
+ var days = ut / 86400;
+ return days.toFixed(0) + 'd';
+ },
+
+ yesText: gettext('Yes'),
+ noText: gettext('No'),
+ noneText: gettext('none'),
+ errorText: gettext('Error'),
+ unknownText: gettext('Unknown'),
+ defaultText: gettext('Default'),
+ daysText: gettext('days'),
+ dayText: gettext('day'),
+ runningText: gettext('running'),
+ stoppedText: gettext('stopped'),
+ neverText: gettext('never'),
+ totalText: gettext('Total'),
+ usedText: gettext('Used'),
+ directoryText: gettext('Directory'),
+ imagesText: gettext('Disk image'),
+ backupFileText: gettext('VZDump backup file'),
+ vztmplText: gettext('Container template'),
+ isoImageText: gettext('ISO image'),
+ containersText: gettext('Container'),
+
+ format_expire: function(date) {
+ if (!date) {
+ return PVE.Utils.neverText;
+ }
+ return Ext.Date.format(date, "Y-m-d");
+ },
+
+ format_storage_type: function(value) {
+ if (value === 'dir') {
+ return PVE.Utils.directoryText;
+ } else if (value === 'nfs') {
+ return 'NFS';
+ } else if (value === 'glusterfs') {
+ return 'GlusterFS';
+ } else if (value === 'lvm') {
+ return 'LVM';
+ } else if (value === 'iscsi') {
+ return 'iSCSI';
+ } else if (value === 'rbd') {
+ return 'RBD';
+ } else if (value === 'sheepdog') {
+ return 'Sheepdog';
+ } else if (value === 'zfs') {
+ return 'ZFS over iSCSI';
+ } else if (value === 'zfspool') {
+ return 'ZFS';
+ } else if (value === 'iscsidirect') {
+ return 'iSCSIDirect';
+ } else {
+ return PVE.Utils.unknownText;
+ }
+ },
+
+ format_boolean_with_default: function(value) {
+ if (Ext.isDefined(value) && value !== '') {
+ return value ? PVE.Utils.yesText : PVE.Utils.noText;
+ }
+ return PVE.Utils.defaultText;
+ },
+
+ format_boolean: function(value) {
+ return value ? PVE.Utils.yesText : PVE.Utils.noText;
+ },
+
+ format_neg_boolean: function(value) {
+ return !value ? PVE.Utils.yesText : PVE.Utils.noText;
+ },
+
+ format_content_types: function(value) {
+ var cta = [];
+
+ Ext.each(value.split(',').sort(), function(ct) {
+ if (ct === 'images') {
+ cta.push(PVE.Utils.imagesText);
+ } else if (ct === 'backup') {
+ cta.push(PVE.Utils.backupFileText);
+ } else if (ct === 'vztmpl') {
+ cta.push(PVE.Utils.vztmplText);
+ } else if (ct === 'iso') {
+ cta.push(PVE.Utils.isoImageText);
+ } else if (ct === 'rootdir') {
+ cta.push(PVE.Utils.containersText);
+ }
+ });
+
+ return cta.join(', ');
+ },
+
+ render_storage_content: function(value, metaData, record) {
+ var data = record.data;
+ if (Ext.isNumber(data.channel) &&
+ Ext.isNumber(data.id) &&
+ Ext.isNumber(data.lun)) {
+ return "CH " +
+ Ext.String.leftPad(data.channel,2, '0') +
+ " ID " + data.id + " LUN " + data.lun;
+ }
+ return data.volid.replace(/^.*:(.*\/)?/,'');
+ },
+
+ render_serverity: function (value) {
+ return PVE.Utils.log_severity_hash[value] || value;
+ },
+
+ render_cpu: function(value, metaData, record, rowIndex, colIndex, store) {
+
+ if (!(record.data.uptime && Ext.isNumeric(value))) {
+ return '';
+ }
+
+ var maxcpu = record.data.maxcpu || 1;
+
+ if (!Ext.isNumeric(maxcpu) && (maxcpu >= 1)) {
+ return '';
+ }
+
+ var per = value * 100;
+
+ return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU');
+ },
+
+ render_size: function(value, metaData, record, rowIndex, colIndex, store) {
+ /*jslint confusion: true */
+
+ if (!Ext.isNumeric(value)) {
+ return '';
+ }
+
+ return PVE.Utils.format_size(value);
+ },
+
+ render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) {
+ var servertime = new Date(value * 1000);
+ return Ext.Date.format(servertime, 'Y-m-d H:i:s');
+ },
+
+ render_mem_usage: function(value, metaData, record, rowIndex, colIndex, store) {
+
+ var mem = value;
+ var maxmem = record.data.maxmem;
+
+ if (!record.data.uptime) {
+ return '';
+ }
+
+ if (!(Ext.isNumeric(mem) && maxmem)) {
+ return '';
+ }
+
+ var per = (mem * 100) / maxmem;
+
+ return per.toFixed(1) + '%';
+ },
+
+ render_disk_usage: function(value, metaData, record, rowIndex, colIndex, store) {
+
+ var disk = value;
+ var maxdisk = record.data.maxdisk;
+
+ if (!(Ext.isNumeric(disk) && maxdisk)) {
+ return '';
+ }
+
+ var per = (disk * 100) / maxdisk;
+
+ return per.toFixed(1) + '%';
+ },
+
+ render_resource_type: function(value, metaData, record, rowIndex, colIndex, store) {
+
+ var cls = 'pve-itype-icon-' + value;
+
+ if (record.data.running) {
+ metaData.tdCls = cls + "-running";
+ } else if (record.data.template) {
+ metaData.tdCls = cls + "-template";
+ } else {
+ metaData.tdCls = cls;
+ }
+
+ return value;
+ },
+
+ render_uptime: function(value, metaData, record, rowIndex, colIndex, store) {
+
+ var uptime = value;
+
+ if (uptime === undefined) {
+ return '';
+ }
+
+ if (uptime <= 0) {
+ return '-';
+ }
+
+ return PVE.Utils.format_duration_long(uptime);
+ },
+
+ render_support_level: function(value, metaData, record) {
+ return PVE.Utils.support_level_hash[value] || '-';
+ },
+
+ render_upid: function(value, metaData, record) {
+ var type = record.data.type;
+ var id = record.data.id;
+
+ return PVE.Utils.format_task_description(type, id);
+ },
+
+ dialog_title: function(subject, create, isAdd) {
+ if (create) {
+ if (isAdd) {
+ return gettext('Add') + ': ' + subject;
+ } else {
+ return gettext('Create') + ': ' + subject;
+ }
+ } else {
+ return gettext('Edit') + ': ' + subject;
+ }
+ },
+
+ windowHostname: function() {
+ return window.location.hostname.replace(IP6_bracket_match,
+ function(m, addr, offset, original) { return addr; });
+ },
+
+ openDefaultConsoleWindow: function(allowSpice, vmtype, vmid, nodename, vmname) {
+ var dv = PVE.Utils.defaultViewer(allowSpice);
+ PVE.Utils.openConsoleWindow(dv, vmtype, vmid, nodename, vmname);
+ },
+
+ openConsoleWindow: function(viewer, vmtype, vmid, nodename, vmname) {
+ // kvm, lxc, shell, upgrade
+
+ if (vmid == undefined && (vmtype === 'kvm' || vmtype === 'lxc')) {
+ throw "missing vmid";
+ }
+
+ if (!nodename) {
+ throw "no nodename specified";
+ }
+
+ if (viewer === 'applet' || viewer === 'html5') {
+ PVE.Utils.openVNCViewer(vmtype, vmid, nodename, vmname, viewer === 'html5');
+ } else if (viewer === 'vv') {
+ var url;
+ var params = { proxy: PVE.Utils.windowHostname() };
+ if (vmtype === 'kvm') {
+ url = '/nodes/' + nodename + '/qemu/' + vmid.toString() + '/spiceproxy';
+ PVE.Utils.openSpiceViewer(url, params);
+ } else if (vmtype === 'lxc') {
+ url = '/nodes/' + nodename + '/lxc/' + vmid.toString() + '/spiceproxy';
+ PVE.Utils.openSpiceViewer(url, params);
+ } else if (vmtype === 'shell') {
+ url = '/nodes/' + nodename + '/spiceshell';
+ PVE.Utils.openSpiceViewer(url, params);
+ } else if (vmtype === 'upgrade') {
+ url = '/nodes/' + nodename + '/spiceshell';
+ params.upgrade = 1;
+ PVE.Utils.openSpiceViewer(url, params);
+ }
+ } else {
+ throw "unknown viewer type";
+ }
+ },
+
+ defaultViewer: function(allowSpice) {
+ var vncdefault = 'html5';
+ var dv = PVE.VersionInfo.console || vncdefault;
+ if (dv === 'vv' && !allowSpice) {
+ dv = vncdefault;
+ }
+
+ return dv;
+ },
+
+ openVNCViewer: function(vmtype, vmid, nodename, vmname, novnc) {
+ var url = Ext.urlEncode({
+ console: vmtype, // kvm, lxc, upgrade or shell
+ novnc: novnc ? 1 : 0,
+ vmid: vmid,
+ vmname: vmname,
+ node: nodename
+ });
+ var nw = window.open("?" + url, '_blank', "innerWidth=745,innerheight=427");
+ nw.focus();
+ },
+
+ openSpiceViewer: function(url, params){
+
+ var downloadWithName = function(uri, name) {
+ var link = Ext.DomHelper.append(document.body, {
+ tag: 'a',
+ href: uri,
+ css : 'display:none;visibility:hidden;height:0px;'
+ });
+
+ // Note: we need to tell android the correct file name extension
+ // but we do not set 'download' tag for other environments, because
+ // It can have strange side effects (additional user prompt on firefox)
+ var andriod = navigator.userAgent.match(/Android/i) ? true : false;
+ if (andriod) {
+ link.download = name;
+ }
+
+ if (link.fireEvent) {
+ link.fireEvent('onclick');
+ } else {
+ var evt = document.createEvent("MouseEvents");
+ evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
+ link.dispatchEvent(evt);
+ }
+ };
+
+ PVE.Utils.API2Request({
+ url: url,
+ params: params,
+ method: 'POST',
+ failure: function(response, opts){
+ Ext.Msg.alert('Error', response.htmlStatus);
+ },
+ success: function(response, opts){
+ var raw = "[virt-viewer]\n";
+ Ext.Object.each(response.result.data, function(k, v) {
+ raw += k + "=" + v + "\n";
+ });
+ var url = 'data:application/x-virt-viewer;charset=UTF-8,' +
+ encodeURIComponent(raw);
+
+ downloadWithName(url, "pve-spice.vv");
+ }
+ });
+ },
+
+ // comp.setLoading() is buggy in ExtJS 4.0.7, so we
+ // use el.mask() instead
+ setErrorMask: function(comp, msg) {
+ var el = comp.el;
+ if (!el) {
+ return;
+ }
+ if (!msg) {
+ el.unmask();
+ } else {
+ if (msg === true) {
+ el.mask(gettext("Loading..."));
+ } else {
+ el.mask(msg);
+ }
+ }
+ },
+
+ monStoreErrors: function(me, store) {
+ me.mon(store, 'beforeload', function(s, operation, eOpts) {
+ if (!me.loadCount) {
+ me.loadCount = 0; // make sure it is numeric
+ PVE.Utils.setErrorMask(me, true);
+ }
+ });
+
+ // only works with 'pve' proxy
+ me.mon(store.proxy, 'afterload', function(proxy, request, success) {
+ me.loadCount++;
+
+ if (success) {
+ PVE.Utils.setErrorMask(me, false);
+ return;
+ }
+
+ var msg;
+ var operation = request.operation;
+ var error = operation.getError();
+ if (error.statusText) {
+ msg = error.statusText + ' (' + error.status + ')';
+ } else {
+ msg = gettext('Connection error');
+ }
+ PVE.Utils.setErrorMask(me, msg);
+ });
+ }
+
+}});
+
--- /dev/null
+Ext.define('PVE.noVncConsole', {
+ extend: 'Ext.panel.Panel',
+ alias: 'widget.pveNoVncConsole',
+
+ nodename: undefined,
+
+ vmid: undefined,
+
+ consoleType: undefined, // lxc or kvm
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ if (!me.consoleType) {
+ throw "no console type specified";
+ }
+
+ if (!me.vmid && me.consoleType !== 'shell') {
+ throw "no VM ID specified";
+ }
+
+ // always use same iframe, to avoid running several noVnc clients
+ // at same time (to avoid performance problems)
+ var box = Ext.create('widget.uxiframe', { id: "vncconsole" });
+
+ Ext.apply(me, {
+ layout: { type: 'fit' },
+ border: false,
+ items: box,
+ listeners: {
+ show: function() {
+ var url = '/?console=' + me.consoleType + '&novnc=1&node=' + me.nodename + '&resize=scale';
+ if (me.vmid) {
+ url += '&vmid='+ me.vmid;
+ }
+ box.load(url);
+ }
+ }
+ });
+
+ me.callParent();
+ }
+});
+
+PVE_vnc_console_event = function(appletid, action, err) {
+ //console.log("TESTINIT param1 " + appletid + " action " + action);
+
+ if (action === "error") {
+ var compid = appletid.replace("-vncapp", "");
+ var comp = Ext.getCmp(compid);
+
+ if (!comp || !comp.vmid || !comp.toplevel) {
+ return;
+ }
+
+ comp.detectMigratedVM();
+ }
+
+ return;
+ };
+
+Ext.define('PVE.VNCConsole', {
+ extend: 'Ext.panel.Panel',
+ alias: ['widget.pveVNCConsole'],
+
+ novnc: false,
+
+ last_novnc_state: undefined,
+ last_novnc_msg: undefined,
+
+ detectMigratedVM: function() {
+ var me = this;
+
+ if (!me.vmid) {
+ return;
+ }
+
+ // try to detect migrated VM
+ PVE.Utils.API2Request({
+ url: '/cluster/resources',
+ method: 'GET',
+ success: function(response) {
+ var list = response.result.data;
+ Ext.Array.each(list, function(item) {
+ if (item.type === 'qemu' && item.vmid == me.vmid) {
+ if (item.node !== me.nodename) {
+ me.nodename = item.node;
+ me.url = "/nodes/" + me.nodename + "/" + item.type + "/" + me.vmid + "/vncproxy";
+ me.wsurl = "/nodes/" + me.nodename + "/" + item.type + "/" + me.vmid + "/vncwebsocket";
+ me.reloadApplet();
+ }
+ return false; // break
+ }
+ });
+ }
+ });
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.url) {
+ throw "no url specified";
+ }
+
+ var myid = me.id + "-vncapp";
+
+ me.appletID = myid;
+
+ var box;
+
+ if (me.novnc) {
+ if (!me.wsurl) {
+ throw "no web socket url specified";
+ }
+ box = Ext.create('widget.uxiframe', { id: myid });
+ } else {
+ box = Ext.create('Ext.Component', { border: false, html: "" });
+ }
+
+ var resize_window = function() {
+ //console.log("resize");
+
+ var aw;
+ var ah;
+ var applet;
+
+ if (me.novnc) {
+ var novnciframe = box.getFrame();
+ // noVNC_canvas
+ var innerDoc = novnciframe.contentDocument || novnciframe.contentWindow.document;
+ aw = innerDoc.getElementById('noVNC_canvas').width;
+ ah = innerDoc.getElementById('noVNC_canvas').height + 8;
+
+ var novnc_state = innerDoc.getElementById('noVNC_status_state').innerHTML;
+ var novnc_msg = innerDoc.getElementById('noVNC_status_msg').innerHTML;
+
+ if (novnc_state !== me.last_novnc_state || novnc_msg !== me.last_novnc_msg) {
+ me.last_novnc_state = novnc_state;
+ me.last_novnc_msg = novnc_msg;
+
+ if (novnc_state !== 'normal') {
+ PVE.Utils.setErrorMask(box, novnc_msg || 'unknown');
+ } else {
+ PVE.Utils.setErrorMask(box); // clear mask
+ }
+
+ if (novnc_state === 'disconnected') {
+ me.detectMigratedVM();
+ }
+ }
+
+ } else {
+ applet = Ext.getDom(myid);
+
+ // try again when dom element is available
+ if (!(applet && Ext.isFunction(applet.getPreferredSize))) {
+ return Ext.Function.defer(resize_window, 1000);
+ }
+
+ var ps = applet.getPreferredSize();
+ aw = ps.width;
+ ah = ps.height;
+ }
+
+ if (aw < 640) { aw = 640; }
+ if (ah < 400) { ah = 400; }
+
+ var tbar = me.getDockedItems("[dock=top]")[0];
+ var tbh = tbar ? tbar.getHeight() : 0;
+
+ var oh;
+ var ow;
+
+ //console.log("size0 " + aw + " " + ah + " tbh " + tbh);
+
+ if (window.innerHeight) {
+ oh = window.innerHeight;
+ ow = window.innerWidth;
+ } else if (document.documentElement &&
+ document.documentElement.clientHeight) {
+ oh = document.documentElement.clientHeight;
+ ow = document.documentElement.clientWidth;
+ } else if (document.body) {
+ oh = document.body.clientHeight;
+ ow = document.body.clientWidth;
+ } else {
+ throw "can't get window size";
+ }
+
+ if (!me.novnc) {
+ Ext.fly(applet).setSize(aw, ah + tbh);
+ }
+
+ var offsetw = aw - ow;
+ var offseth = ah + tbh - oh;
+
+ if (offsetw !== 0 || offseth !== 0) {
+ //console.log("try resize by " + offsetw + " " + offseth);
+ try { window.resizeBy(offsetw, offseth); } catch (e) {}
+ }
+
+ Ext.Function.defer(resize_window, 1000);
+ };
+
+ var resize_box = function() {
+ if (me.novnc) {
+ throw "implement me";
+ } else {
+ var applet = Ext.getDom(myid);
+
+ if ((applet && Ext.isFunction(applet.getPreferredSize))) {
+ var ps = applet.getPreferredSize();
+ Ext.fly(applet).setSize(ps.width, ps.height);
+ }
+ }
+
+ Ext.Function.defer(resize_box, 1000);
+ };
+
+ var start_vnc_viewer = function(param) {
+
+ if (me.novnc) {
+
+ var pveparams = Ext.urlEncode({
+ port: param.port,
+ vncticket: param.ticket
+ });
+
+ var urlparams = Ext.urlEncode({
+ encrypt: 1,
+ path: "api2/json" + me.wsurl + "?" + pveparams,
+ password: param.ticket
+ });
+ box.load('/novnc/vnc_pve.html?' + urlparams);
+
+ } else {
+
+ var cert = param.cert;
+ cert = cert.replace(/\n/g, "|");
+
+ box.update({
+ id: myid,
+ border: false,
+ tag: 'applet',
+ code: 'com.tigervnc.vncviewer.VncViewer',
+ archive: '/vncterm/VncViewer.jar',
+ // NOTE: set size to '100%' - else resize does not work
+ width: "100%",
+ height: "100%",
+ cn: [
+ {tag: 'param', name: 'id', value: myid},
+ {tag: 'param', name: 'PORT', value: param.port},
+ {tag: 'param', name: 'PASSWORD', value: param.ticket},
+ {tag: 'param', name: 'USERNAME', value: param.user},
+ {tag: 'param', name: 'Show Controls', value: 'No'},
+ {tag: 'param', name: 'Offer Relogin', value: 'No'},
+ {tag: 'param', name: 'PVECert', value: cert}
+ ]
+ });
+ }
+
+ if (me.toplevel) {
+ Ext.Function.defer(resize_window, 1000);
+ } else {
+ Ext.Function.defer(resize_box, 1000);
+ }
+ };
+
+ Ext.apply(me, {
+ layout: 'fit',
+ border: false,
+ autoScroll: me.toplevel ? false : true,
+ items: box,
+ reloadApplet: function() {
+ var params = Ext.apply({}, me.params);
+ if (me.novnc) {
+ params.websocket = 1;
+ }
+ PVE.Utils.API2Request({
+ url: me.url,
+ params: params,
+ method: me.method || 'POST',
+ failure: function(response, opts) {
+ box.update(gettext('Error') + ' ' + response.htmlStatus);
+ },
+ success: function(response, opts) {
+ start_vnc_viewer(response.result.data);
+ }
+ });
+ }
+ });
+
+ me.callParent();
+
+ if (me.toplevel) {
+ me.on("render", me.reloadApplet);
+ } else {
+ me.on("show", me.reloadApplet);
+ me.on("hide", function() { box.update(""); });
+ }
+ }
+});
+
+Ext.define('PVE.KVMConsole', {
+ extend: 'PVE.VNCConsole',
+ alias: ['widget.pveKVMConsole'],
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ if (!me.vmid) {
+ throw "no VM ID specified";
+ }
+
+ var baseUrl = "/nodes/" + me.nodename + "/qemu/" + me.vmid;
+
+ var vm_command = function(cmd, params, reload_applet) {
+ PVE.Utils.API2Request({
+ params: params,
+ url: baseUrl + "/status/" + cmd,
+ method: 'POST',
+ waitMsgTarget: me,
+ failure: function(response, opts) {
+ Ext.Msg.alert('Error', response.htmlStatus);
+ },
+ success: function() {
+ if (reload_applet) {
+ Ext.Function.defer(me.reloadApplet, 1000, me);
+ }
+ }
+ });
+ };
+
+ var tbar = [
+ {
+ text: gettext('Start'),
+ handler: function() {
+ vm_command("start", {}, 1);
+ }
+ },
+ {
+ text: gettext('Shutdown'),
+ handler: function() {
+ var msg = Ext.String.format(gettext("Do you really want to shutdown VM {0}?"), me.vmid);
+ Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
+ if (btn !== 'yes') {
+ return;
+ }
+ vm_command('shutdown');
+ });
+ }
+ },
+ {
+ text: gettext('Stop'),
+ handler: function() {
+ var msg = Ext.String.format(gettext("Do you really want to stop VM {0}?"), me.vmid);
+ Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
+ if (btn !== 'yes') {
+ return;
+ }
+ vm_command("stop");
+ });
+ }
+ },
+ {
+ xtype: 'pveQemuSendKeyMenu',
+ nodename: me.nodename,
+ vmid: me.vmid
+ },
+ {
+ text: gettext('Reset'),
+ handler: function() {
+ var msg = Ext.String.format(gettext("Do you really want to reset VM {0}?"), me.vmid);
+ Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
+ if (btn !== 'yes') {
+ return;
+ }
+ vm_command("reset");
+ });
+ }
+ },
+ {
+ text: gettext('Suspend'),
+ handler: function() {
+ var msg = Ext.String.format(gettext("Do you really want to suspend VM {0}?"), me.vmid);
+ Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
+ if (btn !== 'yes') {
+ return;
+ }
+ vm_command("suspend");
+ });
+ }
+ },
+ {
+ text: gettext('Resume'),
+ handler: function() {
+ vm_command("resume");
+ }
+ },
+ // Note: no migrate here, because we can't display migrate log
+ {
+ text: gettext('Console'),
+ handler: function() {
+ PVE.Utils.openVNCViewer('kvm', me.vmid, me.nodename, me.vmname, me.novnc);
+ }
+ },
+ '->',
+ {
+ text: gettext('Refresh'),
+ hidden: me.novnc ? true : false,
+ handler: function() {
+ var applet = Ext.getDom(me.appletID);
+ applet.sendRefreshRequest();
+ }
+ },
+ {
+ text: gettext('Reload'),
+ handler: function () {
+ me.reloadApplet();
+ }
+ }
+ ];
+
+
+ Ext.apply(me, {
+ tbar: tbar,
+ url: baseUrl + "/vncproxy",
+ wsurl: baseUrl + "/vncwebsocket"
+ });
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.LxcConsole', {
+ extend: 'PVE.VNCConsole',
+ alias: ['widget.pveLxcConsole'],
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ if (!me.vmid) {
+ throw "no VM ID specified";
+ }
+
+ var baseUrl = "/nodes/" + me.nodename + "/lxc/" + me.vmid;
+
+ var vm_command = function(cmd, params, reload_applet) {
+ PVE.Utils.API2Request({
+ params: params,
+ url: baseUrl + "/status/" + cmd,
+ waitMsgTarget: me,
+ method: 'POST',
+ failure: function(response, opts) {
+ Ext.Msg.alert('Error', response.htmlStatus);
+ },
+ success: function() {
+ if (reload_applet) {
+ Ext.Function.defer(me.reloadApplet, 1000, me);
+ }
+ }
+ });
+ };
+
+ var tbar = [
+ {
+ text: gettext('Start'),
+ handler: function() {
+ vm_command("start");
+ }
+ },
+ {
+ text: gettext('Shutdown'),
+ handler: function() {
+ var msg = Ext.String.format(gettext("Do you really want to shutdown VM {0}?"), me.vmid);
+ Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
+ if (btn !== 'yes') {
+ return;
+ }
+ vm_command("shutdown");
+ });
+ }
+ },
+ {
+ text: gettext('Stop'),
+ handler: function() {
+ var msg = Ext.String.format(gettext("Do you really want to stop VM {0}?"), me.vmid);
+ Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
+ if (btn !== 'yes') {
+ return;
+ }
+ vm_command("stop");
+ });
+ }
+ },
+ // Note: no migrate here, because we can't display migrate log
+ '->',
+ {
+ text: gettext('Refresh'),
+ hidden: me.novnc ? true : false,
+ handler: function() {
+ var applet = Ext.getDom(me.appletID);
+ applet.sendRefreshRequest();
+ }
+ },
+ {
+ text: gettext('Reload'),
+ handler: function () {
+ me.reloadApplet();
+ }
+ }
+ ];
+
+ Ext.apply(me, {
+ tbar: tbar,
+ url: baseUrl + "/vncproxy",
+ wsurl: baseUrl + "/vncwebsocket"
+ });
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.Shell', {
+ extend: 'PVE.VNCConsole',
+ alias: ['widget.pveShell'],
+
+ ugradeSystem: false, // set to true to run "apt-get dist-upgrade"
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ var tbar = [ '->' ];
+
+ if (!me.novnc) {
+ tbar.push({
+ text: gettext('Refresh'),
+ handler: function() {
+ var applet = Ext.getDom(me.appletID);
+ applet.sendRefreshRequest();
+ }
+ });
+ }
+
+ if (!me.ugradeSystem) {
+ // we dont want to restart the upgrade script
+ tbar.push({
+ text: gettext('Reload'),
+ handler: function () { me.reloadApplet(); }
+ });
+ }
+
+ tbar.push({
+ text: gettext('Shell'),
+ handler: function() {
+ PVE.Utils.openVNCViewer('shell', undefined, me.nodename, undefined, me.novnc);
+ }
+ });
+
+ var baseUrl = "/nodes/" + me.nodename;
+
+ Ext.apply(me, {
+ tbar: tbar,
+ url: baseUrl + "/vncshell",
+ wsurl: baseUrl + "/vncwebsocket"
+ });
+
+ if (me.ugradeSystem) {
+ me.params = { upgrade: 1 };
+ }
+
+ me.callParent();
+ }
+});
--- /dev/null
+/*
+ * Workspace base class
+ *
+ * popup login window when auth fails (call onLogin handler)
+ * update (re-login) ticket every 15 minutes
+ *
+ */
+
+Ext.define('PVE.Workspace', {
+ extend: 'Ext.container.Viewport',
+
+ title: 'Proxmox Virtual Environment',
+
+ loginData: null, // Data from last login call
+
+ onLogin: function(loginData) {},
+
+ // private
+ updateLoginData: function(loginData) {
+ var me = this;
+ me.loginData = loginData;
+ PVE.CSRFPreventionToken = loginData.CSRFPreventionToken;
+ PVE.UserName = loginData.username;
+
+ if (loginData.cap) {
+ Ext.state.Manager.set('GuiCap', loginData.cap);
+ }
+
+ // creates a session cookie (expire = null)
+ // that way the cookie gets deleted after browser window close
+ Ext.util.Cookies.set('PVEAuthCookie', loginData.ticket, null, '/', null, true);
+ me.onLogin(loginData);
+ },
+
+ // private
+ showLogin: function() {
+ var me = this;
+
+ PVE.Utils.authClear();
+ PVE.UserName = null;
+ me.loginData = null;
+
+ if (!me.login) {
+ me.login = Ext.create('PVE.window.LoginWindow', {
+ handler: function(data) {
+ me.login = null;
+ me.updateLoginData(data);
+ PVE.Utils.checked_command(function() {}); // display subscription status
+ }
+ });
+ }
+ me.onLogin(null);
+ me.login.show();
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ Ext.tip.QuickTipManager.init();
+
+ // fixme: what about other errors
+ Ext.Ajax.on('requestexception', function(conn, response, options) {
+ if (response.status == 401) { // auth failure
+ me.showLogin();
+ }
+ });
+
+ document.title = me.title;
+
+ me.callParent();
+
+ if (!PVE.Utils.authOK()) {
+ me.showLogin();
+ } else {
+ if (me.loginData) {
+ me.onLogin(me.loginData);
+ }
+ }
+
+ Ext.TaskManager.start({
+ run: function() {
+ var ticket = PVE.Utils.authOK();
+ if (!ticket || !PVE.UserName) {
+ return;
+ }
+
+ Ext.Ajax.request({
+ params: {
+ username: PVE.UserName,
+ password: ticket
+ },
+ url: '/api2/json/access/ticket',
+ method: 'POST',
+ success: function(response, opts) {
+ var obj = Ext.decode(response.responseText);
+ me.updateLoginData(obj.data);
+ }
+ });
+ },
+ interval: 15*60*1000
+ });
+
+ }
+});
+
+Ext.define('PVE.ConsoleWorkspace', {
+ extend: 'PVE.Workspace',
+
+ alias: ['widget.pveConsoleWorkspace'],
+
+ title: gettext('Console'),
+
+ initComponent : function() {
+ var me = this;
+
+ var param = Ext.Object.fromQueryString(window.location.search);
+ var consoleType = me.consoleType || param.console;
+
+ param.novnc = (param.novnc === '1') ? true : false;
+
+ var content;
+ if (consoleType === 'kvm') {
+ me.title = "VM " + param.vmid;
+ if (param.vmname) {
+ me.title += " ('" + param.vmname + "')";
+ }
+ content = {
+ xtype: 'pveKVMConsole',
+ novnc: param.novnc,
+ vmid: param.vmid,
+ nodename: param.node,
+ vmname: param.vmname,
+ toplevel: true
+ };
+ } else if (consoleType === 'lxc') {
+ me.title = "CT " + param.vmid;
+ if (param.vmname) {
+ me.title += " ('" + param.vmname + "')";
+ }
+ content = {
+ xtype: 'pveLxcConsole',
+ novnc: param.novnc,
+ vmid: param.vmid,
+ nodename: param.node,
+ vmname: param.vmname,
+ toplevel: true
+ };
+ } else if (consoleType === 'shell') {
+ me.title = "node '" + param.node + "'";
+ content = {
+ xtype: 'pveShell',
+ novnc: param.novnc,
+ nodename: param.node,
+ toplevel: true
+ };
+ } else if (consoleType === 'upgrade') {
+ me.title = Ext.String.format(gettext('System upgrade on node {0}'), "'" + param.node + "'");
+ content = {
+ xtype: 'pveShell',
+ novnc: param.novnc,
+ nodename: param.node,
+ ugradeSystem: true,
+ toplevel: true
+ };
+ } else {
+ content = {
+ border: false,
+ bodyPadding: 10,
+ html: gettext('Error') + ': No such console type'
+ };
+ }
+
+ Ext.apply(me, {
+ layout: { type: 'fit' },
+ border: false,
+ items: [ content ]
+ });
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.StdWorkspace', {
+ extend: 'PVE.Workspace',
+
+ alias: ['widget.pveStdWorkspace'],
+
+ // private
+ setContent: function(comp) {
+ var me = this;
+
+ var cont = me.child('#content');
+ cont.removeAll(true);
+
+ if (comp) {
+ PVE.Utils.setErrorMask(cont, false);
+ comp.border = false;
+ cont.add(comp);
+ cont.doLayout();
+ }
+ // else {
+ // TODO: display something useful
+
+ // Note:: error mask has wrong zindex, so we do not
+ // use that - see bug 114
+ // PVE.Utils.setErrorMask(cont, 'nothing selected');
+ //}
+ },
+
+ selectById: function(nodeid) {
+ var me = this;
+ var tree = me.down('pveResourceTree');
+ tree.selectById(nodeid);
+ },
+
+ checkVmMigration: function(record) {
+ var me = this;
+ var tree = me.down('pveResourceTree');
+ tree.checkVmMigration(record);
+ },
+
+ onLogin: function(loginData) {
+ var me = this;
+
+ me.updateUserInfo();
+
+ if (loginData) {
+ PVE.data.ResourceStore.startUpdate();
+
+ PVE.Utils.API2Request({
+ url: '/version',
+ method: 'GET',
+ success: function(response) {
+ PVE.VersionInfo = response.result.data;
+ me.updateVersionInfo();
+ }
+ });
+ }
+ },
+
+ updateUserInfo: function() {
+ var me = this;
+
+ var ui = me.query('#userinfo')[0];
+
+ if (PVE.UserName) {
+ var msg = Ext.String.format(gettext("You are logged in as {0}"), "'" + PVE.UserName + "'");
+ ui.update('<div class="x-unselectable" style="white-space:nowrap;">' + msg + '</div>');
+ } else {
+ ui.update('');
+ }
+ ui.doLayout();
+ },
+
+ updateVersionInfo: function() {
+ var me = this;
+
+ var ui = me.query('#versioninfo')[0];
+
+ if (PVE.VersionInfo) {
+ var version = PVE.VersionInfo.version + '-' + PVE.VersionInfo.release + '/' +
+ PVE.VersionInfo.repoid;
+ ui.update('Proxmox Virtual Environment<br>' + gettext('Version') + ': ' + version);
+ } else {
+ ui.update('Proxmox Virtual Environment');
+ }
+ ui.doLayout();
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ Ext.History.init();
+
+ var sprovider = Ext.create('PVE.StateProvider');
+ Ext.state.Manager.setProvider(sprovider);
+
+ var selview = new PVE.form.ViewSelector({});
+
+ var rtree = Ext.createWidget('pveResourceTree', {
+ viewFilter: selview.getViewFilter(),
+ flex: 1,
+ selModel: new Ext.selection.TreeModel({
+ listeners: {
+ selectionchange: function(sm, selected) {
+ var comp;
+ var tlckup = {
+ root: 'PVE.dc.Config',
+ node: 'PVE.node.Config',
+ qemu: 'PVE.qemu.Config',
+ lxc: 'PVE.lxc.Config',
+ storage: 'PVE.storage.Browser',
+ pool: 'pvePoolConfig'
+ };
+
+ if (selected.length > 0) {
+ var n = selected[0];
+ comp = {
+ xtype: tlckup[n.data.type || 'root'] ||
+ 'pvePanelConfig',
+ layout: { type: 'fit' },
+ showSearch: (n.data.id === 'root') ||
+ Ext.isDefined(n.data.groupbyid),
+ pveSelNode: n,
+ workspace: me,
+ viewFilter: selview.getViewFilter()
+ };
+ PVE.curSelectedNode = n;
+ }
+
+ me.setContent(comp);
+ }
+ }
+ })
+ });
+
+ selview.on('select', function(combo, records) {
+ if (records && records.length) {
+ var view = combo.getViewFilter();
+ rtree.setViewFilter(view);
+ }
+ });
+
+ var caps = sprovider.get('GuiCap');
+
+ var createVM = Ext.createWidget('button', {
+ pack: 'end',
+ margin: '3 5 0 0',
+ baseCls: 'x-btn',
+ text: gettext("Create VM"),
+ disabled: !caps.vms['VM.Allocate'],
+ handler: function() {
+ var wiz = Ext.create('PVE.qemu.CreateWizard', {});
+ wiz.show();
+ }
+ });
+
+ var createCT = Ext.createWidget('button', {
+ pack: 'end',
+ margin: '3 5 0 0',
+ baseCls: 'x-btn',
+ text: gettext("Create CT"),
+ disabled: !caps.vms['VM.Allocate'],
+ handler: function() {
+ var wiz = Ext.create('PVE.lxc.CreateWizard', {});
+ wiz.show();
+ }
+ });
+
+ sprovider.on('statechange', function(sp, key, value) {
+ if (key === 'GuiCap' && value) {
+ caps = value;
+ createVM.setDisabled(!caps.vms['VM.Allocate']);
+ createCT.setDisabled(!caps.vms['VM.Allocate']);
+ }
+ });
+
+ Ext.apply(me, {
+ layout: { type: 'border' },
+ border: false,
+ items: [
+ {
+ region: 'north',
+ height: 30,
+ layout: {
+ type: 'hbox',
+ },
+ baseCls: 'x-plain',
+ defaults: {
+ baseCls: 'x-plain'
+ },
+ border: false,
+ margin: '2 0 5 0',
+ items: [
+ {
+ margin: '0 0 0 4',
+ html: '<a class="x-unselectable" target=_blank href="http://www.proxmox.com">' +
+ '<img height=30 width=209 src="/pve2/images/proxmox_logo.png"/></a>'
+ },
+ {
+ minWidth: 200,
+ flex: 1,
+ id: 'versioninfo',
+ html: 'Proxmox Virtual Environment',
+ height: 30
+ },
+ {
+ pack: 'end',
+ margin: '8 10 0 10',
+ id: 'userinfo',
+ stateful: false
+ },
+ {
+ pack: 'end',
+ margin: '3 5 0 0',
+ xtype: 'button',
+ baseCls: 'x-btn',
+ text: gettext("Logout"),
+ handler: function() {
+ PVE.data.ResourceStore.stopUpdate();
+ me.showLogin();
+ me.setContent();
+ var rt = me.down('pveResourceTree');
+ rt.clearTree();
+ }
+ },
+ createVM,
+ createCT
+ ]
+ },
+ {
+ region: 'center',
+ id: 'content',
+ xtype: 'container',
+ layout: { type: 'fit' },
+ border: false,
+ stateful: false,
+ margin: '0 5 0 0',
+ items: []
+ },
+ {
+ region: 'west',
+ xtype: 'container',
+ border: false,
+ layout: { type: 'vbox', align: 'stretch' },
+ margin: '0 0 0 5',
+ split: true,
+ width: 200,
+ items: [ selview, rtree ]
+ },
+ {
+ xtype: 'pveStatusPanel',
+ region: 'south',
+ margin:'0 5 5 5',
+ height: 200,
+ split:true
+ }
+ ]
+ });
+
+ me.callParent();
+
+ me.updateUserInfo();
+ }
+});
+
--- /dev/null
+/* Button features:
+ * - observe selection changes to enable/disable the button using enableFn()
+ * - pop up confirmation dialog using confirmMsg()
+ */
+Ext.define('PVE.button.Button', {
+ extend: 'Ext.button.Button',
+ alias: 'widget.pveButton',
+
+ // the selection model to observe
+ selModel: undefined,
+
+ // if 'false' handler will not be called (button disabled)
+ enableFn: function(record) { },
+
+ // function(record) or text
+ confirmMsg: false,
+
+ // take special care in confirm box (select no as default).
+ dangerous: false,
+
+ initComponent: function() {
+ /*jslint confusion: true */
+
+ var me = this;
+
+ if (me.handler) {
+ me.realHandler = me.handler;
+
+ me.handler = function(button, event) {
+ var rec, msg;
+ if (me.selModel) {
+ rec = me.selModel.getSelection()[0];
+ if (!rec || (me.enableFn(rec) === false)) {
+ return;
+ }
+ }
+
+ if (me.confirmMsg) {
+ msg = me.confirmMsg;
+ if (Ext.isFunction(me.confirmMsg)) {
+ msg = me.confirmMsg(rec);
+ }
+ Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1;
+ Ext.Msg.show({
+ title: gettext('Confirm'),
+ icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION,
+ msg: msg,
+ buttons: Ext.Msg.YESNO,
+ callback: function(btn) {
+ if (btn !== 'yes') {
+ return;
+ }
+ me.realHandler(button, event, rec);
+ }
+ });
+ } else {
+ me.realHandler(button, event, rec);
+ }
+ };
+ }
+
+ me.callParent();
+
+ if (me.selModel) {
+
+ me.mon(me.selModel, "selectionchange", function() {
+ var rec = me.selModel.getSelection()[0];
+ if (!rec || (me.enableFn(rec) === false)) {
+ me.setDisabled(true);
+ } else {
+ me.setDisabled(false);
+ }
+ });
+ }
+ }
+});
--- /dev/null
+Ext.define('PVE.button.ConsoleButton', {
+ extend: 'Ext.button.Split',
+ alias: 'widget.pveConsoleButton',
+
+ consoleType: 'shell', // one of 'shell', 'kvm', 'lxc', 'upgrade'
+
+ consoleName: undefined,
+
+ enableSpice: true,
+
+ nodename: undefined,
+
+ vmid: 0,
+
+ setEnableSpice: function(enable){
+ var me = this;
+
+ me.enableSpice = enable;
+ me.spiceMenu.setDisabled(!enable);
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ me.spiceMenu = Ext.create('Ext.menu.Item', {
+ text: 'SPICE',
+ iconCls: 'pve-itype-icon-virt-viewer',
+ handler: function() {
+ PVE.Utils.openConsoleWindow('vv', me.consoleType, me.vmid, me.nodename, me.consoleName);
+ }
+ });
+
+ var noVncMenu = Ext.create('Ext.menu.Item', {
+ text: 'noVNC',
+ iconCls: 'pve-itype-icon-novnc',
+ handler: function() {
+ PVE.Utils.openConsoleWindow('html5', me.consoleType, me.vmid, me.nodename, me.consoleName);
+ }
+ });
+
+ Ext.applyIf(me, { text: gettext('Console') });
+
+ Ext.apply(me, {
+ handler: function() {
+ PVE.Utils.openDefaultConsoleWindow(me.enableSpice, me.consoleType, me.vmid,
+ me.nodename, me.consoleName);
+ },
+ menu: new Ext.menu.Menu({
+ items: [ noVncMenu, me.spiceMenu ]
+ })
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+/*
+ * The DiffStore acts as proxy between an UpdateStore instance and a component.
+ * Its purpose is to redisplay the component *only* if the data has been changed
+ * inside the UpdateStore, to avoid the annoying visual flickering of using
+ * the UpdateStore directly.
+ *
+ * Implementation:
+ * The DiffStore monitors via mon() the 'load' events sent by the target store.
+ * On each 'load' event, the DiffStore compares its own content with the target
+ * store (call to cond_add_item()) and then fires a 'refresh' event.
+ * The 'refresh' event will automatically trigger a view refresh on the component
+ * who binds to this store.
+ */
+
+/* Config properties:
+ * rstore: A target store to track changes
+ * Only works if rstore has a model and use 'idProperty'
+ */
+Ext.define('PVE.data.DiffStore', {
+ extend: 'Ext.data.Store',
+
+ sortAfterUpdate: false,
+
+ constructor: function(config) {
+ var me = this;
+
+ config = config || {};
+
+ if (!config.rstore) {
+ throw "no rstore specified";
+ }
+
+ if (!config.rstore.model) {
+ throw "no rstore model specified";
+ }
+
+ var rstore = config.rstore;
+
+ Ext.apply(config, {
+ model: rstore.model,
+ proxy: { type: 'memory' }
+ });
+
+ me.callParent([config]);
+
+ var first_load = true;
+
+ var cond_add_item = function(data, id) {
+ var olditem = me.getById(id);
+ if (olditem) {
+ olditem.beginEdit();
+ Ext.Array.each(me.model.prototype.fields, function(field) {
+ if (olditem.data[field.name] !== data[field.name]) {
+ olditem.set(field.name, data[field.name]);
+ }
+ });
+ olditem.endEdit(true);
+ olditem.commit();
+ } else {
+ var newrec = Ext.create(me.model, data);
+ var pos = (me.appendAtStart && !first_load) ? 0 : me.data.length;
+ me.insert(pos, newrec);
+ }
+ };
+
+ me.mon(rstore, 'load', function(s, records, success) {
+
+ if (!success) {
+ return;
+ }
+
+ me.suspendEvents();
+
+ // remove vanished items
+ (me.snapshot || me.data).each(function(olditem) {
+ var item = rstore.getById(olditem.getId());
+ if (!item) {
+ me.remove(olditem);
+ }
+ });
+
+ rstore.each(function(item) {
+ cond_add_item(item.data, item.getId());
+ });
+
+ me.filter();
+
+ if (me.sortAfterUpdate) {
+ me.sort();
+ }
+
+ first_load = false;
+
+ me.resumeEvents();
+ me.fireEvent('refresh', me);
+ });
+ }
+});
--- /dev/null
+/* This store encapsulates data items which are organized as an Array of key-values Objects
+ * ie data[0] contains something like {key: "keyboard", value: "da"}
+*
+* Designed to work with the KeyValue model and the JsonObject data reader
+*/
+Ext.define('PVE.data.ObjectStore', {
+ extend: 'PVE.data.UpdateStore',
+
+ constructor: function(config) {
+ var me = this;
+
+ config = config || {};
+
+ if (!config.storeid) {
+ config.storeid = 'pve-store-' + (++Ext.idSeed);
+ }
+
+ Ext.applyIf(config, {
+ model: 'KeyValue',
+ proxy: {
+ type: 'pve',
+ url: config.url,
+ extraParams: config.extraParams,
+ reader: {
+ type: 'jsonobject',
+ rows: config.rows,
+ readArray: config.readArray,
+ rootProperty: config.root || 'data'
+ }
+ }
+ });
+
+ me.callParent([config]);
+ }
+});
--- /dev/null
+Ext.define('PVE.RestProxy', {
+ extend: 'Ext.data.RestProxy',
+ alias : 'proxy.pve',
+
+ pageParam : null,
+ startParam: null,
+ limitParam: null,
+ groupParam: null,
+ sortParam: null,
+ filterParam: null,
+ noCache : false,
+ afterRequest: function(request, success) {
+ this.fireEvent('afterload', this, request, success);
+ return;
+ },
+
+ constructor: function(config) {
+
+ Ext.applyIf(config, {
+ reader: {
+ type: 'json',
+ rootProperty: config.root || 'data'
+ },
+ });
+
+ this.callParent([config]);
+ }
+
+}, function() {
+
+ Ext.define('pve-domains', {
+ extend: "Ext.data.Model",
+ fields: [ 'realm', 'type', 'comment', 'default', 'tfa',
+ {
+ name: 'descr',
+ // Note: We use this in the RealmComboBox.js
+ // (see Bug #125)
+ convert: function(value, record) {
+ var info = record.data;
+ var text;
+
+ if (value) {
+ return value;
+ }
+ // return realm if there is no comment
+ text = info.comment || info.realm;
+
+ if (info.tfa) {
+ text += " (+ " + info.tfa + ")";
+ }
+
+ return text;
+ }
+ }
+ ],
+ proxy: {
+ type: 'pve',
+ url: "/api2/json/access/domains"
+ }
+ });
+
+ Ext.define('KeyValue', {
+ extend: "Ext.data.Model",
+ fields: [ 'key', 'value' ]
+ });
+
+ Ext.define('KeyValuePendingDelete', {
+ extend: "Ext.data.Model",
+ fields: [ 'key', 'value', 'pending', 'delete' ],
+ idProperty: 'key'
+ });
+
+ Ext.define('pve-string-list', {
+ extend: 'Ext.data.Model',
+ fields: [ 'n', 't' ],
+ idProperty: 'n'
+ });
+
+ Ext.define('pve-tasks', {
+ extend: 'Ext.data.Model',
+ fields: [
+ { name: 'starttime', type : 'date', dateFormat: 'timestamp' },
+ { name: 'endtime', type : 'date', dateFormat: 'timestamp' },
+ { name: 'pid', type: 'int' },
+ 'node', 'upid', 'user', 'status', 'type', 'id'
+ ],
+ idProperty: 'upid'
+ });
+
+ Ext.define('pve-cluster-log', {
+ extend: 'Ext.data.Model',
+ fields: [
+ { name: 'uid' , type: 'int' },
+ { name: 'time', type : 'date', dateFormat: 'timestamp' },
+ { name: 'pri', type: 'int' },
+ { name: 'pid', type: 'int' },
+ 'node', 'user', 'tag', 'msg',
+ {
+ name: 'id',
+ convert: function(value, record) {
+ var info = record.data;
+ var text;
+
+ if (value) {
+ return value;
+ }
+ // compute unique ID
+ return info.uid + ':' + info.node;
+ }
+ }
+ ],
+ idProperty: 'id'
+ });
+});
--- /dev/null
+Ext.define('PVE.data.ResourceStore', {
+ extend: 'PVE.data.UpdateStore',
+ singleton: true,
+
+ findVMID: function(vmid) {
+ var me = this, i;
+
+ return (me.findExact('vmid', parseInt(vmid, 10)) >= 0);
+ },
+
+ constructor: function(config) {
+ // fixme: how to avoid those warnings
+ /*jslint confusion: true */
+
+ var me = this;
+
+ config = config || {};
+
+ var field_defaults = {
+ type: {
+ header: gettext('Type'),
+ type: 'string',
+ renderer: PVE.Utils.render_resource_type,
+ sortable: true,
+ hideable: false,
+ width: 80
+ },
+ id: {
+ header: 'ID',
+ type: 'string',
+ hidden: true,
+ sortable: true,
+ width: 80
+ },
+ running: {
+ header: gettext('Online'),
+ type: 'boolean',
+ renderer: PVE.Utils.format_boolean,
+ hidden: true,
+ convert: function(value, record) {
+ var info = record.data;
+ if (info.type === 'qemu' || info.type === 'lxc' || info.type === 'node') {
+ return (Ext.isNumeric(info.uptime) && (info.uptime > 0));
+ } else {
+ return false;
+ }
+ }
+ },
+ text: {
+ header: gettext('Description'),
+ type: 'string',
+ sortable: true,
+ width: 200,
+ convert: function(value, record) {
+ var info = record.data;
+ var text;
+
+ if (value) {
+ return value;
+ }
+
+ if (info.type === 'node') {
+ text = info.node;
+ } else if (info.type === 'pool') {
+ text = info.pool;
+ } else if (info.type === 'storage') {
+ text = info.storage + ' (' + info.node + ')';
+ } else if (info.type === 'qemu' || info.type === 'lxc') {
+ text = String(info.vmid);
+ if (info.name) {
+ text += " (" + info.name + ')';
+ }
+ } else {
+ text = info.id;
+ }
+ return text;
+ }
+ },
+ vmid: {
+ header: 'VMID',
+ type: 'integer',
+ hidden: true,
+ sortable: true,
+ width: 80
+ },
+ name: {
+ header: gettext('Name'),
+ hidden: true,
+ sortable: true,
+ type: 'string'
+ },
+ disk: {
+ header: gettext('Disk usage'),
+ type: 'integer',
+ renderer: PVE.Utils.render_disk_usage,
+ sortable: true,
+ width: 100
+ },
+ maxdisk: {
+ header: gettext('Disk size'),
+ type: 'integer',
+ renderer: PVE.Utils.render_size,
+ sortable: true,
+ hidden: true,
+ width: 100
+ },
+ mem: {
+ header: gettext('Memory usage'),
+ type: 'integer',
+ renderer: PVE.Utils.render_mem_usage,
+ sortable: true,
+ width: 100
+ },
+ maxmem: {
+ header: gettext('Memory size'),
+ type: 'integer',
+ renderer: PVE.Utils.render_size,
+ hidden: true,
+ sortable: true,
+ width: 100
+ },
+ cpu: {
+ header: gettext('CPU usage'),
+ type: 'float',
+ renderer: PVE.Utils.render_cpu,
+ sortable: true,
+ width: 100
+ },
+ maxcpu: {
+ header: gettext('maxcpu'),
+ type: 'integer',
+ hidden: true,
+ sortable: true,
+ width: 60
+ },
+ diskread: {
+ header: gettext('Total Disk Read'),
+ type: 'integer',
+ hidden: true,
+ sortable: true,
+ renderer: PVE.Utils.format_size,
+ width: 100
+ },
+ diskwrite: {
+ header: gettext('Total Disk Write'),
+ type: 'integer',
+ hidden: true,
+ sortable: true,
+ renderer: PVE.Utils.format_size,
+ width: 100
+ },
+ netin: {
+ header: gettext('Total NetIn'),
+ type: 'integer',
+ hidden: true,
+ sortable: true,
+ renderer: PVE.Utils.format_size,
+ width: 100
+ },
+ netout: {
+ header: gettext('Total NetOut'),
+ type: 'integer',
+ hidden: true,
+ sortable: true,
+ renderer: PVE.Utils.format_size,
+ width: 100
+ },
+ template: {
+ header: gettext('Template'),
+ type: 'integer',
+ hidden: true,
+ sortable: true,
+ width: 60
+ },
+ uptime: {
+ header: gettext('Uptime'),
+ type: 'integer',
+ renderer: PVE.Utils.render_uptime,
+ sortable: true,
+ width: 110
+ },
+ node: {
+ header: gettext('Node'),
+ type: 'string',
+ hidden: true,
+ sortable: true,
+ width: 110
+ },
+ storage: {
+ header: gettext('Storage'),
+ type: 'string',
+ hidden: true,
+ sortable: true,
+ width: 110
+ },
+ pool: {
+ header: gettext('Pool'),
+ type: 'string',
+ hidden: true,
+ sortable: true,
+ width: 110
+ }
+ };
+
+ var fields = [];
+ var fieldNames = [];
+ Ext.Object.each(field_defaults, function(key, value) {
+ if (!Ext.isDefined(value.convert)) {
+ fields.push({name: key, type: value.type});
+ fieldNames.push(key);
+ } else if (key === 'text' || key === 'running') {
+ fields.push({name: key, type: value.type, convert: value.convert});
+ fieldNames.push(key);
+ }
+ });
+
+ Ext.define('PVEResources', {
+ extend: "Ext.data.Model",
+ fields: fields,
+ proxy: {
+ type: 'pve',
+ url: '/api2/json/cluster/resources'
+ }
+ });
+
+ Ext.define('PVETree', {
+ extend: "Ext.data.Model",
+ fields: fields,
+ proxy: { type: 'memory' }
+ });
+
+ Ext.apply(config, {
+ storeid: 'PVEResources',
+ model: 'PVEResources',
+ defaultColums: function() {
+ var res = [];
+ Ext.Object.each(field_defaults, function(field, info) {
+ var fi = Ext.apply({ dataIndex: field }, info);
+ res.push(fi);
+ });
+ return res;
+ },
+ fieldNames: fieldNames
+ });
+
+ me.callParent([config]);
+ }
+});
--- /dev/null
+Ext.define('PVE.data.TimezoneStore', {
+ extend: 'Ext.data.Store',
+
+ statics: {
+ timezones: [
+ ['Africa/Abidjan'],
+ ['Africa/Accra'],
+ ['Africa/Addis_Ababa'],
+ ['Africa/Algiers'],
+ ['Africa/Asmara'],
+ ['Africa/Bamako'],
+ ['Africa/Bangui'],
+ ['Africa/Banjul'],
+ ['Africa/Bissau'],
+ ['Africa/Blantyre'],
+ ['Africa/Brazzaville'],
+ ['Africa/Bujumbura'],
+ ['Africa/Cairo'],
+ ['Africa/Casablanca'],
+ ['Africa/Ceuta'],
+ ['Africa/Conakry'],
+ ['Africa/Dakar'],
+ ['Africa/Dar_es_Salaam'],
+ ['Africa/Djibouti'],
+ ['Africa/Douala'],
+ ['Africa/El_Aaiun'],
+ ['Africa/Freetown'],
+ ['Africa/Gaborone'],
+ ['Africa/Harare'],
+ ['Africa/Johannesburg'],
+ ['Africa/Kampala'],
+ ['Africa/Khartoum'],
+ ['Africa/Kigali'],
+ ['Africa/Kinshasa'],
+ ['Africa/Lagos'],
+ ['Africa/Libreville'],
+ ['Africa/Lome'],
+ ['Africa/Luanda'],
+ ['Africa/Lubumbashi'],
+ ['Africa/Lusaka'],
+ ['Africa/Malabo'],
+ ['Africa/Maputo'],
+ ['Africa/Maseru'],
+ ['Africa/Mbabane'],
+ ['Africa/Mogadishu'],
+ ['Africa/Monrovia'],
+ ['Africa/Nairobi'],
+ ['Africa/Ndjamena'],
+ ['Africa/Niamey'],
+ ['Africa/Nouakchott'],
+ ['Africa/Ouagadougou'],
+ ['Africa/Porto-Novo'],
+ ['Africa/Sao_Tome'],
+ ['Africa/Tripoli'],
+ ['Africa/Tunis'],
+ ['Africa/Windhoek'],
+ ['America/Adak'],
+ ['America/Anchorage'],
+ ['America/Anguilla'],
+ ['America/Antigua'],
+ ['America/Araguaina'],
+ ['America/Argentina/Buenos_Aires'],
+ ['America/Argentina/Catamarca'],
+ ['America/Argentina/Cordoba'],
+ ['America/Argentina/Jujuy'],
+ ['America/Argentina/La_Rioja'],
+ ['America/Argentina/Mendoza'],
+ ['America/Argentina/Rio_Gallegos'],
+ ['America/Argentina/Salta'],
+ ['America/Argentina/San_Juan'],
+ ['America/Argentina/San_Luis'],
+ ['America/Argentina/Tucuman'],
+ ['America/Argentina/Ushuaia'],
+ ['America/Aruba'],
+ ['America/Asuncion'],
+ ['America/Atikokan'],
+ ['America/Bahia'],
+ ['America/Bahia_Banderas'],
+ ['America/Barbados'],
+ ['America/Belem'],
+ ['America/Belize'],
+ ['America/Blanc-Sablon'],
+ ['America/Boa_Vista'],
+ ['America/Bogota'],
+ ['America/Boise'],
+ ['America/Cambridge_Bay'],
+ ['America/Campo_Grande'],
+ ['America/Cancun'],
+ ['America/Caracas'],
+ ['America/Cayenne'],
+ ['America/Cayman'],
+ ['America/Chicago'],
+ ['America/Chihuahua'],
+ ['America/Costa_Rica'],
+ ['America/Cuiaba'],
+ ['America/Curacao'],
+ ['America/Danmarkshavn'],
+ ['America/Dawson'],
+ ['America/Dawson_Creek'],
+ ['America/Denver'],
+ ['America/Detroit'],
+ ['America/Dominica'],
+ ['America/Edmonton'],
+ ['America/Eirunepe'],
+ ['America/El_Salvador'],
+ ['America/Fortaleza'],
+ ['America/Glace_Bay'],
+ ['America/Godthab'],
+ ['America/Goose_Bay'],
+ ['America/Grand_Turk'],
+ ['America/Grenada'],
+ ['America/Guadeloupe'],
+ ['America/Guatemala'],
+ ['America/Guayaquil'],
+ ['America/Guyana'],
+ ['America/Halifax'],
+ ['America/Havana'],
+ ['America/Hermosillo'],
+ ['America/Indiana/Indianapolis'],
+ ['America/Indiana/Knox'],
+ ['America/Indiana/Marengo'],
+ ['America/Indiana/Petersburg'],
+ ['America/Indiana/Tell_City'],
+ ['America/Indiana/Vevay'],
+ ['America/Indiana/Vincennes'],
+ ['America/Indiana/Winamac'],
+ ['America/Inuvik'],
+ ['America/Iqaluit'],
+ ['America/Jamaica'],
+ ['America/Juneau'],
+ ['America/Kentucky/Louisville'],
+ ['America/Kentucky/Monticello'],
+ ['America/La_Paz'],
+ ['America/Lima'],
+ ['America/Los_Angeles'],
+ ['America/Maceio'],
+ ['America/Managua'],
+ ['America/Manaus'],
+ ['America/Marigot'],
+ ['America/Martinique'],
+ ['America/Matamoros'],
+ ['America/Mazatlan'],
+ ['America/Menominee'],
+ ['America/Merida'],
+ ['America/Mexico_City'],
+ ['America/Miquelon'],
+ ['America/Moncton'],
+ ['America/Monterrey'],
+ ['America/Montevideo'],
+ ['America/Montreal'],
+ ['America/Montserrat'],
+ ['America/Nassau'],
+ ['America/New_York'],
+ ['America/Nipigon'],
+ ['America/Nome'],
+ ['America/Noronha'],
+ ['America/North_Dakota/Center'],
+ ['America/North_Dakota/New_Salem'],
+ ['America/Ojinaga'],
+ ['America/Panama'],
+ ['America/Pangnirtung'],
+ ['America/Paramaribo'],
+ ['America/Phoenix'],
+ ['America/Port-au-Prince'],
+ ['America/Port_of_Spain'],
+ ['America/Porto_Velho'],
+ ['America/Puerto_Rico'],
+ ['America/Rainy_River'],
+ ['America/Rankin_Inlet'],
+ ['America/Recife'],
+ ['America/Regina'],
+ ['America/Resolute'],
+ ['America/Rio_Branco'],
+ ['America/Santa_Isabel'],
+ ['America/Santarem'],
+ ['America/Santiago'],
+ ['America/Santo_Domingo'],
+ ['America/Sao_Paulo'],
+ ['America/Scoresbysund'],
+ ['America/Shiprock'],
+ ['America/St_Barthelemy'],
+ ['America/St_Johns'],
+ ['America/St_Kitts'],
+ ['America/St_Lucia'],
+ ['America/St_Thomas'],
+ ['America/St_Vincent'],
+ ['America/Swift_Current'],
+ ['America/Tegucigalpa'],
+ ['America/Thule'],
+ ['America/Thunder_Bay'],
+ ['America/Tijuana'],
+ ['America/Toronto'],
+ ['America/Tortola'],
+ ['America/Vancouver'],
+ ['America/Whitehorse'],
+ ['America/Winnipeg'],
+ ['America/Yakutat'],
+ ['America/Yellowknife'],
+ ['Antarctica/Casey'],
+ ['Antarctica/Davis'],
+ ['Antarctica/DumontDUrville'],
+ ['Antarctica/Macquarie'],
+ ['Antarctica/Mawson'],
+ ['Antarctica/McMurdo'],
+ ['Antarctica/Palmer'],
+ ['Antarctica/Rothera'],
+ ['Antarctica/South_Pole'],
+ ['Antarctica/Syowa'],
+ ['Antarctica/Vostok'],
+ ['Arctic/Longyearbyen'],
+ ['Asia/Aden'],
+ ['Asia/Almaty'],
+ ['Asia/Amman'],
+ ['Asia/Anadyr'],
+ ['Asia/Aqtau'],
+ ['Asia/Aqtobe'],
+ ['Asia/Ashgabat'],
+ ['Asia/Baghdad'],
+ ['Asia/Bahrain'],
+ ['Asia/Baku'],
+ ['Asia/Bangkok'],
+ ['Asia/Beirut'],
+ ['Asia/Bishkek'],
+ ['Asia/Brunei'],
+ ['Asia/Choibalsan'],
+ ['Asia/Chongqing'],
+ ['Asia/Colombo'],
+ ['Asia/Damascus'],
+ ['Asia/Dhaka'],
+ ['Asia/Dili'],
+ ['Asia/Dubai'],
+ ['Asia/Dushanbe'],
+ ['Asia/Gaza'],
+ ['Asia/Harbin'],
+ ['Asia/Ho_Chi_Minh'],
+ ['Asia/Hong_Kong'],
+ ['Asia/Hovd'],
+ ['Asia/Irkutsk'],
+ ['Asia/Jakarta'],
+ ['Asia/Jayapura'],
+ ['Asia/Jerusalem'],
+ ['Asia/Kabul'],
+ ['Asia/Kamchatka'],
+ ['Asia/Karachi'],
+ ['Asia/Kashgar'],
+ ['Asia/Kathmandu'],
+ ['Asia/Kolkata'],
+ ['Asia/Krasnoyarsk'],
+ ['Asia/Kuala_Lumpur'],
+ ['Asia/Kuching'],
+ ['Asia/Kuwait'],
+ ['Asia/Macau'],
+ ['Asia/Magadan'],
+ ['Asia/Makassar'],
+ ['Asia/Manila'],
+ ['Asia/Muscat'],
+ ['Asia/Nicosia'],
+ ['Asia/Novokuznetsk'],
+ ['Asia/Novosibirsk'],
+ ['Asia/Omsk'],
+ ['Asia/Oral'],
+ ['Asia/Phnom_Penh'],
+ ['Asia/Pontianak'],
+ ['Asia/Pyongyang'],
+ ['Asia/Qatar'],
+ ['Asia/Qyzylorda'],
+ ['Asia/Rangoon'],
+ ['Asia/Riyadh'],
+ ['Asia/Sakhalin'],
+ ['Asia/Samarkand'],
+ ['Asia/Seoul'],
+ ['Asia/Shanghai'],
+ ['Asia/Singapore'],
+ ['Asia/Taipei'],
+ ['Asia/Tashkent'],
+ ['Asia/Tbilisi'],
+ ['Asia/Tehran'],
+ ['Asia/Thimphu'],
+ ['Asia/Tokyo'],
+ ['Asia/Ulaanbaatar'],
+ ['Asia/Urumqi'],
+ ['Asia/Vientiane'],
+ ['Asia/Vladivostok'],
+ ['Asia/Yakutsk'],
+ ['Asia/Yekaterinburg'],
+ ['Asia/Yerevan'],
+ ['Atlantic/Azores'],
+ ['Atlantic/Bermuda'],
+ ['Atlantic/Canary'],
+ ['Atlantic/Cape_Verde'],
+ ['Atlantic/Faroe'],
+ ['Atlantic/Madeira'],
+ ['Atlantic/Reykjavik'],
+ ['Atlantic/South_Georgia'],
+ ['Atlantic/St_Helena'],
+ ['Atlantic/Stanley'],
+ ['Australia/Adelaide'],
+ ['Australia/Brisbane'],
+ ['Australia/Broken_Hill'],
+ ['Australia/Currie'],
+ ['Australia/Darwin'],
+ ['Australia/Eucla'],
+ ['Australia/Hobart'],
+ ['Australia/Lindeman'],
+ ['Australia/Lord_Howe'],
+ ['Australia/Melbourne'],
+ ['Australia/Perth'],
+ ['Australia/Sydney'],
+ ['Europe/Amsterdam'],
+ ['Europe/Andorra'],
+ ['Europe/Athens'],
+ ['Europe/Belgrade'],
+ ['Europe/Berlin'],
+ ['Europe/Bratislava'],
+ ['Europe/Brussels'],
+ ['Europe/Bucharest'],
+ ['Europe/Budapest'],
+ ['Europe/Chisinau'],
+ ['Europe/Copenhagen'],
+ ['Europe/Dublin'],
+ ['Europe/Gibraltar'],
+ ['Europe/Guernsey'],
+ ['Europe/Helsinki'],
+ ['Europe/Isle_of_Man'],
+ ['Europe/Istanbul'],
+ ['Europe/Jersey'],
+ ['Europe/Kaliningrad'],
+ ['Europe/Kiev'],
+ ['Europe/Lisbon'],
+ ['Europe/Ljubljana'],
+ ['Europe/London'],
+ ['Europe/Luxembourg'],
+ ['Europe/Madrid'],
+ ['Europe/Malta'],
+ ['Europe/Mariehamn'],
+ ['Europe/Minsk'],
+ ['Europe/Monaco'],
+ ['Europe/Moscow'],
+ ['Europe/Oslo'],
+ ['Europe/Paris'],
+ ['Europe/Podgorica'],
+ ['Europe/Prague'],
+ ['Europe/Riga'],
+ ['Europe/Rome'],
+ ['Europe/Samara'],
+ ['Europe/San_Marino'],
+ ['Europe/Sarajevo'],
+ ['Europe/Simferopol'],
+ ['Europe/Skopje'],
+ ['Europe/Sofia'],
+ ['Europe/Stockholm'],
+ ['Europe/Tallinn'],
+ ['Europe/Tirane'],
+ ['Europe/Uzhgorod'],
+ ['Europe/Vaduz'],
+ ['Europe/Vatican'],
+ ['Europe/Vienna'],
+ ['Europe/Vilnius'],
+ ['Europe/Volgograd'],
+ ['Europe/Warsaw'],
+ ['Europe/Zagreb'],
+ ['Europe/Zaporozhye'],
+ ['Europe/Zurich'],
+ ['Indian/Antananarivo'],
+ ['Indian/Chagos'],
+ ['Indian/Christmas'],
+ ['Indian/Cocos'],
+ ['Indian/Comoro'],
+ ['Indian/Kerguelen'],
+ ['Indian/Mahe'],
+ ['Indian/Maldives'],
+ ['Indian/Mauritius'],
+ ['Indian/Mayotte'],
+ ['Indian/Reunion'],
+ ['Pacific/Apia'],
+ ['Pacific/Auckland'],
+ ['Pacific/Chatham'],
+ ['Pacific/Chuuk'],
+ ['Pacific/Easter'],
+ ['Pacific/Efate'],
+ ['Pacific/Enderbury'],
+ ['Pacific/Fakaofo'],
+ ['Pacific/Fiji'],
+ ['Pacific/Funafuti'],
+ ['Pacific/Galapagos'],
+ ['Pacific/Gambier'],
+ ['Pacific/Guadalcanal'],
+ ['Pacific/Guam'],
+ ['Pacific/Honolulu'],
+ ['Pacific/Johnston'],
+ ['Pacific/Kiritimati'],
+ ['Pacific/Kosrae'],
+ ['Pacific/Kwajalein'],
+ ['Pacific/Majuro'],
+ ['Pacific/Marquesas'],
+ ['Pacific/Midway'],
+ ['Pacific/Nauru'],
+ ['Pacific/Niue'],
+ ['Pacific/Norfolk'],
+ ['Pacific/Noumea'],
+ ['Pacific/Pago_Pago'],
+ ['Pacific/Palau'],
+ ['Pacific/Pitcairn'],
+ ['Pacific/Pohnpei'],
+ ['Pacific/Port_Moresby'],
+ ['Pacific/Rarotonga'],
+ ['Pacific/Saipan'],
+ ['Pacific/Tahiti'],
+ ['Pacific/Tarawa'],
+ ['Pacific/Tongatapu'],
+ ['Pacific/Wake'],
+ ['Pacific/Wallis']
+ ]
+ },
+
+ constructor: function(config) {
+ var me = this;
+
+ config = config || {};
+
+ Ext.regModel('Timezone', {
+ fields: ['zone'],
+ proxy: {
+ type: 'memory',
+ reader: 'array'
+ }
+ });
+
+ Ext.apply(config, {
+ model: 'Timezone',
+ data: PVE.data.TimezoneStore.timezones
+ });
+
+ me.callParent([config]);
+ }
+});
\ No newline at end of file
--- /dev/null
+// Serialize load (avoid too many parallel connections)
+Ext.define('PVE.data.UpdateQueue', {
+ singleton: true,
+
+ constructor : function(){
+ var me = this;
+
+ var queue = [];
+ var queue_idx = {};
+
+ var idle = true;
+
+ var start_update = function() {
+ if (!idle) {
+ return;
+ }
+
+ var storeid = queue.shift();
+ if (!storeid) {
+ return;
+ }
+ var info = queue_idx[storeid];
+ queue_idx[storeid] = null;
+
+ info.updatestart = new Date();
+
+ idle = false;
+ info.store.load({
+ callback: function(records, operation, success) {
+ idle = true;
+ if (info.callback) {
+ var runtime = (new Date()).getTime() - info.updatestart.getTime();
+ info.callback(runtime, success);
+ }
+ start_update();
+ }
+ });
+ };
+
+ Ext.apply(me, {
+ queue: function(store, cb) {
+ var storeid = store.storeid;
+ if (!storeid) {
+ throw "unable to queue store without storeid";
+ }
+ if (!queue_idx[storeid]) {
+ queue_idx[storeid] = {
+ store: store,
+ callback: cb
+ };
+ queue.push(storeid);
+ }
+ start_update();
+ }
+ });
+ }
+});
--- /dev/null
+/* Extends the Ext.data.Store type
+ * with startUpdate() and stopUpdate() methods
+ * to refresh the store data in the background
+ * Components using this store directly will flicker
+ * due to the redisplay of the element ater 'config.interval' ms
+ */
+Ext.define('PVE.data.UpdateStore', {
+ extend: 'Ext.data.Store',
+
+ constructor: function(config) {
+ var me = this;
+
+ config = config || {};
+
+ if (!config.interval) {
+ config.interval = 3000;
+ }
+
+ if (!config.storeid) {
+ throw "no storeid specified";
+ }
+
+ var load_task = new Ext.util.DelayedTask();
+
+ var run_load_task = function() {
+ if (PVE.Utils.authOK()) {
+ PVE.data.UpdateQueue.queue(me, function(runtime, success) {
+ var interval = config.interval + runtime*2;
+ load_task.delay(interval, run_load_task);
+ });
+ } else {
+ load_task.delay(200, run_load_task);
+ }
+ };
+
+ Ext.apply(config, {
+ startUpdate: function() {
+ run_load_task();
+ },
+ stopUpdate: function() {
+ load_task.cancel();
+ }
+ });
+
+ me.callParent([config]);
+
+ me.on('destroy', function() {
+ load_task.cancel();
+ });
+ }
+});
--- /dev/null
+/* A reader to store a single JSON Object (hash) into a storage.
+ * Also accepts an array containing a single hash.
+ *
+ * So it can read:
+ *
+ * example1: {data1: "xyz", data2: "abc"}
+ * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}]
+ *
+ * example2: [ {data1: "xyz", data2: "abc"} ]
+ * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}]
+ *
+ * If you set 'readArray', the reader expexts the object as array:
+ *
+ * example3: [ { key: "data1", value: "xyz", p2: "cde" }, { key: "data2", value: "abc", p2: "efg" }]
+ * returns [{key: "data1", value: "xyz", p2: "cde}, {key: "data2", value: "abc", p2: "efg"}]
+ *
+ * Note: The records can contain additional properties (like 'p2' above) when you use 'readArray'
+ *
+ * Additional feature: specify allowed properties with default values with 'rows' object
+ *
+ * var rows = {
+ * memory: {
+ * required: true,
+ * defaultValue: 512
+ * }
+ * }
+ *
+ */
+
+Ext.define('PVE.data.reader.JsonObject', {
+ extend: 'Ext.data.reader.Json',
+ alias : 'reader.jsonobject',
+
+ readArray: false,
+
+ rows: undefined,
+
+ constructor: function(config) {
+ var me = this;
+
+ Ext.apply(me, config || {});
+
+ me.callParent([config]);
+ },
+
+ getResponseData: function(response) {
+ var me = this;
+
+ var data = [];
+ try {
+ var result = Ext.decode(response.responseText);
+ // get our data items inside the server response
+ var root = result[me.getRootProperty()];
+
+ if (me.readArray) {
+
+ var rec_hash = {};
+ Ext.Array.each(root, function(rec) {
+ if (Ext.isDefined(rec.key)) {
+ rec_hash[rec.key] = rec;
+ }
+ });
+
+ if (me.rows) {
+ Ext.Object.each(me.rows, function(key, rowdef) {
+ var rec = rec_hash[key];
+ if (Ext.isDefined(rec)) {
+ if (!Ext.isDefined(rec.value)) {
+ rec.value = rowdef.defaultValue;
+ }
+ data.push(rec);
+ } else if (Ext.isDefined(rowdef.defaultValue)) {
+ data.push({key: key, value: rowdef.defaultValue} );
+ } else if (rowdef.required) {
+ data.push({key: key, value: undefined });
+ }
+ });
+ } else {
+ Ext.Array.each(root, function(rec) {
+ if (Ext.isDefined(rec.key)) {
+ data.push(rec);
+ }
+ });
+ }
+
+ } else {
+
+ var org_root = root;
+
+ if (Ext.isArray(org_root)) {
+ if (root.length == 1) {
+ root = org_root[0];
+ } else {
+ root = {};
+ }
+ }
+
+ if (me.rows) {
+ Ext.Object.each(me.rows, function(key, rowdef) {
+ if (Ext.isDefined(root[key])) {
+ data.push({key: key, value: root[key]});
+ } else if (Ext.isDefined(rowdef.defaultValue)) {
+ data.push({key: key, value: rowdef.defaultValue});
+ } else if (rowdef.required) {
+ data.push({key: key, value: undefined});
+ }
+ });
+ } else {
+ Ext.Object.each(root, function(key, value) {
+ data.push({key: key, value: value });
+ });
+ }
+ }
+ }
+ catch (ex) {
+ Ext.Error.raise({
+ response: response,
+ json: response.responseText,
+ parseError: ex,
+ msg: 'Unable to parse the JSON returned by the server: ' + ex.toString()
+ });
+ }
+
+ return data;
+ }
+});
+
--- /dev/null
+Ext.define('PVE.dc.ACLAdd', {
+ extend: 'PVE.window.Edit',
+ alias: ['widget.pveACLAdd'],
+
+ initComponent : function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ me.create = true;
+
+ var items = [
+ {
+ xtype: me.path ? 'hiddenfield' : 'textfield',
+ name: 'path',
+ value: me.path,
+ allowBlank: false,
+ fieldLabel: gettext('Path')
+ }
+ ];
+
+ if (me.aclType === 'group') {
+ me.subject = gettext("Group Permission");
+ items.push({
+ xtype: 'pveGroupSelector',
+ name: 'groups',
+ fieldLabel: gettext('Group')
+ });
+ } else if (me.aclType === 'user') {
+ me.subject = gettext("User Permission");
+ items.push({
+ xtype: 'pveUserSelector',
+ name: 'users',
+ fieldLabel: gettext('User')
+ });
+ } else {
+ throw "unknown ACL type";
+ }
+
+ items.push({
+ xtype: 'pveRoleSelector',
+ name: 'roles',
+ value: 'NoAccess',
+ fieldLabel: gettext('Role')
+ });
+
+ if (!me.path) {
+ items.push({
+ xtype: 'pvecheckbox',
+ name: 'propagate',
+ checked: true,
+ fieldLabel: gettext('Propagate')
+ });
+ }
+
+ var ipanel = Ext.create('PVE.panel.InputPanel', {
+ items: items
+ });
+
+ Ext.apply(me, {
+ url: '/access/acl',
+ method: 'PUT',
+ isAdd: true,
+ items: [ ipanel ]
+ });
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.dc.ACLView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveACLView'],
+
+ // use fixed path
+ path: undefined,
+
+ initComponent : function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-acl',
+ proxy: {
+ type: 'pve',
+ url: "/api2/json/access/acl"
+ },
+ sorters: {
+ property: 'path',
+ order: 'DESC'
+ }
+ });
+
+ if (me.path) {
+ store.filters.add(new Ext.util.Filter({
+ filterFn: function(item) {
+ if (item.data.path === me.path) {
+ return true;
+ }
+ }
+ }));
+ }
+
+ var render_ugid = function(ugid, metaData, record) {
+ if (record.data.type == 'group') {
+ return '@' + ugid;
+ }
+
+ return ugid;
+ };
+
+ var columns = [
+ {
+ header: gettext('User') + '/' + gettext('Group'),
+ flex: 1,
+ sortable: true,
+ renderer: render_ugid,
+ dataIndex: 'ugid'
+ },
+ {
+ header: gettext('Role'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'roleid'
+ }
+ ];
+
+ if (!me.path) {
+ columns.unshift({
+ header: gettext('Path'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'path'
+ });
+ columns.push({
+ header: gettext('Propagate'),
+ width: 80,
+ sortable: true,
+ dataIndex: 'propagate'
+ });
+ }
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var reload = function() {
+ store.load();
+ };
+
+ var remove_btn = new PVE.button.Button({
+ text: gettext('Remove'),
+ disabled: true,
+ selModel: sm,
+ confirmMsg: gettext('Are you sure you want to remove this entry'),
+ handler: function(btn, event, rec) {
+ var params = {
+ 'delete': 1,
+ path: rec.data.path,
+ roles: rec.data.roleid
+ };
+ if (rec.data.type === 'group') {
+ params.groups = rec.data.ugid;
+ } else if (rec.data.type === 'user') {
+ params.users = rec.data.ugid;
+ } else {
+ throw 'unknown data type';
+ }
+
+ PVE.Utils.API2Request({
+ url: '/access/acl',
+ params: params,
+ method: 'PUT',
+ waitMsgTarget: me,
+ callback: function() {
+ reload();
+ },
+ failure: function (response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ }
+ });
+
+ PVE.Utils.monStoreErrors(me, store);
+
+ Ext.apply(me, {
+ store: store,
+ selModel: sm,
+ stateful: false,
+ tbar: [
+ {
+ text: gettext('Add'),
+ menu: new Ext.menu.Menu({
+ items: [
+ {
+ text: gettext('Group Permission'),
+ handler: function() {
+ var win = Ext.create('PVE.dc.ACLAdd',{
+ aclType: 'group',
+ path: me.path
+ });
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ {
+ text: gettext('User Permission'),
+ handler: function() {
+ var win = Ext.create('PVE.dc.ACLAdd',{
+ aclType: 'user',
+ path: me.path
+ });
+ win.on('destroy', reload);
+ win.show();
+ }
+ }
+ ]
+ })
+ },
+ remove_btn
+ ],
+ viewConfig: {
+ trackOver: false
+ },
+ columns: columns,
+ listeners: {
+ show: reload
+ }
+ });
+
+ me.callParent();
+ }
+}, function() {
+
+ Ext.define('pve-acl', {
+ extend: 'Ext.data.Model',
+ fields: [
+ 'path', 'type', 'ugid', 'roleid',
+ {
+ name: 'propagate',
+ type: 'boolean'
+ }
+ ]
+ });
+
+});
\ No newline at end of file
--- /dev/null
+Ext.define('PVE.dc.AuthEdit', {
+ extend: 'PVE.window.Edit',
+ alias: ['widget.pveDcAuthEdit'],
+
+ isAdd: true,
+
+ initComponent : function() {
+ var me = this;
+
+ me.create = !me.realm;
+
+ var url;
+ var method;
+ var serverlist;
+
+ if (me.create) {
+ url = '/api2/extjs/access/domains';
+ method = 'POST';
+ } else {
+ url = '/api2/extjs/access/domains/' + me.realm;
+ method = 'PUT';
+ }
+
+ var column1 = [
+ {
+ xtype: me.create ? 'textfield' : 'displayfield',
+ height: 22, // hack: set same height as text fields
+ name: 'realm',
+ fieldLabel: gettext('Realm'),
+ value: me.realm,
+ allowBlank: false
+ }
+ ];
+
+ if (me.authType === 'ad') {
+
+ me.subject = gettext('Active Directory Server');
+
+ column1.push({
+ xtype: 'textfield',
+ name: 'domain',
+ fieldLabel: gettext('Domain'),
+ emptyText: 'company.net',
+ allowBlank: false
+ });
+
+ } else if (me.authType === 'ldap') {
+
+ me.subject = gettext('LDAP Server');
+
+ column1.push({
+ xtype: 'textfield',
+ name: 'base_dn',
+ fieldLabel: gettext('Base Domain Name'),
+ emptyText: 'CN=Users,DC=Company,DC=net',
+ allowBlank: false
+ });
+
+ column1.push({
+ xtype: 'textfield',
+ name: 'user_attr',
+ emptyText: 'uid / sAMAccountName',
+ fieldLabel: gettext('User Attribute Name'),
+ allowBlank: false
+ });
+ } else if (me.authType === 'pve') {
+
+ if (me.create) throw 'unknown auth type';
+
+ me.subject = 'Proxmox VE authentication server';
+
+ } else if (me.authType === 'pam') {
+
+ if (me.create) throw 'unknown auth type';
+
+ me.subject = 'linux PAM';
+
+ } else {
+ throw 'unknown auth type ';
+ }
+
+ column1.push({
+ xtype: 'pvecheckbox',
+ fieldLabel: gettext('Default'),
+ name: 'default',
+ uncheckedValue: 0
+ });
+
+ var column2 = [];
+
+ if (me.authType === 'ldap' || me.authType === 'ad') {
+ column2.push([
+ {
+ xtype: 'textfield',
+ fieldLabel: gettext('Server'),
+ name: 'server1',
+ allowBlank: false
+ },
+ {
+ xtype: 'pvetextfield',
+ fieldLabel: gettext('Fallback Server'),
+ deleteEmpty: !me.create,
+ name: 'server2'
+ },
+ {
+ xtype: 'numberfield',
+ name: 'port',
+ fieldLabel: gettext('Port'),
+ minValue: 1,
+ maxValue: 65535,
+ emptyText: gettext('Default'),
+ submitEmptyText: false
+ },
+ {
+ xtype: 'pvecheckbox',
+ fieldLabel: 'SSL',
+ name: 'secure',
+ uncheckedValue: 0
+ }
+ ]);
+ }
+
+ // Two Factor Auth settings
+
+ column2.push({
+ xtype: 'pveKVComboBox',
+ name: 'tfa',
+ deleteEmpty: !me.create,
+ value: '',
+ fieldLabel: gettext('TFA'),
+ data: [ ['', PVE.Utils.noneText], ['oath', 'OATH'], ['yubico', 'Yubico']],
+ listeners: {
+ change: function(f, value) {
+ if (!me.rendered) {
+ return;
+ }
+ me.down('field[name=oath_step]').setVisible(value === 'oath');
+ me.down('field[name=oath_digits]').setVisible(value === 'oath');
+ me.down('field[name=yubico_api_id]').setVisible(value === 'yubico');
+ me.down('field[name=yubico_api_key]').setVisible(value === 'yubico');
+ me.down('field[name=yubico_url]').setVisible(value === 'yubico');
+ }
+ }
+ });
+
+ column2.push({
+ xtype: 'numberfield',
+ name: 'oath_step',
+ value: '',
+ minValue: 10,
+ step: 1,
+ allowDecimals: false,
+ allowBlank: true,
+ emptyText: PVE.Utils.defaultText + ' (30)',
+ submitEmptyText: false,
+ hidden: true,
+ fieldLabel: 'OATH time step'
+ });
+
+ column2.push({
+ xtype: 'numberfield',
+ name: 'oath_digits',
+ value: '',
+ minValue: 6,
+ maxValue: 8,
+ step: 1,
+ allowDecimals: false,
+ allowBlank: true,
+ emptyText: PVE.Utils.defaultText + ' (6)',
+ submitEmptyText: false,
+ hidden: true,
+ fieldLabel: 'OATH password length'
+ });
+
+ column2.push({
+ xtype: 'textfield',
+ name: 'yubico_api_id',
+ hidden: true,
+ fieldLabel: 'Yubico API Id'
+ });
+
+ column2.push({
+ xtype: 'textfield',
+ name: 'yubico_api_key',
+ hidden: true,
+ fieldLabel: 'Yubico API Key'
+ });
+
+ column2.push({
+ xtype: 'textfield',
+ name: 'yubico_url',
+ hidden: true,
+ fieldLabel: 'Yubico URL'
+ });
+
+ var ipanel = Ext.create('PVE.panel.InputPanel', {
+ column1: column1,
+ column2: column2,
+ columnB: [{
+ xtype: 'textfield',
+ name: 'comment',
+ fieldLabel: gettext('Comment')
+ }],
+ onGetValues: function(values) {
+ if (!values.port) {
+ if (!me.create) {
+ PVE.Utils.assemble_field_data(values, { 'delete': 'port' });
+ }
+ delete values.port;
+ }
+
+ if (me.create) {
+ values.type = me.authType;
+ }
+
+ if (values.tfa === 'oath') {
+ values.tfa = "type=oath";
+ if (values.oath_step) {
+ values.tfa += ",step=" + values.oath_step;
+ }
+ if (values.oath_digits) {
+ values.tfa += ",digits=" + values.oath_digits;
+ }
+ } else if (values.tfa === 'yubico') {
+ values.tfa = "type=yubico";
+ values.tfa += ",id=" + values.yubico_api_id;
+ values.tfa += ",key=" + values.yubico_api_key;
+ if (values.yubico_url) {
+ values.tfa += ",url=" + values.yubico_url;
+ }
+ } else {
+ delete values.tfa;
+ }
+
+ delete values.oath_step;
+ delete values.oath_digits;
+ delete values.yubico_api_id;
+ delete values.yubico_api_key;
+ delete values.yubico_url;
+
+ return values;
+ }
+ });
+
+ Ext.applyIf(me, {
+ url: url,
+ method: method,
+ fieldDefaults: {
+ labelWidth: 120
+ },
+ items: [ ipanel ]
+ });
+
+ me.callParent();
+
+ if (!me.create) {
+ me.load({
+ success: function(response, options) {
+ var data = response.result.data || {};
+ // just to be sure (should not happen)
+ if (data.type !== me.authType) {
+ me.close();
+ throw "got wrong auth type";
+ }
+
+ if (data.tfa) {
+ var tfacfg = PVE.Parser.parseTfaConfig(data.tfa);
+ data.tfa = tfacfg.type;
+ if (tfacfg.type === 'yubico') {
+ data.yubico_api_key = tfacfg.key;
+ data.yubico_api_id = tfacfg.id;
+ data.yubico_url = tfacfg.url;
+ } else if (tfacfg.type === 'oath') {
+ data.oath_step = tfacfg.step;
+ data.oath_digits = tfacfg.digits;
+ }
+ }
+
+ me.setValues(data);
+ }
+ });
+ }
+ }
+});
--- /dev/null
+Ext.define('PVE.dc.AuthView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveAuthView'],
+
+ initComponent : function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-domains',
+ sorters: {
+ property: 'realm',
+ order: 'DESC'
+ }
+ });
+
+ var reload = function() {
+ store.load();
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+
+ var win = Ext.create('PVE.dc.AuthEdit',{
+ realm: rec.data.realm,
+ authType: rec.data.type
+ });
+ win.on('destroy', reload);
+ win.show();
+ };
+
+ var edit_btn = new PVE.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ var remove_btn = new PVE.button.Button({
+ text: gettext('Remove'),
+ disabled: true,
+ selModel: sm,
+ confirmMsg: function (rec) {
+ return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
+ "'" + rec.data.realm + "'");
+ },
+ enableFn: function(rec) {
+ return !(rec.data.type === 'pve' || rec.data.type === 'pam');
+ },
+ handler: function(btn, event, rec) {
+ var realm = rec.data.realm;
+
+ PVE.Utils.API2Request({
+ url: '/access/domains/' + realm,
+ method: 'DELETE',
+ waitMsgTarget: me,
+ callback: function() {
+ reload();
+ },
+ failure: function (response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ }
+ });
+
+ var tbar = [
+ {
+ text: gettext('Add'),
+ menu: new Ext.menu.Menu({
+ items: [
+ {
+ text: gettext('Active Directory Server'),
+ handler: function() {
+ var win = Ext.create('PVE.dc.AuthEdit', {
+ authType: 'ad'
+ });
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ {
+ text: gettext('LDAP Server'),
+ handler: function() {
+ var win = Ext.create('PVE.dc.AuthEdit',{
+ authType: 'ldap'
+ });
+ win.on('destroy', reload);
+ win.show();
+ }
+ }
+ ]
+ })
+ },
+ edit_btn, remove_btn
+ ];
+
+ Ext.apply(me, {
+ store: store,
+ selModel: sm,
+ stateful: false,
+ tbar: tbar,
+ viewConfig: {
+ trackOver: false
+ },
+ columns: [
+ {
+ header: gettext('Realm'),
+ width: 100,
+ sortable: true,
+ dataIndex: 'realm'
+ },
+ {
+ header: gettext('Type'),
+ width: 100,
+ sortable: true,
+ dataIndex: 'type'
+ },
+ {
+ header: gettext('TFA'),
+ width: 100,
+ sortable: true,
+ dataIndex: 'tfa'
+ },
+ {
+ id: 'comment',
+ header: gettext('Comment'),
+ sortable: false,
+ dataIndex: 'comment',
+ flex: 1
+ }
+ ],
+ listeners: {
+ show: reload,
+ itemdblclick: run_editor
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.dc.BackupEdit', {
+ extend: 'PVE.window.Edit',
+ alias: ['widget.pveDcBackupEdit'],
+
+ initComponent : function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ me.create = !me.jobid;
+
+ var url;
+ var method;
+
+ if (me.create) {
+ url = '/api2/extjs/cluster/backup';
+ method = 'POST';
+ } else {
+ url = '/api2/extjs/cluster/backup/' + me.jobid;
+ method = 'PUT';
+ }
+
+ var vmidField = Ext.create('Ext.form.field.Hidden', {
+ name: 'vmid'
+ });
+
+ var selModeField = Ext.create('PVE.form.KVComboBox', {
+ xtype: 'pveKVComboBox',
+ data: [
+ ['include', gettext('Include selected VMs')],
+ ['all', gettext('All')],
+ ['exclude', gettext('Exclude selected VMs')]
+ ],
+ fieldLabel: gettext('Selection mode'),
+ name: 'selMode',
+ value: ''
+ });
+
+ var insideUpdate = false;
+
+ var sm = Ext.create('Ext.selection.CheckboxModel', {
+ mode: 'SIMPLE',
+ listeners: {
+ selectionchange: function(model, selected) {
+ if (!insideUpdate) { // avoid endless loop
+ var sel = [];
+ Ext.Array.each(selected, function(record) {
+ sel.push(record.data.vmid);
+ });
+
+ insideUpdate = true;
+ vmidField.setValue(sel);
+ insideUpdate = false;
+ }
+ }
+ }
+ });
+
+ var storagesel = Ext.create('PVE.form.StorageSelector', {
+ fieldLabel: gettext('Storage'),
+ nodename: 'localhost',
+ storageContent: 'backup',
+ allowBlank: false,
+ name: 'storage'
+ });
+
+ var store = new Ext.data.Store({
+ model: 'PVEResources',
+ sorters: {
+ property: 'vmid',
+ order: 'ASC'
+ }
+ });
+
+ var vmgrid = Ext.createWidget('grid', {
+ store: store,
+ border: true,
+ height: 300,
+ selModel: sm,
+ disabled: true,
+ columns: [
+ {
+ header: 'ID',
+ dataIndex: 'vmid',
+ width: 60
+ },
+ {
+ header: gettext('Node'),
+ dataIndex: 'node'
+ },
+ {
+ header: gettext('Status'),
+ dataIndex: 'uptime',
+ renderer: function(value) {
+ if (value) {
+ return PVE.Utils.runningText;
+ } else {
+ return PVE.Utils.stoppedText;
+ }
+ }
+ },
+ {
+ header: gettext('Name'),
+ dataIndex: 'name',
+ flex: 1
+ },
+ {
+ header: gettext('Type'),
+ dataIndex: 'type'
+ }
+ ]
+ });
+
+ var nodesel = Ext.create('PVE.form.NodeSelector', {
+ name: 'node',
+ fieldLabel: gettext('Node'),
+ allowBlank: true,
+ editable: true,
+ autoSelect: false,
+ emptyText: '-- ' + gettext('All') + ' --',
+ listeners: {
+ change: function(f, value) {
+ storagesel.setNodename(value || 'localhost');
+ var mode = selModeField.getValue();
+ store.clearFilter();
+ store.filterBy(function(rec) {
+ return (!value || rec.get('node') === value);
+ });
+ if (mode === 'all') {
+ sm.selectAll(true);
+ }
+ }
+ }
+ });
+
+ var column1 = [
+ nodesel,
+ storagesel,
+ {
+ xtype: 'pveDayOfWeekSelector',
+ name: 'dow',
+ fieldLabel: gettext('Day of week'),
+ multiSelect: true,
+ value: ['sat'],
+ allowBlank: false
+ },
+ {
+ xtype: 'timefield',
+ fieldLabel: gettext('Start Time'),
+ name: 'starttime',
+ format: 'H:i',
+ value: '00:00',
+ allowBlank: false
+ },
+ selModeField
+ ];
+
+ var column2 = [
+ {
+ xtype: 'textfield',
+ fieldLabel: gettext('Send email to'),
+ name: 'mailto'
+ },
+ {
+ xtype: 'pveEmailNotificationSelector',
+ fieldLabel: gettext('Email notification'),
+ name: 'mailnotification',
+ deleteEmpty: me.create ? false : true,
+ value: me.create ? 'always' : ''
+ },
+ {
+ xtype: 'pveCompressionSelector',
+ fieldLabel: gettext('Compression'),
+ name: 'compress',
+ deleteEmpty: me.create ? false : true,
+ value: me.create ? 'lzo' : ''
+ },
+ {
+ xtype: 'pveBackupModeSelector',
+ fieldLabel: gettext('Mode'),
+ value: 'snapshot',
+ name: 'mode'
+ },
+ vmidField
+ ];
+
+ var ipanel = Ext.create('PVE.panel.InputPanel', {
+ column1: column1,
+ column2: column2,
+ onGetValues: function(values) {
+ if (!values.node) {
+ if (!me.create) {
+ PVE.Utils.assemble_field_data(values, { 'delete': 'node' });
+ }
+ delete values.node;
+ }
+
+ var selMode = values.selMode;
+ delete values.selMode;
+
+ if (selMode === 'all') {
+ values.all = 1;
+ values.exclude = '';
+ delete values.vmid;
+ } else if (selMode === 'exclude') {
+ values.all = 1;
+ values.exclude = values.vmid;
+ delete values.vmid;
+ }
+ return values;
+ }
+ });
+
+ var update_vmid_selection = function(list, mode) {
+ if (insideUpdate) {
+ return; // should not happen - just to be sure
+ }
+ insideUpdate = true;
+ if (mode !== 'all') {
+ sm.deselectAll(true);
+ if (list) {
+ Ext.Array.each(list.split(','), function(vmid) {
+ var rec = store.findRecord('vmid', vmid);
+ if (rec) {
+ sm.select(rec, true);
+ }
+ });
+ }
+ }
+ insideUpdate = false;
+ };
+
+ vmidField.on('change', function(f, value) {
+ var mode = selModeField.getValue();
+ update_vmid_selection(value, mode);
+ });
+
+ selModeField.on('change', function(f, value, oldValue) {
+ if (value === 'all') {
+ sm.selectAll(true);
+ vmgrid.setDisabled(true);
+ } else {
+ vmgrid.setDisabled(false);
+ }
+ if (oldValue === 'all') {
+ sm.deselectAll(true);
+ vmidField.setValue('');
+ }
+ var list = vmidField.getValue();
+ update_vmid_selection(list, value);
+ });
+
+ var reload = function() {
+ store.load({
+ params: { type: 'vm' },
+ callback: function() {
+ var node = nodesel.getValue();
+ store.clearFilter();
+ store.filterBy(function(rec) {
+ return (!node || rec.get('node') === node);
+ });
+ var list = vmidField.getValue();
+ var mode = selModeField.getValue();
+ if (mode === 'all') {
+ sm.selectAll(true);
+ } else {
+ update_vmid_selection(list, mode);
+ }
+ }
+ });
+ };
+
+ Ext.applyIf(me, {
+ subject: gettext("Backup Job"),
+ url: url,
+ method: method,
+ items: [ ipanel, vmgrid ]
+ });
+
+ me.callParent();
+
+ if (me.create) {
+ selModeField.setValue('include');
+ } else {
+ me.load({
+ success: function(response, options) {
+ var data = response.result.data;
+
+ data.dow = data.dow.split(',');
+
+ if (data.all || data.exclude) {
+ if (data.exclude) {
+ data.vmid = data.exclude;
+ data.selMode = 'exclude';
+ } else {
+ data.vmid = '';
+ data.selMode = 'all';
+ }
+ } else {
+ data.selMode = 'include';
+ }
+
+ me.setValues(data);
+ }
+ });
+ }
+
+ reload();
+ }
+});
+
+
+Ext.define('PVE.dc.BackupView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveDcBackupView'],
+
+ allText: '-- ' + gettext('All') + ' --',
+ allExceptText: gettext('All except {0}'),
+
+ initComponent : function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-cluster-backup',
+ proxy: {
+ type: 'pve',
+ url: "/api2/json/cluster/backup"
+ }
+ });
+
+ var reload = function() {
+ store.load();
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+
+ var win = Ext.create('PVE.dc.BackupEdit',{
+ jobid: rec.data.id
+ });
+ win.on('destroy', reload);
+ win.show();
+ };
+
+ var edit_btn = new PVE.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ var remove_btn = new PVE.button.Button({
+ text: gettext('Remove'),
+ disabled: true,
+ selModel: sm,
+ confirmMsg: gettext('Are you sure you want to remove this entry'),
+ handler: function(btn, event, rec) {
+ PVE.Utils.API2Request({
+ url: '/cluster/backup/' + rec.data.id,
+ method: 'DELETE',
+ waitMsgTarget: me,
+ callback: function() {
+ reload();
+ },
+ failure: function (response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ }
+ });
+
+ PVE.Utils.monStoreErrors(me, store);
+
+ Ext.apply(me, {
+ store: store,
+ selModel: sm,
+ stateful: false,
+ viewConfig: {
+ trackOver: false
+ },
+ tbar: [
+ {
+ text: gettext('Add'),
+ handler: function() {
+ var win = Ext.create('PVE.dc.BackupEdit',{});
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ remove_btn,
+ edit_btn
+ ],
+ columns: [
+ {
+ header: gettext('Node'),
+ width: 100,
+ sortable: true,
+ dataIndex: 'node',
+ renderer: function(value) {
+ if (value) {
+ return value;
+ }
+ return me.allText;
+ }
+ },
+ {
+ header: gettext('Day of week'),
+ width: 200,
+ sortable: false,
+ dataIndex: 'dow'
+ },
+ {
+ header: gettext('Start Time'),
+ width: 60,
+ sortable: true,
+ dataIndex: 'starttime'
+ },
+ {
+ header: gettext('Storage'),
+ width: 100,
+ sortable: true,
+ dataIndex: 'storage'
+ },
+ {
+ header: gettext('Selection'),
+ flex: 1,
+ sortable: false,
+ dataIndex: 'vmid',
+ renderer: function(value, metaData, record) {
+ /*jslint confusion: true */
+ if (record.data.all) {
+ if (record.data.exclude) {
+ return Ext.String.format(me.allExceptText, record.data.exclude);
+ }
+ return me.allText;
+ }
+ if (record.data.vmid) {
+ return record.data.vmid;
+ }
+
+ return "-";
+ }
+ }
+ ],
+ listeners: {
+ show: reload,
+ itemdblclick: run_editor
+ }
+ });
+
+ me.callParent();
+ }
+}, function() {
+
+ Ext.define('pve-cluster-backup', {
+ extend: 'Ext.data.Model',
+ fields: [
+ 'id', 'starttime', 'dow',
+ 'storage', 'node', 'vmid', 'exclude',
+ 'mailto',
+ { name: 'all', type: 'boolean' },
+ { name: 'snapshot', type: 'boolean' },
+ { name: 'stop', type: 'boolean' },
+ { name: 'suspend', type: 'boolean' },
+ { name: 'compress', type: 'boolean' }
+ ]
+ });
+});
\ No newline at end of file
--- /dev/null
+/*
+ * Datacenter config panel, located in the center of the ViewPort after the Datacenter view is selected
+ */
+
+Ext.define('PVE.dc.Config', {
+ extend: 'PVE.panel.Config',
+ alias: 'widget.PVE.dc.Config',
+
+ initComponent: function() {
+ var me = this;
+
+ var caps = Ext.state.Manager.get('GuiCap');
+
+ me.items = [];
+
+ Ext.apply(me, {
+ title: gettext("Datacenter"),
+ hstateid: 'dctab'
+ });
+
+ if (caps.dc['Sys.Audit']) {
+ me.items.push({
+ title: gettext('Summary'),
+ xtype: 'pveDcSummary',
+ itemId: 'summary'
+ });
+
+ me.items.push({
+ xtype: 'pveDcOptionView',
+ title: gettext('Options'),
+ itemId: 'options'
+ });
+ }
+
+ if (caps.storage['Datastore.Allocate'] || caps.dc['Sys.Audit']) {
+ me.items.push({
+ xtype: 'pveStorageView',
+ title: gettext('Storage'),
+ itemId: 'storage'
+ });
+ }
+
+ if (caps.dc['Sys.Audit']) {
+ me.items.push({
+ xtype: 'pveDcBackupView',
+ title: gettext('Backup'),
+ itemId: 'backup'
+ });
+ }
+
+ me.items.push({
+ xtype: 'pveUserView',
+ title: gettext('Users'),
+ itemId: 'users'
+ });
+
+ if (caps.dc['Sys.Audit']) {
+ me.items.push({
+ xtype: 'pveGroupView',
+ title: gettext('Groups'),
+ itemId: 'groups'
+ });
+
+ me.items.push({
+ xtype: 'pvePoolView',
+ title: gettext('Pools'),
+ itemId: 'pools'
+ });
+
+ me.items.push({
+ xtype: 'pveACLView',
+ title: gettext('Permissions'),
+ itemId: 'permissions'
+ });
+
+ me.items.push({
+ xtype: 'pveRoleView',
+ title: gettext('Roles'),
+ itemId: 'roles'
+ });
+
+ me.items.push({
+// xtype: 'pveAuthView',
+// title: gettext('Authentication'),
+ xtype: 'gridpanel',
+ title: 'AuthenTODO',
+ itemId: 'domains'
+ });
+
+ me.items.push({
+// xtype: 'pveHAPanel',
+// title: 'HA',
+ xtype: 'gridpanel',
+ title: 'HATODO',
+ phstateid: me.hstateid,
+ itemId: 'ha'
+ });
+
+ me.items.push({
+// xtype: 'pveFirewallPanel',
+// title: gettext('Firewall'),
+ xtype: 'gridpanel',
+ title: 'FireTODO',
+ base_url: '/cluster/firewall',
+ fwtype: 'dc',
+ phstateid: me.hstateid,
+ itemId: 'firewall'
+ });
+
+ me.items.push({
+ xtype: 'pveDcSupport',
+ title: gettext('Support'),
+ itemId: 'support'
+ });
+ }
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.dc.GroupEdit', {
+ extend: 'PVE.window.Edit',
+ alias: ['widget.pveDcGroupEdit'],
+
+ initComponent : function() {
+ var me = this;
+
+ me.create = !me.groupid;
+
+ var url;
+ var method;
+
+ if (me.create) {
+ url = '/api2/extjs/access/groups';
+ method = 'POST';
+ } else {
+ url = '/api2/extjs/access/groups/' + me.groupid;
+ method = 'PUT';
+ }
+
+ Ext.applyIf(me, {
+ subject: gettext('Group'),
+ url: url,
+ method: method,
+ items: [
+ {
+ xtype: me.create ? 'pvetextfield' : 'displayfield',
+ fieldLabel: gettext('Name'),
+ name: 'groupid',
+ value: me.groupid,
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ fieldLabel: gettext('Comment'),
+ name: 'comment',
+ allowBlank: true
+ }
+ ]
+ });
+
+ me.callParent();
+
+ if (!me.create) {
+ me.load();
+ }
+ }
+});
--- /dev/null
+Ext.define('PVE.dc.GroupView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveGroupView'],
+
+ initComponent : function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-groups',
+ sorters: {
+ property: 'groupid',
+ order: 'DESC'
+ }
+ });
+
+ var reload = function() {
+ store.load();
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var remove_btn = new PVE.button.Button({
+ text: gettext('Remove'),
+ disabled: true,
+ selModel: sm,
+ confirmMsg: function (rec) {
+ return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
+ "'" + rec.data.groupid + "'");
+ },
+ handler: function(btn, event, rec) {
+ PVE.Utils.API2Request({
+ url: '/access/groups/' + rec.data.groupid,
+ method: 'DELETE',
+ waitMsgTarget: me,
+ callback: function() {
+ reload();
+ },
+ failure: function (response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ }
+ });
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+
+ var win = Ext.create('PVE.dc.GroupEdit',{
+ groupid: rec.data.groupid
+ });
+ win.on('destroy', reload);
+ win.show();
+ };
+
+ var edit_btn = new PVE.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ var tbar = [
+ {
+ text: gettext('Create'),
+ handler: function() {
+ var win = Ext.create('PVE.dc.GroupEdit', {});
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ edit_btn, remove_btn
+ ];
+
+ PVE.Utils.monStoreErrors(me, store);
+
+ Ext.apply(me, {
+ store: store,
+ selModel: sm,
+ stateful: false,
+ tbar: tbar,
+ viewConfig: {
+ trackOver: false
+ },
+ columns: [
+ {
+ header: gettext('Name'),
+ width: 200,
+ sortable: true,
+ dataIndex: 'groupid'
+ },
+ {
+ header: gettext('Comment'),
+ sortable: false,
+ dataIndex: 'comment',
+ flex: 1
+ }
+ ],
+ listeners: {
+ show: reload,
+ itemdblclick: run_editor
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+/* This class defines the "Cluster log" tab of the bottom status panel
+ * A log entry is a timestamp associated with an action on a cluster
+ */
+
+Ext.define('PVE.dc.Log', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveClusterLog'],
+
+ initComponent : function() {
+ var me = this;
+
+ var logstore = new PVE.data.UpdateStore({
+ storeid: 'pve-cluster-log',
+ model: 'pve-cluster-log',
+ proxy: {
+ type: 'pve',
+ url: '/api2/json/cluster/log'
+ }
+ });
+
+ var store = Ext.create('PVE.data.DiffStore', {
+ rstore: logstore,
+ appendAtStart: true
+ });
+
+ Ext.apply(me, {
+ store: store,
+ stateful: false,
+
+ viewConfig: {
+ trackOver: false,
+ stripeRows: true,
+
+ getRowClass: function(record, index) {
+ var pri = record.get('pri');
+
+ if (pri && pri <= 3) {
+ return "x-form-invalid-field";
+ }
+ }
+ },
+ sortableColumns: false,
+ columns: [
+ {
+ header: gettext("Time"),
+ dataIndex: 'time',
+ width: 150,
+ renderer: function(value) {
+ return Ext.Date.format(value, "M d H:i:s");
+ }
+ },
+ {
+ header: gettext("Node"),
+ dataIndex: 'node',
+ width: 150
+ },
+ {
+ header: gettext("Service"),
+ dataIndex: 'tag',
+ width: 100
+ },
+ {
+ header: "PID",
+ dataIndex: 'pid',
+ width: 100
+ },
+ {
+ header: gettext("User name"),
+ dataIndex: 'user',
+ width: 150
+ },
+ {
+ header: gettext("Severity"),
+ dataIndex: 'pri',
+ renderer: PVE.Utils.render_serverity,
+ width: 100
+ },
+ {
+ header: gettext("Message"),
+ dataIndex: 'msg',
+ flex: 1
+ }
+ ],
+ listeners: {
+ show: logstore.startUpdate,
+ hide: logstore.stopUpdate,
+ destroy: logstore.stopUpdate
+ }
+ });
+
+ me.callParent();
+ }
+});
\ No newline at end of file
--- /dev/null
+Ext.define('PVE.dc.HttpProxyEdit', {
+ extend: 'PVE.window.Edit',
+
+ initComponent : function() {
+ var me = this;
+
+ Ext.applyIf(me, {
+ subject: gettext('HTTP proxy'),
+ items: {
+ xtype: 'pvetextfield',
+ name: 'http_proxy',
+ vtype: 'HttpProxy',
+ emptyText: gettext('Do not use any proxy'),
+ deleteEmpty: true,
+ value: '',
+ fieldLabel: gettext('HTTP proxy')
+ }
+ });
+
+ me.callParent();
+
+ me.load();
+ }
+});
+
+Ext.define('PVE.dc.KeyboardEdit', {
+ extend: 'PVE.window.Edit',
+
+ initComponent : function() {
+ var me = this;
+
+ Ext.applyIf(me, {
+ subject: gettext('Keyboard Layout'),
+ items: {
+ xtype: 'VNCKeyboardSelector',
+ name: 'keyboard',
+ value: '',
+ fieldLabel: gettext('Keyboard Layout')
+ }
+ });
+
+ me.callParent();
+
+ me.load();
+ }
+});
+
+Ext.define('PVE.dc.ConsoleViewerEdit', {
+ extend: 'PVE.window.Edit',
+
+ initComponent : function() {
+ var me = this;
+
+ var data = [];
+
+ Ext.Array.each(['', 'vv', 'html5'], function(value) {
+ data.push([value, PVE.Utils.render_console_viewer(value)]);
+ });
+
+ Ext.applyIf(me, {
+ subject: gettext('Console Viewer'),
+ items: {
+ xtype: 'pveKVComboBox',
+ name: 'console',
+ value: '',
+ fieldLabel: gettext('Console Viewer'),
+ comboItems: data,
+ }
+ });
+
+ me.callParent();
+
+ me.load();
+ }
+});
+
+Ext.define('PVE.dc.EmailFromEdit', {
+ extend: 'PVE.window.Edit',
+
+ initComponent : function() {
+ var me = this;
+
+ Ext.applyIf(me, {
+ subject: gettext('Email from address'),
+ items: {
+ xtype: 'pvetextfield',
+ name: 'email_from',
+ vtype: 'email',
+ emptyText: gettext('Send emails from root@$hostname'),
+ deleteEmpty: true,
+ value: '',
+ fieldLabel: gettext('Email from address')
+ }
+ });
+
+ me.callParent();
+
+ me.load();
+ }
+});
+
+Ext.define('PVE.dc.OptionView', {
+ extend: 'PVE.grid.ObjectGrid',
+ alias: ['widget.pveDcOptionView'],
+
+ noProxyText: gettext('Do not use any proxy'),
+ noEmailFromText: gettext('Send emails from root@$hostname'),
+
+ initComponent : function() {
+ var me = this;
+
+ var reload = function() {
+ me.rstore.load();
+ };
+
+ var rows = {
+ keyboard: {
+ header: gettext('Keyboard Layout'),
+ editor: 'PVE.dc.KeyboardEdit',
+ renderer: PVE.Utils.render_kvm_language,
+ required: true
+ },
+ http_proxy: {
+ header: gettext('HTTP proxy'),
+ editor: 'PVE.dc.HttpProxyEdit',
+ required: true,
+ renderer: function(value) {
+ if (!value) {
+ return me.noProxyText;
+ }
+ return value;
+ }
+ },
+ console: {
+ header: gettext('Console Viewer'),
+ editor: 'PVE.dc.ConsoleViewerEdit',
+ required: true,
+ renderer: PVE.Utils.render_console_viewer
+ },
+ email_from: {
+ header: gettext('Email from address'),
+ editor: 'PVE.dc.EmailFromEdit',
+ required: true,
+ renderer: function(value) {
+ if (!value) {
+ return me.noEmailFromText;
+ }
+ return value;
+ }
+ }
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+
+ var rowdef = rows[rec.data.key];
+ if (!rowdef.editor) {
+ return;
+ }
+
+ var win = Ext.create(rowdef.editor, {
+ url: "/api2/extjs/cluster/options",
+ confid: rec.data.key
+ });
+ win.show();
+ win.on('destroy', reload);
+ };
+
+ var edit_btn = new PVE.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ Ext.applyIf(me, {
+ url: "/api2/json/cluster/options",
+ cwidth1: 130,
+ interval: 1000,
+ selModel: sm,
+ tbar: [ edit_btn ],
+ rows: rows,
+ listeners: {
+ itemdblclick: run_editor
+ }
+ });
+
+ me.callParent();
+
+ me.on('show', reload);
+ }
+});
--- /dev/null
+Ext.define('PVE.dc.PoolEdit', {
+ extend: 'PVE.window.Edit',
+ alias: ['widget.pveDcPoolEdit'],
+
+ initComponent : function() {
+ var me = this;
+
+ me.create = !me.poolid;
+
+ var url;
+ var method;
+
+ if (me.create) {
+ url = '/api2/extjs/pools';
+ method = 'POST';
+ } else {
+ url = '/api2/extjs/pools/' + me.poolid;
+ method = 'PUT';
+ }
+
+ Ext.applyIf(me, {
+ subject: gettext('Pool'),
+ url: url,
+ method: method,
+ items: [
+ {
+ xtype: me.create ? 'pvetextfield' : 'displayfield',
+ fieldLabel: gettext('Name'),
+ name: 'poolid',
+ value: me.poolid,
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ fieldLabel: gettext('Comment'),
+ name: 'comment',
+ allowBlank: true
+ }
+ ]
+ });
+
+ me.callParent();
+
+ if (!me.create) {
+ me.load();
+ }
+ }
+});
--- /dev/null
+Ext.define('PVE.dc.PoolView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pvePoolView'],
+
+ initComponent : function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-pools',
+ sorters: {
+ property: 'poolid',
+ order: 'DESC'
+ }
+ });
+
+ var reload = function() {
+ store.load();
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var remove_btn = new PVE.button.Button({
+ text: gettext('Remove'),
+ disabled: true,
+ selModel: sm,
+ confirmMsg: function (rec) {
+ return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
+ "'" + rec.data.poolid + "'");
+ },
+ handler: function(btn, event, rec) {
+ PVE.Utils.API2Request({
+ url: '/pools/' + rec.data.poolid,
+ method: 'DELETE',
+ waitMsgTarget: me,
+ callback: function() {
+ reload();
+ },
+ failure: function (response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ }
+ });
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+
+ var win = Ext.create('PVE.dc.PoolEdit',{
+ poolid: rec.data.poolid
+ });
+ win.on('destroy', reload);
+ win.show();
+ };
+
+ var edit_btn = new PVE.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ var tbar = [
+ {
+ text: gettext('Create'),
+ handler: function() {
+ var win = Ext.create('PVE.dc.PoolEdit', {});
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ edit_btn, remove_btn
+ ];
+
+ PVE.Utils.monStoreErrors(me, store);
+
+ Ext.apply(me, {
+ store: store,
+ selModel: sm,
+ stateful: false,
+ tbar: tbar,
+ viewConfig: {
+ trackOver: false
+ },
+ columns: [
+ {
+ header: gettext('Name'),
+ width: 200,
+ sortable: true,
+ dataIndex: 'poolid'
+ },
+ {
+ header: gettext('Comment'),
+ sortable: false,
+ dataIndex: 'comment',
+ flex: 1
+ }
+ ],
+ listeners: {
+ show: reload,
+ itemdblclick: run_editor
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.dc.RoleView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveRoleView'],
+
+ initComponent : function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-roles',
+ sorters: {
+ property: 'roleid',
+ order: 'DESC'
+ }
+ });
+
+ var render_privs = function(value, metaData) {
+
+ if (!value) {
+ return '-';
+ }
+
+ // allow word wrap
+ metaData.style = 'white-space:normal;';
+
+ return value.replace(/\,/g, ' ');
+ };
+
+ PVE.Utils.monStoreErrors(me, store);
+
+ Ext.apply(me, {
+ store: store,
+ stateful: false,
+
+ viewConfig: {
+ trackOver: false
+ },
+ columns: [
+ {
+ header: gettext('Name'),
+ width: 150,
+ sortable: true,
+ dataIndex: 'roleid'
+ },
+ {
+ id: 'privs',
+ header: gettext('Privileges'),
+ sortable: false,
+ renderer: render_privs,
+ dataIndex: 'privs',
+ flex: 1
+ }
+ ],
+ listeners: {
+ show: function() {
+ store.load();
+ }
+ }
+ });
+
+ me.callParent();
+ }
+});
\ No newline at end of file
--- /dev/null
+Ext.define('PVE.SecurityGroupEdit', {
+ extend: 'PVE.window.Edit',
+
+ base_url: "/cluster/firewall/groups",
+
+ allow_iface: false,
+
+ initComponent : function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ me.create = (me.group_name === undefined);
+
+ var subject;
+
+ me.url = '/api2/extjs' + me.base_url;
+ me.method = 'POST';
+
+ var items = [
+ {
+ xtype: 'textfield',
+ name: 'group',
+ value: me.group_name || '',
+ fieldLabel: gettext('Name'),
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ name: 'comment',
+ value: me.group_comment || '',
+ fieldLabel: gettext('Comment')
+ }
+ ];
+
+ if (me.create) {
+ subject = gettext('Security Group');
+ } else {
+ subject = gettext('Security Group') + " '" + me.group_name + "'";
+ items.push({
+ xtype: 'hiddenfield',
+ name: 'rename',
+ value: me.group_name
+ });
+ }
+
+ var ipanel = Ext.create('PVE.panel.InputPanel', {
+ create: me.create,
+ items: items
+ });
+
+
+ Ext.apply(me, {
+ subject: subject,
+ items: [ ipanel ]
+ });
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.SecurityGroupList', {
+ extend: 'Ext.grid.Panel',
+ alias: 'widget.pveSecurityGroupList',
+
+ rule_panel: undefined,
+
+ addBtn: undefined,
+ removeBtn: undefined,
+ editBtn: undefined,
+
+ base_url: "/cluster/firewall/groups",
+
+ initComponent: function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ if (me.rule_panel == undefined) {
+ throw "no rule panel specified";
+ }
+
+ if (me.base_url == undefined) {
+ throw "no base_url specified";
+ }
+
+ var store = new Ext.data.Store({
+ fields: [ 'group', 'comment', 'digest' ],
+ proxy: {
+ type: 'pve',
+ url: '/api2/json' + me.base_url
+ },
+ idProperty: 'group',
+ sorters: {
+ property: 'group',
+ order: 'DESC'
+ }
+ });
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var reload = function() {
+ var oldrec = sm.getSelection()[0];
+ store.load(function(records, operation, success) {
+ if (oldrec) {
+ var rec = store.findRecord('group', oldrec.data.group);
+ if (rec) {
+ sm.select(rec);
+ }
+ }
+ });
+ };
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+ var win = Ext.create('PVE.SecurityGroupEdit', {
+ digest: rec.data.digest,
+ group_name: rec.data.group,
+ group_comment: rec.data.comment
+ });
+ win.show();
+ win.on('destroy', reload);
+ };
+
+ me.editBtn = new PVE.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ me.addBtn = new PVE.button.Button({
+ text: gettext('Create'),
+ handler: function() {
+ sm.deselectAll();
+ var win = Ext.create('PVE.SecurityGroupEdit', {});
+ win.show();
+ win.on('destroy', reload);
+ }
+ });
+
+ me.removeBtn = new PVE.button.Button({
+ text: gettext('Remove'),
+ selModel: sm,
+ disabled: true,
+ handler: function() {
+ var rec = sm.getSelection()[0];
+ if (!rec || !me.base_url) {
+ return;
+ }
+ PVE.Utils.API2Request({
+ url: me.base_url + '/' + rec.data.group,
+ method: 'DELETE',
+ waitMsgTarget: me,
+ failure: function(response, options) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ callback: reload
+ });
+ }
+ });
+
+ Ext.apply(me, {
+ store: store,
+ tbar: [ '<b>' + gettext('Group') + ':</b>', me.addBtn, me.removeBtn, me.editBtn ],
+ selModel: sm,
+ columns: [
+ { header: gettext('Group'), dataIndex: 'group', width: 100 },
+ { header: gettext('Comment'), dataIndex: 'comment', flex: 1 }
+ ],
+ listeners: {
+ itemdblclick: run_editor,
+ select: function(sm, rec) {
+ var url = '/cluster/firewall/groups/' + rec.data.group;
+ me.rule_panel.setBaseUrl(url);
+ },
+ deselect: function() {
+ me.rule_panel.setBaseUrl(undefined);
+ },
+ show: reload
+ }
+ });
+
+ me.callParent();
+
+ store.load();
+ }
+});
+
+Ext.define('PVE.SecurityGroups', {
+ extend: 'Ext.panel.Panel',
+ alias: 'widget.pveSecurityGroups',
+
+ title: 'Security Groups',
+
+ initComponent: function() {
+ var me = this;
+
+ var rule_panel = Ext.createWidget('pveFirewallRules', {
+ region: 'center',
+ allow_groups: false,
+ list_refs_url: '/cluster/firewall/refs',
+ tbar_prefix: '<b>' + gettext('Rules') + ':</b>',
+ flex: 0.75,
+ border: false
+ });
+
+ var sglist = Ext.createWidget('pveSecurityGroupList', {
+ region: 'west',
+ rule_panel: rule_panel,
+ flex: 0.25,
+ border: false,
+ split: true
+ });
+
+
+ Ext.apply(me, {
+ layout: 'border',
+ items: [ sglist, rule_panel ],
+ listeners: {
+ show: function() {
+ sglist.fireEvent('show', sglist);
+ }
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+
+Ext.define('PVE.dc.StorageView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveStorageView'],
+
+ initComponent : function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-storage',
+ proxy: {
+ type: 'pve',
+ url: "/api2/json/storage"
+ },
+ sorters: {
+ property: 'storage',
+ order: 'DESC'
+ }
+ });
+
+ var reload = function() {
+ store.load();
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+ var type = rec.data.type;
+
+ var editor;
+ if (type === 'dir') {
+ editor = 'PVE.storage.DirEdit';
+ } else if (type === 'nfs') {
+ editor = 'PVE.storage.NFSEdit';
+ } else if (type === 'glusterfs') {
+ editor = 'PVE.storage.GlusterFsEdit';
+ } else if (type === 'lvm') {
+ editor = 'PVE.storage.LVMEdit';
+ } else if (type === 'iscsi') {
+ editor = 'PVE.storage.IScsiEdit';
+ } else if (type === 'rbd') {
+ editor = 'PVE.storage.RBDEdit';
+ } else if (type === 'sheepdog') {
+ editor = 'PVE.storage.SheepdogEdit';
+ } else if (type === 'zfs') {
+ editor = 'PVE.storage.ZFSEdit';
+ } else if (type === 'zfspool') {
+ editor = 'PVE.storage.ZFSPoolEdit';
+ } else {
+ return;
+ }
+ var win = Ext.create(editor, {
+ storageId: rec.data.storage
+ });
+
+ win.show();
+ win.on('destroy', reload);
+ };
+
+ var edit_btn = new PVE.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ var remove_btn = new PVE.button.Button({
+ text: gettext('Remove'),
+ disabled: true,
+ selModel: sm,
+ confirmMsg: function (rec) {
+ return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
+ "'" + rec.data.storage + "'");
+ },
+ handler: function(btn, event, rec) {
+ PVE.Utils.API2Request({
+ url: '/storage/' + rec.data.storage,
+ method: 'DELETE',
+ waitMsgTarget: me,
+ callback: function() {
+ reload();
+ },
+ failure: function (response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ }
+ });
+
+ Ext.apply(me, {
+ store: store,
+ selModel: sm,
+ stateful: false,
+ viewConfig: {
+ trackOver: false
+ },
+ tbar: [
+ {
+ text: gettext('Add'),
+ menu: new Ext.menu.Menu({
+ items: [
+ {
+ text: PVE.Utils.format_storage_type('dir'),
+ iconCls: 'pve-itype-icon-itype',
+ handler: function() {
+ var win = Ext.create('PVE.storage.DirEdit', {});
+ win.on('destroy', reload);
+ win.show();
+ }
+
+ },
+ {
+ text: PVE.Utils.format_storage_type('lvm'),
+ iconCls: 'pve-itype-icon-storage',
+ handler: function() {
+ var win = Ext.create('PVE.storage.LVMEdit', {});
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ {
+ text: PVE.Utils.format_storage_type('nfs'),
+ iconCls: 'pve-itype-icon-network-server',
+ handler: function() {
+ var win = Ext.create('PVE.storage.NFSEdit', {});
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ {
+ text: PVE.Utils.format_storage_type('iscsi'),
+ iconCls: 'pve-itype-icon-network-server',
+ handler: function() {
+ var win = Ext.create('PVE.storage.IScsiEdit', {});
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ {
+ text: PVE.Utils.format_storage_type('glusterfs'),
+ iconCls: 'pve-itype-icon-network-server',
+ handler: function() {
+ var win = Ext.create('PVE.storage.GlusterFsEdit', {});
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ {
+ text: PVE.Utils.format_storage_type('rbd'),
+ iconCls: 'pve-itype-icon-network-server',
+ handler: function() {
+ var win = Ext.create('PVE.storage.RBDEdit', {});
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ {
+ text: PVE.Utils.format_storage_type('zfs'),
+ iconCls: 'pve-itype-icon-network-server',
+ handler: function() {
+ var win = Ext.create('PVE.storage.ZFSEdit', {});
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ {
+ text: PVE.Utils.format_storage_type('zfspool'),
+ iconCls: 'pve-itype-icon-storage',
+ handler: function() {
+ var win = Ext.create('PVE.storage.ZFSPoolEdit', {});
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+
+/* the following type are conidered unstable
+ * so we do not enable that on the GUI for now
+ {
+ text: PVE.Utils.format_storage_type('sheepdog'),
+ iconCls: 'pve-itype-icon-network-server',
+ handler: function() {
+ var win = Ext.create('PVE.storage.SheepdogEdit', {});
+ win.on('destroy', reload);
+ win.show();
+ }
+ }
+*/
+ ]
+ })
+ },
+ remove_btn,
+ edit_btn
+ ],
+ columns: [
+ {
+ header: 'ID',
+ width: 100,
+ sortable: true,
+ dataIndex: 'storage'
+ },
+ {
+ header: gettext('Type'),
+ width: 60,
+ sortable: true,
+ dataIndex: 'type',
+ renderer: PVE.Utils.format_storage_type
+ },
+ {
+ header: gettext('Content'),
+ width: 150,
+ sortable: true,
+ dataIndex: 'content',
+ renderer: PVE.Utils.format_content_types
+ },
+ {
+ header: gettext('Path') + '/' + gettext('Target'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'path',
+ renderer: function(value, metaData, record) {
+ if (record.data.target) {
+ return record.data.target;
+ }
+ return value;
+ }
+ },
+ {
+ header: gettext('Shared'),
+ width: 80,
+ sortable: true,
+ dataIndex: 'shared',
+ renderer: PVE.Utils.format_boolean
+ },
+ {
+ header: gettext('Enable'),
+ width: 80,
+ sortable: true,
+ dataIndex: 'disable',
+ renderer: PVE.Utils.format_neg_boolean
+ }
+ ],
+ listeners: {
+ show: reload,
+ itemdblclick: run_editor
+ }
+ });
+
+ me.callParent();
+ }
+}, function() {
+
+ Ext.define('pve-storage', {
+ extend: 'Ext.data.Model',
+ fields: [
+ 'path', 'type', 'content', 'server', 'portal', 'target', 'export', 'storage',
+ { name: 'shared', type: 'boolean'},
+ { name: 'disable', type: 'boolean'}
+ ],
+ idProperty: 'storage'
+ });
+
+});
--- /dev/null
+Ext.define('PVE.dc.NodeView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveDcNodeView'],
+
+ initComponent : function() {
+ var me = this;
+
+ var rstore = Ext.create('PVE.data.UpdateStore', {
+ interval: 3000,
+ storeid: 'pve-dc-nodes',
+ model: 'pve-dc-nodes',
+ proxy: {
+ type: 'pve',
+ url: "/api2/json/cluster/status"
+ },
+ filters: {
+ property: 'type',
+ value : 'node'
+ }
+ });
+
+ var store = Ext.create('PVE.data.DiffStore', { rstore: rstore });
+
+ var noClusterText = gettext("Standalone node - no cluster defined");
+ var status = Ext.create('Ext.Component', {
+ padding: 2,
+ html: ' ',
+ dock: 'bottom'
+ });
+
+ Ext.apply(me, {
+ store: store,
+ stateful: false,
+ bbar: [ status ],
+ columns: [
+ {
+ header: gettext('Name'),
+ width: 200,
+ sortable: true,
+ dataIndex: 'name'
+ },
+ {
+ header: 'ID',
+ width: 50,
+ sortable: true,
+ dataIndex: 'nodeid'
+ },
+ {
+ header: gettext('Online'),
+ width: 100,
+ sortable: true,
+ dataIndex: 'online',
+ renderer: PVE.Utils.format_boolean
+ },
+ {
+ header: gettext('Support'),
+ width: 100,
+ sortable: true,
+ dataIndex: 'level',
+ renderer: PVE.Utils.render_support_level
+ },
+ {
+ header: gettext('Server Address'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'ip'
+ }
+ ],
+ listeners: {
+ show: rstore.startUpdate,
+ hide: rstore.stopUpdate,
+ destroy: rstore.stopUpdate
+ }
+ });
+
+ me.callParent();
+
+ rstore.on('load', function(s, records, success) {
+ if (!success) {
+ return;
+ }
+
+ var cluster_rec = rstore.getById('cluster');
+
+ if (!cluster_rec) {
+ status.update(noClusterText);
+ return;
+ }
+
+ var cluster_raw = cluster_rec.raw;
+ if (!cluster_raw) {
+ status.update(noClusterText);
+ return;
+ }
+ var text = gettext("Cluster") + ": " + cluster_raw.name + ", " +
+ gettext("Quorate") + ": " + PVE.Utils.format_boolean(cluster_raw.quorate);
+ status.update(text);
+ });
+
+ }
+}, function() {
+
+ Ext.define('pve-dc-nodes', {
+ extend: 'Ext.data.Model',
+ fields: [ 'id', 'type', 'name', 'nodeid', 'ip', 'level', 'local', 'online'],
+ idProperty: 'id'
+ });
+
+});
+
+Ext.define('PVE.dc.Summary', {
+ extend: 'Ext.panel.Panel',
+
+ alias: ['widget.pveDcSummary'],
+
+ initComponent: function() {
+ var me = this;
+
+ var nodegrid = Ext.create('PVE.dc.NodeView', {
+ title: gettext('Nodes'),
+ border: false,
+ region: 'center',
+ flex: 3
+ });
+
+ Ext.apply(me, {
+ layout: 'border',
+ items: [ nodegrid ],
+ listeners: {
+ show: function() {
+ nodegrid.fireEvent('show', nodegrid);
+ },
+ hide: function() {
+ nodegrid.fireEvent('hide', nodegrid);
+ }
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.dc.Support', {
+ extend: 'Ext.panel.Panel',
+ alias: 'widget.pveDcSupport',
+
+ invalidHtml: '<h1>No valid subscription</h1>' + PVE.Utils.noSubKeyHtml,
+
+ communityHtml: 'Please use the public community <a target="_blank" href="http://forum.proxmox.com">forum</a> for any questions.',
+
+ activeHtml: 'Please use our <a target="_blank" href="https://my.proxmox.com">support portal</a> for any questions. You can also use the public community <a target="_blank" href="http://forum.proxmox.com">forum</a> to get additional information.',
+
+ bugzillaHtml: '<h1>Bug Tracking</h1>Our bug tracking system is available <a target="_blank" href="https://bugzilla.proxmox.com">here</a>.',
+
+ docuHtml: '<h1>Documentation</h1>Complete documentation, tutorials, videos and more is available at our <a target="_blank" href="http://pve.proxmox.com/wiki/Documentation">wiki</a>.',
+
+ updateActive: function(data) {
+ var me = this;
+
+ var html = '<h1>' + data.productname + '</h1>' + me.activeHtml;
+ html += '<br><br>' + me.docuHtml;
+ html += '<br><br>' + me.bugzillaHtml;
+
+ me.update(html);
+ },
+
+ updateCommunity: function(data) {
+ var me = this;
+
+ var html = '<h1>' + data.productname + '</h1>' + me.communityHtml;
+ html += '<br><br>' + me.docuHtml;
+ html += '<br><br>' + me.bugzillaHtml;
+
+ me.update(html);
+ },
+
+ updateInactive: function(data) {
+ var me = this;
+ me.update(me.invalidHtml);
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ var reload = function() {
+ PVE.Utils.API2Request({
+ url: '/nodes/localhost/subscription',
+ method: 'GET',
+ waitMsgTarget: me,
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ me.update(gettext('Unable to load subscription status') + ": " + response.htmlStatus);
+ },
+ success: function(response, opts) {
+ var data = response.result.data;
+
+ if (data.status === 'Active') {
+ if (data.level === 'c') {
+ me.updateCommunity(data);
+ } else {
+ me.updateActive(data);
+ }
+ } else {
+ me.updateInactive(data);
+ }
+ }
+ });
+ };
+
+ Ext.apply(me, {
+ autoScroll: true,
+ bodyStyle: 'padding:10px',
+ listeners: {
+ show: reload
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+/* This class defines the "Tasks" tab of the bottom status panel
+ * Tasks are jobs with a start, end and log output
+ */
+
+Ext.define('PVE.dc.Tasks', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveClusterTasks'],
+
+ initComponent : function() {
+ var me = this;
+
+ var taskstore = new PVE.data.UpdateStore({
+ storeid: 'pve-cluster-tasks',
+ model: 'pve-tasks',
+ proxy: {
+ type: 'pve',
+ url: '/api2/json/cluster/tasks'
+ }
+ });
+
+ var store = Ext.create('PVE.data.DiffStore', {
+ rstore: taskstore,
+ sortAfterUpdate: true,
+ appendAtStart: true,
+ sorters: [
+ {
+ property : 'pid',
+ direction: 'DESC'
+ },
+ {
+ property : 'starttime',
+ direction: 'DESC'
+ }
+ ]
+
+ });
+
+ var run_task_viewer = function() {
+ var sm = me.getSelectionModel();
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+
+ var win = Ext.create('PVE.window.TaskViewer', {
+ upid: rec.data.upid
+ });
+ win.show();
+ };
+
+ Ext.apply(me, {
+ store: store,
+ stateful: false,
+
+ viewConfig: {
+ trackOver: false,
+ stripeRows: true, // does not work with getRowClass()
+
+ getRowClass: function(record, index) {
+ var status = record.get('status');
+
+ if (status && status != 'OK') {
+ return "x-form-invalid-field";
+ }
+ }
+ },
+ sortableColumns: false,
+ columns: [
+ {
+ header: gettext("Start Time"),
+ dataIndex: 'starttime',
+ width: 150,
+ renderer: function(value) {
+ return Ext.Date.format(value, "M d H:i:s");
+ }
+ },
+ {
+ header: gettext("End Time"),
+ dataIndex: 'endtime',
+ width: 150,
+ renderer: function(value, metaData, record) {
+ if (record.data.pid) {
+ if (record.data.type == "vncproxy" ||
+ record.data.type == "vncshell" ||
+ record.data.type == "spiceproxy") {
+ metaData.tdCls = "x-grid-row-console";
+ } else {
+ metaData.tdCls = "x-grid-row-loading";
+ }
+ return "";
+ }
+ return Ext.Date.format(value, "M d H:i:s");
+ }
+ },
+ {
+ header: gettext("Node"),
+ dataIndex: 'node',
+ width: 100
+ },
+ {
+ header: gettext("User name"),
+ dataIndex: 'user',
+ width: 150
+ },
+ {
+ header: gettext("Description"),
+ dataIndex: 'upid',
+ flex: 1,
+ renderer: PVE.Utils.render_upid
+ },
+ {
+ header: gettext("Status"),
+ dataIndex: 'status',
+ width: 200,
+ renderer: function(value, metaData, record) {
+ if (record.data.pid) {
+ if (record.data.type != "vncproxy") {
+ metaData.tdCls = "x-grid-row-loading";
+ }
+ return "";
+ }
+ if (value == 'OK') {
+ return 'OK';
+ }
+ // metaData.attr = 'style="color:red;"';
+ return PVE.Utils.errorText + ': ' + value;
+ }
+ }
+ ],
+ listeners: {
+ itemdblclick: run_task_viewer,
+ show: taskstore.startUpdate,
+ hide: taskstore.stopUpdate,
+ destroy: taskstore.stopUpdate
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.dc.UserEdit', {
+ extend: 'PVE.window.Edit',
+ alias: ['widget.pveDcUserEdit'],
+
+ isAdd: true,
+
+ initComponent : function() {
+ var me = this;
+
+ me.create = !me.userid;
+
+ var url;
+ var method;
+ var realm;
+
+ if (me.create) {
+ url = '/api2/extjs/access/users';
+ method = 'POST';
+ } else {
+ url = '/api2/extjs/access/users/' + me.userid;
+ method = 'PUT';
+ }
+
+ var verifypw;
+ var pwfield;
+
+ var validate_pw = function() {
+ if (verifypw.getValue() !== pwfield.getValue()) {
+ return gettext("Passwords does not match");
+ }
+ return true;
+ };
+
+ verifypw = Ext.createWidget('textfield', {
+ inputType: 'password',
+ fieldLabel: gettext('Confirm password'),
+ name: 'verifypassword',
+ submitValue: false,
+ disabled: true,
+ hidden: true,
+ validator: validate_pw
+ });
+
+ pwfield = Ext.createWidget('textfield', {
+ inputType: 'password',
+ fieldLabel: gettext('Password'),
+ minLength: 5,
+ name: 'password',
+ disabled: true,
+ hidden: true,
+ validator: validate_pw
+ });
+
+ var update_passwd_field = function(realm) {
+ if (realm === 'pve') {
+ pwfield.setVisible(true);
+ pwfield.setDisabled(false);
+ verifypw.setVisible(true);
+ verifypw.setDisabled(false);
+ } else {
+ pwfield.setVisible(false);
+ pwfield.setDisabled(true);
+ verifypw.setVisible(false);
+ verifypw.setDisabled(true);
+ }
+
+ };
+
+ var column1 = [
+ {
+ xtype: me.create ? 'textfield' : 'displayfield',
+ height: 22, // hack: set same height as text fields
+ name: 'userid',
+ fieldLabel: gettext('User name'),
+ value: me.userid,
+ allowBlank: false,
+ submitValue: me.create ? true : false
+ },
+ pwfield, verifypw,
+ {
+ xtype: 'pveGroupSelector',
+ name: 'groups',
+ multiSelect: true,
+ allowBlank: true,
+ fieldLabel: gettext('Group')
+ },
+ {
+ xtype: 'datefield',
+ name: 'expire',
+ emptyText: 'never',
+ format: 'Y-m-d',
+ submitFormat: 'U',
+ fieldLabel: gettext('Expire')
+ },
+ {
+ xtype: 'pvecheckbox',
+ fieldLabel: gettext('Enabled'),
+ name: 'enable',
+ uncheckedValue: 0,
+ defaultValue: 1,
+ checked: true
+ }
+ ];
+
+ var column2 = [
+ {
+ xtype: 'textfield',
+ name: 'firstname',
+ fieldLabel: gettext('First Name')
+ },
+ {
+ xtype: 'textfield',
+ name: 'lastname',
+ fieldLabel: gettext('Last Name')
+ },
+ {
+ xtype: 'textfield',
+ name: 'email',
+ fieldLabel: gettext('E-Mail'),
+ vtype: 'email'
+ }
+ ];
+
+ var columnB = [
+ {
+ xtype: 'textfield',
+ name: 'comment',
+ fieldLabel: gettext('Comment')
+ },
+ {
+ xtype: 'textfield',
+ name: 'keys',
+ fieldLabel: gettext('Key IDs')
+ }
+ ];
+
+ if (me.create) {
+ column1.splice(1,0,{
+ xtype: 'pveRealmComboBox',
+ name: 'realm',
+ fieldLabel: gettext('Realm'),
+ allowBlank: false,
+ matchFieldWidth: false,
+ listConfig: { width: 300 },
+ listeners: {
+ change: function(combo, newValue){
+ realm = newValue;
+ update_passwd_field(realm);
+ }
+ },
+ submitValue: false
+ });
+ }
+
+ var ipanel = Ext.create('PVE.panel.InputPanel', {
+ column1: column1,
+ column2: column2,
+ columnB: columnB,
+ onGetValues: function(values) {
+ // hack: ExtJS datefield does not submit 0, so we need to set that
+ if (!values.expire) {
+ values.expire = 0;
+ }
+
+ if (realm) {
+ values.userid = values.userid + '@' + realm;
+ }
+
+ if (!values.password) {
+ delete values.password;
+ }
+
+ return values;
+ }
+ });
+
+ Ext.applyIf(me, {
+ subject: gettext('User'),
+ url: url,
+ method: method,
+ fieldDefaults: {
+ labelWidth: 110 // for spanish translation
+ },
+ items: [ ipanel ]
+ });
+
+ me.callParent();
+
+ if (!me.create) {
+ me.load({
+ success: function(response, options) {
+ var data = response.result.data;
+ if (Ext.isDefined(data.expire)) {
+ if (data.expire) {
+ data.expire = new Date(data.expire * 1000);
+ } else {
+ // display 'never' instead of '1970-01-01'
+ data.expire = null;
+ }
+ }
+ me.setValues(data);
+ }
+ });
+ }
+ }
+});
--- /dev/null
+Ext.define('PVE.window.PasswordEdit', {
+ extend: 'PVE.window.Edit',
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.userid) {
+ throw "no userid specified";
+ }
+
+ var verifypw;
+ var pwfield;
+
+ var validate_pw = function() {
+ if (verifypw.getValue() !== pwfield.getValue()) {
+ return gettext("Passwords does not match");
+ }
+ return true;
+ };
+
+ verifypw = Ext.createWidget('textfield', {
+ inputType: 'password',
+ fieldLabel: gettext('Confirm password'),
+ name: 'verifypassword',
+ submitValue: false,
+ validator: validate_pw
+ });
+
+ pwfield = Ext.createWidget('textfield', {
+ inputType: 'password',
+ fieldLabel: gettext('Password'),
+ minLength: 5,
+ name: 'password',
+ validator: validate_pw
+ });
+
+ Ext.apply(me, {
+ subject: gettext('Password'),
+ url: '/api2/extjs/access/password',
+ items: [
+ pwfield, verifypw,
+ {
+ xtype: 'hiddenfield',
+ name: 'userid',
+ value: me.userid
+ }
+ ]
+ });
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.dc.UserView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveUserView'],
+
+ initComponent : function() {
+ var me = this;
+
+ var caps = Ext.state.Manager.get('GuiCap');
+
+ var store = new Ext.data.Store({
+ id: "users",
+ model: 'pve-users',
+ sorters: {
+ property: 'userid',
+ order: 'DESC'
+ }
+ });
+
+ var reload = function() {
+ store.load();
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var remove_btn = new PVE.button.Button({
+ text: gettext('Remove'),
+ disabled: true,
+ selModel: sm,
+ enableFn: function(rec) {
+ if (!caps.access['User.Modify']) {
+ return false;
+ }
+ return rec.data.userid !== 'root@pam';
+ },
+ confirmMsg: function (rec) {
+ return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
+ "'" + rec.data.userid + "'");
+ },
+ handler: function(btn, event, rec) {
+ var userid = rec.data.userid;
+
+ PVE.Utils.API2Request({
+ url: '/access/users/' + userid,
+ method: 'DELETE',
+ waitMsgTarget: me,
+ callback: function() {
+ reload();
+ },
+ failure: function (response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ }
+ });
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec || !caps.access['User.Modify']) {
+ return;
+ }
+
+ var win = Ext.create('PVE.dc.UserEdit',{
+ userid: rec.data.userid
+ });
+ win.on('destroy', reload);
+ win.show();
+ };
+
+ var edit_btn = new PVE.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ enableFn: function(rec) {
+ return !!caps.access['User.Modify'];
+ },
+ selModel: sm,
+ handler: run_editor
+ });
+
+ var pwchange_btn = new PVE.button.Button({
+ text: gettext('Password'),
+ disabled: true,
+ selModel: sm,
+ handler: function(btn, event, rec) {
+ var win = Ext.create('PVE.window.PasswordEdit',{
+ userid: rec.data.userid
+ });
+ win.on('destroy', reload);
+ win.show();
+ }
+ });
+
+ var tbar = [
+ {
+ text: gettext('Add'),
+ disabled: !caps.access['User.Modify'],
+ handler: function() {
+ var win = Ext.create('PVE.dc.UserEdit',{
+ });
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ edit_btn, remove_btn, pwchange_btn
+ ];
+
+ var render_full_name = function(firstname, metaData, record) {
+
+ var first = firstname || '';
+ var last = record.data.lastname || '';
+ return first + " " + last;
+ };
+
+ var render_username = function(userid) {
+ return userid.match(/^(.+)(@[^@]+)$/)[1];
+ };
+
+ var render_realm = function(userid) {
+ return userid.match(/@([^@]+)$/)[1];
+ };
+
+ Ext.apply(me, {
+ store: store,
+ selModel: sm,
+ stateful: false,
+ tbar: tbar,
+ viewConfig: {
+ trackOver: false
+ },
+ columns: [
+ {
+ header: gettext('User name'),
+ width: 200,
+ sortable: true,
+ renderer: render_username,
+ dataIndex: 'userid'
+ },
+ {
+ header: gettext('Realm'),
+ width: 100,
+ sortable: true,
+ renderer: render_realm,
+ dataIndex: 'userid'
+ },
+ {
+ header: gettext('Enabled'),
+ width: 80,
+ sortable: true,
+ renderer: PVE.Utils.format_boolean,
+ dataIndex: 'enable'
+ },
+ {
+ header: gettext('Expire'),
+ width: 80,
+ sortable: true,
+ renderer: PVE.Utils.format_expire,
+ dataIndex: 'expire'
+ },
+ {
+ header: gettext('Name'),
+ width: 150,
+ sortable: true,
+ renderer: render_full_name,
+ dataIndex: 'firstname'
+ },
+ {
+ id: 'comment',
+ header: gettext('Comment'),
+ sortable: false,
+ dataIndex: 'comment',
+ flex: 1
+ }
+ ],
+ listeners: {
+ show: reload,
+ itemdblclick: run_editor
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.BackupModeSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.pveBackupModeSelector'],
+ comboItems: [
+ ['snapshot', gettext('Snapshot')],
+ ['suspend', gettext('Suspend')],
+ ['stop', gettext('Stop')]
+ ]
+});
--- /dev/null
+Ext.define('PVE.form.BondModeSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.bondModeSelector'],
+
+ openvswitch: false,
+
+ initComponent: function() {
+ var me = this;
+
+ if (me.openvswitch) {
+ me.data = [
+ ['active-backup', 'active-backup'],
+ ['balance-slb', 'balance-slb'],
+ ['lacp-balance-slb', 'LACP (balance-slb)'],
+ ['lacp-balance-tcp', 'LACP (balance-tcp)']
+ ];
+ } else {
+ me.data = [
+ ['balance-rr', 'balance-rr'],
+ ['active-backup', 'active-backup'],
+ ['balance-xor', 'balance-xor'],
+ ['broadcast', 'broadcast'],
+ ['802.3ad', 'LACP (802.3ad)'],
+ ['balance-tlb', 'balance-tlb'],
+ ['balance-alb', 'balance-alb']
+ ];
+ }
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.form.BondPolicySelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.bondPolicySelector'],
+
+ initComponent: function() {
+ var me = this;
+ me.data = [
+ ['layer2', 'layer2'],
+ ['layer2+3', 'layer2+3'],
+ ['layer3+4', 'layer3+4']
+ ];
+
+ me.callParent();
+ }
+});
+
--- /dev/null
+// boolean type including 'Default' (delete property from file)
+Ext.define('PVE.form.Boolean', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.booleanfield'],
+
+ initComponent: function() {
+ var me = this;
+
+ me.data = [
+ ['', gettext('Default')],
+ [1, gettext('Yes')],
+ [0, gettext('No')]
+ ];
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.BridgeSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: ['widget.PVE.form.BridgeSelector'],
+
+ bridgeType: 'any_bridge', // bridge, OVSBridge or any_bridge
+
+ setNodename: function(nodename) {
+ var me = this;
+
+ if (!nodename || (me.nodename === nodename)) {
+ return;
+ }
+
+ me.nodename = nodename;
+
+ me.store.setProxy({
+ type: 'pve',
+ url: '/api2/json/nodes/' + me.nodename + '/network?type=' +
+ me.bridgeType
+ });
+
+ me.store.load();
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ var nodename = me.nodename;
+ me.nodename = undefined;
+
+ var store = Ext.create('Ext.data.Store', {
+ fields: [ 'iface', 'active', 'type' ],
+ filterOnLoad: true,
+ sorters: [
+ {
+ property : 'iface',
+ direction: 'ASC'
+ }
+ ]
+ });
+
+ Ext.apply(me, {
+ store: store,
+ valueField: 'iface',
+ displayField: 'iface',
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Bridge'),
+ dataIndex: 'iface',
+ hideable: false,
+ flex: 1
+ },
+ {
+ header: gettext('Active'),
+ width: 60,
+ dataIndex: 'active',
+ renderer: PVE.Utils.format_boolean
+ }
+ ]
+ }
+ });
+
+ me.callParent();
+
+ if (nodename) {
+ me.setNodename(nodename);
+ }
+ }
+});
+
--- /dev/null
+Ext.define('PVE.form.BusTypeSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.PVE.form.BusTypeSelector'],
+
+ noVirtIO: false,
+
+ noScsi: false,
+
+ initComponent: function() {
+ var me = this;
+
+ me.data = [['ide', 'IDE'], ['sata', 'SATA']];
+
+ if (!me.noVirtIO) {
+ me.data.push(['virtio', 'VIRTIO']);
+ }
+
+ if (!me.noScsi) {
+ me.data.push(['scsi', 'SCSI']);
+ }
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.CPUModelSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.CPUModelSelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ me.data = [
+ ['', PVE.Utils.defaultText + ' (kvm64)'],
+ ['486', '486'],
+ ['athlon', 'athlon'],
+ ['core2duo', 'core2duo'],
+ ['coreduo', 'coreduo'],
+ ['kvm32', 'kvm32'],
+ ['kvm64', 'kvm64'],
+ ['pentium', 'pentium'],
+ ['pentium2', 'pentium2'],
+ ['pentium3', 'pentium3'],
+ ['phenom', 'phenom'],
+ ['qemu32', 'qemu32'],
+ ['qemu64', 'qemu64'],
+ ['Conroe', 'Conroe'],
+ ['Penryn', 'Penryn'],
+ ['Nehalem', 'Nehalem'],
+ ['Westmere', 'Westmere'],
+ ['SandyBridge', 'SandyBridge'],
+ ['IvyBridge', 'IvyBridge'],
+ ['Haswell', 'Haswell'],
+ ['Broadwell', 'Broadwell'],
+ ['Opteron_G1', 'Opteron_G1'],
+ ['Opteron_G2', 'Opteron_G2'],
+ ['Opteron_G3', 'Opteron_G3'],
+ ['Opteron_G4', 'Opteron_G4'],
+ ['Opteron_G5', 'Opteron_G5'],
+ ['host', 'host']
+ ];
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.CacheTypeSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.CacheTypeSelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ me.data = [
+ ['', PVE.Utils.defaultText + " (" + gettext('No cache') + ")"],
+ ['directsync', 'Direct sync'],
+ ['writethrough', 'Write through'],
+ ['writeback', 'Write back'],
+ ['unsafe', 'Write back (' + gettext('unsafe') + ')'],
+ ['none', gettext('No cache')]
+ ];
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.Checkbox', {
+ extend: 'Ext.form.field.Checkbox',
+ alias: ['widget.pvecheckbox'],
+
+ defaultValue: undefined,
+
+ deleteDefaultValue: false,
+ deleteEmpty: false,
+
+ inputValue: '1',
+
+ height: 22, // hack: set same height as text fields
+
+ getSubmitData: function() {
+ var me = this,
+ data = null,
+ val;
+ if (!me.disabled && me.submitValue) {
+ val = me.getSubmitValue();
+ if (val !== null) {
+ data = {};
+ if ((val == me.defaultValue) && me.deleteDefaultValue) {
+ data['delete'] = me.getName();
+ } else {
+ data[me.getName()] = val;
+ }
+ } else if (me.deleteEmpty) {
+ data = {};
+ data['delete'] = me.getName();
+ }
+ }
+ return data;
+ },
+
+ // also accept integer 1 as true
+ setRawValue: function(value) {
+ var me = this;
+
+ if (value === 1) {
+ me.callParent([true]);
+ } else {
+ me.callParent([value]);
+ }
+ }
+
+});
\ No newline at end of file
--- /dev/null
+Ext.define('PVE.form.ComboGrid', {
+ extend: 'Ext.form.field.ComboBox',
+ alias: ['widget.PVE.form.ComboGrid'],
+
+ // this value is used as default value after load()
+ preferredValue: undefined,
+
+ computeHeight: function() {
+ var me = this;
+ var lh = PVE.Utils.gridLineHeigh();
+ var count = me.store.getTotalCount();
+ return (count > 10) ? 10*lh : 26+count*lh;
+ },
+
+ // hack: allow to select empty value
+ // seems extjs does not allow that when 'editable == false'
+ onKeyUp: function(e, t) {
+ var me = this;
+ var key = e.getKey();
+
+ if (!me.editable && me.allowBlank && !me.multiSelect &&
+ (key == e.BACKSPACE || key == e.DELETE)) {
+ me.setValue('');
+ }
+
+ me.callParent(arguments);
+ },
+
+ // copied from ComboBox
+ createPicker: function() {
+ var me = this,
+ picker,
+ menuCls = Ext.baseCSSPrefix + 'menu',
+
+ opts = Ext.apply({
+ selModel: {
+ mode: me.multiSelect ? 'SIMPLE' : 'SINGLE'
+ },
+ floating: true,
+ hidden: true,
+ ownerCt: me.ownerCt,
+ cls: me.el.up('.' + menuCls) ? menuCls : '',
+ store: me.store,
+ displayField: me.displayField,
+ focusOnToFront: false,
+ height: me.computeHeight(),
+ pageSize: me.pageSize
+ }, me.listConfig, me.defaultListConfig);
+
+ // NOTE: we simply use a grid panel
+ //picker = me.picker = Ext.create('Ext.view.BoundList', opts);
+ picker = me.picker = Ext.create('Ext.grid.Panel', opts);
+
+ // pass getNode() to the view
+ picker.getNode = function() {
+ picker.getView().getNode(arguments);
+ };
+
+ me.mon(picker, {
+ itemclick: me.onItemClick,
+ refresh: me.onListRefresh,
+ show: function() {
+ picker.setHeight(me.computeHeight());
+ me.syncSelection();
+ },
+ scope: me
+ });
+
+ me.mon(picker.getSelectionModel(), 'selectionchange', me.onListSelectionChange, me);
+
+ return picker;
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ if (me.initialConfig.editable === undefined) {
+ me.editable = false;
+ }
+
+ Ext.apply(me, {
+ queryMode: 'local',
+ matchFieldWidth: false
+ });
+
+ Ext.applyIf(me, { value: ''}); // hack: avoid ExtJS validate() bug
+
+ Ext.applyIf(me.listConfig, { width: 400 });
+
+ me.callParent();
+
+ me.store.on('beforeload', function() {
+ if (!me.isDisabled()) {
+ me.setDisabled(true);
+ me.enableAfterLoad = true;
+ }
+ });
+
+ // hack: autoSelect does not work
+ me.store.on('load', function(store, r, success, o) {
+ if (success) {
+ me.clearInvalid();
+
+ if (me.enableAfterLoad) {
+ delete me.enableAfterLoad;
+ me.setDisabled(false);
+ }
+
+ var def = me.getValue() || me.preferredValue;
+ if (def) {
+ me.setValue(def, true); // sync with grid
+ }
+ var found = false;
+ if (def) {
+ if (Ext.isArray(def)) {
+ Ext.Array.each(def, function(v) {
+ if (store.findRecord(me.valueField, v)) {
+ found = true;
+ return false; // break
+ }
+ });
+ } else {
+ found = store.findRecord(me.valueField, def);
+ }
+ }
+
+ if (!found) {
+ var rec = me.store.first();
+ if (me.autoSelect && rec && rec.data) {
+ def = rec.data[me.valueField];
+ me.setValue(def, true);
+ } else {
+ me.setValue(me.editable ? def : '', true);
+ }
+ }
+ }
+ });
+ }
+});
--- /dev/null
+Ext.define('PVE.form.CompressionSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.pveCompressionSelector'],
+ comboItems: [
+ ['', PVE.Utils.noneText],
+ ['lzo', 'LZO (' + gettext('fast') + ')'],
+ ['gzip', 'GZIP (' + gettext('good') + ')']
+ ]
+});
--- /dev/null
+Ext.define('PVE.form.ContentTypeSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.pveContentTypeSelector'],
+
+ cts: undefined,
+
+ initComponent: function() {
+ var me = this;
+
+ me.data = [];
+
+ if (me.cts === undefined) {
+ me.cts = ['images', 'iso', 'vztmpl', 'backup', 'rootdir'];
+ }
+
+ Ext.Array.each(me.cts, function(ct) {
+ me.data.push([ct, PVE.Utils.format_content_types(ct)]);
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.ControllerSelector', {
+ extend: 'Ext.form.FieldContainer',
+ alias: ['widget.PVE.form.ControllerSelector'],
+
+ statics: {
+ maxIds: {
+ ide: 3,
+ sata: 5,
+ virtio: 15,
+ scsi: 13
+ }
+ },
+
+ noVirtIO: false,
+
+ noScsi: false,
+
+ vmconfig: {}, // used to check for existing devices
+
+ setVMConfig: function(vmconfig, autoSelect) {
+ var me = this;
+
+ me.vmconfig = Ext.apply({}, vmconfig);
+ if (autoSelect) {
+ var clist = ['ide', 'virtio', 'scsi', 'sata'];
+ if (autoSelect === 'cdrom') {
+ clist = ['ide', 'scsi', 'sata'];
+ if (!Ext.isDefined(me.vmconfig.ide2)) {
+ me.down('field[name=controller]').setValue('ide');
+ me.down('field[name=deviceid]').setValue(2);
+ return;
+ }
+ } else if (me.vmconfig.ostype === 'l26') {
+ clist = ['virtio', 'ide', 'scsi', 'sata'];
+ }
+
+ Ext.Array.each(clist, function(controller) {
+ var confid, i;
+ if ((controller === 'virtio' && me.noVirtIO) ||
+ (controller === 'scsi' && me.noScsi)) {
+ return; //continue
+ }
+ me.down('field[name=controller]').setValue(controller);
+ for (i = 0; i <= PVE.form.ControllerSelector.maxIds[controller]; i++) {
+ confid = controller + i.toString();
+ if (!Ext.isDefined(me.vmconfig[confid])) {
+ me.down('field[name=deviceid]').setValue(i);
+ return false; // break
+ }
+ }
+ });
+ }
+ me.down('field[name=deviceid]').validate();
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ Ext.apply(me, {
+ fieldLabel: gettext('Bus/Device'),
+ layout: 'hbox',
+ height: 22, // hack: set to same height as other fields
+ defaults: {
+ flex: 1,
+ hideLabel: true
+ },
+ items: [
+ {
+ xtype: 'PVE.form.BusTypeSelector',
+ name: 'controller',
+ value: 'ide',
+ noVirtIO: me.noVirtIO,
+ noScsi: me.noScsi,
+ allowBlank: false,
+ listeners: {
+ change: function(t, value) {
+ if (!me.rendered || !value) {
+ return;
+ }
+ var field = me.down('field[name=deviceid]');
+ field.setMaxValue(PVE.form.ControllerSelector.maxIds[value]);
+ field.validate();
+ }
+ }
+ },
+ {
+ xtype: 'numberfield',
+ name: 'deviceid',
+ minValue: 0,
+ maxValue: PVE.form.ControllerSelector.maxIds.ide,
+ value: '0',
+ validator: function(value) {
+ /*jslint confusion: true */
+ if (!me.rendered) {
+ return;
+ }
+ var field = me.down('field[name=controller]');
+ var controller = field.getValue();
+ var confid = controller + value;
+ if (Ext.isDefined(me.vmconfig[confid])) {
+ return "This device is already in use.";
+ }
+ return true;
+ }
+ }
+ ]
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.DayOfWeekSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.pveDayOfWeekSelector'],
+ comboItems: [
+ ['sun', Ext.util.Format.htmlDecode(Ext.Date.dayNames[0])],
+ ['mon', Ext.util.Format.htmlDecode(Ext.Date.dayNames[1])],
+ ['tue', Ext.util.Format.htmlDecode(Ext.Date.dayNames[2])],
+ ['wed', Ext.util.Format.htmlDecode(Ext.Date.dayNames[3])],
+ ['thu', Ext.util.Format.htmlDecode(Ext.Date.dayNames[4])],
+ ['fri', Ext.util.Format.htmlDecode(Ext.Date.dayNames[5])],
+ ['sat', Ext.util.Format.htmlDecode(Ext.Date.dayNames[6])]
+ ]
+});
--- /dev/null
+Ext.define('PVE.form.DiskFormatSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.PVE.form.DiskFormatSelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ me.data = [
+ ['raw', gettext('Raw disk image') + ' (raw)'],
+ ['qcow2', gettext('QEMU image format') + ' (qcow2)'],
+ ['vmdk', gettext('VMware image format') + ' (vmdk)']
+ ];
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.DisplaySelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.DisplaySelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ me.data = PVE.Utils.kvm_vga_driver_array();
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.EmailNotificationSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.pveEmailNotificationSelector'],
+ comboItems: [
+ ['always', gettext('Always')],
+ ['failure', gettext('On failure only')]
+ ]
+});
--- /dev/null
+Ext.define('PVE.form.FileSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: ['widget.pveFileSelector'],
+
+ setStorage: function(storage, nodename) {
+ var me = this;
+
+ var change = false;
+ if (storage && (me.storage !== storage)) {
+ me.storage = storage;
+ change = true;
+ }
+
+ if (nodename && (me.nodename !== nodename)) {
+ me.nodename = nodename;
+ change = true;
+ }
+
+ if (!(me.storage && me.nodename && change)) {
+ return;
+ }
+
+ var url = '/api2/json/nodes/' + me.nodename + '/storage/' + me.storage + '/content';
+ if (me.storageContent) {
+ url += '?content=' + me.storageContent;
+ }
+
+ me.store.setProxy({
+ type: 'pve',
+ url: url
+ });
+
+ me.store.load();
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ var store = Ext.create('Ext.data.Store', {
+ model: 'pve-storage-content'
+ });
+
+ Ext.apply(me, {
+ store: store,
+ allowBlank: false,
+ autoSelect: false,
+ valueField: 'volid',
+ displayField: 'text',
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Name'),
+ dataIndex: 'text',
+ hideable: false,
+ flex: 1
+ },
+ {
+ header: gettext('Format'),
+ width: 60,
+ dataIndex: 'format'
+ },
+ {
+ header: gettext('Size'),
+ width: 60,
+ dataIndex: 'size',
+ renderer: PVE.Utils.format_size
+ }
+ ]
+ }
+ });
+
+ me.callParent();
+
+ me.setStorage(me.storage, me.nodename);
+ }
+});
--- /dev/null
+Ext.define('PVE.form.FirewallPolicySelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.pveFirewallPolicySelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ me.data = [
+ ['ACCEPT', 'ACCEPT'],
+ ['REJECT', 'REJECT'],
+ [ 'DROP', 'DROP']
+ ];
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.GroupSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: ['widget.pveGroupSelector'],
+
+ allowBlank: false,
+
+ initComponent: function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-groups'
+ });
+
+ Ext.apply(me, {
+ store: store,
+ autoSelect: false,
+ valueField: 'groupid',
+ displayField: 'groupid',
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Group'),
+ sortable: true,
+ dataIndex: 'groupid',
+ flex: 1
+ },
+ {
+ id: 'comment',
+ header: gettext('Comment'),
+ sortable: false,
+ dataIndex: 'comment',
+ flex: 1
+ }
+ ]
+ }
+ });
+
+ me.callParent();
+
+ store.load();
+ }
+
+}, function() {
+
+ Ext.define('pve-groups', {
+ extend: 'Ext.data.Model',
+ fields: [ 'groupid', 'comment' ],
+ proxy: {
+ type: 'pve',
+ url: "/api2/json/access/groups"
+ },
+ idProperty: 'groupid'
+ });
+
+});
--- /dev/null
+Ext.define('PVE.form.HotplugFeatureSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.pveHotplugFeatureSelector'],
+
+ multiSelect: true,
+ allowBlank: true,
+ deleteEmpty: false,
+
+ setValue: function(value, doSelect) {
+ var me = this;
+
+ if (me.multiSelect && Ext.isString(value)) {
+ if (value === '0') {
+ value = [];
+ } else if (value === '1') {
+ value = ['disk', 'network', 'usb'];
+ } else {
+ value = value.split(',');
+ }
+ }
+
+ me.callParent([value, doSelect]);
+ },
+
+ getSubmitData: function() {
+ var me = this,
+ data = null,
+ val;
+ if (!me.disabled && me.submitValue) {
+ val = me.getSubmitValue();
+ if (Ext.isArray(val)) {
+ val = val.join(',') || '0';
+ }
+ if (val !== null && val !== '') {
+ data = {};
+ data[me.getName()] = val;
+ } else if (me.deleteEmpty) {
+ data = {};
+ data['delete'] = me.getName();
+ }
+ }
+ return data;
+ },
+
+
+ initComponent: function() {
+ var me = this;
+
+ me.data = [['disk', gettext('Disk')],
+ ['network', gettext('Network')],
+ ['usb', gettext('USB')],
+ ['memory', gettext('Memory')],
+ ['cpu', gettext('CPU')]];
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.IPProtocolSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: ['widget.pveIPProtocolSelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ var store = Ext.create('Ext.data.Store', {
+ fields: [ 'p', 'd', 'n'],
+ data: [
+ { p: 'tcp', n: 6, d: 'Transmission Control Protocol' },
+ { p: 'udp', n: 17, d: 'User Datagram Protocol' },
+ { p: 'icmp', n: 1, d: 'Internet Control Message Protocol' },
+ { p: 'igmp', n: 2, d: 'Internet Group Management' },
+ { p: 'ggp', n: 3, d: 'gateway-gateway protocol' },
+ { p: 'ipencap', n: 4, d: 'IP encapsulated in IP' },
+ { p: 'st', n: 5, d: 'ST datagram mode' },
+ { p: 'egp', n: 8, d: 'exterior gateway protocol' },
+ { p: 'igp', n: 9, d: 'any private interior gateway (Cisco)' },
+ { p: 'pup', n: 12, d: 'PARC universal packet protocol' },
+ { p: 'hmp', n: 20, d: 'host monitoring protocol' },
+ { p: 'xns-idp', n: 22, d: 'Xerox NS IDP' },
+ { p: 'rdp', n: 27, d: '"reliable datagram" protocol' },
+ { p: 'iso-tp4', n: 29, d: 'ISO Transport Protocol class 4 [RFC905]' },
+ { p: 'dccp', n: 33, d: 'Datagram Congestion Control Prot. [RFC4340]' },
+ { p: 'xtp', n: 36, d: 'Xpress Transfer Protocol' },
+ { p: 'ddp', n: 37, d: 'Datagram Delivery Protocol' },
+ { p: 'idpr-cmtp', n: 38, d: 'IDPR Control Message Transport' },
+ { p: 'ipv6', n: 41, d: 'Internet Protocol, version 6' },
+ { p: 'ipv6-route', n: 43, d: 'Routing Header for IPv6' },
+ { p: 'ipv6-frag', n: 44, d: 'Fragment Header for IPv6' },
+ { p: 'idrp', n: 45, d: 'Inter-Domain Routing Protocol' },
+ { p: 'rsvp', n: 46, d: 'Reservation Protocol' },
+ { p: 'gre', n: 47, d: 'General Routing Encapsulation' },
+ { p: 'esp', n: 50, d: 'Encap Security Payload [RFC2406]' },
+ { p: 'ah', n: 51, d: 'Authentication Header [RFC2402]' },
+ { p: 'skip', n: 57, d: 'SKIP' },
+ { p: 'ipv6-icmp', n: 58, d: 'ICMP for IPv6' },
+ { p: 'ipv6-nonxt', n: 59, d: 'No Next Header for IPv6' },
+ { p: 'ipv6-opts', n: 60, d: 'Destination Options for IPv6' },
+ { p: 'vmtp', n: 81, d: 'Versatile Message Transport' },
+ { p: 'eigrp', n: 88, d: 'Enhanced Interior Routing Protocol (Cisco)' },
+ { p: 'ospf', n: 89, d: 'Open Shortest Path First IGP' },
+ { p: 'ax.25', n: 93, d: 'AX.25 frames' },
+ { p: 'ipip', n: 94, d: 'IP-within-IP Encapsulation Protocol' },
+ { p: 'etherip', n: 97, d: 'Ethernet-within-IP Encapsulation [RFC3378]' },
+ { p: 'encap', n: 98, d: 'Yet Another IP encapsulation [RFC1241]' },
+ { p: 'pim', n: 103, d: 'Protocol Independent Multicast' },
+ { p: 'ipcomp', n: 108, d: 'IP Payload Compression Protocol' },
+ { p: 'vrrp', n: 112, d: 'Virtual Router Redundancy Protocol [RFC5798]' },
+ { p: 'l2tp', n: 115, d: 'Layer Two Tunneling Protocol [RFC2661]' },
+ { p: 'isis', n: 124, d: 'IS-IS over IPv4' },
+ { p: 'sctp', n: 132, d: 'Stream Control Transmission Protocol' },
+ { p: 'fc', n: 133, d: 'Fibre Channel' },
+ { p: 'mobility-header', n: 135, d: 'Mobility Support for IPv6 [RFC3775]' },
+ { p: 'udplite', n: 136, d: 'UDP-Lite [RFC3828]' },
+ { p: 'mpls-in-ip', n: 137, d: 'MPLS-in-IP [RFC4023]' },
+ { p: 'hip', n: 139, d: 'Host Identity Protocol' },
+ { p: 'shim6', n: 140, d: 'Shim6 Protocol [RFC5533]' },
+ { p: 'wesp', n: 141, d: 'Wrapped Encapsulating Security Payload' },
+ { p: 'rohc', n: 142, d: 'Robust Header Compression' }
+ ]
+ });
+
+ Ext.apply(me, {
+ store: store,
+ valueField: 'p',
+ displayField: 'p',
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Protocol'),
+ dataIndex: 'p',
+ hideable: false,
+ sortable: false,
+ width: 100
+ },
+ {
+ header: gettext('Number'),
+ dataIndex: 'n',
+ hideable: false,
+ sortable: false,
+ width: 50
+ },
+ {
+ header: gettext('Description'),
+ dataIndex: 'd',
+ hideable: false,
+ sortable: false,
+ flex: 1
+ }
+ ]
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.IPRefSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: ['widget.pveIPRefSelector'],
+
+ base_url: undefined,
+
+ preferredValue: '', // hack: else Form sets dirty flag?
+
+ ref_type: undefined, // undefined = any [undefined, 'ipset' or 'alias']
+
+ initComponent: function() {
+ var me = this;
+
+ if (!me.base_url) {
+ throw "no base_url specified";
+ }
+
+ var url = "/api2/json" + me.base_url;
+ if (me.ref_type) {
+ url += "?type=" + me.ref_type;
+ }
+
+ var store = Ext.create('Ext.data.Store', {
+ autoLoad: true,
+ fields: [ 'type', 'name', 'ref', 'comment' ],
+ idProperty: 'ref',
+ proxy: {
+ type: 'pve',
+ url: url
+ },
+ sorters: {
+ property: 'ref',
+ order: 'DESC'
+ }
+ });
+
+ var disable_query_for_ips = function(f, value) {
+ if (value === null ||
+ value.match(/^\d/)) { // IP address starts with \d
+ f.queryDelay = 9999999999; // hack: disbale with long delay
+ } else {
+ f.queryDelay = 10;
+ }
+ };
+
+ var columns = [];
+
+ if (!me.ref_type) {
+ columns.push({
+ header: gettext('Type'),
+ dataIndex: 'type',
+ hideable: false,
+ width: 60
+ });
+ }
+
+ columns.push([
+ {
+ header: gettext('Name'),
+ dataIndex: 'ref',
+ hideable: false,
+ width: 140
+ },
+ {
+ header: gettext('Comment'),
+ dataIndex: 'comment',
+ flex: 1
+ }
+ ]);
+
+ Ext.apply(me, {
+ store: store,
+ valueField: 'ref',
+ displayField: 'ref',
+ listConfig: { columns: columns }
+ });
+
+ me.on('change', disable_query_for_ips);
+
+ me.callParent();
+ }
+});
+
--- /dev/null
+/* Key-Value ComboBox
+ *
+ * config properties:
+ * comboItems: an array of Key - Value pairs
+ * deleteEmpty: if set to true (default), an empty value received from the
+ * comboBox will reset the property to its default value
+ */
+Ext.define('PVE.form.KVComboBox', {
+ extend: 'Ext.form.field.ComboBox',
+ alias: 'widget.pveKVComboBox',
+
+ deleteEmpty: true,
+ comboItems: undefined,
+
+ getSubmitData: function() {
+ var me = this,
+ data = null,
+ val;
+ if (!me.disabled && me.submitValue) {
+ val = me.getSubmitValue();
+ if (val !== null && val !== '') {
+ data = {};
+ data[me.getName()] = val;
+ } else if (me.deleteEmpty) {
+ data = {};
+ data['delete'] = me.getName();
+ }
+ }
+ return data;
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ me.store = Ext.create('Ext.data.ArrayStore', {
+ model: 'KeyValue',
+ data : me.comboItems,
+ });
+
+ if (me.initialConfig.editable === undefined) {
+ me.editable = false;
+ }
+
+ Ext.apply(me, {
+ displayField: 'value',
+ valueField: 'key',
+ queryMode: 'local'
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.LanguageSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.pveLanguageSelector'],
+ comboItems: PVE.Utils.language_array()
+});
--- /dev/null
+Ext.define('PVE.form.MemoryField', {
+ extend: 'Ext.form.field.Number',
+ alias: 'widget.pveMemoryField',
+
+ allowBlank: false,
+
+ hotplug: false,
+
+ minValue: 32,
+
+ maxValue: 4178944,
+
+ step: 32,
+
+ value: '512', // qm default
+
+ computeUpDown: function(value) {
+ var me = this;
+
+ if (!me.hotplug) {
+ return { up: value + me.step, down: value - me.step };
+ }
+
+ var dimm_size = 512;
+ var prev_dimm_size = 0;
+ var min_size = 1024;
+ var current_size = min_size;
+ var value_up = min_size;
+ var value_down = min_size;
+ var value_start = min_size;
+
+ var i, j;
+ for (j = 0; j < 9; j++) {
+ for (i = 0; i < 32; i++) {
+ if ((value >= current_size) && (value < (current_size + dimm_size))) {
+ value_start = current_size,
+ value_up = current_size + dimm_size;
+ value_down = current_size - ((i === 0) ? prev_dimm_size : dimm_size);
+ }
+ current_size += dimm_size;
+ }
+ prev_dimm_size = dimm_size;
+ dimm_size = dimm_size*2;
+ }
+
+ return { up: value_up, down: value_down, start: value_start };
+ },
+
+ onSpinUp: function() {
+ var me = this;
+ if (!me.readOnly) {
+ var res = me.computeUpDown(me.getValue());
+ me.setValue(Ext.Number.constrain(res.up, me.minValue, me.maxValue));
+ }
+ },
+
+ onSpinDown: function() {
+ var me = this;
+ if (!me.readOnly) {
+ var res = me.computeUpDown(me.getValue());
+ me.setValue(Ext.Number.constrain(res.down, me.minValue, me.maxValue));
+ }
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ if (me.hotplug) {
+ me.minValue = 1024;
+
+ me.on('blur', function(field) {
+ value = me.getValue();
+ var res = me.computeUpDown(value);
+ if (value === res.start || value === res.up || value === res.down) {
+ return;
+ }
+ field.setValue(res.up);
+ });
+ }
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.NetworkCardSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.PVE.form.NetworkCardSelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ me.data = [
+ ['e1000', 'Intel E1000'],
+ ['virtio', 'VirtIO (' + gettext('paravirtualized') + ')'],
+ ['rtl8139', 'Realtek RTL8139'],
+ ['vmxnet3', 'VMWare vmxnet3']
+ ];
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.NodeSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: ['widget.PVE.form.NodeSelector'],
+
+ // invalidate nodes which are offline
+ onlineValidator: false,
+
+ selectCurNode: false,
+
+ // only allow those nodes (array)
+ allowedNodes: undefined,
+
+ initComponent: function() {
+ var me = this;
+
+ var store = Ext.create('Ext.data.Store', {
+ fields: [ 'node', 'cpu', 'maxcpu', 'mem', 'maxmem', 'uptime' ],
+ autoLoad: true,
+ proxy: {
+ type: 'pve',
+ url: '/api2/json/nodes'
+ },
+ sorters: [
+ {
+ property : 'node',
+ direction: 'ASC'
+ },
+ {
+ property : 'mem',
+ direction: 'DESC'
+ }
+ ]
+ });
+
+ Ext.apply(me, {
+ store: store,
+ valueField: 'node',
+ displayField: 'node',
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Node'),
+ dataIndex: 'node',
+ sortable: true,
+ hideable: false,
+ flex: 1
+ },
+ {
+ header: gettext('Memory usage'),
+ renderer: PVE.Utils.render_mem_usage,
+ sortable: true,
+ width: 100,
+ dataIndex: 'mem'
+ },
+ {
+ header: gettext('CPU usage'),
+ renderer: PVE.Utils.render_cpu,
+ sortable: true,
+ width: 100,
+ dataIndex: 'cpu'
+ }
+ ]
+ },
+ validator: function(value) {
+ /*jslint confusion: true */
+ if (!me.onlineValidator || (me.allowBlank && !value)) {
+ return true;
+ }
+
+ var offline = [];
+ var notAllowed = [];
+
+ Ext.Array.each(value.split(/\s*,\s*/), function(node) {
+ var rec = me.store.findRecord(me.valueField, node);
+ if (!(rec && rec.data) || !Ext.isNumeric(rec.data.mem)) {
+ offline.push(node);
+ } else if (me.allowedNodes && !Ext.Array.contains(me.allowedNodes, node)) {
+ notAllowed.push(node);
+ }
+ });
+
+ if (notAllowed.length !== 0) {
+ return "Node " + notAllowed.join(', ') + " is not allowed for this action!";
+ }
+
+ if (offline.length !== 0) {
+ return "Node " + offline.join(', ') + " seems to be offline!";
+ }
+ return true;
+ }
+ });
+
+ if (me.selectCurNode && PVE.curSelectedNode.data.node) {
+ me.preferredValue = PVE.curSelectedNode.data.node;
+ }
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.PoolSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: ['widget.pvePoolSelector'],
+
+ allowBlank: false,
+
+ initComponent: function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-pools'
+ });
+
+ Ext.apply(me, {
+ store: store,
+ autoSelect: false,
+ valueField: 'poolid',
+ displayField: 'poolid',
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Pool'),
+ sortable: true,
+ dataIndex: 'poolid',
+ flex: 1
+ },
+ {
+ id: 'comment',
+ header: gettext('Comment'),
+ sortable: false,
+ dataIndex: 'comment',
+ flex: 1
+ }
+ ]
+ }
+ });
+
+ me.callParent();
+
+ store.load();
+ }
+
+}, function() {
+
+ Ext.define('pve-pools', {
+ extend: 'Ext.data.Model',
+ fields: [ 'poolid', 'comment' ],
+ proxy: {
+ type: 'pve',
+ url: "/api2/json/pools"
+ },
+ idProperty: 'poolid'
+ });
+
+});
--- /dev/null
+Ext.define('PVE.form.RRDTypeSelector', {
+ extend: 'Ext.form.field.ComboBox',
+ alias: ['widget.pveRRDTypeSelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ var store = new Ext.data.ArrayStore({
+ fields: [ 'id', 'timeframe', 'cf', 'text' ],
+ data : [
+ [ 'hour', 'hour', 'AVERAGE', "Hour (average)" ],
+ [ 'hourmax', 'hour', 'MAX', "Hour (max)" ],
+ [ 'day', 'day', 'AVERAGE', "Day (average)" ],
+ [ 'daymax', 'day', 'MAX', "Day (max)" ],
+ [ 'week', 'week', 'AVERAGE', "Week (average)" ],
+ [ 'weekmax', 'week', 'MAX', "Week (max)" ],
+ [ 'month', 'month', 'AVERAGE', "Month (average)" ],
+ [ 'monthmax', 'month', 'MAX', "Month (max)" ],
+ [ 'year', 'year', 'AVERAGE', "Year (average)" ],
+ [ 'yearmax', 'year', 'MAX', "Year (max)" ]
+ ]
+ });
+
+ Ext.apply(me, {
+ store: store,
+ displayField: 'text',
+ valueField: 'id',
+ editable: false,
+ queryMode: 'local',
+ value: 'hour',
+ getState: function() {
+ var ind = store.findExact('id', me.getValue());
+ var rec = store.getAt(ind);
+ if (!rec) {
+ return;
+ }
+ return {
+ id: rec.data.id,
+ timeframe: rec.data.timeframe,
+ cf: rec.data.cf
+ };
+ },
+ applyState : function(state) {
+ if (state && state.id) {
+ me.setValue(state.id);
+ }
+ },
+ stateEvents: [ 'select' ],
+ stateful: true,
+ id: 'pveRRDTypeSelection'
+ });
+
+ me.callParent();
+
+ var statechange = function(sp, key, value) {
+ if (key === me.id) {
+ me.applyState(value);
+ }
+ };
+
+ var sp = Ext.state.Manager.getProvider();
+ me.mon(sp, 'statechange', statechange, me);
+ }
+});
+
--- /dev/null
+Ext.define('PVE.form.RealmComboBox', {
+ extend: 'Ext.form.field.ComboBox',
+ alias: ['widget.pveRealmComboBox'],
+
+ needOTP: function(realm) {
+ var me = this;
+
+ var rec = me.store.findRecord('realm', realm);
+
+ return rec && rec.data && rec.data.tfa ? rec.data.tfa : undefined;
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ var stateid = 'pveloginrealm';
+
+ var realmstore = Ext.create('Ext.data.Store', {
+ model: 'pve-domains',
+ });
+
+ Ext.apply(me, {
+ fieldLabel: gettext('Realm'),
+ name: 'realm',
+ store: realmstore,
+ queryMode: 'local',
+ allowBlank: false,
+ forceSelection: true,
+ autoSelect: false,
+ triggerAction: 'all',
+ valueField: 'realm',
+ displayField: 'descr',
+ getState: function() {
+ return { value: this.getValue() };
+ },
+ applyState : function(state) {
+ if (state && state.value) {
+ this.setValue(state.value);
+ }
+ },
+ stateEvents: [ 'select' ],
+ stateful: true,
+ id: stateid, // fixme: remove (Stateful does not work without)
+ stateID: stateid
+ });
+
+ me.callParent();
+
+ realmstore.load({
+ callback: function(r, o, success) {
+ if (success) {
+ var def = me.getValue();
+ if (!def || !realmstore.findRecord('realm', def)) {
+ def = 'pam';
+ Ext.each(r, function(record) {
+ if (record.data && record.data["default"]) {
+ def = record.data.realm;
+ }
+ });
+ }
+ if (def) {
+ me.setValue(def);
+ }
+ }
+ }
+ });
+ }
+});
--- /dev/null
+Ext.define('PVE.form.RoleSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: ['widget.pveRoleSelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-roles'
+ });
+
+ Ext.apply(me, {
+ store: store,
+ allowBlank: false,
+ autoSelect: false,
+ valueField: 'roleid',
+ displayField: 'roleid',
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Role'),
+ sortable: true,
+ dataIndex: 'roleid',
+ flex: 1
+ }
+ ]
+ }
+ });
+
+ me.callParent();
+
+ store.load();
+ }
+
+}, function() {
+
+ Ext.define('pve-roles', {
+ extend: 'Ext.data.Model',
+ fields: [ 'roleid', 'privs' ],
+ proxy: {
+ type: 'pve',
+ url: "/api2/json/access/roles"
+ },
+ idProperty: 'roleid'
+ });
+
+});
--- /dev/null
+Ext.define('PVE.form.ScsiHwSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.pveScsiHwSelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ me.data = [
+ ['', PVE.Utils.render_scsihw('')],
+ ['lsi', PVE.Utils.render_scsihw('lsi')],
+ ['lsi53c810', PVE.Utils.render_scsihw('lsi53c810')],
+ ['megasas', PVE.Utils.render_scsihw('megasas')],
+ ['virtio-scsi-pci', PVE.Utils.render_scsihw('virtio-scsi-pci')],
+ ['virtio-scsi-single', PVE.Utils.render_scsihw('virtio-scsi-single')],
+ ['pvscsi', PVE.Utils.render_scsihw('pvscsi')]
+ ];
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.SecurityGroupsSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: ['widget.pveSecurityGroupsSelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ var store = Ext.create('Ext.data.Store', {
+ autoLoad: true,
+ fields: [ 'group', 'comment' ],
+ idProperty: 'group',
+ proxy: {
+ type: 'pve',
+ url: "/api2/json/cluster/firewall/groups"
+ },
+ sorters: {
+ property: 'group',
+ order: 'DESC'
+ }
+ });
+
+ Ext.apply(me, {
+ store: store,
+ valueField: 'group',
+ displayField: 'group',
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Security Group'),
+ dataIndex: 'group',
+ hideable: false,
+ width: 100
+ },
+ {
+ header: gettext('Comment'),
+ dataIndex: 'comment',
+ flex: 1
+ }
+ ]
+ }
+ });
+
+ me.callParent();
+ }
+});
+
--- /dev/null
+Ext.define('PVE.form.SnapshotSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: ['widget.PVE.form.SnapshotSelector'],
+
+ loadStore: function(nodename, vmid) {
+ var me = this;
+
+ if (!nodename) {
+ return;
+ }
+
+ me.nodename = nodename;
+
+ if (!vmid) {
+ return;
+ }
+
+ me.vmid = vmid;
+
+ me.store.setProxy({
+ type: 'pve',
+ url: '/api2/json/nodes/' + me.nodename + '/qemu/' + me.vmid +'/snapshot'
+ });
+
+ me.store.load();
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ if (!me.vmid) {
+ throw "no VM ID specified";
+ }
+
+ var store = Ext.create('Ext.data.Store', {
+ fields: [ 'name'],
+ filterOnLoad: true
+ });
+
+ Ext.apply(me, {
+ store: store,
+ valueField: 'name',
+ displayField: 'name',
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Snapshot'),
+ dataIndex: 'name',
+ hideable: false,
+ flex: 1
+ }
+ ]
+ }
+ });
+
+ me.callParent();
+
+ me.loadStore(me.nodename, me.vmid);
+ }
+});
--- /dev/null
+Ext.define('PVE.form.StorageSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: ['widget.PVE.form.StorageSelector'],
+
+ reloadStorageList: function() {
+ var me = this;
+ if (!me.nodename) {
+ return;
+ }
+
+ var params = {};
+ var url = '/api2/json/nodes/' + me.nodename + '/storage';
+ if (me.storageContent) {
+ params.content = me.storageContent;
+ }
+ if (me.targetNode) {
+ params.target = me.targetNode;
+ params.enabled = 1; // skip disabled storages
+ }
+ me.store.setProxy({
+ type: 'pve',
+ url: url,
+ extraParams: params
+ });
+
+ me.store.load();
+
+ },
+
+ setTargetNode: function(targetNode) {
+ var me = this;
+
+ if (!targetNode || (me.targetNode === targetNode)) {
+ return;
+ }
+
+ me.targetNode = targetNode;
+
+ me.reloadStorageList();
+ },
+
+ setNodename: function(nodename) {
+ var me = this;
+
+ if (!nodename || (me.nodename === nodename)) {
+ return;
+ }
+
+ me.nodename = nodename;
+
+ me.reloadStorageList();
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ var nodename = me.nodename;
+ me.nodename = undefined;
+
+ var store = Ext.create('Ext.data.Store', {
+ model: 'pve-storage-status',
+ sorters: {
+ property: 'storage',
+ order: 'DESC'
+ }
+ });
+
+ Ext.apply(me, {
+ store: store,
+ allowBlank: false,
+ valueField: 'storage',
+ displayField: 'storage',
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Name'),
+ dataIndex: 'storage',
+ hideable: false,
+ flex: 1
+ },
+ {
+ header: gettext('Type'),
+ width: 60,
+ dataIndex: 'type'
+ },
+ {
+ header: gettext('Avail'),
+ width: 80,
+ dataIndex: 'avail',
+ renderer: PVE.Utils.format_size
+ },
+ {
+ header: gettext('Capacity'),
+ width: 80,
+ dataIndex: 'total',
+ renderer: PVE.Utils.format_size
+ }
+ ]
+ }
+ });
+
+ me.callParent();
+
+ if (nodename) {
+ me.setNodename(nodename);
+ }
+ }
+}, function() {
+
+ Ext.define('pve-storage-status', {
+ extend: 'Ext.data.Model',
+ fields: [ 'storage', 'active', 'type', 'avail', 'total' ],
+ idProperty: 'storage'
+ });
+
+});
--- /dev/null
+Ext.define('PVE.form.Textfield', {
+ extend: 'Ext.form.field.Text',
+ alias: ['widget.pvetextfield'],
+
+ skipEmptyText: true,
+
+ deleteEmpty: false,
+
+ getSubmitData: function() {
+ var me = this,
+ data = null,
+ val;
+ if (!me.disabled && me.submitValue && !me.isFileUpload()) {
+ val = me.getSubmitValue();
+ if (val !== null) {
+ data = {};
+ data[me.getName()] = val;
+ } else if (me.deleteEmpty) {
+ data = {};
+ data['delete'] = me.getName();
+ }
+ }
+ return data;
+ },
+
+ getSubmitValue: function() {
+ var me = this;
+
+ var value = this.processRawValue(this.getRawValue());
+ if (value !== '') {
+ return value;
+ }
+
+ return me.skipEmptyText ? null: value;
+ }
+});
\ No newline at end of file
--- /dev/null
+Ext.define('PVE.form.UserSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: ['widget.pveUserSelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-users'
+ });
+
+ var render_full_name = function(firstname, metaData, record) {
+
+ var first = firstname || '';
+ var last = record.data.lastname || '';
+ return first + " " + last;
+ };
+
+ Ext.apply(me, {
+ store: store,
+ allowBlank: false,
+ autoSelect: false,
+ valueField: 'userid',
+ displayField: 'userid',
+ listConfig: {
+ columns: [
+ {
+ header: gettext('User'),
+ sortable: true,
+ dataIndex: 'userid',
+ flex: 1
+ },
+ {
+ header: gettext('Name'),
+ sortable: true,
+ renderer: render_full_name,
+ dataIndex: 'firstname',
+ flex: 1
+ },
+ {
+ id: 'comment',
+ header: gettext('Comment'),
+ sortable: false,
+ dataIndex: 'comment',
+ flex: 1
+ }
+ ]
+ }
+ });
+
+ me.callParent();
+
+ store.load({ params: { enabled: 1 }});
+ }
+
+}, function() {
+
+ Ext.define('pve-users', {
+ extend: 'Ext.data.Model',
+ fields: [
+ 'userid', 'firstname', 'lastname' , 'email', 'comment',
+ { type: 'boolean', name: 'enable' },
+ { type: 'date', dateFormat: 'timestamp', name: 'expire' }
+ ],
+ proxy: {
+ type: 'pve',
+ url: "/api2/json/access/users"
+ },
+ idProperty: 'userid'
+ });
+
+});
+
+
--- /dev/null
+Ext.define('PVE.form.VlanField', {
+ extend: 'Ext.form.field.Number',
+ alias: ['widget.pveVlanField'],
+
+ deleteEmpty: false,
+
+ emptyText: 'no VLAN',
+
+ fieldLabel: gettext('VLAN Tag'),
+
+ allowBlank: true,
+
+ getSubmitData: function() {
+ var me = this,
+ data = null,
+ val;
+ if (!me.disabled && me.submitValue) {
+ val = me.getSubmitValue();
+ if (val) {
+ data = {};
+ data[me.getName()] = val;
+ } else if (me.deleteEmpty) {
+ data = {};
+ data['delete'] = me.getName();
+ }
+ }
+ return data;
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ Ext.apply(me, {
+ minValue: 1,
+ maxValue: 4094
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.form.VMIDSelector', {
+ extend: 'Ext.form.field.Number',
+ alias: 'widget.pveVMIDSelector',
+
+ allowBlank: false,
+
+ minValue: 100,
+
+ maxValue: 999999999,
+
+ validateExists: undefined,
+
+ loadNextFreeVMID: false,
+
+ initComponent: function() {
+ var me = this;
+
+ Ext.applyIf(me, {
+ fieldLabel: 'VM ID',
+ listeners: {
+ 'change': function(field, newValue, oldValue) {
+ if (!Ext.isDefined(me.validateExists)) {
+ return;
+ }
+ PVE.Utils.API2Request({
+ params: { vmid: newValue },
+ url: '/cluster/nextid',
+ method: 'GET',
+ success: function(response, opts) {
+ if (me.validateExists === true) {
+ me.markInvalid(gettext('This VM ID does not exists'));
+ }
+ },
+ failure: function(response, opts) {
+ if (me.validateExists === false) {
+ me.markInvalid(gettext('This VM ID is already in use'));
+ }
+ }
+ });
+ }
+ }
+ });
+
+ me.callParent();
+
+ if (me.loadNextFreeVMID) {
+ PVE.Utils.API2Request({
+ url: '/cluster/nextid',
+ method: 'GET',
+ success: function(response, opts) {
+ me.setRawValue(response.result.data);
+ }
+ });
+ }
+ }
+});
--- /dev/null
+Ext.define('PVE.form.VNCKeyboardSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.VNCKeyboardSelector'],
+ comboItems: PVE.Utils.kvm_keymap_array()
+});
--- /dev/null
+/*
+ * Top left combobox, used to select a view of the underneath RessourceTree
+ */
+Ext.define('PVE.form.ViewSelector', {
+ extend: 'Ext.form.field.ComboBox',
+ alias: ['widget.pveViewSelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ var default_views = {
+ server: {
+ text: gettext('Server View'),
+ groups: ['node']
+ },
+ folder: {
+ text: gettext('Folder View'),
+ groups: ['type']
+ },
+ storage: {
+ text: gettext('Storage View'),
+ groups: ['node'],
+ filterfn: function(node) {
+ return node.data.type === 'storage' || node.data.type === 'node';
+ }
+ },
+ pool: {
+ text: gettext('Pool View'),
+ groups: ['pool'],
+ // Pool View only lists VMs and Containers
+ filterfn: function(node) {
+ return node.data.type === 'qemu' || node.data.type === 'lxc' || node.data.type === 'openvz' ||
+ node.data.type === 'pool';
+ }
+ }
+ };
+
+ var groupdef = [];
+ Ext.Object.each(default_views, function(viewname, value) {
+ groupdef.push([viewname, value.text]);
+ });
+
+ var store = Ext.create('Ext.data.Store', {
+ model: 'KeyValue',
+ proxy: {
+ type: 'memory',
+ reader: 'array'
+ },
+ data: groupdef,
+ autoload: true,
+ });
+
+ Ext.apply(me, {
+ hideLabel: true,
+ store: store,
+ value: groupdef[0][0],
+ editable: false,
+ allowBlank: false,
+ forceSelection: true,
+ autoSelect: false,
+ valueField: 'key',
+ displayField: 'value',
+
+ getViewFilter: function() {
+ var view = me.getValue();
+ return Ext.apply({ id: view }, default_views[view] || default_views.server);
+ },
+
+ getState: function() {
+ return { value: me.getValue() };
+ },
+
+ applyState : function(state, doSelect) {
+ var view = me.getValue();
+ if (state && state.value && (view != state.value)) {
+ var record = store.findRecord('key', state.value);
+ if (record) {
+ me.setValue(state.value, true);
+ if (doSelect) {
+ me.fireEvent('select', me, [record]);
+ }
+ }
+ }
+ },
+ stateEvents: [ 'select' ],
+ stateful: true,
+ id: 'view'
+ });
+
+ me.callParent();
+
+ var statechange = function(sp, key, value) {
+ if (key === me.id) {
+ me.applyState(value, true);
+ }
+ };
+
+ var sp = Ext.state.Manager.getProvider();
+
+ me.mon(sp, 'statechange', statechange, me);
+ }
+});
--- /dev/null
+Ext.define('PVE.form.iScsiProviderSelector', {
+ extend: 'PVE.form.KVComboBox',
+ alias: ['widget.pveiScsiProviderSelector'],
+
+ initComponent: function() {
+ var me = this;
+
+ me.data = [
+ ['comstar', 'Comstar'],
+ [ 'istgt', 'istgt'],
+ [ 'iet', 'IET']
+ ];
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.grid.BackupView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveBackupView'],
+
+
+ initComponent : function() {
+ var me = this;
+
+ var nodename = me.pveSelNode.data.node;
+ if (!nodename) {
+ throw "no node name specified";
+ }
+
+ var vmid = me.pveSelNode.data.vmid;
+ if (!vmid) {
+ throw "no VM ID specified";
+ }
+
+ var vmtype = me.pveSelNode.data.type;
+ if (!vmtype) {
+ throw "no VM type specified";
+ }
+
+ var filterFn;
+ if (vmtype === 'openvz') {
+ filterFn = function(item) {
+ return item.data.volid.match(':backup/vzdump-openvz-');
+ };
+ } else if (vmtype === 'lxc') {
+ filterFn = function(item) {
+ return item.data.volid.match(':backup/vzdump-lxc-');
+ };
+ } else if (vmtype === 'qemu') {
+ filterFn = function(item) {
+ return item.data.volid.match(':backup/vzdump-qemu-');
+ };
+ } else {
+ throw "unsupported VM type '" + vmtype + "'";
+ }
+
+ me.store = Ext.create('Ext.data.Store', {
+ model: 'pve-storage-content',
+ sorters: {
+ property: 'volid',
+ order: 'DESC'
+ },
+ filters: { filterFn: filterFn }
+ });
+
+ var reload = Ext.Function.createBuffered(function() {
+ if (me.store.proxy.url) {
+ me.store.load();
+ }
+ }, 100);
+
+ var setStorage = function(storage) {
+ var url = '/api2/json/nodes/' + nodename + '/storage/' + storage + '/content';
+ url += '?content=backup';
+
+ me.store.setProxy({
+ type: 'pve',
+ url: url
+ });
+
+ reload();
+ };
+
+ var storagesel = Ext.create('PVE.form.StorageSelector', {
+ nodename: nodename,
+ fieldLabel: gettext('Storage'),
+ labelAlign: 'right',
+ storageContent: 'backup',
+ allowBlank: false,
+ listeners: {
+ change: function(f, value) {
+ setStorage(value);
+ }
+ }
+ });
+
+ var storagefilter = Ext.create('Ext.form.field.Text', {
+ fieldLabel: gettext('Search'),
+ labelWidth: 50,
+ labelAlign: 'right',
+ enableKeyEvents: true,
+ listeners: {
+ buffer: 500,
+ keyup: function(field) {
+ me.store.clearFilter(true);
+ me.store.filter([
+ {
+ property: 'volid',
+ value: field.getValue(),
+ anyMatch: true,
+ caseSensitive: false
+ }
+ ]);
+ }
+ }
+ });
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var backup_btn = Ext.create('Ext.button.Button', {
+ text: gettext('Backup now'),
+ handler: function() {
+ var win = Ext.create('PVE.window.Backup', {
+ nodename: nodename,
+ vmid: vmid,
+ vmtype: vmtype,
+ storage: storagesel.getValue()
+ });
+ win.show();
+ }
+ });
+
+ var restore_btn = Ext.create('PVE.button.Button', {
+ text: gettext('Restore'),
+ disabled: true,
+ selModel: sm,
+ enableFn: function(rec) {
+ return !!rec;
+ },
+ handler: function(b, e, rec) {
+ var volid = rec.data.volid;
+
+ var win = Ext.create('PVE.window.Restore', {
+ nodename: nodename,
+ vmid: vmid,
+ volid: rec.data.volid,
+ volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec),
+ vmtype: vmtype
+ });
+ win.show();
+ win.on('destroy', reload);
+ }
+ });
+
+ var delete_btn = Ext.create('PVE.button.Button', {
+ text: gettext('Remove'),
+ disabled: true,
+ selModel: sm,
+ dangerous: true,
+ confirmMsg: function(rec) {
+ var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
+ "'" + rec.data.volid + "'");
+ msg += " " + gettext('This will permanently erase all image data.');
+
+ return msg;
+ },
+ enableFn: function(rec) {
+ return !!rec;
+ },
+ handler: function(b, e, rec){
+ var storage = storagesel.getValue();
+ if (!storage) {
+ return;
+ }
+
+ var volid = rec.data.volid;
+ PVE.Utils.API2Request({
+ url: "/nodes/" + nodename + "/storage/" + storage + "/content/" + volid,
+ method: 'DELETE',
+ waitMsgTarget: me,
+ failure: function(response, opts) {
+ Ext.Msg.alert('Error', response.htmlStatus);
+ },
+ success: function(response, options) {
+ reload();
+ }
+ });
+ }
+ });
+
+ Ext.apply(me, {
+ stateful: false,
+ selModel: sm,
+ tbar: [ backup_btn, restore_btn, delete_btn, '->', storagesel, storagefilter ],
+ columns: [
+ {
+ header: gettext('Name'),
+ flex: 1,
+ sortable: true,
+ renderer: PVE.Utils.render_storage_content,
+ dataIndex: 'volid'
+ },
+ {
+ header: gettext('Format'),
+ width: 100,
+ dataIndex: 'format'
+ },
+ {
+ header: gettext('Size'),
+ width: 100,
+ renderer: PVE.Utils.format_size,
+ dataIndex: 'size'
+ }
+ ],
+ listeners: {
+ show: reload
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+
+// partly copied from extjs/examples/ux/CheckColumn.js
+
+Ext.define('PVE.CheckColumn', {
+ extend: 'Ext.grid.column.Column',
+ alias: 'widget.checkcolumn',
+
+ constructor: function(cfg) {
+ this.renderer = function(value){
+ var cssPrefix = Ext.baseCSSPrefix,
+ cls = [cssPrefix + 'grid-checkheader'];
+
+ if (value) {
+ cls.push(cssPrefix + 'grid-checkheader-checked');
+ }
+ return '<div class="' + cls.join(' ') + '"> </div>';
+ };
+
+ this.addEvents('checkchange');
+
+ this.callParent(arguments);
+ },
+
+ processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
+ if (type == 'mousedown') {
+ var record = view.panel.store.getAt(recordIndex),
+ dataIndex = this.dataIndex,
+ checked = !record.get(dataIndex);
+ record.set(dataIndex, checked);
+ this.fireEvent('checkchange', this, record, checked);
+ return false;
+ } else {
+ return this.callParent(arguments);
+ }
+ }
+
+});
+
--- /dev/null
+Ext.define('PVE.FirewallAliasEdit', {
+ extend: 'PVE.window.Edit',
+
+ base_url: undefined,
+
+ alias_name: undefined,
+
+ initComponent : function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ me.create = (me.alias_name === undefined);
+
+ if (me.create) {
+ me.url = '/api2/extjs' + me.base_url;
+ me.method = 'POST';
+ } else {
+ me.url = '/api2/extjs' + me.base_url + '/' + me.alias_name;
+ me.method = 'PUT';
+ }
+
+ var items = [
+ {
+ xtype: 'textfield',
+ name: me.create ? 'name' : 'rename',
+ fieldLabel: gettext('Name'),
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ name: 'cidr',
+ fieldLabel: gettext('IP/CIDR'),
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ name: 'comment',
+ fieldLabel: gettext('Comment')
+ }
+ ];
+
+ var ipanel = Ext.create('PVE.panel.InputPanel', {
+ create: me.create,
+ items: items
+ });
+
+ Ext.apply(me, {
+ subject: gettext('Alias'),
+ isAdd: true,
+ items: [ ipanel ]
+ });
+
+ me.callParent();
+
+ if (!me.create) {
+ me.load({
+ success: function(response, options) {
+ var values = response.result.data;
+ values.rename = values.name;
+ ipanel.setValues(values);
+ }
+ });
+ }
+ }
+});
+
+Ext.define('PVE.FirewallAliases', {
+ extend: 'Ext.grid.Panel',
+ alias: ['widget.pveFirewallAliases'],
+
+ base_url: undefined,
+
+ title: gettext('Alias'),
+
+ initComponent : function() {
+ /*jslint confusion: true */
+
+ var me = this;
+
+ if (!me.base_url) {
+ throw "missing base_url configuration";
+ }
+
+ var store = new Ext.data.Store({
+ fields: [ 'name', 'cidr', 'comment', 'digest' ],
+ proxy: {
+ type: 'pve',
+ url: "/api2/json" + me.base_url
+ },
+ idProperty: 'name',
+ sorters: {
+ property: 'name',
+ order: 'DESC'
+ }
+ });
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var reload = function() {
+ var oldrec = sm.getSelection()[0];
+ store.load(function(records, operation, success) {
+ if (oldrec) {
+ var rec = store.findRecord('name', oldrec.data.name);
+ if (rec) {
+ sm.select(rec);
+ }
+ }
+ });
+ };
+
+ var run_editor = function() {
+ var sm = me.getSelectionModel();
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+
+ var win = Ext.create('PVE.FirewallAliasEdit', {
+ base_url: me.base_url,
+ alias_name: rec.data.name
+ });
+
+ win.show();
+ win.on('destroy', reload);
+ };
+
+ me.editBtn = new PVE.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ me.addBtn = Ext.create('Ext.Button', {
+ text: gettext('Add'),
+ handler: function() {
+ var win = Ext.create('PVE.FirewallAliasEdit', {
+ base_url: me.base_url
+ });
+ win.on('destroy', reload);
+ win.show();
+ }
+ });
+
+ me.removeBtn = new PVE.button.Button({
+ text: gettext('Remove'),
+ selModel: sm,
+ disabled: true,
+ handler: function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+ PVE.Utils.API2Request({
+ url: me.base_url + '/' + rec.data.name,
+ method: 'DELETE',
+ waitMsgTarget: me,
+ failure: function(response, options) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ callback: reload
+ });
+ }
+ });
+
+
+ Ext.applyIf(me, {
+ store: store,
+ tbar: [ me.addBtn, me.removeBtn, me.editBtn ],
+ selModel: sm,
+ columns: [
+ { header: gettext('Name'), dataIndex: 'name', width: 100 },
+ { header: gettext('IP/CIDR'), dataIndex: 'cidr', width: 100 },
+ { header: gettext('Comment'), dataIndex: 'comment', flex: 1 }
+ ],
+ listeners: {
+ itemdblclick: run_editor
+ }
+ });
+
+ me.callParent();
+
+ me.on('show', reload);
+ }
+});
--- /dev/null
+Ext.define('PVE.FirewallOptions', {
+ extend: 'PVE.grid.ObjectGrid',
+ alias: ['widget.pveFirewallOptions'],
+
+ fwtype: undefined, // 'dc', 'node' or 'vm'
+
+ base_url: undefined,
+
+ initComponent : function() {
+ /*jslint confusion: true */
+
+ var me = this;
+
+ if (!me.base_url) {
+ throw "missing base_url configuration";
+ }
+
+ if (me.fwtype === 'dc' || me.fwtype === 'node' || me.fwtype === 'vm') {
+ if (me.fwtype === 'node') {
+ me.cwidth1 = 250;
+ }
+ } else {
+ throw "unknown firewall option type";
+ }
+
+ var rows = {};
+
+ var add_boolean_row = function(name, text, defaultValue, labelWidth) {
+ rows[name] = {
+ header: text,
+ required: true,
+ defaultValue: defaultValue || 0,
+ renderer: PVE.Utils.format_boolean,
+ editor: {
+ xtype: 'pveWindowEdit',
+ subject: text,
+ fieldDefaults: { labelWidth: labelWidth || 100 },
+ items: {
+ xtype: 'pvecheckbox',
+ defaultValue: defaultValue || 0,
+ checked: defaultValue ? true : false,
+ name: name,
+ uncheckedValue: 0,
+ fieldLabel: text
+ }
+ }
+ };
+ };
+
+ var add_integer_row = function(name, text, labelWidth, minValue) {
+ rows[name] = {
+ header: text,
+ required: true,
+ renderer: function(value) {
+ return value || PVE.Utils.defaultText;
+ },
+ editor: {
+ xtype: 'pveWindowEdit',
+ subject: text,
+ fieldDefaults: { labelWidth: labelWidth || 100 },
+ items: {
+ xtype: 'numberfield',
+ name: name,
+ minValue: minValue,
+ decimalPrecision: 0,
+ fieldLabel: text,
+ emptyText: gettext('Default'),
+ getSubmitData: function() {
+ var me = this;
+ var val = me.getSubmitValue();
+ if (val !== null && val !== '') {
+ var data = {};
+ data[name] = val;
+ return data;
+ } else {
+ return { 'delete' : name };
+ }
+ }
+ }
+ }
+ };
+ };
+
+ var add_log_row = function(name, labelWidth) {
+ rows[name] = {
+ header: name,
+ required: true,
+ defaultValue: 'nolog',
+ editor: {
+ xtype: 'pveWindowEdit',
+ subject: name,
+ fieldDefaults: { labelWidth: labelWidth || 100 },
+ items: {
+ xtype: 'pveKVComboBox',
+ name: name,
+ fieldLabel: name,
+ data: [['nolog', 'nolog'], ['info', 'info'], ['err', 'err'],
+ ['warning', 'warning'], ['crit', 'crit'], ['alert', 'alert'],
+ ['emerg', 'emerg'], ['debug', 'debug']]
+ }
+ }
+ };
+ };
+
+
+ if (me.fwtype === 'node') {
+ add_boolean_row('enable', gettext('Enable Firewall'), 1);
+ add_boolean_row('nosmurfs', gettext('SMURFS filter'), 1);
+ add_boolean_row('tcpflags', gettext('TCP flags filter'), 0);
+ add_integer_row('nf_conntrack_max', 'nf_conntrack_max', 120, 32768);
+ add_integer_row('nf_conntrack_tcp_timeout_established',
+ 'nf_conntrack_tcp_timeout_established', 250, 7875);
+ add_log_row('log_level_in');
+ add_log_row('log_level_out');
+ add_log_row('tcp_flags_log_level', 120);
+ add_log_row('smurf_log_level');
+ } else if (me.fwtype === 'vm') {
+ add_boolean_row('enable', gettext('Enable Firewall'), 0);
+ add_boolean_row('dhcp', gettext('Enable DHCP'), 0);
+ add_boolean_row('macfilter', gettext('MAC filter'), 1);
+ add_log_row('log_level_in');
+ add_log_row('log_level_out');
+ } else if (me.fwtype === 'dc') {
+ add_boolean_row('enable', gettext('Enable Firewall'), 0);
+ }
+
+ if (me.fwtype === 'dc' || me.fwtype === 'vm') {
+ rows.policy_in = {
+ header: gettext('Input Policy'),
+ required: true,
+ defaultValue: 'DROP',
+ editor: {
+ xtype: 'pveWindowEdit',
+ subject: gettext('Input Policy'),
+ items: {
+ xtype: 'pveFirewallPolicySelector',
+ name: 'policy_in',
+ value: 'DROP',
+ fieldLabel: gettext('Input Policy')
+ }
+ }
+ };
+
+ rows.policy_out = {
+ header: gettext('Output Policy'),
+ required: true,
+ defaultValue: 'ACCEPT',
+ editor: {
+ xtype: 'pveWindowEdit',
+ subject: gettext('Output Policy'),
+ items: {
+ xtype: 'pveFirewallPolicySelector',
+ name: 'policy_out',
+ value: 'ACCEPT',
+ fieldLabel: gettext('Output Policy')
+ }
+ }
+ };
+ }
+
+ var reload = function() {
+ me.rstore.load();
+ };
+
+ var run_editor = function() {
+ var sm = me.getSelectionModel();
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+
+ var rowdef = rows[rec.data.key];
+ if (!rowdef.editor) {
+ return;
+ }
+
+ var win;
+ if (Ext.isString(rowdef.editor)) {
+ win = Ext.create(rowdef.editor, {
+ pveSelNode: me.pveSelNode,
+ confid: rec.data.key,
+ url: '/api2/extjs' + me.base_url
+ });
+ } else {
+ var config = Ext.apply({
+ pveSelNode: me.pveSelNode,
+ confid: rec.data.key,
+ url: '/api2/extjs' + me.base_url
+ }, rowdef.editor);
+ win = Ext.createWidget(rowdef.editor.xtype, config);
+ win.load();
+ }
+
+ win.show();
+ win.on('destroy', reload);
+ };
+
+ var edit_btn = new Ext.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ handler: run_editor
+ });
+
+ var set_button_status = function() {
+ var sm = me.getSelectionModel();
+ var rec = sm.getSelection()[0];
+
+ if (!rec) {
+ edit_btn.disable();
+ return;
+ }
+ var rowdef = rows[rec.data.key];
+ edit_btn.setDisabled(!rowdef.editor);
+ };
+
+ Ext.applyIf(me, {
+ url: "/api2/json" + me.base_url,
+ cwidth1: 150,
+ tbar: [ edit_btn ],
+ rows: rows,
+ listeners: {
+ itemdblclick: run_editor,
+ selectionchange: set_button_status
+ }
+ });
+
+ me.callParent();
+
+ me.on('show', reload);
+ }
+});
--- /dev/null
+Ext.define('PVE.form.FWMacroSelector', {
+ extend: 'PVE.form.ComboGrid',
+ alias: 'widget.pveFWMacroSelector',
+
+ initComponent: function() {
+ var me = this;
+
+ var store = Ext.create('Ext.data.Store', {
+ autoLoad: true,
+ fields: [ 'macro', 'descr' ],
+ idProperty: 'macro',
+ proxy: {
+ type: 'pve',
+ url: "/api2/json/cluster/firewall/macros"
+ },
+ sorters: {
+ property: 'macro',
+ order: 'DESC'
+ }
+ });
+
+ Ext.apply(me, {
+ store: store,
+ allowBlank: true,
+ autoSelect: false,
+ valueField: 'macro',
+ displayField: 'macro',
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Macro'),
+ dataIndex: 'macro',
+ hideable: false,
+ width: 100
+ },
+ {
+ header: gettext('Description'),
+ flex: 1,
+ dataIndex: 'descr'
+ }
+ ]
+ }
+ });
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.FirewallRulePanel', {
+ extend: 'PVE.panel.InputPanel',
+
+ allow_iface: false,
+
+ list_refs_url: undefined,
+
+ onGetValues: function(values) {
+ var me = this;
+
+ // hack: editable ComboGrid returns nothing when empty, so we need to set ''
+ // Also, disabled text fields return nothing, so we need to set ''
+
+ Ext.Array.each(['source', 'dest', 'proto', 'sport', 'dport'], function(key) {
+ if (values[key] === undefined) {
+ values[key] = '';
+ }
+ });
+
+ delete values.modified_marker;
+
+ return values;
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.list_refs_url) {
+ throw "no list_refs_url specified";
+ }
+
+ me.column1 = [
+ {
+ // hack: we use this field to mark the form 'dirty' when the
+ // record has errors- so that the user can safe the unmodified
+ // form again.
+ xtype: 'hiddenfield',
+ name: 'modified_marker',
+ value: '',
+ },
+ {
+ xtype: 'pveKVComboBox',
+ name: 'type',
+ value: 'in',
+ data: [['in', 'in'], ['out', 'out']],
+ fieldLabel: gettext('Direction'),
+ allowBlank: false
+ },
+ {
+ xtype: 'pveKVComboBox',
+ name: 'action',
+ value: 'ACCEPT',
+ data: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']],
+ fieldLabel: gettext('Action'),
+ allowBlank: false
+ }
+ ];
+
+ if (me.allow_iface) {
+ me.column1.push({
+ xtype: 'pvetextfield',
+ name: 'iface',
+ deleteEmpty: !me.create,
+ value: '',
+ fieldLabel: gettext('Interface')
+ });
+ } else {
+ me.column1.push({
+ xtype: 'displayfield',
+ fieldLabel: '',
+ height: 22, // hack: set same height as text fields
+ value: ''
+ });
+ }
+
+ me.column1.push([
+ {
+ xtype: 'displayfield',
+ fieldLabel: '',
+ height: 7,
+ value: ''
+ },
+ {
+ xtype: 'pveIPRefSelector',
+ name: 'source',
+ autoSelect: false,
+ editable: true,
+ base_url: me.list_refs_url,
+ value: '',
+ fieldLabel: gettext('Source')
+
+ },
+ {
+ xtype: 'pveIPRefSelector',
+ name: 'dest',
+ autoSelect: false,
+ editable: true,
+ base_url: me.list_refs_url,
+ value: '',
+ fieldLabel: gettext('Destination')
+ }
+ ]);
+
+
+ me.column2 = [
+ {
+ xtype: 'pvecheckbox',
+ name: 'enable',
+ checked: false,
+ height: 22, // hack: set same height as text fields
+ uncheckedValue: 0,
+ fieldLabel: gettext('Enable')
+ },
+ {
+ xtype: 'pveFWMacroSelector',
+ name: 'macro',
+ value: '',
+ fieldLabel: gettext('Macro'),
+ allowBlank: true,
+ listeners: {
+ change: function(f, value) {
+ if (value === '') {
+ me.down('field[name=proto]').setDisabled(false);
+ me.down('field[name=sport]').setDisabled(false);
+ me.down('field[name=dport]').setDisabled(false);
+ } else {
+ me.down('field[name=proto]').setDisabled(true);
+ me.down('field[name=proto]').setValue('');
+ me.down('field[name=sport]').setDisabled(true);
+ me.down('field[name=sport]').setValue('');
+ me.down('field[name=dport]').setDisabled(true);
+ me.down('field[name=dport]').setValue('');
+ }
+ }
+ }
+ },
+ {
+ xtype: 'pveIPProtocolSelector',
+ name: 'proto',
+ autoSelect: false,
+ editable: true,
+ value: '',
+ fieldLabel: gettext('Protocol')
+ },
+ {
+ xtype: 'displayfield',
+ fieldLabel: '',
+ height: 7,
+ value: ''
+ },
+ {
+ xtype: 'textfield',
+ name: 'sport',
+ value: '',
+ fieldLabel: gettext('Source port')
+ },
+ {
+ xtype: 'textfield',
+ name: 'dport',
+ height: 22, // hack: set same height as text fields
+ value: '',
+ fieldLabel: gettext('Dest. port')
+ }
+ ];
+
+ me.columnB = [
+ {
+ xtype: 'textfield',
+ name: 'comment',
+ value: '',
+ fieldLabel: gettext('Comment')
+ }
+ ];
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.FirewallRuleEdit', {
+ extend: 'PVE.window.Edit',
+
+ base_url: undefined,
+ list_refs_url: undefined,
+
+ allow_iface: false,
+
+ initComponent : function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ if (!me.base_url) {
+ throw "no base_url specified";
+ }
+ if (!me.list_refs_url) {
+ throw "no list_refs_url specified";
+ }
+
+ me.create = (me.rule_pos === undefined);
+
+ if (me.create) {
+ me.url = '/api2/extjs' + me.base_url;
+ me.method = 'POST';
+ } else {
+ me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString();
+ me.method = 'PUT';
+ }
+
+ var ipanel = Ext.create('PVE.FirewallRulePanel', {
+ create: me.create,
+ list_refs_url: me.list_refs_url,
+ allow_iface: me.allow_iface,
+ rule_pos: me.rule_pos
+ });
+
+ Ext.apply(me, {
+ subject: gettext('Rule'),
+ isAdd: true,
+ items: [ ipanel ]
+ });
+
+ me.callParent();
+
+ if (!me.create) {
+ me.load({
+ success: function(response, options) {
+ var values = response.result.data;
+ ipanel.setValues(values);
+ if (values.errors) {
+ var field = me.query('[isFormField][name=modified_marker]')[0];
+ field.setValue(1);
+ Ext.Function.defer(function() {
+ var form = ipanel.up('form').getForm();
+ form.markInvalid(values.errors)
+ }, 100);
+ }
+ }
+ });
+ }
+ }
+});
+
+Ext.define('PVE.FirewallGroupRuleEdit', {
+ extend: 'PVE.window.Edit',
+
+ base_url: undefined,
+
+ allow_iface: false,
+
+ initComponent : function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ me.create = (me.rule_pos === undefined);
+
+ if (me.create) {
+ me.url = '/api2/extjs' + me.base_url;
+ me.method = 'POST';
+ } else {
+ me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString();
+ me.method = 'PUT';
+ }
+
+ var column1 = [
+ {
+ xtype: 'hiddenfield',
+ name: 'type',
+ value: 'group'
+ },
+ {
+ xtype: 'pveSecurityGroupsSelector',
+ name: 'action',
+ value: '',
+ fieldLabel: gettext('Security Group'),
+ allowBlank: false
+ }
+ ];
+
+ if (me.allow_iface) {
+ column1.push({
+ xtype: 'pvetextfield',
+ name: 'iface',
+ deleteEmpty: !me.create,
+ value: '',
+ fieldLabel: gettext('Interface')
+ });
+ }
+
+ var ipanel = Ext.create('PVE.panel.InputPanel', {
+ create: me.create,
+ column1: column1,
+ column2: [
+ {
+ xtype: 'pvecheckbox',
+ name: 'enable',
+ checked: false,
+ height: 22, // hack: set same height as text fields
+ uncheckedValue: 0,
+ fieldLabel: gettext('Enable')
+ }
+ ],
+ columnB: [
+ {
+ xtype: 'textfield',
+ name: 'comment',
+ value: '',
+ fieldLabel: gettext('Comment')
+ }
+ ]
+ });
+
+ Ext.apply(me, {
+ subject: gettext('Rule'),
+ isAdd: true,
+ items: [ ipanel ]
+ });
+
+ me.callParent();
+
+ if (!me.create) {
+ me.load({
+ success: function(response, options) {
+ var values = response.result.data;
+ ipanel.setValues(values);
+ }
+ });
+ }
+ }
+});
+
+Ext.define('PVE.FirewallRules', {
+ extend: 'Ext.grid.Panel',
+ alias: 'widget.pveFirewallRules',
+
+ base_url: undefined,
+ list_refs_url: undefined,
+
+ addBtn: undefined,
+ removeBtn: undefined,
+ editBtn: undefined,
+ groupBtn: undefined,
+
+ tbar_prefix: undefined,
+
+ allow_groups: true,
+ allow_iface: false,
+
+ setBaseUrl: function(url) {
+ var me = this;
+
+ me.base_url = url;
+
+ if (url === undefined) {
+ me.addBtn.setDisabled(true);
+ if (me.groupBtn) {
+ me.groupBtn.setDisabled(true);
+ }
+ me.store.removeAll();
+ } else {
+ me.addBtn.setDisabled(false);
+ if (me.groupBtn) {
+ me.groupBtn.setDisabled(false);
+ }
+ me.store.setProxy({
+ type: 'pve',
+ url: '/api2/json' + url
+ });
+
+ me.store.load();
+ }
+ },
+
+ moveRule: function(from, to) {
+ var me = this;
+
+ if (!me.base_url) {
+ return;
+ }
+
+ PVE.Utils.API2Request({
+ url: me.base_url + "/" + from,
+ method: 'PUT',
+ params: { moveto: to },
+ waitMsgTarget: me,
+ failure: function(response, options) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ callback: function() {
+ me.store.load();
+ }
+ });
+ },
+
+ updateRule: function(rule) {
+ var me = this;
+
+ if (!me.base_url) {
+ return;
+ }
+
+ rule.enable = rule.enable ? 1 : 0;
+
+ var pos = rule.pos;
+ delete rule.pos;
+ delete rule.errors;
+
+ PVE.Utils.API2Request({
+ url: me.base_url + '/' + pos.toString(),
+ method: 'PUT',
+ params: rule,
+ waitMsgTarget: me,
+ failure: function(response, options) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ callback: function() {
+ me.store.load();
+ }
+ });
+ },
+
+ deleteRule: function(rule) {
+ var me = this;
+
+ if (!me.base_url) {
+ return;
+ }
+
+ PVE.Utils.API2Request({
+ url: me.base_url + '/' + rule.pos.toString() +
+ '?digest=' + encodeURIComponent(rule.digest),
+ method: 'DELETE',
+ waitMsgTarget: me,
+ failure: function(response, options) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ callback: function() {
+ me.store.load();
+ }
+ });
+ },
+
+ initComponent: function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ if (!me.list_refs_url) {
+ throw "no list_refs_url specified";
+ }
+
+ var store = new Ext.data.Store({
+ model: 'pve-fw-rule'
+ });
+
+ var reload = function() {
+ store.load();
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+ var type = rec.data.type;
+
+ var editor;
+ if (type === 'in' || type === 'out') {
+ editor = 'PVE.FirewallRuleEdit';
+ } else if (type === 'group') {
+ editor = 'PVE.FirewallGroupRuleEdit';
+ } else {
+ return;
+ }
+
+ var win = Ext.create(editor, {
+ digest: rec.data.digest,
+ allow_iface: me.allow_iface,
+ base_url: me.base_url,
+ list_refs_url: me.list_refs_url,
+ rule_pos: rec.data.pos
+ });
+
+ win.show();
+ win.on('destroy', reload);
+ };
+
+ me.editBtn = new PVE.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ me.addBtn = Ext.create('Ext.Button', {
+ text: gettext('Add'),
+ disabled: true,
+ handler: function() {
+ var win = Ext.create('PVE.FirewallRuleEdit', {
+ allow_iface: me.allow_iface,
+ base_url: me.base_url,
+ list_refs_url: me.list_refs_url
+ });
+ win.on('destroy', reload);
+ win.show();
+ }
+ });
+
+ if (me.allow_groups) {
+ me.groupBtn = Ext.create('Ext.Button', {
+ text: gettext('Insert') + ': ' +
+ gettext('Security Group'),
+ disabled: true,
+ handler: function() {
+ var win = Ext.create('PVE.FirewallGroupRuleEdit', {
+ allow_iface: me.allow_iface,
+ base_url: me.base_url
+ });
+ win.on('destroy', reload);
+ win.show();
+ }
+ });
+ }
+
+ me.removeBtn = new PVE.button.Button({
+ text: gettext('Remove'),
+ selModel: sm,
+ disabled: true,
+ handler: function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+ me.deleteRule(rec.data);
+ }
+ });
+
+ var tbar = me.tbar_prefix ? [ me.tbar_prefix ] : [];
+ tbar.push(me.addBtn);
+ if (me.groupBtn) {
+ tbar.push(me.groupBtn);
+ }
+ tbar.push([ me.removeBtn, me.editBtn ]);
+
+ var render_errors = function(name, value, metaData, record) {
+ var errors = record.data.errors;
+ if (errors && errors[name]) {
+ metaData.tdCls = 'x-form-invalid-field';
+ var html = '<p>' + Ext.htmlEncode(errors[name]) + '</p>';
+ metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' +
+ html.replace(/\"/g,'"') + '"';
+ }
+ return value;
+ };
+
+ var columns = [
+ {
+ // similar to xtype: 'rownumberer',
+ dataIndex: 'pos',
+ resizable: false,
+ width: 23,
+ sortable: false,
+ align: 'right',
+ hideable: false,
+ menuDisabled: true,
+ renderer: function(value, metaData, record, rowIdx, colIdx, store) {
+ metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
+ if (value >= 0) {
+ return value;
+ }
+ return '';
+ }
+ },
+ {
+ xtype: 'checkcolumn',
+ header: gettext('Enable'),
+ dataIndex: 'enable',
+ listeners: {
+ checkchange: function(column, record, checked) {
+ record.commit();
+ var data = {};
+ record.fields.each(function(field) {
+ data[field.name] = record.get(field.name);
+ });
+ if (!me.allow_iface || !data.iface) {
+ delete data.iface;
+ }
+ me.updateRule(data);
+ }
+ },
+ width: 50
+ },
+ {
+ header: gettext('Type'),
+ dataIndex: 'type',
+ renderer: function(value, metaData, record) {
+ return render_errors('type', value, metaData, record);
+ },
+ width: 50
+ },
+ {
+ header: gettext('Action'),
+ dataIndex: 'action',
+ renderer: function(value, metaData, record) {
+ return render_errors('action', value, metaData, record);
+ },
+ width: 80
+ },
+ {
+ header: gettext('Macro'),
+ dataIndex: 'macro',
+ renderer: function(value, metaData, record) {
+ return render_errors('macro', value, metaData, record);
+ },
+ width: 80
+ }
+ ];
+
+ if (me.allow_iface) {
+ columns.push({
+ header: gettext('Interface'),
+ dataIndex: 'iface',
+ renderer: function(value, metaData, record) {
+ return render_errors('iface', value, metaData, record);
+ },
+ width: 80
+ });
+ }
+
+ columns.push([
+ {
+ header: gettext('Source'),
+ dataIndex: 'source',
+ renderer: function(value, metaData, record) {
+ return render_errors('source', value, metaData, record);
+ },
+ width: 100
+ },
+ {
+ header: gettext('Destination'),
+ dataIndex: 'dest',
+ renderer: function(value, metaData, record) {
+ return render_errors('dest', value, metaData, record);
+ },
+ width: 100
+ },
+ {
+ header: gettext('Protocol'),
+ dataIndex: 'proto',
+ renderer: function(value, metaData, record) {
+ return render_errors('proto', value, metaData, record);
+ },
+ width: 100
+ },
+ {
+ header: gettext('Dest. port'),
+ dataIndex: 'dport',
+ renderer: function(value, metaData, record) {
+ return render_errors('dport', value, metaData, record);
+ },
+ width: 100
+ },
+ {
+ header: gettext('Source port'),
+ dataIndex: 'sport',
+ renderer: function(value, metaData, record) {
+ return render_errors('sport', value, metaData, record);
+ },
+ width: 100
+ },
+ {
+ header: gettext('Comment'),
+ dataIndex: 'comment',
+ flex: 1,
+ renderer: function(value, metaData, record) {
+ return render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record);
+ }
+ }
+ ]);
+
+ Ext.apply(me, {
+ store: store,
+ selModel: sm,
+ tbar: tbar,
+ viewConfig: {
+ plugins: [
+ {
+ ptype: 'gridviewdragdrop',
+ dragGroup: 'FWRuleDDGroup',
+ dropGroup: 'FWRuleDDGroup'
+ }
+ ],
+ listeners: {
+ beforedrop: function(node, data, dropRec, dropPosition) {
+ if (!dropRec) {
+ return false; // empty view
+ }
+ var moveto = dropRec.get('pos');
+ if (dropPosition === 'after') {
+ moveto++;
+ }
+ var pos = data.records[0].get('pos');
+ me.moveRule(pos, moveto);
+ return 0;
+ },
+ itemdblclick: run_editor
+ }
+ },
+ sortableColumns: false,
+ columns: columns
+ });
+
+ me.callParent();
+
+ if (me.base_url) {
+ me.setBaseUrl(me.base_url); // load
+ }
+ }
+}, function() {
+
+ Ext.define('pve-fw-rule', {
+ extend: 'Ext.data.Model',
+ fields: [ { name: 'enable', type: 'boolean' },
+ 'type', 'action', 'macro', 'source', 'dest', 'proto', 'iface',
+ 'dport', 'sport', 'comment', 'pos', 'digest', 'errors' ],
+ idProperty: 'pos'
+ });
+
+});
--- /dev/null
+Ext.define('PVE.grid.ObjectGrid', {
+ extend: 'Ext.grid.GridPanel',
+ alias: ['widget.pveObjectGrid'],
+
+ getObjectValue: function(key, defaultValue) {
+ var me = this;
+ var rec = me.store.getById(key);
+ if (rec) {
+ return rec.data.value;
+ }
+ return defaultValue;
+ },
+
+ renderKey: function(key, metaData, record, rowIndex, colIndex, store) {
+ var me = this;
+ var rows = me.rows;
+ var rowdef = (rows && rows[key]) ? rows[key] : {};
+ return rowdef.header || key;
+ },
+
+ renderValue: function(value, metaData, record, rowIndex, colIndex, store) {
+ var me = this;
+ var rows = me.rows;
+ var key = record.data.key;
+ var rowdef = (rows && rows[key]) ? rows[key] : {};
+
+ var renderer = rowdef.renderer;
+ if (renderer) {
+ return renderer(value, metaData, record, rowIndex, colIndex, store);
+ }
+
+ return value;
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ var rows = me.rows;
+
+ if (!me.rstore) {
+ if (!me.url) {
+ throw "no url specified";
+ }
+
+ me.rstore = Ext.create('PVE.data.ObjectStore', {
+ url: me.url,
+ interval: me.interval,
+ extraParams: me.extraParams,
+ rows: me.rows
+ });
+ }
+
+ var rstore = me.rstore;
+
+ var store = Ext.create('PVE.data.DiffStore', { rstore: rstore });
+
+ if (rows) {
+ Ext.Object.each(rows, function(key, rowdef) {
+ if (Ext.isDefined(rowdef.defaultValue)) {
+ store.add({ key: key, value: rowdef.defaultValue });
+ } else if (rowdef.required) {
+ store.add({ key: key, value: undefined });
+ }
+ });
+ }
+
+ if (me.sorterFn) {
+ store.sorters.add(new Ext.util.Sorter({
+ sorterFn: me.sorterFn
+ }));
+ }
+
+ store.filters.add(new Ext.util.Filter({
+ filterFn: function(item) {
+ if (rows) {
+ var rowdef = rows[item.data.key];
+ if (!rowdef || (rowdef.visible === false)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }));
+
+ PVE.Utils.monStoreErrors(me, rstore);
+
+ Ext.applyIf(me, {
+ store: store,
+ hideHeaders: true,
+ stateful: false,
+ columns: [
+ {
+ header: gettext('Name'),
+ width: me.cwidth1 || 100,
+ dataIndex: 'key',
+ renderer: me.renderKey
+ },
+ {
+ flex: 1,
+ header: gettext('Value'),
+ dataIndex: 'value',
+ renderer: me.renderValue
+ }
+ ]
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.grid.PendingObjectGrid', {
+ extend: 'PVE.grid.ObjectGrid',
+ alias: ['widget.pvePendingObjectGrid'],
+
+ getObjectValue: function(key, defaultValue, pending) {
+ var me = this;
+ var rec = me.store.getById(key);
+ if (rec) {
+ var value = (pending && Ext.isDefined(rec.data.pending) && (rec.data.pending !== '')) ?
+ rec.data.pending : rec.data.value;
+
+ if (Ext.isDefined(value) && (value !== '')) {
+ return value;
+ } else {
+ return defaultValue;
+ }
+ }
+ return defaultValue;
+ },
+
+ hasPendingChanges: function(key) {
+ var me = this;
+ var rows = me.rows;
+ var rowdef = (rows && rows[key]) ? rows[key] : {};
+ var keys = rowdef.multiKey || [ key ];
+ var pending = false;
+
+ Ext.Array.each(keys, function(k) {
+ var rec = me.store.getById(k);
+ if (rec && rec.data && Ext.isDefined(rec.data.pending) && (rec.data.pending !== '')) {
+ pending = true;
+ }
+ });
+
+ return pending;
+ },
+
+ renderValue: function(value, metaData, record, rowIndex, colIndex, store) {
+ var me = this;
+ var rows = me.rows;
+ var key = record.data.key;
+ var rowdef = (rows && rows[key]) ? rows[key] : {};
+ var renderer = rowdef.renderer;
+ var current = '';
+ var pendingdelete = '';
+ var pending = '';
+
+ if (renderer) {
+ current = renderer(value, metaData, record, rowIndex, colIndex, store, false);
+ if (me.hasPendingChanges(key)) {
+ pending = renderer(record.data.pending, metaData, record, rowIndex, colIndex, store, true);
+ }
+ if (pending == current) {
+ pending = undefined;
+ }
+ } else {
+ current = value;
+ pending = record.data.pending;
+ }
+
+ if (record.data['delete']) {
+ pendingdelete = '<div style="text-decoration: line-through;">'+ current +'</div>';
+ }
+
+ if (pending || pendingdelete) {
+ return current + '<div style="color:red">' + pending + pendingdelete + '</div>';
+ } else {
+ return current;
+ }
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ var rows = me.rows;
+
+ if (!me.rstore) {
+ if (!me.url) {
+ throw "no url specified";
+ }
+
+ me.rstore = Ext.create('PVE.data.ObjectStore', {
+ model: 'KeyValuePendingDelete',
+ readArray: true,
+ url: me.url,
+ interval: me.interval,
+ extraParams: me.extraParams,
+ rows: me.rows
+ });
+ }
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.pool.AddVM', {
+ extend: 'PVE.window.Edit',
+
+ initComponent : function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ if (!me.pool) {
+ throw "no pool specified";
+ }
+
+ me.create = true;
+ me.isAdd = true;
+ me.url = "/pools/" + me.pool;
+ me.method = 'PUT';
+
+ Ext.apply(me, {
+ subject: gettext('Virtual Machine'),
+ width: 350,
+ items: [
+ {
+ xtype: 'pveVMIDSelector',
+ name: 'vms',
+ validateExists: true,
+ value: '',
+ fieldLabel: "VM ID"
+ }
+ ]
+ });
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.pool.AddStorage', {
+ extend: 'PVE.window.Edit',
+
+ initComponent : function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ if (!me.pool) {
+ throw "no pool specified";
+ }
+
+ me.create = true;
+ me.isAdd = true;
+ me.url = "/pools/" + me.pool;
+ me.method = 'PUT';
+
+ Ext.apply(me, {
+ subject: gettext('Storage'),
+ width: 350,
+ items: [
+ {
+ xtype: 'PVE.form.StorageSelector',
+ name: 'storage',
+ nodename: 'localhost',
+ autoSelect: false,
+ value: '',
+ fieldLabel: gettext("Storage")
+ }
+ ]
+ });
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.grid.PoolMembers', {
+ extend: 'Ext.grid.GridPanel',
+ alias: ['widget.pvePoolMembers'],
+
+ // fixme: dynamic status update ?
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.pool) {
+ throw "no pool specified";
+ }
+
+ var store = Ext.create('Ext.data.Store', {
+ model: 'PVEResources',
+ sorters: [
+ {
+ property : 'type',
+ direction: 'ASC'
+ }
+ ],
+ proxy: {
+ type: 'pve',
+ root: 'data.members',
+ url: "/api2/json/pools/" + me.pool
+ }
+ });
+
+ var coldef = PVE.data.ResourceStore.defaultColums();
+
+ var reload = function() {
+ store.load();
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var remove_btn = new PVE.button.Button({
+ text: gettext('Remove'),
+ disabled: true,
+ selModel: sm,
+ confirmMsg: function (rec) {
+ return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
+ "'" + rec.data.id + "'");
+ },
+ handler: function(btn, event, rec) {
+ var params = { 'delete': 1 };
+ if (rec.data.type === 'storage') {
+ params.storage = rec.data.storage;
+ } else if (rec.data.type === 'qemu' || rec.data.type === 'lxc' || rec.data.type === 'openvz') {
+ params.vms = rec.data.vmid;
+ } else {
+ throw "unknown resource type";
+ }
+
+ PVE.Utils.API2Request({
+ url: '/pools/' + me.pool,
+ method: 'PUT',
+ params: params,
+ waitMsgTarget: me,
+ callback: function() {
+ reload();
+ },
+ failure: function (response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ }
+ });
+
+ Ext.apply(me, {
+ store: store,
+ selModel: sm,
+ tbar: [
+ {
+ text: gettext('Add'),
+ menu: new Ext.menu.Menu({
+ items: [
+ {
+ text: gettext('Virtual Machine'),
+ iconCls: 'pve-itype-icon-qemu',
+ handler: function() {
+ var win = Ext.create('PVE.pool.AddVM', { pool: me.pool });
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ {
+ text: gettext('Storage'),
+ iconCls: 'pve-itype-icon-storage',
+ handler: function() {
+ var win = Ext.create('PVE.pool.AddStorage', { pool: me.pool });
+ win.on('destroy', reload);
+ win.show();
+ }
+ }
+ ]
+ })
+ },
+ remove_btn
+ ],
+ viewConfig: {
+ stripeRows: true
+ },
+ columns: coldef,
+ listeners: {
+ show: reload
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.grid.ResourceGrid', {
+ extend: 'Ext.grid.GridPanel',
+ alias: ['widget.pveResourceGrid'],
+
+ //fixme: this makes still problems with the scrollbar
+ //features: [ {ftype: 'chunking'}],
+
+ title: gettext('Search'),
+
+ initComponent : function() {
+ var me = this;
+
+ var rstore = PVE.data.ResourceStore;
+ var sp = Ext.state.Manager.getProvider();
+
+ var coldef = rstore.defaultColums();
+
+ var store = Ext.create('Ext.data.Store', {
+ model: 'PVEResources',
+ sorters: [
+ {
+ property : 'type',
+ direction: 'ASC'
+ }
+ ],
+ proxy: { type: 'memory' }
+ });
+
+ var textfilter = '';
+
+ var textfilter_match = function(item) {
+ var match = false;
+ Ext.each(['name', 'storage', 'node', 'type', 'text'], function(field) {
+ var v = item.data[field];
+ if (v !== undefined) {
+ v = v.toLowerCase();
+ if (v.indexOf(textfilter) >= 0) {
+ match = true;
+ return false;
+ }
+ }
+ });
+ return match;
+ };
+
+ var updateGrid = function() {
+
+ var filterfn = me.viewFilter ? me.viewFilter.filterfn : null;
+
+ //console.log("START GRID UPDATE " + me.viewFilter);
+
+ store.suspendEvents();
+
+ var nodeidx = {};
+ var gather_child_nodes = function(cn) {
+ if (!cn) {
+ return;
+ }
+ var cs = cn.childNodes;
+ if (!cs) {
+ return;
+ }
+ var len = cs.length, i = 0, n, res;
+
+ for (; i < len; i++) {
+ var child = cs[i];
+ var orgnode = rstore.data.get(child.data.id);
+ if (orgnode) {
+ if ((!filterfn || filterfn(child)) &&
+ (!textfilter || textfilter_match(child))) {
+ nodeidx[child.data.id] = orgnode;
+ }
+ }
+ gather_child_nodes(child);
+ }
+ };
+ gather_child_nodes(me.pveSelNode);
+
+ // remove vanished items
+ var rmlist = [];
+ store.each(function(olditem) {
+ var item = nodeidx[olditem.data.id];
+ if (!item) {
+ //console.log("GRID REM UID: " + olditem.data.id);
+ rmlist.push(olditem);
+ }
+ });
+
+ if (rmlist.length) {
+ store.remove(rmlist);
+ }
+
+ // add new items
+ var addlist = [];
+ var key;
+ for (key in nodeidx) {
+ if (nodeidx.hasOwnProperty(key)) {
+ var item = nodeidx[key];
+
+ // getById() use find(), which is slow (ExtJS4 DP5)
+ //var olditem = store.getById(item.data.id);
+ var olditem = store.data.get(item.data.id);
+
+ if (!olditem) {
+ //console.log("GRID ADD UID: " + item.data.id);
+ var info = Ext.apply({}, item.data);
+ var child = Ext.create(store.model, info);
+ addlist.push(item);
+ continue;
+ }
+ // try to detect changes
+ var changes = false;
+ var fieldkeys = PVE.data.ResourceStore.fieldNames;
+ var fieldcount = fieldkeys.length;
+ var fieldind;
+ for (fieldind = 0; fieldind < fieldcount; fieldind++) {
+ var field = fieldkeys[fieldind];
+ if (field != 'id' && item.data[field] != olditem.data[field]) {
+ changes = true;
+ //console.log("changed item " + item.id + " " + field + " " + item.data[field] + " != " + olditem.data[field]);
+ olditem.beginEdit();
+ olditem.set(field, item.data[field]);
+ }
+ }
+ if (changes) {
+ olditem.endEdit(true);
+ olditem.commit(true);
+ }
+ }
+ }
+
+ if (addlist.length) {
+ store.add(addlist);
+ }
+
+ store.sort();
+
+ store.resumeEvents();
+
+ store.fireEvent('datachanged', store);
+
+ //console.log("END GRID UPDATE");
+ };
+
+ var filter_task = new Ext.util.DelayedTask(function(){
+ updateGrid();
+ });
+
+ var load_cb = function() {
+ updateGrid();
+ };
+
+ Ext.apply(me, {
+ store: store,
+ tbar: [
+ '->',
+ gettext('Search') + ':', ' ',
+ {
+ xtype: 'textfield',
+ width: 200,
+ value: textfilter,
+ enableKeyEvents: true,
+ listeners: {
+ keyup: function(field, e) {
+ var v = field.getValue();
+ textfilter = v.toLowerCase();
+ filter_task.delay(500);
+ }
+ }
+ }
+ ],
+ viewConfig: {
+ stripeRows: true
+ },
+ listeners: {
+ itemcontextmenu: function(v, record, item, index, event) {
+ event.stopEvent();
+ v.select(record);
+ var menu;
+
+ if (record.data.type === 'qemu' && !record.data.template) {
+ menu = Ext.create('PVE.qemu.CmdMenu', {
+ pveSelNode: record
+ });
+ } else if (record.data.type === 'qemu' && record.data.template) {
+ menu = Ext.create('PVE.qemu.TemplateMenu', {
+ pveSelNode: record
+ });
+ } else if (record.data.type === 'lxc') {
+ menu = Ext.create('PVE.lxc.CmdMenu', {
+ pveSelNode: record
+ });
+ } else {
+ return;
+ }
+
+ menu.showAt(event.getXY());
+ },
+ itemdblclick: function(v, record) {
+ var ws = me.up('pveStdWorkspace');
+ ws.selectById(record.data.id);
+ },
+ destroy: function() {
+ rstore.un("load", load_cb);
+ }
+ },
+ columns: coldef
+ });
+
+ me.callParent();
+
+ updateGrid();
+ rstore.on("load", load_cb);
+ }
+});
--- /dev/null
+ Ext.override(Ext.view.Table, {
+ afterRender: function() {
+ var me = this;
+
+ me.callParent();
+// EXT5DEBUG
+// me.mon(me.el, {
+// scroll: me.fireBodyScroll,
+// scope: me
+// });
+// if (!me.featuresMC ||
+// (me.featuresMC.findIndex('ftype', 'selectable') < 0)) {
+// me.el.unselectable();
+// }
+//
+// me.attachEventsForFeatures();
+ }
+});
+
+Ext.define('PVE.grid.SelectFeature', {
+ extend: 'Ext.grid.feature.Feature',
+ alias: 'feature.selectable',
+
+ mutateMetaRowTpl: function(metaRowTpl) {
+ var tpl, i,
+ ln = metaRowTpl.length;
+
+ for (i = 0; i < ln; i++) {
+ tpl = metaRowTpl[i];
+ tpl = tpl.replace(/x-grid-row/, 'x-grid-row x-selectable');
+ tpl = tpl.replace(/x-grid-cell-inner x-unselectable/g, 'x-grid-cell-inner');
+ tpl = tpl.replace(/unselectable="on"/g, '');
+ metaRowTpl[i] = tpl;
+ }
+ }
+});
--- /dev/null
+Ext.define('PVE.panel.Config', {
+ extend: 'Ext.panel.Panel',
+ alias: 'widget.pvePanelConfig',
+
+ initComponent: function() {
+ var me = this;
+
+ var stateid = me.hstateid;
+
+ var sp = Ext.state.Manager.getProvider();
+
+ var activeTab;
+
+ var hsregex = /^([^\-\s]+)(-\S+)?$/;
+
+ if (stateid) {
+ var state = sp.get(stateid);
+ if (state && state.value) {
+ var res = hsregex.exec(state.value);
+ if (res && res[1]) {
+ activeTab = res[1];
+ }
+ }
+ }
+
+ var items = me.items || [];
+ me.items = undefined;
+
+ var tbar = me.tbar || [];
+ me.tbar = undefined;
+
+ var title = me.title || me.pveSelNode.data.text;
+ me.title = undefined;
+
+ tbar.unshift('->');
+ tbar.unshift({
+ xtype: 'tbtext',
+ text: title,
+ baseCls: 'x-panel-header-text',
+ padding: '0 0 5 0'
+ });
+
+ Ext.applyIf(me, { showSearch: true });
+
+ if (me.showSearch) {
+ items.unshift({
+ itemId: 'search',
+ xtype: 'pveResourceGrid'
+ });
+ }
+
+ var toolbar = Ext.create('Ext.toolbar.Toolbar', {
+ items: tbar,
+ style: 'border:0px;',
+ height: 28
+ });
+
+ var tab = Ext.create('Ext.tab.Panel', {
+ flex: 1,
+ border: true,
+ activeTab: activeTab,
+ defaults: Ext.apply(me.defaults || {}, {
+ pveSelNode: me.pveSelNode,
+ viewFilter: me.viewFilter,
+ workspace: me.workspace,
+ border: false
+ }),
+ items: items,
+ listeners: {
+ afterrender: function(tp) {
+ var first = tp.items.get(0);
+ if (first) {
+ first.fireEvent('show', first);
+ }
+ },
+ tabchange: function(tp, newcard, oldcard) {
+ var ntab = newcard.itemId;
+
+ // Note: '' is alias for first tab.
+ // First tab can be 'search' or something else
+ if (newcard.itemId === items[0].itemId) {
+ ntab = '';
+ }
+ if (stateid) {
+ if (newcard.phstateid) {
+ sp.set(newcard.phstateid, newcard.getHState());
+ } else {
+ sp.set(stateid, { value: ntab });
+ }
+ }
+ }
+ }
+ });
+
+ Ext.apply(me, {
+ layout: { type: 'vbox', align: 'stretch' },
+ items: [ toolbar, tab]
+ });
+
+ me.callParent();
+
+ var statechange = function(sp, key, state) {
+ if (stateid && (key === stateid) && state) {
+ var atab = tab.getActiveTab().itemId;
+ var res = hsregex.exec(state.value);
+ var ntab = (res && res[1]) ? res[1] : items[0].itemId;
+ if (ntab && (atab != ntab)) {
+ tab.setActiveTab(ntab);
+ }
+ }
+ };
+
+ if (stateid) {
+ me.mon(sp, 'statechange', statechange);
+ }
+ }
+});
--- /dev/null
+Ext.define('PVE.panel.Firewall', {
+ extend: 'PVE.panel.SubConfig',
+ alias: 'widget.pveFirewallPanel',
+
+ configPrefix: 'firewall',
+
+ fwtype: undefined, // 'dc', 'node' or 'vm'
+
+ base_url: undefined,
+
+ initComponent: function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ if (!me.base_url) {
+ throw "no base_url specified";
+ }
+
+ if (!(me.fwtype === 'dc' || me.fwtype === 'node' || me.fwtype === 'vm')) {
+ throw "unknown firewall panel type";
+ }
+
+ var list_refs_url = me.fwtype === 'vm' ? (me.base_url + '/refs') :
+ '/cluster/firewall/refs';
+
+ var items = [
+ {
+ xtype: 'pveFirewallRules',
+ title: gettext('Rules'),
+ allow_iface: true,
+ base_url: me.base_url + '/rules',
+ list_refs_url: list_refs_url,
+ itemId: 'rules'
+ }
+ ];
+
+ if (me.fwtype === 'dc') {
+ items.push({
+ xtype: 'pveSecurityGroups',
+ title: gettext('Security Group'),
+ itemId: 'sg'
+ });
+ items.push({
+ xtype: 'pveFirewallAliases',
+ base_url: '/cluster/firewall/aliases',
+ itemId: 'aliases'
+ });
+ items.push({
+ xtype: 'pveIPSet',
+ base_url: '/cluster/firewall/ipset',
+ list_refs_url: list_refs_url,
+ itemId: 'ipset'
+ });
+ }
+
+ if (me.fwtype === 'vm') {
+ items.push({
+ xtype: 'pveFirewallAliases',
+ base_url: me.base_url + '/aliases',
+ itemId: 'aliases'
+ });
+ items.push({
+ xtype: 'pveIPSet',
+ base_url: me.base_url + '/ipset',
+ list_refs_url: list_refs_url,
+ itemId: 'ipset'
+ });
+ }
+
+ items.push({
+ xtype: 'pveFirewallOptions',
+ title: gettext('Options'),
+ base_url: me.base_url + '/options',
+ fwtype: me.fwtype,
+ itemId: 'options'
+ });
+
+ if (me.fwtype !== 'dc') {
+ items.push({
+ title: 'Log',
+ itemId: 'fwlog',
+ xtype: 'pveLogView',
+ url: '/api2/extjs' + me.base_url + '/log'
+ });
+ }
+
+ Ext.apply(me, {
+ defaults: {
+ border: false,
+ pveSelNode: me.pveSelNode
+ },
+ items: items
+ });
+
+ me.callParent();
+ }
+});
\ No newline at end of file
--- /dev/null
+Ext.define('PVE.IPSetList', {
+ extend: 'Ext.grid.Panel',
+ alias: 'widget.pveIPSetList',
+
+ ipset_panel: undefined,
+
+ base_url: undefined,
+
+ addBtn: undefined,
+ removeBtn: undefined,
+ editBtn: undefined,
+
+ initComponent: function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ if (me.ipset_panel == undefined) {
+ throw "no rule panel specified";
+ }
+
+ if (me.base_url == undefined) {
+ throw "no base_url specified";
+ }
+
+ var store = new Ext.data.Store({
+ fields: [ 'name', 'comment', 'digest' ],
+ proxy: {
+ type: 'pve',
+ url: "/api2/json" + me.base_url
+ },
+ idProperty: 'name',
+ sorters: {
+ property: 'name',
+ order: 'DESC'
+ }
+ });
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var reload = function() {
+ var oldrec = sm.getSelection()[0];
+ store.load(function(records, operation, success) {
+ if (oldrec) {
+ var rec = store.findRecord('name', oldrec.data.name);
+ if (rec) {
+ sm.select(rec);
+ }
+ }
+ });
+ };
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+ var win = Ext.create('PVE.window.Edit', {
+ subject: "IPSet '" + rec.data.name + "'",
+ url: me.base_url,
+ method: 'POST',
+ digest: rec.data.digest,
+ items: [
+ {
+ xtype: 'hiddenfield',
+ name: 'rename',
+ value: rec.data.name
+ },
+ {
+ xtype: 'textfield',
+ name: 'name',
+ value: rec.data.name,
+ fieldLabel: gettext('Name'),
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ name: 'comment',
+ value: rec.data.comment,
+ fieldLabel: gettext('Comment')
+ }
+ ]
+ });
+ win.show();
+ win.on('destroy', reload);
+ };
+
+ me.editBtn = new PVE.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ me.addBtn = new PVE.button.Button({
+ text: gettext('Create'),
+ handler: function() {
+ sm.deselectAll();
+ var win = Ext.create('PVE.window.Edit', {
+ subject: 'IPSet',
+ url: me.base_url,
+ method: 'POST',
+ items: [
+ {
+ xtype: 'textfield',
+ name: 'name',
+ value: '',
+ fieldLabel: gettext('Name'),
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ name: 'comment',
+ value: '',
+ fieldLabel: gettext('Comment')
+ }
+ ]
+ });
+ win.show();
+ win.on('destroy', reload);
+
+ }
+ });
+
+ me.removeBtn = new PVE.button.Button({
+ text: gettext('Remove'),
+ selModel: sm,
+ disabled: true,
+ handler: function() {
+ var rec = sm.getSelection()[0];
+ if (!rec || !me.base_url) {
+ return;
+ }
+ PVE.Utils.API2Request({
+ url: me.base_url + '/' + rec.data.name,
+ method: 'DELETE',
+ waitMsgTarget: me,
+ failure: function(response, options) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ callback: reload
+ });
+ }
+ });
+
+ Ext.apply(me, {
+ store: store,
+ tbar: [ '<b>IPSet:</b>', me.addBtn, me.removeBtn, me.editBtn ],
+ selModel: sm,
+ columns: [
+ { header: 'IPSet', dataIndex: 'name', width: 100 },
+ { header: gettext('Comment'), dataIndex: 'comment', flex: 1 }
+ ],
+ listeners: {
+ itemdblclick: run_editor,
+ select: function(sm, rec) {
+ var url = me.base_url + '/' + rec.data.name;
+ me.ipset_panel.setBaseUrl(url);
+ },
+ deselect: function() {
+ me.ipset_panel.setBaseUrl(undefined);
+ },
+ show: reload
+ }
+ });
+
+ me.callParent();
+
+ store.load();
+ }
+});
+
+Ext.define('PVE.IPSetCidrEdit', {
+ extend: 'PVE.window.Edit',
+
+ cidr: undefined,
+
+ initComponent : function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ me.create = (me.cidr === undefined);
+
+
+ if (me.create) {
+ me.url = '/api2/extjs' + me.base_url;
+ me.method = 'POST';
+ } else {
+ me.url = '/api2/extjs' + me.base_url + '/' + me.cidr;
+ me.method = 'PUT';
+ }
+
+ var column1 = [];
+
+ if (me.create) {
+ if (!me.list_refs_url) {
+ throw "no alias_base_url specified";
+ }
+
+ column1.push({
+ xtype: 'pveIPRefSelector',
+ name: 'cidr',
+ ref_type: 'alias',
+ autoSelect: false,
+ editable: true,
+ base_url: me.list_refs_url,
+ value: '',
+ fieldLabel: gettext('IP/CIDR')
+ });
+ } else {
+ column1.push({
+ xtype: 'displayfield',
+ name: 'cidr',
+ height: 22, // hack: set same height as text fields
+ value: '',
+ fieldLabel: gettext('IP/CIDR')
+ });
+ }
+
+ var ipanel = Ext.create('PVE.panel.InputPanel', {
+ create: me.create,
+ column1: column1,
+ column2: [
+ {
+ xtype: 'pvecheckbox',
+ name: 'nomatch',
+ checked: false,
+ height: 22, // hack: set same height as text fields
+ uncheckedValue: 0,
+ fieldLabel: gettext('nomatch')
+ }
+ ],
+ columnB: [
+ {
+ xtype: 'textfield',
+ name: 'comment',
+ value: '',
+ fieldLabel: gettext('Comment')
+ }
+ ]
+ });
+
+ Ext.apply(me, {
+ subject: gettext('IP/CIDR'),
+ items: [ ipanel ]
+ });
+
+ me.callParent();
+
+ if (!me.create) {
+ me.load({
+ success: function(response, options) {
+ var values = response.result.data;
+ ipanel.setValues(values);
+ }
+ });
+ }
+ }
+});
+
+Ext.define('PVE.IPSetGrid', {
+ extend: 'Ext.grid.Panel',
+ alias: 'widget.pveIPSetGrid',
+
+ base_url: undefined,
+ list_refs_url: undefined,
+
+ addBtn: undefined,
+ removeBtn: undefined,
+ editBtn: undefined,
+
+ setBaseUrl: function(url) {
+ var me = this;
+
+ me.base_url = url;
+
+ if (url === undefined) {
+ me.addBtn.setDisabled(true);
+ me.store.removeAll();
+ } else {
+ me.addBtn.setDisabled(false);
+ me.store.setProxy({
+ type: 'pve',
+ url: '/api2/json' + url
+ });
+
+ me.store.load();
+ }
+ },
+
+ initComponent: function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ if (!me.list_refs_url) {
+ throw "no1 list_refs_url specified";
+ }
+
+ var store = new Ext.data.Store({
+ model: 'pve-ipset'
+ });
+
+ var reload = function() {
+ store.load();
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+ var win = Ext.create('PVE.IPSetCidrEdit', {
+ base_url: me.base_url,
+ cidr: rec.data.cidr
+ });
+ win.show();
+ win.on('destroy', reload);
+ };
+
+ me.editBtn = new PVE.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ me.addBtn = new PVE.button.Button({
+ text: gettext('Add'),
+ disabled: true,
+ handler: function() {
+ if (!me.base_url) {
+ return;
+ }
+ var win = Ext.create('PVE.IPSetCidrEdit', {
+ base_url: me.base_url,
+ list_refs_url: me.list_refs_url
+ });
+ win.show();
+ win.on('destroy', reload);
+ }
+ });
+
+ me.removeBtn = new PVE.button.Button({
+ text: gettext('Remove'),
+ selModel: sm,
+ disabled: true,
+ handler: function() {
+ var rec = sm.getSelection()[0];
+ if (!rec || !me.base_url) {
+ return;
+ }
+
+ PVE.Utils.API2Request({
+ url: me.base_url + '/' + rec.data.cidr,
+ method: 'DELETE',
+ waitMsgTarget: me,
+ failure: function(response, options) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ callback: reload
+ });
+ }
+ });
+
+ var render_errors = function(value, metaData, record) {
+ var errors = record.data.errors;
+ if (errors) {
+ var msg = errors.cidr || errors.nomatch;
+ if (msg) {
+ metaData.tdCls = 'x-form-invalid-field';
+ var html = '<p>' + Ext.htmlEncode(msg) + '</p>';
+ metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' +
+ html.replace(/\"/g,'"') + '"';
+ }
+ }
+ return value;
+ };
+
+ Ext.apply(me, {
+ tbar: [ '<b>IP/CIDR:</b>', me.addBtn, me.removeBtn, me.editBtn ],
+ store: store,
+ selModel: sm,
+ listeners: {
+ itemdblclick: run_editor
+ },
+ columns: [
+ {
+ xtype: 'rownumberer'
+ },
+ {
+ header: gettext('IP/CIDR'),
+ dataIndex: 'cidr',
+ width: 150,
+ renderer: function(value, metaData, record) {
+ value = render_errors(value, metaData, record);
+ if (record.data.nomatch) {
+ return '<b>! </b>' + value;
+ }
+ return value;
+ }
+ },
+ {
+ header: gettext('Comment'),
+ dataIndex: 'comment',
+ flex: 1,
+ renderer: function(value) {
+ return Ext.util.Format.htmlEncode(value);
+ }
+ }
+ ]
+ });
+
+ me.callParent();
+
+ if (me.base_url) {
+ me.setBaseUrl(me.base_url); // load
+ }
+ }
+}, function() {
+
+ Ext.define('pve-ipset', {
+ extend: 'Ext.data.Model',
+ fields: [ { name: 'nomatch', type: 'boolean' },
+ 'cidr', 'comment', 'errors' ],
+ idProperty: 'cidr'
+ });
+
+});
+
+Ext.define('PVE.IPSet', {
+ extend: 'Ext.panel.Panel',
+ alias: 'widget.pveIPSet',
+
+ title: 'IPSet',
+
+ list_refs_url: undefined,
+
+ initComponent: function() {
+ var me = this;
+
+ if (!me.list_refs_url) {
+ throw "no list_refs_url specified";
+ }
+
+ var ipset_panel = Ext.createWidget('pveIPSetGrid', {
+ region: 'center',
+ list_refs_url: me.list_refs_url,
+ flex: 0.5,
+ border: false
+ });
+
+ var ipset_list = Ext.createWidget('pveIPSetList', {
+ region: 'west',
+ ipset_panel: ipset_panel,
+ base_url: me.base_url,
+ flex: 0.5,
+ border: false,
+ split: true
+ });
+
+ Ext.apply(me, {
+ layout: 'border',
+ items: [ ipset_list, ipset_panel ],
+ listeners: {
+ show: function() {
+ ipset_list.fireEvent('show', ipset_list);
+ }
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.panel.InputPanel', {
+ extend: 'Ext.panel.Panel',
+ alias: ['widget.inputpanel'],
+
+ border: false,
+
+ // overwrite this to modify submit data
+ onGetValues: function(values) {
+ return values;
+ },
+
+ getValues: function(dirtyOnly) {
+ var me = this;
+
+ if (Ext.isFunction(me.onGetValues)) {
+ dirtyOnly = false;
+ }
+
+ var values = {};
+
+ Ext.Array.each(me.query('[isFormField]'), function(field) {
+ if (!dirtyOnly || field.isDirty()) {
+ PVE.Utils.assemble_field_data(values, field.getSubmitData());
+ }
+ });
+
+ return me.onGetValues(values);
+ },
+
+ setValues: function(values) {
+ var me = this;
+
+ var form = me.up('form');
+
+ Ext.iterate(values, function(fieldId, val) {
+ var field = me.query('[isFormField][name=' + fieldId + ']')[0];
+ if (field) {
+ field.setValue(val);
+ if (form.trackResetOnLoad) {
+ field.resetOriginalValue();
+ }
+ }
+ });
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ var items;
+
+ if (me.items) {
+ me.columns = 1;
+ items = [
+ {
+ columnWidth: 1,
+ layout: 'anchor',
+ items: me.items
+ }
+ ];
+ me.items = undefined;
+ } else if (me.column1) {
+ me.columns = 2;
+ items = [
+ {
+ columnWidth: 0.5,
+ padding: '0 10 0 0',
+ layout: 'anchor',
+ items: me.column1
+ },
+ {
+ columnWidth: 0.5,
+ padding: '0 0 0 10',
+ layout: 'anchor',
+ items: me.column2 || [] // allow empty column
+ }
+ ];
+ if (me.columnB) {
+ items.push({
+ columnWidth: 1,
+ padding: '10 0 0 0',
+ layout: 'anchor',
+ items: me.columnB
+ });
+ }
+ } else {
+ throw "unsupported config";
+ }
+
+ if (me.useFieldContainer) {
+ Ext.apply(me, {
+ layout: 'fit',
+ items: Ext.apply(me.useFieldContainer, {
+ layout: 'column',
+ defaultType: 'container',
+ items: items
+ })
+ });
+ } else {
+ Ext.apply(me, {
+ layout: 'column',
+ defaultType: 'container',
+ items: items
+ });
+ }
+
+ me.callParent();
+ }
+});
--- /dev/null
+/*
+ * Display log entries in a panel with scrollbar
+ * The log entries are automatically refreshed via a background task,
+ * with newest entries comming at the bottom
+ */
+Ext.define('PVE.panel.LogView', {
+ extend: 'Ext.panel.Panel',
+
+ alias: ['widget.pveLogView'],
+
+ pageSize: 500,
+
+ lineHeight: 16,
+
+ viewInfo: undefined,
+
+ scrollToEnd: true,
+
+ getMaxDown: function(scrollToEnd) {
+ var me = this;
+
+ var target = me.getTargetEl();
+ var dom = target.dom;
+ if (scrollToEnd) {
+ dom.scrollTop = dom.scrollHeight - dom.clientHeight;
+ }
+
+ var maxDown = dom.scrollHeight - dom.clientHeight -
+ dom.scrollTop;
+
+ return maxDown;
+ },
+
+ updateView: function(start, end, total, text) {
+ var me = this;
+ var el = me.dataCmp.el;
+
+ if (me.viewInfo && me.viewInfo.start === start &&
+ me.viewInfo.end === end && me.viewInfo.total === total &&
+ me.viewInfo.textLength === text.length) {
+ return; // same content
+ }
+
+ var maxDown = me.getMaxDown();
+ var scrollToEnd = (maxDown <= 0) && me.scrollToEnd;
+
+ el.setStyle('padding-top', start*me.lineHeight + 'px');
+ el.update(text);
+ me.dataCmp.setHeight(total*me.lineHeight);
+
+ if (scrollToEnd) {
+ me.getMaxDown(true);
+ }
+
+ me.viewInfo = {
+ start: start,
+ end: end,
+ total: total,
+ textLength: text.length
+ };
+ },
+
+ doAttemptLoad: function(start) {
+ var me = this;
+
+ PVE.Utils.API2Request({
+ url: me.url,
+ params: {
+ start: start,
+ limit: me.pageSize
+ },
+ method: 'GET',
+ success: function(response) {
+ PVE.Utils.setErrorMask(me, false);
+ var list = response.result.data;
+ var total = response.result.total;
+ var first = 0, last = 0;
+ var text = '';
+ Ext.Array.each(list, function(item) {
+ if (!first|| item.n < first) {
+ first = item.n;
+ }
+ if (!last || item.n > last) {
+ last = item.n;
+ }
+ text = text + Ext.htmlEncode(item.t) + "<br>";
+ });
+
+ if (first && last && total) {
+ me.updateView(first -1 , last -1, total, text);
+ } else {
+ me.updateView(0, 0, 0, '');
+ }
+ },
+ failure: function(response) {
+ var msg = response.htmlStatus;
+ PVE.Utils.setErrorMask(me, msg);
+ }
+ });
+ },
+
+ attemptLoad: function(start) {
+ var me = this;
+ if (!me.loadTask) {
+ me.loadTask = Ext.create('Ext.util.DelayedTask', me.doAttemptLoad, me, []);
+ }
+ me.loadTask.delay(200, me.doAttemptLoad, me, [start]);
+ },
+
+ requestUpdate: function(top, force) {
+ var me = this;
+
+ if (top === undefined) {
+ var target = me.getTargetEl();
+ top = target.dom.scrollTop;
+ }
+
+ var viewStart = parseInt((top / me.lineHeight) - 1, 10);
+ if (viewStart < 0) {
+ viewStart = 0;
+ }
+ var viewEnd = parseInt(((top + me.getHeight())/ me.lineHeight) + 1, 10);
+ var info = me.viewInfo;
+
+ if (info && !force) {
+ if (viewStart >= info.start && viewEnd <= info.end) {
+ return;
+ }
+ }
+
+ var line = parseInt((top / me.lineHeight) - (me.pageSize / 2) + 10, 10);
+ if (line < 0) {
+ line = 0;
+ }
+
+ me.attemptLoad(line);
+ },
+
+ afterRender: function() {
+ var me = this;
+
+ me.callParent(arguments);
+
+ Ext.Function.defer(function() {
+ var target = me.getTargetEl();
+ target.on('scroll', function(e) {
+ me.requestUpdate();
+ });
+ me.requestUpdate(0);
+ }, 20);
+ },
+
+ initComponent : function() {
+ /*jslint confusion: true */
+
+ var me = this;
+
+ if (!me.url) {
+ throw "no url specified";
+ }
+
+ me.dataCmp = Ext.create('Ext.Component', {
+ style: 'font:normal 11px tahoma, arial, verdana, sans-serif;' +
+ 'line-height: ' + me.lineHeight.toString() + 'px; white-space: pre;'
+ });
+
+ me.task = Ext.TaskManager.start({
+ run: function() {
+ if (!me.isVisible() || !me.scrollToEnd || !me.viewInfo) {
+ return;
+ }
+
+ var maxDown = me.getMaxDown();
+ if (maxDown > 0) {
+ return;
+ }
+
+ me.requestUpdate(undefined, true);
+ },
+ interval: 1000
+ });
+
+ Ext.apply(me, {
+ autoScroll: true,
+ layout: 'auto',
+ items: me.dataCmp,
+ bodyStyle: 'padding: 5px;',
+ listeners: {
+ show: function() {
+ var target = me.getTargetEl();
+ if (target && target.dom) {
+ target.dom.scrollTop = me.savedScrollTop;
+ }
+ },
+ beforehide: function() {
+ // Hack: chrome reset scrollTop to 0, so we save/restore
+ var target = me.getTargetEl();
+ if (target && target.dom) {
+ me.savedScrollTop = target.dom.scrollTop;
+ }
+ },
+ destroy: function() {
+ Ext.TaskManager.stop(me.task);
+ }
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.panel.NotesView', {
+ extend: 'Ext.panel.Panel',
+
+ load: function() {
+ var me = this;
+
+ PVE.Utils.API2Request({
+ url: me.url,
+ waitMsgTarget: me,
+ failure: function(response, opts) {
+ me.update(gettext('Error') + " " + response.htmlStatus);
+ },
+ success: function(response, opts) {
+ var data = response.result.data.description || '';
+ me.update(Ext.htmlEncode(data));
+ }
+ });
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ var nodename = me.pveSelNode.data.node;
+ if (!nodename) {
+ throw "no node name specified";
+ }
+
+ var vmid = me.pveSelNode.data.vmid;
+ if (!vmid) {
+ throw "no VM ID specified";
+ }
+
+ var vmtype = me.pveSelNode.data.type;
+ var url;
+
+ if (vmtype === 'qemu') {
+ me.url = '/api2/extjs/nodes/' + nodename + '/qemu/' + vmid + '/config';
+ } else if (vmtype === 'lxc') {
+ me.url = '/api2/extjs/nodes/' + nodename + '/lxc/' + vmid + '/config';
+ } else {
+ throw "unknown vm type '" + vmtype + "'";
+ }
+
+ Ext.apply(me, {
+ title: gettext("Notes"),
+ style: 'padding-left:10px',
+ bodyStyle: 'white-space:pre',
+ bodyPadding: 10,
+ autoScroll: true,
+ listeners: {
+ render: function(c) {
+ c.el.on('dblclick', function() {
+ var win = Ext.create('PVE.window.NotesEdit', {
+ pveSelNode: me.pveSelNode,
+ url: me.url
+ });
+ win.show();
+ win.on('destroy', me.load, me);
+ });
+ }
+ }
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.panel.RRDView', {
+ extend: 'Ext.panel.Panel',
+ alias: 'widget.pveRRDView',
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.datasource) {
+ throw "no datasource specified";
+ }
+
+ if (!me.rrdurl) {
+ throw "no rrdurl specified";
+ }
+
+ var stateid = 'pveRRDTypeSelection';
+ var sp = Ext.state.Manager.getProvider();
+ var stateinit = sp.get(stateid);
+
+ if (stateinit) {
+ if(stateinit.timeframe !== me.timeframe || stateinit.cf !== me.rrdcffn){
+ me.timeframe = stateinit.timeframe;
+ me.rrdcffn = stateinit.cf;
+ }
+ }
+
+ if (!me.timeframe) {
+ if(stateinit && stateinit.timeframe){
+ me.timeframe = stateinit.timeframe;
+ }else{
+ me.timeframe = 'hour';
+ }
+ }
+
+ if (!me.rrdcffn) {
+ if(stateinit && stateinit.rrdcffn){
+ me.rrdcffn = stateinit.cf;
+ }else{
+ me.rrdcffn = 'AVERAGE';
+ }
+ }
+
+
+ var datasource = me.datasource;
+
+ // fixme: dcindex??
+ var dcindex = 0;
+ var create_url = function() {
+ var url = me.rrdurl + "?ds=" + datasource +
+ "&timeframe=" + me.timeframe + "&cf=" + me.rrdcffn +
+ "&_dc=" + dcindex.toString();
+ dcindex++;
+ return url;
+ };
+
+
+ Ext.apply(me, {
+ layout: 'fit',
+ html: {
+ tag: 'img',
+ width: 800,
+ height: 200,
+ src: create_url()
+ },
+ applyState : function(state) {
+ if (state && state.id) {
+ if(state.timeframe !== me.timeframe || state.cf !== me.rrdcffn){
+ me.timeframe = state.timeframe;
+ me.rrdcffn = state.cf;
+ me.reload_task.delay(10);
+ }
+ }
+ }
+ });
+
+ me.callParent();
+
+ me.reload_task = new Ext.util.DelayedTask(function() {
+ if (me.rendered) {
+ try {
+ var html = {
+ tag: 'img',
+ width: 800,
+ height: 200,
+ src: create_url()
+ };
+ me.update(html);
+ } catch (e) {
+ // fixme:
+ console.log(e);
+ }
+ me.reload_task.delay(30000);
+ } else {
+ me.reload_task.delay(1000);
+ }
+ });
+
+ me.reload_task.delay(30000);
+
+ me.on('destroy', function() {
+ me.reload_task.cancel();
+ });
+
+ var state_change_fn = function(prov, key, value) {
+ if (key == stateid) {
+ me.applyState(value);
+ }
+ };
+
+ me.mon(sp, 'statechange', state_change_fn);
+ }
+});
--- /dev/null
+/*
+ * This class describes the bottom panel
+ */
+Ext.define('PVE.panel.StatusPanel', {
+ extend: 'Ext.tab.Panel',
+ alias: 'widget.pveStatusPanel',
+
+
+ //title: "Logs",
+ //tabPosition: 'bottom',
+
+ initComponent: function() {
+ var me = this;
+
+ var stateid = 'ltab';
+ var sp = Ext.state.Manager.getProvider();
+
+ var state = sp.get(stateid);
+ if (state && state.value) {
+ me.activeTab = state.value;
+ }
+
+ Ext.apply(me, {
+ listeners: {
+ tabchange: function() {
+ var atab = me.getActiveTab().itemId;
+ var state = { value: atab };
+ sp.set(stateid, state);
+ }
+ },
+ items: [
+ {
+ itemId: 'tasks',
+ title: gettext('Tasks'),
+ xtype: 'pveClusterTasks'
+ },
+ {
+ itemId: 'clog',
+ title: gettext('Cluster log'),
+ xtype: 'pveClusterLog'
+ }
+ ]
+ });
+
+ me.callParent();
+
+ me.items.get(0).fireEvent('show', me.items.get(0));
+
+ var statechange = function(sp, key, state) {
+ if (key === stateid) {
+ var atab = me.getActiveTab().itemId;
+ var ntab = state.value;
+ if (state && ntab && (atab != ntab)) {
+ me.setActiveTab(ntab);
+ }
+ }
+ };
+
+ sp.on('statechange', statechange);
+ me.on('destroy', function() {
+ sp.un('statechange', statechange);
+ });
+
+ }
+});
--- /dev/null
+Ext.define('PVE.panel.SubConfig', {
+ extend: 'Ext.tab.Panel',
+ alias: ['widget.pvePanelSubConfig'],
+
+ configPrefix: undefined,
+
+ getHState: function(itemId) {
+ /*jslint confusion: true */
+ var me = this;
+
+ if (!itemId) {
+ itemId = me.getActiveTab().itemId;
+ }
+
+ var first = me.items.get(0);
+ var ntab;
+
+ // Note: '' is alias for first tab.
+ if (itemId === first.itemId) {
+ ntab = me.configPrefix;
+ } else {
+ ntab = me.configPrefix + '-' + itemId;
+ }
+
+ return { value: ntab };
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ if (!me.phstateid) {
+ throw "no parent history state specified";
+ }
+
+ var sp = Ext.state.Manager.getProvider();
+ var state = sp.get(me.phstateid);
+
+ var hsregex = /^([^\-\s]+)-(\S+)?$/;
+
+ if (state && state.value) {
+ var res = hsregex.exec(state.value);
+ if (res && res[1] && res[2] && res[1] === me.configPrefix) {
+ me.activeTab = res[2];
+ }
+ }
+
+ Ext.apply(me, {
+ plain: true,
+ tabPosition: 'bottom',
+ listeners: {
+ afterrender: function(tp) {
+ var first = tp.items.get(0);
+ if (first) {
+ first.fireEvent('show', first);
+ }
+ },
+ tabchange: function(tp, newcard, oldcard) {
+ var state = me.getHState(newcard.itemId);
+ sp.set(me.phstateid, state);
+ }
+ }
+ });
+
+ me.callParent();
+
+ var statechange = function(sp, key, state) {
+ if ((key === me.phstateid) && state) {
+ var first = me.items.get(0);
+ var atab = me.getActiveTab().itemId;
+ var res = hsregex.exec(state.value);
+ var ntab = (res && res[1]) ? res[1] : first.itemId;
+ if (ntab && (atab != ntab)) {
+ me.setActiveTab(ntab);
+ }
+ }
+ };
+
+ me.mon(sp, 'statechange', statechange);
+ }
+});
--- /dev/null
+Ext.define('PVE.pool.Config', {
+ extend: 'PVE.panel.Config',
+ alias: 'widget.pvePoolConfig',
+
+ initComponent: function() {
+ var me = this;
+
+ var pool = me.pveSelNode.data.pool;
+ if (!pool) {
+ throw "no pool specified";
+ }
+
+ Ext.apply(me, {
+ title: Ext.String.format(gettext("Resource Pool") + ': ' + pool),
+ hstateid: 'pooltab',
+ items: [
+ {
+ title: gettext('Summary'),
+ xtype: 'pvePoolSummary',
+ itemId: 'summary'
+ },
+ {
+ title: gettext('Members'),
+ xtype: 'pvePoolMembers',
+ pool: pool,
+ itemId: 'members'
+ },
+ {
+ xtype: 'pveACLView',
+ title: gettext('Permissions'),
+ itemId: 'permissions',
+ path: '/pool/' + pool
+ }
+ ]
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.pool.StatusView', {
+ extend: 'PVE.grid.ObjectGrid',
+ alias: ['widget.pvePoolStatusView'],
+
+ initComponent : function() {
+ var me = this;
+
+ var pool = me.pveSelNode.data.pool;
+ if (!pool) {
+ throw "no pool specified";
+ }
+
+ var rows = {
+ comment: {
+ header: gettext('Comment'),
+ required: true
+ }
+ };
+
+ Ext.applyIf(me, {
+ title: gettext('Status'),
+ url: "/api2/json/pools/" + pool,
+ cwidth1: 150,
+ interval: 30000,
+ //height: 195,
+ rows: rows
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.pool.Summary', {
+ extend: 'Ext.panel.Panel',
+ alias: 'widget.pvePoolSummary',
+
+ initComponent: function() {
+ var me = this;
+
+ var pool = me.pveSelNode.data.pool;
+ if (!pool) {
+ throw "no pool specified";
+ }
+
+ var statusview = Ext.create('PVE.pool.StatusView', {
+ pveSelNode: me.pveSelNode,
+ style: 'padding-top:0px'
+ });
+
+ var rstore = statusview.rstore;
+
+ Ext.apply(me, {
+ autoScroll: true,
+ bodyStyle: 'padding:10px',
+ defaults: {
+ style: 'padding-top:10px',
+ width: 800
+ },
+ items: [ statusview ]
+ });
+
+ me.on('show', rstore.startUpdate);
+ me.on('hide', rstore.stopUpdate);
+ me.on('destroy', rstore.stopUpdate);
+
+ me.callParent();
+ }
+});
--- /dev/null
+/*
+ * Left Treepanel, containing all the ressources we manage in this datacenter: server nodes, server storages, VMs and Containers
+ */
+Ext.define('PVE.tree.ResourceTree', {
+ extend: 'Ext.tree.TreePanel',
+ alias: ['widget.pveResourceTree'],
+
+ statics: {
+ typeDefaults: {
+ node: {
+ iconCls: 'x-tree-node-server',
+ text: gettext('Node list')
+ },
+ pool: {
+ iconCls: 'x-tree-node-pool',
+ text: gettext('Resource Pool')
+ },
+ storage: {
+ iconCls: 'x-tree-node-harddisk',
+ text: gettext('Storage list')
+ },
+ qemu: {
+ iconCls: 'x-tree-node-computer',
+ text: gettext('Virtual Machine')
+ },
+ lxc: {
+ iconCls: 'x-tree-node-lxc',
+ text: gettext('LXC Container')
+ }
+ }
+ },
+
+ // private
+ nodeSortFn: function(node1, node2) {
+ var n1 = node1.data;
+ var n2 = node2.data;
+
+ if ((n1.groupbyid && n2.groupbyid) ||
+ !(n1.groupbyid || n2.groupbyid)) {
+
+ var tcmp;
+
+ var v1 = n1.type;
+ var v2 = n2.type;
+
+ if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) {
+ return tcmp;
+ }
+
+ // numeric compare for VM IDs
+ // sort templates after regular VMs
+ if (v1 === 'qemu' || v1 === 'lxc') {
+ if (n1.template && !n2.template) {
+ return 1;
+ } else if (n2.template && !n1.template) {
+ return -1;
+ }
+ v1 = n1.vmid;
+ v2 = n2.vmid;
+ if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) {
+ return tcmp;
+ }
+ }
+
+ return n1.text > n2.text ? 1 : (n1.text < n2.text ? -1 : 0);
+ } else if (n1.groupbyid) {
+ return -1;
+ } else if (n2.groupbyid) {
+ return 1;
+ }
+ },
+
+ // private: fast binary search
+ findInsertIndex: function(node, child, start, end) {
+ var me = this;
+
+ var diff = end - start;
+
+ var mid = start + (diff>>1);
+
+ if (diff <= 0) {
+ return start;
+ }
+
+ var res = me.nodeSortFn(child, node.childNodes[mid]);
+ if (res <= 0) {
+ return me.findInsertIndex(node, child, start, mid);
+ } else {
+ return me.findInsertIndex(node, child, mid + 1, end);
+ }
+ },
+
+ setIconCls: function(info) {
+ var me = this;
+
+ var defaults = PVE.tree.ResourceTree.typeDefaults[info.type];
+ if (defaults && defaults.iconCls) {
+ var running = info.running ? '-running' : '';
+ var template = info.template ? '-template' : '';
+ info.iconCls = defaults.iconCls + running + template;
+ }
+ },
+
+ // private
+ addChildSorted: function(node, info) {
+ var me = this;
+
+ me.setIconCls(info);
+
+ var defaults;
+ if (info.groupbyid) {
+ info.text = info.groupbyid;
+ if (info.type === 'type') {
+ defaults = PVE.tree.ResourceTree.typeDefaults[info.groupbyid];
+ if (defaults && defaults.text) {
+ info.text = defaults.text;
+ }
+ }
+ }
+ var child = Ext.create('PVETree', info);
+
+ var cs = node.childNodes;
+ var pos;
+ if (cs) {
+ pos = cs[me.findInsertIndex(node, child, 0, cs.length)];
+ }
+
+ node.insertBefore(child, pos);
+
+ return child;
+ },
+
+ // private
+ groupChild: function(node, info, groups, level) {
+ var me = this;
+
+ var groupby = groups[level];
+ var v = info[groupby];
+
+ if (v) {
+ var group = node.findChild('groupbyid', v);
+ if (!group) {
+ var groupinfo;
+ if (info.type === groupby) {
+ groupinfo = info;
+ } else {
+ groupinfo = {
+ type: groupby,
+ id : groupby + "/" + v
+ };
+ if (groupby !== 'type') {
+ groupinfo[groupby] = v;
+ }
+ }
+ groupinfo.leaf = false;
+ groupinfo.groupbyid = v;
+ group = me.addChildSorted(node, groupinfo);
+ // fixme: remove when EXTJS has fixed those bugs?!
+ group.expand(); group.collapse();
+ }
+ if (info.type === groupby) {
+ return group;
+ }
+ if (group) {
+ return me.groupChild(group, info, groups, level + 1);
+ }
+ }
+
+ return me.addChildSorted(node, info);
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ var rstore = PVE.data.ResourceStore;
+ var sp = Ext.state.Manager.getProvider();
+
+ if (!me.viewFilter) {
+ me.viewFilter = {};
+ }
+
+ var pdata = {
+ dataIndex: {},
+ updateCount: 0
+ };
+
+ var store = Ext.create('Ext.data.TreeStore', {
+ model: 'PVETree',
+ root: {
+ expanded: true,
+ id: 'root',
+ text: gettext('Datacenter')
+ }
+ });
+
+ var stateid = 'rid';
+
+ var updateTree = function() {
+ var tmp;
+
+ // fixme: suspend events ?
+
+ var rootnode = me.store.getRootNode();
+
+ // remember selected node (and all parents)
+ var sm = me.getSelectionModel();
+
+ var lastsel = sm.getSelection()[0];
+ var parents = [];
+ var p = lastsel;
+ while (p && !!(p = p.parentNode)) {
+ parents.push(p);
+ }
+
+ var index = pdata.dataIndex;
+
+ var groups = me.viewFilter.groups || [];
+ var filterfn = me.viewFilter.filterfn;
+
+ // remove vanished or changed items
+ var key;
+ for (key in index) {
+ if (index.hasOwnProperty(key)) {
+ var olditem = index[key];
+
+ // getById() use find(), which is slow (ExtJS4 DP5)
+ //var item = rstore.getById(olditem.data.id);
+ var item = rstore.data.get(olditem.data.id);
+
+ var changed = false;
+ if (item) {
+ // test if any grouping attributes changed
+ var i, len;
+ for (i = 0, len = groups.length; i < len; i++) {
+ var attr = groups[i];
+ if (item.data[attr] != olditem.data[attr]) {
+ //console.log("changed " + attr);
+ changed = true;
+ break;
+ }
+ }
+ if ((item.data.text !== olditem.data.text) ||
+ (item.data.node !== olditem.data.node) ||
+ (item.data.running !== olditem.data.running) ||
+ (item.data.template !== olditem.data.template)) {
+ //console.log("changed node/text/running " + olditem.data.id);
+ changed = true;
+ }
+
+ // fixme: also test filterfn()?
+ }
+
+ if (!item || changed) {
+ //console.log("REM UID: " + key + " ITEM " + olditem.data.id);
+ if (olditem.isLeaf()) {
+ delete index[key];
+ var parentNode = olditem.parentNode;
+ parentNode.removeChild(olditem, true);
+ } else {
+ if (item && changed) {
+ olditem.beginEdit();
+ //console.log("REM UPDATE UID: " + key + " ITEM " + item.data.running);
+ var info = olditem.data;
+ Ext.apply(info, item.data);
+ me.setIconCls(info);
+ olditem.commit();
+ }
+ }
+ }
+ }
+ }
+
+ // add new items
+ rstore.each(function(item) {
+ var olditem = index[item.data.id];
+ if (olditem) {
+ return;
+ }
+
+ if (filterfn && !filterfn(item)) {
+ return;
+ }
+
+ //console.log("ADD UID: " + item.data.id);
+
+ var info = Ext.apply({ leaf: true }, item.data);
+
+ var child = me.groupChild(rootnode, info, groups, 0);
+ if (child) {
+ index[item.data.id] = child;
+ }
+ });
+
+ // select parent node is selection vanished
+ if (lastsel && !rootnode.findChild('id', lastsel.data.id, true)) {
+ lastsel = rootnode;
+ while (!!(p = parents.shift())) {
+ if (!!(tmp = rootnode.findChild('id', p.data.id, true))) {
+ lastsel = tmp;
+ break;
+ }
+ }
+ me.selectById(lastsel.data.id);
+ }
+
+ if (!pdata.updateCount) {
+ rootnode.collapse();
+ rootnode.expand();
+ me.applyState(sp.get(stateid));
+ }
+
+ pdata.updateCount++;
+ };
+
+ var statechange = function(sp, key, value) {
+ if (key === stateid) {
+ me.applyState(value);
+ }
+ };
+
+ sp.on('statechange', statechange);
+
+ Ext.apply(me, {
+ store: store,
+ viewConfig: {
+ // note: animate cause problems with applyState
+ animate: false
+ },
+ //useArrows: true,
+ //rootVisible: false,
+ //title: 'Resource Tree',
+ listeners: {
+ itemcontextmenu: function(v, record, item, index, event) {
+ event.stopEvent();
+ //v.select(record);
+ var menu;
+
+ if (record.data.type === 'qemu' && !record.data.template) {
+ menu = Ext.create('PVE.qemu.CmdMenu', {
+ pveSelNode: record
+ });
+ } else if (record.data.type === 'qemu' && record.data.template) {
+ menu = Ext.create('PVE.qemu.TemplateMenu', {
+ pveSelNode: record
+ });
+ } else if (record.data.type === 'lxc') {
+ menu = Ext.create('PVE.lxc.CmdMenu', {
+ pveSelNode: record
+ });
+ } else {
+ return;
+ }
+
+ menu.showAt(event.getXY());
+ },
+ destroy: function() {
+ rstore.un("load", updateTree);
+ }
+ },
+ setViewFilter: function(view) {
+ me.viewFilter = view;
+ me.clearTree();
+ updateTree();
+ },
+ clearTree: function() {
+ pdata.updateCount = 0;
+ var rootnode = me.store.getRootNode();
+ rootnode.collapse();
+ rootnode.removeAll(true);
+ pdata.dataIndex = {};
+ me.getSelectionModel().deselectAll();
+ },
+ selectExpand: function(node) {
+ var sm = me.getSelectionModel();
+ if (!sm.isSelected(node)) {
+ sm.select(node);
+ var cn = node;
+ while (!!(cn = cn.parentNode)) {
+ if (!cn.isExpanded()) {
+ cn.expand();
+ }
+ }
+ }
+ },
+ selectById: function(nodeid) {
+ var rootnode = me.store.getRootNode();
+ var sm = me.getSelectionModel();
+ var node;
+ if (nodeid === 'root') {
+ node = rootnode;
+ } else {
+ node = rootnode.findChild('id', nodeid, true);
+ }
+ if (node) {
+ me.selectExpand(node);
+ }
+ },
+ checkVmMigration: function(record) {
+ if (!(record.data.type === 'qemu' || record.data.type === 'lxc')) {
+ throw "not a vm type";
+ }
+
+ var rootnode = me.store.getRootNode();
+ var node = rootnode.findChild('id', record.data.id, true);
+
+ if (node && node.data.type === record.data.type &&
+ node.data.node !== record.data.node) {
+ // defer select (else we get strange errors)
+ Ext.defer(function() { me.selectExpand(node); }, 100, me);
+ }
+ },
+ applyState : function(state) {
+ var sm = me.getSelectionModel();
+ if (state && state.value) {
+ me.selectById(state.value);
+ } else {
+ sm.deselectAll();
+ }
+ }
+ });
+
+ me.callParent();
+
+ var sm = me.getSelectionModel();
+ sm.on('select', function(sm, n) {
+ sp.set(stateid, { value: n.data.id});
+ });
+
+ rstore.on("load", updateTree);
+ rstore.startUpdate();
+ //rstore.stopUpdate();
+ }
+
+});
--- /dev/null
+Ext.define('PVE.window.Backup', {
+ extend: 'Ext.window.Window',
+
+ resizable: false,
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ if (!me.vmid) {
+ throw "no VM ID specified";
+ }
+
+ if (!me.vmtype) {
+ throw "no VM type specified";
+ }
+
+ var storagesel = Ext.create('PVE.form.StorageSelector', {
+ nodename: me.nodename,
+ name: 'storage',
+ value: me.storage,
+ fieldLabel: gettext('Storage'),
+ storageContent: 'backup',
+ allowBlank: false
+ });
+
+ me.formPanel = Ext.create('Ext.form.Panel', {
+ bodyPadding: 10,
+ border: false,
+ fieldDefaults: {
+ labelWidth: 100,
+ anchor: '100%'
+ },
+ items: [
+ storagesel,
+ {
+ xtype: 'pveBackupModeSelector',
+ fieldLabel: gettext('Mode'),
+ value: 'snapshot',
+ name: 'mode'
+ },
+ {
+ xtype: 'pveCompressionSelector',
+ name: 'compress',
+ value: 'lzo',
+ fieldLabel: gettext('Compression')
+ }
+ ]
+ });
+
+ var form = me.formPanel.getForm();
+
+ var submitBtn = Ext.create('Ext.Button', {
+ text: gettext('Backup'),
+ handler: function(){
+ var storage = storagesel.getValue();
+ var values = form.getValues();
+ var params = {
+ storage: storage,
+ vmid: me.vmid,
+ mode: values.mode,
+ remove: 0
+ };
+ if (values.compress) {
+ params.compress = values.compress;
+ }
+
+ PVE.Utils.API2Request({
+ url: '/nodes/' + me.nodename + '/vzdump',
+ params: params,
+ method: 'POST',
+ failure: function (response, opts) {
+ Ext.Msg.alert('Error',response.htmlStatus);
+ },
+ success: function(response, options) {
+ var upid = response.result.data;
+
+ var win = Ext.create('PVE.window.TaskViewer', {
+ upid: upid
+ });
+ win.show();
+ me.close();
+ }
+ });
+ }
+ });
+
+ var title = gettext('Backup') + " " +
+ ((me.vmtype === 'openvz') ? "CT" : "VM") +
+ " " + me.vmid;
+
+ Ext.apply(me, {
+ title: title,
+ width: 350,
+ modal: true,
+ layout: 'auto',
+ border: false,
+ items: [ me.formPanel ],
+ buttons: [ submitBtn ]
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+// fixme: how can we avoid those lint errors?
+/*jslint confusion: true */
+Ext.define('PVE.window.Edit', {
+ extend: 'Ext.window.Window',
+ alias: 'widget.pveWindowEdit',
+
+ resizable: false,
+
+ // use this tio atimatically generate a title like
+ // Create: <subject>
+ subject: undefined,
+
+ // set create to true if you want a Create button (instead
+ // OK and RESET)
+ create: false,
+
+ // set to true if you want an Add button (instead of Create)
+ isAdd: false,
+
+ // set to true if you want an Remove button (instead of Create)
+ isRemove: false,
+
+ backgroundDelay: 0,
+
+ showProgress: false,
+
+ isValid: function() {
+ var me = this;
+
+ var form = me.formPanel.getForm();
+ return form.isValid();
+ },
+
+ getValues: function(dirtyOnly) {
+ var me = this;
+
+ var values = {};
+
+ var form = me.formPanel.getForm();
+
+ form.getFields().each(function(field) {
+ if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) {
+ PVE.Utils.assemble_field_data(values, field.getSubmitData());
+ }
+ });
+
+ Ext.Array.each(me.query('inputpanel'), function(panel) {
+ PVE.Utils.assemble_field_data(values, panel.getValues(dirtyOnly));
+ });
+
+ return values;
+ },
+
+ setValues: function(values) {
+ var me = this;
+
+ var form = me.formPanel.getForm();
+
+ Ext.iterate(values, function(fieldId, val) {
+ var field = form.findField(fieldId);
+ if (field && !field.up('inputpanel')) {
+ field.setValue(val);
+ if (form.trackResetOnLoad) {
+ field.resetOriginalValue();
+ }
+ }
+ });
+
+ Ext.Array.each(me.query('inputpanel'), function(panel) {
+ panel.setValues(values);
+ });
+ },
+
+ submit: function() {
+ var me = this;
+
+ var form = me.formPanel.getForm();
+
+ var values = me.getValues();
+ Ext.Object.each(values, function(name, val) {
+ if (values.hasOwnProperty(name)) {
+ if (Ext.isArray(val) && !val.length) {
+ values[name] = '';
+ }
+ }
+ });
+
+ if (me.digest) {
+ values.digest = me.digest;
+ }
+
+ if (me.backgroundDelay) {
+ values.background_delay = me.backgroundDelay;
+ }
+
+ var url = me.url;
+ if (me.method === 'DELETE') {
+ url = url + "?" + Ext.Object.toQueryString(values);
+ values = undefined;
+ }
+
+ PVE.Utils.API2Request({
+ url: url,
+ waitMsgTarget: me,
+ method: me.method || (me.backgroundDelay ? 'POST' : 'PUT'),
+ params: values,
+ failure: function(response, options) {
+ if (response.result && response.result.errors) {
+ form.markInvalid(response.result.errors);
+ }
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ success: function(response, options) {
+ me.close();
+ if ((me.backgroundDelay || me.showProgress) &&
+ response.result.data) {
+ var upid = response.result.data;
+ var win = Ext.create('PVE.window.TaskProgress', {
+ upid: upid
+ });
+ win.show();
+ }
+ }
+ });
+ },
+
+ load: function(options) {
+ var me = this;
+
+ var form = me.formPanel.getForm();
+
+ options = options || {};
+
+ var newopts = Ext.apply({
+ waitMsgTarget: me
+ }, options);
+
+ var createWrapper = function(successFn) {
+ Ext.apply(newopts, {
+ url: me.url,
+ method: 'GET',
+ success: function(response, opts) {
+ form.clearInvalid();
+ me.digest = response.result.data.digest;
+ if (successFn) {
+ successFn(response, opts);
+ } else {
+ me.setValues(response.result.data);
+ }
+ // hack: fix ExtJS bug
+ Ext.Array.each(me.query('radiofield'), function(f) {
+ f.resetOriginalValue();
+ });
+ },
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus, function() {
+ me.close();
+ });
+ }
+ });
+ };
+
+ createWrapper(options.success);
+
+ PVE.Utils.API2Request(newopts);
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.url) {
+ throw "no url specified";
+ }
+
+ var items = Ext.isArray(me.items) ? me.items : [ me.items ];
+
+ me.items = undefined;
+
+ me.formPanel = Ext.create('Ext.form.Panel', {
+ url: me.url,
+ method: me.method || 'PUT',
+ trackResetOnLoad: true,
+ bodyPadding: 10,
+ border: false,
+ defaults: {
+ border: false
+ },
+ fieldDefaults: Ext.apply({}, me.fieldDefaults, {
+ labelWidth: 100,
+ anchor: '100%'
+ }),
+ items: items
+ });
+
+ var form = me.formPanel.getForm();
+
+ var submitBtn = Ext.create('Ext.Button', {
+ text: me.create ? (me.isAdd ? gettext('Add') : ( me.isRemove ? gettext('Remove') : gettext('Create'))) : gettext('OK'),
+ disabled: !me.create,
+ handler: function() {
+ me.submit();
+ }
+ });
+
+ var resetBtn = Ext.create('Ext.Button', {
+ text: 'Reset',
+ disabled: true,
+ handler: function(){
+ form.reset();
+ }
+ });
+
+ var set_button_status = function() {
+ var valid = form.isValid();
+ var dirty = form.isDirty();
+ submitBtn.setDisabled(!valid || !(dirty || me.create));
+ resetBtn.setDisabled(!dirty);
+ };
+
+ form.on('dirtychange', set_button_status);
+ form.on('validitychange', set_button_status);
+
+ var colwidth = 300;
+ if (me.fieldDefaults && me.fieldDefaults.labelWidth) {
+ colwidth += me.fieldDefaults.labelWidth - 100;
+ }
+
+
+ var twoColumn = items[0].column1 || items[0].column2;
+
+ if (me.subject && !me.title) {
+ me.title = PVE.Utils.dialog_title(me.subject, me.create, me.isAdd);
+ }
+
+ if (me.create) {
+ me.buttons = [ submitBtn ] ;
+ } else {
+ me.buttons = [ submitBtn, resetBtn ];
+ }
+
+ Ext.applyIf(me, {
+ modal: true,
+ width: twoColumn ? colwidth*2 : colwidth,
+ border: false,
+ items: [ me.formPanel ],
+ });
+
+ me.callParent();
+
+ // always mark invalid fields
+ me.on('afterlayout', function() {
+ me.isValid();
+ });
+ }
+});
--- /dev/null
+Ext.define('PVE.window.LoginWindow', {
+ extend: 'Ext.window.Window',
+
+ // private
+ onLogon: function() {
+ var me = this;
+
+ var form = me.getComponent(0).getForm();
+
+ if(form.isValid()){
+ me.el.mask(gettext('Please wait...'), 'x-mask-loading');
+
+ form.submit({
+ failure: function(f, resp){
+ me.el.unmask();
+ Ext.MessageBox.alert(gettext('Error'),
+ gettext("Login failed. Please try again"),
+ function() {
+ var uf = form.findField('username');
+ uf.focus(true, true);
+ });
+ },
+ success: function(f, resp){
+ me.el.unmask();
+
+ var handler = me.handler || Ext.emptyFn;
+ handler.call(me, resp.result.data);
+ me.close();
+ }
+ });
+ }
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ var otp_field = Ext.createWidget('textfield', {
+ fieldLabel: gettext('OTP'),
+ name: 'otp',
+ allowBlank: false,
+ hidden: true
+ });
+
+ Ext.apply(me, {
+ width: 400,
+ modal: true,
+ border: false,
+ draggable: true,
+ closable: false,
+ resizable: false,
+ layout: 'auto',
+ title: gettext('Proxmox VE Login'),
+
+ items: [{
+ xtype: 'form',
+ frame: true,
+ url: '/api2/extjs/access/ticket',
+
+ fieldDefaults: {
+ labelAlign: 'right'
+ },
+
+ defaults: {
+ anchor: '-5',
+ allowBlank: false
+ },
+
+ items: [
+ {
+ xtype: 'textfield',
+ fieldLabel: gettext('User name'),
+ name: 'username',
+ blankText: gettext("Enter your user name"),
+ listeners: {
+ afterrender: function(f) {
+ // Note: only works if we pass delay 1000
+ f.focus(true, 1000);
+ },
+ specialkey: function(f, e) {
+ if (e.getKey() === e.ENTER) {
+ var pf = me.query('textfield[name="password"]')[0];
+ if (pf.getValue()) {
+ me.onLogon();
+ } else {
+ pf.focus(false);
+ }
+ }
+ }
+ }
+ },
+ {
+ xtype: 'textfield',
+ inputType: 'password',
+ fieldLabel: gettext('Password'),
+ name: 'password',
+ blankText: gettext("Enter your password"),
+ listeners: {
+ specialkey: function(field, e) {
+ if (e.getKey() === e.ENTER) {
+ me.onLogon();
+ }
+ }
+ }
+ },
+ otp_field,
+ {
+ xtype: 'pveRealmComboBox',
+ name: 'realm',
+ listeners: {
+ change: function(f, value) {
+ if (f.needOTP(value)) {
+ otp_field.setVisible(true);
+ otp_field.setDisabled(false);
+ } else {
+ otp_field.setVisible(false);
+ otp_field.setDisabled(true);
+ }
+ }
+ }
+ },
+ {
+ xtype: 'pveLanguageSelector',
+ fieldLabel: gettext('Language'),
+ value: Ext.util.Cookies.get('PVELangCookie') || 'en',
+ name: 'lang',
+ submitValue: false,
+ listeners: {
+ change: function(t, value) {
+ var dt = Ext.Date.add(new Date(), Ext.Date.YEAR, 10);
+ Ext.util.Cookies.set('PVELangCookie', value, dt);
+ me.el.mask(gettext('Please wait...'), 'x-mask-loading');
+ window.location.reload();
+ }
+ }
+ }
+ ],
+ buttons: [
+ {
+ text: gettext('Login'),
+ handler: function(){
+ me.onLogon();
+ }
+ }
+ ]
+ }]
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.window.Migrate', {
+ extend: 'Ext.window.Window',
+
+ resizable: false,
+
+ migrate: function(target, online) {
+ var me = this;
+ PVE.Utils.API2Request({
+ params: { target: target, online: online },
+ url: '/nodes/' + me.nodename + '/' + me.vmtype + '/' + me.vmid + "/migrate",
+ waitMsgTarget: me,
+ method: 'POST',
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ success: function(response, options) {
+ var upid = response.result.data;
+
+ var win = Ext.create('PVE.window.TaskViewer', {
+ upid: upid
+ });
+ win.show();
+ me.close();
+ }
+ });
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ if (!me.vmid) {
+ throw "no VM ID specified";
+ }
+
+ if (!me.vmtype) {
+ throw "no VM type specified";
+ }
+
+ var running = false;
+ var vmrec = PVE.data.ResourceStore.findRecord('vmid', me.vmid,
+ 0, false, false, true);
+ if (vmrec && vmrec.data && vmrec.data.running) {
+ running = true;
+ }
+
+ me.formPanel = Ext.create('Ext.form.Panel', {
+ bodyPadding: 10,
+ border: false,
+ fieldDefaults: {
+ labelWidth: 100,
+ anchor: '100%'
+ },
+ items: [
+ {
+ xtype: 'PVE.form.NodeSelector',
+ name: 'target',
+ fieldLabel: gettext('Target node'),
+ allowBlank: false,
+ onlineValidator: true
+ },
+ {
+ xtype: 'pvecheckbox',
+ name: 'online',
+ uncheckedValue: 0,
+ defaultValue: 0,
+ checked: running,
+ fieldLabel: gettext('Online')
+ }
+ ]
+ });
+
+ var form = me.formPanel.getForm();
+
+ var submitBtn = Ext.create('Ext.Button', {
+ text: gettext('Migrate'),
+ handler: function() {
+ var values = form.getValues();
+ me.migrate(values.target, values.online);
+ }
+ });
+
+ Ext.apply(me, {
+ title: gettext('Migrate') + ' VM ' + me.vmid,
+ width: 350,
+ modal: true,
+ layout: 'auto',
+ border: false,
+ items: [ me.formPanel ],
+ buttons: [ submitBtn ]
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.window.MigrateAll', {
+ extend: 'Ext.window.Window',
+
+ resizable: false,
+
+ migrate: function(target, maxworkers) {
+ var me = this;
+ PVE.Utils.API2Request({
+ params: { target: target, maxworkers: maxworkers},
+ url: '/nodes/' + me.nodename + '/' + "/migrateall",
+ waitMsgTarget: me,
+ method: 'POST',
+ failure: function(response, opts) {
+ Ext.Msg.alert('Error', response.htmlStatus);
+ },
+ success: function(response, options) {
+ var upid = response.result.data;
+
+ var win = Ext.create('PVE.window.TaskViewer', {
+ upid: upid
+ });
+ win.show();
+ me.close();
+ }
+ });
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ me.formPanel = Ext.create('Ext.form.Panel', {
+ bodyPadding: 10,
+ border: false,
+ fieldDefaults: {
+ labelWidth: 100,
+ anchor: '100%'
+ },
+ items: [
+ {
+ xtype: 'PVE.form.NodeSelector',
+ name: 'target',
+ fieldLabel: 'Target node',
+ allowBlank: false,
+ onlineValidator: true
+ },
+ {
+ xtype: 'numberfield',
+ name: 'maxworkers',
+ minValue: 1,
+ maxValue: 100,
+ value: 1,
+ fieldLabel: 'Parallel jobs',
+ allowBlank: false
+ },
+ ]
+ });
+
+ var form = me.formPanel.getForm();
+
+ var submitBtn = Ext.create('Ext.Button', {
+ text: 'Migrate',
+ handler: function() {
+ var values = form.getValues();
+ me.migrate(values.target, values.maxworkers);
+ }
+ });
+
+ Ext.apply(me, {
+ title: "Migrate All VMs",
+ width: 350,
+ modal: true,
+ layout: 'auto',
+ border: false,
+ items: [ me.formPanel ],
+ buttons: [ submitBtn ]
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.window.NotesEdit', {
+ extend: 'PVE.window.Edit',
+
+ initComponent : function() {
+ var me = this;
+
+ Ext.apply(me, {
+ title: gettext('Notes'),
+ width: 600,
+ layout: 'fit',
+ items: {
+ xtype: 'textarea',
+ name: 'description',
+ rows: 7,
+ value: '',
+ hideLabel: true
+ }
+ });
+
+ me.callParent();
+
+ me.load();
+ }
+});
--- /dev/null
+Ext.define('PVE.window.Restore', {
+ extend: 'Ext.window.Window', // fixme: PVE.window.Edit?
+
+ resizable: false,
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ if (!me.volid) {
+ throw "no volume ID specified";
+ }
+
+ if (!me.vmtype) {
+ throw "no vmtype specified";
+ }
+
+ var storagesel = Ext.create('PVE.form.StorageSelector', {
+ nodename: me.nodename,
+ name: 'storage',
+ value: '',
+ fieldLabel: gettext('Storage'),
+ storageContent: (me.vmtype === 'lxc') ? 'rootdir' : 'images',
+ allowBlank: true
+ });
+
+ me.formPanel = Ext.create('Ext.form.Panel', {
+ bodyPadding: 10,
+ border: false,
+ fieldDefaults: {
+ labelWidth: 100,
+ anchor: '100%'
+ },
+ items: [
+ {
+ xtype: 'displayfield',
+ value: me.volidText || me.volid,
+ fieldLabel: gettext('Source')
+ },
+ storagesel,
+ {
+ xtype: me.vmid ? 'displayfield' : 'pveVMIDSelector',
+ name: 'vmid',
+ fieldLabel: 'VM ID',
+ value: me.vmid,
+ loadNextFreeVMID: me.vmid ? false: true,
+ validateExists: false
+ }
+ ]
+ });
+
+ var form = me.formPanel.getForm();
+
+ var doRestore = function(url, params) {
+ PVE.Utils.API2Request({
+ url: url,
+ params: params,
+ method: 'POST',
+ waitMsgTarget: me,
+ failure: function (response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ success: function(response, options) {
+ var upid = response.result.data;
+
+ var win = Ext.create('PVE.window.TaskViewer', {
+ upid: upid
+ });
+ win.show();
+ me.close();
+ }
+ });
+ };
+
+ var submitBtn = Ext.create('Ext.Button', {
+ text: gettext('Restore'),
+ handler: function(){
+ var storage = storagesel.getValue();
+ var values = form.getValues();
+
+ var params = {
+ storage: storage,
+ vmid: me.vmid || values.vmid,
+ force: me.vmid ? 1 : 0
+ };
+
+ var url;
+ if (me.vmtype === 'lxc') {
+ url = '/nodes/' + me.nodename + '/lxc';
+ params.ostemplate = me.volid;
+ params.restore = 1;
+ } else if (me.vmtype === 'qemu') {
+ url = '/nodes/' + me.nodename + '/qemu';
+ params.archive = me.volid;
+ } else {
+ throw 'unknown VM type';
+ }
+
+ if (me.vmid) {
+ var msg = gettext('Are you sure you want to restore this VM?') + ' ' +
+ gettext('This will permanently erase current VM data.');
+ Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
+ if (btn !== 'yes') {
+ return;
+ }
+ doRestore(url, params);
+ });
+ } else {
+ doRestore(url, params);
+ }
+ }
+ });
+
+ form.on('validitychange', function(f, valid) {
+ submitBtn.setDisabled(!valid);
+ });
+
+ var title = (me.vmtype === 'openvz') ? gettext('Restore CT') :
+ gettext('Restore VM');
+
+ Ext.apply(me, {
+ title: title,
+ width: 500,
+ modal: true,
+ layout: 'auto',
+ border: false,
+ items: [ me.formPanel ],
+ buttons: [ submitBtn ]
+ });
+
+ me.callParent();
+ }
+});
--- /dev/null
+Ext.define('PVE.window.TaskProgress', {
+ extend: 'Ext.window.Window',
+ alias: 'widget.pveTaskProgress',
+
+ initComponent: function() {
+ var me = this;
+
+ if (!me.upid) {
+ throw "no task specified";
+ }
+
+ var task = PVE.Utils.parse_task_upid(me.upid);
+
+ var statstore = Ext.create('PVE.data.ObjectStore', {
+ url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status",
+ interval: 1000,
+ rows: {
+ status: { defaultValue: 'unknown' },
+ exitstatus: { defaultValue: 'unknown' }
+ }
+ });
+
+ me.on('destroy', statstore.stopUpdate);
+
+ var getObjectValue = function(key, defaultValue) {
+ var rec = statstore.getById(key);
+ if (rec) {
+ return rec.data.value;
+ }
+ return defaultValue;
+ };
+
+ var pbar = Ext.create('Ext.ProgressBar', { text: 'running...' });
+
+ me.mon(statstore, 'load', function() {
+ var status = getObjectValue('status');
+ if (status === 'stopped') {
+ var exitstatus = getObjectValue('exitstatus');
+ if (exitstatus == 'OK') {
+ pbar.reset();
+ pbar.updateText("Done!");
+ Ext.Function.defer(me.close, 1000, me);
+ } else {
+ me.close();
+ Ext.Msg.alert('Task failed', exitstatus);
+ }
+ }
+ });
+
+ var descr = PVE.Utils.format_task_description(task.type, task.id);
+
+ Ext.applyIf(me, {
+ title: "Task: " + descr,
+ width: 300,
+ layout: 'auto',
+ modal: true,
+ bodyPadding: 5,
+ items: pbar,
+ buttons: [
+ {
+ text: gettext('Details'),
+ handler: function() {
+ var win = Ext.create('PVE.window.TaskViewer', {
+ upid: me.upid
+ });
+ win.show();
+ me.close();
+ }
+ }
+ ]
+ });
+
+ me.callParent();
+
+ statstore.startUpdate();
+
+ pbar.wait();
+ }
+});
+
+// fixme: how can we avoid those lint errors?
+/*jslint confusion: true */
+
+Ext.define('PVE.window.TaskViewer', {
+ extend: 'Ext.window.Window',
+ alias: 'widget.pveTaskViewer',
+
+ initComponent: function() {
+ var me = this;
+
+ if (!me.upid) {
+ throw "no task specified";
+ }
+
+ var task = PVE.Utils.parse_task_upid(me.upid);
+
+ var statgrid;
+
+ var rows = {
+ status: {
+ header: gettext('Status'),
+ defaultValue: 'unknown',
+ renderer: function(value) {
+ if (value != 'stopped') {
+ return value;
+ }
+ var es = statgrid.getObjectValue('exitstatus');
+ if (es) {
+ return value + ': ' + es;
+ }
+ }
+ },
+ exitstatus: {
+ visible: false
+ },
+ type: {
+ header: gettext('Task type'),
+ required: true
+ },
+ user: {
+ header: gettext('User name'),
+ required: true
+ },
+ node: {
+ header: gettext('Node'),
+ required: true
+ },
+ pid: {
+ header: gettext('Process ID'),
+ required: true
+ },
+ starttime: {
+ header: gettext('Start Time'),
+ required: true,
+ renderer: PVE.Utils.render_timestamp
+ },
+ upid: {
+ header: gettext('Unique task ID')
+ }
+ };
+
+ var statstore = Ext.create('PVE.data.ObjectStore', {
+ url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status",
+ interval: 1000,
+ rows: rows
+ });
+
+ me.on('destroy', statstore.stopUpdate);
+
+ var stop_task = function() {
+ PVE.Utils.API2Request({
+ url: "/nodes/" + task.node + "/tasks/" + me.upid,
+ waitMsgTarget: me,
+ method: 'DELETE',
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ };
+
+ var stop_btn1 = new Ext.Button({
+ text: gettext('Stop'),
+ disabled: true,
+ handler: stop_task
+ });
+
+ var stop_btn2 = new Ext.Button({
+ text: gettext('Stop'),
+ disabled: true,
+ handler: stop_task
+ });
+
+ statgrid = Ext.create('PVE.grid.ObjectGrid', {
+ title: gettext('Status'),
+ layout: 'fit',
+ tbar: [ stop_btn1 ],
+ rstore: statstore,
+ rows: rows,
+ border: false
+ });
+
+ var logView = Ext.create('PVE.panel.LogView', {
+ title: gettext('Output'),
+ tbar: [ stop_btn2 ],
+ border: false,
+ url: "/api2/extjs/nodes/" + task.node + "/tasks/" + me.upid + "/log"
+ });
+
+ me.mon(statstore, 'load', function() {
+ var status = statgrid.getObjectValue('status');
+
+ if (status === 'stopped') {
+ logView.requestUpdate(undefined, true);
+ logView.scrollToEnd = false;
+ statstore.stopUpdate();
+ }
+
+ stop_btn1.setDisabled(status !== 'running');
+ stop_btn2.setDisabled(status !== 'running');
+ });
+
+ statstore.startUpdate();
+
+ Ext.applyIf(me, {
+ title: "Task viewer: " + task.desc,
+ width: 800,
+ height: 400,
+ layout: 'fit',
+ modal: true,
+ bodyPadding: 5,
+ items: [{
+ xtype: 'tabpanel',
+ region: 'center',
+ items: [ logView, statgrid ]
+ }]
+ });
+
+ me.callParent();
+
+ logView.fireEvent('show', logView);
+ }
+});
+
--- /dev/null
+Ext.define('PVE.window.Wizard', {
+ extend: 'Ext.window.Window',
+
+ getValues: function(dirtyOnly) {
+ var me = this;
+
+ var values = {};
+
+ var form = me.down('form').getForm();
+
+ form.getFields().each(function(field) {
+ if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) {
+ PVE.Utils.assemble_field_data(values, field.getSubmitData());
+ }
+ });
+
+ Ext.Array.each(me.query('inputpanel'), function(panel) {
+ PVE.Utils.assemble_field_data(values, panel.getValues(dirtyOnly));
+ });
+
+ return values;
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ var tabs = me.items || [];
+ delete me.items;
+
+ /*
+ * Items may have the following functions:
+ * validator(): per tab custom validation
+ * onSubmit(): submit handler
+ * onGetValues(): overwrite getValues results
+ */
+
+ Ext.Array.each(tabs, function(tab) {
+ tab.disabled = true;
+ });
+ tabs[0].disabled = false;
+
+ var check_card = function(card) {
+ var valid = true;
+ var fields = card.query('field, fieldcontainer');
+ if (card.isXType('fieldcontainer')) {
+ fields.unshift(card);
+ }
+ Ext.Array.each(fields, function(field) {
+ // Note: not all fielcontainer have isValid()
+ if (Ext.isFunction(field.isValid) && !field.isValid()) {
+ valid = false;
+ }
+ });
+
+ if (Ext.isFunction(card.validator)) {
+ return card.validator();
+ }
+
+ return valid;
+ };
+
+
+ var tbar = Ext.create('Ext.toolbar.Toolbar', {
+ ui: 'footer',
+ region: 'south',
+ margins: '0 5 5 5',
+ items: [
+ '->',
+ {
+ text: gettext('Back'),
+ disabled: true,
+ itemId: 'back',
+ minWidth: 60,
+ handler: function() {
+ var tp = me.down('#wizcontent');
+ var atab = tp.getActiveTab();
+ var prev = tp.items.indexOf(atab) - 1;
+ if (prev < 0) {
+ return;
+ }
+ var ntab = tp.items.getAt(prev);
+ if (ntab) {
+ tp.setActiveTab(ntab);
+ }
+
+
+ }
+ },
+ {
+ text: gettext('Next'),
+ disabled: true,
+ itemId: 'next',
+ minWidth: 60,
+ handler: function() {
+
+ var form = me.down('form').getForm();
+
+ var tp = me.down('#wizcontent');
+ var atab = tp.getActiveTab();
+ if (!check_card(atab)) {
+ return;
+ }
+
+ var next = tp.items.indexOf(atab) + 1;
+ var ntab = tp.items.getAt(next);
+ if (ntab) {
+ ntab.enable();
+ tp.setActiveTab(ntab);
+ }
+
+ }
+ },
+ {
+ text: gettext('Finish'),
+ minWidth: 60,
+ hidden: true,
+ itemId: 'submit',
+ handler: function() {
+ var tp = me.down('#wizcontent');
+ var atab = tp.getActiveTab();
+ atab.onSubmit();
+ }
+ }
+ ]
+ });
+
+ var display_header = function(newcard) {
+ var html = '<h1>' + newcard.title + '</h1>';
+ if (newcard.descr) {
+ html += newcard.descr;
+ }
+ me.down('#header').update(html);
+ };
+
+ var disable_at = function(card) {
+ var tp = me.down('#wizcontent');
+ var idx = tp.items.indexOf(card);
+ for(;idx < tp.items.getCount();idx++) {
+ var nc = tp.items.getAt(idx);
+ if (nc) {
+ nc.disable();
+ }
+ }
+ };
+
+ var tabchange = function(tp, newcard, oldcard) {
+ if (newcard.onSubmit) {
+ me.down('#next').setVisible(false);
+ me.down('#submit').setVisible(true);
+ } else {
+ me.down('#next').setVisible(true);
+ me.down('#submit').setVisible(false);
+ }
+ var valid = check_card(newcard);
+ me.down('#next').setDisabled(!valid);
+ me.down('#submit').setDisabled(!valid);
+ me.down('#back').setDisabled(tp.items.indexOf(newcard) == 0);
+
+ if (oldcard && !check_card(oldcard)) {
+ disable_at(oldcard);
+ }
+
+ var next = tp.items.indexOf(newcard) + 1;
+ var ntab = tp.items.getAt(next);
+ if (valid && ntab && !newcard.onSubmit) {
+ ntab.enable();
+ }
+ };
+
+ if (me.subject && !me.title) {
+ me.title = PVE.Utils.dialog_title(me.subject, true, false);
+ }
+
+ Ext.applyIf(me, {
+ width: 620,
+ height: 400,
+ modal: true,
+ border: false,
+ draggable: true,
+ closable: true,
+ resizable: false,
+ layout: 'border',
+ items: [
+ {
+ // disabled for now - not really needed
+ hidden: true,
+ region: 'north',
+ itemId: 'header',
+ layout: 'fit',
+ margins: '5 5 0 5',
+ bodyPadding: 10,
+ html: ''
+ },
+ {
+ xtype: 'form',
+ region: 'center',
+ layout: 'fit',
+ border: false,
+ margins: '5 5 0 5',
+ fieldDefaults: {
+ labelWidth: 100,
+ anchor: '100%'
+ },
+ items: [{
+ itemId: 'wizcontent',
+ xtype: 'tabpanel',
+ activeItem: 0,
+ bodyPadding: 10,
+ listeners: {
+ afterrender: function(tp) {
+ var atab = this.getActiveTab();
+ tabchange(tp, atab);
+ },
+ tabchange: function(tp, newcard, oldcard) {
+ display_header(newcard);
+ tabchange(tp, newcard, oldcard);
+ }
+ },
+ items: tabs
+ }]
+ },
+ tbar
+ ]
+ });
+ me.callParent();
+ display_header(tabs[0]);
+
+ Ext.Array.each(me.query('field'), function(field) {
+ field.on('validitychange', function(f) {
+ var tp = me.down('#wizcontent');
+ var atab = tp.getActiveTab();
+ var valid = check_card(atab);
+ me.down('#next').setDisabled(!valid);
+ me.down('#submit').setDisabled(!valid);
+ var next = tp.items.indexOf(atab) + 1;
+ var ntab = tp.items.getAt(next);
+ if (!valid) {
+ disable_at(ntab);
+ } else if (ntab && !atab.onSubmit) {
+ ntab.enable();
+ }
+ });
+ });
+ }
+});