From: pujashahu Date: Mon, 17 Nov 2025 14:12:03 +0000 (+0530) Subject: 'mgr/dashboard: Carbonize Block Module > NVme-Listing Gateway group X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=18a1f7c1b7356d55a9f06485c03e77d3e71d4acc;p=ceph.git 'mgr/dashboard: Carbonize Block Module > NVme-Listing Gateway group Fixes: https://tracker.ceph.com/issues/73719 Signed-off-by: pujaoshahu (cherry picked from commit 0755593b4c87e519474f15011e3aac8bc086386f) Conflicts: src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.html src/pybind/mgr/dashboard/frontend/src/styles/_carbon-defaults.scss src/pybind/mgr/dashboard/frontend/src/styles/ceph-custom/_spacings.scss --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts index 66c0b781117a..df03b8ecb84c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts @@ -40,7 +40,6 @@ import { RbdTrashRestoreModalComponent } from './rbd-trash-restore-modal/rbd-tra import { NvmeofGatewayComponent } from './nvmeof-gateway/nvmeof-gateway.component'; import { NvmeofSubsystemsComponent } from './nvmeof-subsystems/nvmeof-subsystems.component'; import { NvmeofSubsystemsDetailsComponent } from './nvmeof-subsystems-details/nvmeof-subsystems-details.component'; -import { NvmeofTabsComponent } from './nvmeof-tabs/nvmeof-tabs.component'; import { NvmeofSubsystemsFormComponent } from './nvmeof-subsystems-form/nvmeof-subsystems-form.component'; import { NvmeofListenersFormComponent } from './nvmeof-listeners-form/nvmeof-listeners-form.component'; import { NvmeofListenersListComponent } from './nvmeof-listeners-list/nvmeof-listeners-list.component'; @@ -74,6 +73,7 @@ import Close from '@carbon/icons/es/close/32'; import AddFilled from '@carbon/icons/es/add--filled/32'; import SubtractFilled from '@carbon/icons/es/subtract--filled/32'; import Reset from '@carbon/icons/es/reset/32'; +import { NvmeofGatewayGroupComponent } from './nvmeof-gateway-group/nvmeof-gateway-group.component'; @NgModule({ imports: [ @@ -131,7 +131,7 @@ import Reset from '@carbon/icons/es/reset/32'; NvmeofGatewayComponent, NvmeofSubsystemsComponent, NvmeofSubsystemsDetailsComponent, - NvmeofTabsComponent, + NvmeofGatewayGroupComponent, NvmeofSubsystemsFormComponent, NvmeofListenersFormComponent, NvmeofListenersListComponent, @@ -283,7 +283,8 @@ const routes: Routes = [ } }, children: [ - { path: '', redirectTo: 'subsystems', pathMatch: 'full' }, + { path: '', redirectTo: 'gateways', pathMatch: 'full' }, + { path: '', component: NvmeofGatewayComponent, data: { breadcrumbs: 'Gateways' } }, { path: 'subsystems', component: NvmeofSubsystemsComponent, @@ -320,8 +321,7 @@ const routes: Routes = [ outlet: 'modal' } ] - }, - { path: 'gateways', component: NvmeofGatewayComponent, data: { breadcrumbs: 'Gateways' } } + } ] } ]; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.spec.ts new file mode 100644 index 000000000000..d61a4f36894f --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.spec.ts @@ -0,0 +1,245 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { NvmeofGatewayGroupComponent } from './nvmeof-gateway-group.component'; +import { GridModule, TabsModule } from 'carbon-components-angular'; +import { NvmeofService } from '~/app/shared/api/nvmeof.service'; +import { of } from 'rxjs'; +import { HttpClientModule } from '@angular/common/http'; +import { SharedModule } from '~/app/shared/shared.module'; + +describe('NvmeofGatewayGroupComponent', () => { + let component: NvmeofGatewayGroupComponent; + let fixture: ComponentFixture; + let nvmeofService: any; + + beforeEach(async () => { + const nvmeofServiceSpy = { + listGatewayGroups: jest.fn().mockReturnValue(of([])), + listSubsystems: jest.fn().mockReturnValue(of([])) + }; + + await TestBed.configureTestingModule({ + imports: [HttpClientModule, SharedModule, TabsModule, GridModule], + declarations: [NvmeofGatewayGroupComponent], + providers: [{ provide: NvmeofService, useValue: nvmeofServiceSpy }] + }).compileComponents(); + + fixture = TestBed.createComponent(NvmeofGatewayGroupComponent); + component = fixture.componentInstance; + nvmeofService = TestBed.inject(NvmeofService); + fixture.detectChanges(); + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); + + it('should call listGatewayGroups and getDaemons on initialization', () => { + expect(nvmeofService.listGatewayGroups).toHaveBeenCalled(); + }); + + it('should populate gatewayGroup$ with the correct data', (done) => { + const mockData = [ + { + service_type: 'nvmeof', + service_id: 'rbd.default', + service_name: 'nvmeof.rbd.default', + placement: { + count: 1 + }, + spec: { + abort_discovery_on_errors: true, + abort_on_errors: true, + allowed_consecutive_spdk_ping_failures: 1, + break_update_interval_sec: 25, + cluster_connections: 32, + conn_retries: 10, + discovery_port: 8009, + enable_monitor_client: true, + enable_prometheus_exporter: true, + group: 'default', + log_directory: '/var/log/ceph/', + log_files_enabled: true, + log_files_rotation_enabled: true, + log_level: 'INFO', + max_gws_in_grp: 16, + max_hosts: 2048, + max_hosts_per_namespace: 8, + max_hosts_per_subsystem: 128, + max_log_directory_backups: 10, + max_log_file_size_in_mb: 10, + max_log_files_count: 20, + max_namespaces: 4096, + max_namespaces_per_subsystem: 512, + max_namespaces_with_netmask: 1000, + max_ns_to_change_lb_grp: 8, + max_subsystems: 128, + monitor_timeout: 1, + notifications_interval: 60, + omap_file_lock_duration: 20, + omap_file_lock_on_read: true, + omap_file_lock_retries: 30, + omap_file_lock_retry_sleep_interval: 1, + omap_file_update_attempts: 500, + omap_file_update_reloads: 10, + pool: 'rbd', + port: 5500, + prometheus_connection_list_cache_expiration: 60, + prometheus_cycles_to_adjust_speed: 3, + prometheus_frequency_slow_down_factor: 3, + prometheus_port: 10008, + prometheus_startup_delay: 240, + prometheus_stats_interval: 10, + rebalance_period_sec: 7, + rpc_socket_dir: '/var/tmp/', + rpc_socket_name: 'spdk.sock', + spdk_path: '/usr/local/bin/nvmf_tgt', + spdk_ping_interval_in_seconds: 2, + spdk_protocol_log_level: 'WARNING', + spdk_timeout: 60, + state_update_interval_sec: 5, + state_update_notify: true, + subsystem_cache_expiration: 5, + tgt_path: '/usr/local/bin/nvmf_tgt', + transport_tcp_options: { + in_capsule_data_size: 8192, + max_io_qpairs_per_ctrlr: 7 + }, + transports: 'tcp', + verbose_log_messages: true, + verify_keys: true, + verify_listener_ip: true, + verify_nqns: true + }, + status: { + size: 1, + running: 1, + last_refresh: new Date('2025-12-01T16:50:21.122930Z'), + created: new Date('2025-10-16T16:35:09.623842Z'), + ports: [5500, 4420, 8009, 10008], + container_image_id: 'image_id_1', + container_image_name: 'image_name_1' + }, + events: [ + { + created: '2025-10-16T16:35:59.879726Z', + subject: 'service:nvmeof.rbd.default', + level: 'INFO', + message: 'service was created' + } + ], + name: 'default', + gatewayCount: { + running: 1, + error: 0 + }, + subSystemCount: 0, + nodeCount: 0, + unmanaged: true + }, + { + service_type: 'nvmeof', + service_id: 'rbd.foo', + service_name: 'nvmeof.rbd.foo', + placement: { + hosts: ['ceph-node-01', 'ceph-node-02', 'ceph-node-03'] + }, + spec: { + abort_discovery_on_errors: true, + abort_on_errors: true, + allowed_consecutive_spdk_ping_failures: 1, + break_update_interval_sec: 25, + cluster_connections: 32, + conn_retries: 10, + discovery_port: 8009, + enable_monitor_client: true, + enable_prometheus_exporter: true, + group: 'foo', + log_directory: '/var/log/ceph/', + log_files_enabled: true, + log_files_rotation_enabled: true, + log_level: 'INFO', + max_gws_in_grp: 16, + max_hosts: 2048, + max_hosts_per_namespace: 8, + max_hosts_per_subsystem: 128, + max_log_directory_backups: 10, + max_log_file_size_in_mb: 10, + max_log_files_count: 20, + max_namespaces: 4096, + max_namespaces_per_subsystem: 512, + max_namespaces_with_netmask: 1000, + max_ns_to_change_lb_grp: 8, + max_subsystems: 128, + monitor_timeout: 1, + notifications_interval: 60, + omap_file_lock_duration: 20, + omap_file_lock_on_read: true, + omap_file_lock_retries: 30, + omap_file_lock_retry_sleep_interval: 1, + omap_file_update_attempts: 500, + omap_file_update_reloads: 10, + pool: 'rbd', + port: 5500, + prometheus_connection_list_cache_expiration: 60, + prometheus_cycles_to_adjust_speed: 3, + prometheus_frequency_slow_down_factor: 3, + prometheus_port: 10008, + prometheus_startup_delay: 240, + prometheus_stats_interval: 10, + rebalance_period_sec: 7, + rpc_socket_dir: '/var/tmp/', + rpc_socket_name: 'spdk.sock', + spdk_path: '/usr/local/bin/nvmf_tgt', + spdk_ping_interval_in_seconds: 2, + spdk_protocol_log_level: 'WARNING', + spdk_timeout: 60, + state_update_interval_sec: 5, + state_update_notify: true, + subsystem_cache_expiration: 5, + tgt_path: '/usr/local/bin/nvmf_tgt', + transport_tcp_options: { + in_capsule_data_size: 8192, + max_io_qpairs_per_ctrlr: 7 + }, + transports: 'tcp', + verbose_log_messages: true, + verify_keys: true, + verify_listener_ip: true, + verify_nqns: true + }, + status: { + size: 3, + running: 2, + last_refresh: new Date('2025-12-01T16:44:42.361882Z'), + created: new Date('2025-11-11T12:55:32.770910Z'), + ports: [5500, 4420, 8009, 10008], + container_image_id: 'image_id_2', + container_image_name: 'image_name_2' + }, + events: [ + { + created: '2025-11-11T12:56:42.509108Z', + subject: 'service:nvmeof.rbd.foo', + level: 'INFO', + message: 'service was created' + } + ], + name: 'foo', + gatewayCount: { + running: 2, + error: 1 + }, + subSystemCount: 0, + nodeCount: 3, + unmanaged: true + } + ]; + + component.gatewayGroup$ = of(mockData as any); + + component.gatewayGroup$.subscribe((data) => { + expect(data).toEqual(mockData); + done(); + }); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.ts new file mode 100644 index 000000000000..cc594d1e5f1c --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.ts @@ -0,0 +1,118 @@ +import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { BehaviorSubject, forkJoin, Observable, of } from 'rxjs'; +import { catchError, map, switchMap } from 'rxjs/operators'; +import { GatewayGroup, NvmeofService } from '~/app/shared/api/nvmeof.service'; +import { ActionLabelsI18n } from '~/app/shared/constants/app.constants'; +import { TableComponent } from '~/app/shared/datatable/table/table.component'; +import { CdTableAction } from '~/app/shared/models/cd-table-action'; +import { CdTableColumn } from '~/app/shared/models/cd-table-column'; +import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context'; +import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; +import { Permission } from '~/app/shared/models/permissions'; +import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; +import { Icons, IconSize } from '~/app/shared/enum/icons.enum'; +import { NvmeofGatewayGroup } from '~/app/shared/models/nvmeof'; +import { CephServiceSpec } from '~/app/shared/models/service.interface'; + +@Component({ + selector: 'cd-nvmeof-gateway-group', + templateUrl: './nvmeof-gateway-group.component.html', + styleUrls: ['./nvmeof-gateway-group.component.scss'] +}) +export class NvmeofGatewayGroupComponent implements OnInit { + @ViewChild(TableComponent, { static: true }) + table: TableComponent; + + @ViewChild('dateTpl', { static: true }) + dateTpl: TemplateRef; + + @ViewChild('gatewayStatusTpl', { static: true }) + gatewayStatusTpl: TemplateRef; + + permission: Permission; + tableActions: CdTableAction[]; + columns: CdTableColumn[] = []; + selection: CdTableSelection = new CdTableSelection(); + gatewayGroup$: Observable; + subject = new BehaviorSubject([]); + context: CdTableFetchDataContext; + gatewayGroupName: string; + subsystemCount: number; + gatewayCount: number; + + icons = Icons; + + iconSize = IconSize; + + constructor( + public actionLabels: ActionLabelsI18n, + private authStorageService: AuthStorageService, + private nvmeofService: NvmeofService + ) {} + + ngOnInit(): void { + this.permission = this.authStorageService.getPermissions().nvmeof; + + this.columns = [ + { + name: $localize`Name`, + prop: 'name' + }, + { + name: $localize`Gateways`, + prop: 'statusCount', + cellTemplate: this.gatewayStatusTpl + }, + { + name: $localize`Subsystems`, + prop: 'subSystemCount' + }, + { + name: $localize`Created on`, + prop: 'created', + cellTemplate: this.dateTpl + } + ]; + + this.gatewayGroup$ = this.subject.pipe( + switchMap(() => + this.nvmeofService.listGatewayGroups().pipe( + switchMap((gatewayGroups: GatewayGroup[][]) => { + const groups = gatewayGroups?.[0] ?? []; + return forkJoin( + groups.map((group: NvmeofGatewayGroup) => + this.nvmeofService.listSubsystems(group.spec.group).pipe( + catchError(() => of([])), + map((subs) => ({ + ...group, + name: group.spec?.group, + statusCount: { + running: group.status?.running ?? 0, + error: (group.status?.size ?? 0) - (group.status?.running ?? 0) + }, + + subSystemCount: Array.isArray(subs) ? subs.length : 0, + gateWayNode: group.placement?.hosts?.length ?? 0, + created: group.status?.created ? new Date(group.status.created) : null + })) + ) + ) + ); + }), + catchError((error) => { + this.context?.error?.(error); + return of([]); + }) + ) + ) + ); + } + + fetchData(): void { + this.subject.next([]); + } + + updateSelection(selection: CdTableSelection): void { + this.selection = selection; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.html index 7da764d12029..37a344907f30 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.html @@ -1,85 +1,44 @@ - - -
- - - -
- - - - {{ row.status_desc }} - - - - +
+ +

