async-xenapi 1.0.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 +48 -0
- package/dist/XenAPI.d.ts +35 -0
- package/dist/XenAPI.js +97 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# async-xenapi (JavaScript / TypeScript)
|
|
2
|
+
|
|
3
|
+
An async TypeScript library for [XenAPI](https://xapi-project.github.io/xen-api)
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
npm install async-xenapi
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires **Node.js 18+** (uses the global `fetch` API).
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { AsyncXenAPISession } from "async-xenapi";
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
const session = new AsyncXenAPISession(process.env.HOST_URL);
|
|
20
|
+
try {
|
|
21
|
+
await session.login_with_password(process.env.USERNAME, process.env.PASSWORD);
|
|
22
|
+
const hosts = await session.xenapi.host.get_all();
|
|
23
|
+
console.log(hosts);
|
|
24
|
+
} finally {
|
|
25
|
+
await session.xenapi.session.logout();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
main();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The API mirrors the synchronous Python XenAPI SDK — any dotted method path under `session.xenapi` is translated directly to the corresponding JSON-RPC call. See the [XenAPI Reference](https://xapi-project.github.io/xen-api) for all available classes and fields.
|
|
33
|
+
|
|
34
|
+
## Run tests
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
git clone git@github.com:acefei/async-xenapi.git
|
|
38
|
+
cd async-xenapi/javascript
|
|
39
|
+
npm install
|
|
40
|
+
echo "HOST_URL=https://<xen-host>" >> .env
|
|
41
|
+
echo "USERNAME=root" >> .env
|
|
42
|
+
echo "PASSWORD=<password>" >> .env
|
|
43
|
+
npm test tests/getXapiVersion.test.ts
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
GPL-2.0-only
|
package/dist/XenAPI.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async XenAPI session via JSON-RPC (built-in fetch + crypto only, Node 18+)
|
|
3
|
+
*
|
|
4
|
+
* Usage mirrors the Python AsyncXenAPISession:
|
|
5
|
+
*
|
|
6
|
+
* const session = new AsyncXenAPISession("https://host-ip");
|
|
7
|
+
* await session.login_with_password("root", "password");
|
|
8
|
+
*
|
|
9
|
+
* const vms = await session.xenapi.VM.get_all();
|
|
10
|
+
* for (const vm of vms) {
|
|
11
|
+
* const record = await session.xenapi.VM.get_record(vm);
|
|
12
|
+
* console.log(record.name_label);
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* await session.logout();
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Recursive type describing the xenapi namespace proxy.
|
|
19
|
+
* Every property access returns another XenApiProxy;
|
|
20
|
+
* invoking it dispatches an authenticated JSON-RPC call.
|
|
21
|
+
*/
|
|
22
|
+
export type XenApiProxy = ((...args: unknown[]) => Promise<unknown>) & {
|
|
23
|
+
readonly [key: string]: XenApiProxy;
|
|
24
|
+
};
|
|
25
|
+
export declare class AsyncXenAPISession {
|
|
26
|
+
private readonly _url;
|
|
27
|
+
private _sessionRef;
|
|
28
|
+
readonly xenapi: XenApiProxy;
|
|
29
|
+
constructor(url: string | undefined);
|
|
30
|
+
login_with_password(user: string, password: string): Promise<string>;
|
|
31
|
+
logout(): Promise<void>;
|
|
32
|
+
_call(method: string, params?: unknown[]): Promise<unknown>;
|
|
33
|
+
}
|
|
34
|
+
/** Convenience factory — equivalent to `new AsyncXenAPISession(url)` */
|
|
35
|
+
export declare function xapi_client(url: string | undefined): AsyncXenAPISession;
|
package/dist/XenAPI.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Async XenAPI session via JSON-RPC (built-in fetch + crypto only, Node 18+)
|
|
4
|
+
*
|
|
5
|
+
* Usage mirrors the Python AsyncXenAPISession:
|
|
6
|
+
*
|
|
7
|
+
* const session = new AsyncXenAPISession("https://host-ip");
|
|
8
|
+
* await session.login_with_password("root", "password");
|
|
9
|
+
*
|
|
10
|
+
* const vms = await session.xenapi.VM.get_all();
|
|
11
|
+
* for (const vm of vms) {
|
|
12
|
+
* const record = await session.xenapi.VM.get_record(vm);
|
|
13
|
+
* console.log(record.name_label);
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* await session.logout();
|
|
17
|
+
*/
|
|
18
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
19
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
20
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
21
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
22
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
23
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
24
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.AsyncXenAPISession = void 0;
|
|
29
|
+
exports.xapi_client = xapi_client;
|
|
30
|
+
function _jsonrpcReq(method, params) {
|
|
31
|
+
return { jsonrpc: "2.0", method, params, id: crypto.randomUUID() };
|
|
32
|
+
}
|
|
33
|
+
function _post(url, payload) {
|
|
34
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
+
const response = yield fetch(url, {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: { "content-type": "application/json" },
|
|
38
|
+
body: JSON.stringify(payload),
|
|
39
|
+
});
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
42
|
+
}
|
|
43
|
+
return response.json();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
// Accumulates dotted property access (e.g. xenapi.VM.get_all) and dispatches
|
|
47
|
+
// the final call as an authenticated JSON-RPC request.
|
|
48
|
+
function _xenApiNamespace(session, path = []) {
|
|
49
|
+
return new Proxy(() => { }, {
|
|
50
|
+
get(_target, property) {
|
|
51
|
+
return _xenApiNamespace(session, path.concat(property));
|
|
52
|
+
},
|
|
53
|
+
apply(_target, _self, args) {
|
|
54
|
+
return session._call(path.join("."), args);
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
class AsyncXenAPISession {
|
|
59
|
+
constructor(url) {
|
|
60
|
+
this._url = `${url}/jsonrpc`;
|
|
61
|
+
this.xenapi = _xenApiNamespace(this);
|
|
62
|
+
}
|
|
63
|
+
login_with_password(user, password) {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
const ret = yield _post(this._url, _jsonrpcReq("session.login_with_password", [user, password, "version", "originator"]));
|
|
66
|
+
if (ret.error)
|
|
67
|
+
throw new Error(`Login failed: ${JSON.stringify(ret.error)}`);
|
|
68
|
+
this._sessionRef = ret.result;
|
|
69
|
+
return this._sessionRef;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
logout() {
|
|
73
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
if (this._sessionRef) {
|
|
75
|
+
const ret = yield _post(this._url, _jsonrpcReq("session.logout", [this._sessionRef]));
|
|
76
|
+
if (ret.error)
|
|
77
|
+
throw new Error(`Logout failed: ${JSON.stringify(ret.error)}`);
|
|
78
|
+
this._sessionRef = undefined;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
_call(method_1) {
|
|
83
|
+
return __awaiter(this, arguments, void 0, function* (method, params = []) {
|
|
84
|
+
if (!this._sessionRef)
|
|
85
|
+
throw new Error("Not logged in");
|
|
86
|
+
const ret = yield _post(this._url, _jsonrpcReq(method, [this._sessionRef, ...params]));
|
|
87
|
+
if (ret.error)
|
|
88
|
+
throw new Error(`XAPI ${method} failed: ${JSON.stringify(ret.error)}`);
|
|
89
|
+
return ret.result;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.AsyncXenAPISession = AsyncXenAPISession;
|
|
94
|
+
/** Convenience factory — equivalent to `new AsyncXenAPISession(url)` */
|
|
95
|
+
function xapi_client(url) {
|
|
96
|
+
return new AsyncXenAPISession(url);
|
|
97
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "async-xenapi",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "An async TypeScript library for Xen API",
|
|
5
|
+
"main": "dist/XenAPI.js",
|
|
6
|
+
"types": "dist/XenAPI.d.ts",
|
|
7
|
+
"files": ["/dist"],
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "node -r ts-node/register -r dotenv/config ",
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"lint": "biome check src/ tests/",
|
|
12
|
+
"fmt": "biome check --write src/ tests/",
|
|
13
|
+
"lint:ci": "biome check --diagnostic-level=error src/ tests/"
|
|
14
|
+
},
|
|
15
|
+
"keywords": ["xenserver", "xen-api", "typescript", "async"],
|
|
16
|
+
"author": "Su Fei <fei.su@cloud.com>",
|
|
17
|
+
"license": "GPL-2.0-only",
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@biomejs/biome": "^1.9.0",
|
|
20
|
+
"@types/node": "^20.8.9",
|
|
21
|
+
"axios": "^1.6.0",
|
|
22
|
+
"dotenv": "^16.3.1",
|
|
23
|
+
"ts-node": "^10.9.1",
|
|
24
|
+
"typescript": "^5.2.2"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {}
|
|
27
|
+
}
|