diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs index d6e96b62..50c6541e 100644 --- a/src/api2/admin/datastore.rs +++ b/src/api2/admin/datastore.rs @@ -843,6 +843,46 @@ fn upload_backup_log( }.boxed() } +#[api( + input: { + properties: { + store: { + schema: DATASTORE_SCHEMA, + }, + timeframe: { + type: RRDTimeFrameResolution, + }, + cf: { + type: RRDMode, + }, + }, + }, + access: { + permission: &Permission::Privilege(&["datastore", "{store}"], PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP, true), + }, +)] +/// Read datastore stats +fn get_rrd_stats( + store: String, + timeframe: RRDTimeFrameResolution, + cf: RRDMode, + _param: Value, +) -> Result { + + let rrd_dir = format!("datastore/{}", store); + + crate::rrd::extract_data( + &rrd_dir, + &[ + "total", "used", + "read_ios", "read_bytes", "read_ticks", + "write_ios", "write_bytes", "write_ticks", + ], + timeframe, + cf, + ) +} + #[sortable] const DATASTORE_INFO_SUBDIRS: SubdirMap = &[ ( @@ -871,6 +911,11 @@ const DATASTORE_INFO_SUBDIRS: SubdirMap = &[ &Router::new() .post(&API_METHOD_PRUNE) ), + ( + "rrd", + &Router::new() + .get(&API_METHOD_GET_RRD_STATS) + ), ( "snapshots", &Router::new() diff --git a/www/DataStorePanel.js b/www/DataStorePanel.js index 1b157518..85790902 100644 --- a/www/DataStorePanel.js +++ b/www/DataStorePanel.js @@ -22,6 +22,12 @@ Ext.define('PBS.DataStorePanel', { datastore: '{datastore}', }, }, + { + xtype: 'pbsDataStoreStatistic', + cbind: { + datastore: '{datastore}', + }, + }, { itemId: 'acl', xtype: 'pbsACLView', diff --git a/www/DataStoreStatistic.js b/www/DataStoreStatistic.js new file mode 100644 index 00000000..e9f2b6a3 --- /dev/null +++ b/www/DataStoreStatistic.js @@ -0,0 +1,101 @@ +Ext.define('pve-rrd-datastore', { + extend: 'Ext.data.Model', + fields: [ + 'used', + 'total', + 'read_ios', + 'read_bytes', + 'read_ticks', + 'write_ios', + 'write_bytes', + 'write_ticks', + { + name: 'read_delay', calculate: function(data) { + if (data.read_ios === undefined || data.read_ios === 0 || data.read_ticks == undefined) { + return undefined; + } + return (data.read_ticks*1000)/data.read_ios; + } + }, + { + name: 'write_delay', calculate: function(data) { + if (data.write_ios === undefined || data.write_ios === 0 || data.write_ticks == undefined) { + return undefined; + } + return (data.write_ticks*1000)/data.write_ios; + } + }, + { type: 'date', dateFormat: 'timestamp', name: 'time' } + ] +}); + +Ext.define('PBS.DataStoreStatistic', { + extend: 'Ext.panel.Panel', + alias: 'widget.pbsDataStoreStatistic', + + title: gettext('Statistics'), + + scrollable: true, + + initComponent: function() { + var me = this; + + if (!me.datastore) { + throw "no datastore specified"; + } + + me.tbar = [ '->', { xtype: 'proxmoxRRDTypeSelector' } ]; + + var rrdstore = Ext.create('Proxmox.data.RRDStore', { + rrdurl: "/api2/json/admin/datastore/" + me.datastore + "/rrd", + model: 'pve-rrd-datastore' + }); + + me.items = { + xtype: 'container', + itemId: 'itemcontainer', + layout: 'column', + minWidth: 700, + defaults: { + minHeight: 320, + padding: 5, + columnWidth: 1 + }, + items: [ + { + xtype: 'proxmoxRRDChart', + title: gettext('Storage usage'), + fields: ['total','used'], + fieldTitles: [gettext('Total'), gettext('Storage usage')], + store: rrdstore + }, + { + xtype: 'proxmoxRRDChart', + title: gettext('IOPS'), + fields: ['read_ios','write_ios'], + fieldTitles: [gettext('Read IOPS'), gettext('Write IOPS')], + store: rrdstore + }, + { + xtype: 'proxmoxRRDChart', + title: gettext('Delay'), + fields: ['read_delay','write_delay'], + fieldTitles: [gettext('Read delay'), gettext('Write delay')], + store: rrdstore + }, + ] + }; + + me.listeners = { + activate: function() { + rrdstore.startUpdate(); + }, + destroy: function() { + rrdstore.stopUpdate(); + }, + }; + + me.callParent(); + } + +}); diff --git a/www/Makefile b/www/Makefile index 7bbc3441..cf551b31 100644 --- a/www/Makefile +++ b/www/Makefile @@ -18,6 +18,7 @@ JSSRC= \ DataStorePrune.js \ DataStoreConfig.js \ DataStoreStatus.js \ + DataStoreStatistic.js \ DataStoreContent.js \ DataStorePanel.js \ ServerStatus.js \