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