]> git.proxmox.com Git - proxmox-backup.git/blob - www/datastore/Summary.js
ui: datastore/DataStoreListSummary: catch and show errors per datastore
[proxmox-backup.git] / www / datastore / Summary.js
1 Ext.define('pve-rrd-datastore', {
2 extend: 'Ext.data.Model',
3 fields: [
4 'used',
5 'total',
6 'read_ios',
7 'read_bytes',
8 'write_ios',
9 'write_bytes',
10 'io_ticks',
11 {
12 name: 'io_delay', calculate: function(data) {
13 let ios = 0;
14 if (data.read_ios !== undefined) { ios += data.read_ios; }
15 if (data.write_ios !== undefined) { ios += data.write_ios; }
16 if (data.io_ticks === undefined) {
17 return undefined;
18 } else if (ios === 0) {
19 return 0;
20 }
21 return (data.io_ticks*1000.0)/ios;
22 },
23 },
24 { type: 'date', dateFormat: 'timestamp', name: 'time' },
25 ],
26 });
27
28 Ext.define('PBS.DataStoreInfo', {
29 extend: 'Ext.panel.Panel',
30 alias: 'widget.pbsDataStoreInfo',
31
32 viewModel: {
33 data: {
34 countstext: '',
35 usage: {},
36 stillbad: 0,
37 mountpoint: "",
38 },
39 },
40
41 controller: {
42 xclass: 'Ext.app.ViewController',
43
44 onLoad: function(store, data, success) {
45 if (!success) return;
46 let me = this;
47 let vm = me.getViewModel();
48
49 let counts = store.getById('counts').data.value;
50 let total = store.getById('total').data.value;
51 let used = store.getById('used').data.value;
52
53 let usage = PBS.Utils.render_size_usage(used, total);
54 vm.set('usagetext', usage);
55 vm.set('usage', used/total);
56
57 let gcstatus = store.getById('gc-status').data.value;
58
59 let dedup = PBS.Utils.calculate_dedup_factor(gcstatus);
60
61 let countstext = function(count) {
62 count = count || {};
63 return `${count.groups || 0} ${gettext('Groups')}, ${count.snapshots || 0} ${gettext('Snapshots')}`;
64 };
65
66 vm.set('ctcount', countstext(counts.ct));
67 vm.set('vmcount', countstext(counts.vm));
68 vm.set('hostcount', countstext(counts.host));
69 vm.set('deduplication', dedup.toFixed(2));
70 vm.set('stillbad', gcstatus['still-bad']);
71 },
72
73 startStore: function() { this.store.startUpdate(); },
74 stopStore: function() { this.store.stopUpdate(); },
75
76 init: function(view) {
77 let me = this;
78 let datastore = encodeURIComponent(view.datastore);
79 me.store = Ext.create('Proxmox.data.ObjectStore', {
80 interval: 5*1000,
81 url: `/api2/json/admin/datastore/${datastore}/status/?verbose=true`,
82 });
83 me.store.on('load', me.onLoad, me);
84 },
85 },
86
87 listeners: {
88 activate: 'startStore',
89 beforedestroy: 'stopStore',
90 deactivate: 'stopStore',
91 },
92
93 defaults: {
94 xtype: 'pmxInfoWidget',
95 },
96
97 bodyPadding: 20,
98
99 items: [
100 {
101 iconCls: 'fa fa-fw fa-hdd-o',
102 title: gettext('Usage'),
103 bind: {
104 data: {
105 usage: '{usage}',
106 text: '{usagetext}',
107 },
108 },
109 },
110 {
111 xtype: 'box',
112 html: `<b>${gettext('Backup Count')}</b>`,
113 padding: '10 0 5 0',
114 },
115 {
116 iconCls: 'fa fa-fw fa-cube',
117 title: gettext('CT'),
118 printBar: false,
119 bind: {
120 data: {
121 text: '{ctcount}',
122 },
123 },
124 },
125 {
126 iconCls: 'fa fa-fw fa-building',
127 title: gettext('Host'),
128 printBar: false,
129 bind: {
130 data: {
131 text: '{hostcount}',
132 },
133 },
134 },
135 {
136 iconCls: 'fa fa-fw fa-desktop',
137 title: gettext('VM'),
138 printBar: false,
139 bind: {
140 data: {
141 text: '{vmcount}',
142 },
143 },
144 },
145 {
146 xtype: 'box',
147 html: `<b>${gettext('Stats from last Garbage Collection')}</b>`,
148 padding: '10 0 5 0',
149 },
150 {
151 iconCls: 'fa fa-fw fa-compress',
152 title: gettext('Deduplication Factor'),
153 printBar: false,
154 bind: {
155 data: {
156 text: '{deduplication}',
157 },
158 },
159 },
160 {
161 iconCls: 'fa critical fa-fw fa-exclamation-triangle',
162 title: gettext('Bad Chunks'),
163 printBar: false,
164 bind: {
165 data: {
166 text: '{stillbad}',
167 },
168 visible: '{stillbad}',
169 },
170 },
171 ],
172 });
173
174 Ext.define('PBS.DataStoreSummary', {
175 extend: 'Ext.panel.Panel',
176 alias: 'widget.pbsDataStoreSummary',
177 mixins: ['Proxmox.Mixin.CBind'],
178
179 layout: 'column',
180 scrollable: true,
181
182 bodyPadding: 5,
183 defaults: {
184 columnWidth: 1,
185 padding: 5,
186 },
187
188 tbar: ['->', { xtype: 'proxmoxRRDTypeSelector' }],
189
190 items: [
191 {
192 xtype: 'container',
193 height: 300,
194 layout: {
195 type: 'hbox',
196 align: 'stretch',
197 },
198 items: [
199 {
200 xtype: 'pbsDataStoreInfo',
201 flex: 1,
202 padding: '0 10 0 0',
203 cbind: {
204 title: '{datastore}',
205 datastore: '{datastore}',
206 },
207 },
208 {
209 xtype: 'pbsDataStoreNotes',
210 flex: 1,
211 cbind: {
212 datastore: '{datastore}',
213 },
214 },
215 ],
216 },
217 {
218 xtype: 'proxmoxRRDChart',
219 title: gettext('Storage usage (bytes)'),
220 fields: ['total', 'used'],
221 fieldTitles: [gettext('Total'), gettext('Storage usage')],
222 },
223 {
224 xtype: 'proxmoxRRDChart',
225 title: gettext('Transfer Rate (bytes/second)'),
226 fields: ['read_bytes', 'write_bytes'],
227 fieldTitles: [gettext('Read'), gettext('Write')],
228 },
229 {
230 xtype: 'proxmoxRRDChart',
231 title: gettext('Input/Output Operations per Second (IOPS)'),
232 fields: ['read_ios', 'write_ios'],
233 fieldTitles: [gettext('Read'), gettext('Write')],
234 },
235 {
236 xtype: 'proxmoxRRDChart',
237 title: gettext('IO Delay (ms)'),
238 fields: ['io_delay'],
239 fieldTitles: [gettext('IO Delay')],
240 },
241 ],
242
243 listeners: {
244 activate: function() { this.rrdstore.startUpdate(); },
245 deactivate: function() { this.rrdstore.stopUpdate(); },
246 destroy: function() { this.rrdstore.stopUpdate(); },
247 },
248
249 initComponent: function() {
250 let me = this;
251
252 me.rrdstore = Ext.create('Proxmox.data.RRDStore', {
253 rrdurl: "/api2/json/admin/datastore/" + me.datastore + "/rrd",
254 model: 'pve-rrd-datastore',
255 });
256
257 me.callParent();
258
259 Proxmox.Utils.API2Request({
260 url: `/config/datastore/${me.datastore}`,
261 waitMsgTarget: me.down('pbsDataStoreInfo'),
262 success: function(response) {
263 let path = Ext.htmlEncode(response.result.data.path);
264 me.down('pbsDataStoreInfo').setTitle(`${me.datastore} (${path})`);
265 me.down('pbsDataStoreNotes').setNotes(response.result.data.comment);
266 },
267 failure: function(response) {
268 // fallback if e.g. we have no permissions to the config
269 let rec = Ext.getStore('pbs-datastore-list')
270 .findRecord('store', me.datastore, 0, false, true, true);
271 if (rec) {
272 me.down('pbsDataStoreNotes').setNotes(rec.data.comment || "");
273 }
274 },
275 });
276
277 me.query('proxmoxRRDChart').forEach((chart) => {
278 chart.setStore(me.rrdstore);
279 });
280
281 me.down('pbsDataStoreInfo').relayEvents(me, ['activate', 'deactivate']);
282 },
283 });