]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Add NFS frontend services
authorTiago Melo <tspmelo@gmail.com>
Thu, 4 Oct 2018 16:14:26 +0000 (17:14 +0100)
committerTiago Melo <tmelo@suse.com>
Thu, 14 Feb 2019 10:29:14 +0000 (10:29 +0000)
Signed-off-by: Tiago Melo <tmelo@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/models/permission.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/models/permissions.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts

index 179ab0aedd73b295da8baf63b369eee5116f4f54..fcb99c2f0d0fc9139bb0badddae22e352704e688 100644 (file)
@@ -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 (file)
index 0000000..e5f039f
--- /dev/null
@@ -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 (file)
index 0000000..3ad1aad
--- /dev/null
@@ -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'
+    });
+  }
+}
index e2725bc856d8c67985010c4de6b1405692dd72e9..fb2c90469ccc2cb4e9bb7df09e15e730b4c12223 100644 (file)
@@ -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 },
index 0d8d580266c7a4e47fe29f99d516fb0da40770ed..3f2c87ed1a0fe109602775e1ff4f8c920674e7f6 100644 (file)
@@ -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']);
   }
 }
index 265ccdeec5476f6875679704d8a69b58cdffdcec..0806935989127bea19a25fd59ee638b94021a806 100644 (file)
@@ -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;
   }