@xen-orchestra/rest-api 0.33.0 → 0.34.0

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.
@@ -28,6 +28,7 @@ import { BackupJobService } from '../backup-jobs/backup-job.service.mjs';
28
28
  import { partialVmBackupJobs, vmBackupJobIds } from '../open-api/oa-examples/backup-job.oa-example.mjs';
29
29
  import { messageIds, partialMessages } from '../open-api/oa-examples/message.oa-example.mjs';
30
30
  import { Task } from '@vates/task';
31
+ import { vmExportCompressDeprecated } from '../middlewares/deprecated.middleware.mjs';
31
32
  const IGNORED_VDIS_TAG = '[NOSNAP]';
32
33
  // `datasources` is managed through the dedicated `/vms/{id}/stats/data_source`
33
34
  // endpoints, not as a direct VM property, so it cannot be updated via PATCH /vms.
@@ -447,6 +448,8 @@ let VmController = class VmController extends XapiXoController {
447
448
  });
448
449
  }
449
450
  /**
451
+ * Required privilege:
452
+ * - resource: vm, action: clone
450
453
  *
451
454
  * - For fast clone on the same SR, omit `srId` and set `fast` to `true`.
452
455
  * - For full copy on the same SR, omit `srId` and set `fast` to `false`.
@@ -630,6 +633,10 @@ let VmController = class VmController extends XapiXoController {
630
633
  }
631
634
  }
632
635
  /**
636
+ * Required privileges:
637
+ * - resource: vm, action: migrate-send
638
+ * - resource: host, action: migrate-receive (on the destination host)
639
+ *
633
640
  * VIF mapping is not allowed for intra-pool migration
634
641
  *
635
642
  * Networks and SRs must belong to the same pool as the destination host
@@ -649,6 +656,7 @@ let VmController = class VmController extends XapiXoController {
649
656
  migrationNetworkId: migrationNetworkId,
650
657
  sr: 'srId' in body ? body.srId : undefined,
651
658
  });
659
+ return;
652
660
  };
653
661
  return this.createAction(action, {
654
662
  sync,
@@ -673,7 +681,7 @@ __decorate([
673
681
  __decorate([
674
682
  Extension('x-mcp-exposure', 'deny'),
675
683
  Get('{id}.{format}'),
676
- Middlewares(acl({ resource: 'vm', action: 'export', objectId: 'params.id' })),
684
+ Middlewares([acl({ resource: 'vm', action: 'export', objectId: 'params.id' }), vmExportCompressDeprecated]),
677
685
  SuccessResponse(200, 'Download started', 'application/octet-stream'),
678
686
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
679
687
  Response(notFoundResp.status, notFoundResp.description),
@@ -919,8 +927,9 @@ __decorate([
919
927
  Example(taskLocation),
920
928
  Extension('x-mcp-exposure', 'confirm'),
921
929
  Post('{id}/actions/clone'),
922
- Middlewares(json()),
930
+ Middlewares([json(), acl({ resource: 'vm', action: 'clone', objectId: 'params.id' })]),
923
931
  SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
932
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
924
933
  Response(createdResp.status, createdResp.description),
925
934
  Response(notFoundResp.status, notFoundResp.description),
926
935
  Response(internalServerErrorResp.status, internalServerErrorResp.description),
@@ -1041,8 +1050,17 @@ __decorate([
1041
1050
  Example(taskLocation),
1042
1051
  Extension('x-mcp-exposure', 'confirm'),
1043
1052
  Post('{id}/actions/migrate'),
1044
- Middlewares(json()),
1053
+ Middlewares([
1054
+ json(),
1055
+ // Two separate checks allow independent control so a user can be allowed to migrate a VM away
1056
+ // without being allowed to place VMs on any specific host, and vice versa.
1057
+ acl([
1058
+ { resource: 'vm', action: 'migrate-send', objectId: 'params.id' },
1059
+ { resource: 'host', action: 'migrate-receive', objectId: 'body.hostId' },
1060
+ ]),
1061
+ ]),
1045
1062
  SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
1063
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
1046
1064
  Response(noContentResp.status, noContentResp.description),
1047
1065
  Response(notFoundResp.status, notFoundResp.description),
1048
1066
  Response(invalidParametersResp.status, invalidParametersResp.description),
@@ -120,7 +120,7 @@ export class VmService {
120
120
  }
121
121
  return vdis;
122
122
  }
123
- async export(id, vmType, { compress, format, response }) {
123
+ async export(id, vmType, { compress, format, response, }) {
124
124
  const xapiVm = this.#restApi.getXapiObject(id, vmType);
125
125
  let stream;
126
126
  if (format === 'xva') {
@@ -196,23 +196,24 @@ export class VmService {
196
196
  }
197
197
  #getLastReplication(id) {
198
198
  const vm = this.#restApi.getObject(id, 'VM');
199
- const replicatedVms = this.#restApi.getObjectsByType('VM', {
200
- filter: obj => obj.other['xo:backup:vm'] === vm.id,
199
+ const snapshotReplicas = this.#restApi.getObjectsByType('VM-snapshot', {
200
+ filter: obj => obj.other['xo:backup:vm'] === vm.id && obj.$snapshot_of !== vm.id,
201
201
  });
202
202
  let lastTimestamp;
203
- let lastReplica;
204
- for (const id in replicatedVms) {
205
- const replica = replicatedVms[id];
206
- const timestamp = parseDateTime(replica.other['xo:backup:datetime']);
203
+ let lastReplicaId;
204
+ for (const id in snapshotReplicas) {
205
+ const snapshot = snapshotReplicas[id];
206
+ const timestamp = parseDateTime(snapshot.other['xo:backup:datetime']);
207
207
  if (lastTimestamp === undefined || lastTimestamp < timestamp) {
208
208
  lastTimestamp = timestamp;
209
- lastReplica = replica;
209
+ lastReplicaId = snapshot.$snapshot_of;
210
210
  }
211
211
  }
212
- if (lastReplica === undefined) {
212
+ if (lastReplicaId === undefined) {
213
213
  return {};
214
214
  }
215
- const vdis = this.getVmVdis(id, 'VM');
215
+ const replica = this.#restApi.getObject(lastReplicaId, 'VM');
216
+ const vdis = this.getVmVdis(replica.id, 'VM');
216
217
  let sr = undefined;
217
218
  for (const vdi of vdis) {
218
219
  if (sr === undefined) {
@@ -226,7 +227,7 @@ export class VmService {
226
227
  }
227
228
  }
228
229
  return {
229
- id: lastReplica.id,
230
+ id: replica.id,
230
231
  timestamp: lastTimestamp * 1000,
231
232
  sr,
232
233
  };