bfg-common 1.3.372 → 1.3.374

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 (59) hide show
  1. package/assets/scss/common/icons/icons-4.scss +3 -3
  2. package/components/atoms/loader/Loader.vue +30 -30
  3. package/components/atoms/loader/PreLoader.vue +38 -38
  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/shortcuts/lib/models/interfaces.ts +1 -1
  12. package/components/common/vm/actions/add/Add.vue +602 -602
  13. package/components/common/vm/actions/clone/Clone.vue +518 -518
  14. package/components/common/vm/actions/common/customizeHardware/CustomizeHardware.vue +273 -273
  15. package/components/common/vm/actions/common/customizeHardware/virtualHardware/VirtualHardware.vue +697 -697
  16. package/components/common/vm/actions/common/customizeHardware/virtualHardware/cdDvdDrive/CdDvdDrive.vue +256 -256
  17. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newHardDisk/NewHardDisk.vue +458 -458
  18. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newNetwork/NewNetwork.vue +304 -304
  19. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newPciDevice/NewPciDevice.vue +167 -167
  20. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newUsbController/NewUsbController.vue +82 -82
  21. package/components/common/vm/actions/common/customizeHardware/virtualHardware/other/Other.vue +32 -32
  22. package/components/common/vm/actions/common/customizeHardware/virtualHardware/videoCard/VideoCard.vue +182 -182
  23. package/components/common/vm/actions/common/select/computeResource/ComputeResource.vue +164 -164
  24. package/components/common/vm/actions/common/select/name/Name.vue +235 -235
  25. package/components/common/vm/actions/common/select/storage/Storage.vue +198 -198
  26. package/components/common/vm/actions/editSettings/EditSettings.vue +316 -316
  27. package/components/common/weekSelect/lib/config/options.ts +10 -10
  28. package/components/common/wizards/datastore/add/Add.vue +436 -436
  29. package/components/common/wizards/datastore/add/lib/config/stepItems.ts +191 -191
  30. package/components/common/wizards/datastore/add/lib/models/interfaces.ts +38 -38
  31. package/components/common/wizards/datastore/add/lib/utils.ts +70 -70
  32. package/components/common/wizards/datastore/add/local/createName/CreateName.vue +160 -160
  33. package/components/common/wizards/datastore/add/nfs/Nfs.vue +112 -112
  34. package/components/common/wizards/datastore/add/nfs/accessibility/Accessibility.vue +104 -104
  35. package/components/common/wizards/datastore/add/nfs/accessibility/tablesView/lib/config/compatibleTable.ts +62 -62
  36. package/components/common/wizards/datastore/add/nfs/accessibility/tablesView/lib/config/hostsTableItems.ts +53 -53
  37. package/components/common/wizards/datastore/add/readyComplete/ReadyComplete.vue +86 -86
  38. package/components/common/wizards/datastore/add/readyComplete/lib/config/propertiesDetails.ts +184 -184
  39. package/components/common/wizards/datastore/add/sharedStorm/deviceSelection/DeviceSelection.vue +459 -459
  40. package/components/common/wizards/datastore/add/types/Types.vue +80 -80
  41. package/components/common/wizards/datastore/add/types/lib/config/typeOptions.ts +49 -49
  42. package/composables/useAppVersion.ts +21 -21
  43. package/composables/useEnvLanguage.ts +20 -20
  44. package/lib/utils/sendTask.ts +72 -72
  45. package/minify.js +146 -146
  46. package/package.json +1 -1
  47. package/plugins/spice-console/spice.console.ts +147 -18
  48. package/public/spice-console/application/codec.js +257 -257
  49. package/public/spice-console/application/inputmanager.js +1 -1
  50. package/public/spice-console/lib/rasterEngine.js +907 -907
  51. package/public/spice-console/network/spicechannel.js +375 -369
  52. package/public/spice-console/process/mainprocess.js +2 -2
  53. package/public/spice-console-minify/run.min.js +1 -1
  54. package/store/main/lib/interfaces.ts +9 -9
  55. package/store/tasks/actions.ts +133 -133
  56. package/store/tasks/getters.ts +25 -25
  57. package/store/tasks/lib/models/interfaces.ts +18 -18
  58. package/store/tasks/mutations.ts +58 -58
  59. package/store/tasks/state.ts +16 -16
@@ -1,458 +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
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>
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>