]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts
import ceph 16.2.7
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / rgw / rgw-bucket-form / rgw-bucket-form.component.ts
CommitLineData
11fdf7f2 1import { Component, OnInit } from '@angular/core';
a4b75251 2import { Validators } from '@angular/forms';
11fdf7f2
TL
3import { ActivatedRoute, Router } from '@angular/router';
4
f67539c2 5import _ from 'lodash';
a4b75251 6import { forkJoin } from 'rxjs';
11fdf7f2 7
f67539c2
TL
8import { RgwBucketService } from '~/app/shared/api/rgw-bucket.service';
9import { RgwSiteService } from '~/app/shared/api/rgw-site.service';
10import { RgwUserService } from '~/app/shared/api/rgw-user.service';
11import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
12import { Icons } from '~/app/shared/enum/icons.enum';
13import { NotificationType } from '~/app/shared/enum/notification-type.enum';
14import { CdForm } from '~/app/shared/forms/cd-form';
15import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder';
16import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
17import { CdValidators } from '~/app/shared/forms/cd-validators';
18import { NotificationService } from '~/app/shared/services/notification.service';
9f95a23c
TL
19import { RgwBucketMfaDelete } from '../models/rgw-bucket-mfa-delete';
20import { RgwBucketVersioning } from '../models/rgw-bucket-versioning';
11fdf7f2
TL
21
22@Component({
23 selector: 'cd-rgw-bucket-form',
24 templateUrl: './rgw-bucket-form.component.html',
25 styleUrls: ['./rgw-bucket-form.component.scss']
26})
f67539c2 27export class RgwBucketFormComponent extends CdForm implements OnInit {
11fdf7f2
TL
28 bucketForm: CdFormGroup;
29 editing = false;
9f95a23c 30 owners: string[] = null;
11fdf7f2
TL
31 action: string;
32 resource: string;
9f95a23c
TL
33 zonegroup: string;
34 placementTargets: object[] = [];
9f95a23c 35 isVersioningAlreadyEnabled = false;
9f95a23c
TL
36 isMfaDeleteAlreadyEnabled = false;
37 icons = Icons;
11fdf7f2 38
f67539c2
TL
39 get isVersioningEnabled(): boolean {
40 return this.bucketForm.getValue('versioning');
41 }
42 get isMfaDeleteEnabled(): boolean {
43 return this.bucketForm.getValue('mfa-delete');
44 }
45
11fdf7f2
TL
46 constructor(
47 private route: ActivatedRoute,
48 private router: Router,
49 private formBuilder: CdFormBuilder,
50 private rgwBucketService: RgwBucketService,
9f95a23c 51 private rgwSiteService: RgwSiteService,
11fdf7f2
TL
52 private rgwUserService: RgwUserService,
53 private notificationService: NotificationService,
11fdf7f2
TL
54 public actionLabels: ActionLabelsI18n
55 ) {
f67539c2 56 super();
11fdf7f2
TL
57 this.editing = this.router.url.startsWith(`/rgw/bucket/${URLVerbs.EDIT}`);
58 this.action = this.editing ? this.actionLabels.EDIT : this.actionLabels.CREATE;
f67539c2 59 this.resource = $localize`bucket`;
11fdf7f2
TL
60 this.createForm();
61 }
62
63 createForm() {
9f95a23c 64 const self = this;
b3b6e05e 65 const lockDaysValidator = CdValidators.custom('lockDays', () => {
9f95a23c
TL
66 if (!self.bucketForm || !_.get(self.bucketForm.getRawValue(), 'lock_enabled')) {
67 return false;
68 }
b3b6e05e
TL
69 const lockDays = Number(self.bucketForm.getValue('lock_retention_period_days'));
70 return !Number.isInteger(lockDays) || lockDays === 0;
9f95a23c 71 });
11fdf7f2
TL
72 this.bucketForm = this.formBuilder.group({
73 id: [null],
a4b75251
TL
74 bid: [
75 null,
76 [Validators.required],
77 this.editing
78 ? []
79 : [CdValidators.bucketName(), CdValidators.bucketExistence(false, this.rgwBucketService)]
80 ],
9f95a23c
TL
81 owner: [null, [Validators.required]],
82 'placement-target': [null, this.editing ? [] : [Validators.required]],
83 versioning: [null],
84 'mfa-delete': [null],
85 'mfa-token-serial': [''],
86 'mfa-token-pin': [''],
87 lock_enabled: [{ value: false, disabled: this.editing }],
88 lock_mode: ['COMPLIANCE'],
b3b6e05e 89 lock_retention_period_days: [0, [CdValidators.number(false), lockDaysValidator]]
11fdf7f2
TL
90 });
91 }
92
93 ngOnInit() {
f67539c2
TL
94 const promises = {
95 owners: this.rgwUserService.enumerate()
96 };
11fdf7f2 97
9f95a23c 98 if (!this.editing) {
f67539c2 99 promises['getPlacementTargets'] = this.rgwSiteService.get('placement-targets');
9f95a23c
TL
100 }
101
102 // Process route parameters.
103 this.route.params.subscribe((params: { bid: string }) => {
f67539c2
TL
104 if (params.hasOwnProperty('bid')) {
105 const bid = decodeURIComponent(params.bid);
106 promises['getBid'] = this.rgwBucketService.get(bid);
11fdf7f2 107 }
9f95a23c 108
f67539c2
TL
109 forkJoin(promises).subscribe((data: any) => {
110 // Get the list of possible owners.
111 this.owners = (<string[]>data.owners).sort();
112
113 // Get placement targets:
114 if (data['getPlacementTargets']) {
115 const placementTargets = data['getPlacementTargets'];
116 this.zonegroup = placementTargets['zonegroup'];
117 _.forEach(placementTargets['placement_targets'], (placementTarget) => {
118 placementTarget['description'] = `${placementTarget['name']} (${$localize`pool`}: ${
119 placementTarget['data_pool']
120 })`;
121 this.placementTargets.push(placementTarget);
122 });
123
124 // If there is only 1 placement target, select it by default:
125 if (this.placementTargets.length === 1) {
126 this.bucketForm.get('placement-target').setValue(this.placementTargets[0]['name']);
127 }
128 }
129
130 if (data['getBid']) {
131 const bidResp = data['getBid'];
132 // Get the default values (incl. the values from disabled fields).
133 const defaults = _.clone(this.bucketForm.getRawValue());
134
135 // Get the values displayed in the form. We need to do that to
136 // extract those key/value pairs from the response data, otherwise
137 // the Angular react framework will throw an error if there is no
138 // field for a given key.
139 let value: object = _.pick(bidResp, _.keys(defaults));
b3b6e05e 140 value['lock_retention_period_days'] = this.rgwBucketService.getLockDays(bidResp);
f67539c2
TL
141 value['placement-target'] = bidResp['placement_rule'];
142 value['versioning'] = bidResp['versioning'] === RgwBucketVersioning.ENABLED;
143 value['mfa-delete'] = bidResp['mfa_delete'] === RgwBucketMfaDelete.ENABLED;
144
145 // Append default values.
146 value = _.merge(defaults, value);
147
148 // Update the form.
149 this.bucketForm.setValue(value);
150 if (this.editing) {
151 this.isVersioningAlreadyEnabled = this.isVersioningEnabled;
152 this.isMfaDeleteAlreadyEnabled = this.isMfaDeleteEnabled;
153 this.setMfaDeleteValidators();
b3b6e05e
TL
154 if (value['lock_enabled']) {
155 this.bucketForm.controls['versioning'].disable();
156 }
f67539c2 157 }
9f95a23c 158 }
f67539c2
TL
159
160 this.loadingReady();
9f95a23c
TL
161 });
162 });
11fdf7f2
TL
163 }
164
165 goToListView() {
166 this.router.navigate(['/rgw/bucket']);
167 }
168
169 submit() {
170 // Exit immediately if the form isn't dirty.
171 if (this.bucketForm.pristine) {
172 this.goToListView();
173 return;
174 }
9f95a23c 175 const values = this.bucketForm.value;
11fdf7f2
TL
176 if (this.editing) {
177 // Edit
9f95a23c
TL
178 const versioning = this.getVersioningStatus();
179 const mfaDelete = this.getMfaDeleteStatus();
180 this.rgwBucketService
181 .update(
182 values['bid'],
183 values['id'],
184 values['owner'],
185 versioning,
186 mfaDelete,
187 values['mfa-token-serial'],
188 values['mfa-token-pin'],
189 values['lock_mode'],
b3b6e05e 190 values['lock_retention_period_days']
9f95a23c
TL
191 )
192 .subscribe(
193 () => {
194 this.notificationService.show(
195 NotificationType.success,
f67539c2 196 $localize`Updated Object Gateway bucket '${values.bid}'.`
9f95a23c
TL
197 );
198 this.goToListView();
199 },
200 () => {
201 // Reset the 'Submit' button.
202 this.bucketForm.setErrors({ cdSubmitButton: true });
203 }
204 );
11fdf7f2
TL
205 } else {
206 // Add
9f95a23c
TL
207 this.rgwBucketService
208 .create(
209 values['bid'],
210 values['owner'],
211 this.zonegroup,
212 values['placement-target'],
213 values['lock_enabled'],
214 values['lock_mode'],
b3b6e05e 215 values['lock_retention_period_days']
9f95a23c
TL
216 )
217 .subscribe(
218 () => {
219 this.notificationService.show(
220 NotificationType.success,
f67539c2 221 $localize`Created Object Gateway bucket '${values.bid}'`
9f95a23c
TL
222 );
223 this.goToListView();
224 },
225 () => {
226 // Reset the 'Submit' button.
227 this.bucketForm.setErrors({ cdSubmitButton: true });
228 }
229 );
11fdf7f2
TL
230 }
231 }
232
9f95a23c
TL
233 areMfaCredentialsRequired() {
234 return (
235 this.isMfaDeleteEnabled !== this.isMfaDeleteAlreadyEnabled ||
236 (this.isMfaDeleteAlreadyEnabled &&
237 this.isVersioningEnabled !== this.isVersioningAlreadyEnabled)
238 );
239 }
240
241 setMfaDeleteValidators() {
242 const mfaTokenSerialControl = this.bucketForm.get('mfa-token-serial');
243 const mfaTokenPinControl = this.bucketForm.get('mfa-token-pin');
244
245 if (this.areMfaCredentialsRequired()) {
246 mfaTokenSerialControl.setValidators(Validators.required);
247 mfaTokenPinControl.setValidators(Validators.required);
248 } else {
249 mfaTokenSerialControl.setValidators(null);
250 mfaTokenPinControl.setValidators(null);
251 }
252
253 mfaTokenSerialControl.updateValueAndValidity();
254 mfaTokenPinControl.updateValueAndValidity();
255 }
256
257 getVersioningStatus() {
258 return this.isVersioningEnabled ? RgwBucketVersioning.ENABLED : RgwBucketVersioning.SUSPENDED;
259 }
260
9f95a23c
TL
261 getMfaDeleteStatus() {
262 return this.isMfaDeleteEnabled ? RgwBucketMfaDelete.ENABLED : RgwBucketMfaDelete.DISABLED;
263 }
11fdf7f2 264}