import { PoolDetailComponent } from './ceph/block/pool-detail/pool-detail.component';
import { CephfsComponent } from './ceph/cephfs/cephfs/cephfs.component';
import { ClientsComponent } from './ceph/cephfs/clients/clients.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 { DashboardComponent } from './ceph/dashboard/dashboard/dashboard.component';
{ path: 'monitor', component: MonitorComponent, canActivate: [AuthGuardService] },
{ path: 'cephfs/:id/clients', component: ClientsComponent, canActivate: [AuthGuardService] },
{ path: 'cephfs/:id', component: CephfsComponent, canActivate: [AuthGuardService] },
+ { path: 'configuration', component: ConfigurationComponent, canActivate: [AuthGuardService] },
{ path: '404', component: NotFoundComponent },
{ path: '**', redirectTo: '/404'}
];
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
+import { SharedModule } from '../shared/shared.module';
import { BlockModule } from './block/block.module';
import { CephfsModule } from './cephfs/cephfs.module';
import { ClusterModule } from './cluster/cluster.module';
DashboardModule,
RgwModule,
BlockModule,
- CephfsModule
+ CephfsModule,
+ SharedModule
],
declarations: []
})
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { ComponentsModule } from '../../shared/components/components.module';
import { SharedModule } from '../../shared/shared.module';
+import { ConfigurationComponent } from './configuration/configuration.component';
import { HostsComponent } from './hosts/hosts.component';
import { MonitorService } from './monitor.service';
import { MonitorComponent } from './monitor/monitor.component';
CommonModule,
ComponentsModule,
SharedModule,
- RouterModule
+ RouterModule,
+ FormsModule
],
declarations: [
HostsComponent,
MonitorComponent,
+ ConfigurationComponent
],
providers: [
MonitorService
--- /dev/null
+<nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item">Cluster</li>
+ <li class="breadcrumb-item active"
+ aria-current="page">Configuration Documentation</li>
+ </ol>
+</nav>
+
+<div class="dataTables_wrapper">
+ <div class="dataTables_header clearfix form-inline">
+ <!-- filters -->
+ <div class="form-group pull-right filter"
+ *ngFor="let filter of filters">
+ <label>{{ filter.label }}: </label>
+ <select class="form-control input-sm"
+ [(ngModel)]="filter.value"
+ (ngModelChange)="updateFilter()">
+ <option *ngFor="let opt of filter.options">{{ opt }}</option>
+ </select>
+ </div>
+ <!-- end filters -->
+ </div>
+
+ <table class="oadatatable table table-striped table-condensed table-bordered table-hover">
+ <thead class="datatable-header">
+ <tr>
+ <th >Name</th>
+ <th style="width:400px;">Description</th>
+ <th>Type</th>
+ <th>Level</th>
+ <th style="width: 200px">Default</th>
+ <th>Tags</th>
+ <th>Services</th>
+ <th>See_also</th>
+ <th>Max</th>
+ <th>Min</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngFor="let row of data | filter:filters">
+ <td >{{ row.name }}</td>
+ <td>
+ <p>
+ {{ row.desc }}</p>
+ <p *ngIf="row.long_desc"
+ class=text-muted>{{ row.long_desc }}</p>
+ </td>
+ <td>{{ row.type }}</td>
+ <td>{{ row.level }}</td>
+ <td class="wrap">
+ {{ row.default }} {{ row.daemon_default }}
+ </td>
+ <td>
+ <p *ngFor="let item of row.tags">{{ item }}</p>
+ </td>
+ <td>
+ <p *ngFor="let item of row.services">{{ item }}</p>
+ </td>
+ <td class="wrap">
+ <p *ngFor="let item of row.see_also">{{ item }}</p>
+ </td>
+ <td>{{ row.max }}</td>
+ <td>{{ row.min }}</td>
+ </tr>
+ </tbody>
+ </table>
+</div>
--- /dev/null
+@import '../../../shared/datatable/table/table.component.scss';
+
+td.wrap {
+ word-break: break-all;
+}
--- /dev/null
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule } from '@angular/forms';
+
+import { Observable } from 'rxjs/Observable';
+
+import { ConfigurationService } from '../../../shared/services/configuration.service';
+import { SharedModule } from '../../../shared/shared.module';
+import { ConfigurationComponent } from './configuration.component';
+
+describe('ConfigurationComponent', () => {
+ let component: ConfigurationComponent;
+ let fixture: ComponentFixture<ConfigurationComponent>;
+
+ const fakeService = {
+ getConfigData: () => {
+ return Observable.create(observer => {
+ return () => console.log('disposed');
+ });
+ }
+ };
+
+ beforeEach(
+ async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ConfigurationComponent],
+ providers: [{ provide: ConfigurationService, useValue: fakeService }],
+ imports: [SharedModule, FormsModule]
+ }).compileComponents();
+ })
+ );
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ConfigurationComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
+
+import { ConfigurationService } from '../../../shared/services/configuration.service';
+
+@Component({
+ selector: 'cd-configuration',
+ templateUrl: './configuration.component.html',
+ styleUrls: ['./configuration.component.scss']
+})
+export class ConfigurationComponent implements OnInit {
+ @ViewChild('arrayTmpl') arrayTmpl: TemplateRef<any>;
+
+ data = [];
+ columns: any;
+
+ filters = [
+ {
+ label: 'Level',
+ prop: 'level',
+ value: 'basic',
+ options: ['basic', 'advanced', 'developer'],
+ applyFilter: (row, value) => {
+ enum Level {
+ basic = 0,
+ advanced = 1,
+ developer = 2
+ }
+
+ const levelVal = Level[value];
+
+ return Level[row.level] <= levelVal;
+ }
+ },
+ {
+ label: 'Service',
+ prop: 'services',
+ value: 'any',
+ options: ['mon', 'mgr', 'osd', 'mds', 'common', 'mds_client', 'rgw', 'any'],
+ applyFilter: (row, value) => {
+ if (value === 'any') {
+ return true;
+ }
+
+ return row.services.includes(value);
+ }
+ }
+ ];
+
+ constructor(private configurationService: ConfigurationService) {}
+
+ ngOnInit() {
+ this.columns = [
+ { flexGrow: 2, canAutoResize: true, prop: 'name' },
+ { flexGrow: 2, prop: 'desc', name: 'Description' },
+ { flexGrow: 2, prop: 'long_desc', name: 'Long description' },
+ { flexGrow: 1, prop: 'type' },
+ { flexGrow: 1, prop: 'level' },
+ { flexGrow: 1, prop: 'default' },
+ { flexGrow: 2, prop: 'daemon_default', name: 'Daemon default' },
+ { flexGrow: 1, prop: 'tags', name: 'Tags', cellTemplate: this.arrayTmpl },
+ { flexGrow: 1, prop: 'services', name: 'Services', cellTemplate: this.arrayTmpl },
+ { flexGrow: 1, prop: 'see_also', name: 'See_also', cellTemplate: this.arrayTmpl },
+ { flexGrow: 1, prop: 'max', name: 'Max' },
+ { flexGrow: 1, prop: 'min', name: 'Min' }
+ ];
+
+ this.fetchData();
+ }
+
+ fetchData() {
+ this.configurationService.getConfigData().subscribe((data: any) => {
+ this.data = data;
+ });
+ }
+
+ updateFilter() {
+ this.data = [...this.data];
+ }
+}
routerLink="/monitor/"> Monitors
</a>
</li>
+
+ <li routerLinkActive="active"
+ class="tc_submenuitem tc_submenuitem_configuration">
+ <a i18n
+ class="dropdown-item"
+ routerLink="/configuration">Configuration Doc.
+ </a>
+ </li>
</ul>
</li>
routerLink="/block/iscsi">iSCSI</a>
</li>
<li class="dropdown-submenu">
- <a class="dropdown-toggle" data-toggle="dropdown">Pools</a>
+ <a class="dropdown-toggle"
+ data-toggle="dropdown">Pools</a>
<ul *dropdownMenu
class="dropdown-menu">
<li routerLinkActive="active"
</li>
<li class="tc_submenuitem tc_submenuitem_cephfs_nofs"
*ngIf="rbdPools.length === 0">
- <a class="dropdown-item disabled" i18n>There are no pools</a>
+ <a class="dropdown-item disabled"
+ i18n>There are no pools</a>
</li>
</ul>
</li>
--- /dev/null
+import { FilterPipe } from './filter.pipe';
+
+describe('FilterPipe', () => {
+ it('create an instance', () => {
+ const pipe = new FilterPipe();
+ expect(pipe).toBeTruthy();
+ });
+});
--- /dev/null
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'filter'
+})
+export class FilterPipe implements PipeTransform {
+ transform(value: any, args?: any): any {
+ return value.filter(row => {
+ let result = true;
+
+ args.forEach(filter => {
+ if (!filter.value) {
+ return;
+ }
+
+ result = result && filter.applyFilter(row, filter.value);
+ if (!result) {
+ return result;
+ }
+ });
+
+ return result;
+ });
+ }
+}
import { CephShortVersionPipe } from './ceph-short-version.pipe';
import { DimlessBinaryPipe } from './dimless-binary.pipe';
import { DimlessPipe } from './dimless.pipe';
+import { FilterPipe } from './filter.pipe';
import { HealthColorPipe } from './health-color.pipe';
import { ListPipe } from './list.pipe';
import { RelativeDatePipe } from './relative-date.pipe';
DimlessPipe,
CephShortVersionPipe,
RelativeDatePipe,
- ListPipe
+ ListPipe,
+ FilterPipe
],
exports: [
DimlessBinaryPipe,
DimlessPipe,
CephShortVersionPipe,
RelativeDatePipe,
- ListPipe
+ ListPipe,
+ FilterPipe
],
providers: [
CephShortVersionPipe,
--- /dev/null
+import { HttpClientModule } from '@angular/common/http';
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+import { inject, TestBed } from '@angular/core/testing';
+
+import { ConfigurationService } from './configuration.service';
+
+describe('ConfigurationService', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [ConfigurationService],
+ imports: [HttpClientTestingModule, HttpClientModule]
+ });
+ });
+
+ it(
+ 'should be created',
+ inject([ConfigurationService], (service: ConfigurationService) => {
+ expect(service).toBeTruthy();
+ })
+ );
+});
--- /dev/null
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class ConfigurationService {
+ constructor(private http: HttpClient) {}
+
+ getConfigData() {
+ return this.http.get('/api/cluster_conf/');
+ }
+}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
+import { ConfigurationService } from './configuration.service';
import { FormatterService } from './formatter.service';
import { TcmuIscsiService } from './tcmu-iscsi.service';
import { TopLevelService } from './top-level.service';
CommonModule
],
declarations: [],
- providers: [FormatterService, TopLevelService, TcmuIscsiService]
+ providers: [FormatterService, TopLevelService, TcmuIscsiService, ConfigurationService]
})
export class ServicesModule { }
ComponentsModule,
ServicesModule,
PasswordButtonDirective,
- ComponentsModule,
DataTableModule
],
declarations: [