@xen-orchestra/rest-api 0.13.1 → 0.15.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 (33) hide show
  1. package/dist/abstract-classes/xo-controller.mjs +3 -9
  2. package/dist/helpers/error.helper.mjs +10 -0
  3. package/dist/helpers/object-wrapper.helper.mjs +10 -7
  4. package/dist/helpers/utils.helper.mjs +11 -0
  5. package/dist/hosts/host.controller.mjs +42 -4
  6. package/dist/hosts/host.service.mjs +6 -7
  7. package/dist/hosts/host.type.mjs +1 -0
  8. package/dist/index.mjs +4 -0
  9. package/dist/middlewares/generic-error-handler.middleware.mjs +5 -1
  10. package/dist/open-api/common/response.common.mjs +4 -0
  11. package/dist/open-api/oa-examples/host.oa-example.mjs +31 -0
  12. package/dist/open-api/oa-examples/pool.oa-example.mjs +30 -0
  13. package/dist/open-api/oa-examples/task.oa-example.mjs +46 -0
  14. package/dist/open-api/oa-examples/vm-controller.oa-example.mjs +14 -0
  15. package/dist/open-api/oa-examples/vm-snapshot.oa-example.mjs +14 -0
  16. package/dist/open-api/oa-examples/vm-template.oa-example.mjs +14 -0
  17. package/dist/open-api/oa-examples/vm.oa-example.mjs +14 -0
  18. package/dist/open-api/routes/routes.js +364 -47
  19. package/dist/pools/pool.controller.mjs +16 -1
  20. package/dist/pools/pool.service.mjs +18 -8
  21. package/dist/rest-api/rest-api.mjs +10 -2
  22. package/dist/tasks/task.controller.mjs +121 -0
  23. package/dist/users/user.controller.mjs +0 -3
  24. package/dist/vm-controller/vm-controller.controller.mjs +30 -4
  25. package/dist/vm-snapshots/vm-snapshot.controller.mjs +30 -4
  26. package/dist/vm-templates/vm-template.controller.mjs +30 -4
  27. package/dist/vms/vm.controller.mjs +30 -4
  28. package/dist/vms/vm.service.mjs +14 -1
  29. package/dist/xoa/xoa.service.mjs +14 -4
  30. package/open-api/spec/swagger.json +1919 -499
  31. package/package.json +5 -4
  32. package/tsconfig.json +1 -0
  33. package/tsoa.json +12 -0
@@ -17,7 +17,7 @@ import { asynchronousActionResp, createdResp, featureUnauthorized, internalServe
17
17
  import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
18
18
  import { AlarmService } from '../alarms/alarm.service.mjs';
19
19
  import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
20
- import { createVm, importVm, partialPools, pool, poolDashboard, poolIds, poolStats, } from '../open-api/oa-examples/pool.oa-example.mjs';
20
+ import { createVm, importVm, partialPools, pool, poolDashboard, poolIds, poolMissingPatches, poolStats, } from '../open-api/oa-examples/pool.oa-example.mjs';
21
21
  import { taskLocation } from '../open-api/oa-examples/task.oa-example.mjs';
22
22
  import { createNetwork } from '../open-api/oa-examples/schedule.oa-example.mjs';
23
23
  import { BASE_URL } from '../index.mjs';
@@ -221,6 +221,14 @@ let PoolController = class PoolController extends XapiXoController {
221
221
  });
222
222
  return this.sendObjects(Object.values(alarms), req, 'alarms');
223
223
  }
224
+ /**
225
+ * @example id "355ee47d-ff4c-4924-3db2-fd86ae629676"
226
+ */
227
+ async getPoolMissingPatches(id) {
228
+ const pool = this.getObject(id);
229
+ const { missingPatches } = await this.#poolService.getMissingPatches(pool.id);
230
+ return missingPatches;
231
+ }
224
232
  };
