summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts')
-rw-r--r--src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts254
1 files changed, 254 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts
new file mode 100644
index 000000000..e0d82cb19
--- /dev/null
+++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts
@@ -0,0 +1,254 @@
+import {
+ Component,
+ EventEmitter,
+ Input,
+ OnDestroy,
+ OnInit,
+ Output,
+ ViewChild
+} from '@angular/core';
+
+import _ from 'lodash';
+import { Subscription } from 'rxjs';
+
+import { HostService } from '~/app/shared/api/host.service';
+import { OrchestratorService } from '~/app/shared/api/orchestrator.service';
+import { FormModalComponent } from '~/app/shared/components/form-modal/form-modal.component';
+import { TableComponent } from '~/app/shared/datatable/table/table.component';
+import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
+import { Icons } from '~/app/shared/enum/icons.enum';
+import { NotificationType } from '~/app/shared/enum/notification-type.enum';
+import { CdTableAction } from '~/app/shared/models/cd-table-action';
+import { CdTableColumn } from '~/app/shared/models/cd-table-column';
+import { CdTableColumnFiltersChange } from '~/app/shared/models/cd-table-column-filters-change';
+import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
+import { OrchestratorFeature } from '~/app/shared/models/orchestrator.enum';
+import { OrchestratorStatus } from '~/app/shared/models/orchestrator.interface';
+import { Permission } from '~/app/shared/models/permissions';
+import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
+import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
+import { ModalService } from '~/app/shared/services/modal.service';
+import { NotificationService } from '~/app/shared/services/notification.service';
+import { InventoryDevice } from './inventory-device.model';
+
+@Component({
+ selector: 'cd-inventory-devices',
+ templateUrl: './inventory-devices.component.html',
+ styleUrls: ['./inventory-devices.component.scss']
+})
+export class InventoryDevicesComponent implements OnInit, OnDestroy {
+ @ViewChild(TableComponent, { static: true })
+ table: TableComponent;
+
+ // Devices
+ @Input() devices: InventoryDevice[] = [];
+
+ @Input() showAvailDeviceOnly = false;
+ // Do not display these columns
+ @Input() hiddenColumns: string[] = [];
+
+ // Show filters for these columns, specify empty array to disable
+ @Input() filterColumns = [
+ 'hostname',
+ 'human_readable_type',
+ 'available',
+ 'sys_api.vendor',
+ 'sys_api.model',
+ 'sys_api.size'
+ ];
+
+ // Device table row selection type
+ @Input() selectionType: string = undefined;
+
+ @Output() filterChange = new EventEmitter<CdTableColumnFiltersChange>();
+
+ @Output() fetchInventory = new EventEmitter();
+
+ icons = Icons;
+ columns: Array<CdTableColumn> = [];
+ selection: CdTableSelection = new CdTableSelection();
+ permission: Permission;
+ tableActions: CdTableAction[];
+ fetchInventorySub: Subscription;
+
+ @Input() orchStatus: OrchestratorStatus = undefined;
+
+ actionOrchFeatures = {
+ identify: [OrchestratorFeature.DEVICE_BLINK_LIGHT]
+ };
+
+ constructor(
+ private authStorageService: AuthStorageService,
+ private dimlessBinary: DimlessBinaryPipe,
+ private modalService: ModalService,
+ private notificationService: NotificationService,
+ private orchService: OrchestratorService,
+ private hostService: HostService
+ ) {}
+
+ ngOnInit() {
+ this.permission = this.authStorageService.getPermissions().osd;
+ this.tableActions = [
+ {
+ permission: 'update',
+ icon: Icons.show,
+ click: () => this.identifyDevice(),
+ name: $localize`Identify`,
+ disable: (selection: CdTableSelection) => this.getDisable('identify', selection),
+ canBePrimary: (selection: CdTableSelection) => !selection.hasSingleSelection,
+ visible: () => _.isString(this.selectionType)
+ }
+ ];
+ const columns = [
+ {
+ name: $localize`Hostname`,
+ prop: 'hostname',
+ flexGrow: 1
+ },
+ {
+ name: $localize`Device path`,
+ prop: 'path',
+ flexGrow: 1
+ },
+ {
+ name: $localize`Type`,
+ prop: 'human_readable_type',
+ flexGrow: 1,
+ cellTransformation: CellTemplate.badge,
+ customTemplateConfig: {
+ map: {
+ hdd: { value: 'HDD', class: 'badge-hdd' },
+ ssd: { value: 'SSD', class: 'badge-ssd' }
+ }
+ }
+ },
+ {
+ name: $localize`Available`,
+ prop: 'available',
+ flexGrow: 1,
+ cellClass: 'text-center',
+ cellTransformation: CellTemplate.checkIcon
+ },
+ {
+ name: $localize`Vendor`,
+ prop: 'sys_api.vendor',
+ flexGrow: 1
+ },
+ {
+ name: $localize`Model`,
+ prop: 'sys_api.model',
+ flexGrow: 1
+ },
+ {
+ name: $localize`Size`,
+ prop: 'sys_api.size',
+ flexGrow: 1,
+ pipe: this.dimlessBinary
+ },
+ {
+ name: $localize`OSDs`,
+ prop: 'osd_ids',
+ flexGrow: 1,
+ cellTransformation: CellTemplate.badge,
+ customTemplateConfig: {
+ class: 'badge-dark',
+ prefix: 'osd.'
+ }
+ }
+ ];
+
+ this.columns = columns.filter((col: any) => {
+ return !this.hiddenColumns.includes(col.prop);
+ });
+
+ // init column filters
+ _.forEach(this.filterColumns, (prop) => {
+ const col = _.find(this.columns, { prop: prop });
+ if (col) {
+ col.filterable = true;
+ }
+ });
+
+ if (this.fetchInventory.observers.length > 0) {
+ this.fetchInventorySub = this.table.fetchData.subscribe(() => {
+ this.fetchInventory.emit();
+ });
+ }
+ }
+
+ getDevices() {
+ if (this.showAvailDeviceOnly) {
+ this.hostService.inventoryDeviceList().subscribe(
+ (devices: InventoryDevice[]) => {
+ this.devices = _.filter(devices, 'available');
+ this.devices = [...this.devices];
+ },
+ () => {
+ this.devices = [];
+ }
+ );
+ } else {
+ this.devices = [...this.devices];
+ }
+ }
+
+ ngOnDestroy() {
+ if (this.fetchInventorySub) {
+ this.fetchInventorySub.unsubscribe();
+ }
+ }
+
+ onColumnFiltersChanged(event: CdTableColumnFiltersChange) {
+ this.filterChange.emit(event);
+ }
+
+ getDisable(action: 'identify', selection: CdTableSelection): boolean | string {
+ if (!selection.hasSingleSelection) {
+ return true;
+ }
+ return this.orchService.getTableActionDisableDesc(
+ this.orchStatus,
+ this.actionOrchFeatures[action]
+ );
+ }
+
+ updateSelection(selection: CdTableSelection) {
+ this.selection = selection;
+ }
+
+ identifyDevice() {
+ const selected = this.selection.first();
+ const hostname = selected.hostname;
+ const device = selected.path || selected.device_id;
+ this.modalService.show(FormModalComponent, {
+ titleText: $localize`Identify device ${device}`,
+ message: $localize`Please enter the duration how long to blink the LED.`,
+ fields: [
+ {
+ type: 'select',
+ name: 'duration',
+ value: 300,
+ required: true,
+ typeConfig: {
+ options: [
+ { text: $localize`1 minute`, value: 60 },
+ { text: $localize`2 minutes`, value: 120 },
+ { text: $localize`5 minutes`, value: 300 },
+ { text: $localize`10 minutes`, value: 600 },
+ { text: $localize`15 minutes`, value: 900 }
+ ]
+ }
+ }
+ ],
+ submitButtonText: $localize`Execute`,
+ onSubmit: (values: any) => {
+ this.hostService.identifyDevice(hostname, device, values.duration).subscribe(() => {
+ this.notificationService.show(
+ NotificationType.success,
+ $localize`Identifying '${device}' started on host '${hostname}'`
+ );
+ });
+ }
+ });
+ }
+}