bfg-common 1.3.357 → 1.3.359

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/components/atoms/TheIcon2.vue +0 -173
  2. package/components/atoms/collapse/CollapseNavItem.vue +3 -8
  3. package/components/atoms/collapse/lib/models/interfaces.ts +0 -1
  4. package/components/atoms/switch/Switch.vue +111 -111
  5. package/components/atoms/table/compact/Compact.vue +526 -526
  6. package/components/common/adapterManager/ui/table/Header.vue +93 -93
  7. package/components/common/adapterManager/ui/table/Table.vue +162 -162
  8. package/components/common/context/Context.vue +99 -99
  9. package/components/common/lib/config/states.ts +126 -126
  10. package/components/common/resource/progressBlock/ProgressBlock.vue +142 -142
  11. package/components/common/vm/actions/add/Add.vue +602 -602
  12. package/components/common/vm/actions/clone/Clone.vue +518 -518
  13. package/components/common/vm/actions/common/customizeHardware/CustomizeHardware.vue +273 -273
  14. package/components/common/vm/actions/common/customizeHardware/virtualHardware/VirtualHardware.vue +697 -697
  15. package/components/common/vm/actions/common/customizeHardware/virtualHardware/cdDvdDrive/CdDvdDrive.vue +256 -254
  16. package/components/common/vm/actions/common/customizeHardware/virtualHardware/cpu/Cpu.vue +1 -1
  17. package/components/common/vm/actions/common/customizeHardware/virtualHardware/memory/Memory.vue +1 -1
  18. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newHardDisk/NewHardDisk.vue +458 -454
  19. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newNetwork/NewNetwork.vue +304 -299
  20. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newPciDevice/NewPciDevice.vue +167 -165
  21. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newUsbController/NewUsbController.vue +82 -78
  22. package/components/common/vm/actions/common/customizeHardware/virtualHardware/other/Other.vue +32 -27
  23. package/components/common/vm/actions/common/customizeHardware/virtualHardware/reservation/Reservation.vue +2 -3
  24. package/components/common/vm/actions/common/customizeHardware/virtualHardware/videoCard/VideoCard.vue +182 -178
  25. package/components/common/vm/actions/common/select/computeResource/ComputeResource.vue +164 -164
  26. package/components/common/vm/actions/common/select/name/Name.vue +235 -235
  27. package/components/common/vm/actions/common/select/storage/Storage.vue +198 -198
  28. package/components/common/vm/actions/editSettings/EditSettings.vue +316 -313
  29. package/components/common/weekSelect/lib/config/options.ts +10 -10
  30. package/components/common/wizards/datastore/add/Add.vue +436 -436
  31. package/components/common/wizards/datastore/add/lib/config/stepItems.ts +191 -191
  32. package/components/common/wizards/datastore/add/lib/models/interfaces.ts +38 -38
  33. package/components/common/wizards/datastore/add/lib/utils.ts +70 -70
  34. package/components/common/wizards/datastore/add/local/createName/CreateName.vue +160 -160
  35. package/components/common/wizards/datastore/add/nfs/Nfs.vue +112 -112
  36. package/components/common/wizards/datastore/add/nfs/accessibility/Accessibility.vue +104 -104
  37. package/components/common/wizards/datastore/add/nfs/accessibility/tablesView/lib/config/compatibleTable.ts +62 -62
  38. package/components/common/wizards/datastore/add/nfs/accessibility/tablesView/lib/config/hostsTableItems.ts +53 -53
  39. package/components/common/wizards/datastore/add/readyComplete/ReadyComplete.vue +86 -86
  40. package/components/common/wizards/datastore/add/readyComplete/lib/config/propertiesDetails.ts +184 -184
  41. package/components/common/wizards/datastore/add/sharedStorm/deviceSelection/DeviceSelection.vue +459 -459
  42. package/components/common/wizards/datastore/add/types/Types.vue +80 -80
  43. package/components/common/wizards/datastore/add/types/lib/config/typeOptions.ts +49 -49
  44. package/composables/useAppVersion.ts +21 -21
  45. package/composables/useEnvLanguage.ts +20 -20
  46. package/lib/utils/sendTask.ts +72 -72
  47. package/minify.js +146 -146
  48. package/package.json +1 -1
  49. package/public/spice-console/application/codec.js +257 -257
  50. package/public/spice-console/lib/rasterEngine.js +907 -907
  51. package/public/spice-console/network/spicechannel.js +369 -369
  52. package/store/main/lib/interfaces.ts +9 -9
  53. package/store/tasks/actions.ts +133 -133
  54. package/store/tasks/getters.ts +25 -25
  55. package/store/tasks/lib/models/interfaces.ts +18 -18
  56. package/store/tasks/mutations.ts +58 -58
  57. package/store/tasks/state.ts +16 -16
