]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.component.ts
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / cluster / services / services.component.ts
CommitLineData
9f95a23c 1import { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';
9f95a23c 2
adb31ebb
TL
3import { delay, finalize } from 'rxjs/operators';
4
f67539c2
TL
5import { CephServiceService } from '~/app/shared/api/ceph-service.service';
6import { OrchestratorService } from '~/app/shared/api/orchestrator.service';
7import { ListWithDetails } from '~/app/shared/classes/list-with-details.class';
8import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
9import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
10import { TableComponent } from '~/app/shared/datatable/table/table.component';
11import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
12import { Icons } from '~/app/shared/enum/icons.enum';
13import { CdTableAction } from '~/app/shared/models/cd-table-action';
14import { CdTableColumn } from '~/app/shared/models/cd-table-column';
15import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
16import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
17import { FinishedTask } from '~/app/shared/models/finished-task';
18import { OrchestratorFeature } from '~/app/shared/models/orchestrator.enum';
19import { OrchestratorStatus } from '~/app/shared/models/orchestrator.interface';
20import { Permissions } from '~/app/shared/models/permissions';
21import { CephServiceSpec } from '~/app/shared/models/service.interface';
22import { RelativeDatePipe } from '~/app/shared/pipes/relative-date.pipe';
23import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
24import { ModalService } from '~/app/shared/services/modal.service';
25import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
26import { URLBuilderService } from '~/app/shared/services/url-builder.service';
adb31ebb
TL
27import { PlacementPipe } from './placement.pipe';
28
29const BASE_URL = 'services';
9f95a23c
TL
30
31@Component({
32 selector: 'cd-services',
33 templateUrl: './services.component.html',
adb31ebb
TL
34 styleUrls: ['./services.component.scss'],
35 providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }]
9f95a23c 36})
e306af50 37export class ServicesComponent extends ListWithDetails implements OnChanges, OnInit {
f67539c2 38 @ViewChild(TableComponent, { static: true })
9f95a23c
TL
39 table: TableComponent;
40
41 @Input() hostname: string;
42
43 // Do not display these columns
44 @Input() hiddenColumns: string[] = [];
45
46 permissions: Permissions;
adb31ebb 47 tableActions: CdTableAction[];
f67539c2 48 showDocPanel = false;
9f95a23c 49
f67539c2
TL
50 orchStatus: OrchestratorStatus;
51 actionOrchFeatures = {
52 create: [OrchestratorFeature.SERVICE_CREATE],
53 delete: [OrchestratorFeature.SERVICE_DELETE]
54 };
9f95a23c
TL
55
56 columns: Array<CdTableColumn> = [];
1911f103 57 services: Array<CephServiceSpec> = [];
9f95a23c 58 isLoadingServices = false;
adb31ebb 59 selection: CdTableSelection = new CdTableSelection();
9f95a23c
TL
60
61 constructor(
adb31ebb 62 private actionLabels: ActionLabelsI18n,
9f95a23c 63 private authStorageService: AuthStorageService,
f67539c2 64 private modalService: ModalService,
9f95a23c 65 private orchService: OrchestratorService,
adb31ebb 66 private cephServiceService: CephServiceService,
f67539c2 67 private relativeDatePipe: RelativeDatePipe,
adb31ebb
TL
68 private taskWrapperService: TaskWrapperService,
69 private urlBuilder: URLBuilderService
9f95a23c 70 ) {
e306af50 71 super();
9f95a23c 72 this.permissions = this.authStorageService.getPermissions();
adb31ebb
TL
73 this.tableActions = [
74 {
75 permission: 'create',
76 icon: Icons.add,
77 routerLink: () => this.urlBuilder.getCreate(),
78 name: this.actionLabels.CREATE,
f67539c2
TL
79 canBePrimary: (selection: CdTableSelection) => !selection.hasSelection,
80 disable: (selection: CdTableSelection) => this.getDisable('create', selection)
adb31ebb
TL
81 },
82 {
83 permission: 'delete',
84 icon: Icons.destroy,
85 click: () => this.deleteAction(),
f67539c2
TL
86 name: this.actionLabels.DELETE,
87 disable: (selection: CdTableSelection) => this.getDisable('delete', selection)
adb31ebb
TL
88 }
89 ];
9f95a23c
TL
90 }
91
92 ngOnInit() {
93 const columns = [
94 {
f67539c2 95 name: $localize`Service`,
9f95a23c
TL
96 prop: 'service_name',
97 flexGrow: 1
98 },
99 {
f67539c2 100 name: $localize`Container image name`,
1911f103 101 prop: 'status.container_image_name',
9f95a23c
TL
102 flexGrow: 3
103 },
104 {
f67539c2 105 name: $localize`Container image ID`,
1911f103
TL
106 prop: 'status.container_image_id',
107 flexGrow: 3,
108 cellTransformation: CellTemplate.truncate,
109 customTemplateConfig: {
110 length: 12
111 }
9f95a23c 112 },
adb31ebb 113 {
f67539c2 114 name: $localize`Placement`,
adb31ebb 115 prop: '',
f67539c2 116 pipe: new PlacementPipe(),
adb31ebb
TL
117 flexGrow: 1
118 },
9f95a23c 119 {
f67539c2 120 name: $localize`Running`,
1911f103 121 prop: 'status.running',
adb31ebb 122 flexGrow: 1
9f95a23c
TL
123 },
124 {
f67539c2 125 name: $localize`Size`,
1911f103 126 prop: 'status.size',
9f95a23c
TL
127 flexGrow: 1
128 },
129 {
f67539c2 130 name: $localize`Last Refreshed`,
1911f103 131 prop: 'status.last_refresh',
f67539c2 132 pipe: this.relativeDatePipe,
9f95a23c
TL
133 flexGrow: 1
134 }
135 ];
136
137 this.columns = columns.filter((col: any) => {
138 return !this.hiddenColumns.includes(col.prop);
139 });
140
f67539c2
TL
141 this.orchService.status().subscribe((status: OrchestratorStatus) => {
142 this.orchStatus = status;
adb31ebb 143 this.showDocPanel = !status.available;
9f95a23c
TL
144 });
145 }
146
147 ngOnChanges() {
f67539c2 148 if (this.orchStatus?.available) {
9f95a23c
TL
149 this.services = [];
150 this.table.reloadData();
151 }
152 }
153
f67539c2
TL
154 getDisable(action: 'create' | 'delete', selection: CdTableSelection): boolean | string {
155 if (action === 'delete') {
156 if (!selection?.hasSingleSelection) {
157 return true;
158 }
159 }
160 return this.orchService.getTableActionDisableDesc(
161 this.orchStatus,
162 this.actionOrchFeatures[action]
163 );
164 }
165
9f95a23c
TL
166 getServices(context: CdTableFetchDataContext) {
167 if (this.isLoadingServices) {
168 return;
169 }
170 this.isLoadingServices = true;
171 this.cephServiceService.list().subscribe(
1911f103 172 (services: CephServiceSpec[]) => {
9f95a23c
TL
173 this.services = services;
174 this.isLoadingServices = false;
175 },
176 () => {
177 this.isLoadingServices = false;
178 this.services = [];
179 context.error();
180 }
181 );
182 }
adb31ebb
TL
183
184 updateSelection(selection: CdTableSelection) {
185 this.selection = selection;
186 }
187
188 deleteAction() {
189 const service = this.selection.first();
f67539c2
TL
190 this.modalService.show(CriticalConfirmationModalComponent, {
191 itemDescription: $localize`Service`,
192 itemNames: [service.service_name],
193 actionDescription: 'delete',
194 submitActionObservable: () =>
195 this.taskWrapperService
196 .wrapTaskAroundCall({
197 task: new FinishedTask(`service/${URLVerbs.DELETE}`, {
198 service_name: service.service_name
199 }),
200 call: this.cephServiceService.delete(service.service_name)
201 })
202 .pipe(
203 // Delay closing the dialog, otherwise the datatable still
204 // shows the deleted service after forcing a reload.
205 // Showing the dialog while delaying is done to increase
206 // the user experience.
207 delay(2000),
208 finalize(() => {
209 // Force reloading the data table content because it is
210 // auto-reloaded only every 60s.
211 this.table.refreshBtn();
adb31ebb 212 })
f67539c2 213 )
adb31ebb
TL
214 });
215 }
9f95a23c 216}