]> git.proxmox.com Git - ceph.git/blobdiff - 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
index 623434b6e4f33f138972e421fb5465eed1289f47..b159f12530d04d28383d7fc82414c03ff0140aad 100644 (file)
@@ -1,31 +1,20 @@
-<div class="col-sm-12 col-lg-6">
-  <h1 *ngIf="!(info && ecProfiles)"
-      class="jumbotron">
-    <i class="fa fa-lg fa-pulse fa-spinner text-primary"></i>
-    <ng-container i18n>Loading...</ng-container>
-  </h1>
+<div class="cd-col-form"
+     *cdFormLoading="loading">
   <form name="form"
-        *ngIf="info && ecProfiles"
-        class="form-horizontal"
         #formDir="ngForm"
         [formGroup]="form"
         novalidate>
-    <div class="panel panel-default">
-      <div class="panel-heading">
-        <h3 i18n="form title|Example: Create Pool@@formTitle"
-            class="panel-title">{{ action | titlecase }} {{ resource | upperFirst }}</h3>
-      </div>
+    <div class="card">
+      <div i18n="form title|Example: Create Pool@@formTitle"
+           class="card-header">{{ action | titlecase }} {{ resource | upperFirst }}</div>
 
-      <div class="panel-body">
+      <div class="card-body">
         <!-- Name -->
-        <div class="form-group"
-             [ngClass]="{'has-error': form.showError('name', formDir)}">
-          <label class="control-label col-sm-3"
-                 for="name">
-            <ng-container i18n>Name</ng-container>
-            <span class="required"></span>
-          </label>
-          <div class="col-sm-9">
+        <div class="form-group row">
+          <label class="cd-col-form-label required"
+                 for="name"
+                 i18n>Name</label>
+          <div class="cd-col-form-input">
             <input id="name"
                    name="name"
                    type="text"
                    i18n-placeholder
                    formControlName="name"
                    autofocus>
-            <span class="help-block"
+            <span class="invalid-feedback"
                   *ngIf="form.showError('name', formDir, 'required')"
                   i18n>This field is required!</span>
-            <span class="help-block"
+            <span class="invalid-feedback"
                   *ngIf="form.showError('name', formDir, 'uniqueName')"
                   i18n>The chosen Ceph pool name is already in use.</span>
+            <span *ngIf="form.showError('name', formDir, 'rbdPool')"
+                  class="invalid-feedback"
+                  i18n>It's not possible to create an RBD pool with '/' in the name.
+              Please change the name or remove 'rbd' from the applications list.</span>
+            <span *ngIf="form.showError('name', formDir, 'pattern')"
+                  class="invalid-feedback"
+                  i18n>Pool name can only contain letters, numbers, '.', '-', '_' or '/'.</span>
           </div>
         </div>
 
         <!-- Pool type selection -->
-        <div class="form-group"
-             [ngClass]="{'has-error': form.showError('poolType', formDir)}">
-          <label class="control-label col-sm-3"
-                 for="poolType">
-            <ng-container i18n>Pool type</ng-container>
-            <span class="required"></span>
-          </label>
-          <div class="col-sm-9">
-            <select class="form-control"
+        <div class="form-group row">
+          <label class="cd-col-form-label required"
+                 for="poolType"
+                 i18n>Pool type</label>
+          <div class="cd-col-form-input">
+            <select class="form-select"
                     id="poolType"
                     formControlName="poolType"
                     name="poolType">
                 {{ poolType }}
               </option>
             </select>
-            <span class="help-block"
+            <span class="invalid-feedback"
                   *ngIf="form.showError('poolType', formDir, 'required')"
                   i18n>This field is required!</span>
           </div>
         </div>
 
-        <div *ngIf="form.getValue('poolType')">
+        <div *ngIf="isReplicated || isErasure">
+          <!-- PG Autoscale Mode -->
+          <div class="form-group row">
+            <label i18n
+                   class="cd-col-form-label"
+                   for="pgAutoscaleMode">PG Autoscale</label>
+            <div class="cd-col-form-input">
+              <select class="form-select"
+                      id="pgAutoscaleMode"
+                      name="pgAutoscaleMode"
+                      formControlName="pgAutoscaleMode">
+                <option *ngFor="let mode of pgAutoscaleModes"
+                        [value]="mode">
+                  {{ mode }}
+                </option>
+              </select>
+            </div>
+          </div>
+
           <!-- Pg number -->
