@xen-orchestra/rest-api 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/abstract-classes/base-controller.mjs +24 -2
  2. package/dist/alarms/alarm.controller.mjs +11 -61
  3. package/dist/alarms/alarm.service.mjs +67 -0
  4. package/dist/backup-repositories/backup-repositories.controller.mjs +61 -0
  5. package/dist/groups/group.controller.mjs +15 -2
  6. package/dist/helpers/object-wrapper.helper.mjs +8 -1
  7. package/dist/helpers/utils.helper.mjs +74 -1
  8. package/dist/hosts/host.controller.mjs +87 -3
  9. package/dist/hosts/host.service.mjs +78 -0
  10. package/dist/ioc/ioc.mjs +25 -1
  11. package/dist/messages/message.controller.mjs +2 -2
  12. package/dist/middlewares/generic-error-handler.middleware.mjs +15 -11
  13. package/dist/middlewares/tsoa-to-xo-error.middleware.mjs +1 -1
  14. package/dist/networks/network.controller.mjs +34 -2
  15. package/dist/open-api/oa-examples/alarm.oa-example.mjs +12 -0
  16. package/dist/open-api/oa-examples/backup-repository.oa-example.mjs +31 -0
  17. package/dist/open-api/oa-examples/pool.oa-example.mjs +401 -0
  18. package/dist/open-api/oa-examples/user.oa-example.mjs +1 -0
  19. package/dist/open-api/routes/routes.js +708 -113
  20. package/dist/pifs/pif.controller.mjs +34 -2
  21. package/dist/pools/pool.controller.mjs +84 -3
  22. package/dist/pools/pool.service.mjs +212 -0
  23. package/dist/rest-api/rest-api.mjs +6 -1
  24. package/dist/srs/sr.controller.mjs +34 -2
  25. package/dist/users/user.controller.mjs +32 -3
  26. package/dist/vdi-snapshots/vdi-snapshot.controller.mjs +34 -2
  27. package/dist/vdis/vdi.controller.mjs +34 -2
  28. package/dist/vm-templates/vm-template.controller.mjs +34 -2
  29. package/dist/vms/vm.controller.mjs +1 -1
  30. package/dist/vms/vm.service.mjs +40 -0
  31. package/dist/xoa/xoa.service.mjs +96 -110
  32. package/open-api/spec/swagger.json +6069 -1899
  33. package/package.json +3 -3
@@ -10,13 +10,18 @@ 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 { provide } from 'inversify-binding-decorators';
13
+ import { AlarmService } from '../alarms/alarm.service.mjs';
14
+ import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
15
+ import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
13
16
  import { notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
14
17
  import { partialPifs, pif, pifIds } from '../open-api/oa-examples/pif.oa-example.mjs';
15
18
  import { RestApi } from '../rest-api/rest-api.mjs';
16
19
  import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
17
20
  let PifController = class PifController extends XapiXoController {
18
- constructor(restApi) {
21
+ #alarmService;
22
+ constructor(restApi, alarmService) {
19
23
  super('PIF', restApi);
24
+ this.#alarmService = alarmService;
20
25
  }
21
26
  /**
22
27
  * @example fields "attached,device,deviceName,id"
@@ -32,6 +37,20 @@ let PifController = class PifController extends XapiXoController {
32
37
  getPif(id) {
33
38
  return this.getObject(id);
34
39
  }
40
+ /**
41
+ * @example id "d9e42451-3794-089f-de81-4ee0e6137bee"
42
+ * @example fields "id,time"
43
+ * @example filter "time:>1747053793"
44
+ * @example limit 42
45
+ */
46
+ getPifAlarms(req, id, fields, ndjson, filter, limit) {
47
+ const pif = this.getObject(id);
48
+ const alarms = this.#alarmService.getAlarms({
49
+ filter: `${escapeUnsafeComplexMatcher(filter) ?? ''} object:uuid:${pif.uuid}`,
50
+ limit,
51
+ });
52
+ return this.sendObjects(Object.values(alarms), req, 'alarms');
53
+ }
35
54
  };
