]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Add Filesystems list component
authorTiago Melo <tmelo@suse.com>
Tue, 8 May 2018 15:17:10 +0000 (16:17 +0100)
committerTiago Melo <tmelo@suse.com>
Thu, 31 May 2018 21:50:43 +0000 (22:50 +0100)
Also adapted the existing cephfs components for them
to be used inside the new cephfs-list componment.

Fixes: https://tracker.ceph.com/issues/23825
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/ceph/cephfs/cephfs-list/cephfs-list.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs/cephfs.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs/cephfs.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/clients/clients.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/clients/clients.component.ts
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.html

index bca500fff68a0e9344f92821827da34e3152cd44..03ef61c751281f8368df23576567583e293e7363 100644 (file)
@@ -5,16 +5,13 @@ import { IscsiComponent } from './ceph/block/iscsi/iscsi.component';
 import { MirroringComponent } from './ceph/block/mirroring/mirroring.component';
 import { RbdFormComponent } from './ceph/block/rbd-form/rbd-form.component';
 import { RbdListComponent } from './ceph/block/rbd-list/rbd-list.component';
-import { CephfsComponent } from './ceph/cephfs/cephfs/cephfs.component';
-import { ClientsComponent } from './ceph/cephfs/clients/clients.component';
+import { CephfsListComponent } from './ceph/cephfs/cephfs-list/cephfs-list.component';
 import { ConfigurationComponent } from './ceph/cluster/configuration/configuration.component';
 import { HostsComponent } from './ceph/cluster/hosts/hosts.component';
 import { MonitorComponent } from './ceph/cluster/monitor/monitor.component';
 import { OsdListComponent } from './ceph/cluster/osd/osd-list/osd-list.component';
 import { DashboardComponent } from './ceph/dashboard/dashboard/dashboard.component';
-import {
-  PerformanceCounterComponent
-} from './ceph/performance-counter/performance-counter/performance-counter.component';
+import { PerformanceCounterComponent } from './ceph/performance-counter/performance-counter/performance-counter.component';
 import { PoolListComponent } from './ceph/pool/pool-list/pool-list.component';
 import { Rgw501Component } from './ceph/rgw/rgw-501/rgw-501.component';
 import { RgwBucketFormComponent } from './ceph/rgw/rgw-bucket-form/rgw-bucket-form.component';
@@ -107,17 +104,16 @@ const routes: Routes = [
     canActivate: [AuthGuardService]
   },
   { path: 'monitor', component: MonitorComponent, canActivate: [AuthGuardService] },
-  { path: 'cephfs/:id/clients', component: ClientsComponent, canActivate: [AuthGuardService] },
-  { path: 'cephfs/:id', component: CephfsComponent, canActivate: [AuthGuardService] },
+  { path: 'cephfs', component: CephfsListComponent, canActivate: [AuthGuardService] },
   { path: 'configuration', component: ConfigurationComponent, canActivate: [AuthGuardService] },
   { path: 'mirroring', component: MirroringComponent, canActivate: [AuthGuardService] },
   { path: '404', component: NotFoundComponent },
   { path: 'osd', component: OsdListComponent, canActivate: [AuthGuardService] },
-  { path: '**', redirectTo: '/404'}
+  { path: '**', redirectTo: '/404' }
 ];
 
 @NgModule({
   imports: [RouterModule.forRoot(routes, { useHash: true })],
   exports: [RouterModule]
 })