-          <div class="form-group"
-               [ngClass]="{'has-error': form.showError('pgNum', formDir)}">
-            <label class="control-label col-sm-3"
-                   for="pgNum">
-              <ng-container i18n>Placement groups</ng-container>
-              <span class="required"></span>
-            </label>
-            <div class="col-sm-9">
+          <div class="form-group row"
+               *ngIf="form.getValue('pgAutoscaleMode') !== 'on'">
+            <label class="cd-col-form-label required"
+                   for="pgNum"
+                   i18n>Placement groups</label>
+            <div class="cd-col-form-input">
               <input class="form-control"
                      id="pgNum"
                      name="pgNum"
                      (focus)="externalPgChange = false"
                      (blur)="alignPgs()"
                      required>
-              <span class="help-block"
+              <span class="invalid-feedback"
                     *ngIf="form.showError('pgNum', formDir, 'required')"
                     i18n>This field is required!</span>
-              <span class="help-block"
+              <span class="invalid-feedback"
                     *ngIf="form.showError('pgNum', formDir, 'min')"
                     i18n>At least one placement group is needed!</span>
-              <span class="help-block"
+              <span class="invalid-feedback"
                     *ngIf="form.showError('pgNum', formDir, '34')"
                     i18n>Your cluster can't handle this many PGs. Please recalculate the PG amount needed.</span>
-              <span class="help-block"
-                    *ngIf="form.showError('pgNum', formDir, 'noDecrease')"
-                    i18n>You can only increase the number of PGs of an existing pool.
-                Currently your pool has {{ data.pool.pg_num }} PGs.</span>
-              <span class="help-block">
-                <a i18n
-                   target="_blank"
-                   href="http://ceph.com/pgcalc">Calculation help</a>
+              <span class="form-text text-muted">
+                <cd-doc section="pgs"
+                        docText="Calculation help"
+                        i18n-docText></cd-doc>
               </span>
-              <span class="help-block"
+              <span class="form-text text-muted"
                     *ngIf="externalPgChange"
                     i18n>The current PGs settings were calculated for you, you
-                    should make sure the values suit your needs before submit.</span>
-            </div>
-          </div>
-
-          <!-- Crush ruleset selection -->
-          <div class="form-group"
-               [ngClass]="{'has-error': form.showError('crushRule', formDir)}"
-               *ngIf="form.getValue('poolType') && current.rules.length > 0">
-            <label class="control-label col-sm-3"
-                   for="crushRule"
-                   i18n>Crush ruleset</label>
-            <div class="col-sm-9">
-              <div class="input-group">
-                <select class="form-control"
-                        id="crushRule"
-                        formControlName="crushRule"
-                        name="crushSet">
-                  <option [ngValue]="null"
-                          i18n>-- Select a crush rule --</option>
-                  <option *ngFor="let rule of current.rules"
-                          [ngValue]="rule">
-                    {{ rule.rule_name }}
-                  </option>
-                </select>
-                <span class="input-group-btn">
-                  <button class="btn btn-default"
-                          [ngClass]="{'active': data.crushInfo}"
-                          id="crush-info-button"
-                          type="button"
-                          (click)="data.crushInfo = !data.crushInfo">
-                    <i class="fa fa-question-circle"
-                       aria-hidden="true"></i>
-                  </button>
-                </span>
-              </div>
-              <span class="help-block"
-                    id="crush-info-block"
-                    *ngIf="data.crushInfo && form.getValue('crushRule')">
-                <tabset>
-                  <tab i18n-heading heading="Crush rule" class="crush-rule-info">
-                    <cd-table-key-value [renderObjects]="true"
-                                        [data]="form.getValue('crushRule')"
-                                        [autoReload]="false">
-                    </cd-table-key-value>
-                  </tab>
-                  <tab i18n-heading heading="Crush steps" class="crush-rule-steps">
-                    <ol>
-                      <li *ngFor="let step of form.get('crushRule').value.steps">
-                        {{ describeCrushStep(step) }}
-                      </li>
-                    </ol>
-                  </tab>
-                </tabset>
-              </span>
-              <span class="help-block"
-                    *ngIf="form.showError('crushRule', formDir, 'tooFewOsds')"
-                    i18n>The rule can't be used in the current cluster as it has
-                to few OSDs to meet the minimum required OSD by this rule.</span>
+                should make sure the values suit your needs before submit.</span>
             </div>
           </div>
 
           <!-- Replica Size -->
