1 import { Component, OnInit } from '@angular/core';
2 import { UntypedFormControl, Validators } from '@angular/forms';
3 import { Router } from '@angular/router';
5 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
6 import expand from 'brace-expansion';
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 { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
16 import { FinishedTask } from '~/app/shared/models/finished-task';
17 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
20 selector: 'cd-host-form',
21 templateUrl: './host-form.component.html',
22 styleUrls: ['./host-form.component.scss']
24 export class HostFormComponent extends CdForm implements OnInit {
25 hostForm: CdFormGroup;
29 hostnameArray: string[] = [];
35 labelsOption: Array<SelectOption> = [];
36 hideMaintenance: boolean;
38 messages = new SelectMessages({
39 empty: $localize`There are no labels.`,
40 filter: $localize`Filter or add labels`,
41 add: $localize`Add label`
45 private router: Router,
46 private actionLabels: ActionLabelsI18n,
47 private hostService: HostService,
48 private taskWrapper: TaskWrapperService,
49 public activeModal: NgbActiveModal
52 this.resource = $localize`host`;
53 this.action = this.actionLabels.ADD;
57 if (this.router.url.includes('hosts')) {
58 this.pageURL = 'hosts';
61 const hostContext = new CdTableFetchDataContext(() => undefined);
62 this.hostService.list(hostContext.toParams(), 'false').subscribe((resp: any[]) => {
63 this.hostnames = resp.map((host) => {
64 return host['hostname'];
69 this.hostService.getLabels().subscribe((resp: string[]) => {
70 const uniqueLabels = new Set(resp.concat(this.hostService.predefinedLabels));
71 this.labelsOption = Array.from(uniqueLabels).map((label) => {
72 return { enabled: true, name: label, selected: false, description: null };
77 // check if hostname is a single value or pattern to hide network address field
78 checkHostNameValue() {
79 const hostNames = this.hostForm.get('hostname').value;
80 hostNames.match(/[()\[\]{},]/g) ? (this.hostPattern = true) : (this.hostPattern = false);
83 private createForm() {
84 this.hostForm = new CdFormGroup({
85 hostname: new UntypedFormControl('', {
88 CdValidators.custom('uniqueName', (hostname: string) => {
89 return this.hostnames && this.hostnames.indexOf(hostname) !== -1;
93 addr: new UntypedFormControl('', {
94 validators: [CdValidators.ip()]
96 labels: new UntypedFormControl([]),
97 maintenance: new UntypedFormControl(false)
101 private isCommaSeparatedPattern(hostname: string) {
102 // eg. ceph-node-01.cephlab.com,ceph-node-02.cephlab.com
103 return hostname.includes(',');
106 private isRangeTypePattern(hostname: string) {
107 // check if it is a range expression or comma separated range expression
108 // eg. ceph-mon-[01-05].lab.com,ceph-osd-[02-08].lab.com,ceph-rgw-[01-09]
109 return hostname.includes('[') && hostname.includes(']') && !hostname.match(/(?![^(]*\)),/g);
112 private replaceBraces(hostname: string) {
113 // pattern to replace range [0-5] to [0..5](valid expression for brace expansion)
114 // replace any kind of brackets with curly braces
116 .replace(/(\d)\s*-\s*(\d)/g, '$1..$2')
123 // expand hostnames in case hostname is a pattern
124 private checkHostNamePattern(hostname: string) {
125 if (this.isRangeTypePattern(hostname)) {
126 const hostnameRange = this.replaceBraces(hostname);
127 this.hostnameArray = expand(hostnameRange);
128 } else if (this.isCommaSeparatedPattern(hostname)) {
130 hostArray = hostname.split(',');
131 hostArray.forEach((host: string) => {
132 if (this.isRangeTypePattern(host)) {
133 const hostnameRange = this.replaceBraces(host);
134 this.hostnameArray = this.hostnameArray.concat(expand(hostnameRange));
136 this.hostnameArray.push(host);
141 this.hostnameArray.push(hostname);
146 const hostname = this.hostForm.get('hostname').value;
147 this.checkHostNamePattern(hostname);
148 this.addr = this.hostForm.get('addr').value;
149 this.status = this.hostForm.get('maintenance').value ? 'maintenance' : '';
150 this.allLabels = this.hostForm.get('labels').value;
151 if (this.pageURL !== 'hosts' && !this.allLabels.includes('_no_schedule')) {
152 this.allLabels.push('_no_schedule');
154 this.hostnameArray.forEach((hostName: string) => {
156 .wrapTaskAroundCall({
157 task: new FinishedTask('host/' + URLVerbs.ADD, {
160 call: this.hostService.create(hostName, this.addr, this.allLabels, this.status)
164 this.hostForm.setErrors({ cdSubmitButton: true });
167 this.pageURL === 'hosts'
168 ? this.router.navigate([this.pageURL, { outlets: { modal: null } }])
169 : this.activeModal.close();