NVMe over Fabrics (TCP)

+ Monitor and manage NVMe-over-TCP resources for high-performance block storage. +
+
+
+ + (selected)="onSelected(Tabs.subsystem)"> + (selected)="onSelected(Tabs.namespace)"> - - Gateways - - Ceph NVMe-oF gateways provide Ceph Block Device storage through NVMe/TCP. For VMware clients the NVMe/TCP volumes display as VMFS Datastores. For Linux clients the NVMe/TCP volumes display as as block devices. - - -
- - -
+
- - - + + - - - + + +
+ diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.spec.ts index f4fb0c50d870..893a3aefc2ba 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.spec.ts @@ -1,113 +1,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { of } from 'rxjs'; + import { NvmeofGatewayComponent } from './nvmeof-gateway.component'; -import { NvmeofService } from '../../../shared/api/nvmeof.service'; + import { HttpClientModule } from '@angular/common/http'; import { SharedModule } from '~/app/shared/shared.module'; -import { ComboBoxModule, GridModule } from 'carbon-components-angular'; -import { NvmeofTabsComponent } from '../nvmeof-tabs/nvmeof-tabs.component'; -import { CephServiceService } from '~/app/shared/api/ceph-service.service'; -import { CephServiceSpec } from '~/app/shared/models/service.interface'; - -const mockServiceDaemons = [ - { - daemon_type: 'nvmeof', - daemon_id: 'nvmeof.default.ceph-node-01.kdcguk', - daemon_name: 'nvmeof.nvmeof.default.ceph-node-01.kdcguk', - hostname: 'ceph-node-01', - container_id: '6fe5a9ae9c96', - container_image_id: '32a3d75b7c146d6c37b04ee3c9ba883ab88a8f7ae8f286de268d0f41ebd86a51', - container_image_name: 'quay.io/ceph/nvmeof:1.2.17', - container_image_digests: [ - 'quay.io/ceph/nvmeof@sha256:4308d05d3bb2167fc695d755316fec8d12ec3f00eb7639eeeabad38a5c4df0f9' - ], - memory_usage: 89443532, - cpu_percentage: '98.87%', - version: '1.2.17', - status: 1, - status_desc: 'running' - }, - { - daemon_type: 'nvmeof', - daemon_id: 'nvmeof.default.ceph-node-02.hybprc', - daemon_name: 'nvmeof.nvmeof.default.ceph-node-02.hybprc', - hostname: 'ceph-node-02', - container_id: '2b061130726b', - container_image_id: '32a3d75b7c146d6c37b04ee3c9ba883ab88a8f7ae8f286de268d0f41ebd86a51', - container_image_name: 'quay.io/ceph/nvmeof:1.2.17', - container_image_digests: [ - 'quay.io/ceph/nvmeof@sha256:4308d05d3bb2167fc695d755316fec8d12ec3f00eb7639eeeabad38a5c4df0f9' - ], - memory_usage: 89328189, - cpu_percentage: '98.89%', - version: '1.2.17', - status: 1, - status_desc: 'running' - } -]; - -const mockGateways = [ - { - id: 'client.nvmeof.nvmeof.default.ceph-node-01.kdcguk', - hostname: 'ceph-node-01', - status_desc: 'running', - status: 1 - }, - { - id: 'client.nvmeof.nvmeof.default.ceph-node-02.hybprc', - hostname: 'ceph-node-02', - status_desc: 'running', - status: 1 - } -]; - -const mockformattedGwGroups = [ - { - content: 'default', - serviceName: 'nvmeof.rbd.default' - }, - { - content: 'foo', - serviceName: 'nvmeof.rbd.foo' - } -]; - -const mockServices = [ - [ - { - service_name: 'nvmeof.rbd.default', - service_type: 'nvmeof', - unmanaged: false, - spec: { - group: 'default' - } - }, - { - service_name: 'nvmeof.rbd.foo', - service_type: 'nvmeof', - unmanaged: false, - spec: { - group: 'foo' - } - } - ], - 2 -]; -class MockNvmeOfService { - listGatewayGroups() { - return of(mockServices); - } - - formatGwGroupsList(_data: CephServiceSpec[][]) { - return mockformattedGwGroups; - } -} - -class MockCephServiceService { - getDaemons(_service: string) { - return of(mockServiceDaemons); - } -} +import { ComboBoxModule, GridModule, TabsModule } from 'carbon-components-angular'; describe('NvmeofGatewayComponent', () => { let component: NvmeofGatewayComponent; @@ -115,35 +12,17 @@ describe('NvmeofGatewayComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [NvmeofGatewayComponent, NvmeofTabsComponent], - imports: [HttpClientModule, SharedModule, ComboBoxModule, GridModule], - providers: [ - { provide: NvmeofService, useClass: MockNvmeOfService }, - { provide: CephServiceService, useClass: MockCephServiceService } - ] + declarations: [NvmeofGatewayComponent], + imports: [HttpClientModule, SharedModule, ComboBoxModule, GridModule, TabsModule], + providers: [] }).compileComponents(); fixture = TestBed.createComponent(NvmeofGatewayComponent); component = fixture.componentInstance; - component.ngOnInit(); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); - - it('should load gateway groups correctly', () => { - expect(component.gwGroups.length).toBe(2); - expect(component.gwGroups).toStrictEqual(mockformattedGwGroups); - }); - - it('should set service name of gateway groups correctly', () => { - expect(component.groupService).toBe(mockServices[0][0].service_name); - }); - - it('should set gateways correctly', () => { - expect(component.gateways.length).toBe(2); - expect(component.gateways).toStrictEqual(mockGateways); - }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.ts index 33764a87be56..44f6b9f04a13 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.ts @@ -1,35 +1,22 @@ -import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { Component, TemplateRef, ViewChild } from '@angular/core'; import _ from 'lodash'; import { ActionLabelsI18n } from '~/app/shared/constants/app.constants'; import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; -import { GroupsComboboxItem, NvmeofService } from '../../../shared/api/nvmeof.service'; -import { CephServiceSpec } from '~/app/shared/models/service.interface'; -import { CephServiceService } from '~/app/shared/api/ceph-service.service'; -import { Daemon } from '~/app/shared/models/daemon.interface'; - -type Gateway = { - id: string; - hostname: string; - status: number; - status_desc: string; -}; - enum TABS { 'gateways', - 'overview' + 'subsystem', + 'namespace' } -const DEFAULT_PLACEHOLDER = $localize`Enter group name`; - @Component({ selector: 'cd-nvmeof-gateway', templateUrl: './nvmeof-gateway.component.html', styleUrls: ['./nvmeof-gateway.component.scss'] }) -export class NvmeofGatewayComponent implements OnInit { +export class NvmeofGatewayComponent { selectedTab: TABS; onSelected(tab: TABS) { @@ -42,97 +29,7 @@ export class NvmeofGatewayComponent implements OnInit { @ViewChild('statusTpl', { static: true }) statusTpl: TemplateRef; - - gateways: Gateway[] = []; - gatewayColumns: any; selection = new CdTableSelection(); - gwGroups: GroupsComboboxItem[] = []; - groupService: string = null; - selectedGatewayGroup: string = null; - gwGroupsEmpty: boolean = false; - gwGroupPlaceholder: string = DEFAULT_PLACEHOLDER; - - constructor( - private nvmeofService: NvmeofService, - private cephServiceService: CephServiceService, - public actionLabels: ActionLabelsI18n - ) {} - - ngOnInit() { - this.setGatewayGroups(); - this.gatewayColumns = [ - { - name: $localize`Gateway ID`, - prop: 'id' - }, - { - name: $localize`Hostname`, - prop: 'hostname' - }, - { - name: $localize`Status`, - prop: 'status_desc', - cellTemplate: this.statusTpl - } - ]; - } - - // for Status column - getStatusClass(row: Gateway): string { - return _.get( - { - '-1': 'tag-danger', - '0': 'tag-warning', - '1': 'tag-success' - }, - row.status, - 'tag-dark' - ); - } - - // Gateways - getGateways() { - this.cephServiceService.getDaemons(this.groupService).subscribe((daemons: Daemon[]) => { - this.gateways = daemons.length - ? daemons.map((daemon: Daemon) => { - return { - id: `client.${daemon.daemon_name}`, - hostname: daemon.hostname, - status_desc: daemon.status_desc, - status: daemon.status - }; - }) - : []; - }); - } - - // Gateway groups - onGroupSelection(selected: GroupsComboboxItem) { - selected.selected = true; - this.groupService = selected.serviceName; - this.selectedGatewayGroup = selected.content; - this.getGateways(); - } - - onGroupClear() { - this.groupService = null; - this.getGateways(); - } - setGatewayGroups() { - this.nvmeofService.listGatewayGroups().subscribe((response: CephServiceSpec[][]) => { - if (response?.[0]?.length) { - this.gwGroups = this.nvmeofService.formatGwGroupsList(response, true); - } else this.gwGroups = []; - // Select first group if no group is selected - if (!this.groupService && this.gwGroups.length) { - this.onGroupSelection(this.gwGroups[0]); - this.gwGroupsEmpty = false; - this.gwGroupPlaceholder = DEFAULT_PLACEHOLDER; - } else { - this.gwGroupsEmpty = true; - this.gwGroupPlaceholder = $localize`No groups available`; - } - }); - } + constructor(public actionLabels: ActionLabelsI18n) {} } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.spec.ts index 75562626ee50..30775d215691 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.spec.ts @@ -8,7 +8,6 @@ import { NvmeofService } from '../../../shared/api/nvmeof.service'; import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; import { ModalService } from '~/app/shared/services/modal.service'; import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service'; -import { NvmeofTabsComponent } from '../nvmeof-tabs/nvmeof-tabs.component'; import { NvmeofSubsystemsDetailsComponent } from '../nvmeof-subsystems-details/nvmeof-subsystems-details.component'; import { NvmeofNamespacesListComponent } from './nvmeof-namespaces-list.component'; @@ -51,11 +50,7 @@ describe('NvmeofNamespacesListComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ - NvmeofNamespacesListComponent, - NvmeofTabsComponent, - NvmeofSubsystemsDetailsComponent - ], + declarations: [NvmeofNamespacesListComponent, NvmeofSubsystemsDetailsComponent], imports: [HttpClientModule, RouterTestingModule, SharedModule], providers: [ { provide: NvmeofService, useClass: MockNvmeOfService }, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.html index f3120ba500ca..84a1c8763b1b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.html @@ -1,5 +1,3 @@ - -
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.spec.ts index 3246f7286f51..413cec31ce34 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.spec.ts @@ -9,7 +9,6 @@ import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; import { ModalService } from '~/app/shared/services/modal.service'; import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service'; import { NvmeofSubsystemsComponent } from './nvmeof-subsystems.component'; -import { NvmeofTabsComponent } from '../nvmeof-tabs/nvmeof-tabs.component'; import { NvmeofSubsystemsDetailsComponent } from '../nvmeof-subsystems-details/nvmeof-subsystems-details.component'; import { ComboBoxModule, GridModule } from 'carbon-components-angular'; import { CephServiceSpec } from '~/app/shared/models/service.interface'; @@ -89,11 +88,7 @@ describe('NvmeofSubsystemsComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ - NvmeofSubsystemsComponent, - NvmeofTabsComponent, - NvmeofSubsystemsDetailsComponent - ], + declarations: [NvmeofSubsystemsComponent, NvmeofSubsystemsDetailsComponent], imports: [HttpClientModule, RouterTestingModule, SharedModule, ComboBoxModule, GridModule], providers: [ { provide: NvmeofService, useClass: MockNvmeOfService }, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.html deleted file mode 100644 index 29f1e2ad6643..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.html +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.scss deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.spec.ts deleted file mode 100644 index 23e334a6e142..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { NvmeofTabsComponent } from './nvmeof-tabs.component'; - -describe('NvmeofTabsComponent', () => { - let component: NvmeofTabsComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [NvmeofTabsComponent] - }).compileComponents(); - - fixture = TestBed.createComponent(NvmeofTabsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.ts deleted file mode 100644 index 507116c466ff..000000000000 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-tabs/nvmeof-tabs.component.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'cd-nvmeof-tabs', - templateUrl: './nvmeof-tabs.component.html', - styleUrls: ['./nvmeof-tabs.component.scss'] -}) -export class NvmeofTabsComponent {} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nvmeof.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nvmeof.service.ts index 1420841a6efb..aaccc68e14c1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nvmeof.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nvmeof.service.ts @@ -74,7 +74,7 @@ export class NvmeofService { // Gateway groups listGatewayGroups() { - return this.http.get(`${API_PATH}/gateway/group`); + return this.http.get(`${API_PATH}/gateway/group`); } // Gateways diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/nvmeof.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/nvmeof.ts index db3bc8b1fad8..43a24a4b5ce2 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/nvmeof.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/nvmeof.ts @@ -1,3 +1,5 @@ +import { CephServiceSpec } from './service.interface'; + export interface NvmeofGateway { version: string; name: string; @@ -47,3 +49,13 @@ export interface NvmeofSubsystemNamespace { r_mbytes_per_second: number; w_mbytes_per_second: number; } + +export interface NvmeofGatewayGroup extends CephServiceSpec { + name: string; + gatewayCount: { + running: number; + error: number; + }; + subSystemCount: number; + nodeCount: number; +}