]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html
Import ceph 15.2.8
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / pool / pool-form / pool-form.component.html
1 <cd-loading-panel *ngIf="!(info && ecProfiles)"
2 i18n>Loading...</cd-loading-panel>
3
4 <div class="cd-col-form">
5 <form name="form"
6 *ngIf="info && ecProfiles"
7 #formDir="ngForm"
8 [formGroup]="form"
9 novalidate>
10 <div class="card">
11 <div i18n="form title|Example: Create Pool@@formTitle"
12 class="card-header">{{ action | titlecase }} {{ resource | upperFirst }}</div>
13
14 <div class="card-body">
15 <!-- Name -->
16 <div class="form-group row">
17 <label class="cd-col-form-label required"
18 for="name"
19 i18n>Name</label>
20 <div class="cd-col-form-input">
21 <input id="name"
22 name="name"
23 type="text"
24 class="form-control"
25 placeholder="Name..."
26 i18n-placeholder
27 formControlName="name"
28 autofocus>
29 <span class="invalid-feedback"
30 *ngIf="form.showError('name', formDir, 'required')"
31 i18n>This field is required!</span>
32 <span class="invalid-feedback"
33 *ngIf="form.showError('name', formDir, 'uniqueName')"
34 i18n>The chosen Ceph pool name is already in use.</span>
35 <span *ngIf="form.showError('name', formDir, 'rbdPool')"
36 class="invalid-feedback"
37 i18n>It's not possible to create an RBD pool with '/' in the name.
38 Please change the name or remove 'rbd' from the applications list.</span>
39 <span *ngIf="form.showError('name', formDir, 'pattern')"
40 class="invalid-feedback"
41 i18n>Pool name can only contain letters, numbers, '.', '-', '_' or '/'.</span>
42 </div>
43 </div>
44
45 <!-- Pool type selection -->
46 <div class="form-group row">
47 <label class="cd-col-form-label required"
48 for="poolType"
49 i18n>Pool type</label>
50 <div class="cd-col-form-input">
51 <select class="form-control custom-select"
52 id="poolType"
53 formControlName="poolType"
54 name="poolType">
55 <option ngValue=""
56 i18n>-- Select a pool type --</option>
57 <option *ngFor="let poolType of data.poolTypes"
58 [value]="poolType">
59 {{ poolType }}
60 </option>
61 </select>
62 <span class="invalid-feedback"
63 *ngIf="form.showError('poolType', formDir, 'required')"
64 i18n>This field is required!</span>
65 </div>
66 </div>
67
68 <div *ngIf="isReplicated || isErasure">
69 <!-- PG Autoscale Mode -->
70 <div class="form-group row">
71 <label i18n
72 class="cd-col-form-label"
73 for="pgAutoscaleMode">PG Autoscale</label>
74 <div class="cd-col-form-input">
75 <select class="form-control custom-select"
76 id="pgAutoscaleMode"
77 name="pgAutoscaleMode"
78 formControlName="pgAutoscaleMode">
79 <option *ngFor="let mode of pgAutoscaleModes"
80 [value]="mode">
81 {{ mode }}
82 </option>
83 </select>
84 </div>
85 </div>
86
87 <!-- Pg number -->
88 <div class="form-group row"
89 *ngIf="form.getValue('pgAutoscaleMode') !== 'on'">
90 <label class="cd-col-form-label required"
91 for="pgNum"
92 i18n>Placement groups</label>
93 <div class="cd-col-form-input">
94 <input class="form-control"
95 id="pgNum"
96 name="pgNum"
97 formControlName="pgNum"
98 min="1"
99 type="number"
100 (focus)="externalPgChange = false"
101 (blur)="alignPgs()"
102 required>
103 <span class="invalid-feedback"
104 *ngIf="form.showError('pgNum', formDir, 'required')"
105 i18n>This field is required!</span>
106 <span class="invalid-feedback"
107 *ngIf="form.showError('pgNum', formDir, 'min')"
108 i18n>At least one placement group is needed!</span>
109 <span class="invalid-feedback"
110 *ngIf="form.showError('pgNum', formDir, '34')"
111 i18n>Your cluster can't handle this many PGs. Please recalculate the PG amount needed.</span>
112 <span class="form-text text-muted">
113 <cd-doc section="pgs"
114 docText="Calculation help"
115 i18n-docText></cd-doc>
116 </span>
117 <span class="form-text text-muted"
118 *ngIf="externalPgChange"
119 i18n>The current PGs settings were calculated for you, you
120 should make sure the values suit your needs before submit.</span>
121 </div>
122 </div>
123
124 <!-- Replica Size -->
125 <div class="form-group row"
126 *ngIf="isReplicated">
127 <label class="cd-col-form-label required"
128 for="size"
129 i18n>Replicated size</label>
130 <div class="cd-col-form-input">
131 <input class="form-control"
132 id="size"
133 [max]="getMaxSize()"
134 [min]="getMinSize()"
135 name="size"
136 type="number"
137 formControlName="size">
138 <span class="invalid-feedback"
139 *ngIf="form.showError('size', formDir)">
140 <ul class="list-inline">
141 <li i18n>Minimum: {{ getMinSize() }}</li>
142 <li i18n>Maximum: {{ getMaxSize() }}</li>
143 </ul>
144 </span>
145 <span class="invalid-feedback"
146 *ngIf="form.showError('size', formDir)"
147 i18n>The size specified is out of range. A value from
148 {{ getMinSize() }} to {{ getMaxSize() }} is usable.</span>
149 <span class="text-warning-dark"
150 *ngIf="form.getValue('size') === 1"
151 i18n>A size of 1 will not create a replication of the
152 object. The 'Replicated size' includes the object itself.</span>
153 </div>
154 </div>
155
156 <!-- Flags -->
157 <div class="form-group row"
158 *ngIf="info.is_all_bluestore && isErasure">
159 <label i18n
160 class="cd-col-form-label">Flags</label>
161 <div class="cd-col-form-input">
162 <div class="custom-control custom-checkbox">
163 <input type="checkbox"
164 class="custom-control-input"
165 id="ec-overwrites"
166 formControlName="ecOverwrites">
167 <label class="custom-control-label"
168 for="ec-overwrites"
169 i18n>EC Overwrites</label>
170 </div>
171 </div>
172 </div>
173
174 </div>
175 <!-- Applications -->
176 <div class="form-group row">
177 <label i18n
178 class="cd-col-form-label"
179 for="applications">Applications</label>
180 <div class="cd-col-form-input">
181 <cd-select-badges id="applications"
182 [customBadges]="true"
183 [customBadgeValidators]="data.applications.validators"
184 [messages]="data.applications.messages"
185 [data]="data.applications.selected"
186 [options]="data.applications.available"
187 [selectionLimit]="4"
188 (selection)="appSelection()">
189 </cd-select-badges>
190 </div>
191 </div>
192
193 <!-- CRUSH -->
194 <div *ngIf="isErasure || isReplicated">
195
196 <legend i18n>CRUSH</legend>
197
198 <!-- Erasure Profile select -->
199 <div class="form-group row"
200 *ngIf="isErasure">
201 <label i18n
202 class="cd-col-form-label"
203 for="erasureProfile">Erasure code profile</label>
204 <div class="cd-col-form-input">
205 <div class="input-group">
206 <select class="form-control custom-select"
207 id="erasureProfile"
208 name="erasureProfile"
209 formControlName="erasureProfile">
210 <option *ngIf="!ecProfiles"
211 ngValue=""
212 i18n>Loading...</option>
213 <option *ngIf="ecProfiles && ecProfiles.length === 0"
214 [ngValue]="null"
215 i18n>-- No erasure code profile available --</option>
216 <option *ngIf="ecProfiles && ecProfiles.length > 0"
217 [ngValue]="null"
218 i18n>-- Select an erasure code profile --</option>
219 <option *ngFor="let ecp of ecProfiles"
220 [ngValue]="ecp">
221 {{ ecp.name }}
222 </option>
223 </select>
224 <span class="input-group-append">
225 <button class="btn btn-light"
226 [ngClass]="{'active': data.erasureInfo}"
227 id="ecp-info-button"
228 type="button"
229 (click)="data.erasureInfo = !data.erasureInfo">
230 <i [ngClass]="[icons.questionCircle]"
231 aria-hidden="true"></i>
232 </button>
233 <button class="btn btn-light"
234 type="button"
235 *ngIf="!editing"
236 (click)="addErasureCodeProfile()">
237 <i [ngClass]="[icons.add]"
238 aria-hidden="true"></i>
239 </button>
240 <button class="btn btn-light"
241 type="button"
242 *ngIf="!editing"
243 tooltip="This profile can't be deleted as it is in use."
244 i18n-tooltip
245 triggers=""
246 #ecpDeletionBtn="bs-tooltip"
247 (click)="deleteErasureCodeProfile()">
248 <i [ngClass]="[icons.trash]"
249 aria-hidden="true"></i>
250 </button>
251 </span>
252 </div>
253 <span class="form-text text-muted"
254 id="ecp-info-block"
255 *ngIf="data.erasureInfo && form.getValue('erasureProfile')">
256 <tabset #ecpInfoTabs>
257 <tab i18n-heading
258 heading="Profile"
259 class="ecp-info">
260 <cd-table-key-value [renderObjects]="true"
261 [hideKeys]="['name']"
262 [data]="form.getValue('erasureProfile')"
263 [autoReload]="false">
264 </cd-table-key-value>
265 </tab>
266 <tab i18n-heading
267 heading="Used by pools"
268 class="used-by-pools">
269 <ng-template #ecpIsNotUsed>
270 <span i18n>Profile is not in use.</span>
271 </ng-template>
272 <ul *ngIf="ecpUsage; else ecpIsNotUsed">
273 <li *ngFor="let pool of ecpUsage">
274 {{ pool }}
275 </li>
276 </ul>
277 </tab>
278 </tabset>
279 </span>
280 </div>
281 </div>
282
283 <!-- Crush ruleset selection -->
284 <div class="form-group row"
285 *ngIf="isErasure && !editing">
286 <label class="cd-col-form-label"
287 for="crushRule"
288 i18n>Crush ruleset</label>
289 <div class="cd-col-form-input">
290 <span class="form-text text-muted"
291 i18n>A new crush ruleset will be implicitly created.</span>
292 </div>
293 </div>
294 <div class="form-group row"
295 *ngIf="isReplicated || editing">
296 <label class="cd-col-form-label"
297 for="crushRule"
298 i18n>Crush ruleset</label>
299 <div class="cd-col-form-input">
300 <ng-template #noRules>
301 <span class="form-text text-muted">
302 <span i18n>There are no rules.</span>&nbsp;
303 </span>
304 </ng-template>
305 <div *ngIf="current.rules.length > 0; else noRules">
306 <div class="input-group">
307 <select class="form-control custom-select"
308 id="crushRule"
309 formControlName="crushRule"
310 name="crushSet">
311 <option [ngValue]="null"
312 i18n>-- Select a crush rule --</option>
313 <option *ngFor="let rule of current.rules"
314 [ngValue]="rule">
315 {{ rule.rule_name }}
316 </option>
317 </select>
318 <span class="input-group-append">
319 <button class="btn btn-light"
320 [ngClass]="{'active': data.crushInfo}"
321 id="crush-info-button"
322 type="button"
323 (click)="data.crushInfo = !data.crushInfo">
324 <i [ngClass]="[icons.questionCircle]"
325 aria-hidden="true"></i>
326 </button>
327 <button class="btn btn-light"
328 type="button"
329 *ngIf="isReplicated && !editing"
330 (click)="addCrushRule()">
331 <i [ngClass]="[icons.add]"
332 aria-hidden="true"></i>
333 </button>
334 <button class="btn btn-light"
335 *ngIf="isReplicated && !editing"
336 type="button"
337 tooltip="This rule can't be deleted as it is in use."
338 i18n-tooltip
339 triggers=""
340 #crushDeletionBtn="bs-tooltip"
341 (click)="deleteCrushRule()">
342 <i [ngClass]="[icons.trash]"
343 aria-hidden="true"></i>
344 </button>
345 </span>
346 </div>
347 <span class="form-text text-muted"
348 id="crush-info-block"
349 *ngIf="data.crushInfo && form.getValue('crushRule')">
350 <tabset #crushInfoTabs>
351 <tab i18n-heading
352 heading="Crush rule"
353 class="crush-rule-info">
354 <cd-table-key-value [renderObjects]="false"
355 [hideKeys]="['steps', 'ruleset', 'type', 'rule_name']"
356 [data]="form.getValue('crushRule')"
357 [autoReload]="false">
358 </cd-table-key-value>
359 </tab>
360 <tab i18n-heading
361 heading="Crush steps"
362 class="crush-rule-steps">
363 <ol>
364 <li *ngFor="let step of form.get('crushRule').value.steps">
365 {{ describeCrushStep(step) }}
366 </li>
367 </ol>
368 </tab>
369 <tab i18n-heading
370 heading="Used by pools"
371 class="used-by-pools">
372 <ng-template #ruleIsNotUsed>
373 <span i18n>Rule is not in use.</span>
374 </ng-template>
375 <ul *ngIf="crushUsage; else ruleIsNotUsed">
376 <li *ngFor="let pool of crushUsage">
377 {{ pool }}
378 </li>
379 </ul>
380 </tab>
381 </tabset>
382 </span>
383 <span class="invalid-feedback"
384 *ngIf="form.showError('crushRule', formDir, 'required')"
385 i18n>This field is required!</span>
386 <span class="invalid-feedback"
387 *ngIf="form.showError('crushRule', formDir, 'tooFewOsds')"
388 i18n>The rule can't be used in the current cluster as it has
389 too few OSDs to meet the minimum required OSD by this rule.</span>
390 </div>
391 </div>
392 </div>
393
394 </div>
395
396 <!-- Compression -->
397 <div *ngIf="info.is_all_bluestore"
398 formGroupName="compression">
399 <legend i18n>Compression</legend>
400
401 <!-- Compression Mode -->
402 <div class="form-group row">
403 <label i18n
404 class="cd-col-form-label"
405 for="mode">Mode</label>
406 <div class="cd-col-form-input">
407 <select class="form-control custom-select"
408 id="mode"
409 name="mode"
410 formControlName="mode">
411 <option *ngFor="let mode of info.compression_modes"
412 [value]="mode">
413 {{ mode }}
414 </option>
415 </select>
416 </div>
417 </div>
418 <div *ngIf="hasCompressionEnabled()">
419 <!-- Compression algorithm selection -->
420 <div class="form-group row">
421 <label i18n
422 class="cd-col-form-label"
423 for="algorithm">Algorithm</label>
424 <div class="cd-col-form-input">
425 <select class="form-control custom-select"
426 id="algorithm"
427 name="algorithm"
428 formControlName="algorithm">
429 <option *ngIf="!info.compression_algorithms"
430 ngValue=""
431 i18n>Loading...</option>
432 <option *ngIf="info.compression_algorithms && info.compression_algorithms.length === 0"
433 i18n
434 ngValue="">-- No erasure compression algorithm available --</option>
435 <option *ngFor="let algorithm of info.compression_algorithms"
436 [value]="algorithm">
437 {{ algorithm }}
438 </option>
439 </select>
440 </div>
441 </div>
442
443 <!-- Compression min blob size -->
444 <div class="form-group row">
445 <label i18n
446 class="cd-col-form-label"
447 for="minBlobSize">Minimum blob size</label>
448 <div class="cd-col-form-input">
449 <input id="minBlobSize"
450 name="minBlobSize"
451 formControlName="minBlobSize"
452 type="text"
453 min="0"
454 class="form-control"
455 i18n-placeholder
456 placeholder="e.g., 128KiB"
457 defaultUnit="KiB"
458 cdDimlessBinary>
459 <span class="invalid-feedback"
460 *ngIf="form.showError('minBlobSize', formDir, 'min')"
461 i18n>Value should be greater than 0</span>
462 <span class="invalid-feedback"
463 *ngIf="form.showError('minBlobSize', formDir, 'maximum')"
464 i18n>Value should be less than the maximum blob size</span>
465 </div>
466 </div>
467
468 <!-- Compression max blob size -->
469 <div class="form-group row">
470 <label i18n
471 class="cd-col-form-label"
472 for="maxBlobSize">Maximum blob size</label>
473 <div class="cd-col-form-input">
474 <input id="maxBlobSize"
475 type="text"
476 min="0"
477 formControlName="maxBlobSize"
478 class="form-control"
479 i18n-placeholder
480 placeholder="e.g., 512KiB"
481 defaultUnit="KiB"
482 cdDimlessBinary>
483 <span class="invalid-feedback"
484 *ngIf="form.showError('maxBlobSize', formDir, 'min')"
485 i18n>Value should be greater than 0</span>
486 <span class="invalid-feedback"
487 *ngIf="form.showError('maxBlobSize', formDir, 'minimum')"
488 i18n>Value should be greater than the minimum blob size</span>
489 </div>
490 </div>
491
492 <!-- Compression ratio -->
493 <div class="form-group row">
494 <label i18n
495 class="cd-col-form-label"
496 for="ratio">Ratio</label>
497 <div class="cd-col-form-input">
498 <input id="ratio"
499 name="ratio"
500 formControlName="ratio"
501 type="number"
502 min="0"
503 max="1"
504 step="0.1"
505 class="form-control"
506 i18n-placeholder
507 placeholder="Compression ratio">
508 <span class="invalid-feedback"
509 *ngIf="form.showError('ratio', formDir, 'min') || form.showError('ratio', formDir, 'max')"
510 i18n>Value should be between 0.0 and 1.0</span>
511 </div>
512 </div>
513
514 </div>
515 </div>
516
517 <!-- Quotas -->
518 <div>
519 <legend i18n>Quotas</legend>
520
521 <!-- Max Bytes -->
522 <div class="form-group row">
523 <label class="cd-col-form-label"
524 for="max_bytes">
525 <ng-container i18n>Max bytes</ng-container>
526 <cd-helper>
527 <span i18n>Leave it blank or specify 0 to disable this quota.</span>
528 <br>
529 <span i18n>A valid quota should be greater than 0.</span>
530 </cd-helper>
531 </label>
532 <div class="cd-col-form-input">
533 <input class="form-control"
534 id="max_bytes"
535 name="max_bytes"
536 type="text"
537 formControlName="max_bytes"
538 i18n-placeholder
539 placeholder="e.g., 10GiB"
540 defaultUnit="GiB"
541 cdDimlessBinary>
542 </div>
543 </div>
544
545 <!-- Max Objects -->
546 <div class="form-group row">
547 <label class="cd-col-form-label"
548 for="max_objects">
549 <ng-container i18n>Max objects</ng-container>
550 <cd-helper>
551 <span i18n>Leave it blank or specify 0 to disable this quota.</span>
552 <br>
553 <span i18n>A valid quota should be greater than 0.</span>
554 </cd-helper>
555 </label>
556 <div class="cd-col-form-input">
557 <input class="form-control"
558 id="max_objects"
559 min="0"
560 name="max_objects"
561 type="number"
562 formControlName="max_objects">
563 <span class="invalid-feedback"
564 *ngIf="form.showError('max_objects', formDir, 'min')"
565 i18n>The value should be greater or equal to 0</span>
566 </div>
567 </div>
568 </div>
569
570 <!-- Pool configuration -->
571 <div [hidden]="isErasure || data.applications.selected.indexOf('rbd') === -1">
572 <cd-rbd-configuration-form [form]="form"
573 [initializeData]="initializeConfigData"
574 (changes)="currentConfigurationValues = $event()">
575 </cd-rbd-configuration-form>
576 </div>
577 </div>
578 <div class="card-footer">
579 <div class="button-group text-right">
580 <cd-submit-button [form]="formDir"
581 i18n="form action button|Example: Create Pool@@formActionButton"
582 (submitAction)="submit()">{{ action | titlecase }} {{ resource | upperFirst }}
583 </cd-submit-button>
584 <cd-back-button></cd-back-button>
585 </div>
586 </div>
587
588 </div>
589
590 </form>
591 </div>