-          <div class="form-group"
-               [ngClass]="{'has-error': form.showError('size', formDir)}"
-               *ngIf="form.getValue('poolType') === 'replicated'">
-            <label class="control-label col-sm-3"
-                   for="size">
-              <ng-container i18n>Replicated size</ng-container>
-              <span class="required"></span>
-            </label>
-            <div class="col-sm-9">
+          <div class="form-group row"
+               *ngIf="isReplicated">
+            <label class="cd-col-form-label required"
+                   for="size"
+                   i18n>Replicated size</label>
+            <div class="cd-col-form-input">
               <input class="form-control"
                      id="size"
                      [max]="getMaxSize()"
                      name="size"
                      type="number"
                      formControlName="size">
-              <span class="help-block"
+              <span class="invalid-feedback"
                     *ngIf="form.showError('size', formDir)">
                 <ul class="list-inline">
                   <li i18n>Minimum: {{ getMinSize() }}</li>
                   <li i18n>Maximum: {{ getMaxSize() }}</li>
                 </ul>
               </span>
-              <span class="help-block"
+              <span class="invalid-feedback"
                     *ngIf="form.showError('size', formDir)"
                     i18n>The size specified is out of range. A value from
-                    {{ getMinSize() }} to {{ getMaxSize() }} is valid.</span>
+                {{ getMinSize() }} to {{ getMaxSize() }} is usable.</span>
+              <span class="text-warning-dark"
+                    *ngIf="form.getValue('size') === 1"
+                    i18n>A size of 1 will not create a replication of the
+                object. The 'Replicated size' includes the object itself.</span>
+            </div>
+          </div>
+
+          <!-- Flags -->
+          <div class="form-group row"
+               *ngIf="info.is_all_bluestore && isErasure">
+            <label i18n
+                   class="cd-col-form-label">Flags</label>
+            <div class="cd-col-form-input">
+              <div class="custom-control custom-checkbox">
+                <input type="checkbox"
+                       class="custom-control-input"
+                       id="ec-overwrites"
+                       formControlName="ecOverwrites">
+                <label class="custom-control-label"
+                       for="ec-overwrites"
+                       i18n>EC Overwrites</label>
+              </div>
             </div>
           </div>
 
+        </div>
+        <!-- Applications -->
+        <div class="form-group row">
+          <label i18n
+                 class="cd-col-form-label"
+                 for="applications">Applications</label>
+          <div class="cd-col-form-input">
+            <cd-select-badges id="applications"
+                              [customBadges]="true"
+                              [customBadgeValidators]="data.applications.validators"
+                              [messages]="data.applications.messages"
+                              [data]="data.applications.selected"
+                              [options]="data.applications.available"
+                              [selectionLimit]="4"
+                              (selection)="appSelection()">
+            </cd-select-badges>
+            <i *ngIf="data.applications.selected <= 0"
+               i18n-title
+               title="Pools should be associated with an application tag"
+               class="{{icons.warning}} icon-warning-color">
+            </i>
+          </div>
+        </div>
+        <!-- CRUSH -->
+        <div *ngIf="isErasure || isReplicated">
+
+          <legend i18n>CRUSH</legend>
+
           <!-- Erasure Profile select -->
-          <div class="form-group"
-               *ngIf="form.getValue('poolType') === 'erasure'">
+          <div class="form-group row"
+               *ngIf="isErasure">
             <label i18n
-                   class="control-label col-sm-3"
+                   class="cd-col-form-label"
                    for="erasureProfile">Erasure code profile</label>
-            <div class="col-sm-9">
-              <div class="input-group">
-                <select class="form-control"
+            <div class="cd-col-form-input">
+              <div class="input-group mb-1">
+                <select class="form-select"
                         id="erasureProfile"
                         name="erasureProfile"
                         formControlName="erasureProfile">
                     {{ ecp.name }}
                   </option>
                 </select>
