]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html
import 15.2.4
[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 <a i18n
114 target="_blank"
115 href="http://ceph.com/pgcalc">Calculation help</a>
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 valid.</span>
149 </div>
150 </div>
151
152 <!-- Flags -->
153 <div class="form-group row"
154 *ngIf="info.is_all_bluestore && isErasure">
155 <label i18n
156 class="cd-col-form-label">Flags</label>
157 <div class="cd-col-form-input">
158 <div class="custom-control custom-checkbox">
159 <input type="checkbox"
160 class="custom-control-input"
161 id="ec-overwrites"
162 formControlName="ecOverwrites">
163 <label class="custom-control-label"
164 for="ec-overwrites"
165 i18n>EC Overwrites</label>
166 </div>
167 </div>
168 </div>
169
170 </div>
171 <!-- Applications -->
172 <div class="form-group row">
173 <label i18n
174 class="cd-col-form-label"
175 for="applications">Applications</label>
176 <div class="cd-col-form-input">
177 <cd-select-badges id="applications"
178 [customBadges]="true"
179 [customBadgeValidators]="data.applications.validators"
180 [messages]="data.applications.messages"
181 [data]="data.applications.selected"
182 [options]="data.applications.available"
183 [selectionLimit]="4"
184 (selection)="appSelection()">
185 </cd-select-badges>
186 </div>
187 </div>
188
189 <!-- CRUSH -->
190 <div *ngIf="isErasure || isReplicated">
191
192 <legend i18n>CRUSH</legend>
193
194 <!-- Erasure Profile select -->
195 <div class="form-group row"
196 *ngIf="isErasure">
197 <label i18n
198 class="cd-col-form-label"
199 for="erasureProfile">Erasure code profile</label>
200 <div class="cd-col-form-input">
201 <div class="input-group">
202 <select class="form-control custom-select"
203 id="erasureProfile"
204 name="erasureProfile"
205 formControlName="erasureProfile">
206 <option *ngIf="!ecProfiles"
207 ngValue=""
208 i18n>Loading...</option>
209 <option *ngIf="ecProfiles && ecProfiles.length === 0"
210 [ngValue]="null"
211 i18n>-- No erasure code profile available --</option>
212 <option *ngIf="ecProfiles && ecProfiles.length > 0"
213 [ngValue]="null"
214 i18n>-- Select an erasure code profile --</option>
215 <option *ngFor="let ecp of ecProfiles"
216 [ngValue]="ecp">
217 {{ ecp.name }}
218 </option>
219 </select>
220 <span class="input-group-append">
221 <button class="btn btn-light"
222 [ngClass]="{'active': data.erasureInfo}"
223 id="ecp-info-button"
224 type="button"
225 (click)="data.erasureInfo = !data.erasureInfo">
226 <i [ngClass]="[icons.questionCircle]"
227 aria-hidden="true"></i>
228 </button>
229 <button class="btn btn-light"
230 type="button"
231 *ngIf="!editing"
232 (click)="addErasureCodeProfile()">
233 <i [ngClass]="[icons.add]"
234 aria-hidden="true"></i>
235 </button>
236 <button class="btn btn-light"
237 type="button"
238 *ngIf="!editing"
239 tooltip="This profile can't be deleted as it is in use."
240 i18n-tooltip
241 triggers=""
242 #ecpDeletionBtn="bs-tooltip"
243 (click)="deleteErasureCodeProfile()">
244 <i [ngClass]="[icons.trash]"
245 aria-hidden="true"></i>
246 </button>
247 </span>
248 </div>
249 <span class="form-text text-muted"
250 id="ecp-info-block"
251 *ngIf="data.erasureInfo && form.getValue('erasureProfile')">
252 <tabset #ecpInfoTabs>
253 <tab i18n-heading
254 heading="Profile"
255 class="ecp-info">
256 <cd-table-key-value [renderObjects]="true"
257 [data]="form.getValue('erasureProfile')"
258 [autoReload]="false">
259 </cd-table-key-value>
260 </tab>
261 <tab i18n-heading
262 heading="Used by pools"
263 class="used-by-pools">
264 <ng-template #ecpIsNotUsed>
265 <span i18n>Profile is not in use.</span>
266 </ng-template>
267 <ul *ngIf="ecpUsage; else ecpIsNotUsed">
268 <li *ngFor="let pool of ecpUsage">
269 {{ pool }}
270 </li>
271 </ul>
272 </tab>
273 </tabset>
274 </span>
275 </div>
276 </div>
277
278 <!-- Crush ruleset selection -->
279 <div class="form-group row"
280 *ngIf="isErasure && !editing">
281 <label class="cd-col-form-label"
282 for="crushRule"
283 i18n>Crush ruleset</label>
284 <div class="cd-col-form-input">
285 <span class="form-text text-muted"
286 i18n>A new crush ruleset will be implicitly created.</span>
287 </div>
288 </div>
289 <div class="form-group row"
290 *ngIf="isReplicated || editing">
291 <label class="cd-col-form-label"
292 for="crushRule"
293 i18n>Crush ruleset</label>
294 <div class="cd-col-form-input">
295 <ng-template #noRules>
296 <span class="form-text text-muted">
297 <span i18n>There are no rules.</span>&nbsp;
298 </span>
299 </ng-template>
300 <div *ngIf="current.rules.length > 0; else noRules">
301 <div class="input-group">
302 <select class="form-control custom-select"
303 id="crushRule"
304 formControlName="crushRule"
305 name="crushSet">
306 <option [ngValue]="null"
307 i18n>-- Select a crush rule --</option>
308 <option *ngFor="let rule of current.rules"
309 [ngValue]="rule">
310 {{ rule.rule_name }}
311 </option>
312 </select>
313 <span class="input-group-append">
314 <button class="btn btn-light"
315 [ngClass]="{'active': data.crushInfo}"
316 id="crush-info-button"
317 type="button"
318 (click)="data.crushInfo = !data.crushInfo">
319 <i [ngClass]="[icons.questionCircle]"
320 aria-hidden="true"></i>
321 </button>
322 <button class="btn btn-light"
323 type="button"
324 *ngIf="isReplicated && !editing"
325 (click)="addCrushRule()">
326 <i [ngClass]="[icons.add]"
327 aria-hidden="true"></i>
328 </button>
329 <button class="btn btn-light"
330 *ngIf="isReplicated && !editing"
331 type="button"
332 tooltip="This rule can't be deleted as it is in use."
333 i18n-tooltip
334 triggers=""
335 #crushDeletionBtn="bs-tooltip"
336 (click)="deleteCrushRule()">
337 <i [ngClass]="[icons.trash]"
338 aria-hidden="true"></i>
339 </button>
340 </span>
341 </div>
342 <span class="form-text text-muted"
343 id="crush-info-block"
344 *ngIf="data.crushInfo && form.getValue('crushRule')">
345 <tabset #crushInfoTabs>
346 <tab i18n-heading
347 heading="Crush rule"
348 class="crush-rule-info">
349 <cd-table-key-value [renderObjects]="true"
350 [data]="form.getValue('crushRule')"
351 [autoReload]="false">
352 </cd-table-key-value>
353 </tab>
354 <tab i18n-heading
355 heading="Crush steps"
356 class="crush-rule-steps">
357 <ol>
358 <li *ngFor="let step of form.get('crushRule').value.steps">
359 {{ describeCrushStep(step) }}
360 </li>
361 </ol>
362 </tab>
363 <tab i18n-heading
364 heading="Used by pools"
365 class="used-by-pools">
366 <ng-template #ruleIsNotUsed>
367 <span i18n>Rule is not in use.</span>
368 </ng-template>
369 <ul *ngIf="crushUsage; else ruleIsNotUsed">
370 <li *ngFor="let pool of crushUsage">
371 {{ pool }}
372 </li>
373 </ul>
374 </tab>
375 </tabset>
376 </span>
377 <span class="invalid-feedback"
378 *ngIf="form.showError('crushRule', formDir, 'required')"
379 i18n>This field is required!</span>
380 <span class="invalid-feedback"
381 *ngIf="form.showError('crushRule', formDir, 'tooFewOsds')"
382 i18n>The rule can't be used in the current cluster as it has
383 too few OSDs to meet the minimum required OSD by this rule.</span>
384 </div>
385 </div>
386 </div>
387
388 </div>
389
390 <!-- Compression -->
391 <div *ngIf="info.is_all_bluestore"
392 formGroupName="compression">
393 <legend i18n>Compression</legend>
394
395 <!-- Compression Mode -->
396 <div class="form-group row">
397 <label i18n
398 class="cd-col-form-label"
399 for="mode">Mode</label>
400 <div class="cd-col-form-input">
401 <select class="form-control custom-select"
402 id="mode"
403 name="mode"
404 formControlName="mode">
405 <option *ngFor="let mode of info.compression_modes"
406 [value]="mode">
407 {{ mode }}
408 </option>
409 </select>
410 </div>
411 </div>
412 <div *ngIf="hasCompressionEnabled()">
413 <!-- Compression algorithm selection -->
414 <div class="form-group row">
415 <label i18n
416 class="cd-col-form-label"
417 for="algorithm">Algorithm</label>
418 <div class="cd-col-form-input">
419 <select class="form-control custom-select"
420 id="algorithm"
421 name="algorithm"
422 formControlName="algorithm">
423 <option *ngIf="!info.compression_algorithms"
424 ngValue=""
425 i18n>Loading...</option>
426 <option *ngIf="info.compression_algorithms && info.compression_algorithms.length === 0"
427 i18n
428 ngValue="">-- No erasure compression algorithm available --</option>
429 <option *ngFor="let algorithm of info.compression_algorithms"
430 [value]="algorithm">
431 {{ algorithm }}
432 </option>
433 </select>
434 </div>
435 </div>
436
437 <!-- Compression min blob size -->
438 <div class="form-group row">
439 <label i18n
440 class="cd-col-form-label"
441 for="minBlobSize">Minimum blob size</label>
442 <div class="cd-col-form-input">
443 <input id="minBlobSize"
444 name="minBlobSize"
445 formControlName="minBlobSize"
446 type="text"
447 min="0"
448 class="form-control"
449 i18n-placeholder
450 placeholder="e.g., 128KiB"
451 defaultUnit="KiB"
452 cdDimlessBinary>
453 <span class="invalid-feedback"
454 *ngIf="form.showError('minBlobSize', formDir, 'min')"
455 i18n>Value should be greater than 0</span>
456 <span class="invalid-feedback"
457 *ngIf="form.showError('minBlobSize', formDir, 'maximum')"
458 i18n>Value should be less than the maximum blob size</span>
459 </div>
460 </div>
461
462 <!-- Compression max blob size -->
463 <div class="form-group row">
464 <label i18n
465 class="cd-col-form-label"
466 for="maxBlobSize">Maximum blob size</label>
467 <div class="cd-col-form-input">
468 <input id="maxBlobSize"
469 type="text"
470 min="0"
471 formControlName="maxBlobSize"
472 class="form-control"
473 i18n-placeholder
474 placeholder="e.g., 512KiB"
475 defaultUnit="KiB"
476 cdDimlessBinary>
477 <span class="invalid-feedback"
478 *ngIf="form.showError('maxBlobSize', formDir, 'min')"
479 i18n>Value should be greater than 0</span>
480 <span class="invalid-feedback"
481 *ngIf="form.showError('maxBlobSize', formDir, 'minimum')"
482 i18n>Value should be greater than the minimum blob size</span>
483 </div>
484 </div>
485
486 <!-- Compression ratio -->
487 <div class="form-group row">
488 <label i18n
489 class="cd-col-form-label"
490 for="ratio">Ratio</label>
491 <div class="cd-col-form-input">
492 <input id="ratio"
493 name="ratio"
494 formControlName="ratio"
495 type="number"
496 min="0"
497 max="1"
498 step="0.1"
499 class="form-control"
500 i18n-placeholder
501 placeholder="Compression ratio">
502 <span class="invalid-feedback"
503 *ngIf="form.showError('ratio', formDir, 'min') || form.showError('ratio', formDir, 'max')"
504 i18n>Value should be between 0.0 and 1.0</span>
505 </div>
506 </div>
507
508 </div>
509 </div>
510
511 <!-- Quotas -->
512 <div>
513 <legend i18n>Quotas</legend>
514
515 <!-- Max Bytes -->
516 <div class="form-group row">
517 <label class="cd-col-form-label"
518 for="max_bytes">
519 <ng-container i18n>Max bytes</ng-container>
520 <cd-helper>
521 <span i18n>Leave it blank or specify 0 to disable this quota.</span>
522 <br>
523 <span i18n>A valid quota should be greater than 0.</span>
524 </cd-helper>
525 </label>
526 <div class="cd-col-form-input">
527 <input class="form-control"
528 id="max_bytes"
529 name="max_bytes"
530 type="text"
531 formControlName="max_bytes"
532 i18n-placeholder
533 placeholder="e.g., 10GiB"
534 defaultUnit="GiB"
535 cdDimlessBinary>
536 </div>
537 </div>
538
539 <!-- Max Objects -->
540 <div class="form-group row">
541 <label class="cd-col-form-label"
542 for="max_objects">
543 <ng-container i18n>Max objects</ng-container>
544 <cd-helper>
545 <span i18n>Leave it blank or specify 0 to disable this quota.</span>
546 <br>
547 <span i18n>A valid quota should be greater than 0.</span>
548 </cd-helper>
549 </label>
550 <div class="cd-col-form-input">
551 <input class="form-control"
552 id="max_objects"
553 min="0"
554 name="max_objects"
555 type="number"
556 formControlName="max_objects">
557 <span class="invalid-feedback"
558 *ngIf="form.showError('max_objects', formDir, 'min')"
559 i18n>The value should be greater or equal to 0</span>
560 </div>
561 </div>
562 </div>
563
564 <!-- Pool configuration -->
565 <div [hidden]="isErasure || data.applications.selected.indexOf('rbd') === -1">
566 <cd-rbd-configuration-form [form]="form"
567 [initializeData]="initializeConfigData"
568 (changes)="currentConfigurationValues = $event()">
569 </cd-rbd-configuration-form>
570 </div>
571 </div>
572 <div class="card-footer">
573 <div class="button-group text-right">
574 <cd-submit-button [form]="formDir"
575 i18n="form action button|Example: Create Pool@@formActionButton"
576 (submitAction)="submit()">{{ action | titlecase }} {{ resource | upperFirst }}
577 </cd-submit-button>
578 <cd-back-button></cd-back-button>
579 </div>
580 </div>
581
582 </div>
583
584 </form>
585 </div>