-export class AppRoutingModule { }
+export class AppRoutingModule {}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.html
new file mode 100644 (file)
index 0000000..e72238d
--- /dev/null
@@ -0,0 +1,19 @@
+<nav aria-label="breadcrumb">
+  <ol class="breadcrumb">
+    <li i18n
+        class="breadcrumb-item active">Filesystems</li>
+  </ol>
+</nav>
+
+<cd-table [data]="filesystems"
+          columnMode="flex"
+          [columns]="columns"
+          (fetchData)="loadFilesystems()"
+          identifier="id"
+          forceIdentifier="true"
+          selectionType="single"
+          (updateSelection)="updateSelection($event)">
+  <cd-cephfs cdTableDetail
+             [selection]="selection">
+  </cd-cephfs>
+</cd-table>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.scss
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.spec.ts
new file mode 100644 (file)
index 0000000..9649715
--- /dev/null
@@ -0,0 +1,45 @@
+import { Component, Input } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { Observable } from 'rxjs/Observable';
+
+import { CephfsService } from '../../../shared/api/cephfs.service';
+import { CdTableSelection } from '../../../shared/models/cd-table-selection';
+import { SharedModule } from '../../../shared/shared.module';
+import { CephfsListComponent } from './cephfs-list.component';
+
+@Component({ selector: 'cd-cephfs', template: '' })
+class CephfsStubComponent {
+  @Input() selection: CdTableSelection;
+}
+
+describe('CephfsListComponent', () => {
+  let component: CephfsListComponent;
+  let fixture: ComponentFixture<CephfsListComponent>;
+
+  const fakeService = {
+    get: (service_type: string, service_id: string) => {
+      return Observable.create((observer) => {
+        return () => console.log('disposed');
+      });
+    }
+  };
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [SharedModule],
+      declarations: [CephfsListComponent, CephfsStubComponent],
+      providers: [{ provide: CephfsService, useValue: fakeService }]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CephfsListComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-list/cephfs-list.component.ts
new file mode 100644 (file)
index 0000000..d6ac4d1
--- /dev/null
@@ -0,0 +1,50 @@
+import { Component, OnInit } from '@angular/core';
+
+import * as _ from 'lodash';
+
+import { CephfsService } from '../../../shared/api/cephfs.service';
+import { CdTableColumn } from '../../../shared/models/cd-table-column';
+import { CdTableSelection } from '../../../shared/models/cd-table-selection';
+
+@Component({
+  selector: 'cd-cephfs-list',
+  templateUrl: './cephfs-list.component.html',
+  styleUrls: ['./cephfs-list.component.scss']
+})
+export class CephfsListComponent implements OnInit {
+  columns: CdTableColumn[];
+  filesystems: any = [];
+  selection = new CdTableSelection();
+
+  constructor(private cephfsService: CephfsService) {}
+
+  ngOnInit() {
+    this.columns = [
+      {
+        name: 'Name',
+        prop: 'mdsmap.fs_name',
+        flexGrow: 2
+      },
+      {
+        name: 'Created',
+        prop: 'mdsmap.created',
+        flexGrow: 2
+      },
+      {
+        name: 'Enabled',
+        prop: 'mdsmap.enabled',
+        flexGrow: 1
+      }
+    ];
+  }
+
+  loadFilesystems() {
+    this.cephfsService.list().subscribe((resp: any[]) => {
+      this.filesystems = resp;
+    });
+  }
+
+  updateSelection(selection: CdTableSelection) {
+    this.selection = selection;
+  }
+}
index 16ccbf41e208fb490bdcb1cba8a941d5bde77359..62a9f368042965110815d2b616c9a3bbd4823fb8 100644 (file)
@@ -3,10 +3,12 @@ import { NgModule } from '@angular/core';
 
 import { ChartsModule } from 'ng2-charts/ng2-charts';
 import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
+import { TabsModule } from 'ngx-bootstrap/tabs';
 
 import { AppRoutingModule } from '../../app-routing.module';
 import { SharedModule } from '../../shared/shared.module';
 import { CephfsChartComponent } from './cephfs-chart/cephfs-chart.component';
+import { CephfsListComponent } from './cephfs-list/cephfs-list.component';
 import { CephfsComponent } from './cephfs/cephfs.component';
 import { ClientsComponent } from './clients/clients.component';
 
@@ -16,8 +18,9 @@ import { ClientsComponent } from './clients/clients.component';
     SharedModule,
     AppRoutingModule,
     ChartsModule,
-    ProgressbarModule.forRoot()
+    ProgressbarModule.forRoot(),
+    TabsModule.forRoot()
   ],
-  declarations: [CephfsComponent, ClientsComponent, CephfsChartComponent]
+  declarations: [CephfsComponent, ClientsComponent, CephfsChartComponent, CephfsListComponent]
 })
 export class CephfsModule {}
