From 6009d2d6545a4536b66153fdb3aaaf6cdc94f25f Mon Sep 17 00:00:00 2001 From: Kiefer Chang Date: Tue, 17 Mar 2020 16:07:45 +0800 Subject: [PATCH] mgr/dashboard: adapt create_osds interface change Support the parameter change of create_osds since https://github.com/ceph/ceph/pull/33922. Fixes: https://tracker.ceph.com/issues/44632 Signed-off-by: Kiefer Chang --- qa/tasks/mgr/dashboard/test_osd.py | 8 ++-- src/pybind/mgr/dashboard/controllers/osd.py | 7 +-- .../osd-creation-preview-modal.component.ts | 5 +- .../cluster/osd/osd-form/drive-group.model.ts | 15 ++++-- .../osd/osd-form/drive-groups.interface.ts | 3 -- .../osd/osd-form/osd-form.component.ts | 7 +-- .../src/app/shared/api/osd.service.spec.ts | 12 +++-- .../src/app/shared/api/osd.service.ts | 5 +- .../mgr/dashboard/services/orchestrator.py | 18 ++++--- src/pybind/mgr/dashboard/tests/test_osd.py | 48 +++++++++++++------ 10 files changed, 79 insertions(+), 49 deletions(-) delete mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/drive-groups.interface.ts diff --git a/qa/tasks/mgr/dashboard/test_osd.py b/qa/tasks/mgr/dashboard/test_osd.py index 0c17be4f6a104..1bd75e4b09472 100644 --- a/qa/tasks/mgr/dashboard/test_osd.py +++ b/qa/tasks/mgr/dashboard/test_osd.py @@ -111,8 +111,10 @@ class OsdTest(DashboardTestCase): def test_create_with_drive_group(self): data = { 'method': 'drive_groups', - 'data': { - 'test': { + 'data': [ + { + 'service_type': 'osd', + 'service_id': 'test', 'host_pattern': '*', 'data_devices': { 'vendor': 'abc', @@ -136,7 +138,7 @@ class OsdTest(DashboardTestCase): 'db_slots': 5, 'encrypted': True } - }, + ], 'tracking_id': 'test' } self._post('/api/osd', data) diff --git a/src/pybind/mgr/dashboard/controllers/osd.py b/src/pybind/mgr/dashboard/controllers/osd.py index f65af062c5186..4327f2e92fa9d 100644 --- a/src/pybind/mgr/dashboard/controllers/osd.py +++ b/src/pybind/mgr/dashboard/controllers/osd.py @@ -249,11 +249,12 @@ class Osd(RESTController): @raise_if_no_orchestrator @handle_orchestrator_error('osd') - def _create_with_drive_groups(self, drive_group): + def _create_with_drive_groups(self, drive_groups): """Create OSDs with DriveGroups.""" orch = OrchClient.instance() try: - orch.osds.create(DriveGroupSpec.from_json(drive_group)) + dg_specs = [DriveGroupSpec.from_json(dg) for dg in drive_groups] + orch.osds.create(dg_specs) except (ValueError, TypeError, DriveGroupValidationError) as e: raise DashboardException(e, component='osd') @@ -262,7 +263,7 @@ class Osd(RESTController): def create(self, method, data, tracking_id): # pylint: disable=W0622 if method == 'bare': return self._create_bare(data) - if method == 'drive_group': + if method == 'drive_groups': return self._create_with_drive_groups(data) raise DashboardException( component='osd', http_status_code=400, msg='Unknown method: {}'.format(method)) diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-creation-preview-modal/osd-creation-preview-modal.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-creation-preview-modal/osd-creation-preview-modal.component.ts index ae35da6d6b540..eb8ded17349bb 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-creation-preview-modal/osd-creation-preview-modal.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-creation-preview-modal/osd-creation-preview-modal.component.ts @@ -9,7 +9,6 @@ import { CdFormBuilder } from '../../../../shared/forms/cd-form-builder'; import { CdFormGroup } from '../../../../shared/forms/cd-form-group'; import { FinishedTask } from '../../../../shared/models/finished-task'; import { TaskWrapperService } from '../../../../shared/services/task-wrapper.service'; -import { DriveGroups } from '../osd-form/drive-groups.interface'; @Component({ selector: 'cd-osd-creation-preview-modal', @@ -18,7 +17,7 @@ import { DriveGroups } from '../osd-form/drive-groups.interface'; }) export class OsdCreationPreviewModalComponent implements OnInit { @Input() - driveGroups: DriveGroups = {}; + driveGroups: Object[] = []; @Output() submitAction = new EventEmitter(); @@ -47,7 +46,7 @@ export class OsdCreationPreviewModalComponent implements OnInit { this.taskWrapper .wrapTaskAroundCall({ task: new FinishedTask('osd/' + URLVerbs.CREATE, { - tracking_id: _.join(_.keys(this.driveGroups), ', ') + tracking_id: _.join(_.map(this.driveGroups, 'service_id'), ', ') }), call: this.osdService.create(this.driveGroups) }) diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/drive-group.model.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/drive-group.model.ts index 976a2a59c7fac..dd5d3b0b13ed8 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/drive-group.model.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/drive-group.model.ts @@ -1,9 +1,10 @@ +import * as _ from 'lodash'; + import { CdTableColumnFiltersChange } from '../../../../shared/models/cd-table-column-filters-change'; import { FormatterService } from '../../../../shared/services/formatter.service'; export class DriveGroup { - // DriveGroupSpec object. - spec = {}; + spec: Object; // Map from filter column prop to device selection attribute name private deviceSelectionAttrs: { @@ -16,6 +17,7 @@ export class DriveGroup { private formatterService: FormatterService; constructor() { + this.reset(); this.formatterService = new FormatterService(); this.deviceSelectionAttrs = { 'sys_api.vendor': { @@ -45,7 +47,14 @@ export class DriveGroup { } reset() { - this.spec = {}; + this.spec = { + service_type: 'osd', + service_id: `dashboard-${_.now()}` + }; + } + + setName(name: string) { + this.spec['service_id'] = name; } setHostPattern(pattern: string) { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/drive-groups.interface.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/drive-groups.interface.ts deleted file mode 100644 index bccb7c9eed5d5..0000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/drive-groups.interface.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface DriveGroups { - [key: string]: object; -} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.ts index 5defdbeecc5ba..e6be9f0ea5c9f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.ts @@ -19,7 +19,6 @@ import { DevicesSelectionChangeEvent } from '../osd-devices-selection-groups/dev import { DevicesSelectionClearEvent } from '../osd-devices-selection-groups/devices-selection-clear-event.interface'; import { OsdDevicesSelectionGroupsComponent } from '../osd-devices-selection-groups/osd-devices-selection-groups.component'; import { DriveGroup } from './drive-group.model'; -import { DriveGroups } from './drive-groups.interface'; import { OsdFeature } from './osd-feature.interface'; @Component({ @@ -209,11 +208,9 @@ export class OsdFormComponent implements OnInit { submit() { // use user name and timestamp for drive group name const user = this.authStorageService.getUsername(); - const driveGroups: DriveGroups = { - [`dashboard-${user}-${_.now()}`]: this.driveGroup.spec - }; + this.driveGroup.setName(`dashboard-${user}-${_.now()}`); const modalRef = this.bsModalService.show(OsdCreationPreviewModalComponent, { - initialState: { driveGroups: driveGroups } + initialState: { driveGroups: [this.driveGroup.spec] } }); modalRef.content.submitAction.subscribe(() => { this.router.navigate(['/osd']); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.spec.ts index 3f1abd4ace4f7..44db3f5c85a20 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.spec.ts @@ -29,20 +29,24 @@ describe('OsdService', () => { it('should call create', () => { const post_data = { method: 'drive_groups', - data: { - all_hdd: { + data: [ + { + service_name: 'osd', + service_id: 'all_hdd', host_pattern: '*', data_devices: { rotational: true } }, - host1_ssd: { + { + service_name: 'osd', + service_id: 'host1_ssd', host_pattern: 'host1', data_devices: { rotational: false } } - }, + ], tracking_id: 'all_hdd, host1_ssd' }; service.create(post_data.data).subscribe(); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.ts index 30cdcd1ea3697..8d6369a216233 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.ts @@ -5,7 +5,6 @@ import { I18n } from '@ngx-translate/i18n-polyfill'; import { map } from 'rxjs/operators'; import * as _ from 'lodash'; -import { DriveGroups } from '../../ceph/cluster/osd/osd-form/drive-groups.interface'; import { CdDevice } from '../models/devices'; import { SmartDataResponseV1 } from '../models/smart'; import { DeviceService } from '../services/device.service'; @@ -64,11 +63,11 @@ export class OsdService { constructor(private http: HttpClient, private i18n: I18n, private deviceService: DeviceService) {} - create(driveGroups: DriveGroups) { + create(driveGroups: Object[]) { const request = { method: 'drive_groups', data: driveGroups, - tracking_id: _.join(_.keys(driveGroups), ', ') + tracking_id: _.join(_.map(driveGroups, 'service_id'), ', ') }; return this.http.post(this.path, request, { observe: 'response' }); } diff --git a/src/pybind/mgr/dashboard/services/orchestrator.py b/src/pybind/mgr/dashboard/services/orchestrator.py index a1fa708432475..400055c8620c1 100644 --- a/src/pybind/mgr/dashboard/services/orchestrator.py +++ b/src/pybind/mgr/dashboard/services/orchestrator.py @@ -36,11 +36,15 @@ class OrchestratorAPI(OrchestratorClientMixin): def wait_api_result(method): @wraps(method) def inner(self, *args, **kwargs): - completion = method(self, *args, **kwargs) - self.api.orchestrator_wait([completion]) - raise_if_exception(completion) - return completion.result - + completions = method(self, *args, **kwargs) + if not isinstance(completions, list): + completions = [completions] + self.api.orchestrator_wait(completions) + for compl in completions: + raise_if_exception(compl) + if len(completions) == 1: + return completions[0].result + return [compl.result for compl in completions] return inner @@ -105,8 +109,8 @@ class ServiceManager(ResourceManager): class OsdManager(ResourceManager): @wait_api_result - def create(self, drive_group): - return self.api.create_osds(drive_group) + def create(self, drive_group_specs): + return self.api.apply_drivegroups(drive_group_specs) @wait_api_result def remove(self, osd_ids): diff --git a/src/pybind/mgr/dashboard/tests/test_osd.py b/src/pybind/mgr/dashboard/tests/test_osd.py index 549d8b477e74c..aeb32ed576452 100644 --- a/src/pybind/mgr/dashboard/tests/test_osd.py +++ b/src/pybind/mgr/dashboard/tests/test_osd.py @@ -273,11 +273,20 @@ class OsdTest(ControllerTestCase): instance.return_value = fake_client # Valid DriveGroup - data = {'method': 'drive_group', - 'data': {'service_type': 'osd', 'service_id': 'all_hdd', - 'data_devices': {'rotational': True}, - 'host_pattern': '*'}, - 'tracking_id': 'all_hdd, b_ssd'} + data = { + 'method': 'drive_groups', + 'data': [ + { + 'service_type': 'osd', + 'service_id': 'all_hdd', + 'data_devices': { + 'rotational': True + }, + 'host_pattern': '*', + } + ], + 'tracking_id': 'all_hdd, b_ssd' + } # Without orchestrator service fake_client.available.return_value = False @@ -288,11 +297,11 @@ class OsdTest(ControllerTestCase): fake_client.available.return_value = True self._task_post('/api/osd', data) self.assertStatus(201) - fake_client.osds.create.assert_called_with( - DriveGroupSpec(placement=PlacementSpec(host_pattern='*'), - service_id='all_hdd', - service_type='osd', - data_devices=DeviceSelection(rotational=True))) + dg_specs = [DriveGroupSpec(placement=PlacementSpec(host_pattern='*'), + service_id='all_hdd', + service_type='osd', + data_devices=DeviceSelection(rotational=True))] + fake_client.osds.create.assert_called_with(dg_specs) @mock.patch('dashboard.controllers.orchestrator.OrchClient.instance') def test_osd_create_with_invalid_drive_groups(self, instance): @@ -301,10 +310,19 @@ class OsdTest(ControllerTestCase): instance.return_value = fake_client # Invalid DriveGroup - data = {'method': 'drive_group', - 'data': {'service_type': 'osd', 'service_id': 'invalid_dg', - 'data_devices': {'rotational': True}, - 'host_pattern_wrong': 'unknown'}, - 'tracking_id': 'all_hdd, b_ssd'} + data = { + 'method': 'drive_groups', + 'data': [ + { + 'service_type': 'osd', + 'service_id': 'invalid_dg', + 'data_devices': { + 'rotational': True + }, + 'host_pattern_wrong': 'unknown', + } + ], + 'tracking_id': 'all_hdd, b_ssd' + } self._task_post('/api/osd', data) self.assertStatus(400) -- 2.39.5