-                <span class="input-group-btn">
-                  <button class="btn btn-default"
-                          [ngClass]="{'active': data.erasureInfo}"
-                          id="ecp-info-button"
+                <button class="btn btn-light"
+                        [ngClass]="{'active': data.erasureInfo}"
+                        id="ecp-info-button"
+                        type="button"
+                        (click)="data.erasureInfo = !data.erasureInfo">
+                  <i [ngClass]="[icons.questionCircle]"
+                     aria-hidden="true"></i>
+                </button>
+                <button class="btn btn-light"
+                        type="button"
+                        *ngIf="!editing"
+                        (click)="addErasureCodeProfile()">
+                  <i [ngClass]="[icons.add]"
+                     aria-hidden="true"></i>
+                </button>
+                <button class="btn btn-light"
+                        type="button"
+                        *ngIf="!editing"
+                        ngbTooltip="This profile can't be deleted as it is in use."
+                        i18n-ngbTooltip
+                        triggers="manual"
+                        #ecpDeletionBtn="ngbTooltip"
+                        (click)="deleteErasureCodeProfile()">
+                  <i [ngClass]="[icons.trash]"
+                     aria-hidden="true"></i>
+                </button>
+              </div>
+              <span class="form-text text-muted"
+                    id="ecp-info-block"
+                    *ngIf="data.erasureInfo && form.getValue('erasureProfile')">
+                <nav ngbNav
+                     #ecpInfoTabs="ngbNav"
+                     class="nav-tabs">
+                  <ng-container ngbNavItem="ecp-info">
+                    <a ngbNavLink
+                       i18n>Profile</a>
+                    <ng-template ngbNavContent>
+                      <cd-table-key-value [renderObjects]="true"
+                                          [hideKeys]="['name']"
+                                          [data]="form.getValue('erasureProfile')"
+                                          [autoReload]="false">
+                      </cd-table-key-value>
+                    </ng-template>
+                  </ng-container>
+                  <ng-container ngbNavItem="used-by-pools">
+                    <a ngbNavLink
+                       i18n>Used by pools</a>
+                    <ng-template ngbNavContent>
+                      <ng-template #ecpIsNotUsed>
+                        <span i18n>Profile is not in use.</span>
+                      </ng-template>
+                      <ul *ngIf="ecpUsage; else ecpIsNotUsed">
+                        <li *ngFor="let pool of ecpUsage">
+                          {{ pool }}
+                        </li>
+                      </ul>
+                    </ng-template>
+                  </ng-container>
+                </nav>
+
+                <div [ngbNavOutlet]="ecpInfoTabs"></div>
+              </span>
+            </div>
+          </div>
+
+          <!-- Crush ruleset selection -->
+          <div class="form-group row"
+               *ngIf="isErasure && !editing">
+            <label class="cd-col-form-label"
+                   for="crushRule"
+                   i18n>Crush ruleset</label>
+            <div class="cd-col-form-input">
+              <span class="form-text text-muted"
+                    i18n>A new crush ruleset will be implicitly created.</span>
+            </div>
+          </div>
+          <div class="form-group row"
+               *ngIf="isReplicated || editing">
+            <label class="cd-col-form-label"
+                   for="crushRule"
+                   i18n>Crush ruleset</label>
+            <div class="cd-col-form-input">
+              <ng-template #noRules>
+                <span class="form-text text-muted">
+                  <span i18n>There are no rules.</span>&nbsp;
+                </span>
+              </ng-template>
+              <div *ngIf="current.rules.length > 0; else noRules">
+                <div class="input-group">
+                  <select class="form-select"
+                          id="crushRule"
+                          formControlName="crushRule"
+                          name="crushSet">
+                    <option [ngValue]="null"
+                            i18n>-- Select a crush rule --</option>
+                    <option *ngFor="let rule of current.rules"
+                            [ngValue]="rule">
+                      {{ rule.rule_name }}
+                    </option>
+                  </select>
+                  <button class="btn btn-light"
+                          [ngClass]="{'active': data.crushInfo}"
+                          id="crush-info-button"
                           type="button"