@@ -1,454 +1,458 @@
1
- <template>
2
- <div class="hardware-hard-disk">
3
- <atoms-stack-block
4
- :has-children="true"
5
- :is-roll-back="props.type === 'removed'"
6
- :removable="isRemovable"
7
- @remove="onRemoveHardDisk"
8
- @roll-back="onRollBackHardDisk"
9
- >
10
- <template #stackBlockKey>
11
- <div class="vm-wizard-toggle-block-hard-disk flex-align-center">
12
- <span>{{ label }}</span>
13
- <atoms-the-icon
14
- v-show="hardDiskInvalid"
15
- width="24px"
16
- height="24px"
17
- class="is-error tooltip-trigger"
18
- name="info"
19
- />
20
- </div>
21
- </template>
22
- <template #stackBlockContent>
23
- <div class="flex-align-center flex-space-between mr-1">
24
- <template v-if="props.type === 'removed'">
25
- <span>{{ localization.deviceWillRemoved }}</span>
26
- <div @click.stop class="flex-align-center">
27
- <input
28
- :id="`hard-disk-delete-files-from-datastore-${props.index}`"
29
- v-model="deleteFilesFromDatastore"
30
- :data-id="`hard-disk-delete-files-from-datastore-${props.index}`"
31
- :disabled="props.disabled"
32
- type="checkbox"
33
- class="pointer"
34
- @click.stop
35
- />
36
- <label
37
- :for="`hard-disk-delete-files-from-datastore-${props.index}`"
38
- :class="[
39
- 'label-text-normal pointer',
40
- props.disabled && 'div-disabled',
41
- ]"
42
- >{{ localization.deleteFilesFromDatastore }}</label
43
- >
44
- </div>
45
- </template>
46
- <template v-else>
47
- <atoms-tooltip-error
48
- :has-error="!!hardDiskLocalAndApiErrorsTexts"
49
- :selector="`#vm-wizard-hard-disk-${props.index}`"
50
- @remove="
51
- emits(
52
- 'remove-error-by-title',
53
- `disk_devices[${props.index}].size`
54
- )
55
- "
56
- >
57
- <template #elem>
58
- <input
59
- :id="`vm-wizard-hard-disk-${props.index}`"
60
- v-model.number="size"
61
- :data-id="`vm-wizard-hard-disk-${props.index}`"
62
- type="number"
63
- :class="[
64
- 'spinfree edit-setting-input input-text-color',
65
- { 'has-error': hardDiskTypeErrorLocalText },
66
- ]"
67
- :disabled="isDisabledSize"
68
- @blur="onValidateSize"
69
- @click.stop
70
- />
71
- </template>
72
- <template #content>
73
- {{ hardDiskLocalAndApiErrorsTexts }}
74
- </template>
75
- </atoms-tooltip-error>
76
- <div class="select ml-1">
77
- <select
78
- :id="`hard-disk-type-select-${props.index}`"
79
- v-model="hardDiskType"
80
- :data-id="`hard-disk-type-select-${props.index}`"
81
- :disabled="props.type === 'exist'"
82
- class="input-text-color"
83
- @change="onValidateSize"
84
- @click.stop
85
- >
86
- <option
87
- v-for="(item, key) in hardDiskTypeOptions"
88
- :key="key"
89
- :value="item.value"
90
- >
91
- {{ item.text }}
92
- </option>
93
- </select>
94
- </div>
95
- </template>
96
- </div>
97
- </template>
98
- <template #stackChildren>
99
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-maximum-size
100
- :free-mb="maxHardDisk"
101
- />
102
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-location
103
- v-if="props.type === 'new'"
104
- :location="location"
105
- :index="props.index"
106
- :error-validation-fields="props.errorValidationFields"
107
- :get-datastore-table-func="props.getDatastoreTableFunc"
108
- :datastore="props.datastore"
109
- @change="onChangeStorage"
110
- @remove-error-by-title="emits('remove-error-by-title', $event)"
111
- />
112
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-file
113
- v-else
114
- :path="props.hardDisk?.source"
115
- />
116
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-provisioning
117
- v-model:disk-provisioning="diskProvisioning"
118
- :index="props.index"
119
- :error-validation-fields="props.errorValidationFields"
120
- :disabled="(props.isEdit && props.type !== 'new') || isRunning"
121
- @remove-error-by-title="emits('remove-error-by-title', $event)"
122
- />
123
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-sharing
124
- v-model:sharing="sharing"
125
- :index="props.index"
126
- :error-validation-fields="props.errorValidationFields"
127
- :disabled="isRunning && props.type !== 'new'"
128
- @remove-error-by-title="emits('remove-error-by-title', $event)"
129
- />
130
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-limit-iops
131
- v-model:limit-iops="limitIops"
132
- v-model:limit-iops-type="limitIopsType"
133
- :index="props.index"
134
- :error-validation-fields="props.errorValidationFields"
135
- @valid="limitIopsInvalid = $event"
136
- @remove-error-by-title="emits('remove-error-by-title', $event)"
137
- />
138
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-mode
139
- v-model:diskMode="diskMode"
140
- :index="props.index"
141
- :error-validation-fields="props.errorValidationFields"
142
- :disabled="isRunning && props.type !== 'new'"
143
- @remove-error-by-title="emits('remove-error-by-title', $event)"
144
- />
145
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-cache
146
- v-model:cache="cache"
147
- :index="props.index"
148
- :error-validation-fields="props.errorValidationFields"
149
- :disabled="(props.isEdit && props.type !== 'new') || isRunning"
150
- @remove-error-by-title="emits('remove-error-by-title', $event)"
151
- />
152
- <common-vm-actions-common-customize-hardware-virtual-hardware-bus
153
- v-model:bus="bus"
154
- :index="props.index"
155
- :error-validation-fields="props.errorValidationFields"
156
- :disabled="isRunning"
157
- :help-text="localization.busVirtioHelpDesc"
158
- component-type="disk_devices"
159
- @remove-error-by-title="emits('remove-error-by-title', $event)"
160
- />
161
- </template>
162
- </atoms-stack-block>
163
- </div>
164
- </template>
165
-
166
- <script setup lang="ts">
167
- import { UI_I_DatastoreTableItem } from '~/lib/models/store/storage/interfaces'
168
- import {
169
- UI_I_SendDataNewHardDisk,
170
- UI_I_SendDataNewHardDiskStorage,
171
- } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/models/interfaces'
172
- import { UI_T_HardDiskType } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/models/types'
173
- import { UI_I_Localization } from '~/lib/models/interfaces'
174
- import { UI_I_ErrorValidationField } from '~/lib/models/store/interfaces'
175
- import { UI_I_TablePayload } from '~/lib/models/table/interfaces'
176
- import { UI_I_OptionItem } from '~/components/atoms/lib/models/interfaces'
177
- import { binaryOptionsFunc } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/config/binaryOptions'
178
-
179
- const props = defineProps<{
180
- storage: UI_I_DatastoreTableItem | null
181
- hardDisk: UI_I_SendDataNewHardDisk
182
- type: UI_T_HardDiskType
183
- index: number
184
- errorValidationFields: UI_I_ErrorValidationField<string>[]
185
- getDatastoreTableFunc: (payload: UI_I_TablePayload) => Promise<void>
186
- datastore: UI_I_DatastoreTableItem[]
187
- isEdit: boolean
188
- state?: string | number
189
- }>()
190
- const emits = defineEmits<{
191
- (event: 'send-data', value: UI_I_SendDataNewHardDisk): void
192
- (event: 'invalid', value: boolean): void
193
- (event: 'remove'): void
194
- (event: 'roll-back'): void
195
- (event: 'remove-error-by-title', value: string): void
196
- }>()
197
-
198
- const localization = computed<UI_I_Localization>(() => useLocal())
199
-
200
- const isRunning = computed<boolean>(() => {
201
- return props.state === 2
202
- })
203
-
204
- const isRemovable = computed<boolean>(() => {
205
- return (
206
- !props.isEdit ||
207
- !isRunning.value ||
208
- (isRunning.value && bus.value === 'virtio')
209
- )
210
- })
211
-
212
- const typeError = computed<string>(() => `disk_devices[${props.index}].size`)
213
-
214
- const apiErrorLocal = computed<string>(() => {
215
- return (
216
- props.errorValidationFields?.find(
217
- (message) => message.field === typeError.value
218
- )?.error_message || ''
219
- )
220
- })
221
-
222
- const { $binary } = useNuxtApp()
223
-
224
- const label = computed<string>(() => {
225
- if (props.type === 'edit' || props.type === 'removed')
226
- return `${localization.value.hardDisk} ${props.index + 1}`
227
-
228
- return `${localization.value.newHardDisk} *`
229
- })
230
-
231
- const maxHardDisk = computed<number>(() => {
232
- if (props.type === 'exist') return $binary.gbToMb(props.hardDisk.size)
233
-
234
- if (!storage.value) return 0
235
-
236
- if (props.isEdit)
237
- return storage.value.capacity.free_mb + $binary.gbToMb(props.hardDisk.size)
238
-
239
- return storage.value.capacity.free_mb
240
- })
241
- const hardDiskTypeErrorLocalText = computed<string>(() => {
242
- if (+sizeInMb.value > maxHardDisk.value) {
243
- return localization.value.diskCapacitySpecifiedGreater
244
- }
245
-
246
- if (+sizeInMb.value <= 0) {
247
- return localization.value.diskCapacityCannotZero
248
- }
249
-
250
- return ''
251
- })
252
-
253
- const hardDiskLocalAndApiErrorsTexts = computed<string>(() => {
254
- const localError = hardDiskTypeErrorLocalText.value
255
- const apiError = apiErrorLocal.value
256
-
257
- let result = ''
258
- if (localError && !apiError) result = localError
259
- if (!localError && apiError) result = apiError
260
- if (localError && apiError) result = localError + ', ' + apiError
261
- if (!localError && apiError) result = apiError
262
-
263
- return result
264
- })
265
-
266
- const deleteFilesFromDatastore = ref<boolean>(false)
267
-
268
- const hardDiskType = ref<string>('gb')
269
- const hardDiskTypeOptions = ref<UI_I_OptionItem[]>(
270
- binaryOptionsFunc(localization.value)
271
- )
272
-
273
- const size = ref<number>(props.hardDisk.size) // Изначально всегда приходит в gb
274
- const sizeInMb = computed<number>(() => {
275
- if (hardDiskType.value === 'mb') {
276
- return size.value
277
- }
278
-
279
- return $binary.universalFromTo(size.value, hardDiskType.value, 'mb')
280
- })
281
- const onValidateSize = (): void => {
282
- if (props.type !== 'edit') return
283
- if (sizeInMb.value < $binary.gbToMb(props.hardDisk.size)) {
284
- size.value = $binary.universalFromTo(
285
- props.hardDisk.size,
286
- 'gb',
287
- hardDiskType.value
288
- )
289
- }
290
- }
291
-
292
- const isDisabledSize = computed<boolean>(() => {
293
- return props.type === 'exist' || (props.type === 'edit' && readOnly.value)
294
- })
295
-
296
- const onRemoveHardDisk = (): void => {
297
- emits('remove')
298
- }
299
- const onRollBackHardDisk = (): void => {
300
- emits('roll-back')
301
- }
302
-
303
- const location = ref<UI_I_DatastoreTableItem | null>(props.storage || null)
304
- watch(
305
- () => props.storage,
306
- (newValue) => {
307
- location.value = newValue
308
- storage.value = newValue
309
- }
310
- )
311
- const storage = ref<UI_I_DatastoreTableItem | null>(props.storage)
312
- const onChangeStorage = (item: UI_I_DatastoreTableItem): void => {
313
- storage.value = item
314
- }
315
-
316
- const diskProvisioning = ref<string>('thick')
317
- const sharing = ref<string>('')
318
- const shares = ref<number>(0)
319
-
320
- const limitIops = ref<number>(16)
321
- const limitIopsType = ref<string>('unlimited')
322
- const limitIopsInvalid = ref<boolean>(false)
323
-
324
- const diskMode = ref<string>('dependent')
325
-
326
- const cache = ref<string>('none')
327
-
328
- const bus = ref<string>('ide')
329
-
330
- const readOnly = ref<boolean>(false)
331
-
332
- const discard = ref<string>('')
333
-
334
- const io = ref<string>('')
335
-
336
- const target = ref<string>('')
337
-
338
- watch(
339
- [
340
- sizeInMb,
341
- storage,
342
- diskProvisioning,
343
- sharing,
344
- limitIops,
345
- limitIopsType,
346
- diskMode,
347
- cache,
348
- bus,
349
- deleteFilesFromDatastore,
350
- () => props.type,
351
- ],
352
- () => {
353
- const limitIopsLocal =
354
- limitIopsType.value === 'unlimited' ? 0 : limitIops.value
355
- const storageLocal: UI_I_SendDataNewHardDiskStorage = {
356
- id: storage.value?.id || '',
357
- name: storage.value?.name || '',
358
- type: storage.value?.type_text || '',
359
- pool: storage.value?.pool_name || '',
360
- user: '',
361
- endpoint: '',
362
- password: '',
363
- protocol: '',
364
- options: '',
365
- }
366
-
367
- emits('send-data', {
368
- create: props.hardDisk.create,
369
- attach: props.hardDisk.attach,
370
- source: props.hardDisk.source,
371
- size: Math.ceil(sizeInMb.value),
372
- bus: bus.value,
373
- target: target.value,
374
- storage: storageLocal,
375
- device_type: 'disk',
376
- provision_type: diskProvisioning.value,
377
- disk_mode: diskMode.value,
378
- sharing: sharing.value === 'yes',
379
- read_only: readOnly.value,
380
- shares: shares.value,
381
- cache: cache.value,
382
- io: io.value,
383
- limit_iops: limitIopsLocal,
384
- discard: discard.value,
385
- capacity: Math.ceil(sizeInMb.value),
386
- boot_order: props.hardDisk.boot_order,
387
- detach: props.type === 'removed',
388
- remove: props.type === 'removed' && deleteFilesFromDatastore.value, // Проверяем если удалили диск и указали Delete files from datastore
389
- })
390
- },
391
- { immediate: true }
392
- )
393
-
394
- // Добавляем данные для редактирования
395
- watch(
396
- () => props.hardDisk,
397
- (newValue) => {
398
- // TODO refactoring
399
- if (props.type === 'exist') {
400
- diskProvisioning.value = newValue.provision_type || 'thick'
401
- return
402
- }
403
- if (props.type !== 'edit') return
404
-
405
- diskProvisioning.value = newValue.provision_type || 'thick'
406
- sharing.value = newValue.sharing ? 'yes' : ''
407
- shares.value = newValue.shares || 0
408
- // limitIopsType.value = newValue.io === 'native' ? 'unlimited' : 'custom'
409
- limitIopsType.value = newValue.limit_iops === 0 ? 'unlimited' : 'custom'
410
- limitIops.value = newValue.limit_iops || 16
411
- diskMode.value = newValue.disk_mode || 'dependent'
412
- cache.value = newValue.cache || 'none'
413
- bus.value = newValue.bus || 'ide'
414
- readOnly.value = newValue.read_only || false
415
- discard.value = newValue.discard || ''
416
- io.value = newValue.io || ''
417
- target.value = newValue.target || ''
418
- },
419
- { immediate: true }
420
- )
421
- watch(
422
- isRunning,
423
- (newValue) => {
424
- if (newValue && props.type === 'new') {
425
- bus.value = 'virtio'
426
- }
427
- },
428
- { immediate: true }
429
- )
430
-
431
- const hardDiskInvalid = computed<boolean>(() => {
432
- return !!hardDiskTypeErrorLocalText.value || limitIopsInvalid.value
433
- })
434
- watch(
435
- hardDiskInvalid,
436
- (newValue) => {
437
- emits('invalid', newValue)
438
- },
439
- { immediate: true }
440
- )
441
- </script>
442
-
443
- <style scoped lang="scss">
444
- .hardware-cpu {
445
- #hard-disk {
446
- width: 100px;
447
- -webkit-appearance: none;
448
-
449
- &.has-error {
450
- border: 1px solid #db2100;
451
- }
452
- }
453
- }
454
- </style>
1
+ <template>
2
+ <div class="hardware-hard-disk">
3
+ <atoms-stack-block
4
+ :has-children="true"
5
+ :is-roll-back="props.type === 'removed'"
6
+ :removable="isRemovable"
7
+ @remove="onRemoveHardDisk"
8
+ @roll-back="onRollBackHardDisk"
9
+ >
10
+ <template #stackBlockKey>
11
+ <div
12
+ :id="`vm-wizard-toggle-block-hard-disk-${props.index}`"
13
+ :data-id="`vm-wizard-toggle-block-hard-disk-${props.index}`"
14
+ class="vm-wizard-toggle-block-hard-disk flex-align-center"
15
+ >
16
+ <span>{{ label }}</span>
17
+ <atoms-the-icon
18
+ v-show="hardDiskInvalid"
19
+ width="24px"
20
+ height="24px"
21
+ class="is-error tooltip-trigger"
22
+ name="info"
23
+ />
24
+ </div>
25
+ </template>
26
+ <template #stackBlockContent>
27
+ <div class="flex-align-center flex-space-between mr-1">
28
+ <template v-if="props.type === 'removed'">
29
+ <span>{{ localization.deviceWillRemoved }}</span>
30
+ <div @click.stop class="flex-align-center">
31
+ <input
32
+ :id="`hard-disk-delete-files-from-datastore-${props.index}`"
33
+ v-model="deleteFilesFromDatastore"
34
+ :data-id="`hard-disk-delete-files-from-datastore-${props.index}`"
35
+ :disabled="props.disabled"
36
+ type="checkbox"
37
+ class="pointer"
38
+ @click.stop
39
+ />
40
+ <label
41
+ :for="`hard-disk-delete-files-from-datastore-${props.index}`"
42
+ :class="[
43
+ 'label-text-normal pointer',
44
+ props.disabled && 'div-disabled',
45
+ ]"
46
+ >{{ localization.deleteFilesFromDatastore }}</label
47
+ >
48
+ </div>
49
+ </template>
50
+ <template v-else>
51
+ <atoms-tooltip-error
52
+ :has-error="!!hardDiskLocalAndApiErrorsTexts"
53
+ :selector="`#vm-wizard-hard-disk-${props.index}`"
54
+ @remove="
55
+ emits(
56
+ 'remove-error-by-title',
57
+ `disk_devices[${props.index}].size`
58
+ )
59
+ "
60
+ >
61
+ <template #elem>
62
+ <input
63
+ :id="`vm-wizard-hard-disk-${props.index}`"
64
+ v-model.number="size"
65
+ :data-id="`vm-wizard-hard-disk-${props.index}`"
66
+ type="number"
67
+ :class="[
68
+ 'spinfree edit-setting-input input-text-color',
69
+ { 'has-error': hardDiskTypeErrorLocalText },
70
+ ]"
71
+ :disabled="isDisabledSize"
72
+ @blur="onValidateSize"
73
+ @click.stop
74
+ />
75
+ </template>
76
+ <template #content>
77
+ {{ hardDiskLocalAndApiErrorsTexts }}
78
+ </template>
79
+ </atoms-tooltip-error>
80
+ <div class="select ml-1">
81
+ <select
82
+ :id="`hard-disk-type-select-${props.index}`"
83
+ v-model="hardDiskType"
84
+ :data-id="`hard-disk-type-select-${props.index}`"
85
+ :disabled="props.type === 'exist'"
86
+ class="input-text-color"
87
+ @change="onValidateSize"
88
+ @click.stop
89
+ >
90
+ <option
91
+ v-for="(item, key) in hardDiskTypeOptions"
92
+ :key="key"
93
+ :value="item.value"
94
+ >
95
+ {{ item.text }}
96
+ </option>
97
+ </select>
98
+ </div>
99
+ </template>
100
+ </div>
101
+ </template>
102
+ <template #stackChildren>
103
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-maximum-size
104
+ :free-mb="maxHardDisk"
105
+ />
106
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-location
107
+ v-if="props.type === 'new'"
108
+ :location="location"
109
+ :index="props.index"
110
+ :error-validation-fields="props.errorValidationFields"
111
+ :get-datastore-table-func="props.getDatastoreTableFunc"
112
+ :datastore="props.datastore"
113
+ @change="onChangeStorage"
114
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
115
+ />
116
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-file
117
+ v-else
118
+ :path="props.hardDisk?.source"
119
+ />
120
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-provisioning
121
+ v-model:disk-provisioning="diskProvisioning"
122
+ :index="props.index"
123
+ :error-validation-fields="props.errorValidationFields"
124
+ :disabled="(props.isEdit && props.type !== 'new') || isRunning"
125
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
126
+ />
127
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-sharing
128
+ v-model:sharing="sharing"
129
+ :index="props.index"
130
+ :error-validation-fields="props.errorValidationFields"
131
+ :disabled="isRunning && props.type !== 'new'"
132
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
133
+ />
134
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-limit-iops
135
+ v-model:limit-iops="limitIops"
136
+ v-model:limit-iops-type="limitIopsType"
137
+ :index="props.index"
138
+ :error-validation-fields="props.errorValidationFields"
139
+ @valid="limitIopsInvalid = $event"
140
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
141
+ />
142
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-mode
143
+ v-model:diskMode="diskMode"
144
+ :index="props.index"
145
+ :error-validation-fields="props.errorValidationFields"
146
+ :disabled="isRunning && props.type !== 'new'"
147
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
148
+ />
149
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-cache
150
+ v-model:cache="cache"
151
+ :index="props.index"
152
+ :error-validation-fields="props.errorValidationFields"
153
+ :disabled="(props.isEdit && props.type !== 'new') || isRunning"
154
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
155
+ />
156
+ <common-vm-actions-common-customize-hardware-virtual-hardware-bus
157
+ v-model:bus="bus"
158
+ :index="props.index"
159
+ :error-validation-fields="props.errorValidationFields"
160
+ :disabled="isRunning"
161
+ :help-text="localization.busVirtioHelpDesc"
162
+ component-type="disk_devices"
163
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
164
+ />
165
+ </template>
166
+ </atoms-stack-block>
167
+ </div>
168
+ </template>
169
+
170
+ <script setup lang="ts">
171
+ import { UI_I_DatastoreTableItem } from '~/lib/models/store/storage/interfaces'
172
+ import {
173
+ UI_I_SendDataNewHardDisk,
174
+ UI_I_SendDataNewHardDiskStorage,
175
+ } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/models/interfaces'
176
+ import { UI_T_HardDiskType } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/models/types'
177
+ import { UI_I_Localization } from '~/lib/models/interfaces'
178
+ import { UI_I_ErrorValidationField } from '~/lib/models/store/interfaces'
179
+ import { UI_I_TablePayload } from '~/lib/models/table/interfaces'
180
+ import { UI_I_OptionItem } from '~/components/atoms/lib/models/interfaces'
181
+ import { binaryOptionsFunc } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/config/binaryOptions'
182
+
183
+ const props = defineProps<{
184
+ storage: UI_I_DatastoreTableItem | null
185
+ hardDisk: UI_I_SendDataNewHardDisk
186
+ type: UI_T_HardDiskType
187
+ index: number
188
+ errorValidationFields: UI_I_ErrorValidationField<string>[]
189
+ getDatastoreTableFunc: (payload: UI_I_TablePayload) => Promise<void>
190
+ datastore: UI_I_DatastoreTableItem[]
191
+ isEdit: boolean
192
+ state?: string | number
193
+ }>()
194
+ const emits = defineEmits<{
195
+ (event: 'send-data', value: UI_I_SendDataNewHardDisk): void
196
+ (event: 'invalid', value: boolean): void
197
+ (event: 'remove'): void
198
+ (event: 'roll-back'): void
199
+ (event: 'remove-error-by-title', value: string): void
200
+ }>()
201
+
202
+ const localization = computed<UI_I_Localization>(() => useLocal())
203
+
204
+ const isRunning = computed<boolean>(() => {
205
+ return props.state === 2
206
+ })
207
+
208
+ const isRemovable = computed<boolean>(() => {
209
+ return (
210
+ !props.isEdit ||
211
+ !isRunning.value ||
212
+ (isRunning.value && bus.value === 'virtio')
213
+ )
214
+ })
215
+
216
+ const typeError = computed<string>(() => `disk_devices[${props.index}].size`)
217
+
218
+ const apiErrorLocal = computed<string>(() => {
219
+ return (
220
+ props.errorValidationFields?.find(
221
+ (message) => message.field === typeError.value
222
+ )?.error_message || ''
223
+ )
224
+ })
225
+
226
+ const { $binary } = useNuxtApp()
227
+
228
+ const label = computed<string>(() => {
229
+ if (props.type === 'edit' || props.type === 'removed')
230
+ return `${localization.value.hardDisk} ${props.index + 1}`
231
+
232
+ return `${localization.value.newHardDisk} *`
233
+ })
234
+
235
+ const maxHardDisk = computed<number>(() => {
236
+ if (props.type === 'exist') return $binary.gbToMb(props.hardDisk.size)
237
+
238
+ if (!storage.value) return 0
239
+
240
+ if (props.isEdit)
241
+ return storage.value.capacity.free_mb + $binary.gbToMb(props.hardDisk.size)
242
+
243
+ return storage.value.capacity.free_mb
244
+ })
245
+ const hardDiskTypeErrorLocalText = computed<string>(() => {
246
+ if (+sizeInMb.value > maxHardDisk.value) {
247
+ return localization.value.diskCapacitySpecifiedGreater
248
+ }
249
+
250
+ if (+sizeInMb.value <= 0) {
251
+ return localization.value.diskCapacityCannotZero
252
+ }
253
+
254
+ return ''
255
+ })
256
+
257
+ const hardDiskLocalAndApiErrorsTexts = computed<string>(() => {
258
+ const localError = hardDiskTypeErrorLocalText.value
259
+ const apiError = apiErrorLocal.value
260
+
261
+ let result = ''
262
+ if (localError && !apiError) result = localError
263
+ if (!localError && apiError) result = apiError
264
+ if (localError && apiError) result = localError + ', ' + apiError
265
+ if (!localError && apiError) result = apiError
266
+
267
+ return result
268
+ })
269
+
270
+ const deleteFilesFromDatastore = ref<boolean>(false)
271
+
272
+ const hardDiskType = ref<string>('gb')
273
+ const hardDiskTypeOptions = ref<UI_I_OptionItem[]>(
274
+ binaryOptionsFunc(localization.value)
275
+ )
276
+
277
+ const size = ref<number>(props.hardDisk.size) // Изначально всегда приходит в gb
278
+ const sizeInMb = computed<number>(() => {
279
+ if (hardDiskType.value === 'mb') {
280
+ return size.value
281
+ }
282
+
283
+ return $binary.universalFromTo(size.value, hardDiskType.value, 'mb')
284
+ })
285
+ const onValidateSize = (): void => {
286
+ if (props.type !== 'edit') return
287
+ if (sizeInMb.value < $binary.gbToMb(props.hardDisk.size)) {
288
+ size.value = $binary.universalFromTo(
289
+ props.hardDisk.size,
290
+ 'gb',
291
+ hardDiskType.value
292
+ )
293
+ }
294
+ }
295
+
296
+ const isDisabledSize = computed<boolean>(() => {
297
+ return props.type === 'exist' || (props.type === 'edit' && readOnly.value)
298
+ })
299
+
300
+ const onRemoveHardDisk = (): void => {
301
+ emits('remove')
302
+ }
303
+ const onRollBackHardDisk = (): void => {
304
+ emits('roll-back')
305
+ }
306
+
307
+ const location = ref<UI_I_DatastoreTableItem | null>(props.storage || null)
308
+ watch(
309
+ () => props.storage,
310
+ (newValue) => {
311
+ location.value = newValue
312
+ storage.value = newValue
313
+ }
314
+ )
315
+ const storage = ref<UI_I_DatastoreTableItem | null>(props.storage)
316
+ const onChangeStorage = (item: UI_I_DatastoreTableItem): void => {
317
+ storage.value = item
318
+ }
319
+
320
+ const diskProvisioning = ref<string>('thick')
321
+ const sharing = ref<string>('')
322
+ const shares = ref<number>(0)
323
+
324
+ const limitIops = ref<number>(16)
325
+ const limitIopsType = ref<string>('unlimited')
326
+ const limitIopsInvalid = ref<boolean>(false)
327
+
328
+ const diskMode = ref<string>('dependent')
329
+
330
+ const cache = ref<string>('none')
331
+
332
+ const bus = ref<string>('ide')
333
+
334
+ const readOnly = ref<boolean>(false)
335
+
336
+ const discard = ref<string>('')
337
+
338
+ const io = ref<string>('')
339
+
340
+ const target = ref<string>('')
341
+
342
+ watch(
343
+ [
344
+ sizeInMb,
345
+ storage,
346
+ diskProvisioning,
347
+ sharing,
348
+ limitIops,
349
+ limitIopsType,
350
+ diskMode,
351
+ cache,
352
+ bus,
353
+ deleteFilesFromDatastore,
354
+ () => props.type,
355
+ ],
356
+ () => {
357
+ const limitIopsLocal =
358
+ limitIopsType.value === 'unlimited' ? 0 : limitIops.value
359
+ const storageLocal: UI_I_SendDataNewHardDiskStorage = {
360
+ id: storage.value?.id || '',
361
+ name: storage.value?.name || '',
362
+ type: storage.value?.type_text || '',
363
+ pool: storage.value?.pool_name || '',
364
+ user: '',
365
+ endpoint: '',
366
+ password: '',
367
+ protocol: '',
368
+ options: '',
369
+ }
370
+
371
+ emits('send-data', {
372
+ create: props.hardDisk.create,
373
+ attach: props.hardDisk.attach,
374
+ source: props.hardDisk.source,
375
+ size: Math.ceil(sizeInMb.value),
376
+ bus: bus.value,
377
+ target: target.value,
378
+ storage: storageLocal,
379
+ device_type: 'disk',
380
+ provision_type: diskProvisioning.value,
381
+ disk_mode: diskMode.value,
382
+ sharing: sharing.value === 'yes',
383
+ read_only: readOnly.value,
384
+ shares: shares.value,
385
+ cache: cache.value,
386
+ io: io.value,
387
+ limit_iops: limitIopsLocal,
388
+ discard: discard.value,
389
+ capacity: Math.ceil(sizeInMb.value),
390
+ boot_order: props.hardDisk.boot_order,
391
+ detach: props.type === 'removed',
392
+ remove: props.type === 'removed' && deleteFilesFromDatastore.value, // Проверяем если удалили диск и указали Delete files from datastore
393
+ })
394
+ },
395
+ { immediate: true }
396
+ )
397
+
398
+ // Добавляем данные для редактирования
399
+ watch(
400
+ () => props.hardDisk,
401
+ (newValue) => {
402
+ // TODO refactoring
403
+ if (props.type === 'exist') {
404
+ diskProvisioning.value = newValue.provision_type || 'thick'
405
+ return
406
+ }
407
+ if (props.type !== 'edit') return
408
+
409
+ diskProvisioning.value = newValue.provision_type || 'thick'
410
+ sharing.value = newValue.sharing ? 'yes' : ''
411
+ shares.value = newValue.shares || 0
412
+ // limitIopsType.value = newValue.io === 'native' ? 'unlimited' : 'custom'
413
+ limitIopsType.value = newValue.limit_iops === 0 ? 'unlimited' : 'custom'
414
+ limitIops.value = newValue.limit_iops || 16
415
+ diskMode.value = newValue.disk_mode || 'dependent'
416
+ cache.value = newValue.cache || 'none'
417
+ bus.value = newValue.bus || 'ide'
418
+ readOnly.value = newValue.read_only || false
419
+ discard.value = newValue.discard || ''
420
+ io.value = newValue.io || ''
421
+ target.value = newValue.target || ''
422
+ },
423
+ { immediate: true }
424
+ )
425
+ watch(
426
+ isRunning,
427
+ (newValue) => {
428
+ if (newValue && props.type === 'new') {
429
+ bus.value = 'virtio'
430
+ }
431
+ },
432
+ { immediate: true }
433
+ )
434
+
435
+ const hardDiskInvalid = computed<boolean>(() => {
436
+ return !!hardDiskTypeErrorLocalText.value || limitIopsInvalid.value
437
+ })
438
+ watch(
439
+ hardDiskInvalid,
440
+ (newValue) => {
441
+ emits('invalid', newValue)
442
+ },
443
+ { immediate: true }
444
+ )
445
+ </script>
446
+
447
+ <style scoped lang="scss">
448
+ .hardware-cpu {
449
+ #hard-disk {
450
+ width: 100px;
451
+ -webkit-appearance: none;
452
+
453
+ &.has-error {
454
+ border: 1px solid #db2100;
455
+ }
456
+ }
457
+ }
458
+ </style>