@xen-orchestra/rest-api 0.26.0 → 0.28.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.
@@ -8,6 +8,8 @@ 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, Path, Post, Put, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
11
+ import { asyncEach } from '@vates/async-each';
12
+ import { defer } from 'golike-defer';
11
13
  import { json } from 'express';
12
14
  import { inject } from 'inversify';
13
15
  import { invalidParameters } from 'xo-common/api-errors.js';
@@ -182,7 +184,71 @@ let HostController = class HostController extends XapiXoController {
182
184
  taskProperties: {
183
185
  name: 'reconfigure host management interface',
184
186
  objectId: hostId,
185
- args: body,
187
+ params: body,
188
+ },
189
+ });
190
+ }
191
+ /**
192
+ * Disable a host.
193
+ *
194
+ * Set `evacuate` to `true` to also evacuate all running VMs to other hosts in the pool.
195
+ *
196
+ * Use `vmIdsToForceMigrate` to unblock VMs whose migration is currently blocked (e.g. by `pool_migrate` or `migrate_send` blocked operations).
197
+ *
198
+ * Use `force` to ignore evacuation errors.
199
+ *
200
+ * @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
201
+ * @example body { "evacuate": true, "vmIdsToForceMigrate": ["f07ab729-c0e8-721c-45ec-f11276377030"] }
202
+ */
203
+ disable(id, body, sync) {
204
+ const hostId = id;
205
+ const action = defer(async ($defer) => {
206
+ const xapiHost = this.getXapiObject(hostId);
207
+ const xapi = xapiHost.$xapi;
208
+ if (body?.evacuate !== true) {
209
+ await xapi.call('host.disable', xapiHost.$ref);
210
+ return;
211
+ }
212
+ if (body.vmIdsToForceMigrate !== undefined) {
213
+ await asyncEach(body.vmIdsToForceMigrate, async (vmId) => {
214
+ const xoVm = this.restApi.getObject(vmId, 'VM');
215
+ for (const operation of ['pool_migrate', 'migrate_send']) {
216
+ const reason = xoVm.blockedOperations[operation];
217
+ if (reason !== undefined) {
218
+ await xapi.call('VM.remove_from_blocked_operations', xoVm._xapiRef, operation);
219
+ $defer(() => xapi.call('VM.add_to_blocked_operations', xoVm._xapiRef, operation, reason));
220
+ }
221
+ }
222
+ });
223
+ }
224
+ await xapi.clearHost(xapiHost, body.force);
225
+ });
226
+ return this.createAction(action, {
227
+ sync,
228
+ statusCode: noContentResp.status,
229
+ taskProperties: {
230
+ name: body?.evacuate === true ? 'disable and evacuate host' : 'disable host',
231
+ objectId: hostId,
232
+ params: body,
233
+ },
234
+ });
235
+ }
236
+ /**
237
+ * Enable a host, taking it out of disabled state.
238
+ *
239
+ * @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
240
+ */
241
+ enable(id, sync) {
242
+ const hostId = id;
243
+ const action = async () => {
244
+ await this.getXapiObject(hostId).$xapi.enableHost(hostId);
245
+ };
246
+ return this.createAction(action, {
247
+ sync,
248
+ statusCode: noContentResp.status,
249
+ taskProperties: {
250
+ name: 'enable host',
251
+ objectId: hostId,
186
252
  },
187
253
  });
188
254
  }
@@ -308,6 +374,29 @@ __decorate([
308
374
  __param(1, Body()),
309
375
  __param(2, Query())
310
376
  ], HostController.prototype, "managementReconfigure", null);
377
+ __decorate([
378
+ Example(taskLocation),
379
+ Post('{id}/actions/disable'),
380
+ Middlewares(json()),
381
+ SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
382
+ Response(noContentResp.status, noContentResp.description),
383
+ Response(notFoundResp.status, notFoundResp.description),
384
+ Response(invalidParametersResp.status, invalidParametersResp.description),
385
+ Response(internalServerErrorResp.status, internalServerErrorResp.description),
386
+ __param(0, Path()),
387
+ __param(1, Body()),
388
+ __param(2, Query())
389
+ ], HostController.prototype, "disable", null);
390
+ __decorate([
391
+ Example(taskLocation),
392
+ Post('{id}/actions/enable'),
393
+ SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
394
+ Response(noContentResp.status, noContentResp.description),
395
+ Response(notFoundResp.status, notFoundResp.description),
396
+ Response(internalServerErrorResp.status, internalServerErrorResp.description),
397
+ __param(0, Path()),
398
+ __param(1, Query())
399
+ ], HostController.prototype, "enable", null);
311
400
  HostController = __decorate([
312
401
  Route('hosts'),
313
402
  Security('*'),
package/dist/ioc/ioc.mjs CHANGED
@@ -12,6 +12,7 @@ import { UserService } from '../users/user.service.mjs';
12
12
  import { BackupJobService } from '../backup-jobs/backup-job.service.mjs';
13
13
  import { BackupLogService } from '../backup-logs/backup-log.service.mjs';
14
14
  import { EventService } from '../events/event.service.mjs';
15
+ import { NetworkService } from '../networks/network.service.mjs';
15
16
  const iocContainer = new Container();
16
17
  decorate(injectable(), Controller);
17
18
  iocContainer.load(buildProviderModule());
@@ -92,5 +93,12 @@ export function setupContainer(xoApp) {
92
93
  return new EventService(restApi);
93
94
  })
94
95
  .inSingletonScope();
96
+ iocContainer
97
+ .bind(NetworkService)
98
+ .toDynamicValue(ctx => {
99
+ const restApi = ctx.container.get(RestApi);
100
+ return new NetworkService(restApi);
101
+ })
102
+ .inSingletonScope();
95
103
  }
96
104
  export { iocContainer };
@@ -6,7 +6,7 @@ const log = createLogger('xo:rest-api:error-handler');
6
6
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
7
7
  export default function genericErrorHandler(error, req, res, _next) {
8
8
  if (!(error instanceof Error)) {
9
- log.error(error);
9
+ log.error(JSON.stringify(error));
10
10
  res.status(500).json({ error });
11
11
  return;
12
12
  }
@@ -0,0 +1,23 @@
1
+ export class NetworkService {
2
+ #restApi;
3
+ constructor(restApi) {
4
+ this.#restApi = restApi;
5
+ }
6
+ async create(poolId, body) {
7
+ const xapiPool = this.#restApi.getXapiObject(poolId, 'pool');
8
+ const xapi = xapiPool.$xapi;
9
+ let xapiNetwork;
10
+ const { nbd, ...rest } = body;
11
+ const createParams = rest;
12
+ if ('pifIds' in createParams) {
13
+ xapiNetwork = await xapi.createBondedNetwork(createParams);
14
+ }
15
+ else {
16
+ xapiNetwork = await xapi.createNetwork(createParams);
17
+ }
18
+ if (nbd) {
19
+ await xapiNetwork.$call('add_purpose', 'nbd');
20
+ }
21
+ return xapiNetwork.uuid;
22
+ }
23
+ }