@xen-orchestra/rest-api 0.14.0 → 0.16.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.
Files changed (49) hide show
  1. package/dist/abstract-classes/base-controller.mjs +4 -2
  2. package/dist/abstract-classes/xapi-xo-controller.mjs +2 -6
  3. package/dist/alarms/alarm.service.mjs +2 -1
  4. package/dist/backup-jobs/backup-job.controller.mjs +284 -0
  5. package/dist/backup-jobs/backup-job.service.mjs +25 -0
  6. package/dist/backup-jobs/backup-job.type.mjs +1 -0
  7. package/dist/backup-logs/backup-log.controller.mjs +75 -0
  8. package/dist/backup-logs/backup-log.service.mjs +5 -0
  9. package/dist/groups/group.controller.mjs +37 -1
  10. package/dist/helpers/error.helper.mjs +10 -0
  11. package/dist/hosts/host.controller.mjs +24 -4
  12. package/dist/hosts/host.service.mjs +6 -7
  13. package/dist/hosts/host.type.mjs +1 -0
  14. package/dist/index.mjs +4 -0
  15. package/dist/ioc/ioc.mjs +36 -0
  16. package/dist/middlewares/generic-error-handler.middleware.mjs +5 -1
  17. package/dist/open-api/common/response.common.mjs +8 -0
  18. package/dist/open-api/oa-examples/backup-job.oa-example.mjs +128 -0
  19. package/dist/open-api/oa-examples/backup-log.oa-example.mjs +93 -0
  20. package/dist/open-api/oa-examples/host.oa-example.mjs +30 -0
  21. package/dist/open-api/oa-examples/pool.oa-example.mjs +30 -0
  22. package/dist/open-api/oa-examples/proxy.oa-example.mjs +25 -0
  23. package/dist/open-api/oa-examples/restore-log.oa-example.mjs +53 -0
  24. package/dist/open-api/oa-examples/task.oa-example.mjs +46 -0
  25. package/dist/open-api/oa-examples/user.oa-example.mjs +32 -0
  26. package/dist/open-api/oa-examples/vdi.oa-example.mjs +3 -0
  27. package/dist/open-api/routes/routes.js +2079 -490
  28. package/dist/pools/pool.controller.mjs +16 -1
  29. package/dist/pools/pool.service.mjs +18 -8
  30. package/dist/proxies/proxy.controller.mjs +61 -0
  31. package/dist/rest-api/rest-api.mjs +6 -2
  32. package/dist/restore-logs/restore-log.controller.mjs +144 -0
  33. package/dist/srs/sr.controller.mjs +39 -2
  34. package/dist/tasks/task.controller.mjs +167 -0
  35. package/dist/tasks/task.service.mjs +24 -0
  36. package/dist/users/user.controller.mjs +91 -12
  37. package/dist/users/user.service.mjs +21 -0
  38. package/dist/vdi-snapshots/vdi-snapshot.controller.mjs +46 -4
  39. package/dist/vdis/vdi.controller.mjs +43 -4
  40. package/dist/vdis/vdi.service.mjs +21 -0
  41. package/dist/vm-snapshots/vm-snapshot.controller.mjs +72 -3
  42. package/dist/vm-templates/vm-template.controller.mjs +43 -2
  43. package/dist/vms/vm.controller.mjs +135 -5
  44. package/dist/vms/vm.service.mjs +18 -0
  45. package/dist/xoa/xoa.service.mjs +14 -4
  46. package/open-api/spec/swagger.json +11733 -6660
  47. package/package.json +6 -5
  48. package/tsconfig.json +1 -0
  49. package/tsoa.json +27 -0
