]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
3779144ffbef07e4aac9f5ca3f49de187e3faf65
[ceph.git] /
1 import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
2 import { ComponentFixture, TestBed } from '@angular/core/testing';
3 import { ReactiveFormsModule } from '@angular/forms';
4 import { ActivatedRoute } from '@angular/router';
5 import { RouterTestingModule } from '@angular/router/testing';
6
7 import { ToastrModule } from 'ngx-toastr';
8
9 import { ActivatedRouteStub } from '../../../../testing/activated-route-stub';
10 import {
11   configureTestBed,
12   FormHelper,
13   i18nProviders,
14   IscsiHelper
15 } from '../../../../testing/unit-test-helper';
16 import { CdFormGroup } from '../../../shared/forms/cd-form-group';
17 import { SharedModule } from '../../../shared/shared.module';
18 import { IscsiTargetFormComponent } from './iscsi-target-form.component';
19
20 describe('IscsiTargetFormComponent', () => {
21   let component: IscsiTargetFormComponent;
22   let fixture: ComponentFixture<IscsiTargetFormComponent>;
23   let httpTesting: HttpTestingController;
24   let activatedRoute: ActivatedRouteStub;
25
26   const SETTINGS = {
27     config: { minimum_gateways: 2 },
28     disk_default_controls: {
29       'backstore:1': {
30         hw_max_sectors: 1024,
31         osd_op_timeout: 30
32       },
33       'backstore:2': {
34         qfull_timeout: 5
35       }
36     },
37     target_default_controls: {
38       cmdsn_depth: 128,
39       dataout_timeout: 20,
40       immediate_data: true
41     },
42     required_rbd_features: {
43       'backstore:1': 0,
44       'backstore:2': 0
45     },
46     unsupported_rbd_features: {
47       'backstore:1': 0,
48       'backstore:2': 0
49     },
50     backstores: ['backstore:1', 'backstore:2'],
51     default_backstore: 'backstore:1',
52     api_version: 1
53   };
54
55   const LIST_TARGET: any[] = [
56     {
57       target_iqn: 'iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw',
58       portals: [{ host: 'node1', ip: '192.168.100.201' }],
59       disks: [
60         {
61           pool: 'rbd',
62           image: 'disk_1',
63           controls: {},
64           backstore: 'backstore:1',
65           wwn: '64af6678-9694-4367-bacc-f8eb0baa'
66         }
67       ],
68       clients: [
69         {
70           client_iqn: 'iqn.1994-05.com.redhat:rh7-client',
71           luns: [{ pool: 'rbd', image: 'disk_1', lun: 0 }],
72           auth: {
73             user: 'myiscsiusername',
74             password: 'myiscsipassword',
75             mutual_user: null,
76             mutual_password: null
77           }
78         }
79       ],
80       groups: [],
81       target_controls: {}
82     }
83   ];
84
85   const PORTALS = [
86     { name: 'node1', ip_addresses: ['192.168.100.201', '10.0.2.15'] },
87     { name: 'node2', ip_addresses: ['192.168.100.202'] }
88   ];
89
90   const VERSION = {
91     ceph_iscsi_config_version: 11
92   };
93
94   const RBD_LIST: any[] = [
95     { status: 0, value: [], pool_name: 'ganesha' },
96     {
97       status: 0,
98       value: [
99         {
100           size: 96636764160,
101           obj_size: 4194304,
102           num_objs: 23040,
103           order: 22,
104           block_name_prefix: 'rbd_data.148162fb31a8',
105           name: 'disk_1',
106           id: '148162fb31a8',
107           pool_name: 'rbd',
108           features: 61,
109           features_name: ['deep-flatten', 'exclusive-lock', 'fast-diff', 'layering', 'object-map'],
110           timestamp: '2019-01-18T10:44:26Z',
111           stripe_count: 1,
112           stripe_unit: 4194304,
113           data_pool: null,
114           parent: null,
115           snapshots: [],
116           total_disk_usage: 0,
117           disk_usage: 0
118         },
119         {
120           size: 119185342464,
121           obj_size: 4194304,
122           num_objs: 28416,
123           order: 22,
124           block_name_prefix: 'rbd_data.14b292cee6cb',
125           name: 'disk_2',
126           id: '14b292cee6cb',
127           pool_name: 'rbd',
128           features: 61,
129           features_name: ['deep-flatten', 'exclusive-lock', 'fast-diff', 'layering', 'object-map'],
130           timestamp: '2019-01-18T10:45:56Z',
131           stripe_count: 1,
132           stripe_unit: 4194304,
133           data_pool: null,
134           parent: null,
135           snapshots: [],
136           total_disk_usage: 0,
137           disk_usage: 0
138         }
139       ],
140       pool_name: 'rbd'
141     }
142   ];
143
144   configureTestBed(
145     {
146       declarations: [IscsiTargetFormComponent],
147       imports: [
148         SharedModule,
149         ReactiveFormsModule,
150         HttpClientTestingModule,
151         RouterTestingModule,
152         ToastrModule.forRoot()
153       ],
154       providers: [
155         i18nProviders,
156         {
157           provide: ActivatedRoute,
158           useValue: new ActivatedRouteStub({ target_iqn: undefined })
159         }
160       ]
161     },
162     true
163   );
164
165   beforeEach(() => {
166     fixture = TestBed.createComponent(IscsiTargetFormComponent);
167     component = fixture.componentInstance;
168     httpTesting = TestBed.get(HttpTestingController);
169     activatedRoute = TestBed.get(ActivatedRoute);
170     fixture.detectChanges();
171
172     httpTesting.expectOne('ui-api/iscsi/settings').flush(SETTINGS);
173     httpTesting.expectOne('ui-api/iscsi/portals').flush(PORTALS);
174     httpTesting.expectOne('ui-api/iscsi/version').flush(VERSION);
175     httpTesting.expectOne('api/block/image').flush(RBD_LIST);
176     httpTesting.expectOne('api/iscsi/target').flush(LIST_TARGET);
177     httpTesting.verify();
178   });
179
180   it('should create', () => {
181     expect(component).toBeTruthy();
182   });
183
184   it('should only show images not used in other targets', () => {
185     expect(component.imagesAll).toEqual([RBD_LIST[1]['value'][1]]);
186     expect(component.imagesSelections).toEqual([
187       { description: '', name: 'rbd/disk_2', selected: false, enabled: true }
188     ]);
189   });
190
191   it('should generate portals selectOptions', () => {
192     expect(component.portalsSelections).toEqual([
193       { description: '', name: 'node1:192.168.100.201', selected: false, enabled: true },
194       { description: '', name: 'node1:10.0.2.15', selected: false, enabled: true },
195       { description: '', name: 'node2:192.168.100.202', selected: false, enabled: true }
196     ]);
197   });
198
199   it('should create the form', () => {
200     expect(component.targetForm.value).toEqual({
201       disks: [],
202       groups: [],
203       initiators: [],
204       acl_enabled: false,
205       auth: {
206         password: '',
207         user: '',
208         mutual_password: '',
209         mutual_user: ''
210       },
211       portals: [],
212       target_controls: {},
213       target_iqn: component.targetForm.value.target_iqn
214     });
215   });
216
217   it('should prepare data when selecting an image', () => {
218     expect(component.imagesSettings).toEqual({});
219     component.onImageSelection({ option: { name: 'rbd/disk_2', selected: true } });
220     expect(component.imagesSettings).toEqual({
221       'rbd/disk_2': {
222         lun: 0,
223         backstore: 'backstore:1',
224         'backstore:1': {}
225       }
226     });
227   });
228
229   it('should clean data when removing an image', () => {
230     component.onImageSelection({ option: { name: 'rbd/disk_2', selected: true } });
231     component.addGroup();
232     component.groups.controls[0].patchValue({
233       group_id: 'foo',
234       disks: ['rbd/disk_2']
235     });
236
237     expect(component.groups.controls[0].value).toEqual({
238       disks: ['rbd/disk_2'],
239       group_id: 'foo',
240       members: []
241     });
242
243     component.onImageSelection({ option: { name: 'rbd/disk_2', selected: false } });
244
245     expect(component.groups.controls[0].value).toEqual({ disks: [], group_id: 'foo', members: [] });
246     expect(component.imagesSettings).toEqual({
247       'rbd/disk_2': {
248         lun: 0,
249         backstore: 'backstore:1',
250         'backstore:1': {}
251       }
252     });
253   });
254
255   it('should validate authentication', () => {
256     const control = component.targetForm;
257     const formHelper = new FormHelper(control as CdFormGroup);
258     formHelper.expectValid('auth');
259     validateAuth(formHelper);
260   });
261
262   describe('should test initiators', () => {
263     beforeEach(() => {
264       component.onImageSelection({ option: { name: 'rbd/disk_2', selected: true } });
265       component.targetForm.patchValue({ disks: ['rbd/disk_2'], acl_enabled: true });
266       component.addGroup().patchValue({ name: 'group_1' });
267       component.addGroup().patchValue({ name: 'group_2' });
268
269       component.addInitiator();
270       component.initiators.controls[0].patchValue({
271         client_iqn: 'iqn.initiator'
272       });
273       component.updatedInitiatorSelector();
274     });
275
276     it('should prepare data when creating an initiator', () => {
277       expect(component.initiators.controls.length).toBe(1);
278       expect(component.initiators.controls[0].value).toEqual({
279         auth: { mutual_password: '', mutual_user: '', password: '', user: '' },
280         cdIsInGroup: false,
281         client_iqn: 'iqn.initiator',
282         luns: []
283       });
284       expect(component.imagesInitiatorSelections).toEqual([
285         [{ description: '', name: 'rbd/disk_2', selected: false, enabled: true }]
286       ]);
287       expect(component.groupMembersSelections).toEqual([
288         [{ description: '', name: 'iqn.initiator', selected: false, enabled: true }],
289         [{ description: '', name: 'iqn.initiator', selected: false, enabled: true }]
290       ]);
291     });
292
293     it('should update data when changing an initiator name', () => {
294       expect(component.groupMembersSelections).toEqual([
295         [{ description: '', name: 'iqn.initiator', selected: false, enabled: true }],
296         [{ description: '', name: 'iqn.initiator', selected: false, enabled: true }]
297       ]);
298
299       component.initiators.controls[0].patchValue({
300         client_iqn: 'iqn.initiator_new'
301       });
302       component.updatedInitiatorSelector();
303
304       expect(component.groupMembersSelections).toEqual([
305         [{ description: '', name: 'iqn.initiator_new', selected: false, enabled: true }],
306         [{ description: '', name: 'iqn.initiator_new', selected: false, enabled: true }]
307       ]);
308     });
309
310     it('should clean data when removing an initiator', () => {
311       component.groups.controls[0].patchValue({
312         group_id: 'foo',
313         members: ['iqn.initiator']
314       });
315
316       expect(component.groups.controls[0].value).toEqual({
317         disks: [],
318         group_id: 'foo',
319         members: ['iqn.initiator']
320       });
321
322       component.removeInitiator(0);
323
324       expect(component.groups.controls[0].value).toEqual({
325         disks: [],
326         group_id: 'foo',
327         members: []
328       });
329       expect(component.groupMembersSelections).toEqual([[], []]);
330       expect(component.imagesInitiatorSelections).toEqual([]);
331     });
332
333     it('should remove images in the initiator when added in a group', () => {
334       component.initiators.controls[0].patchValue({
335         luns: ['rbd/disk_2']
336       });
337       expect(component.initiators.controls[0].value).toEqual({
338         auth: { mutual_password: '', mutual_user: '', password: '', user: '' },
339         cdIsInGroup: false,
340         client_iqn: 'iqn.initiator',
341         luns: ['rbd/disk_2']
342       });
343
344       component.addGroup();
345       component.groups.controls[0].patchValue({
346         group_id: 'foo',
347         members: ['iqn.initiator']
348       });
349       component.onGroupMemberSelection({
350         option: {
351           name: 'iqn.initiator',
352           selected: true
353         }
354       });
355
356       expect(component.initiators.controls[0].value).toEqual({
357         auth: { mutual_password: '', mutual_user: '', password: '', user: '' },
358         cdIsInGroup: true,
359         client_iqn: 'iqn.initiator',
360         luns: []
361       });
362     });
363
364     it('should disabled the initiator when selected', () => {
365       expect(component.groupMembersSelections).toEqual([
366         [{ description: '', enabled: true, name: 'iqn.initiator', selected: false }],
367         [{ description: '', enabled: true, name: 'iqn.initiator', selected: false }]
368       ]);
369
370       component.groupMembersSelections[0][0].selected = true;
371       component.onGroupMemberSelection({ option: { name: 'iqn.initiator', selected: true } });
372
373       expect(component.groupMembersSelections).toEqual([
374         [{ description: '', enabled: false, name: 'iqn.initiator', selected: true }],
375         [{ description: '', enabled: false, name: 'iqn.initiator', selected: false }]
376       ]);
377     });
378
379     it('should validate authentication', () => {
380       const control = component.initiators.controls[0];
381       const formHelper = new FormHelper(control as CdFormGroup);
382       formHelper.expectValid(control);
383       validateAuth(formHelper);
384     });
385   });
386
387   describe('should submit request', () => {
388     beforeEach(() => {
389       component.onImageSelection({ option: { name: 'rbd/disk_2', selected: true } });
390       component.targetForm.patchValue({ disks: ['rbd/disk_2'], acl_enabled: true });
391       component.portals.setValue(['node1:192.168.100.201', 'node2:192.168.100.202']);
392       component.addInitiator().patchValue({
393         client_iqn: 'iqn.initiator'
394       });
395       component.addGroup().patchValue({
396         group_id: 'foo',
397         members: ['iqn.initiator'],
398         disks: ['rbd/disk_2']
399       });
400     });
401
402     it('should call update', () => {
403       activatedRoute.setParams({ target_iqn: 'iqn.iscsi' });
404       component.isEdit = true;
405       component.target_iqn = 'iqn.iscsi';
406
407       component.submit();
408
409       const req = httpTesting.expectOne('api/iscsi/target/iqn.iscsi');
410       expect(req.request.method).toBe('PUT');
411       expect(req.request.body).toEqual({
412         clients: [
413           {
414             auth: { mutual_password: '', mutual_user: '', password: '', user: '' },
415             client_iqn: 'iqn.initiator',
416             luns: []
417           }
418         ],
419         disks: [
420           {
421             backstore: 'backstore:1',
422             controls: {},
423             image: 'disk_2',
424             pool: 'rbd',
425             lun: 0,
426             wwn: undefined
427           }
428         ],
429         groups: [
430           { disks: [{ image: 'disk_2', pool: 'rbd' }], group_id: 'foo', members: ['iqn.initiator'] }
431         ],
432         new_target_iqn: component.targetForm.value.target_iqn,
433         portals: [
434           { host: 'node1', ip: '192.168.100.201' },
435           { host: 'node2', ip: '192.168.100.202' }
436         ],
437         target_controls: {},
438         target_iqn: component.target_iqn,
439         acl_enabled: true,
440         auth: {
441           password: '',
442           user: '',
443           mutual_password: '',
444           mutual_user: ''
445         }
446       });
447     });
448
449     it('should call create', () => {
450       component.submit();
451
452       const req = httpTesting.expectOne('api/iscsi/target');
453       expect(req.request.method).toBe('POST');
454       expect(req.request.body).toEqual({
455         clients: [
456           {
457             auth: { mutual_password: '', mutual_user: '', password: '', user: '' },
458             client_iqn: 'iqn.initiator',
459             luns: []
460           }
461         ],
462         disks: [
463           {
464             backstore: 'backstore:1',
465             controls: {},
466             image: 'disk_2',
467             pool: 'rbd',
468             lun: 0,
469             wwn: undefined
470           }
471         ],
472         groups: [
473           {
474             disks: [{ image: 'disk_2', pool: 'rbd' }],
475             group_id: 'foo',
476             members: ['iqn.initiator']
477           }
478         ],
479         portals: [
480           { host: 'node1', ip: '192.168.100.201' },
481           { host: 'node2', ip: '192.168.100.202' }
482         ],
483         target_controls: {},
484         target_iqn: component.targetForm.value.target_iqn,
485         acl_enabled: true,
486         auth: {
487           password: '',
488           user: '',
489           mutual_password: '',
490           mutual_user: ''
491         }
492       });
493     });
494
495     it('should call create with acl_enabled disabled', () => {
496       component.targetForm.patchValue({ acl_enabled: false });
497       component.submit();
498
499       const req = httpTesting.expectOne('api/iscsi/target');
500       expect(req.request.method).toBe('POST');
501       expect(req.request.body).toEqual({
502         clients: [],
503         disks: [
504           {
505             backstore: 'backstore:1',
506             controls: {},
507             image: 'disk_2',
508             pool: 'rbd',
509             lun: 0,
510             wwn: undefined
511           }
512         ],
513         groups: [],
514         acl_enabled: false,
515         auth: {
516           password: '',
517           user: '',
518           mutual_password: '',
519           mutual_user: ''
520         },
521         portals: [
522           { host: 'node1', ip: '192.168.100.201' },
523           { host: 'node2', ip: '192.168.100.202' }
524         ],
525         target_controls: {},
526         target_iqn: component.targetForm.value.target_iqn
527       });
528     });
529   });
530
531   function validateAuth(formHelper: FormHelper) {
532     IscsiHelper.validateUser(formHelper, 'auth.user');
533     IscsiHelper.validatePassword(formHelper, 'auth.password');
534     IscsiHelper.validateUser(formHelper, 'auth.mutual_user');
535     IscsiHelper.validatePassword(formHelper, 'auth.mutual_password');
536   }
537 });