]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.ts
import 15.2.5
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / cluster / hosts / hosts.component.ts
1 import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
2 import { Router } from '@angular/router';
3
4 import { I18n } from '@ngx-translate/i18n-polyfill';
5 import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
6
7 import { HostService } from '../../../shared/api/host.service';
8 import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
9 import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
10 import { FormModalComponent } from '../../../shared/components/form-modal/form-modal.component';
11 import { SelectMessages } from '../../../shared/components/select/select-messages.model';
12 import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
13 import { TableComponent } from '../../../shared/datatable/table/table.component';
14 import { Icons } from '../../../shared/enum/icons.enum';
15 import { NotificationType } from '../../../shared/enum/notification-type.enum';
16 import { CdTableAction } from '../../../shared/models/cd-table-action';
17 import { CdTableColumn } from '../../../shared/models/cd-table-column';
18 import { CdTableFetchDataContext } from '../../../shared/models/cd-table-fetch-data-context';
19 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
20 import { FinishedTask } from '../../../shared/models/finished-task';
21 import { Permissions } from '../../../shared/models/permissions';
22 import { CephShortVersionPipe } from '../../../shared/pipes/ceph-short-version.pipe';
23 import { JoinPipe } from '../../../shared/pipes/join.pipe';
24 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
25 import { DepCheckerService } from '../../../shared/services/dep-checker.service';
26 import { NotificationService } from '../../../shared/services/notification.service';
27 import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
28 import { URLBuilderService } from '../../../shared/services/url-builder.service';
29
30 const BASE_URL = 'hosts';
31
32 @Component({
33 selector: 'cd-hosts',
34 templateUrl: './hosts.component.html',
35 styleUrls: ['./hosts.component.scss'],
36 providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }]
37 })
38 export class HostsComponent extends ListWithDetails implements OnInit {
39 @ViewChild(TableComponent, { static: true })
40 table: TableComponent;
41 @ViewChild('servicesTpl', { static: true })
42 public servicesTpl: TemplateRef<any>;
43
44 permissions: Permissions;
45 columns: Array<CdTableColumn> = [];
46 hosts: Array<object> = [];
47 isLoadingHosts = false;
48 cdParams = { fromLink: '/hosts' };
49 tableActions: CdTableAction[];
50 selection = new CdTableSelection();
51 modalRef: BsModalRef;
52
53 constructor(
54 private authStorageService: AuthStorageService,
55 private hostService: HostService,
56 private cephShortVersionPipe: CephShortVersionPipe,
57 private joinPipe: JoinPipe,
58 private i18n: I18n,
59 private urlBuilder: URLBuilderService,
60 private actionLabels: ActionLabelsI18n,
61 private modalService: BsModalService,
62 private taskWrapper: TaskWrapperService,
63 private router: Router,
64 private depCheckerService: DepCheckerService,
65 private notificationService: NotificationService
66 ) {
67 super();
68 this.permissions = this.authStorageService.getPermissions();
69 this.tableActions = [
70 {
71 name: this.actionLabels.CREATE,
72 permission: 'create',
73 icon: Icons.add,
74 click: () => {
75 this.depCheckerService.checkOrchestratorOrModal(
76 this.actionLabels.CREATE,
77 this.i18n('Host'),
78 () => {
79 this.router.navigate([this.urlBuilder.getCreate()]);
80 }
81 );
82 }
83 },
84 {
85 name: this.actionLabels.EDIT,
86 permission: 'update',
87 icon: Icons.edit,
88 click: () => {
89 this.depCheckerService.checkOrchestratorOrModal(
90 this.actionLabels.EDIT,
91 this.i18n('Host'),
92 () => this.editAction()
93 );
94 },
95 disable: (selection: CdTableSelection) =>
96 !selection.hasSingleSelection || !selection.first().sources.orchestrator,
97 disableDesc: this.getEditDisableDesc.bind(this)
98 },
99 {
100 name: this.actionLabels.DELETE,
101 permission: 'delete',
102 icon: Icons.destroy,
103 click: () => {
104 this.depCheckerService.checkOrchestratorOrModal(
105 this.actionLabels.DELETE,
106 this.i18n('Host'),
107 () => this.deleteAction()
108 );
109 },
110 disable: () => !this.selection.hasSelection
111 }
112 ];
113 }
114
115 ngOnInit() {
116 this.columns = [
117 {
118 name: this.i18n('Hostname'),
119 prop: 'hostname',
120 flexGrow: 1
121 },
122 {
123 name: this.i18n('Services'),
124 prop: 'services',
125 flexGrow: 3,
126 cellTemplate: this.servicesTpl
127 },
128 {
129 name: this.i18n('Labels'),
130 prop: 'labels',
131 flexGrow: 1,
132 pipe: this.joinPipe
133 },
134 {
135 name: this.i18n('Version'),
136 prop: 'ceph_version',
137 flexGrow: 1,
138 pipe: this.cephShortVersionPipe
139 }
140 ];
141 }
142
143 updateSelection(selection: CdTableSelection) {
144 this.selection = selection;
145 }
146
147 editAction() {
148 this.hostService.getLabels().subscribe((resp: string[]) => {
149 const host = this.selection.first();
150 const allLabels = resp.map((label) => {
151 return { enabled: true, name: label };
152 });
153 this.modalService.show(FormModalComponent, {
154 initialState: {
155 titleText: this.i18n('Edit Host: {{hostname}}', host),
156 fields: [
157 {
158 type: 'select-badges',
159 name: 'labels',
160 value: host['labels'],
161 label: this.i18n('Labels'),
162 typeConfig: {
163 customBadges: true,
164 options: allLabels,
165 messages: new SelectMessages(
166 {
167 empty: this.i18n('There are no labels.'),
168 filter: this.i18n('Filter or add labels'),
169 add: this.i18n('Add label')
170 },
171 this.i18n
172 )
173 }
174 }
175 ],
176 submitButtonText: this.i18n('Edit Host'),
177 onSubmit: (values: any) => {
178 this.hostService.update(host['hostname'], values.labels).subscribe(() => {
179 this.notificationService.show(
180 NotificationType.success,
181 this.i18n('Updated Host "{{hostname}}"', host)
182 );
183 // Reload the data table content.
184 this.table.refreshBtn();
185 });
186 }
187 }
188 });
189 });
190 }
191
192 getEditDisableDesc(selection: CdTableSelection): string | undefined {
193 if (selection && selection.hasSingleSelection && !selection.first().sources.orchestrator) {
194 return this.i18n('Host editing is disabled because the host is not managed by Orchestrator.');
195 }
196 return undefined;
197 }
198
199 deleteAction() {
200 const hostname = this.selection.first().hostname;
201 this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
202 initialState: {
203 itemDescription: 'Host',
204 itemNames: [hostname],
205 actionDescription: 'delete',
206 submitActionObservable: () =>
207 this.taskWrapper.wrapTaskAroundCall({
208 task: new FinishedTask('host/delete', { hostname: hostname }),
209 call: this.hostService.delete(hostname)
210 })
211 }
212 });
213 }
214
215 getHosts(context: CdTableFetchDataContext) {
216 if (this.isLoadingHosts) {
217 return;
218 }
219 const typeToPermissionKey = {
220 mds: 'cephfs',
221 mon: 'monitor',
222 osd: 'osd',
223 rgw: 'rgw',
224 'rbd-mirror': 'rbdMirroring',
225 mgr: 'manager',
226 'tcmu-runner': 'iscsi'
227 };
228 this.isLoadingHosts = true;
229 this.hostService.list().subscribe(
230 (resp: any[]) => {
231 resp.map((host) => {
232 host.services.map((service: any) => {
233 service.cdLink = `/perf_counters/${service.type}/${encodeURIComponent(service.id)}`;
234 const permission = this.permissions[typeToPermissionKey[service.type]];
235 service.canRead = permission ? permission.read : false;
236 return service;
237 });
238 return host;
239 });
240 this.hosts = resp;
241 this.isLoadingHosts = false;
242 },
243 () => {
244 this.isLoadingHosts = false;
245 context.error();
246 }
247 );
248 }
249 }