]>
Commit | Line | Data |
---|---|---|
a4b75251 | 1 | import { Component, Input, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; |
9f95a23c | 2 | import { Router } from '@angular/router'; |
11fdf7f2 | 3 | |
f67539c2 TL |
4 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; |
5 | import _ from 'lodash'; | |
39ae355f TL |
6 | import { Subscription } from 'rxjs'; |
7 | import { mergeMap } from 'rxjs/operators'; | |
11fdf7f2 | 8 | |
f67539c2 TL |
9 | import { HostService } from '~/app/shared/api/host.service'; |
10 | import { OrchestratorService } from '~/app/shared/api/orchestrator.service'; | |
11 | import { ListWithDetails } from '~/app/shared/classes/list-with-details.class'; | |
12 | import { ConfirmationModalComponent } from '~/app/shared/components/confirmation-modal/confirmation-modal.component'; | |
13 | import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component'; | |
14 | import { FormModalComponent } from '~/app/shared/components/form-modal/form-modal.component'; | |
15 | import { SelectMessages } from '~/app/shared/components/select/select-messages.model'; | |
a4b75251 | 16 | import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants'; |
f67539c2 TL |
17 | import { TableComponent } from '~/app/shared/datatable/table/table.component'; |
18 | import { CellTemplate } from '~/app/shared/enum/cell-template.enum'; | |
19 | import { Icons } from '~/app/shared/enum/icons.enum'; | |
20 | import { NotificationType } from '~/app/shared/enum/notification-type.enum'; | |
21 | import { CdTableAction } from '~/app/shared/models/cd-table-action'; | |
22 | import { CdTableColumn } from '~/app/shared/models/cd-table-column'; | |
23 | import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context'; | |
24 | import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; | |
25 | import { FinishedTask } from '~/app/shared/models/finished-task'; | |
26 | import { OrchestratorFeature } from '~/app/shared/models/orchestrator.enum'; | |
27 | import { OrchestratorStatus } from '~/app/shared/models/orchestrator.interface'; | |
28 | import { Permissions } from '~/app/shared/models/permissions'; | |
a4b75251 | 29 | import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe'; |
33c7a0ef | 30 | import { EmptyPipe } from '~/app/shared/pipes/empty.pipe'; |
f67539c2 TL |
31 | import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; |
32 | import { ModalService } from '~/app/shared/services/modal.service'; | |
33 | import { NotificationService } from '~/app/shared/services/notification.service'; | |
34 | import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service'; | |
35 | import { URLBuilderService } from '~/app/shared/services/url-builder.service'; | |
a4b75251 | 36 | import { HostFormComponent } from './host-form/host-form.component'; |
9f95a23c TL |
37 | |
38 | const BASE_URL = 'hosts'; | |
11fdf7f2 TL |
39 | |
40 | @Component({ | |
41 | selector: 'cd-hosts', | |
42 | templateUrl: './hosts.component.html', | |
9f95a23c TL |
43 | styleUrls: ['./hosts.component.scss'], |
44 | providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }] | |
11fdf7f2 | 45 | }) |
a4b75251 TL |
46 | export class HostsComponent extends ListWithDetails implements OnDestroy, OnInit { |
47 | private sub = new Subscription(); | |
48 | ||
f67539c2 | 49 | @ViewChild(TableComponent) |
f6b5b4d7 TL |
50 | table: TableComponent; |
51 | @ViewChild('servicesTpl', { static: true }) | |
52 | public servicesTpl: TemplateRef<any>; | |
f67539c2 TL |
53 | @ViewChild('maintenanceConfirmTpl', { static: true }) |
54 | maintenanceConfirmTpl: TemplateRef<any>; | |
a4b75251 TL |
55 | @ViewChild('orchTmpl', { static: true }) |
56 | orchTmpl: TemplateRef<any>; | |
57 | @ViewChild('flashTmpl', { static: true }) | |
58 | flashTmpl: TemplateRef<any>; | |
1e59de90 TL |
59 | @ViewChild('hostNameTpl', { static: true }) |
60 | hostNameTpl: TemplateRef<any>; | |
a4b75251 TL |
61 | |
62 | @Input() | |
63 | hiddenColumns: string[] = []; | |
64 | ||
65 | @Input() | |
2a845540 | 66 | hideMaintenance = false; |
a4b75251 TL |
67 | |
68 | @Input() | |
69 | hasTableDetails = true; | |
70 | ||
71 | @Input() | |
72 | hideToolHeader = false; | |
73 | ||
74 | @Input() | |
75 | showGeneralActionsOnly = false; | |
f6b5b4d7 | 76 | |
11fdf7f2 TL |
77 | permissions: Permissions; |
78 | columns: Array<CdTableColumn> = []; | |
79 | hosts: Array<object> = []; | |
80 | isLoadingHosts = false; | |
81 | cdParams = { fromLink: '/hosts' }; | |
9f95a23c | 82 | tableActions: CdTableAction[]; |
11fdf7f2 | 83 | selection = new CdTableSelection(); |
f67539c2 TL |
84 | modalRef: NgbModalRef; |
85 | isExecuting = false; | |
86 | errorMessage: string; | |
20effc67 TL |
87 | enableMaintenanceBtn: boolean; |
88 | enableDrainBtn: boolean; | |
a4b75251 | 89 | bsModalRef: NgbModalRef; |
f67539c2 TL |
90 | |
91 | icons = Icons; | |
92 | ||
93 | messages = { | |
94 | nonOrchHost: $localize`The feature is disabled because the selected host is not managed by Orchestrator.` | |
95 | }; | |
96 | ||
97 | orchStatus: OrchestratorStatus; | |
98 | actionOrchFeatures = { | |
a4b75251 | 99 | add: [OrchestratorFeature.HOST_ADD], |
f67539c2 | 100 | edit: [OrchestratorFeature.HOST_LABEL_ADD, OrchestratorFeature.HOST_LABEL_REMOVE], |
a4b75251 | 101 | remove: [OrchestratorFeature.HOST_REMOVE], |
f67539c2 TL |
102 | maintenance: [ |
103 | OrchestratorFeature.HOST_MAINTENANCE_ENTER, | |
104 | OrchestratorFeature.HOST_MAINTENANCE_EXIT | |
20effc67 TL |
105 | ], |
106 | drain: [OrchestratorFeature.HOST_DRAIN] | |
f67539c2 | 107 | }; |
11fdf7f2 | 108 | |
11fdf7f2 TL |
109 | constructor( |
110 | private authStorageService: AuthStorageService, | |
a4b75251 | 111 | private dimlessBinary: DimlessBinaryPipe, |
33c7a0ef | 112 | private emptyPipe: EmptyPipe, |
11fdf7f2 | 113 | private hostService: HostService, |
9f95a23c | 114 | private actionLabels: ActionLabelsI18n, |
f67539c2 | 115 | private modalService: ModalService, |
9f95a23c TL |
116 | private taskWrapper: TaskWrapperService, |
117 | private router: Router, | |
f67539c2 TL |
118 | private notificationService: NotificationService, |
119 | private orchService: OrchestratorService | |
11fdf7f2 | 120 | ) { |
e306af50 | 121 | super(); |
11fdf7f2 | 122 | this.permissions = this.authStorageService.getPermissions(); |
9f95a23c TL |
123 | this.tableActions = [ |
124 | { | |
a4b75251 | 125 | name: this.actionLabels.ADD, |
9f95a23c TL |
126 | permission: 'create', |
127 | icon: Icons.add, | |
a4b75251 TL |
128 | click: () => |
129 | this.router.url.includes('/hosts') | |
130 | ? this.router.navigate([BASE_URL, { outlets: { modal: [URLVerbs.ADD] } }]) | |
2a845540 TL |
131 | : (this.bsModalRef = this.modalService.show(HostFormComponent, { |
132 | hideMaintenance: this.hideMaintenance | |
133 | })), | |
a4b75251 | 134 | disable: (selection: CdTableSelection) => this.getDisable('add', selection) |
9f95a23c | 135 | }, |
f6b5b4d7 TL |
136 | { |
137 | name: this.actionLabels.EDIT, | |
138 | permission: 'update', | |
139 | icon: Icons.edit, | |
f67539c2 TL |
140 | click: () => this.editAction(), |
141 | disable: (selection: CdTableSelection) => this.getDisable('edit', selection) | |
f6b5b4d7 | 142 | }, |
20effc67 TL |
143 | { |
144 | name: this.actionLabels.START_DRAIN, | |
145 | permission: 'update', | |
146 | icon: Icons.exit, | |
147 | click: () => this.hostDrain(), | |
148 | disable: (selection: CdTableSelection) => | |
149 | this.getDisable('drain', selection) || !this.enableDrainBtn, | |
150 | visible: () => !this.showGeneralActionsOnly && this.enableDrainBtn | |
151 | }, | |
152 | { | |
153 | name: this.actionLabels.STOP_DRAIN, | |
154 | permission: 'update', | |
155 | icon: Icons.exit, | |
156 | click: () => this.hostDrain(true), | |
157 | disable: (selection: CdTableSelection) => | |
158 | this.getDisable('drain', selection) || this.enableDrainBtn, | |
159 | visible: () => !this.showGeneralActionsOnly && !this.enableDrainBtn | |
160 | }, | |
9f95a23c | 161 | { |
a4b75251 | 162 | name: this.actionLabels.REMOVE, |
9f95a23c TL |
163 | permission: 'delete', |
164 | icon: Icons.destroy, | |
f67539c2 | 165 | click: () => this.deleteAction(), |
a4b75251 | 166 | disable: (selection: CdTableSelection) => this.getDisable('remove', selection) |
f67539c2 TL |
167 | }, |
168 | { | |
169 | name: this.actionLabels.ENTER_MAINTENANCE, | |
170 | permission: 'update', | |
171 | icon: Icons.enter, | |
172 | click: () => this.hostMaintenance(), | |
173 | disable: (selection: CdTableSelection) => | |
20effc67 TL |
174 | this.getDisable('maintenance', selection) || |
175 | this.isExecuting || | |
176 | this.enableMaintenanceBtn, | |
177 | visible: () => !this.showGeneralActionsOnly && !this.enableMaintenanceBtn | |
f67539c2 TL |
178 | }, |
179 | { | |
180 | name: this.actionLabels.EXIT_MAINTENANCE, | |
181 | permission: 'update', | |
182 | icon: Icons.exit, | |
183 | click: () => this.hostMaintenance(), | |
184 | disable: (selection: CdTableSelection) => | |
20effc67 TL |
185 | this.getDisable('maintenance', selection) || |
186 | this.isExecuting || | |
187 | !this.enableMaintenanceBtn, | |
188 | visible: () => !this.showGeneralActionsOnly && this.enableMaintenanceBtn | |
9f95a23c TL |
189 | } |
190 | ]; | |
11fdf7f2 TL |
191 | } |
192 | ||
193 | ngOnInit() { | |
194 | this.columns = [ | |
195 | { | |
f67539c2 | 196 | name: $localize`Hostname`, |
11fdf7f2 | 197 | prop: 'hostname', |
1e59de90 TL |
198 | flexGrow: 1, |
199 | cellTemplate: this.hostNameTpl | |
11fdf7f2 TL |
200 | }, |
201 | { | |
33c7a0ef TL |
202 | name: $localize`Service Instances`, |
203 | prop: 'service_instances', | |
39ae355f | 204 | flexGrow: 1.5, |
11fdf7f2 TL |
205 | cellTemplate: this.servicesTpl |
206 | }, | |
e306af50 | 207 | { |
f67539c2 | 208 | name: $localize`Labels`, |
e306af50 TL |
209 | prop: 'labels', |
210 | flexGrow: 1, | |
f67539c2 TL |
211 | cellTransformation: CellTemplate.badge, |
212 | customTemplateConfig: { | |
213 | class: 'badge-dark' | |
214 | } | |
215 | }, | |
216 | { | |
217 | name: $localize`Status`, | |
218 | prop: 'status', | |
39ae355f | 219 | flexGrow: 0.8, |
f67539c2 TL |
220 | cellTransformation: CellTemplate.badge, |
221 | customTemplateConfig: { | |
222 | map: { | |
1e59de90 TL |
223 | maintenance: { class: 'badge-warning' }, |
224 | available: { class: 'badge-success' } | |
f67539c2 TL |
225 | } |
226 | } | |
e306af50 | 227 | }, |
11fdf7f2 | 228 | { |
a4b75251 TL |
229 | name: $localize`Model`, |
230 | prop: 'model', | |
231 | flexGrow: 1 | |
232 | }, | |
233 | { | |
234 | name: $localize`CPUs`, | |
235 | prop: 'cpu_count', | |
236 | flexGrow: 0.3 | |
237 | }, | |
238 | { | |
239 | name: $localize`Cores`, | |
240 | prop: 'cpu_cores', | |
241 | flexGrow: 0.3 | |
242 | }, | |
243 | { | |
244 | name: $localize`Total Memory`, | |
245 | prop: 'memory_total_bytes', | |
246 | pipe: this.dimlessBinary, | |
247 | flexGrow: 0.4 | |
248 | }, | |
249 | { | |
250 | name: $localize`Raw Capacity`, | |
251 | prop: 'raw_capacity', | |
252 | pipe: this.dimlessBinary, | |
253 | flexGrow: 0.5 | |
254 | }, | |
255 | { | |
256 | name: $localize`HDDs`, | |
257 | prop: 'hdd_count', | |
258 | flexGrow: 0.3 | |
259 | }, | |
260 | { | |
261 | name: $localize`Flash`, | |
262 | prop: 'flash_count', | |
263 | headerTemplate: this.flashTmpl, | |
264 | flexGrow: 0.3 | |
265 | }, | |
266 | { | |
267 | name: $localize`NICs`, | |
268 | prop: 'nic_count', | |
269 | flexGrow: 0.3 | |
11fdf7f2 TL |
270 | } |
271 | ]; | |
a4b75251 TL |
272 | |
273 | this.columns = this.columns.filter((col: any) => { | |
274 | return !this.hiddenColumns.includes(col.prop); | |
f67539c2 | 275 | }); |
11fdf7f2 TL |
276 | } |
277 | ||
a4b75251 TL |
278 | ngOnDestroy() { |
279 | this.sub.unsubscribe(); | |
280 | } | |
281 | ||
11fdf7f2 TL |
282 | updateSelection(selection: CdTableSelection) { |
283 | this.selection = selection; | |
20effc67 TL |
284 | this.enableMaintenanceBtn = false; |
285 | this.enableDrainBtn = false; | |
f67539c2 TL |
286 | if (this.selection.hasSelection) { |
287 | if (this.selection.first().status === 'maintenance') { | |
20effc67 TL |
288 | this.enableMaintenanceBtn = true; |
289 | } | |
290 | ||
291 | if (!this.selection.first().labels.includes('_no_schedule')) { | |
292 | this.enableDrainBtn = true; | |
f67539c2 TL |
293 | } |
294 | } | |
11fdf7f2 TL |
295 | } |
296 | ||
f6b5b4d7 TL |
297 | editAction() { |
298 | this.hostService.getLabels().subscribe((resp: string[]) => { | |
299 | const host = this.selection.first(); | |
a4b75251 TL |
300 | const labels = new Set(resp.concat(this.hostService.predefinedLabels)); |
301 | const allLabels = Array.from(labels).map((label) => { | |
f6b5b4d7 TL |
302 | return { enabled: true, name: label }; |
303 | }); | |
304 | this.modalService.show(FormModalComponent, { | |
f67539c2 TL |
305 | titleText: $localize`Edit Host: ${host.hostname}`, |
306 | fields: [ | |
307 | { | |
308 | type: 'select-badges', | |
309 | name: 'labels', | |
310 | value: host['labels'], | |
311 | label: $localize`Labels`, | |
312 | typeConfig: { | |
313 | customBadges: true, | |
314 | options: allLabels, | |
315 | messages: new SelectMessages({ | |
316 | empty: $localize`There are no labels.`, | |
317 | filter: $localize`Filter or add labels`, | |
318 | add: $localize`Add label` | |
319 | }) | |
f6b5b4d7 | 320 | } |
f6b5b4d7 | 321 | } |
f67539c2 TL |
322 | ], |
323 | submitButtonText: $localize`Edit Host`, | |
324 | onSubmit: (values: any) => { | |
325 | this.hostService.update(host['hostname'], true, values.labels).subscribe(() => { | |
326 | this.notificationService.show( | |
327 | NotificationType.success, | |
328 | $localize`Updated Host "${host.hostname}"` | |
329 | ); | |
330 | // Reload the data table content. | |
331 | this.table.refreshBtn(); | |
332 | }); | |
f6b5b4d7 TL |
333 | } |
334 | }); | |
335 | }); | |
336 | } | |
337 | ||
f67539c2 TL |
338 | hostMaintenance() { |
339 | this.isExecuting = true; | |
340 | const host = this.selection.first(); | |
341 | if (host['status'] !== 'maintenance') { | |
342 | this.hostService.update(host['hostname'], false, [], true).subscribe( | |
343 | () => { | |
344 | this.isExecuting = false; | |
345 | this.notificationService.show( | |
346 | NotificationType.success, | |
347 | $localize`"${host.hostname}" moved to maintenance` | |
348 | ); | |
349 | this.table.refreshBtn(); | |
350 | }, | |
351 | (error) => { | |
352 | this.isExecuting = false; | |
353 | this.errorMessage = error.error['detail'].split(/\n/); | |
354 | error.preventDefault(); | |
355 | if ( | |
356 | error.error['detail'].includes('WARNING') && | |
357 | !error.error['detail'].includes('It is NOT safe to stop') && | |
b3b6e05e | 358 | !error.error['detail'].includes('ALERT') && |
522d829b | 359 | !error.error['detail'].includes('unsafe to stop') |
f67539c2 | 360 | ) { |
522d829b | 361 | const modalVariables = { |
f67539c2 TL |
362 | titleText: $localize`Warning`, |
363 | buttonText: $localize`Continue`, | |
364 | warning: true, | |
365 | bodyTpl: this.maintenanceConfirmTpl, | |
366 | showSubmit: true, | |
367 | onSubmit: () => { | |
368 | this.hostService.update(host['hostname'], false, [], true, true).subscribe( | |
369 | () => { | |
370 | this.modalRef.close(); | |
371 | }, | |
372 | () => this.modalRef.close() | |
373 | ); | |
374 | } | |
375 | }; | |
522d829b | 376 | this.modalRef = this.modalService.show(ConfirmationModalComponent, modalVariables); |
f67539c2 TL |
377 | } else { |
378 | this.notificationService.show( | |
379 | NotificationType.error, | |
380 | $localize`"${host.hostname}" cannot be put into maintenance`, | |
381 | $localize`${error.error['detail']}` | |
382 | ); | |
383 | } | |
384 | } | |
385 | ); | |
386 | } else { | |
387 | this.hostService.update(host['hostname'], false, [], true).subscribe(() => { | |
388 | this.isExecuting = false; | |
389 | this.notificationService.show( | |
390 | NotificationType.success, | |
391 | $localize`"${host.hostname}" has exited maintenance` | |
f91f0fd5 | 392 | ); |
f67539c2 TL |
393 | this.table.refreshBtn(); |
394 | }); | |
f6b5b4d7 | 395 | } |
f67539c2 | 396 | } |
f91f0fd5 | 397 | |
20effc67 TL |
398 | hostDrain(stop = false) { |
399 | const host = this.selection.first(); | |
400 | if (stop) { | |
401 | const index = host['labels'].indexOf('_no_schedule', 0); | |
402 | host['labels'].splice(index, 1); | |
403 | this.hostService.update(host['hostname'], true, host['labels']).subscribe(() => { | |
404 | this.notificationService.show( | |
405 | NotificationType.info, | |
406 | $localize`"${host['hostname']}" stopped draining` | |
407 | ); | |
408 | this.table.refreshBtn(); | |
409 | }); | |
410 | } else { | |
411 | this.hostService.update(host['hostname'], false, [], false, false, true).subscribe(() => { | |
412 | this.notificationService.show( | |
413 | NotificationType.info, | |
414 | $localize`"${host['hostname']}" started draining` | |
415 | ); | |
416 | this.table.refreshBtn(); | |
417 | }); | |
418 | } | |
419 | } | |
420 | ||
f67539c2 | 421 | getDisable( |
20effc67 | 422 | action: 'add' | 'edit' | 'remove' | 'maintenance' | 'drain', |
f67539c2 TL |
423 | selection: CdTableSelection |
424 | ): boolean | string { | |
20effc67 TL |
425 | if ( |
426 | action === 'remove' || | |
427 | action === 'edit' || | |
428 | action === 'maintenance' || | |
429 | action === 'drain' | |
430 | ) { | |
f67539c2 TL |
431 | if (!selection?.hasSingleSelection) { |
432 | return true; | |
433 | } | |
434 | if (!_.every(selection.selected, 'sources.orchestrator')) { | |
435 | return this.messages.nonOrchHost; | |
436 | } | |
437 | } | |
438 | return this.orchService.getTableActionDisableDesc( | |
439 | this.orchStatus, | |
440 | this.actionOrchFeatures[action] | |
441 | ); | |
f6b5b4d7 TL |
442 | } |
443 | ||
444 | deleteAction() { | |
9f95a23c TL |
445 | const hostname = this.selection.first().hostname; |
446 | this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, { | |
f67539c2 TL |
447 | itemDescription: 'Host', |
448 | itemNames: [hostname], | |
a4b75251 | 449 | actionDescription: 'remove', |
f67539c2 TL |
450 | submitActionObservable: () => |
451 | this.taskWrapper.wrapTaskAroundCall({ | |
a4b75251 | 452 | task: new FinishedTask('host/remove', { hostname: hostname }), |
f67539c2 TL |
453 | call: this.hostService.delete(hostname) |
454 | }) | |
9f95a23c TL |
455 | }); |
456 | } | |
457 | ||
a4b75251 TL |
458 | checkHostsFactsAvailable() { |
459 | const orchFeatures = this.orchStatus.features; | |
460 | if (!_.isEmpty(orchFeatures)) { | |
461 | if (orchFeatures.get_facts.available) { | |
462 | return true; | |
463 | } | |
464 | return false; | |
465 | } | |
466 | return false; | |
467 | } | |
468 | ||
469 | transformHostsData() { | |
470 | if (this.checkHostsFactsAvailable()) { | |
471 | _.forEach(this.hosts, (hostKey) => { | |
33c7a0ef TL |
472 | hostKey['memory_total_bytes'] = this.emptyPipe.transform(hostKey['memory_total_kb'] * 1024); |
473 | hostKey['raw_capacity'] = this.emptyPipe.transform( | |
474 | hostKey['hdd_capacity_bytes'] + hostKey['flash_capacity_bytes'] | |
475 | ); | |
a4b75251 TL |
476 | }); |
477 | } else { | |
478 | // mark host facts columns unavailable | |
479 | for (let column = 4; column < this.columns.length; column++) { | |
a4b75251 TL |
480 | this.columns[column]['cellTemplate'] = this.orchTmpl; |
481 | } | |
482 | } | |
483 | } | |
484 | ||
11fdf7f2 TL |
485 | getHosts(context: CdTableFetchDataContext) { |
486 | if (this.isLoadingHosts) { | |
487 | return; | |
488 | } | |
11fdf7f2 | 489 | this.isLoadingHosts = true; |
a4b75251 TL |
490 | this.sub = this.orchService |
491 | .status() | |
492 | .pipe( | |
493 | mergeMap((orchStatus) => { | |
494 | this.orchStatus = orchStatus; | |
495 | const factsAvailable = this.checkHostsFactsAvailable(); | |
496 | return this.hostService.list(`${factsAvailable}`); | |
39ae355f | 497 | }) |
a4b75251 TL |
498 | ) |
499 | .subscribe( | |
500 | (hostList) => { | |
501 | this.hosts = hostList; | |
1e59de90 TL |
502 | this.hosts.forEach((host: object) => { |
503 | if (host['status'] === '') { | |
504 | host['status'] = 'available'; | |
505 | } | |
506 | }); | |
a4b75251 TL |
507 | this.transformHostsData(); |
508 | this.isLoadingHosts = false; | |
509 | }, | |
510 | () => { | |
511 | this.isLoadingHosts = false; | |
512 | context.error(); | |
513 | } | |
514 | ); | |
11fdf7f2 TL |
515 | } |
516 | } |