Commit 8d20a4ba authored by Dietmar Maurer's avatar Dietmar Maurer

remove unused files

parent 99a22765
include $(top_builddir)/common.mk
JSSRC= \
PVEUtils.js \
StateProvider.js \
VNCConsole.js \
data/TimezoneStore.js \
data/reader/JsonObject.js \
data/PVEProxy.js \
data/UpdateQueue.js \
data/UpdateStore.js \
data/DiffStore.js \
data/ObjectStore.js \
data/ResourceStore.js \
form/Checkbox.js \
form/TextField.js \
form/RRDTypeSelector.js \
form/ComboGrid.js \
form/NetworkCardSelector.js \
form/DiskFormatSelector.js \
form/BusTypeSelector.js \
form/RealmComboBox.js \
form/BondModeSelector.js \
form/ViewSelector.js \
form/NodeSelector.js \
form/FileSelector.js \
form/StorageSelector.js \
dc/Tasks.js \
dc/Log.js \
panel/StatusPanel.js \
panel/RRDView.js \
window/LoginWindow.js \
window/TaskViewer.js \
window/Wizard.js \
grid/SelectFeature.js \
grid/ObjectGrid.js \
grid/ResourceGrid.js \
tree/ResourceTree.js \
panel/ConfigPanel.js \
node/DNSEdit.js \
node/DNSView.js \
node/TimeView.js \
node/TimeEdit.js \
node/StatusView.js \
node/Summary.js \
node/ServiceView.js \
node/NetworkEdit.js \
node/NetworkView.js \
node/Syslog.js \
node/Tasks.js \
node/Config.js \
qemu/StatusView.js \
qemu/Summary.js \
qemu/Config.js \
qemu/HardwareView.js \
qemu/CreateWizard.js \
openvz/CreateWizard.js \
storage/Browser.js \
dc/UserView.js \
dc/GroupView.js \
dc/RoleView.js \
dc/ACLView.js \
dc/AuthView.js \
dc/Config.js \
Workspace.js
pvemanagerlib.js: ${JSSRC}
cat ${JSSRC} >$@
pvelib_DATA = pvemanagerlib.js
pvelibdir = ${WWW_EXTDIR}
privatedir = ${WWW_BASEDIR}
private_SCRIPTS = \
startup.pl
managerdir = ${WWW_ROOTDIR}
manager_SCRIPTS = \
index.pl
install-data-hook:
chown -R www-data:www-data ${DESTDIR}/${privatedir}
chown -R www-data:www-data ${DESTDIR}/${managerdir}
chown -R www-data:www-data ${DESTDIR}/${pvelibdir}
clean-local:
-rm -rf *~ store/*~ pvemanagerlib.js
This diff is collapsed.
/* 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', 'clog'],
['nodetab', ''],
['storagetab', ''],
['kvmtab', ''],
['dctab', '']
],
hprefix: 'v1',
compDict: {
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 === '') {
// do nothing
} else 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 = confirm('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){
var me = this;
var data;
if (typeof me.UIState[name] != "undefined") {
data = { value: me.UIState[name] };
} else {
data = me.callParent(arguments);
}
//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);
}
}
});
\ No newline at end of file
PVE_vnc_console_event = function(appletid, action, err) {
//console.log("TESTINIT param1 " + appletid + " action " + action);
return;
var el = Ext.get(appletid);
if (!el)
return;
if (action === "close") {
// el.remove();
} else if (action === "error") {
// console.log("TESTERROR: " + err);
// var compid = appletid.replace("-vncapp", "");
// var comp = Ext.getCmp(compid);
}
//Ext.get('mytestid').remove();
};
Ext.define('PVE.VNCConsole', {
extend: 'Ext.panel.Panel',
alias: ['widget.pveVNCConsole'],
initComponent : function() {
var me = this;
if (!me.url)
throw "no url specified";
var myid = me.id + "-vncapp";
me.appletID = myid;
var box = Ext.create('Ext.Component', {
border: false,
html: ""
});
var resize_window = function() {
//console.log("resize");
var applet = Ext.getDom(myid);
//console.log("resize " + myid + " " + applet);
// try again when dom element is available
if (!(applet && Ext.isFunction(applet.getPreferredSize)))
return Ext.Function.defer(resize_window, 1000);
var tbh = me.tbar ? me.tbar.getHeight() : 0;
var ps = applet.getPreferredSize();
var aw = ps.width;
var ah = ps.height;
if (aw < 640) aw = 640;
if (ah < 400) ah = 400;
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";
}
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() {
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) {
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() {
PVE.Utils.API2Request({
url: me.url,
params: me.params,
method: me.method || 'POST',
failure: function(response, opts) {
box.update("Error " + response.htmlStatus);
},
success: function(response, opts) {
var obj = Ext.decode(response.responseText);
start_vnc_viewer(obj.data);
}
});
}
});
me.callParent();
if (me.toplevel) {
me.on("render", function() { me.reloadApplet();});
} else {
me.on("show", function() { 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 vm_command = function(cmd, reload_applet) {
me.setLoading(true, true);
PVE.Utils.API2Request({
params: { command: cmd },
url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/status",
method: 'PUT',
callback: function() {
me.setLoading(false);
},
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: 'Start',
handler: function() {
vm_command("start", 1);
}
},
{
text: 'Stop',
handler: function() {
var msg = "Do you really want to stop the VM?";
Ext.Msg.confirm('Confirm', msg, function(btn) {
if (btn !== 'yes')
return;
vm_command("stop");
});
}
},
{
text: 'Reset',
handler: function() {
var msg = "Do you really want to reset the VM?";
Ext.Msg.confirm('Confirm', msg, function(btn) {
if (btn !== 'yes')
return;
vm_command("reset");
});
}
},
{
text: 'Shutdown',
handler: function() {
var msg = "Do you really want to shutdown the VM?";
Ext.Msg.confirm('Confirm', msg, function(btn) {
if (btn !== 'yes')
return;
vm_command('shutdown');
});
}
},
{
text: 'Suspend',
handler: function() {
var msg = "Do you really want to suspend the VM?";
Ext.Msg.confirm('Confirm', msg, function(btn) {
if (btn !== 'yes')
return;
vm_command("suspend");
});
}
},
{
text: 'Resume',
handler: function() {
vm_command("resume");
}
},
'->',
{
text: 'Refresh',
handler: function() {
var applet = Ext.getDom(me.appletID);
applet.sendRefreshRequest();
}
},
{
text: 'Reload',
handler: function () {
me.reloadApplet();
}
},
{
text: 'Console',
handler: function() {
var url = Ext.urlEncode({
console: 'kvm',
vmid: me.vmid,
node: me.nodename
});
var nw = window.open("?" + url, '_blank',
"innerWidth=745,innerheight=427");
nw.focus();
}
}
];
Ext.apply(me, {
tbar: tbar,
url: "/nodes/" + me.nodename + "/qemu/" + me.vmid + "/vncproxy"
});
me.callParent();
}
});
Ext.define('PVE.Shell', {
extend: 'PVE.VNCConsole',
alias: ['widget.pveShell'],
initComponent : function() {
var me = this;
if (!me.nodename)
throw "no node name specified";
var tbar = [
'->',
{
text: 'Refresh',
handler: function() {
var applet = Ext.getDom(me.appletID);
applet.sendRefreshRequest();
}
},
{
text: 'Reload',
handler: function () { me.reloadApplet(); }
},
{
text: 'Shell',
handler: function() {
var url = Ext.urlEncode({
console: 'shell',
node: me.nodename
});
var nw = window.open("?" + url, '_blank',
"innerWidth=745,innerheight=427");
nw.focus();
}
}
];
Ext.apply(me, {
tbar: tbar,
url: "/nodes/" + me.nodename + "/vncshell"
});
me.callParent();
}
});
\ No newline at end of file
/*
* 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',
requires: [
'Ext.tip.*',
'PVE.Utils',
'PVE.window.LoginWindow'
],
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;
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);
}
});
}
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) {
// cookie is automatically updated
var obj = Ext.decode(response.responseText);
me.updateLoginData(obj.data);
}
});
},
interval: 15*60*1000
});
}
});
Ext.define('PVE.ConsoleWorkspace', {
extend: 'PVE.Workspace',
requires: [
'PVE.KVMConsole'
],
alias: ['widget.pveConsoleWorkspace'],
title: 'Proxmox Console',
initComponent : function() {
var me = this;
var param = Ext.Object.fromQueryString(window.location.search);
var consoleType = me.consoleType || param.console;
var content;
if (consoleType === 'kvm') {
me.title = "VM " + param.vmid;
content = {
xtype: 'pveKVMConsole',
vmid: param.vmid,
nodename: param.node,
toplevel: true
};
} else if (consoleType === 'shell') {
me.title = "node " + param.node + " - Proxmox Shell"
content = {
xtype: 'pveShell',
nodename: param.node,
toplevel: true
};
} else {
content = {
border: false,
bodyPadding: 10,
html: 'Error: No such console type'
};
}
Ext.apply(me, {
layout: 'fit',
border: false,
items: content
});
me.callParent();
}
});
Ext.define('PVE.StdWorkspace', {
extend: 'PVE.Workspace',
requires: [
'Ext.History',
'Ext.state.*',
'Ext.selection.*',
'PVE.form.ViewSelector',
'PVE.data.ResourceStore',
'PVE.tree.ResourceTree'
],
alias: ['widget.pveStdWorkspace'],
// private
defaultContent: {
title: 'Nothing selected',
region: 'center'
},
setContent: function(comp) {
var me = this;
if (!comp)
comp = me.defaultContent;
var cont = me.child('#content');
cont.removeAll(true);
cont.add(comp);
cont.doLayout();
},
selectById: function(nodeid) {
var me = this;
var tree = me.down('pveResourceTree');
tree.selectById(nodeid);
},
onLogin: function(loginData) {
var me = this;
me.updateUserInfo();
if (loginData)
PVE.data.ResourceStore.startUpdate();
},
updateUserInfo: function() {
var me = this;
var ui = me.query('#userinfo')[0];
if (PVE.UserName) {
ui.update('<div class="x-unselectable" style="white-space:nowrap;">You are logged in as "' + PVE.UserName + '"</div>');
} else {
ui.update('');
}
ui.doLayout();
},
initComponent : function() {
var me = this;
Ext.History.init();
Ext.state.Manager.setProvider(Ext.create('PVE.StateProvider'));
//document.title = ;
var selview = new PVE.form.ViewSelector({
listeners: {
select: function(combo, records) {
if (records && records.length) {
var view = combo.getViewFilter();
combo.up('pveResourceTree').setViewFilter(view);
}
}
}
});
var rtree = Ext.createWidget('pveResourceTree', {
width: 200,
region: 'west',
margins: '0 0 0 5',
split: true,
viewFilter: selview.getViewFilter(),
tbar: [ ' ', selview ],
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',
storage: 'PVE.storage.Browser'
};
if (selected.length > 0) {
var n = selected[0];
comp = {
xtype: tlckup[n.data.type || 'root'] ||
'PVE.panel.Config',
layout: 'fit',
showSearch: (n.data.id === 'root') ||
Ext.isDefined(n.data.groupbyid),
pveSelNode: n,
viewFilter: selview.getViewFilter()
};
}
me.setContent(comp);
}
}
})
});
Ext.apply(me, {
layout: 'border',
border: false,
items: [
{
region: 'north',
height: 30,
layout: {
type: 'hbox',
align : 'middle'
},
baseCls: 'x-plain',
defaults: {
baseCls: 'x-plain'
},
border: false,
margins: '2 0 5 0',
items: [
{
margins: '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,
html: '<span class="x-panel-header-text">Proxmox Virtual Environment<br>Version ' + PVE.GUIVersion + "</span>"
},
{
pack: 'end',
margins: '8 10 0 10',
id: 'userinfo',
stateful: false
},
{
pack: 'end',
margins: '3 5 0 0',
xtype: 'button',
baseCls: 'x-btn',
text: "Logout",
handler: function() {
PVE.data.ResourceStore.stopUpdate();
me.showLogin();
me.setContent();
var rt = me.down('pveResourceTree');
rt.clearTree();
}
},
{
pack: 'end',
margins: '3 5 0 0',
xtype: 'button',
baseCls: 'x-btn',
text: "Create VM",
handler: function() {
var wiz = Ext.create('PVE.qemu.CreateWizard', {});
wiz.show();
}
},
{
pack: 'end',
margins: '3 5 0 0',
xtype: 'button',
baseCls: 'x-btn',
text: "Create CT",
handler: function() {
var wiz = Ext.create('PVE.openvz.CreateWizard', {});
wiz.show();
}
}
]
},
{
region: 'center',
id: 'content',
xtype: 'panel',
layout: 'fit',
border: false,
stateful: false,
margins:'0 5 0 0',
items: me.defaultContent
},
rtree,
{
xtype: 'pveStatusPanel',
region: 'south',
margins:'0 5 5 5',
height: 200,
collapsible: true,
split:true
}
]
});
me.callParent();
me.updateUserInfo();
}
});
/* Config properties:
* rstore: A storage to track changes
* Only works if rstore has a model and use 'idProperty'
*/
Ext.define('PVE.data.DiffStore', {
extend: 'Ext.data.Store',
constructor: function(config) {
var me = this;
var 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()
me.model.prototype.fields.eachKey(function(field) {
if (olditem.data[field] !== data[field])
olditem.set(field, data[field]);
});
olditem.endEdit(true);
olditem.commit();
} else {
var newrec = Ext.ModelMgr.create(data, me.model, id);
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.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();
first_load = false;
me.resumeEvents();
me.fireEvent('datachanged', me);
});
}
});
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,
reader: {
type: 'jsonobject',
rows: config.rows
}
}
});
me.callParent([config]);
}
});
Ext.define('PVE.RestProxy', {
extend: 'Ext.data.RestProxy',
alias : 'proxy.pve',
constructor: function(config) {
var me = this;
config = config || {};
Ext.applyIf(config, {
pageParam : null,
startParam: null,
limitParam: null,
groupParam: null,
sortParam: null,
filterParam: null,
noCache : false,
reader: {
type: 'json',
root: 'data'
}
});
me.callParent([config]);
}
}, function() {
Ext.define('pve-domains', {
extend: "Ext.data.Model",
fields: [ 'realm', 'type', 'comment', 'default' ],
proxy: {
type: 'pve',
url: "/api2/json/access/domains"
}
});
Ext.define('KeyValue', {
extend: "Ext.data.Model",
fields: [ 'key', 'value' ],
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'
});
});
Ext.define('PVE.data.ResourceStore', {
extend: 'PVE.data.UpdateStore',
requires: ['PVE.Utils'],
singleton: true,
findNextVMID: function() {
var me = this, i;
for (i = 100; i < 10000; i++) {
if (me.findExact('vmid', i) < 0)
return i;
}
},
findVMID: function(vmid) {
var me = this, i;
return (me.findExact('vmid', parseInt(vmid)) >= 0);
},
constructor: function(config) {
var me = this;
config = config || {};
var field_defaults = {
type: {
header: 'Type',
type: 'text',
renderer: PVE.Utils.render_resource_type,
sortable: true,
hideable: false,
width: 80
},
id: {
header: 'ID',
type: 'text',
hidden: true,
sortable: true,
width: 80
},
text: {
header: 'Text',
type: 'text',
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 === 'storage') {
text = info.storage + ' (' + info.node + ')';
} else if (info.type === 'qemu' || info.type === 'openvz') {
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: 'Name',
hidden: true,
sortable: true,
type: 'text'
},
disk: {
header: 'Disk usage',
type: 'integer',
renderer: PVE.Utils.render_disk_usage,
sortable: true,
width: 100
},
maxdisk: {
header: 'Disk size',
type: 'integer',
renderer: PVE.Utils.render_size,
sortable: true,
hidden: true,
width: 100
},
mem: {
header: 'Memory usage',
type: 'integer',
renderer: PVE.Utils.render_mem_usage,
sortable: true,
width: 100
},
maxmem: {
header: 'Mem size',
type:'integer',
renderer: PVE.Utils.render_size,
hidden: true,
sortable: true,
width: 100
},
cpu: {
header: 'CPU usage',
type: 'float',
renderer: PVE.Utils.render_cpu,
sortable: true,
width: 100
},
maxcpu: {
header: 'maxcpu',
type: 'integer',
hidden: true,
sortable: true,
width: 60
},
uptime: {
header: 'Uptime',
type: 'integer',
renderer: PVE.Utils.render_uptime,
sortable: true,
width: 110
},
node: {
header: 'Node',
type: 'text',
hidden: true,
sortable: true,
width: 110
},
storage: {
header: 'Storage',
type: 'text',
hidden: true,
sortable: true,
width: 110
}
};
var fields = [];
Ext.Object.each(field_defaults, function(key, value) {
if (!Ext.isDefined(value.convert))
fields.push({name: key, type: value.type});
else if (key === 'text')
fields.push({name: key, type: value.type, convert: value.convert});
});
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',
autoDestory: false,
defaultColums: function() {
var res = [];
for (field in field_defaults) {
var info = field_defaults[field];
var fi = Ext.apply({ dataIndex: field }, info);
res.push(fi);
}
return res;
}
});
me.callParent([config]);
}
});
This diff is collapsed.
// 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 store = queue.shift();
if (!store)
return;
queue_idx[store.storeid] = null;
idle = false;
store.load({
callback: function(records, operation, success) {
idle = true;
start_update();
}
});
};
Ext.apply(me, {
queue: function(store) {
if (!store.storeid)
throw "unable to queue store without storeid";
if (!queue_idx[store.storeid]) {
queue_idx[store.storeid] = store;
queue.push(store);
}
start_update();
}
});
}
});
Ext.define('PVE.data.UpdateStore', {
extend: 'Ext.data.Store',
requires: [
'PVE.Utils',
'Ext.util.*',
'PVE.data.UpdateQueue'
],
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);
load_task.delay(config.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() {
loadtask.cancel();
});
}
});
/* 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: { data: "xyz" }
* example2: [ { data: "xyz" } ]
*/
Ext.define('PVE.data.reader.JsonObject', {
extend: 'Ext.data.reader.Json',
alias : 'reader.jsonobject',
root: 'data',
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);
var root = me.getRoot(result);
if (Ext.isArray(root)) {
if (root.length == 1)
root = 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;
}
});
Ext.define('PVE.dc.Config', {
extend: 'PVE.panel.Config',
alias: 'widget.PVE.dc.Config',
initComponent: function() {
var me = this;
Ext.apply(me, {
title: "Datacenter",
hstateid: 'dctab',
items: [
{
title: 'Summary',
itemId: 'summary',
html: 'summary '
},
{
title: 'Storage',
itemId: 'storage',
html: 'storage '
},
{
xtype: 'pveUserView',
title: 'Users',
itemId: 'users'
},
{
xtype: 'pveGroupView',
title: 'Groups',
itemId: 'groups'
},
{
xtype: 'pveACLView',
title: 'Permissions',
itemId: 'permissions'
},
{
xtype: 'pveRoleView',
title: 'Roles',
itemId: 'roles'
},
{
xtype: 'pveAuthView',
title: 'Authentication',
itemId: 'domains'
}
]
});
me.callParent();
}
});
Ext.define('PVE.dc.GroupView', {
extend: 'Ext.grid.GridPanel',
alias: ['widget.pveGroupView'],
initComponent : function() {
var me = this;
var store = new Ext.data.Store({
model: Ext.define('pve-groups', {
extend: 'Ext.data.Model',
fields: [ 'groupid', 'comment' ],
idProperty: 'groupid'
}),
proxy: {
type: 'pve',
url: "/api2/json/access/groups"
},
sorters: {
property: 'groupid',
order: 'DESC'
}
});
Ext.apply(me, {
store: store,
stateful: false,
viewConfig: {
trackOver: false
},
columns: [
{
header: 'Group name',
width: 200,
sortable: true,
dataIndex: 'groupid'
},
{
header: 'Comment',
sortable: false,
dataIndex: 'comment',
flex: 1
}
],
listeners: {
show: function() {
store.load();
}
}
});
me.callParent();
}
});
\ No newline at end of file
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: false, // does not work with getRowClass()
getRowClass: function(record, index) {
var pri = record.get('pri');
if (pri && pri <= 3)
return "x-form-invalid-field";
}
},
sortableColumns: false,
columns: [
{
header: "Start Time",
dataIndex: 'time',
width: 100,
renderer: function(value) {
return Ext.Date.format(value, "M d H:i:s");
}
},
{
header: "Node",
dataIndex: 'node',
width: 100
},
{
header: "Tag",
dataIndex: 'tag',
width: 100
},
{
header: "PID",
dataIndex: 'pid',
width: 100,
},
{
header: "User",
dataIndex: 'user',
width: 150
},
{
header: "Severity",
dataIndex: 'pri',
renderer: PVE.Utils.render_serverity,
width: 100
},
{
header: "Message",
dataIndex: 'msg',
flex: 1
}
],
listeners: {
show: logstore.startUpdate,
hide: logstore.stopUpdate,
destroy: logstore.stopUpdate
}
});
me.callParent();
}
});
\ No newline at end of file
Ext.define('PVE.dc.RoleView', {
extend: 'Ext.grid.GridPanel',
alias: ['widget.pveRoleView'],
initComponent : function() {
var me = this;
var store = new Ext.data.Store({
model: Ext.define('pve-roles', {
extend: 'Ext.data.Model',
fields: [ 'roleid', 'privs' ],
idProperty: 'roleid'
}),
proxy: {
type: 'pve',
url: "/api2/json/access/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, ' ');
};
Ext.apply(me, {
store: store,
stateful: false,
viewConfig: {
trackOver: false
},
columns: [
{
header: 'Role name',
width: 150,
sortable: true,
dataIndex: 'roleid'
},
{
id: 'privs',
header: 'Privileges',
sortable: false,
renderer: render_privs,
dataIndex: 'privs',
flex: 1
}
],
listeners: {
show: function() {
store.load();
}
}
});
me.callParent();
}
});
\ No newline at end of file
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'
},
sorters: [
{
property : 'starttime',
direction: 'ASC'
}
]
});
var store = Ext.create('PVE.data.DiffStore', {
rstore: taskstore,
appendAtStart: true
});
var run_task_viewer = function() {
var sm = me.getSelectionModel();
var rec = sm.getLastSelected();
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: false, // 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: "Start Time",
dataIndex: 'starttime',
width: 100,
renderer: function(value) {
return Ext.Date.format(value, "M d H:i:s");
}
},
{
header: "End Time",
dataIndex: 'endtime',
width: 100,
renderer: function(value, metaData, record) {
if (record.data.pid) {
metaData.css = "x-grid-row-loading";
return "";
}
return Ext.Date.format(value, "M d H:i:s");
}
},
{
header: "Node",
dataIndex: 'node',
width: 100
},
{
header: "User",
dataIndex: 'user',
width: 150
},
{
header: "Description",
dataIndex: 'upid',
flex: 1,
renderer: PVE.Utils.render_upid
},
{
header: "Status",
dataIndex: 'status',
width: 200,
renderer: function(value, metaData, record) {
if (record.data.pid) {
metaData.css = "x-grid-row-loading";
return "";
}
if (value == 'OK')
return 'OK';
// metaData.attr = 'style="color:red;"';
return "ERROR: " + value;
}
}
],
listeners: {
itemdblclick: run_task_viewer,
show: taskstore.startUpdate,
hide: taskstore.stopUpdate,
destroy: taskstore.stopUpdate
}
});
me.callParent();
}
});
\ No newline at end of file
Ext.define('PVE.dc.UserView', {
extend: 'Ext.grid.GridPanel',
alias: ['widget.pveUserView'],
initComponent : function() {
var me = this;
var store = new Ext.data.Store({
model: Ext.define('pve-users', {
extend: 'Ext.data.Model',
fields: [
'userid', 'firstname', 'lastname' , 'email', 'comment',
{ type: 'boolean', name: 'enabled' },
{ type: 'date', dateFormat: 'timestamp', name: 'expire' },
],
idProperty: 'userid'
}),
proxy: {
type: 'pve',
url: "/api2/json/access/users"
},
sorters: {
property: 'userid',
order: 'DESC'
}
});
var render_expire = function(date) {
if (!date)
return 'never';
return Ext.Date.format(date, "Y-m-d");
};
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,
stateful: false,
viewConfig: {
trackOver: false
},
columns: [
{
header: 'User name',
width: 200,
sortable: true,
renderer: render_username,
dataIndex: 'userid'
},
{
header: 'Realm',
width: 100,
sortable: true,
renderer: render_realm,
dataIndex: 'userid'
},
{
header: 'Enabled',
width: 80,
sortable: true,
dataIndex: 'enabled'
},
{
header: 'Expire',
width: 80,
sortable: true,
renderer: render_expire,
dataIndex: 'expire'
},
{
header: 'Name',
width: 150,
sortable: true,
renderer: render_full_name,
dataIndex: 'firstname'
},
{
id: 'comment',
header: 'Comment',
sortable: false,
dataIndex: 'comment',
flex: 1
}
],
listeners: {
show: function() {
store.load();
}
}
});
me.callParent();
}
});
\ No newline at end of file
Ext.define('PVE.form.BondModeSelector', {
extend: 'Ext.form.field.ComboBox',
alias: ['widget.bondModeSelector'],
initComponent: function() {
var me = this;
var store = Ext.create('Ext.data.ArrayStore', {
model: 'KeyValue',
data : [
['balance-rr', ''],
['active-backup', ''],
['balance-xor', ''],
['broadcast', ''],
['802.3ad', ''],
['balance-tlb', ''],
['balance-alb', ''],
]
});
Ext.apply(me, {
store: store,
queryMode: 'local',
editable: false,
displayField: 'key',
valueField: 'key'
});
me.callParent();
}
});
Ext.define('PVE.form.BusTypeSelector', {
extend: 'Ext.form.field.ComboBox',
alias: ['widget.PVE.form.BusTypeSelector'],
initComponent: function() {
var me = this;
var store = Ext.create('Ext.data.ArrayStore', {
model: 'KeyValue',
data : [
['ide', 'IDE'],
['virtio', 'VIRTIO'],
['scsi', 'SCSI']
]
});
Ext.apply(me, {
store: store,
queryMode: 'local',
editable: false,
displayField: 'value',
valueField: 'key'
});
me.callParent();
}
});
Ext.define('PVE.form.Checkbox', {
extend: 'Ext.form.field.Checkbox',
alias: ['widget.pvecheckbox'],
inputValue: '1',
// also accept integer 1 as true
setRawValue: function(value) {
var me = this;
if (value === 1)
value = true;
me.callParent([value]);
}
});
\ No newline at end of file
Ext.define('PVE.form.ComboGrid', {
extend: 'Ext.form.ComboBox',
requires: [
'Ext.grid.Panel',
'PVE.Utils'
],
alias: ['widget.PVE.form.ComboGrid'],
computeHeight: function() {
var me = this;
var lh = PVE.Utils.gridLineHeigh();
var count = me.store.getCount();
return (count > 10) ? 10*lh : 26+count*lh;
},
// 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());
},
scope: me
});
me.mon(picker.getSelectionModel(), {
selectionChange: me.onListSelectionChange,
scope: me
});
return picker;
},
initComponent: function() {
var me = this;
Ext.apply(me, {
queryMode: 'local',
editable: false,
matchFieldWidth: false
});
Ext.applyIf(me.listConfig, { width: 400 });
me.callParent();
me.store.on('beforeload', function() {
me.up('form').setLoading(true, true);
});
// hack: autoSelect does not work
me.store.on('load', function(store, r, success, o) {
if (success) {
var def = me.getValue();
if (!def || !store.findRecord(me.valueField, def)) {
var rec = me.store.first();
if (me.autoSelect && rec && rec.data) {
def = rec.data[me.valueField];
me.setValue(def);
} else {
me.setValue('');
}
}
}
me.up('form').setLoading(false);
});
}
});
\ No newline at end of file
Ext.define('PVE.form.DiskFormatSelector', {
extend: 'Ext.form.field.ComboBox',
alias: ['widget.PVE.form.DiskFormatSelector'],
initComponent: function() {
var me = this;
var store = Ext.create('Ext.data.ArrayStore', {
model: 'KeyValue',
data : [
['raw', 'Raw disk image (raw)'],
['qcow2', 'QEMU image format (qcow2)'],
['vmdk', 'VMware image format (vmdk)']
]
});
Ext.apply(me, {
store: store,
queryMode: 'local',
editable: false,
displayField: 'value',
valueField: 'key'
});
me.callParent();
}
});
Ext.define('PVE.form.FileSelector', {
extend: 'PVE.form.ComboGrid',
requires: [
'Ext.data.Store',
'PVE.RestProxy'
],
alias: ['widget.PVE.form.FileSelector'],
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;
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', {
fields: [ 'volid', 'format', 'size', 'used', 'vmid',
{ name: 'text',
convert: function(value, record) {
if (value)
return value;
return record.data.volid.replace(/^.*:.*\//,'');;
}
}],
});
Ext.apply(me, {
store: store,
allowBlank: false,
autoSelect: false,
valueField: 'volid',
displayField: 'text',
listConfig: {
columns: [
{
header: 'Name',
dataIndex: 'text',
hideable: false,
flex: 1
},
{
header: 'Format',
width: 60,
dataIndex: 'format'
},
{
header: 'Size',
width: 60,
dataIndex: 'size',
renderer: PVE.Utils.format_size
}
]
}
});
me.callParent();
me.setStorage(me.storage, me.nodename);
}
});
\ No newline at end of file
Ext.define('PVE.form.NetworkCardSelector', {
extend: 'Ext.form.field.ComboBox',
alias: ['widget.PVE.form.NetworkCardSelector'],
initComponent: function() {
var me = this;
var store = Ext.create('Ext.data.ArrayStore', {
model: 'KeyValue',
data : [
['rtl8139', 'Realtec RTL8139'],
['e1000', 'Intel E1000'],
['virtio', 'VirtIO (paravirtualized)']
]
});
Ext.apply(me, {
store: store,
queryMode: 'local',
editable: false,
displayField: 'value',
valueField: 'key'
});
me.callParent();
}
});
Ext.define('PVE.form.NodeSelector', {
extend: 'PVE.form.ComboGrid',
requires: [
'Ext.data.Store',
'PVE.RestProxy'
],
alias: ['widget.PVE.form.NodeSelector'],
initComponent: function() {
var me = this;
var store = Ext.create('Ext.data.Store', {
fields: [ 'name', 'cpu', 'maxcpu', 'mem', 'maxmem', 'uptime' ],
autoLoad: true,
proxy: {
type: 'pve',
url: '/api2/json/nodes'
},
autoDestory: true,
sorters: [
{
property : 'mem',
direction: 'DESC'
},
{
property : 'name',
direction: 'ASC'
}
]
});
Ext.apply(me, {
store: store,
allowBlank: false,
valueField: 'name',
displayField: 'name',
listConfig: {
columns: [
{
header: 'Node',
dataIndex: 'name',
hideable: false,
flex: 1
},
{
header: 'Memory usage',
renderer: PVE.Utils.render_mem_usage,
width: 100,
dataIndex: 'mem'
},
{
header: 'CPU usage',
renderer: PVE.Utils.render_cpu,
sortable: true,
width: 100,
dataIndex: 'cpu'
}
]
},
validator: function(value) {
var rec = me.store.findRecord(me.valueField, value);
if (rec && rec.data && Ext.isNumeric(rec.data.mem))
return true;
return "Node " + value + " seems to be offline!";
}
});
me.callParent();
}
});
\ No newline at end of file
Ext.define('PVE.form.RRDTypeSelector', {
extend: 'Ext.form.field.ComboBox',
requires: [
'Ext.state.Manager'
],
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);
}
});
Ext.define('PVE.form.RealmComboBox', {
extend: 'Ext.form.ComboBox',
requires: ['Ext.data.Store', 'PVE.RestProxy'],
alias: ['widget.pveRealmComboBox'],
initComponent: function() {
var me = this;
var stateid = 'pveloginrealm';
var realmstore = Ext.create('Ext.data.Store', {
model: 'pve-domains',
autoDestory: true
});
Ext.apply(me, {
fieldLabel: 'Realm',
name: 'realm',
store: realmstore,
queryMode: 'local',
allowBlank: false,
forceSelection: true,
autoSelect: false,
triggerAction: 'all',
valueField: 'realm',
displayField: 'comment',
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)) {
if (r[0] && r[0].data)
def = r[0].data.realm;
Ext.each(r, function(record) {
if (record.data && record.data["default"])
def = record.data.realm;
});
}
if (def)
me.setValue(def)
}
}
});
}
});
\ No newline at end of file
Ext.define('PVE.form.StorageSelector', {
extend: 'PVE.form.ComboGrid',
requires: [
'Ext.data.Store',
'PVE.RestProxy'
],
alias: ['widget.PVE.form.StorageSelector'],
setNodename: function(nodename) {
var me = this;
if (!nodename || (me.nodename === nodename))
return;
me.nodename = nodename;
var url = '/api2/json/nodes/' + me.nodename + '/storage';
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', {
fields: [ 'storage', 'active', 'type', 'used', 'total' ],
autoDestory: true
});
Ext.apply(me, {
store: store,
allowBlank: false,
valueField: 'storage',
displayField: 'storage',
listConfig: {
columns: [
{
header: 'Name',
dataIndex: 'storage',
hideable: false,
flex: 1
},
{
header: 'Type',
width: 60,
dataIndex: 'type'
},
{
header: 'Used',
width: 60,
dataIndex: 'used',
renderer: PVE.Utils.format_size
},
{
header: 'Capacity',
width: 60,
dataIndex: 'total',
renderer: PVE.Utils.format_size
}
]
}
});
me.callParent();
me.setNodename(me.nodename);
}
});
\ No newline at end of file
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
Ext.define('PVE.form.ViewSelector', {
extend: 'Ext.form.ComboBox',
requires: ['Ext.data.Store'],
alias: ['widget.pveViewSelector'],
initComponent: function() {
var me = this;
var default_views = {
server: {
text: 'Server View',
groups: ['node']
},
folder: {
text: 'Folder View',
groups: ['type']
},
storage: {
text: 'Storage View',
groups: ['node'],
filterfn: function(node) {
return node.data.type === 'storage';
}
}
};
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,
autoDestory: true
});
Ext.apply(me, {
hideLabel: true,
store: store,
value: groupdef[0][0],
editable: false,
queryMode: 'local',
allowBlank: false,
forceSelection: true,
autoSelect: false,
triggerAction: 'all',
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);
}
});
\ No newline at end of file
Ext.define('PVE.grid.ObjectGrid', {
extend: 'Ext.grid.GridPanel',
requires: [
'Ext.grid.*',
'PVE.data.ObjectStore'
],
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,
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 });
}
});
}
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;
}
}));
var load_count = 0;
me.mon(rstore, 'load', function(s, records, success) {
load_count++;
me.setLoading(false);
if (!success) {
me.setLoading("Data load error");
return;
}
});
Ext.applyIf(me, {
store: store,
hideHeaders: true,
stateful: false,
columns: [
{
header: 'Name',
width: me.cwidth1 || 100,
dataIndex: 'key',
renderer: me.renderKey
},
{
flex: 1,
header: 'Value',
dataIndex: 'value',
renderer: me.renderValue
}
]
});
me.on('afterlayout', function() {
if (!load_count)
me.setLoading(true);
});
me.callParent();
}
});
// fixme: remove this fix
// this hack is required for ExtJS 4.0.0
Ext.override(Ext.grid.feature.Chunking, {
attachEvents: function() {
var grid = this.view.up('gridpanel'),
scroller = grid.down('gridscroller[dock=right]');
if (scroller === null ) {
grid.on("afterlayout", this.attachEvents, this);
return;
}
scroller.el.on('scroll', this.onBodyScroll, this, {buffer: 300});
},
rowHeight: PVE.Utils.gridLineHeigh()
});
Ext.define('PVE.grid.ResourceGrid', {
extend: 'Ext.grid.GridPanel',
requires: [
'Ext.grid.*',
'Ext.grid.feature.Chunking',
'Ext.state.Manager',
'Ext.data.*',
'Ext.data.Store',
'Ext.util.*',
'PVE.Utils',
'PVE.data.ResourceStore'
],
alias: ['widget.pveResourceGrid'],
features: [ {ftype: 'chunking'}],
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) {
var cs = cn.childNodes,
len = cs.length,
i = 0, n, res;
var orgnode = rstore.data.get(cn.data.id);
if (orgnode) {
if ((!filterfn || filterfn(cn)) &&
(!textfilter || textfilter_match(cn)))
nodeidx[cn.data.id] = orgnode;
}
for (; i < len; i++) {
gather_child_nodes(cs[i]);
}
};
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 = [];
for (var key in nodeidx) {
if (!nodeidx.hasOwnProperty(key))
continue;
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.ModelMgr.create(info, store.model, info.id);
addlist.push(item);
continue;
}
// try to detect changes
var changes = false;
store.model.prototype.fields.eachKey(function(field) {
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, {
title: 'Search',
store: store,
tbar: [
'->',
'Search:', ' ',
{
xtype: 'textfield',
width: 200,
value: textfilter,
enableKeyEvents: true,
listeners: {
keyup: function(field, e) {
var v = field.getValue();
textfilter = v;
filter_task.delay(500);
}
}
}
],
viewConfig: {
stripeRows: true,
trackOver: false
},
listeners: {
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);
}
});
\ No newline at end of file
Ext.override(Ext.view.Table, {
afterRender: function() {
var me = this;
me.callParent();
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 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;
};
}
});
#!/usr/bin/perl -w
use strict;
use mod_perl2 '1.9922';
use Encode;
use CGI;
use PVE::pvecfg;
use PVE::JSONSchema;
use PVE::AccessControl;
use PVE::REST;
sub send_output {
my ($r, $data) = @_;
my $encdata = encode('UTF-8', $data);
$r->no_cache (1);
my $x = length ($encdata);
$r->content_type ("text/html;charset=UTF-8");
$r->headers_out->set ("Content-length", "$x");
$r->headers_out->set ("Pragma", "no-cache");
$r->print ($encdata);
}
# NOTE: Requests to this page are not authenticated
# so we must be very careful here
my $r = Apache2::RequestUtil->request();
my $username;
my $token = 'null';
if (my $cookie = $r->headers_in->{Cookie}) {
my $ticket = PVE::REST::extract_auth_cookie($cookie);
if (($username = PVE::AccessControl::verify_ticket($ticket, 1))) {
$token = PVE::AccessControl::assemble_csrf_prevention_token($username);
}
}
my $version = PVE::pvecfg::version() . "/" . PVE::pvecfg::repoid();
$username = '' if !$username;
my $cgi = CGI->new($r);
my %args = $cgi->Vars();
my $workspace = defined($args{console}) ?
"PVE.ConsoleWorkspace" : "PVE.StdWorkspace";
my $jssrc = <<_EOJS;
if (!PVE) PVE = {};
PVE.GUIVersion = '$version';
PVE.UserName = '$username';
PVE.CSRFPreventionToken = '$token';
Ext.require(['*', '$workspace']);
// we need this (the java applet ignores the zindex)
Ext.useShims = true;
Ext.History.fieldid = 'x-history-field';
Ext.onReady(function() { Ext.create('$workspace');});
_EOJS
$jssrc .= "";
my $page = <<_EOD;
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Proxmox Virtual Environment</title>
<link rel="stylesheet" type="text/css" href="/pve2/ext4/resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="/pve2/css/ext-pve.css" />
<script type="text/javascript" src="/pve2/ext4/ext-all-debug.js"></script>
<script type="text/javascript" src="/pve2/ext4/pvemanagerlib.js"></script>
<script type="text/javascript">$jssrc</script>
</head>
<body>
<!-- Fields required for history management -->
<form id="history-form" class="x-hidden">
<input type="hidden" id="x-history-field"/>
</form>
</body>
</html>
_EOD
send_output ($r, $page);
exit (0);
Ext.define('PVE.node.Config', {
extend: 'PVE.panel.Config',
alias: 'widget.PVE.node.Config',
initComponent: function() {
var me = this;
var nodename = me.pveSelNode.data.node;
if (!nodename)
throw "no node name specified";
Ext.apply(me, {
title: "Node '" + nodename + "'",
hstateid: 'nodetab',
items: [
{
title: 'Summary',
itemId: 'summary',
xtype: 'pveNodeSummary'
},
{
title: 'Services',
itemId: 'services',
xtype: 'pveNodeServiceView'
},
{
title: 'Network',
itemId: 'network',
xtype: 'pveNodeNetworkView'
},
{
title: 'DNS',
itemId: 'dns',
xtype: 'pveNodeDNSView',
},
{
title: 'Time',
itemId: 'time',
xtype: 'pveNodeTimeView'
},
{
title: 'Syslog',
itemId: 'syslog',
xtype: 'pveNodeSyslog'
},
{
title: 'Task History',
itemId: 'tasks',
xtype: 'pveNodeTasks'
}
]
});
me.callParent();
}
});
Ext.define('PVE.node.DNSEdit', {
extend: 'Ext.window.Window',
requires: [
'PVE.Utils'
],
alias: ['widget.pveNodeDNSEdit'],
initComponent : function() {
var me = this;
var nodename = me.pveSelNode.data.node;
if (!nodename)
throw "no node name specified";
var formpanel = Ext.create('Ext.form.Panel', {
url: "/api2/extjs/nodes/" + nodename + "/dns",
method: 'PUT',
trackResetOnLoad: true,
bodyPadding: 10,
fieldDefaults: {
labelWidth: 130,
anchor: '100%'
},
items: [
{
xtype: 'textfield',
fieldLabel: 'Search domain',
name: 'search',
allowBlank: false
},
{
xtype: 'textfield',
fieldLabel: 'First DNS server',
vtype: 'IPAddress',
name: 'dns1'
},
{
xtype: 'textfield',
fieldLabel: 'Second DNS server',
vtype: 'IPAddress',
name: 'dns2'
},
{
xtype: 'textfield',
fieldLabel: 'Third DNS server',
vtype: 'IPAddress',
name: 'dns3'
}
]
});
var form = formpanel.getForm();
var submitBtn = Ext.create('Ext.Button', {
text: 'OK',
disabled: true,
handler: function() {
formpanel.submit({
success: function() {
me.close();
},
failure: function(form, action) {
Ext.Msg.alert('Error', PVE.Utils.extractFormActionError(action));
}
});
}
});
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));
resetBtn.setDisabled(!dirty);
};
form.on('dirtychange', set_button_status);
form.on('validitychange', set_button_status);
formpanel.load({
method: 'GET',
failure: function(form, action) {
var msg = PVE.Utils.extractFormActionError(action);
Ext.Msg.alert("Load failed", msg, function() {
me.close();
});
}
});
Ext.applyIf(me, {
title: "Edit DNS settings",
modal: true,
width: 400,
height: 200,
layout: 'fit',
border: false,
items: formpanel,
buttons: [ submitBtn, resetBtn ]
});
me.callParent();
}
});
Ext.define('PVE.node.DNSView', {
extend: 'PVE.grid.ObjectGrid',
alias: ['widget.pveNodeDNSView'],
initComponent : function() {
var me = this;
var nodename = me.pveSelNode.data.node;
if (!nodename)
throw "no node name specified";
var run_editor = function() {
var win = Ext.create('PVE.node.DNSEdit', {
pveSelNode: me.pveSelNode
});
win.show();
};
Ext.applyIf(me, {
url: "/api2/json/nodes/" + nodename + "/dns",
cwidth1: 130,
interval: 1000,
rows: {
search: { header: 'Search domain', required: true },
dns1: { header: 'First DNS server', required: true },
dns2: { header: 'Second DNS server' },
dns3: { header: 'Third DNS server' },
},
tbar: [
{
text: "Edit",
handler: run_editor
}
],
listeners: {
itemdblclick: run_editor
}
});
me.callParent();
me.on('show', me.rstore.startUpdate);
me.on('hide', me.rstore.stopUpdate);
me.on('destroy', me.rstore.stopUpdate);
}
});
Ext.define('PVE.node.NetworkEdit', {
extend: 'Ext.window.Window',
alias: ['widget.pveNodeNetworkEdit'],
initComponent : function() {
var me = this;
var nodename = me.pveSelNode.data.node;
if (!nodename)
throw "no node name specified";
if (!me.iftype)
throw "no network device type specified";
var create = !me.iface;
var title;
var iface_vtype;
if (create) {
if (me.iftype === 'bridge') {
title = "Create Bridge";
iface_vtype = 'BridgeName';
} else if (me.iftype === 'bond') {
title = "Create Bond";
iface_vtype = 'BondName';
} else
throw "can't create unknown device type";
} else {
title = "Edit network device '" + me.iface + "'";
}
var col2 = [
{
xtype: 'pvecheckbox',
fieldLabel: 'Autostart',
name: 'autostart',
uncheckedValue: 0,
checked: create ? true : undefined
}
];
if (me.iftype === 'bridge') {
col2.push({
xtype: 'textfield',
fieldLabel: 'Bridge ports',
name: 'bridge_ports'
});
} else if (me.iftype === 'bond') {
col2.push({
xtype: 'textfield',
fieldLabel: 'Slaves',
name: 'slaves'
});
col2.push({
xtype: 'bondModeSelector',
fieldLabel: 'Mode',
name: 'bond_mode',
value: create ? 'balance-rr' : undefined,
allowBlank: false
});
}
var url;
var method;
if (create) {
url = "/api2/extjs/nodes/" + nodename + "/network";
method = 'POST';
} else {
url = "/api2/extjs/nodes/" + nodename + "/network/" + me.iface;
method = 'PUT';
}
var formpanel = Ext.create('Ext.form.Panel', {
url: url,
method: method,
trackResetOnLoad: true,
bodyPadding: 10,
border: false,
fieldDefaults: {
labelWidth: 100,
anchor: '100%'
},
layout: 'column',
defaultType: 'container',
items: [
{
columnWidth: .5,
items: [
{
xtype: 'textfield',
fieldLabel: 'Name',
name: 'iface',
value: me.iface,
disabled: !create,
vtype: iface_vtype,
allowBlank: false
},
{
xtype: 'pvetextfield',
deleteEmpty: !create,
fieldLabel: 'IP address',
vtype: 'IPAddress',
name: 'address'
},
{
xtype: 'pvetextfield',
deleteEmpty: !create,
fieldLabel: 'Subnet mask',
vtype: 'IPAddress',
name: 'netmask',
validator: function(value) {
if (!me.items)
return true;
var address = me.down('field[name=address]').getValue();
if (value !== '') {
if (address === '')
return "Subnet mask requires option 'IP address'";
} else {
if (address !== '')
return "Option 'IP address' requires a subnet mask";
}
return true;
}
},
{
xtype: 'pvetextfield',
deleteEmpty: !create,
fieldLabel: 'Gateway',
vtype: 'IPAddress',
name: 'gateway'
}
]
},
{
columnWidth: .5,
items: col2
}
]
});
var form = formpanel.getForm();
var submitBtn = Ext.create('Ext.Button', {
text: 'OK',
disabled: true,
handler: function() {
formpanel.submit({
success: function() {
me.close();
},
failure: function(form, action) {
Ext.Msg.alert('Error', PVE.Utils.extractFormActionError(action));
}
});
}
});
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));
resetBtn.setDisabled(!dirty);
};
form.on('dirtychange', set_button_status);
form.on('validitychange', set_button_status);
if (!create) {
formpanel.load({
url: "/api2/extjs/nodes/" + nodename + "/network/" + me.iface,
method: 'GET',
failure: function(form, action) {
var msg = PVE.Utils.extractFormActionError(action);
Ext.Msg.alert("Load failed", msg, function() {
me.close();
});
},
success: function(form, action) {
if (action.result.data.type !== me.iftype) {
var msg = "Got unexpected device type";
Ext.Msg.alert("Load failed", msg, function() {
me.close();
});
return;
}
}
});
}
Ext.applyIf(me, {
title: title,
modal: true,
width: 600,
height: 200,
layout: 'fit',
border: false,
items: formpanel,
buttons: [ submitBtn, resetBtn ]
});
if (create)
form.findField('iface').setValue(me.iface_default);
me.callParent();
}
});
Ext.define('PVE.node.NetworkView', {
extend: 'Ext.panel.Panel',
alias: ['widget.pveNodeNetworkView'],
initComponent : function() {
var me = this;
var nodename = me.pveSelNode.data.node;
if (!nodename)
throw "no node name specified";
var rstore = Ext.create('PVE.data.UpdateStore', {
interval: 1000,
storeid: 'pve-networks',
model: Ext.define('pve-networks', {
extend: 'Ext.data.Model',
fields: [
'iface', 'type', 'active', 'autostart',
'bridge_ports', 'slaves', 'address',
'netmask', 'gateway'
],
idProperty: 'iface',
proxy: {
type: 'pve',
url: "/api2/json/nodes/" + nodename + "/network",
}
}),
sorters: [
{
property : 'iface',
direction: 'ASC'
}
]
});
var store = Ext.create('PVE.data.DiffStore', { rstore: rstore });
var view_changes = function() {
var changeitem = me.down('#changes');
PVE.Utils.API2Request({
url: '/nodes/' + nodename + '/network_changes',
failure: function(response, opts) {
changeitem.update('Error: ' + response.htmlStatus);
},
success: function(response, opts) {
var result = Ext.decode(response.responseText);
var data = result.data;
if (data === '')
data = "no changes"
changeitem.update("<pre>" + Ext.htmlEncode(data) + "</pre>");
}
});
};
var reload = function() {
rstore.load();
view_changes();
};
var run_editor = function() {
var grid = me.down('gridpanel');
var sm = grid.getSelectionModel();
var rec = sm.getLastSelected();
if (!rec)
return;
var win = Ext.create('PVE.node.NetworkEdit', {
pveSelNode: me.pveSelNode,
iface: rec.data.iface,
iftype: rec.data.type
});
win.show();
win.on('destroy', reload)
};
var edit_btn = new Ext.Button({
text: 'Edit',
disabled: true,
handler: run_editor
});
var del_btn = new Ext.Button({
text: 'Delete',
disabled: true,
handler: function(){
var grid = me.down('gridpanel');
var sm = grid.getSelectionModel();
var rec = sm.getLastSelected();
if (!rec)
return;
var iface = rec.data.iface;
me.setLoading(true, true);
PVE.Utils.API2Request({
url: '/nodes/' + nodename + '/network/' + iface,
method: 'DELETE',
callback: function() {
me.setLoading(false);
reload();
},
failure: function(response, opts) {
Ext.Msg.alert('Error', response.htmlStatus);
}
});
}
});
var set_button_status = function() {
var grid = me.down('gridpanel');
var sm = grid.getSelectionModel();
var rec = sm.getLastSelected();
edit_btn.setDisabled(!rec);
del_btn.setDisabled(!rec);
};
me.mon(rstore, 'load', function(s, records, success) {
if (!success) {
me.setLoading("Data load error");
return;
} else {
me.setLoading(false);
}
});
var render_ports = function(value, metaData, record) {
if (value === 'bridge')
return record.data.bridge_ports;
if (value === 'bond')
return record.data.slaves;
};
Ext.apply(me, {
layout: 'border',
tbar: [
{
text: 'Create',
menu: new Ext.menu.Menu({
items: [
{
text: 'Bridge',
handler: function() {
var next;
for (next = 0; next <= 9999; next++) {
if (!rstore.data.get('vmbr' + next))
break;
}
var win = Ext.create('PVE.node.NetworkEdit', {
pveSelNode: me.pveSelNode,
iftype: 'bridge',
iface_default: 'vmbr' + next
});
win.on('destroy', reload)
win.show();
}
},
{
text: 'Bond',
handler: function() {
var next;
for (next = 0; next <= 9999; next++) {
if (!rstore.data.get('bond' + next))
break;
}
var win = Ext.create('PVE.node.NetworkEdit', {
pveSelNode: me.pveSelNode,
iftype: 'bond',
iface_default: 'bond' + next
});
win.on('destroy', reload)
win.show();
}
}
]
})
}, ' ',
{
text: 'Revert changes',
handler: function() {
me.setLoading(true, true);
PVE.Utils.API2Request({
url: '/nodes/' + nodename + '/network_changes',
method: 'DELETE',
callback: function() {
me.setLoading(false);
reload();
},
failure: function(response, opts) {
Ext.Msg.alert('Error', response.htmlStatus);
}
});
}
},
edit_btn,
del_btn
],
items: [
{
xtype: 'gridpanel',
stateful: false,
store: store,
region: 'center',
border: false,
columns: [
{
header: 'Interface Name',
width: 100,
sortable: true,
dataIndex: 'iface'
},
{
xtype: 'booleancolumn',
header: 'Active',
width: 80,
sortable: true,
dataIndex: 'active',
trueText: 'Yes',
falseText: 'No',
undefinedText: 'No'
},
{
xtype: 'booleancolumn',
header: 'Autostart',
width: 80,
sortable: true,
dataIndex: 'autostart',
trueText: 'Yes',
falseText: 'No',
undefinedText: 'No'
},
{
header: 'Ports/Slaves',
dataIndex: 'type',
renderer: render_ports
},
{
header: 'IP address',
sortable: true,
dataIndex: 'address'
},
{
header: 'Subnet mask',
sortable: true,
dataIndex: 'netmask'
},
{
header: 'Gateway',
sortable: true,
dataIndex: 'gateway'
}
],
listeners: {
selectionchange: set_button_status,
itemdblclick: run_editor
}
},
{
border: false,
region: 'south',
autoScroll: true,
itemId: 'changes',
tbar: [
'Pending changes (please reboot to activate changes)'
],
split: true,
bodyPadding: 5,
flex: 0.6,
html: "no changes"
}
],
listeners: {
show: reload
}
});
me.callParent();
}
});
\ No newline at end of file
Ext.define('PVE.node.ServiceView', {
extend: 'Ext.grid.GridPanel',
alias: ['widget.pveNodeServiceView'],
initComponent : function() {
var me = this;
var nodename = me.pveSelNode.data.node;
if (!nodename)
throw "no node name specified";
var rstore = Ext.create('PVE.data.UpdateStore', {
interval: 1000,
storeid: 'pve-services',
model: Ext.define('pve-services', {
extend: 'Ext.data.Model',
fields: [ 'service', 'name', 'desc', 'state' ],
idProperty: 'service',
proxy: {
type: 'pve',
url: "/api2/json/nodes/" + nodename + "/services",
}
})
});
var store = Ext.create('PVE.data.DiffStore', { rstore: rstore });
var service_cmd = function(cmd) {
var sm = me.getSelectionModel();
var rec = sm.getLastSelected();
PVE.Utils.API2Request({
url: "/nodes/" + nodename + "/services/" + rec.data.service,
params: { command: cmd },
method: 'PUT',
failure: function(response, opts) {
Ext.Msg.alert('Error', response.htmlStatus);
me.loading = true;
},
success: function(response, opts) {
rstore.startUpdate();
}
});
};
var start_btn = new Ext.Button({
text: 'Start',
disabled: true,
handler: function(){
service_cmd("start");
}
});
var stop_btn = new Ext.Button({
text: 'Stop',
disabled: true,
handler: function(){
service_cmd("stop");
}
});
var restart_btn = new Ext.Button({
text: 'Restart',
disabled: true,
handler: function(){
service_cmd("restart");
}
});
var set_button_status = function() {
var sm = me.getSelectionModel();
var rec = sm.getLastSelected();
if (!rec) {
start_btn.disable();
stop_btn.disable();
restart_btn.disable();
return;
}
var service = rec.data.service;
var state = rec.data.state;
if (service == 'apache' ||
service == 'pvecluster' ||
service == 'pvedaemon') {
if (state == 'running') {
start_btn.disable();
restart_btn.enable();
} else {
start_btn.enable();
restart_btn.disable();
}
stop_btn.disable();
} else {
if (state == 'running') {
start_btn.disable();
restart_btn.enable();
stop_btn.enable();
} else {
start_btn.enable();
restart_btn.disable();
stop_btn.disable();
}
}
};
me.mon(store, 'datachanged', set_button_status);
me.mon(rstore, 'load', function(s, records, success) {
if (!success) {
me.setLoading("Data load error");
return;
} else {
me.setLoading(false);
}
});
Ext.apply(me, {
store: store,
stateful: false,
tbar: [ start_btn, stop_btn, restart_btn ],
columns: [
{
header: 'Name',
width: 100,
sortable: true,
dataIndex: 'name'
},
{
header: 'State',
width: 100,
sortable: true,
dataIndex: 'state'
},
{
header: 'Description',
dataIndex: 'desc',
flex: 1
}
],
listeners: {
selectionchange: set_button_status,
show: rstore.startUpdate,
hide: rstore.stopUpdate,
destroy: rstore.stopUpdate,
}
});
me.callParent();
}
});
\ No newline at end of file
Ext.define('PVE.node.StatusView', {
extend: 'PVE.grid.ObjectGrid',
alias: ['widget.pveNodeStatusView'],
initComponent : function() {
var me = this;
var render_cpuinfo = function(value) {
return value.cpus + " x " + value.model;
};
var render_loadavg = function(value) {
return value[0] + ", " + value[1] + ", " + value[2];
};
var render_cpu = function(value) {
var per = value * 100;
return per.toFixed(2) + "%";
};
var render_meminfo = function(value) {
var per = (value.used / value.total)*100;
var text = "<div>Total: " + PVE.Utils.format_size(value.total) + "</div>" +
"<div>Used: " + PVE.Utils.format_size(value.used) + "</div>";
return text;
};
var rows = {
uptime: { header: 'Uptime', required: true, renderer: PVE.Utils.format_duration_long },
loadavg: { header: 'Load average', required: true, renderer: render_loadavg },
cpuinfo: { header: 'CPUs', required: true, renderer: render_cpuinfo },
cpu: { header: 'CPU usage',required: true, renderer: render_cpu },
wait: { header: 'IO delay', required: true, renderer: render_cpu },
memory: { header: 'RAM usage', required: true, renderer: render_meminfo },
swap: { header: 'SWAP usage', required: true, renderer: render_meminfo },
rootfs: { header: 'HD space (root)', required: true, renderer: render_meminfo },
pveversion: { header: 'PVE Manager version', required: true,},
kversion: { header: 'Kernel version', required: true,}
};
Ext.applyIf(me, {
cwidth1: 150,
interval: 1000,
height: 286,
rows: rows
});
me.callParent();
}
});
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Ext.ns("PVE.data");
// a store for simple JSON Object (hash)
PVE.data.ObjectStore = Ext.extend(Ext.data.Store, {
constructor: function(config) {
PVE.data.ObjectStore.superclass.constructor.call(this, Ext.apply(config, {
reader: new PVE.data.ObjectReader(config)
}));
}
});
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment