@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.
- package/dist/abstract-classes/base-controller.mjs +4 -2
- package/dist/abstract-classes/xapi-xo-controller.mjs +2 -6
- package/dist/alarms/alarm.service.mjs +2 -1
- package/dist/backup-jobs/backup-job.controller.mjs +284 -0
- package/dist/backup-jobs/backup-job.service.mjs +25 -0
- package/dist/backup-jobs/backup-job.type.mjs +1 -0
- package/dist/backup-logs/backup-log.controller.mjs +75 -0
- package/dist/backup-logs/backup-log.service.mjs +5 -0
- package/dist/groups/group.controller.mjs +37 -1
- package/dist/helpers/error.helper.mjs +10 -0
- package/dist/hosts/host.controller.mjs +24 -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/ioc/ioc.mjs +36 -0
- package/dist/middlewares/generic-error-handler.middleware.mjs +5 -1
- package/dist/open-api/common/response.common.mjs +8 -0
- package/dist/open-api/oa-examples/backup-job.oa-example.mjs +128 -0
- package/dist/open-api/oa-examples/backup-log.oa-example.mjs +93 -0
- package/dist/open-api/oa-examples/host.oa-example.mjs +30 -0
- package/dist/open-api/oa-examples/pool.oa-example.mjs +30 -0
- package/dist/open-api/oa-examples/proxy.oa-example.mjs +25 -0
- package/dist/open-api/oa-examples/restore-log.oa-example.mjs +53 -0
- package/dist/open-api/oa-examples/task.oa-example.mjs +46 -0
- package/dist/open-api/oa-examples/user.oa-example.mjs +32 -0
- package/dist/open-api/oa-examples/vdi.oa-example.mjs +3 -0
- package/dist/open-api/routes/routes.js +2079 -490
- package/dist/pools/pool.controller.mjs +16 -1
- package/dist/pools/pool.service.mjs +18 -8
- package/dist/proxies/proxy.controller.mjs +61 -0
- package/dist/rest-api/rest-api.mjs +6 -2
- package/dist/restore-logs/restore-log.controller.mjs +144 -0
- package/dist/srs/sr.controller.mjs +39 -2
- package/dist/tasks/task.controller.mjs +167 -0
- package/dist/tasks/task.service.mjs +24 -0
- package/dist/users/user.controller.mjs +91 -12
- package/dist/users/user.service.mjs +21 -0
- package/dist/vdi-snapshots/vdi-snapshot.controller.mjs +46 -4
- package/dist/vdis/vdi.controller.mjs +43 -4
- package/dist/vdis/vdi.service.mjs +21 -0
- package/dist/vm-snapshots/vm-snapshot.controller.mjs +72 -3
- package/dist/vm-templates/vm-template.controller.mjs +43 -2
- package/dist/vms/vm.controller.mjs +135 -5
- package/dist/vms/vm.service.mjs +18 -0
- package/dist/xoa/xoa.service.mjs +14 -4
- package/open-api/spec/swagger.json +11733 -6660
- package/package.json +6 -5
- package/tsconfig.json +1 -0
- package/tsoa.json +27 -0
|
@@ -53,8 +53,10 @@ export class BaseController extends Controller {
|
|
|
53
53
|
}
|
|
54
54
|
maybeCompressResponse(req, res) {
|
|
55
55
|
let transform;
|
|
56
|
-
|
|
57
|
-
acceptEncoding = Array.isArray(
|
|
56
|
+
const _acceptEncoding = req.headers['accept-encoding'];
|
|
57
|
+
const acceptEncoding = Array.isArray(_acceptEncoding)
|
|
58
|
+
? _acceptEncoding
|
|
59
|
+
: _acceptEncoding?.split(',');
|
|
58
60
|
if (acceptEncoding !== undefined &&
|
|
59
61
|
acceptEncoding.some(encoding => {
|
|
60
62
|
const value = encoding.split(';')[0].trim().toLowerCase();
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as CM from 'complex-matcher';
|
|
2
1
|
import { BaseController } from './base-controller.mjs';
|
|
3
2
|
export class XapiXoController extends BaseController {
|
|
4
3
|
#type;
|
|
@@ -6,11 +5,8 @@ export class XapiXoController extends BaseController {
|
|
|
6
5
|
super(restApi);
|
|
7
6
|
this.#type = type;
|
|
8
7
|
}
|
|
9
|
-
getObjects(
|
|
10
|
-
|
|
11
|
-
filter = CM.parse(filter).createPredicate();
|
|
12
|
-
}
|
|
13
|
-
return this.restApi.getObjectsByType(this.#type, { filter: filter, limit });
|
|
8
|
+
getObjects(opts) {
|
|
9
|
+
return this.restApi.getObjectsByType(this.#type, opts);
|
|
14
10
|
}
|
|
15
11
|
getObject(id) {
|
|
16
12
|
return this.restApi.getObject(id, this.#type);
|
|
@@ -3,7 +3,8 @@ import { BASE_URL } from '../index.mjs';
|
|
|
3
3
|
// E.g: 'value: 0.6\nconfig:\n<variable>\n<name value="cpu_usage"/>\n<alarm_trigger_level value="0.4"/>\n<alarm_trigger_period value ="60"/>\n</variable>';
|
|
4
4
|
const ALARM_BODY_REGEX = /^value:\s*(Infinity|NaN|-Infinity|\d+(?:\.\d+)?)\s*config:\s*<variable>\s*<name value="(.*?)"/;
|
|
5
5
|
const ALARM_NAMES = ['ALARM', 'BOND_STATUS_CHANGED', 'MULTIPATH_PERIODIC_ALERT'];
|
|
6
|
-
export const
|
|
6
|
+
export const RAW_ALARM_FILTER = `name:|(${ALARM_NAMES.join(' ')})`;
|
|
7
|
+
export const alarmPredicate = CM.parse(RAW_ALARM_FILTER).createPredicate();
|
|
7
8
|
export class AlarmService {
|
|
8
9
|
#restApi;
|
|
9
10
|
constructor(restApi) {
|
|
@@ -0,0 +1,284 @@
|
|
|
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 { createLogger } from '@xen-orchestra/log';
|
|
12
|
+
import { inject } from 'inversify';
|
|
13
|
+
import { noSuchObject } from 'xo-common/api-errors.js';
|
|
14
|
+
import { Deprecated, Example, Get, Hidden, Middlewares, Path, Query, Request, Response, Route, Security, Tags, } from 'tsoa';
|
|
15
|
+
import { provide } from 'inversify-binding-decorators';
|
|
16
|
+
import { backupLog, backupLogIds, partialBackupLogs } from '../open-api/oa-examples/backup-log.oa-example.mjs';
|
|
17
|
+
import { BackupLogService } from '../backup-logs/backup-log.service.mjs';
|
|
18
|
+
import { notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
|
|
19
|
+
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
20
|
+
import { limitAndFilterArray } from '../helpers/utils.helper.mjs';
|
|
21
|
+
import { XoController } from '../abstract-classes/xo-controller.mjs';
|
|
22
|
+
import { metadataBackupJob, metadataBackupJobIds, mirrorBackupJob, mirrorBackupJobIds, partialMetadataBackupJobs, partialMirrorBackupJobs, partialVmBackupJobs, vmBackupJob, vmBackupJobIds, } from '../open-api/oa-examples/backup-job.oa-example.mjs';
|
|
23
|
+
import { BASE_URL } from '../index.mjs';
|
|
24
|
+
const log = createLogger('xo:rest-api:backupJob-controller');
|
|
25
|
+
let BackupJobController = class BackupJobController extends XoController {
|
|
26
|
+
async getAllCollectionObjects() {
|
|
27
|
+
const allJobs = await this.restApi.xoApp.getAllJobs();
|
|
28
|
+
const backupJobs = allJobs.filter(job => 'type' in job);
|
|
29
|
+
return backupJobs;
|
|
30
|
+
}
|
|
31
|
+
async getCollectionObject(id) {
|
|
32
|
+
const job = await this.restApi.xoApp.getJob(id);
|
|
33
|
+
if (!('type' in job)) {
|
|
34
|
+
// not a backup job
|
|
35
|
+
throw noSuchObject(id, 'backup-job');
|
|
36
|
+
}
|
|
37
|
+
return job;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
*
|
|
41
|
+
* @example fields "name,mode,type,id"
|
|
42
|
+
* @example filter "type:backup"
|
|
43
|
+
* @example limit 42
|
|
44
|
+
*/
|
|
45
|
+
async getBackupJobs(req, fields, ndjson, filter, limit) {
|
|
46
|
+
const backupJobs = await this.getObjects({ filter, limit });
|
|
47
|
+
return this.sendObjects(Object.values(backupJobs), req, 'backup-jobs');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* @example id "d33f3dc1-92b4-469c-ad58-4c2a106a4721"
|
|
51
|
+
*/
|
|
52
|
+
getBackupJob(id) {
|
|
53
|
+
return this.getObject(id);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
__decorate([
|
|
57
|
+
Example(vmBackupJobIds),
|
|
58
|
+
Example(partialVmBackupJobs),
|
|
59
|
+
Get(''),
|
|
60
|
+
__param(0, Request()),
|
|
61
|
+
__param(1, Query()),
|
|
62
|
+
__param(2, Query()),
|
|
63
|
+
__param(3, Query()),
|
|
64
|
+
__param(4, Query())
|
|
65
|
+
], BackupJobController.prototype, "getBackupJobs", null);
|
|
66
|
+
__decorate([
|
|
67
|
+
Example(vmBackupJob),
|
|
68
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
69
|
+
Get('{id}'),
|
|
70
|
+
__param(0, Path())
|
|
71
|
+
], BackupJobController.prototype, "getBackupJob", null);
|
|
72
|
+
BackupJobController = __decorate([
|
|
73
|
+
Route('backup-jobs'),
|
|
74
|
+
Security('*'),
|
|
75
|
+
Response(unauthorizedResp.status, unauthorizedResp.description),
|
|
76
|
+
Tags('backup-jobs'),
|
|
77
|
+
provide(BackupJobController)
|
|
78
|
+
], BackupJobController);
|
|
79
|
+
export { BackupJobController };
|
|
80
|
+
// ----------- DEPRECATED TO BE REMOVED IN ONE YEAR (09-12-2026)--------------------
|
|
81
|
+
let DeprecatedBackupController = class DeprecatedBackupController extends XoController {
|
|
82
|
+
#backupLogService;
|
|
83
|
+
constructor(restApi, backupLogService) {
|
|
84
|
+
super(restApi);
|
|
85
|
+
this.#backupLogService = backupLogService;
|
|
86
|
+
}
|
|
87
|
+
async getAllCollectionObjects() {
|
|
88
|
+
const backupJobs = await this.restApi.xoApp.getAllJobs();
|
|
89
|
+
return backupJobs.filter(job => 'type' in job);
|
|
90
|
+
}
|
|
91
|
+
async getCollectionObject(id) {
|
|
92
|
+
const backupJob = await this.restApi.xoApp.getJob(id);
|
|
93
|
+
if (!('type' in backupJob)) {
|
|
94
|
+
throw noSuchObject(id, 'backup-job');
|
|
95
|
+
}
|
|
96
|
+
return backupJob;
|
|
97
|
+
}
|
|
98
|
+
async getObject(id, type) {
|
|
99
|
+
const backupJob = await super.getObject(id);
|
|
100
|
+
if (backupJob.type !== type) {
|
|
101
|
+
throw noSuchObject(id, type);
|
|
102
|
+
}
|
|
103
|
+
return backupJob;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
*
|
|
107
|
+
* @example fields "name,mode,id"
|
|
108
|
+
* @example filter "mode:delta"
|
|
109
|
+
* @example limit 42
|
|
110
|
+
*/
|
|
111
|
+
async getVmBackupJobs(req, fields, ndjson, filter, limit) {
|
|
112
|
+
const vmBackupJobs = await this.restApi.xoApp.getAllJobs('backup');
|
|
113
|
+
return this.sendObjects(limitAndFilterArray(vmBackupJobs, { filter, limit }), req, 'backup-jobs');
|
|
114
|
+
}
|
|
115
|
+
// For compatibility, redirect /backup/jobs/:id to /backup/jobs/vm/:id
|
|
116
|
+
async redirectToVmBackupJob(req, id) {
|
|
117
|
+
const res = req.res;
|
|
118
|
+
res.redirect(308, BASE_URL + '/backup/jobs/vm/' + id);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* @example id "d33f3dc1-92b4-469c-ad58-4c2a106a4721"
|
|
122
|
+
*/
|
|
123
|
+
getVmBackupJob(id) {
|
|
124
|
+
return this.getObject(id, 'backup');
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
*
|
|
128
|
+
* @example fields "name,xoMetadata,id"
|
|
129
|
+
* @example filter "xoMetadata?"
|
|
130
|
+
* @example limit 42
|
|
131
|
+
*/
|
|
132
|
+
async getMetadataBackupJobs(req, fields, ndjson, filter, limit) {
|
|
133
|
+
const metadataBackupJobs = await this.restApi.xoApp.getAllJobs('metadataBackup');
|
|
134
|
+
return this.sendObjects(limitAndFilterArray(metadataBackupJobs, { filter, limit }), req, 'backup-jobs');
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* @example id "b50f95fd-f6b7-4027-87b6-6a02c7dcd5f5"
|
|
138
|
+
*/
|
|
139
|
+
getMetadataBackupJob(id) {
|
|
140
|
+
return this.getObject(id, 'metadataBackup');
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
*
|
|
144
|
+
* @example fields "name,mode,id"
|
|
145
|
+
* @example filter "mode:delta"
|
|
146
|
+
* @example limit 42
|
|
147
|
+
*/
|
|
148
|
+
async getMirrorBackupJobs(req, fields, ndjson, filter, limit) {
|
|
149
|
+
const mirrorBackupJobs = await this.restApi.xoApp.getAllJobs('mirrorBackup');
|
|
150
|
+
return this.sendObjects(limitAndFilterArray(mirrorBackupJobs, { filter, limit }), req, 'backup-jobs');
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* @example id "e680c14c-ab52-45c8-bb0e-bd4ca12ea8f9"
|
|
154
|
+
*/
|
|
155
|
+
getMirrorBackupJob(id) {
|
|
156
|
+
return this.getObject(id, 'mirrorBackup');
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* @example fields "jobName,status,data"
|
|
160
|
+
* @example filter "status:success"
|
|
161
|
+
* @example limit 42
|
|
162
|
+
*/
|
|
163
|
+
async getDeprecatedBackupLogs(req, fields, ndjson, filter, limit) {
|
|
164
|
+
const userFilter = filter === undefined ? () => true : CM.parse(filter).createPredicate();
|
|
165
|
+
const predicate = (log) => {
|
|
166
|
+
if (!this.#backupLogService.isBackupLog(log)) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
return userFilter(log);
|
|
170
|
+
};
|
|
171
|
+
const logs = (await this.restApi.xoApp.getBackupNgLogsSorted({ filter: predicate, limit }));
|
|
172
|
+
return this.sendObjects(logs, req, 'backup-logs');
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* @example id "1753776067468"
|
|
176
|
+
*/
|
|
177
|
+
async getDeprecatedBackupLog(id) {
|
|
178
|
+
const log = await this.restApi.xoApp.getBackupNgLogs(id);
|
|
179
|
+
if (!this.#backupLogService.isBackupLog(log)) {
|
|
180
|
+
throw noSuchObject('backup-log');
|
|
181
|
+
}
|
|
182
|
+
return log;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
__decorate([
|
|
186
|
+
Example(vmBackupJobIds),
|
|
187
|
+
Example(partialVmBackupJobs),
|
|
188
|
+
Deprecated(),
|
|
189
|
+
Get('jobs/vm'),
|
|
190
|
+
Tags('backup-jobs'),
|
|
191
|
+
__param(0, Request()),
|
|
192
|
+
__param(1, Query()),
|
|
193
|
+
__param(2, Query()),
|
|
194
|
+
__param(3, Query()),
|
|
195
|
+
__param(4, Query())
|
|
196
|
+
], DeprecatedBackupController.prototype, "getVmBackupJobs", null);
|
|
197
|
+
__decorate([
|
|
198
|
+
Hidden(),
|
|
199
|
+
Get('jobs/{id}'),
|
|
200
|
+
Tags('backup-jobs'),
|
|
201
|
+
__param(0, Request()),
|
|
202
|
+
__param(1, Path())
|
|
203
|
+
], DeprecatedBackupController.prototype, "redirectToVmBackupJob", null);
|
|
204
|
+
__decorate([
|
|
205
|
+
Example(vmBackupJob),
|
|
206
|
+
Deprecated(),
|
|
207
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
208
|
+
Get('jobs/vm/{id}'),
|
|
209
|
+
Tags('backup-jobs'),
|
|
210
|
+
__param(0, Path())
|
|
211
|
+
], DeprecatedBackupController.prototype, "getVmBackupJob", null);
|
|
212
|
+
__decorate([
|
|
213
|
+
Example(metadataBackupJobIds),
|
|
214
|
+
Example(partialMetadataBackupJobs),
|
|
215
|
+
Deprecated(),
|
|
216
|
+
Get('jobs/metadata'),
|
|
217
|
+
Tags('backup-jobs'),
|
|
218
|
+
__param(0, Request()),
|
|
219
|
+
__param(1, Query()),
|
|
220
|
+
__param(2, Query()),
|
|
221
|
+
__param(3, Query()),
|
|
222
|
+
__param(4, Query())
|
|
223
|
+
], DeprecatedBackupController.prototype, "getMetadataBackupJobs", null);
|
|
224
|
+
__decorate([
|
|
225
|
+
Example(metadataBackupJob),
|
|
226
|
+
Deprecated(),
|
|
227
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
228
|
+
Get('jobs/metadata/{id}'),
|
|
229
|
+
Tags('backup-jobs'),
|
|
230
|
+
__param(0, Path())
|
|
231
|
+
], DeprecatedBackupController.prototype, "getMetadataBackupJob", null);
|
|
232
|
+
__decorate([
|
|
233
|
+
Example(mirrorBackupJobIds),
|
|
234
|
+
Example(partialMirrorBackupJobs),
|
|
235
|
+
Deprecated(),
|
|
236
|
+
Get('jobs/mirror'),
|
|
237
|
+
Tags('backup-jobs'),
|
|
238
|
+
__param(0, Request()),
|
|
239
|
+
__param(1, Query()),
|
|
240
|
+
__param(2, Query()),
|
|
241
|
+
__param(3, Query()),
|
|
242
|
+
__param(4, Query())
|
|
243
|
+
], DeprecatedBackupController.prototype, "getMirrorBackupJobs", null);
|
|
244
|
+
__decorate([
|
|
245
|
+
Example(mirrorBackupJob),
|
|
246
|
+
Deprecated(),
|
|
247
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
248
|
+
Get('jobs/mirror/{id}'),
|
|
249
|
+
Tags('backup-jobs'),
|
|
250
|
+
__param(0, Path())
|
|
251
|
+
], DeprecatedBackupController.prototype, "getMirrorBackupJob", null);
|
|
252
|
+
__decorate([
|
|
253
|
+
Example(backupLogIds),
|
|
254
|
+
Example(partialBackupLogs),
|
|
255
|
+
Deprecated(),
|
|
256
|
+
Get('logs'),
|
|
257
|
+
Tags('backup-logs'),
|
|
258
|
+
__param(0, Request()),
|
|
259
|
+
__param(1, Query()),
|
|
260
|
+
__param(2, Query()),
|
|
261
|
+
__param(3, Query()),
|
|
262
|
+
__param(4, Query())
|
|
263
|
+
], DeprecatedBackupController.prototype, "getDeprecatedBackupLogs", null);
|
|
264
|
+
__decorate([
|
|
265
|
+
Example(backupLog),
|
|
266
|
+
Deprecated(),
|
|
267
|
+
Get('logs/{id}'),
|
|
268
|
+
Tags('backup-logs'),
|
|
269
|
+
__param(0, Path())
|
|
270
|
+
], DeprecatedBackupController.prototype, "getDeprecatedBackupLog", null);
|
|
271
|
+
DeprecatedBackupController = __decorate([
|
|
272
|
+
Route('backup'),
|
|
273
|
+
Security('*'),
|
|
274
|
+
Response(unauthorizedResp.status, unauthorizedResp.description),
|
|
275
|
+
Middlewares((_req, _res, next) => {
|
|
276
|
+
log.warn('You are calling a deprecated route. It will be removed in the futur. Please use `/rest/v0/backup-jobs` or `/rest/v0/backup-logs` instead');
|
|
277
|
+
next();
|
|
278
|
+
}),
|
|
279
|
+
provide(DeprecatedBackupController),
|
|
280
|
+
__param(0, inject(RestApi)),
|
|
281
|
+
__param(1, inject(BackupLogService))
|
|
282
|
+
], DeprecatedBackupController);
|
|
283
|
+
export { DeprecatedBackupController };
|
|
284
|
+
// ----------- DEPRECATED TO BE REMOVED IN ONE YEAR (09-12-2026)--------------------
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createPredicate } from 'value-matcher';
|
|
2
|
+
import { extractIdsFromSimplePattern } from '@xen-orchestra/backups/extractIdsFromSimplePattern.mjs';
|
|
3
|
+
const NO_BAK_TAG = 'xo:no-bak';
|
|
4
|
+
export class BackupJobService {
|
|
5
|
+
#restApi;
|
|
6
|
+
constructor(restApi) {
|
|
7
|
+
this.#restApi = restApi;
|
|
8
|
+
}
|
|
9
|
+
async isVmInBackupJob(backupJobId, vmId) {
|
|
10
|
+
const backupJob = await this.#restApi.xoApp.getJob(backupJobId);
|
|
11
|
+
const vm = this.#restApi.getObject(vmId, 'VM');
|
|
12
|
+
if (vm.tags.includes(NO_BAK_TAG)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const vmIds = extractIdsFromSimplePattern(backupJob.vms);
|
|
17
|
+
return vmIds.some(id => id === vm.id);
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
19
|
+
}
|
|
20
|
+
catch (_) {
|
|
21
|
+
const predicate = createPredicate(backupJob.vms);
|
|
22
|
+
return predicate(vm);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
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 { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
|
|
11
|
+
import { inject } from 'inversify';
|
|
12
|
+
import { noSuchObject } from 'xo-common/api-errors.js';
|
|
13
|
+
import { provide } from 'inversify-binding-decorators';
|
|
14
|
+
import { backupLog, backupLogIds, partialBackupLogs } from '../open-api/oa-examples/backup-log.oa-example.mjs';
|
|
15
|
+
import { BackupLogService } from './backup-log.service.mjs';
|
|
16
|
+
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
17
|
+
import { unauthorizedResp } from '../open-api/common/response.common.mjs';
|
|
18
|
+
import { XoController } from '../abstract-classes/xo-controller.mjs';
|
|
19
|
+
let BackupLogController = class BackupLogController extends XoController {
|
|
20
|
+
#backupLogService;
|
|
21
|
+
constructor(restApi, backupLogService) {
|
|
22
|
+
super(restApi);
|
|
23
|
+
this.#backupLogService = backupLogService;
|
|
24
|
+
}
|
|
25
|
+
getAllCollectionObjects() {
|
|
26
|
+
return this.restApi.xoApp.getBackupNgLogsSorted({ filter: this.#backupLogService.isBackupLog });
|
|
27
|
+
}
|
|
28
|
+
async getCollectionObject(id) {
|
|
29
|
+
const log = await this.restApi.xoApp.getBackupNgLogs(id);
|
|
30
|
+
if (!this.#backupLogService.isBackupLog(log)) {
|
|
31
|
+
throw noSuchObject('backup-log');
|
|
32
|
+
}
|
|
33
|
+
return log;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* @example fields "jobName,status,data"
|
|
37
|
+
* @example filter "status:success"
|
|
38
|
+
* @example limit 42
|
|
39
|
+
*/
|
|
40
|
+
async getBackupLogs(req, fields, ndjson, filter, limit) {
|
|
41
|
+
const backupLogs = await this.getObjects({ filter, limit });
|
|
42
|
+
return this.sendObjects(Object.values(backupLogs), req);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* @example id "1753776067468"
|
|
46
|
+
*/
|
|
47
|
+
getBackupLog(id) {
|
|
48
|
+
return this.getObject(id);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
__decorate([
|
|
52
|
+
Example(backupLogIds),
|
|
53
|
+
Example(partialBackupLogs),
|
|
54
|
+
Get(''),
|
|
55
|
+
__param(0, Request()),
|
|
56
|
+
__param(1, Query()),
|
|
57
|
+
__param(2, Query()),
|
|
58
|
+
__param(3, Query()),
|
|
59
|
+
__param(4, Query())
|
|
60
|
+
], BackupLogController.prototype, "getBackupLogs", null);
|
|
61
|
+
__decorate([
|
|
62
|
+
Example(backupLog),
|
|
63
|
+
Get('{id}'),
|
|
64
|
+
__param(0, Path())
|
|
65
|
+
], BackupLogController.prototype, "getBackupLog", null);
|
|
66
|
+
BackupLogController = __decorate([
|
|
67
|
+
Route('backup-logs'),
|
|
68
|
+
Security('*'),
|
|
69
|
+
Response(unauthorizedResp.status, unauthorizedResp.description),
|
|
70
|
+
Tags('backup-logs'),
|
|
71
|
+
provide(BackupLogController),
|
|
72
|
+
__param(0, inject(RestApi)),
|
|
73
|
+
__param(1, inject(BackupLogService))
|
|
74
|
+
], BackupLogController);
|
|
75
|
+
export { BackupLogController };
|
|
@@ -8,13 +8,23 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
10
|
import { Body, Delete, Example, Get, Middlewares, Patch, Path, Post, Put, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
|
|
11
|
+
import { inject } from 'inversify';
|
|
11
12
|
import { json } from 'express';
|
|
12
13
|
import { provide } from 'inversify-binding-decorators';
|
|
13
14
|
import { forbiddenOperation } from 'xo-common/api-errors.js';
|
|
14
15
|
import { createdResp, forbiddenOperationResp, invalidParameters, noContentResp, notFoundResp, resourceAlreadyExists, unauthorizedResp, } from '../open-api/common/response.common.mjs';
|
|
15
16
|
import { group, groupId, groupIds, partialGroups } from '../open-api/oa-examples/group.oa-example.mjs';
|
|
16
17
|
import { XoController } from '../abstract-classes/xo-controller.mjs';
|
|
18
|
+
import { UserService } from '../users/user.service.mjs';
|
|
19
|
+
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
20
|
+
import { limitAndFilterArray } from '../helpers/utils.helper.mjs';
|
|
21
|
+
import { partialUsers, userIds } from '../open-api/oa-examples/user.oa-example.mjs';
|
|
17
22
|
let GroupController = class GroupController extends XoController {
|
|
23
|
+
#userService;
|
|
24
|
+
constructor(restApi, userService) {
|
|
25
|
+
super(restApi);
|
|
26
|
+
this.#userService = userService;
|
|
27
|
+
}
|
|
18
28
|
// --- abstract methods
|
|
19
29
|
getAllCollectionObjects() {
|
|
20
30
|
return this.restApi.xoApp.getAllGroups();
|
|
@@ -85,6 +95,17 @@ let GroupController = class GroupController extends XoController {
|
|
|
85
95
|
}
|
|
86
96
|
await this.restApi.xoApp.addUserToGroup(userId, group.id);
|
|
87
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* @example id "6c81b5e1-afc1-43ea-8f8d-939ceb5f3f90"
|
|
100
|
+
* @example fields "permission,name,id"
|
|
101
|
+
* @example filter "permission:none"
|
|
102
|
+
* @example limit 42
|
|
103
|
+
*/
|
|
104
|
+
async getGroupUsers(req, id, fields, ndjson, filter, limit) {
|
|
105
|
+
const group = await this.getObject(id);
|
|
106
|
+
const users = await Promise.all(group.users.map(id => this.#userService.getUser(id)));
|
|
107
|
+
return this.sendObjects(limitAndFilterArray(users, { filter, limit }), req, 'users');
|
|
108
|
+
}
|
|
88
109
|
};
|
|
89
110
|
__decorate([
|
|
90
111
|
Example(groupIds),
|
|
@@ -143,11 +164,26 @@ __decorate([
|
|
|
143
164
|
__param(0, Path()),
|
|
144
165
|
__param(1, Path())
|
|
145
166
|
], GroupController.prototype, "addUserToGroup", null);
|
|
167
|
+
__decorate([
|
|
168
|
+
Example(userIds),
|
|
169
|
+
Example(partialUsers),
|
|
170
|
+
Get('{id}/users'),
|
|
171
|
+
Tags('users'),
|
|
172
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
173
|
+
__param(0, Request()),
|
|
174
|
+
__param(1, Path()),
|
|
175
|
+
__param(2, Query()),
|
|
176
|
+
__param(3, Query()),
|
|
177
|
+
__param(4, Query()),
|
|
178
|
+
__param(5, Query())
|
|
179
|
+
], GroupController.prototype, "getGroupUsers", null);
|
|
146
180
|
GroupController = __decorate([
|
|
147
181
|
Route('groups'),
|
|
148
182
|
Security('*'),
|
|
149
183
|
Response(unauthorizedResp.status, unauthorizedResp.description),
|
|
150
184
|
Tags('groups'),
|
|
151
|
-
provide(GroupController)
|
|
185
|
+
provide(GroupController),
|
|
186
|
+
__param(0, inject(RestApi)),
|
|
187
|
+
__param(1, inject(UserService))
|
|
152
188
|
], GroupController);
|
|
153
189
|
export { GroupController };
|
|
@@ -14,15 +14,18 @@ import { provide } from 'inversify-binding-decorators';
|
|
|
14
14
|
import { AlarmService } from '../alarms/alarm.service.mjs';
|
|
15
15
|
import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
|
|
16
16
|
import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
|
|
17
|
-
import { host, hostIds, hostSmt, hostStats, partialHosts } from '../open-api/oa-examples/host.oa-example.mjs';
|
|
17
|
+
import { host, hostIds, hostSmt, hostMissingPatches, hostStats, partialHosts, } from '../open-api/oa-examples/host.oa-example.mjs';
|
|
18
18
|
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
19
19
|
import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
|
|
20
|
-
import { internalServerErrorResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
|
|
20
|
+
import { featureUnauthorized, internalServerErrorResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
|
|
21
|
+
import { HostService } from './host.service.mjs';
|
|
21
22
|
let HostController = class HostController extends XapiXoController {
|
|
22
23
|
#alarmService;
|
|
23
|
-
|
|
24
|
+
#hostService;
|
|
25
|
+
constructor(restApi, alarmService, hostService) {
|
|
24
26
|
super('host', restApi);
|
|
25
27
|
this.#alarmService = alarmService;
|
|
28
|
+
this.#hostService = hostService;
|
|
26
29
|
}
|
|
27
30
|
/**
|
|
28
31
|
* @example fields "id,name_label,productBrand"
|
|
@@ -105,6 +108,15 @@ let HostController = class HostController extends XapiXoController {
|
|
|
105
108
|
const enabled = Boolean(await xapiHost.$xapi.isHyperThreadingEnabled(hostId));
|
|
106
109
|
return { enabled };
|
|
107
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Host must be running
|
|
113
|
+
*
|
|
114
|
+
* @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
|
|
115
|
+
*/
|
|
116
|
+
async getMissingPatches(id) {
|
|
117
|
+
const { missingPatches } = await this.#hostService.getMissingPatchesInfo({ filter: host => host.id === id });
|
|
118
|
+
return missingPatches;
|
|
119
|
+
}
|
|
108
120
|
};
|
|
109
121
|
__decorate([
|
|
110
122
|
Example(hostIds),
|
|
@@ -166,6 +178,13 @@ __decorate([
|
|
|
166
178
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
167
179
|
__param(0, Path())
|
|
168
180
|
], HostController.prototype, "gethostSmt", null);
|
|
181
|
+
__decorate([
|
|
182
|
+
Example(hostMissingPatches),
|
|
183
|
+
Get('{id}/missing_patches'),
|
|
184
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
185
|
+
Response(featureUnauthorized.status, featureUnauthorized.description),
|
|
186
|
+
__param(0, Path())
|
|
187
|
+
], HostController.prototype, "getMissingPatches", null);
|
|
169
188
|
HostController = __decorate([
|
|
170
189
|
Route('hosts'),
|
|
171
190
|
Security('*'),
|
|
@@ -173,6 +192,7 @@ HostController = __decorate([
|
|
|
173
192
|
Tags('hosts'),
|
|
174
193
|
provide(HostController),
|
|
175
194
|
__param(0, inject(RestApi)),
|
|
176
|
-
__param(1, inject(AlarmService))
|
|
195
|
+
__param(1, inject(AlarmService)),
|
|
196
|
+
__param(2, inject(HostService))
|
|
177
197
|
], HostController);
|
|
178
198
|
export { HostController };
|
|
@@ -41,13 +41,12 @@ export class HostService {
|
|
|
41
41
|
total,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
const hosts = Object.values(this.#restApi.getObjectsByType('host', opts));
|
|
44
|
+
/**
|
|
45
|
+
* Throw if no authorization
|
|
46
|
+
*/
|
|
47
|
+
async getMissingPatchesInfo({ filter, } = {}) {
|
|
48
|
+
await this.#restApi.xoApp.checkFeatureAuthorization('LIST_MISSING_PATCHES');
|
|
49
|
+
const hosts = Object.values(this.#restApi.getObjectsByType('host', { filter }));
|
|
51
50
|
const missingPatches = new Map();
|
|
52
51
|
const poolsWithMissingPatches = new Set();
|
|
53
52
|
let nHostsWithMissingPatches = 0;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.mjs
CHANGED
|
@@ -32,6 +32,10 @@ const SWAGGER_UI_OPTIONS = {
|
|
|
32
32
|
export default function setupRestApi(express, xoApp) {
|
|
33
33
|
setupContainer(xoApp);
|
|
34
34
|
RegisterRoutes(express);
|
|
35
|
+
express.get(`${BASE_URL}/docs/swagger.json`, (_req, res) => {
|
|
36
|
+
res.setHeader('Content-Type', 'application/json');
|
|
37
|
+
res.json(swaggerOpenApiSpec);
|
|
38
|
+
});
|
|
35
39
|
// do not register the doc at the root level, or it may lead to unwanted behaviour
|
|
36
40
|
// uncomment when all endpoints are migrated to this API
|
|
37
41
|
// express.get('/rest/v0', (_req, res) => res.redirect('/rest/v0/docs'))
|
package/dist/ioc/ioc.mjs
CHANGED
|
@@ -7,6 +7,11 @@ import { XoaService } from '../xoa/xoa.service.mjs';
|
|
|
7
7
|
import { HostService } from '../hosts/host.service.mjs';
|
|
8
8
|
import { PoolService } from '../pools/pool.service.mjs';
|
|
9
9
|
import { AlarmService } from '../alarms/alarm.service.mjs';
|
|
10
|
+
import { VdiService } from '../vdis/vdi.service.mjs';
|
|
11
|
+
import { TaskService } from '../tasks/task.service.mjs';
|
|
12
|
+
import { UserService } from '../users/user.service.mjs';
|
|
13
|
+
import { BackupJobService } from '../backup-jobs/backup-job.service.mjs';
|
|
14
|
+
import { BackupLogService } from '../backup-logs/backup-log.service.mjs';
|
|
10
15
|
const iocContainer = new Container();
|
|
11
16
|
decorate(injectable(), Controller);
|
|
12
17
|
iocContainer.load(buildProviderModule());
|
|
@@ -53,5 +58,36 @@ export function setupContainer(xoApp) {
|
|
|
53
58
|
return new AlarmService(restApi);
|
|
54
59
|
})
|
|
55
60
|
.inSingletonScope();
|
|
61
|
+
iocContainer.bind(TaskService).toDynamicValue(ctx => {
|
|
62
|
+
const restApi = ctx.container.get(RestApi);
|
|
63
|
+
return new TaskService(restApi);
|
|
64
|
+
});
|
|
65
|
+
iocContainer
|
|
66
|
+
.bind(VdiService)
|
|
67
|
+
.toDynamicValue(ctx => {
|
|
68
|
+
const restApi = ctx.container.get(RestApi);
|
|
69
|
+
return new VdiService(restApi);
|
|
70
|
+
})
|
|
71
|
+
.inSingletonScope();
|
|
72
|
+
iocContainer
|
|
73
|
+
.bind(UserService)
|
|
74
|
+
.toDynamicValue(ctx => {
|
|
75
|
+
const restApi = ctx.container.get(RestApi);
|
|
76
|
+
return new UserService(restApi);
|
|
77
|
+
})
|
|
78
|
+
.inSingletonScope();
|
|
79
|
+
iocContainer
|
|
80
|
+
.bind(BackupJobService)
|
|
81
|
+
.toDynamicValue(ctx => {
|
|
82
|
+
const restApi = ctx.container.get(RestApi);
|
|
83
|
+
return new BackupJobService(restApi);
|
|
84
|
+
})
|
|
85
|
+
.inSingletonScope();
|
|
86
|
+
iocContainer
|
|
87
|
+
.bind(BackupLogService)
|
|
88
|
+
.toDynamicValue(() => {
|
|
89
|
+
return new BackupLogService();
|
|
90
|
+
})
|
|
91
|
+
.inSingletonScope();
|
|
56
92
|
}
|
|
57
93
|
export { iocContainer };
|