summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html
diff options
context:
space:
mode:
Diffstat (limited to 'src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html')
-rw-r--r--src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html609
1 files changed, 609 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html
new file mode 100644
index 000000000..07a26f9eb
--- /dev/null
+++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html
@@ -0,0 +1,609 @@
+<div class="cd-col-form"
+ *cdFormLoading="loading">
+ <form name="form"
+ #formDir="ngForm"
+ [formGroup]="form"
+ novalidate>
+ <div class="card">
+ <div i18n="form title|Example: Create Pool@@formTitle"
+ class="card-header">{{ action | titlecase }} {{ resource | upperFirst }}</div>
+
+ <div class="card-body">
+ <!-- Name -->
+ <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"
+ class="form-control"
+ placeholder="Name..."
+ i18n-placeholder
+ formControlName="name"
+ autofocus>
+ <span class="invalid-feedback"
+ *ngIf="form.showError('name', formDir, 'required')"
+ i18n>This field is required!</span>
+ <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 row">
+ <label class="cd-col-form-label required"
+ for="poolType"
+ i18n>Pool type</label>
+ <div class="cd-col-form-input">
+ <select class="form-control"
+ id="poolType"
+ formControlName="poolType"
+ name="poolType">
+ <option ngValue=""
+ i18n>-- Select a pool type --</option>
+ <option *ngFor="let poolType of data.poolTypes"
+ [value]="poolType">
+ {{ poolType }}
+ </option>
+ </select>
+ <span class="invalid-feedback"
+ *ngIf="form.showError('poolType', formDir, 'required')"
+ i18n>This field is required!</span>
+ </div>
+ </div>
+
+ <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-control"
+ 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 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"
+ formControlName="pgNum"
+ min="1"
+ type="number"
+ (focus)="externalPgChange = false"
+ (blur)="alignPgs()"
+ required>
+ <span class="invalid-feedback"
+ *ngIf="form.showError('pgNum', formDir, 'required')"
+ i18n>This field is required!</span>
+ <span class="invalid-feedback"
+ *ngIf="form.showError('pgNum', formDir, 'min')"
+ i18n>At least one placement group is needed!</span>
+ <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="form-text text-muted">
+ <cd-doc section="pgs"
+ docText="Calculation help"
+ i18n-docText></cd-doc>
+ </span>
+ <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>
+
+ <!-- Replica Size -->
+ <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()"
+ [min]="getMinSize()"
+ name="size"
+ type="number"
+ formControlName="size">
+ <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="invalid-feedback"
+ *ngIf="form.showError('size', formDir)"
+ i18n>The size specified is out of range. A value from
+ {{ 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>
+ </div>
+ </div>
+
+ <!-- CRUSH -->
+ <div *ngIf="isErasure || isReplicated">
+
+ <legend i18n>CRUSH</legend>
+
+ <!-- Erasure Profile select -->
+ <div class="form-group row"
+ *ngIf="isErasure">
+ <label i18n
+ 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"
+ id="erasureProfile"
+ name="erasureProfile"
+ formControlName="erasureProfile">
+ <option *ngIf="!ecProfiles"
+ ngValue=""
+ i18n>Loading...</option>
+ <option *ngIf="ecProfiles && ecProfiles.length === 0"
+ [ngValue]="null"
+ i18n>-- No erasure code profile available --</option>
+ <option *ngIf="ecProfiles && ecProfiles.length > 0"
+ [ngValue]="null"
+ i18n>-- Select an erasure code profile --</option>
+ <option *ngFor="let ecp of ecProfiles"
+ [ngValue]="ecp">
+ {{ 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"
+ 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>
+ </span>
+ </div>
+ <span class="form-text text-muted"
+ id="ecp-info-block"
+ *ngIf="data.erasureInfo && form.getValue('erasureProfile')">
+ <ul ngbNav
+ #ecpInfoTabs="ngbNav"
+ class="nav-tabs">
+ <li 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>
+ </li>
+ <li 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>
+ </li>
+ </ul>
+
+ <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-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-append">
+ <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>
+ </span>
+ </div>
+
+ <div class="form-text text-muted"
+ id="crush-info-block"
+ *ngIf="data.crushInfo && form.getValue('crushRule')">
+ <ul ngbNav
+ #crushInfoTabs="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem="crush-rule-info">
+ <a ngbNavLink
+ i18n>Crush rule</a>
+ <ng-template ngbNavContent>
+ <cd-table-key-value [renderObjects]="false"
+ [hideKeys]="['steps', 'ruleset', 'type', 'rule_name']"
+ [data]="form.getValue('crushRule')"
+ [autoReload]="false">
+ </cd-table-key-value>
+ </ng-template>
+ </li>
+ <li 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>
+ </li>
+ <li 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>
+ </li>
+ </ul>
+
+ <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>
+
+ <!-- Compression -->
+ <div *ngIf="info.is_all_bluestore"
+ formGroupName="compression">
+ <legend i18n>Compression</legend>
+
+ <!-- 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-control"
+ 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="cd-col-form-label"
+ for="algorithm">Algorithm</label>
+ <div class="cd-col-form-input">
+ <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 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 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 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]="isErasure || data.applications.selected.indexOf('rbd') === -1">
+ <cd-rbd-configuration-form [form]="form"
+ [initializeData]="initializeConfigData"
+ (changes)="currentConfigurationValues = $event()">
+ </cd-rbd-configuration-form>
+ </div>
+ </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>
+
+ </form>
+</div>