-                          (click)="data.erasureInfo = !data.erasureInfo">
-                    <i class="fa fa-question-circle"
+                          ngbTooltip="Placement and
+                          replication strategies or distribution policies that allow to
+                          specify how CRUSH places data replicas."
+                          i18n-ngbTooltip
+                          (click)="data.crushInfo = !data.crushInfo">
+                    <i [ngClass]="[icons.questionCircle]"
                        aria-hidden="true"></i>
                   </button>
-                  <button class="btn btn-default"
+                  <button class="btn btn-light"
                           type="button"
-                          [disabled]="editing"
-                          (click)="addErasureCodeProfile()">
-                    <i class="fa fa-plus"
+                          *ngIf="isReplicated && !editing"
+                          (click)="addCrushRule()">
+                    <i [ngClass]="[icons.add]"
                        aria-hidden="true"></i>
                   </button>
-                  <button class="btn btn-default"
+                  <button class="btn btn-light"
+                          *ngIf="isReplicated && !editing"
                           type="button"
-                          (click)="deleteErasureCodeProfile()"
-                          [disabled]="editing || ecProfiles.length < 1">
-                    <i class="fa fa-trash-o"
+                          ngbTooltip="This rule can't be deleted as it is in use."
+                          i18n-ngbTooltip
+                          triggers="manual"
+                          #crushDeletionBtn="ngbTooltip"
+                          (click)="deleteCrushRule()">
+                    <i [ngClass]="[icons.trash]"
                        aria-hidden="true"></i>
                   </button>
-                </span>
-              </div>
-              <span class="help-block"
-                    id="ecp-info-block"
-                    *ngIf="data.erasureInfo && form.getValue('erasureProfile')">
-                <cd-table-key-value [renderObjects]="true"
-                                    [data]="form.getValue('erasureProfile')"
-                                    [autoReload]="false">
-                </cd-table-key-value>
-              </span>
-            </div>
-          </div>
+                </div>
 
-          <!-- Flags -->
-          <div class="form-group"
-               *ngIf="info.is_all_bluestore && form.getValue('poolType') === 'erasure'">
-            <label i18n
-                   class="control-label col-sm-3">Flags</label>
-            <div class="col-sm-9">
-              <div class="input-group">
-                <div class="checkbox checkbox-primary">
-                  <input id="ec-overwrites"
-                         type="checkbox"
-                         formControlName="ecOverwrites">
-                  <label for="ec-overwrites"
-                         i18n>EC Overwrites</label>
+                <div class="form-text text-muted"
+                     id="crush-info-block"
+                     *ngIf="data.crushInfo && form.getValue('crushRule')">
+                  <nav ngbNav
+                       #crushInfoTabs="ngbNav"
+                       class="nav-tabs">
+                    <ng-container ngbNavItem="crush-rule-info">
+                      <a ngbNavLink
+                         i18n>Crush rule</a>
+                      <ng-template ngbNavContent>
+                        <cd-table-key-value [renderObjects]="false"
+                                            [hideKeys]="['steps', 'type', 'rule_name']"
+                                            [data]="form.getValue('crushRule')"
+                                            [autoReload]="false">
+                        </cd-table-key-value>
+                      </ng-template>
+                    </ng-container>
+                    <ng-container ngbNavItem="crush-rule-steps">
+                      <a ngbNavLink
+                         i18n>Crush steps</a>
+                      <ng-template ngbNavContent>
+                        <ol>
+                          <li *ngFor="let step of form.get('crushRule').value.steps">
+                            {{ describeCrushStep(step) }}
+                          </li>
+                        </ol>
+                      </ng-template>
+                    </ng-container>
+                    <ng-container ngbNavItem="used-by-pools">
+                      <a ngbNavLink
+                         i18n>Used by pools</a>
+                      <ng-template ngbNavContent>
+
+                        <ng-template #ruleIsNotUsed>
+                          <span i18n>Rule is not in use.</span>
+                        </ng-template>
+                        <ul *ngIf="crushUsage; else ruleIsNotUsed">
+                          <li *ngFor="let pool of crushUsage">
+                            {{ pool }}
+                          </li>
+                        </ul>
+                      </ng-template>
+                    </ng-container>
+                  </nav>
+
+                  <div [ngbNavOutlet]="crushInfoTabs"></div>
                 </div>
