menu: 'File',
submenus: [
{ menu: 'File Systems', component: 'cd-cephfs-list' },
- { menu: 'NFS', component: 'cd-error' }
+ { menu: 'NFS', component: 'cd-error' },
+ { menu: 'SMB', component: 'cd-smb-cluster-list' }
]
},
{
import { MultiClusterComponent } from './ceph/cluster/multi-cluster/multi-cluster.component';
import { MultiClusterListComponent } from './ceph/cluster/multi-cluster/multi-cluster-list/multi-cluster-list.component';
import { MultiClusterDetailsComponent } from './ceph/cluster/multi-cluster/multi-cluster-details/multi-cluster-details.component';
+import { SmbClusterListComponent } from './ceph/smb/smb-cluster-list/smb-cluster-list.component';
@Injectable()
export class PerformanceCounterBreadcrumbsResolver extends BreadcrumbsResolver {
data: { breadcrumbs: ActionLabels.EDIT }
}
]
+ },
+ {
+ path: 'smb',
+ data: {
+ breadcrumbs: 'File/SMB'
+ },
+ children: [{ path: '', component: SmbClusterListComponent }]
}
]
},
import { DashboardModule } from './dashboard/dashboard.module';
import { NfsModule } from './nfs/nfs.module';
import { PerformanceCounterModule } from './performance-counter/performance-counter.module';
+import { SmbModule } from './smb/smb.module';
@NgModule({
imports: [
PerformanceCounterModule,
CephfsModule,
NfsModule,
+ SmbModule,
SharedModule
],
declarations: []
--- /dev/null
+<ng-container *ngIf="smbClusters$ | async as smbClusters">
+ <cd-table
+ #table
+ [data]="smbClusters"
+ columnMode="flex"
+ [columns]="columns"
+ identifier="id"
+ forceIdentifier="true"
+ selectionType="single"
+ [hasDetails]="false"
+ (setExpandedRow)="setExpandedRow($event)"
+ (fetchData)="loadSMBCluster($event)"
+ >
+ </cd-table>
+</ng-container>
--- /dev/null
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SmbClusterListComponent } from './smb-cluster-list.component';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { SharedModule } from '~/app/shared/shared.module';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { ToastrModule } from 'ngx-toastr';
+
+describe('SmbClusterListComponent', () => {
+ let component: SmbClusterListComponent;
+ let fixture: ComponentFixture<SmbClusterListComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ BrowserAnimationsModule,
+ SharedModule,
+ HttpClientTestingModule,
+ ToastrModule.forRoot(),
+ RouterTestingModule
+ ],
+ declarations: [SmbClusterListComponent]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(SmbClusterListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { catchError, switchMap } from 'rxjs/operators';
+import { BehaviorSubject, Observable, of } from 'rxjs';
+
+import _ from 'lodash';
+
+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 { ListWithDetails } from '~/app/shared/classes/list-with-details.class';
+import { Permission } from '~/app/shared/models/permissions';
+
+import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
+import { SmbService } from '~/app/shared/api/smb.service';
+import { SMBCluster } from '../smb.model';
+
+@Component({
+ selector: 'cd-smb-cluster-list',
+ templateUrl: './smb-cluster-list.component.html',
+ styleUrls: ['./smb-cluster-list.component.scss']
+})
+export class SmbClusterListComponent extends ListWithDetails implements OnInit {
+ @ViewChild('table', { static: true })
+ table: TableComponent;
+ columns: CdTableColumn[];
+ permission: Permission;
+ tableActions: CdTableAction[];
+ context: CdTableFetchDataContext;
+
+ smbClusters$: Observable<SMBCluster[]>;
+ subject$ = new BehaviorSubject<SMBCluster[]>([]);
+
+ constructor(
+ private authStorageService: AuthStorageService,
+ public actionLabels: ActionLabelsI18n,
+ private smbService: SmbService
+ ) {
+ super();
+ this.permission = this.authStorageService.getPermissions().smb;
+ }
+
+ ngOnInit() {
+ this.columns = [
+ {
+ name: $localize`Name`,
+ prop: 'cluster_id',
+ flexGrow: 2
+ },
+ {
+ name: $localize`Authentication Mode`,
+ prop: 'auth_mode',
+ flexGrow: 2
+ }
+ ];
+
+ this.smbClusters$ = this.subject$.pipe(
+ switchMap(() =>
+ this.smbService.listClusters().pipe(
+ catchError(() => {
+ this.context.error();
+ return of(null);
+ })
+ )
+ )
+ );
+ }
+
+ loadSMBCluster() {
+ this.subject$.next([]);
+ }
+}
--- /dev/null
+import { CephServicePlacement } from '~/app/shared/models/service.interface';
+
+export interface SMBCluster {
+ cluster_id: string;
+ auth_mode: AuthMode;
+ intent: string;
+ domain_settings?: DomainSettings;
+ user_group_settings?: string[];
+ custom_dns?: string[];
+ placement?: CephServicePlacement;
+ clustering?: string;
+ public_addrs?: PublicAddress;
+}
+
+export interface DomainSettings {
+ realm?: string;
+ join_sources_ref?: string[];
+}
+
+export interface PublicAddress {
+ address: string;
+ destination: string;
+}
+
+export interface AuthMode {
+ user: 'User';
+ activeDirectory: 'active-directory';
+}
--- /dev/null
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterModule } from '@angular/router';
+
+import { NgbNavModule, NgbTooltipModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
+
+import { SharedModule } from '~/app/shared/shared.module';
+
+import {
+ ButtonModule,
+ GridModule,
+ IconModule,
+ IconService,
+ InputModule,
+ SelectModule
+} from 'carbon-components-angular';
+
+import Close from '@carbon/icons/es/close/32';
+import { SmbClusterListComponent } from './smb-cluster-list/smb-cluster-list.component';
+
+@NgModule({
+ imports: [
+ ReactiveFormsModule,
+ RouterModule,
+ SharedModule,
+ NgbNavModule,
+ CommonModule,
+ NgbTypeaheadModule,
+ NgbTooltipModule,
+ GridModule,
+ SelectModule,
+ InputModule,
+ ButtonModule,
+ IconModule
+ ],
+ exports: [SmbClusterListComponent],
+ declarations: [SmbClusterListComponent]
+})
+export class SmbModule {
+ constructor(private iconService: IconService) {
+ this.iconService.registerAll([Close]);
+ }
+}
<!-- Filesystem -->
<cds-sidenav-menu title="File"
i18n-title
- *ngIf="permissions.nfs.read && enabledFeature.nfs || permissions.cephfs.read && enabledFeature.cephfs"
+ *ngIf="permissions.nfs.read && enabledFeature.nfs || permissions.cephfs.read && enabledFeature.cephfs || permissions.smb.read"
class="tc_menuitem_file">
<svg cdsIcon="file-storage"
icon
i18n-title
*ngIf="permissions.nfs.read && enabledFeature.nfs"
class="tc_submenuitem tc_submenuitem_file_nfs"><span i18n>NFS</span></cds-sidenav-item>
+ <cds-sidenav-item route="/cephfs/smb"
+ [useRouter]="true"
+ title="SMB"
+ i18n-title
+ *ngIf="permissions.smb.read"
+ class="tc_submenuitem tc_submenuitem_file_smb"><span i18n>SMB</span></cds-sidenav-item>
</cds-sidenav-menu>
<!-- Observability -->
<cds-sidenav-menu title="Observability"
--- /dev/null
+import { TestBed } from '@angular/core/testing';
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+
+import { SmbService } from './smb.service';
+import { configureTestBed } from '~/testing/unit-test-helper';
+
+describe('SmbService', () => {
+ let service: SmbService;
+ let httpTesting: HttpTestingController;
+
+ configureTestBed({
+ providers: [SmbService],
+ imports: [HttpClientTestingModule]
+ });
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(SmbService);
+ httpTesting = TestBed.inject(HttpTestingController);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+
+ it('should call list', () => {
+ service.listClusters().subscribe();
+ const req = httpTesting.expectOne('api/smb/cluster');
+ expect(req.request.method).toBe('GET');
+ });
+});
--- /dev/null
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+
+import { SMBCluster } from '~/app/ceph/smb/smb.model';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class SmbService {
+ baseURL = 'api/smb';
+
+ constructor(private http: HttpClient) {}
+
+ listClusters(): Observable<SMBCluster[]> {
+ return this.http.get<SMBCluster[]>(`${this.baseURL}/cluster`);
+ }
+}
rbdImage: { create: false, delete: false, read: false, update: false },
rbdMirroring: { create: false, delete: false, read: false, update: false },
rgw: { create: false, delete: false, read: false, update: false },
- user: { create: false, delete: false, read: false, update: false }
+ user: { create: false, delete: false, read: false, update: false },
+ smb: { create: false, delete: false, read: false, update: false }
});
});
'rbd-image': ['create', 'read', 'update', 'delete'],
'rbd-mirroring': ['create', 'read', 'update', 'delete'],
rgw: ['create', 'read', 'update', 'delete'],
- user: ['create', 'read', 'update', 'delete']
+ user: ['create', 'read', 'update', 'delete'],
+ smb: ['create', 'read', 'update', 'delete']
};
expect(new Permissions(fullyGranted)).toEqual({
cephfs: { create: true, delete: true, read: true, update: true },
rbdImage: { create: true, delete: true, read: true, update: true },
rbdMirroring: { create: true, delete: true, read: true, update: true },
rgw: { create: true, delete: true, read: true, update: true },
- user: { create: true, delete: true, read: true, update: true }
+ user: { create: true, delete: true, read: true, update: true },
+ smb: { create: true, delete: true, read: true, update: true }
});
});
});
grafana: Permission;
prometheus: Permission;
nfs: Permission;
+ smb: Permission;
constructor(serverPermissions: any) {
this.hosts = new Permission(serverPermissions['hosts']);
this.grafana = new Permission(serverPermissions['grafana']);
this.prometheus = new Permission(serverPermissions['prometheus']);
this.nfs = new Permission(serverPermissions['nfs-ganesha']);
+ this.smb = new Permission(serverPermissions['smb']);
}
}