@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.
- package/dist/abstract-classes/xo-controller.mjs +3 -9
- package/dist/helpers/error.helper.mjs +10 -0
- package/dist/helpers/object-wrapper.helper.mjs +10 -7
- package/dist/helpers/utils.helper.mjs +11 -0
- package/dist/hosts/host.controller.mjs +42 -4
- package/dist/hosts/host.service.mjs +6 -7
- package/dist/hosts/host.type.mjs +1 -0
- package/dist/index.mjs +4 -0
- package/dist/middlewares/generic-error-handler.middleware.mjs +5 -1
- package/dist/open-api/common/response.common.mjs +4 -0
- package/dist/open-api/oa-examples/host.oa-example.mjs +31 -0
- package/dist/open-api/oa-examples/pool.oa-example.mjs +30 -0
- package/dist/open-api/oa-examples/task.oa-example.mjs +46 -0
- package/dist/open-api/oa-examples/vm-controller.oa-example.mjs +14 -0
- package/dist/open-api/oa-examples/vm-snapshot.oa-example.mjs +14 -0
- package/dist/open-api/oa-examples/vm-template.oa-example.mjs +14 -0
- package/dist/open-api/oa-examples/vm.oa-example.mjs +14 -0
- package/dist/open-api/routes/routes.js +364 -47
- package/dist/pools/pool.controller.mjs +16 -1
- package/dist/pools/pool.service.mjs +18 -8
- package/dist/rest-api/rest-api.mjs +10 -2
- package/dist/tasks/task.controller.mjs +121 -0
- package/dist/users/user.controller.mjs +0 -3
- package/dist/vm-controller/vm-controller.controller.mjs +30 -4
- package/dist/vm-snapshots/vm-snapshot.controller.mjs +30 -4
- package/dist/vm-templates/vm-template.controller.mjs +30 -4
- package/dist/vms/vm.controller.mjs +30 -4
- package/dist/vms/vm.service.mjs +14 -1
- package/dist/xoa/xoa.service.mjs +14 -4
- package/open-api/spec/swagger.json +1919 -499
- package/package.json +5 -4
- package/tsconfig.json +1 -0
- 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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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({
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|
package/dist/vms/vm.service.mjs
CHANGED
|
@@ -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
|
|
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
|
}
|
package/dist/xoa/xoa.service.mjs
CHANGED
|
@@ -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({
|
|
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',
|