36
55
  __decorate([
37
56
  Example(pifIds),
@@ -49,12 +68,25 @@ __decorate([
49
68
  Response(notFoundResp.status, notFoundResp.description),
50
69
  __param(0, Path())
51
70
  ], PifController.prototype, "getPif", null);
71
+ __decorate([
72
+ Example(genericAlarmsExample),
73
+ Get('{id}/alarms'),
74
+ Tags('alarms'),
75
+ Response(notFoundResp.status, notFoundResp.description),
76
+ __param(0, Request()),
77
+ __param(1, Path()),
78
+ __param(2, Query()),
79
+ __param(3, Query()),
80
+ __param(4, Query()),
81
+ __param(5, Query())
82
+ ], PifController.prototype, "getPifAlarms", null);
52
83
  PifController = __decorate([
53
84
  Route('pifs'),
54
85
  Security('*'),
55
86
  Response(unauthorizedResp.status, unauthorizedResp.description),
56
87
  Tags('pifs'),
57
88
  provide(PifController),
58
- __param(0, inject(RestApi))
89
+ __param(0, inject(RestApi)),
90
+ __param(1, inject(AlarmService))
59
91
  ], PifController);
60
92
  export { PifController };
@@ -9,21 +9,30 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
9
9
  };
10
10
  import { Example, Get, Path, Query, Response, Request, Route, Security, Tags, Post, Middlewares, Body, SuccessResponse, } from 'tsoa';
11
11
  import { inject } from 'inversify';
12
+ import { PassThrough } from 'node:stream';
12
13
  import { provide } from 'inversify-binding-decorators';
13
14
  import { json } from 'express';
14
15
  import { RestApi } from '../rest-api/rest-api.mjs';
15
16
  import { asynchronousActionResp, createdResp, featureUnauthorized, internalServerErrorResp, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
16
17
  import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
17
- import { createVm, importVm, partialPools, pool, poolIds } from '../open-api/oa-examples/pool.oa-example.mjs';
18
+ import { AlarmService } from '../alarms/alarm.service.mjs';
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';
18
21
  import { taskLocation } from '../open-api/oa-examples/task.oa-example.mjs';
19
22
  import { createNetwork } from '../open-api/oa-examples/schedule.oa-example.mjs';
20
23
  import { BASE_URL } from '../index.mjs';
21
24
  import { VmService } from '../vms/vm.service.mjs';
25
+ import { PoolService } from './pool.service.mjs';
26
+ import { escapeUnsafeComplexMatcher, NDJSON_CONTENT_TYPE } from '../helpers/utils.helper.mjs';
22
27
  let PoolController = class PoolController extends XapiXoController {
23
28
  #vmService;
24
- constructor(restApi, vmService) {
29
+ #poolService;
30
+ #alarmService;
31
+ constructor(restApi, vmService, poolService, alarmService) {
25
32
  super('pool', restApi);
26
33
  this.#vmService = vmService;
34
+ this.#poolService = poolService;
35
+ this.#alarmService = alarmService;
27
36
  }
28
37
  /**
29
38
  *
@@ -170,6 +179,48 @@ let PoolController = class PoolController extends XapiXoController {
170
179
  },
171
180
  });
172
181
  }
182
+ /**
183
+ * @example id "355ee47d-ff4c-4924-3db2-fd86ae629677"
184
+ */
185
+ getStats(id, granularity) {
186
+ return this.restApi.xoApp.getXapiPoolStats(id, granularity);
187
+ }
188
+ /**
189
+ * @example id "355ee47d-ff4c-4924-3db2-fd86ae629677"
190
+ */
191
+ async getPoolDashboard(req, id, ndjson) {
192
+ const poolId = id;
193
+ // throw if pool not found
194
+ this.getObject(poolId);
195
+ const stream = ndjson ? new PassThrough() : undefined;
196
+ const isStream = ndjson && stream !== undefined;
197
+ if (isStream) {
198
+ const res = req.res;
199
+ res.setHeader('Content-Type', NDJSON_CONTENT_TYPE);
200
+ stream.pipe(res);
201
+ }
202
+ const dashboard = await this.#poolService.getDashboard(poolId, { stream });
203
+ if (isStream) {
204
+ stream.end();
205
+ }
206
+ else {
207
+ return dashboard;
208
+ }
209
+ }
210
+ /**
211
+ * @example id "355ee47d-ff4c-4924-3db2-fd86ae629676"
212
+ * @example fields "id,time"
213
+ * @example filter "time:>1747053793"
214
+ * @example limit 42
215
+ */
216
+ getPoolAlarms(req, id, fields, ndjson, filter, limit) {
217
+ const pool = this.getObject(id);
218
+ const alarms = this.#alarmService.getAlarms({
219
+ filter: `${escapeUnsafeComplexMatcher(filter) ?? ''} object:uuid:${pool.uuid}`,
220
+ limit,
221
+ });
222
+ return this.sendObjects(Object.values(alarms), req, 'alarms');
223
+ }
173
224
  };
174
225
  __decorate([
175
226
  Example(poolIds),
@@ -256,6 +307,34 @@ __decorate([
256
307
  __param(1, Body()),
257
308
  __param(2, Query())
258
309
  ], PoolController.prototype, "createVm", null);
310
+ __decorate([
311
+ Example(poolStats),
312
+ Get('{id}/stats'),
313
+ Response(notFoundResp.status, notFoundResp.description),
314
+ Response(422, 'Invalid granularity'),
315
+ __param(0, Path()),
316
+ __param(1, Query())
317
+ ], PoolController.prototype, "getStats", null);
318
+ __decorate([
319
+ Example(poolDashboard),
320
+ Get('{id}/dashboard'),
321
+ Response(notFoundResp.status, notFoundResp.description),
322
+ __param(0, Request()),
323
+ __param(1, Path()),
324
+ __param(2, Query())
325
+ ], PoolController.prototype, "getPoolDashboard", null);
326
+ __decorate([
327
+ Example(genericAlarmsExample),
328
+ Get('{id}/alarms'),
329
+ Tags('alarms'),
330
+ Response(notFoundResp.status, notFoundResp.description),
331
+ __param(0, Request()),
332
+ __param(1, Path()),
333
+ __param(2, Query()),
334
+ __param(3, Query()),
335
+ __param(4, Query()),
336
+ __param(5, Query())
337
+ ], PoolController.prototype, "getPoolAlarms", null);
259
338
  PoolController = __decorate([
260
339
  Route('pools'),
261
340
  Security('*'),
@@ -263,6 +342,8 @@ PoolController = __decorate([
263
342
  Tags('pools'),
264
343
  provide(PoolController),
265
344
  __param(0, inject(RestApi)),
266
- __param(1, inject(VmService))
345
+ __param(1, inject(VmService)),
346
+ __param(2, inject(PoolService)),
347
+ __param(3, inject(AlarmService))
267
348
  ], PoolController);
268
349
  export { PoolController };
@@ -0,0 +1,212 @@
1
+ import { createLogger } from '@xen-orchestra/log';
2
+ import { HOST_POWER_STATE, VM_POWER_STATE, } from '@vates/types';
3
+ import { HostService } from '../hosts/host.service.mjs';
4
+ import { VmService } from '../vms/vm.service.mjs';
5
+ import { AlarmService } from '../alarms/alarm.service.mjs';
6
+ import { getTopPerProperty, isSrWritableOrIso, promiseWriteInStream } from '../helpers/utils.helper.mjs';
7
+ import { getFromAsyncCache } from '../helpers/cache.helper.mjs';
8
+ const log = createLogger('xo:rest-api:pool-service');
9
+ export class PoolService {
10
+ #restApi;
11
+ #hostService;
12
+ #vmService;
13
+ #alarmService;
14
+ #dashboardAsyncCache = new Map();
15
+ #dashboardCacheOpts;
16
+ constructor(restApi) {
17
+ this.#restApi = restApi;
18
+ this.#hostService = this.#restApi.ioc.get(HostService);
19
+ this.#vmService = this.#restApi.ioc.get(VmService);
20
+ this.#alarmService = this.#restApi.ioc.get(AlarmService);
21
+ this.#dashboardCacheOpts = {
22
+ timeout: this.#restApi.xoApp.config.getOptionalDuration('rest-api.dashboardCacheTimeout') ?? 60000,
23
+ expiresIn: this.#restApi.xoApp.config.getOptionalDuration('rest-api.dashboardCacheExpiresIn'),
24
+ };
25
+ }
26
+ #getHostsStatus(poolId) {
27
+ const { running, disabled, halted, total } = this.#hostService.getHostsStatus({
28
+ filter: host => host.$pool === poolId,
29
+ });
30
+ return { running, disabled, halted, total };
31
+ }
32
+ #getVmsStatus(poolId) {
33
+ const { running, halted, paused, total, suspended } = this.#vmService.getVmsStatus({
34
+ filter: vm => vm.$pool === poolId,
35
+ });
36
+ return {
37
+ running,
38
+ halted,
39
+ paused,
40
+ total,
41
+ suspended,
42
+ };
43
+ }
44
+ #getAlarms(poolId) {
45
+ const alarms = this.#alarmService.getAlarms({ filter: alarm => alarm.$pool === poolId });
46
+ return Object.keys(alarms);
47
+ }
48
+ async #getMissingPatches(poolId) {
49
+ const missingPatchesInfo = await this.#hostService.getMissingPatchesInfo({ filter: host => host.$pool === poolId });
50
+ if (!missingPatchesInfo.hasAuthorization) {
51
+ return {
52
+ hasAuthorization: false,
53
+ };
54
+ }
55
+ const { hasAuthorization, missingPatches } = missingPatchesInfo;
56
+ return {
57
+ hasAuthorization,
58
+ missingPatches,
59
+ };
60
+ }
61
+ #getTopFiveSrsUsage(poolId) {
62
+ const srs = Object.values(this.#restApi.getObjectsByType('SR', {
63
+ filter: sr => sr.$pool === poolId && isSrWritableOrIso(sr),
64
+ }));
65
+ const topFive = getTopPerProperty(srs.map(({ name_label, id, physical_usage, size }) => ({
66
+ name_label,
67
+ id,
68
+ percent: (physical_usage / size) * 100,
69
+ physical_usage,
70
+ size,
71
+ })), { prop: 'percent', length: 5 });
72
+ return topFive;
73
+ }
74
+ #getTopFiveHostsRamUsage(poolId) {
75
+ const hosts = Object.values(this.#restApi.getObjectsByType('host', {
76
+ filter: host => host.$pool === poolId && host.power_state === HOST_POWER_STATE.RUNNING,
77
+ }));
78
+ const topFive = getTopPerProperty(hosts.map(({ name_label, id, memory: { size, usage } }) => ({
79
+ name_label,
80
+ id,
81
+ size,
82
+ usage,
83
+ percent: (usage / size) * 100,
84
+ })), { length: 5, prop: 'percent' });
85
+ return topFive;
86
+ }
87
+ #getVmWithLastRamInfo(vm, stats) {
88
+ const memory = stats.stats.memory?.pop() ?? 0;
89
+ const memoryFree = stats.stats.memoryFree?.pop() ?? 0;
90
+ const usage = memory - memoryFree;
91
+ return { ...vm, memoryStats: { memory, memoryFree, usage } };
92
+ }
93
+ #getVmWithLastCpuInfo(vm, stats) {
94
+ const cpusStats = Object.values(stats.stats.cpus ?? {});
95
+ const usage = cpusStats.reduce((total, cpuStats, index) => {
96
+ const lastCpuStat = cpuStats.pop();
97
+ if (lastCpuStat == null) {
98
+ log.warn(`cpu#${index} is null. vm:`, vm.id);
99
+ }
100
+ total += lastCpuStat ?? 0;
101
+ return total;
102
+ }, 0) / cpusStats.length;
103
+ return { ...vm, usage };
104
+ }
105
+ async #getTopFiveVmsRamCpuUsage(poolId) {
106
+ const vmsUsageResult = await getFromAsyncCache(this.#dashboardAsyncCache, 'vmsRamCpuUsage', async () => {
107
+ const vms = this.#restApi.getObjectsByType('VM', {
108
+ filter: vm => vm.$pool === poolId &&
109
+ (vm.power_state === VM_POWER_STATE.RUNNING || vm.power_state === VM_POWER_STATE.PAUSED),
110
+ });
111
+ const vmsWithRamInfo = [];
112
+ const vmsWithCpuInfo = [];
113
+ for (const id in vms) {
114
+ const vm = vms[id];
115
+ const stats = await this.#restApi.xoApp.getXapiVmStats(vm.id);
116
+ if (vm.managementAgentDetected) {
117
+ const { memoryStats: { memory, memoryFree, usage }, } = this.#getVmWithLastRamInfo(vm, stats);
118
+ vmsWithRamInfo.push({
119
+ id: vm.id,
120
+ name_label: vm.name_label,
121
+ memory,
122
+ memoryFree,
123
+ percent: (usage / memory) * 100,
124
+ });
125
+ }
126
+ const { usage: cpuUsage } = this.#getVmWithLastCpuInfo(vm, stats);
127
+ vmsWithCpuInfo.push({ id: vm.id, name_label: vm.name_label, percent: cpuUsage });
128
+ }
129
+ const topFiveRamUsage = getTopPerProperty(vmsWithRamInfo, { prop: 'percent', length: 5 });
130
+ const topFiveCpuUsage = getTopPerProperty(vmsWithCpuInfo, { prop: 'percent', length: 5 });
131
+ return {
132
+ ram: topFiveRamUsage,
133
+ cpu: topFiveCpuUsage,
134
+ };
135
+ }, this.#dashboardCacheOpts);
136
+ if (vmsUsageResult?.value !== undefined) {
137
+ return { ...vmsUsageResult.value, isExpired: vmsUsageResult.isExpired };
138
+ }
139
+ }
140
+ async #getTopFiveHostsCpuUsage(poolId) {
141
+ const hosts = this.#restApi.getObjectsByType('host', {
142
+ filter: host => host.$pool === poolId && host.power_state === HOST_POWER_STATE.RUNNING,
143
+ });
144
+ const hostsWithPercent = [];
145
+ for (const id in hosts) {
146
+ const host = hosts[id];
147
+ const stats = await this.#restApi.xoApp.getXapiHostStats(host.id, 'seconds');
148
+ const cpusStats = Object.values(stats.stats.cpus ?? {});
149
+ const percent = cpusStats.reduce((total, cpuStats, index) => {
150
+ const lastCpuStat = cpuStats.pop();
151
+ if (lastCpuStat == null) {
152
+ log.warn(`cpu#${index} is null. host:`, host.id);
153
+ }
154
+ total += lastCpuStat ?? 0;
155
+ return total;
156
+ }, 0) / cpusStats.length;
157
+ hostsWithPercent.push({ percent, id: host.id, name_label: host.name_label });
158
+ }
159
+ const topFive = getTopPerProperty(hostsWithPercent, { prop: 'percent', length: 5 });
160
+ return topFive;
161
+ }
162
+ #getCpuProvisioning(poolId) {
163
+ const pool = this.#restApi.getObject(poolId, 'pool');
164
+ const vms = this.#restApi.getObjectsByType('VM', {
165
+ filter: vm => vm.$pool === poolId && (vm.power_state === VM_POWER_STATE.RUNNING || vm.power_state === VM_POWER_STATE.PAUSED),
166
+ });
167
+ let assignedVcpu = 0;
168
+ for (const id in vms) {
169
+ const vm = vms[id];
170
+ assignedVcpu += vm.CPUs.number;
171
+ }
172
+ const total = pool.cpus.cores ?? 0;
173
+ const percent = total === 0 ? 0 : (assignedVcpu * 100) / total;
174
+ return {
175
+ total,
176
+ assigned: assignedVcpu,
177
+ percent,
178
+ };
179
+ }
180
+ async getDashboard(id, { stream } = {}) {
181
+ const [hostStatus, vmsStatus, alarms, missingPatches, storageUsage, hostsRamUsage, hostsCpuUsage, cpuProvisioning, vmsUsage,] = await Promise.all([
182
+ promiseWriteInStream({ maybePromise: this.#getHostsStatus(id), path: 'hosts.status', stream }),
183
+ promiseWriteInStream({ maybePromise: this.#getVmsStatus(id), path: 'vms.status', stream }),
184
+ promiseWriteInStream({ maybePromise: this.#getAlarms(id), path: 'alarms', stream }),
185
+ promiseWriteInStream({ maybePromise: this.#getMissingPatches(id), path: 'hosts.missingPatches', stream }),
186
+ promiseWriteInStream({ maybePromise: this.#getTopFiveSrsUsage(id), path: 'srs.topFiveUsage', stream }),
187
+ promiseWriteInStream({ maybePromise: this.#getTopFiveHostsRamUsage(id), path: 'hosts.topFiveUsage.ram', stream }),
188
+ promiseWriteInStream({ maybePromise: this.#getTopFiveHostsCpuUsage(id), path: 'hosts.topFiveUsage.cpu', stream }),
189
+ promiseWriteInStream({ maybePromise: this.#getCpuProvisioning(id), path: 'cpuProvisioning', stream }),
190
+ promiseWriteInStream({ maybePromise: this.#getTopFiveVmsRamCpuUsage(id), path: 'vms.topFiveUsage', stream }),
191
+ ]);
192
+ return {
193
+ hosts: {
194
+ status: hostStatus,
195
+ topFiveUsage: {
196
+ ram: hostsRamUsage,
197
+ cpu: hostsCpuUsage,
198
+ },
199
+ missingPatches,
200
+ },
201
+ vms: {
202
+ status: vmsStatus,
203
+ topFiveUsage: vmsUsage,
204
+ },
205
+ srs: {
206
+ topFiveUsage: storageUsage,
207
+ },
208
+ alarms,
209
+ cpuProvisioning,
210
+ };
211
+ }
212
+ }
@@ -1,7 +1,9 @@
1
1
  export class RestApi {
2
+ #ioc;
2
3
  #xoApp;
3
- constructor(xoApp) {
4
+ constructor(xoApp, iocContainer) {
4
5
  this.#xoApp = xoApp;
6
+ this.#ioc = iocContainer;
5
7
  }
6
8
  get tasks() {
7
9
  return this.#xoApp.tasks;
@@ -9,6 +11,9 @@ export class RestApi {
9
11
  get xoApp() {
10
12
  return this.#xoApp;
11
13
  }
14
+ get ioc() {
15
+ return this.#ioc;
16
+ }
12
17
  authenticateUser(...args) {
13
18
  return this.#xoApp.authenticateUser(...args);
14
19
  }
@@ -10,13 +10,18 @@ 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 { provide } from 'inversify-binding-decorators';
13
+ import { AlarmService } from '../alarms/alarm.service.mjs';
14
+ import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
15
+ import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
13
16
  import { notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
14
17
  import { partialSrs, sr, srIds } from '../open-api/oa-examples/sr.oa-example.mjs';
15
18
  import { RestApi } from '../rest-api/rest-api.mjs';
16
19
  import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
17
20
  let SrController = class SrController extends XapiXoController {
18
- constructor(restApi) {
21
+ #alarmService;
22
+ constructor(restApi, alarmService) {
19
23
  super('SR', restApi);
24
+ this.#alarmService = alarmService;
20
25
  }
21
26
  /**
22
27
  * @example fields "uuid,name_label,allocationStrategy"
@@ -32,6 +37,20 @@ let SrController = class SrController extends XapiXoController {
32
37
  getSr(id) {
33
38
  return this.getObject(id);
34
39
  }
40
+ /**
41
+ * @example id "c4284e12-37c9-7967-b9e8-83ef229c3e03"
42
+ * @example fields "id,time"
43
+ * @example filter "time:>1747053793"
44
+ * @example limit 42
45
+ */
46
+ getSrAlarms(req, id, fields, ndjson, filter, limit) {
47
+ const sr = this.getObject(id);
48
+ const alarms = this.#alarmService.getAlarms({
49
+ filter: `${escapeUnsafeComplexMatcher(filter) ?? ''} object:uuid:${sr.uuid}`,
50
+ limit,
51
+ });
52
+ return this.sendObjects(Object.values(alarms), req, 'alarms');
53
+ }
35
54
  };
36
55
  __decorate([
37
56
  Example(srIds),
@@ -49,12 +68,25 @@ __decorate([
49
68
  Response(notFoundResp.status, notFoundResp.description),
50
69
  __param(0, Path())
51
70
  ], SrController.prototype, "getSr", null);
71
+ __decorate([
72
+ Example(genericAlarmsExample),
73
+ Get('{id}/alarms'),
74
+ Tags('alarms'),
75
+ Response(notFoundResp.status, notFoundResp.description),
76
+ __param(0, Request()),
77
+ __param(1, Path()),
78
+ __param(2, Query()),
79
+ __param(3, Query()),
80
+ __param(4, Query()),
81
+ __param(5, Query())
82
+ ], SrController.prototype, "getSrAlarms", null);
52
83
  SrController = __decorate([
53
84
  Route('srs'),
54
85
  Security('*'),
55
86
  Response(unauthorizedResp.status, unauthorizedResp.description),
56
87
  Tags('srs'),
57
88
  provide(SrController),
58
- __param(0, inject(RestApi))
89
+ __param(0, inject(RestApi)),
90
+ __param(1, inject(AlarmService))
59
91
  ], SrController);
60
92
  export { SrController };
@@ -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 { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
10
+ import { Body, Delete, Example, Get, Middlewares, Path, Post, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
11
+ import { json } from 'express';
11
12
  import { provide } from 'inversify-binding-decorators';
12
- import { notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
13
- import { partialUsers, user, userIds } from '../open-api/oa-examples/user.oa-example.mjs';
13
+ import { createdResp, invalidParameters, noContentResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
14
+ import { partialUsers, user, userId, userIds } from '../open-api/oa-examples/user.oa-example.mjs';
14
15
  import { XoController } from '../abstract-classes/xo-controller.mjs';
15
16
  let UserController = class UserController extends XoController {
16
17
  // --- abstract methods
@@ -44,6 +45,19 @@ let UserController = class UserController extends XoController {
44
45
  getUser(id) {
45
46
  return this.getObject(id);
46
47
  }
48
+ /**
49
+ * @example body { "name": "new user", "password": "password", "permission": "none" }
50
+ */
51
+ async createUser(body) {
52
+ const user = await this.restApi.xoApp.createUser(body);
53
+ return { id: user.id };
54
+ }
55
+ /**
56
+ * @example id "722d17b9-699b-49d2-8193-be1ac573d3de"
57
+ */
58
+ async deleteUser(id) {
59
+ await this.restApi.xoApp.deleteUser(id);
60
+ }
47
61
  };
48
62
  __decorate([
49
63
  Example(userIds),
@@ -61,6 +75,21 @@ __decorate([
61
75
  Response(notFoundResp.status, notFoundResp.description),
62
76
  __param(0, Path())
63
77
  ], UserController.prototype, "getUser", null);
78
+ __decorate([
79
+ Example(userId),
80
+ Post(''),
81
+ Middlewares(json()),
82
+ SuccessResponse(createdResp.status, createdResp.description),
83
+ Response(unauthorizedResp.status, unauthorizedResp.description),
84
+ Response(invalidParameters.status, invalidParameters.description),
85
+ __param(0, Body())
86
+ ], UserController.prototype, "createUser", null);
87
+ __decorate([
88
+ Delete('{id}'),
89
+ SuccessResponse(noContentResp.status, noContentResp.description),
90
+ Response(notFoundResp.status, notFoundResp.description),
91
+ __param(0, Path())
92
+ ], UserController.prototype, "deleteUser", null);
64
93
  UserController = __decorate([
65
94
  Route('users'),
66
95
  Security('*'),
@@ -10,13 +10,18 @@ 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 { provide } from 'inversify-binding-decorators';
13
+ import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs';
13
14
  import { notFoundResp, unauthorizedResp } from '../open-api/common/response.common.mjs';
14
15
  import { RestApi } from '../rest-api/rest-api.mjs';
15
16
  import { partialVdiSnapshots, vdiSnapshot, vdiSnapshotIds } from '../open-api/oa-examples/vdi-snapshot.oa-example.mjs';
16
17
  import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
18
+ import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs';
19
+ import { AlarmService } from '../alarms/alarm.service.mjs';
17
20
  let VdiSnapshotController = class VdiSnapshotController extends XapiXoController {
18
- constructor(restApi) {
21
+ #alarmService;
22
+ constructor(restApi, alarmService) {
19
23
  super('VDI-snapshot', restApi);
24
+ this.#alarmService = alarmService;
20
25
  }
21
26
  /**
22
27
  * @example fields "uuid,snapshot_time,$snapshot_of"
@@ -32,6 +37,20 @@ let VdiSnapshotController = class VdiSnapshotController extends XapiXoController
32
37
  getVdiSnapshot(id) {
33
38
  return this.getObject(id);
34
39
  }
40
+ /**
41
+ * @example id "d2727772-735b-478f-b6f9-11e7db56dfd0"
42
+ * @example fields "id,time"
43
+ * @example filter "time:>1747053793"
44
+ * @example limit 42
45
+ */
46
+ getVdiSnapshotAlarms(req, id, fields, ndjson, filter, limit) {
47
+ const vdiSnapshot = this.getObject(id);
48
+ const alarms = this.#alarmService.getAlarms({
49
+ filter: `${escapeUnsafeComplexMatcher(filter) ?? ''} object:uuid:${vdiSnapshot.uuid}`,
50
+ limit,
51
+ });
52
+ return this.sendObjects(Object.values(alarms), req, 'alarms');
53
+ }
35
54
  };
36
55
  __decorate([
37
56
  Example(vdiSnapshotIds),
@@ -49,12 +68,25 @@ __decorate([
49
68
  Response(notFoundResp.status, notFoundResp.description),
50
69
  __param(0, Path())
51
70
  ], VdiSnapshotController.prototype, "getVdiSnapshot", null);
71
+ __decorate([
72
+ Example(genericAlarmsExample),
73
+ Get('{id}/alarms'),
74
+ Tags('alarms'),
75
+ Response(notFoundResp.status, notFoundResp.description),
76
+ __param(0, Request()),
77
+ __param(1, Path()),
78
+ __param(2, Query()),
79
+ __param(3, Query()),
80
+ __param(4, Query()),
81
+ __param(5, Query())
82
+ ], VdiSnapshotController.prototype, "getVdiSnapshotAlarms", null);
52
83
  VdiSnapshotController = __decorate([
53
84
  Route('vdi-snapshots'),
54
85
  Security('*'),
55
86
  Response(unauthorizedResp.status, unauthorizedResp.description),
56
87
  Tags('vdis'),
57
88
  provide(VdiSnapshotController),
58
- __param(0, inject(RestApi))
89
+ __param(0, inject(RestApi)),
90
+ __param(1, inject(AlarmService))
59
91
  ], VdiSnapshotController);
60
92
  export { VdiSnapshotController };