parameters={'status': (str, 'Cluster Status')})
def singleton_set(self, status: str):
ClusterModel(status).to_db()
-
- @RESTController.Collection('GET', 'capacity')
- def get_capacity(self):
- return ClusterModel.get_capacity()
from ..rest_client import RequestException
from ..security import Permission, Scope
from ..services.ceph_service import CephService
+from ..services.cluster import ClusterModel
from ..services.iscsi_cli import IscsiGatewaysConfig
from ..services.iscsi_client import IscsiClient
from ..tools import partial_dict
responses={200: HEALTH_MINIMAL_SCHEMA})
def minimal(self):
return self.health_minimal.all_health()
+
+ @Endpoint()
+ def get_cluster_capacity(self):
+ return ClusterModel.get_capacity()
+
+ @Endpoint()
+ def get_cluster_fsid(self):
+ return mgr.get('config')['fsid']
from functools import wraps
+from .. import mgr
from ..exceptions import DashboardException
from ..services.orchestrator import OrchClient
from . import APIDoc, Endpoint, EndpointDoc, ReadPermission, RESTController, UIRouter
responses={200: STATUS_SCHEMA})
def status(self):
return OrchClient.instance().status()
+
+ @Endpoint()
+ def get_name(self):
+ return mgr.get_module_option_ex('orchestrator', 'orchestrator')
# -*- coding: utf-8 -*-
-
import json
import os
import tempfile
from ..exceptions import DashboardException
from ..security import Scope
from ..services import ceph_service
-from ..settings import Settings
-from . import APIDoc, APIRouter, BaseController, Endpoint, RESTController, Router
+from ..services.settings import SettingsService
+from ..settings import Options, Settings
+from . import APIDoc, APIRouter, BaseController, Endpoint, RESTController, Router, UIRouter
@Router('/api/prometheus_receiver', secure=False)
return PrometheusReceiver.notifications[-1:]
return PrometheusReceiver.notifications[int(f) + 1:]
return PrometheusReceiver.notifications
+
+
+@UIRouter('/prometheus', Scope.PROMETHEUS)
+class PrometheusSettings(RESTController):
+ def get(self, name):
+ with SettingsService.attribute_handler(name) as settings_name:
+ setting = getattr(Options, settings_name)
+ return {
+ 'name': settings_name,
+ 'default': setting.default_value,
+ 'type': setting.types_as_str(),
+ 'value': getattr(Settings, settings_name)
+ }
# -*- coding: utf-8 -*-
-from contextlib import contextmanager
-
-import cherrypy
-
from ..security import Scope
+from ..services.settings import SettingsService, _to_native
from ..settings import Options
from ..settings import Settings as SettingsModule
from . import APIDoc, APIRouter, EndpointDoc, RESTController, UIRouter
"""
Enables to manage the settings of the dashboard (not the Ceph cluster).
"""
- @contextmanager
- def _attribute_handler(self, name):
- """
- :type name: str|dict[str, str]
- :rtype: str|dict[str, str]
- """
- if isinstance(name, dict):
- result = {
- self._to_native(key): value
- for key, value in name.items()
- }
- else:
- result = self._to_native(name)
-
- try:
- yield result
- except AttributeError: # pragma: no cover - handling is too obvious
- raise cherrypy.NotFound(result) # pragma: no cover - handling is too obvious
-
- @staticmethod
- def _to_native(setting):
- return setting.upper().replace('-', '_')
-
@EndpointDoc("Display Settings Information",
parameters={
'names': (str, 'Name of Settings'),
return [self._get(name) for name in option_names]
def _get(self, name):
- with self._attribute_handler(name) as sname:
+ with SettingsService.attribute_handler(name) as sname:
setting = getattr(Options, sname)
return {
'name': sname,
return self._get(name)
def set(self, name, value):
- with self._attribute_handler(name) as sname:
- setattr(SettingsModule, self._to_native(sname), value)
+ with SettingsService.attribute_handler(name) as sname:
+ setattr(SettingsModule, _to_native(sname), value)
def delete(self, name):
- with self._attribute_handler(name) as sname:
- delattr(SettingsModule, self._to_native(sname))
+ with SettingsService.attribute_handler(name) as sname:
+ delattr(SettingsModule, _to_native(sname))
def bulk_set(self, **kwargs):
- with self._attribute_handler(kwargs) as data:
+ with SettingsService.attribute_handler(kwargs) as data:
for name, value in data.items():
- setattr(SettingsModule, self._to_native(name), value)
+ setattr(SettingsModule, _to_native(name), value)
@UIRouter('/standard_settings')
import { ToastrModule } from 'ngx-toastr';
import { BehaviorSubject, of } from 'rxjs';
-import { ConfigurationService } from '~/app/shared/api/configuration.service';
import { HealthService } from '~/app/shared/api/health.service';
-import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
import { PrometheusService } from '~/app/shared/api/prometheus.service';
import { CssHelper } from '~/app/shared/classes/css-helper';
import { AlertmanagerAlert } from '~/app/shared/models/prometheus-alerts';
import { DashboardPieComponent } from '../dashboard-pie/dashboard-pie.component';
import { PgSummaryPipe } from '../pg-summary.pipe';
import { DashboardV3Component } from './dashboard-v3.component';
+import { OrchestratorService } from '~/app/shared/api/orchestrator.service';
export class SummaryServiceMock {
summaryDataSource = new BehaviorSubject({
describe('Dashbord Component', () => {
let component: DashboardV3Component;
let fixture: ComponentFixture<DashboardV3Component>;
- let configurationService: ConfigurationService;
- let orchestratorService: MgrModuleService;
+ let healthService: HealthService;
+ let orchestratorService: OrchestratorService;
let getHealthSpy: jasmine.Spy;
let getAlertsSpy: jasmine.Spy;
let fakeFeatureTogglesService: jasmine.Spy;
}
];
- const configValueData: any = {
- value: [
- {
- section: 'mgr',
- value: 'e90a0d58-658e-4148-8f61-e896c86f0696'
- }
- ]
- };
+ const configValueData: any = 'e90a0d58-658e-4148-8f61-e896c86f0696';
- const orchData: any = {
- log_level: '',
- log_to_cluster: false,
- log_to_cluster_level: 'info',
- log_to_file: false,
- orchestrator: 'cephadm'
- };
+ const orchName: any = 'Cephadm';
configureTestBed({
imports: [RouterTestingModule, HttpClientTestingModule, ToastrModule.forRoot(), SharedModule],
);
fixture = TestBed.createComponent(DashboardV3Component);
component = fixture.componentInstance;
- configurationService = TestBed.inject(ConfigurationService);
- orchestratorService = TestBed.inject(MgrModuleService);
+ healthService = TestBed.inject(HealthService);
+ orchestratorService = TestBed.inject(OrchestratorService);
getHealthSpy = spyOn(TestBed.inject(HealthService), 'getMinimalHealth');
getHealthSpy.and.returnValue(of(healthPayload));
spyOn(TestBed.inject(PrometheusService), 'ifAlertmanagerConfigured').and.callFake((fn) => fn());
});
it('should get corresponding data into detailsCardData', () => {
- spyOn(configurationService, 'get').and.returnValue(of(configValueData));
- spyOn(orchestratorService, 'getConfig').and.returnValue(of(orchData));
+ spyOn(healthService, 'getClusterFsid').and.returnValue(of(configValueData));
+ spyOn(orchestratorService, 'getName').and.returnValue(of(orchName));
component.ngOnInit();
expect(component.detailsCardData.fsid).toBe('e90a0d58-658e-4148-8f61-e896c86f0696');
expect(component.detailsCardData.orchestrator).toBe('Cephadm');
import { take } from 'rxjs/operators';
import moment from 'moment';
-import { ClusterService } from '~/app/shared/api/cluster.service';
-import { ConfigurationService } from '~/app/shared/api/configuration.service';
import { HealthService } from '~/app/shared/api/health.service';
-import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
import { OsdService } from '~/app/shared/api/osd.service';
import { PrometheusService } from '~/app/shared/api/prometheus.service';
import { Promqls as queries } from '~/app/shared/enum/dashboard-promqls.enum';
import { SummaryService } from '~/app/shared/services/summary.service';
import { PrometheusListHelper } from '~/app/shared/helpers/prometheus-list-helper';
import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service';
+import { OrchestratorService } from '~/app/shared/api/orchestrator.service';
@Component({
selector: 'cd-dashboard-v3',
constructor(
private summaryService: SummaryService,
- private configService: ConfigurationService,
- private mgrModuleService: MgrModuleService,
- private clusterService: ClusterService,
+ private orchestratorService: OrchestratorService,
private osdService: OsdService,
private authStorageService: AuthStorageService,
private featureToggles: FeatureTogglesService,
}
getDetailsCardData() {
- this.configService.get('fsid').subscribe((data) => {
- this.detailsCardData.fsid = data['value'][0]['value'];
+ this.healthService.getClusterFsid().subscribe((data: string) => {
+ this.detailsCardData.fsid = data;
});
- this.mgrModuleService.getConfig('orchestrator').subscribe((data) => {
- const orchStr = data['orchestrator'];
- this.detailsCardData.orchestrator = orchStr.charAt(0).toUpperCase() + orchStr.slice(1);
+ this.orchestratorService.getName().subscribe((data: string) => {
+ this.detailsCardData.orchestrator = data;
});
this.summaryService.subscribe((summary) => {
const version = summary.version.replace('ceph version ', '').split(' ');
.subscribe((data: any) => {
this.osdSettings = data;
});
- this.capacityService = this.clusterService.getCapacity().subscribe((data: any) => {
+ this.capacityService = this.healthService.getClusterCapacity().subscribe((data: any) => {
this.capacity = data;
});
}
{ headers: { Accept: 'application/vnd.ceph.api.v0.1+json' } }
);
}
-
- getCapacity() {
- return this.http.get(`${this.baseURL}/capacity`, {});
- }
}
getMinimalHealth() {
return this.http.get('api/health/minimal');
}
+
+ getClusterCapacity() {
+ return this.http.get('api/health/get_cluster_capacity');
+ }
+
+ getClusterFsid() {
+ return this.http.get('api/health/get_cluster_fsid');
+ }
+
+ getOrchestratorName() {
+ return this.http.get('api/health/get_orchestrator_name');
+ }
}
}
return false;
}
+
+ getName() {
+ return this.http.get(`${this.url}/get_name`);
+ }
}
let host: string;
const receiveConfig = () => {
- const req = httpTesting.expectOne('api/settings/alertmanager-api-host');
+ const req = httpTesting.expectOne('ui-api/prometheus/alertmanager-api-host');
expect(req.request.method).toBe('GET');
req.flush({ value: host });
};
let host: string;
const receiveConfig = () => {
- const req = httpTesting.expectOne('api/settings/prometheus-api-host');
+ const req = httpTesting.expectOne('ui-api/prometheus/prometheus-api-host');
expect(req.request.method).toBe('GET');
req.flush({ value: host });
};
AlertmanagerNotification,
PrometheusRuleGroup
} from '../models/prometheus-alerts';
-import { SettingsService } from './settings.service';
@Injectable({
providedIn: 'root'
export class PrometheusService {
private baseURL = 'api/prometheus';
private settingsKey = {
- alertmanager: 'api/settings/alertmanager-api-host',
- prometheus: 'api/settings/prometheus-api-host'
+ alertmanager: 'ui-api/prometheus/alertmanager-api-host',
+ prometheus: 'ui-api/prometheus/prometheus-api-host'
};
+ private settings: { [url: string]: string } = {};
- constructor(private http: HttpClient, private settingsService: SettingsService) {}
+ constructor(private http: HttpClient) {}
getPrometheusData(params: any): any {
return this.http.get<any>(`${this.baseURL}/data`, { params });
}
ifAlertmanagerConfigured(fn: (value?: string) => void, elseFn?: () => void): void {
- this.settingsService.ifSettingConfigured(this.settingsKey.alertmanager, fn, elseFn);
+ this.ifSettingConfigured(this.settingsKey.alertmanager, fn, elseFn);
}
disableAlertmanagerConfig(): void {
- this.settingsService.disableSetting(this.settingsKey.alertmanager);
+ this.disableSetting(this.settingsKey.alertmanager);
}
ifPrometheusConfigured(fn: (value?: string) => void, elseFn?: () => void): void {
- this.settingsService.ifSettingConfigured(this.settingsKey.prometheus, fn, elseFn);
+ this.ifSettingConfigured(this.settingsKey.prometheus, fn, elseFn);
}
disablePrometheusConfig(): void {
- this.settingsService.disableSetting(this.settingsKey.prometheus);
+ this.disableSetting(this.settingsKey.prometheus);
}
getAlerts(params = {}): Observable<AlertmanagerAlert[]> {
}`;
return this.http.get<AlertmanagerNotification[]>(url);
}
+
+ ifSettingConfigured(url: string, fn: (value?: string) => void, elseFn?: () => void): void {
+ const setting = this.settings[url];
+ if (setting === undefined) {
+ this.http.get(url).subscribe(
+ (data: any) => {
+ this.settings[url] = this.getSettingsValue(data);
+ this.ifSettingConfigured(url, fn, elseFn);
+ },
+ (resp) => {
+ if (resp.status !== 401) {
+ this.settings[url] = '';
+ }
+ }
+ );
+ } else if (setting !== '') {
+ fn(setting);
+ } else {
+ if (elseFn) {
+ elseFn();
+ }
+ }
+ }
+
+ // Easiest way to stop reloading external content that can't be reached
+ disableSetting(url: string) {
+ this.settings[url] = '';
+ }
+
+ private getSettingsValue(data: any): string {
+ return data.value || data.instance || '';
+ }
}
summary: Update the cluster status
tags:
- Cluster
- /api/cluster/capacity:
- get:
- parameters: []
- responses:
- '200':
- content:
- application/vnd.ceph.api.v1.0+json:
- type: object
- description: OK
- '400':
- description: Operation exception. Please check the response body for details.
- '401':
- description: Unauthenticated access. Please login first.
- '403':
- description: Unauthorized access. Please check your permissions.
- '500':
- description: Unexpected error. Please check the response body for the stack
- trace.
- security:
- - jwt: []
- tags:
- - Cluster
/api/cluster/user:
get:
description: "\n Get list of ceph users and its respective data\n \
- jwt: []
tags:
- Health
+ /api/health/get_cluster_capacity:
+ get:
+ parameters: []
+ responses:
+ '200':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: OK
+ '400':
+ description: Operation exception. Please check the response body for details.
+ '401':
+ description: Unauthenticated access. Please login first.
+ '403':
+ description: Unauthorized access. Please check your permissions.
+ '500':
+ description: Unexpected error. Please check the response body for the stack
+ trace.
+ security:
+ - jwt: []
+ tags:
+ - Health
+ /api/health/get_cluster_fsid:
+ get:
+ parameters: []
+ responses:
+ '200':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: OK
+ '400':
+ description: Operation exception. Please check the response body for details.
+ '401':
+ description: Unauthenticated access. Please login first.
+ '403':
+ description: Unauthorized access. Please check your permissions.
+ '500':
+ description: Unexpected error. Please check the response body for the stack
+ trace.
+ security:
+ - jwt: []
+ tags:
+ - Health
/api/health/minimal:
get:
parameters: []
--- /dev/null
+# -*- coding: utf-8 -*-
+from contextlib import contextmanager
+
+import cherrypy
+
+
+class SettingsService:
+ @contextmanager
+ # pylint: disable=no-self-argument
+ def attribute_handler(name):
+ """
+ :type name: str|dict[str, str]
+ :rtype: str|dict[str, str]
+ """
+ if isinstance(name, dict):
+ result = {
+ _to_native(key): value
+ for key, value in name.items()
+ }
+ else:
+ result = _to_native(name)
+
+ try:
+ yield result
+ except AttributeError: # pragma: no cover - handling is too obvious
+ raise cherrypy.NotFound(result) # pragma: no cover - handling is too obvious
+
+
+def _to_native(setting):
+ return setting.upper().replace('-', '_')