parameters={
'subsystem_nqn': (str, 'Subsystem NQN'),
"host_nqn": Param(str, 'Comma separated list of NVMeoF host NQNs'),
+ "gw_group": Param(str, "NVMeoF gateway group")
})
@empty_response
@handle_nvmeof_error
@CreatePermission
- def add(self, subsystem_nqn: str, host_nqn: str = ""):
+ def add(self, subsystem_nqn: str, gw_group: str, host_nqn: str = ""):
response = None
all_host_nqns = host_nqn.split(',')
for nqn in all_host_nqns:
- response = NVMeoFClient().stub.add_host(
+ response = NVMeoFClient(gw_group=gw_group).stub.add_host(
NVMeoFClient.pb2.add_host_req(subsystem_nqn=subsystem_nqn, host_nqn=nqn)
)
if response.status != 0:
parameters={
"subsystem_nqn": Param(str, "NVMeoF subsystem NQN"),
"host_nqn": Param(str, 'Comma separated list of NVMeoF host NQN.'),
+ "gw_group": Param(str, "NVMeoF gateway group")
})
@empty_response
@handle_nvmeof_error
@DeletePermission
- def remove(self, subsystem_nqn: str, host_nqn: str):
+ def remove(self, subsystem_nqn: str, host_nqn: str, gw_group: str):
response = None
to_delete_nqns = host_nqn.split(',')
for del_nqn in to_delete_nqns:
- response = NVMeoFClient().stub.remove_host(
+ response = NVMeoFClient(gw_group=gw_group).stub.remove_host(
NVMeoFClient.pb2.remove_host_req(subsystem_nqn=subsystem_nqn, host_nqn=del_nqn)
)
if response.status != 0:
import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
import { FinishedTask } from '~/app/shared/models/finished-task';
import { ActivatedRoute, Router } from '@angular/router';
-import { NvmeofService } from '~/app/shared/api/nvmeof.service';
+import { InitiatorRequest, NvmeofService } from '~/app/shared/api/nvmeof.service';
@Component({
selector: 'cd-nvmeof-initiators-form',
remove: boolean = false;
subsystemNQN: string;
removeHosts: { name: string; value: boolean; id: number }[] = [];
+ group: string;
constructor(
private authStorageService: AuthStorageService,
);
ngOnInit() {
+ this.route.queryParams.subscribe((params) => {
+ this.group = params?.['group'];
+ });
this.createForm();
this.action = this.actionLabels.ADD;
this.route.params.subscribe((params: { subsystem_nqn: string }) => {
const hosts: string[] = this.addedHosts.value;
let taskUrl = `nvmeof/initiator/${URLVerbs.ADD}`;
- const request = {
- host_nqn: hosts.join(',')
+ const request: InitiatorRequest = {
+ host_nqn: hosts.join(','),
+ gw_group: this.group
};
if (allowAnyHost) {
-import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core';
+import { Component, Input, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { NvmeofService } from '~/app/shared/api/nvmeof.service';
import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
templateUrl: './nvmeof-initiators-list.component.html',
styleUrls: ['./nvmeof-initiators-list.component.scss']
})
-export class NvmeofInitiatorsListComponent implements OnInit, OnChanges {
+export class NvmeofInitiatorsListComponent implements OnInit {
@Input()
subsystemNQN: string;
+ @Input()
+ group: string;
@ViewChild('hostTpl', { static: true })
hostTpl: TemplateRef<any>;
permission: 'create',
icon: Icons.add,
click: () =>
- this.router.navigate([
- BASE_URL,
- { outlets: { modal: [URLVerbs.ADD, this.subsystemNQN, 'initiator'] } }
- ]),
+ this.router.navigate(
+ [BASE_URL, { outlets: { modal: [URLVerbs.ADD, this.subsystemNQN, 'initiator'] } }],
+ { queryParams: { group: this.group } }
+ ),
canBePrimary: (selection: CdTableSelection) => !selection.hasSelection
},
{
return this.selection.selected.findIndex((selected) => selected.nqn === '*');
}
- ngOnChanges() {
- this.listInitiators();
- }
-
updateSelection(selection: CdTableSelection) {
this.selection = selection;
}
listInitiators() {
this.nvmeofService
- .getInitiators(this.subsystemNQN)
+ .getInitiators(this.subsystemNQN, this.group)
.subscribe((initiators: NvmeofSubsystemInitiator[]) => {
this.initiators = initiators;
});
nqn: this.subsystemNQN,
plural: itemNames.length > 1
}),
- call: this.nvmeofService.removeInitiators(this.subsystemNQN, { host_nqn })
+ call: this.nvmeofService.removeInitiators(this.subsystemNQN, {
+ host_nqn,
+ gw_group: this.group
+ })
})
});
}
const host = this.listenerForm.getValue('host');
let trsvcid = Number(this.listenerForm.getValue('trsvcid'));
if (!trsvcid) trsvcid = 4420;
- const request = {
+ const request: ListenerRequest = {
+ gw_group: this.group,
host_name: host.hostname,
traddr: host.addr,
trsvcid
component.listenerForm.setErrors({ cdSubmitButton: true });
},
complete: () => {
- this.router.navigate([this.pageURL, { outlets: { modal: null } }], {
- queryParams: { group: this.group }
- });
+ this.router.navigate([this.pageURL, { outlets: { modal: null } }]);
}
});
}
-import { Component, Input, OnChanges, OnInit } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { NvmeofService } from '~/app/shared/api/nvmeof.service';
import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
templateUrl: './nvmeof-listeners-list.component.html',
styleUrls: ['./nvmeof-listeners-list.component.scss']
})
-export class NvmeofListenersListComponent implements OnInit, OnChanges {
+export class NvmeofListenersListComponent implements OnInit {
@Input()
subsystemNQN: string;
@Input()
];
}
- ngOnChanges() {
- this.listListeners();
- }
-
updateSelection(selection: CdTableSelection) {
this.selection = selection;
}
listListeners() {
this.nvmeofService
- .listListeners(this.subsystemNQN)
+ .listListeners(this.subsystemNQN, this.group)
.subscribe((listResponse: NvmeofListener[]) => {
this.listeners = listResponse.map((listener, index) => {
listener['id'] = index;
nsid: string;
currentBytes: number;
invalidSizeError: boolean;
+ group: string;
constructor(
public actionLabels: ActionLabelsI18n,
}
init() {
+ this.route.queryParams.subscribe((params) => {
+ this.group = params?.['group'];
+ });
this.createForm();
this.action = this.actionLabels.CREATE;
this.route.params.subscribe((params: { subsystem_nqn: string; nsid: string }) => {
this.edit = true;
this.action = this.actionLabels.EDIT;
this.nvmeofService
- .getNamespace(this.subsystemNQN, this.nsid)
+ .getNamespace(this.subsystemNQN, this.nsid, this.group)
.subscribe((res: NvmeofSubsystemNamespace) => {
const convertedSize = this.dimlessBinaryPipe.transform(res.rbd_image_size).split(' ');
this.currentBytes = res.rbd_image_size;
const image_size = this.nsForm.getValue('image_size');
const image_size_unit = this.nsForm.getValue('unit');
const request = {} as NamespaceCreateRequest | NamespaceEditRequest;
+ request['gw_group'] = this.group;
if (image_size) {
const key: string = this.edit ? 'rbd_image_size' : 'size';
const value: number = this.formatterService.toBytes(image_size + image_size_unit);
-import { Component, Input, OnChanges, OnInit } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { NvmeofService } from '~/app/shared/api/nvmeof.service';
import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
templateUrl: './nvmeof-namespaces-list.component.html',
styleUrls: ['./nvmeof-namespaces-list.component.scss']
})
-export class NvmeofNamespacesListComponent implements OnInit, OnChanges {
+export class NvmeofNamespacesListComponent implements OnInit {
@Input()
subsystemNQN: string;
+ @Input()
+ group: string;
namespacesColumns: any;
tableActions: CdTableAction[];
permission: 'create',
icon: Icons.add,
click: () =>
- this.router.navigate([
- BASE_URL,
- { outlets: { modal: [URLVerbs.CREATE, this.subsystemNQN, 'namespace'] } }
- ]),
+ this.router.navigate(
+ [BASE_URL, { outlets: { modal: [URLVerbs.CREATE, this.subsystemNQN, 'namespace'] } }],
+ { queryParams: { group: this.group } }
+ ),
canBePrimary: (selection: CdTableSelection) => !selection.hasSelection
},
{
permission: 'update',
icon: Icons.edit,
click: () =>
- this.router.navigate([
- BASE_URL,
- {
- outlets: {
- modal: [URLVerbs.EDIT, this.subsystemNQN, 'namespace', this.selection.first().nsid]
+ this.router.navigate(
+ [
+ BASE_URL,
+ {
+ outlets: {
+ modal: [
+ URLVerbs.EDIT,
+ this.subsystemNQN,
+ 'namespace',
+ this.selection.first().nsid
+ ]
+ }
}
- }
- ])
+ ],
+ { queryParams: { group: this.group } }
+ )
},
{
name: this.actionLabels.DELETE,
permission: 'delete',
icon: Icons.destroy,
- click: () => this.deleteSubsystemModal()
+ click: () => this.deleteNamespaceModal()
}
];
}
- ngOnChanges() {
- this.listNamespaces();
- }
-
updateSelection(selection: CdTableSelection) {
this.selection = selection;
}
listNamespaces() {
this.nvmeofService
- .listNamespaces(this.subsystemNQN)
+ .listNamespaces(this.subsystemNQN, this.group)
.subscribe((res: NvmeofSubsystemNamespace[]) => {
this.namespaces = res;
});
}
- deleteSubsystemModal() {
+ deleteNamespaceModal() {
const namespace = this.selection.first();
this.modalService.show(CriticalConfirmationModalComponent, {
itemDescription: 'Namespace',
nqn: this.subsystemNQN,
nsid: namespace.nsid
}),
- call: this.nvmeofService.deleteNamespace(this.subsystemNQN, namespace.nsid)
+ call: this.nvmeofService.deleteNamespace(this.subsystemNQN, namespace.nsid, this.group)
})
});
}
<a ngbNavLink
i18n>Namespaces</a>
<ng-template ngbNavContent>
- <cd-nvmeof-namespaces-list [subsystemNQN]="subsystemNQN"></cd-nvmeof-namespaces-list>
+ <cd-nvmeof-namespaces-list [subsystemNQN]="subsystemNQN"
+ [group]="group">
+ </cd-nvmeof-namespaces-list>
</ng-template>
</ng-container>
<ng-container ngbNavItem="initiators">
<a ngbNavLink
i18n>Initiators</a>
<ng-template ngbNavContent>
- <cd-nvmeof-initiators-list [subsystemNQN]="subsystemNQN"></cd-nvmeof-initiators-list>
+ <cd-nvmeof-initiators-list [subsystemNQN]="subsystemNQN"
+ [group]="group">
+ </cd-nvmeof-initiators-list>
</ng-template>
</ng-container>
</nav>
component.subsystemForm.setErrors({ cdSubmitButton: true });
},
complete: () => {
- this.router.navigate([this.pageURL, { outlets: { modal: null } }], {
- queryParams: { group: this.group }
- });
+ this.router.navigate([this.pageURL, { outlets: { modal: null } }]);
}
});
}
expect(service).toBeTruthy();
});
+ // gateways
it('should call listGatewayGroups', () => {
service.listGatewayGroups().subscribe();
const req = httpTesting.expectOne('api/nvmeof/gateway/group');
expect(req.request.method).toBe('GET');
});
+ // subsystems
it('should call listSubsystems', () => {
service.listSubsystems(mockGroupName).subscribe();
const req = httpTesting.expectOne(`api/nvmeof/subsystem?gw_group=${mockGroupName}`);
expect(req.request.method).toBe('DELETE');
});
+ // initiators
it('should call getInitiators', () => {
- service.getInitiators(mockNQN).subscribe();
- const req = httpTesting.expectOne(`api/nvmeof/subsystem/${mockNQN}/host`);
+ service.getInitiators(mockNQN, mockGroupName).subscribe();
+ const req = httpTesting.expectOne(
+ `api/nvmeof/subsystem/${mockNQN}/host?gw_group=${mockGroupName}`
+ );
expect(req.request.method).toBe('GET');
});
});
export const MAX_NAMESPACE = 1024;
export interface ListenerRequest {
+ gw_group: string;
host_name: string;
traddr: string;
trsvcid: number;
rbd_image_name: string;
rbd_pool: string;
size: number;
+ gw_group: string;
}
export interface NamespaceEditRequest {
rbd_image_size: number;
+ gw_group: string;
}
export interface InitiatorRequest {
host_nqn: string;
+ gw_group: string;
}
const API_PATH = 'api/nvmeof';
}
// Initiators
- getInitiators(subsystemNQN: string) {
- return this.http.get(`${API_PATH}/subsystem/${subsystemNQN}/host`);
+ getInitiators(subsystemNQN: string, group: string) {
+ return this.http.get(`${API_PATH}/subsystem/${subsystemNQN}/host?gw_group=${group}`);
}
addInitiators(subsystemNQN: string, request: InitiatorRequest) {
}
removeInitiators(subsystemNQN: string, request: InitiatorRequest) {
- return this.http.delete(`${UI_API_PATH}/subsystem/${subsystemNQN}/host/${request.host_nqn}`, {
- observe: 'response'
- });
+ return this.http.delete(
+ `${UI_API_PATH}/subsystem/${subsystemNQN}/host/${request.host_nqn}/${request.gw_group}`,
+ {
+ observe: 'response'
+ }
+ );
}
// Listeners
- listListeners(subsystemNQN: string) {
- return this.http.get(`${API_PATH}/subsystem/${subsystemNQN}/listener`);
+ listListeners(subsystemNQN: string, group: string) {
+ return this.http.get(`${API_PATH}/subsystem/${subsystemNQN}/listener?gw_group=${group}`);
}
createListener(subsystemNQN: string, request: ListenerRequest) {
}
// Namespaces
- listNamespaces(subsystemNQN: string) {
- return this.http.get(`${API_PATH}/subsystem/${subsystemNQN}/namespace`);
+ listNamespaces(subsystemNQN: string, group: string) {
+ return this.http.get(`${API_PATH}/subsystem/${subsystemNQN}/namespace?gw_group=${group}`);
}
- getNamespace(subsystemNQN: string, nsid: string) {
- return this.http.get(`${API_PATH}/subsystem/${subsystemNQN}/namespace/${nsid}`);
+ getNamespace(subsystemNQN: string, nsid: string, group: string) {
+ return this.http.get(
+ `${API_PATH}/subsystem/${subsystemNQN}/namespace/${nsid}?gw_group=${group}`
+ );
}
createNamespace(subsystemNQN: string, request: NamespaceCreateRequest) {
});
}
- deleteNamespace(subsystemNQN: string, nsid: string) {
- return this.http.delete(`${API_PATH}/subsystem/${subsystemNQN}/namespace/${nsid}`, {
- observe: 'response'
- });
+ deleteNamespace(subsystemNQN: string, nsid: string, group: string) {
+ return this.http.delete(
+ `${API_PATH}/subsystem/${subsystemNQN}/namespace/${nsid}?gw_group=${group}`,
+ {
+ observe: 'response'
+ }
+ );
}
}