@xen-orchestra/rest-api 0.1.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 +74 -0
- package/dist/abstract-classes/xapi-xo-controller.mjs +26 -0
- package/dist/helpers/helper.type.mjs +1 -0
- package/dist/helpers/object-wrapper.helper.mjs +28 -0
- package/dist/index.mjs +20 -0
- package/dist/ioc/ioc.mjs +17 -0
- package/dist/middlewares/authentication.middleware.mjs +34 -0
- package/dist/middlewares/generic-error-handler.middleware.mjs +33 -0
- package/dist/middlewares/tsoa-to-xo-error.middleware.mjs +8 -0
- package/dist/open-api/routes/routes.js +181 -0
- package/dist/rest-api/rest-api.mjs +18 -0
- package/dist/rest-api/rest-api.type.mjs +1 -0
- package/dist/vms/vm.controller.mjs +62 -0
- package/open-api/spec/swagger.json +1282 -0
- package/package.json +47 -0
- package/tsconfig.json +18 -0
- package/tsoa.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
|
2
|
+
|
|
3
|
+
# @xen-orchestra/rest-api
|
|
4
|
+
|
|
5
|
+
[](https://npmjs.org/package/@xen-orchestra/rest-api)  [](https://bundlephobia.com/result?p=@xen-orchestra/rest-api) [](https://npmjs.org/package/@xen-orchestra/rest-api)
|
|
6
|
+
|
|
7
|
+
> REST API to manage your XOA
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
Installation of the [npm package](https://npmjs.org/package/@xen-orchestra/rest-api):
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install --save @xen-orchestra/rest-api
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
# @xen-orchestra/rest-api
|
|
20
|
+
|
|
21
|
+
## Rules
|
|
22
|
+
|
|
23
|
+
The REST API is based on the `TSOA` framework and therefore we use decorators a lot to define the behavior of a route or a group of routes. To keep things easily visible, it is best to always use the decorators in the same order.
|
|
24
|
+
|
|
25
|
+
### Class decorator
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
@Routes('foo')
|
|
29
|
+
@Security('*')
|
|
30
|
+
@Response(401)
|
|
31
|
+
@provide(Foo)
|
|
32
|
+
class Foo extends Controller {}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Methods decorator
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
@Routes('foo')
|
|
39
|
+
...
|
|
40
|
+
class Foo extends Controller {
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* any jsdoc anotations
|
|
45
|
+
* @example id 1234
|
|
46
|
+
*/
|
|
47
|
+
@Example(['foo', 'bar'])
|
|
48
|
+
@Get('{id}')
|
|
49
|
+
@Security('*')
|
|
50
|
+
@Response(404)
|
|
51
|
+
getFoo(@Path() id: string) {
|
|
52
|
+
return this.getFoo(id)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Examples
|
|
58
|
+
|
|
59
|
+
In order not to pollute important decorators, all example structures should be in a separate file. `src/open-api/examples/<resource>.example.mts`
|
|
60
|
+
|
|
61
|
+
## Contributions
|
|
62
|
+
|
|
63
|
+
Contributions are _very_ welcomed, either on the documentation or on
|
|
64
|
+
the code.
|
|
65
|
+
|
|
66
|
+
You may:
|
|
67
|
+
|
|
68
|
+
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
|
69
|
+
you've encountered;
|
|
70
|
+
- fork and create a pull request.
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
[AGPL-3.0-or-later](https://spdx.org/licenses/AGPL-3.0-or-later) © [Vates SAS](https://vates.fr)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as CM from 'complex-matcher';
|
|
2
|
+
import { Controller } from 'tsoa';
|
|
3
|
+
import { makeObjectMapper } from '../helpers/object-wrapper.helper.mjs';
|
|
4
|
+
export class XapiXoController extends Controller {
|
|
5
|
+
#type;
|
|
6
|
+
restApi;
|
|
7
|
+
constructor(type, restApi) {
|
|
8
|
+
super();
|
|
9
|
+
this.#type = type;
|
|
10
|
+
this.restApi = restApi;
|
|
11
|
+
}
|
|
12
|
+
getObjects({ filter, limit } = {}) {
|
|
13
|
+
if (filter !== undefined) {
|
|
14
|
+
filter = CM.parse(filter).createPredicate();
|
|
15
|
+
}
|
|
16
|
+
return this.restApi.getObjectsByType(this.#type, { filter, limit });
|
|
17
|
+
}
|
|
18
|
+
getObject(id) {
|
|
19
|
+
return this.restApi.getObject(id, this.#type);
|
|
20
|
+
}
|
|
21
|
+
sendObjects(objects, req) {
|
|
22
|
+
const mapper = makeObjectMapper(req);
|
|
23
|
+
const mappedObjects = objects.map(mapper);
|
|
24
|
+
return mappedObjects;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import pick from 'lodash/pick.js';
|
|
3
|
+
const { join } = path.posix;
|
|
4
|
+
export function makeObjectMapper(req, path = req.path) {
|
|
5
|
+
const makeUrl = ({ id }) => join(baseUrl, path, typeof id === 'number' ? String(id) : id);
|
|
6
|
+
let objectMapper;
|
|
7
|
+
const { query, baseUrl } = req;
|
|
8
|
+
const { fields } = query;
|
|
9
|
+
if (fields === '*') {
|
|
10
|
+
objectMapper = object => ({
|
|
11
|
+
...object,
|
|
12
|
+
href: makeUrl(object),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
else if (typeof fields === 'string') {
|
|
16
|
+
const _fields = fields.split(',');
|
|
17
|
+
objectMapper = object => {
|
|
18
|
+
const url = makeUrl(object);
|
|
19
|
+
return { ...pick(object, _fields), href: url };
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
objectMapper = makeUrl;
|
|
24
|
+
}
|
|
25
|
+
return function (obj) {
|
|
26
|
+
return objectMapper(obj);
|
|
27
|
+
};
|
|
28
|
+
}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as swaggerUi from 'swagger-ui-express';
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
import genericErrorHandler from './middlewares/generic-error-handler.middleware.mjs';
|
|
4
|
+
import tsoaToXoErrorHandler from './middlewares/tsoa-to-xo-error.middleware.mjs';
|
|
5
|
+
import { RegisterRoutes } from './open-api/routes/routes.js';
|
|
6
|
+
import { setupContainer } from './ioc/ioc.mjs';
|
|
7
|
+
// Avoid using "import from" to import a json file as this requires assert/with and will break compatibility with recent node versions
|
|
8
|
+
// https://github.com/nodejs/node/issues/51622
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const swaggerOpenApiSpec = require('../open-api/spec/swagger.json');
|
|
11
|
+
export default function setupRestApi(express, xoApp) {
|
|
12
|
+
setupContainer(xoApp);
|
|
13
|
+
RegisterRoutes(express);
|
|
14
|
+
// do not register the doc at the root level, or it may lead to unwated behaviour
|
|
15
|
+
// uncomment when all endpoints are migrated to this API
|
|
16
|
+
// express.get('/rest/v0', (_req, res) => res.redirect('/rest/v0/docs'))
|
|
17
|
+
express.use('/rest/v0/docs', swaggerUi.serve, swaggerUi.setup(swaggerOpenApiSpec));
|
|
18
|
+
express.use('/rest/v0', tsoaToXoErrorHandler);
|
|
19
|
+
express.use('/rest/v0', genericErrorHandler);
|
|
20
|
+
}
|
package/dist/ioc/ioc.mjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { buildProviderModule } from 'inversify-binding-decorators';
|
|
2
|
+
import { Container, decorate, injectable } from 'inversify';
|
|
3
|
+
import { Controller } from 'tsoa';
|
|
4
|
+
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
5
|
+
const iocContainer = new Container();
|
|
6
|
+
decorate(injectable(), Controller);
|
|
7
|
+
iocContainer.load(buildProviderModule());
|
|
8
|
+
export function setupContainer(xoApp) {
|
|
9
|
+
if (iocContainer.isBound(RestApi)) {
|
|
10
|
+
iocContainer.unbind(RestApi);
|
|
11
|
+
}
|
|
12
|
+
iocContainer
|
|
13
|
+
.bind(RestApi)
|
|
14
|
+
.toDynamicValue(() => new RestApi(xoApp))
|
|
15
|
+
.inSingletonScope();
|
|
16
|
+
}
|
|
17
|
+
export { iocContainer };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createLogger } from '@xen-orchestra/log';
|
|
2
|
+
import { invalidCredentials, notImplemented, unauthorized } from 'xo-common/api-errors.js';
|
|
3
|
+
import { iocContainer } from '../ioc/ioc.mjs';
|
|
4
|
+
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
5
|
+
const noop = () => { };
|
|
6
|
+
const log = createLogger('xo:rest-api:authentication');
|
|
7
|
+
function getCredentials(securityName, req) {
|
|
8
|
+
const token = req.cookies.token ?? req.cookies.authenticationToken;
|
|
9
|
+
if (securityName === '*' || securityName === 'token') {
|
|
10
|
+
if (token !== undefined) {
|
|
11
|
+
return { token };
|
|
12
|
+
}
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
log.error(`${securityName} not implemented.`);
|
|
16
|
+
throw notImplemented();
|
|
17
|
+
}
|
|
18
|
+
// TODO: correctly handle ACL/Resource set users
|
|
19
|
+
// for now only support "xoa-admin"
|
|
20
|
+
export async function expressAuthentication(req, securityName) {
|
|
21
|
+
const restApi = iocContainer.get(RestApi);
|
|
22
|
+
const ip = req.ip;
|
|
23
|
+
const credentials = getCredentials(securityName, req);
|
|
24
|
+
if (credentials === undefined) {
|
|
25
|
+
throw invalidCredentials();
|
|
26
|
+
}
|
|
27
|
+
const { user } = await restApi.authenticateUser(credentials, { ip });
|
|
28
|
+
if (user.permission !== 'admin') {
|
|
29
|
+
log.error(`The REST API can only be used by 'xoa-admin' users for now. Your permission: ${user.permission}`);
|
|
30
|
+
throw unauthorized();
|
|
31
|
+
}
|
|
32
|
+
await restApi.runWithApiContext(user, noop);
|
|
33
|
+
Promise.resolve(user);
|
|
34
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createLogger } from '@xen-orchestra/log';
|
|
2
|
+
import { featureUnauthorized, forbiddenOperation, invalidCredentials, invalidParameters, noSuchObject, notImplemented, unauthorized, } from 'xo-common/api-errors.js';
|
|
3
|
+
const log = createLogger('xo:rest-api:error-handler');
|
|
4
|
+
// must have 4 parameters to be recognized as an error middleware by express
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
6
|
+
export default function genericErrorHandler(error, req, res, _next) {
|
|
7
|
+
if (!(error instanceof Error)) {
|
|
8
|
+
log.error(error);
|
|
9
|
+
res.status(500).json({ error });
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (noSuchObject.is(error)) {
|
|
13
|
+
res.status(404);
|
|
14
|
+
}
|
|
15
|
+
else if (unauthorized.is(error) || forbiddenOperation.is(error) || featureUnauthorized.is(error)) {
|
|
16
|
+
res.status(403);
|
|
17
|
+
}
|
|
18
|
+
else if (invalidCredentials.is(error)) {
|
|
19
|
+
res.status(401);
|
|
20
|
+
}
|
|
21
|
+
else if (invalidParameters.is(error)) {
|
|
22
|
+
res.status(422);
|
|
23
|
+
}
|
|
24
|
+
else if (notImplemented.is(error)) {
|
|
25
|
+
res.status(501);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
res.status(500);
|
|
29
|
+
log.error(error);
|
|
30
|
+
}
|
|
31
|
+
log.info(`[${req.method}] ${req.path} (${res.statusCode})`);
|
|
32
|
+
res.json({ error: error.message });
|
|
33
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { invalidParameters } from 'xo-common/api-errors.js';
|
|
2
|
+
import { ValidateError } from 'tsoa';
|
|
3
|
+
export default function tsoaToXoErrorHandler(error, _req, _res, next) {
|
|
4
|
+
if (error instanceof ValidateError) {
|
|
5
|
+
/* throw */ invalidParameters(error.fields);
|
|
6
|
+
}
|
|
7
|
+
return next(error);
|
|
8
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { fetchMiddlewares, ExpressTemplateService } from '@tsoa/runtime';
|
|
2
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
3
|
+
import { VmController } from './../../vms/vm.controller.mjs';
|
|
4
|
+
import { expressAuthentication } from './../../middlewares/authentication.middleware.mjs';
|
|
5
|
+
// @ts-ignore - no great way to install types from subpackage
|
|
6
|
+
import { iocContainer } from './../../ioc/ioc.mjs';
|
|
7
|
+
const expressAuthenticationRecasted = expressAuthentication;
|
|
8
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
9
|
+
const models = {
|
|
10
|
+
"Record_string.string_": {
|
|
11
|
+
"dataType": "refAlias",
|
|
12
|
+
"type": { "dataType": "nestedObjectLiteral", "nestedProperties": {}, "additionalProperties": { "dataType": "string" }, "validators": {} },
|
|
13
|
+
},
|
|
14
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
15
|
+
"Record_VM_OPERATIONS.string_": {
|
|
16
|
+
"dataType": "refAlias",
|
|
17
|
+
"type": { "dataType": "nestedObjectLiteral", "nestedProperties": { "assert_operation_valid": { "dataType": "string", "required": true }, "awaiting_memory_live": { "dataType": "string", "required": true }, "call_plugin": { "dataType": "string", "required": true }, "changing_dynamic_range": { "dataType": "string", "required": true }, "changing_memory_limits": { "dataType": "string", "required": true }, "changing_memory_live": { "dataType": "string", "required": true }, "changing_NVRAM": { "dataType": "string", "required": true }, "changing_shadow_memory": { "dataType": "string", "required": true }, "changing_shadow_memory_live": { "dataType": "string", "required": true }, "changing_static_range": { "dataType": "string", "required": true }, "changing_VCPUs": { "dataType": "string", "required": true }, "changing_VCPUs_live": { "dataType": "string", "required": true }, "checkpoint": { "dataType": "string", "required": true }, "clean_reboot": { "dataType": "string", "required": true }, "clean_shutdown": { "dataType": "string", "required": true }, "clone": { "dataType": "string", "required": true }, "copy": { "dataType": "string", "required": true }, "create_template": { "dataType": "string", "required": true }, "create_vtpm": { "dataType": "string", "required": true }, "csvm": { "dataType": "string", "required": true }, "data_source_op": { "dataType": "string", "required": true }, "destroy": { "dataType": "string", "required": true }, "export": { "dataType": "string", "required": true }, "get_boot_record": { "dataType": "string", "required": true }, "hard_reboot": { "dataType": "string", "required": true }, "hard_shutdown": { "dataType": "string", "required": true }, "import": { "dataType": "string", "required": true }, "make_into_template": { "dataType": "string", "required": true }, "metadata_export": { "dataType": "string", "required": true }, "migrate_send": { "dataType": "string", "required": true }, "pause": { "dataType": "string", "required": true }, "pool_migrate": { "dataType": "string", "required": true }, "power_state_reset": { "dataType": "string", "required": true }, "provision": { "dataType": "string", "required": true }, "query_services": { "dataType": "string", "required": true }, "resume": { "dataType": "string", "required": true }, "resume_on": { "dataType": "string", "required": true }, "revert": { "dataType": "string", "required": true }, "reverting": { "dataType": "string", "required": true }, "send_sysrq": { "dataType": "string", "required": true }, "send_trigger": { "dataType": "string", "required": true }, "shutdown": { "dataType": "string", "required": true }, "snapshot": { "dataType": "string", "required": true }, "snapshot_with_quiesce": { "dataType": "string", "required": true }, "start": { "dataType": "string", "required": true }, "start_on": { "dataType": "string", "required": true }, "suspend": { "dataType": "string", "required": true }, "unpause": { "dataType": "string", "required": true }, "update_allowed_operations": { "dataType": "string", "required": true } }, "validators": {} },
|
|
18
|
+
},
|
|
19
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
20
|
+
"VM_OPERATIONS": {
|
|
21
|
+
"dataType": "refAlias",
|
|
22
|
+
"type": { "dataType": "union", "subSchemas": [{ "dataType": "enum", "enums": ["assert_operation_valid"] }, { "dataType": "enum", "enums": ["awaiting_memory_live"] }, { "dataType": "enum", "enums": ["call_plugin"] }, { "dataType": "enum", "enums": ["changing_dynamic_range"] }, { "dataType": "enum", "enums": ["changing_memory_limits"] }, { "dataType": "enum", "enums": ["changing_memory_live"] }, { "dataType": "enum", "enums": ["changing_NVRAM"] }, { "dataType": "enum", "enums": ["changing_shadow_memory"] }, { "dataType": "enum", "enums": ["changing_shadow_memory_live"] }, { "dataType": "enum", "enums": ["changing_static_range"] }, { "dataType": "enum", "enums": ["changing_VCPUs"] }, { "dataType": "enum", "enums": ["changing_VCPUs_live"] }, { "dataType": "enum", "enums": ["checkpoint"] }, { "dataType": "enum", "enums": ["clean_reboot"] }, { "dataType": "enum", "enums": ["clean_shutdown"] }, { "dataType": "enum", "enums": ["clone"] }, { "dataType": "enum", "enums": ["copy"] }, { "dataType": "enum", "enums": ["create_template"] }, { "dataType": "enum", "enums": ["create_vtpm"] }, { "dataType": "enum", "enums": ["csvm"] }, { "dataType": "enum", "enums": ["data_source_op"] }, { "dataType": "enum", "enums": ["destroy"] }, { "dataType": "enum", "enums": ["export"] }, { "dataType": "enum", "enums": ["get_boot_record"] }, { "dataType": "enum", "enums": ["hard_reboot"] }, { "dataType": "enum", "enums": ["hard_shutdown"] }, { "dataType": "enum", "enums": ["import"] }, { "dataType": "enum", "enums": ["make_into_template"] }, { "dataType": "enum", "enums": ["metadata_export"] }, { "dataType": "enum", "enums": ["migrate_send"] }, { "dataType": "enum", "enums": ["pause"] }, { "dataType": "enum", "enums": ["pool_migrate"] }, { "dataType": "enum", "enums": ["power_state_reset"] }, { "dataType": "enum", "enums": ["provision"] }, { "dataType": "enum", "enums": ["query_services"] }, { "dataType": "enum", "enums": ["resume"] }, { "dataType": "enum", "enums": ["resume_on"] }, { "dataType": "enum", "enums": ["revert"] }, { "dataType": "enum", "enums": ["reverting"] }, { "dataType": "enum", "enums": ["send_sysrq"] }, { "dataType": "enum", "enums": ["send_trigger"] }, { "dataType": "enum", "enums": ["shutdown"] }, { "dataType": "enum", "enums": ["snapshot"] }, { "dataType": "enum", "enums": ["snapshot_with_quiesce"] }, { "dataType": "enum", "enums": ["start"] }, { "dataType": "enum", "enums": ["start_on"] }, { "dataType": "enum", "enums": ["suspend"] }, { "dataType": "enum", "enums": ["unpause"] }, { "dataType": "enum", "enums": ["update_allowed_operations"] }], "validators": {} },
|
|
23
|
+
},
|
|
24
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
25
|
+
"Record_string.VM_OPERATIONS_": {
|
|
26
|
+
"dataType": "refAlias",
|
|
27
|
+
"type": { "dataType": "nestedObjectLiteral", "nestedProperties": {}, "additionalProperties": { "ref": "VM_OPERATIONS" }, "validators": {} },
|
|
28
|
+
},
|
|
29
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
30
|
+
"VM_POWER_STATE": {
|
|
31
|
+
"dataType": "refAlias",
|
|
32
|
+
"type": { "dataType": "union", "subSchemas": [{ "dataType": "enum", "enums": ["Halted"] }, { "dataType": "enum", "enums": ["Paused"] }, { "dataType": "enum", "enums": ["Running"] }, { "dataType": "enum", "enums": ["Suspended"] }], "validators": {} },
|
|
33
|
+
},
|
|
34
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
35
|
+
"DOMAIN_TYPE": {
|
|
36
|
+
"dataType": "refAlias",
|
|
37
|
+
"type": { "dataType": "union", "subSchemas": [{ "dataType": "enum", "enums": ["hvm"] }, { "dataType": "enum", "enums": ["pv"] }, { "dataType": "enum", "enums": ["pvh"] }, { "dataType": "enum", "enums": ["pv_in_pvh"] }, { "dataType": "enum", "enums": ["unspecified"] }], "validators": {} },
|
|
38
|
+
},
|
|
39
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
40
|
+
"Unbrand_XoVm_": {
|
|
41
|
+
"dataType": "refAlias",
|
|
42
|
+
"type": { "dataType": "nestedObjectLiteral", "nestedProperties": { "$VBDs": { "dataType": "array", "array": { "dataType": "string" }, "required": true }, "$VGPUs": { "dataType": "array", "array": { "dataType": "string" }, "required": true }, "$container": { "dataType": "string", "required": true }, "$pool": { "dataType": "string", "required": true }, "$poolId": { "dataType": "string", "required": true }, "_xapiRef": { "dataType": "string", "required": true }, "CPUs": { "dataType": "nestedObjectLiteral", "nestedProperties": { "number": { "dataType": "double", "required": true }, "max": { "dataType": "double", "required": true } }, "required": true }, "PV_args": { "dataType": "string" }, "VGPUs": { "dataType": "array", "array": { "dataType": "string" }, "required": true }, "VIFs": { "dataType": "array", "array": { "dataType": "string" }, "required": true }, "VTPMs": { "dataType": "array", "array": { "dataType": "string" }, "required": true }, "addresses": { "ref": "Record_string.string_", "required": true }, "affinityHost": { "dataType": "string" }, "attachedPcis": { "dataType": "array", "array": { "dataType": "string" } }, "auto_poweron": { "dataType": "boolean", "required": true }, "bios_strings": { "ref": "Record_string.string_", "required": true }, "blockedOperations": { "ref": "Record_VM_OPERATIONS.string_", "required": true }, "boot": { "ref": "Record_string.string_", "required": true }, "coresPerSocket": { "dataType": "double" }, "cpuCap": { "dataType": "double" }, "cpuMask": { "dataType": "array", "array": { "dataType": "double" } }, "cpuWeight": { "dataType": "double" }, "creation": { "ref": "Record_string.string_", "required": true }, "current_operations": { "ref": "Record_string.VM_OPERATIONS_", "required": true }, "docker": { "dataType": "nestedObjectLiteral", "nestedProperties": { "version": { "dataType": "string" }, "process": { "dataType": "string" }, "info": { "dataType": "string" }, "enabled": { "dataType": "boolean", "required": true }, "containers": { "dataType": "array", "array": { "dataType": "string" } } } }, "expNestedHvm": { "dataType": "boolean", "required": true }, "hasVendorDevice": { "dataType": "boolean", "required": true }, "high_availability": { "dataType": "string", "required": true }, "installTime": { "dataType": "double" }, "isFirmwareSupported": { "dataType": "boolean", "required": true }, "memory": { "dataType": "nestedObjectLiteral", "nestedProperties": { "usage": { "dataType": "double" }, "static": { "dataType": "array", "array": { "dataType": "double" }, "required": true }, "size": { "dataType": "double", "required": true }, "dynamic": { "dataType": "array", "array": { "dataType": "double" }, "required": true } }, "required": true }, "mainIpAddress": { "dataType": "string" }, "managementAgentDetected": { "dataType": "boolean" }, "name_description": { "dataType": "string", "required": true }, "name_label": { "dataType": "string", "required": true }, "needsVtpm": { "dataType": "boolean", "required": true }, "nicType": { "dataType": "string" }, "notes": { "dataType": "string" }, "os_version": { "ref": "Record_string.string_", "required": true }, "other": { "ref": "Record_string.string_", "required": true }, "parent": { "dataType": "string" }, "power_state": { "ref": "VM_POWER_STATE", "required": true }, "pvDriversDetected": { "dataType": "boolean" }, "pvDriversUpToDate": { "dataType": "boolean" }, "pvDriversVersion": { "dataType": "string" }, "resourceSet": { "dataType": "string" }, "secureBoot": { "dataType": "boolean", "required": true }, "snapshots": { "dataType": "array", "array": { "dataType": "string" }, "required": true }, "startDelay": { "dataType": "double", "required": true }, "startTime": { "dataType": "double" }, "suspendSr": { "dataType": "string" }, "tags": { "dataType": "array", "array": { "dataType": "string" }, "required": true }, "vga": { "dataType": "string" }, "videoram": { "dataType": "double" }, "viridian": { "dataType": "boolean", "required": true }, "virtualizationMode": { "ref": "DOMAIN_TYPE", "required": true }, "xenStoreData": { "ref": "Record_string.string_", "required": true }, "xentools": { "dataType": "union", "subSchemas": [{ "dataType": "enum", "enums": [false] }, { "dataType": "nestedObjectLiteral", "nestedProperties": { "version": { "dataType": "string", "required": true }, "minor": { "dataType": "double", "required": true }, "major": { "dataType": "double", "required": true } } }] }, "id": { "dataType": "string", "required": true }, "type": { "dataType": "enum", "enums": ["VM"], "required": true } }, "validators": {} },
|
|
43
|
+
},
|
|
44
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
45
|
+
"WithHref_Unbrand_XoVm__": {
|
|
46
|
+
"dataType": "refAlias",
|
|
47
|
+
"type": { "dataType": "intersection", "subSchemas": [{ "ref": "Unbrand_XoVm_" }, { "dataType": "nestedObjectLiteral", "nestedProperties": { "href": { "dataType": "string", "required": true } } }], "validators": {} },
|
|
48
|
+
},
|
|
49
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
50
|
+
"Partial_Unbrand_XoVm__": {
|
|
51
|
+
"dataType": "refAlias",
|
|
52
|
+
"type": { "dataType": "nestedObjectLiteral", "nestedProperties": { "$VBDs": { "dataType": "array", "array": { "dataType": "string" } }, "$VGPUs": { "dataType": "array", "array": { "dataType": "string" } }, "$container": { "dataType": "string" }, "$pool": { "dataType": "string" }, "$poolId": { "dataType": "string" }, "_xapiRef": { "dataType": "string" }, "CPUs": { "dataType": "nestedObjectLiteral", "nestedProperties": { "number": { "dataType": "double", "required": true }, "max": { "dataType": "double", "required": true } } }, "PV_args": { "dataType": "string" }, "VGPUs": { "dataType": "array", "array": { "dataType": "string" } }, "VIFs": { "dataType": "array", "array": { "dataType": "string" } }, "VTPMs": { "dataType": "array", "array": { "dataType": "string" } }, "addresses": { "ref": "Record_string.string_" }, "affinityHost": { "dataType": "string" }, "attachedPcis": { "dataType": "array", "array": { "dataType": "string" } }, "auto_poweron": { "dataType": "boolean" }, "bios_strings": { "ref": "Record_string.string_" }, "blockedOperations": { "ref": "Record_VM_OPERATIONS.string_" }, "boot": { "ref": "Record_string.string_" }, "coresPerSocket": { "dataType": "double" }, "cpuCap": { "dataType": "double" }, "cpuMask": { "dataType": "array", "array": { "dataType": "double" } }, "cpuWeight": { "dataType": "double" }, "creation": { "ref": "Record_string.string_" }, "current_operations": { "ref": "Record_string.VM_OPERATIONS_" }, "docker": { "dataType": "nestedObjectLiteral", "nestedProperties": { "version": { "dataType": "string" }, "process": { "dataType": "string" }, "info": { "dataType": "string" }, "enabled": { "dataType": "boolean", "required": true }, "containers": { "dataType": "array", "array": { "dataType": "string" } } } }, "expNestedHvm": { "dataType": "boolean" }, "hasVendorDevice": { "dataType": "boolean" }, "high_availability": { "dataType": "string" }, "installTime": { "dataType": "double" }, "isFirmwareSupported": { "dataType": "boolean" }, "memory": { "dataType": "nestedObjectLiteral", "nestedProperties": { "usage": { "dataType": "double" }, "static": { "dataType": "array", "array": { "dataType": "double" }, "required": true }, "size": { "dataType": "double", "required": true }, "dynamic": { "dataType": "array", "array": { "dataType": "double" }, "required": true } } }, "mainIpAddress": { "dataType": "string" }, "managementAgentDetected": { "dataType": "boolean" }, "name_description": { "dataType": "string" }, "name_label": { "dataType": "string" }, "needsVtpm": { "dataType": "boolean" }, "nicType": { "dataType": "string" }, "notes": { "dataType": "string" }, "os_version": { "ref": "Record_string.string_" }, "other": { "ref": "Record_string.string_" }, "parent": { "dataType": "string" }, "power_state": { "ref": "VM_POWER_STATE" }, "pvDriversDetected": { "dataType": "boolean" }, "pvDriversUpToDate": { "dataType": "boolean" }, "pvDriversVersion": { "dataType": "string" }, "resourceSet": { "dataType": "string" }, "secureBoot": { "dataType": "boolean" }, "snapshots": { "dataType": "array", "array": { "dataType": "string" } }, "startDelay": { "dataType": "double" }, "startTime": { "dataType": "double" }, "suspendSr": { "dataType": "string" }, "tags": { "dataType": "array", "array": { "dataType": "string" } }, "vga": { "dataType": "string" }, "videoram": { "dataType": "double" }, "viridian": { "dataType": "boolean" }, "virtualizationMode": { "ref": "DOMAIN_TYPE" }, "xenStoreData": { "ref": "Record_string.string_" }, "xentools": { "dataType": "union", "subSchemas": [{ "dataType": "enum", "enums": [false] }, { "dataType": "nestedObjectLiteral", "nestedProperties": { "version": { "dataType": "string", "required": true }, "minor": { "dataType": "double", "required": true }, "major": { "dataType": "double", "required": true } } }] }, "id": { "dataType": "string" }, "type": { "dataType": "enum", "enums": ["VM"] } }, "validators": {} },
|
|
53
|
+
},
|
|
54
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
55
|
+
"WithHref_Partial_Unbrand_XoVm___": {
|
|
56
|
+
"dataType": "refAlias",
|
|
57
|
+
"type": { "dataType": "intersection", "subSchemas": [{ "ref": "Partial_Unbrand_XoVm__" }, { "dataType": "nestedObjectLiteral", "nestedProperties": { "href": { "dataType": "string", "required": true } } }], "validators": {} },
|
|
58
|
+
},
|
|
59
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
60
|
+
};
|
|
61
|
+
const templateService = new ExpressTemplateService(models, { "noImplicitAdditionalProperties": "throw-on-extras", "bodyCoercion": true });
|
|
62
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
63
|
+
export function RegisterRoutes(app) {
|
|
64
|
+
// ###########################################################################################################
|
|
65
|
+
// NOTE: If you do not see routes for all of your controllers in this file, then you might not have informed tsoa of where to look
|
|
66
|
+
// Please look into the "controllerPathGlobs" config option described in the readme: https://github.com/lukeautry/tsoa
|
|
67
|
+
// ###########################################################################################################
|
|
68
|
+
const argsVmController_getVms = {
|
|
69
|
+
req: { "in": "request", "name": "req", "required": true, "dataType": "object" },
|
|
70
|
+
fields: { "in": "query", "name": "fields", "dataType": "string" },
|
|
71
|
+
filter: { "in": "query", "name": "filter", "dataType": "string" },
|
|
72
|
+
limit: { "in": "query", "name": "limit", "dataType": "double" },
|
|
73
|
+
};
|
|
74
|
+
app.get('/rest/v0/vms', authenticateMiddleware([{ "*": [] }]), ...(fetchMiddlewares(VmController)), ...(fetchMiddlewares(VmController.prototype.getVms)), async function VmController_getVms(request, response, next) {
|
|
75
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
76
|
+
let validatedArgs = [];
|
|
77
|
+
try {
|
|
78
|
+
validatedArgs = templateService.getValidatedArgs({ args: argsVmController_getVms, request, response });
|
|
79
|
+
const container = typeof iocContainer === 'function' ? iocContainer(request) : iocContainer;
|
|
80
|
+
const controller = await container.get(VmController);
|
|
81
|
+
if (typeof controller['setStatus'] === 'function') {
|
|
82
|
+
controller.setStatus(undefined);
|
|
83
|
+
}
|
|
84
|
+
await templateService.apiHandler({
|
|
85
|
+
methodName: 'getVms',
|
|
86
|
+
controller,
|
|
87
|
+
response,
|
|
88
|
+
next,
|
|
89
|
+
validatedArgs,
|
|
90
|
+
successStatus: undefined,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
return next(err);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
98
|
+
const argsVmController_getVm = {
|
|
99
|
+
id: { "in": "path", "name": "id", "required": true, "dataType": "string" },
|
|
100
|
+
};
|
|
101
|
+
app.get('/rest/v0/vms/:id', authenticateMiddleware([{ "*": [] }]), ...(fetchMiddlewares(VmController)), ...(fetchMiddlewares(VmController.prototype.getVm)), async function VmController_getVm(request, response, next) {
|
|
102
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
103
|
+
let validatedArgs = [];
|
|
104
|
+
try {
|
|
105
|
+
validatedArgs = templateService.getValidatedArgs({ args: argsVmController_getVm, request, response });
|
|
106
|
+
const container = typeof iocContainer === 'function' ? iocContainer(request) : iocContainer;
|
|
107
|
+
const controller = await container.get(VmController);
|
|
108
|
+
if (typeof controller['setStatus'] === 'function') {
|
|
109
|
+
controller.setStatus(undefined);
|
|
110
|
+
}
|
|
111
|
+
await templateService.apiHandler({
|
|
112
|
+
methodName: 'getVm',
|
|
113
|
+
controller,
|
|
114
|
+
response,
|
|
115
|
+
next,
|
|
116
|
+
validatedArgs,
|
|
117
|
+
successStatus: undefined,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
return next(err);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
125
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
126
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
127
|
+
function authenticateMiddleware(security = []) {
|
|
128
|
+
return async function runAuthenticationMiddleware(request, response, next) {
|
|
129
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
130
|
+
// keep track of failed auth attempts so we can hand back the most
|
|
131
|
+
// recent one. This behavior was previously existing so preserving it
|
|
132
|
+
// here
|
|
133
|
+
const failedAttempts = [];
|
|
134
|
+
const pushAndRethrow = (error) => {
|
|
135
|
+
failedAttempts.push(error);
|
|
136
|
+
throw error;
|
|
137
|
+
};
|
|
138
|
+
const secMethodOrPromises = [];
|
|
139
|
+
for (const secMethod of security) {
|
|
140
|
+
if (Object.keys(secMethod).length > 1) {
|
|
141
|
+
const secMethodAndPromises = [];
|
|
142
|
+
for (const name in secMethod) {
|
|
143
|
+
secMethodAndPromises.push(expressAuthenticationRecasted(request, name, secMethod[name], response)
|
|
144
|
+
.catch(pushAndRethrow));
|
|
145
|
+
}
|
|
146
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
147
|
+
secMethodOrPromises.push(Promise.all(secMethodAndPromises)
|
|
148
|
+
.then(users => { return users[0]; }));
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
for (const name in secMethod) {
|
|
152
|
+
secMethodOrPromises.push(expressAuthenticationRecasted(request, name, secMethod[name], response)
|
|
153
|
+
.catch(pushAndRethrow));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
158
|
+
try {
|
|
159
|
+
request['user'] = await Promise.any(secMethodOrPromises);
|
|
160
|
+
// Response was sent in middleware, abort
|
|
161
|
+
if (response.writableEnded) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
next();
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
// Show most recent error as response
|
|
168
|
+
const error = failedAttempts.pop();
|
|
169
|
+
error.status = error.status || 401;
|
|
170
|
+
// Response was sent in middleware, abort
|
|
171
|
+
if (response.writableEnded) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
next(error);
|
|
175
|
+
}
|
|
176
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
180
|
+
}
|
|
181
|
+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export class RestApi {
|
|
2
|
+
#xoApp;
|
|
3
|
+
constructor(xoApp) {
|
|
4
|
+
this.#xoApp = xoApp;
|
|
5
|
+
}
|
|
6
|
+
authenticateUser(...args) {
|
|
7
|
+
return this.#xoApp.authenticateUser(...args);
|
|
8
|
+
}
|
|
9
|
+
getObject(id, type) {
|
|
10
|
+
return this.#xoApp.getObject(id, type);
|
|
11
|
+
}
|
|
12
|
+
getObjectsByType(type, opts) {
|
|
13
|
+
return this.#xoApp.getObjectsByType(type, opts);
|
|
14
|
+
}
|
|
15
|
+
runWithApiContext(...args) {
|
|
16
|
+
return this.#xoApp.runWithApiContext(...args);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
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 } from 'tsoa';
|
|
11
|
+
import { inject } from 'inversify';
|
|
12
|
+
import { provide } from 'inversify-binding-decorators';
|
|
13
|
+
import { partialVms, vm, vmIds } from '../open-api/examples/vm.example.mjs';
|
|
14
|
+
import { RestApi } from '../rest-api/rest-api.mjs';
|
|
15
|
+
import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
|
|
16
|
+
let VmController = class VmController extends XapiXoController {
|
|
17
|
+
constructor(restApi) {
|
|
18
|
+
super('VM', restApi);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
*
|
|
22
|
+
* @example fields "name_label,power_state,uuid"
|
|
23
|
+
* @example filter "power_state:Running"
|
|
24
|
+
* @example limit 42
|
|
25
|
+
*/
|
|
26
|
+
getVms(req, fields, filter, limit) {
|
|
27
|
+
return this.sendObjects(Object.values(this.getObjects({ filter, limit })), req);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
* @example id "f07ab729-c0e8-721c-45ec-f11276377030"
|
|
32
|
+
*/
|
|
33
|
+
getVm(id) {
|
|
34
|
+
return this.getObject(id);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
__decorate([
|
|
38
|
+
Example(vmIds),
|
|
39
|
+
Example(partialVms),
|
|
40
|
+
Get(''),
|
|
41
|
+
__param(0, Request()),
|
|
42
|
+
__param(1, Query()),
|
|
43
|
+
__param(2, Query()),
|
|
44
|
+
__param(3, Query())
|
|
45
|
+
], VmController.prototype, "getVms", null);
|
|
46
|
+
__decorate([
|
|
47
|
+
Example(vm),
|
|
48
|
+
Get('{id}'),
|
|
49
|
+
Response(404),
|
|
50
|
+
__param(0, Path())
|
|
51
|
+
], VmController.prototype, "getVm", null);
|
|
52
|
+
VmController = __decorate([
|
|
53
|
+
Route('vms'),
|
|
54
|
+
Security('*'),
|
|
55
|
+
Response(401)
|
|
56
|
+
// the `provide` decorator is mandatory on class that injects/receives dependencies.
|
|
57
|
+
// It automatically bind the class to the IOC container that handles dependency injection
|
|
58
|
+
,
|
|
59
|
+
provide(VmController),
|
|
60
|
+
__param(0, inject(RestApi))
|
|
61
|
+
], VmController);
|
|
62
|
+
export { VmController };
|