-<cd-loading-panel *ngIf="!(info && ecProfiles)"
- i18n>Loading...</cd-loading-panel>
-
-<div class="cd-col-form">
+<div class="cd-col-form"
+ *cdFormLoading="loading">
<form name="form"
- *ngIf="info && ecProfiles"
#formDir="ngForm"
[formGroup]="form"
novalidate>
for="poolType"
i18n>Pool type</label>
<div class="cd-col-form-input">
- <select class="form-control custom-select"
+ <select class="form-select"
id="poolType"
formControlName="poolType"
name="poolType">
class="cd-col-form-label"
for="pgAutoscaleMode">PG Autoscale</label>
<div class="cd-col-form-input">
- <select class="form-control custom-select"
+ <select class="form-select"
id="pgAutoscaleMode"
name="pgAutoscaleMode"
formControlName="pgAutoscaleMode">
*ngIf="form.showError('pgNum', formDir, '34')"
i18n>Your cluster can't handle this many PGs. Please recalculate the PG amount needed.</span>
<span class="form-text text-muted">
- <a i18n
- target="_blank"
- href="http://ceph.com/pgcalc">Calculation help</a>
+ <cd-doc section="pgs"
+ docText="Calculation help"
+ i18n-docText></cd-doc>
</span>
<span class="form-text text-muted"
*ngIf="externalPgChange"
<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>
[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">
class="cd-col-form-label"
for="erasureProfile">Erasure code profile</label>
<div class="cd-col-form-input">
- <div class="input-group">
- <select class="form-control custom-select"
+ <div class="input-group mb-1">
+ <select class="form-select"
id="erasureProfile"
name="erasureProfile"
formControlName="erasureProfile">
{{ ecp.name }}
</option>
</select>
- <span class="input-group-append">
- <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"
- (click)="deleteErasureCodeProfile()">
- <i [ngClass]="[icons.trash]"
- aria-hidden="true"></i>
- </button>
- </span>
+ <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')">
- <cd-table-key-value [renderObjects]="true"
- [data]="form.getValue('erasureProfile')"
- [autoReload]="false">
- </cd-table-key-value>
+ <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>
</ng-template>
<div *ngIf="current.rules.length > 0; else noRules">
<div class="input-group">
- <select class="form-control custom-select"
+ <select class="form-select"
id="crushRule"
formControlName="crushRule"
name="crushSet">
{{ rule.rule_name }}
</option>
</select>
- <span class="input-group-append">
- <button class="btn btn-light"
- [ngClass]="{'active': data.crushInfo}"
- id="crush-info-button"
- type="button"
- (click)="data.crushInfo = !data.crushInfo">
- <i [ngClass]="[icons.questionCircle]"
- aria-hidden="true"></i>
- </button>
- <button class="btn btn-light"
- type="button"
- *ngIf="isReplicated && !editing"
- (click)="addCrushRule()">
- <i [ngClass]="[icons.add]"
- aria-hidden="true"></i>
- </button>
- <button class="btn btn-light"
- *ngIf="isReplicated && !editing"
- type="button"
- tooltip="This rule can't be deleted as it is in use."
- i18n-tooltip
- triggers=""
- #crushDeletionBtn="bs-tooltip"
- (click)="deleteCrushRule()">
- <i [ngClass]="[icons.trash]"
- aria-hidden="true"></i>
- </button>
- </span>
+ <button class="btn btn-light"
+ [ngClass]="{'active': data.crushInfo}"
+ id="crush-info-button"
+ type="button"
+ 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-light"
+ type="button"
+ *ngIf="isReplicated && !editing"
+ (click)="addCrushRule()">
+ <i [ngClass]="[icons.add]"
+ aria-hidden="true"></i>
+ </button>
+ <button class="btn btn-light"
+ *ngIf="isReplicated && !editing"
+ type="button"
+ 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>
</div>
- <span class="form-text text-muted"
- id="crush-info-block"
- *ngIf="data.crushInfo && form.getValue('crushRule')">
- <tabset #crushInfoTabs>
- <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>
- <tab i18n-heading
- heading="Used by pools"
- class="used-by-pools">
- <ng-template #ruleIsNotUsed>
- <span i18n>Rule is not in use.</span>
+
+ <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>
- <ul *ngIf="crushUsage; else ruleIsNotUsed">
- <li *ngFor="let pool of crushUsage">
- {{ pool }}
- </li>
- </ul>
- </tab>
- </tabset>
- </span>
+ </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>
class="cd-col-form-label"
for="mode">Mode</label>
<div class="cd-col-form-input">
- <select class="form-control custom-select"
+ <select class="form-select"
id="mode"
name="mode"
formControlName="mode">
class="cd-col-form-label"
for="algorithm">Algorithm</label>
<div class="cd-col-form-input">
- <select class="form-control custom-select"
+ <select class="form-select"
id="algorithm"
name="algorithm"
formControlName="algorithm">
</div>
</div>
<div class="card-footer">
- <div class="button-group text-right">
- <cd-submit-button [form]="formDir"
- i18n="form action button|Example: Create Pool@@formActionButton"
- (submitAction)="submit()">{{ action | titlecase }} {{ resource | upperFirst }}
- </cd-submit-button>
- <cd-back-button></cd-back-button>
- </div>
+ <cd-form-button-panel (submitActionEvent)="submit()"
+ [form]="form"
+ [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"
+ wrappingClass="text-right"></cd-form-button-panel>
</div>
</div>