index 9da4446f39bd631463690c0ae67c3199decd6994..219defb8ffb4d1cf7535b38e4b78d0563d71d34f 100644 (file)
@@ -1,67 +1,59 @@
-<nav aria-label="breadcrumb">
-  <ol class="breadcrumb">
-    <li i18n
-        class="breadcrumb-item">Filesystem</li>
-    <li class="breadcrumb-item active"
-        aria-current="page">{{ name }}</li>
-  </ol>
-</nav>
-
-<div class="row">
-  <div class="col-md-12">
-    <i class="fa fa-desktop"></i>
-    <a i18n
-       [routerLink]="['/cephfs/' + id + '/clients']">
-      <span style="font-weight:bold;">{{ clientCount }}</span>
-      Clients
-    </a>
-  </div>
-</div>
-
-<div class="row">
-  <div class="col-sm-6">
-    <fieldset>
-      <legend i18n>Ranks</legend>
-
-      <cd-table [data]="ranks.data"
-                [columns]="ranks.columns"
-                (fetchData)="refresh()"
-                [toolHeader]="false">
-      </cd-table>
-    </fieldset>
-
-    <cd-table-key-value [data]="standbys">
-    </cd-table-key-value>
-  </div>
-
-  <div class="col-sm-6">
-    <fieldset>
-      <legend i18n>Pools</legend>
-
-      <cd-table [data]="pools.data"
-                [columns]="pools.columns"
-                [toolHeader]="false">
-      </cd-table>
-
-    </fieldset>
-  </div>
-</div>
-
-<div class="row"
-     *ngFor="let mdsCounter of objectValues(mdsCounters); trackBy: trackByFn">
-  <div class="cold-md-12">
-    <cd-cephfs-chart [mdsCounter]="mdsCounter"></cd-cephfs-chart>
-  </div>
-</div>
-
-<!-- templates -->
-<ng-template #poolUsageTpl
-             let-row="row">
-    <cd-usage-bar [totalBytes]="row.size" [usedBytes]="row.used"></cd-usage-bar>
-</ng-template>
-
-<ng-template #activityTmpl
-             let-row="row"
-             let-value="value">
-  {{ row.state === 'standby-replay' ? 'Evts' : 'Reqs' }}: {{ value | dimless }} /s
-</ng-template>
+<tabset *ngIf="selectedItem">
+  <tab i18n-heading
+       heading="Details">
+    <div class="row">
+      <div class="col-sm-6">
+        <fieldset>
+          <legend i18n>Ranks</legend>
+
+          <cd-table [data]="ranks.data"
+                    [columns]="ranks.columns"
+                    (fetchData)="refresh()"
+                    [toolHeader]="false">
+          </cd-table>
+        </fieldset>
+
+        <cd-table-key-value [data]="standbys">
+        </cd-table-key-value>
+      </div>
+
+      <div class="col-sm-6">
+        <fieldset>
+          <legend i18n>Pools</legend>
+
+          <cd-table [data]="pools.data"
+                    [columns]="pools.columns"
+                    [toolHeader]="false">
+          </cd-table>
+
+        </fieldset>
+      </div>
+    </div>
+
+    <div class="row"
+         *ngFor="let mdsCounter of objectValues(mdsCounters); trackBy: trackByFn">
+      <div class="cold-md-12">
+        <cd-cephfs-chart [mdsCounter]="mdsCounter"></cd-cephfs-chart>
+      </div>
+    </div>
+
+    <!-- templates -->
+    <ng-template #poolUsageTpl
+                 let-row="row">
+      <cd-usage-bar [totalBytes]="row.size"
+                    [usedBytes]="row.used"></cd-usage-bar>
+    </ng-template>
+
+    <ng-template #activityTmpl
+                 let-row="row"
+                 let-value="value">
+      {{ row.state === 'standby-replay' ? 'Evts' : 'Reqs' }}: {{ value | dimless }} /s
+    </ng-template>
+  </tab>
+  <tab i18n-heading
+       heading="Clients: {{ clientCount }}"
+       (select)="clientsSelect=true"
+       (deselect)="clientsSelect=false">
+    <cd-clients [id]="id" *ngIf="clientsSelect"></cd-clients>
+  </tab>
+</tabset>
index 9e9b64ba749b0cd29cdcf4177aecfe228e0ceac2..f35dee2519b508ba4fa9f20ae582f0afdb1a76d1 100644 (file)
@@ -1,9 +1,9 @@
-import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
+import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core';
 
 import * as _ from 'lodash';
 
 import { CephfsService } from '../../../shared/api/cephfs.service';
