]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html
2df480edab26af314ef8bdcf3f11204304ead428
[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-control"
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-control"
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">
207 <select class="form-control"
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 <span class="input-group-append">
226 <button class="btn btn-light"
227 [ngClass]="{'active': data.erasureInfo}"
228 id="ecp-info-button"
229 type="button"
230 (click)="data.erasureInfo = !data.erasureInfo">
231 <i [ngClass]="[icons.questionCircle]"
232 aria-hidden="true"></i>
233 </button>
234 <button class="btn btn-light"
235 type="button"
236 *ngIf="!editing"
237 (click)="addErasureCodeProfile()">
238 <i [ngClass]="[icons.add]"
239 aria-hidden="true"></i>
240 </button>
241 <button class="btn btn-light"
242 type="button"
243 *ngIf="!editing"
244 ngbTooltip="This profile can't be deleted as it is in use."
245 i18n-ngbTooltip
246 triggers="manual"
247 #ecpDeletionBtn="ngbTooltip"
248 (click)="deleteErasureCodeProfile()">
249 <i [ngClass]="[icons.trash]"
250 aria-hidden="true"></i>
251 </button>
252 </span>
253 </div>
254 <span class="form-text text-muted"
255 id="ecp-info-block"
256 *ngIf="data.erasureInfo && form.getValue('erasureProfile')">
257 <ul ngbNav
258 #ecpInfoTabs="ngbNav"
259 class="nav-tabs">
260 <li ngbNavItem="ecp-info">
261 <a ngbNavLink
262 i18n>Profile</a>
263 <ng-template ngbNavContent>
264 <cd-table-key-value [renderObjects]="true"
265 [hideKeys]="['name']"
266 [data]="form.getValue('erasureProfile')"
267 [autoReload]="false">
268 </cd-table-key-value>
269 </ng-template>
270 </li>
271 <li ngbNavItem="used-by-pools">
272 <a ngbNavLink
273 i18n>Used by pools</a>
274 <ng-template ngbNavContent>
275 <ng-template #ecpIsNotUsed>
276 <span i18n>Profile is not in use.</span>
277 </ng-template>
278 <ul *ngIf="ecpUsage; else ecpIsNotUsed">
279 <li *ngFor="let pool of ecpUsage">
280 {{ pool }}
281 </li>
282 </ul>
283 </ng-template>
284 </li>
285 </ul>
286
287 <div [ngbNavOutlet]="ecpInfoTabs"></div>
288 </span>
289 </div>
290 </div>
291
292 <!-- Crush ruleset selection -->
293 <div class="form-group row"
294 *ngIf="isErasure && !editing">
295 <label class="cd-col-form-label"
296 for="crushRule"
297 i18n>Crush ruleset</label>
298 <div class="cd-col-form-input">
299 <span class="form-text text-muted"
300 i18n>A new crush ruleset will be implicitly created.</span>
301 </div>
302 </div>
303 <div class="form-group row"
304 *ngIf="isReplicated || editing">
305 <label class="cd-col-form-label"
306 for="crushRule"
307 i18n>Crush ruleset</label>
308 <div class="cd-col-form-input">
309 <ng-template #noRules>
310 <span class="form-text text-muted">
311 <span i18n>There are no rules.</span>&nbsp;
312 </span>
313 </ng-template>
314 <div *ngIf="current.rules.length > 0; else noRules">
315 <div class="input-group">
316 <select class="form-control"
317 id="crushRule"
318 formControlName="crushRule"
319 name="crushSet">
320 <option [ngValue]="null"
321 i18n>-- Select a crush rule --</option>
322 <option *ngFor="let rule of current.rules"
323 [ngValue]="rule">
324 {{ rule.rule_name }}
325 </option>
326 </select>
327 <span class="input-group-append">
328 <button class="btn btn-light"
329 [ngClass]="{'active': data.crushInfo}"
330 id="crush-info-button"
331 type="button"
332 ngbTooltip="Placement and
333 replication strategies or distribution policies that allow to
334 specify how CRUSH places data replicas."
335 i18n-ngbTooltip
336 (click)="data.crushInfo = !data.crushInfo">
337 <i [ngClass]="[icons.questionCircle]"
338 aria-hidden="true"></i>
339 </button>
340 <button class="btn btn-light"
341 type="button"
342 *ngIf="isReplicated && !editing"
343 (click)="addCrushRule()">
344 <i [ngClass]="[icons.add]"
345 aria-hidden="true"></i>
346 </button>
347 <button class="btn btn-light"
348 *ngIf="isReplicated && !editing"
349 type="button"
350 ngbTooltip="This rule can't be deleted as it is in use."
351 i18n-ngbTooltip
352 triggers="manual"
353 #crushDeletionBtn="ngbTooltip"
354 (click)="deleteCrushRule()">
355 <i [ngClass]="[icons.trash]"
356 aria-hidden="true"></i>
357 </button>
358 </span>
359 </div>
360
361 <div class="form-text text-muted"
362 id="crush-info-block"
363 *ngIf="data.crushInfo && form.getValue('crushRule')">
364 <ul ngbNav
365 #crushInfoTabs="ngbNav"
366 class="nav-tabs">
367 <li ngbNavItem="crush-rule-info">
368 <a ngbNavLink
369 i18n>Crush rule</a>
370 <ng-template ngbNavContent>
371 <cd-table-key-value [renderObjects]="false"
372 [hideKeys]="['steps', 'type', 'rule_name']"
373 [data]="form.getValue('crushRule')"
374 [autoReload]="false">
375 </cd-table-key-value>
376 </ng-template>
377 </li>
378 <li ngbNavItem="crush-rule-steps">
379 <a ngbNavLink
380 i18n>Crush steps</a>
381 <ng-template ngbNavContent>
382 <ol>
383 <li *ngFor="let step of form.get('crushRule').value.steps">
384 {{ describeCrushStep(step) }}
385 </li>
386 </ol>
387 </ng-template>
388 </li>
389 <li ngbNavItem="used-by-pools">
390 <a ngbNavLink
391 i18n>Used by pools</a>
392 <ng-template ngbNavContent>
393
394 <ng-template #ruleIsNotUsed>
395 <span i18n>Rule is not in use.</span>
396 </ng-template>
397 <ul *ngIf="crushUsage; else ruleIsNotUsed">
398 <li *ngFor="let pool of crushUsage">
399 {{ pool }}
400 </li>
401 </ul>
402 </ng-template>
403 </li>
404 </ul>
405
406 <div [ngbNavOutlet]="crushInfoTabs"></div>
407 </div>
408 <span class="invalid-feedback"
409 *ngIf="form.showError('crushRule', formDir, 'required')"
410 i18n>This field is required!</span>
411 <span class="invalid-feedback"
412 *ngIf="form.showError('crushRule', formDir, 'tooFewOsds')"
413 i18n>The rule can't be used in the current cluster as it has
414 too few OSDs to meet the minimum required OSD by this rule.</span>
415 </div>
416 </div>
417 </div>
418
419 </div>
420
421 <!-- Compression -->
422 <div *ngIf="info.is_all_bluestore"
423 formGroupName="compression">
424 <legend i18n>Compression</legend>
425
426 <!-- Compression Mode -->
427 <div class="form-group row">
428 <label i18n
429 class="cd-col-form-label"
430 for="mode">Mode</label>
431 <div class="cd-col-form-input">
432 <select class="form-control"
433 id="mode"
434 name="mode"
435 formControlName="mode">
436 <option *ngFor="let mode of info.compression_modes"
437 [value]="mode">
438 {{ mode }}
439 </option>
440 </select>
441 </div>
442 </div>
443 <div *ngIf="hasCompressionEnabled()">
444 <!-- Compression algorithm selection -->
445 <div class="form-group row">
446 <label i18n
447 class="cd-col-form-label"
448 for="algorithm">Algorithm</label>
449 <div class="cd-col-form-input">
450 <select class="form-control"
451 id="algorithm"
452 name="algorithm"
453 formControlName="algorithm">
454 <option *ngIf="!info.compression_algorithms"
455 ngValue=""
456 i18n>Loading...</option>
457 <option *ngIf="info.compression_algorithms && info.compression_algorithms.length === 0"
458 i18n
459 ngValue="">-- No erasure compression algorithm available --</option>
460 <option *ngFor="let algorithm of info.compression_algorithms"
461 [value]="algorithm">
462 {{ algorithm }}
463 </option>
464 </select>
465 </div>
466 </div>
467
468 <!-- Compression min blob size -->
469 <div class="form-group row">
470 <label i18n
471 class="cd-col-form-label"
472 for="minBlobSize">Minimum blob size</label>
473 <div class="cd-col-form-input">
474 <input id="minBlobSize"
475 name="minBlobSize"
476 formControlName="minBlobSize"
477 type="text"
478 min="0"
479 class="form-control"
480 i18n-placeholder
481 placeholder="e.g., 128KiB"
482 defaultUnit="KiB"
483 cdDimlessBinary>
484 <span class="invalid-feedback"
485 *ngIf="form.showError('minBlobSize', formDir, 'min')"
486 i18n>Value should be greater than 0</span>
487 <span class="invalid-feedback"
488 *ngIf="form.showError('minBlobSize', formDir, 'maximum')"
489 i18n>Value should be less than the maximum blob size</span>
490 </div>
491 </div>
492
493 <!-- Compression max blob size -->
494 <div class="form-group row">
495 <label i18n
496 class="cd-col-form-label"
497 for="maxBlobSize">Maximum blob size</label>
498 <div class="cd-col-form-input">
499 <input id="maxBlobSize"
500 type="text"
501 min="0"
502 formControlName="maxBlobSize"
503 class="form-control"
504 i18n-placeholder
505 placeholder="e.g., 512KiB"
506 defaultUnit="KiB"
507 cdDimlessBinary>
508 <span class="invalid-feedback"
509 *ngIf="form.showError('maxBlobSize', formDir, 'min')"
510 i18n>Value should be greater than 0</span>
511 <span class="invalid-feedback"
512 *ngIf="form.showError('maxBlobSize', formDir, 'minimum')"
513 i18n>Value should be greater than the minimum blob size</span>
514 </div>
515 </div>
516
517 <!-- Compression ratio -->
518 <div class="form-group row">
519 <label i18n
520 class="cd-col-form-label"
521 for="ratio">Ratio</label>
522 <div class="cd-col-form-input">
523 <input id="ratio"
524 name="ratio"
525 formControlName="ratio"
526 type="number"
527 min="0"
528 max="1"
529 step="0.1"
530 class="form-control"
531 i18n-placeholder
532 placeholder="Compression ratio">
533 <span class="invalid-feedback"
534 *ngIf="form.showError('ratio', formDir, 'min') || form.showError('ratio', formDir, 'max')"
535 i18n>Value should be between 0.0 and 1.0</span>
536 </div>
537 </div>
538
539 </div>
540 </div>
541
542 <!-- Quotas -->
543 <div>
544 <legend i18n>Quotas</legend>
545
546 <!-- Max Bytes -->
547 <div class="form-group row">
548 <label class="cd-col-form-label"
549 for="max_bytes">
550 <ng-container i18n>Max bytes</ng-container>
551 <cd-helper>
552 <span i18n>Leave it blank or specify 0 to disable this quota.</span>
553 <br>
554 <span i18n>A valid quota should be greater than 0.</span>
555 </cd-helper>
556 </label>
557 <div class="cd-col-form-input">
558 <input class="form-control"
559 id="max_bytes"
560 name="max_bytes"
561 type="text"
562 formControlName="max_bytes"
563 i18n-placeholder
564 placeholder="e.g., 10GiB"
565 defaultUnit="GiB"
566 cdDimlessBinary>
567 </div>
568 </div>
569
570 <!-- Max Objects -->
571 <div class="form-group row">
572 <label class="cd-col-form-label"
573 for="max_objects">
574 <ng-container i18n>Max objects</ng-container>
575 <cd-helper>
576 <span i18n>Leave it blank or specify 0 to disable this quota.</span>
577 <br>
578 <span i18n>A valid quota should be greater than 0.</span>
579 </cd-helper>
580 </label>
581 <div class="cd-col-form-input">
582 <input class="form-control"
583 id="max_objects"
584 min="0"
585 name="max_objects"
586 type="number"
587 formControlName="max_objects">
588 <span class="invalid-feedback"
589 *ngIf="form.showError('max_objects', formDir, 'min')"
590 i18n>The value should be greater or equal to 0</span>
591 </div>
592 </div>
593 </div>
594
595 <!-- Pool configuration -->
596 <div [hidden]="isErasure || data.applications.selected.indexOf('rbd') === -1">
597 <cd-rbd-configuration-form [form]="form"
598 [initializeData]="initializeConfigData"
599 (changes)="currentConfigurationValues = $event()">
600 </cd-rbd-configuration-form>
601 </div>
602 </div>
603 <div class="card-footer">
604 <cd-form-button-panel (submitActionEvent)="submit()"
605 [form]="form"
606 [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"
607 wrappingClass="text-right"></cd-form-button-panel>
608 </div>
609
610 </div>
611
612 </form>
613 </div>