+                <span class="invalid-feedback"
+                      *ngIf="form.showError('crushRule', formDir, 'required')"
+                      i18n>This field is required!</span>
+                <span class="invalid-feedback"
+                      *ngIf="form.showError('crushRule', formDir, 'tooFewOsds')"
+                      i18n>The rule can't be used in the current cluster as it has
+                  too few OSDs to meet the minimum required OSD by this rule.</span>
               </div>
             </div>
           </div>
 
-        </div>
-        <!-- Applications -->
-        <div class="form-group">
-          <label i18n
-                 class="col-sm-3 control-label"
-                 for="applications">Applications</label>
-          <div class="col-sm-9">
-            <span class="form-control no-border full-height">
-              <cd-select-badges id="applications"
-                                [customBadges]="true"
-                                [customBadgeValidators]="data.applications.validators"
-                                [messages]="data.applications.messages"
-                                [data]="data.applications.selected"
-                                [options]="data.applications.available"
-                                [selectionLimit]="4"
-                                (selection)="appSelection()">
-              </cd-select-badges>
-            </span>
-          </div>
         </div>
 
-          <!-- Compression -->
-          <div *ngIf="info.is_all_bluestore"
-               formGroupName="compression">
-            <legend i18n>Compression</legend>
+        <!-- Compression -->
+        <div *ngIf="info.is_all_bluestore"
+             formGroupName="compression">
+          <legend i18n>Compression</legend>
 
-            <!-- Compression Mode -->
-            <div class="form-group">
+          <!-- Compression Mode -->
+          <div class="form-group row">
+            <label i18n
+                   class="cd-col-form-label"
+                   for="mode">Mode</label>
+            <div class="cd-col-form-input">
+              <select class="form-select"
+                      id="mode"
+                      name="mode"
+                      formControlName="mode">
+                <option *ngFor="let mode of info.compression_modes"
+                        [value]="mode">
+                  {{ mode }}
+                </option>
+              </select>
+            </div>
+          </div>
+          <div *ngIf="hasCompressionEnabled()">
+            <!-- Compression algorithm selection -->
+            <div class="form-group row">
               <label i18n
-                     class="control-label col-sm-3"
-                     for="mode">Mode</label>
-              <div class="col-sm-9">
-                <select class="form-control"
-                        id="mode"
-                        name="mode"
-                        formControlName="mode">
-                  <option *ngFor="let mode of info.compression_modes"
-                          [value]="mode">
-                    {{ mode }}
+                     class="cd-col-form-label"
+                     for="algorithm">Algorithm</label>
+              <div class="cd-col-form-input">
+                <select class="form-select"
+                        id="algorithm"
+                        name="algorithm"
+                        formControlName="algorithm">
+                  <option *ngIf="!info.compression_algorithms"
+                          ngValue=""
+                          i18n>Loading...</option>
+                  <option *ngIf="info.compression_algorithms && info.compression_algorithms.length === 0"
+                          i18n
+                          ngValue="">-- No erasure compression algorithm available --</option>
+                  <option *ngFor="let algorithm of info.compression_algorithms"
+                          [value]="algorithm">
+                    {{ algorithm }}
                   </option>
                 </select>
               </div>
             </div>
-            <div *ngIf="hasCompressionEnabled()">
-              <!-- Compression algorithm selection -->
-              <div class="form-group"
-                   [ngClass]="{'has-error': form.showError('algorithm', formDir)}">
-                <label i18n
-                       class="control-label col-sm-3"
-                       for="algorithm">Algorithm</label>
-                <div class="col-sm-9">
-                  <select class="form-control"
-                          id="algorithm"
-                          name="algorithm"
-                          formControlName="algorithm">
-                    <option *ngIf="!info.compression_algorithms"
-                            ngValue=""
-                            i18n>Loading...</option>
-                    <option *ngIf="info.compression_algorithms && info.compression_algorithms.length === 0"
-                            i18n
-                            ngValue="">-- No erasure compression algorithm available --</option>
-                    <option *ngFor="let algorithm of info.compression_algorithms"
-                            [value]="algorithm">
-                      {{ algorithm }}
-                    </option>
-                  </select>
-                </div>
-              </div>
 
