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