+import { CdTableSelection } from '../../../shared/models/cd-table-selection';
 import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
 import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
 
@@ -12,11 +12,13 @@ import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
   templateUrl: './cephfs.component.html',
   styleUrls: ['./cephfs.component.scss']
 })
-export class CephfsComponent implements OnInit {
+export class CephfsComponent implements OnChanges, OnInit {
   @ViewChild('poolUsageTpl') poolUsageTpl: TemplateRef<any>;
   @ViewChild('activityTmpl') activityTmpl: TemplateRef<any>;
 
-  objectValues = Object.values;
+  @Input() selection: CdTableSelection;
+
+  selectedItem: any;
 
   id: number;
   name: string;
@@ -24,16 +26,31 @@ export class CephfsComponent implements OnInit {
   pools: any;
   standbys = [];
   clientCount: number;
-
   mdsCounters = {};
 
+  objectValues = Object.values;
+  clientsSelect = false;
+
   constructor(
-    private route: ActivatedRoute,
     private cephfsService: CephfsService,
     private dimlessBinary: DimlessBinaryPipe,
     private dimless: DimlessPipe
   ) {}
 
+  ngOnChanges() {
+    if (this.selection.hasSelection) {
+      this.selectedItem = this.selection.first();
+
+      if (this.id !== this.selectedItem.id) {
+        this.id = this.selectedItem.id;
+        this.ranks.data = [];
+        this.pools.data = [];
+        this.standbys = [];
+        this.mdsCounters = {};
+      }
+    }
+  }
+
   ngOnInit() {
     this.ranks = {
       columns: [
@@ -73,15 +90,6 @@ export class CephfsComponent implements OnInit {
       ],
       data: []
     };
-
-    this.route.params.subscribe((params: { id: number }) => {
-      this.id = params.id;
-
-      this.ranks.data = [];
-      this.pools.data = [];
-      this.standbys = [];
-      this.mdsCounters = {};
-    });
   }
 
   refresh() {
@@ -94,14 +102,14 @@ export class CephfsComponent implements OnInit {
       this.standbys = [
         {
           key: 'Standby daemons',
-          value: data.standbys.map(value => value.name).join(', ')
+          value: data.standbys.map((value) => value.name).join(', ')
         }
       ];
       this.name = data.cephfs.name;
       this.clientCount = data.cephfs.client_count;
     });
 
-    this.cephfsService.getMdsCounters(this.id).subscribe(data => {
+    this.cephfsService.getMdsCounters(this.id).subscribe((data) => {
       _.each(this.mdsCounters, (value, key) => {
         if (data[key] === undefined) {
           delete this.mdsCounters[key];
index 7832a38744f0f8a085f368341f8fc2c1a7367e8b..ae2e2160b1974c5a8e7b44677e4dbc5676f88b9b 100644 (file)
@@ -1,22 +1,7 @@
-<nav aria-label="breadcrumb">
-  <ol class="breadcrumb">
-    <li i18n
-        class="breadcrumb-item">Filesystem</li>
-    <li class="breadcrumb-item">
-      <a [routerLink]="['/cephfs/' + id]">{{ name }}</a>
-    </li>
-    <li i18n
-        class="breadcrumb-item active"
-        aria-current="page">Clients</li>
-  </ol>
-</nav>
+<cd-view-cache [status]="viewCacheStatus"></cd-view-cache>
 
-<fieldset>
-  <cd-view-cache [status]="viewCacheStatus"></cd-view-cache>
-
-  <cd-table [data]="clients.data"
-            [columns]="clients.columns"
-            (fetchData)="refresh()"
-            [header]="false">
-  </cd-table>
-</fieldset>
+<cd-table [data]="clients.data"
+          [columns]="clients.columns"
+          (fetchData)="refresh()"
+          [header]="false">
+</cd-table>
index 49aacd85b6b19d4365553f7add22ba352067f480..f5957166993e31d5ec2ba04064b1cceacc7af170 100644 (file)
@@ -1,5 +1,4 @@
-import { Component, OnInit } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
+import { Component, Input, OnInit } from '@angular/core';
 
 import { CephfsService } from '../../../shared/api/cephfs.service';
 import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
@@ -10,13 +9,12 @@ import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
   styleUrls: ['./clients.component.scss']
 })
 export class ClientsComponent implements OnInit {
+  @Input() id: number;
 
-  id: number;
-  name: string;
   clients: any;
   viewCacheStatus: ViewCacheStatus;
 
-  constructor(private route: ActivatedRoute, private cephfsService: CephfsService) {}
+  constructor(private cephfsService: CephfsService) {}
 
   ngOnInit() {
     this.clients = {
@@ -31,15 +29,8 @@ export class ClientsComponent implements OnInit {
       data: []
     };
 
-    this.route.params.subscribe((params: { id: number }) => {
-      this.id = params.id;
-      this.clients.data = [];
-      this.viewCacheStatus = ViewCacheStatus.ValueNone;
-
-      this.cephfsService.getCephfs(this.id).subscribe((data: any) => {
-        this.name = data.cephfs.name;
-      });
-    });
+    this.clients.data = [];
+    this.viewCacheStatus = ViewCacheStatus.ValueNone;
   }
 
   refresh() {
index 6444f8be56cfdd7b532f32b86f7f3b06107114be..942436cb949c85c80176817d0b93df536c3c937d 100644 (file)
       </li>
 
       <!-- Filesystem -->
-      <li dropdown
-          routerLinkActive="active"
-          class="dropdown tc_menuitem tc_menuitem_cephs">
-        <a dropdownToggle
-           class="dropdown-toggle"
-           data-toggle="dropdown">
-          <ng-container i18n>Filesystems</ng-container>
-          <span class="caret"></span>
+      <li routerLinkActive="active"
+          class="tc_menuitem tc_menuitem_cephs">
+        <a i18n
+           routerLink="/cephfs">Filesystems
         </a>
-        <ul *dropdownMenu
-            class="dropdown-menu">
-          <li routerLinkActive="active"
-              class="tc_submenuitem tc_submenuitem_cephfs_fs"
-              *ngFor="let fs of summaryData?.filesystems">
-            <a i18n
-               class="dropdown-item"
-               routerLink="/cephfs/{{fs.id}}">{{ fs.name }}
-            </a>
-          </li>
-          <li class="tc_submenuitem tc_submenuitem_cephfs_nofs"
-              *ngIf="summaryData.filesystems.length === 0">
-            <span i18n>There are no filesystems</span>
-          </li>
-        </ul>
       </li>
       <!--
   <li routerLinkActive="active"