]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-form/host-form.component.ts
import ceph quincy 17.2.4
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / cluster / hosts / host-form / host-form.component.ts
1 import { Component, OnInit } from '@angular/core';
2 import { FormControl, Validators } from '@angular/forms';
3 import { Router } from '@angular/router';
4
5 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
6 import expand from 'brace-expansion';
7
8 import { HostService } from '~/app/shared/api/host.service';
9 import { SelectMessages } from '~/app/shared/components/select/select-messages.model';
10 import { SelectOption } from '~/app/shared/components/select/select-option.model';
11 import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
12 import { CdForm } from '~/app/shared/forms/cd-form';
13 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
14 import { CdValidators } from '~/app/shared/forms/cd-validators';
15 import { FinishedTask } from '~/app/shared/models/finished-task';
16 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
17
18 @Component({
19 selector: 'cd-host-form',
20 templateUrl: './host-form.component.html',
21 styleUrls: ['./host-form.component.scss']
22 })
23 export class HostFormComponent extends CdForm implements OnInit {
24 hostForm: CdFormGroup;
25 action: string;
26 resource: string;
27 hostnames: string[];
28 hostnameArray: string[] = [];
29 addr: string;
30 status: string;
31 allLabels: string[];
32 pageURL: string;
33 hostPattern = false;
34 labelsOption: Array<SelectOption> = [];
35 hideMaintenance: boolean;
36
37 messages = new SelectMessages({
38 empty: $localize`There are no labels.`,
39 filter: $localize`Filter or add labels`,
40 add: $localize`Add label`
41 });
42
43 constructor(
44 private router: Router,
45 private actionLabels: ActionLabelsI18n,
46 private hostService: HostService,
47 private taskWrapper: TaskWrapperService,
48 public activeModal: NgbActiveModal
49 ) {
50 super();
51 this.resource = $localize`host`;
52 this.action = this.actionLabels.ADD;
53 }
54
55 ngOnInit() {
56 if (this.router.url.includes('hosts')) {
57 this.pageURL = 'hosts';
58 }
59 this.createForm();
60 this.hostService.list('false').subscribe((resp: any[]) => {
61 this.hostnames = resp.map((host) => {
62 return host['hostname'];
63 });
64 this.loadingReady();
65 });
66
67 this.hostService.getLabels().subscribe((resp: string[]) => {
68 const uniqueLabels = new Set(resp.concat(this.hostService.predefinedLabels));
69 this.labelsOption = Array.from(uniqueLabels).map((label) => {
70 return { enabled: true, name: label, selected: false, description: null };
71 });
72 });
73 }
74
75 // check if hostname is a single value or pattern to hide network address field
76 checkHostNameValue() {
77 const hostNames = this.hostForm.get('hostname').value;
78 hostNames.match(/[()\[\]{},]/g) ? (this.hostPattern = true) : (this.hostPattern = false);
79 }
80
81 private createForm() {
82 this.hostForm = new CdFormGroup({
83 hostname: new FormControl('', {
84 validators: [
85 Validators.required,
86 CdValidators.custom('uniqueName', (hostname: string) => {
87 return this.hostnames && this.hostnames.indexOf(hostname) !== -1;
88 })
89 ]
90 }),
91 addr: new FormControl('', {
92 validators: [CdValidators.ip()]
93 }),
94 labels: new FormControl([]),
95 maintenance: new FormControl(false)
96 });
97 }
98
99 private isCommaSeparatedPattern(hostname: string) {
100 // eg. ceph-node-01.cephlab.com,ceph-node-02.cephlab.com
101 return hostname.includes(',');
102 }
103
104 private isRangeTypePattern(hostname: string) {
105 // check if it is a range expression or comma separated range expression
106 // eg. ceph-mon-[01-05].lab.com,ceph-osd-[02-08].lab.com,ceph-rgw-[01-09]
107 return hostname.includes('[') && hostname.includes(']') && !hostname.match(/(?![^(]*\)),/g);
108 }
109
110 private replaceBraces(hostname: string) {
111 // pattern to replace range [0-5] to [0..5](valid expression for brace expansion)
112 // replace any kind of brackets with curly braces
113 return hostname
114 .replace(/(\d)\s*-\s*(\d)/g, '$1..$2')
115 .replace(/\(/g, '{')
116 .replace(/\)/g, '}')
117 .replace(/\[/g, '{')
118 .replace(/]/g, '}');
119 }
120
121 // expand hostnames in case hostname is a pattern
122 private checkHostNamePattern(hostname: string) {
123 if (this.isRangeTypePattern(hostname)) {
124 const hostnameRange = this.replaceBraces(hostname);
125 this.hostnameArray = expand(hostnameRange);
126 } else if (this.isCommaSeparatedPattern(hostname)) {
127 let hostArray = [];
128 hostArray = hostname.split(',');
129 hostArray.forEach((host: string) => {
130 if (this.isRangeTypePattern(host)) {
131 const hostnameRange = this.replaceBraces(host);
132 this.hostnameArray = this.hostnameArray.concat(expand(hostnameRange));
133 } else {
134 this.hostnameArray.push(host);
135 }
136 });
137 } else {
138 // single hostname
139 this.hostnameArray.push(hostname);
140 }
141 }
142
143 submit() {
144 const hostname = this.hostForm.get('hostname').value;
145 this.checkHostNamePattern(hostname);
146 this.addr = this.hostForm.get('addr').value;
147 this.status = this.hostForm.get('maintenance').value ? 'maintenance' : '';
148 this.allLabels = this.hostForm.get('labels').value;
149 if (this.pageURL !== 'hosts' && !this.allLabels.includes('_no_schedule')) {
150 this.allLabels.push('_no_schedule');
151 }
152 this.hostnameArray.forEach((hostName: string) => {
153 this.taskWrapper
154 .wrapTaskAroundCall({
155 task: new FinishedTask('host/' + URLVerbs.ADD, {
156 hostname: hostName
157 }),
158 call: this.hostService.create(hostName, this.addr, this.allLabels, this.status)
159 })
160 .subscribe({
161 error: () => {
162 this.hostForm.setErrors({ cdSubmitButton: true });
163 },
164 complete: () => {
165 this.pageURL === 'hosts'
166 ? this.router.navigate([this.pageURL, { outlets: { modal: null } }])
167 : this.activeModal.close();
168 }
169 });
170 });
171 }
172 }