diff options
Diffstat (limited to 'src/pybind/mgr/dashboard/frontend/src/app/shared/models')
46 files changed, 1490 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/alertmanager-silence.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/alertmanager-silence.ts new file mode 100644 index 000000000..b7b886295 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/alertmanager-silence.ts @@ -0,0 +1,23 @@ +export class AlertmanagerSilenceMatcher { + name: string; + value: any; + isRegex: boolean; +} + +export class AlertmanagerSilenceMatcherMatch { + status: string; + cssClass: string; +} + +export class AlertmanagerSilence { + id?: string; + matchers: AlertmanagerSilenceMatcher[]; + startsAt: string; // DateStr + endsAt: string; // DateStr + updatedAt?: string; // DateStr + createdBy: string; + comment: string; + status?: { + state: 'expired' | 'active' | 'pending'; + }; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/breadcrumbs.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/breadcrumbs.ts new file mode 100644 index 000000000..10e799929 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/breadcrumbs.ts @@ -0,0 +1,59 @@ +/* +The MIT License + +Copyright (c) 2017 (null) McNull https://github.com/McNull + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ + +import { ActivatedRouteSnapshot, Resolve, UrlSegment } from '@angular/router'; + +import { Observable, of } from 'rxjs'; + +export class BreadcrumbsResolver implements Resolve<IBreadcrumb[]> { + public resolve( + route: ActivatedRouteSnapshot + ): Observable<IBreadcrumb[]> | Promise<IBreadcrumb[]> | IBreadcrumb[] { + const data = route.routeConfig.data; + const path = data.path === null ? null : this.getFullPath(route); + + const text = + typeof data.breadcrumbs === 'string' + ? data.breadcrumbs + : data.breadcrumbs.text || data.text || path; + + const crumbs: IBreadcrumb[] = [{ text: text, path: path }]; + + return of(crumbs); + } + + public getFullPath(route: ActivatedRouteSnapshot): string { + const relativePath = (segments: UrlSegment[]) => + segments.reduce((a, v) => (a += '/' + v.path), ''); + const fullPath = (routes: ActivatedRouteSnapshot[]) => + routes.reduce((a, v) => (a += relativePath(v.url)), ''); + + return fullPath(route.pathFromRoot); + } +} + +export interface IBreadcrumb { + text: string; + path: string; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-form-modal-field-config.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-form-modal-field-config.ts new file mode 100644 index 000000000..e327be59a --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-form-modal-field-config.ts @@ -0,0 +1,32 @@ +import { ValidatorFn } from '@angular/forms'; + +export class CdFormModalFieldConfig { + // --- Generic field properties --- + name: string; + // 'binary' will use cdDimlessBinary directive on input element + // 'select' will use select element + type: 'number' | 'text' | 'binary' | 'select' | 'select-badges'; + label?: string; + required?: boolean; + value?: any; + errors?: { [errorName: string]: string }; + validators: ValidatorFn[]; + + // --- Specific field properties --- + typeConfig?: { + [prop: string]: any; + // 'select': + // --------- + // placeholder?: string; + // options?: Array<{ + // text: string; + // value: any; + // }>; + // + // 'select-badges': + // ---------------- + // customBadges: boolean; + // options: Array<SelectOption>; + // messages: SelectMessages; + }; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-notification.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-notification.spec.ts new file mode 100644 index 000000000..df6e8899b --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-notification.spec.ts @@ -0,0 +1,95 @@ +import { NotificationType } from '../enum/notification-type.enum'; +import { CdNotification, CdNotificationConfig } from './cd-notification'; + +describe('cd-notification classes', () => { + const expectObject = (something: object, expected: object) => { + Object.keys(expected).forEach((key) => expect(something[key]).toBe(expected[key])); + }; + + // As these Models have a view methods they need to be tested + describe('CdNotificationConfig', () => { + it('should create a new config without any parameters', () => { + expectObject(new CdNotificationConfig(), { + application: 'Ceph', + applicationClass: 'ceph-icon', + message: undefined, + options: undefined, + title: undefined, + type: 1 + }); + }); + + it('should create a new config with parameters', () => { + expectObject( + new CdNotificationConfig( + NotificationType.error, + 'Some Alert', + 'Something failed', + undefined, + 'Prometheus' + ), + { + application: 'Prometheus', + applicationClass: 'prometheus-icon', + message: 'Something failed', + options: undefined, + title: 'Some Alert', + type: 0 + } + ); + }); + }); + + describe('CdNotification', () => { + beforeEach(() => { + const baseTime = new Date('2022-02-22'); + spyOn(global, 'Date').and.returnValue(baseTime); + }); + + it('should create a new config without any parameters', () => { + expectObject(new CdNotification(), { + application: 'Ceph', + applicationClass: 'ceph-icon', + iconClass: 'fa fa-info', + message: undefined, + options: undefined, + textClass: 'text-info', + timestamp: '2022-02-22T00:00:00.000Z', + title: undefined, + type: 1 + }); + }); + + it('should create a new config with parameters', () => { + expectObject( + new CdNotification( + new CdNotificationConfig( + NotificationType.error, + 'Some Alert', + 'Something failed', + undefined, + 'Prometheus' + ) + ), + { + application: 'Prometheus', + applicationClass: 'prometheus-icon', + iconClass: 'fa fa-exclamation-triangle', + message: 'Something failed', + options: undefined, + textClass: 'text-danger', + timestamp: '2022-02-22T00:00:00.000Z', + title: 'Some Alert', + type: 0 + } + ); + }); + + it('should expect the right success classes', () => { + expectObject(new CdNotification(new CdNotificationConfig(NotificationType.success)), { + iconClass: 'fa fa-check', + textClass: 'text-success' + }); + }); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-notification.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-notification.ts new file mode 100644 index 000000000..c283c5d80 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-notification.ts @@ -0,0 +1,48 @@ +import { IndividualConfig } from 'ngx-toastr'; + +import { Icons } from '../enum/icons.enum'; +import { NotificationType } from '../enum/notification-type.enum'; + +export class CdNotificationConfig { + applicationClass: string; + isFinishedTask = false; + + private classes = { + Ceph: 'ceph-icon', + Prometheus: 'prometheus-icon' + }; + + constructor( + public type: NotificationType = NotificationType.info, + public title?: string, + public message?: string, // Use this for additional information only + public options?: any | IndividualConfig, + public application: string = 'Ceph' + ) { + this.applicationClass = this.classes[this.application]; + } +} + +export class CdNotification extends CdNotificationConfig { + timestamp: string; + textClass: string; + iconClass: string; + duration: number; + borderClass: string; + + private textClasses = ['text-danger', 'text-info', 'text-success']; + private iconClasses = [Icons.warning, Icons.info, Icons.check]; + private borderClasses = ['border-danger', 'border-info', 'border-success']; + + constructor(private config: CdNotificationConfig = new CdNotificationConfig()) { + super(config.type, config.title, config.message, config.options, config.application); + delete this.config; + /* string representation of the Date object so it can be directly compared + with the timestamps parsed from localStorage */ + this.timestamp = new Date().toJSON(); + this.iconClass = this.iconClasses[this.type]; + this.textClass = this.textClasses[this.type]; + this.borderClass = this.borderClasses[this.type]; + this.isFinishedTask = config.isFinishedTask; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-pwd-expiration-settings.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-pwd-expiration-settings.ts new file mode 100644 index 000000000..53b9d14fd --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-pwd-expiration-settings.ts @@ -0,0 +1,11 @@ +export class CdPwdExpirationSettings { + pwdExpirationSpan = 0; + pwdExpirationWarning1: number; + pwdExpirationWarning2: number; + + constructor(settings: { [key: string]: any }) { + this.pwdExpirationSpan = settings.user_pwd_expiration_span; + this.pwdExpirationWarning1 = settings.user_pwd_expiration_warning_1; + this.pwdExpirationWarning2 = settings.user_pwd_expiration_warning_2; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-pwd-policy-settings.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-pwd-policy-settings.ts new file mode 100644 index 000000000..fef570f21 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-pwd-policy-settings.ts @@ -0,0 +1,23 @@ +export class CdPwdPolicySettings { + pwdPolicyEnabled: boolean; + pwdPolicyMinLength: number; + pwdPolicyCheckLengthEnabled: boolean; + pwdPolicyCheckOldpwdEnabled: boolean; + pwdPolicyCheckUsernameEnabled: boolean; + pwdPolicyCheckExclusionListEnabled: boolean; + pwdPolicyCheckRepetitiveCharsEnabled: boolean; + pwdPolicyCheckSequentialCharsEnabled: boolean; + pwdPolicyCheckComplexityEnabled: boolean; + + constructor(settings: { [key: string]: any }) { + this.pwdPolicyEnabled = settings.pwd_policy_enabled; + this.pwdPolicyMinLength = settings.pwd_policy_min_length; + this.pwdPolicyCheckLengthEnabled = settings.pwd_policy_check_length_enabled; + this.pwdPolicyCheckOldpwdEnabled = settings.pwd_policy_check_oldpwd_enabled; + this.pwdPolicyCheckUsernameEnabled = settings.pwd_policy_check_username_enabled; + this.pwdPolicyCheckExclusionListEnabled = settings.pwd_policy_check_exclusion_list_enabled; + this.pwdPolicyCheckRepetitiveCharsEnabled = settings.pwd_policy_check_repetitive_chars_enabled; + this.pwdPolicyCheckSequentialCharsEnabled = settings.pwd_policy_check_sequential_chars_enabled; + this.pwdPolicyCheckComplexityEnabled = settings.pwd_policy_check_complexity_enabled; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-action.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-action.ts new file mode 100644 index 000000000..70f06e506 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-action.ts @@ -0,0 +1,44 @@ +import { CdTableSelection } from './cd-table-selection'; + +export class CdTableAction { + // It's possible to assign a string + // or a function that returns the link if it has to be dynamic + // or none if it's not needed + routerLink?: string | Function; + + preserveFragment? = false; + + // This is the function that will be triggered on a click event if defined + click?: Function; + + permission: 'create' | 'update' | 'delete' | 'read'; + + // The name of the action + name: string; + + // The font awesome icon that will be used + icon: string; + + /** + * You can define the condition to disable the action. + * By default all 'update' and 'delete' actions will only be enabled + * if one selection is made and no task is running on the selected item.` + * + * In some cases you might want to give the user a hint why a button is + * disabled. This is achieved by returning a string. + * */ + disable?: (_: CdTableSelection) => boolean | string; + + /** + * Defines if the button can become 'primary' (displayed as button and not + * 'hidden' in the menu). Only one button can be primary at a time. By + * default all 'create' actions can be the action button if no or multiple + * items are selected. Also, all 'update' and 'delete' actions can be the + * action button by default, provided only one item is selected. + */ + canBePrimary?: (_: CdTableSelection) => boolean; + + // In some rare cases you want to hide a action that can be used by the user for example + // if one action can lock the item and another action unlocks it + visible?: (_: CdTableSelection) => boolean; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column-filter.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column-filter.ts new file mode 100644 index 000000000..ccdbe82fc --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column-filter.ts @@ -0,0 +1,7 @@ +import { CdTableColumn } from './cd-table-column'; + +export interface CdTableColumnFilter { + column: CdTableColumn; + options: { raw: string; formatted: string }[]; // possible options of a filter + value?: { raw: string; formatted: string }; // selected option +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column-filters-change.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column-filters-change.ts new file mode 100644 index 000000000..17601f0ad --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column-filters-change.ts @@ -0,0 +1,22 @@ +import { TableColumnProp } from '@swimlane/ngx-datatable'; + +export interface CdTableColumnFiltersChange { + /** + * Applied filters. + */ + filters: { + name: string; + prop: TableColumnProp; + value: { raw: string; formatted: string }; + }[]; + + /** + * Filtered data. + */ + data: any[]; + + /** + * Filtered out data. + */ + dataOut: any[]; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column.ts new file mode 100644 index 000000000..4ed5fdd58 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column.ts @@ -0,0 +1,38 @@ +import { TableColumn, TableColumnProp } from '@swimlane/ngx-datatable'; + +import { CellTemplate } from '../enum/cell-template.enum'; + +export interface CdTableColumn extends TableColumn { + cellTransformation?: CellTemplate; + isHidden?: boolean; + prop: TableColumnProp; // Enforces properties to get sortable columns + customTemplateConfig?: any; // Custom configuration used by cell templates. + + /** + * Add a filter for the column if true. + * + * By default, options for the filter are deduced from values of the column. + */ + filterable?: boolean; + + /** + * Use these options for filter rather than deducing from values of the column. + * + * If there is a pipe function associated with the column, pipe function is applied + * to the options before displaying them. + */ + filterOptions?: any[]; + + /** + * Default applied option, should be value in filterOptions. + */ + filterInitValue?: any; + + /** + * Specify a custom function for filtering. + * + * By default, the filter compares if values are string-equal with options. Specify + * a customize function if that's not desired. Return true to include a row. + */ + filterPredicate?: (row: any, value: any) => boolean; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-fetch-data-context.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-fetch-data-context.ts new file mode 100644 index 000000000..7937d82e6 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-fetch-data-context.ts @@ -0,0 +1,44 @@ +import { HttpParams } from '@angular/common/http'; + +import { PageInfo } from './cd-table-paging'; + +export class CdTableFetchDataContext { + errorConfig = { + resetData: true, // Force data table to show no data + displayError: true // Show an error panel above the data table + }; + + /** + * The function that should be called from within the error handler + * of the 'fetchData' function to display the error panel and to + * reset the data table to the correct state. + */ + error: Function; + pageInfo: PageInfo = new PageInfo(); + search = ''; + sort = '+name'; + + constructor(error: () => void) { + this.error = error; + } + + toParams(): HttpParams { + if (this.pageInfo.limit === null) { + this.pageInfo.limit = 0; + } + if (!this.search) { + this.search = ''; + } + if (!this.sort || this.sort.length < 2) { + this.sort = '+name'; + } + return new HttpParams({ + fromObject: { + offset: String(this.pageInfo.offset * this.pageInfo.limit), + limit: String(this.pageInfo.limit), + search: this.search, + sort: this.sort + } + }); + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-paging.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-paging.ts new file mode 100644 index 000000000..3693b527d --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-paging.ts @@ -0,0 +1,20 @@ +export const PAGE_LIMIT = 10; + +export class PageInfo { + // Total number of rows in a table + count: number; + + // Current page (current row = offset x limit or pageSize) + offset = 0; + + // Max. number of rows fetched from the server + limit: number = PAGE_LIMIT; + + /* + pageSize and limit can be decoupled if hybrid server-side and client-side + are used. A use-case would be to reduce the amount of queries: that is, + the pageSize (client-side paging) might be 10, but the back-end queries + could have a limit of 100. That would avoid triggering requests + */ + pageSize: number = PAGE_LIMIT; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-selection.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-selection.ts new file mode 100644 index 000000000..bbe1e5088 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-selection.ts @@ -0,0 +1,45 @@ +export class CdTableSelection { + private _selected: any[] = []; + hasMultiSelection: boolean; + hasSingleSelection: boolean; + hasSelection: boolean; + + constructor(rows?: any[]) { + if (rows) { + this._selected = rows; + } + this.update(); + } + + /** + * Recalculate the variables based on the current number + * of selected rows. + */ + private update() { + this.hasSelection = this._selected.length > 0; + this.hasSingleSelection = this._selected.length === 1; + this.hasMultiSelection = this._selected.length > 1; + } + + set selected(selection: any[]) { + this._selected = selection; + this.update(); + } + + get selected() { + return this._selected; + } + + add(row: any) { + this._selected.push(row); + this.update(); + } + + /** + * Get the first selected row. + * @return {any | null} + */ + first() { + return this.hasSelection ? this._selected[0] : null; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-user-config.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-user-config.ts new file mode 100644 index 000000000..edd1af784 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-user-config.ts @@ -0,0 +1,11 @@ +import { SortPropDir } from '@swimlane/ngx-datatable'; + +import { CdTableColumn } from './cd-table-column'; + +export interface CdUserConfig { + limit?: number; + offset?: number; + search?: string; + sorts?: SortPropDir[]; + columns?: CdTableColumn[]; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs-directory-models.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs-directory-models.ts new file mode 100644 index 000000000..92186aecc --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs-directory-models.ts @@ -0,0 +1,21 @@ +import { TreeStatus } from '@swimlane/ngx-datatable'; + +export class CephfsSnapshot { + name: string; + path: string; + created: string; +} + +export class CephfsQuotas { + max_bytes?: number; + max_files?: number; +} + +export class CephfsDir { + name: string; + path: string; + quotas: CephfsQuotas; + snapshots: CephfsSnapshot[]; + parent: string; + treeStatus?: TreeStatus; // Needed for table tree view +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/chart-tooltip.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/chart-tooltip.ts new file mode 100644 index 000000000..93a259e79 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/chart-tooltip.ts @@ -0,0 +1,115 @@ +import { ElementRef } from '@angular/core'; + +export class ChartTooltip { + tooltipEl: any; + chartEl: any; + getStyleLeft: Function; + getStyleTop: Function; + customColors: Record<string, any> = { + backgroundColor: undefined, + borderColor: undefined + }; + checkOffset = false; + + /** + * Creates an instance of ChartTooltip. + * @param {ElementRef} chartCanvas Canvas Element + * @param {ElementRef} chartTooltip Tooltip Element + * @param {Function} getStyleLeft Function that calculates the value of Left + * @param {Function} getStyleTop Function that calculates the value of Top + * @memberof ChartTooltip + */ + constructor( + chartCanvas: ElementRef, + chartTooltip: ElementRef, + getStyleLeft: Function, + getStyleTop: Function + ) { + this.chartEl = chartCanvas.nativeElement; + this.getStyleLeft = getStyleLeft; + this.getStyleTop = getStyleTop; + this.tooltipEl = chartTooltip.nativeElement; + } + + /** + * Implementation of a ChartJS custom tooltip function. + * + * @param {any} tooltip + * @memberof ChartTooltip + */ + customTooltips(tooltip: any) { + // Hide if no tooltip + if (tooltip.opacity === 0) { + this.tooltipEl.style.opacity = 0; + return; + } + + // Set caret Position + this.tooltipEl.classList.remove('above', 'below', 'no-transform'); + if (tooltip.yAlign) { + this.tooltipEl.classList.add(tooltip.yAlign); + } else { + this.tooltipEl.classList.add('no-transform'); + } + + // Set Text + if (tooltip.body) { + const titleLines = tooltip.title || []; + const bodyLines = tooltip.body.map((bodyItem: any) => { + return bodyItem.lines; + }); + + let innerHtml = '<thead>'; + + titleLines.forEach((title: string) => { + innerHtml += '<tr><th>' + this.getTitle(title) + '</th></tr>'; + }); + innerHtml += '</thead><tbody>'; + + bodyLines.forEach((body: string, i: number) => { + const colors = tooltip.labelColors[i]; + let style = 'background:' + (this.customColors.backgroundColor || colors.backgroundColor); + style += '; border-color:' + (this.customColors.borderColor || colors.borderColor); + style += '; border-width: 2px'; + const span = '<span class="chartjs-tooltip-key" style="' + style + '"></span>'; + innerHtml += '<tr><td nowrap>' + span + this.getBody(body) + '</td></tr>'; + }); + innerHtml += '</tbody>'; + + const tableRoot = this.tooltipEl.querySelector('table'); + tableRoot.innerHTML = innerHtml; + } + + const positionY = this.chartEl.offsetTop; + const positionX = this.chartEl.offsetLeft; + + // Display, position, and set styles for font + if (this.checkOffset) { + const halfWidth = tooltip.width / 2; + this.tooltipEl.classList.remove('transform-left'); + this.tooltipEl.classList.remove('transform-right'); + if (tooltip.caretX - halfWidth < 0) { + this.tooltipEl.classList.add('transform-left'); + } else if (tooltip.caretX + halfWidth > this.chartEl.width) { + this.tooltipEl.classList.add('transform-right'); + } + } + + this.tooltipEl.style.left = this.getStyleLeft(tooltip, positionX); + this.tooltipEl.style.top = this.getStyleTop(tooltip, positionY); + + this.tooltipEl.style.opacity = 1; + this.tooltipEl.style.fontFamily = tooltip._fontFamily; + this.tooltipEl.style.fontSize = tooltip.fontSize; + this.tooltipEl.style.fontStyle = tooltip._fontStyle; + this.tooltipEl.style.padding = tooltip.yPadding + 'px ' + tooltip.xPadding + 'px'; + } + + getBody(body: string) { + return body; + } + + getTitle(title: string) { + return title; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/configuration.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/configuration.ts new file mode 100644 index 000000000..0a8e403d7 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/configuration.ts @@ -0,0 +1,43 @@ +export enum RbdConfigurationSourceField { + global = 0, + pool = 1, + image = 2 +} + +export enum RbdConfigurationType { + bps, + iops, + milliseconds +} + +/** + * This configuration can also be set on a pool level. + */ +export interface RbdConfigurationEntry { + name: string; + source: RbdConfigurationSourceField; + value: any; + type?: RbdConfigurationType; // Non-external field. + description?: string; // Non-external field. + displayName?: string; // Non-external field. Nice name for the UI which is added in the UI. +} + +/** + * This object contains additional information injected into the elements retrieved by the service. + */ +export interface RbdConfigurationExtraField { + name: string; + displayName: string; + description: string; + type: RbdConfigurationType; + readOnly?: boolean; +} + +/** + * Represents a set of data to be used for editing or creating configuration options + */ +export interface RbdConfigurationSection { + heading: string; + class: string; + options: RbdConfigurationExtraField[]; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/credentials.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/credentials.ts new file mode 100644 index 000000000..2c2b7d76e --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/credentials.ts @@ -0,0 +1,4 @@ +export class Credentials { + username: string; + password: string; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/crush-node.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/crush-node.ts new file mode 100644 index 000000000..a8c8288b6 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/crush-node.ts @@ -0,0 +1,17 @@ +export class CrushNode { + id: number; + name: string; + type: string; + type_id: number; + // For nodes with leafs (Buckets) + children?: number[]; // Holds node id's of children + // For non root nodes + pool_weights?: object; + // For leafs (Devices) + device_class?: string; + crush_weight?: number; + exists?: number; + primary_affinity?: number; + reweight?: number; + status?: string; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/crush-rule.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/crush-rule.ts new file mode 100644 index 000000000..83c1db6b6 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/crush-rule.ts @@ -0,0 +1,18 @@ +import { CrushStep } from './crush-step'; + +export class CrushRule { + max_size: number; + usable_size?: number; + min_size: number; + rule_id: number; + rule_name: string; + ruleset: number; + steps: CrushStep[]; +} + +export class CrushRuleConfig { + root: string; // The name of the node under which data should be placed. + name: string; + failure_domain: string; // The type of CRUSH nodes across which we should separate replicas. + device_class?: string; // The device class data should be placed on. +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/crush-step.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/crush-step.ts new file mode 100644 index 000000000..3c46a7cd6 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/crush-step.ts @@ -0,0 +1,7 @@ +export class CrushStep { + op: string; + item_name?: string; + item?: number; + type?: string; + num?: number; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/daemon.interface.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/daemon.interface.ts new file mode 100644 index 000000000..c69a27851 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/daemon.interface.ts @@ -0,0 +1,12 @@ +export interface Daemon { + nodename: string; + container_id: string; + container_image_id: string; + container_image_name: string; + daemon_id: string; + daemon_type: string; + version: string; + status: number; + status_desc: string; + last_refresh: Date; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/devices.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/devices.ts new file mode 100644 index 000000000..69ab3f5f3 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/devices.ts @@ -0,0 +1,25 @@ +/** + * Fields returned by the back-end. + */ +export interface CephDevice { + devid: string; + location: { host: string; dev: string }[]; + daemons: string[]; + life_expectancy_min?: string; + life_expectancy_max?: string; + life_expectancy_stamp?: string; + life_expectancy_enabled?: boolean; +} + +/** + * Fields added by the front-end. Fields may be empty if no expectancy is provided for the + * CephDevice interface. + */ +export interface CdDevice extends CephDevice { + life_expectancy_weeks?: { + max: number; + min: number; + }; + state?: 'good' | 'warning' | 'bad' | 'stale' | 'unknown'; + readableDaemons?: string; // Human readable daemons (which can wrap lines inside the table cell) +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/erasure-code-profile.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/erasure-code-profile.ts new file mode 100644 index 000000000..ea9985ccd --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/erasure-code-profile.ts @@ -0,0 +1,17 @@ +export class ErasureCodeProfile { + name: string; + plugin: string; + k?: number; + m?: number; + c?: number; + l?: number; + d?: number; + packetsize?: number; + technique?: string; + scalar_mds?: 'jerasure' | 'isa' | 'shec'; + 'crush-root'?: string; + 'crush-locality'?: string; + 'crush-failure-domain'?: string; + 'crush-device-class'?: string; + 'directory'?: string; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/executing-task.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/executing-task.ts new file mode 100644 index 000000000..27dc5968e --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/executing-task.ts @@ -0,0 +1,6 @@ +import { Task } from './task'; + +export class ExecutingTask extends Task { + begin_time: number; + progress: number; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/finished-task.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/finished-task.ts new file mode 100644 index 000000000..9e7dd5f98 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/finished-task.ts @@ -0,0 +1,15 @@ +import { Task } from './task'; +import { TaskException } from './task-exception'; + +export class FinishedTask extends Task { + begin_time: string; + end_time: string; + exception: TaskException; + latency: number; + progress: number; + ret_value: any; + success: boolean; + duration: number; + + errorMessage: string; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/flag.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/flag.ts new file mode 100644 index 000000000..075decbf7 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/flag.ts @@ -0,0 +1,8 @@ +export class Flag { + code: 'noout' | 'noin' | 'nodown' | 'noup'; + name: string; + description: string; + value: boolean; + clusterWide: boolean; + indeterminate: boolean; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/image-spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/image-spec.ts new file mode 100644 index 000000000..8b56b291c --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/image-spec.ts @@ -0,0 +1,25 @@ +export class ImageSpec { + static fromString(imageSpec: string) { + const imageSpecSplited = imageSpec.split('/'); + + const poolName = imageSpecSplited[0]; + const namespace = imageSpecSplited.length >= 3 ? imageSpecSplited[1] : null; + const imageName = imageSpecSplited.length >= 3 ? imageSpecSplited[2] : imageSpecSplited[1]; + + return new this(poolName, namespace, imageName); + } + + constructor(public poolName: string, public namespace: string, public imageName: string) {} + + private getNameSpace() { + return this.namespace ? `${this.namespace}/` : ''; + } + + toString() { + return `${this.poolName}/${this.getNameSpace()}${this.imageName}`; + } + + toStringEncoded() { + return encodeURIComponent(`${this.poolName}/${this.getNameSpace()}${this.imageName}`); + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/inventory-device-type.model.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/inventory-device-type.model.ts new file mode 100644 index 000000000..2155c2d87 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/inventory-device-type.model.ts @@ -0,0 +1,9 @@ +import { InventoryDevice } from '~/app/ceph/cluster/inventory/inventory-devices/inventory-device.model'; + +export interface InventoryDeviceType { + type: string; + capacity: number; + devices: InventoryDevice[]; + canSelect: boolean; + totalDevices: number; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/login-response.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/login-response.ts new file mode 100644 index 000000000..12b4b8348 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/login-response.ts @@ -0,0 +1,7 @@ +export class LoginResponse { + username: string; + permissions: object; + pwdExpirationDate: number; + sso: boolean; + pwdUpdateRequired: boolean; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/mirroring-summary.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/mirroring-summary.ts new file mode 100644 index 000000000..5487fab0a --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/mirroring-summary.ts @@ -0,0 +1,5 @@ +export interface MirroringSummary { + content_data?: any; + site_name?: any; + status?: any; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/orchestrator.enum.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/orchestrator.enum.ts new file mode 100644 index 000000000..22101caaa --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/orchestrator.enum.ts @@ -0,0 +1,25 @@ +export enum OrchestratorFeature { + HOST_LIST = 'get_hosts', + HOST_ADD = 'add_host', + HOST_REMOVE = 'remove_host', + HOST_LABEL_ADD = 'add_host_label', + HOST_LABEL_REMOVE = 'remove_host_label', + HOST_MAINTENANCE_ENTER = 'enter_host_maintenance', + HOST_MAINTENANCE_EXIT = 'exit_host_maintenance', + HOST_FACTS = 'get_facts', + HOST_DRAIN = 'drain_host', + + SERVICE_LIST = 'describe_service', + SERVICE_CREATE = 'apply', + SERVICE_EDIT = 'apply', + SERVICE_DELETE = 'remove_service', + SERVICE_RELOAD = 'service_action', + DAEMON_LIST = 'list_daemons', + + OSD_GET_REMOVE_STATUS = 'remove_osds_status', + OSD_CREATE = 'apply_drivegroups', + OSD_DELETE = 'remove_osds', + + DEVICE_LIST = 'get_inventory', + DEVICE_BLINK_LIGHT = 'blink_device_light' +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/orchestrator.interface.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/orchestrator.interface.ts new file mode 100644 index 000000000..4eceba8c0 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/orchestrator.interface.ts @@ -0,0 +1,9 @@ +export interface OrchestratorStatus { + available: boolean; + message: string; + features: { + [feature: string]: { + available: boolean; + }; + }; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/osd-deployment-options.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/osd-deployment-options.ts new file mode 100644 index 000000000..cae869efe --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/osd-deployment-options.ts @@ -0,0 +1,24 @@ +export enum OsdDeploymentOptions { + COST_CAPACITY = 'cost_capacity', + THROUGHPUT = 'throughput_optimized', + IOPS = 'iops_optimized' +} + +export interface DeploymentOption { + name: OsdDeploymentOptions; + title: string; + desc: string; + capacity: number; + available: boolean; + hdd_used: number; + used: number; + nvme_used: number; + ssd_used: number; +} + +export interface DeploymentOptions { + options: { + [key in OsdDeploymentOptions]: DeploymentOption; + }; + recommended_option: OsdDeploymentOptions; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/osd-settings.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/osd-settings.ts new file mode 100644 index 000000000..b7bc10fc0 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/osd-settings.ts @@ -0,0 +1,4 @@ +export class OsdSettings { + nearfull_ratio: number; + full_ratio: number; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permission.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permission.spec.ts new file mode 100644 index 000000000..fb2c90469 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permission.spec.ts @@ -0,0 +1,62 @@ +import { Permissions } from './permissions'; + +describe('cd-notification classes', () => { + it('should show empty permissions', () => { + expect(new Permissions({})).toEqual({ + cephfs: { create: false, delete: false, read: false, update: false }, + configOpt: { create: false, delete: false, read: false, update: false }, + grafana: { create: false, delete: false, read: false, update: false }, + hosts: { create: false, delete: false, read: false, update: false }, + iscsi: { create: false, delete: false, read: false, update: false }, + log: { create: false, delete: false, read: false, update: false }, + manager: { create: false, delete: false, read: false, update: false }, + monitor: { create: false, delete: false, read: false, update: false }, + nfs: { create: false, delete: false, read: false, update: false }, + osd: { create: false, delete: false, read: false, update: false }, + pool: { create: false, delete: false, read: false, update: false }, + prometheus: { create: false, delete: false, read: false, update: false }, + rbdImage: { create: false, delete: false, read: false, update: false }, + rbdMirroring: { create: false, delete: false, read: false, update: false }, + rgw: { create: false, delete: false, read: false, update: false }, + user: { create: false, delete: false, read: false, update: false } + }); + }); + + it('should show full permissions', () => { + const fullyGranted = { + cephfs: ['create', 'read', 'update', 'delete'], + 'config-opt': ['create', 'read', 'update', 'delete'], + grafana: ['create', 'read', 'update', 'delete'], + hosts: ['create', 'read', 'update', 'delete'], + iscsi: ['create', 'read', 'update', 'delete'], + log: ['create', 'read', 'update', 'delete'], + manager: ['create', 'read', 'update', 'delete'], + monitor: ['create', 'read', 'update', 'delete'], + osd: ['create', 'read', 'update', 'delete'], + pool: ['create', 'read', 'update', 'delete'], + prometheus: ['create', 'read', 'update', 'delete'], + 'rbd-image': ['create', 'read', 'update', 'delete'], + 'rbd-mirroring': ['create', 'read', 'update', 'delete'], + rgw: ['create', 'read', 'update', 'delete'], + user: ['create', 'read', 'update', 'delete'] + }; + expect(new Permissions(fullyGranted)).toEqual({ + cephfs: { create: true, delete: true, read: true, update: true }, + configOpt: { create: true, delete: true, read: true, update: true }, + grafana: { create: true, delete: true, read: true, update: true }, + hosts: { create: true, delete: true, read: true, update: true }, + iscsi: { create: true, delete: true, read: true, update: true }, + log: { create: true, delete: true, read: true, update: true }, + manager: { create: true, delete: true, read: true, update: true }, + monitor: { create: true, delete: true, read: true, update: true }, + nfs: { create: false, delete: false, read: false, update: false }, + osd: { create: true, delete: true, read: true, update: true }, + pool: { create: true, delete: true, read: true, update: true }, + prometheus: { create: true, delete: true, read: true, update: true }, + rbdImage: { create: true, delete: true, read: true, update: true }, + rbdMirroring: { create: true, delete: true, read: true, update: true }, + rgw: { create: true, delete: true, read: true, update: true }, + user: { create: true, delete: true, read: true, update: true } + }); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permissions.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permissions.ts new file mode 100644 index 000000000..3f2c87ed1 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/permissions.ts @@ -0,0 +1,50 @@ +export class Permission { + read: boolean; + create: boolean; + update: boolean; + delete: boolean; + + constructor(serverPermission: Array<string> = []) { + ['read', 'create', 'update', 'delete'].forEach( + (permission) => (this[permission] = serverPermission.includes(permission)) + ); + } +} + +export class Permissions { + hosts: Permission; + configOpt: Permission; + pool: Permission; + osd: Permission; + monitor: Permission; + rbdImage: Permission; + iscsi: Permission; + rbdMirroring: Permission; + rgw: Permission; + cephfs: Permission; + manager: Permission; + log: Permission; + user: Permission; + grafana: Permission; + prometheus: Permission; + nfs: Permission; + + constructor(serverPermissions: any) { + this.hosts = new Permission(serverPermissions['hosts']); + this.configOpt = new Permission(serverPermissions['config-opt']); + this.pool = new Permission(serverPermissions['pool']); + this.osd = new Permission(serverPermissions['osd']); + this.monitor = new Permission(serverPermissions['monitor']); + this.rbdImage = new Permission(serverPermissions['rbd-image']); + this.iscsi = new Permission(serverPermissions['iscsi']); + this.rbdMirroring = new Permission(serverPermissions['rbd-mirroring']); + this.rgw = new Permission(serverPermissions['rgw']); + this.cephfs = new Permission(serverPermissions['cephfs']); + this.manager = new Permission(serverPermissions['manager']); + this.log = new Permission(serverPermissions['log']); + this.user = new Permission(serverPermissions['user']); + this.grafana = new Permission(serverPermissions['grafana']); + this.prometheus = new Permission(serverPermissions['prometheus']); + this.nfs = new Permission(serverPermissions['nfs-ganesha']); + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/pool-form-info.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/pool-form-info.ts new file mode 100644 index 000000000..c5cc0bb6d --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/pool-form-info.ts @@ -0,0 +1,20 @@ +import { CrushNode } from './crush-node'; +import { CrushRule } from './crush-rule'; +import { ErasureCodeProfile } from './erasure-code-profile'; + +export class PoolFormInfo { + pool_names: string[]; + osd_count: number; + is_all_bluestore: boolean; + bluestore_compression_algorithm: string; + compression_algorithms: string[]; + compression_modes: string[]; + crush_rules_replicated: CrushRule[]; + crush_rules_erasure: CrushRule[]; + pg_autoscale_default_mode: string; + pg_autoscale_modes: string[]; + erasure_code_profiles: ErasureCodeProfile[]; + used_rules: { [rule_name: string]: string[] }; + used_profiles: { [profile_name: string]: string[] }; + nodes: CrushNode[]; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/prometheus-alerts.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/prometheus-alerts.ts new file mode 100644 index 000000000..1239dcccd --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/prometheus-alerts.ts @@ -0,0 +1,84 @@ +export class PrometheusAlertLabels { + alertname: string; + instance: string; + job: string; + severity: string; +} + +class Annotations { + description: string; +} + +class CommonAlertmanagerAlert { + labels: PrometheusAlertLabels; + annotations: Annotations; + startsAt: string; // Date string + endsAt: string; // Date string + generatorURL: string; +} + +class PrometheusAlert { + labels: PrometheusAlertLabels; + annotations: Annotations; + state: 'pending' | 'firing'; + activeAt: string; // Date string + value: number; +} + +export interface PrometheusRuleGroup { + name: string; + file: string; + rules: PrometheusRule[]; +} + +export class PrometheusRule { + name: string; // => PrometheusAlertLabels.alertname + query: string; + duration: 10; + labels: { + severity: string; // => PrometheusAlertLabels.severity + }; + annotations: Annotations; + alerts: PrometheusAlert[]; // Shows only active alerts + health: string; + type: string; + group?: string; // Added field for flattened list +} + +export class AlertmanagerAlert extends CommonAlertmanagerAlert { + status: { + state: 'unprocessed' | 'active' | 'suppressed'; + silencedBy: null | string[]; + inhibitedBy: null | string[]; + }; + receivers: string[]; + fingerprint: string; +} + +export class AlertmanagerNotificationAlert extends CommonAlertmanagerAlert { + status: 'firing' | 'resolved'; +} + +export class AlertmanagerNotification { + status: 'firing' | 'resolved'; + groupLabels: object; + commonAnnotations: object; + groupKey: string; + notified: string; + id: string; + alerts: AlertmanagerNotificationAlert[]; + version: string; + receiver: string; + externalURL: string; + commonLabels: { + severity: string; + }; +} + +export class PrometheusCustomAlert { + status: 'resolved' | 'unprocessed' | 'active' | 'suppressed'; + name: string; + url: string; + description: string; + fingerprint?: string | boolean; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/service.interface.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/service.interface.ts new file mode 100644 index 000000000..dd64422e1 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/service.interface.ts @@ -0,0 +1,45 @@ +export interface CephServiceStatus { + container_image_id: string; + container_image_name: string; + size: number; + running: number; + last_refresh: Date; + created: Date; +} + +// This will become handy when creating arbitrary services +export interface CephServiceSpec { + service_name: string; + service_type: string; + service_id: string; + unmanaged: boolean; + status: CephServiceStatus; + spec: CephServiceAdditionalSpec; + placement: CephServicePlacement; +} + +export interface CephServiceAdditionalSpec { + backend_service: string; + api_user: string; + api_password: string; + api_port: number; + api_secure: boolean; + rgw_frontend_port: number; + trusted_ip_list: string[]; + virtual_ip: string; + frontend_port: number; + monitor_port: number; + virtual_interface_networks: string[]; + pool: string; + rgw_frontend_ssl_certificate: string; + ssl: boolean; + ssl_cert: string; + ssl_key: string; +} + +export interface CephServicePlacement { + count: number; + placement: string; + hosts: string[]; + label: string; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/smart.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/smart.ts new file mode 100644 index 000000000..f553652bc --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/smart.ts @@ -0,0 +1,253 @@ +export interface SmartAttribute { + flags: { + auto_keep: boolean; + error_rate: boolean; + event_count: boolean; + performance: boolean; + prefailure: boolean; + string: string; + updated_online: boolean; + value: number; + }; + id: number; + name: string; + raw: { string: string; value: number }; + thresh: number; + value: number; + when_failed: string; + worst: number; +} + +/** + * The error structure returned from the back-end if SMART data couldn't be + * retrieved. + */ +export interface SmartError { + dev: string; + error: string; + nvme_smart_health_information_add_log_error: string; + nvme_smart_health_information_add_log_error_code: number; + nvme_vendor: string; + smartctl_error_code: number; + smartctl_output: string; +} + +/** + * Common smartctl output structure. + */ +interface SmartCtlOutput { + argv: string[]; + build_info: string; + exit_status: number; + output: string[]; + platform_info: string; + svn_revision: string; + version: number[]; +} + +/** + * Common smartctl device structure. + */ +interface SmartCtlDevice { + info_name: string; + name: string; + protocol: string; + type: string; +} + +/** + * smartctl data structure shared among HDD/NVMe. + */ +interface SmartCtlBaseDataV1 { + device: SmartCtlDevice; + firmware_version: string; + json_format_version: number[]; + local_time: { asctime: string; time_t: number }; + logical_block_size: number; + model_name: string; + nvme_smart_health_information_add_log_error: string; + nvme_smart_health_information_add_log_error_code: number; + nvme_vendor: string; + power_cycle_count: number; + power_on_time: { hours: number }; + serial_number: string; + smart_status: { passed: boolean; nvme?: { value: number } }; + smartctl: SmartCtlOutput; + temperature: { current: number }; + user_capacity: { blocks: number; bytes: number }; +} + +export interface RVWAttributes { + correction_algorithm_invocations: number; + errors_corrected_by_eccdelayed: number; + errors_corrected_by_eccfast: number; + errors_corrected_by_rereads_rewrites: number; + gigabytes_processed: number; + total_errors_corrected: number; + total_uncorrected_errors: number; +} + +/** + * Result structure of `smartctl` applied on an SCSI. Returned by the back-end. + */ +export interface IscsiSmartDataV1 extends SmartCtlBaseDataV1 { + scsi_error_counter_log: { + read: RVWAttributes[]; + }; + scsi_grown_defect_list: number; +} + +/** + * Result structure of `smartctl` applied on an HDD. Returned by the back-end. + */ +export interface AtaSmartDataV1 extends SmartCtlBaseDataV1 { + ata_sct_capabilities: { + data_table_supported: boolean; + error_recovery_control_supported: boolean; + feature_control_supported: boolean; + value: number; + }; + ata_smart_attributes: { + revision: number; + table: SmartAttribute[]; + }; + ata_smart_data: { + capabilities: { + attribute_autosave_enabled: boolean; + conveyance_self_test_supported: boolean; + error_logging_supported: boolean; + exec_offline_immediate_supported: boolean; + gp_logging_supported: boolean; + offline_is_aborted_upon_new_cmd: boolean; + offline_surface_scan_supported: boolean; + selective_self_test_supported: boolean; + self_tests_supported: boolean; + values: number[]; + }; + offline_data_collection: { + completion_seconds: number; + status: { string: string; value: number }; + }; + self_test: { + polling_minutes: { conveyance: number; extended: number; short: number }; + status: { passed: boolean; string: string; value: number }; + }; + }; + ata_smart_error_log: { summary: { count: number; revision: number } }; + ata_smart_selective_self_test_log: { + flags: { remainder_scan_enabled: boolean; value: number }; + power_up_scan_resume_minutes: number; + revision: number; + table: { + lba_max: number; + lba_min: number; + status: { string: string; value: number }; + }[]; + }; + ata_smart_self_test_log: { standard: { count: number; revision: number } }; + ata_version: { major_value: number; minor_value: number; string: string }; + in_smartctl_database: boolean; + interface_speed: { + current: { + bits_per_unit: number; + sata_value: number; + string: string; + units_per_second: number; + }; + max: { + bits_per_unit: number; + sata_value: number; + string: string; + units_per_second: number; + }; + }; + model_family: string; + physical_block_size: number; + rotation_rate: number; + sata_version: { string: string; value: number }; + smart_status: { passed: boolean }; + smartctl: SmartCtlOutput; + wwn: { id: number; naa: number; oui: number }; +} + +/** + * Result structure of `smartctl` returned by Ceph and then back-end applied on + * an NVMe. + */ +export interface NvmeSmartDataV1 extends SmartCtlBaseDataV1 { + nvme_controller_id: number; + nvme_ieee_oui_identifier: number; + nvme_namespaces: { + capacity: { blocks: number; bytes: number }; + eui64: { ext_id: number; oui: number }; + formatted_lba_size: number; + id: number; + size: { blocks: number; bytes: number }; + utilization: { blocks: number; bytes: number }; + }[]; + nvme_number_of_namespaces: number; + nvme_pci_vendor: { id: number; subsystem_id: number }; + nvme_smart_health_information_log: { + available_spare: number; + available_spare_threshold: number; + controller_busy_time: number; + critical_comp_time: number; + critical_warning: number; + data_units_read: number; + data_units_written: number; + host_reads: number; + host_writes: number; + media_errors: number; + num_err_log_entries: number; + percentage_used: number; + power_cycles: number; + power_on_hours: number; + temperature: number; + temperature_sensors: number[]; + unsafe_shutdowns: number; + warning_temp_time: number; + }; + nvme_total_capacity: number; + nvme_unallocated_capacity: number; +} + +/** + * The shared fields each result has after it has been processed by the front-end. + */ +interface SmartBasicResult { + device: string; + identifier: string; +} + +/** + * The SMART data response structure of the back-end. Per device it will either + * contain the structure for a HDD, NVMe or an error. + */ +export interface SmartDataResponseV1 { + [deviceId: string]: AtaSmartDataV1 | NvmeSmartDataV1 | SmartError; +} + +/** + * The SMART data result after it has been processed by the front-end. + */ +export interface SmartDataResult extends SmartBasicResult { + info: { [key: string]: any }; + smart: { + attributes?: any; + data?: any; + nvmeData?: any; + scsi_error_counter_log?: any; + scsi_grown_defect_list?: any; + }; +} + +/** + * The SMART error result after is has been processed by the front-end. If SMART + * data couldn't be retrieved, this is the structure which is returned. + */ +export interface SmartErrorResult extends SmartBasicResult { + error: string; + smartctl_error_code: number; + smartctl_output: string; + userMessage: string; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/summary.model.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/summary.model.ts new file mode 100644 index 000000000..f2854a0eb --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/summary.model.ts @@ -0,0 +1,15 @@ +import { ExecutingTask } from './executing-task'; +import { FinishedTask } from './finished-task'; + +export class Summary { + executing_tasks?: ExecutingTask[]; + filesystems?: any[]; + finished_tasks?: FinishedTask[]; + have_mon_connection?: boolean; + health_status?: string; + mgr_host?: string; + mgr_id?: string; + rbd_mirroring?: any; + rbd_pools?: any[]; + version?: string; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/task-exception.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/task-exception.ts new file mode 100644 index 000000000..ba38e4aab --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/task-exception.ts @@ -0,0 +1,9 @@ +import { Task } from './task'; + +export class TaskException { + status: number; + code: number; + component: string; + detail: string; + task: Task; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/task.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/task.ts new file mode 100644 index 000000000..0adec5a0f --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/task.ts @@ -0,0 +1,10 @@ +export class Task { + constructor(name?: string, metadata?: object) { + this.name = name; + this.metadata = metadata; + } + name: string; + metadata: object; + + description: string; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/wizard-steps.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/wizard-steps.ts new file mode 100644 index 000000000..177feb486 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/wizard-steps.ts @@ -0,0 +1,4 @@ +export interface WizardStepModel { + stepIndex: number; + isComplete: boolean; +} |