From a7142a2105136d9ce03a1fdfc95684c836365f51 Mon Sep 17 00:00:00 2001 From: Tiago Melo Date: Thu, 4 Oct 2018 17:14:26 +0100 Subject: [PATCH] mgr/dashboard: Add NFS frontend services Signed-off-by: Tiago Melo --- .../frontend/src/app/app-routing.module.ts | 16 +++ .../src/app/shared/api/nfs.service.spec.ts | 90 ++++++++++++++ .../src/app/shared/api/nfs.service.ts | 113 ++++++++++++++++++ .../src/app/shared/models/permission.spec.ts | 2 + .../src/app/shared/models/permissions.ts | 2 + .../shared/services/task-message.service.ts | 13 ++ 6 files changed, 236 insertions(+) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.ts diff --git a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts index 179ab0aedd73b..fcb99c2f0d0fc 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts @@ -305,6 +305,22 @@ const routes: Routes = [ } ] }, + // NFS + { + path: 'nfs', + canActivate: [AuthGuardService], + canActivateChild: [AuthGuardService], + data: { breadcrumbs: 'NFS' }, + children: [ + { path: '', component: NfsListComponent }, + { path: 'add', component: NfsFormComponent, data: { breadcrumbs: 'Add' } }, + { + path: 'edit/:cluster_id/:export_id', + component: NfsFormComponent, + data: { breadcrumbs: 'Edit' } + } + ] + }, // Single Sign-On (SSO) { path: 'sso/404', component: SsoNotFoundComponent }, // System diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.spec.ts new file mode 100644 index 0000000000000..e5f039ff39e97 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.spec.ts @@ -0,0 +1,90 @@ +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; + +import { configureTestBed, i18nProviders } from '../../../testing/unit-test-helper'; +import { NfsService } from './nfs.service'; + +describe('NfsService', () => { + let service: NfsService; + let httpTesting: HttpTestingController; + + configureTestBed({ + providers: [NfsService, i18nProviders], + imports: [HttpClientTestingModule] + }); + + beforeEach(() => { + service = TestBed.get(NfsService); + httpTesting = TestBed.get(HttpTestingController); + }); + + afterEach(() => { + httpTesting.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should call list', () => { + service.list().subscribe(); + const req = httpTesting.expectOne('api/nfs-ganesha/export'); + expect(req.request.method).toBe('GET'); + }); + + it('should call get', () => { + service.get('cluster_id', 'export_id').subscribe(); + const req = httpTesting.expectOne('api/nfs-ganesha/export/cluster_id/export_id'); + expect(req.request.method).toBe('GET'); + }); + + it('should call create', () => { + service.create('foo').subscribe(); + const req = httpTesting.expectOne('api/nfs-ganesha/export'); + expect(req.request.method).toBe('POST'); + expect(req.request.body).toEqual('foo'); + }); + + it('should call update', () => { + service.update('cluster_id', 'export_id', 'foo').subscribe(); + const req = httpTesting.expectOne('api/nfs-ganesha/export/cluster_id/export_id'); + expect(req.request.body).toEqual('foo'); + expect(req.request.method).toBe('PUT'); + }); + + it('should call delete', () => { + service.delete('hostName', 'exportId').subscribe(); + const req = httpTesting.expectOne('api/nfs-ganesha/export/hostName/exportId'); + expect(req.request.method).toBe('DELETE'); + }); + + it('should call lsDir', () => { + service.lsDir('foo_dir').subscribe(); + const req = httpTesting.expectOne('ui-api/nfs-ganesha/lsdir?root_dir=foo_dir'); + expect(req.request.method).toBe('GET'); + }); + + it('should call buckets', () => { + service.buckets('user_foo').subscribe(); + const req = httpTesting.expectOne('ui-api/nfs-ganesha/rgw/buckets?user_id=user_foo'); + expect(req.request.method).toBe('GET'); + }); + + it('should call daemon', () => { + service.daemon().subscribe(); + const req = httpTesting.expectOne('api/nfs-ganesha/daemon'); + expect(req.request.method).toBe('GET'); + }); + + it('should call start', () => { + service.start('host_name').subscribe(); + const req = httpTesting.expectOne('api/nfs-ganesha/service/host_name/start'); + expect(req.request.method).toBe('PUT'); + }); + + it('should call stop', () => { + service.stop('host_name').subscribe(); + const req = httpTesting.expectOne('api/nfs-ganesha/service/host_name/stop'); + expect(req.request.method).toBe('PUT'); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.ts new file mode 100644 index 0000000000000..3ad1aadcd3c53 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.ts @@ -0,0 +1,113 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; + +import { I18n } from '@ngx-translate/i18n-polyfill'; + +import { ApiModule } from './api.module'; + +@Injectable({ + providedIn: ApiModule +}) +export class NfsService { + apiPath = 'api/nfs-ganesha'; + uiApiPath = 'ui-api/nfs-ganesha'; + + nfsAccessType = [ + { + value: 'RW', + help: this.i18n('Allows all operations') + }, + { + value: 'RO', + help: this.i18n('Allows only operations that do not modify the server') + }, + { + value: 'MDONLY', + help: this.i18n('Does not allow read or write operations, but allows any other operation') + }, + { + value: 'MDONLY_RO', + help: this.i18n( + 'Does not allow read, write, or any operation that modifies file \ + attributes or directory content' + ) + }, + { + value: 'NONE', + help: this.i18n('Allows no access at all') + } + ]; + + nfsFsal = [ + { + value: 'CEPH', + descr: this.i18n('CephFS') + }, + { + value: 'RGW', + descr: this.i18n('Object Gateway') + } + ]; + + nfsSquash = ['no_root_squash', 'root_id_squash', 'root_squash', 'all_squash']; + + constructor(private http: HttpClient, private i18n: I18n) {} + + list() { + return this.http.get(`${this.apiPath}/export`); + } + + get(clusterId, exportId) { + return this.http.get(`${this.apiPath}/export/${clusterId}/${exportId}`); + } + + create(nfs) { + return this.http.post(`${this.apiPath}/export`, nfs, { observe: 'response' }); + } + + update(clusterId, id, nfs) { + return this.http.put(`${this.apiPath}/export/${clusterId}/${id}`, nfs, { observe: 'response' }); + } + + delete(clusterId, exportId) { + return this.http.delete(`${this.apiPath}/export/${clusterId}/${exportId}`, { + observe: 'response' + }); + } + + lsDir(root_dir) { + return this.http.get(`${this.uiApiPath}/lsdir?root_dir=${root_dir}`); + } + + buckets(user_id) { + return this.http.get(`${this.uiApiPath}/rgw/buckets?user_id=${user_id}`); + } + + clients() { + return this.http.get(`${this.uiApiPath}/cephx/clients`); + } + + fsals() { + return this.http.get(`${this.uiApiPath}/fsals`); + } + + filesystems() { + return this.http.get(`${this.uiApiPath}/cephfs/filesystems`); + } + + daemon() { + return this.http.get(`${this.apiPath}/daemon`); + } + + start(host_name: string) { + return this.http.put(`${this.apiPath}/service/${host_name}/start`, null, { + observe: 'response' + }); + } + + stop(host_name: string) { + return this.http.put(`${this.apiPath}/service/${host_name}/stop`, null, { + observe: 'response' + }); + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permission.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permission.spec.ts index e2725bc856d8c..fb2c90469ccc2 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permission.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permission.spec.ts @@ -11,6 +11,7 @@ describe('cd-notification classes', () => { log: { create: false, delete: false, read: false, update: false }, manager: { create: false, delete: false, read: false, update: false }, monitor: { create: false, delete: false, read: false, update: false }, + nfs: { create: false, delete: false, read: false, update: false }, osd: { create: false, delete: false, read: false, update: false }, pool: { create: false, delete: false, read: false, update: false }, prometheus: { create: false, delete: false, read: false, update: false }, @@ -48,6 +49,7 @@ describe('cd-notification classes', () => { log: { create: true, delete: true, read: true, update: true }, manager: { create: true, delete: true, read: true, update: true }, monitor: { create: true, delete: true, read: true, update: true }, + nfs: { create: false, delete: false, read: false, update: false }, osd: { create: true, delete: true, read: true, update: true }, pool: { create: true, delete: true, read: true, update: true }, prometheus: { create: true, delete: true, read: true, update: true }, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permissions.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permissions.ts index 0d8d580266c7a..3f2c87ed1a0fe 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permissions.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permissions.ts @@ -27,6 +27,7 @@ export class Permissions { user: Permission; grafana: Permission; prometheus: Permission; + nfs: Permission; constructor(serverPermissions: any) { this.hosts = new Permission(serverPermissions['hosts']); @@ -44,5 +45,6 @@ export class Permissions { this.user = new Permission(serverPermissions['user']); this.grafana = new Permission(serverPermissions['grafana']); this.prometheus = new Permission(serverPermissions['prometheus']); + this.nfs = new Permission(serverPermissions['nfs-ganesha']); } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts index 265ccdeec5476..0806935989127 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts @@ -321,6 +321,13 @@ export class TaskMessageService { ), 'iscsi/target/delete': this.newTaskMessage(this.commonOperations.delete, (metadata) => this.iscsiTarget(metadata) + ), + 'nfs/create': this.newTaskMessage(this.commonOperations.create, (metadata) => + this.nfs(metadata) + ), + 'nfs/edit': this.newTaskMessage(this.commonOperations.update, (metadata) => this.nfs(metadata)), + 'nfs/delete': this.newTaskMessage(this.commonOperations.delete, (metadata) => + this.nfs(metadata) ) }; @@ -346,6 +353,12 @@ export class TaskMessageService { return this.i18n(`target '{{target_iqn}}'`, { target_iqn: metadata.target_iqn }); } + nfs(metadata) { + return this.i18n(`NFS {{nfs_id}}`, { + nfs_id: `'${metadata.cluster_id}:${metadata.export_id ? metadata.export_id : metadata.path}'` + }); + } + _getTaskTitle(task: Task) { return this.messages[task.name] || this.defaultMessage; } -- 2.39.5