]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/blob
4200fd73622e3467a548830bbfcf25c17676093e
[ceph-ci.git] /
1 <form
2   [formGroup]="nsForm"
3   novalidate>
4   <div cdsGrid
5        [useCssGrid]="true"
6        [narrow]="true"
7        [fullWidth]="true">
8
9     <div cdsCol
10          [columnNumbers]="{sm: 4, md: 8}">
11       <div cdsRow
12            class="form-heading form-item">
13         <h3>{{ action | titlecase }} {{ resource | titlecase }}</h3>
14         <cd-help-text [formAllFieldsRequired]="true">
15           <span i18n>
16             Namespaces define the storage volumes that subsystems present to hosts.
17           </span>
18         </cd-help-text>
19       </div>
20
21       <!-- Namespace Count (Create only) -->
22       @if (!edit) {
23       <div cdsRow
24            class="form-item">
25         <div cdsCol>
26           <cds-number
27             formControlName="nsCount"
28             cdValidate
29             #nsCountRef="cdValidate"
30             label="Number of namespaces"
31             helperText="No. of namespaces to generate. Value must be between 1 and 5."
32             [min]="MIN_NAMESPACE_CREATE"
33             [max]="MAX_NAMESPACE_CREATE"
34             [invalid]="nsCountRef.isInvalid"
35             [invalidText]="nsCountError"
36             i18n-label
37             i18n-helperText>
38           </cds-number>
39           <ng-template #nsCountError>
40             <ng-container *ngTemplateOutlet="validationErrors; context: { control: nsForm.get('nsCount') }"></ng-container>
41           </ng-template>
42         </div>
43       </div>
44       }
45
46       <!-- Namespace Size (sent as block_size) -->
47       @if (!edit) {
48       <div cdsRow
49            class="form-item">
50         <div cdsCol>
51           <cds-number
52             formControlName="namespace_size"
53             cdOptionalField="Namespace size (GiB)"
54             label="Namespace size (GiB)"
55             helperText="Specify the size to expose to hosts. Leave blank for full device/file."
56             placeholder="e.g. 100"
57             [min]="0"
58             [invalid]="nsForm.controls['namespace_size'].invalid && (nsForm.controls['namespace_size'].dirty || nsForm.controls['namespace_size'].touched)"
59             invalidText="Value must be greater than or equal to 0."
60             i18n-label
61             i18n-helperText
62             i18n-invalidText>
63           </cds-number>
64         </div>
65       </div>
66       }
67
68       <!-- Host Access (drives no_auto_visible in create request) -->
69       @if (!edit) {
70       <div cdsRow
71            class="form-item">
72         <div cdsCol>
73           <legend
74             class="cds--type-label-01"
75             i18n>Host access (Initiators)</legend>
76           <div class="form-item">
77             <cds-radio-group
78               formControlName="host_access"
79               orientation="horizontal">
80               <cds-radio
81                 value="all"
82                 [checked]="true">
83                 <div>
84                   <span
85                     class="cds--type-body-compact-01"
86                     i18n>All hosts on the subsystem</span>
87                   <span
88                     class="cds--type-helper-text-01 cds-ml-1 d-block"
89                     i18n>Allow all hosts associated with the selected subsystem to access the namespace.</span>
90                 </div>
91               </cds-radio>
92               <cds-radio value="specific">
93                 <div>
94                   <span
95                     class="cds--type-body-compact-01"
96                     i18n>Select specific hosts</span>
97                   <span
98                     class="cds--type-helper-text-01 cds-ml-1 d-block"
99                     i18n>Only the selected hosts will be able to access this namespace.</span>
100                 </div>
101               </cds-radio>
102             </cds-radio-group>
103           </div>
104         </div>
105       </div>
106       }
107
108       <!-- Host Selection (Visible only when 'specific' is selected) -->
109       @if (!edit && nsForm.getValue('host_access') === 'specific') {
110       <div cdsRow
111            class="form-item">
112         <div cdsCol>
113           <div class="form-item">
114             <cds-combo-box
115               type="multi"
116               selectionFeedback="top-after-reopen"
117               label="Select hosts"
118               i18n-label
119               placeholder="Select one or more hosts"
120               [appendInline]="true"
121               [items]="initiatorCandidates"
122               (selected)="onInitiatorSelection($event)"
123               [invalid]="nsForm.controls['initiators'].invalid && (nsForm.controls['initiators'].dirty || nsForm.controls['initiators'].touched)"
124               invalidText="This field is required."
125               i18n-invalidText
126               i18n-placeholder>
127               <cds-dropdown-list></cds-dropdown-list>
128             </cds-combo-box>
129           </div>
130         </div>
131       </div>
132       }
133
134       <!-- Subsystem -->
135       <div cdsRow
136            class="form-item">
137         <div cdsCol>
138           @if (edit) {
139           <cds-text-label
140             label="Subsystem"
141             i18n-label>
142             <input cdsText
143                    readonly
144                    [value]="nsForm.get('subsystem').value" />
145           </cds-text-label>
146           } @else {
147           <cds-select
148             formControlName="subsystem"
149             cdValidate
150             #subsystemRef="cdValidate"
151             label="Select subsystem"
152             [invalid]="subsystemRef.isInvalid"
153             invalidText="This field is required."
154             i18n-label
155             i18n-invalidText>
156             @if (subsystems === undefined) {
157             <option
158               [ngValue]="null"
159               disabled>Loading...</option>
160             }
161             @if (subsystems && subsystems.length === 0) {
162             <option
163               [ngValue]="null"
164               disabled>-- No subsystems available --</option>
165             }
166             @if (subsystems && subsystems.length > 0) {
167             <option
168               value=""
169               selected>Select a subsystem</option>
170             }
171             @for (subsystem of subsystems; track subsystem.nqn) {
172             <option
173               [value]="subsystem.nqn">{{ subsystem.nqn }}</option>
174             }
175           </cds-select>
176           }
177         </div>
178       </div>
179
180       <!-- Pool -->
181       <div cdsRow
182            class="form-item">
183         <div cdsCol>
184           @if (edit) {
185           <cds-text-label
186             label="RBD image pool"
187             i18n-label>
188             <input cdsText
189                    readonly
190                    [value]="nsForm.get('pool').value" />
191           </cds-text-label>
192           } @else {
193           <cds-select
194             formControlName="pool"
195             cdValidate
196             #poolRef="cdValidate"
197             label="RBD image pool"
198             [invalid]="poolRef.isInvalid"
199             invalidText="This field is required."
200             helperText="Pool where the backing Ceph block device resides."
201             i18n-label
202             i18n-invalidText
203             i18n-helperText>
204             @if (rbdPools === null) {
205             <option
206               [ngValue]="null"
207               disabled>Loading...</option>
208             }
209             @if (rbdPools && rbdPools.length === 0) {
210             <option
211               [ngValue]="null"
212               disabled>-- No block pools available --</option>
213             }
214             @if (rbdPools && rbdPools.length > 0) {
215             <option
216               value=""
217               selected>Select a RBD image pool</option>
218             }
219             @for (pool of rbdPools; track pool.pool_name) {
220             <option
221               [value]="pool.pool_name">{{ pool.pool_name }}</option>
222             }
223           </cds-select>
224           }
225         </div>
226       </div>
227
228       <!-- RBD image creation (drives create_image flag in request) -->
229       @if (!edit) {
230       <div cdsRow
231            class="form-item">
232         <div cdsCol>
233           <legend
234             class="cds--type-label-01"
235             i18n>RBD image creation</legend>
236           <cds-radio-group
237             formControlName="rbd_image_creation"
238             orientation="vertical">
239             <cds-radio
240               value="gateway_provisioned"
241               i18n>Gateway-provisioned image</cds-radio>
242             <cds-radio
243               value="externally_managed"
244               [disabled]="nsForm.getValue('nsCount') > 1"
245               [title]="nsForm.getValue('nsCount') > 1 ? 'Unavailable during bulk creation. RBD images are created automatically.' : ''"
246               i18n>Externally managed image</cds-radio>
247           </cds-radio-group>
248         </div>
249       </div>
250       }
251
252       <!-- Image Name (Visible when 'gateway_provisioned' and nsCount > 1) -->
253       @if (!edit && nsForm.getValue('rbd_image_creation') === 'gateway_provisioned' && nsForm.getValue('nsCount') > 1) {
254       <div cdsRow
255            class="form-item">
256         <div cdsCol>
257           <cd-alert-panel
258             type="info"
259             [dismissible]="false"
260             [showTitle]="false">
261             <strong i18n>For bulk namespace creation, RBD images are provisioned automatically.</strong>
262           </cd-alert-panel>
263
264           <div class="form-item"
265                cdOptionalField="Image name">
266             <label
267               class="cds--type-label-01 cds--label"
268               i18n>Image name</label>
269             <cds-text-label
270               helperText="Provide a name for the images. For bulk creation, this will be used as the base prefix with numeric suffixes (e.g., img-1, img-2). Leave blank to auto-generate."
271               [invalid]="rbdImageNameRef.isInvalid"
272               [invalidText]="rbdImageNameError"
273               i18n-helperText>
274               <input cdsText
275                      placeholder="Enter a name"
276                      formControlName="rbd_image_name"
277                      cdValidate
278                      #rbdImageNameRef="cdValidate"
279                      [invalid]="rbdImageNameRef.isInvalid" />
280             </cds-text-label>
281             <ng-template #rbdImageNameError>
282               <ng-container *ngTemplateOutlet="validationErrors; context: { control: nsForm.get('rbd_image_name') }"></ng-container>
283             </ng-template>
284           </div>
285         </div>
286       </div>
287       }
288
289       <!-- Image Selection (Visible only when 'externally_managed' is selected) -->
290       @if (!edit && nsForm.getValue('rbd_image_creation') === 'externally_managed') {
291       <div cdsRow
292            class="form-item">
293         <div cdsCol>
294           <cds-select
295             formControlName="rbd_image_name"
296             cdValidate
297             #rbdImageSelectRef="cdValidate"
298             label="RBD Image"
299             [invalid]="rbdImageSelectRef.isInvalid"
300             invalidText="This field is required."
301             helperText="Select an existing RBD image from the pool to expose as a namespace."
302             i18n-label
303             i18n-invalidText
304             i18n-helperText>
305             <option
306               [ngValue]="null"
307               disabled
308               selected>Select an image</option>
309             @for (img of rbdImages; track img.name) {
310             <option
311               [value]="img.name">{{ img.name }} ({{ img.size | dimlessBinary }})</option>
312             }
313           </cds-select>
314         </div>
315       </div>
316       }
317
318       <!-- Image Size -->
319       @if (!edit && nsForm.getValue('rbd_image_creation') !== 'externally_managed') {
320       <div cdsRow
321            class="form-item">
322         <div
323           cdsCol
324           [columnNumbers]="{md: 4}">
325           <cds-text-label
326             helperText="The size of the namespace image."
327             [invalid]="imageSizeRef.isInvalid"
328             [invalidText]="sizeError"
329             i18n-helperText>
330             Image Size
331             <input cdsText
332                    type="text"
333                    placeholder="e.g. 100 GiB"
334                    id="image_size"
335                    formControlName="image_size"
336                    cdValidate
337                    #imageSizeRef="cdValidate"
338                    [invalid]="imageSizeRef.isInvalid"
339                    defaultUnit="GiB"
340                    [min]="0"
341                    cdDimlessBinary>
342           </cds-text-label>
343           <ng-template #sizeError>
344             <ng-container *ngTemplateOutlet="validationErrors; context: { control: nsForm.get('image_size') }"></ng-container>
345           </ng-template>
346         </div>
347       </div>
348       }
349
350       <div cdsRow>
351         <cd-form-button-panel
352           (submitActionEvent)="onSubmit()"
353           [form]="nsForm"
354           [submitText]="(action | titlecase) + ' ' + (resource | titlecase)"
355           wrappingClass="text-right form-button">
356         </cd-form-button-panel>
357       </div>
358
359     </div>
360   </div>
361 </form>
362
363 <ng-template #validationErrors
364              let-control="control">
365   @if (control.errors) {
366   @for (err of control.errors | keyvalue; track err.key) {
367   <span class="invalid-feedback">{{ INVALID_TEXTS[err.key] }}</span>
368   }
369   }
370 </ng-template>