225
233
  __decorate([
226
234
  Example(poolIds),
@@ -335,6 +343,13 @@ __decorate([
335
343
  __param(4, Query()),
336
344
  __param(5, Query())
337
345
  ], PoolController.prototype, "getPoolAlarms", null);
346
+ __decorate([
347
+ Example(poolMissingPatches),
348
+ Get('{id}/missing_patches'),
349
+ Response(notFoundResp.status, notFoundResp.description),
350
+ Response(featureUnauthorized.status, featureUnauthorized.description),
351
+ __param(0, Path())
352
+ ], PoolController.prototype, "getPoolMissingPatches", null);
338
353
  PoolController = __decorate([
339
354
  Route('pools'),
340
355
  Security('*'),
@@ -1,4 +1,5 @@
1
1
  import { createLogger } from '@xen-orchestra/log';
2
+ import { featureUnauthorized } from 'xo-common/api-errors.js';
2
3
  import { HOST_POWER_STATE, VM_POWER_STATE, } from '@vates/types';
3
4
  import { HostService } from '../hosts/host.service.mjs';
4
5
  import { VmService } from '../vms/vm.service.mjs';
@@ -45,13 +46,13 @@ export class PoolService {
45
46
  const alarms = this.#alarmService.getAlarms({ filter: alarm => alarm.$pool === poolId });
46
47
  return Object.keys(alarms);
47
48
  }
48
- async #getMissingPatches(poolId) {
49
- const missingPatchesInfo = await this.#hostService.getMissingPatchesInfo({ filter: host => host.$pool === poolId });
50
- if (!missingPatchesInfo.hasAuthorization) {
51
- return {
52
- hasAuthorization: false,
53
- };
54
- }
49
+ /**
50
+ * Throw if no authorization
51
+ */
52
+ async getMissingPatches(poolId) {
53
+ const missingPatchesInfo = await this.#hostService.getMissingPatchesInfo({
54
+ filter: host => host.$pool === poolId,
55
+ });
55
56
  const { hasAuthorization, missingPatches } = missingPatchesInfo;
56
57
  return {
57
58
  hasAuthorization,
@@ -182,7 +183,16 @@ export class PoolService {
182
183
  promiseWriteInStream({ maybePromise: this.#getHostsStatus(id), path: 'hosts.status', stream }),
183
184
  promiseWriteInStream({ maybePromise: this.#getVmsStatus(id), path: 'vms.status', stream }),
184
185
  promiseWriteInStream({ maybePromise: this.#getAlarms(id), path: 'alarms', stream }),
185
- promiseWriteInStream({ maybePromise: this.#getMissingPatches(id), path: 'hosts.missingPatches', stream }),
186
+ promiseWriteInStream({
187
+ maybePromise: this.getMissingPatches(id).catch(err => {
188
+ if (featureUnauthorized.is(err)) {
189
+ return { hasAuthorization: false };
190
+ }
191
+ throw err;
192
+ }),
193
+ path: 'hosts.missingPatches',
194
+ stream,
195
+ }),
186
196
  promiseWriteInStream({ maybePromise: this.#getTopFiveSrsUsage(id), path: 'srs.topFiveUsage', stream }),
187
197
  promiseWriteInStream({ maybePromise: this.#getTopFiveHostsRamUsage(id), path: 'hosts.topFiveUsage.ram', stream }),
188
198
  promiseWriteInStream({ maybePromise: this.#getTopFiveHostsCpuUsage(id), path: 'hosts.topFiveUsage.cpu', stream }),
@@ -1,3 +1,6 @@
1
+ import { createLogger } from '@xen-orchestra/log';
2
+ import { invalidCredentials } from 'xo-common/api-errors.js';
3
+ const log = createLogger('xo:rest-api:error-handler');
1
4
  export class RestApi {
2
5
  #ioc;
3
6
  #xoApp;
@@ -17,8 +20,13 @@ export class RestApi {
17
20
  authenticateUser(...args) {
18
21
  return this.#xoApp.authenticateUser(...args);
19
22
  }
20
- getCurrentUser() {
21
- return this.#xoApp.apiContext.user;
23
+ getCurrentUser({ throwUnauthenticated = true } = {}) {
24
+ const user = this.#xoApp.apiContext.user;
25
+ if (user === undefined && throwUnauthenticated) {
26
+ log.error('getCurrentUser received an unauthenticated API call');
27
+ throw invalidCredentials();
28
+ }
29
+ return user;
22
30
  }
23
31
  getObject(id, type) {
24
32
  return this.#xoApp.getObject(id, type);
@@ -0,0 +1,121 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
+ return function (target, key) { decorator(target, key, paramIndex); }
9
+ };
10
+ import * as CM from 'complex-matcher';
11
+ import { XoController } from '../abstract-classes/xo-controller.mjs';
12
+ import { Get, Path, Query, Request, Route, Security, Tags, Response, Example } from 'tsoa';
13
+ import { badRequestResp, notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
14
+ import { provide } from 'inversify-binding-decorators';
15
+ import { partialTasks, task, taskIds } from '../open-api/oa-examples/task.oa-example.mjs';
16
+ import pDefer from 'promise-toolbox/defer';
17
+ import { ApiError } from '../helpers/error.helper.mjs';
18
+ import { Transform } from 'node:stream';
19
+ import { makeObjectMapper } from '../helpers/object-wrapper.helper.mjs';
20
+ let TaskController = class TaskController extends XoController {
21
+ async getAllCollectionObjects() {
22
+ const result = [];
23
+ for await (const task of this.restApi.tasks.list()) {
24
+ result.push(task);
25
+ }
26
+ return result;
27
+ }
28
+ getCollectionObject(id) {
29
+ return this.restApi.tasks.get(id);
30
+ }
31
+ /**
32
+ *
33
+ * If watch is true, ndjson must also be true
34
+ *
35
+ * @example fields "status,id,properties"
36
+ * @example filter "status:failure"
37
+ * @example limit 42
38
+ */
39
+ async getTasks(req, fields, ndjson, watch, filter, limit) {
40
+ if (watch) {
41
+ if (!ndjson) {
42
+ throw new ApiError('watch=true requires ndjson=true', 400);
43
+ }
44
+ const userFilter = filter === undefined ? undefined : CM.parse(filter).createPredicate();
45
+ const stream = new Transform({
46
+ objectMode: true,
47
+ transform([event, object], encoding, callback) {
48
+ const mapper = makeObjectMapper(req);
49
+ callback(null, JSON.stringify([event, mapper(object)]) + '\n');
50
+ },
51
+ });
52
+ stream.on('close', () => {
53
+ this.restApi.tasks.off('update', update).off('remove', remove);
54
+ });
55
+ req.on('close', () => {
56
+ stream.destroy();
57
+ });
58
+ process.on('SIGTERM', () => {
59
+ req.destroy();
60
+ });
61
+ function update(task) {
62
+ if (userFilter === undefined || userFilter(task)) {
63
+ stream.write(['update', task]);
64
+ }
65
+ }
66
+ function remove(taskId) {
67
+ stream.write(['remove', { id: taskId }]);
68
+ }
69
+ this.restApi.tasks.on('update', update).on('remove', remove);
70
+ return stream;
71
+ }
72
+ const tasks = Object.values(await this.getObjects({ filter, limit }));
73
+ return this.sendObjects(tasks, req);
74
+ }
75
+ /**
76
+ * @example id "0mdd1basu"
77
+ */
78
+ async getTask(req, id, wait) {
79
+ const taskId = id;
80
+ if (wait) {
81
+ const { promise, resolve } = pDefer();
82
+ const stopWatch = await this.restApi.tasks.watch(taskId, task => {
83
+ if (task.status !== 'pending') {
84
+ stopWatch();
85
+ resolve(task);
86
+ }
87
+ });
88
+ req.on('close', stopWatch);
89
+ return promise;
90
+ }
91
+ return this.getObject(taskId);
92
+ }
93
+ };
94
+ __decorate([
95
+ Example(taskIds),
96
+ Example(partialTasks),
97
+ Get(''),
98
+ Response(badRequestResp.status, badRequestResp.description),
99
+ __param(0, Request()),
100
+ __param(1, Query()),
101
+ __param(2, Query()),
102
+ __param(3, Query()),
103
+ __param(4, Query()),
104
+ __param(5, Query())
105
+ ], TaskController.prototype, "getTasks", null);
106
+ __decorate([
107
+ Example(task),
108
+ Get('{id}'),
109
+ Response(notFoundResp.status, notFoundResp.description),
110
+ __param(0, Request()),
111
+ __param(1, Path()),
112
+ __param(2, Query())
113
+ ], TaskController.prototype, "getTask", null);
114
+ TaskController = __decorate([
115
+ Route('tasks'),
116
+ Security('*'),
117
+ Response(unauthorizedResp.status, unauthorizedResp.description),
118
+ Tags('tasks'),
119
+ provide(TaskController)
120
+ ], TaskController);
121
+ export { TaskController };
@@ -57,9 +57,6 @@ let UserController = class UserController extends XoController {
57
57
  */
58
58
  async updateUser(id, body) {
59
59
  const currentUser = this.restApi.getCurrentUser();
60
- if (currentUser === undefined) {
61
- throw new Error('current user is not defined');
62
- }
63
60
  const isAdmin = currentUser.permission === 'admin';
64
61
  if (isAdmin) {
65
62
  if (body.permission !== undefined && currentUser.id === id) {
@@ -12,16 +12,19 @@ import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
12
12
  import { RestApi } from '../rest-api/rest-api.mjs';
13
13
  import { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
14
14
  import { AlarmService } from '../alarms/alarm.service.mjs';
15
- import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
15
+ import { escapeUnsafeComplexMatcher, limitAndFilterArray } from '../helpers/utils.helper.mjs';
16
16
  import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
17
17
  import { notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
18
18
  import { provide } from 'inversify-binding-decorators';
19
- import { partialVmControllers, vmController, vmControllerIds, } from '../open-api/oa-examples/vm-controller.oa-example.mjs';
19
+ import { partialVmControllers, vmController, vmControllerIds, vmControllerVdis, } from '../open-api/oa-examples/vm-controller.oa-example.mjs';
20
+ import { VmService } from '../vms/vm.service.mjs';
20
21
  let VmControllerController = class VmControllerController extends XapiXoController {
21
22
  #alarmService;
22
- constructor(restApi, alarmService) {
23
+ #vmService;
24
+ constructor(restApi, alarmService, vmService) {
23
25
  super('VM-controller', restApi);
24
26
  this.#alarmService = alarmService;
27
+ this.#vmService = vmService;
25
28
  }
26
29
  /**
27
30
  *
@@ -52,6 +55,16 @@ let VmControllerController = class VmControllerController extends XapiXoControll
52
55
  });
53
56
  return this.sendObjects(Object.values(alarms), req, 'alarms');
54
57
  }
58
+ /**
59
+ * @example id "9b4775bd-9493-490a-9afa-f786a44caa4f"
60
+ * @example fields "VDI_type,id,name_label"
61
+ * @example filter "VDI_type:user"
62
+ * @example limit 42
63
+ */
64
+ getVmControllerVdis(req, id, fields, ndjson, filter, limit) {
65
+ const vdis = this.#vmService.getVmVdis(id, 'VM-controller');
66
+ return this.sendObjects(limitAndFilterArray(vdis, { filter, limit }), req, obj => obj.type.toLowerCase() + 's');
67
+ }
55
68
  };
56
69
  __decorate([
57
70
  Example(vmControllerIds),
@@ -81,6 +94,18 @@ __decorate([
81
94
  __param(4, Query()),
82
95
  __param(5, Query())
83
96
  ], VmControllerController.prototype, "getVmControllerAlarms", null);
97
+ __decorate([
98
+ Example(vmControllerVdis),
99
+ Get('{id}/vdis'),
100
+ Tags('vdis'),
101
+ Response(notFoundResp.status, notFoundResp.description),
102
+ __param(0, Request()),
103
+ __param(1, Path()),
104
+ __param(2, Query()),
105
+ __param(3, Query()),
106
+ __param(4, Query()),
107
+ __param(5, Query())
108
+ ], VmControllerController.prototype, "getVmControllerVdis", null);
84
109
  VmControllerController = __decorate([
85
110
  Route('vm-controllers'),
86
111
  Security('*'),
@@ -88,6 +113,7 @@ VmControllerController = __decorate([
88
113
  Tags('vms'),
89
114
  provide(VmControllerController),
90
115
  __param(0, inject(RestApi)),
91
- __param(1, inject(AlarmService))
116
+ __param(1, inject(AlarmService)),
117
+ __param(2, inject(VmService))
92
118
  ], VmControllerController);
93
119
  export { VmControllerController };
@@ -10,18 +10,21 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
10
10
  import { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
11
11
  import { inject } from 'inversify';
12
12
  import { AlarmService } from '../alarms/alarm.service.mjs';
13
- import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
13
+ import { escapeUnsafeComplexMatcher, limitAndFilterArray } from '../helpers/utils.helper.mjs';
14
14
  import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
15
15
  import { notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
16
16
  import { RestApi } from '../rest-api/rest-api.mjs';
17
17
  import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
18
18
  import { provide } from 'inversify-binding-decorators';
19
- import { partialVmSnapshots, vmSnapshot, vmSnapshotIds } from '../open-api/oa-examples/vm-snapshot.oa-example.mjs';
19
+ import { partialVmSnapshots, vmSnapshot, vmSnapshotIds, vmSnapshotVdis, } from '../open-api/oa-examples/vm-snapshot.oa-example.mjs';
20
+ import { VmService } from '../vms/vm.service.mjs';
20
21
  let VmSnapshotController = class VmSnapshotController extends XapiXoController {
21
22
  #alarmService;
22
- constructor(restApi, alarmService) {
23
+ #vmService;
24
+ constructor(restApi, alarmService, vmService) {
23
25
  super('VM-snapshot', restApi);
24
26
  this.#alarmService = alarmService;
27
+ this.#vmService = vmService;
25
28
  }
26
29
  /**
27
30
  *
@@ -52,6 +55,16 @@ let VmSnapshotController = class VmSnapshotController extends XapiXoController {
52
55
  });
53
56
  return this.sendObjects(Object.values(alarms), req, 'alarms');
54
57
  }
58
+ /**
59
+ * @example id "d68fca2c-41e6-be87-d790-105c1642a090"
60
+ * @example fields "VDI_type,id,name_label"
61
+ * @example filter "VDI_type:user"
62
+ * @example limit 42
63
+ */
64
+ getVmSnapshotVdis(req, id, fields, ndjson, filter, limit) {
65
+ const vdis = this.#vmService.getVmVdis(id, 'VM-snapshot');
66
+ return this.sendObjects(limitAndFilterArray(vdis, { filter, limit }), req, obj => obj.type.toLowerCase() + 's');
67
+ }
55
68
  };
56
69
  __decorate([
57
70
  Example(vmSnapshotIds),
@@ -81,6 +94,18 @@ __decorate([
81
94
  __param(4, Query()),
82
95
  __param(5, Query())
83
96
  ], VmSnapshotController.prototype, "getVmSnapshotAlarms", null);
97
+ __decorate([
98
+ Example(vmSnapshotVdis),
99
+ Get('{id}/vdis'),
100
+ Tags('vdis'),
101
+ Response(notFoundResp.status, notFoundResp.description),
102
+ __param(0, Request()),
103
+ __param(1, Path()),
104
+ __param(2, Query()),
105
+ __param(3, Query()),
106
+ __param(4, Query()),
107
+ __param(5, Query())
108
+ ], VmSnapshotController.prototype, "getVmSnapshotVdis", null);
84
109
  VmSnapshotController = __decorate([
85
110
  Route('vm-snapshots'),
86
111
  Security('*'),
@@ -88,6 +113,7 @@ VmSnapshotController = __decorate([
88
113
  Tags('vms'),
89
114
  provide(VmSnapshotController),
90
115
  __param(0, inject(RestApi)),
91
- __param(1, inject(AlarmService))
116
+ __param(1, inject(AlarmService)),
117
+ __param(2, inject(VmService))
92
118
  ], VmSnapshotController);
93
119
  export { VmSnapshotController };
@@ -11,17 +11,20 @@ import { Example, Get, Security, Query, Request, Response, Route, Tags, Path } f
11
11
  import { inject } from 'inversify';
12
12
  import { provide } from 'inversify-binding-decorators';
13
13
  import { AlarmService } from '../alarms/alarm.service.mjs';
14
- import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
14
+ import { escapeUnsafeComplexMatcher, limitAndFilterArray } from '../helpers/utils.helper.mjs';
15
15
  import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
16
16
  import { notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
17
17
  import { RestApi } from '../rest-api/rest-api.mjs';
18
18
  import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
19
- import { partialVmTemplates, vmTemplate, vmTemplateIds } from '../open-api/oa-examples/vm-template.oa-example.mjs';
19
+ import { partialVmTemplates, vmTemplate, vmTemplateIds, vmTemplateVdis, } from '../open-api/oa-examples/vm-template.oa-example.mjs';
20
+ import { VmService } from '../vms/vm.service.mjs';
20
21
  let VmTemplateController = class VmTemplateController extends XapiXoController {
21
22
  #alarmService;
22
- constructor(restApi, alarmService) {
23
+ #vmService;
24
+ constructor(restApi, alarmService, vmService) {
23
25
  super('VM-template', restApi);
24
26
  this.#alarmService = alarmService;
27
+ this.#vmService = vmService;
25
28
  }
26
29
  /**
27
30
  * @example fields "id,isDefaultTemplate,name_label"
@@ -51,6 +54,16 @@ let VmTemplateController = class VmTemplateController extends XapiXoController {
51
54
  });
52
55
  return this.sendObjects(Object.values(alarms), req, 'alarms');
53
56
  }
57
+ /**
58
+ * @example id "6d50ba76-0f11-1ff1-4f6a-b502afc31b8e"
59
+ * @example fields "VDI_type,id,name_label"
60
+ * @example filter "VDI_type:user"
61
+ * @example limit 42
62
+ */
63
+ getVmTemplateVdis(req, id, fields, ndjson, filter, limit) {
64
+ const vdis = this.#vmService.getVmVdis(id, 'VM-template');
65
+ return this.sendObjects(limitAndFilterArray(vdis, { filter, limit }), req, obj => obj.type.toLowerCase() + 's');
66
+ }
54
67
  };
55
68
  __decorate([
56
69
  Example(vmTemplateIds),
@@ -80,6 +93,18 @@ __decorate([
80
93
  __param(4, Query()),
81
94
  __param(5, Query())
82
95
  ], VmTemplateController.prototype, "getVmTemplateAlarms", null);
96
+ __decorate([
97
+ Example(vmTemplateVdis),
98
+ Get('{id}/vdis'),
99
+ Tags('vdis'),
100
+ Response(notFoundResp.status, notFoundResp.description),
101
+ __param(0, Request()),
102
+ __param(1, Path()),
103
+ __param(2, Query()),
104
+ __param(3, Query()),
105
+ __param(4, Query()),
106
+ __param(5, Query())
107
+ ], VmTemplateController.prototype, "getVmTemplateVdis", null);
83
108
  VmTemplateController = __decorate([
84
109
  Route('vm-templates'),
85
110
  Security('*'),
@@ -87,6 +112,7 @@ VmTemplateController = __decorate([
87
112
  Tags('vms'),
88
113
  provide(VmTemplateController),
89
114
  __param(0, inject(RestApi)),
90
- __param(1, inject(AlarmService))
115
+ __param(1, inject(AlarmService)),
116
+ __param(2, inject(VmService))
91
117
  ], VmTemplateController);
92
118
  export { VmTemplateController };
@@ -14,18 +14,21 @@ import { provide } from 'inversify-binding-decorators';
14
14
  import { AlarmService } from '../alarms/alarm.service.mjs';
15
15
  import { asynchronousActionResp, createdResp, internalServerErrorResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
16
16
  import { BASE_URL } from '../index.mjs';
17
- import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
17
+ import { escapeUnsafeComplexMatcher, limitAndFilterArray } from '../helpers/utils.helper.mjs';
18
18
  import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
19
- import { partialVms, vm, vmIds, vmStatsExample } from '../open-api/oa-examples/vm.oa-example.mjs';
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
21
  import { taskLocation } from '../open-api/oa-examples/task.oa-example.mjs';
22
22
  import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
23
+ import { VmService } from './vm.service.mjs';
23
24
  const IGNORED_VDIS_TAG = '[NOSNAP]';
24
25
  let VmController = class VmController extends XapiXoController {
25
26
  #alarmService;
26
- constructor(restApi, alarmService) {
27
+ #vmService;
28
+ constructor(restApi, alarmService, vmService) {
27
29
  super('VM', restApi);
28
30
  this.#alarmService = alarmService;
31
+ this.#vmService = vmService;
29
32
  }
30
33
  /**
31
34
  *
@@ -313,6 +316,16 @@ let VmController = class VmController extends XapiXoController {
313
316
  });
314
317
  return this.sendObjects(Object.values(alarms), req, 'alarms');
315
318
  }
319
+ /**
320
+ * @example id "f07ab729-c0e8-721c-45ec-f11276377030"
321
+ * @example fields "VDI_type,id,name_label"
322
+ * @example filter "VDI_type:user"
323
+ * @example limit 42
324
+ */
325
+ getVmVdis(req, id, fields, ndjson, filter, limit) {
326
+ const vdis = this.#vmService.getVmVdis(id, 'VM');
327
+ return this.sendObjects(limitAndFilterArray(vdis, { filter, limit }), req, obj => obj.type.toLowerCase() + 's');
328
+ }
316
329
  };
317
330
  __decorate([
318
331
  Example(vmIds),
@@ -464,6 +477,18 @@ __decorate([
464
477
  __param(4, Query()),
465
478
  __param(5, Query())
466
479
  ], VmController.prototype, "getVmAlarms", null);
480
+ __decorate([
481
+ Example(vmVdis),
482
+ Get('{id}/vdis'),
483
+ Tags('vdis'),
484
+ Response(notFoundResp.status, notFoundResp.description),
485
+ __param(0, Request()),
486
+ __param(1, Path()),
487
+ __param(2, Query()),
488
+ __param(3, Query()),
489
+ __param(4, Query()),
490
+ __param(5, Query())
491
+ ], VmController.prototype, "getVmVdis", null);
467
492
  VmController = __decorate([
468
493
  Route('vms'),
469
494
  Security('*'),
@@ -474,6 +499,7 @@ VmController = __decorate([
474
499
  ,
475
500
  provide(VmController),
476
501
  __param(0, inject(RestApi)),
477
- __param(1, inject(AlarmService))
502
+ __param(1, inject(AlarmService)),
503
+ __param(2, inject(VmService))
478
504
  ], VmController);
479
505
  export { VmController };
@@ -13,7 +13,7 @@ export class VmService {
13
13
  const xoApp = this.#restApi.xoApp;
14
14
  const xapi = xoApp.getXapi(pool);
15
15
  const currentUser = this.#restApi.getCurrentUser();
16
- const xapiVm = await xapi.createVm(template, rest, undefined, currentUser?.id);
16
+ const xapiVm = await xapi.createVm(template, rest, undefined, currentUser.id);
17
17
  $defer.onFailure(() => xapi.VM_destroy(xapiVm.$ref));
18
18
  const xoVm = this.#restApi.getObject(xapiVm.uuid, 'VM');
19
19
  let cloudConfigVdi;
@@ -84,4 +84,17 @@ export class VmService {
84
84
  total,
85
85
  };
86
86
  }
87
+ getVmVdis(id, vmType) {
88
+ const getObject = this.#restApi.getObject.bind(this.#restApi);
89
+ const vdis = [];
90
+ const vm = getObject(id, vmType);
91
+ for (const vbdId of vm.$VBDs) {
92
+ const vbd = getObject(vbdId, 'VBD');
93
+ if (vbd.VDI !== undefined) {
94
+ const vdi = getObject(vbd.VDI, ['VDI-snapshot', 'VDI']);
95
+ vdis.push(vdi);
96
+ }
97
+ }
98
+ return vdis;
99
+ }
87
100
  }
@@ -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',