]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboar: Add refresh nterval to the dashboard landing page 27267/head
authorguodan1 <guodan1@lenovo.com>
Fri, 18 Jan 2019 06:33:43 +0000 (14:33 +0800)
committerTatjana Dehler <tdehler@suse.com>
Fri, 29 Mar 2019 10:35:16 +0000 (11:35 +0100)
Fixes: http://tracker.ceph.com/issues/26872
Signed-off-by: guodan1 <guodan1@lenovo.com>
(cherry picked from commit 58e9d48259ad7ac562ffb85bc22b3d6fba6120e1)

src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health/health.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health/health.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/refresh-selector/refresh-selector.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/refresh-selector/refresh-selector.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/refresh-selector/refresh-selector.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/refresh-selector/refresh-selector.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/locale/messages.xlf

index 89a37fd6c0eb27e1f885fdd771df13f95cedb9cf..c59717c524fdfdd320e62490d831a02ad4012691 100644 (file)
@@ -1,4 +1,5 @@
 <div>
+  <cd-refresh-selector></cd-refresh-selector>
   <tabset *ngIf="hasGrafana">
     <tab i18n-heading
          heading="Health">
index cc5a3b61dbecca5e03b320a49355aba278a05871..99bc05fc8db217174b510b18ad9485e80d804045 100644 (file)
@@ -12,6 +12,7 @@ import { HealthService } from '../../../shared/api/health.service';
 import { Permissions } from '../../../shared/models/permissions';
 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
 import { FeatureTogglesService } from '../../../shared/services/feature-toggles.service';
+import { RefreshIntervalService } from '../../../shared/services/refresh-interval.service';
 import { SharedModule } from '../../../shared/shared.module';
 import { PgCategoryService } from '../../shared/pg-category.service';
 import { HealthPieColor } from '../health-pie/health-pie-color.enum';
@@ -62,7 +63,8 @@ describe('HealthComponent', () => {
     providers: [
       i18nProviders,
       { provide: AuthStorageService, useValue: fakeAuthStorageService },
-      PgCategoryService
+      PgCategoryService,
+      RefreshIntervalService
     ]
   });
 
index a722afebd7e1c784795968c644365f4015571a73..c5748642d357c18c4fe925c30244ae3f61e65edc 100644 (file)
@@ -2,6 +2,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
 
 import { I18n } from '@ngx-translate/i18n-polyfill';
 import * as _ from 'lodash';
+import { Subscription } from 'rxjs/Subscription';
 
 import { HealthService } from '../../../shared/api/health.service';
 import { Permissions } from '../../../shared/models/permissions';
@@ -10,6 +11,7 @@ import {
   FeatureTogglesMap$,
   FeatureTogglesService
 } from '../../../shared/services/feature-toggles.service';
+import { RefreshIntervalService } from '../../../shared/services/refresh-interval.service';
 import { PgCategoryService } from '../../shared/pg-category.service';
 import { HealthPieColor } from '../health-pie/health-pie-color.enum';
 