-              <!-- Compression min blob size -->
-              <div class="form-group"
-                   [ngClass]="{'has-error': form.showError('minBlobSize', formDir)}">
-                <label i18n
-                       class="control-label col-sm-3"
-                       for="minBlobSize">Minimum blob size</label>
-                <div class="col-sm-9">
-                  <input id="minBlobSize"
-                         name="minBlobSize"
-                         formControlName="minBlobSize"
-                         type="text"
-                         min="0"
-                         class="form-control"
-                         i18n-placeholder
-                         placeholder="e.g., 128KiB"
-                         defaultUnit="KiB"
-                         cdDimlessBinary>
-                  <span class="help-block"
-                        *ngIf="form.showError('minBlobSize', formDir, 'min')"
-                        i18n>Value should be greater than 0</span>
-                  <span class="help-block"
-                        *ngIf="form.showError('minBlobSize', formDir, 'maximum')"
-                        i18n>Value should be greater than the maximum blob size</span>
-                </div>
+            <!-- Compression min blob size -->
+            <div class="form-group row">
+              <label i18n
+                     class="cd-col-form-label"
+                     for="minBlobSize">Minimum blob size</label>
+              <div class="cd-col-form-input">
+                <input id="minBlobSize"
+                       name="minBlobSize"
+                       formControlName="minBlobSize"
+                       type="text"
+                       min="0"
+                       class="form-control"
+                       i18n-placeholder
+                       placeholder="e.g., 128KiB"
+                       defaultUnit="KiB"
+                       cdDimlessBinary>
+                <span class="invalid-feedback"
+                      *ngIf="form.showError('minBlobSize', formDir, 'min')"
+                      i18n>Value should be greater than 0</span>
+                <span class="invalid-feedback"
+                      *ngIf="form.showError('minBlobSize', formDir, 'maximum')"
+                      i18n>Value should be less than the maximum blob size</span>
               </div>
+            </div>
 
-              <!-- Compression max blob size -->
-              <div class="form-group"
-                   [ngClass]="{'has-error': form.showError('maxBlobSize', formDir)}">
-                <label i18n
-                       class="control-label col-sm-3"
-                       for="maxBlobSize">Maximum blob size</label>
-                <div class="col-sm-9">
-                  <input id="maxBlobSize"
-                         type="text"
-                         min="0"
-                         formControlName="maxBlobSize"
-                         class="form-control"
-                         i18n-placeholder
-                         placeholder="e.g., 512KiB"
-                         defaultUnit="KiB"
-                         cdDimlessBinary>
-                  <span class="help-block"
-                        *ngIf="form.showError('maxBlobSize', formDir, 'min')"
-                        i18n>Value should be greater than 0</span>
-                  <span class="help-block"
-                        *ngIf="form.showError('maxBlobSize', formDir, 'minimum')"
-                        i18n>Value should be greater than the minimum blob size</span>
-                </div>
+            <!-- Compression max blob size -->
+            <div class="form-group row">
+              <label i18n
+                     class="cd-col-form-label"
+                     for="maxBlobSize">Maximum blob size</label>
+              <div class="cd-col-form-input">
+                <input id="maxBlobSize"
+                       type="text"
+                       min="0"
+                       formControlName="maxBlobSize"
+                       class="form-control"
+                       i18n-placeholder
+                       placeholder="e.g., 512KiB"
+                       defaultUnit="KiB"
+                       cdDimlessBinary>
+                <span class="invalid-feedback"
+                      *ngIf="form.showError('maxBlobSize', formDir, 'min')"
+                      i18n>Value should be greater than 0</span>
+                <span class="invalid-feedback"
+                      *ngIf="form.showError('maxBlobSize', formDir, 'minimum')"
+                      i18n>Value should be greater than the minimum blob size</span>
               </div>
+            </div>
 