@@ -11,24 +11,32 @@ import { Example, Get, Path, Post, Query, Request, Response, Route, Security, Ta
11
11
  import { inject } from 'inversify';
12
12
  import { incorrectState, invalidParameters } from 'xo-common/api-errors.js';
13
13
  import { provide } from 'inversify-binding-decorators';
14
- import { AlarmService } from '../alarms/alarm.service.mjs';
15
- import { asynchronousActionResp, createdResp, internalServerErrorResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
14
+ import { AlarmService, RAW_ALARM_FILTER } from '../alarms/alarm.service.mjs';
15
+ import { asynchronousActionResp, createdResp, forbiddenOperationResp, incorrectStateResp, internalServerErrorResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
16
16
  import { BASE_URL } from '../index.mjs';
17
17
  import { escapeUnsafeComplexMatcher, limitAndFilterArray } from '../helpers/utils.helper.mjs';
18
18
  import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
19
19
  import { partialVms, vm, vmIds, vmStatsExample, vmVdis } from '../open-api/oa-examples/vm.oa-example.mjs';
20
20
  import { RestApi } from '../rest-api/rest-api.mjs';
21
- import { taskLocation } from '../open-api/oa-examples/task.oa-example.mjs';
21
+ import { partialTasks, taskIds, taskLocation } from '../open-api/oa-examples/task.oa-example.mjs';
22
22
  import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
23
23
  import { VmService } from './vm.service.mjs';
24
+ import { TaskService } from '../tasks/task.service.mjs';
25
+ import { BackupJobService } from '../backup-jobs/backup-job.service.mjs';
26
+ import { partialVmBackupJobs, vmBackupJobIds } from '../open-api/oa-examples/backup-job.oa-example.mjs';
27
+ import { messageIds, partialMessages } from '../open-api/oa-examples/message.oa-example.mjs';
24
28
  const IGNORED_VDIS_TAG = '[NOSNAP]';
25
29
  let VmController = class VmController extends XapiXoController {
26
30
  #alarmService;
27
31
  #vmService;
28
- constructor(restApi, alarmService, vmService) {
32
+ #taskService;
33
+ #backupJobService;
34
+ constructor(restApi, alarmService, vmService, taskService, backupJobService) {
29
35
  super('VM', restApi);
30
36
  this.#alarmService = alarmService;
31
37
  this.#vmService = vmService;
38
+ this.#taskService = taskService;
39
+ this.#backupJobService = backupJobService;
32
40
  }
33
41
  /**
34
42
  *
@@ -39,6 +47,18 @@ let VmController = class VmController extends XapiXoController {
39
47
  getVms(req, fields, ndjson, filter, limit) {
40
48
  return this.sendObjects(Object.values(this.getObjects({ filter, limit })), req);
41
49
  }
50
+ /**
51
+ *
52
+ * Export VM. Compress is only used for XVA format
53
+ *
54
+ * @example id "f07ab729-c0e8-721c-45ec-f11276377030"
55
+ */
56
+ async exportVm(req, id, format, compress) {
57
+ const stream = await this.#vmService.export(id, 'VM', { compress, format, response: req.res });
58
+ process.on('SIGTERM', () => req.destroy());
59
+ req.on('close', () => stream.destroy());
60
+ return stream;
61
+ }
42
62
  /**
43
63
  *
44
64
  * @example id "f07ab729-c0e8-721c-45ec-f11276377030"
@@ -46,6 +66,13 @@ let VmController = class VmController extends XapiXoController {
46
66
  getVm(id) {
47
67
  return this.getObject(id);
48
68
  }
69
+ /**
70
+ * @example id "f07ab729-c0e8-721c-45ec-f11276377030"
71
+ */
72
+ async deleteVm(id) {
73
+ const xapiVm = this.getXapiObject(id);
74
+ await xapiVm.$xapi.VM_destroy(xapiVm.$ref);
75
+ }
49
76
  /**
50
77
  *
51
78
  * VM must be running
@@ -326,6 +353,50 @@ let VmController = class VmController extends XapiXoController {
326
353
  const vdis = this.#vmService.getVmVdis(id, 'VM');
327
354
  return this.sendObjects(limitAndFilterArray(vdis, { filter, limit }), req, obj => obj.type.toLowerCase() + 's');
328
355
  }
356
+ /**
357
+ * @example id "f07ab729-c0e8-721c-45ec-f11276377030"
358
+ * @example fields "mode,name,type,id"
359
+ * @example filter "mode:full"
360
+ * @example limit 42
361
+ */
362
+ async vmGetVmBackupJobs(req, id, fields, ndjson, filter, limit) {
363
+ const backupJobs = await this.restApi.xoApp.getAllJobs('backup');
364
+ const vmBackupJobs = [];
365
+ for (const backupJob of backupJobs) {
366
+ if (await this.#backupJobService.isVmInBackupJob(backupJob.id, id)) {
367
+ vmBackupJobs.push(backupJob);
368
+ }
369
+ }
370
+ return this.sendObjects(limitAndFilterArray(vmBackupJobs, { filter, limit }), req, '/backup-jobs');
371
+ }
372
+ /**
373
+ * @example id "cef5f68c-61ae-3831-d2e6-1590d4934acf"
374
+ * @example fields "name,id,$object"
375
+ * @example filter "name:VM_STARTED"
376
+ * @example limit 42
377
+ */
378
+ getVmMessages(req, id, fields, ndjson, filter, limit) {
379
+ const vm = this.getObject(id);
380
+ const messages = this.restApi.getObjectsByType('message', {
381
+ filter: `${escapeUnsafeComplexMatcher(filter) ?? ''} $object:${vm.uuid} !${RAW_ALARM_FILTER}`,
382
+ limit,
383
+ });
384
+ return this.sendObjects(Object.values(messages), req, 'messages');
385
+ }
386
+ /**
387
+ * @example id "613f541c-4bed-fc77-7ca8-2db6b68f079c"
388
+ * @example fields "id,status,properties"
389
+ * @example filter "status:failure"
390
+ * @example limit 42
391
+ */
392
+ async getVmTasks(req, id, fields, ndjson, filter, limit) {
393
+ const vm = this.getObject(id);
394
+ const tasks = await this.#taskService.getTasks({
395
+ filter: `${escapeUnsafeComplexMatcher(filter) ?? ''} |(properties:objectId:${vm.id} properties:params:id:${vm.id})`,
396
+ limit,
397
+ });
398
+ return this.sendObjects(tasks, req, 'tasks');
399
+ }
329
400
  };
330
401
  __decorate([
331
402
  Example(vmIds),
@@ -337,12 +408,30 @@ __decorate([
337
408
  __param(3, Query()),
338
409
  __param(4, Query())
339
410
  ], VmController.prototype, "getVms", null);
411
+ __decorate([
412
+ Get('{id}.{format}'),
413
+ SuccessResponse(200, 'Download started', 'application/octet-stream'),
414
+ Response(notFoundResp.status, notFoundResp.description),
415
+ Response(422, 'Invalid format, Invalid compress'),
416
+ __param(0, Request()),
417
+ __param(1, Path()),
418
+ __param(2, Path()),
419
+ __param(3, Query())
420
+ ], VmController.prototype, "exportVm", null);
340
421
  __decorate([
341
422
  Example(vm),
342
423
  Get('{id}'),
343
424
  Response(notFoundResp.status, notFoundResp.description),
344
425
  __param(0, Path())
345
426
  ], VmController.prototype, "getVm", null);
427
+ __decorate([
428
+ Delete('{id}'),
429
+ SuccessResponse(noContentResp.status, noContentResp.description),
430
+ Response(notFoundResp.status, notFoundResp.description),
431
+ Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
432
+ Response(incorrectStateResp.status, incorrectStateResp.description),
433
+ __param(0, Path())
434
+ ], VmController.prototype, "deleteVm", null);
346
435
  __decorate([
347
436
  Example(vmStatsExample),
348
437
  Get('{id}/stats'),
@@ -489,6 +578,45 @@ __decorate([
489
578
  __param(4, Query()),
490
579
  __param(5, Query())
491
580
  ], VmController.prototype, "getVmVdis", null);
581
+ __decorate([
582
+ Example(vmBackupJobIds),
583
+ Example(partialVmBackupJobs),
584
+ Get('{id}/backup-jobs'),
585
+ Tags('backup-jobs'),
586
+ Response(notFoundResp.status, notFoundResp.description),
587
+ __param(0, Request()),
588
+ __param(1, Path()),
589
+ __param(2, Query()),
590
+ __param(3, Query()),
591
+ __param(4, Query()),
592
+ __param(5, Query())
593
+ ], VmController.prototype, "vmGetVmBackupJobs", null);
594
+ __decorate([
595
+ Example(messageIds),
596
+ Example(partialMessages),
597
+ Get('{id}/messages'),
598
+ Tags('messages'),
599
+ Response(notFoundResp.status, notFoundResp.description),
600
+ __param(0, Request()),
601
+ __param(1, Path()),
602
+ __param(2, Query()),
603
+ __param(3, Query()),
604
+ __param(4, Query()),
605
+ __param(5, Query())
606
+ ], VmController.prototype, "getVmMessages", null);
607
+ __decorate([
608
+ Example(taskIds),
609
+ Example(partialTasks),
610
+ Get('{id}/tasks'),
611
+ Tags('tasks'),
612
+ Response(notFoundResp.status, notFoundResp.description),
613
+ __param(0, Request()),
614
+ __param(1, Path()),
615
+ __param(2, Query()),
616
+ __param(3, Query()),
617
+ __param(4, Query()),
618
+ __param(5, Query())
619
+ ], VmController.prototype, "getVmTasks", null);
492
620
  VmController = __decorate([
493
621
  Route('vms'),
494
622
  Security('*'),
@@ -500,6 +628,8 @@ VmController = __decorate([
500
628
  provide(VmController),
501
629
  __param(0, inject(RestApi)),
502
630
  __param(1, inject(AlarmService)),
503
- __param(2, inject(VmService))
631
+ __param(2, inject(VmService)),
632
+ __param(3, inject(TaskService)),
633
+ __param(4, inject(BackupJobService))
504
634
  ], VmController);
505
635
  export { VmController };
@@ -97,4 +97,22 @@ export class VmService {
97
97
  }
98
98
  return vdis;
99
99
  }
100
+ async export(id, vmType, { compress, format, response }) {
101
+ const xapiVm = this.#restApi.getXapiObject(id, vmType);
102
+ let stream;
103
+ if (format === 'xva') {
104
+ stream = (await xapiVm.$xapi.VM_export(xapiVm.$ref, { compress })).body;
105
+ }
106
+ else {
107
+ stream = await xapiVm.$xapi.exportVmOva(xapiVm.$ref);
108
+ }
109
+ if (response !== undefined) {
110
+ const headers = new Headers({
111
+ 'content-disposition': `attachment; filename=${id}.${format}`,
112
+ 'content-type': 'application/octet-stream',
113
+ });
114
+ response.setHeaders(headers);
115
+ }
116
+ return stream;
117
+ }
100
118
  }
@@ -1,4 +1,5 @@
1
1
  import groupBy from 'lodash/groupBy.js';
2
+ import { featureUnauthorized } from 'xo-common/api-errors.js';
2
3
  import semver from 'semver';
3
4
  import { BACKUP_TYPE, VM_POWER_STATE, } from '@vates/types';
4
5
  import { createLogger } from '@xen-orchestra/log';
@@ -167,11 +168,11 @@ export class XoaService {
167
168
  }
168
169
  return nHostsEol;
169
170
  }
171
+ /**
172
+ * Throw if no authorization
173
+ */
170
174
  async #getMissingPatchesInfo() {
171
175
  const missingPatchesInfo = await this.#hostService.getMissingPatchesInfo();
172
- if (!missingPatchesInfo.hasAuthorization) {
173
- return { hasAuthorization: false };
174
- }
175
176
  const { hasAuthorization, nHostsFailed, nHostsWithMissingPatches, nPoolsWithMissingPatches } = missingPatchesInfo;
176
177
  return {
177
178
  hasAuthorization,
@@ -437,7 +438,16 @@ export class XoaService {
437
438
  handleError: true,
438
439
  }),
439
440
  promiseWriteInStream({ maybePromise: this.#getPoolsStatus(), path: 'poolsStatus', stream }),
440
- promiseWriteInStream({ maybePromise: this.#getMissingPatchesInfo(), path: 'missingPatches', stream }),
441
+ promiseWriteInStream({
442
+ maybePromise: this.#getMissingPatchesInfo().catch(err => {
443
+ if (featureUnauthorized.is(err)) {
444
+ return { hasAuthorization: false };
445
+ }
446
+ throw err;
447
+ }),
448
+ path: 'missingPatches',
449
+ stream,
450
+ }),
441
451
  promiseWriteInStream({
442
452
  maybePromise: this.#getBackupRepositoriesSizeInfo(),
443
453
  path: 'backupRepositories',