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