-              <!-- Compression ratio -->
-              <div class="form-group"
-                   [ngClass]="{'has-error': form.showError('ratio', formDir)}">
-                <label i18n
-                       class="control-label col-sm-3"
-                       for="ratio">Ratio</label>
-                <div class="col-sm-9">
-                  <input id="ratio"
-                         name="ratio"
-                         formControlName="ratio"
-                         type="number"
-                         min="0"
-                         max="1"
-                         step="0.1"
-                         class="form-control"
-                         i18n-placeholder
-                         placeholder="Compression ratio">
-                  <span class="help-block"
-                        *ngIf="form.showError('ratio', formDir, 'min') || form.showError('ratio', formDir, 'max')"
-                        i18n>Value should be between 0.0 and 1.0</span>
-                </div>
+            <!-- Compression ratio -->
+            <div class="form-group row">
+              <label i18n
+                     class="cd-col-form-label"
+                     for="ratio">Ratio</label>
+              <div class="cd-col-form-input">
+                <input id="ratio"
+                       name="ratio"
+                       formControlName="ratio"
+                       type="number"
+                       min="0"
+                       max="1"
+                       step="0.1"
+                       class="form-control"
+                       i18n-placeholder
+                       placeholder="Compression ratio">
+                <span class="invalid-feedback"
+                      *ngIf="form.showError('ratio', formDir, 'min') || form.showError('ratio', formDir, 'max')"
+                      i18n>Value should be between 0.0 and 1.0</span>
               </div>
+            </div>
+
+          </div>
+        </div>
+
+        <!-- Quotas -->
+        <div>
+          <legend i18n>Quotas</legend>
 
+          <!-- Max Bytes -->
+          <div class="form-group row">
+            <label class="cd-col-form-label"
+                   for="max_bytes">
+              <ng-container i18n>Max bytes</ng-container>
+              <cd-helper>
+                <span i18n>Leave it blank or specify 0 to disable this quota.</span>
+                <br>
+                <span i18n>A valid quota should be greater than 0.</span>
+              </cd-helper>
+            </label>
+            <div class="cd-col-form-input">
+              <input class="form-control"
+                     id="max_bytes"
+                     name="max_bytes"
+                     type="text"
+                     formControlName="max_bytes"
+                     i18n-placeholder
+                     placeholder="e.g., 10GiB"
+                     defaultUnit="GiB"
+                     cdDimlessBinary>
             </div>
           </div>
 
+          <!-- Max Objects -->
+          <div class="form-group row">
+            <label class="cd-col-form-label"
+                   for="max_objects">
+              <ng-container i18n>Max objects</ng-container>
+              <cd-helper>
+                <span i18n>Leave it blank or specify 0 to disable this quota.</span>
+                <br>
+                <span i18n>A valid quota should be greater than 0.</span>
+              </cd-helper>
+            </label>
+            <div class="cd-col-form-input">
+              <input class="form-control"
+                     id="max_objects"
+                     min="0"
+                     name="max_objects"
+                     type="number"
+                     formControlName="max_objects">
+              <span class="invalid-feedback"
+                    *ngIf="form.showError('max_objects', formDir, 'min')"
+                    i18n>The value should be greater or equal to 0</span>
+            </div>
+          </div>
+        </div>
+
         <!-- Pool configuration -->
-        <div [hidden]="form.get('poolType').value !== 'replicated' || data.applications.selected.indexOf('rbd') === -1">
+        <div [hidden]="isErasure || data.applications.selected.indexOf('rbd') === -1">
           <cd-rbd-configuration-form [form]="form"
                                      [initializeData]="initializeConfigData"
                                      (changes)="currentConfigurationValues = $event()">
           </cd-rbd-configuration-form>
         </div>
-
-        <div class="form-group has-error">
-          <div class="col-sm-offset-3 col-sm-9"
-               *ngIf="form.hasError('rbdPool')">
-            <br>
-            <span class="help-block"
-                  i18n>It's not possible to create an RBD pool with '/' in the name.</span>
-            <span class="help-block"
-                  i18n>Please change the name or remove 'rbd' from the applications list.</span>
-          </div>
-        </div>
       </div>
-      <div class="panel-footer">
-        <div class="button-group text-right">
-          <cd-submit-button
-            [form]="formDir"
-            type="button"
-            i18n="form action button|Example: Create Pool@@formActionButton"
-            (submitAction)="submit()">{{ action | titlecase }} {{ resource | upperFirst }}</cd-submit-button>
-          <cd-back-button></cd-back-button>
-        </div>
+      <div class="card-footer">
+        <cd-form-button-panel (submitActionEvent)="submit()"
+                              [form]="form"
+                              [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"
+                              wrappingClass="text-right"></cd-form-button-panel>
       </div>
 
     </div>