Ext.define('PVE.dc.Summary', { extend: 'Ext.panel.Panel', alias: 'widget.pveDcSummary', scrollable: true, bodyPadding: 5, layout: 'column', defaults: { padding: 5, columnWidth: 1, }, items: [ { itemId: 'dcHealth', xtype: 'pveDcHealth', }, { itemId: 'dcGuests', xtype: 'pveDcGuests', }, { title: gettext('Resources'), xtype: 'panel', minHeight: 250, bodyPadding: 5, layout: 'hbox', defaults: { xtype: 'proxmoxGauge', flex: 1, }, items: [ { title: gettext('CPU'), itemId: 'cpu', }, { title: gettext('Memory'), itemId: 'memory', }, { title: gettext('Storage'), itemId: 'storage', }, ], }, { itemId: 'nodeview', xtype: 'pveDcNodeView', height: 250, }, { title: gettext('Subscriptions'), height: 220, items: [ { itemId: 'subscriptions', xtype: 'pveHealthWidget', userCls: 'pointer', listeners: { element: 'el', click: function() { if (this.component.userCls === 'pointer') { window.open('https://www.proxmox.com/en/proxmox-ve/pricing', '_blank'); } }, }, }, ], }, ], listeners: { resize: function(panel) { Proxmox.Utils.updateColumns(panel); }, }, initComponent: function() { var me = this; var rstore = Ext.create('Proxmox.data.UpdateStore', { interval: 3000, storeid: 'pve-cluster-status', model: 'pve-dc-nodes', proxy: { type: 'proxmox', url: "/api2/json/cluster/status", }, }); var gridstore = Ext.create('Proxmox.data.DiffStore', { rstore: rstore, filters: { property: 'type', value: 'node', }, sorters: { property: 'id', direction: 'ASC', }, }); me.callParent(); me.getComponent('nodeview').setStore(gridstore); var gueststatus = me.getComponent('dcGuests'); var cpustat = me.down('#cpu'); var memorystat = me.down('#memory'); var storagestat = me.down('#storage'); var sp = Ext.state.Manager.getProvider(); me.mon(PVE.data.ResourceStore, 'load', function(curstore, results) { me.suspendLayout = true; let cpu = 0, maxcpu = 0; let memory = 0, maxmem = 0; let used = 0, total = 0; let countedStorage = {}, usableStorages = {}; let storages = sp.get('dash-storages') || ''; storages.split(',').filter(v => v !== '').forEach(storage => { usableStorages[storage] = true; }); let qemu = { running: 0, paused: 0, stopped: 0, template: 0, }; let lxc = { running: 0, paused: 0, stopped: 0, template: 0, }; let error = 0; for (const { data } of results) { switch (data.type) { case 'node': cpu += data.cpu * data.maxcpu; maxcpu += data.maxcpu || 0; memory += data.mem || 0; maxmem += data.maxmem || 0; if (gridstore.getById(data.id)) { let griditem = gridstore.getById(data.id); griditem.set('cpuusage', data.cpu); let max = data.maxmem || 1; let val = data.mem || 0; griditem.set('memoryusage', val / max); griditem.set('uptime', data.uptime); griditem.commit(); // else the store marks the field as dirty } break; case 'storage': { let sid = !data.shared || data.storage === 'local' ? data.id : data.storage; if (!Ext.Object.isEmpty(usableStorages)) { if (usableStorages[data.id] !== true) { break; } sid = data.id; } else if (countedStorage[sid]) { break; } used += data.disk; total += data.maxdisk; countedStorage[sid] = true; break; } case 'qemu': qemu[data.template ? 'template' : data.status]++; if (data.hastate === 'error') { error++; } break; case 'lxc': lxc[data.template ? 'template' : data.status]++; if (data.hastate === 'error') { error++; } break; default: break; } } let text = Ext.String.format(gettext('of {0} CPU(s)'), maxcpu); cpustat.updateValue(cpu/maxcpu, text); text = Ext.String.format(gettext('{0} of {1}'), Proxmox.Utils.render_size(memory), Proxmox.Utils.render_size(maxmem)); memorystat.updateValue(memory/maxmem, text); text = Ext.String.format(gettext('{0} of {1}'), Proxmox.Utils.render_size(used), Proxmox.Utils.render_size(total)); storagestat.updateValue(used/total, text); gueststatus.updateValues(qemu, lxc, error); me.suspendLayout = false; me.updateLayout(true); }); let dcHealth = me.getComponent('dcHealth'); me.mon(rstore, 'load', dcHealth.updateStatus, dcHealth); let subs = me.down('#subscriptions'); me.mon(rstore, 'load', function(store, records, success) { var level; var mixed = false; for (let i = 0; i < records.length; i++) { let node = records[i]; if (node.get('type') !== 'node' || node.get('status') === 'offline') { continue; } let curlevel = node.get('level'); if (curlevel === '') { // no subscription beats all, set it and break the loop level = ''; break; } if (level === undefined) { // save level level = curlevel; } else if (level !== curlevel) { // detect different levels mixed = true; } } let data = { title: Proxmox.Utils.unknownText, text: Proxmox.Utils.unknownText, iconCls: PVE.Utils.get_health_icon(undefined, true), }; if (level === '') { data = { title: gettext('No Subscription'), iconCls: PVE.Utils.get_health_icon('critical', true), text: gettext('You have at least one node without subscription.'), }; subs.setUserCls('pointer'); } else if (mixed) { data = { title: gettext('Mixed Subscriptions'), iconCls: PVE.Utils.get_health_icon('warning', true), text: gettext('Warning: Your subscription levels are not the same.'), }; subs.setUserCls('pointer'); } else if (level) { data = { title: PVE.Utils.render_support_level(level), iconCls: PVE.Utils.get_health_icon('good', true), text: gettext('Your subscription status is valid.'), }; subs.setUserCls(''); } subs.setData(data); }); me.on('destroy', function() { rstore.stopUpdate(); }); me.mon(sp, 'statechange', function(provider, key, value) { if (key !== 'summarycolumns') { return; } Proxmox.Utils.updateColumns(me); }); rstore.startUpdate(); }, });