@xen-orchestra/rest-api 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -4
- package/dist/backup-jobs/backup-job.service.mjs +4 -0
- package/dist/backup-repositories/backup-repositories.controller.mjs +115 -6
- package/dist/backup-repositories/backup-repository.service.mjs +46 -0
- package/dist/ioc/ioc.mjs +8 -0
- package/dist/middlewares/acl.middleware.mjs +10 -1
- package/dist/middlewares/deprecated.middleware.mjs +10 -0
- package/dist/middlewares/generic-error-handler.middleware.mjs +1 -0
- package/dist/open-api/oa-examples/backup-repository.oa-example.mjs +1 -0
- package/dist/open-api/routes/routes.js +266 -20
- package/dist/pbds/pbd.controller.mjs +10 -0
- package/dist/pools/pool.controller.mjs +48 -0
- package/dist/srs/sr.controller.mjs +15 -0
- package/dist/vbds/vbd.controller.mjs +20 -1
- package/dist/vdi-snapshots/vdi-snapshot.controller.mjs +27 -2
- package/dist/vdis/vdi.controller.mjs +26 -2
- package/dist/vifs/vif.controller.mjs +79 -6
- package/dist/vm-snapshots/vm-snapshot.controller.mjs +2 -1
- package/dist/vm-templates/vm-template.controller.mjs +2 -1
- package/dist/vms/vm.controller.mjs +70 -5
- package/dist/vms/vm.service.mjs +29 -11
- package/open-api/spec/swagger.json +1707 -68
- package/package.json +7 -7
|
@@ -44,6 +44,9 @@ let PbdController = class PbdController extends XapiXoController {
|
|
|
44
44
|
return this.getObject(id);
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
|
+
* Required privilege:
|
|
48
|
+
* - resource: pbd, action: plug
|
|
49
|
+
*
|
|
47
50
|
* @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
|
|
48
51
|
*/
|
|
49
52
|
async plugPbd(id, sync) {
|
|
@@ -62,6 +65,9 @@ let PbdController = class PbdController extends XapiXoController {
|
|
|
62
65
|
});
|
|
63
66
|
}
|
|
64
67
|
/**
|
|
68
|
+
* Required privilege:
|
|
69
|
+
* - resource: pbd, action: unplug
|
|
70
|
+
*
|
|
65
71
|
* @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
|
|
66
72
|
*/
|
|
67
73
|
async unplugPbd(id, sync) {
|
|
@@ -106,8 +112,10 @@ __decorate([
|
|
|
106
112
|
Example(taskLocation),
|
|
107
113
|
Extension('x-mcp-exposure', 'confirm'),
|
|
108
114
|
Post('{id}/actions/plug'),
|
|
115
|
+
Middlewares(acl({ resource: 'pbd', action: 'plug', objectId: 'params.id' })),
|
|
109
116
|
SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
|
|
110
117
|
Response(noContentResp.status, noContentResp.description),
|
|
118
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
111
119
|
Response(notFoundResp.status, notFoundResp.description),
|
|
112
120
|
Response(invalidParametersResp.status, invalidParametersResp.description),
|
|
113
121
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
@@ -118,8 +126,10 @@ __decorate([
|
|
|
118
126
|
Example(taskLocation),
|
|
119
127
|
Extension('x-mcp-exposure', 'confirm'),
|
|
120
128
|
Post('{id}/actions/unplug'),
|
|
129
|
+
Middlewares(acl({ resource: 'pbd', action: 'unplug', objectId: 'params.id' })),
|
|
121
130
|
SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
|
|
122
131
|
Response(noContentResp.status, noContentResp.description),
|
|
132
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
123
133
|
Response(notFoundResp.status, notFoundResp.description),
|
|
124
134
|
Response(invalidParametersResp.status, invalidParametersResp.description),
|
|
125
135
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
@@ -423,6 +423,33 @@ let PoolController = class PoolController extends XapiXoController {
|
|
|
423
423
|
},
|
|
424
424
|
});
|
|
425
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* Required privileges:
|
|
428
|
+
* - resource: pool, action: add-host
|
|
429
|
+
* - resource: host, action: join-pool
|
|
430
|
+
*
|
|
431
|
+
* Add a host to the pool.
|
|
432
|
+
*
|
|
433
|
+
* @example id "355ee47d-ff4c-4924-3db2-fd86ae629676"
|
|
434
|
+
* @example body { "host": "c787b75c-3e0d-70fa-d0c3-cbfd382d7e33", "force": "false" }
|
|
435
|
+
*/
|
|
436
|
+
addHost(id, body, sync) {
|
|
437
|
+
const poolId = id;
|
|
438
|
+
const action = async () => {
|
|
439
|
+
const { _auth: { user, password }, _url: { hostnameRaw }, } = this.restApi.xoApp.getXapi(poolId);
|
|
440
|
+
const hostXapi = this.restApi.xoApp.getXapi(body.host);
|
|
441
|
+
await hostXapi.joinPool(hostnameRaw, user, password, body.force);
|
|
442
|
+
};
|
|
443
|
+
return this.createAction(action, {
|
|
444
|
+
sync,
|
|
445
|
+
statusCode: noContentResp.status,
|
|
446
|
+
taskProperties: {
|
|
447
|
+
name: 'add host',
|
|
448
|
+
objectId: poolId,
|
|
449
|
+
params: body,
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
}
|
|
426
453
|
};
|
|
427
454
|
__decorate([
|
|
428
455
|
Example(poolIds),
|
|
@@ -729,6 +756,27 @@ __decorate([
|
|
|
729
756
|
__param(1, Body()),
|
|
730
757
|
__param(2, Query())
|
|
731
758
|
], PoolController.prototype, "managementReconfigure", null);
|
|
759
|
+
__decorate([
|
|
760
|
+
Example(taskLocation),
|
|
761
|
+
Post('{id}/actions/add_host'),
|
|
762
|
+
Middlewares([
|
|
763
|
+
json(),
|
|
764
|
+
acl([
|
|
765
|
+
{ resource: 'pool', action: 'add-host', objectId: 'params.id' },
|
|
766
|
+
{ resource: 'host', action: 'join-pool', objectId: 'body.host' },
|
|
767
|
+
]),
|
|
768
|
+
]),
|
|
769
|
+
SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
|
|
770
|
+
Response(noContentResp.status, noContentResp.description),
|
|
771
|
+
Response(badRequestResp.status, badRequestResp.description),
|
|
772
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
773
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
774
|
+
Response(invalidParametersResp.status, invalidParametersResp.description),
|
|
775
|
+
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
776
|
+
__param(0, Path()),
|
|
777
|
+
__param(1, Body()),
|
|
778
|
+
__param(2, Query())
|
|
779
|
+
], PoolController.prototype, "addHost", null);
|
|
732
780
|
PoolController = __decorate([
|
|
733
781
|
Route('pools'),
|
|
734
782
|
Security('*'),
|
|
@@ -158,6 +158,9 @@ let SrController = class SrController extends XapiXoController {
|
|
|
158
158
|
await sr.$call('remove_tags', tag);
|
|
159
159
|
}
|
|
160
160
|
/**
|
|
161
|
+
* Required privilege:
|
|
162
|
+
* - resource: sr, action: reclaim-space
|
|
163
|
+
*
|
|
161
164
|
* @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
|
|
162
165
|
*/
|
|
163
166
|
async reclaimSpaceSr(id, sync) {
|
|
@@ -176,6 +179,9 @@ let SrController = class SrController extends XapiXoController {
|
|
|
176
179
|
});
|
|
177
180
|
}
|
|
178
181
|
/**
|
|
182
|
+
* Required privilege:
|
|
183
|
+
* - resource: sr, action: scan
|
|
184
|
+
*
|
|
179
185
|
* @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
|
|
180
186
|
*/
|
|
181
187
|
async scanSr(id, sync) {
|
|
@@ -194,6 +200,9 @@ let SrController = class SrController extends XapiXoController {
|
|
|
194
200
|
});
|
|
195
201
|
}
|
|
196
202
|
/**
|
|
203
|
+
* Required privilege:
|
|
204
|
+
* - resource: sr, action: forget
|
|
205
|
+
*
|
|
197
206
|
* @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
|
|
198
207
|
*/
|
|
199
208
|
async forgetSr(id, sync) {
|
|
@@ -329,8 +338,10 @@ __decorate([
|
|
|
329
338
|
Example(taskLocation),
|
|
330
339
|
Extension('x-mcp-exposure', 'confirm'),
|
|
331
340
|
Post('{id}/actions/reclaim_space'),
|
|
341
|
+
Middlewares(acl({ resource: 'sr', action: 'reclaim-space', objectId: 'params.id' })),
|
|
332
342
|
SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
|
|
333
343
|
Response(noContentResp.status, noContentResp.description),
|
|
344
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
334
345
|
Response(notFoundResp.status, notFoundResp.description),
|
|
335
346
|
Response(invalidParametersResp.status, invalidParametersResp.description),
|
|
336
347
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
@@ -341,8 +352,10 @@ __decorate([
|
|
|
341
352
|
Example(taskLocation),
|
|
342
353
|
Extension('x-mcp-exposure', 'confirm'),
|
|
343
354
|
Post('{id}/actions/scan'),
|
|
355
|
+
Middlewares(acl({ resource: 'sr', action: 'scan', objectId: 'params.id' })),
|
|
344
356
|
SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
|
|
345
357
|
Response(noContentResp.status, noContentResp.description),
|
|
358
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
346
359
|
Response(notFoundResp.status, notFoundResp.description),
|
|
347
360
|
Response(invalidParametersResp.status, invalidParametersResp.description),
|
|
348
361
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
@@ -353,8 +366,10 @@ __decorate([
|
|
|
353
366
|
Example(taskLocation),
|
|
354
367
|
Extension('x-mcp-exposure', 'confirm'),
|
|
355
368
|
Post('{id}/actions/forget'),
|
|
369
|
+
Middlewares(acl({ resource: 'sr', action: 'forget', objectId: 'params.id' })),
|
|
356
370
|
SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
|
|
357
371
|
Response(noContentResp.status, noContentResp.description),
|
|
372
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
358
373
|
Response(notFoundResp.status, notFoundResp.description),
|
|
359
374
|
Response(invalidParametersResp.status, invalidParametersResp.description),
|
|
360
375
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
@@ -53,6 +53,9 @@ let VbdController = class VbdController extends XapiXoController {
|
|
|
53
53
|
return this.getObject(id);
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
+
* Required privilege:
|
|
57
|
+
* - resource: vbd, action: create
|
|
58
|
+
*
|
|
56
59
|
* Create a VBD to attach a VDI to a VM
|
|
57
60
|
*
|
|
58
61
|
* @example body { "VM": "4fe90510-8da4-1530-38e2-a7876ef374c7", "VDI": "656052a2-2e3e-467b-88ba-63a9ea5e4a54", "bootable": false, "mode": "RW" }
|
|
@@ -76,6 +79,9 @@ let VbdController = class VbdController extends XapiXoController {
|
|
|
76
79
|
return { id: vbdUuid };
|
|
77
80
|
}
|
|
78
81
|
/**
|
|
82
|
+
* Required privilege:
|
|
83
|
+
* - resource: vbd, action: delete
|
|
84
|
+
*
|
|
79
85
|
* Delete a VBD
|
|
80
86
|
*
|
|
81
87
|
* Removes the virtual block device, detaching the VDI from the VM.
|
|
@@ -142,6 +148,9 @@ let VbdController = class VbdController extends XapiXoController {
|
|
|
142
148
|
});
|
|
143
149
|
}
|
|
144
150
|
/**
|
|
151
|
+
* Required privilege:
|
|
152
|
+
* - resource: vbd, action: connect
|
|
153
|
+
*
|
|
145
154
|
* Hotplug the VBD, dynamically attaching it to the running VM
|
|
146
155
|
* @example id "f07ab729-c0e8-721c-45ec-f11276377030"
|
|
147
156
|
*/
|
|
@@ -161,6 +170,9 @@ let VbdController = class VbdController extends XapiXoController {
|
|
|
161
170
|
});
|
|
162
171
|
}
|
|
163
172
|
/**
|
|
173
|
+
* Required privilege:
|
|
174
|
+
* - resource: vbd, action: disconnect
|
|
175
|
+
*
|
|
164
176
|
* Hot-unplug the VBD, dynamically detaching it from the running VM
|
|
165
177
|
* @example id "f07ab729-c0e8-721c-45ec-f11276377030"
|
|
166
178
|
*/
|
|
@@ -206,8 +218,9 @@ __decorate([
|
|
|
206
218
|
Example(vbdId),
|
|
207
219
|
Extension('x-mcp-exposure', 'confirm'),
|
|
208
220
|
Post(''),
|
|
209
|
-
Middlewares(json()),
|
|
221
|
+
Middlewares([json(), acl({ resource: 'vbd', action: 'create', object: ({ req }) => req.body })]),
|
|
210
222
|
SuccessResponse(createdResp.status, createdResp.description),
|
|
223
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
211
224
|
Response(notFoundResp.status, notFoundResp.description),
|
|
212
225
|
Response(invalidParameters.status, invalidParameters.description),
|
|
213
226
|
__param(0, Body())
|
|
@@ -215,7 +228,9 @@ __decorate([
|
|
|
215
228
|
__decorate([
|
|
216
229
|
Extension('x-mcp-exposure', 'confirm'),
|
|
217
230
|
Delete('{id}'),
|
|
231
|
+
Middlewares(acl({ resource: 'vbd', action: 'delete', objectId: 'params.id' })),
|
|
218
232
|
SuccessResponse(noContentResp.status, noContentResp.description),
|
|
233
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
219
234
|
Response(notFoundResp.status, notFoundResp.description),
|
|
220
235
|
__param(0, Path())
|
|
221
236
|
], VbdController.prototype, "deleteVbd", null);
|
|
@@ -270,8 +285,10 @@ __decorate([
|
|
|
270
285
|
Example(taskLocation),
|
|
271
286
|
Extension('x-mcp-exposure', 'confirm'),
|
|
272
287
|
Post('{id}/actions/connect'),
|
|
288
|
+
Middlewares(acl({ resource: 'vbd', action: 'connect', objectId: 'params.id' })),
|
|
273
289
|
SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
|
|
274
290
|
Response(noContentResp.status, noContentResp.description),
|
|
291
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
275
292
|
Response(notFoundResp.status, notFoundResp.description),
|
|
276
293
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
277
294
|
__param(0, Path()),
|
|
@@ -281,8 +298,10 @@ __decorate([
|
|
|
281
298
|
Example(taskLocation),
|
|
282
299
|
Extension('x-mcp-exposure', 'confirm'),
|
|
283
300
|
Post('{id}/actions/disconnect'),
|
|
301
|
+
Middlewares(acl({ resource: 'vbd', action: 'disconnect', objectId: 'params.id' })),
|
|
284
302
|
SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
|
|
285
303
|
Response(noContentResp.status, noContentResp.description),
|
|
304
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
286
305
|
Response(notFoundResp.status, notFoundResp.description),
|
|
287
306
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
288
307
|
__param(0, Path()),
|
|
@@ -7,11 +7,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
-
import { Delete, Example, Extension, Get, Path, Put, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
|
|
10
|
+
import { Delete, Example, Extension, Get, Middlewares, Path, Put, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
|
|
11
11
|
import { inject } from 'inversify';
|
|
12
12
|
import { provide } from 'inversify-binding-decorators';
|
|
13
|
+
import { acl } from '../middlewares/acl.middleware.mjs';
|
|
13
14
|
import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
|
|
14
|
-
import { badRequestResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
|
|
15
|
+
import { badRequestResp, forbiddenOperationResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
|
|
15
16
|
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
16
17
|
import { partialVdiSnapshots, vdiSnapshot, vdiSnapshotIds } from '../open-api/oa-examples/vdi-snapshot.oa-example.mjs';
|
|
17
18
|
import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
|
|
@@ -43,6 +44,8 @@ let VdiSnapshotController = class VdiSnapshotController extends XapiXoController
|
|
|
43
44
|
});
|
|
44
45
|
}
|
|
45
46
|
/**
|
|
47
|
+
* Required privilege:
|
|
48
|
+
* - resource: vdi-snapshot, action: export
|
|
46
49
|
*
|
|
47
50
|
* Export VDI-snapshot content
|
|
48
51
|
*
|
|
@@ -59,6 +62,9 @@ let VdiSnapshotController = class VdiSnapshotController extends XapiXoController
|
|
|
59
62
|
return stream;
|
|
60
63
|
}
|
|
61
64
|
/**
|
|
65
|
+
* Required privilege:
|
|
66
|
+
* - resource: vdi-snapshot, action: read
|
|
67
|
+
*
|
|
62
68
|
* @example id "d2727772-735b-478f-b6f9-11e7db56dfd0"
|
|
63
69
|
*/
|
|
64
70
|
getVdiSnapshot(id) {
|
|
@@ -85,6 +91,9 @@ let VdiSnapshotController = class VdiSnapshotController extends XapiXoController
|
|
|
85
91
|
});
|
|
86
92
|
}
|
|
87
93
|
/**
|
|
94
|
+
* Required privilege:
|
|
95
|
+
* - resource: vdi-snapshot, action: delete
|
|
96
|
+
*
|
|
88
97
|
* @example id "d2727772-735b-478f-b6f9-11e7db56dfd0"
|
|
89
98
|
*/
|
|
90
99
|
async deleteVdiSnapshot(id) {
|
|
@@ -126,6 +135,9 @@ let VdiSnapshotController = class VdiSnapshotController extends XapiXoController
|
|
|
126
135
|
});
|
|
127
136
|
}
|
|
128
137
|
/**
|
|
138
|
+
* Required privilege:
|
|
139
|
+
* - resource: vdi-snapshot, action: update:tags
|
|
140
|
+
*
|
|
129
141
|
* @example id "d2727772-735b-478f-b6f9-11e7db56dfd0"
|
|
130
142
|
* @example tag "from-rest-api"
|
|
131
143
|
*/
|
|
@@ -134,6 +146,9 @@ let VdiSnapshotController = class VdiSnapshotController extends XapiXoController
|
|
|
134
146
|
await vdiSnapshot.$call('add_tags', tag);
|
|
135
147
|
}
|
|
136
148
|
/**
|
|
149
|
+
* Required privilege:
|
|
150
|
+
* - resource: vdi-snapshot, action: update:tags
|
|
151
|
+
*
|
|
137
152
|
* @example id "d2727772-735b-478f-b6f9-11e7db56dfd0"
|
|
138
153
|
* @example tag "from-rest-api"
|
|
139
154
|
*/
|
|
@@ -158,7 +173,9 @@ __decorate([
|
|
|
158
173
|
__decorate([
|
|
159
174
|
Extension('x-mcp-exposure', 'deny'),
|
|
160
175
|
Get('{id}.{format}'),
|
|
176
|
+
Middlewares(acl({ resource: 'vdi-snapshot', action: 'export', objectId: 'params.id' })),
|
|
161
177
|
SuccessResponse(200, 'Download started', 'application/octet-stream'),
|
|
178
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
162
179
|
Response(notFoundResp.status, notFoundResp.description),
|
|
163
180
|
Response(422, 'Invalid format'),
|
|
164
181
|
__param(0, Request()),
|
|
@@ -169,6 +186,8 @@ __decorate([
|
|
|
169
186
|
Example(vdiSnapshot),
|
|
170
187
|
Extension('x-mcp-exposure', 'allow'),
|
|
171
188
|
Get('{id}'),
|
|
189
|
+
Middlewares(acl({ resource: 'vdi-snapshot', action: 'read', objectId: 'params.id' })),
|
|
190
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
172
191
|
Response(notFoundResp.status, notFoundResp.description),
|
|
173
192
|
__param(0, Path())
|
|
174
193
|
], VdiSnapshotController.prototype, "getVdiSnapshot", null);
|
|
@@ -190,7 +209,9 @@ __decorate([
|
|
|
190
209
|
__decorate([
|
|
191
210
|
Extension('x-mcp-exposure', 'confirm'),
|
|
192
211
|
Delete('{id}'),
|
|
212
|
+
Middlewares(acl({ resource: 'vdi-snapshot', action: 'delete', objectId: 'params.id' })),
|
|
193
213
|
SuccessResponse(noContentResp.status, noContentResp.description),
|
|
214
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
194
215
|
Response(notFoundResp.status, notFoundResp.description),
|
|
195
216
|
__param(0, Path())
|
|
196
217
|
], VdiSnapshotController.prototype, "deleteVdiSnapshot", null);
|
|
@@ -229,7 +250,9 @@ __decorate([
|
|
|
229
250
|
__decorate([
|
|
230
251
|
Extension('x-mcp-exposure', 'confirm'),
|
|
231
252
|
Put('{id}/tags/{tag}'),
|
|
253
|
+
Middlewares(acl({ resource: 'vdi-snapshot', action: 'update:tags', objectId: 'params.id' })),
|
|
232
254
|
SuccessResponse(noContentResp.status, noContentResp.description),
|
|
255
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
233
256
|
Response(notFoundResp.status, notFoundResp.description),
|
|
234
257
|
__param(0, Path()),
|
|
235
258
|
__param(1, Path())
|
|
@@ -237,7 +260,9 @@ __decorate([
|
|
|
237
260
|
__decorate([
|
|
238
261
|
Extension('x-mcp-exposure', 'confirm'),
|
|
239
262
|
Delete('{id}/tags/{tag}'),
|
|
263
|
+
Middlewares(acl({ resource: 'vdi-snapshot', action: 'update:tags', objectId: 'params.id' })),
|
|
240
264
|
SuccessResponse(noContentResp.status, noContentResp.description),
|
|
265
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
241
266
|
Response(notFoundResp.status, notFoundResp.description),
|
|
242
267
|
__param(0, Path()),
|
|
243
268
|
__param(1, Path())
|
|
@@ -110,6 +110,10 @@ let VdiController = class VdiController extends XapiXoController {
|
|
|
110
110
|
/**
|
|
111
111
|
* Create an empty VDI.
|
|
112
112
|
*
|
|
113
|
+
* Required privileges:
|
|
114
|
+
* - resource: sr, action: import:vdi (on the target SR)
|
|
115
|
+
* - resource: vdi, action: create
|
|
116
|
+
*
|
|
113
117
|
* @example body { "srId": "c4284e12-37c9-7967-b9e8-83ef229c3e03", "virtual_size": 10737418240, "name_label": "test VDI" }
|
|
114
118
|
*/
|
|
115
119
|
async createVdi(body) {
|
|
@@ -173,6 +177,10 @@ let VdiController = class VdiController extends XapiXoController {
|
|
|
173
177
|
/**
|
|
174
178
|
* Migrate a VDI to another SR.
|
|
175
179
|
*
|
|
180
|
+
* Required privileges:
|
|
181
|
+
* - resource: vdi, action: migrate-send
|
|
182
|
+
* - resource: sr, action: migrate-receive (on the target SR)
|
|
183
|
+
*
|
|
176
184
|
* Note: After migration, the VDI will have a new ID. The new ID is returned in the response.
|
|
177
185
|
*
|
|
178
186
|
* @example id "c77f9955-c1d2-4b39-aa1c-73cdb2dacb7e"
|
|
@@ -280,8 +288,15 @@ __decorate([
|
|
|
280
288
|
Example(vdiId),
|
|
281
289
|
Extension('x-mcp-exposure', 'confirm'),
|
|
282
290
|
Post(''),
|
|
283
|
-
Middlewares(
|
|
291
|
+
Middlewares([
|
|
292
|
+
json(),
|
|
293
|
+
acl([
|
|
294
|
+
{ resource: 'sr', action: 'import:vdi', objectId: 'body.srId' },
|
|
295
|
+
{ resource: 'vdi', action: 'create', object: ({ req }) => req.body },
|
|
296
|
+
]),
|
|
297
|
+
]),
|
|
284
298
|
SuccessResponse(createdResp.status, createdResp.description),
|
|
299
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
285
300
|
Response(notFoundResp.status, notFoundResp.description),
|
|
286
301
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
287
302
|
__param(0, Body())
|
|
@@ -332,8 +347,17 @@ __decorate([
|
|
|
332
347
|
Example(vdiId),
|
|
333
348
|
Extension('x-mcp-exposure', 'confirm'),
|
|
334
349
|
Post('{id}/actions/migrate'),
|
|
335
|
-
Middlewares(
|
|
350
|
+
Middlewares([
|
|
351
|
+
json(),
|
|
352
|
+
// Two separate checks allow independent control: a user can be allowed to migrate a VDI away
|
|
353
|
+
// without being allowed to place VDIs on any specific SR, and vice versa.
|
|
354
|
+
acl([
|
|
355
|
+
{ resource: 'vdi', action: 'migrate-send', objectId: 'params.id' },
|
|
356
|
+
{ resource: 'sr', action: 'migrate-receive', objectId: 'body.srId' },
|
|
357
|
+
]),
|
|
358
|
+
]),
|
|
336
359
|
SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
|
|
360
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
337
361
|
Response(200, 'Ok'),
|
|
338
362
|
Response(notFoundResp.status, notFoundResp.description),
|
|
339
363
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
@@ -7,10 +7,11 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
-
import { Body, Delete, Example, Extension, Get, Middlewares, Path, Post, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
|
|
10
|
+
import { Body, Delete, Example, Extension, Get, Middlewares, Patch, Path, Post, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
|
|
11
11
|
import { inject } from 'inversify';
|
|
12
12
|
import { json } from 'express';
|
|
13
|
-
import {
|
|
13
|
+
import { SUPPORTED_ACTIONS_BY_RESOURCE } from '@xen-orchestra/acl';
|
|
14
|
+
import { acl, actionsFromBody } from '../middlewares/acl.middleware.mjs';
|
|
14
15
|
import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
|
|
15
16
|
import { provide } from 'inversify-binding-decorators';
|
|
16
17
|
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
@@ -22,6 +23,7 @@ import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.m
|
|
|
22
23
|
import { AlarmService } from '../alarms/alarm.service.mjs';
|
|
23
24
|
import { messageIds, partialMessages } from '../open-api/oa-examples/message.oa-example.mjs';
|
|
24
25
|
import { taskIds, partialTasks, taskLocation } from '../open-api/oa-examples/task.oa-example.mjs';
|
|
26
|
+
const UPDATE_VIF_ACTIONS = Object.keys(SUPPORTED_ACTIONS_BY_RESOURCE.vif.update).map(k => `update:${k}`);
|
|
25
27
|
let VifController = class VifController extends XapiXoController {
|
|
26
28
|
#alarmService;
|
|
27
29
|
constructor(restApi, alarmService) {
|
|
@@ -51,6 +53,39 @@ let VifController = class VifController extends XapiXoController {
|
|
|
51
53
|
getVif(id) {
|
|
52
54
|
return this.getObject(id);
|
|
53
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Partial update of a VIF: only the fields present in the body are modified.
|
|
58
|
+
* Setting the allowed IPs to a non-empty list switches the locking mode to `locked`.
|
|
59
|
+
* `rateLimit` is expressed in kB/s (kilobytes per second).
|
|
60
|
+
*
|
|
61
|
+
* Required privilege per field provided in the body:
|
|
62
|
+
* - resource: vif, action: update:<field>
|
|
63
|
+
*
|
|
64
|
+
* @example id "f028c5d4-578a-332c-394e-087aaca32dd3"
|
|
65
|
+
* @example body { "lockingMode": "locked", "allowedIpv4Addresses": ["192.168.0.42"], "rateLimit": 1000 }
|
|
66
|
+
*/
|
|
67
|
+
async updateVif(id, body) {
|
|
68
|
+
const vifId = id;
|
|
69
|
+
const { allowedIpv4Addresses, allowedIpv6Addresses, lockingMode, rateLimit, txChecksumming } = body;
|
|
70
|
+
// resolve first so a missing VIF 404s before any side effect
|
|
71
|
+
const vif = this.getObject(vifId);
|
|
72
|
+
if (Object.keys(body).length === 0) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// keep XO's IP-pool accounting in sync when the allowed IPs change
|
|
76
|
+
if (allowedIpv4Addresses !== undefined || allowedIpv6Addresses !== undefined) {
|
|
77
|
+
const oldIpAddresses = vif.allowedIpv4Addresses.concat(vif.allowedIpv6Addresses);
|
|
78
|
+
const newIpAddresses = (allowedIpv4Addresses ?? vif.allowedIpv4Addresses).concat(allowedIpv6Addresses ?? vif.allowedIpv6Addresses);
|
|
79
|
+
await this.restApi.xoApp.allocIpAddresses(vifId, newIpAddresses.filter(address => !oldIpAddresses.includes(address)), oldIpAddresses.filter(address => !newIpAddresses.includes(address)));
|
|
80
|
+
}
|
|
81
|
+
await this.getXapi(vifId).editVif(vifId, {
|
|
82
|
+
ipv4Allowed: allowedIpv4Addresses,
|
|
83
|
+
ipv6Allowed: allowedIpv6Addresses,
|
|
84
|
+
lockingMode,
|
|
85
|
+
rateLimit,
|
|
86
|
+
txChecksumming,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
54
89
|
/**
|
|
55
90
|
* Returns all alarms that match the following privilege:
|
|
56
91
|
* - resource: alarm, action: read
|
|
@@ -106,12 +141,15 @@ let VifController = class VifController extends XapiXoController {
|
|
|
106
141
|
});
|
|
107
142
|
}
|
|
108
143
|
/**
|
|
144
|
+
* Required privilege:
|
|
145
|
+
* - resource: vif, action: create
|
|
146
|
+
*
|
|
109
147
|
* @example body {
|
|
110
148
|
* "networkId": "6b6ca0f5-6611-0636-4b0a-1fb1c1e96414",
|
|
111
149
|
* "vmId": "613f541c-4bed-fc77-7ca8-2db6b68f079c",
|
|
112
|
-
*
|
|
113
|
-
*"ethtool-tx": "false"
|
|
114
|
-
*
|
|
150
|
+
* "other_config": {
|
|
151
|
+
* "ethtool-tx": "false"
|
|
152
|
+
* },
|
|
115
153
|
* "qos_algorithm_params": {
|
|
116
154
|
* "kbps": "42"
|
|
117
155
|
* },
|
|
@@ -137,6 +175,9 @@ let VifController = class VifController extends XapiXoController {
|
|
|
137
175
|
return { id: xapiVif.uuid };
|
|
138
176
|
}
|
|
139
177
|
/**
|
|
178
|
+
* Required privilege:
|
|
179
|
+
* - resource: vif, action: delete
|
|
180
|
+
*
|
|
140
181
|
* @example id "6b6ca0f5-6611-0636-4b0a-1fb1c1e96414"
|
|
141
182
|
*/
|
|
142
183
|
async destroyVif(id) {
|
|
@@ -147,6 +188,9 @@ let VifController = class VifController extends XapiXoController {
|
|
|
147
188
|
* Hotplug the VIF, dynamically attaching it to the running VM
|
|
148
189
|
* Requires PV drivers to be installed on the VM
|
|
149
190
|
*
|
|
191
|
+
* Required privilege:
|
|
192
|
+
* - resource: vif, action: connect
|
|
193
|
+
*
|
|
150
194
|
* @example id "f07ab729-c0e8-721c-45ec-f11276377030"
|
|
151
195
|
*/
|
|
152
196
|
async connectVif(id, sync) {
|
|
@@ -168,6 +212,9 @@ let VifController = class VifController extends XapiXoController {
|
|
|
168
212
|
* Hot-unplug the VIF, dynamically detaching it from the running VM
|
|
169
213
|
* Requires PV drivers to be installed on the VM
|
|
170
214
|
*
|
|
215
|
+
* Required privilege:
|
|
216
|
+
* - resource: vif, action: disconnect
|
|
217
|
+
*
|
|
171
218
|
* @example id "f07ab729-c0e8-721c-45ec-f11276377030"
|
|
172
219
|
*/
|
|
173
220
|
async disconnectVif(id, sync) {
|
|
@@ -208,6 +255,25 @@ __decorate([
|
|
|
208
255
|
Response(notFoundResp.status, notFoundResp.description),
|
|
209
256
|
__param(0, Path())
|
|
210
257
|
], VifController.prototype, "getVif", null);
|
|
258
|
+
__decorate([
|
|
259
|
+
Extension('x-mcp-exposure', 'confirm'),
|
|
260
|
+
Patch('{id}'),
|
|
261
|
+
Middlewares([
|
|
262
|
+
json(),
|
|
263
|
+
acl({
|
|
264
|
+
resource: 'vif',
|
|
265
|
+
actions: actionsFromBody(UPDATE_VIF_ACTIONS),
|
|
266
|
+
objectId: 'params.id',
|
|
267
|
+
}),
|
|
268
|
+
]),
|
|
269
|
+
SuccessResponse(noContentResp.status, noContentResp.description),
|
|
270
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
271
|
+
Response(notFoundResp.status, notFoundResp.description),
|
|
272
|
+
Response(invalidParametersResp.status, invalidParametersResp.description),
|
|
273
|
+
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
274
|
+
__param(0, Path()),
|
|
275
|
+
__param(1, Body())
|
|
276
|
+
], VifController.prototype, "updateVif", null);
|
|
211
277
|
__decorate([
|
|
212
278
|
Example(genericAlarmsExample),
|
|
213
279
|
Extension('x-mcp-exposure', 'allow'),
|
|
@@ -259,9 +325,10 @@ __decorate([
|
|
|
259
325
|
Example(vifId),
|
|
260
326
|
Extension('x-mcp-exposure', 'confirm'),
|
|
261
327
|
Post(''),
|
|
262
|
-
Middlewares(json()),
|
|
328
|
+
Middlewares([json(), acl({ resource: 'vif', action: 'create', object: ({ req }) => req.body })]),
|
|
263
329
|
SuccessResponse(createdResp.status, createdResp.description),
|
|
264
330
|
Response(notFoundResp.status, notFoundResp.description),
|
|
331
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
265
332
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
266
333
|
Response(invalidParametersResp.status, invalidParametersResp.description),
|
|
267
334
|
__param(0, Body())
|
|
@@ -270,6 +337,8 @@ __decorate([
|
|
|
270
337
|
Extension('x-mcp-exposure', 'confirm'),
|
|
271
338
|
Delete('{id}'),
|
|
272
339
|
SuccessResponse(noContentResp.status, noContentResp.description),
|
|
340
|
+
Middlewares(acl({ resource: 'vif', action: 'delete', objectId: 'params.id' })),
|
|
341
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
273
342
|
Response(notFoundResp.status, notFoundResp.description),
|
|
274
343
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
275
344
|
__param(0, Path())
|
|
@@ -277,9 +346,11 @@ __decorate([
|
|
|
277
346
|
__decorate([
|
|
278
347
|
Example(taskLocation),
|
|
279
348
|
Extension('x-mcp-exposure', 'confirm'),
|
|
349
|
+
Middlewares(acl({ resource: 'vif', action: 'connect', objectId: 'params.id' })),
|
|
280
350
|
Post('{id}/actions/connect'),
|
|
281
351
|
SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
|
|
282
352
|
Response(noContentResp.status, noContentResp.description),
|
|
353
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
283
354
|
Response(notFoundResp.status, notFoundResp.description),
|
|
284
355
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
285
356
|
__param(0, Path()),
|
|
@@ -289,8 +360,10 @@ __decorate([
|
|
|
289
360
|
Example(taskLocation),
|
|
290
361
|
Extension('x-mcp-exposure', 'confirm'),
|
|
291
362
|
Post('{id}/actions/disconnect'),
|
|
363
|
+
Middlewares(acl({ resource: 'vif', action: 'disconnect', objectId: 'params.id' })),
|
|
292
364
|
SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
|
|
293
365
|
Response(noContentResp.status, noContentResp.description),
|
|
366
|
+
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
294
367
|
Response(notFoundResp.status, notFoundResp.description),
|
|
295
368
|
Response(internalServerErrorResp.status, internalServerErrorResp.description),
|
|
296
369
|
__param(0, Path()),
|
|
@@ -21,6 +21,7 @@ import { provide } from 'inversify-binding-decorators';
|
|
|
21
21
|
import { partialVmSnapshots, vmSnapshot, vmSnapshotIds, vmSnapshotVdis, } from '../open-api/oa-examples/vm-snapshot.oa-example.mjs';
|
|
22
22
|
import { VmService } from '../vms/vm.service.mjs';
|
|
23
23
|
import { messageIds, partialMessages } from '../open-api/oa-examples/message.oa-example.mjs';
|
|
24
|
+
import { vmExportCompressDeprecated } from '../middlewares/deprecated.middleware.mjs';
|
|
24
25
|
let VmSnapshotController = class VmSnapshotController extends XapiXoController {
|
|
25
26
|
#alarmService;
|
|
26
27
|
#vmService;
|
|
@@ -191,7 +192,7 @@ __decorate([
|
|
|
191
192
|
__decorate([
|
|
192
193
|
Extension('x-mcp-exposure', 'deny'),
|
|
193
194
|
Get('{id}.{format}'),
|
|
194
|
-
Middlewares(acl({ resource: 'vm-snapshot', action: 'export', objectId: 'params.id' })),
|
|
195
|
+
Middlewares([acl({ resource: 'vm-snapshot', action: 'export', objectId: 'params.id' }), vmExportCompressDeprecated]),
|
|
195
196
|
SuccessResponse(200, 'Download started', 'application/octet-stream'),
|
|
196
197
|
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
197
198
|
Response(notFoundResp.status, notFoundResp.description),
|
|
@@ -21,6 +21,7 @@ import { partialVmTemplates, vmTemplate, vmTemplateIds, vmTemplateVdis, } from '
|
|
|
21
21
|
import { VmService } from '../vms/vm.service.mjs';
|
|
22
22
|
import { messageIds, partialMessages } from '../open-api/oa-examples/message.oa-example.mjs';
|
|
23
23
|
import { partialTasks, taskIds } from '../open-api/oa-examples/task.oa-example.mjs';
|
|
24
|
+
import { vmExportCompressDeprecated } from '../middlewares/deprecated.middleware.mjs';
|
|
24
25
|
let VmTemplateController = class VmTemplateController extends XapiXoController {
|
|
25
26
|
#alarmService;
|
|
26
27
|
#vmService;
|
|
@@ -191,7 +192,7 @@ __decorate([
|
|
|
191
192
|
__decorate([
|
|
192
193
|
Extension('x-mcp-exposure', 'deny'),
|
|
193
194
|
Get('{id}.{format}'),
|
|
194
|
-
Middlewares(acl({ resource: 'vm-template', action: 'export', objectId: 'params.id' })),
|
|
195
|
+
Middlewares([acl({ resource: 'vm-template', action: 'export', objectId: 'params.id' }), vmExportCompressDeprecated]),
|
|
195
196
|
SuccessResponse(200, 'Download started', 'application/octet-stream'),
|
|
196
197
|
Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
|
|
197
198
|
Response(notFoundResp.status, notFoundResp.description),
|