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