data: { breadcrumbs: 'Targets' },
children: [
{ path: '', component: IscsiTargetListComponent },
- { path: 'add', component: IscsiTargetFormComponent, data: { breadcrumbs: 'Add' } }
+ { path: 'add', component: IscsiTargetFormComponent, data: { breadcrumbs: 'Add' } },
+ {
+ path: 'edit/:target_iqn',
+ component: IscsiTargetFormComponent,
+ data: { breadcrumbs: 'Edit' }
+ }
]
}
]
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"
- i18n>Create target</h3>
+ i18n>{isEdit, select, 1 {Edit} other {Add}} target</h3>
</div>
<div class="panel-body">
<div class="col-sm-9"
formArrayName="initiators">
<div class="panel panel-default"
- *ngFor="let initiator of initiators.controls; let i = index"
- [formGroupName]="i">
+ *ngFor="let initiator of initiators.controls; let ii = index"
+ [formGroupName]="ii">
<div class="panel-heading">
<ng-container i18n>Initiator</ng-container>: {{ initiator.getValue('client_iqn') }}
<button type="button"
class="close"
- (click)="removeInitiator(i)">
+ (click)="removeInitiator(ii)">
<i class="fa fa-remove fa-fw"></i>
</button>
</div>
for="luns"
i18n>Images</label>
<div class="col-sm-9">
- <ng-container *ngFor="let image of initiator.getValue('luns'); let i = index">
+ <ng-container *ngFor="let image of initiator.getValue('luns'); let li = index">
<div class="input-group cd-mb">
<input class="form-control"
type="text"
<span class="input-group-btn">
<button class="btn btn-default"
type="button"
- (click)="initiator.getValue('luns').splice(i, 1)">
+ (click)="removeInitiatorImage(initiator, li, ii, image)">
<i class="fa fa-remove fa-fw"
aria-hidden="true"></i>
</button>
*ngIf="!initiator.getValue('cdIsInGroup')">
<div class="col-md-12">
<cd-select [data]="initiator.getValue('luns')"
- [options]="imagesInitiatorSelections"
+ [options]="imagesInitiatorSelections[ii]"
[messages]="messages.initiatorImage"
elemClass="btn btn-default pull-right">
<i class="fa fa-fw fa-plus"></i>
*ngIf="initiators.controls.length === 0"
i18n>No items added.</span>
- <button (click)="addInitiator()"
+ <button (click)="addInitiator(); false"
class="btn btn-default pull-right">
<i class="fa fa-fw fa-plus"></i>
<ng-container i18n>Add initiator</ng-container>
<div class="col-sm-9"
formArrayName="groups">
<div class="panel panel-default"
- *ngFor="let group of groups.controls; let i = index"
- [formGroupName]="i">
+ *ngFor="let group of groups.controls; let gi = index"
+ [formGroupName]="gi">
<div class="panel-heading">
<ng-container i18n>Group</ng-container>: {{ group.getValue('group_id') }}
<button type="button"
class="close"
- (click)="groups.removeAt(i)">
+ (click)="groups.removeAt(gi)">
<i class="fa fa-remove fa-fw"></i>
</button>
</div>
<span class="input-group-btn">
<button class="btn btn-default"
type="button"
- (click)="removeGroupInitiator(group, i)">
+ (click)="removeGroupInitiator(group, i, gi)">
<i class="fa fa-remove fa-fw"
aria-hidden="true"></i>
</button>
<div class="row">
<div class="col-md-12">
<cd-select [data]="group.getValue('members')"
- [options]="groupMembersSelections"
+ [options]="groupMembersSelections[gi]"
[messages]="messages.groupInitiator"
(selection)="onGroupMemberSelection($event)"
elemClass="btn btn-default pull-right">
<span class="input-group-btn">
<button class="btn btn-default"
type="button"
- (click)="group.getValue('disks').splice(i, 1)">
+ (click)="removeGroupDisk(group, i, gi)">
<i class="fa fa-remove fa-fw"
aria-hidden="true"></i>
</button>
<div class="row">
<div class="col-md-12">
<cd-select [data]="group.getValue('disks')"
- [options]="groupDiskSelections"
+ [options]="groupDiskSelections[gi]"
[messages]="messages.initiatorImage"
elemClass="btn btn-default pull-right">
<i class="fa fa-fw fa-plus"></i>
*ngIf="groups.controls.length === 0"
i18n>No items added.</span>
- <button (click)="addGroup()"
+ <button (click)="addGroup(); false"
class="btn btn-default pull-right">
<i class="fa fa-fw fa-plus"></i>
<ng-container i18n>Add group</ng-container>
<cd-submit-button [form]="formDir"
type="button"
(submitAction)="submit()"
- i18n>Create target</cd-submit-button>
+ i18n>Submit</cd-submit-button>
<button type="button"
class="btn btn-sm btn-default"
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
+import { ActivatedRoute } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { ToastModule } from 'ng2-toastr';
+import { ActivatedRouteStub } from '../../../../testing/activated-route-stub';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
import { SharedModule } from '../../../shared/shared.module';
import { IscsiTargetFormComponent } from './iscsi-target-form.component';
let component: IscsiTargetFormComponent;
let fixture: ComponentFixture<IscsiTargetFormComponent>;
let httpTesting: HttpTestingController;
+ let activatedRoute: ActivatedRouteStub;
const SETTINGS = {
config: { minimum_gateways: 2 },
RouterTestingModule,
ToastModule.forRoot()
],
- providers: [i18nProviders]
+ providers: [
+ i18nProviders,
+ {
+ provide: ActivatedRoute,
+ useValue: new ActivatedRouteStub({ target_iqn: undefined })
+ }
+ ]
},
true
);
fixture = TestBed.createComponent(IscsiTargetFormComponent);
component = fixture.componentInstance;
httpTesting = TestBed.get(HttpTestingController);
+ activatedRoute = TestBed.get(ActivatedRoute);
fixture.detectChanges();
httpTesting.expectOne('ui-api/iscsi/settings').flush(SETTINGS);
});
it('should prepare data when selecting an image', () => {
- expect(component.imagesInitiatorSelections).toEqual([]);
- expect(component.groupDiskSelections).toEqual([]);
expect(component.imagesSettings).toEqual({});
-
component.onImageSelection({ option: { name: 'rbd/disk_1', selected: true } });
-
- expect(component.imagesInitiatorSelections).toEqual([
- { description: '', name: 'rbd/disk_1', selected: false }
- ]);
- expect(component.groupDiskSelections).toEqual([
- { description: '', name: 'rbd/disk_1', selected: false }
- ]);
expect(component.imagesSettings).toEqual({ 'rbd/disk_1': {} });
});
component.onImageSelection({ option: { name: 'rbd/disk_1', selected: false } });
expect(component.groups.controls[0].value).toEqual({ disks: [], group_id: 'foo', members: [] });
- expect(component.imagesInitiatorSelections).toEqual([]);
- expect(component.groupDiskSelections).toEqual([]);
expect(component.imagesSettings).toEqual({ 'rbd/disk_1': {} });
});
describe('should test initiators', () => {
beforeEach(() => {
+ component.targetForm.patchValue({ disks: ['rbd/disk_1'] });
+ component.addGroup().patchValue({ name: 'group_1' });
+ component.onImageSelection({ option: { name: 'rbd/disk_1', selected: true } });
+
component.addInitiator();
component.initiators.controls[0].patchValue({
client_iqn: 'iqn.initiator'
client_iqn: 'iqn.initiator',
luns: []
});
+ expect(component.imagesInitiatorSelections).toEqual([
+ [{ description: '', name: 'rbd/disk_1', selected: false }]
+ ]);
expect(component.groupMembersSelections).toEqual([
- { description: '', name: 'iqn.initiator', selected: false }
+ [{ description: '', name: 'iqn.initiator', selected: false }]
]);
});
it('should update data when changing an initiator name', () => {
expect(component.groupMembersSelections).toEqual([
- { description: '', name: 'iqn.initiator', selected: false }
+ [{ description: '', name: 'iqn.initiator', selected: false }]
]);
component.initiators.controls[0].patchValue({
component.updatedInitiatorSelector();
expect(component.groupMembersSelections).toEqual([
- { description: '', name: 'iqn.initiator_new', selected: false }
+ [{ description: '', name: 'iqn.initiator_new', selected: false }]
]);
});
it('should clean data when removing an initiator', () => {
- component.addGroup();
component.groups.controls[0].patchValue({
group_id: 'foo',
members: ['iqn.initiator']
group_id: 'foo',
members: []
});
- expect(component.groupMembersSelections).toEqual([]);
+ expect(component.groupMembersSelections).toEqual([[]]);
+ expect(component.imagesInitiatorSelections).toEqual([]);
});
it('should remove images in the initiator when added in a group', () => {
});
});
- it('should generate the request data', () => {
- component.onImageSelection({ option: { name: 'rbd/disk_1', selected: true } });
- component.portals.setValue(['node1:192.168.100.201', 'node2:192.168.100.202']);
- component.addInitiator();
- component.initiators.controls[0].patchValue({
- client_iqn: 'iqn.initiator'
- });
- component.addGroup();
- component.groups.controls[0].patchValue({
- group_id: 'foo',
- members: ['iqn.initiator'],
- disks: ['rbd/disk_1']
+ describe('should submit request', () => {
+ beforeEach(() => {
+ component.targetForm.patchValue({ disks: ['rbd/disk_1'] });
+ component.onImageSelection({ option: { name: 'rbd/disk_1', selected: true } });
+ component.portals.setValue(['node1:192.168.100.201', 'node2:192.168.100.202']);
+ component.addInitiator().patchValue({
+ client_iqn: 'iqn.initiator'
+ });
+ component.addGroup().patchValue({
+ group_id: 'foo',
+ members: ['iqn.initiator'],
+ disks: ['rbd/disk_1']
+ });
});
- component.submit();
+ it('should call update', () => {
+ activatedRoute.setParams({ target_iqn: 'iqn.iscsi' });
+ component.isEdit = true;
+ component.target_iqn = 'iqn.iscsi';
+
+ component.submit();
+
+ const req = httpTesting.expectOne('api/iscsi/target/iqn.iscsi');
+ expect(req.request.method).toBe('PUT');
+ expect(req.request.body).toEqual({
+ clients: [
+ {
+ auth: { mutual_password: null, mutual_user: null, password: null, user: null },
+ cdIsInGroup: false,
+ client_iqn: 'iqn.initiator',
+ luns: []
+ }
+ ],
+ disks: [{ controls: {}, image: 'disk_1', pool: 'rbd' }],
+ groups: [
+ { disks: [{ image: 'disk_1', pool: 'rbd' }], group_id: 'foo', members: ['iqn.initiator'] }
+ ],
+ new_target_iqn: component.targetForm.value.target_iqn,
+ portals: [
+ { host: 'node1', ip: '192.168.100.201' },
+ { host: 'node2', ip: '192.168.100.202' }
+ ],
+ target_controls: {},
+ target_iqn: component.target_iqn
+ });
+ });
- const req = httpTesting.expectOne('api/iscsi/target');
- expect(req.request.method).toBe('POST');
- expect(req.request.body).toEqual({
- clients: [
- {
- auth: { mutual_password: null, mutual_user: null, password: null, user: null },
- cdIsInGroup: false,
- client_iqn: 'iqn.initiator',
- luns: []
- }
- ],
- disks: [],
- groups: [
- { disks: [{ image: 'disk_1', pool: 'rbd' }], group_id: 'foo', members: ['iqn.initiator'] }
- ],
- portals: [{ host: 'node1', ip: '192.168.100.201' }, { host: 'node2', ip: '192.168.100.202' }],
- target_controls: {},
- target_iqn: component.targetForm.value.target_iqn
+ it('should call create', () => {
+ component.submit();
+
+ const req = httpTesting.expectOne('api/iscsi/target');
+ expect(req.request.method).toBe('POST');
+ expect(req.request.body).toEqual({
+ clients: [
+ {
+ auth: { mutual_password: null, mutual_user: null, password: null, user: null },
+ cdIsInGroup: false,
+ client_iqn: 'iqn.initiator',
+ luns: []
+ }
+ ],
+ disks: [{ controls: {}, image: 'disk_1', pool: 'rbd' }],
+ groups: [
+ { disks: [{ image: 'disk_1', pool: 'rbd' }], group_id: 'foo', members: ['iqn.initiator'] }
+ ],
+ portals: [
+ { host: 'node1', ip: '192.168.100.201' },
+ { host: 'node2', ip: '192.168.100.202' }
+ ],
+ target_controls: {},
+ target_iqn: component.targetForm.value.target_iqn
+ });
});
});
});
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
import { FormArray, FormControl, Validators } from '@angular/forms';
-import { Router } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
import { I18n } from '@ngx-translate/i18n-polyfill';
import * as _ from 'lodash';
templateUrl: './iscsi-target-form.component.html',
styleUrls: ['./iscsi-target-form.component.scss']
})
-export class IscsiTargetFormComponent {
+export class IscsiTargetFormComponent implements OnInit {
targetForm: CdFormGroup;
modalRef: BsModalRef;
minimum_gateways = 1;
target_default_controls: any;
disk_default_controls: any;
+ isEdit = false;
+ target_iqn: string;
+
imagesAll: any[];
imagesSelections: SelectOption[];
portalsSelections: SelectOption[] = [];
- imagesInitiatorSelections: SelectOption[] = [];
- groupDiskSelections: SelectOption[] = [];
- groupMembersSelections: SelectOption[] = [];
+
+ imagesInitiatorSelections: SelectOption[][] = [];
+ groupDiskSelections: SelectOption[][] = [];
+ groupMembersSelections: SelectOption[][] = [];
+
imagesSettings: any = {};
messages = {
portals: new SelectMessages(
private modalService: BsModalService,
private rbdService: RbdService,
private router: Router,
+ private route: ActivatedRoute,
private i18n: I18n,
private taskWrapper: TaskWrapperService
- ) {
- forkJoin([this.rbdService.list(), this.iscsiService.listTargets()]).subscribe((data: any[]) => {
- const usedImages = _(data[1])
+ ) {}
+
+ ngOnInit() {
+ const promises: any[] = [
+ this.iscsiService.listTargets(),
+ this.rbdService.list(),
+ this.iscsiService.portals(),
+ this.iscsiService.settings()
+ ];
+
+ if (this.router.url.startsWith('/block/iscsi/targets/edit')) {
+ this.isEdit = true;
+ this.route.params.subscribe((params: { target_iqn: string }) => {
+ this.target_iqn = decodeURIComponent(params.target_iqn);
+ promises.push(this.iscsiService.getTarget(this.target_iqn));
+ });
+ }
+
+ forkJoin(promises).subscribe((data: any[]) => {
+ // iscsiService.listTargets
+ const usedImages = _(data[0])
+ .filter((target) => target.target_iqn !== this.target_iqn)
.flatMap((target) => target.disks)
.map((image) => `${image.pool}/${image.image}`)
.value();
- this.imagesAll = _(data[0])
+ // rbdService.list()
+ this.imagesAll = _(data[1])
.flatMap((pool) => pool.value)
.map((image) => `${image.pool_name}/${image.name}`)
.filter((image) => usedImages.indexOf(image) === -1)
.value();
this.imagesSelections = this.imagesAll.map((image) => new SelectOption(false, image, ''));
- });
- this.iscsiService.portals().subscribe((result: any) => {
+ // iscsiService.portals()
const portals: SelectOption[] = [];
- result.forEach((portal) => {
+ data[2].forEach((portal) => {
portal.ip_addresses.forEach((ip) => {
portals.push(new SelectOption(false, portal.name + ':' + ip, ''));
});
});
this.portalsSelections = [...portals];
- });
- this.iscsiService.settings().subscribe((result: any) => {
- this.minimum_gateways = result.config.minimum_gateways;
- this.target_default_controls = result.target_default_controls;
- this.disk_default_controls = result.disk_default_controls;
+ // iscsiService.settings()
+ 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.createForm();
+
+ // iscsiService.getTarget()
+ if (data[4]) {
+ this.resolveModel(data[4]);
+ }
});
}
});
}
+ resolveModel(res) {
+ this.targetForm.patchValue({
+ target_iqn: res.target_iqn,
+ target_controls: res.target_controls
+ });
+
+ const portals = [];
+ _.forEach(res.portals, (portal) => {
+ const id = `${portal.host}:${portal.ip}`;
+ portals.push(id);
+ });
+ this.targetForm.patchValue({
+ portals: portals
+ });
+
+ const disks = [];
+ _.forEach(res.disks, (disk) => {
+ const id = `${disk.pool}/${disk.image}`;
+ disks.push(id);
+ this.imagesSettings[id] = disk.controls;
+ this.onImageSelection({ option: { name: id, selected: true } });
+ });
+ this.targetForm.patchValue({
+ disks: disks
+ });
+
+ _.forEach(res.clients, (client) => {
+ const initiator = this.addInitiator();
+ client.luns = _.map(client.luns, (lun) => `${lun.pool}/${lun.image}`);
+ initiator.patchValue(client);
+ // updatedInitiatorSelector()
+ });
+
+ _.forEach(res.groups, (group) => {
+ const fg = this.addGroup();
+ console.log(group);
+ group.disks = _.map(group.disks, (disk) => `${disk.pool}/${disk.image}`);
+ fg.patchValue(group);
+ _.forEach(group.members, (member) => {
+ this.onGroupMemberSelection({ option: new SelectOption(true, member, '') });
+ });
+ });
+ }
+
hasAdvancedSettings(settings: any) {
return Object.values(settings).length > 0;
}
return this.targetForm.get('portals') as FormControl;
}
- onPortalSelection($event) {
+ onPortalSelection() {
this.portals.setValue(this.portals.value);
}
element.get('disks').setValue(newDisks);
});
- this.imagesInitiatorSelections = this.imagesInitiatorSelections.filter(
- (item) => item.name !== name
- );
- this.groupDiskSelections = this.groupDiskSelections.filter((item) => item.name !== name);
+ _.forEach(this.imagesInitiatorSelections, (selections, i) => {
+ this.imagesInitiatorSelections[i] = selections.filter((item: any) => item.name !== name);
+ });
+ _.forEach(this.groupDiskSelections, (selections, i) => {
+ this.groupDiskSelections[i] = selections.filter((item: any) => item.name !== name);
+ });
}
onImageSelection($event) {
if (!this.imagesSettings[option.name]) {
this.imagesSettings[option.name] = {};
}
- this.imagesInitiatorSelections.push(new SelectOption(false, option.name, ''));
- this.groupDiskSelections.push(new SelectOption(false, option.name, ''));
+
+ _.forEach(this.imagesInitiatorSelections, (selections, i) => {
+ selections.push(new SelectOption(false, option.name, ''));
+ this.imagesInitiatorSelections[i] = [...selections];
+ });
+
+ _.forEach(this.groupDiskSelections, (selections, i) => {
+ selections.push(new SelectOption(false, option.name, ''));
+ this.groupDiskSelections[i] = [...selections];
+ });
} else {
this.removeImageRefs(option.name);
}
-
- this.imagesInitiatorSelections = [...this.imagesInitiatorSelections];
- this.groupDiskSelections = [...this.groupDiskSelections];
}
// Initiators
this.initiators.push(fg);
- this.groupMembersSelections.push(new SelectOption(false, '', ''));
- this.groupMembersSelections = [...this.groupMembersSelections];
+ _.forEach(this.groupMembersSelections, (selections, i) => {
+ selections.push(new SelectOption(false, '', ''));
+ this.groupMembersSelections[i] = [...selections];
+ });
- return false;
+ const disks = _.map(
+ this.targetForm.getValue('disks'),
+ (disk) => new SelectOption(false, disk, '')
+ );
+ this.imagesInitiatorSelections.push(disks);
+
+ return fg;
}
removeInitiator(index) {
+ const removed = this.initiators.value[index];
+
this.initiators.removeAt(index);
- const removed: SelectOption[] = this.groupMembersSelections.splice(index, 1);
- this.groupMembersSelections = [...this.groupMembersSelections];
+ _.forEach(this.groupMembersSelections, (selections, i) => {
+ selections.splice(index, 1);
+ this.groupMembersSelections[i] = [...selections];
+ });
this.groups.controls.forEach((element) => {
- const newMembers = element.value.members.filter((item) => item !== removed[0].name);
+ const newMembers = element.value.members.filter((item) => item !== removed.client_iqn);
element.get('members').setValue(newMembers);
});
+
+ this.imagesInitiatorSelections.splice(index, 1);
}
updatedInitiatorSelector() {
});
// Update Group Initiator Selector
- this.groupMembersSelections.forEach((elem, index) => {
- const oldName = elem.name;
- elem.name = this.initiators.controls[index].value.client_iqn;
-
- this.groups.controls.forEach((element) => {
- const members = element.value.members;
- const i = members.indexOf(oldName);
-
- if (i !== -1) {
- members[i] = elem.name;
- }
- element.get('members').setValue(members);
+ _.forEach(this.groupMembersSelections, (group, group_index) => {
+ _.forEach(group, (elem, index) => {
+ const oldName = elem.name;
+ elem.name = this.initiators.controls[index].value.client_iqn;
+
+ this.groups.controls.forEach((element) => {
+ const members = element.value.members;
+ const i = members.indexOf(oldName);
+
+ if (i !== -1) {
+ members[i] = elem.name;
+ }
+ element.get('members').setValue(members);
+ });
});
+ this.groupMembersSelections[group_index] = [...this.groupMembersSelections[group_index]];
});
- this.groupMembersSelections = [...this.groupMembersSelections];
+ }
+
+ removeInitiatorImage(initiator: any, lun_index: number, initiator_index: string, image: string) {
+ const luns = initiator.getValue('luns');
+ luns.splice(lun_index, 1);
+ initiator.patchValue({ luns: luns });
+
+ this.imagesInitiatorSelections[initiator_index].forEach((value) => {
+ if (value.name === image) {
+ value.selected = false;
+ }
+ });
+
+ return false;
}
// Groups
}
addGroup() {
- this.groups.push(
- new CdFormGroup({
- group_id: new FormControl('', { validators: [Validators.required] }),
- members: new FormControl([]),
- disks: new FormControl([])
- })
+ const fg = new CdFormGroup({
+ group_id: new FormControl('', { validators: [Validators.required] }),
+ members: new FormControl([]),
+ disks: new FormControl([])
+ });
+
+ this.groups.push(fg);
+
+ const disks = _.map(
+ this.targetForm.getValue('disks'),
+ (disk) => new SelectOption(false, disk, '')
);
- return false;
+ this.groupDiskSelections.push(disks);
+
+ const initiators = _.map(
+ this.initiators.value,
+ (initiator) => new SelectOption(false, initiator.client_iqn, '')
+ );
+ this.groupMembersSelections.push(initiators);
+
+ return fg;
+ }
+
+ removeGroup(index) {
+ this.groups.removeAt(index);
+ this.groupDiskSelections.splice(index, 1);
}
onGroupMemberSelection($event) {
}
});
}
- removeGroupInitiator(group, i) {
- const name = group.getValue('members')[i];
- group.getValue('members').splice(i, 1);
- this.groupMembersSelections.forEach((value) => {
+ removeGroupInitiator(group, member_index, group_index) {
+ const name = group.getValue('members')[member_index];
+ group.getValue('members').splice(member_index, 1);
+
+ this.groupMembersSelections[group_index].forEach((value) => {
if (value.name === name) {
value.selected = false;
}
});
- this.groupMembersSelections = [...this.groupMembersSelections];
+ this.groupMembersSelections[group_index] = [...this.groupMembersSelections[group_index]];
this.onGroupMemberSelection({ option: new SelectOption(false, name, '') });
}
+ removeGroupDisk(group, disk_index, group_index) {
+ const name = group.getValue('disks')[disk_index];
+ group.getValue('disks').splice(disk_index, 1);
+
+ this.groupDiskSelections[group_index].forEach((value) => {
+ if (value.name === name) {
+ value.selected = false;
+ }
+ });
+ this.groupDiskSelections[group_index] = [...this.groupDiskSelections[group_index]];
+ }
+
submit() {
const formValue = this.targetForm.value;
});
request.groups = formValue.groups;
- this.taskWrapper
- .wrapTaskAroundCall({
+ let wrapTask;
+ if (this.isEdit) {
+ request['new_target_iqn'] = request.target_iqn;
+ request.target_iqn = this.target_iqn;
+ wrapTask = this.taskWrapper.wrapTaskAroundCall({
+ task: new FinishedTask('iscsi/target/edit', {
+ target_iqn: request.target_iqn
+ }),
+ call: this.iscsiService.updateTarget(this.target_iqn, request)
+ });
+ } else {
+ wrapTask = this.taskWrapper.wrapTaskAroundCall({
task: new FinishedTask('iscsi/target/create', {
target_iqn: request.target_iqn
}),
call: this.iscsiService.createTarget(request)
- })
- .subscribe(
- undefined,
- () => {
- this.targetForm.setErrors({ cdSubmitButton: true });
- },
- () => this.router.navigate(['/block/iscsi/targets'])
- );
+ });
+ }
+
+ wrapTask.subscribe(
+ undefined,
+ () => {
+ this.targetForm.setErrors({ cdSubmitButton: true });
+ },
+ () => this.router.navigate(['/block/iscsi/targets'])
+ );
}
targetSettingsModal() {
);
scenario = {
fn: () => tableActions.getCurrentButton().name,
- single: 'Delete',
+ single: 'Edit',
empty: 'Add'
};
});
tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
});
- it(`shows 'Delete' for single selection else 'Add' as main action`, () =>
- permissionHelper.testScenarios(scenario));
+ it(`shows 'Edit' for single selection else 'Add' as main action`, () => {
+ permissionHelper.testScenarios(scenario);
+ });
it('shows all actions', () => {
- expect(tableActions.tableActions.length).toBe(2);
+ expect(tableActions.tableActions.length).toBe(3);
expect(tableActions.tableActions).toEqual(component.tableActions);
});
});
describe('with read, create and update', () => {
beforeEach(() => {
tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 0);
- scenario.single = 'Add';
+ scenario.single = 'Edit';
});
- it(`should always show 'Add'`, () => {
+ it(`should always show 'Edit'`, () => {
permissionHelper.testScenarios(scenario);
});
it(`shows all actions except for 'Delete'`, () => {
- expect(tableActions.tableActions.length).toBe(1);
+ expect(tableActions.tableActions.length).toBe(2);
component.tableActions.pop();
expect(tableActions.tableActions).toEqual(component.tableActions);
});
expect(tableActions.tableActions.length).toBe(2);
expect(tableActions.tableActions).toEqual([
component.tableActions[0],
- component.tableActions[1]
+ component.tableActions[2]
]);
});
});
tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 1);
});
- it(`shows always 'Delete' as main action`, () => {
- scenario.empty = 'Delete';
+ it(`shows always 'Edit' as main action`, () => {
+ scenario.empty = 'Edit';
permissionHelper.testScenarios(scenario);
});
- it(`shows 'Delete' action`, () => {
- expect(tableActions.tableActions.length).toBe(1);
- expect(tableActions.tableActions).toEqual([component.tableActions[1]]);
+ it(`shows 'Edit' and 'Delete' actions`, () => {
+ expect(tableActions.tableActions.length).toBe(2);
+ expect(tableActions.tableActions).toEqual([
+ component.tableActions[1],
+ component.tableActions[2]
+ ]);
});
});
});
it(`shows no actions`, () => {
- expect(tableActions.tableActions.length).toBe(0);
- expect(tableActions.tableActions).toEqual([]);
+ expect(tableActions.tableActions.length).toBe(1);
+ expect(tableActions.tableActions).toEqual([component.tableActions[1]]);
});
});
it(`shows 'Delete' actions`, () => {
expect(tableActions.tableActions.length).toBe(1);
- expect(tableActions.tableActions).toEqual([component.tableActions[1]]);
+ expect(tableActions.tableActions).toEqual([component.tableActions[2]]);
});
});
routerLink: () => '/block/iscsi/targets/add',
name: this.i18n('Add')
},
+ {
+ permission: 'update',
+ icon: 'fa-pencil',
+ routerLink: () => `/block/iscsi/targets/edit/${this.selection.first().target_iqn}`,
+ name: this.i18n('Edit')
+ },
{
permission: 'delete',
icon: 'fa-times',
}
taskFilter(task) {
- return ['iscsi/target/create', 'iscsi/target/delete'].includes(task.name);
+ return ['iscsi/target/create', 'iscsi/target/edit', 'iscsi/target/delete'].includes(task.name);
}
updateSelection(selection: CdTableSelection) {
expect(req.request.method).toBe('GET');
});
+ it('should call getTarget', () => {
+ service.getTarget('iqn.foo').subscribe();
+ const req = httpTesting.expectOne('api/iscsi/target/iqn.foo');
+ expect(req.request.method).toBe('GET');
+ });
+
it('should call status', () => {
service.status().subscribe();
const req = httpTesting.expectOne('ui-api/iscsi/status');
});
it('should call createTarget', () => {
- service.createTarget('foo').subscribe();
+ service.createTarget({ target_iqn: 'foo' }).subscribe();
const req = httpTesting.expectOne('api/iscsi/target');
expect(req.request.method).toBe('POST');
- expect(req.request.body).toEqual('foo');
+ expect(req.request.body).toEqual({ target_iqn: 'foo' });
+ });
+
+ it('should call updateTarget', () => {
+ service.updateTarget('iqn.foo', { target_iqn: 'foo' }).subscribe();
+ const req = httpTesting.expectOne('api/iscsi/target/iqn.foo');
+ expect(req.request.method).toBe('PUT');
+ expect(req.request.body).toEqual({ target_iqn: 'foo' });
});
it('should call deleteTarget', () => {
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
+import { cdEncode } from '../decorators/cd-encode';
import { ApiModule } from './api.module';
+@cdEncode
@Injectable({
providedIn: ApiModule
})
return this.http.get(`api/iscsi/target`);
}
+ getTarget(target_iqn) {
+ return this.http.get(`api/iscsi/target/${target_iqn}`);
+ }
+
+ updateTarget(target_iqn, target) {
+ return this.http.put(`api/iscsi/target/${target_iqn}`, target, { observe: 'response' });
+ }
+
status() {
return this.http.get(`ui-api/iscsi/status`);
}
return this.http.post(`api/iscsi/target`, target, { observe: 'response' });
}
- deleteTarget(targetIqn) {
- return this.http.delete(`api/iscsi/target/${targetIqn}`, { observe: 'response' });
+ deleteTarget(target_iqn) {
+ return this.http.delete(`api/iscsi/target/${target_iqn}`, { observe: 'response' });
}
getDiscovery() {
<context context-type="sourcefile">app/ceph/block/iscsi-target-iqn-settings-modal/iscsi-target-iqn-settings-modal.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
- </trans-unit><trans-unit id="a01e6937a5d1ee040a02416eed34544c4ea61e38" datatype="html">
- <source>Create target</source>
+ </trans-unit><trans-unit id="602ace8a29cf86e2e7db2f6b0cdb896ddf760e0d" datatype="html">
+ <source><x id="ICU" equiv-text="{isEdit, select, 1 {...} other {...}}"/> target</source>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/block/iscsi-target-form/iscsi-target-form.component.html</context>
<context context-type="linenumber">11</context>
</context-group>
+ </trans-unit><trans-unit id="457db3d876e89cae583be9845912213ae73dc398" datatype="html">
+ <source>{VAR_SELECT, select, 1 {Edit} other {Add} }</source>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/block/iscsi-target-form/iscsi-target-form.component.html</context>
- <context context-type="linenumber">539</context>
+ <context context-type="linenumber">11</context>
</context-group>
</trans-unit><trans-unit id="1406c2fb12a20c1528b19bcc5e24a6a2386167f3" datatype="html">
<source>Target IQN</source>
<context context-type="sourcefile">app/ceph/block/iscsi-target-form/iscsi-target-form.component.html</context>
<context context-type="linenumber">526</context>
</context-group>
+ </trans-unit><trans-unit id="71c77bb8cecdf11ec3eead24dd1ba506573fa9cd" datatype="html">
+ <source>Submit</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/block/iscsi-target-form/iscsi-target-form.component.html</context>
+ <context context-type="linenumber">539</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/block/iscsi-target-discovery-modal/iscsi-target-discovery-modal.component.html</context>
+ <context context-type="linenumber">133</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/osd/osd-flags-modal/osd-flags-modal.component.html</context>
+ <context context-type="linenumber">34</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.html</context>
+ <context context-type="linenumber">87</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/osd/osd-scrub-modal/osd-scrub-modal.component.html</context>
+ <context context-type="linenumber">21</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/block/mirroring/pool-edit-peer-modal/pool-edit-peer-modal.component.html</context>
+ <context context-type="linenumber">106</context>
+ </context-group>
</trans-unit><trans-unit id="54839ebc827b73c9dc4e1df731c6d36a85036af7" datatype="html">
<source>Are you sure that you want to <x id="INTERPOLATION" equiv-text="{{ actionDescription | lowercase }}"/> the selected <x id="INTERPOLATION_1" equiv-text="{{ itemDescription }}"/>?</source>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/block/iscsi-target-discovery-modal/iscsi-target-discovery-modal.component.html</context>
<context context-type="linenumber">123</context>
</context-group>
- </trans-unit><trans-unit id="71c77bb8cecdf11ec3eead24dd1ba506573fa9cd" datatype="html">
- <source>Submit</source>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/block/iscsi-target-discovery-modal/iscsi-target-discovery-modal.component.html</context>
- <context context-type="linenumber">133</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/cluster/osd/osd-flags-modal/osd-flags-modal.component.html</context>
- <context context-type="linenumber">34</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.html</context>
- <context context-type="linenumber">87</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/cluster/osd/osd-scrub-modal/osd-scrub-modal.component.html</context>
- <context context-type="linenumber">21</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/block/mirroring/pool-edit-peer-modal/pool-edit-peer-modal.component.html</context>
- <context context-type="linenumber">106</context>
- </context-group>
</trans-unit><trans-unit id="53a583cd5f15059cc958b7d547f72cc78f68e123" datatype="html">
<source>Please consult the <x id="START_LINK" ctype="x-a" equiv-text="<a>"/>documentation<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/>
on how to configure and enable the iSCSI Targets management functionality.</source>