@@ -20,7 +22,7 @@ import { HealthPieColor } from '../health-pie/health-pie-color.enum';
 })
 export class HealthComponent implements OnInit, OnDestroy {
   healthData: any;
-  interval: number;
+  interval = new Subscription();
   permissions: Permissions;
   enabledFeature$: FeatureTogglesMap$;
 
@@ -29,7 +31,8 @@ export class HealthComponent implements OnInit, OnDestroy {
     private i18n: I18n,
     private authStorageService: AuthStorageService,
     private pgCategoryService: PgCategoryService,
-    private featureToggles: FeatureTogglesService
+    private featureToggles: FeatureTogglesService,
+    private refreshIntervalService: RefreshIntervalService
   ) {
     this.permissions = this.authStorageService.getPermissions();
     this.enabledFeature$ = this.featureToggles.get();
@@ -37,13 +40,13 @@ export class HealthComponent implements OnInit, OnDestroy {
 
   ngOnInit() {
     this.getHealth();
-    this.interval = window.setInterval(() => {
+    this.interval = this.refreshIntervalService.intervalData$.subscribe(() => {
       this.getHealth();
-    }, 5000);
+    });
   }
 
   ngOnDestroy() {
-    clearInterval(this.interval);
+    this.interval.unsubscribe();
   }
 
   getHealth() {
index 02c54aa160c9e1cebc6c67c7c59fa62c76085b92..517c5282331703962f41c388a809238ee740ca07 100644 (file)
@@ -20,6 +20,7 @@ import { InfoPanelComponent } from './info-panel/info-panel.component';
 import { LanguageSelectorComponent } from './language-selector/language-selector.component';
 import { LoadingPanelComponent } from './loading-panel/loading-panel.component';
 import { ModalComponent } from './modal/modal.component';
+import { RefreshSelectorComponent } from './refresh-selector/refresh-selector.component';
 import { SelectBadgesComponent } from './select-badges/select-badges.component';
 import { SelectComponent } from './select/select.component';
 import { SparklineComponent } from './sparkline/sparkline.component';
@@ -59,7 +60,8 @@ import { WarningPanelComponent } from './warning-panel/warning-panel.component';
     WarningPanelComponent,
     LanguageSelectorComponent,
     GrafanaComponent,
-    SelectComponent
+    SelectComponent,
+    RefreshSelectorComponent
   ],
   providers: [],
   exports: [
@@ -76,7 +78,8 @@ import { WarningPanelComponent } from './warning-panel/warning-panel.component';
     WarningPanelComponent,
     LanguageSelectorComponent,
     GrafanaComponent,
-    SelectComponent
+    SelectComponent,
+    RefreshSelectorComponent
   ],
   entryComponents: [ModalComponent, CriticalConfirmationModalComponent, ConfirmationModalComponent]
 })
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/refresh-selector/refresh-selector.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/refresh-selector/refresh-selector.component.html
new file mode 100644 (file)
index 0000000..ce90465
--- /dev/null
@@ -0,0 +1,18 @@
+<div class="row">
+  <div class="col-xs-5 col-sm-2 refresh-selector">
+    <label class="control-label col-xs-5 col-sm-5" 
+           for="refreshInterval">
+      <span i18n>Refresh</span>
+    </label>
+    <div class="col-xs-7 col-sm-7">
+      <select id="refreshInterval" 
+              name="refreshInterval" 
+              class="form-control" 
+              (change)="changeRefreshInterval($event.target.value)"
+              [(ngModel)]="selectedInterval">
+        <option *ngFor="let key of intervalKeys" 
+                [value]="intervalList[key]">{{ key }}</option>
+      </select>
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/refresh-selector/refresh-selector.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/refresh-selector/refresh-selector.component.scss
new file mode 100644 (file)
index 0000000..3440b87
--- /dev/null
@@ -0,0 +1,26 @@
+.refresh-selector {
+  padding: 0;
+  float: right;
+  margin-right: 60px;
+
+  * {
+    padding: 0;
+    box-sizing: border-box;
+  }
+
+  label {
+    padding: 10px 10px 0 0;
+    text-align: right;
+    margin: 0;
+  }
+
+  @media (min-width: 500px) and (max-width: 767px) {
+    width: 24vw;
+  }
+  @media (min-width: 1200px) {
+    width: 12vw;
+  }
+  @media (min-width: 1400px) {
+    width: 10vw;
+  }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/refresh-selector/refresh-selector.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/refresh-selector/refresh-selector.component.spec.ts
new file mode 100644 (file)
index 0000000..87389fc
--- /dev/null
@@ -0,0 +1,28 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule } from '@angular/forms';
+
+import { configureTestBed } from '../../../../testing/unit-test-helper';
+
+import { RefreshIntervalService } from '../../services/refresh-interval.service';
+import { RefreshSelectorComponent } from './refresh-selector.component';
+
+describe('RefreshSelectorComponent', () => {
+  let component: RefreshSelectorComponent;
+  let fixture: ComponentFixture<RefreshSelectorComponent>;
+
+  configureTestBed({
+    imports: [FormsModule],
+    declarations: [RefreshSelectorComponent],
+    providers: [RefreshIntervalService]
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(RefreshSelectorComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/refresh-selector/refresh-selector.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/refresh-selector/refresh-selector.component.ts
new file mode 100644 (file)
index 0000000..632ddf6
--- /dev/null
@@ -0,0 +1,32 @@
+import { Component, OnInit } from '@angular/core';
+
+import { RefreshIntervalService } from '../../services/refresh-interval.service';
+
+@Component({
+  selector: 'cd-refresh-selector',
+  templateUrl: './refresh-selector.component.html',
+  styleUrls: ['./refresh-selector.component.scss']
+})
+export class RefreshSelectorComponent implements OnInit {
+  selectedInterval: number;
+  intervalList: { [key: string]: number } = {
+    '5 s': 5000,
+    '10 s': 10000,
+    '15 s': 15000,
+    '30 s': 30000,
+    '1 min': 60000,
+    '3 min': 180000,
+    '5 min': 300000
+  };
+  intervalKeys = Object.keys(this.intervalList);
+
+  constructor(private refreshIntervalService: RefreshIntervalService) {}
+
+  ngOnInit() {
+    this.selectedInterval = this.refreshIntervalService.getRefreshInterval() || 5000;
+  }
+
+  changeRefreshInterval(interval: number) {
+    this.refreshIntervalService.setRefreshInterval(interval);
+  }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.spec.ts
new file mode 100644 (file)
index 0000000..da55b44
--- /dev/null
@@ -0,0 +1,45 @@
+import { fakeAsync, tick } from '@angular/core/testing';
+
+import { RefreshIntervalService } from './refresh-interval.service';
+
+describe('RefreshIntervalService', () => {
+  let service: RefreshIntervalService;
+
+  beforeEach(() => {
+    service = new RefreshIntervalService();
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+
+  it('should initial private interval time right', () => {
+    sessionStorage.setItem('dashboard_interval', '10000');
+    service = new RefreshIntervalService();
+    expect(service.getRefreshInterval()).toBe(10000);
+  });
+
+  describe('setRefreshInterval', () => {
+    let notifyCount: number;
+
+    it('should send notification to component at correct interval time when interval changed', fakeAsync(() => {
+      service.intervalData$.subscribe(() => {
+        notifyCount++;
+      });
+
+      notifyCount = 0;
+      service.setRefreshInterval(10000);
+      tick(10000);
+      expect(service.getRefreshInterval()).toBe(10000);
+      expect(notifyCount).toBe(1);
+
+      notifyCount = 0;
+      service.setRefreshInterval(30000);
+      tick(30000);
+      expect(service.getRefreshInterval()).toBe(30000);
+      expect(notifyCount).toBe(1);
+
+      service.ngOnDestroy();
+    }));
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.ts
new file mode 100644 (file)
index 0000000..4cc846a
--- /dev/null
@@ -0,0 +1,43 @@
+import { Injectable, OnDestroy } from '@angular/core';
+
+import { BehaviorSubject, interval, Subscription } from 'rxjs';
+
+import { ServicesModule } from './services.module';
+@Injectable({
+  providedIn: ServicesModule
+})
+export class RefreshIntervalService implements OnDestroy {
+  private intervalTime: number;
+  // Observable sources
+  private intervalDataSource = new BehaviorSubject(null);
+  private intervalSubscription: Subscription;
+  // Observable streams
+  intervalData$ = this.intervalDataSource.asObservable();
+
+  constructor() {
+    const initialInterval = parseInt(sessionStorage.getItem('dashboard_interval'), 10) || 5000;
+    this.setRefreshInterval(initialInterval);
+  }
+
+  setRefreshInterval(newInterval: number) {
+    this.intervalTime = newInterval;
+    sessionStorage.setItem('dashboard_interval', newInterval.toString());
+
+    if (this.intervalSubscription) {
+      this.intervalSubscription.unsubscribe();
+    }
+    this.intervalSubscription = interval(this.intervalTime).subscribe(() =>
+      this.intervalDataSource.next(this.intervalTime)
+    );
+  }
+
+  getRefreshInterval() {
+    return this.intervalTime;
+  }
+
+  ngOnDestroy() {
+    if (this.intervalSubscription) {
+      this.intervalSubscription.unsubscribe();
+    }
+  }
+}
index 8f404a7f4fceab72d79c583a80b93f333223fa49..127290d7714c14e9a398a12532fb323be851bbea 100644 (file)
           <context context-type="sourcefile">app/shared/components/grafana/grafana.component.html</context>
           <context context-type="linenumber">35</context>
         </context-group>
+      </trans-unit><trans-unit id="c8d1785038d461ec66b5799db21864182b35900a" datatype="html">
+        <source>Refresh</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">app/shared/components/refresh-selector/refresh-selector.component.html</context>
+          <context context-type="linenumber">5</context>
+        </context-group>
       </trans-unit><trans-unit id="012741ee52b3c050e4a977c37cc2334f7974f141" datatype="html">
         <source>Failed to load data.</source>
         <context-group purpose="location">
         <source>Health</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/dashboard/dashboard/dashboard.component.html</context>
-          <context context-type="linenumber">4</context>
+          <context context-type="linenumber">5</context>
         </context-group>
       </trans-unit><trans-unit id="61e0f26d843eec0b33ff475e111b0c2f7a80b835" datatype="html">
         <source>Statistics</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/dashboard/dashboard/dashboard.component.html</context>
-          <context context-type="linenumber">8</context>
+          <context context-type="linenumber">9</context>
         </context-group>
       </trans-unit><trans-unit id="f8f74e5f683012b9c0702b1446011c6b9158bc67" datatype="html">
         <source>Please consult the <x id="START_LINK" ctype="x-a" equiv-text="&lt;a&gt;"/>documentation<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a&gt;"/>