From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 951006CB7A for ; Tue, 2 Feb 2021 14:00:42 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 92F8EEEA2 for ; Tue, 2 Feb 2021 14:00:42 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 399C7EE7D for ; Tue, 2 Feb 2021 14:00:41 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 0198C4617C for ; Tue, 2 Feb 2021 14:00:41 +0100 (CET) From: Dominik Csapak To: pbs-devel@lists.proxmox.com Date: Tue, 2 Feb 2021 14:00:36 +0100 Message-Id: <20210202130039.6564-3-d.csapak@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210202130039.6564-1-d.csapak@proxmox.com> References: <20210202130039.6564-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.238 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pbs-devel] [PATCH proxmox-backup 3/6] ui: tape: rework BackupOverview X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 02 Feb 2021 13:00:42 -0000 instead of grouping by tape (which is rarely interesting), group by pool -> group -> id -> mediaset this way a user looking for a backup of specific vm can do just that we may want to have an additional view here were we list all snapshots included in the selected media-set ? Signed-off-by: Dominik Csapak --- www/Utils.js | 9 ++ www/tape/BackupOverview.js | 169 +++++++++++++++++++++---------------- 2 files changed, 103 insertions(+), 75 deletions(-) diff --git a/www/Utils.js b/www/Utils.js index acfc4556..3bb709e4 100644 --- a/www/Utils.js +++ b/www/Utils.js @@ -260,6 +260,15 @@ Ext.define('PBS.Utils', { return dedup; }, + parse_snapshot_id: function(snapshot) { + if (!snapshot) { + return [undefined, undefined, undefined]; + } + let [_match, type, group, id] = /^([^/]+)\/([^/]+)\/(.+)$/.exec(snapshot); + + return [type, group, id]; + }, + get_type_icon_cls: function(btype) { var cls = ''; if (btype.startsWith('vm')) { diff --git a/www/tape/BackupOverview.js b/www/tape/BackupOverview.js index 4743dcc0..f850c29a 100644 --- a/www/tape/BackupOverview.js +++ b/www/tape/BackupOverview.js @@ -16,50 +16,98 @@ Ext.define('PBS.TapeManagement.BackupOverview', { }).show(); }, - reload: async function() { + restore: function(button, record) { let me = this; let view = me.getView(); + let selection = view.getSelection(); + if (!selection || selection.length < 1) { + return; + } - Proxmox.Utils.setErrorMask(view, true); + let mediaset = selection[0].data.text; + let uuid = selection[0].data.uuid; + Ext.create('PBS.TapeManagement.TapeRestoreWindow', { + mediaset, + uuid, + listeners: { + destroy: function() { + me.reload(); + }, + }, + }).show(); + }, - try { - let list_response = await PBS.Async.api2({ - url: '/api2/extjs/tape/media/list', - }); - let list = list_response.result.data.sort( - (a, b) => a['label-text'].localeCompare(b['label-text']), - ); + loadContent: async function() { + let content_response = await PBS.Async.api2({ + url: '/api2/extjs/tape/media/content', + }); + let data = {}; + + for (const entry of content_response.result.data) { + let pool = entry.pool; + let [type, group_id, id] = PBS.Utils.parse_snapshot_id(entry.snapshot); + let group = `${type}/${group_id}`; + let media_set = entry['media-set-name']; + let uuid = entry['media-set-uuid']; + let ctime = entry['media-set-ctime']; + if (data[pool] === undefined) { + data[pool] = {}; + } - let content = {}; + if (data[pool][group] === undefined) { + data[pool][group] = {}; + } - let content_response = await PBS.Async.api2({ - url: '/api2/extjs/tape/media/content', + if (data[pool][group][id] === undefined) { + data[pool][group][id] = []; + } + data[pool][group][id].push({ + text: media_set, + uuid, + ctime, + leaf: true, }); + } - let content_list = content_response.result.data.sort( - (a, b) => a.snapshot.localeCompare(b.snapshot), - ); - - for (let entry of content_list) { - let tape = entry['label-text']; - entry['label-text'] = entry.snapshot; - entry.leaf = true; - if (content[tape] === undefined) { - content[tape] = [entry]; - } else { - content[tape].push(entry); + let list = []; + + for (const [pool, groups] of Object.entries(data)) { + let pool_entry = { + text: pool, + leaf: false, + children: [], + }; + for (const [group, ids] of Object.entries(groups)) { + let group_entry = { + text: group, + iconCls: "fa " + PBS.Utils.get_type_icon_cls(group), + leaf: false, + children: [], + }; + for (const [id, media_sets] of Object.entries(ids)) { + let id_entry = { + text: `${group}/${id}`, + leaf: false, + children: media_sets, + }; + group_entry.children.push(id_entry); } + pool_entry.children.push(group_entry); } + list.push(pool_entry); + } - for (let child of list) { - let tape = child['label-text']; - if (content[tape]) { - child.children = content[tape]; - child.leaf = false; - } else { - child.leaf = true; - } - } + return list; + }, + + reload: async function() { + let me = this; + let view = me.getView(); + + Proxmox.Utils.setErrorMask(view, true); + + try { + let list = await me.loadContent(); view.setRootNode({ expanded: true, @@ -78,8 +126,14 @@ Ext.define('PBS.TapeManagement.BackupOverview', { }, store: { - sorters: 'label-text', data: [], + sorters: function(a, b) { + if (a.data.leaf && b.data.leaf) { + return a.data.ctime - b.data.ctime; + } else { + return a.data.text.localeCompare(b.data.text); + } + }, }, rootVisible: false, @@ -99,50 +153,15 @@ Ext.define('PBS.TapeManagement.BackupOverview', { columns: [ { xtype: 'treecolumn', - text: gettext('Tape/Backup'), - dataIndex: 'label-text', + text: gettext('Pool/Group/Snapshot/Media Set'), + dataIndex: 'text', + sortable: false, flex: 3, }, { - text: gettext('Location'), - dataIndex: 'location', - flex: 1, - renderer: function(value) { - if (!value) { - return ""; - } - let result; - if ((result = /^online-(.+)$/.exec(value)) !== null) { - return Ext.htmlEncode(result[1]); - } - - return value; - }, - }, - { - text: gettext('Status'), - dataIndex: 'status', - flex: 1, - }, - { - text: gettext('Media Set'), - dataIndex: 'media-set-name', - flex: 2, - }, - { - text: gettext('Pool'), - dataIndex: 'pool', - flex: 1, - }, - { - text: gettext('Sequence Nr.'), - dataIndex: 'seq-nr', - flex: 0.5, - }, - { - text: gettext('Backup Time'), - dataIndex: 'backup-time', - renderer: (time) => time !== undefined ? new Date(time*1000) : "", + text: gettext('Media Set UUID'), + dataIndex: 'uuid', + sortable: false, flex: 1, }, ], -- 2.20.1