From 4a6116f12c77fd7452295a7ec73ce731c918a93f Mon Sep 17 00:00:00 2001 From: Tiago Melo Date: Wed, 20 Feb 2019 10:58:54 +0000 Subject: [PATCH] mgr/dashboard: Add support for iSCSI's multi backstores (UI) Fixes: https://tracker.ceph.com/issues/38286 Signed-off-by: Tiago Melo --- .../iscsi-target-details.component.spec.ts | 28 ++++-- .../iscsi-target-details.component.ts | 15 ++- .../iscsi-target-form.component.html | 11 ++- .../iscsi-target-form.component.spec.ts | 38 ++++++-- .../iscsi-target-form.component.ts | 22 ++++- ...target-image-settings-modal.component.html | 75 +++++++++------ ...get-image-settings-modal.component.spec.ts | 37 +++++--- ...i-target-image-settings-modal.component.ts | 21 ++--- .../frontend/src/locale/messages.xlf | 92 +++++++++++-------- 9 files changed, 220 insertions(+), 119 deletions(-) diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.spec.ts index b855256394c..339a3c94e33 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.spec.ts @@ -24,20 +24,35 @@ describe('IscsiTargetDetailsComponent', () => { component.settings = { config: { minimum_gateways: 2 }, disk_default_controls: { - hw_max_sectors: 1024, - max_data_area_mb: 8 + 'backstore:1': { + hw_max_sectors: 1024, + max_data_area_mb: 8 + }, + 'backstore:2': { + hw_max_sectors: 1024, + max_data_area_mb: 8 + } }, target_default_controls: { cmdsn_depth: 128, dataout_timeout: 20 - } + }, + backstores: ['backstore:1', 'backstore:2'], + default_backstore: 'backstore:1' }; component.selection = new CdTableSelection(); component.selection.selected = [ { target_iqn: 'iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw', portals: [{ host: 'node1', ip: '192.168.100.201' }], - disks: [{ pool: 'rbd', image: 'disk_1', controls: { hw_max_sectors: 1 } }], + disks: [ + { + pool: 'rbd', + image: 'disk_1', + backstore: 'backstore:1', + controls: { hw_max_sectors: 1 } + } + ], clients: [ { client_iqn: 'iqn.1994-05.com.redhat:rh7-client', @@ -74,7 +89,7 @@ describe('IscsiTargetDetailsComponent', () => { expect(component.data).toBeUndefined(); expect(component.metadata).toEqual({ 'client_iqn.1994-05.com.redhat:rh7-client': { user: 'myiscsiusername' }, - disk_rbd_disk_1: { hw_max_sectors: 1 }, + disk_rbd_disk_1: { backstore: 'backstore:1', controls: { hw_max_sectors: 1 } }, root: { dataout_timeout: 2 } }); expect(component.tree).toEqual({ @@ -153,7 +168,8 @@ describe('IscsiTargetDetailsComponent', () => { component.onNodeSelected(node); expect(component.data).toEqual([ { current: 1, default: 1024, displayName: 'hw_max_sectors' }, - { current: 8, default: 8, displayName: 'max_data_area_mb' } + { current: 8, default: 8, displayName: 'max_data_area_mb' }, + { current: 'backstore:1', default: 'backstore:1', displayName: 'backstore' } ]); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts index aa8878ada27..814dc44b9f5 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts @@ -100,7 +100,11 @@ export class IscsiTargetDetailsComponent implements OnChanges, OnInit { const disks = []; _.forEach(this.selectedItem.disks, (disk) => { const id = 'disk_' + disk.pool + '_' + disk.image; - this.metadata[id] = disk.controls; + this.metadata[id] = { + controls: disk.controls, + backstore: disk.backstore + }; + disks.push({ value: `${disk.pool}/${disk.image}`, id: id @@ -235,13 +239,18 @@ export class IscsiTargetDetailsComponent implements OnChanges, OnInit { }); } else if (e.node.id.toString().startsWith('disk_')) { this.columns[2].isHidden = false; - this.data = _.map(this.settings.disk_default_controls, (value, key) => { + this.data = _.map(this.settings.disk_default_controls[tempData.backstore], (value, key) => { return { displayName: key, default: value, - current: tempData[key] || value + current: !_.isUndefined(tempData.controls[key]) ? tempData.controls[key] : value }; }); + this.data.push({ + displayName: 'backstore', + default: this.settings.default_backstore, + current: tempData.backstore + }); } else { this.columns[2].isHidden = true; this.data = _.map(tempData, (value, key) => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-form/iscsi-target-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-form/iscsi-target-form.component.html index 750173873ae..6c3b3363406 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-form/iscsi-target-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-form/iscsi-target-form.component.html @@ -141,9 +141,14 @@ - This image has modified settings. + + + Backstore: {{ imagesSettings[image].backstore }}.  + + This image has modified settings. + { const SETTINGS = { config: { minimum_gateways: 2 }, disk_default_controls: { - hw_max_sectors: 1024, - osd_op_timeout: 30, - qfull_timeout: 5 + 'backstore:1': { + hw_max_sectors: 1024, + osd_op_timeout: 30 + }, + 'backstore:2': { + qfull_timeout: 5 + } }, target_default_controls: { cmdsn_depth: 128, dataout_timeout: 20, immediate_data: 'Yes' - } + }, + backstores: ['backstore:1', 'backstore:2'], + default_backstore: 'backstore:1' }; const LIST_TARGET = [ @@ -177,7 +183,12 @@ describe('IscsiTargetFormComponent', () => { it('should prepare data when selecting an image', () => { expect(component.imagesSettings).toEqual({}); component.onImageSelection({ option: { name: 'rbd/disk_1', selected: true } }); - expect(component.imagesSettings).toEqual({ 'rbd/disk_1': {} }); + expect(component.imagesSettings).toEqual({ + 'rbd/disk_1': { + backstore: 'backstore:1', + 'backstore:1': {} + } + }); }); it('should clean data when removing an image', () => { @@ -197,7 +208,12 @@ describe('IscsiTargetFormComponent', () => { component.onImageSelection({ option: { name: 'rbd/disk_1', selected: false } }); expect(component.groups.controls[0].value).toEqual({ disks: [], group_id: 'foo', members: [] }); - expect(component.imagesSettings).toEqual({ 'rbd/disk_1': {} }); + expect(component.imagesSettings).toEqual({ + 'rbd/disk_1': { + backstore: 'backstore:1', + 'backstore:1': {} + } + }); }); describe('should test initiators', () => { @@ -332,7 +348,7 @@ describe('IscsiTargetFormComponent', () => { luns: [] } ], - disks: [{ controls: {}, image: 'disk_1', pool: 'rbd' }], + disks: [{ backstore: 'backstore:1', controls: {}, image: 'disk_1', pool: 'rbd' }], groups: [ { disks: [{ image: 'disk_1', pool: 'rbd' }], group_id: 'foo', members: ['iqn.initiator'] } ], @@ -360,9 +376,13 @@ describe('IscsiTargetFormComponent', () => { luns: [] } ], - disks: [{ controls: {}, image: 'disk_1', pool: 'rbd' }], + disks: [{ backstore: 'backstore:1', controls: {}, image: 'disk_1', pool: 'rbd' }], groups: [ - { disks: [{ image: 'disk_1', pool: 'rbd' }], group_id: 'foo', members: ['iqn.initiator'] } + { + disks: [{ image: 'disk_1', pool: 'rbd' }], + group_id: 'foo', + members: ['iqn.initiator'] + } ], portals: [ { host: 'node1', ip: '192.168.100.201' }, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-form/iscsi-target-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-form/iscsi-target-form.component.ts index ee7205018ff..a765d499d53 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-form/iscsi-target-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-form/iscsi-target-form.component.ts @@ -29,6 +29,8 @@ export class IscsiTargetFormComponent implements OnInit { minimum_gateways = 1; target_default_controls: any; disk_default_controls: any; + backstores: string[]; + default_backstore: string; isEdit = false; target_iqn: string; @@ -129,6 +131,8 @@ export class IscsiTargetFormComponent implements OnInit { this.minimum_gateways = data[3].config.minimum_gateways; this.target_default_controls = data[3].target_default_controls; this.disk_default_controls = data[3].disk_default_controls; + this.backstores = data[3].backstores; + this.default_backstore = data[3].default_backstore; this.createForm(); @@ -178,7 +182,11 @@ export class IscsiTargetFormComponent implements OnInit { _.forEach(res.disks, (disk) => { const id = `${disk.pool}/${disk.image}`; disks.push(id); - this.imagesSettings[id] = disk.controls; + this.imagesSettings[id] = { + backstore: disk.backstore + }; + this.imagesSettings[id][disk.backstore] = disk.controls; + this.onImageSelection({ option: { name: id, selected: true } }); }); this.targetForm.patchValue({ @@ -268,7 +276,10 @@ export class IscsiTargetFormComponent implements OnInit { if (option.selected) { if (!this.imagesSettings[option.name]) { - this.imagesSettings[option.name] = {}; + this.imagesSettings[option.name] = { + backstore: this.default_backstore + }; + this.imagesSettings[option.name][this.default_backstore] = {}; } _.forEach(this.imagesInitiatorSelections, (selections, i) => { @@ -507,10 +518,12 @@ export class IscsiTargetFormComponent implements OnInit { // Disks formValue.disks.forEach((disk) => { const imageSplit = disk.split('/'); + const backstore = this.imagesSettings[disk].backstore; request.disks.push({ pool: imageSplit[0], image: imageSplit[1], - controls: this.imagesSettings[disk] + backstore: backstore, + controls: this.imagesSettings[disk][backstore] }); }); @@ -607,7 +620,8 @@ export class IscsiTargetFormComponent implements OnInit { const initialState = { imagesSettings: this.imagesSettings, image: image, - disk_default_controls: this.disk_default_controls + disk_default_controls: this.disk_default_controls, + backstores: this.backstores }; this.modalRef = this.modalService.show(IscsiTargetImageSettingsModalComponent, { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-image-settings-modal/iscsi-target-image-settings-modal.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-image-settings-modal/iscsi-target-image-settings-modal.component.html index 27b13dcb61c..ff1e54d5e09 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-image-settings-modal/iscsi-target-image-settings-modal.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-image-settings-modal/iscsi-target-image-settings-modal.component.html @@ -5,40 +5,55 @@ -
-