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',
'db_slots': 5,
'encrypted': True
}
- },
+ ],
'tracking_id': 'test'
}
self._post('/api/osd', data)
@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')
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))
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',
})
export class OsdCreationPreviewModalComponent implements OnInit {
@Input()
- driveGroups: DriveGroups = {};
+ driveGroups: Object[] = [];
@Output()
submitAction = new EventEmitter();
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)
})
+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: {
private formatterService: FormatterService;
constructor() {
+ this.reset();
this.formatterService = new FormatterService();
this.deviceSelectionAttrs = {
'sys_api.vendor': {
}
reset() {
- this.spec = {};
+ this.spec = {
+ service_type: 'osd',
+ service_id: `dashboard-${_.now()}`
+ };
+ }
+
+ setName(name: string) {
+ this.spec['service_id'] = name;
}
setHostPattern(pattern: string) {
+++ /dev/null
-export interface DriveGroups {
- [key: string]: object;
-}
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({
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']);
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();
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';
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' });
}
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
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):
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
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):
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)