bfg-common 1.3.370 → 1.3.372

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