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';
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 {}
--- /dev/null
+<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>
--- /dev/null
+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();
+ });
+});
--- /dev/null
+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;
+ }
+}
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';
SharedModule,
AppRoutingModule,
ChartsModule,
- ProgressbarModule.forRoot()
+ ProgressbarModule.forRoot(),
+ TabsModule.forRoot()
],
- declarations: [CephfsComponent, ClientsComponent, CephfsChartComponent]
+ declarations: [CephfsComponent, ClientsComponent, CephfsChartComponent, CephfsListComponent]
})
export class CephfsModule {}
-<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>
-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';
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;
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: [
],
data: []
};
-
- this.route.params.subscribe((params: { id: number }) => {
- this.id = params.id;
-
- this.ranks.data = [];
- this.pools.data = [];
- this.standbys = [];
- this.mdsCounters = {};
- });
}
refresh() {
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];
-<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>
-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';
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 = {
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() {
</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"