@xen-orchestra/backups 0.73.3 → 0.73.5

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.
@@ -16,6 +16,44 @@ import { toQcow2Stream } from '@xen-orchestra/qcow2'
16
16
 
17
17
  const ensureArray = value => (value === undefined ? [] : Array.isArray(value) ? value : [value])
18
18
 
19
+ const orderedMemoryLimits = ['memory_static_min', 'memory_dynamic_min', 'memory_dynamic_max', 'memory_static_max']
20
+
21
+ // The dynamic memory range MUST respect this inequality at any moment: static_min <= dynamic_min <= dynamic_max <= static_max.
22
+ // We must update these properties in the right order to avoid XAPI error.
23
+ // The order depends on the values. It can be an increase, a decrease or a mix of both, so any order could be required.
24
+ export async function updateMemoryFields(xapi, targetVm, vmRecord) {
25
+ const memoryValues = {}
26
+ for (const key of orderedMemoryLimits) {
27
+ memoryValues[key] = {
28
+ currentValue: targetVm[key],
29
+ newValue: vmRecord[key] ?? targetVm[key],
30
+ }
31
+ }
32
+
33
+ while (await updateNextMemoryField(xapi, memoryValues, targetVm.$ref)) {
34
+ /* execute until all memory fields are updated */
35
+ }
36
+ }
37
+
38
+ // Update one more memory field if needed, then return a boolean describing if a field was updated or if all fields are up to date
39
+ async function updateNextMemoryField(xapi, memoryValues, vmRef) {
40
+ for (let i = 0; i < orderedMemoryLimits.length; i++) {
41
+ const currentField = memoryValues[orderedMemoryLimits[i]]
42
+ const nextField = i === orderedMemoryLimits.length - 1 ? undefined : memoryValues[orderedMemoryLimits[i + 1]]
43
+ if (
44
+ currentField.newValue !== currentField.currentValue &&
45
+ (nextField === undefined || currentField.newValue <= nextField.currentValue)
46
+ ) {
47
+ // no need to check that previousField.currentValue <= currentField.newValue, as we can deduce it
48
+ await xapi.setField('VM', vmRef, orderedMemoryLimits[i], currentField.newValue)
49
+ await xapi.barrier()
50
+ currentField.currentValue = currentField.newValue
51
+ return true
52
+ }
53
+ }
54
+ return false
55
+ }
56
+
19
57
  export async function exportIncrementalVm(
20
58
  vm,
21
59
  baseVdis = {},
@@ -160,6 +198,9 @@ export const importIncrementalVm = defer(async function importIncrementalVm(
160
198
  let vmRef
161
199
  if (isUpdate) {
162
200
  vmRef = targetRef
201
+
202
+ await updateMemoryFields(xapi, targetVm, vmRecord)
203
+
163
204
  await Promise.all([
164
205
  xapi.setFields('VM', vmRef, {
165
206
  actions_after_crash: vmRecord.actions_after_crash,
@@ -171,10 +212,6 @@ export const importIncrementalVm = defer(async function importIncrementalVm(
171
212
  HVM_boot_params: vmRecord.HVM_boot_params,
172
213
  HVM_boot_policy: vmRecord.HVM_boot_policy,
173
214
  HVM_shadow_multiplier: vmRecord.HVM_shadow_multiplier,
174
- memory_dynamic_max: vmRecord.memory_dynamic_max,
175
- memory_dynamic_min: vmRecord.memory_dynamic_min,
176
- memory_static_max: vmRecord.memory_static_max,
177
- memory_static_min: vmRecord.memory_static_min,
178
215
  name_label: vmRecord.name_label,
179
216
  name_description: vmRecord.name_description,
180
217
  order: vmRecord.order,
@@ -101,10 +101,14 @@ export const VmsXapi = class VmsXapiBackupRunner extends Abstract {
101
101
 
102
102
  const handleVm = vmUuid => {
103
103
  const getVmTask = () => {
104
- if (taskByVmId[vmUuid] === undefined) {
104
+ const started = taskByVmId[vmUuid] !== undefined
105
+ if (!started) {
105
106
  taskByVmId[vmUuid] = new Task(taskStart)
106
107
  }
107
- return taskByVmId[vmUuid]
108
+ return {
109
+ task: taskByVmId[vmUuid],
110
+ started,
111
+ }
108
112
  }
109
113
  const vmBackupFailed = async (error, task) => {
110
114
  if (isLastRun) {
@@ -135,7 +139,7 @@ export const VmsXapi = class VmsXapiBackupRunner extends Abstract {
135
139
  taskStart.properties.name_label = vm.name_label
136
140
  }
137
141
 
138
- const task = getVmTask()
142
+ const { task } = getVmTask()
139
143
  // error has to be caught in the task to prevent its failure, but handled outside the task to execute another task.run()
140
144
  let taskError
141
145
  return task
@@ -174,12 +178,19 @@ export const VmsXapi = class VmsXapiBackupRunner extends Abstract {
174
178
  task.success(result)
175
179
  } else {
176
180
  // ending the task with error or not ending the task
177
- vmBackupFailed(taskError, task)
181
+ return vmBackupFailed(taskError, task)
178
182
  }
179
183
  })
180
184
  .catch(noop) // errors are handled by logs
181
185
  }),
182
- error => vmBackupFailed(error, getVmTask())
186
+ error => {
187
+ const { task: vmTask, started } = getVmTask()
188
+ if (!started) {
189
+ // the task is not started (except if it's a retry), and an unstarted task can't be failed
190
+ vmTask.start()
191
+ }
192
+ return vmBackupFailed(error, vmTask)
193
+ }
183
194
  )
184
195
  }
185
196
  const { concurrency } = settings
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/vatesfr/xen-orchestra.git"
10
10
  },
11
- "version": "0.73.3",
11
+ "version": "0.73.5",
12
12
  "engines": {
13
13
  "node": ">=14.18"
14
14
  },
@@ -30,7 +30,7 @@
30
30
  "@vates/nbd-client": "^3.4.0",
31
31
  "@vates/parse-duration": "^0.1.1",
32
32
  "@vates/task": "^0.7.0",
33
- "@vates/types": "^1.27.0",
33
+ "@vates/types": "^1.28.0",
34
34
  "@xen-orchestra/async-map": "^0.1.3",
35
35
  "@xen-orchestra/disk-transform": "^1.3.1",
36
36
  "@xen-orchestra/fs": "^4.9.1",